123395Smckusick /* 223395Smckusick * Copyright (c) 1982 Regents of the University of California. 323395Smckusick * All rights reserved. The Berkeley software License Agreement 423395Smckusick * specifies the terms and conditions for redistribution. 523395Smckusick * 6*24829Skarels * @(#)vfs_cluster.c 6.7 (Berkeley) 09/17/85 723395Smckusick */ 88Sbill 99763Ssam #include "../machine/pte.h" 109763Ssam 1117098Sbloom #include "param.h" 1217098Sbloom #include "systm.h" 1317098Sbloom #include "dir.h" 1417098Sbloom #include "user.h" 1517098Sbloom #include "buf.h" 1617098Sbloom #include "conf.h" 1717098Sbloom #include "proc.h" 1817098Sbloom #include "seg.h" 1917098Sbloom #include "vm.h" 2017098Sbloom #include "trace.h" 218Sbill 2291Sbill /* 238Sbill * Read in (if necessary) the block and return a buffer pointer. 248Sbill */ 258Sbill struct buf * 266563Smckusic bread(dev, blkno, size) 276563Smckusic dev_t dev; 286563Smckusic daddr_t blkno; 296563Smckusic int size; 308Sbill { 318Sbill register struct buf *bp; 328Sbill 338670S if (size == 0) 348670S panic("bread: size 0"); 356563Smckusic bp = getblk(dev, blkno, size); 368Sbill if (bp->b_flags&B_DONE) { 3715795Ssam trace(TR_BREADHIT, pack(dev, size), blkno); 388Sbill return(bp); 398Sbill } 408Sbill bp->b_flags |= B_READ; 418670S if (bp->b_bcount > bp->b_bufsize) 428670S panic("bread"); 438Sbill (*bdevsw[major(dev)].d_strategy)(bp); 4415795Ssam trace(TR_BREADMISS, pack(dev, size), blkno); 458039Sroot u.u_ru.ru_inblock++; /* pay for read */ 467015Smckusick biowait(bp); 478Sbill return(bp); 488Sbill } 498Sbill 508Sbill /* 518Sbill * Read in the block, like bread, but also start I/O on the 528Sbill * read-ahead block (which is not allocated to the caller) 538Sbill */ 548Sbill struct buf * 558592Sroot breada(dev, blkno, size, rablkno, rabsize) 566563Smckusic dev_t dev; 577114Smckusick daddr_t blkno; int size; 588592Sroot daddr_t rablkno; int rabsize; 598Sbill { 608Sbill register struct buf *bp, *rabp; 618Sbill 628Sbill bp = NULL; 637015Smckusick /* 647015Smckusick * If the block isn't in core, then allocate 657015Smckusick * a buffer and initiate i/o (getblk checks 667015Smckusick * for a cache hit). 677015Smckusick */ 688Sbill if (!incore(dev, blkno)) { 696563Smckusic bp = getblk(dev, blkno, size); 708Sbill if ((bp->b_flags&B_DONE) == 0) { 718Sbill bp->b_flags |= B_READ; 728670S if (bp->b_bcount > bp->b_bufsize) 738670S panic("breada"); 748Sbill (*bdevsw[major(dev)].d_strategy)(bp); 7515795Ssam trace(TR_BREADMISS, pack(dev, size), blkno); 768039Sroot u.u_ru.ru_inblock++; /* pay for read */ 777015Smckusick } else 7815795Ssam trace(TR_BREADHIT, pack(dev, size), blkno); 798Sbill } 807015Smckusick 817015Smckusick /* 827015Smckusick * If there's a read-ahead block, start i/o 837015Smckusick * on it also (as above). 847015Smckusick */ 858Sbill if (rablkno && !incore(dev, rablkno)) { 868592Sroot rabp = getblk(dev, rablkno, rabsize); 872045Swnj if (rabp->b_flags & B_DONE) { 888Sbill brelse(rabp); 8915795Ssam trace(TR_BREADHITRA, pack(dev, rabsize), blkno); 902045Swnj } else { 918Sbill rabp->b_flags |= B_READ|B_ASYNC; 928670S if (rabp->b_bcount > rabp->b_bufsize) 938670S panic("breadrabp"); 948Sbill (*bdevsw[major(dev)].d_strategy)(rabp); 9515795Ssam trace(TR_BREADMISSRA, pack(dev, rabsize), rablock); 968039Sroot u.u_ru.ru_inblock++; /* pay in advance */ 978Sbill } 988Sbill } 997015Smckusick 1007015Smckusick /* 1017114Smckusick * If block was in core, let bread get it. 1027114Smckusick * If block wasn't in core, then the read was started 1037114Smckusick * above, and just wait for it. 1047015Smckusick */ 1057114Smckusick if (bp == NULL) 1067114Smckusick return (bread(dev, blkno, size)); 1077015Smckusick biowait(bp); 1087114Smckusick return (bp); 1098Sbill } 1108Sbill 1118Sbill /* 1128Sbill * Write the buffer, waiting for completion. 1138Sbill * Then release the buffer. 1148Sbill */ 1158Sbill bwrite(bp) 1167015Smckusick register struct buf *bp; 1178Sbill { 1188Sbill register flag; 1198Sbill 1208Sbill flag = bp->b_flags; 1219857Ssam bp->b_flags &= ~(B_READ | B_DONE | B_ERROR | B_DELWRI); 1228Sbill if ((flag&B_DELWRI) == 0) 1238039Sroot u.u_ru.ru_oublock++; /* noone paid yet */ 12415795Ssam trace(TR_BWRITE, pack(bp->b_dev, bp->b_bcount), bp->b_blkno); 1258670S if (bp->b_bcount > bp->b_bufsize) 1268670S panic("bwrite"); 1278Sbill (*bdevsw[major(bp->b_dev)].d_strategy)(bp); 1287015Smckusick 1297015Smckusick /* 1307015Smckusick * If the write was synchronous, then await i/o completion. 1317015Smckusick * If the write was "delayed", then we put the buffer on 1327015Smckusick * the q of blocks awaiting i/o completion status. 1337015Smckusick */ 1348Sbill if ((flag&B_ASYNC) == 0) { 1357015Smckusick biowait(bp); 1368Sbill brelse(bp); 1378Sbill } else if (flag & B_DELWRI) 1388Sbill bp->b_flags |= B_AGE; 1398Sbill } 1408Sbill 1418Sbill /* 1428Sbill * Release the buffer, marking it so that if it is grabbed 1438Sbill * for another purpose it will be written out before being 1448Sbill * given up (e.g. when writing a partial block where it is 1458Sbill * assumed that another write for the same block will soon follow). 1468Sbill * This can't be done for magtape, since writes must be done 1478Sbill * in the same order as requested. 1488Sbill */ 1498Sbill bdwrite(bp) 1507015Smckusick register struct buf *bp; 1518Sbill { 1522403Skre register int flags; 1538Sbill 1548Sbill if ((bp->b_flags&B_DELWRI) == 0) 1558039Sroot u.u_ru.ru_oublock++; /* noone paid yet */ 1562403Skre flags = bdevsw[major(bp->b_dev)].d_flags; 1572403Skre if(flags & B_TAPE) 1588Sbill bawrite(bp); 1598Sbill else { 1608Sbill bp->b_flags |= B_DELWRI | B_DONE; 1618Sbill brelse(bp); 1628Sbill } 1638Sbill } 1648Sbill 1658Sbill /* 1668Sbill * Release the buffer, start I/O on it, but don't wait for completion. 1678Sbill */ 1688Sbill bawrite(bp) 1697015Smckusick register struct buf *bp; 1708Sbill { 1718Sbill 1728Sbill bp->b_flags |= B_ASYNC; 1738Sbill bwrite(bp); 1748Sbill } 1758Sbill 1768Sbill /* 1777015Smckusick * Release the buffer, with no I/O implied. 1788Sbill */ 1798Sbill brelse(bp) 1807015Smckusick register struct buf *bp; 1818Sbill { 1822325Swnj register struct buf *flist; 1838Sbill register s; 1848Sbill 18515795Ssam trace(TR_BRELSE, pack(bp->b_dev, bp->b_bufsize), bp->b_blkno); 1867015Smckusick /* 1877015Smckusick * If someone's waiting for the buffer, or 1887015Smckusick * is waiting for a buffer wake 'em up. 1897015Smckusick */ 1908Sbill if (bp->b_flags&B_WANTED) 1918Sbill wakeup((caddr_t)bp); 1922325Swnj if (bfreelist[0].b_flags&B_WANTED) { 1932325Swnj bfreelist[0].b_flags &= ~B_WANTED; 1942325Swnj wakeup((caddr_t)bfreelist); 1958Sbill } 1962683Swnj if (bp->b_flags&B_ERROR) 1972683Swnj if (bp->b_flags & B_LOCKED) 1982683Swnj bp->b_flags &= ~B_ERROR; /* try again later */ 1992683Swnj else 2002683Swnj bp->b_dev = NODEV; /* no assoc */ 2017015Smckusick 2027015Smckusick /* 2037015Smckusick * Stick the buffer back on a free list. 2047015Smckusick */ 2058Sbill s = spl6(); 2068670S if (bp->b_bufsize <= 0) { 2078670S /* block has no buffer ... put at front of unused buffer list */ 2088670S flist = &bfreelist[BQ_EMPTY]; 2098670S binsheadfree(bp, flist); 2108670S } else if (bp->b_flags & (B_ERROR|B_INVAL)) { 2112325Swnj /* block has no info ... put at front of most free list */ 2128670S flist = &bfreelist[BQ_AGE]; 2137015Smckusick binsheadfree(bp, flist); 2148Sbill } else { 2152325Swnj if (bp->b_flags & B_LOCKED) 2162325Swnj flist = &bfreelist[BQ_LOCKED]; 2172325Swnj else if (bp->b_flags & B_AGE) 2182325Swnj flist = &bfreelist[BQ_AGE]; 2192325Swnj else 2202325Swnj flist = &bfreelist[BQ_LRU]; 2217015Smckusick binstailfree(bp, flist); 2228Sbill } 2238Sbill bp->b_flags &= ~(B_WANTED|B_BUSY|B_ASYNC|B_AGE); 2248Sbill splx(s); 2258Sbill } 2268Sbill 2278Sbill /* 2288Sbill * See if the block is associated with some buffer 2298Sbill * (mainly to avoid getting hung up on a wait in breada) 2308Sbill */ 2318Sbill incore(dev, blkno) 2327015Smckusick dev_t dev; 2337015Smckusick daddr_t blkno; 2348Sbill { 2358Sbill register struct buf *bp; 2362325Swnj register struct buf *dp; 2378Sbill 2386563Smckusic dp = BUFHASH(dev, blkno); 2392325Swnj for (bp = dp->b_forw; bp != dp; bp = bp->b_forw) 2406563Smckusic if (bp->b_blkno == blkno && bp->b_dev == dev && 2417015Smckusick (bp->b_flags & B_INVAL) == 0) 24291Sbill return (1); 24391Sbill return (0); 2448Sbill } 2458Sbill 2468Sbill struct buf * 2476563Smckusic baddr(dev, blkno, size) 2486563Smckusic dev_t dev; 2496563Smckusic daddr_t blkno; 2506563Smckusic int size; 2518Sbill { 2528Sbill 2538Sbill if (incore(dev, blkno)) 2546563Smckusic return (bread(dev, blkno, size)); 2558Sbill return (0); 2568Sbill } 2578Sbill 2588Sbill /* 2598Sbill * Assign a buffer for the given block. If the appropriate 2608Sbill * block is already associated, return it; otherwise search 2618Sbill * for the oldest non-busy buffer and reassign it. 2625424Swnj * 2635424Swnj * We use splx here because this routine may be called 2645424Swnj * on the interrupt stack during a dump, and we don't 2655424Swnj * want to lower the ipl back to 0. 2668Sbill */ 2678Sbill struct buf * 2686563Smckusic getblk(dev, blkno, size) 2696563Smckusic dev_t dev; 2706563Smckusic daddr_t blkno; 2716563Smckusic int size; 2728Sbill { 2738670S register struct buf *bp, *dp; 2745424Swnj int s; 2758Sbill 2767015Smckusick /* 27724730Smckusick * To prevent overflow of 32-bit ints when converting block 27824730Smckusick * numbers to byte offsets, blknos > 2^32 / DEV_BSIZE are set 27924730Smckusick * to the maximum number that can be converted to a byte offset 28024730Smckusick * without overflow. This is historic code; what bug it fixed, 28124730Smckusick * or whether it is still a reasonable thing to do is open to 28224730Smckusick * dispute. mkm 9/85 28324730Smckusick */ 28424730Smckusick if ((unsigned)blkno >= 1 << (sizeof(int)*NBBY-DEV_BSHIFT)) 28524730Smckusick blkno = 1 << ((sizeof(int)*NBBY-DEV_BSHIFT) + 1); 28624730Smckusick /* 2877015Smckusick * Search the cache for the block. If we hit, but 2887015Smckusick * the buffer is in use for i/o, then we wait until 2897015Smckusick * the i/o has completed. 2907015Smckusick */ 2916563Smckusic dp = BUFHASH(dev, blkno); 2927015Smckusick loop: 2932325Swnj for (bp = dp->b_forw; bp != dp; bp = bp->b_forw) { 2946563Smckusic if (bp->b_blkno != blkno || bp->b_dev != dev || 2952325Swnj bp->b_flags&B_INVAL) 2968Sbill continue; 2975424Swnj s = spl6(); 2988Sbill if (bp->b_flags&B_BUSY) { 2998Sbill bp->b_flags |= B_WANTED; 3008Sbill sleep((caddr_t)bp, PRIBIO+1); 3015424Swnj splx(s); 3028Sbill goto loop; 3038Sbill } 3045424Swnj splx(s); 3058Sbill notavail(bp); 30616855Smckusick if (bp->b_bcount != size && brealloc(bp, size) == 0) 3077188Sroot goto loop; 3088Sbill bp->b_flags |= B_CACHE; 3098Sbill return(bp); 3108Sbill } 31191Sbill if (major(dev) >= nblkdev) 31291Sbill panic("blkdev"); 3138670S bp = getnewbuf(); 3146563Smckusic bfree(bp); 3157015Smckusick bremhash(bp); 3167015Smckusick binshash(bp, dp); 3178Sbill bp->b_dev = dev; 3186563Smckusic bp->b_blkno = blkno; 3198670S bp->b_error = 0; 3207188Sroot if (brealloc(bp, size) == 0) 3217188Sroot goto loop; 3228Sbill return(bp); 3238Sbill } 3248Sbill 3258Sbill /* 3268Sbill * get an empty block, 3278Sbill * not assigned to any particular device 3288Sbill */ 3298Sbill struct buf * 3306563Smckusic geteblk(size) 3316563Smckusic int size; 3328Sbill { 3338670S register struct buf *bp, *flist; 3348Sbill 3358Sbill loop: 3368670S bp = getnewbuf(); 3378670S bp->b_flags |= B_INVAL; 3387015Smckusick bfree(bp); 3397015Smckusick bremhash(bp); 3408670S flist = &bfreelist[BQ_AGE]; 3418670S binshash(bp, flist); 3428Sbill bp->b_dev = (dev_t)NODEV; 3438670S bp->b_error = 0; 3447188Sroot if (brealloc(bp, size) == 0) 3457188Sroot goto loop; 3468Sbill return(bp); 3478Sbill } 3488Sbill 3498Sbill /* 3506563Smckusic * Allocate space associated with a buffer. 3519763Ssam * If can't get space, buffer is released 3526563Smckusic */ 3536563Smckusic brealloc(bp, size) 3546563Smckusic register struct buf *bp; 3556563Smckusic int size; 3566563Smckusic { 3576563Smckusic daddr_t start, last; 3586563Smckusic register struct buf *ep; 3596563Smckusic struct buf *dp; 3606563Smckusic int s; 3616563Smckusic 3626563Smckusic /* 3636563Smckusic * First need to make sure that all overlaping previous I/O 3646563Smckusic * is dispatched with. 3656563Smckusic */ 3666563Smckusic if (size == bp->b_bcount) 3677188Sroot return (1); 3687188Sroot if (size < bp->b_bcount) { 3697188Sroot if (bp->b_flags & B_DELWRI) { 3707188Sroot bwrite(bp); 3717188Sroot return (0); 3727188Sroot } 3737188Sroot if (bp->b_flags & B_LOCKED) 3747188Sroot panic("brealloc"); 3759763Ssam return (allocbuf(bp, size)); 3767188Sroot } 3777188Sroot bp->b_flags &= ~B_DONE; 3789763Ssam if (bp->b_dev == NODEV) 3799763Ssam return (allocbuf(bp, size)); 3807016Smckusick 38115795Ssam trace(TR_BREALLOC, pack(bp->b_dev, size), bp->b_blkno); 3827188Sroot /* 3837188Sroot * Search cache for any buffers that overlap the one that we 3847188Sroot * are trying to allocate. Overlapping buffers must be marked 3857188Sroot * invalid, after being written out if they are dirty. (indicated 3867188Sroot * by B_DELWRI) A disk block must be mapped by at most one buffer 3877188Sroot * at any point in time. Care must be taken to avoid deadlocking 3887188Sroot * when two buffer are trying to get the same set of disk blocks. 3897188Sroot */ 3907188Sroot start = bp->b_blkno; 39112644Ssam last = start + btodb(size) - 1; 3926563Smckusic dp = BUFHASH(bp->b_dev, bp->b_blkno); 3936563Smckusic loop: 3946563Smckusic for (ep = dp->b_forw; ep != dp; ep = ep->b_forw) { 3957188Sroot if (ep == bp || ep->b_dev != bp->b_dev || (ep->b_flags&B_INVAL)) 3966563Smckusic continue; 3977188Sroot /* look for overlap */ 3987188Sroot if (ep->b_bcount == 0 || ep->b_blkno > last || 39912644Ssam ep->b_blkno + btodb(ep->b_bcount) <= start) 4007188Sroot continue; 4016563Smckusic s = spl6(); 4026563Smckusic if (ep->b_flags&B_BUSY) { 4036563Smckusic ep->b_flags |= B_WANTED; 4046563Smckusic sleep((caddr_t)ep, PRIBIO+1); 4058670S splx(s); 4066563Smckusic goto loop; 4076563Smckusic } 4088670S splx(s); 4097188Sroot notavail(ep); 4106563Smckusic if (ep->b_flags & B_DELWRI) { 4116563Smckusic bwrite(ep); 4126563Smckusic goto loop; 4136563Smckusic } 4147188Sroot ep->b_flags |= B_INVAL; 4157188Sroot brelse(ep); 4166563Smckusic } 4179763Ssam return (allocbuf(bp, size)); 4188670S } 4198670S 4208670S /* 4218670S * Find a buffer which is available for use. 4228670S * Select something from a free list. 4238670S * Preference is to AGE list, then LRU list. 4248670S */ 4258670S struct buf * 4268670S getnewbuf() 4278670S { 4288670S register struct buf *bp, *dp; 4298670S int s; 4308670S 4318670S loop: 4328670S s = spl6(); 4338670S for (dp = &bfreelist[BQ_AGE]; dp > bfreelist; dp--) 4348670S if (dp->av_forw != dp) 4358670S break; 4368670S if (dp == bfreelist) { /* no free blocks */ 4378670S dp->b_flags |= B_WANTED; 4388670S sleep((caddr_t)dp, PRIBIO+1); 43912170Ssam splx(s); 4408670S goto loop; 4418670S } 4428670S splx(s); 4438670S bp = dp->av_forw; 4448670S notavail(bp); 4458670S if (bp->b_flags & B_DELWRI) { 4468670S bp->b_flags |= B_ASYNC; 4478670S bwrite(bp); 4488670S goto loop; 4498670S } 45015795Ssam trace(TR_BRELSE, pack(bp->b_dev, bp->b_bufsize), bp->b_blkno); 4518670S bp->b_flags = B_BUSY; 4528670S return (bp); 4538670S } 4548670S 4558670S /* 4568Sbill * Wait for I/O completion on the buffer; return errors 4578Sbill * to the user. 4588Sbill */ 4597015Smckusick biowait(bp) 4606563Smckusic register struct buf *bp; 4618Sbill { 4625431Sroot int s; 4638Sbill 4645431Sroot s = spl6(); 4658Sbill while ((bp->b_flags&B_DONE)==0) 4668Sbill sleep((caddr_t)bp, PRIBIO); 4675431Sroot splx(s); 46811841Ssam if (u.u_error == 0) /* XXX */ 46911841Ssam u.u_error = geterror(bp); 4708Sbill } 4718Sbill 4728Sbill /* 47313128Ssam * Mark I/O complete on a buffer. 47413128Ssam * If someone should be called, e.g. the pageout 47513128Ssam * daemon, do so. Otherwise, wake up anyone 47613128Ssam * waiting for it. 4778Sbill */ 4787015Smckusick biodone(bp) 4797015Smckusick register struct buf *bp; 4808Sbill { 4818Sbill 482420Sbill if (bp->b_flags & B_DONE) 4837015Smckusick panic("dup biodone"); 4848Sbill bp->b_flags |= B_DONE; 4859763Ssam if (bp->b_flags & B_CALL) { 4869763Ssam bp->b_flags &= ~B_CALL; 4879763Ssam (*bp->b_iodone)(bp); 4889763Ssam return; 4899763Ssam } 4908Sbill if (bp->b_flags&B_ASYNC) 4918Sbill brelse(bp); 4928Sbill else { 4938Sbill bp->b_flags &= ~B_WANTED; 4948Sbill wakeup((caddr_t)bp); 4958Sbill } 4968Sbill } 4978Sbill 4988Sbill /* 4998670S * Insure that no part of a specified block is in an incore buffer. 5008670S */ 5018670S blkflush(dev, blkno, size) 5028670S dev_t dev; 5038670S daddr_t blkno; 5048670S long size; 5058670S { 5068670S register struct buf *ep; 5078670S struct buf *dp; 5088670S daddr_t start, last; 5098670S int s; 5108670S 5118670S start = blkno; 51212644Ssam last = start + btodb(size) - 1; 5138670S dp = BUFHASH(dev, blkno); 5148670S loop: 5158670S for (ep = dp->b_forw; ep != dp; ep = ep->b_forw) { 5168670S if (ep->b_dev != dev || (ep->b_flags&B_INVAL)) 5178670S continue; 5188670S /* look for overlap */ 5198670S if (ep->b_bcount == 0 || ep->b_blkno > last || 52012644Ssam ep->b_blkno + btodb(ep->b_bcount) <= start) 5218670S continue; 5228670S s = spl6(); 5238670S if (ep->b_flags&B_BUSY) { 5248670S ep->b_flags |= B_WANTED; 5258670S sleep((caddr_t)ep, PRIBIO+1); 5268670S splx(s); 5278670S goto loop; 5288670S } 5298670S if (ep->b_flags & B_DELWRI) { 5308670S splx(s); 5318670S notavail(ep); 5328670S bwrite(ep); 5338670S goto loop; 5348670S } 5358670S splx(s); 5368670S } 5378670S } 5388670S 5398670S /* 54013128Ssam * Make sure all write-behind blocks 5418Sbill * on dev (or NODEV for all) 5428Sbill * are flushed out. 5438Sbill * (from umount and update) 5448Sbill */ 5458Sbill bflush(dev) 5467015Smckusick dev_t dev; 5478Sbill { 5488Sbill register struct buf *bp; 5492325Swnj register struct buf *flist; 5505431Sroot int s; 5518Sbill 5528Sbill loop: 5535431Sroot s = spl6(); 5548670S for (flist = bfreelist; flist < &bfreelist[BQ_EMPTY]; flist++) 5552325Swnj for (bp = flist->av_forw; bp != flist; bp = bp->av_forw) { 5567015Smckusick if ((bp->b_flags & B_DELWRI) == 0) 5577015Smckusick continue; 5587015Smckusick if (dev == NODEV || dev == bp->b_dev) { 5598Sbill bp->b_flags |= B_ASYNC; 5608Sbill notavail(bp); 5618Sbill bwrite(bp); 56212173Ssam splx(s); 5638Sbill goto loop; 5648Sbill } 5658Sbill } 5665431Sroot splx(s); 5678Sbill } 5688Sbill 5698Sbill /* 5708Sbill * Pick up the device's error number and pass it to the user; 571*24829Skarels * if there is an error but the number is 0 set a generalized code. 5728Sbill */ 5738Sbill geterror(bp) 5747015Smckusick register struct buf *bp; 5758Sbill { 5767723Swnj int error = 0; 5778Sbill 5788Sbill if (bp->b_flags&B_ERROR) 5797723Swnj if ((error = bp->b_error)==0) 5807723Swnj return (EIO); 5817723Swnj return (error); 5828Sbill } 5832299Skre 5842299Skre /* 5852299Skre * Invalidate in core blocks belonging to closed or umounted filesystem 5862299Skre * 5872299Skre * This is not nicely done at all - the buffer ought to be removed from the 5882299Skre * hash chains & have its dev/blkno fields clobbered, but unfortunately we 5892299Skre * can't do that here, as it is quite possible that the block is still 5902299Skre * being used for i/o. Eventually, all disc drivers should be forced to 5912299Skre * have a close routine, which ought ensure that the queue is empty, then 5922299Skre * properly flush the queues. Until that happy day, this suffices for 5932299Skre * correctness. ... kre 5942299Skre */ 5952299Skre binval(dev) 5967015Smckusick dev_t dev; 5972299Skre { 5982361Skre register struct buf *bp; 5992361Skre register struct bufhd *hp; 6002361Skre #define dp ((struct buf *)hp) 6012299Skre 6022361Skre for (hp = bufhash; hp < &bufhash[BUFHSZ]; hp++) 6032361Skre for (bp = dp->b_forw; bp != dp; bp = bp->b_forw) 6042361Skre if (bp->b_dev == dev) 6052361Skre bp->b_flags |= B_INVAL; 6062299Skre } 607