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.


Add HVM configuration capability, init/deinit, etc
[palacios.git] / palacios / src / palacios / vmm_swapping.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) 2014, The V3VEE Project <http://www.v3vee.org> 
11  * All rights reserved.
12  *
13  * Author: Daniel Zuo <pengzuo2014@u.northwestern.edu>
14  *         Nikhat Karimi <nikhatkarimi@gmail.com>
15  *         Ahalya Srinivasan <AhalyaSrinivasan2015@u.northwestern.edu>
16  *         Peter Dinda <pdinda@northwestern.edu> (pinning, cleanup, integration, locking etc)
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 #include <palacios/vmm_mem.h>
23 #include <palacios/vmm.h>
24 #include <palacios/vmm_util.h>
25 #include <palacios/vmm_emulator.h>
26 #include <palacios/vm_guest.h>
27 #include <palacios/vmm_debug.h>
28
29 #include <palacios/vmm_shadow_paging.h>
30 #include <palacios/vmm_direct_paging.h>
31
32 #include <palacios/vmm_xml.h>
33
34 #include <stdio.h>
35 #include <stdlib.h>
36
37 /*
38
39   <mem ... >N_MB</mem>             Size of memory in the GPA
40
41   <swapping enable="y">
42      <allocated>M_MB</allocated>   Allocated space (M_MB <= N_MB)
43      <file>FILENAME</file>         Where to swap to
44      <strategy>STRATEGY</strategy> Victim picker to use NEXT_FIT, RANDOM (default), LRU, DEFAULT 
45   </swapping>
46
47 */
48
49
50 #ifndef V3_CONFIG_DEBUG_SWAPPING
51 #undef PrintDebug
52 #define PrintDebug(fmt, args...)
53 #endif
54
55 int v3_init_swapping()
56 {
57     PrintDebug(VM_NONE,VCORE_NONE, "swapper: init\n");
58     return 0;
59
60 }
61
62 int v3_deinit_swapping()
63 {
64     PrintDebug(VM_NONE,VCORE_NONE, "swapper: deinit\n");
65     return 0;
66 }
67
68
69 static int write_all(v3_file_t fd, void *buf, uint64_t len, uint64_t offset)
70 {
71     sint64_t thisop;
72
73     while (len>0) { 
74         thisop = v3_file_write(fd, buf, len, offset);
75         if (thisop <= 0) { 
76             return -1;
77         }
78         buf+=thisop;
79         offset+=thisop;
80         len-=thisop;
81     }
82     return 0; 
83 }
84
85 static int read_all(v3_file_t fd, void *buf, uint64_t len, uint64_t offset)
86 {
87     sint64_t thisop;
88
89     while (len>0) { 
90         thisop = v3_file_read(fd, buf, len, offset);
91         if (thisop <= 0) { 
92             return -1;
93         }
94         buf+=thisop;
95         offset+=thisop;
96         len-=thisop;
97     }
98     return 0; 
99 }
100
101
102 #define REGION_WARN_THRESH 16
103
104 #define CEIL_DIV(x,y) (((x)/(y)) + !!((x)%(y)))
105
106 int v3_init_swapping_vm(struct v3_vm_info *vm, struct v3_xml *config)
107 {
108     v3_cfg_tree_t *swap_config;
109     char *enable;
110     char *allocated;
111     char *strategy;
112     char *file;
113     uint64_t alloc;
114     extern uint64_t v3_mem_block_size;
115
116
117     PrintDebug(vm, VCORE_NONE, "swapper: vm init\n");
118
119     memset(&vm->swap_state,0,sizeof(struct v3_swap_impl_state));
120
121     v3_lock_init(&(vm->swap_state.lock));
122
123     vm->swap_state.enable_swapping=0;
124     vm->swap_state.host_mem_size=vm->mem_size;
125     
126     if (!config || !(swap_config=v3_cfg_subtree(config,"swapping"))) {
127         PrintDebug(vm,VCORE_NONE,"swapper: no swapping configuration found\n");
128         return 0;
129     }
130     
131     if (!(enable=v3_cfg_val(swap_config,"enable")) || strcasecmp(enable,"y")) {
132         PrintDebug(vm,VCORE_NONE,"swapper: swapping configuration disabled\n");
133         return 0;
134     }
135
136     allocated = v3_cfg_val(swap_config,"allocated");
137     if (!allocated) { 
138         PrintError(vm,VCORE_NONE,"swapper: swapping configuration must included allocated block\n");
139         return -1;
140     }
141     alloc = ((uint64_t)atoi(allocated))*1024*1024;
142
143     // make alloc an integer multiple of the memory block size
144     alloc = CEIL_DIV(alloc, v3_mem_block_size) * v3_mem_block_size;
145
146     PrintDebug(vm,VCORE_NONE,"swapper: adjusted allocated size is %llu\n",alloc);
147
148     if (alloc > vm->mem_size) { 
149         PrintError(vm,VCORE_NONE,"swapper: cannot allocate more than the VM's memory size....\n");
150         return -1;
151     }
152
153     
154     file = v3_cfg_val(swap_config,"file");
155     if (!file) { 
156         PrintError(vm,VCORE_NONE,"swapper: swapping configuration must included swap file name\n");
157         return -1;
158     }
159     
160     strategy = v3_cfg_val(swap_config,"strategy");
161     if (!strategy) { 
162         PrintDebug(vm,VCORE_NONE,"swapper: default strategy selected\n");
163         strategy="default";
164     }
165
166     // Can we allocate the file?
167
168     if ((vm->swap_state.swapfd = v3_file_open(vm,file, FILE_OPEN_MODE_READ | FILE_OPEN_MODE_WRITE | FILE_OPEN_MODE_CREATE))<0) {
169         PrintError(vm,VCORE_NONE,"swapper: cannot open or create swap file\n");
170         return -1;
171     } else {
172         // Make sure we can write the whole thing
173         uint64_t addr;
174         char *buf = V3_Malloc(PAGE_SIZE_4KB);
175         if (!buf) { 
176             PrintError(vm,VCORE_NONE,"swapper: unable to allocate space for writing file\n");
177             return -1;
178         }
179         memset(buf,0,PAGE_SIZE_4KB);
180         for (addr=0;addr<vm->mem_size;addr+=PAGE_SIZE_4KB) { 
181             if (write_all(vm->swap_state.swapfd, 
182                           buf,
183                           PAGE_SIZE_4KB,
184                           addr)) { 
185                 PrintError(vm,VCORE_NONE,"swapper: unable to write initial swap file\n");
186                 V3_Free(buf);
187                 v3_file_close(vm->swap_state.swapfd);
188                 return -1;
189             }
190         }
191         V3_Free(buf);
192     }
193
194     // We are now set - we have space to swap to
195     vm->swap_state.enable_swapping=1;
196
197     vm->swap_state.strategy = 
198         !strcasecmp(strategy,"next_fit") ? V3_SWAP_NEXT_FIT :
199         !strcasecmp(strategy,"random") ? V3_SWAP_RANDOM :
200         !strcasecmp(strategy,"lru") ? V3_SWAP_LRU :
201         !strcasecmp(strategy,"default") ? V3_SWAP_RANDOM :
202         V3_SWAP_RANDOM;
203
204     vm->swap_state.host_mem_size=alloc;
205     vm->swap_state.swap_count=0;
206     vm->swap_state.last_region_used=0;
207     // already have set swapfd
208
209
210     V3_Print(vm,VCORE_NONE,"swapper: swapping enabled (%llu allocated of %llu using %s on %s)\n",
211              (uint64_t)vm->swap_state.host_mem_size, (uint64_t) vm->mem_size, strategy, file);
212
213     if (vm->swap_state.host_mem_size / v3_mem_block_size < REGION_WARN_THRESH) { 
214         V3_Print(vm,VCORE_NONE,"swapper: WARNING: %llu regions is less than threshold of %llu, GUEST MAY FAIL TO MAKE PROGRESS\n",
215                  (uint64_t)vm->swap_state.host_mem_size/v3_mem_block_size, (uint64_t)REGION_WARN_THRESH);
216     }
217
218     return 0;
219     
220 }
221
222 int v3_deinit_swapping_vm(struct v3_vm_info *vm)
223 {
224     PrintDebug(vm, VCORE_NONE, "swapper: vm deinit\n");
225
226     if (vm->swap_state.enable_swapping) {
227         v3_file_close(vm->swap_state.swapfd);
228     }
229
230     v3_lock_deinit(&(vm->swap_state.lock));
231
232     return 0;
233 }
234
235
236 int v3_pin_region(struct v3_vm_info *vm, struct v3_mem_region *region)
237 {
238     unsigned int flags;
239
240     PrintDebug(vm,VCORE_NONE, "Pin Region GPA=%p to %p\n",(void*) region->guest_start, (void*)region->guest_end);
241
242     if (!(region->flags.base)) { 
243         PrintError(vm,VCORE_NONE,"Attempt to pin non-base region\n");
244         return -1;
245     }
246     
247     if (region->flags.pinned) { 
248         return 0;
249     }
250            
251     flags = v3_lock_irqsave(vm->swap_state.lock);
252     
253     if (region->flags.swapped) {
254         // can't pin since it's swapped out, swap it in an try again
255         v3_unlock_irqrestore(vm->swap_state.lock, flags);
256         if (v3_swap_in_region(vm,region)) { 
257             PrintError(vm,VCORE_NONE,"Cannot swap in during a pin operation\n");
258             return -1;
259         } else {
260             return v3_pin_region(vm,region);
261         }
262     }
263     
264     // still holding lock if we got here, so we're the exclusive
265     // manipulator of the swap state
266     region->flags.pinned=1;
267     
268     v3_unlock_irqrestore(vm->swap_state.lock, flags);
269     
270     return 0;
271 }
272
273
274 int v3_unpin_region(struct v3_vm_info *vm, struct v3_mem_region *region)
275 {
276     unsigned int flags = v3_lock_irqsave(vm->swap_state.lock);
277     
278     region->flags.pinned=0;
279
280     v3_unlock_irqrestore(vm->swap_state.lock,flags);
281
282     return 0;
283
284 }
285
286
287 #define SEARCH_LIMIT 1024
288
289 // Must be called with the lock held
290 static struct v3_mem_region * choose_random_victim(struct v3_vm_info * vm) 
291 {
292     
293     struct v3_mem_map * map = &(vm->mem_map);
294     uint64_t num_base_regions = map->num_base_regions;
295     uint64_t thetime;
296     struct v3_mem_region *reg=0;
297     uint32_t i=0;
298         
299     PrintDebug(vm, VCORE_NONE, "swapper: choosing random victim\n");
300
301     for (i=0, reg=0 ; 
302          i<SEARCH_LIMIT && reg==0 ; 
303          i++) {
304
305         // cycle counter used as pseudorandom number generator
306         rdtscll(thetime);
307         
308         reg = &(map->base_regions[thetime % num_base_regions]);
309
310         if (reg->flags.swapped || reg->flags.pinned) { 
311             // region is already swapped or is pinned - try again
312             reg = 0;
313         } 
314     }
315
316     if (!reg) { 
317         PrintError(vm,VCORE_NONE,"swapper: Unable to find a random victim\n");
318     } else {
319         PrintDebug(vm,VCORE_NONE,"swapper: Random victim GPA=%p to %p\n", (void*)reg->guest_start, (void*)reg->guest_end);
320     }
321     
322     return reg;
323 }
324
325
326 // Must be called with the lock held
327 static struct v3_mem_region * choose_next_victim(struct v3_vm_info * vm) 
328 {
329     struct v3_mem_map * map = &(vm->mem_map);
330     uint64_t num_base_regions = map->num_base_regions;
331     struct v3_mem_region *reg=0;
332     uint32_t i=0;
333         
334     PrintDebug(vm, VCORE_NONE, "swapper: choosing next victim\n");
335
336     // forward to end
337     for (i=vm->swap_state.last_region_used+1, reg=0; 
338          i<num_base_regions && reg==0; 
339          i++) {
340
341         reg = &(map->base_regions[i]);
342
343         if (reg->flags.swapped || reg->flags.pinned) { 
344             // region is already swapped or is pinned - try again
345             reg = 0;
346         } 
347     }
348
349     for (i=0; 
350          i < vm->swap_state.last_region_used+1 && reg==0;
351          i++) { 
352         
353         reg = &(map->base_regions[i]);
354
355         if (reg->flags.swapped || reg->flags.pinned) { 
356             // region is already swapped or is pinned - try again
357             reg = 0;
358         } 
359     }
360
361     if (!reg) { 
362         PrintError(vm,VCORE_NONE,"swapper: Unable to find the next victim\n");
363     } else {
364         PrintDebug(vm,VCORE_NONE,"swapper: Next victim GPA=%p to %p\n", (void*)reg->guest_start, (void*)reg->guest_end);
365     }
366     
367     return reg;
368 }
369
370 // Must be called with the lock held
371 static struct v3_mem_region * choose_lru_victim(struct v3_vm_info * vm) 
372 {
373     struct v3_mem_map * map = &(vm->mem_map);
374     uint64_t num_base_regions = map->num_base_regions;
375     struct v3_mem_region *reg=0;
376     struct v3_mem_region *oldest_reg=0;
377     uint32_t i=0;
378     uint64_t oldest_time;
379         
380     PrintDebug(vm, VCORE_NONE, "swapper: choosing pseudo-lru victim\n");
381
382
383     for (i=0, oldest_time=0, oldest_reg=0;
384          i<num_base_regions; 
385          i++) {
386
387         reg = &(map->base_regions[i]);
388
389         if (reg->flags.swapped || reg->flags.pinned) {
390             if (!oldest_reg ||
391                 reg->swap_state.last_accessed < oldest_time) { 
392
393                 oldest_time = reg->swap_state.last_accessed;
394                 oldest_reg = reg;
395             }
396         }
397     }
398
399     if (!oldest_reg) { 
400         PrintError(vm,VCORE_NONE,"swapper: Unable to find pseudo-lru victim\n");
401     } else {
402         PrintDebug(vm,VCORE_NONE,"swapper: Pseudo-lru victim GPA=%p to %p\n", (void*)oldest_reg->guest_start, (void*)oldest_reg->guest_end);
403     }
404     
405     return oldest_reg;
406 }
407
408
409 // Must be called with the lock held
410 static struct v3_mem_region * choose_victim(struct v3_vm_info * vm) 
411 {
412     switch (vm->swap_state.strategy) { 
413         case V3_SWAP_NEXT_FIT:
414             return choose_next_victim(vm);
415             break;
416         case V3_SWAP_RANDOM:
417             return choose_random_victim(vm);
418             break;
419         case V3_SWAP_LRU:
420             return choose_lru_victim(vm);
421             break;
422         default:
423             return choose_random_victim(vm);
424             break;
425     }
426 }
427
428
429 // swaps out region, and marks it as swapped and pinned
430 // no lock should be held
431 static int swap_out_region_internal(struct v3_vm_info *vm, struct v3_mem_region *victim, int ignore_pinning)
432 {
433     unsigned int flags;
434     int i; 
435     int fail=0;
436
437     flags = v3_lock_irqsave(vm->swap_state.lock);
438
439     if (victim->flags.swapped) { 
440         v3_unlock_irqrestore(vm->swap_state.lock,flags);
441         PrintDebug(vm,VCORE_NONE,"swapper: swap out already swapped out region\n");
442         return 0;
443     }
444     
445     if (!ignore_pinning && victim->flags.pinned) { 
446         v3_unlock_irqrestore(vm->swap_state.lock,flags);
447         PrintError(vm,VCORE_NONE,"swapper: attempt to swap out pinned region\n");
448         return -1;
449     }
450
451     // now mark it as pinned until we are done with it.
452     victim->flags.pinned=1;
453
454     // release lock - it's marked pinned so nothing else will touch it
455     v3_unlock_irqrestore(vm->swap_state.lock,flags);
456     
457     // do NOT do this without irqs on... 
458     if (write_all(vm->swap_state.swapfd, 
459                   (uint8_t *)V3_VAddr((void *)victim->host_addr), 
460                   victim->guest_end - victim->guest_start, 
461                   victim->guest_start)) {
462         PrintError(vm, VCORE_NONE, "swapper: failed to swap out victim"); //write victim to disk
463         // note write only here - it returns unswapped and unpinned
464         victim->flags.pinned=0;
465         return -1;
466     }
467
468     // Now invalidate it
469     
470     //Invalidate the victim on all cores
471     
472     for (i=0, fail=0; i<vm->num_cores;i++ ) {
473         struct guest_info * core = &(vm->cores[i]);
474         int rc;
475
476         if (core->shdw_pg_mode == SHADOW_PAGING) {
477             v3_mem_mode_t mem_mode = v3_get_vm_mem_mode(core);
478             if (mem_mode == PHYSICAL_MEM) {
479                 PrintDebug(vm, VCORE_NONE, "swapper: v3_invalidate_passthrough_addr_range() called for core %d",i);
480                 rc = v3_invalidate_passthrough_addr_range(core, victim->guest_start,  victim->guest_end-1,NULL,NULL ); 
481             } else {
482                 PrintDebug(vm, VCORE_NONE, "swapper: v3_invalidate_shadow_pts() called for core %d",i);
483                 rc = v3_invalidate_shadow_pts(core);
484             }
485         } else if (core->shdw_pg_mode == NESTED_PAGING) {
486             PrintDebug(vm, VCORE_NONE, "swapper: v3_invalidate_nested_addr_range() called for core %d",i);
487             rc = v3_invalidate_nested_addr_range(core,  victim->guest_start,  victim->guest_end-1,NULL,NULL );
488         }
489
490         if (rc) { 
491             PrintError(vm,VCORE_NONE,"swapper: paging invalidation failed for victim on core %d.... continuing, but this is not good.\n", i);
492             fail=1;
493         }
494     }
495
496     victim->flags.swapped=1;   // now it is in "swapped + pinned" state, meaning it has been written and is now holding for future use
497     
498     if (fail) { 
499         return -1;
500     } else {
501         return 0;
502     }
503 }
504
505
506 // swaps out region, and marks it as swapped
507 int v3_swap_out_region(struct v3_vm_info *vm, struct v3_mem_region *victim)
508 {
509     if (!victim->flags.base) { 
510         PrintError(vm, VCORE_NONE,"swapper: can only swap out base regions\n");
511         return -1;
512     }
513
514     if (victim->flags.pinned) { 
515         PrintError(vm, VCORE_NONE,"swapper: cannot swap out a pinned region\n");
516         return -1;
517     }
518     
519     if (swap_out_region_internal(vm,victim,0)) { 
520         PrintError(vm, VCORE_NONE,"swapper: failed to swap out victim....  bad\n");
521         return -1;
522     }
523
524     // victim now has its old info, and is marked swapped and pinned
525
526     victim->host_addr = 0;
527     victim->flags.pinned = 0;  
528
529     // now is simply swapped
530     
531     return 0;
532 }
533
534
535 int v3_swap_in_region(struct v3_vm_info *vm, struct v3_mem_region *perp)
536 {
537     unsigned int flags;
538     struct v3_mem_region *victim;
539
540     flags = v3_lock_irqsave(vm->swap_state.lock);
541
542     if (!perp->flags.base) { 
543         v3_unlock_irqrestore(vm->swap_state.lock,flags);
544         PrintError(vm,VCORE_NONE,"swapper: can only swap in base regions\n");
545         return -1;
546     }
547
548     if (!perp->flags.swapped) { 
549         v3_unlock_irqrestore(vm->swap_state.lock,flags);
550         PrintDebug(vm,VCORE_NONE,"swapper: region is already swapped in\n");
551         return 0;
552     }
553
554     // while still holding the lock, we will pin it to make sure no one 
555     // else will attempt to swap in a race with us
556     perp->flags.pinned=1;
557     
558     victim = choose_victim(vm);
559
560     if (!victim) { 
561         perp->flags.pinned=0;  // leave perp swapped 
562         v3_unlock_irqrestore(vm->swap_state.lock,flags);
563         PrintError(vm,VCORE_NONE,"swapper: cannot find victim\n");
564         return -1;
565     }
566
567     victim->flags.pinned=1;
568
569
570     // update the next fit info
571     // pointer arith in units of relevant structs... 
572     vm->swap_state.last_region_used = (victim - &(vm->mem_map.base_regions[0])); 
573     
574
575     // Now we hold both the perp and the victim (pinned)
576     // and so can release the lcok
577     v3_unlock_irqrestore(vm->swap_state.lock,flags);
578
579     
580     if (swap_out_region_internal(vm,victim,1)) {  // ignore that the victim is marked pinned
581         PrintError(vm, VCORE_NONE,"swapper: failed to swap out victim....  bad\n");
582         return -1;
583     }
584
585     // victim is still marked pinned
586
587     // mug the victim
588     perp->host_addr = victim->host_addr;
589     victim->host_addr = 0;
590     // and we're done, so release it
591     victim->flags.swapped=1;
592     victim->flags.pinned=0;
593
594
595     // Now swap in the perp
596     
597     if (read_all(vm->swap_state.swapfd, 
598                  (uint8_t *)V3_VAddr((void *)perp->host_addr), 
599                  perp->guest_end - perp->guest_start, 
600                  perp->guest_start)) {
601         
602         PrintError(vm, VCORE_NONE, "swapper: swap in of region failed!\n"); 
603         // leave it swapped, but unpin the memory... 
604         perp->flags.pinned = 0; 
605         return -1;
606     } else {
607         perp->flags.swapped = 0;  // perp is now OK, so release it
608         perp->flags.pinned = 0; 
609         vm->swap_state.swap_count++;
610         return 0;
611     }
612 }
613
614
615
616 void v3_touch_region(struct v3_vm_info *vm, struct v3_mem_region *region)
617 {
618     // should be uniform host time, not per core...
619     rdtscll(region->swap_state.last_accessed);
620 }
621
622