fixed a bug in isomallocfree which involves freeing a remote slot. no converse handle...
[charm.git] / src / conv-core / isomalloc.c
1 /**************************************************************************
2 Isomalloc:
3   A way to allocate memory at the same address on every processor.
4 This enables linked data structures, like thread stacks, to be migrated
5 to the same address range on other processors.  This is similar to an
6 explicitly managed shared memory system.
7
8   The memory is used and released via the mmap()/mumap() calls, so unused
9 memory does not take any (RAM, swap or disk) space.
10
11   The way it's implemented is that each processor claims some section 
12 of the available virtual address space, and satisfies all new allocations
13 from that space.  Migrating structures use whatever space they started with.
14
15 Written for migratable threads by Milind Bhandarkar around August 2000;
16 generalized by Orion Lawlor November 2001.
17 */
18 #include "converse.h"
19 #include "memory-isomalloc.h"
20
21 #define CMK_THREADS_DEBUG 0 
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <errno.h> /* just so I can find dynamically-linked symbols */
26
27 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                 CmiSetHandler(m, freeOP.remote);
771                 CmiSyncSendAndFree(pe,sizeof(slotmsg),m);
772         }
773 }
774
775 /*Apply the given operation to all slots in the range [s, s+n) 
776 After a restart from checkpoint, a slotset can cross an 
777 arbitrary set of processors.
778 */
779 static void all_slotOP(const slotOP *op,int s,int n)
780 {
781         int spe=slot2pe(s), epe=slot2pe(s+n-1);
782         int pe;
783         for (pe=spe; pe<=epe; pe++)
784                 one_slotOP(op,pe,s,n);
785 }
786
787 /************** External interface ***************/
788 void *CmiIsomalloc(int size)
789 {
790         int s,n;
791         CmiIsomallocBlock *blk;
792         if (isomallocStart==NULL) return disabled_map(size);
793         n=length2slots(size);
794         /*Always satisfy mallocs with local slots:*/
795         s=get_slots(CpvAccess(myss),n);
796         if (s==-1) {
797                 CmiError("Not enough address space left on processor %d to isomalloc %d bytes!\n",
798                          CmiMyPe(),size);
799                 CmiAbort("Out of virtual address space for isomalloc");
800         }
801         grab_slots(CpvAccess(myss),s,n);
802         blk=map_slots(s,n);
803         if (!blk) map_failed(s,n);
804         blk->slot=s;
805         blk->length=size;
806         return block2pointer(blk);
807 }
808
809 void CmiIsomallocPup(pup_er p,void **blockPtrPtr)
810 {
811         CmiIsomallocBlock *blk;
812         int s,n,length;
813         if (isomallocStart==NULL) CmiAbort("isomalloc is disabled-- cannot use IsomallocPup");
814
815         if (!pup_isUnpacking(p)) 
816         { /*We have an existing block-- unpack start slot & length*/
817                 blk=pointer2block(*blockPtrPtr);
818                 s=blk->slot;
819                 length=blk->length;
820         }
821         
822         pup_int(p,&s);
823         pup_int(p,&length);
824         n=length2slots(length);
825         
826         if (pup_isUnpacking(p)) 
827         { /*Must allocate a new block in its old location*/
828                 if (pup_isUserlevel(p))
829                         /*Checkpoint: must grab old slots (even remote!)*/
830                         all_slotOP(&grabOP,s,n);
831                 blk=map_slots(s,n);
832                 if (!blk) map_failed(s,n);
833                 blk->slot=s;
834                 blk->length=length;
835                 *blockPtrPtr=block2pointer(blk);
836         }
837         
838         /*Pup the allocated data*/
839         pup_bytes(p,*blockPtrPtr,length);
840         
841         if (pup_isDeleting(p)) 
842         { /*Unmap old slots, but do not mark as free*/
843                 unmap_slots(s,n);
844                 *blockPtrPtr=NULL; /*Zero out user's pointer*/
845         }
846 }
847
848 void CmiIsomallocFree(void *blockPtr)
849 {
850         if (isomallocStart==NULL) {
851                 disabled_unmap(blockPtr);
852         }
853         else if (blockPtr!=NULL)
854         {
855                 CmiIsomallocBlock *blk=pointer2block(blockPtr);
856                 int s=blk->slot, n=length2slots(blk->length);
857                 unmap_slots(s,n);
858                 /*Mark used slots as free*/
859                 all_slotOP(&freeOP,s,n);
860         }
861 }
862 int   CmiIsomallocLength(void *block)
863 {
864         return pointer2block(block)->length;
865 }
866
867 /*Return true if this address is in the region managed by isomalloc*/
868 int CmiIsomallocInRange(void *addr)
869 {
870         if (isomallocStart==NULL) return 0; /* There is no range we manage! */
871         return pointer_ge((char *)addr,isomallocStart) && 
872                pointer_lt((char*)addr,isomallocEnd);
873 }
874
875 void CmiIsomallocInit(char **argv)
876 {
877 #if CMK_NO_ISO_MALLOC
878   disable_isomalloc("isomalloc disabled by conv-mach");
879 #else
880   init_comm(argv);
881   if (!init_map(argv)) {
882     disable_isomalloc("mmap() does not work");
883   }
884   else {
885     init_ranges(argv);
886   }
887 #endif
888 }
889
890 /***************** BlockList interface *********
891 This was moved here from memory-isomalloc.c when it 
892 was realized that a list-of-isomalloc'd-blocks is useful for
893 more than just isomalloc heaps.
894 */
895
896 typedef CmiIsomallocBlockList Slot;
897
898 /*Convert a slot to a user address*/
899 static char *Slot_toUser(Slot *s) {return (char *)(s+1);}
900 static Slot *Slot_fmUser(void *s) {return ((Slot *)s)-1;}
901
902
903 /*Build a new blockList.*/
904 CmiIsomallocBlockList *CmiIsomallocBlockListNew(void)
905 {
906         CmiIsomallocBlockList *ret;
907         ret=(CmiIsomallocBlockList *)CmiIsomalloc(sizeof(*ret));
908         ret->next=ret; /*1-entry circular linked list*/
909         ret->prev=ret;
910         return ret;
911 }
912
913 /*Pup all the blocks in this list.  This amounts to two circular
914 list traversals.  Because everything's isomalloc'd, we don't even
915 have to restore the pointers-- they'll be restored automatically!
916 */
917 void CmiIsomallocBlockListPup(pup_er p,CmiIsomallocBlockList **lp)
918 {
919         int i,nBlocks=0;
920         Slot *cur=NULL, *start=*lp;
921 #if 0 /*#ifndef CMK_OPTIMIZE*/
922         if (CpvAccess(isomalloc_blocklist)!=NULL)
923                 CmiAbort("Called CmiIsomallocBlockListPup while a blockList is active!\n"
924                         "You should swap out the active blocklist before pupping.\n");
925 #endif
926         /*Count the number of blocks in the list*/
927         if (!pup_isUnpacking(p)) {
928                 nBlocks=1; /*<- Since we have to skip the start block*/
929                 for (cur=start->next; cur!=start; cur=cur->next) 
930                         nBlocks++;
931                 /*Prepare for next trip around list:*/
932                 cur=start;
933         }
934         pup_int(p,&nBlocks);
935         
936         /*Pup each block in the list*/
937         for (i=0;i<nBlocks;i++) {
938                 void *newBlock=cur;
939                 if (!pup_isUnpacking(p)) 
940                 { /*While packing, we traverse the list to find our blocks*/
941                         cur=cur->next;
942                 }
943                 CmiIsomallocPup(p,&newBlock);
944                 if (i==0 && pup_isUnpacking(p))
945                         *lp=(Slot *)newBlock;
946         }
947         if (pup_isDeleting(p))
948                 *lp=NULL;
949 }
950
951 /*Delete all the blocks in this list.*/
952 void CmiIsomallocBlockListDelete(CmiIsomallocBlockList *l)
953 {
954         Slot *start=l;
955         Slot *cur=start;
956         if (cur==NULL) return; /*Already deleted*/
957         do {
958                 Slot *doomed=cur;
959                 cur=cur->next; /*Have to stash next before deleting cur*/
960                 CmiIsomallocFree(doomed);
961         } while (cur!=start);
962 }
963
964 /*Allocate a block from this blockList*/
965 void *CmiIsomallocBlockListMalloc(CmiIsomallocBlockList *l,int nBytes)
966 {
967         Slot *n; /*Newly created slot*/
968         n=(Slot *)CmiIsomalloc(sizeof(Slot)+nBytes);
969         /*Link the new block into the circular blocklist*/
970         n->prev=l;
971         n->next=l->next;
972         l->next->prev=n;
973         l->next=n;
974         return Slot_toUser(n);
975 }
976
977 /*Remove this block from its list and memory*/
978 void CmiIsomallocBlockListFree(void *block)
979 {
980         Slot *n=Slot_fmUser(block);
981 #if DOHEAPCHECK
982         if (n->prev->next!=n || n->next->prev!=n) 
983                 CmiAbort("Heap corruption detected in isomalloc block list header!\n"
984                         "  Run with ++debug and look for writes to negative array indices");
985 #endif
986         /*Link ourselves out of the blocklist*/
987         n->prev->next=n->next;
988         n->next->prev=n->prev;
989         CmiIsomallocFree(n);
990 }
991
992
993
994
995