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