Use MAP_FIXED even for MAP_ANON. Solaris requires MAP_FIXED,
[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-- currently unused
219 static void
220 delete_slotset(slotset* ss)
221 {
222   free_reentrant(ss->buf);
223   free_reentrant(ss);
224 }
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("charm 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 #if !CMK_HAS_MMAP_ANON
298 CpvStaticDeclare(int, zerofd); /*File descriptor for /dev/zero, for mmap*/
299 #endif
300
301 /*
302  * maps the virtual memory associated with slot using mmap
303  */
304 static CmiIsomallocBlock *
305 map_slots(int slot, int nslots)
306 {
307   void *pa;
308   void *addr=slot2addr(slot);
309   pa = mmap(addr, slotsize*nslots, 
310             PROT_READ|PROT_WRITE, 
311 #if CMK_HAS_MMAP_ANON
312             MAP_PRIVATE|MAP_FIXED|MAP_ANON,-1,
313 #else
314             MAP_PRIVATE|MAP_FIXED,CpvAccess(zerofd),
315 #endif
316             0);
317   if (pa==((void*)(-1)) || pa==NULL) 
318   { /*Map just failed completely*/
319 #if CMK_THREADS_DEBUG
320     perror("mmap failed");
321     CmiPrintf("[%d] tried to mmap %p, but encountered error\n",CmiMyPe(),addr);
322 #endif
323     return NULL;
324   }
325   if (pa != addr)
326   { /*Map worked, but gave us back the wrong place*/
327 #if CMK_THREADS_DEBUG
328     CmiPrintf("[%d] tried to mmap %p, but got %p back\n",CmiMyPe(),addr,pa);
329 #endif
330     munmap(addr,slotsize*nslots);
331     return NULL;
332   }
333 #if CMK_THREADS_DEBUG
334   CmiPrintf("[%d] mmap'd slots %d-%d to address %p\n",CmiMyPe(),
335             slot,slot+nslots-1,addr);
336 #endif
337   return (CmiIsomallocBlock *)pa;
338 }
339
340 /*
341  * unmaps the virtual memory associated with slot using munmap
342  */
343 static void
344 unmap_slots(int slot, int nslots)
345 {
346   void *addr=slot2addr(slot);
347   int retval = munmap(addr, slotsize*nslots);
348   if (retval==(-1))
349     CmiAbort("munmap call failed to deallocate requested memory.\n");
350 #if CMK_THREADS_DEBUG
351   CmiPrintf("[%d] munmap'd slots %d-%d from address %p\n",CmiMyPe(),
352             slot,slot+nslots-1,addr);
353 #endif
354 }
355
356 static int 
357 init_map(char **argv)
358 {
359 #if CMK_HAS_MMAP_ANON
360   /*Don't need /dev/zero*/
361 #else
362   CpvInitialize(int, zerofd);  
363   CpvAccess(zerofd) = open("/dev/zero", O_RDWR);
364   if(CpvAccess(zerofd)<0)
365     return 0; /* Cannot open /dev/zero or use MMAP_ANON, so can't mmap memory */
366 #endif
367   return 1;
368 }
369
370 #endif /* UNIX memory map */
371
372
373 static void map_failed(int s,int n)
374 {
375   void *addr=slot2addr(s);
376   CmiError("charm isomalloc.c> map failed to allocate %d bytes at %p.\n",
377       slotsize*n, addr);
378   CmiAbort("Exiting\n");
379 }
380
381
382
383 /************ Address space voodoo: find free address range **********/
384
385 CpvStaticDeclare(slotset *, myss); /*My managed slots*/
386
387 /*This struct describes a range of virtual addresses*/
388 typedef struct {
389   char *start; /*First byte of region*/
390   memRange_t len; /*Number of bytes in region*/
391   const char *type; /*String describing memory in region (debugging only)*/
392 } memRegion_t;
393
394 /*Estimate the top of the current stack*/
395 static void *__cur_stack_frame(void)
396 {
397   char __dummy;
398   void *top_of_stack=(void *)&__dummy;
399   return top_of_stack;
400 }
401 /*Estimate the location of the static data region*/
402 static void *__static_data_loc(void)
403 {
404   static char __dummy;
405   return (void *)&__dummy;
406 }
407
408 /*Pointer comparison is in these subroutines, because
409   comparing arbitrary pointers is nonportable and tricky.
410 */
411 static int pointer_lt(const char *a,const char *b) {
412         return ((memRange_t)a)<((memRange_t)b);
413 }
414 static int pointer_ge(const char *a,const char *b) {
415         return ((memRange_t)a)>=((memRange_t)b);
416 }
417
418 static char *pmin(char *a,char *b) {return pointer_lt(a,b)?a:b;}
419 static char *pmax(char *a,char *b) {return pointer_lt(a,b)?b:a;}
420
421 /*Check if this memory location is usable.  
422   If not, return 1.
423 */
424 static int bad_range(char *loc) {
425   void *addr;
426   isomallocStart=loc;
427   addr=map_slots(0,1);
428   if (addr==NULL) {
429 #if CMK_THREADS_DEBUG
430     CmiPrintf("[%d] Skipping unmappable space at %p\n",CmiMyPe(),loc);
431 #endif
432     return 1; /*No good*/
433   }
434   unmap_slots(0,1);
435   return 0; /*This works*/
436 }
437
438 /*Check if this memory range is usable.  
439   If so, write it into max.
440 */
441 static void check_range(char *start,char *end,memRegion_t *max)
442 {
443   memRange_t len;
444   memRange_t searchQuantStart=128u*1024*1024; /*Shift search location by this far*/
445   memRange_t searchQuant;
446   char *initialStart=start, *initialEnd=end;
447
448   if (start>=end) return; /*Ran out of hole*/
449   len=(memRange_t)end-(memRange_t)start;
450   if (len<=max->len) return; /*It's too short already!*/
451 #if CMK_THREADS_DEBUG
452   CmiPrintf("[%d] Checking usable address space at %p - %p\n",CmiMyPe(),start,end);
453 #endif
454
455   /* Trim off start of range until we hit usable memory*/  
456   searchQuant=searchQuantStart;
457   while (bad_range(start)) {
458         start=initialStart+searchQuant;
459         if (pointer_ge(start,end)) return; /*Ran out of hole*/
460         searchQuant*=2; /*Exponential search*/
461         if (searchQuant==0) return; /*SearchQuant overflowed-- no good memory anywhere*/
462   }
463
464   /* Trim off end of range until we hit usable memory*/
465   searchQuant=searchQuantStart;
466   while (bad_range(end-slotsize)) {
467         end=initialEnd-searchQuant;
468         if (pointer_ge(start,end)) return; /*Ran out of hole*/
469         searchQuant*=2;
470         if (searchQuant==0) return; /*SearchQuant overflowed-- no good memory anywhere*/
471   }
472   
473   len=(memRange_t)end-(memRange_t)start;
474   if (len<max->len) return; /*It's now too short.*/
475   
476 #if CMK_THREADS_DEBUG
477   CmiPrintf("[%d] Address space at %p - %p is largest\n",CmiMyPe(),start,end);
478 #endif
479
480   /*If we got here, we're the new largest usable range*/
481   max->len=len;
482   max->start=start;
483   max->type="Unused";
484 }
485
486 /*Find the first available memory region of at least the
487   given size not touching any data in the used list.
488  */
489 static memRegion_t find_free_region(memRegion_t *used,int nUsed,int atLeast) 
490 {
491   memRegion_t max;
492   int i,j;  
493
494   max.len=0;
495   /*Find the largest hole between regions*/
496   for (i=0;i<nUsed;i++) {
497     /*Consider a hole starting at the end of region i*/
498     char *holeStart=used[i].start+used[i].len;
499     char *holeEnd=(void *)(-1);
500     
501     /*Shrink the hole by all others*/ 
502     for (j=0;j<nUsed && pointer_lt(holeStart,holeEnd);j++) {
503       if (pointer_lt(used[j].start,holeStart)) 
504         holeStart=pmax(holeStart,used[j].start+used[j].len);
505       else if (pointer_lt(used[j].start,holeEnd)) 
506         holeEnd=pmin(holeEnd,used[j].start);
507     } 
508
509     check_range(holeStart,holeEnd,&max);
510   }
511
512   return max; 
513 }
514
515 static void init_ranges(char **argv)
516 {
517   /*Largest value a signed int can hold*/
518   memRange_t intMax=(((memRange_t)1)<<(sizeof(int)*8-1))-1;
519
520   /*Round slot size up to nearest page size*/
521   slotsize=16*1024;
522   slotsize=(slotsize+CMK_MEMORY_PAGESIZE-1) & ~(CMK_MEMORY_PAGESIZE-1);
523 #if CMK_THREADS_DEBUG
524   CmiPrintf("[%d] Using slotsize of %d\n", CmiMyPe(), slotsize);
525 #endif
526
527   if (CmiMyRank()==0 && numslots==0)
528   { /* Find the largest unused region of virtual address space */
529     char *staticData =(char *) __static_data_loc();
530     char *code = (char *)&init_ranges;
531     char *codeDll = (char *)&fclose;
532     char *heapLil = (char*) malloc(1);
533     char *heapBig = (char*) malloc(4*1024*1024);
534     char *stack = (char *)__cur_stack_frame();
535
536     memRange_t meg=1024*1024; /*One megabyte*/
537     memRange_t gig=1024*meg; /*One gigabyte*/
538     int i,nRegions=7;
539     memRegion_t regions[7]; /*used portions of address space*/
540     memRegion_t freeRegion; /*Largest unused block of address space*/
541
542 /*Mark off regions of virtual address space as ususable*/
543     regions[0].type="NULL (inaccessible)";
544     regions[0].start=NULL; regions[0].len=16u*meg;
545     
546     regions[1].type="Static program data";
547     regions[1].start=staticData; regions[1].len=256u*meg;
548     
549     regions[2].type="Program executable code";
550     regions[2].start=code; regions[2].len=256u*meg;
551     
552     regions[3].type="Heap (small blocks)";
553     regions[3].start=heapLil; regions[3].len=2u*gig;
554     
555     regions[4].type="Heap (large blocks)";
556     regions[4].start=heapBig; regions[4].len=1u*gig;
557     
558     regions[5].type="Stack space";
559     regions[5].start=stack; regions[5].len=256u*meg;
560
561     regions[6].type="Program dynamically linked code";
562     regions[6].start=codeDll; regions[6].len=256u*meg;    
563
564     _MEMCHECK(heapBig); free(heapBig);
565     _MEMCHECK(heapLil); free(heapLil); 
566     
567     /*Align each memory region*/
568     for (i=0;i<nRegions;i++) {
569       memRange_t p=(memRange_t)regions[i].start;
570       p&=~(regions[i].len-1); /*Round down to a len-boundary (mask off low bits)*/
571       regions[i].start=(char *)p;
572 #if CMK_THREADS_DEBUG
573       CmiPrintf("[%d] Memory map: %p - %p  %s\n",CmiMyPe(),
574               regions[i].start,regions[i].start+regions[i].len,regions[i].type);
575 #endif
576     }
577     
578     /*Find a large, unused region*/
579     freeRegion=find_free_region(regions,nRegions,(512u)*meg);
580     
581     if (freeRegion.len==0) 
582     { /*No free address space-- disable isomalloc:*/
583       disable_isomalloc("no free virtual address space");
584     }
585     else 
586     {
587       /*If the unused region is very large, pad it on both ends for safety*/
588       if (freeRegion.len/gig>64u) {
589         freeRegion.start+=16u*gig;
590         freeRegion.len-=20u*gig;
591       }
592
593 #if CMK_THREADS_DEBUG
594       CmiPrintf("[%d] Largest unused region: %p - %p (%d megs)\n",CmiMyPe(),
595               freeRegion.start,freeRegion.start+freeRegion.len,
596               freeRegion.len/meg);
597 #endif
598
599       /*Allocate stacks in unused region*/
600       isomallocStart=freeRegion.start;
601       isomallocEnd=freeRegion.start+freeRegion.len;
602
603       /*Make sure our largest slot number doesn't overflow an int:*/
604       if (freeRegion.len/slotsize>intMax)
605         freeRegion.len=intMax*slotsize;
606     
607       numslots=(freeRegion.len/slotsize)/CmiNumPes();
608     
609 #if CMK_THREADS_DEBUG
610       CmiPrintf("[%d] Can isomalloc up to %lu megs per pe\n",CmiMyPe(),
611               ((memRange_t)numslots)*slotsize/meg);
612 #endif
613     }
614   }
615   /*SMP Mode: wait here for rank 0 to initialize numslots so we can set up myss*/
616   CmiNodeAllBarrier(); 
617   
618   if (isomallocStart!=NULL) {
619     CpvInitialize(slotset *, myss);
620     CpvAccess(myss) = new_slotset(pe2slot(CmiMyPe()), numslots);
621   }
622 }
623
624
625 /************* Communication: for grabbing/freeing remote slots *********/
626 typedef struct _slotmsg
627 {
628   char cmicore[CmiMsgHeaderSizeBytes];
629   int pe; /*Source processor*/
630   int slot; /*First requested slot*/
631   int nslots; /*Number of requested slots*/
632 } slotmsg;
633
634 static slotmsg *prepare_slotmsg(int slot,int nslots)
635 {
636         slotmsg *m=(slotmsg *)CmiAlloc(sizeof(slotmsg));
637         m->pe=CmiMyPe();
638         m->slot=slot;
639         m->nslots=nslots;
640         return m;
641 }
642
643 static void grab_remote(slotmsg *msg)
644 {
645         grab_slots(CpvAccess(myss),msg->slot,msg->nslots);
646         CmiFree(msg);
647 }
648
649 static void free_remote(slotmsg *msg)
650 {
651         free_slots(CpvAccess(myss),msg->slot,msg->nslots);
652         CmiFree(msg);
653 }
654 static int grab_remote_idx, free_remote_idx;
655
656 struct slotOP {
657         /*Function pointer to perform local operation*/
658         void (*local)(slotset *ss,int s,int n);
659         /*Index to perform remote operation*/
660         int remote;
661 };
662 typedef struct slotOP slotOP;
663 static slotOP grabOP,freeOP;
664
665 static void init_comm(char **argv)
666 {
667         grab_remote_idx=CmiRegisterHandler((CmiHandler)grab_remote);
668         free_remote_idx=CmiRegisterHandler((CmiHandler)free_remote);    
669         grabOP.local=grab_slots;
670         grabOP.remote=grab_remote_idx;
671         freeOP.local=free_slots;
672         freeOP.remote=free_remote_idx;  
673 }
674
675 /*Apply the given operation to the given slots which
676   lie on the given processor.*/
677 static void one_slotOP(const slotOP *op,int pe,int s,int n)
678 {
679 /*Shrink range to only those covered by this processor*/
680         /*First and last slot for this processor*/
681         int p_s=pe2slot(pe), p_e=pe2slot(pe+1);
682         int e=s+n;
683         if (s<p_s) s=p_s;
684         if (e>p_e) e=p_e;
685         n=e-s;
686
687 /*Send off range*/
688         if (pe==CmiMyPe()) 
689                 op->local(CpvAccess(myss),s,n);
690         else 
691         {/*Remote request*/
692                 slotmsg *m=prepare_slotmsg(s,e);
693                 CmiSyncSendAndFree(pe,sizeof(slotmsg),m);
694         }
695 }
696
697 /*Apply the given operation to all slots in the range [s, s+n) 
698 After a restart from checkpoint, a slotset can cross an 
699 arbitrary set of processors.
700 */
701 static void all_slotOP(const slotOP *op,int s,int n)
702 {
703         int spe=slot2pe(s), epe=slot2pe(s+n-1);
704         int pe;
705         for (pe=spe; pe<=epe; pe++)
706                 one_slotOP(op,pe,s,n);
707 }
708
709 /************** External interface ***************/
710 void *CmiIsomalloc(int size)
711 {
712         int s,n;
713         CmiIsomallocBlock *blk;
714         if (isomallocStart==NULL) return disabled_map(size);
715         n=length2slots(size);
716         /*Always satisfy mallocs with local slots:*/
717         s=get_slots(CpvAccess(myss),n);
718         if (s==-1) {
719                 CmiError("Not enough address space left on processor %d to isomalloc %d bytes!\n",
720                          CmiMyPe(),size);
721                 CmiAbort("Out of virtual address space for isomalloc");
722         }
723         grab_slots(CpvAccess(myss),s,n);
724         blk=map_slots(s,n);
725         if (!blk) map_failed(s,n);
726         blk->slot=s;
727         blk->length=size;
728         return block2pointer(blk);
729 }
730
731 void CmiIsomallocPup(pup_er p,void **blockPtrPtr)
732 {
733         CmiIsomallocBlock *blk;
734         int s,n,length;
735         if (isomallocStart==NULL) CmiAbort("isomalloc is disabled-- cannot use IsomallocPup");
736
737         if (!pup_isUnpacking(p)) 
738         { /*We have an existing block-- unpack start slot & length*/
739                 blk=pointer2block(*blockPtrPtr);
740                 s=blk->slot;
741                 length=blk->length;
742         }
743         
744         pup_int(p,&s);
745         pup_int(p,&length);
746         n=length2slots(length);
747         
748         if (pup_isUnpacking(p)) 
749         { /*Must allocate a new block in its old location*/
750                 if (pup_isUserlevel(p))
751                         /*Checkpoint: must grab old slots (even remote!)*/
752                         all_slotOP(&grabOP,s,n);
753                 blk=map_slots(s,n);
754                 if (!blk) map_failed(s,n);
755                 blk->slot=s;
756                 blk->length=length;
757                 *blockPtrPtr=block2pointer(blk);
758         }
759         
760         /*Pup the allocated data*/
761         pup_bytes(p,*blockPtrPtr,length);
762         
763         if (pup_isDeleting(p)) 
764         { /*Unmap old slots, but do not mark as free*/
765                 unmap_slots(s,n);
766                 *blockPtrPtr=NULL; /*Zero out user's pointer*/
767         }
768 }
769
770 void CmiIsomallocFree(void *blockPtr)
771 {
772         if (isomallocStart==NULL) {
773                 disabled_unmap(blockPtr);
774         }
775         else if (blockPtr!=NULL)
776         {
777                 CmiIsomallocBlock *blk=pointer2block(blockPtr);
778                 int s=blk->slot, n=length2slots(blk->length);
779                 unmap_slots(s,n);
780                 /*Mark used slots as free*/
781                 all_slotOP(&freeOP,s,n);
782         }
783 }
784 int   CmiIsomallocLength(void *block)
785 {
786         return pointer2block(block)->length;
787 }
788
789 /*Return true if this address is in the region managed by isomalloc*/
790 int CmiIsomallocInRange(void *addr)
791 {
792         if (isomallocStart==NULL) return 0; /* There is no range we manage! */
793         return pointer_ge((char *)addr,isomallocStart) && 
794                pointer_lt((char*)addr,isomallocEnd);
795 }
796
797 void CmiIsomallocInit(char **argv)
798 {
799   init_comm(argv);
800   if (!init_map(argv)) {
801     disable_isomalloc("mmap() does not work");
802   }
803   else {
804     init_ranges(argv);
805   }
806 }
807
808 /***************** BlockList interface *********
809 This was moved here from memory-isomalloc.c when it 
810 was realized that a list-of-isomalloc'd-blocks is useful for
811 more than just isomalloc heaps.
812 */
813
814 typedef CmiIsomallocBlockList Slot;
815
816 /*Convert a slot to a user address*/
817 static char *Slot_toUser(Slot *s) {return (char *)(s+1);}
818 static Slot *Slot_fmUser(void *s) {return ((Slot *)s)-1;}
819
820
821 /*Build a new blockList.*/
822 CmiIsomallocBlockList *CmiIsomallocBlockListNew(void)
823 {
824         CmiIsomallocBlockList *ret;
825         ret=(CmiIsomallocBlockList *)CmiIsomalloc(sizeof(*ret));
826         ret->next=ret; /*1-entry circular linked list*/
827         ret->prev=ret;
828         return ret;
829 }
830
831 /*Pup all the blocks in this list.  This amounts to two circular
832 list traversals.  Because everything's isomalloc'd, we don't even
833 have to restore the pointers-- they'll be restored automatically!
834 */
835 void CmiIsomallocBlockListPup(pup_er p,CmiIsomallocBlockList **lp)
836 {
837         int i,nBlocks=0;
838         Slot *cur=NULL, *start=*lp;
839 #if 0 /*#ifndef CMK_OPTIMIZE*/
840         if (CpvAccess(isomalloc_blocklist)!=NULL)
841                 CmiAbort("Called CmiIsomallocBlockListPup while a blockList is active!\n"
842                         "You should swap out the active blocklist before pupping.\n");
843 #endif
844         /*Count the number of blocks in the list*/
845         if (!pup_isUnpacking(p)) {
846                 nBlocks=1; /*<- Since we have to skip the start block*/
847                 for (cur=start->next; cur!=start; cur=cur->next) 
848                         nBlocks++;
849                 /*Prepare for next trip around list:*/
850                 cur=start;
851         }
852         pup_int(p,&nBlocks);
853         
854         /*Pup each block in the list*/
855         for (i=0;i<nBlocks;i++) {
856                 void *newBlock=cur;
857                 if (!pup_isUnpacking(p)) 
858                 { /*While packing, we traverse the list to find our blocks*/
859                         cur=cur->next;
860                 }
861                 CmiIsomallocPup(p,&newBlock);
862                 if (i==0 && pup_isUnpacking(p))
863                         *lp=(Slot *)newBlock;
864         }
865         if (pup_isDeleting(p))
866                 *lp=NULL;
867 }
868
869 /*Delete all the blocks in this list.*/
870 void CmiIsomallocBlockListDelete(CmiIsomallocBlockList *l)
871 {
872         Slot *start=l;
873         Slot *cur=start;
874         if (cur==NULL) return; /*Already deleted*/
875         do {
876                 Slot *doomed=cur;
877                 cur=cur->next; /*Have to stash next before deleting cur*/
878                 CmiIsomallocFree(doomed);
879         } while (cur!=start);
880 }
881
882 /*Allocate a block from this blockList*/
883 void *CmiIsomallocBlockListMalloc(CmiIsomallocBlockList *l,int nBytes)
884 {
885         Slot *n; /*Newly created slot*/
886         n=(Slot *)CmiIsomalloc(sizeof(Slot)+nBytes);
887         /*Link the new block into the circular blocklist*/
888         n->prev=l;
889         n->next=l->next;
890         l->next->prev=n;
891         l->next=n;
892         return Slot_toUser(n);
893 }
894
895 /*Remove this block from its list and memory*/
896 void CmiIsomallocBlockListFree(void *block)
897 {
898         Slot *n=Slot_fmUser(block);
899 #if DOHEAPCHECK
900         if (n->prev->next!=n || n->next->prev!=n) 
901                 CmiAbort("Heap corruption detected in isomalloc block list header!\n"
902                         "  Run with ++debug and look for writes to negative array indices");
903 #endif
904         /*Link ourselves out of the blocklist*/
905         n->prev->next=n->next;
906         n->next->prev=n->prev;
907         CmiIsomallocFree(n);
908 }
909
910
911
912
913