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