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