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.


added cache disk based symswap
[palacios.git] / palacios / src / devices / sym_swap2.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) 2008, Jack Lange <jarusl@cs.northwestern.edu>
11  * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org> 
12  * All rights reserved.
13  *
14  * Author: Jack Lange <jarusl@cs.northwestern.edu>
15  *
16  * This is free software.  You are permitted to use,
17  * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
18  */
19
20 #include <palacios/vmm.h>
21 #include <palacios/vmm_dev_mgr.h>
22 #include <palacios/vmm_sym_swap.h>
23 #include <palacios/vm_guest.h>
24 #include <palacios/vmm_hashtable.h>
25
26
27 #ifdef CONFIG_SYMBIOTIC_SWAP_TELEMETRY
28 #include <palacios/vmm_telemetry.h>
29 #endif
30
31
32 #undef PrintDebug
33 #define PrintDebug(fmt, ...)
34
35
36 /* This is the first page that linux writes to the swap area */
37 /* Taken from Linux */
38 union swap_header {
39     struct {
40         char reserved[PAGE_SIZE - 10];
41         char magic[10];                 /* SWAP-SPACE or SWAPSPACE2 */
42     } magic;
43     struct {
44         char                    bootbits[1024]; /* Space for disklabel etc. */
45         uint32_t                version;
46         uint32_t                last_page;
47         uint32_t                nr_badpages;
48         unsigned char           sws_uuid[16];
49         unsigned char           sws_volume[16];
50         uint32_t                type;           // The index into the swap_map
51         uint32_t                padding[116];
52
53         uint32_t                badpages[1];
54     } info;
55 };
56
57
58 struct cache_entry {
59     uint32_t disk_index;
60     struct list_head cache_node;
61 };
62
63 // Per instance data structure
64 struct swap_state {
65     int active;
66     int disabled;
67
68     struct guest_info * vm;
69     struct swap_state * swap_info;
70
71     int symbiotic;
72
73     union swap_header hdr;
74
75     uint_t swapped_pages;
76     uint_t unswapped_pages;
77     uint32_t disk_writes;
78     uint32_t disk_reads;
79
80
81     uint32_t seek_usecs;
82
83     struct v3_dev_blk_ops * ops;
84     void * private_data;
85
86 #ifdef CONFIG_SYMBIOTIC_SWAP_TELEMETRY
87     uint32_t pages_in;
88     uint32_t pages_out;
89 #endif
90
91     int io_flag;
92
93     uint64_t cache_size;
94     uint8_t * cache;
95     uint64_t cache_base_addr;
96     uint_t pages_in_cache;
97
98     struct cache_entry * entry_map;
99     struct list_head entry_list;
100     struct list_head free_list;
101
102     struct hashtable * entry_ht;
103 };
104
105
106
107 void __udelay(unsigned long usecs);
108
109 static uint_t cache_hash_fn(addr_t key) {
110     return v3_hash_long(key, 32);
111 }
112
113
114 static int cache_eq_fn(addr_t key1, addr_t key2) {
115     return (key1 == key2);
116 }
117
118
119
120
121
122 static inline uint32_t get_swap_index_from_offset(uint32_t offset) {
123     // CAREFUL: The index might be offset by 1, because the first 4K is the header
124     return (offset / 4096);
125 }
126
127 static inline uint32_t get_swap_offset_from_index(uint32_t index) {
128     // CAREFUL: The index might be offset by 1, because the first 4K is the header
129     return (index * 4096);
130 }
131
132
133 static inline uint32_t get_cache_entry_index(struct swap_state * swap, struct cache_entry * entry) {
134     return (entry - swap->entry_map); // / sizeof(struct cache_entry);
135 }
136
137
138
139
140
141 static inline void * get_swap_entry(uint32_t pg_index, void * private_data) {
142     struct swap_state * swap = (struct swap_state *)private_data;
143     struct cache_entry * entry = NULL;
144     void * pg_addr = NULL;
145     uint32_t swap_index = pg_index * 4096;
146
147     if (swap->disabled) {
148         return NULL;
149     }
150
151     PrintDebug("Getting swap entry for index %d\n", pg_index);
152
153     entry = (struct cache_entry *)v3_htable_search(swap->entry_ht, swap_index);
154
155     if (entry != NULL) {
156         uint32_t cache_index = get_cache_entry_index(swap, entry);
157         PrintDebug("Found cached entry (%d)\n", cache_index);
158         pg_addr = swap->cache + (cache_index * 4096);
159     }
160
161     return pg_addr;
162 }
163
164
165
166 static int read_disk(uint8_t * buf, uint64_t lba, uint64_t num_bytes, struct swap_state * swap) {
167     if ((swap->io_flag == 0) && (swap->seek_usecs > 0)) {
168         __udelay(swap->seek_usecs);
169         swap->io_flag = 1;
170     }
171
172     swap->disk_reads += num_bytes / 4096;
173     return swap->ops->read(buf, lba, num_bytes, swap->private_data);
174
175 }
176
177
178 static int write_disk(uint8_t * buf, uint64_t lba, uint64_t num_bytes, struct swap_state * swap) {
179     if ((swap->io_flag == 0) && (swap->seek_usecs > 0)) {
180         __udelay(swap->seek_usecs);
181         swap->io_flag = 1;
182     }
183     
184     swap->disk_writes += num_bytes / 4096;
185
186
187     return swap->ops->write(buf, lba, num_bytes, swap->private_data);
188 }
189
190
191 static uint64_t swap_get_capacity(void * private_data) {
192     struct swap_state * swap = (struct swap_state *)private_data;
193     return swap->ops->get_capacity(swap->private_data);
194 }
195
196
197 static struct v3_swap_ops swap_ops = {
198     .get_swap_entry = get_swap_entry,
199 };
200
201
202
203 static int buf_read(uint8_t * buf, uint64_t lba, uint64_t num_bytes, void * private_data) {
204     struct swap_state * swap = (struct swap_state *)private_data;
205     uint32_t offset = lba;
206     uint32_t length = num_bytes;
207
208     swap->io_flag = 0;
209
210     if (length % 4096) {
211         PrintError("Swapping in length that is not a page multiple\n");
212     }
213
214     if (swap->disabled) {
215         return read_disk(buf, lba, num_bytes, swap);
216     }
217
218
219     PrintDebug("SymSwap: Reading %d bytes to %p (lba=%p)\n", (uint32_t)num_bytes, buf, (void *)(addr_t)lba);
220         
221
222     if (length % 4096) {
223         PrintError("Swapping in length that is not a page multiple\n");
224         return -1;
225     }
226
227
228     if ((swap->active == 1) && (offset >= 4096)) { 
229         int i = 0;
230         int read_pages = (length / 4096);
231         
232
233         // Notify the shadow paging layer
234         
235         swap->unswapped_pages += (length / 4096);
236
237
238 #ifdef CONFIG_SYMBIOTIC_SWAP_TELEMETRY
239         swap->pages_in += length / 4096;
240 #endif
241
242         for (i = 0; i < read_pages; i++) {
243             uint32_t swap_index = offset + (i * 4096);
244             uint32_t cache_index = 0;
245             struct cache_entry * entry = NULL;
246
247             if (swap->symbiotic == 1) {
248                 v3_swap_in_notify(swap->vm, get_swap_index_from_offset(offset + i), swap->hdr.info.type);
249             }       
250
251             PrintDebug("Searching for swap index %d\n", swap_index);
252
253             entry = (struct cache_entry *)v3_htable_search(swap->entry_ht, (addr_t)swap_index);
254
255             if (entry != NULL) {
256                 
257                 cache_index = get_cache_entry_index(swap, entry);
258                 
259                 PrintDebug("Reading from cache entry %d\n", cache_index);
260
261                 memcpy(buf, swap->cache + (cache_index * 4096), 4096);
262
263             } else {
264                 PrintDebug("Reading from disk offset = %p\n", (void *)(addr_t)offset); 
265
266                 if (read_disk(buf, offset, 4096, swap) == -1) {
267                     PrintError("Error reading disk\n");
268                     return -1;
269                 }
270             }
271
272             offset += 4096;
273             buf += 4096;                          
274         }
275     } else {
276         return read_disk(buf, lba, num_bytes, swap);
277     }
278
279
280     return 0;
281 }
282
283
284 static int flush_cache(struct swap_state * swap, int num_to_flush) {
285     int i;
286
287     PrintDebug("Flushing %d pages\n", num_to_flush);
288
289     for (i = 0; i < num_to_flush; i++) {
290         struct cache_entry * entry = NULL;
291         uint32_t entry_index = 0;
292
293         entry = list_first_entry(&(swap->entry_list), struct cache_entry, cache_node);
294
295         entry_index = get_cache_entry_index(swap, entry);
296         PrintDebug("Flushing cache entry %d\n", entry_index);
297
298         if (write_disk(swap->cache + (entry_index * 4096), entry->disk_index, 4096, swap) == -1) {
299             PrintError("Error in disk write\n");
300             return -1;
301         }
302
303         
304         if (swap->symbiotic == 1) {
305             v3_swap_in_notify(swap->vm, entry->disk_index / 4096, swap->hdr.info.type);
306         }           
307         
308         // invalidate swap entry
309
310
311         v3_htable_remove(swap->entry_ht, entry->disk_index, 0);
312
313         list_move(&(entry->cache_node), &(swap->free_list));
314
315         swap->pages_in_cache--;
316     }
317
318     return 0;
319 }
320
321
322
323
324
325
326
327
328 static int buf_write(uint8_t * buf,  uint64_t lba, uint64_t num_bytes, void * private_data) {
329     struct swap_state * swap = (struct swap_state *)private_data;
330     uint32_t offset = lba;
331     uint32_t length = num_bytes;
332
333     swap->io_flag = 0;
334
335
336
337     if (swap->disabled) {
338         return write_disk(buf, lba, num_bytes, swap);
339     }
340
341
342     /*
343       PrintDebug("SymSwap: Writing %d bytes to %p from %p\n", length, 
344       (void *)(swap->swap_space + offset), buf);
345     */
346
347
348     if ((swap->active == 0) && (offset == 0)) {
349         // This is the swap header page
350
351         swap->active = 1;
352
353         // store a local copy
354         memcpy(&(swap->hdr), buf, sizeof(union swap_header));
355
356
357         PrintError("Swap Type=%d (magic=%s)\n", swap->hdr.info.type, swap->hdr.magic.magic);
358
359         if (swap->symbiotic == 1) {
360             if (v3_register_swap_disk(swap->vm, swap->hdr.info.type, &swap_ops, swap) == -1) {
361                 PrintError("Error registering symbiotic swap disk\n");
362                 return -1;
363             }
364
365             PrintError("Swap disk registered\n");
366         }
367
368
369         if (write_disk(buf, lba, num_bytes, swap) == -1) {
370             PrintError("Error writing swap header to disk\n");
371             return -1;
372         }
373
374         PrintDebug("Wrote header to disk\n");
375
376         return 0;
377     }
378
379     if ((swap->active == 1) && (offset >= 4096)) {
380         int i = 0;
381         int written_pages = (length / 4096);
382         int avail_space = (swap->cache_size / 4096) - swap->pages_in_cache;
383
384
385         swap->swapped_pages += written_pages;
386         
387 #ifdef CONFIG_SYMBIOTIC_SWAP_TELEMETRY
388         swap->pages_out += length / 4096;
389 #endif
390
391         PrintDebug("available cache space = %d, pages written = %d\n", avail_space, written_pages);
392
393         if (avail_space < written_pages) {
394             flush_cache(swap, written_pages - avail_space);
395         }
396         
397
398         for (i = 0; i < written_pages; i += 1) {
399             //      set_index_usage(swap, get_swap_index_from_offset(offset + i), 1);
400             struct cache_entry * new_entry = NULL;
401             uint32_t swap_index = offset + (i * 4096);
402             uint32_t cache_index = 0;
403
404             new_entry = (struct cache_entry *)v3_htable_search(swap->entry_ht, (addr_t)swap_index);
405
406             if (new_entry == NULL) {
407                 new_entry = list_tail_entry(&(swap->free_list), struct cache_entry, cache_node);
408
409                 new_entry->disk_index = swap_index;
410
411                 list_move_tail(&(new_entry->cache_node), &(swap->entry_list));
412
413                 v3_htable_insert(swap->entry_ht, (addr_t)swap_index, (addr_t)new_entry);
414
415                 swap->pages_in_cache++;
416             }
417             
418             cache_index = get_cache_entry_index(swap, new_entry);
419             
420             PrintDebug("Writing to cache entry %d\n", cache_index);
421
422             memcpy(swap->cache + (cache_index * 4096), buf, 4096);
423
424             buf += 4096;
425         }
426     } else {
427         if (write_disk(buf, lba, num_bytes, swap) == -1) {
428             PrintError("Error writing swap header to disk\n");
429             return -1;
430         }
431     }
432
433     return 0;
434 }
435
436
437
438
439 static uint8_t write_buf[4096];
440
441
442 static int swap_write(uint8_t * buf,  uint64_t lba, uint64_t num_bytes, void * private_data) {
443     int idx = lba % 4096;
444
445     if (num_bytes != 512) {
446         PrintError("Write for %d bytes\n", (uint32_t)num_bytes);
447         return -1;
448     }
449
450     
451     memcpy(write_buf + idx, buf, num_bytes);
452
453     if (idx + num_bytes == 4096) {
454         return buf_write(write_buf, lba - idx, 4096, private_data);
455     }
456
457     return 0;
458 }
459
460
461
462 static uint8_t read_buf[4096];
463
464
465
466 static int swap_read(uint8_t * buf,  uint64_t lba, uint64_t num_bytes, void * private_data) {
467     int idx = lba % 4096;
468     
469
470     if (num_bytes != 512) {
471         PrintError("Read for %d bytes\n", (uint32_t)num_bytes);
472         return -1;
473     }
474
475     if (idx == 0) {
476         if (buf_read(read_buf, lba - idx, 4096, private_data) == -1) {
477             PrintError("Error reading buffer\n");
478             return -1;
479         }
480     }
481
482     memcpy(buf, read_buf + idx, num_bytes);
483         
484     return 0;
485 }
486
487
488 static int swap_free(struct vm_device * dev) {
489     return -1;
490 }
491
492
493 static struct v3_dev_blk_ops blk_ops = {
494     .read = swap_read, 
495     .write = swap_write, 
496     .get_capacity = swap_get_capacity,
497 };
498
499
500
501 static struct v3_device_ops dev_ops = {
502     .free = swap_free,
503     .reset = NULL,
504     .start = NULL,
505     .stop = NULL,
506 };
507
508
509 #ifdef CONFIG_SYMBIOTIC_SWAP_TELEMETRY
510 static void telemetry_cb(struct guest_info * info, void * private_data, char * hdr) {
511     struct swap_state * swap = (struct swap_state *)private_data;
512
513     V3_Print("%sSwap Device:\n", hdr);
514     V3_Print("%s\tPages Swapped in=%d\n", hdr, swap->pages_in);
515     V3_Print("%s\tPages Swapped out=%d\n", hdr, swap->pages_out);
516     V3_Print("%s\tPages Written to Disk=%d\n", hdr, swap->disk_writes);
517     V3_Print("%s\tPages Read from Disk=%d\n", hdr, swap->disk_reads);
518 }
519 #endif
520
521
522 static int connect_fn(struct guest_info * info, 
523                       void * frontend_data, 
524                       struct v3_dev_blk_ops * ops, 
525                       v3_cfg_tree_t * cfg, 
526                       void * private_data) {
527     v3_cfg_tree_t * frontend_cfg = v3_cfg_subtree(cfg, "frontend");
528     uint32_t cache_size = atoi(v3_cfg_val(cfg, "cache_size")) * 1024 * 1024;
529     uint32_t seek_us = atoi(v3_cfg_val(cfg, "seek_us"));
530     int symbiotic = atoi(v3_cfg_val(cfg, "symbiotic"));
531     struct swap_state * swap = NULL;
532     int i;
533
534     if (!frontend_cfg) {
535         PrintError("Initializing sym swap without a frontend device\n");
536         return -1;
537     }
538
539     PrintError("Creating Swap filter (cache size=%dMB)\n", cache_size / (1024 * 1024));
540
541     swap = (struct swap_state *)V3_Malloc(sizeof(struct swap_state));
542
543     swap->vm = info;
544     swap->cache_size = cache_size;
545     swap->io_flag = 0;
546     swap->seek_usecs = seek_us;
547     swap->symbiotic = symbiotic;
548
549     swap->ops = ops;
550     swap->private_data = private_data;
551
552     swap->swapped_pages = 0;
553     swap->unswapped_pages = 0;
554     //    swap->cached_pages = 0;
555
556     if (cache_size == 0) {
557         swap->disabled = 1;
558     } else {
559         swap->disabled = 0;
560
561         INIT_LIST_HEAD(&(swap->entry_list));
562         INIT_LIST_HEAD(&(swap->free_list));
563         swap->entry_map = (struct cache_entry *)V3_Malloc(sizeof(struct cache_entry) * (cache_size / 4096));
564         
565         for (i = 0; i < (cache_size / 4096); i++) {
566             list_add(&(swap->entry_map[i].cache_node), &(swap->free_list));
567         }
568
569         swap->entry_ht = v3_create_htable(0, cache_hash_fn, cache_eq_fn);
570
571         swap->active = 0;
572
573         swap->cache_base_addr = (addr_t)V3_AllocPages(swap->cache_size / 4096);
574         swap->cache = (uint8_t *)V3_VAddr((void *)(swap->cache_base_addr));
575         memset(swap->cache, 0, swap->cache_size);
576     }
577
578     if (v3_dev_connect_blk(info, v3_cfg_val(frontend_cfg, "tag"), 
579                            &blk_ops, frontend_cfg, swap) == -1) {
580         PrintError("Could not connect to frontend %s\n", 
581                     v3_cfg_val(frontend_cfg, "tag"));
582         return -1;
583     }
584
585
586 #ifdef CONFIG_SYMBIOTIC_SWAP_TELEMETRY
587
588     if (info->enable_telemetry == 1) {
589         v3_add_telemetry_cb(info, telemetry_cb, swap);
590     }
591     
592 #endif
593
594     return 0;
595 }
596
597
598
599
600 static int swap_init(struct guest_info * vm, v3_cfg_tree_t * cfg) {
601
602     char * name = v3_cfg_val(cfg, "name");
603
604     struct vm_device * dev = v3_allocate_device(name, &dev_ops, NULL);
605
606     if (v3_attach_device(vm, dev) == -1) {
607         PrintError("Could not attach device %s\n", name);
608         return -1;
609     }
610
611     if (v3_dev_add_blk_frontend(vm, name, connect_fn, NULL) == -1) {
612         PrintError("Could not register %s as block frontend\n", name);
613         return -1;
614     }
615
616
617     return 0;
618 }
619
620 device_register("SWAPCACHE", swap_init)