make the /proc/sys/kernel/randomize_va_space check as a warning, instead of disabling...
[charm.git] / src / conv-core / isomalloc.c
1 /**************************************************************************
2 Isomalloc:
3   A way to allocate memory at the same address on every processor.
4 This enables linked data structures, like thread stacks, to be migrated
5 to the same address range on other processors.  This is similar to an
6 explicitly managed shared memory system.
7
8   The memory is used and released via the mmap()/mumap() calls, so unused
9 memory does not take any (RAM, swap or disk) space.
10
11   The way it's implemented is that each processor claims some section 
12 of the available virtual address space, and satisfies all new allocations
13 from that space.  Migrating structures use whatever space they started with.
14
15 Written for migratable threads by Milind Bhandarkar around August 2000;
16 generalized by Orion Lawlor November 2001.
17 */
18 #include "converse.h"
19 #include "memory-isomalloc.h"
20
21 #define CMK_THREADS_DEBUG 0 
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <errno.h> /* just so I can find dynamically-linked symbols */
26
27 static int read_randomflag(void)
28 {
29   FILE *fp;
30   int random_flag;
31   CmiLock(smp_mutex);
32   fp = fopen("/proc/sys/kernel/randomize_va_space", "r");
33   if (fp != NULL) {
34     fscanf(fp, "%d", &random_flag);
35   }
36   else {
37     random_flag = -1;
38   }
39   CmiUnlock(smp_mutex);
40   return random_flag;
41 }
42
43 struct CmiIsomallocBlock {
44       int slot; /*First mapped slot*/
45       int length; /*Length of (user portion of) mapping, in bytes*/
46 };
47 typedef struct CmiIsomallocBlock CmiIsomallocBlock;
48
49 /*Convert a heap block pointer to/from a CmiIsomallocBlock header*/
50 static void *block2pointer(CmiIsomallocBlock *blockHeader) {
51         return (void *)(blockHeader+1);
52 }
53 static CmiIsomallocBlock *pointer2block(void *heapBlock) {
54         return ((CmiIsomallocBlock *)heapBlock)-1;
55 }
56
57 /*Integral type to be used for pointer arithmetic:*/
58 typedef unsigned long memRange_t;
59
60 /*Size in bytes of a single slot*/
61 static size_t slotsize;
62
63 /*Total number of slots per processor*/
64 static int numslots=0;
65
66 /*Start and end of isomalloc-managed addresses.
67 If isomallocStart==NULL, isomalloc is disabled.
68 */
69 static char *isomallocStart=NULL;
70 static char *isomallocEnd=NULL;
71
72 /*Utility conversion functions*/
73 static void *slot2addr(int slot) {
74         return isomallocStart+((memRange_t)slotsize)*((memRange_t)slot);
75 }
76 static int slot2pe(int slot) {
77         return slot/numslots;
78 }
79 static int pe2slot(int pe) {
80         return pe*numslots;
81 }
82 /*Return the number of slots in a block with n user data bytes*/
83 static int length2slots(int nBytes) {
84         return (sizeof(CmiIsomallocBlock)+nBytes+slotsize-1)/slotsize;
85 }
86
87 typedef struct _slotblock
88 {
89   int startslot;
90   int nslots;
91 } slotblock;
92
93 typedef struct _slotset
94 {
95   int maxbuf;
96   slotblock *buf;
97   int emptyslots;
98 } slotset;
99
100 /*
101  * creates a new slotset of nslots entries, starting with all
102  * empty slots. The slot numbers are [startslot,startslot+nslot-1]
103  */
104 static slotset *
105 new_slotset(int startslot, int nslots)
106 {
107   int i;
108   slotset *ss = (slotset*) malloc_reentrant(sizeof(slotset));
109   _MEMCHECK(ss);
110   ss->maxbuf = 16;
111   ss->buf = (slotblock *) malloc_reentrant(sizeof(slotblock)*ss->maxbuf);
112   _MEMCHECK(ss->buf);
113   ss->emptyslots = nslots;
114   ss->buf[0].startslot = startslot;
115   ss->buf[0].nslots = nslots;
116   for (i=1; i<ss->maxbuf; i++)
117     ss->buf[i].nslots = 0;
118   return ss;
119 }
120
121 /*
122  * returns new block of empty slots. if it cannot find any, returns (-1).
123  */
124 static int
125 get_slots(slotset *ss, int nslots)
126 {
127   int i;
128   if(ss->emptyslots < nslots)
129     return (-1);
130   for(i=0;i<(ss->maxbuf);i++)
131     if(ss->buf[i].nslots >= nslots)
132       return ss->buf[i].startslot;
133   return (-1);
134 }
135
136 /* just adds a slotblock to an empty position in the given slotset. */
137 static void
138 add_slots(slotset *ss, int sslot, int nslots)
139 {
140   int pos, emptypos = -1;
141   if (nslots == 0)
142     return;
143   for (pos=0; pos < (ss->maxbuf); pos++) {
144     if (ss->buf[pos].nslots == 0) {
145       emptypos = pos;
146       break; /* found empty slotblock */
147     }
148   }
149   if (emptypos == (-1)) /*no empty slotblock found */
150   {
151     int i;
152     int newsize = ss->maxbuf*2;
153     slotblock *newbuf = (slotblock *) malloc_reentrant(sizeof(slotblock)*newsize);
154     _MEMCHECK(newbuf);
155     for (i=0; i<(ss->maxbuf); i++)
156       newbuf[i] = ss->buf[i];
157     for (i=ss->maxbuf; i<newsize; i++)
158       newbuf[i].nslots  = 0;
159     free_reentrant(ss->buf);
160     ss->buf = newbuf;
161     emptypos = ss->maxbuf;
162     ss->maxbuf = newsize;
163   }
164   ss->buf[emptypos].startslot = sslot;
165   ss->buf[emptypos].nslots = nslots;
166   ss->emptyslots += nslots;
167   return;
168 }
169
170 /* grab a slotblock with specified range of blocks
171  * this is different from get_slots, since it pre-specifies the
172  * slots to be grabbed.
173  */
174 static void
175 grab_slots(slotset *ss, int sslot, int nslots)
176 {
177   int pos, eslot, e;
178   eslot = sslot + nslots;
179   for (pos=0; pos < (ss->maxbuf); pos++)
180   {
181     if (ss->buf[pos].nslots == 0)
182       continue;
183     e = ss->buf[pos].startslot + ss->buf[pos].nslots;
184     if(sslot >= ss->buf[pos].startslot && eslot <= e)
185     {
186       int old_nslots;
187       old_nslots = ss->buf[pos].nslots;
188       ss->buf[pos].nslots = sslot - ss->buf[pos].startslot;
189       ss->emptyslots -= (old_nslots - ss->buf[pos].nslots);
190       add_slots(ss, sslot + nslots, old_nslots - ss->buf[pos].nslots - nslots);
191       return;
192     }
193   }
194   CmiAbort("requested a non-existent slotblock\n");
195 }
196
197 /*
198  * Frees slot by adding it to one of the blocks of empty slots.
199  * this slotblock is one which is contiguous with the slots to be freed.
200  * if it cannot find such a slotblock, it creates a new slotblock.
201  * If the buffer fills up, it adds up extra buffer space.
202  */
203 static void
204 free_slots(slotset *ss, int sslot, int nslots)
205 {
206   int pos;
207   /* eslot is the ending slot of the block to be freed */
208   int eslot = sslot + nslots;
209   for (pos=0; pos < (ss->maxbuf); pos++)
210   {
211     int e = ss->buf[pos].startslot + ss->buf[pos].nslots;
212     if (ss->buf[pos].nslots == 0)
213       continue;
214     /* e is the ending slot of pos'th slotblock */
215     if (e == sslot) /* append to the current slotblock */
216     {
217             ss->buf[pos].nslots += nslots;
218       ss->emptyslots += nslots;
219             return;
220     }
221     if(eslot == ss->buf[pos].startslot) /* prepend to the current slotblock */
222     {
223             ss->buf[pos].startslot = sslot;
224             ss->buf[pos].nslots += nslots;
225       ss->emptyslots += nslots;
226             return;
227     }
228   }
229   /* if we are here, it means we could not find a slotblock that the */
230   /* block to be freed was combined with. */
231   add_slots(ss, sslot, nslots);
232 }
233
234 /*
235  * destroys slotset-- currently unused
236 static void
237 delete_slotset(slotset* ss)
238 {
239   free_reentrant(ss->buf);
240   free_reentrant(ss);
241 }
242  */
243
244 #if CMK_THREADS_DEBUG
245 static void
246 print_slots(slotset *ss)
247 {
248   int i;
249   CmiPrintf("[%d] maxbuf = %d\n", CmiMyPe(), ss->maxbuf);
250   CmiPrintf("[%d] emptyslots = %d\n", CmiMyPe(), ss->emptyslots);
251   for(i=0;i<ss->maxbuf;i++) {
252     if(ss->buf[i].nslots)
253       CmiPrintf("[%d] (%d, %d) \n", CmiMyPe(), ss->buf[i].startslot, 
254           ss->buf[i].nslots);
255   }
256 }
257 #else
258 #  define print_slots(ss) /*empty*/
259 #endif
260
261 /*This version of the allocate/deallocate calls are used if the 
262 real mmap versions are disabled.*/
263 static int disabled_map_warned=0;
264 static void *disabled_map(int nBytes) 
265 {
266         if (!disabled_map_warned) {
267                 disabled_map_warned=1;
268                 if (CmiMyPe()==0)
269                         CmiError("charm isomalloc.c> Warning: since mmap() doesn't work,"
270                         " you won't be able to migrate threads\n");
271         }
272         return malloc(nBytes);
273 }
274 static void disabled_unmap(void *bk) {
275         free(bk);
276 }
277
278 /*Turn off isomalloc memory, for the given reason*/
279 static void disable_isomalloc(const char *why)
280 {
281     isomallocStart=NULL;
282     isomallocEnd=NULL;
283 #if CMK_THREADS_DEBUG
284     CmiPrintf("[%d] isomalloc.c> Disabling isomalloc because %s\n",CmiMyPe(),why);
285 #endif
286 }
287
288 #if ! CMK_HAS_MMAP
289 /****************** Manipulate memory map (Win32 non-version) *****************/
290 static void *call_mmap_fixed(void *addr,size_t len) {
291         CmiAbort("isomalloc.c: mmap_fixed should never be called here.");
292 }
293 static void *call_mmap_anywhere(size_t len) {
294         CmiAbort("isomalloc.c: mmap_anywhere should never be called here.");
295 }
296 static void call_munmap(void *addr,size_t len) {
297         CmiAbort("isomalloc.c: munmap should never be called here.");
298 }
299
300 static int 
301 init_map(char **argv)
302 {
303   return 0; /*Isomalloc never works without mmap*/
304 }
305 #else /* CMK_HAS_MMAP */
306 /****************** Manipulate memory map (UNIX version) *****************/
307 #include <sys/types.h>
308 #include <sys/mman.h>
309 #include <sys/stat.h>
310 #include <fcntl.h>
311
312 #if !CMK_HAS_MMAP_ANON
313 CpvStaticDeclare(int, zerofd); /*File descriptor for /dev/zero, for mmap*/
314 #endif
315
316 /**
317  * Maps this address with these flags.
318  */
319 static void *call_mmap(void *addr,size_t len, int flags) {
320   void *ret=mmap(addr, len, PROT_READ|PROT_WRITE,
321 #if CMK_HAS_MMAP_ANON
322             flags|MAP_PRIVATE|MAP_ANON,-1,
323 #else
324             flags|MAP_PRIVATE,CpvAccess(zerofd),
325 #endif
326             0);
327   if (ret==((void*)(-1))) return (void *)0; /* all-ones means failure */
328   else return ret;
329 }
330 static void *call_mmap_fixed(void *addr,size_t len) {
331         return call_mmap(addr,len,MAP_FIXED);
332 }
333 static void *call_mmap_anywhere(size_t len) {
334         return call_mmap((void *)0,len,0);
335 }
336
337 /* Unmaps this address range */
338 static void call_munmap(void *addr,size_t len) {
339   int retval;
340   if (addr == 0) return; /* NULL address is never mapped */ 
341   retval = munmap(addr, len);
342   if (retval==(-1))
343     CmiAbort("munmap call failed to deallocate requested memory.\n");
344 }
345
346 static int 
347 init_map(char **argv)
348 {
349 #if CMK_HAS_MMAP_ANON
350   /*Don't need /dev/zero*/
351 #else
352   CpvInitialize(int, zerofd);  
353   CpvAccess(zerofd) = open("/dev/zero", O_RDWR);
354   if(CpvAccess(zerofd)<0)
355     return 0; /* Cannot open /dev/zero or use MMAP_ANON, so can't mmap memory */
356 #endif
357   return 1;
358 }
359
360 #endif /* UNIX memory map */
361
362
363 /**
364  * maps the virtual memory associated with slot using mmap
365  */
366 static CmiIsomallocBlock *
367 map_slots(int slot, int nslots)
368 {
369   void *pa;
370   void *addr=slot2addr(slot);
371   pa = call_mmap_fixed(addr, slotsize*nslots);
372   
373   if (pa==NULL) 
374   { /*Map just failed completely*/
375 #if CMK_THREADS_DEBUG
376     perror("mmap failed");
377     CmiPrintf("[%d] tried to mmap %p, but encountered error\n",CmiMyPe(),addr);
378 #endif
379     return NULL;
380   }
381   if (pa != addr)
382   { /*Map worked, but gave us back the wrong place*/
383 #if CMK_THREADS_DEBUG
384     CmiPrintf("[%d] tried to mmap %p, but got %p back\n",CmiMyPe(),addr,pa);
385 #endif
386     call_munmap(addr,slotsize*nslots);
387     return NULL;
388   }
389 #if CMK_THREADS_DEBUG
390   CmiPrintf("[%d] mmap'd slots %d-%d to address %p\n",CmiMyPe(),
391             slot,slot+nslots-1,addr);
392 #endif
393   return (CmiIsomallocBlock *)pa;
394 }
395
396 /*
397  * unmaps the virtual memory associated with slot using munmap
398  */
399 static void
400 unmap_slots(int slot, int nslots)
401 {
402   void *addr=slot2addr(slot);
403   call_munmap(addr, slotsize*nslots);
404 #if CMK_THREADS_DEBUG
405   CmiPrintf("[%d] munmap'd slots %d-%d from address %p\n",CmiMyPe(),
406             slot,slot+nslots-1,addr);
407 #endif
408 }
409
410 static void map_failed(int s,int n)
411 {
412   void *addr=slot2addr(s);
413   CmiError("charm isomalloc.c> map failed to allocate %d bytes at %p.\n",
414       slotsize*n, addr);
415   CmiAbort("Exiting\n");
416 }
417
418
419
420 /************ Address space voodoo: find free address range **********/
421
422 CpvStaticDeclare(slotset *, myss); /*My managed slots*/
423
424 /*This struct describes a range of virtual addresses*/
425 typedef struct {
426   char *start; /*First byte of region*/
427   memRange_t len; /*Number of bytes in region*/
428   const char *type; /*String describing memory in region (debugging only)*/
429 } memRegion_t;
430
431 /*Estimate the top of the current stack*/
432 static void *__cur_stack_frame(void)
433 {
434   char __dummy;
435   void *top_of_stack=(void *)&__dummy;
436   return top_of_stack;
437 }
438 /*Estimate the location of the static data region*/
439 static void *__static_data_loc(void)
440 {
441   static char __dummy;
442   return (void *)&__dummy;
443 }
444
445 /*Pointer comparison is in these subroutines, because
446   comparing arbitrary pointers is nonportable and tricky.
447 */
448 static int pointer_lt(const char *a,const char *b) {
449         return ((memRange_t)a)<((memRange_t)b);
450 }
451 static int pointer_ge(const char *a,const char *b) {
452         return ((memRange_t)a)>=((memRange_t)b);
453 }
454
455 static char *pmin(char *a,char *b) {return pointer_lt(a,b)?a:b;}
456 static char *pmax(char *a,char *b) {return pointer_lt(a,b)?b:a;}
457
458 const static memRange_t meg=1024u*1024u; /*One megabyte*/
459 const static memRange_t gig=1024u*1024u*1024u; /*One gigabyte*/
460
461 /*Check if this memory location is usable.  
462   If not, return 1.
463 */
464 static int bad_location(char *loc) {
465   void *addr;
466   addr=call_mmap_fixed(loc,slotsize);
467   if (addr==NULL) {
468 #if CMK_THREADS_DEBUG
469     CmiPrintf("[%d] Skipping unmappable space at %p\n",CmiMyPe(),loc);
470 #endif
471     return 1; /*No good*/
472   }
473   call_munmap(addr,slotsize);
474   return 0; /*This works*/
475 }
476
477 /* Split this range up into n pieces, returning the size of each piece */
478 static memRange_t divide_range(memRange_t len,int n) {
479         return (len+1)/n;
480 }
481
482 /* Return if this memory region has *any* good parts. */
483 static int partially_good(char *start,memRange_t len,int n) {
484   int i;
485   memRange_t quant=divide_range(len,n);
486   for (i=0;i<n;i++)
487     if (!bad_location(start+i*quant)) return 1; /* it's got some good parts */
488   return 0; /* all locations are bad */
489 }
490
491 /* Return if this memory region is usable at n samples.  
492 */
493 static int good_range(char *start,memRange_t len,int n) {
494   int i;
495   memRange_t quant=divide_range(len,n);
496   for (i=0;i<n;i++)
497     if (bad_location(start+i*quant)) return 0; /* it's got some bad parts */
498   /* It's all good: */
499   return 1;
500 }
501
502 /*Check if this entire memory range, or some subset 
503   of the range, is usable.  If so, write it into max.
504 */
505 static void check_range(char *start,char *end,memRegion_t *max)
506 {
507   memRange_t len;
508   char *initialStart=start, *initialEnd=end;
509
510   if (start>=end) return; /*Ran out of hole*/
511   len=(memRange_t)end-(memRange_t)start;
512   
513   if (len/gig>64u) { /* This is an absurd amount of space-- cut it down, for safety */
514      start+=16u*gig;
515      end=start+32u*gig;
516      len=(memRange_t)end-(memRange_t)start;  
517   }
518   if (len<=max->len) return; /*It's too short already!*/
519 #if CMK_THREADS_DEBUG
520   CmiPrintf("[%d] Checking at %p - %p\n",CmiMyPe(),start,end);
521 #endif
522   
523   /* Check the middle of the range */
524   if (!good_range(start,len,256)) {
525     /* Try to split into subranges: */
526     int i,n=2;
527 #if CMK_THREADS_DEBUG
528     CmiPrintf("[%d] Trying to split bad address space at %p - %p...\n",CmiMyPe(),start,end);
529 #endif
530     len=divide_range(len,n);
531     for (i=0;i<n;i++) {
532         char *cur=start+i*len;
533         if (partially_good(cur,len,16))
534            check_range(cur,cur+len,max);
535     }
536     return; /* Hopefully one of the subranges will be any good */
537   }
538   else /* range is good */
539   { 
540 #if CMK_THREADS_DEBUG
541     CmiPrintf("[%d] Address space at %p - %p is largest\n",CmiMyPe(),start,end);
542 #endif
543
544     /*If we got here, we're the new largest usable range*/
545     max->len=len;
546     max->start=start;
547     max->type="Unused";
548   }
549 }
550
551 /*Find the first available memory region of at least the
552   given size not touching any data in the used list.
553  */
554 static memRegion_t find_free_region(memRegion_t *used,int nUsed,int atLeast) 
555 {
556   memRegion_t max;
557   int i,j;  
558
559   max.start=0; 
560   max.len=atLeast;
561   /*Find the largest hole between regions*/
562   for (i=0;i<nUsed;i++) {
563     /*Consider a hole starting at the end of region i*/
564     char *holeStart=used[i].start+used[i].len;
565     char *holeEnd=(void *)(-1);
566     
567     /*Shrink the hole by all others*/ 
568     for (j=0;j<nUsed && pointer_lt(holeStart,holeEnd);j++) {
569       if (pointer_lt(used[j].start,holeStart)) 
570         holeStart=pmax(holeStart,used[j].start+used[j].len);
571       else if (pointer_lt(used[j].start,holeEnd)) 
572         holeEnd=pmin(holeEnd,used[j].start);
573     } 
574
575     check_range(holeStart,holeEnd,&max);
576   }
577
578   return max; 
579 }
580
581 /*
582 By looking at the address range carefully, try to find 
583 the largest usable free region on the machine.
584 */
585 static int find_largest_free_region(memRegion_t *destRegion) {
586     char *staticData =(char *) __static_data_loc();
587     char *code = (char *)&find_free_region;
588     char *threadData = (char *)&errno;
589     char *codeDll = (char *)fprintf;
590     char *heapLil = (char*) malloc(1);
591     char *heapBig = (char*) malloc(6*meg);
592     char *stack = (char *)__cur_stack_frame();
593     size_t mmapAnyLen = 1*meg;
594     char *mmapAny = (char*) call_mmap_anywhere(mmapAnyLen);
595
596     int i,nRegions=8;
597     memRegion_t regions[10]; /*used portions of address space*/
598     memRegion_t freeRegion; /*Largest unused block of address space*/
599
600 /*Mark off regions of virtual address space as ususable*/
601     regions[0].type="NULL";
602     regions[0].start=NULL; regions[0].len=16u*meg;
603     
604     regions[1].type="Static program data";
605     regions[1].start=staticData; regions[1].len=256u*meg;
606     
607     regions[2].type="Program executable code";
608     regions[2].start=code; regions[2].len=256u*meg;
609     
610     regions[3].type="Heap (small blocks)";
611     regions[3].start=heapLil; regions[3].len=1u*gig;
612     
613     regions[4].type="Heap (large blocks)";
614     regions[4].start=heapBig; regions[4].len=1u*gig;
615     
616     regions[5].type="Stack space";
617     regions[5].start=stack; regions[5].len=256u*meg;
618
619     regions[6].type="Program dynamically linked code";
620     regions[6].start=codeDll; regions[6].len=256u*meg; 
621
622     regions[7].type="Result of a non-fixed call to mmap";
623     regions[7].start=mmapAny; regions[7].len=1u*gig; 
624
625     regions[8].type="Thread private data";
626     regions[8].start=threadData; regions[8].len=256u*meg; 
627
628     _MEMCHECK(heapBig); free(heapBig);
629     _MEMCHECK(heapLil); free(heapLil); 
630     call_munmap(mmapAny,mmapAnyLen);
631     
632     /*Align each memory region*/
633     for (i=0;i<nRegions;i++) {
634       memRegion_t old=regions[i];
635       memRange_t p=(memRange_t)regions[i].start;
636       p&=~(regions[i].len-1); /*Round start down to a len-boundary (mask off low bits)*/
637       regions[i].start=(char *)p;
638 #if CMK_THREADS_DEBUG
639       CmiPrintf("[%d] Memory map: %p - %p %s (at %p)\n",CmiMyPe(),
640               regions[i].start,regions[i].start+regions[i].len,
641               regions[i].type, old.start);
642 #endif
643     }
644     
645     /*Find a large, unused region in this map: */
646     freeRegion=find_free_region(regions,nRegions,(512u)*meg);
647     
648     if (freeRegion.start==0) 
649     { /*No free address space-- disable isomalloc:*/
650       return 0;
651     }
652     else /* freeRegion is valid */
653     {
654       *destRegion=freeRegion;
655       
656       return 1;
657     }
658 }
659
660 static void init_ranges(char **argv)
661 {
662   /*Largest value a signed int can hold*/
663   memRange_t intMax=(((memRange_t)1)<<(sizeof(int)*8-1))-1;
664
665   /*Round slot size up to nearest page size*/
666   slotsize=16*1024;
667   slotsize=(slotsize+CMK_MEMORY_PAGESIZE-1) & ~(CMK_MEMORY_PAGESIZE-1);
668 #if CMK_THREADS_DEBUG
669   CmiPrintf("[%d] Using slotsize of %d\n", CmiMyPe(), slotsize);
670 #endif
671
672   if (CmiMyRank()==0 && numslots==0)
673   { /* Find the largest unused region of virtual address space */
674       memRegion_t freeRegion;
675       freeRegion.len=0u;
676 #ifdef CMK_MMAP_START_ADDRESS /* Hardcoded start address, for machines where automatic fails */
677       freeRegion.start=CMK_MMAP_START_ADDRESS;
678       freeRegion.len=CMK_MMAP_LENGTH_MEGS*meg;
679 #endif
680       if (freeRegion.len==0u) find_largest_free_region(&freeRegion);
681       
682       /*Make sure our largest slot number doesn't overflow an int:*/
683       if (freeRegion.len/slotsize>intMax)
684         freeRegion.len=intMax*slotsize;
685       
686       if (freeRegion.len==0u) {
687         disable_isomalloc("no free virtual address space");
688       }
689       else /* freeRegion.len>0, so can isomalloc */
690       {
691 #if CMK_THREADS_DEBUG
692         CmiPrintf("[%d] Isomalloc memory region: %p - %p (%d megs)\n",CmiMyPe(),
693               freeRegion.start,freeRegion.start+freeRegion.len,
694               freeRegion.len/meg);
695 #endif
696         
697         /*Isomalloc covers entire unused region*/
698         isomallocStart=freeRegion.start;
699         isomallocEnd=freeRegion.start+freeRegion.len;
700         numslots=(freeRegion.len/slotsize)/CmiNumPes();
701         
702 #if CMK_THREADS_DEBUG
703         CmiPrintf("[%d] Can isomalloc up to %lu megs per pe\n",CmiMyPe(),
704               ((memRange_t)numslots)*slotsize/meg);
705 #endif
706       }
707   }
708   /*SMP Mode: wait here for rank 0 to initialize numslots so we can set up myss*/
709   CmiNodeAllBarrier(); 
710   
711   if (isomallocStart!=NULL) {
712     CpvInitialize(slotset *, myss);
713     CpvAccess(myss) = new_slotset(pe2slot(CmiMyPe()), numslots);
714   }
715 }
716
717
718 /************* Communication: for grabbing/freeing remote slots *********/
719 typedef struct _slotmsg
720 {
721   char cmicore[CmiMsgHeaderSizeBytes];
722   int pe; /*Source processor*/
723   int slot; /*First requested slot*/
724   int nslots; /*Number of requested slots*/
725 } slotmsg;
726
727 static slotmsg *prepare_slotmsg(int slot,int nslots)
728 {
729         slotmsg *m=(slotmsg *)CmiAlloc(sizeof(slotmsg));
730         m->pe=CmiMyPe();
731         m->slot=slot;
732         m->nslots=nslots;
733         return m;
734 }
735
736 static void grab_remote(slotmsg *msg)
737 {
738         grab_slots(CpvAccess(myss),msg->slot,msg->nslots);
739         CmiFree(msg);
740 }
741
742 static void free_remote(slotmsg *msg)
743 {
744         free_slots(CpvAccess(myss),msg->slot,msg->nslots);
745         CmiFree(msg);
746 }
747 static int grab_remote_idx, free_remote_idx;
748
749 struct slotOP {
750         /*Function pointer to perform local operation*/
751         void (*local)(slotset *ss,int s,int n);
752         /*Index to perform remote operation*/
753         int remote;
754 };
755 typedef struct slotOP slotOP;
756 static slotOP grabOP,freeOP;
757
758 static void init_comm(char **argv)
759 {
760         grab_remote_idx=CmiRegisterHandler((CmiHandler)grab_remote);
761         free_remote_idx=CmiRegisterHandler((CmiHandler)free_remote);    
762         grabOP.local=grab_slots;
763         grabOP.remote=grab_remote_idx;
764         freeOP.local=free_slots;
765         freeOP.remote=free_remote_idx;  
766 }
767
768 /*Apply the given operation to the given slots which
769   lie on the given processor.*/
770 static void one_slotOP(const slotOP *op,int pe,int s,int n)
771 {
772 /*Shrink range to only those covered by this processor*/
773         /*First and last slot for this processor*/
774         int p_s=pe2slot(pe), p_e=pe2slot(pe+1);
775         int e=s+n;
776         if (s<p_s) s=p_s;
777         if (e>p_e) e=p_e;
778         n=e-s;
779
780 /*Send off range*/
781         if (pe==CmiMyPe()) 
782                 op->local(CpvAccess(myss),s,n);
783         else 
784         {/*Remote request*/
785                 slotmsg *m=prepare_slotmsg(s,e);
786                 CmiSetHandler(m, freeOP.remote);
787                 CmiSyncSendAndFree(pe,sizeof(slotmsg),m);
788         }
789 }
790
791 /*Apply the given operation to all slots in the range [s, s+n) 
792 After a restart from checkpoint, a slotset can cross an 
793 arbitrary set of processors.
794 */
795 static void all_slotOP(const slotOP *op,int s,int n)
796 {
797         int spe=slot2pe(s), epe=slot2pe(s+n-1);
798         int pe;
799         for (pe=spe; pe<=epe; pe++)
800                 one_slotOP(op,pe,s,n);
801 }
802
803 /************** External interface ***************/
804 void *CmiIsomalloc(int size)
805 {
806         int s,n;
807         CmiIsomallocBlock *blk;
808         if (isomallocStart==NULL) return disabled_map(size);
809         n=length2slots(size);
810         /*Always satisfy mallocs with local slots:*/
811         s=get_slots(CpvAccess(myss),n);
812         if (s==-1) {
813                 CmiError("Not enough address space left on processor %d to isomalloc %d bytes!\n",
814                          CmiMyPe(),size);
815                 CmiAbort("Out of virtual address space for isomalloc");
816         }
817         grab_slots(CpvAccess(myss),s,n);
818         blk=map_slots(s,n);
819         if (!blk) map_failed(s,n);
820         blk->slot=s;
821         blk->length=size;
822         return block2pointer(blk);
823 }
824
825 void CmiIsomallocPup(pup_er p,void **blockPtrPtr)
826 {
827         CmiIsomallocBlock *blk;
828         int s,n,length;
829         if (isomallocStart==NULL) CmiAbort("isomalloc is disabled-- cannot use IsomallocPup");
830
831         if (!pup_isUnpacking(p)) 
832         { /*We have an existing block-- unpack start slot & length*/
833                 blk=pointer2block(*blockPtrPtr);
834                 s=blk->slot;
835                 length=blk->length;
836         }
837         
838         pup_int(p,&s);
839         pup_int(p,&length);
840         n=length2slots(length);
841         
842         if (pup_isUnpacking(p)) 
843         { /*Must allocate a new block in its old location*/
844                 if (pup_isUserlevel(p))
845                         /*Checkpoint: must grab old slots (even remote!)*/
846                         all_slotOP(&grabOP,s,n);
847                 blk=map_slots(s,n);
848                 if (!blk) map_failed(s,n);
849                 blk->slot=s;
850                 blk->length=length;
851                 *blockPtrPtr=block2pointer(blk);
852         }
853         
854         /*Pup the allocated data*/
855         pup_bytes(p,*blockPtrPtr,length);
856         
857         if (pup_isDeleting(p)) 
858         { /*Unmap old slots, but do not mark as free*/
859                 unmap_slots(s,n);
860                 *blockPtrPtr=NULL; /*Zero out user's pointer*/
861         }
862 }
863
864 void CmiIsomallocFree(void *blockPtr)
865 {
866         if (isomallocStart==NULL) {
867                 disabled_unmap(blockPtr);
868         }
869         else if (blockPtr!=NULL)
870         {
871                 CmiIsomallocBlock *blk=pointer2block(blockPtr);
872                 int s=blk->slot, n=length2slots(blk->length);
873                 unmap_slots(s,n);
874                 /*Mark used slots as free*/
875                 all_slotOP(&freeOP,s,n);
876         }
877 }
878 int   CmiIsomallocLength(void *block)
879 {
880         return pointer2block(block)->length;
881 }
882
883 /*Return true if this address is in the region managed by isomalloc*/
884 int CmiIsomallocInRange(void *addr)
885 {
886         if (isomallocStart==NULL) return 0; /* There is no range we manage! */
887         return pointer_ge((char *)addr,isomallocStart) && 
888                pointer_lt((char*)addr,isomallocEnd);
889 }
890
891 void CmiIsomallocInit(char **argv)
892 {
893 #if CMK_NO_ISO_MALLOC
894   disable_isomalloc("isomalloc disabled by conv-mach");
895 #else
896   init_comm(argv);
897   if (!init_map(argv)) {
898     disable_isomalloc("mmap() does not work");
899   }
900   else {
901     if (read_randomflag() == 1) {    /* randomization stack pointer */
902       if (CmiMyPe() == 0)
903         printf("Charm warning> Randomization of stack pointer is turned on in Kernel, run 'echo 0 > /proc/sys/kernel/randomize_va_space' as root to disable it. Thread migration may not work! \n");
904     }
905     init_ranges(argv);
906   }
907 #endif
908 }
909
910 /***************** BlockList interface *********
911 This was moved here from memory-isomalloc.c when it 
912 was realized that a list-of-isomalloc'd-blocks is useful for
913 more than just isomalloc heaps.
914 */
915
916 typedef CmiIsomallocBlockList Slot;
917
918 /*Convert a slot to a user address*/
919 static char *Slot_toUser(Slot *s) {return (char *)(s+1);}
920 static Slot *Slot_fmUser(void *s) {return ((Slot *)s)-1;}
921
922
923 /*Build a new blockList.*/
924 CmiIsomallocBlockList *CmiIsomallocBlockListNew(void)
925 {
926         CmiIsomallocBlockList *ret;
927         ret=(CmiIsomallocBlockList *)CmiIsomalloc(sizeof(*ret));
928         ret->next=ret; /*1-entry circular linked list*/
929         ret->prev=ret;
930         return ret;
931 }
932
933 /*Pup all the blocks in this list.  This amounts to two circular
934 list traversals.  Because everything's isomalloc'd, we don't even
935 have to restore the pointers-- they'll be restored automatically!
936 */
937 void CmiIsomallocBlockListPup(pup_er p,CmiIsomallocBlockList **lp)
938 {
939         int i,nBlocks=0;
940         Slot *cur=NULL, *start=*lp;
941 #if 0 /*#ifndef CMK_OPTIMIZE*/
942         if (CpvAccess(isomalloc_blocklist)!=NULL)
943                 CmiAbort("Called CmiIsomallocBlockListPup while a blockList is active!\n"
944                         "You should swap out the active blocklist before pupping.\n");
945 #endif
946         /*Count the number of blocks in the list*/
947         if (!pup_isUnpacking(p)) {
948                 nBlocks=1; /*<- Since we have to skip the start block*/
949                 for (cur=start->next; cur!=start; cur=cur->next) 
950                         nBlocks++;
951                 /*Prepare for next trip around list:*/
952                 cur=start;
953         }
954         pup_int(p,&nBlocks);
955         
956         /*Pup each block in the list*/
957         for (i=0;i<nBlocks;i++) {
958                 void *newBlock=cur;
959                 if (!pup_isUnpacking(p)) 
960                 { /*While packing, we traverse the list to find our blocks*/
961                         cur=cur->next;
962                 }
963                 CmiIsomallocPup(p,&newBlock);
964                 if (i==0 && pup_isUnpacking(p))
965                         *lp=(Slot *)newBlock;
966         }
967         if (pup_isDeleting(p))
968                 *lp=NULL;
969 }
970
971 /*Delete all the blocks in this list.*/
972 void CmiIsomallocBlockListDelete(CmiIsomallocBlockList *l)
973 {
974         Slot *start=l;
975         Slot *cur=start;
976         if (cur==NULL) return; /*Already deleted*/
977         do {
978                 Slot *doomed=cur;
979                 cur=cur->next; /*Have to stash next before deleting cur*/
980                 CmiIsomallocFree(doomed);
981         } while (cur!=start);
982 }
983
984 /*Allocate a block from this blockList*/
985 void *CmiIsomallocBlockListMalloc(CmiIsomallocBlockList *l,int nBytes)
986 {
987         Slot *n; /*Newly created slot*/
988         n=(Slot *)CmiIsomalloc(sizeof(Slot)+nBytes);
989         /*Link the new block into the circular blocklist*/
990         n->prev=l;
991         n->next=l->next;
992         l->next->prev=n;
993         l->next=n;
994         return Slot_toUser(n);
995 }
996
997 /*Remove this block from its list and memory*/
998 void CmiIsomallocBlockListFree(void *block)
999 {
1000         Slot *n=Slot_fmUser(block);
1001 #if DOHEAPCHECK
1002         if (n->prev->next!=n || n->next->prev!=n) 
1003                 CmiAbort("Heap corruption detected in isomalloc block list header!\n"
1004                         "  Run with ++debug and look for writes to negative array indices");
1005 #endif
1006         /*Link ourselves out of the blocklist*/
1007         n->prev->next=n->next;
1008         n->next->prev=n->prev;
1009         CmiIsomallocFree(n);
1010 }
1011
1012
1013
1014
1015