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