Main Page | Namespace List | Class Hierarchy | Class List | Directories | File List | Namespace Members | Class Members | File Members

VMemPool.C

Go to the documentation of this file.
00001 // Copyright (C) 2001, Compaq Computer Corporation
00002 // 
00003 // This file is part of Vesta.
00004 // 
00005 // Vesta is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU Lesser General Public
00007 // License as published by the Free Software Foundation; either
00008 // version 2.1 of the License, or (at your option) any later version.
00009 // 
00010 // Vesta is distributed in the hope that it will be useful,
00011 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 // Lesser General Public License for more details.
00014 // 
00015 // You should have received a copy of the GNU Lesser General Public
00016 // License along with Vesta; if not, write to the Free Software
00017 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00018 
00019 //
00020 // VMemPool.C
00021 // Last modified on Mon May 23 21:53:56 EDT 2005 by ken@xorian.net  
00022 //      modified on Tue Aug  7 19:43:45 PDT 2001 by mann  
00023 //      modified on Mon Apr 28 17:30:26 PDT 1997 by heydon
00024 //
00025 // Memory pool for Vesta repository directory structure.  Confined to
00026 // a range of addresses that can be represented in 32 bits.
00027 
00028 // High-level garbage collection and checkpointing code is here.
00029 
00030 // For now, we use a dumb first-fit allocation algorithm.  All freed
00031 // memory is linked in a circular list with a cursor pointing into it.
00032 // An allocation request searches around the list to find the first
00033 // large-enough block and returns it.  (If there is none, a new block
00034 // is made from currently unused memory.)  The block is split if it
00035 // was larger than the requested size.  A block on the free list may
00036 // not be smaller than FREEBK_MINSIZE = sizeof(VMemPoolFreeBlock), so:
00037 // (1) Smaller sized allocates (and frees) are rounded up.  (2) If
00038 // splitting a block would leave a too-small fragment, the fragment is
00039 // filled with "freeByte" type codes and left off the free list.  (3)
00040 // If gc or checkpointing compacts a block to less than FREEBK_MINSIZE
00041 // and it is later freed, again the fragment is filled with "freeByte"
00042 // type codes and left off the free list.  Such fragments are
00043 // reclaimed by the next gc or checkpoint.
00044 
00045 
00046 #if __linux__
00047 #include <stdint.h>
00048 #endif
00049 #include <stdlib.h>
00050 #include <sys/mman.h>
00051 #include "Basics.H"
00052 #include "VMemPool.H"
00053 #include "VestaSource.H"
00054 #include "VestaConfig.H"
00055 #include "logging.H"
00056 #include "DirShortId.H"
00057 #include "VDirChangeable.H"
00058 #include "VestaAttribsRep.H"
00059 #include "FPShortId.H"
00060 #include <Thread.H>
00061 
00062 using std::fstream;
00063 using std::hex;
00064 using std::endl;
00065 using std::streampos;
00066 
00067 // Internal block types.
00068 // See VDirChangeable.H for an explanation of the notation.
00069 //
00070 // packed struct VMemPoolFreeByte {
00071 //     Bit8 clientBits: 4;           // Always 0.
00072 //     Bit8 type: 4;                 // Must be VMemPool::freeByte
00073 // };
00074 //
00075 // packed struct VMemPoolFreeBlock {
00076 //     Bit8 clientBits: 4;           // Always 0.
00077 //     Bit8 type: 4;                 // Must be VMemPool::freeBlock
00078 //     Bit32 length;
00079 //     VMemPoolFreeBlock@ next;
00080 // };
00081 //
00082 
00083 // Offsets in packed struct
00084 #define FREEBK_FLAGS 0
00085 #define FREEBK_LENGTH 1
00086 #define FREEBK_NEXT 5
00087 #define FREEBK_MINSIZE 9
00088 
00089 // Short pointer to a VMemPool block, with methods for common fields
00090 // and fields in freed blocks.
00091 class VMemPoolBlockSP : VMemPool {
00092   public:
00093     Bit32 sp;
00094     inline VMemPool::typeCode type() {
00095         return (VMemPool::typeCode)
00096           ((base[sp - 1 + FREEBK_FLAGS] >> 4) & 0xf);
00097     }; 
00098     inline void setType(VMemPool::typeCode val)
00099       { base[sp - 1 + FREEBK_FLAGS] = ((int) val) << 4; };
00100     inline Bit32 length() {
00101         Bit32 val;
00102         memcpy(&val, &base[sp - 1 + FREEBK_LENGTH], sizeof(Bit32));
00103         return val;
00104     };
00105     inline void setLength(Bit32 val) {
00106         memcpy(&base[sp - 1 + FREEBK_LENGTH], &val, sizeof(Bit32));
00107     };
00108     inline VMemPoolBlockSP next() {
00109         VMemPoolBlockSP val;
00110         memcpy(&val.sp, &base[sp - 1 + FREEBK_NEXT], sizeof(Bit32));
00111         return val;
00112     };
00113     inline void setNext(VMemPoolBlockSP val) {
00114         memcpy(&base[sp - 1 + FREEBK_NEXT], &val.sp, sizeof(Bit32));
00115     };
00116     inline friend int operator==(VMemPoolBlockSP a, VMemPoolBlockSP b)
00117       { return a.sp == b.sp; };
00118     inline friend int operator!=(VMemPoolBlockSP a, VMemPoolBlockSP b)
00119       { return a.sp != b.sp; };
00120     inline VMemPoolBlockSP() { };
00121     inline VMemPoolBlockSP(Bit32 sp) { this->sp = sp; };
00122 };
00123 
00124 const VMemPoolBlockSP SNULL(0);
00125 
00126 // Module globals
00127 static pthread_once_t once = PTHREAD_ONCE_INIT;
00128 static Basics::mutex mu;
00129 static long page_size;
00130 Bit8* VMemPool::base;
00131 Bit32 VMemPool::totalSize;
00132 Bit32 VMemPool::minGrowBy;
00133 static VMemPoolBlockSP freedMemCursor;
00134 static VMemPoolBlockSP unusedMem;
00135 static VMemPoolBlockSP unusedMemEnd;
00136 static struct {
00137     VMemPool::markCallback markcb;
00138     void* markcl;
00139     VMemPool::sweepCallback sweepcb;
00140     void* sweepcl;
00141     VMemPool::rebuildCallback rebuildcb;
00142     void* rebuildcl;
00143 
00144     // Statistical information; examine in debugger.
00145     // Not saved in a checkpoint; accurate only
00146     // if there has been at least one sweep (source
00147     // weed) since recovery from a checkpoint.
00148     Bit32 totalSize;    
00149 } callback[VMemPool::maxTypeCodes];
00150 static const int CkptMinVersion = 4;
00151 static const int CkptMaxVersion = 11;  // version 11 adds (vroot ...)
00152 static int currentCkptVersion;
00153 int VMemPool::alignmentMask;
00154 
00155 // Set the prevailing checkpoint version: if writing a new checkpoint,
00156 // the latest version; else if recovered from a checkpoint, the
00157 // version recovered from; else the latest version.
00158 static void
00159 setCurrentCkptVersion(int ckptVersion)
00160 {
00161   currentCkptVersion = ckptVersion;
00162   if (ckptVersion >= 7) {
00163     // Note: the alignment code in version 7 was buggy; if you can't
00164     // recover from a version 7 checkpoint due to assertion failures,
00165     // delete it and recover from the most recent version 6 or 5
00166     // checkpoint instead.
00167     VMemPool::alignmentMask = 7;
00168   } else if (ckptVersion == 6) {
00169     VMemPool::alignmentMask = 3;
00170   } else {
00171     VMemPool::alignmentMask = 0;
00172   }
00173 }
00174 
00175 extern "C"
00176 {
00177   void VMemPool_init()
00178   {
00179     VMemPool::init();
00180   }
00181 }
00182 
00183 void*
00184 VMemPool::allocate(VMemPool::typeCode type, Bit32 size) throw ()
00185 {
00186     VMemPoolBlockSP p;
00187     Bit32 asize = size;
00188     pthread_once(&once, VMemPool_init);
00189 
00190     // Round up to minimum size that can be put on free list.
00191     // Note: a sweep or checkpoint can still make a block
00192     // too small to be put on the free list.
00193     if (asize < FREEBK_MINSIZE) {
00194         asize = FREEBK_MINSIZE;
00195     }
00196 
00197     // Round up to the required alignment
00198     // Assumes 2's complement negation.
00199     asize += (-asize) & VMemPool::alignmentMask;
00200     mu.lock();
00201 
00202     //Repos::dprintf(DBG_VMEMPOOL, "allocate(%d, %u) ", (int) type, size);
00203 
00204     if (freedMemCursor != SNULL) {
00205         VMemPoolBlockSP prev = freedMemCursor;
00206         p = prev.next();
00207         do {
00208             if (p.length() >= asize) {
00209                 // Found a block
00210                 Bit32 excess = p.length() - asize;
00211                 assert((excess & VMemPool::alignmentMask) == 0);
00212                 if (excess < FREEBK_MINSIZE) {
00213                     // Exact size, or nearly
00214                     asize = p.length();
00215                     if (prev == p) {
00216                         // Free list was of length 1
00217                         freedMemCursor = SNULL;
00218                     } else {
00219                         prev.setNext(p.next());
00220                         freedMemCursor = prev;
00221                     }
00222                 } else {
00223                     // Larger block, need to split
00224                     VMemPoolBlockSP q;
00225                     q.sp = p.sp + asize;
00226                     q.setType(VMemPool::freeBlock);
00227                     q.setLength(excess);
00228                     if (prev == p) {
00229                         // Free list was of length 1
00230                         q.setNext(q);
00231                         freedMemCursor = q;
00232                     } else {
00233                         q.setNext(p.next());
00234                         prev.setNext(q);
00235                         freedMemCursor = prev;
00236                     }
00237                 }
00238                 goto finish;
00239             }
00240             prev = p;
00241             p = p.next();
00242         } while (prev != freedMemCursor);
00243         // fall though => couldn't find a big enough block
00244     }   
00245     // Make a new block in the previously unused area
00246     p = unusedMem;
00247     if (unusedMemEnd.sp - unusedMem.sp <= asize) {
00248         // Grow pool by (at least) the amount needed
00249         grow(asize - (unusedMemEnd.sp - unusedMem.sp));
00250     }
00251     unusedMem.sp += asize;
00252 
00253   finish:
00254     callback[(int)type].totalSize += size;
00255     callback[(int)VMemPool::freeBlock].totalSize -= asize;
00256     while (size < asize) {
00257         ((VMemPoolBlockSP) (p.sp + size++)).setType(VMemPool::freeByte);
00258         callback[(int)VMemPool::freeByte].totalSize++;
00259     }
00260     p.setType(type);
00261     mu.unlock();
00262 
00263     void *ret = (void*) VMemPool::lengthenPointer(p.sp);
00264     assert((((PointerInt)ret) & VMemPool::alignmentMask) == 0);
00265     //Repos::dprintf(DBG_VMEMPOOL, "returns 0x%016x\n", ret);
00266     return ret;
00267 }
00268 
00269 void
00270 VMemPool::free(void* addr, Bit32 size, typeCode type) throw ()
00271 {
00272     VMemPoolBlockSP p;
00273     p.sp = VMemPool::shortenPointer(addr);
00274     assert((((PointerInt)(addr)) & VMemPool::alignmentMask) == 0);
00275     if (type != unspecified) {
00276         assert(p.type() == type);
00277     }
00278     mu.lock();
00279 
00280     //Repos::dprintf(DBG_VMEMPOOL, "free(0x%016x, %d) ", addr, size);
00281 
00282     if (type != unspecified) {
00283         assert(p.type() == type);
00284     }
00285     callback[(int)p.type()].totalSize -= size;
00286     p.setType(VMemPool::freeBlock);
00287     // Find size to free by merging in following freeBytes
00288     Bit32 asize = size;
00289     VMemPoolBlockSP q;
00290     q.sp = p.sp + asize;
00291     while (q.sp < unusedMem.sp && q.type() == VMemPool::freeByte) {
00292         q.sp++;
00293         asize++;
00294         callback[(int)VMemPool::freeByte].totalSize--;
00295     }
00296     assert((asize & VMemPool::alignmentMask) == 0);
00297 
00298     if (asize < FREEBK_MINSIZE) {
00299         // The block is too small to go on the free list; fill it with
00300         // freeBytes instead.
00301         while (size--) {
00302             p.setType(VMemPool::freeByte);
00303             p.sp++;
00304         }
00305         callback[(int)VMemPool::freeByte].totalSize += asize;
00306     } else {
00307         p.setLength(asize);
00308         callback[(int)VMemPool::freeBlock].totalSize += asize;
00309 
00310         if (freedMemCursor == SNULL) {
00311             freedMemCursor = p;
00312             p.setNext(p);
00313         } else {
00314             p.setNext(freedMemCursor.next());
00315             freedMemCursor.setNext(p);
00316         }       
00317     }
00318     mu.unlock();
00319 }
00320 
00321 VMemPool::typeCode
00322 VMemPool::type(void* addr) throw ()
00323 {
00324     VMemPoolBlockSP p;
00325     p.sp = VMemPool::shortenPointer(addr);
00326     return p.type();
00327 }
00328 
00329 void
00330 VMemPool::gc(ShortId keepDerivedSid) throw ()
00331 {
00332     int i;
00333     pthread_once(&once, VMemPool_init);
00334     mu.lock();
00335     // Source mark phase.  Recursively mark everything reachable from
00336     // the roots.
00337     for (i = 0; i < (int) VMemPool::maxTypeCodes; i++) {
00338         if (callback[i].markcb != NULL) {
00339             callback[i].markcb(callback[i].markcl,
00340                                (VMemPool::typeCode) i);
00341         }
00342         callback[i].totalSize = 0;
00343     }
00344 
00345     // Recursively mark immutable directories that are on the derived
00346     // keep list and everything reachable from them.  (Note that we
00347     // don't do anything about the derived keep time, because sources
00348     // are not leased; see design document on Vesta internal home page.)
00349     if (keepDerivedSid != NullShortId) {
00350         SourceOrDerived sidstream;
00351         sidstream.open(keepDerivedSid); 
00352         sidstream >> hex;
00353         for (;;) {
00354             ShortId nextkeep;
00355             sidstream >> nextkeep;
00356             if (sidstream.fail()) {
00357                 assert(sidstream.eof());
00358                 break;
00359             }
00360             if (SourceOrDerived::dirShortId(nextkeep)) {
00361                 Bit32 sp = GetDirShortId(nextkeep);
00362                 if (sp != 0) {
00363                     VDirChangeable vs(VestaSource::immutableDirectory,
00364                                       (Bit8*) VMemPool::lengthenPointer(sp));
00365                     if (!vs.hasName()) {
00366                         vs.setHasName(true);
00367                         vs.mark();
00368                     }
00369                 }
00370             }
00371         }
00372         sidstream.close();
00373     }
00374 
00375     // Sweep phase.  Also rebuilds the free list, compacting adjacent
00376     // free blocks.
00377     VMemPoolBlockSP p;
00378     p.sp = VMemPool::shortenPointer(base);
00379     VMemPoolBlockSP curFree;
00380     curFree = SNULL;
00381     Bit32 curFreeSize = 0;
00382     freedMemCursor = SNULL;
00383     while (p.sp < unusedMem.sp) {
00384         VMemPool::typeCode ptype = p.type();
00385         Bit32 size;
00386         bool free =
00387           !callback[(int) ptype].sweepcb(callback[(int) ptype].sweepcl, 
00388                                          ptype, (void*)
00389                                          VMemPool::lengthenPointer(p.sp),
00390                                          size);
00391         if (free) {
00392             //Repos::dprintf(DBG_VMEMPOOL, 
00393             //        "gc free, type %d, sp 0x%08x\n", (int) ptype, p.sp);
00394             if (curFree == SNULL) {
00395                 curFree = p;
00396                 curFreeSize = size;
00397             } else {
00398                 curFreeSize += size;
00399             }
00400         } else {
00401             if (curFree != SNULL) {
00402                 // Skip free bytes to achieve required alignment
00403                 while ((curFree.sp - 1) & VMemPool::alignmentMask) {
00404                     callback[(int)VMemPool::freeByte].totalSize++;
00405                     curFree.setType(VMemPool::freeByte);
00406                     curFree.sp++;
00407                     curFreeSize--;
00408                 }
00409                 assert((curFreeSize & VMemPool::alignmentMask) == 0);
00410                 if (curFreeSize < FREEBK_MINSIZE) {
00411                     callback[(int)VMemPool::freeByte].totalSize +=curFreeSize;
00412                     while (curFreeSize--) {
00413                         curFree.setType(VMemPool::freeByte);
00414                         curFree.sp++;
00415                     }
00416                 } else {
00417                     callback[(int)VMemPool::freeBlock].totalSize +=curFreeSize;
00418                     curFree.setType(VMemPool::freeBlock);
00419                     curFree.setLength(curFreeSize);
00420                     if (freedMemCursor == SNULL) {
00421                         freedMemCursor = curFree;
00422                         curFree.setNext(curFree);
00423                     } else {
00424                         curFree.setNext(freedMemCursor.next());
00425                         freedMemCursor.setNext(curFree);
00426                     }
00427                 }
00428                 curFree = SNULL;
00429                 curFreeSize = 0;
00430             }
00431             callback[(int) ptype].totalSize += size;
00432         }
00433         p.sp += size;
00434         assert(p.sp <= unusedMem.sp); // bug trap
00435     }
00436 
00437     if (curFree != SNULL) {
00438       // Adjust curFree to proper alignment
00439       while ((curFree.sp - 1) & VMemPool::alignmentMask) {
00440         assert(curFree.sp < unusedMemEnd.sp);
00441         callback[(int)VMemPool::freeByte].totalSize++;
00442         curFree.setType(VMemPool::freeByte);
00443         curFree.sp++;
00444         curFreeSize--;
00445       }
00446       unusedMem = curFree;
00447     }    
00448 
00449     // Enumerate and log freed DirShortIds
00450     LogAllDirShortIds("fsid");
00451 
00452     // Rebuild hash tables
00453     VMemPool::rebuildDirShortIdTable();
00454 
00455     if (Repos::isDebugLevel(DBG_VMEMPOOL)) {
00456       Repos::dprintf(0, "Memory pool size statistics:\n"
00457                      "freeByte\t%u\n"
00458                      "freeBlock\t%u\n"
00459                      "vDirChangeable\t%u\n"
00460                      "vForward\t%u\n"
00461                      "vDirEvaluator\t%u\n"
00462                      "vDirImmutable\t%u\n"
00463                      "vAttrib\t\t%u\n"
00464                      "vDirAppendable\t%u\n",
00465                      callback[freeByte].totalSize,
00466                      callback[freeBlock].totalSize,
00467                      callback[vDirChangeable].totalSize,
00468                      callback[vForward].totalSize,
00469                      callback[vDirEvaluator].totalSize,
00470                      callback[vDirImmutable].totalSize,
00471                      callback[vAttrib].totalSize,
00472                      callback[vDirAppendable].totalSize);
00473       VDirChangeable::printStats();
00474     }
00475 
00476     mu.unlock();
00477 }
00478 
00479 // Assumes mutex is locked!
00480 void
00481 VMemPool::rebuildDirShortIdTable() throw ()
00482 {
00483     DeleteAllDirShortIds();
00484     DeleteAllFPShortId();
00485 
00486     // Sweep through memory, rebuilding the DirShortId table
00487     // and the FPShortId table.
00488     VMemPoolBlockSP p;
00489     p.sp = VMemPool::shortenPointer(base);
00490     while (p.sp < unusedMem.sp) {
00491         VMemPool::typeCode ptype = p.type();
00492         Bit32 size = 0, oldsize;
00493         oldsize = size; // for debugging only
00494         callback[(int) ptype].rebuildcb(callback[(int) ptype].rebuildcl, 
00495                                         ptype, (void*)
00496                                         VMemPool::lengthenPointer(p.sp),
00497                                         size);
00498         assert(size > 0); // bug trap
00499         p.sp += size;
00500         assert(p.sp <= unusedMem.sp); // bug trap
00501     }
00502 }
00503 
00504 void
00505 VMemPool::registerCallbacks(VMemPool::typeCode type,
00506                             VMemPool::markCallback markcb, void* markcl,
00507                             VMemPool::sweepCallback sweepcb, void* sweepcl,
00508                             VMemPool::rebuildCallback rebuildcb,
00509                             void* rebuildcl) throw ()
00510 {
00511     pthread_once(&once, VMemPool_init);
00512     mu.lock();
00513     callback[(int) type].markcb = markcb;
00514     callback[(int) type].markcl = markcl;
00515     callback[(int) type].sweepcb = sweepcb;
00516     callback[(int) type].sweepcl = sweepcl;
00517     callback[(int) type].rebuildcb = rebuildcb;
00518     callback[(int) type].rebuildcl = rebuildcl;
00519     callback[(int) type].totalSize = 0;
00520     mu.unlock();
00521 }
00522 
00523 // Callbacks for free bytes and blocks
00524 static bool sweepFreeByteCallback(void* closure, VMemPool::typeCode type,
00525                                   void* addr, Bit32& size)
00526 {
00527     size = 1;
00528     return false;
00529 }
00530 
00531 static void rebuildFreeByteCallback(void* closure, VMemPool::typeCode type,
00532                                     void* addr, Bit32& size)
00533 {
00534     size = 1;
00535 }
00536 
00537 static bool sweepFreeBlockCallback(void* closure, VMemPool::typeCode type,
00538                                    void* addr, Bit32& size)
00539 {
00540     size = ((VMemPoolBlockSP) VMemPool::shortenPointer(addr)).length();
00541     return false;
00542 }
00543 
00544 static void rebuildFreeBlockCallback(void* closure, VMemPool::typeCode type,
00545                                      void* addr, Bit32& size)
00546 {
00547     size = ((VMemPoolBlockSP) VMemPool::shortenPointer(addr)).length();
00548 }
00549 
00550 void
00551 VMemPool::init()
00552 {
00553     page_size = sysconf(_SC_PAGE_SIZE);
00554     setCurrentCkptVersion(CkptMaxVersion); // default if no ckpt read
00555     mu.lock();
00556     Bit8* baseReq;
00557     try {
00558         Text t;
00559         if (VestaConfig::get("Repository", "VMemPool_size", t)) {
00560             // Clear errno, as strtoul will only modify it on
00561             // overflow.
00562             errno = 0;
00563             totalSize = strtoul(t.cchars(), NULL, 0);
00564             if(errno == ERANGE)
00565               {
00566                 Repos::dprintf(DBG_ALWAYS,
00567                                "Fatal: [Repository]VMemPool_size is too large\n");
00568                 abort();
00569               }
00570             assert(totalSize > 0);
00571             totalSize = (((totalSize - 1) / page_size) + 1) * page_size;
00572         } else {
00573             totalSize = page_size;
00574         }
00575         if (VestaConfig::get("Repository", "VMemPool_base", t)) {
00576             // Clear errno, as strtoul will only modify it on
00577             // overflow.
00578             errno = 0;
00579             baseReq = (Bit8*) strtoul(t.cchars(), NULL, 0);
00580             if(errno == ERANGE)
00581               {
00582                 Repos::dprintf(DBG_ALWAYS,
00583                                "Fatal: [Repository]VMemPool_base is too large\n");
00584                 abort();
00585               }
00586             baseReq = (Bit8*) (((((PointerInt) baseReq - 1)
00587                                  / page_size) + 1) * page_size);
00588         } else {
00589             baseReq = NULL;
00590         }
00591         if (VestaConfig::get("Repository", "VMemPool_minGrowBy", t)) {
00592             // Clear errno, as strtoul will only modify it on
00593             // overflow.
00594             errno = 0;
00595             minGrowBy = (Bit32) strtoul(t.cchars(), NULL, 0);
00596             if(errno == ERANGE)
00597               {
00598                 Repos::dprintf(DBG_ALWAYS,
00599                                "Fatal: [Repository]VMemPool_minGrowBy is too large\n");
00600                 abort();
00601               }
00602         } else {
00603             minGrowBy = page_size;
00604         }
00605     } catch (VestaConfig::failure f) {
00606         Repos::dprintf(DBG_ALWAYS, "VMemPool::init got VestaConfig::failure: %s\n",
00607                        f.msg.cchars());
00608         abort();
00609     }
00610 #if MALLOC_VMEMPOOL
00611     base = (Bit8*) malloc(totalSize);
00612     assert(base != NULL);
00613 #else
00614     if (baseReq == NULL) {
00615         base = (Bit8*) mmap(baseReq, totalSize, PROT_READ|PROT_WRITE,
00616                             MAP_ANONYMOUS|/*MAP_VARIABLE|*/MAP_PRIVATE, -1, 0);
00617     } else {
00618         base = (Bit8*) mmap(baseReq, totalSize, PROT_READ|PROT_WRITE,
00619                             MAP_ANONYMOUS|MAP_FIXED|MAP_PRIVATE, -1, 0);
00620     }
00621     assert(base != (Bit8*) -1);
00622 #endif
00623     unusedMemEnd.sp = totalSize;
00624     freedMemCursor = SNULL;
00625     unusedMem.sp = VMemPool::shortenPointer(base);
00626     callback[(int) VMemPool::freeByte].sweepcb = sweepFreeByteCallback;
00627     callback[(int) VMemPool::freeByte].rebuildcb = rebuildFreeByteCallback;
00628     callback[(int) VMemPool::freeByte].totalSize = 0;
00629     callback[(int) VMemPool::freeBlock].sweepcb = sweepFreeBlockCallback;
00630     callback[(int) VMemPool::freeBlock].rebuildcb = rebuildFreeBlockCallback;
00631     callback[(int) VMemPool::freeBlock].totalSize = totalSize;
00632     Repos::dprintf(DBG_VMEMPOOL, "VMemPool baseReq 0x%p, base 0x%p,\n"
00633                    "  size 0x%x, minGrowBy 0x%x\n",
00634                    baseReq, base, totalSize, minGrowBy);
00635     mu.unlock();
00636 }
00637 
00638 void
00639 VMemPool::grow(Bit32 growBy) throw ()
00640 {
00641 #if NOGROW_VMEMPOOL
00642     Repos::dprintf(DBG_ALWAYS, 
00643                    "Out of memory; crashing! (total %d, used %d, req %d)\n",
00644                    unusedMemEnd.sp, unusedMem.sp, growBy);
00645     abort();
00646 #else
00647     Bit32 gb = growBy;
00648     if (gb < minGrowBy) gb = minGrowBy;
00649 #if MALLOC_VMEMPOOL
00650     // Can this work?  Might invalidate pointers in other threads
00651     base = (Bit8*) realloc(base, totalSize + gb);
00652     assert(base != NULL);
00653 #else
00654     gb = (((gb - 1) / page_size) + 1) * page_size;
00655     void* more = mmap(base + totalSize, gb, PROT_READ|PROT_WRITE,
00656                       MAP_ANONYMOUS|/*MAP_VARIABLE|*/MAP_PRIVATE, -1, 0);
00657     assert(more == base + totalSize);
00658 #endif
00659     totalSize += gb;
00660     callback[(int)VMemPool::freeBlock].totalSize += gb;
00661     Repos::dprintf(DBG_VMEMPOOL, 
00662                    "VMemPool grow req 0x%x, rounded 0x%x, new size 0x%x\n",
00663                    growBy, gb, totalSize);
00664     unusedMemEnd.sp = totalSize;
00665 #endif
00666 }
00667 
00668 //
00669 // Checkpointing code
00670 //
00671 
00672 void
00673 VMemPool::writeCheckpoint(fstream& ckpt) throw ()
00674 {
00675     Bit32 nextSP = 1;
00676     Bit32 endianFlag = 0x01020304;
00677 
00678     mu.lock(); // needed?
00679 
00680     // Get pointers to roots now; don't call these functions after
00681     // possibly munging memory, because they read the attributes to
00682     // set the access permissions.
00683     VestaSource* rr = VestaSource::repositoryRoot();
00684     VestaSource* mr = VestaSource::mutableRoot();
00685     VestaSource* vr = VestaSource::volatileRoot();
00686 
00687     // Start checkpointing stable memory
00688     setCurrentCkptVersion(CkptMaxVersion);
00689     ckpt << "(smem " << CkptMaxVersion << endl;
00690     ckpt.write((char *) &endianFlag, sizeof(Bit32));
00691 
00692     // Leave space to write first unused SP value
00693     streampos lenpos = ckpt.tellp();
00694     ckpt.write((char *) &nextSP, sizeof(Bit32));
00695 
00696     // Checkpoint all directory structure and internal attributes
00697     Bit32 rSP = rr->checkpoint(nextSP, ckpt);
00698     Bit32 mSP = mr->checkpoint(nextSP, ckpt);
00699     CheckpointAllDirShortIds(nextSP, ckpt);
00700 
00701     // Checkpoint attributes of stable roots
00702     VestaAttribsRep* ar;
00703     ar = (VestaAttribsRep*) VMemPool::lengthenPointer(rr->firstAttrib());
00704     Bit32 raSP = 0;
00705     if (ar) raSP = ar->checkpoint(nextSP, ckpt);
00706     rr->rep = NULL;
00707     *(Bit32*)rr->attribs = 0;
00708     ar = (VestaAttribsRep*) VMemPool::lengthenPointer(mr->firstAttrib());
00709     Bit32 maSP = 0;
00710     if (ar) maSP = ar->checkpoint(nextSP, ckpt);
00711     mr->rep = NULL;
00712     *(Bit32*)mr->attribs = 0;
00713 
00714     // Write end of stable memory checkpoint
00715     streampos endpos = ckpt.tellp();
00716     ckpt.seekp(lenpos, fstream::beg);
00717     ckpt.write((char *) &nextSP, sizeof(Bit32));
00718     ckpt.seekp(endpos, fstream::beg);
00719     ckpt << "\n)\n";
00720 
00721     // Record new stable root rep and attrib pointers
00722     ckpt << "(rroot " << rSP << " " << raSP << ")\n";
00723     ckpt << "(mroot " << mSP << " " << maSP << ")\n";
00724 
00725     // Start checkpointing volatile memory (for immediate re-read)
00726     ckpt << "(vmem " << CkptMaxVersion << endl;
00727     ckpt.write((char *) &endianFlag, sizeof(Bit32));
00728 
00729     // Leave space to write first unused SP value
00730     lenpos = ckpt.tellp();
00731     ckpt.write((char *) &nextSP, sizeof(Bit32));
00732 
00733     (void) vr->checkpoint(nextSP, ckpt);
00734 
00735     // Checkpoint attributes of volatile root
00736     ar = (VestaAttribsRep*) VMemPool::lengthenPointer(vr->firstAttrib());
00737     Bit32 vaSP = 0;
00738     if (ar) vaSP = ar->checkpoint(nextSP, ckpt);
00739     *(Bit32*)vr->attribs = 0;
00740 
00741     // Write end of volatile memory checkpoint
00742     endpos = ckpt.tellp();
00743     ckpt.seekp(lenpos, fstream::beg);
00744     ckpt.write((char *) &nextSP, sizeof(Bit32));
00745     ckpt.seekp(endpos, fstream::beg);
00746     ckpt << "\n)\n";
00747 
00748     // Record new volatile root attrib pointer
00749     ckpt << "(vroot " << vaSP << ")\n";
00750 
00751     mu.unlock();
00752 }
00753 
00754 static void assertGood(fstream& ckpt)
00755 {
00756     if (!ckpt.good()) {
00757         Repos::dprintf(DBG_ALWAYS, 
00758                        "read from checkpoint file failed: errno %d\n", errno);
00759         assert(ckpt.good());  // crash
00760     }
00761 }
00762 
00763 void
00764 VMemPool::readCheckpoint(fstream& ckpt, bool readVolatile) throw ()
00765 {
00766     char buf1[64];
00767     char buf2[64];
00768     char newline;
00769     Bit32 endianFlag;
00770     Bit32 nextSP, rootSP, rootAttribsSP;
00771     int ckptVersion;
00772 
00773     mu.lock();
00774 
00775     // Get pointers to roots
00776     VestaSource* rr = VestaSource::repositoryRoot();
00777     VestaSource* mr = VestaSource::mutableRoot();
00778     VestaSource* vr = VestaSource::volatileRoot();
00779 
00780     // Get header for stable data
00781     ckpt.get(buf1, sizeof(buf1));
00782     assertGood(ckpt);
00783     int ok = sscanf(buf1, "(smem %d", &ckptVersion);
00784     assert(ok == 1);
00785     if (ckptVersion < CkptMinVersion || ckptVersion > CkptMaxVersion) {
00786         Repos::dprintf(DBG_ALWAYS, "checkpoint version %d not between %d and %d\n",
00787                        ckptVersion, CkptMinVersion, CkptMaxVersion);
00788         exit(2);
00789     }
00790     setCurrentCkptVersion(ckptVersion);
00791     ckpt.get(newline);
00792     assertGood(ckpt);
00793     assert(newline == '\n');
00794     ckpt.read((char *) &endianFlag, sizeof(Bit32));
00795     assertGood(ckpt);
00796     switch (endianFlag) {
00797       case 0x01020304:
00798         break;
00799       case 0x04030201:
00800         assert(false);  // Not implemented yet
00801         break;
00802       default:
00803         assert(false);
00804         break;
00805     }
00806     ckpt.read((char *) &nextSP, sizeof(Bit32));
00807     assertGood(ckpt);
00808 
00809     // Get stable data
00810     if (nextSP > unusedMemEnd.sp) {
00811       grow(nextSP - unusedMemEnd.sp);
00812     }
00813     ckpt.read((char *) VMemPool::base, nextSP - 1);
00814     assertGood(ckpt);
00815     freedMemCursor = SNULL;
00816     Bit32 volSP = nextSP;
00817     assert(((nextSP - 1) & VMemPool::alignmentMask) == 0);
00818     unusedMem.sp = nextSP;
00819 
00820     // Get stable data trailer
00821     ckpt.get(newline);
00822     assertGood(ckpt);
00823     assert(newline == '\n');
00824     ckpt.get(buf2, sizeof(buf2));
00825     assertGood(ckpt);
00826     assert(strcmp(buf2, ")") == 0);
00827     ckpt.get(newline);
00828     assertGood(ckpt);
00829     assert(newline == '\n');
00830 
00831     // Get stable roots.
00832     if (ckptVersion >= 5) {
00833         ckpt >> buf1 >> rootSP >> rootAttribsSP >> buf2;
00834     } else {
00835         ckpt >> buf1 >> rootSP >> buf2;
00836         rootAttribsSP = 0;
00837     }   
00838     assertGood(ckpt);
00839     assert(strcmp(buf1, "(rroot") == 0);
00840     assert(strcmp(buf2, ")") == 0);
00841     ckpt.get(newline);
00842     assertGood(ckpt);
00843     assert(newline == '\n');
00844     rr->rep = (Bit8*) VMemPool::lengthenPointer(rootSP);
00845     *(Bit32*) rr->attribs = rootAttribsSP;
00846     rr->resync();
00847 
00848     if (ckptVersion >= 5) {
00849         ckpt >> buf1 >> rootSP >> rootAttribsSP >> buf2;
00850     } else {
00851         ckpt >> buf1 >> rootSP >> buf2;
00852         rootAttribsSP = 0;
00853     }   
00854     assertGood(ckpt);
00855     assert(strcmp(buf1, "(mroot") == 0);
00856     assert(strcmp(buf2, ")") == 0);
00857     ckpt.get(newline);
00858     assertGood(ckpt);
00859     assert(newline == '\n');
00860     mr->rep = (Bit8*) VMemPool::lengthenPointer(rootSP);
00861     *(Bit32*) mr->attribs = rootAttribsSP;
00862     mr->resync();
00863 
00864     // Get header for volatile data
00865     ckpt.get(buf1, sizeof(buf1));
00866     assertGood(ckpt);
00867     ok = sscanf(buf1, "(vmem %d", &ckptVersion);
00868     assert(ok == 1);
00869 
00870     ckpt.get(newline);
00871     assertGood(ckpt);
00872     assert(newline == '\n');
00873     ckpt.read((char *) &endianFlag, sizeof(Bit32));
00874     assertGood(ckpt);
00875     switch (endianFlag) {
00876       case 0x01020304:
00877         break;
00878       case 0x04030201:
00879         assert(false);          // Not implemented yet
00880         break;
00881       default:
00882         assert(false);
00883         break;
00884     }
00885     ckpt.read((char *) &nextSP, sizeof(Bit32));
00886     assertGood(ckpt);
00887 
00888     // Get or skip volatile data
00889     if (readVolatile) {
00890         // We only read volatile checkpoints we just wrote, so no
00891         // need to be able to handle old versions.
00892         assert(ckptVersion == CkptMaxVersion);
00893         assert(nextSP >= unusedMem.sp);
00894 
00895         if (nextSP > unusedMemEnd.sp) {
00896           grow(nextSP - unusedMemEnd.sp);
00897         }
00898 #if 0
00899         // Seems to be a cxx library bug that causes 1 byte to be
00900         // skipped if we try to read 0 bytes here!?
00901         ckpt.read((char *) VMemPool::lengthenPointer(unusedMem.sp),
00902                   nextSP - unusedMem.sp);
00903 #else
00904         if (nextSP > unusedMem.sp) {
00905             ckpt.read((char *) VMemPool::lengthenPointer(unusedMem.sp),
00906                       nextSP - unusedMem.sp);
00907             assertGood(ckpt);
00908         }
00909 #endif
00910         unusedMem.sp = nextSP;
00911     } else {
00912         // Skip volatile data
00913         ckpt.seekg(nextSP - volSP, std::ios::cur);
00914         assertGood(ckpt);
00915     }
00916     
00917     // Get volatile data trailer
00918     ckpt.get(newline);
00919     assertGood(ckpt);
00920     assert(newline == '\n');
00921     ckpt.get(buf2, sizeof(buf2));
00922     assertGood(ckpt);
00923     assert(strcmp(buf2, ")") == 0);
00924     ckpt.get(newline);
00925     assertGood(ckpt);
00926     assert(newline == '\n');
00927 
00928     // Get or skip volatile root attribs pointer
00929     if (ckptVersion >= 11) {
00930       ckpt >> buf1 >> rootAttribsSP >> buf2;
00931       assertGood(ckpt);
00932       assert(strcmp(buf1, "(vroot") == 0);
00933       assert(strcmp(buf2, ")") == 0);
00934       if (readVolatile) {
00935         *(Bit32*) vr->attribs = rootAttribsSP;
00936         vr->resync();
00937       }
00938     }
00939 
00940     rebuildDirShortIdTable();
00941 
00942     mu.unlock();
00943 }
00944 

Generated on Mon May 8 00:48:53 2006 for Vesta by  doxygen 1.4.2