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