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 }
1.4.6