Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Use passed in options to set scheduler
[palacios.git] / palacios / src / extensions / ext_sched_edf.c
1 /* 
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.  
5  *
6  * The V3VEE Project is a joint project between Northwestern University
7  * and the University of New Mexico.  You can find out more at 
8  * http://www.v3vee.org
9  *
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.
14  *
15  * Author: Oscar Mondragon <omondrag@cs.unm.edu>
16  *         Patrick G. Bridges <bridges@cs.unm.edu>
17  *
18  * This is free software.  You are permitted to use,
19  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20  */
21
22
23 #include <palacios/vmm.h>
24 #include <palacios/vmm_time.h>
25 #include <palacios/vm_guest.h>
26 #include <palacios/vmm_hashtable.h>
27 #include <palacios/vmm_config.h>
28 #include <palacios/vmm_extensions.h>
29 #include <palacios/vmm_rbtree.h>
30
31
32 #ifndef V3_CONFIG_DEBUG_EXT_EDF_SCHED
33 #undef PrintDebug
34 #define PrintDebug(fmt, args...)
35 #endif
36
37 /* Overview 
38  *
39  * EDF Scheduling
40  *
41  * The EDF scheduler uses a dynamic calculated priority as scheduling criteria to choose
42  * what thread will be scheduled.That priority is calculated according with the relative 
43  * deadline of the threads that are ready to run in the runqueue. This runqueue is a per-logical
44  * core data structure used to keep the runnable virtual cores (threads) allocated to that 
45  * logical core.The threads with less time before its deadline will receive better priorities. 
46  * The runqueue is sorted each time that a vCPU becomes runnable. At that time the vCPU is 
47  * enqueue and a new scheduling decision is taken. Each time a vCPU is scheduled, the parameter
48  * slice used time is set to zero and the current deadline is calculated using its period. Once
49  * the vCPU uses the logical core for slice seconds, that vCPU sleeps until its next scheduling 
50  * period (when is re-inserted in the runqueue) and  yields the CPU to allow the scheduling 
51  * of the vCPU with best priority in the runqueue. 
52  */
53
54 // Default configuration values for the EDF Scheduler
55 // time parameters in microseconds 
56
57 #define MAX_PERIOD 1000000000
58 #define MIN_PERIOD 50000
59 #define MAX_SLICE 1000000000
60 #define MIN_SLICE 10000
61 #define CPU_PERCENT 100
62 typedef uint64_t time_us;
63
64 /* 
65  * Per-core EDF Scheduling information 
66  */
67
68 struct vm_core_edf_sched {
69     struct guest_info *info;   // Core struct
70     struct rb_node node;      // red-black tree node
71     time_us period;           // Amount of time (us) during which the core may received a CPU allocation
72     time_us slice;            // Minimum amount of time (us) received for the core during each period 
73     time_us current_deadline; // Time (us) at which current core period ends
74     time_us used_time;        // Amount of time (us) of the slice used whiting the current period
75     time_us last_wakeup_time; // Time at which the last wakeup started for this core   
76     time_us remaining_time;   // Remaining time (us) before current core period ends (before current deadline) 
77     bool extra_time;          // Specifies if the virtual core is eligible to receive extra CPU time
78     int miss_deadline;        // Number of times the core has missed its deadline
79     time_us total_time;       // Total scheduled time for this core. For now used for debugging purposes 
80     int slice_overuse;        // Statistical purposes
81     time_us extra_time_given;     // Statistical
82 };
83
84 /* 
85  * Scheduler configuration
86  */
87
88 struct vm_edf_sched_config {
89     time_us min_slice;       // Minimum allowed slice
90     time_us max_slice;       // Maximum allowed slice
91     time_us min_period;      // Minimum allowed period
92     time_us max_period;      // Maximum allowed period
93     int cpu_percent;       // Percentange of CPU utilization for the scheduler in each physical CPU (100 or less)
94    
95 };
96
97 /* 
98  * Run queue structure. Per-logical core data structure  used to keep the runnable virtual cores (threads) allocated to that logical core 
99  * Contains a pointer to the red black tree, the structure of configuration options and other info
100  */
101
102 struct vm_edf_rq{
103     
104     //int cpu_id; // Physical CPU id
105     int cpu_u;  // CPU utilization (must be less or equal to the cpu_percent in vm_edf_sched_config)     
106     struct rb_root vCPUs_tree;  // Red-Black Tree
107     struct vm_edf_sched_config edf_config;      // Scheduling configuration structure
108     int nr_vCPU;        // Number of cores in the runqueue
109     struct vm_core_edf_sched *curr_vCPU;        // Current running CPU          
110     struct rb_node *rb_leftmost;     // vCPU with the earliest deadline (leftmost in the tree)
111     time_us last_sched_time;  // statistical purposes
112 };
113
114 /* 
115  * Basic functions for scheduling 
116  */
117
118 int v3_init_edf_scheduling();
119
120
121
122
123 /*
124  * init_edf_config: Initialize scheduler configuration
125  */
126
127 static void 
128 init_edf_config(struct vm_edf_sched_config *edf_config){
129
130     edf_config->min_slice = MIN_SLICE;
131     edf_config->max_slice = MAX_SLICE;
132     edf_config->min_period = MIN_PERIOD;
133     edf_config->max_period = MAX_PERIOD;
134     edf_config->cpu_percent = CPU_PERCENT;
135 }
136
137
138 /*
139  * priv_data_init: Initialize the run queue
140  */
141
142 int 
143 priv_data_init(struct v3_vm_info *vm){
144
145     PrintDebug(vm, VCORE_NONE,"EDF Sched. Initializing EDF Scheduling \n");
146
147     vm->sched_priv_data = V3_Malloc( vm->avail_cores * sizeof(struct vm_edf_rq));
148
149     if (!vm->sched_priv_data) {
150         PrintError(vm, VCORE_NONE,"Cannot allocate in priv_data in priv_data_init\n");
151         return -1;
152     }
153
154     int lcore = 0;
155   
156     PrintDebug(vm, VCORE_NONE,"EDF Sched. priv_data_init. Available cores %d\n", vm->avail_cores);
157
158     for(lcore = 0; lcore < vm->avail_cores ; lcore++){
159
160         PrintDebug(vm, VCORE_NONE,"EDF Sched. priv_data_init. Initializing logical core %d\n", lcore);
161
162         struct vm_edf_rq * edf_rq_list =   (struct vm_edf_rq *)vm->sched_priv_data;
163         struct vm_edf_rq * edf_rq = &edf_rq_list[lcore];
164     
165         edf_rq->vCPUs_tree = RB_ROOT;
166         edf_rq->cpu_u=0;
167         edf_rq->nr_vCPU=0;
168         edf_rq->curr_vCPU=NULL;
169         edf_rq->rb_leftmost=NULL;
170         edf_rq->last_sched_time=0;
171         init_edf_config(&edf_rq->edf_config);
172
173     }
174  
175    return 0;
176    
177 }
178
179 /*
180  * is_admissible_core: Decides if a core is admited to the red black tree according with 
181  * the admisibility formula.
182  */
183
184 static bool 
185 is_admissible_core(struct vm_core_edf_sched * new_sched_core, struct vm_edf_rq *runqueue){
186
187     int curr_utilization = runqueue->cpu_u;
188     int new_utilization = curr_utilization + (100 * new_sched_core->slice / new_sched_core->period);
189     int cpu_percent = (runqueue->edf_config).cpu_percent; 
190
191     if (new_utilization <= cpu_percent)
192         return true;
193     else
194         return false;    
195
196 return true;
197 }
198
199
200 /*
201  * count_cores: Function useful to count the number of cores in a runqueue (Not used for now)
202  *
203  */
204
205
206 /*static int count_cores(struct vm_edf_rq *runqueue){
207
208   struct rb_node *node = v3_rb_first(&runqueue->vCPUs_tree);
209   struct vm_core_edf_sched *curr_core;
210   int number_cores = 0;    
211
212     while(node){
213         
214         curr_core = container_of(node, struct vm_core_edf_sched, node);
215         node = v3_rb_next(node);
216         number_cores++;
217     }
218
219    return number_cores;
220 }*/ 
221
222
223
224 /*
225  * insert_core_edf: Finds a place in the tree for a newly activated core, adds the node 
226  * and rebalaces the tree
227  */
228
229 static bool 
230 insert_core_edf(struct vm_core_edf_sched *core, struct vm_edf_rq *runqueue){
231
232     struct rb_node **new_core = &(runqueue->vCPUs_tree.rb_node);
233     struct rb_node *parent = NULL;
234     struct vm_core_edf_sched *curr_core;
235
236     // Find out place in the tree for the new core 
237     while (*new_core) {
238     
239         curr_core = container_of(*new_core, struct vm_core_edf_sched, node);
240         parent = *new_core;
241         
242         if (core->current_deadline < curr_core->current_deadline)
243             new_core = &((*new_core)->rb_left);
244         else if (core->current_deadline > curr_core->current_deadline)
245             new_core = &((*new_core)->rb_right);
246         else // Is Possible to have same current deadlines in both cores!
247             return false;
248     }
249     // Add new node and rebalance tree. 
250     rb_link_node(&core->node, parent, new_core);
251     v3_rb_insert_color(&core->node, &runqueue->vCPUs_tree);
252     
253     return true;
254  } 
255
256
257 /*
258  * get_curr_host_time: Calculates the current host time (microseconds)
259  */
260
261 static uint64_t 
262 get_curr_host_time(struct vm_core_time *core_time){
263
264     uint64_t cur_cycle = v3_get_host_time(core_time);
265     uint64_t cpu_khz = core_time->host_cpu_freq;
266     uint64_t curr_time_us = 1000 * cur_cycle / cpu_khz;
267
268     return curr_time_us;
269
270 }
271
272
273 /*
274  * next_start_period: Given the current host time and the period of a given vCPU, 
275  * calculates the time in which its next period starts.
276  *
277  */
278
279 static uint64_t 
280 next_start_period(uint64_t curr_time_us, uint64_t period_us){
281
282     uint64_t time_period_us = curr_time_us % period_us;
283     uint64_t remaining_time_us = period_us - time_period_us;
284     uint64_t next_start_us = curr_time_us + remaining_time_us;
285
286     return next_start_us;
287
288 }
289
290 /*
291  * get_runqueue: Get the runqueue assigned to a virtual core.
292  */
293
294 struct vm_edf_rq * get_runqueue(struct guest_info *info){
295
296     struct vm_edf_rq *runqueue_list = (struct vm_edf_rq *) info->vm_info->sched_priv_data;
297     struct vm_edf_rq *runqueue = &runqueue_list[info->pcpu_id]; 
298     return runqueue;
299 }
300
301
302 /*
303  * wakeup_core: Wakeup a given vCPU thread
304  */
305
306 static void 
307 wakeup_core(struct guest_info *info){
308
309     struct vm_core_edf_sched *core = info->sched_priv_data;
310     struct vm_edf_rq *runqueue = get_runqueue(info);
311
312     if (!info->core_thread) {
313               PrintError(info->vm_info, info,"ERROR: Tried to wakeup non-existent core thread vCPU_id %d \n",info->vcpu_id);
314     } 
315     else {
316
317         PrintDebug(info->vm_info, info,"EDF Sched. run_next_core. vcpu_id %d, logical id %d, Total time %llu, Miss_deadlines %d, slice_overuses %d extra_time %llu, thread (%p)\n", 
318             core->info->vcpu_id,
319             core->info->pcpu_id,
320             core->total_time,
321             core->miss_deadline,
322             core->slice_overuse,
323             core->extra_time_given,
324             (struct task_struct *)info->core_thread); 
325        
326        V3_Wakeup(info->core_thread);
327        core->last_wakeup_time = get_curr_host_time(&core->info->time_state);
328        runqueue->curr_vCPU = core;
329
330     }
331
332 }
333
334
335 /*
336  * activate_core - Moves a core to the red-black tree.
337  * used time is set to zero and current deadline is calculated 
338  */
339
340 static void 
341 activate_core(struct vm_core_edf_sched * core, struct vm_edf_rq *runqueue){
342     
343     if (is_admissible_core(core, runqueue)){
344              
345         uint64_t curr_time_us = get_curr_host_time(&core->info->time_state);
346         uint64_t curr_deadline = next_start_period(curr_time_us, core->period);
347         
348         core->current_deadline = curr_deadline;
349         core->used_time=0; 
350         core->remaining_time=core->slice; 
351         
352         bool ins = insert_core_edf(core, runqueue);
353         /* 
354          * If not inserted is possible that there is other core with the same deadline.
355          * Then, the deadline is modified and try again 
356          */   
357         while(!ins){  
358             core->current_deadline ++;
359             ins = insert_core_edf(core, runqueue);  
360         }    
361      
362         runqueue->cpu_u += 100 * core->slice / core->period;
363         runqueue->nr_vCPU ++;
364         
365         /*
366          * If this is the first time to be activated pick first earliest deadline core to wakeup.
367          */
368
369         if(core->last_wakeup_time == 0){
370
371             struct vm_core_edf_sched *next_core;
372         
373             /*
374              * Pick first earliest deadline core
375              */
376             struct rb_node *node = v3_rb_first(&runqueue->vCPUs_tree);
377             next_core = container_of(node, struct vm_core_edf_sched, node);
378           
379             // Wakeup next_core
380             wakeup_core(next_core->info);
381        
382             //Sleep old core
383   
384             V3_Sleep(0);
385         }
386         
387       }
388       else 
389           PrintError(core->info->vm_info, core->info,"EDF Sched. activate_core. CPU cannot activate the core. It is not admissible");   
390 }
391
392
393 /*
394  * edf_sched_core_init: Initializes per core data structure and 
395  * calls activate function.
396  */
397
398 int 
399 edf_sched_core_init(struct guest_info * info){
400
401     struct vm_edf_rq *runqueue = get_runqueue(info);
402     struct vm_core_edf_sched *core_edf;
403
404     PrintDebug(info->vm_info, info,"EDF Sched. Initializing vcore %d\n", info->vcpu_id);
405
406     core_edf = (struct vm_core_edf_sched *) V3_Malloc(sizeof (struct vm_core_edf_sched));
407     if (!core_edf) {
408         PrintError(info->vm_info, info,"Cannot allocate private_data in edf_sched_core_init\n");
409         return -1;
410     }
411     info->sched_priv_data = core_edf;
412     
413     // Default configuration if not specified in configuration file  
414   
415     core_edf->info = info; 
416     core_edf->period = 500000;
417     core_edf->slice = 50000;
418     core_edf->used_time = 0;
419     core_edf->last_wakeup_time = 0;
420     core_edf->remaining_time = core_edf->slice;  
421     core_edf->miss_deadline = 0;
422     core_edf->extra_time = true;
423     core_edf->total_time = 0;
424     core_edf->slice_overuse = 0;
425     core_edf->extra_time_given = 0;
426
427     v3_cfg_tree_t * cfg_tree = core_edf->info->vm_info->cfg_data->cfg;
428     v3_cfg_tree_t * core = v3_cfg_subtree(v3_cfg_subtree(cfg_tree, "cores"), "core");
429     
430     while (core){
431         char *id = v3_cfg_val(core, "vcpu_id");
432         char *period = v3_cfg_val(core, "period");
433         char *slice = v3_cfg_val(core, "slice");
434         char *extra_time = v3_cfg_val(core, "extra_time");
435         
436         if (atoi(id) == core_edf->info->vcpu_id){
437    
438             core_edf->period = atoi(period);
439             core_edf->slice = atoi(slice);
440             core_edf->remaining_time = core_edf->slice;  
441             if (strcasecmp(extra_time, "true") == 0)
442                 core_edf->extra_time = true;
443             else    
444                 core_edf->extra_time = false;
445             break;
446         }
447         core = v3_cfg_next_branch(core);
448     }
449
450     activate_core(core_edf,runqueue); 
451     return 0; 
452 }
453
454 /*
455  * search_core_edf: Searches a core in the red-black tree by using its vcpu_id
456  */
457 static struct vm_core_edf_sched * 
458 search_core_edf(struct vm_core_edf_sched *core_edf, struct vm_edf_rq *runqueue){
459
460     struct rb_node *node = runqueue->vCPUs_tree.rb_node;
461         
462     while (node) {
463      
464         struct vm_core_edf_sched *core = container_of(node, struct vm_core_edf_sched, node);
465         
466         if (core_edf->current_deadline < core->current_deadline)
467             node = node->rb_left;
468         else if (core_edf->current_deadline > core->current_deadline)
469             node = node->rb_right;
470         else
471             if(core->info->vcpu_id == core_edf->info->vcpu_id){
472                 return core;
473             }
474     }
475     return NULL;
476 }
477
478
479 /* 
480  * delete_core_edf: Deletes a core from the red black tree, generally when it has 
481  * consumed its time slice within the current period.
482  */
483
484 static bool 
485 delete_core_edf( struct vm_core_edf_sched *core_edf  , struct vm_edf_rq *runqueue){
486
487     struct vm_core_edf_sched *core = search_core_edf(core_edf, runqueue);
488         if (core){ 
489
490             v3_rb_erase(&core->node, &runqueue->vCPUs_tree);  
491             return true;
492         } 
493         else{
494             PrintError(core->info->vm_info, core->info,"EDF Sched. delete_core_edf.Attempted to erase unexisting core");
495             return false;         
496         }
497 }
498
499
500 /*
501  * deactivate_core - Removes a core from the red-black tree.
502  */
503
504 static void 
505 deactivate_core(struct vm_core_edf_sched * core, struct vm_edf_rq *runqueue){
506
507      if(delete_core_edf(core, runqueue)){
508          runqueue->cpu_u -= 100 * core->slice / core->period;
509          runqueue->nr_vCPU -- ;
510      }          
511 }
512
513
514 /*
515  * pick_next_core: Returns the next core to be scheduled from the red black tree
516  */
517
518 static struct vm_core_edf_sched * 
519 pick_next_core(struct vm_edf_rq *runqueue){
520   
521   
522     /*
523      * Pick first earliest deadline core
524      */
525     struct rb_node *node = v3_rb_first(&runqueue->vCPUs_tree);
526     struct vm_core_edf_sched *next_core = container_of(node, struct vm_core_edf_sched, node);
527  
528     /* 
529      * Verify if the earliest deadline core has used its complete slice and return it if not
530      */
531
532     if (next_core->used_time < next_core->slice){
533         if(next_core->current_deadline < get_curr_host_time(&next_core->info->time_state))
534             next_core->miss_deadline++; 
535         return next_core;
536     }
537     /*
538      * If slice used, pick the next core that has not used its complete slice    
539      */
540
541     else {  
542         while(next_core->used_time >= next_core->slice){
543             
544             if(next_core->current_deadline < get_curr_host_time(&next_core->info->time_state) || !next_core->extra_time ){
545
546                 deactivate_core(next_core,runqueue); 
547                 activate_core(next_core,runqueue);
548            
549             }            
550
551             node = v3_rb_next(node);
552             if(node){
553                 next_core = container_of(node, struct vm_core_edf_sched, node);
554             }
555             else{   
556                 node = v3_rb_first(&runqueue->vCPUs_tree); // If all cores have used its slice return the first one
557             return container_of(node, struct vm_core_edf_sched, node);
558             }   
559
560         }
561     }
562
563     return next_core;
564 }
565
566
567 static void 
568 adjust_slice(struct guest_info * info, int used_time, int extra_time)
569 {
570     struct vm_core_edf_sched *core = info->sched_priv_data;
571     struct vm_edf_rq *runqueue = get_runqueue(info);
572
573     core->used_time = used_time;
574  
575     if (extra_time >= 0) {
576         core->used_time += extra_time;
577     }
578
579     if( core->used_time >= core->slice){     
580         deactivate_core(core,runqueue);
581         activate_core(core,runqueue);
582     }
583 }
584
585
586 /*
587  * run_next_core: Pick next core to be scheduled and wakeup it
588  */
589
590 static void 
591 run_next_core(struct guest_info *info, int used_time, int usec)
592 {
593     struct vm_core_edf_sched *core = info->sched_priv_data;
594     struct vm_core_edf_sched *next_core;
595     struct vm_edf_rq *runqueue = get_runqueue(info);
596    
597     /* The next core to be scheduled is choosen from the tree (Function pick_next_core). 
598      * The selected core is the one with the earliest deadline and with available time 
599      * to use within the current period (used_time < slice)   
600      */
601    
602      next_core = pick_next_core(runqueue); // Pick next core to schedule
603           
604      if (core != next_core){
605
606          // Wakeup next_core
607          wakeup_core(next_core->info);
608          core->total_time += used_time;
609
610         if (used_time > core->slice){
611             core->slice_overuse++;
612             core->extra_time_given += (used_time - core->slice);
613         }
614
615          // Sleep old core
616   
617          V3_Sleep(usec);
618        
619        }
620 }
621
622
623 /*
624  * edf_schedule: Scheduling function
625  */
626
627 static void
628 edf_schedule(struct guest_info * info, int usec){
629
630     uint64_t host_time = get_curr_host_time(&info->time_state);
631     struct vm_edf_rq *runqueue = get_runqueue(info);  
632     struct vm_core_edf_sched *core = (struct vm_core_edf_sched *) info->sched_priv_data;
633
634     uint64_t used_time = 0;
635     if(core->last_wakeup_time != 0) 
636         used_time =  host_time - core->last_wakeup_time;
637
638     if(usec == 0) runqueue->last_sched_time = host_time; // Called from edf_sched_scheduled
639     adjust_slice(core->info, host_time - core->last_wakeup_time, usec);
640
641     run_next_core(core->info,used_time, usec);
642     return;
643
644 }
645
646 /*
647  * edf_sched_schedule: Main scheduling function. Computes amount of time in period left,
648  * recomputing the current core's deadline if it has expired, then runs
649  * scheduler 
650  * It is called in the following cases:
651  *    A vCPU becomes runnable
652  *    The slice of the current vCPU was used
653  *    The period of a vCPU in the runqueue starts
654  *    Other case?? 
655  * TODO Something to do with extra time?
656  * TODO Check the use of remaining_time
657  */
658
659 void 
660 edf_sched_schedule(struct guest_info * info){
661
662     edf_schedule(info, 0);
663     return;
664 }
665
666 /*
667  * edf_sched_yield: Called when yielding the logical cpu for usec is needed
668  */
669
670 void 
671 edf_sched_yield(struct guest_info * info, int usec){
672  
673     edf_schedule(info, usec);
674     return;
675     
676 }
677
678 /*
679  * edf_sched_deinit: Frees edf scheduler data structures
680  */
681
682
683 int 
684 edf_sched_deinit(struct v3_vm_info *vm)
685 {
686     void *priv_data = vm->sched_priv_data;
687     
688     if (priv_data) 
689         V3_Free(priv_data);
690
691     return 0;
692
693 }
694
695 /*
696  * edf_sched_deinit: Frees virtual core data structures
697  */
698
699 int 
700 edf_sched_core_deinit(struct guest_info *core)
701 {
702     void *priv_data = core->sched_priv_data;
703     
704     if (priv_data) 
705         V3_Free(priv_data);
706
707     return 0;
708 }
709
710 int edf_sched_vm_init(struct v3_vm_info *vm){
711     return 0;
712 }
713
714 int edf_sched_admit(struct v3_vm_info *vm){
715
716     /*
717      * Initialize priv_data for the vm: 
718      * For EDF this is done here because we need the parameter
719      * avail_core which is set in v3_start_vm before the
720      * v3_scheduler_admit_vm function is called.
721      */
722    
723     priv_data_init(vm);
724
725     // TODO Admission
726      
727     return 0;
728 }
729
730
731 static struct vm_scheduler_impl edf_sched = {
732
733     .name = "edf",
734     .init = NULL,
735     .deinit = NULL,
736     .vm_init = edf_sched_vm_init,
737     .vm_deinit = NULL,
738     .core_init = edf_sched_core_init,
739     .core_deinit = edf_sched_core_deinit,
740     .schedule = edf_sched_schedule,
741     .yield = edf_sched_yield,
742     .admit = edf_sched_admit,
743     .remap = NULL,
744     .dvfs=NULL
745 };
746
747 static int 
748 ext_sched_edf_init() {
749     PrintDebug(VM_NONE, VCORE_NONE,"Sched. Creating (%s) scheduler\n",edf_sched.name);
750     return v3_register_scheduler(&edf_sched);
751 }
752
753 static int 
754 ext_sched_edf_vm_init() {
755     return 0;
756 }
757
758 static struct v3_extension_impl sched_edf_impl = {
759         .name = "EDF Scheduler",
760         .init = ext_sched_edf_init,
761         .vm_init = ext_sched_edf_vm_init,
762         .vm_deinit = NULL,
763         .core_init = NULL,
764         .core_deinit = NULL,
765         .on_entry = NULL,
766         .on_exit = NULL
767 };
768
769 register_extension(&sched_edf_impl);