/* * This file is part of the Palacios Virtual Machine Monitor developed * by the V3VEE Project with funding from the United States National * Science Foundation and the Department of Energy. * * The V3VEE Project is a joint project between Northwestern University * and the University of New Mexico. You can find out more at * http://www.v3vee.org * * Copyright (c) 2008, Jack Lange * Copyright (c) 2008, The V3VEE Project * All rights reserved. * * Author: Jack Lange * Patrick G. Bridges * * This is free software. You are permitted to use, * redistribute, and modify it as specified in the file "V3VEE_LICENSE". */ #include #include #include #ifndef CONFIG_DEBUG_TIME #undef PrintDebug #define PrintDebug(fmt, args...) #endif static int handle_cpufreq_hcall(struct guest_info * info, uint_t hcall_id, void * priv_data) { struct vm_time * time_state = &(info->time_state); info->vm_regs.rbx = time_state->cpu_freq; PrintDebug("Guest request cpu frequency: return %ld\n", (long)info->vm_regs.rbx); return 0; } void v3_init_time(struct guest_info * info) { struct vm_time * time_state = &(info->time_state); time_state->cpu_freq = V3_CPU_KHZ(); time_state->pause_time = 0; time_state->last_update = 0; time_state->host_offset = 0; time_state->offset_sum = 0; INIT_LIST_HEAD(&(time_state->timers)); time_state->num_timers = 0; v3_register_hypercall(info->vm_info, TIME_CPUFREQ_HCALL, handle_cpufreq_hcall, NULL); } int v3_start_time(struct guest_info * info) { /* We start running with guest_time == host_time */ uint64_t t = v3_get_host_time(&info->time_state); PrintDebug("Starting initial guest time as %llu\n", t); info->time_state.last_update = t; info->time_state.pause_time = t; info->yield_start_cycle = t; return 0; } int v3_pause_time(struct guest_info * info) { V3_ASSERT(info->time_state.pause_time == 0); info->time_state.pause_time = v3_get_guest_time(&info->time_state); PrintDebug("Time paused at guest time as %llu\n", info->time_state.pause_time); return 0; } /* Use a control-theoretic approach, specifically a PI control approach, * to adjust host_offset towards 0. Overall control documentation in * palacios/docs/time_control.tex Control parameters are P and I, * broken into rational numbers */ /* These numbers need to be actually determined by pole placement work. They're * just blind guesses for now, which is a really bad idea. :) */ #define P_NUM 1 #define P_DENOM 2 #define I_NUM 1 #define I_DENOM 20 void adjust_time_offset(struct guest_info * info) { /* Set point for control: Desired offset = 0; * Error = host_offset - 0 = host_offset */ sint64_t adjust; /* Update the integral of the errror */ info->time_state.offset_sum += info->time_state.host_offset; adjust = (P_NUM * info->time_state.host_offset) / P_DENOM + (I_NUM * info->time_state.offset_sum) / I_DENOM; /* We may want to constrain *adjust* because of * resolution/accuracy constraints. Explore that later. */ info->time_state.host_offset -= adjust; return; } int v3_resume_time(struct guest_info * info) { uint64_t t = v3_get_host_time(&info->time_state); V3_ASSERT(info->time_state.pause_time != 0); info->time_state.host_offset = (sint64_t)info->time_state.pause_time - (sint64_t)t; #ifdef CONFIG_TIME_TSC_OFFSET_ADJUST adjust_time_offset(info); #endif info->time_state.pause_time = 0; PrintDebug("Time resumed paused at guest time as %llu " "offset %lld from host time.\n", t, info->time_state.host_offset); return 0; } int v3_add_timer(struct guest_info * info, struct vm_timer_ops * ops, void * private_data) { struct vm_timer * timer = NULL; timer = (struct vm_timer *)V3_Malloc(sizeof(struct vm_timer)); V3_ASSERT(timer != NULL); timer->ops = ops; timer->private_data = private_data; list_add(&(timer->timer_link), &(info->time_state.timers)); info->time_state.num_timers++; return 0; } int v3_remove_timer(struct guest_info * info, struct vm_timer * timer) { list_del(&(timer->timer_link)); info->time_state.num_timers--; V3_Free(timer); return 0; } void v3_update_timers(struct guest_info * info) { struct vm_timer * tmp_timer; uint64_t old_time = info->time_state.last_update; uint64_t cycles; info->time_state.last_update = v3_get_guest_time(&info->time_state); cycles = info->time_state.last_update - old_time; list_for_each_entry(tmp_timer, &(info->time_state.timers), timer_link) { tmp_timer->ops->update_timer(info, cycles, info->time_state.cpu_freq, tmp_timer->private_data); } }