From: Patrick G. Bridges Date: Tue, 17 Jan 2012 17:53:22 +0000 (-0700) Subject: Start at allowing setting the vmx schedule timeout X-Git-Url: http://v3vee.org/palacios/gitweb/gitweb.cgi?a=commitdiff_plain;h=635a17215f8c66fa9ef54a7bb2cef6c709ddf523;p=palacios.releases.git Start at allowing setting the vmx schedule timeout --- diff --git a/palacios/include/palacios/vmm_time.h b/palacios/include/palacios/vmm_time.h index 33ffa2c..25eed02 100644 --- a/palacios/include/palacios/vmm_time.h +++ b/palacios/include/palacios/vmm_time.h @@ -49,6 +49,11 @@ struct vm_time { // Installed Timers slaved off of the guest monotonic TSC uint_t num_timers; struct list_head timers; + + // Installed timeout handlers, and the time (in monotonic guest time) of hte + // next timeout. + uint64_t next_timeout; + struct list_head timeout_hooks; }; struct v3_timer_ops { @@ -60,9 +65,19 @@ struct v3_timer { void * private_data; struct v3_timer_ops * ops; + // Need to add accuracy/resolution fields later. + struct list_head timer_link; }; +typedef void (*v3_timeout_callback_t)(struct guest_info * info, void * priv_data); +struct v3_timeout_hook { + void * private_data; + v3_timeout_callback_t callback; + + struct list_head hook_link; +}; + // Basic functions for handling passage of time in palacios void v3_init_time_core(struct guest_info * core); int v3_init_time_vm(struct v3_vm_info * vm); @@ -78,11 +93,21 @@ int v3_time_exit_vm(struct guest_info * core); int v3_adjust_time(struct guest_info * core); int v3_offset_time(struct guest_info * core, sint64_t offset); -// Basic functions for attaching timers to the passage of time +// Basic functions for attaching timers to the passage of time - these timers +// should eventually specify their accuracy and resolution. struct v3_timer * v3_add_timer(struct guest_info * info, struct v3_timer_ops * ops, void * private_data); int v3_remove_timer(struct guest_info * info, struct v3_timer * timer); void v3_update_timers(struct guest_info * info); +// Functions for handling one-shot timeouts in Palacios. Note that only one +// timeout is every currently outstanding (the soonest scheduled one!), and that +// all hooks are called on any timeout. If a hook gets called before the desired +// timeout time, that hook should reschedule its own timeout if desired. +struct v3_timeout_hook * v3_add_timeout_hook(struct guest_info * info, v3_timeout_callback_t callback, void * priv_data); +int v3_remove_timeout_hook(struct guest_info * info, struct v3_timeout_hook * hook); +int v3_schedule_timeout(struct guest_info * info, ullong_t cycles); +int v3_check_timeout(struct guest_info * info); + // Functions to return the different notions of time in Palacios. static inline uint64_t v3_get_host_time(struct vm_time *t) { uint64_t tmp; diff --git a/palacios/src/palacios/svm.c b/palacios/src/palacios/svm.c index ce6fe5d..e6ded68 100644 --- a/palacios/src/palacios/svm.c +++ b/palacios/src/palacios/svm.c @@ -539,6 +539,11 @@ int v3_svm_enter(struct guest_info * info) { // Perform any additional yielding needed for time adjustment v3_adjust_time(info); + // Check for timeout - since this calls generic hooks in devices + // that may do things like pause the VM, it cannot be with interrupts + // disabled. + v3_check_timeout(info); + // disable global interrupts for vm state transition v3_clgi(); diff --git a/palacios/src/palacios/vmm_time.c b/palacios/src/palacios/vmm_time.c index 7661832..a3a5e5e 100644 --- a/palacios/src/palacios/vmm_time.c +++ b/palacios/src/palacios/vmm_time.c @@ -296,6 +296,52 @@ void v3_update_timers(struct guest_info * info) { } } +/* Handle TSC timeout hooks */ +struct v3_timeout_hook * +v3_add_timeout_hook(struct guest_info * info, v3_timeout_callback_t callback, + void * priv_data) { + struct v3_timeout_hook * timeout = NULL; + timeout = (struct v3_timeout_hook *)V3_Malloc(sizeof(struct v3_timeout_hook)); + V3_ASSERT(timeout != NULL); + + timeout->callback = callback; + timeout->private_data = priv_data; + + list_add(&(timeout->hook_link), &(info->time_state.timeout_hooks)); + return timeout; +} + +int +v3_remove_timeout_hook(struct guest_info * info, struct v3_timeout_hook * hook) { + list_del(&(hook->hook_link)); + V3_Free(hook); + return 0; +} + +int v3_schedule_timeout(struct guest_info * info, ullong_t guest_timeout) { + struct vm_time *time_state = &info->time_state; + /* Note that virtualization architectures that support it (like newer + * VMX systems) will turn on an active preemption timeout if + * available to get this timeout as closely as possible. Other systems + * only catch it in the periodic interrupt and so are less precise */ + if (guest_timeout < time_state->next_timeout) { + time_state->next_timeout = guest_timeout; + } + return 0; +} + +int v3_check_timeout( struct guest_info * info ) { + struct vm_time *time_state = &info->time_state; + if (time_state->next_timeout <= v3_get_guest_time(time_state)) { + struct v3_timeout_hook * tmp_timeout; + time_state->next_timeout = (ullong_t)-1; + list_for_each_entry(tmp_timeout, &(time_state->timeout_hooks), hook_link) { + tmp_timeout->callback(info, tmp_timeout->private_data); + } + } + return 0; +} + /* * Handle full virtualization of the time stamp counter. As noted * above, we don't store the actual value of the TSC, only the guest's @@ -478,6 +524,9 @@ void v3_init_time_core(struct guest_info * info) { time_state->guest_host_offset = 0; time_state->tsc_guest_offset = 0; + INIT_LIST_HEAD(&(time_state->timeout_hooks)); + time_state->next_timeout = (ullong_t)-1; + INIT_LIST_HEAD(&(time_state->timers)); time_state->num_timers = 0; diff --git a/palacios/src/palacios/vmx.c b/palacios/src/palacios/vmx.c index 7df8f18..04482ee 100644 --- a/palacios/src/palacios/vmx.c +++ b/palacios/src/palacios/vmx.c @@ -757,6 +757,34 @@ static void print_exit_log(struct guest_info * info) { } +int +v3_vmx_schedule_timeout(struct guest_info * info) +{ + struct vmx_data * vmx_state = (struct vmx_data *)(info->vmm_data); + sint64_t cycles; + uint32_t timeout; + + /* Check if the hardware supports an active timeout */ +#define VMX_ACTIVE_PREEMPT_TIMER_PIN 0x40 + if (hw_info.pin_ctrls.req_mask & VMX_ACTIVE_PREEMPT_TIMER_PIN) { + return 0; + } + + /* Check if we have one to schedule */ + cycles = (sint64_t)info->time_state.next_timeout - (sint64_t)v3_get_guest_time(&info->time_state); + if ((info->time_state.next_timeout == (ullong_t) -1) || (cycles < 0)) { + vmx_state->pin_ctrls.active_preempt_timer = 0; + } else { + /* The hardware supports scheduling a timeout, and we have one to + * schedule */ + timeout = (uint32_t)cycles >> hw_info.misc_info.tsc_multiple; + vmx_state->pin_ctrls.active_preempt_timer = 1; + check_vmcs_write(VMCS_PREEMPT_TIMER, timeout); + } + check_vmcs_write(VMCS_PIN_CTRLS, vmx_state->pin_ctrls.value); + return 0; +} + /* * CAUTION and DANGER!!! * @@ -777,11 +805,16 @@ int v3_vmx_enter(struct guest_info * info) { // Perform any additional yielding needed for time adjustment v3_adjust_time(info); + // Check for timeout - since this calls generic hooks in devices + // that may do things like pause the VM, it cannot be with interrupts + // disabled. + v3_check_timeout(info); + // disable global interrupts for vm state transition v3_disable_ints(); // Update timer devices late after being in the VM so that as much - // of hte time in the VM is accounted for as possible. Also do it before + // of the time in the VM is accounted for as possible. Also do it before // updating IRQ entry state so that any interrupts the timers raise get // handled on the next VM entry. Must be done with interrupts disabled. v3_update_timers(info); @@ -809,6 +842,10 @@ int v3_vmx_enter(struct guest_info * info) { vmcs_write(VMCS_GUEST_CR3, guest_cr3); } + // Update vmx active preemption timer to exit at the next timeout if + // the hardware supports it. + v3_vmx_schedule_timeout(info); + // Perform last-minute time bookkeeping prior to entering the VM v3_time_enter_vm(info);