1 /* vfs_bio.c 4.44 83/05/02 */ 2 3 #include "../machine/pte.h" 4 5 #include "../h/param.h" 6 #include "../h/systm.h" 7 #include "../h/dir.h" 8 #include "../h/user.h" 9 #include "../h/buf.h" 10 #include "../h/conf.h" 11 #include "../h/proc.h" 12 #include "../h/seg.h" 13 #include "../h/vm.h" 14 #include "../h/trace.h" 15 16 /* 17 * Read in (if necessary) the block and return a buffer pointer. 18 */ 19 struct buf * 20 bread(dev, blkno, size) 21 dev_t dev; 22 daddr_t blkno; 23 int size; 24 { 25 register struct buf *bp; 26 27 if (size == 0) 28 panic("bread: size 0"); 29 bp = getblk(dev, blkno, size); 30 if (bp->b_flags&B_DONE) { 31 trace(TR_BREADHIT, dev, blkno); 32 return(bp); 33 } 34 bp->b_flags |= B_READ; 35 if (bp->b_bcount > bp->b_bufsize) 36 panic("bread"); 37 (*bdevsw[major(dev)].d_strategy)(bp); 38 trace(TR_BREADMISS, dev, blkno); 39 u.u_ru.ru_inblock++; /* pay for read */ 40 biowait(bp); 41 return(bp); 42 } 43 44 /* 45 * Read in the block, like bread, but also start I/O on the 46 * read-ahead block (which is not allocated to the caller) 47 */ 48 struct buf * 49 breada(dev, blkno, size, rablkno, rabsize) 50 dev_t dev; 51 daddr_t blkno; int size; 52 daddr_t rablkno; int rabsize; 53 { 54 register struct buf *bp, *rabp; 55 56 bp = NULL; 57 /* 58 * If the block isn't in core, then allocate 59 * a buffer and initiate i/o (getblk checks 60 * for a cache hit). 61 */ 62 if (!incore(dev, blkno)) { 63 bp = getblk(dev, blkno, size); 64 if ((bp->b_flags&B_DONE) == 0) { 65 bp->b_flags |= B_READ; 66 if (bp->b_bcount > bp->b_bufsize) 67 panic("breada"); 68 (*bdevsw[major(dev)].d_strategy)(bp); 69 trace(TR_BREADMISS, dev, blkno); 70 u.u_ru.ru_inblock++; /* pay for read */ 71 } else 72 trace(TR_BREADHIT, dev, blkno); 73 } 74 75 /* 76 * If there's a read-ahead block, start i/o 77 * on it also (as above). 78 */ 79 if (rablkno && !incore(dev, rablkno)) { 80 rabp = getblk(dev, rablkno, rabsize); 81 if (rabp->b_flags & B_DONE) { 82 brelse(rabp); 83 trace(TR_BREADHITRA, dev, blkno); 84 } else { 85 rabp->b_flags |= B_READ|B_ASYNC; 86 if (rabp->b_bcount > rabp->b_bufsize) 87 panic("breadrabp"); 88 (*bdevsw[major(dev)].d_strategy)(rabp); 89 trace(TR_BREADMISSRA, dev, rablock); 90 u.u_ru.ru_inblock++; /* pay in advance */ 91 } 92 } 93 94 /* 95 * If block was in core, let bread get it. 96 * If block wasn't in core, then the read was started 97 * above, and just wait for it. 98 */ 99 if (bp == NULL) 100 return (bread(dev, blkno, size)); 101 biowait(bp); 102 return (bp); 103 } 104 105 /* 106 * Write the buffer, waiting for completion. 107 * Then release the buffer. 108 */ 109 bwrite(bp) 110 register struct buf *bp; 111 { 112 register flag; 113 114 flag = bp->b_flags; 115 bp->b_flags &= ~(B_READ | B_DONE | B_ERROR | B_DELWRI); 116 if ((flag&B_DELWRI) == 0) 117 u.u_ru.ru_oublock++; /* noone paid yet */ 118 trace(TR_BWRITE, bp->b_dev, bp->b_blkno); 119 if (bp->b_bcount > bp->b_bufsize) 120 panic("bwrite"); 121 (*bdevsw[major(bp->b_dev)].d_strategy)(bp); 122 123 /* 124 * If the write was synchronous, then await i/o completion. 125 * If the write was "delayed", then we put the buffer on 126 * the q of blocks awaiting i/o completion status. 127 */ 128 if ((flag&B_ASYNC) == 0) { 129 biowait(bp); 130 brelse(bp); 131 } else if (flag & B_DELWRI) 132 bp->b_flags |= B_AGE; 133 } 134 135 /* 136 * Release the buffer, marking it so that if it is grabbed 137 * for another purpose it will be written out before being 138 * given up (e.g. when writing a partial block where it is 139 * assumed that another write for the same block will soon follow). 140 * This can't be done for magtape, since writes must be done 141 * in the same order as requested. 142 */ 143 bdwrite(bp) 144 register struct buf *bp; 145 { 146 register int flags; 147 148 if ((bp->b_flags&B_DELWRI) == 0) 149 u.u_ru.ru_oublock++; /* noone paid yet */ 150 flags = bdevsw[major(bp->b_dev)].d_flags; 151 if(flags & B_TAPE) 152 bawrite(bp); 153 else { 154 bp->b_flags |= B_DELWRI | B_DONE; 155 brelse(bp); 156 } 157 } 158 159 /* 160 * Release the buffer, start I/O on it, but don't wait for completion. 161 */ 162 bawrite(bp) 163 register struct buf *bp; 164 { 165 166 bp->b_flags |= B_ASYNC; 167 bwrite(bp); 168 } 169 170 /* 171 * Release the buffer, with no I/O implied. 172 */ 173 brelse(bp) 174 register struct buf *bp; 175 { 176 register struct buf *flist; 177 register s; 178 179 /* 180 * If someone's waiting for the buffer, or 181 * is waiting for a buffer wake 'em up. 182 */ 183 if (bp->b_flags&B_WANTED) 184 wakeup((caddr_t)bp); 185 if (bfreelist[0].b_flags&B_WANTED) { 186 bfreelist[0].b_flags &= ~B_WANTED; 187 wakeup((caddr_t)bfreelist); 188 } 189 if (bp->b_flags&B_ERROR) 190 if (bp->b_flags & B_LOCKED) 191 bp->b_flags &= ~B_ERROR; /* try again later */ 192 else 193 bp->b_dev = NODEV; /* no assoc */ 194 195 /* 196 * Stick the buffer back on a free list. 197 */ 198 s = spl6(); 199 if (bp->b_bufsize <= 0) { 200 /* block has no buffer ... put at front of unused buffer list */ 201 flist = &bfreelist[BQ_EMPTY]; 202 binsheadfree(bp, flist); 203 } else if (bp->b_flags & (B_ERROR|B_INVAL)) { 204 /* block has no info ... put at front of most free list */ 205 flist = &bfreelist[BQ_AGE]; 206 binsheadfree(bp, flist); 207 } else { 208 if (bp->b_flags & B_LOCKED) 209 flist = &bfreelist[BQ_LOCKED]; 210 else if (bp->b_flags & B_AGE) 211 flist = &bfreelist[BQ_AGE]; 212 else 213 flist = &bfreelist[BQ_LRU]; 214 binstailfree(bp, flist); 215 } 216 bp->b_flags &= ~(B_WANTED|B_BUSY|B_ASYNC|B_AGE); 217 splx(s); 218 } 219 220 /* 221 * See if the block is associated with some buffer 222 * (mainly to avoid getting hung up on a wait in breada) 223 */ 224 incore(dev, blkno) 225 dev_t dev; 226 daddr_t blkno; 227 { 228 register struct buf *bp; 229 register struct buf *dp; 230 231 dp = BUFHASH(dev, blkno); 232 for (bp = dp->b_forw; bp != dp; bp = bp->b_forw) 233 if (bp->b_blkno == blkno && bp->b_dev == dev && 234 (bp->b_flags & B_INVAL) == 0) 235 return (1); 236 return (0); 237 } 238 239 struct buf * 240 baddr(dev, blkno, size) 241 dev_t dev; 242 daddr_t blkno; 243 int size; 244 { 245 246 if (incore(dev, blkno)) 247 return (bread(dev, blkno, size)); 248 return (0); 249 } 250 251 /* 252 * Assign a buffer for the given block. If the appropriate 253 * block is already associated, return it; otherwise search 254 * for the oldest non-busy buffer and reassign it. 255 * 256 * We use splx here because this routine may be called 257 * on the interrupt stack during a dump, and we don't 258 * want to lower the ipl back to 0. 259 */ 260 struct buf * 261 getblk(dev, blkno, size) 262 dev_t dev; 263 daddr_t blkno; 264 int size; 265 { 266 register struct buf *bp, *dp; 267 int s; 268 269 if ((unsigned)blkno >= 1 << (sizeof(int)*NBBY-PGSHIFT)) /* XXX */ 270 blkno = 1 << ((sizeof(int)*NBBY-PGSHIFT) + 1); 271 /* 272 * Search the cache for the block. If we hit, but 273 * the buffer is in use for i/o, then we wait until 274 * the i/o has completed. 275 */ 276 dp = BUFHASH(dev, blkno); 277 loop: 278 for (bp = dp->b_forw; bp != dp; bp = bp->b_forw) { 279 if (bp->b_blkno != blkno || bp->b_dev != dev || 280 bp->b_flags&B_INVAL) 281 continue; 282 s = spl6(); 283 if (bp->b_flags&B_BUSY) { 284 bp->b_flags |= B_WANTED; 285 sleep((caddr_t)bp, PRIBIO+1); 286 splx(s); 287 goto loop; 288 } 289 splx(s); 290 notavail(bp); 291 if (brealloc(bp, size) == 0) 292 goto loop; 293 bp->b_flags |= B_CACHE; 294 return(bp); 295 } 296 if (major(dev) >= nblkdev) 297 panic("blkdev"); 298 bp = getnewbuf(); 299 bfree(bp); 300 bremhash(bp); 301 binshash(bp, dp); 302 bp->b_dev = dev; 303 bp->b_blkno = blkno; 304 bp->b_error = 0; 305 if (brealloc(bp, size) == 0) 306 goto loop; 307 return(bp); 308 } 309 310 /* 311 * get an empty block, 312 * not assigned to any particular device 313 */ 314 struct buf * 315 geteblk(size) 316 int size; 317 { 318 register struct buf *bp, *flist; 319 320 loop: 321 bp = getnewbuf(); 322 bp->b_flags |= B_INVAL; 323 bfree(bp); 324 bremhash(bp); 325 flist = &bfreelist[BQ_AGE]; 326 binshash(bp, flist); 327 bp->b_dev = (dev_t)NODEV; 328 bp->b_error = 0; 329 if (brealloc(bp, size) == 0) 330 goto loop; 331 return(bp); 332 } 333 334 /* 335 * Allocate space associated with a buffer. 336 * If can't get space, buffer is released 337 */ 338 brealloc(bp, size) 339 register struct buf *bp; 340 int size; 341 { 342 daddr_t start, last; 343 register struct buf *ep; 344 struct buf *dp; 345 int s; 346 347 /* 348 * First need to make sure that all overlaping previous I/O 349 * is dispatched with. 350 */ 351 if (size == bp->b_bcount) 352 return (1); 353 if (size < bp->b_bcount) { 354 if (bp->b_flags & B_DELWRI) { 355 bwrite(bp); 356 return (0); 357 } 358 if (bp->b_flags & B_LOCKED) 359 panic("brealloc"); 360 return (allocbuf(bp, size)); 361 } 362 bp->b_flags &= ~B_DONE; 363 if (bp->b_dev == NODEV) 364 return (allocbuf(bp, size)); 365 366 /* 367 * Search cache for any buffers that overlap the one that we 368 * are trying to allocate. Overlapping buffers must be marked 369 * invalid, after being written out if they are dirty. (indicated 370 * by B_DELWRI) A disk block must be mapped by at most one buffer 371 * at any point in time. Care must be taken to avoid deadlocking 372 * when two buffer are trying to get the same set of disk blocks. 373 */ 374 start = bp->b_blkno; 375 last = start + (size / DEV_BSIZE) - 1; 376 dp = BUFHASH(bp->b_dev, bp->b_blkno); 377 loop: 378 for (ep = dp->b_forw; ep != dp; ep = ep->b_forw) { 379 if (ep == bp || ep->b_dev != bp->b_dev || (ep->b_flags&B_INVAL)) 380 continue; 381 /* look for overlap */ 382 if (ep->b_bcount == 0 || ep->b_blkno > last || 383 ep->b_blkno + (ep->b_bcount / DEV_BSIZE) <= start) 384 continue; 385 s = spl6(); 386 if (ep->b_flags&B_BUSY) { 387 ep->b_flags |= B_WANTED; 388 sleep((caddr_t)ep, PRIBIO+1); 389 splx(s); 390 goto loop; 391 } 392 splx(s); 393 notavail(ep); 394 if (ep->b_flags & B_DELWRI) { 395 bwrite(ep); 396 goto loop; 397 } 398 ep->b_flags |= B_INVAL; 399 brelse(ep); 400 } 401 return (allocbuf(bp, size)); 402 } 403 404 /* 405 * Expand or contract the actual memory allocated to a buffer. 406 * If no memory is available, release buffer and take error exit 407 */ 408 allocbuf(tp, size) 409 register struct buf *tp; 410 int size; 411 { 412 register struct buf *bp, *ep; 413 int sizealloc, take; 414 #ifdef sun 415 register char *a; 416 int osize; 417 #endif 418 419 #ifndef sun 420 sizealloc = roundup(size, CLBYTES); 421 #else 422 sizealloc = roundup(size, BUFALLOCSIZE); 423 #endif 424 /* 425 * Buffer size does not change 426 */ 427 if (sizealloc == tp->b_bufsize) 428 goto out; 429 #ifndef sun 430 /* 431 * Buffer size is shrinking. 432 * Place excess space in a buffer header taken from the 433 * BQ_EMPTY buffer list and placed on the "most free" list. 434 * If no extra buffer headers are available, leave the 435 * extra space in the present buffer. 436 */ 437 if (sizealloc < tp->b_bufsize) { 438 ep = bfreelist[BQ_EMPTY].av_forw; 439 if (ep == &bfreelist[BQ_EMPTY]) 440 goto out; 441 notavail(ep); 442 pagemove(tp->b_un.b_addr + sizealloc, ep->b_un.b_addr, 443 (int)tp->b_bufsize - sizealloc); 444 ep->b_bufsize = tp->b_bufsize - sizealloc; 445 tp->b_bufsize = sizealloc; 446 ep->b_flags |= B_INVAL; 447 ep->b_bcount = 0; 448 brelse(ep); 449 goto out; 450 } 451 /* 452 * More buffer space is needed. Get it out of buffers on 453 * the "most free" list, placing the empty headers on the 454 * BQ_EMPTY buffer header list. 455 */ 456 while (tp->b_bufsize < sizealloc) { 457 take = sizealloc - tp->b_bufsize; 458 bp = getnewbuf(); 459 if (take >= bp->b_bufsize) 460 take = bp->b_bufsize; 461 pagemove(&bp->b_un.b_addr[bp->b_bufsize - take], 462 &tp->b_un.b_addr[tp->b_bufsize], take); 463 tp->b_bufsize += take; 464 bp->b_bufsize = bp->b_bufsize - take; 465 if (bp->b_bcount > bp->b_bufsize) 466 bp->b_bcount = bp->b_bufsize; 467 if (bp->b_bufsize <= 0) { 468 bremhash(bp); 469 binshash(bp, &bfreelist[BQ_EMPTY]); 470 bp->b_dev = (dev_t)NODEV; 471 bp->b_error = 0; 472 bp->b_flags |= B_INVAL; 473 } 474 brelse(bp); 475 } 476 #else 477 /* 478 * Buffer size is shrinking 479 * Just put the tail end back in the map 480 */ 481 if (sizealloc < tp->b_bufsize) { 482 rmfree(buffermap, (long)(tp->b_bufsize - sizealloc), 483 (long)(tp->b_un.b_addr + sizealloc)); 484 tp->b_bufsize = sizealloc; 485 goto out; 486 } 487 /* 488 * Buffer is being expanded or created 489 * If being expanded, attempt to get contiguous 490 * section, otherwise get a new chunk and copy. 491 * If no space, free up a buffer on the AGE list 492 * and try again. 493 */ 494 do { 495 if ((osize = tp->b_bufsize)) { 496 a = (char *)rmget(buffermap, (long)(sizealloc-osize), 497 (long)(tp->b_un.b_addr + osize)); 498 if (a == 0) { 499 a = (char *)rmalloc(buffermap, (long)sizealloc); 500 if (a != 0) { 501 bcopy(tp->b_un.b_addr, a, osize); 502 rmfree(buffermap, (long)osize, 503 (long)tp->b_un.b_addr); 504 tp->b_un.b_addr = a; 505 } 506 } 507 } else { 508 a = (char *)rmalloc(buffermap, (long)sizealloc); 509 if (a != 0) 510 tp->b_un.b_addr = a; 511 } 512 } while (a == 0 && bfreemem()); 513 if (a == 0) { 514 brelse(tp); 515 return (0); 516 } 517 tp->b_bufsize = sizealloc; 518 #endif 519 out: 520 tp->b_bcount = size; 521 return (1); 522 } 523 524 /* 525 * Release space associated with a buffer. 526 */ 527 bfree(bp) 528 struct buf *bp; 529 { 530 #ifdef sun 531 if (bp->b_bufsize) { 532 rmfree(buffermap, (long)bp->b_bufsize, (long)bp->b_un.b_addr); 533 bp->b_bufsize = 0; 534 } 535 #endif 536 bp->b_bcount = 0; 537 } 538 539 #ifdef sun 540 /* 541 * Attempt to free up buffer space by flushing 542 * something in the free list. 543 * Don't wait for something, that could cause deadlocks 544 * We start with BQ_AGE because we know BQ_EMPTY take no memory. 545 */ 546 bfreemem() 547 { 548 register struct buf *bp, *dp; 549 int s; 550 551 loop: 552 s = spl6(); 553 for (dp = &bfreelist[BQ_AGE]; dp > bfreelist; dp--) 554 if (dp->av_forw != dp) 555 break; 556 splx(s); 557 if (dp == bfreelist) { /* no free blocks */ 558 return (0); 559 } 560 bp = dp->av_forw; 561 notavail(bp); 562 if (bp->b_flags & B_DELWRI) { 563 bp->b_flags |= B_ASYNC; 564 bwrite(bp); 565 goto loop; 566 } 567 trace(TR_BRELSE, bp->b_dev, bp->b_blkno); 568 bp->b_flags = B_BUSY | B_INVAL; 569 bfree(bp); 570 bremhash(bp); 571 binshash(bp, &bfreelist[BQ_EMPTY]); 572 bp->b_dev = (dev_t)NODEV; 573 bp->b_error = 0; 574 brelse(bp); 575 return (1); 576 } 577 #endif 578 579 /* 580 * Find a buffer which is available for use. 581 * Select something from a free list. 582 * Preference is to AGE list, then LRU list. 583 */ 584 struct buf * 585 getnewbuf() 586 { 587 register struct buf *bp, *dp; 588 int s; 589 590 loop: 591 s = spl6(); 592 #ifndef sun 593 for (dp = &bfreelist[BQ_AGE]; dp > bfreelist; dp--) 594 #else 595 for (dp = &bfreelist[BQ_EMPTY]; dp > bfreelist; dp--) 596 #endif 597 if (dp->av_forw != dp) 598 break; 599 if (dp == bfreelist) { /* no free blocks */ 600 dp->b_flags |= B_WANTED; 601 sleep((caddr_t)dp, PRIBIO+1); 602 splx(s); 603 goto loop; 604 } 605 splx(s); 606 bp = dp->av_forw; 607 notavail(bp); 608 if (bp->b_flags & B_DELWRI) { 609 bp->b_flags |= B_ASYNC; 610 bwrite(bp); 611 goto loop; 612 } 613 trace(TR_BRELSE, bp->b_dev, bp->b_blkno); 614 bp->b_flags = B_BUSY; 615 return (bp); 616 } 617 618 /* 619 * Wait for I/O completion on the buffer; return errors 620 * to the user. 621 */ 622 biowait(bp) 623 register struct buf *bp; 624 { 625 int s; 626 627 s = spl6(); 628 while ((bp->b_flags&B_DONE)==0) 629 sleep((caddr_t)bp, PRIBIO); 630 splx(s); 631 if (u.u_error == 0) /* XXX */ 632 u.u_error = geterror(bp); 633 } 634 635 /* 636 * Mark I/O complete on a buffer. If the header 637 * indicates a dirty page push completion, the 638 * header is inserted into the ``cleaned'' list 639 * to be processed by the pageout daemon. Otherwise 640 * release it if I/O is asynchronous, and wake 641 * up anyone waiting for it. 642 */ 643 biodone(bp) 644 register struct buf *bp; 645 { 646 register int s; 647 648 if (bp->b_flags & B_DONE) 649 panic("dup biodone"); 650 bp->b_flags |= B_DONE; 651 if (bp->b_flags & B_DIRTY) { 652 if (bp->b_flags & B_ERROR) 653 panic("IO err in push"); 654 s = spl6(); 655 bp->av_forw = bclnlist; 656 bp->b_bcount = swsize[bp - swbuf]; 657 bp->b_pfcent = swpf[bp - swbuf]; 658 cnt.v_pgout++; 659 cnt.v_pgpgout += bp->b_bcount / NBPG; 660 bclnlist = bp; 661 if (bswlist.b_flags & B_WANTED) 662 wakeup((caddr_t)&proc[2]); 663 splx(s); 664 return; 665 } 666 if (bp->b_flags & B_CALL) { 667 bp->b_flags &= ~B_CALL; 668 (*bp->b_iodone)(bp); 669 return; 670 } 671 if (bp->b_flags&B_ASYNC) 672 brelse(bp); 673 else { 674 bp->b_flags &= ~B_WANTED; 675 wakeup((caddr_t)bp); 676 } 677 } 678 679 /* 680 * Insure that no part of a specified block is in an incore buffer. 681 */ 682 blkflush(dev, blkno, size) 683 dev_t dev; 684 daddr_t blkno; 685 long size; 686 { 687 register struct buf *ep; 688 struct buf *dp; 689 daddr_t start, last; 690 int s; 691 692 start = blkno; 693 last = start + (size / DEV_BSIZE) - 1; 694 dp = BUFHASH(dev, blkno); 695 loop: 696 for (ep = dp->b_forw; ep != dp; ep = ep->b_forw) { 697 if (ep->b_dev != dev || (ep->b_flags&B_INVAL)) 698 continue; 699 /* look for overlap */ 700 if (ep->b_bcount == 0 || ep->b_blkno > last || 701 ep->b_blkno + (ep->b_bcount / DEV_BSIZE) <= start) 702 continue; 703 s = spl6(); 704 if (ep->b_flags&B_BUSY) { 705 ep->b_flags |= B_WANTED; 706 sleep((caddr_t)ep, PRIBIO+1); 707 splx(s); 708 goto loop; 709 } 710 if (ep->b_flags & B_DELWRI) { 711 splx(s); 712 notavail(ep); 713 bwrite(ep); 714 goto loop; 715 } 716 splx(s); 717 } 718 } 719 720 /* 721 * make sure all write-behind blocks 722 * on dev (or NODEV for all) 723 * are flushed out. 724 * (from umount and update) 725 * (and temporarily pagein) 726 */ 727 bflush(dev) 728 dev_t dev; 729 { 730 register struct buf *bp; 731 register struct buf *flist; 732 int s; 733 734 loop: 735 s = spl6(); 736 for (flist = bfreelist; flist < &bfreelist[BQ_EMPTY]; flist++) 737 for (bp = flist->av_forw; bp != flist; bp = bp->av_forw) { 738 if ((bp->b_flags & B_DELWRI) == 0) 739 continue; 740 if (dev == NODEV || dev == bp->b_dev) { 741 bp->b_flags |= B_ASYNC; 742 notavail(bp); 743 bwrite(bp); 744 splx(s); 745 goto loop; 746 } 747 } 748 splx(s); 749 } 750 751 /* 752 * Pick up the device's error number and pass it to the user; 753 * if there is an error but the number is 0 set a generalized 754 * code. Actually the latter is always true because devices 755 * don't yet return specific errors. 756 */ 757 geterror(bp) 758 register struct buf *bp; 759 { 760 int error = 0; 761 762 if (bp->b_flags&B_ERROR) 763 if ((error = bp->b_error)==0) 764 return (EIO); 765 return (error); 766 } 767 768 /* 769 * Invalidate in core blocks belonging to closed or umounted filesystem 770 * 771 * This is not nicely done at all - the buffer ought to be removed from the 772 * hash chains & have its dev/blkno fields clobbered, but unfortunately we 773 * can't do that here, as it is quite possible that the block is still 774 * being used for i/o. Eventually, all disc drivers should be forced to 775 * have a close routine, which ought ensure that the queue is empty, then 776 * properly flush the queues. Until that happy day, this suffices for 777 * correctness. ... kre 778 */ 779 binval(dev) 780 dev_t dev; 781 { 782 register struct buf *bp; 783 register struct bufhd *hp; 784 #define dp ((struct buf *)hp) 785 786 for (hp = bufhash; hp < &bufhash[BUFHSZ]; hp++) 787 for (bp = dp->b_forw; bp != dp; bp = bp->b_forw) 788 if (bp->b_dev == dev) 789 bp->b_flags |= B_INVAL; 790 } 791