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