* redistribute, and modify it as specified in the file "V3VEE_LICENSE".
*/
-#include <palacios/vmm_time.h>
#include <palacios/vmm.h>
+#include <palacios/vmm_time.h>
#include <palacios/vm_guest.h>
#ifndef CONFIG_DEBUG_TIME
uint64_t t = v3_get_host_time(&info->time_state);
PrintDebug("Starting initial guest time as %llu\n", t);
-#ifdef CONFIG_TIME_HIDE_VM_COST
- info->time_state.pause_time = t;
-#else
- info->time_state.pause_time = 0;
-#endif
+
+ info->time_state.enter_time = 0;
+ info->time_state.exit_time = t;
info->time_state.last_update = t;
info->time_state.initial_time = t;
info->yield_start_cycle = t;
+
return 0;
}
-// If the guest is supposed to run slower than the host, yield out until
-// the host time is appropriately far along;
-int v3_adjust_time(struct guest_info * info) {
+int v3_offset_time( struct guest_info * info, sint64_t offset )
+{
struct vm_time * time_state = &(info->time_state);
+// PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
+ time_state->guest_host_offset += offset;
+ return 0;
+}
- if (time_state->host_cpu_freq != time_state->guest_cpu_freq) {
- uint64_t guest_time, host_time, target_host_time;
- sint64_t guest_elapsed, desired_elapsed;
+// Control guest time in relation to host time so that the two stay
+// appropriately synchronized to the extent possible.
+int v3_adjust_time(struct guest_info * info) {
+ struct vm_time * time_state = &(info->time_state);
+ uint64_t host_time, target_host_time;
+ uint64_t guest_time, target_guest_time, old_guest_time;
+ uint64_t guest_elapsed, host_elapsed, desired_elapsed;
- guest_time = v3_get_guest_time(time_state);
+ /* Compute the target host time given how much time has *already*
+ * passed in the guest */
+ guest_time = v3_get_guest_time(time_state);
+ guest_elapsed = (guest_time - time_state->initial_time);
+ desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
+ target_host_time = time_state->initial_time + desired_elapsed;
- /* Compute what host time this guest time should correspond to. */
- guest_elapsed = (guest_time - time_state->initial_time);
- desired_elapsed = (guest_elapsed * time_state->host_cpu_freq) / time_state->guest_cpu_freq;
- target_host_time = time_state->initial_time + desired_elapsed;
+ /* Now, let the host run while the guest is stopped to make the two
+ * sync up. */
+ host_time = v3_get_host_time(time_state);
+ old_guest_time = v3_get_guest_time(time_state);
- /* Yield until that host time is reached */
+ while (target_host_time > host_time) {
+ v3_yield(info);
host_time = v3_get_host_time(time_state);
+ }
- if (host_time < target_host_time) {
- PrintDebug("Yielding until host time (%llu) greater than target (%llu).\n", host_time, target_host_time);
- }
+ guest_time = v3_get_guest_time(time_state);
- while (host_time < target_host_time) {
- v3_yield(info);
- host_time = v3_get_host_time(time_state);
+ // We do *not* assume the guest timer was paused in the VM. If it was
+ // this offseting is 0. If it wasn't we need this.
+ v3_offset_time(info, (sint64_t)old_guest_time - (sint64_t)guest_time);
+
+ /* Now the host may have gotten ahead of the guest because
+ * yielding is a coarse grained thing. Figure out what guest time
+ * we want to be at, and use the use the offsetting mechanism in
+ * the VMM to make the guest run forward. We limit *how* much we skew
+ * it forward to prevent the guest time making large jumps,
+ * however. */
+ host_elapsed = host_time - time_state->initial_time;
+ desired_elapsed = (host_elapsed * time_state->guest_cpu_freq) / time_state->host_cpu_freq;
+ target_guest_time = time_state->initial_time + desired_elapsed;
+
+ if (guest_time < target_guest_time) {
+ uint64_t max_skew, desired_skew, skew;
+
+ if (time_state->enter_time) {
+ max_skew = (time_state->exit_time - time_state->enter_time) / 10;
+ } else {
+ max_skew = 0;
}
-#ifndef CONFIG_TIME_HIDE_VM_COST
- // XXX This should turn into a target offset we want to move towards XXX
- time_state->guest_host_offset =
- (sint64_t)guest_time - (sint64_t)host_time;
-#endif
+ desired_skew = target_guest_time - guest_time;
+ skew = desired_skew > max_skew ? max_skew : desired_skew;
+/* PrintDebug("Guest %llu cycles behind where it should be.\n",
+ desired_skew);
+ PrintDebug("Limit on forward skew is %llu. Skewing forward %llu.\n",
+ max_skew, skew); */
+
+ v3_offset_time(info, skew);
}
-
+
return 0;
}
+/* Called immediately upon entry in the the VMM */
int
-v3_pause_time( struct guest_info * info )
+v3_time_exit_vm( struct guest_info * info )
{
struct vm_time * time_state = &(info->time_state);
- if (time_state->pause_time == 0) {
- time_state->pause_time = v3_get_host_time(time_state);
-// PrintDebug("Pausing at host time %llu.\n", time_state->pause_time);
- } else {
- PrintError("Palacios timekeeping paused when already paused.\n");
- }
+
+ time_state->exit_time = v3_get_host_time(time_state);
+
return 0;
}
+/* Called immediately prior to entry to the VM */
int
-v3_restart_time( struct guest_info * info )
+v3_time_enter_vm( struct guest_info * info )
{
struct vm_time * time_state = &(info->time_state);
+ uint64_t guest_time, host_time;
- if (time_state->pause_time) {
- sint64_t pause_diff = (v3_get_host_time(time_state) - time_state->pause_time);
- time_state->guest_host_offset -= pause_diff;
- time_state->pause_time = 0;
-// PrintDebug("Resuming time after %lld cycles with offset %lld.\n", pause_diff, time_state->guest_host_offset);
- } else {
- PrintError( "Palacios time keeping restarted when not paused.");
- }
+ guest_time = v3_get_guest_time(time_state);
+ host_time = v3_get_host_time(time_state);
+ time_state->enter_time = host_time;
+ time_state->guest_host_offset = guest_time - host_time;
+
+ // Because we just modified the offset - shouldn't matter as this should be
+ // the last time-related call prior to entering the VMM, but worth it
+ // just in case.
+ time_state->exit_time = host_time;
return 0;
}
-int v3_offset_time( struct guest_info * info, sint64_t offset )
-{
- struct vm_time * time_state = &(info->time_state);
-// PrintDebug("Adding additional offset of %lld to guest time.\n", offset);
- time_state->guest_host_offset += offset;
- return 0;
-}
+
struct v3_timer * v3_add_timer(struct guest_info * info,
struct v3_timer_ops * ops,
time_state->last_update = v3_get_guest_time(time_state);
cycles = time_state->last_update - old_time;
- // PrintDebug("Updating timer for %lld elapsed cycles (pt=%llu, offset=%lld).\n",
- // cycles, time_state->pause_time, time_state->guest_host_offset);
-
list_for_each_entry(tmp_timer, &(time_state->timers), timer_link) {
tmp_timer->ops->update_timer(info, cycles, time_state->guest_cpu_freq, tmp_timer->private_data);
}
int v3_rdtsc(struct guest_info * info) {
uint64_t tscval = v3_get_guest_tsc(&info->time_state);
+
info->vm_regs.rdx = tscval >> 32;
info->vm_regs.rax = tscval & 0xffffffffLL;
+
return 0;
}
info->cpu_id, time_state->guest_cpu_freq);
}
- if ((khz == NULL) || (time_state->guest_cpu_freq <= 0)
- || (time_state->guest_cpu_freq > time_state->host_cpu_freq)) {
+ if ( (khz == NULL) ||
+ (time_state->guest_cpu_freq <= 0) ||
+ (time_state->guest_cpu_freq > time_state->host_cpu_freq) ) {
+
time_state->guest_cpu_freq = time_state->host_cpu_freq;
}
time_state->tsc_aux.lo = 0;
time_state->tsc_aux.hi = 0;
-
-
}
}
}
-
-
-
-
-
-