8806f7a12c7885bb38760aca764d907185adae54
[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 *codeDll = (char *)&errno;
573     char *heapLil = (char*) malloc(1);
574     char *heapBig = (char*) malloc(6*meg);
575     char *stack = (char *)__cur_stack_frame();
576     size_t mmapAnyLen = 1*meg;
577     char *mmapAny = (char*) call_mmap_anywhere(mmapAnyLen);
578
579     int i,nRegions=8;
580     memRegion_t regions[10]; /*used portions of address space*/
581     memRegion_t freeRegion; /*Largest unused block of address space*/
582
583 /*Mark off regions of virtual address space as ususable*/
584     regions[0].type="NULL";
585     regions[0].start=NULL; regions[0].len=16u*meg;
586     
587     regions[1].type="Static program data";
588     regions[1].start=staticData; regions[1].len=256u*meg;
589     
590     regions[2].type="Program executable code";
591     regions[2].start=code; regions[2].len=256u*meg;
592     
593     regions[3].type="Heap (small blocks)";
594     regions[3].start=heapLil; regions[3].len=1u*gig;
595     
596     regions[4].type="Heap (large blocks)";
597     regions[4].start=heapBig; regions[4].len=1u*gig;
598     
599     regions[5].type="Stack space";
600     regions[5].start=stack; regions[5].len=256u*meg;
601
602     regions[6].type="Program dynamically linked code";
603     regions[6].start=codeDll; regions[6].len=256u*meg; 
604
605     regions[7].type="Result of a non-fixed call to mmap";
606     regions[7].start=mmapAny; regions[7].len=1u*gig; 
607
608     _MEMCHECK(heapBig); free(heapBig);
609     _MEMCHECK(heapLil); free(heapLil); 
610     call_munmap(mmapAny,mmapAnyLen);
611     
612     /*Align each memory region*/
613     for (i=0;i<nRegions;i++) {
614       memRegion_t old=regions[i];
615       memRange_t p=(memRange_t)regions[i].start;
616       p&=~(regions[i].len-1); /*Round start down to a len-boundary (mask off low bits)*/
617       regions[i].start=(char *)p;
618 #if CMK_THREADS_DEBUG
619       CmiPrintf("[%d] Memory map: %p - %p %s (at %p)\n",CmiMyPe(),
620               regions[i].start,regions[i].start+regions[i].len,
621               regions[i].type, old.start);
622 #endif
623     }
624     
625     /*Find a large, unused region in this map: */
626     freeRegion=find_free_region(regions,nRegions,(512u)*meg);
627     
628     if (freeRegion.start==0) 
629     { /*No free address space-- disable isomalloc:*/
630       return 0;
631     }
632     else /* freeRegion is valid */
633     {
634       *destRegion=freeRegion;
635       
636       return 1;
637     }
638 }
639
640 static void init_ranges(char **argv)
641 {
642   /*Largest value a signed int can hold*/
643   memRange_t intMax=(((memRange_t)1)<<(sizeof(int)*8-1))-1;
644
645   /*Round slot size up to nearest page size*/
646   slotsize=16*1024;
647   slotsize=(slotsize+CMK_MEMORY_PAGESIZE-1) & ~(CMK_MEMORY_PAGESIZE-1);
648 #if CMK_THREADS_DEBUG
649   CmiPrintf("[%d] Using slotsize of %d\n", CmiMyPe(), slotsize);
650 #endif
651
652   if (CmiMyRank()==0 && numslots==0)
653   { /* Find the largest unused region of virtual address space */
654       memRegion_t freeRegion;
655       freeRegion.len=0u;
656 #ifdef CMK_MMAP_START_ADDRESS /* Hardcoded start address, for machines where automatic fails */
657       freeRegion.start=CMK_MMAP_START_ADDRESS;
658       freeRegion.len=CMK_MMAP_LENGTH_MEGS*meg;
659 #endif
660       if (freeRegion.len==0u) find_largest_free_region(&freeRegion);
661       
662       /*Make sure our largest slot number doesn't overflow an int:*/
663       if (freeRegion.len/slotsize>intMax)
664         freeRegion.len=intMax*slotsize;
665       
666       if (freeRegion.len==0u) {
667         disable_isomalloc("no free virtual address space");
668       }
669       else /* freeRegion.len>0, so can isomalloc */
670       {
671 #if CMK_THREADS_DEBUG
672         CmiPrintf("[%d] Isomalloc memory region: %p - %p (%d megs)\n",CmiMyPe(),
673               freeRegion.start,freeRegion.start+freeRegion.len,
674               freeRegion.len/meg);
675 #endif
676         
677         /*Isomalloc covers entire unused region*/
678         isomallocStart=freeRegion.start;
679         isomallocEnd=freeRegion.start+freeRegion.len;
680         numslots=(freeRegion.len/slotsize)/CmiNumPes();
681         
682 #if CMK_THREADS_DEBUG
683         CmiPrintf("[%d] Can isomalloc up to %lu megs per pe\n",CmiMyPe(),
684               ((memRange_t)numslots)*slotsize/meg);
685 #endif
686       }
687   }
688   /*SMP Mode: wait here for rank 0 to initialize numslots so we can set up myss*/
689   CmiNodeAllBarrier(); 
690   
691   if (isomallocStart!=NULL) {
692     CpvInitialize(slotset *, myss);
693     CpvAccess(myss) = new_slotset(pe2slot(CmiMyPe()), numslots);
694   }
695 }
696
697
698 /************* Communication: for grabbing/freeing remote slots *********/
699 typedef struct _slotmsg
700 {
701   char cmicore[CmiMsgHeaderSizeBytes];
702   int pe; /*Source processor*/
703   int slot; /*First requested slot*/
704   int nslots; /*Number of requested slots*/
705 } slotmsg;
706
707 static slotmsg *prepare_slotmsg(int slot,int nslots)
708 {
709         slotmsg *m=(slotmsg *)CmiAlloc(sizeof(slotmsg));
710         m->pe=CmiMyPe();
711         m->slot=slot;
712         m->nslots=nslots;
713         return m;
714 }
715
716 static void grab_remote(slotmsg *msg)
717 {
718         grab_slots(CpvAccess(myss),msg->slot,msg->nslots);
719         CmiFree(msg);
720 }
721
722 static void free_remote(slotmsg *msg)
723 {
724         free_slots(CpvAccess(myss),msg->slot,msg->nslots);
725         CmiFree(msg);
726 }
727 static int grab_remote_idx, free_remote_idx;
728
729 struct slotOP {
730         /*Function pointer to perform local operation*/
731         void (*local)(slotset *ss,int s,int n);
732         /*Index to perform remote operation*/
733         int remote;
734 };
735 typedef struct slotOP slotOP;
736 static slotOP grabOP,freeOP;
737
738 static void init_comm(char **argv)
739 {
740         grab_remote_idx=CmiRegisterHandler((CmiHandler)grab_remote);
741         free_remote_idx=CmiRegisterHandler((CmiHandler)free_remote);    
742         grabOP.local=grab_slots;
743         grabOP.remote=grab_remote_idx;
744         freeOP.local=free_slots;
745         freeOP.remote=free_remote_idx;  
746 }
747
748 /*Apply the given operation to the given slots which
749   lie on the given processor.*/
750 static void one_slotOP(const slotOP *op,int pe,int s,int n)
751 {
752 /*Shrink range to only those covered by this processor*/
753         /*First and last slot for this processor*/
754         int p_s=pe2slot(pe), p_e=pe2slot(pe+1);
755         int e=s+n;
756         if (s<p_s) s=p_s;
757         if (e>p_e) e=p_e;
758         n=e-s;
759
760 /*Send off range*/
761         if (pe==CmiMyPe()) 
762                 op->local(CpvAccess(myss),s,n);
763         else 
764         {/*Remote request*/
765                 slotmsg *m=prepare_slotmsg(s,e);
766                 CmiSyncSendAndFree(pe,sizeof(slotmsg),m);
767         }
768 }
769
770 /*Apply the given operation to all slots in the range [s, s+n) 
771 After a restart from checkpoint, a slotset can cross an 
772 arbitrary set of processors.
773 */
774 static void all_slotOP(const slotOP *op,int s,int n)
775 {
776         int spe=slot2pe(s), epe=slot2pe(s+n-1);
777         int pe;
778         for (pe=spe; pe<=epe; pe++)
779                 one_slotOP(op,pe,s,n);
780 }
781
782 /************** External interface ***************/
783 void *CmiIsomalloc(int size)
784 {
785         int s,n;
786         CmiIsomallocBlock *blk;
787         if (isomallocStart==NULL) return disabled_map(size);
788         n=length2slots(size);
789         /*Always satisfy mallocs with local slots:*/
790         s=get_slots(CpvAccess(myss),n);
791         if (s==-1) {
792                 CmiError("Not enough address space left on processor %d to isomalloc %d bytes!\n",
793                          CmiMyPe(),size);
794                 CmiAbort("Out of virtual address space for isomalloc");
795         }
796         grab_slots(CpvAccess(myss),s,n);
797         blk=map_slots(s,n);
798         if (!blk) map_failed(s,n);
799         blk->slot=s;
800         blk->length=size;
801         return block2pointer(blk);
802 }
803
804 void CmiIsomallocPup(pup_er p,void **blockPtrPtr)
805 {
806         CmiIsomallocBlock *blk;
807         int s,n,length;
808         if (isomallocStart==NULL) CmiAbort("isomalloc is disabled-- cannot use IsomallocPup");
809
810         if (!pup_isUnpacking(p)) 
811         { /*We have an existing block-- unpack start slot & length*/
812                 blk=pointer2block(*blockPtrPtr);
813                 s=blk->slot;
814                 length=blk->length;
815         }
816         
817         pup_int(p,&s);
818         pup_int(p,&length);
819         n=length2slots(length);
820         
821         if (pup_isUnpacking(p)) 
822         { /*Must allocate a new block in its old location*/
823                 if (pup_isUserlevel(p))
824                         /*Checkpoint: must grab old slots (even remote!)*/
825                         all_slotOP(&grabOP,s,n);
826                 blk=map_slots(s,n);
827                 if (!blk) map_failed(s,n);
828                 blk->slot=s;
829                 blk->length=length;
830                 *blockPtrPtr=block2pointer(blk);
831         }
832         
833         /*Pup the allocated data*/
834         pup_bytes(p,*blockPtrPtr,length);
835         
836         if (pup_isDeleting(p)) 
837         { /*Unmap old slots, but do not mark as free*/
838                 unmap_slots(s,n);
839                 *blockPtrPtr=NULL; /*Zero out user's pointer*/
840         }
841 }
842
843 void CmiIsomallocFree(void *blockPtr)
844 {
845         if (isomallocStart==NULL) {
846                 disabled_unmap(blockPtr);
847         }
848         else if (blockPtr!=NULL)
849         {
850                 CmiIsomallocBlock *blk=pointer2block(blockPtr);
851                 int s=blk->slot, n=length2slots(blk->length);
852                 unmap_slots(s,n);
853                 /*Mark used slots as free*/
854                 all_slotOP(&freeOP,s,n);
855         }
856 }
857 int   CmiIsomallocLength(void *block)
858 {
859         return pointer2block(block)->length;
860 }
861
862 /*Return true if this address is in the region managed by isomalloc*/
863 int CmiIsomallocInRange(void *addr)
864 {
865         if (isomallocStart==NULL) return 0; /* There is no range we manage! */
866         return pointer_ge((char *)addr,isomallocStart) && 
867                pointer_lt((char*)addr,isomallocEnd);
868 }
869
870 void CmiIsomallocInit(char **argv)
871 {
872 #if CMK_NO_ISO_MALLOC
873   disable_isomalloc("isomalloc disabled by conv-mach");
874 #else
875   init_comm(argv);
876   if (!init_map(argv)) {
877     disable_isomalloc("mmap() does not work");
878   }
879   else {
880     init_ranges(argv);
881   }
882 #endif
883 }
884
885 /***************** BlockList interface *********
886 This was moved here from memory-isomalloc.c when it 
887 was realized that a list-of-isomalloc'd-blocks is useful for
888 more than just isomalloc heaps.
889 */
890
891 typedef CmiIsomallocBlockList Slot;
892
893 /*Convert a slot to a user address*/
894 static char *Slot_toUser(Slot *s) {return (char *)(s+1);}
895 static Slot *Slot_fmUser(void *s) {return ((Slot *)s)-1;}
896
897
898 /*Build a new blockList.*/
899 CmiIsomallocBlockList *CmiIsomallocBlockListNew(void)
900 {
901         CmiIsomallocBlockList *ret;
902         ret=(CmiIsomallocBlockList *)CmiIsomalloc(sizeof(*ret));
903         ret->next=ret; /*1-entry circular linked list*/
904         ret->prev=ret;
905         return ret;
906 }
907
908 /*Pup all the blocks in this list.  This amounts to two circular
909 list traversals.  Because everything's isomalloc'd, we don't even
910 have to restore the pointers-- they'll be restored automatically!
911 */
912 void CmiIsomallocBlockListPup(pup_er p,CmiIsomallocBlockList **lp)
913 {
914         int i,nBlocks=0;
915         Slot *cur=NULL, *start=*lp;
916 #if 0 /*#ifndef CMK_OPTIMIZE*/
917         if (CpvAccess(isomalloc_blocklist)!=NULL)
918                 CmiAbort("Called CmiIsomallocBlockListPup while a blockList is active!\n"
919                         "You should swap out the active blocklist before pupping.\n");
920 #endif
921         /*Count the number of blocks in the list*/
922         if (!pup_isUnpacking(p)) {
923                 nBlocks=1; /*<- Since we have to skip the start block*/
924                 for (cur=start->next; cur!=start; cur=cur->next) 
925                         nBlocks++;
926                 /*Prepare for next trip around list:*/
927                 cur=start;
928         }
929         pup_int(p,&nBlocks);
930         
931         /*Pup each block in the list*/
932         for (i=0;i<nBlocks;i++) {
933                 void *newBlock=cur;
934                 if (!pup_isUnpacking(p)) 
935                 { /*While packing, we traverse the list to find our blocks*/
936                         cur=cur->next;
937                 }
938                 CmiIsomallocPup(p,&newBlock);
939                 if (i==0 && pup_isUnpacking(p))
940                         *lp=(Slot *)newBlock;
941         }
942         if (pup_isDeleting(p))
943                 *lp=NULL;
944 }
945
946 /*Delete all the blocks in this list.*/
947 void CmiIsomallocBlockListDelete(CmiIsomallocBlockList *l)
948 {
949         Slot *start=l;
950         Slot *cur=start;
951         if (cur==NULL) return; /*Already deleted*/
952         do {
953                 Slot *doomed=cur;
954                 cur=cur->next; /*Have to stash next before deleting cur*/
955                 CmiIsomallocFree(doomed);
956         } while (cur!=start);
957 }
958
959 /*Allocate a block from this blockList*/
960 void *CmiIsomallocBlockListMalloc(CmiIsomallocBlockList *l,int nBytes)
961 {
962         Slot *n; /*Newly created slot*/
963         n=(Slot *)CmiIsomalloc(sizeof(Slot)+nBytes);
964         /*Link the new block into the circular blocklist*/
965         n->prev=l;
966         n->next=l->next;
967         l->next->prev=n;
968         l->next=n;
969         return Slot_toUser(n);
970 }
971
972 /*Remove this block from its list and memory*/
973 void CmiIsomallocBlockListFree(void *block)
974 {
975         Slot *n=Slot_fmUser(block);
976 #if DOHEAPCHECK
977         if (n->prev->next!=n || n->next->prev!=n) 
978                 CmiAbort("Heap corruption detected in isomalloc block list header!\n"
979                         "  Run with ++debug and look for writes to negative array indices");
980 #endif
981         /*Link ourselves out of the blocklist*/
982         n->prev->next=n->next;
983         n->next->prev=n->prev;
984         CmiIsomallocFree(n);
985 }
986
987
988
989
990