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.


v3_mem enhancements - managed offlining; ability to deallocate memory (obviates v3_me...
[palacios.git] / linux_usr / v3_mem.c
1 /* 
2  * V3 Control utility
3  * (c) Jack lange, 2010
4  */
5
6
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <fcntl.h> 
10 #include <sys/ioctl.h> 
11 #include <sys/stat.h> 
12 #include <sys/types.h> 
13 #include <unistd.h> 
14 #include <string.h>
15 #include <dirent.h>
16 #include <alloca.h> 
17
18 #include "v3_ctrl.h"
19
20 #define SYS_PATH "/sys/devices/system/memory/"
21
22 #define BUF_SIZE 128
23
24 char offname[256];
25 FILE *off;
26
27 int num_offline;
28 unsigned long long *start_offline;
29 unsigned long long *len_offline;
30
31 static int read_offlined();
32 static int write_offlined();
33 static int find_offlined(unsigned long long base_addr);
34 static int clear_offlined();
35
36
37 static int offline_memory(unsigned long long mem_size_bytes,
38                           unsigned long long mem_min_start,
39                           int limit32, 
40                           unsigned long long *num_bytes, 
41                           unsigned long long *base_addr);
42
43 static int online_memory(unsigned long long num_bytes, 
44                          unsigned long long base_addr);
45
46
47
48
49 int main(int argc, char * argv[]) {
50     unsigned long long mem_size_bytes = 0;
51     unsigned long long mem_min_start = 0;
52     int v3_fd = -1;
53     int request = 0;
54     int limit32 = 0;
55     int help=0;
56     int alloffline=0;
57     enum {NONE, ADD, REMOVE} op;
58     int node = -1;
59     int c;
60     unsigned long long num_bytes, base_addr;
61     struct v3_mem_region mem;
62
63     while ((c=getopt(argc,argv,"harklm:n:"))!=-1) {
64         switch (c) {
65             case 'h':
66                 help=1;
67                 break;
68             case 'a':
69                 op=ADD;
70                 break;
71             case 'r':
72                 op=REMOVE;
73                 break;
74             case 'k':
75                 request=1;
76                 break;
77             case 'l':
78                 limit32=1;
79                 break;
80             case 'm':
81                 mem_min_start = atoll(optarg) * (1024*1024);
82                 break;
83             case 'n':
84                 node = atoi(optarg);
85                 break;
86             case '?':
87                 if (optopt=='n') { 
88                     printf("-n requires the numa node...\n");
89             return -1;
90                 } else if (optopt=='m') { 
91                     printf("-m requires the minimum starting address (in MB)...\n");
92             return -1;
93                 } else {
94                     printf("Unknown option %c\n",optopt);
95             return -1;
96                 }
97                 break;
98             default:
99                 printf("Unknown option %c\n",optopt);
100                 break;
101         }
102     }
103
104
105     if (op==NONE || optind==argc || help) {
106         printf("usage: v3_mem [ [-k] [-l] [-n k] [-m n] -a <memory size (MB)>] | [-r <hexaddr> | offline]\n\n"
107                "Palacios Memory Management\n\nMemory Addition\n"
108                " -a <mem>      Allocate memory for use by Palacios (MB).\n\n"
109                " With    -k    this requests in-kernel allocation\n"
110                " Without -k    this attempts to offline memory via hot remove\n\n"
111                " With    -l    the request or offlining is limited to first 4 GB\n"
112                " Without -l    the request or offlining has no limits\n\n"
113                " With    -m n  the search for offlineable memory starts at n MB\n"
114                " Without -m n  the search for offlineable memory starts at 0 MB\n\n"
115                " With    -n i  the request is for numa node i\n"
116                " Without -n i  the request can be satified on any numa node\n\n"
117                "Memory Removal\n"
118                " -r <hexaddr>  Free Palacios memory containing hexaddr, online it if needed\n"
119                " -r offline    Free all offline Palacios memory and online it\n"
120                );
121         
122         return -1;
123     }
124
125     if (op==ADD) {
126         mem_size_bytes = atoll(argv[optind]) * (1024 * 1024);
127     } else if (op==REMOVE) { 
128         if (!strcasecmp(argv[optind],"offline")) {
129             alloffline=1;
130         } else {
131             base_addr=strtoll(argv[optind],NULL,16);
132         }
133     }
134     
135     if (!getenv("PALACIOS_DIR")) { 
136         printf("Please set the PALACIOS_DIR variable\n");
137         return -1;
138     }
139
140     strcpy(offname,getenv("PALACIOS_DIR"));
141     strcat(offname,"/.v3offlinedmem");
142
143     if (!(off=fopen(offname,"a+"))) { 
144         printf("Cannot open or create offline memory file %s",offname);
145         return -1;
146     }
147
148     // removing all offlined memory we added is a special case
149     if (op==REMOVE && alloffline) {
150         int i;
151         int rc=0;
152
153         // we just need to reinvoke ourselves
154         read_offlined();
155         for (i=0;i<num_offline;i++) {
156             char cmd[256];
157             sprintf(cmd,"v3_mem -r %llx", start_offline[i]);
158             rc|=system(cmd);
159         }
160         clear_offlined();
161         return rc;
162     }
163
164         
165     v3_fd = open(v3_dev, O_RDONLY);
166     
167     if (v3_fd == -1) {
168         printf("Error opening V3Vee control device\n");
169         fclose(off);
170         return -1;
171     }
172
173
174     if (op==ADD) { 
175         if (!request) { 
176             printf("Trying to offline memory (size=%llu, min_start=%llu, limit32=%d)\n",mem_size_bytes,mem_min_start,limit32);
177             if (offline_memory(mem_size_bytes,mem_min_start,limit32, &num_bytes, &base_addr)) { 
178                 printf("Could not offline memory\n");
179                 fclose(off);
180                 close(v3_fd);
181                 return -1;
182             }
183             
184             fprintf(off,"%llx\t%llx\n",base_addr, num_bytes);
185             
186             mem.type=PREALLOCATED;
187             mem.node=node;
188             mem.base_addr=base_addr;
189             mem.num_pages=num_bytes/4096;
190             
191         } else {
192             printf("Generating memory allocation request (size=%llu, limit32=%d)\n", mem_size_bytes, limit32);
193             mem.type = limit32 ? REQUESTED32 : REQUESTED;
194             mem.node = node;
195             mem.base_addr = 0;
196             mem.num_pages = mem_size_bytes / 4096;
197         }
198         
199         printf("Allocation request is: type=%d, node=%d, base_addr=0x%llx, num_pages=%llu\n",
200                mem.type, mem.node, mem.base_addr, mem.num_pages);
201         
202         if (ioctl(v3_fd, V3_ADD_MEMORY, &mem)<0) { 
203             printf("Request rejected by Palacios\n");
204             close(v3_fd);
205             fclose(off);
206             return -1;
207         } else {
208             printf("Request accepted by Palacios\n");
209             close(v3_fd);       
210             fclose(off);
211             return 0;
212         }
213         
214     } else if (op==REMOVE) { 
215         int entry;
216
217         read_offlined();
218
219         entry=find_offlined(base_addr);
220         
221         if (entry<0) { 
222             // no need to offline
223             mem.type=REQUESTED;
224         } else {
225             mem.type=PREALLOCATED;
226         }
227         
228         mem.base_addr=base_addr;
229
230         // now remove it from palacios
231         printf("Deallocation request is: type=%d, base_addr=0x%llx\n",
232                mem.type, mem.base_addr);
233         
234         if (ioctl(v3_fd, V3_REMOVE_MEMORY, &mem)<0) { 
235             printf("Request rejected by Palacios\n");
236             close(v3_fd);
237             fclose(off);
238             return -1;
239         } 
240
241         printf("Request accepted by Palacios\n");
242
243         if (entry>=0) { 
244             printf("Onlining the memory to make it available to the kernel\n");
245             online_memory(start_offline[entry],len_offline[entry]);
246         
247             len_offline[entry] = 0;
248
249             write_offlined();
250
251             
252         } else {
253             printf("Memory was deallocated in the kernel\n");
254         }
255
256         clear_offlined();
257         close(v3_fd);
258         fclose(off);
259
260         return 0;
261     }
262
263
264
265
266 static int dir_filter(const struct dirent * dir) {
267     if (strncmp("memory", dir->d_name, 6) == 0) {
268         return 1;
269     }
270
271     return 0;
272 }
273
274
275 static int dir_cmp(const struct dirent **dir1, const struct dirent ** dir2) {
276     int num1 = atoi((*dir1)->d_name + 6);
277     int num2 = atoi((*dir2)->d_name + 6);
278     
279     return num1 - num2;
280 }
281
282
283
284 #define UNWIND(first,last)                                      \
285 do {                                                            \
286     int i;                                                      \
287     for (i = first; i <= last; i++) {                           \
288         FILE *f;                                                \
289         char name[256];                                         \
290         snprintf(name,256,"%smemory%d/state",SYS_PATH,i);       \
291         f=fopen(name,"r+");                                     \
292         if (!f) {                                               \
293             perror("Cannot open state file\n");                 \
294             return -1;                                          \
295         }                                                       \
296         printf("Re-onlining block %d (%s)\n",i,name);           \
297         fprintf(f,"online\n");                                  \
298         fclose(f);                                              \
299     }                                                           \
300 } while (0)
301
302
303 static int offline_memory(unsigned long long mem_size_bytes,
304                           unsigned long long mem_min_start,
305                           int limit32, 
306                           unsigned long long *num_bytes, 
307                           unsigned long long *base_addr)
308 {
309
310     unsigned int block_size_bytes = 0;
311     int bitmap_entries = 0;
312     unsigned char * bitmap = NULL;
313     int num_blocks = 0;    
314     int reg_start = 0;
315     int mem_ready = 0;
316     
317     
318
319     printf("Trying to find %dMB (%d bytes) of memory above %llu with limit32=%d\n", mem_size_bytes/(1024*1024), mem_size_bytes, mem_min_start, limit32);
320         
321     /* Figure out the block size */
322     {
323         int tmp_fd = 0;
324         char tmp_buf[BUF_SIZE];
325         
326         tmp_fd = open(SYS_PATH "block_size_bytes", O_RDONLY);
327         
328         if (tmp_fd == -1) {
329             perror("Could not open block size file: " SYS_PATH "block_size_bytes");
330             return -1;
331         }
332     
333         if (read(tmp_fd, tmp_buf, BUF_SIZE) <= 0) {
334             perror("Could not read block size file: " SYS_PATH "block_size_bytes");
335             return -1;
336         }
337         
338         close(tmp_fd);
339         
340         block_size_bytes = strtoll(tmp_buf, NULL, 16);
341         
342         printf("Memory block size is %dMB (%d bytes)\n", block_size_bytes / (1024 * 1024), block_size_bytes);
343         
344     }
345     
346     
347     num_blocks =  mem_size_bytes / block_size_bytes;
348     if (mem_size_bytes % block_size_bytes) num_blocks++;
349     
350     mem_min_start = block_size_bytes * 
351         ((mem_min_start / block_size_bytes) + (!!(mem_min_start % block_size_bytes)));
352     
353     printf("Looking for %d blocks of memory starting at %p (block %llu) with limit32=%d\n", num_blocks, (void*)mem_min_start, mem_min_start/block_size_bytes,limit32);
354         
355     
356     // We now need to find <num_blocks> consecutive offlinable memory blocks
357     
358     /* Scan the memory directories */
359     {
360         struct dirent ** namelist = NULL;
361         int size = 0;
362         int i = 0;
363         int j = 0;
364         int last_block = 0;
365         int first_block = mem_min_start/block_size_bytes;
366         int limit_block = 0xffffffff / block_size_bytes; // for 32 bit limiting
367         
368         last_block = scandir(SYS_PATH, &namelist, dir_filter, dir_cmp);
369         bitmap_entries = atoi(namelist[last_block - 1]->d_name + 6) + 1;
370             
371         size = bitmap_entries / 8;
372         if (bitmap_entries % 8) size++;
373             
374         bitmap = alloca(size);
375
376         if (!bitmap) {
377             printf("ERROR: could not allocate space for bitmap\n");
378             return -1;
379         }
380         
381         memset(bitmap, 0, size);
382         
383         for (i = 0 ; j < bitmap_entries - 1; i++) {
384             struct dirent * tmp_dir = namelist[i];
385             int block_fd = 0;       
386             char status_str[BUF_SIZE];
387             char fname[BUF_SIZE];
388                 
389             memset(status_str, 0, BUF_SIZE);
390             memset(fname, 0, BUF_SIZE);
391                 
392             snprintf(fname, BUF_SIZE, "%s%s/removable", SYS_PATH, tmp_dir->d_name);
393                 
394             j = atoi(tmp_dir->d_name + 6);
395             int major = j / 8;
396             int minor = j % 8;
397                 
398                 
399             if (i<first_block) { 
400                 printf("Skipping %s due to minimum start constraint\n",fname);
401                 continue;
402             } 
403
404             if (limit32 && i>limit_block) { 
405                 printf("Skipping %s due to 32 bit constraint\n",fname);
406                 continue;
407             } 
408                 
409
410             // The prospective block must be (a) removable, and (b) currently online
411                 
412             printf("Checking %s...", fname);
413                 
414             block_fd = open(fname, O_RDONLY);
415                 
416             if (block_fd == -1) {
417                 printf("Hotpluggable memory not supported or could not determine if block is removable...\n");
418                 return -1;
419             }
420                 
421             if (read(block_fd, status_str, BUF_SIZE) <= 0) {
422                 perror("Could not read block removability information\n");
423                 return -1;
424             }
425             
426             status_str[BUF_SIZE-1]=0;
427                 
428             close(block_fd);
429                 
430             if (atoi(status_str) == 1) {
431                 printf("Removable ");
432             } else {
433                 printf("Not removable\n");
434                 continue;
435             }
436             
437             snprintf(fname, BUF_SIZE, "%s%s/state", SYS_PATH, tmp_dir->d_name);
438             
439             block_fd = open(fname, O_RDONLY);
440             
441             if (block_fd<0) { 
442                 perror("Could not open block state\n");
443                 return -1;
444             }
445
446             if (read(block_fd, status_str, BUF_SIZE) <=0) { 
447                 perror("Could not read block state information\n");
448                 return -1;
449             }
450
451             status_str[BUF_SIZE-1]=0;
452
453             close(block_fd);
454
455             if (!strncasecmp(status_str,"offline",7)) {
456                 printf("and Already Offline (unusable)\n");
457             } else if (!strncasecmp(status_str,"online",6)) { 
458                 printf("and Online (usable)\n");
459                 bitmap[major] |= (0x1 << minor);
460             } else {
461                 printf("and in Unknown State '%s' (unusable)\n",status_str);
462             }
463             
464         }
465         
466     }
467     
468     while (!mem_ready) {
469
470         /* Scan bitmap for enough consecutive space */
471         {
472             // num_blocks: The number of blocks we need to find
473             // bitmap: bitmap of blocks (1 == allocatable)
474             // bitmap_entries: number of blocks in the system/number of bits in bitmap
475             // reg_start: The block index where our allocation will start
476                 
477             int i = 0;
478             int run_len = 0;
479             
480             for (i = 0; i < bitmap_entries; i++) {
481                 int i_major = i / 8;
482                 int i_minor = i % 8;
483                 
484                 if (!(bitmap[i_major] & (0x1 << i_minor))) {
485                     reg_start = i + 1; // skip the region start to next entry
486                     run_len = 0;
487                     continue;
488                 }
489                 
490                 run_len++;
491                     
492                 if (run_len >= num_blocks) {
493                     break;
494                 }
495             }
496             
497             
498             if (run_len < num_blocks) {
499                 fprintf(stderr, "Could not find enough consecutive memory blocks... (found %d)\n", run_len);
500                 // no offlining yet, so no need to unwind here
501                 return -1;
502             }
503         }
504         
505         
506         /* Offline memory blocks starting at reg_start */
507         {
508             int i = 0;
509             
510             for (i = 0; i < num_blocks; i++) {  
511                 FILE * block_file = NULL;
512                 char fname[256];
513                 
514                 memset(fname, 0, 256);
515                 
516                 snprintf(fname, 256, "%smemory%d/state", SYS_PATH, i + reg_start);
517                 
518                 block_file = fopen(fname, "r+");
519                 
520                 if (block_file == NULL) {
521                     perror("Could not open block file");
522                     UNWIND(reg_start, i+reg_start-1);
523                     return -1;
524                 }
525                 
526                     
527                 printf("Offlining block %d (%s)\n", i + reg_start, fname);
528                 fprintf(block_file, "offline\n");
529                 
530                 fclose(block_file);
531                 
532             }
533         }
534         
535         
536         /*  We asked to offline set of blocks, but Linux could have lied. 
537          *  To be safe, check whether blocks were offlined and start again if not 
538          */
539         
540         {
541             int i = 0;
542             
543             mem_ready = 1; // Hopefully we are ok...
544             
545             
546             for (i = 0; i < num_blocks; i++) {
547                 int block_fd = 0;
548                 char fname[BUF_SIZE];
549                 char status_buf[BUF_SIZE];
550                 
551                 
552                 memset(fname, 0, BUF_SIZE);
553                 memset(status_buf, 0, BUF_SIZE);
554                 
555                 snprintf(fname, BUF_SIZE, "%smemory%d/state", SYS_PATH, i + reg_start);
556                 
557                 
558                 block_fd = open(fname, O_RDONLY);
559                 
560                 if (block_fd == -1) {
561                     perror("Could not open block state file");
562                     return -1;
563                 }
564                 
565                 if (read(block_fd, status_buf, BUF_SIZE) <= 0) {
566                     perror("Could not read block state");
567                     return -1;
568                 }
569
570                 status_buf[BUF_SIZE]=0;
571                 
572                 printf("Checking offlined block %d (%s)...", i + reg_start, fname);
573                 
574                 int ret = strncmp(status_buf, "offline", strlen("offline"));
575                 
576                 if (ret != 0) {  // uh oh
577                     int j = 0;
578                     int major = (i + reg_start) / 8;
579                     int minor = (i + reg_start) % 8;
580
581                     bitmap[major] &= ~(0x1 << minor); // mark the block as not removable in bitmap
582                     
583                     mem_ready = 0; // Keep searching
584                     
585                     printf("ERROR - block status is '%s'\n", status_buf);
586
587                     // Unwind space
588                     UNWIND(reg_start,reg_start+num_blocks-1);
589                     
590                     break;
591                 } 
592             }
593             
594             printf("Offlined Memory OK\n");
595                 
596         }
597     }
598     
599     /* Memory is offlined. Calculate size and phys start addr to send to Palacios */
600     *num_bytes = (unsigned long long)(num_blocks) * (unsigned long long)(block_size_bytes);
601     *base_addr = (unsigned long long)(reg_start) * (unsigned long long)(block_size_bytes);
602     
603     return 0;
604 }
605
606
607 static int online_memory(unsigned long long base_addr,
608                          unsigned long long num_bytes)
609 {
610     
611     unsigned int block_size_bytes = 0;
612     int bitmap_entries = 0;
613     unsigned char * bitmap = NULL;
614     int num_blocks = 0;    
615     int reg_start = 0;
616     int mem_ready = 0;
617     
618     
619
620     printf("Trying to online memory from %llu to %llu\n",base_addr,base_addr+num_bytes-1);
621         
622     /* Figure out the block size */
623     {
624         int tmp_fd = 0;
625         char tmp_buf[BUF_SIZE];
626         
627         tmp_fd = open(SYS_PATH "block_size_bytes", O_RDONLY);
628         
629         if (tmp_fd == -1) {
630             perror("Could not open block size file: " SYS_PATH "block_size_bytes");
631             return -1;
632         }
633     
634         if (read(tmp_fd, tmp_buf, BUF_SIZE) <= 0) {
635             perror("Could not read block size file: " SYS_PATH "block_size_bytes");
636             return -1;
637         }
638         
639         close(tmp_fd);
640         
641         block_size_bytes = strtoll(tmp_buf, NULL, 16);
642         
643         printf("Memory block size is %dMB (%d bytes)\n", block_size_bytes / (1024 * 1024), block_size_bytes);
644         
645     }
646     
647     num_blocks =  num_bytes / block_size_bytes;
648     if (num_bytes % block_size_bytes) num_blocks++;
649
650     reg_start = base_addr / block_size_bytes;
651
652     printf("That is %lu blocks of size %llu starting at block %d\n", num_blocks, block_size_bytes, reg_start);
653    
654     
655         
656     /* Online memory blocks starting at reg_start */
657     {
658         int i = 0;
659             
660         for (i = 0; i < num_blocks; i++) {      
661             FILE * block_file = NULL;
662             char fname[256];
663                 
664             memset(fname, 0, 256);
665             
666             snprintf(fname, 256, "%smemory%d/state", SYS_PATH, i + reg_start);
667             
668             block_file = fopen(fname, "r+");
669             
670             if (block_file == NULL) {
671                 perror("Could not open block file");
672                 return -1;
673             }
674                 
675             
676             printf("Onlining block %d (%s)\n", i + reg_start, fname);
677             fprintf(block_file, "online\n");
678             
679             fclose(block_file);
680             
681         }
682     }
683     
684     return 0;
685     
686 }
687
688
689
690 static int read_offlined()
691 {
692     rewind(off);
693     unsigned long long base, len;
694     int i;
695     
696     num_offline=0;
697     while (fscanf(off,"%llx\t%llx\n",&base,&len)==2) { num_offline++; }
698
699
700     start_offline=(unsigned long long *)calloc(num_offline, sizeof(unsigned long long));
701     len_offline=(unsigned long long *)calloc(num_offline, sizeof(unsigned long long));
702
703     if (!start_offline || !len_offline) { 
704         printf("Cannot allocate space to load offline map\n");
705         return -1;
706     }
707
708     rewind(off);
709     for (i=0;i<num_offline;i++) { 
710         fscanf(off,"%llx\t%llx",&(start_offline[i]),&(len_offline[i]));
711     }
712     // we are now back to the end, and can keep appending
713     return 0;
714 }
715
716
717 static int write_offlined()
718 {
719     int i;
720
721     fclose(off);
722     if (!(off=fopen(offname,"w+"))) {  // truncate
723         printf("Cannot open %s for writing!\n");
724         return -1;
725     }
726
727     for (i=0;i<num_offline;i++) { 
728         if (len_offline[i]) { 
729             fprintf(off,"%llx\t%llx\n",start_offline[i],len_offline[i]);
730         }
731     }
732     // we are now back to the end, and can keep appending
733     return 0;
734 }
735
736
737 static int clear_offlined()
738 {
739     free(start_offline);
740     free(len_offline);
741     return 0;
742 }
743
744 static int find_offlined(unsigned long long base_addr)
745 {
746     int i;
747
748     for (i=0;i<num_offline;i++) { 
749         if (base_addr>=start_offline[i] &&
750             base_addr<(start_offline[i]+len_offline[i])) { 
751             return i;
752         }
753     }
754
755     return -1;
756
757 }
758
759
760     
761
762