sbm.c

Go to the documentation of this file.
00001 /* SB - Copyright 1982 by Ken Harrenstien, SRI International
00002  *      This software is quasi-public; it may be used freely with
00003  *      like software, but may NOT be sold or made part of licensed
00004  *      products without permission of the author.  In all cases
00005  *      the source code and any modifications thereto must remain
00006  *      available to any user.
00007  *
00008  *      This is part of the SB library package.
00009  *      Any software using the SB library must likewise be made
00010  *      quasi-public, with freely available sources.
00011  */
00012 
00013 #if 0
00014         This file contains the low-level memory allocation
00015 subroutines which are used by the SBLK routines.  The code here
00016 is quite machine-dependent, and the definitions in "sb.h" should be
00017 carefully checked to verify that they are correct for the target
00018 machine.
00019 
00020         The ultimate low-level routine is "sbrk()" which must be
00021 provided by the system''s C library.  SBM expects that successive calls
00022 to sbrk() will return contiguous areas of memory with progressively
00023 higher addresses.  Also, the very first call to sbrk() is assumed to
00024 return a word-aligned address.
00025 #endif /*COMMENT*/
00026 
00027 #include "sb.h"
00028 
00029 #define FUDGE (sizeof(struct smblk))    /* Allow this much fudge in
00030                                 allocation, to prevent undue fragmentation */
00031 
00032 char *(*sbm_debug)();           /* Debug switch - user-furnished routine */
00033 
00034 struct smblk *sbm_nfl;          /* Pointer to node freelist */
00035 struct smblk *sbm_nxtra;        /* Reserved extra free node */
00036 struct smblk *sbm_list;         /* Pointer to smblk memory alloc list.
00037                                  * ALL smblks are strung onto this list
00038                                  * except for the freelist!
00039                                  */
00040 SBMA sbm_lowaddr;               /* Lowest word-aligned address we know about.*/
00041 
00042 /* If compiling with debug switch set, use special routine in place of
00043  * sbrk so we can pretend we have a very limited area of free memory.
00044  */
00045 #ifdef DBG_SIZE
00046 #define SBM_SBRK sbm_brk
00047 char *sbm_brk();
00048 #else
00049 #define SBM_SBRK sbrk
00050 #endif /*DBG_SIZE*/
00051 
00052 /* Forward routine declarations */
00053 char *sbrk();
00054 struct smblk *sbm_nmak(), *sbm_nget(), *sbm_mget(), *sbm_split();
00055 struct smblk *sbm_lmak(), *sbm_err();
00056 
00057 /* SBM_INIT - Initialize storage management.
00058  *      If args are zero, normal initialization is done.  Otherwise,
00059  *      args are understood to be pointers to an area of memory allocated
00060  *      on the stack (eg by an "int mem[2000]" declaration in MAIN) and
00061  *      initialization will include this area in addition to the
00062  *      unused space between "_end" and the start of the stack segment.
00063  *      This is mostly of use for PDP11s which would otherwise waste a lot
00064  *      of address space.
00065  *      Maybe should have a SBM_RESET() function?
00066  */
00067 
00068 struct smblk *
00069 sbm_init(xaddr,xlen)
00070 SBMA xaddr;             /* Address of allocated stack area if any */
00071 SBMO xlen;              /* Size of this area */
00072 {       register struct smblk *sm, *sml;
00073         register char *cp;
00074 
00075         /* Get initial chunk of memory from standard system rtn */
00076         if((cp = SBM_SBRK(SMNODES*sizeof(struct smblk))) == 0
00077           || (int) cp == -1)
00078                 return(sbm_err(0,"Can't sbrk"));
00079         sm = (struct smblk *)cp;                /* Better be word-aligned! */
00080         sbm_lmak(sm,(SBMO)sizeof(struct smblk),SMNODES);      /* Make list */
00081         sbm_nfl = sm;                           /* Point freelist at it */
00082         sbm_lowaddr = (SBMA)sm;                 /* Remember lowest addr seen */
00083 
00084         /* Set up 1st node pointing to all memory from here on up.
00085          * We don't know exactly how much will be available at this point,
00086          * so we just pretend we have the maximum possible.
00087          */
00088         sbm_list = sml = sbm_nget();
00089         sml->smforw = sml->smback = 0;
00090         sml->smflags = SM_USE|SM_NID;           /* Initial flags */
00091         sml->smaddr = (SBMA) sml;
00092         sml->smlen = MAXSBMO;                   /* Pretend we have lots */
00093         sml->smuse = (SMNODES * sizeof(struct smblk));
00094 
00095         /* Now split off everything above initial allocation as NXM. */
00096         sm = sbm_split(sml, sm->smuse);
00097         sml->smflags |= SM_MNODS;       /* Mark 1st node as having SM nodes */
00098         sm->smflags  |= SM_NXM;         /* Mark 2nd node as NXM */
00099 
00100         /* Now possibly set up extra nodes, if stack mem is being allocated
00101          * (From our viewpoint it looks as if a chunk in the middle of
00102          * the initial NXM section has been declared usable)
00103          */
00104         if(xlen)
00105           {     /* Allow for "extra" static stack memory */
00106                 /* Will lose if xaddr <= 1st NXM! */
00107                 sml = sbm_split(sm, (SBMO)(xaddr - sm->smaddr));
00108                 sbm_split(sml, xlen);           /* Split off following NXM */
00109                 sml->smflags &= ~(SM_USE|SM_NXM); /* This node is free mem! */
00110           }
00111 
00112         /* Now set up a small additional node which points to the NXM
00113          * that we cannot get from SBRK.  At this stage, this is just
00114          * a place-holder, to reserve the node so we don't have to
00115          * worry about running out of nodes at the same time sbrk stops
00116          * returning memory.
00117          * SM points to the NXM that we expect SBRK to dig into.
00118          */
00119         sbm_split(sm, sm->smlen - WDSIZE); /* Chop off teensy bit */
00120         sm->smflags &= ~SM_USE;         /* Now mark NXM "free"!! */
00121 
00122         /* Finally, reserve an "extra" SM node for use by sbm_nget
00123          * when it is allocating more freelist node chunks.
00124          */
00125         sbm_nxtra = sbm_nget();
00126 
00127         return(sbm_list);
00128 }
00129 
00130 /* SBM_NGET() - Get a free SM node.
00131  *      Note the hair to provide a spare SM node when
00132  *      we are allocating memory for more SM nodes.  This is necessary
00133  *      because sbm_mget and sbm_nget call each other recursively and
00134  *      sbm_mget cannot create any new memory without a SM node to point
00135  *      at the allocated chunk.
00136  */
00137 struct smblk *
00138 sbm_nget()
00139 {       register struct smblk *sm, *sml;
00140 
00141         if(!(sm = sbm_nfl))             /* Get a node from freelist */
00142           {     /* Freelist is empty, try to allocate more nodes. */
00143 
00144                 /* Put our "spare" smblk on freelist temporarily so that
00145                  * sbm_mget has a chance of winning.
00146                  * Infinite recursion is avoided by a test
00147                  * in sbm_mget which checks sbm_nfl and sbm_nxtra.
00148                  */
00149                 if(!(sm = sbm_nxtra))
00150                         return(sbm_err(0,"Zero sbm_nxtra!"));
00151                 sm->smforw = 0;
00152                 sbm_nfl = sm;
00153                 sbm_nxtra = 0;
00154 
00155                 /* Try to allocate another chunk of SM nodes. */
00156                 sml = sbm_nmak(sizeof(struct smblk),SM_MNODS);
00157 
00158                 /* Put the new free nodes (if any) on freelist.
00159                  * Done this way because freelist may have had one or two
00160                  * nodes added to it by sbm_mget, so can't just stick
00161                  * a new pointer in sbm_nfl.
00162                  */
00163                 while(sm = sml)
00164                   {     sml = sm->smforw;
00165                         sbm_nfre(sm);
00166                   }
00167 
00168                 /* Now reserve an extra node again.
00169                  * It is an error if there is nothing on freelist here,
00170                  * because even if sbm_mget failed the "extra node" should
00171                  * still be on freelist.  The check for a zero sbm_nxtra
00172                  * above will catch such an error.
00173                  */
00174                 sbm_nxtra = sbm_nget();
00175 
00176                 /* Now see if anything to return */
00177                 if(!(sm = sbm_nfl))             /* If freelist empty again, */
00178                         return(0);              /* give up. */
00179           }
00180         sbm_nfl = sm->smforw;   /* If win, take it off freelist */
00181         return(sm);             /* Return ptr or 0 if none */
00182 }
00183 
00184 /* SBM_NFRE(sm) - Return a SM node to the SM freelist.
00185  */
00186 sbm_nfre(smp)
00187 struct smblk *smp;
00188 {       register struct smblk *sm;
00189         (sm = smp)->smflags = 0;
00190         sm->smforw = sbm_nfl;
00191         sbm_nfl = sm;
00192 }
00193 
00194 /* SBM_NMAK(elsize, flag) - Make (allocate & build) a typeless node freelist.
00195  */
00196 struct smblk *
00197 sbm_nmak(elsize, flag)
00198 SBMO elsize;
00199 unsigned flag;
00200 {       register struct smblk *sm, *smp;
00201         register int cnt;
00202 
00203         if((sm = sbm_mget(SMNODES*elsize,SMNODES*elsize)) == 0)
00204                 return(0);
00205 
00206         sm->smflags |= flag;            /* Indicate type of nodes */
00207         cnt = sm->smlen/elsize;         /* Find # nodes that will fit */
00208         sm->smuse = cnt * elsize;       /* Actual size used */
00209         smp = (struct smblk *)(sm->smaddr);     /* Ptr to 1st loc of mem */
00210         sbm_lmak(smp, (SBMO)elsize, cnt);       /* Build freelist */
00211         return(smp);            /* Return 1st free node. Caller is */
00212                                 /* responsible for setting freelist ptr. */
00213 }
00214 
00215 /* SBM_LMAK - Build freelist of typeless nodes.
00216  *      Note this does not allocate memory, it just converts an already
00217  *      allocated memory area.
00218  */
00219 struct smblk *
00220 sbm_lmak(addr, elsize, num)
00221 SBMA addr;
00222 SBMO elsize;
00223 int num;
00224 {       register struct smblk *sm, *smp;
00225         register int cnt;
00226 
00227         smp = (struct smblk *) addr;
00228         if((cnt = num) <= 0)
00229                 return(0);
00230         do {    sm = smp;       /* Save ptr */
00231                 sm->smforw = (smp = (struct smblk *) ((SBMA)smp + elsize));
00232                 sm->smflags = 0;
00233           } while(--cnt);
00234         sm->smforw = 0;         /* Last node points to nothing */
00235         return(sm);             /* Return ptr to last node */
00236 }
00237 
00238 /* SBM_NMOV(sm1, sm2, begp, elsize) - Move a typeless node.
00239  *      Copy sm1 to sm2, adjust ptrs, leave sm1 free.
00240  */
00241 sbm_nmov(smp1,smp2,begp,elsize)
00242 struct smblk *smp1, *smp2, **begp;
00243 int elsize;
00244 {       register struct smblk *sm;
00245 
00246         bcopy((SBMA)smp1,(SBMA)(sm = smp2), elsize);     /* Copy the stuff */
00247         if(sm->smforw) sm->smforw->smback = sm; /* Fix up links */
00248         if(sm->smback) sm->smback->smforw = sm;
00249         else *begp = sm;
00250 }
00251 
00252 /* SBM_MGET(min,max) - Get a SMBLK with specified amount of memory.
00253  *      Returns 0 if none available.
00254  *      Memory is guaranteed to start on word boundary, but may not
00255  *              end on one.  Note that sbm_mfree is responsible for
00256  *              ensuring that free mem starts word-aligned.
00257  *      A subtle but major concern of this code is the number of freelist
00258  * nodes gobbled by a single call.  If the freelist happens to not have
00259  * enough nodes, then a recursive call to sbm_mget is made (via sbm_nget)
00260  * in order to allocate a new batch of freelist nodes!  sbm_nget will
00261  * always provide a single "spare" node during such an allocation, but
00262  * there is only one and it is essential that sbm_mget gobble only ONE
00263  * (if any) during such a call, which is indicated by sbm_nxtra==0.
00264  *      The maximum # of freelist nodes that sbm_mget can gobble is
00265  * 2, when (1) NXM memory is obtained, and a SM is needed to point at
00266  * the new free mem, plus (2) the resulting SM is too big, and has to
00267  * be split up, which requires another SM for the remainder.
00268  *      The "used-NXM" smblk is set up at init time precisely in order to
00269  * avoid the necessity of creating it here when sbrk stops winning, since
00270  * that would require yet another freelist node and make it possible for
00271  * sbm_mget to gobble 3 during one call -- too many.
00272  *      Further note: the sbm_nfl checks are necessary in order
00273  * to ensure that a SM node is available for use by sbm_split.  Otherwise
00274  * the calls to sbm_split might create a new SM freelist by gobbling the
00275  * very memory which we are hoping to return!
00276  */
00277 SBMO sbm_chksiz = SMCHUNKSIZ;   /* Current chunk size to feed sbrk */
00278 
00279 struct smblk *
00280 sbm_mget(cmin,cmax)
00281 SBMO cmin,cmax;
00282 {       register struct smblk *sm, *sml;
00283         register SBMO csiz;
00284         register SBMA addr, xaddr;
00285 
00286         if((sm = sbm_list) == 0         /* If never done, */
00287           && (sm = sbm_init((SBMA)0,(SBMO)0)) == 0)     /* initialize mem alloc stuff. */
00288                 return(0);              /* Can't init??? */
00289 
00290         /* Round up sizes to word boundary */
00291         if(rndrem(cmin)) cmin = rndup(cmin);
00292         if(rndrem(cmax)) cmax = rndup(cmax);
00293 
00294         /* Search for a free block having enough memory.
00295          * If run into a free-NXM block, always "win", since there may be
00296          * a combination of preceding free-mem and new mem which will satisfy
00297          * the request.  If it turns out this didn't work, we'll just fail
00298          * a little farther on.
00299          */
00300 retry:  csiz = cmin;                    /* Set size that will satisfy us */
00301         do {
00302                 if(  ((sm->smflags&SM_USE) == 0)
00303                   && ((sm->smlen >= csiz) || (sm->smflags&SM_NXM)) )
00304                         break;
00305           } while(sm = sm->smforw);
00306         if(sm == 0)
00307                 return(0);      /* Found none that minimum would fit */
00308 
00309         if(sm->smflags&SM_NXM)
00310           {     /* Found free area, but it's marked NXM and the system
00311                  * must be persuaded (via sbrk) to let us use that portion
00312                  * of our address space.  Grab a good-sized chunk.
00313                  */
00314                 if(sbm_nfl == 0)        /* Verify a spare SM node is avail */
00315                         goto getnod;    /* Nope, must get one. */
00316 
00317                 /* Decide amount of mem to ask system for, via sbrk.
00318                  * The fine point here is the check of sbm_nxtra to make sure
00319                  * that, when building more freelist nodes, we don't have
00320                  * to use more than one SM node in the process.  If we
00321                  * asked for too much mem, we'd have to use a SM node
00322                  * to hold the excess after splitting.
00323                  */
00324                 csiz = cmax;
00325                 if(sbm_nxtra            /* If normal then try for big chunk */
00326                   && csiz < sbm_chksiz) csiz = sbm_chksiz;      /* Max */
00327                 if (csiz > sm->smlen)  csiz = sm->smlen;        /* Min */
00328 
00329                 /* Get the NXM mem */
00330                 if((addr = (SBMA)SBM_SBRK(csiz)) != sm->smaddr)
00331                   {     /* Unexpected value returned from SBRK! */
00332 
00333                         if((int)addr != 0 && (int)addr != -1)
00334                           {     return(sbm_err(0,"SBRK %o != %o", addr,
00335                                                 sm->smaddr));
00336 #if 0
00337                         /* If value indicates couldn't get the stuff, then
00338                          * we have probably hit our limit and the rest of
00339                          * NXM should be declared "used" to prevent further
00340                          * hopeless sbrk calls.  We split off the portion
00341                          * of NXM that is known for sure to be unavailable,
00342                          * and mark it "used".  If a "used NXM" area already
00343                          * exists following this one, the two are merged.
00344                          * The chunk size is then reduced by half, so
00345                          * only log2(SMCHUNKSIZ) attempts will be made, and
00346                          * we try again.
00347                          */
00348                                 /* If returned some mem which starts outside
00349                                  * the NXM then something is screwed up. */
00350                                 if(addr < sm->smaddr
00351                                   || (addr >= sm->smaddr+sm->smlen))
00352                                         return(sbm_err(0,"SBRK %o != %o",
00353                                                 addr, sm->smaddr));
00354                                 /* Got some mem, falls within NXM.
00355                                  * Presumably someone else has called sbrk
00356                                  * since last time, so we need to fence off
00357                                  * the intervening area. */
00358                                 sm = sbm_split((sml=sm),(addr - sm->smaddr));
00359                                 sml->smflags |= SM_USE|SM_EXT;
00360                                 return(sbm_mget(cmin,cmax));
00361 #endif /*COMMENT*/
00362                           }
00363 
00364                         /* Handle case of SBRK claiming no more memory.
00365                          * Gobble as much as we can, and then turn this NXM
00366                          * block into a free-mem block, and leave the
00367                          * remainder in the used-NXM block (which should
00368                          * immediately follow this free-NXM block!)
00369                          */
00370                         if(!(sml = sm->smforw)  /* Ensure have used-NXM blk */
00371                           || (sml->smflags&(SM_USE|SM_NXM))
00372                                         != (SM_USE|SM_NXM))
00373                                 return(sbm_err(0,"No uNXM node!"));
00374                         xaddr = sm->smaddr;     /* Use this for checking */
00375                         sm->smuse = 0;          /* Use this for sum */
00376                         for(csiz = sm->smlen; csiz > 0;)
00377                           {     addr = SBM_SBRK(csiz);
00378                                 if((int)addr == 0 || (int)addr == -1)
00379                                   {     csiz >>= 1;
00380                                         continue;
00381                                   }
00382                                 if(addr != xaddr)
00383                                         return(sbm_err(0,"SBRK %o != %o", addr,
00384                                                 xaddr));
00385                                 sm->smuse += csiz;
00386                                 xaddr += csiz;
00387                           }
00388 
00389                         /* Have gobbled as much from SBRK as we could.
00390                          * Turn the free-NXM block into a free-mem block,
00391                          * unless we got nothing, in which case just merge
00392                          * it into the used-NXM block and continue
00393                          * searching from this point.
00394                          */
00395                         if(!(csiz = sm->smuse)) /* Get total added */
00396                           {     sm->smflags = sml->smflags;     /* Ugh. */
00397                                 sbm_mmrg(sm);
00398                                 goto retry;             /* Keep looking */
00399                           }
00400                         else
00401                           {     sml->smaddr = sm->smaddr + csiz;
00402                                 sml->smlen += sm->smlen - csiz;
00403                                 sm->smlen = csiz;
00404                                 sm->smflags &= ~SM_NXM; /* No longer NXM */
00405                           }
00406                   }
00407 
00408                 /* Here when we've acquired CSIZ more memory from sbrk.
00409                  * If preceding mem area is not in use, merge new mem
00410                  * into it.
00411                  */
00412                 if((sml = sm->smback) && 
00413                   (sml->smflags&(SM_USE|SM_NXM))==0)    /* Previous free? */
00414                   {     sml->smlen += csiz;             /* Yes, simple! */
00415                         sm->smaddr += csiz;             /* Fix up */
00416                         if((sm->smlen -= csiz) == 0)    /* If no NXM left,*/
00417                                 sbm_mmrg(sml);  /* Merge NXM node w/prev */
00418                         sm = sml;               /* Prev is now winning node */
00419                   }
00420                 else
00421                   {     /* Prev node isn't a free area.  Split up the NXM
00422                          * node to account for acquired mem, unless we
00423                          * gobbled all the mem available.
00424                          */
00425                         if(sm->smlen > csiz     /* Split unless all used */
00426                           && !sbm_split(sm,csiz)) /* Call shd always win */
00427                                 return(sbm_err(0,"getsplit err: %o",sm));
00428                         sm->smflags &= ~SM_NXM; /* Node is now real mem */
00429                   }
00430 
00431                 /* Now make a final check that we have enough memory.
00432                  * This can fail because SBRK may not have been able
00433                  * to gobble enough memory, either because (1) not
00434                  * as much NXM was available as we thought,
00435                  * or (2) we noticed the free-NXM area and immediately
00436                  * gambled on trying it without checking any lengths.
00437                  * In any case, we try again starting from the current SM
00438                  * because there may be more free mem higher up (eg on
00439                  * stack).
00440                  */
00441                 if(sm->smlen < cmin)
00442                         goto retry;
00443           }
00444 
00445         /* Check to see if node has too much mem.  This is especially true
00446          * for memory just acquired via sbrk, which gobbles a huge chunk each
00447          * time.  If there's too much, we split up the area.
00448          */
00449         if(sm->smlen > cmax+FUDGE)      /* Got too much?  (Allow some fudge)*/
00450                 /* Yes, split up so don't gobble too much. */
00451                 if(sbm_nfl)                     /* If success guaranteed, */
00452                         sbm_split(sm,cmax);     /* split it, all's well. */
00453                 else goto getnod;
00454 
00455         sm->smuse = 0;
00456         sm->smflags |= SM_USE;  /* Finally seize it by marking "in-use". */
00457         return(sm);
00458 
00459         /* Come here when we will need to get another SM node but the
00460          * SM freelist is empty.  We have to forget about using the area
00461          * we just found, because sbm_nget may gobble it for the
00462          * freelist.  So, we first force a refill of the freelist, and then
00463          * invoke ourselves again on what's left.
00464          */
00465 getnod:
00466         if(sml = sbm_nget())            /* Try to build freelist */
00467           {     sbm_nfre(sml);          /* Won, give node back, */
00468                 sm = sbm_list;          /* and retry, starting over! */
00469                 goto retry;     
00470           }
00471         /* Failed.  Not enough memory for both this request
00472          * and one more block of SM nodes.  Since such a SM_MNODS
00473          * block isn't very big, we are so close to the limits that it
00474          * isn't worth trying to do something fancy here to satisfy the
00475          * original request.  So we just fail.
00476          */
00477         return(0);
00478 }
00479 
00480 #ifdef DBG_SIZE
00481 /* Code for debugging stuff by imposing an artificial limitation on size
00482  * of available memory.
00483  */
00484 SBMO sbm_dlim = MAXSBMO;        /* Amount of mem to allow (default is max) */
00485 
00486 char *
00487 sbm_brk(size)
00488 unsigned size;
00489 {       register char *addr;
00490 
00491         if(size > sbm_dlim) return(0);
00492         addr = sbrk(size);
00493         if((int)addr == 0 || (int)addr == -1)
00494                 return(0);
00495         sbm_dlim -= size;
00496         return(addr);
00497 }
00498 #endif /*DBG_SIZE*/
00499 
00500 /* SBM_MFREE(sm) - Free up an allocated memory area.
00501  */
00502 sbm_mfree(sm)
00503 register struct smblk *sm;
00504 {       register struct smblk *smx;
00505         register SBMO crem;
00506 
00507         sm->smflags &= ~SM_USE;                 /* Say mem is free */
00508         if((smx = sm->smback)                   /* Check preceding mem */
00509           && (smx->smflags&(SM_USE|SM_NXM))==0) /*   If it's free, */
00510                 sbm_mmrg(sm = smx);             /*   then merge 'em. */
00511         if((smx = sm->smforw)                   /* Check following mem */
00512           && (smx->smflags&(SM_USE|SM_NXM))==0) /*   Again, if free,  */
00513                 sbm_mmrg(sm);                   /*   merge them.   */
00514 
00515         if(sm->smlen == 0)              /* Just in case, chk for null blk */
00516           {     if(smx = sm->smback)            /* If pred exists, */
00517                         sbm_mmrg(smx);          /* merge quietly. */
00518                 else {
00519                         sbm_list = sm->smforw;  /* 1st node on list, so */
00520                         sbm_nfre(sm);           /* simply flush it. */
00521                   }
00522                 return;
00523           }
00524 
00525         /* This code is slightly over-general for some machines.
00526          * The pointer subtraction is done in order to get a valid integer
00527          * offset value regardless of the internal representation of a pointer.
00528          * We cannot reliably force alignment via casts; some C implementations
00529          * treat that as a no-op.
00530          */
00531         if(crem = rndrem(sm->smaddr - sbm_lowaddr))     /* On word bndry? */
00532           {     /* No -- must adjust.  All free mem blks MUST, by fiat,
00533                  * start on word boundary.  Here we fix things by
00534                  * making the leftover bytes belong to the previous blk,
00535                  * no matter what it is used for.  Prev blk is guaranteed to
00536                  * (1) Exist (this cannot be 1st blk since 1st is known to
00537                  * start on wd boundary) and to be (2) Non-free (else it would
00538                  * have been merged).
00539                  */
00540                 if((smx = sm->smback) == 0)     /* Get ptr to prev blk */
00541                   {     sbm_err(0,"Align err"); /* Catch screws */
00542                         return;
00543                   }
00544                 crem = WDSIZE - crem;   /* Find # bytes to flush */
00545                 if(crem >= sm->smlen)   /* Make sure node has that many */
00546                   {     sbm_mmrg(smx);  /* Flush node to avoid zero length */
00547                         return;
00548                   }
00549                 smx->smlen += crem;     /* Make stray bytes part of prev */
00550                 sm->smaddr += crem;     /* And flush from current. */
00551                 sm->smlen -= crem;
00552           }
00553 }
00554 
00555 /* SBM_EXP - Expand (or shrink) size of an allocated memory chunk.
00556  *      "nsize" is desired new size; may be larger or smaller than current
00557  *      size.
00558  */
00559 struct smblk *
00560 sbm_exp(sm,size)
00561 register struct smblk *sm;
00562 register SBMO size;
00563 {       register struct smblk *smf;
00564         register SBMO mexp, pred, succ;
00565 
00566         if(sm->smlen >= size)           /* Do we want truncation? */
00567                 goto realo2;            /* Yup, go split block */
00568 
00569         /* Block is expanding. */
00570         mexp = size - sm->smlen;                /* Get # bytes to expand by */
00571         pred = succ = 0;
00572         if((smf = sm->smforw)                   /* See if free mem follows */
00573          && (smf->smflags&(SM_USE|SM_NXM)) == 0)
00574                 if((succ = smf->smlen) >= mexp)
00575                         goto realo1;            /* Quick stuff if succ OK */
00576 
00577         if((smf = sm->smback)                   /* See if free mem precedes */
00578          && (smf->smflags&(SM_USE|SM_NXM)) == 0)
00579                 pred = smf->smlen;
00580 
00581         /* If not enough free space combined on both sides of this chunk,
00582          * we have to look for a completely new block.
00583          */
00584         if(pred+succ < mexp)
00585           {     if((smf = sbm_mget(size,size)) == 0)
00586                         return(0);              /* Couldn't find one */
00587                 else pred = 0;                  /* Won, indicate new block */
00588           }
00589 
00590         /* OK, must copy either into new block or down into predecessor
00591          * (overlap is OK as long as bcopy moves 1st byte first)
00592          */
00593         bcopy(sm->smaddr, smf->smaddr, sm->smlen);
00594         smf->smflags = sm->smflags;     /* Copy extra attribs */
00595         smf->smuse = sm->smuse;
00596         if(!pred)                       /* If invoked sbm_mget */
00597           {     sbm_mfree(sm);          /* then must free up old area */
00598                 return(smf);            /* and can return immediately. */
00599           }
00600         sbm_mmrg(smf);                  /* Merge current into pred blk */
00601         sm = smf;                       /* Now pred is current blk. */
00602 
00603         if(succ)
00604 realo1:         sbm_mmrg(sm);           /* Merge succ into current blk */
00605 realo2: if(sm->smlen > size             /* If now have too much, */
00606           && sbm_split(sm, size))       /* split up and possibly */
00607                 sbm_mfree(sm->smforw);  /* free up unused space. */
00608         return(sm);
00609 
00610         /* Note that sbm_split can fail if it can't get a free node,
00611          * which is only possible if we are reducing the size of an area.
00612          * If it fails, we just return anyway without truncating the area.
00613          */
00614 }
00615 
00616 /* SBM_MMRG(sm) - Merge a memory area with the area following it.
00617  *      The node (and memory area) following the SM pointed to are
00618  *      merged in and the successor node freed up.  The flags
00619  *      and smuse of the current SM (which is not moved or anything)
00620  *      remain the same.
00621  */
00622 sbm_mmrg(smp)
00623 struct smblk *smp;
00624 {       register struct smblk *sm, *sm2;
00625 
00626         sm = smp;
00627         sm->smlen += (sm2 = sm->smforw)->smlen; /* Add succ's len */
00628         if(sm->smforw = sm2->smforw)            /* and fix linkages */
00629                 sm->smforw->smback = sm;
00630         sbm_nfre(sm2);                          /* now can flush succ node */
00631 }
00632 
00633 /* SBM_SPLIT - Split up an area (gets a new smblk to point to split-off
00634  *      portion.)
00635  * Note returned value is ptr to 2nd smblk, since this is a new one.
00636  * Ptr to 1st remains valid since original smblk stays where it is.
00637  * NOTE: Beware of splitting up free mem (SM_USE == 0) since sbm_nget may
00638  * steal it out from under unless precautions are taken!  See comments
00639  * at sbm_mget related to this.
00640  */
00641 struct smblk *
00642 sbm_split(smp,coff)
00643 struct smblk *smp;
00644 SBMO coff;
00645 {       register struct smblk *sm, *smx;
00646         register SBMO csiz;
00647 
00648         if((sm = smp)->smlen <= (csiz = coff))
00649                 return(0);
00650         if((smx = sbm_nget()) == 0)
00651                 return(0);
00652         smx->smlen = sm->smlen - csiz;          /* Set 2nd size */
00653         smx->smaddr = sm->smaddr + csiz;        /* Set 2nd addr */
00654         sm->smlen = csiz;                       /* Take from 1st size */
00655         smx->smflags = sm->smflags;             /* Copy flags */
00656         if(smx->smforw = sm->smforw)            /* Splice 2nd after 1 */
00657                 smx->smforw->smback = smx;
00658         smx->smback = sm;
00659         sm->smforw = smx;                       /* Put 2nd into chain */
00660         return(smx);                            /* Return ptr to 2nd smblk */
00661 }
00662 
00663 #if 0   /* Replaced by "bcopy" for system-dep efficiency */
00664 /* SBM_SCPY - Copy string of bytes.  Somewhat machine-dependent;
00665  *      Tries to be clever about using word moves instead of byte moves.
00666  */
00667 sbm_scpy(from, to, count)       /* Copy count bytes from -> to */
00668 char *from, *to;
00669 unsigned count;
00670 {       register char *s1, *s2;
00671         register unsigned cnt;
00672         int tmp;
00673 
00674         if((cnt = count) == 0)
00675                 return;
00676         s1 = from;
00677         s2 = to;
00678         while(rndrem((int)s1))          /* Get 1st ptr aligned */
00679           {     *s2++ = *s1++;
00680                 if(--cnt == 0) return;
00681           }
00682         if(rndrem((int)s2) == 0)        /* Do wd move if ptr 2 now aligned */
00683           {
00684 #ifdef DUMBPCC /* Code for dumber (Portable C type) compiler */
00685                 register WORD *ap, *bp;
00686                 tmp = cnt;
00687                 ap = (WORD *) s1;
00688                 bp = (WORD *) s2;
00689                 if(cnt = rnddiv(cnt))
00690                         do { *bp++ = *ap++; }
00691                         while(--cnt);
00692                 if ((cnt = rndrem(tmp)) ==0)
00693                         return;
00694                 s1 = (char *) ap;
00695                 s2 = (char *) bp;
00696 #else
00697         /* Tight loop for efficient copying on 11s */
00698                 tmp = cnt;
00699                 if(cnt = rnddiv(cnt))
00700                         do { *((WORD *)s2)++ = *((WORD *)s1)++; }
00701                         while(--cnt);
00702                 if((cnt = rndrem(tmp)) == 0)
00703                         return;
00704 #endif /*-DUMBPCC*/
00705           }                             
00706         do { *s2++ = *s1++; }   /* Finish up with byte loop */
00707         while(--cnt);
00708 }
00709 #endif /*COMMENT*/
00710 
00711 struct smblk *          /* If it returns at all, this is most common type */
00712 sbm_err(val,str,a0,a1,a2,a3)
00713 char *str;
00714 struct smblk *val;
00715 {       int *sptr;
00716 
00717         sptr = (int *) &sptr;   /* Point to self on stack */
00718         sptr += 5;              /* Point to return addr */
00719         if((int)sbm_debug==1)
00720                 abort();
00721         if(sbm_debug)
00722                 (*sbm_debug)(0,*sptr,str,a0,a1,a2,a3);
00723         return(val);
00724 }
00725 
00726 /* These routines correspond to the V7 LIBC routines as described
00727  * in the V7 UPM (3).  They should provide satisfactory emulation
00728  * if the documentation is correct.  Replacement is necessary since
00729  * the SBM routines are jealous and cannot tolerate competition for
00730  * calls of SBRK; i.e. the memory being managed must be contiguous.
00731  */
00732 
00733 /* Guaranteed to return word-aligned pointer to area of AT LEAST
00734  * requested size.  Area size is rounded up to word boundary.
00735  */
00736 
00737 char *
00738 malloc(size)
00739 unsigned size;
00740 {       register struct smblk *sm, **sma;
00741         register SBMO siz;
00742 
00743         siz = rndup(size + sizeof (struct smblk *));   /* Make room for ptr */
00744         if((sm = sbm_mget(siz,siz)) == 0)
00745                 return(0);
00746         *(sma = (struct smblk **)sm->smaddr) = sm; /* Store ptr in addr-1 */
00747         return((char *)++sma);
00748 }
00749 
00750 char *
00751 alloc(size)     /* For V6 programs - note different failure value! */
00752 unsigned size;
00753 {       register char *addr;
00754         return((addr = malloc(size)) ? addr : (char *) -1);
00755 }
00756 
00757 free(ptr)
00758 char *ptr;
00759 {       register struct smblk *sm, **smp;
00760 
00761         smp = &((struct smblk **)ptr)[-1];      /* Point to addr-1 */
00762         sm = *smp;                              /* Pluck SM ptr therefrom */
00763         if(((sm->smflags&0377) != SM_NID) || sm->smaddr != (SBMA)smp)
00764                 return((int)sbm_err(0,"free: bad arg %o", ptr));
00765         sbm_mfree(sm);
00766         return(1);
00767 }
00768 
00769 char *
00770 realloc(ptr,size)
00771 char *ptr;
00772 unsigned size;
00773 {       register struct smblk *sm, **smp;
00774 
00775         smp = &((struct smblk **)ptr)[-1];      /* Point to addr-1 */
00776         sm = *smp;                              /* Pluck SM ptr therefrom */
00777         if(((sm->smflags&0377) != SM_NID) || (sm->smaddr != (SBMA)smp))
00778                 return((char *)sbm_err(0,"realloc: bad arg %o",ptr));
00779         if((sm = sbm_exp(sm, rndup(size+(sizeof(struct smblk *))))) == 0)
00780                 return(0);
00781         *(smp = (struct smblk **)sm->smaddr) = sm;      /* Save smblk ptr */
00782         return((char *)++smp);
00783 }
00784 
00785 char *
00786 calloc(nelem,elsize)
00787 unsigned nelem, elsize;
00788 {       register SBMO cmin;
00789         register WORD *ip;                     /* Clear in units of words */
00790         register char *addr;
00791 
00792         if((cmin = nelem*elsize) == 0           /* Find # bytes to get */
00793           || (addr = malloc(cmin)) == 0)        /* Get it */
00794                 return(0);
00795         ip = (WORD *) addr;                     /* Set up ptr to area */
00796         cmin = rnddiv(cmin+WDSIZE-1);           /* Find # words to clear */
00797         do { *ip++ = 0; } while (--cmin);       /* Zap the area */
00798         return(addr);
00799 }
00800 
00801 /* SBM_NGC() - Specific routine for GC'ing SMBLK nodes.
00802  *
00803  * SBM_XNGC(begp, elsize, type) - Compact nodes of specified type.
00804  *      Scans allocated mem from low to high to find chunks with nodes of
00805  *      the specified type.
00806  *      Flushes current freelist and rebuilds it as scan progresses,
00807  *      such that 1st thing on list is lowest-addr node.  When a node is
00808  *      seen that can be moved, new node is acquired from freelist if
00809  *      it exists, otherwise no move is made.  If a chunk has been scanned
00810  *      and no active nodes remain, it is flushed and freelist updated.
00811  *      NOTE: This has not yet been verified to work with nodes of any
00812  *              type other than SMBLK.
00813  */
00814 
00815 sbm_ngc()
00816 {       register struct smblk *sm;
00817         if(!(sm = sbm_nxtra))
00818                 return((int)sbm_err(0,"Zero sbm_nxtra"));
00819         sm->smflags |= SM_USE;          /* Ensure this one isn't GC'd */
00820         sbm_xngc(&sbm_nfl, sizeof(struct smblk), SM_MNODS);
00821         sm->smflags = 0;                /* Flush temporary crock */
00822 }
00823 sbm_xngc(begp, elsize, flag)
00824 struct smblk **begp;
00825 unsigned elsize, flag;
00826 {       register struct smblk *sm, *chk, *smf;
00827         struct smblk *ftail, *savtail;
00828         int cnt, inuse;
00829 
00830         *begp = ftail = 0;              /* Flush node freelist */
00831         for(chk = sbm_list; chk; chk = chk->smforw)
00832           if(chk->smflags&flag)
00833             {   sm = (struct smblk *) chk->smaddr;
00834                 cnt = (chk->smuse)/elsize;
00835                 savtail = ftail;
00836                 inuse = 0;
00837                 smf = *begp;
00838                                          /* Set up ptr to 1st freelist node */
00839                 while(--cnt >= 0)
00840                   {     /* Here decide if movable */
00841                         if(sm->smflags && smf   /* Live and have copy place */
00842                           && (
00843                                 (sm->smflags&SM_USE) == 0       /* Free mem? */
00844                             ||  (sm->smflags&(SM_MNODS|SM_DNODS))
00845                              )
00846                           && sm->smback)        /* has backptr (see ncpy) */
00847                           {                             /* Move the node */
00848                                 *begp = smf->smforw;    /* Get free node */
00849                                 if(smf == ftail)
00850                                         ftail = 0;
00851                                 if(smf == savtail)
00852                                         savtail = 0;
00853                                 /* Move node.  Already checked for back ptr
00854                                  * of 0 since no obvious way to tell where
00855                                  * the ptr to list is kept.  Sigh.
00856                                  */
00857                                 sbm_nmov(sm,smf,(struct smblk **)0,elsize);
00858                                 /* Get ptr to new freelist node.  Note
00859                                  * check to ensure that it is not in this
00860                                  * same chunk (if it is, no point in moving
00861                                  * any nodes!)
00862                                  */
00863                                 if((smf = *begp) >= chk)
00864                                         smf = 0;        /* Zero if same chk */
00865                                 sm->smflags = 0;        /* Make node free */
00866                           }
00867                         /* At this point, not movable */
00868                         if(sm->smflags == 0)            /* Free node? */
00869                           {     if(ftail)               /* Add to freelist */
00870                                         ftail->smforw = sm;
00871                                 ftail = sm;
00872                                 if(*begp == 0)
00873                                         *begp = sm;
00874                                 sm->smforw = 0;
00875                           }
00876                         else inuse++;
00877                         sm = (struct smblk *)((SBMA)sm + elsize);
00878                   }
00879                 if(inuse == 0                           /* All free? */
00880                   && (sm = chk->smback))                /* & not 1st? */
00881                   {     if(savtail)                     /* Edit freelist */
00882                                 (ftail = savtail)->smforw = 0;
00883                         else *begp = ftail = 0;
00884                         sbm_mfree(chk);
00885                         chk = sm;
00886                   }
00887             }
00888 }
00889 
00890 /*
00891  *      Note that proc must return a zero value, or loop aborts and
00892  *      returns that selfsame value.
00893  */
00894 sbm_nfor(flag,nodsiz,proc,arg)
00895 int flag;
00896 int (*proc)();
00897 int nodsiz;
00898 struct sbfile *arg;
00899 {       register struct smblk *sm, *np;
00900         register int cnt;
00901         int res;
00902 
00903         for(sm = sbm_list; sm; sm = sm->smforw)
00904           if(sm->smflags&flag)
00905             {   np = (struct smblk *) sm->smaddr;
00906                 cnt = sm->smuse/nodsiz;
00907                 do {
00908                         if(np->smflags)
00909                                 if(res = (*proc)(np,arg))
00910                                         return(res);
00911                         np = (struct smblk *)((SBMA)np + nodsiz);
00912                   } while(--cnt);
00913             }
00914         return(0);
00915 }

Generated on Fri Apr 14 22:56:51 2006 for minix by  doxygen 1.4.6