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