2 * This file is part of the Palacios Virtual Machine Monitor developed
3 * by the V3VEE Project with funding from the United States National
4 * Science Foundation and the Department of Energy.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2013, Oscar Mondragon <omondrag@cs.unm.edu>
11 * Copyright (c) 2013, Patrick G. Bridges <bridges@cs.unm.edu>
12 * Copyright (c) 2013, The V3VEE Project <http://www.v3vee.org>
13 * All rights reserved.
15 * Author: Oscar Mondragon <omondrag@cs.unm.edu>
16 * Patrick G. Bridges <bridges@cs.unm.edu>
18 * This is free software. You are permitted to use,
19 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
22 #include <palacios/vmm.h>
23 #include <palacios/vm_guest.h>
24 #include <palacios/vmm_scheduler.h>
25 #include <palacios/vmm_hashtable.h>
27 #ifndef V3_CONFIG_DEBUG_SCHEDULER
29 #define PrintDebug(fmt, args...)
32 static char default_strategy[] = "host";
33 static struct hashtable * master_scheduler_table = NULL;
34 static int create_host_scheduler();
35 static int destroy_host_scheduler();
37 static struct vm_scheduler_impl *scheduler = NULL;
39 static uint_t scheduler_hash_fn(addr_t key) {
40 char * name = (char *)key;
41 return v3_hash_buffer((uint8_t *)name, strlen(name));
44 static int scheduler_eq_fn(addr_t key1, addr_t key2) {
45 char * name1 = (char *)key1;
46 char * name2 = (char *)key2;
48 return (strcmp(name1, name2) == 0);
51 int V3_init_scheduling() {
53 PrintDebug(VM_NONE, VCORE_NONE,"Initializing scheduler");
55 master_scheduler_table = v3_create_htable(0, scheduler_hash_fn, scheduler_eq_fn);
56 return create_host_scheduler();
59 int V3_deinit_scheduling()
61 destroy_host_scheduler();
62 // important not to remove any keys or values since we don't know
63 // if they are malloced or other
64 v3_free_htable(master_scheduler_table,0,0);
69 int v3_register_scheduler(struct vm_scheduler_impl *s) {
71 PrintDebug(VM_NONE, VCORE_NONE,"Registering Scheduler (%s)\n", s->name);
73 if (v3_htable_search(master_scheduler_table, (addr_t)(s->name))) {
74 PrintError(VM_NONE, VCORE_NONE, "Multiple instances of scheduler (%s)\n", s->name);
78 if (v3_htable_insert(master_scheduler_table,
81 PrintError(VM_NONE, VCORE_NONE, "Could not register scheduler (%s)\n", s->name);
89 struct vm_scheduler_impl *v3_unregister_scheduler(char *name) {
91 PrintDebug(VM_NONE, VCORE_NONE,"Unregistering Scheduler (%s)\n",name);
93 struct vm_scheduler_impl *f = (struct vm_scheduler_impl *) v3_htable_remove(master_scheduler_table,(addr_t)(name),0);
96 PrintError(VM_NONE,VCORE_NONE,"Could not find Scheduler (%s)\n",name);
106 struct vm_scheduler_impl *v3_scheduler_lookup(char *name)
108 return (struct vm_scheduler_impl *)v3_htable_search(master_scheduler_table, (addr_t)(name));
111 int V3_enable_scheduler() {
115 sched_name = v3_lookup_option("scheduler");
118 scheduler = v3_scheduler_lookup(sched_name);
122 scheduler = v3_scheduler_lookup(default_strategy);
126 PrintError(VM_NONE, VCORE_NONE,"Specified Palacios scheduler \"%s\" not found.\n", default_strategy);
130 PrintDebug(VM_NONE, VCORE_NONE,"Scheduler %s found",scheduler->name);
132 if (scheduler->init) {
133 return scheduler->init();
139 int V3_disable_scheduler()
141 if (scheduler->deinit) {
142 return scheduler->deinit();
148 int v3_scheduler_register_vm(struct v3_vm_info *vm) {
149 if (scheduler->vm_init) {
150 return scheduler->vm_init(vm);
155 int v3_scheduler_register_core(struct guest_info *core) {
156 if (scheduler->core_init) {
157 return scheduler->core_init(core);
162 int v3_scheduler_admit_vm(struct v3_vm_info *vm) {
163 if (scheduler->admit) {
164 return scheduler->admit(vm);
169 int v3_scheduler_notify_remap(struct v3_vm_info *vm) {
170 if (scheduler->remap) {
171 return scheduler->remap(vm);
176 int v3_scheduler_notify_dvfs(struct v3_vm_info *vm) {
177 if (scheduler->dvfs) {
178 return scheduler->dvfs(vm);
183 void v3_schedule(struct guest_info *core) {
184 if (scheduler->schedule) {
185 scheduler->schedule(core);
189 void v3_yield(struct guest_info *core, int usec) {
190 if (scheduler->yield) {
191 scheduler->yield(core, usec);
196 int host_sched_vm_init(struct v3_vm_info *vm)
199 PrintDebug(vm, VCORE_NONE,"Sched. host_sched_init\n");
201 char * schedule_hz_str = v3_cfg_val(vm->cfg_data->cfg, "schedule_hz");
202 uint32_t sched_hz = 100;
205 if (schedule_hz_str) {
206 sched_hz = atoi(schedule_hz_str);
209 PrintDebug(vm, VCORE_NONE,"CPU_KHZ = %d, schedule_freq=%p\n", V3_CPU_KHZ(),
210 (void *)(addr_t)sched_hz);
212 uint64_t yield_cycle_period = (V3_CPU_KHZ() * 1000) / sched_hz;
213 vm->sched_priv_data = (void *)yield_cycle_period;
218 int host_sched_core_init(struct guest_info *core)
220 PrintDebug(core->vm_info, core,"Sched. host_sched_core_init\n");
222 uint64_t t = v3_get_host_time(&core->time_state);
223 core->sched_priv_data = (void *)t;
228 int host_sched_core_stop(struct guest_info *core){
235 void host_sched_schedule(struct guest_info *core)
238 cur_cycle = v3_get_host_time(&core->time_state);
240 if (cur_cycle > ( (uint64_t)core->sched_priv_data + (uint64_t)core->vm_info->sched_priv_data)) {
245 uint64_t yield_start_cycle = (uint64_t) core->sched_priv_data;
246 yield_start_cycle += (uint64_t)core->vm_info->sched_priv_data;
247 core->sched_priv_data = (void *)yield_start_cycle;
249 core->sched_priv_data = (void*)v3_get_host_time(&(core->time_state));
256 * unconditional cpu yield
257 * if the yielding thread is a guest context, the guest quantum is reset on resumption
258 * Non guest context threads should call this function with a NULL argument
260 * usec <0 => the non-timed yield is used
261 * usec >=0 => the timed yield is used, which also usually implies interruptible
263 void host_sched_yield(struct guest_info * core, int usec) {
271 uint64_t yield_start_cycle;
272 yield_start_cycle = (uint64_t) core->sched_priv_data
273 + (uint64_t)core->vm_info->sched_priv_data;
274 core->sched_priv_data = (void *)yield_start_cycle;
276 core->sched_priv_data = (void*)v3_get_host_time(&(core->time_state));
282 int host_sched_admit(struct v3_vm_info *vm){
286 int v3_scheduler_free_vm(struct v3_vm_info *vm) {
287 if (scheduler->vm_deinit) {
288 return scheduler->vm_deinit(vm);
294 int v3_scheduler_free_core(struct guest_info *core) {
295 if (scheduler->core_deinit) {
296 return scheduler->core_deinit(core);
303 int v3_scheduler_stop_core(struct guest_info *core){
304 if (scheduler->core_stop) {
305 return scheduler->core_stop(core);
311 static struct vm_scheduler_impl host_sched_impl = {
315 .vm_init = host_sched_vm_init,
317 .core_init = host_sched_core_init,
318 .core_stop = host_sched_core_stop,
320 .schedule = host_sched_schedule,
321 .yield = host_sched_yield,
322 .admit = host_sched_admit,
327 static int create_host_scheduler()
329 v3_register_scheduler(&host_sched_impl);
333 static int destroy_host_scheduler()
335 v3_unregister_scheduler(host_sched_impl.name);
336 // no deletion of this since it's a pointer to a global var