1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/param.h> 29 #include <sys/t_lock.h> 30 #include <sys/errno.h> 31 #include <sys/sysmacros.h> 32 #include <sys/buf.h> 33 #include <sys/systm.h> 34 #include <sys/vfs.h> 35 #include <sys/vnode.h> 36 #include <sys/kmem.h> 37 #include <sys/proc.h> 38 #include <sys/cred.h> 39 #include <sys/cmn_err.h> 40 #include <sys/debug.h> 41 #include <vm/pvn.h> 42 #include <sys/fs/pc_label.h> 43 #include <sys/fs/pc_fs.h> 44 #include <sys/fs/pc_dir.h> 45 #include <sys/fs/pc_node.h> 46 #include <sys/dirent.h> 47 #include <sys/fdio.h> 48 #include <sys/file.h> 49 #include <sys/conf.h> 50 51 struct pchead pcfhead[NPCHASH]; 52 struct pchead pcdhead[NPCHASH]; 53 54 extern krwlock_t pcnodes_lock; 55 56 static int pc_getentryblock(struct pcnode *, struct buf **); 57 static int syncpcp(struct pcnode *, int); 58 59 /* 60 * fake entry for root directory, since this does not have a parent 61 * pointing to it. 62 */ 63 struct pcdir pcfs_rootdirentry = { 64 "", 65 "", 66 PCA_DIR 67 }; 68 69 void 70 pc_init(void) 71 { 72 struct pchead *hdp, *hfp; 73 int i; 74 for (i = 0; i < NPCHASH; i++) { 75 hdp = &pcdhead[i]; 76 hfp = &pcfhead[i]; 77 hdp->pch_forw = (struct pcnode *)hdp; 78 hdp->pch_back = (struct pcnode *)hdp; 79 hfp->pch_forw = (struct pcnode *)hfp; 80 hfp->pch_back = (struct pcnode *)hfp; 81 } 82 } 83 84 struct pcnode * 85 pc_getnode( 86 struct pcfs *fsp, /* filsystem for node */ 87 daddr_t blkno, /* phys block no of dir entry */ 88 int offset, /* offset of dir entry in block */ 89 struct pcdir *ep) /* node dir entry */ 90 { 91 struct pcnode *pcp; 92 struct pchead *hp; 93 struct vnode *vp; 94 pc_cluster32_t scluster; 95 96 ASSERT(fsp->pcfs_flags & PCFS_LOCKED); 97 if (ep == (struct pcdir *)0) { 98 ep = &pcfs_rootdirentry; 99 scluster = 0; 100 } else { 101 scluster = pc_getstartcluster(fsp, ep); 102 } 103 /* 104 * First look for active nodes. 105 * File nodes are identified by the location (blkno, offset) of 106 * its directory entry. 107 * Directory nodes are identified by the starting cluster number 108 * for the entries. 109 */ 110 if (ep->pcd_attr & PCA_DIR) { 111 hp = &pcdhead[PCDHASH(fsp, scluster)]; 112 rw_enter(&pcnodes_lock, RW_READER); 113 for (pcp = hp->pch_forw; 114 pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) { 115 if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) && 116 (scluster == pcp->pc_scluster)) { 117 VN_HOLD(PCTOV(pcp)); 118 rw_exit(&pcnodes_lock); 119 return (pcp); 120 } 121 } 122 rw_exit(&pcnodes_lock); 123 } else { 124 hp = &pcfhead[PCFHASH(fsp, blkno, offset)]; 125 rw_enter(&pcnodes_lock, RW_READER); 126 for (pcp = hp->pch_forw; 127 pcp != (struct pcnode *)hp; pcp = pcp->pc_forw) { 128 if ((fsp == VFSTOPCFS(PCTOV(pcp)->v_vfsp)) && 129 ((pcp->pc_flags & PC_INVAL) == 0) && 130 (blkno == pcp->pc_eblkno) && 131 (offset == pcp->pc_eoffset)) { 132 VN_HOLD(PCTOV(pcp)); 133 rw_exit(&pcnodes_lock); 134 return (pcp); 135 } 136 } 137 rw_exit(&pcnodes_lock); 138 } 139 /* 140 * Cannot find node in active list. Allocate memory for a new node 141 * initialize it, and put it on the active list. 142 */ 143 pcp = kmem_alloc(sizeof (struct pcnode), KM_SLEEP); 144 bzero(pcp, sizeof (struct pcnode)); 145 vp = vn_alloc(KM_SLEEP); 146 pcp->pc_vn = vp; 147 pcp->pc_entry = *ep; 148 pcp->pc_eblkno = blkno; 149 pcp->pc_eoffset = offset; 150 pcp->pc_scluster = scluster; 151 pcp->pc_lcluster = scluster; 152 pcp->pc_lindex = 0; 153 pcp->pc_flags = 0; 154 if (ep->pcd_attr & PCA_DIR) { 155 vn_setops(vp, pcfs_dvnodeops); 156 vp->v_type = VDIR; 157 if (scluster == 0) { 158 vp->v_flag = VROOT; 159 blkno = offset = 0; 160 if (IS_FAT32(fsp)) { 161 pcp->pc_size = pc_fileclsize(fsp, 162 fsp->pcfs_rdirstart) * fsp->pcfs_clsize; 163 } else { 164 pcp->pc_size = 165 fsp->pcfs_rdirsec * fsp->pcfs_secsize; 166 } 167 } else 168 pcp->pc_size = pc_fileclsize(fsp, scluster) * 169 fsp->pcfs_clsize; 170 } else { 171 vn_setops(vp, pcfs_fvnodeops); 172 vp->v_type = VREG; 173 vp->v_flag = VNOSWAP; 174 fsp->pcfs_frefs++; 175 pcp->pc_size = ltohi(ep->pcd_size); 176 } 177 fsp->pcfs_nrefs++; 178 VFS_HOLD(PCFSTOVFS(fsp)); 179 vp->v_data = (caddr_t)pcp; 180 vp->v_vfsp = PCFSTOVFS(fsp); 181 vn_exists(vp); 182 rw_enter(&pcnodes_lock, RW_WRITER); 183 insque(pcp, hp); 184 rw_exit(&pcnodes_lock); 185 return (pcp); 186 } 187 188 int 189 syncpcp(struct pcnode *pcp, int flags) 190 { 191 int err; 192 if (!vn_has_cached_data(PCTOV(pcp))) 193 err = 0; 194 else 195 err = VOP_PUTPAGE(PCTOV(pcp), (offset_t)0, (uint_t)0, 196 flags, (struct cred *)0); 197 198 return (err); 199 } 200 201 void 202 pc_rele(struct pcnode *pcp) 203 { 204 struct pcfs *fsp; 205 struct vnode *vp; 206 int err; 207 208 vp = PCTOV(pcp); 209 PC_DPRINTF1(8, "pc_rele vp=0x%p\n", (void *)vp); 210 211 fsp = VFSTOPCFS(vp->v_vfsp); 212 ASSERT(fsp->pcfs_flags & PCFS_LOCKED); 213 214 rw_enter(&pcnodes_lock, RW_WRITER); 215 pcp->pc_flags |= PC_RELEHOLD; 216 217 retry: 218 if (vp->v_type != VDIR && (pcp->pc_flags & PC_INVAL) == 0) { 219 /* 220 * If the file was removed while active it may be safely 221 * truncated now. 222 */ 223 224 if (pcp->pc_entry.pcd_filename[0] == PCD_ERASED) { 225 (void) pc_truncate(pcp, 0); 226 } else if (pcp->pc_flags & PC_CHG) { 227 (void) pc_nodeupdate(pcp); 228 } 229 err = syncpcp(pcp, B_INVAL); 230 if (err) { 231 (void) syncpcp(pcp, B_INVAL|B_FORCE); 232 } 233 } 234 if (vn_has_cached_data(vp)) { 235 /* 236 * pvn_vplist_dirty will abort all old pages 237 */ 238 (void) pvn_vplist_dirty(vp, (u_offset_t)0, 239 pcfs_putapage, B_INVAL, (struct cred *)NULL); 240 } 241 242 (void) pc_syncfat(fsp); 243 mutex_enter(&vp->v_lock); 244 if (vn_has_cached_data(vp)) { 245 mutex_exit(&vp->v_lock); 246 goto retry; 247 } 248 ASSERT(!vn_has_cached_data(vp)); 249 250 vp->v_count--; /* release our hold from vn_rele */ 251 if (vp->v_count > 0) { /* Is this check still needed? */ 252 PC_DPRINTF1(3, "pc_rele: pcp=0x%p HELD AGAIN!\n", (void *)pcp); 253 mutex_exit(&vp->v_lock); 254 pcp->pc_flags &= ~PC_RELEHOLD; 255 rw_exit(&pcnodes_lock); 256 return; 257 } 258 259 remque(pcp); 260 rw_exit(&pcnodes_lock); 261 if ((vp->v_type == VREG) && !(pcp->pc_flags & PC_INVAL)) { 262 fsp->pcfs_frefs--; 263 } 264 fsp->pcfs_nrefs--; 265 VFS_RELE(vp->v_vfsp); 266 267 if (fsp->pcfs_nrefs < 0) { 268 panic("pc_rele: nrefs count"); 269 } 270 if (fsp->pcfs_frefs < 0) { 271 panic("pc_rele: frefs count"); 272 } 273 274 mutex_exit(&vp->v_lock); 275 vn_invalid(vp); 276 vn_free(vp); 277 kmem_free(pcp, sizeof (struct pcnode)); 278 } 279 280 /* 281 * Mark a pcnode as modified with the current time. 282 */ 283 void 284 pc_mark_mod(struct pcnode *pcp) 285 { 286 timestruc_t now; 287 288 if (PCTOV(pcp)->v_type == VREG) { 289 gethrestime(&now); 290 if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime)) 291 PC_DPRINTF1(2, "pc_mark_mod failed timestamp " 292 "conversion, curtime = %lld\n", 293 (long long)now.tv_sec); 294 pcp->pc_flags |= PC_CHG; 295 } 296 } 297 298 /* 299 * Mark a pcnode as accessed with the current time. 300 */ 301 void 302 pc_mark_acc(struct pcnode *pcp) 303 { 304 struct pctime pt = { 0, 0 }; 305 timestruc_t now; 306 307 if (PCTOV(pcp)->v_type == VREG) { 308 gethrestime(&now); 309 if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime)) 310 PC_DPRINTF1(2, "pc_mark_acc failed timestamp " 311 "conversion, curtime = %lld\n", 312 (long long)now.tv_sec); 313 pcp->pc_entry.pcd_ladate = pt.pct_date; 314 pcp->pc_flags |= PC_CHG; 315 } 316 } 317 318 /* 319 * Truncate a file to a length. 320 * Node must be locked. 321 */ 322 int 323 pc_truncate(struct pcnode *pcp, uint_t length) 324 { 325 struct pcfs *fsp; 326 struct vnode *vp; 327 int error = 0; 328 329 PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n", 330 (void *)pcp, length, pcp->pc_size); 331 vp = PCTOV(pcp); 332 if (pcp->pc_flags & PC_INVAL) 333 return (EIO); 334 fsp = VFSTOPCFS(vp->v_vfsp); 335 /* 336 * directories are always truncated to zero and are not marked 337 */ 338 if (vp->v_type == VDIR) { 339 error = pc_bfree(pcp, 0); 340 return (error); 341 } 342 /* 343 * If length is the same as the current size 344 * just mark the pcnode and return. 345 */ 346 if (length > pcp->pc_size) { 347 daddr_t bno; 348 uint_t llcn; 349 350 /* 351 * We are extending a file. 352 * Extend it with _one_ call to pc_balloc (no holes) 353 * since we don't need to use the block number(s). 354 */ 355 if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) < 356 (llcn = (daddr_t)howmany((offset_t)length, 357 fsp->pcfs_clsize))) { 358 error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno); 359 } 360 if (error) { 361 PC_DPRINTF1(2, "pc_truncate: error=%d\n", error); 362 /* 363 * probably ran out disk space; 364 * determine current file size 365 */ 366 pcp->pc_size = fsp->pcfs_clsize * 367 pc_fileclsize(fsp, pcp->pc_scluster); 368 } else 369 pcp->pc_size = length; 370 371 } else if (length < pcp->pc_size) { 372 /* 373 * We are shrinking a file. 374 * Free blocks after the block that length points to. 375 */ 376 if (pc_blkoff(fsp, length) == 0) { 377 /* 378 * Truncation to a block (cluster size) boundary only 379 * requires us to invalidate everything after the new 380 * end of the file. 381 */ 382 (void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length, 383 pcfs_putapage, B_INVAL | B_TRUNC, CRED()); 384 } else { 385 /* 386 * pvn_vpzero() cannot deal with more than MAXBSIZE 387 * chunks. Since the FAT clustersize can get larger 388 * than that, we'll zero from the new length to the 389 * end of the cluster for clustersizes smaller than 390 * MAXBSIZE - or the end of the MAXBSIZE block in 391 * case we've got a large clustersize. 392 */ 393 size_t nbytes = 394 roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) - 395 length; 396 397 pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes); 398 (void) pvn_vplist_dirty(PCTOV(pcp), 399 (u_offset_t)length + nbytes, 400 pcfs_putapage, B_INVAL | B_TRUNC, CRED()); 401 } 402 error = pc_bfree(pcp, 403 (pc_cluster32_t)howmany((offset_t)length, 404 fsp->pcfs_clsize)); 405 pcp->pc_size = length; 406 } 407 pc_mark_mod(pcp); 408 return (error); 409 } 410 411 /* 412 * Get block for entry. 413 */ 414 static int 415 pc_getentryblock(struct pcnode *pcp, struct buf **bpp) 416 { 417 struct pcfs *fsp; 418 419 PC_DPRINTF0(7, "pc_getentryblock "); 420 fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp); 421 if (pcp->pc_eblkno >= fsp->pcfs_datastart || 422 (pcp->pc_eblkno - fsp->pcfs_rdirstart) < 423 (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 424 *bpp = bread(fsp->pcfs_xdev, 425 pc_dbdaddr(fsp, pcp->pc_eblkno), fsp->pcfs_clsize); 426 } else { 427 *bpp = bread(fsp->pcfs_xdev, 428 pc_dbdaddr(fsp, pcp->pc_eblkno), 429 (int)(fsp->pcfs_datastart-pcp->pc_eblkno) * 430 fsp->pcfs_secsize); 431 } 432 if ((*bpp)->b_flags & B_ERROR) { 433 PC_DPRINTF0(1, "pc_getentryblock: error "); 434 brelse(*bpp); 435 pc_mark_irrecov(fsp); 436 return (EIO); 437 } 438 return (0); 439 } 440 441 /* 442 * Sync all data associated with a file. 443 * Flush all the blocks in the buffer cache out to disk, sync the FAT and 444 * update the directory entry. 445 */ 446 int 447 pc_nodesync(struct pcnode *pcp) 448 { 449 struct pcfs *fsp; 450 int err; 451 struct vnode *vp; 452 453 PC_DPRINTF1(7, "pc_nodesync pcp=0x%p\n", (void *)pcp); 454 vp = PCTOV(pcp); 455 fsp = VFSTOPCFS(vp->v_vfsp); 456 err = 0; 457 if (pcp->pc_flags & PC_MOD) { 458 /* 459 * Flush all data blocks from buffer cache and 460 * update the FAT which points to the data. 461 */ 462 if (err = syncpcp(pcp, 0)) { /* %% ?? how to handle error? */ 463 if (err == ENOMEM) 464 return (err); 465 else { 466 pc_mark_irrecov(fsp); 467 return (EIO); 468 } 469 } 470 pcp->pc_flags &= ~PC_MOD; 471 } 472 /* 473 * update the directory entry 474 */ 475 if (pcp->pc_flags & PC_CHG) 476 (void) pc_nodeupdate(pcp); 477 return (err); 478 } 479 480 /* 481 * Update the node's directory entry. 482 */ 483 int 484 pc_nodeupdate(struct pcnode *pcp) 485 { 486 struct buf *bp; 487 int error; 488 struct vnode *vp; 489 struct pcfs *fsp; 490 491 vp = PCTOV(pcp); 492 fsp = VFSTOPCFS(vp->v_vfsp); 493 if (IS_FAT32(fsp) && (vp->v_flag & VROOT)) { 494 /* no node to update */ 495 pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC); 496 return (0); 497 } 498 if (vp->v_flag & VROOT) { 499 panic("pc_nodeupdate"); 500 } 501 if (pcp->pc_flags & PC_INVAL) 502 return (0); 503 PC_DPRINTF3(7, "pc_nodeupdate pcp=0x%p, bn=%ld, off=%d\n", (void *)pcp, 504 pcp->pc_eblkno, pcp->pc_eoffset); 505 506 if (error = pc_getentryblock(pcp, &bp)) { 507 return (error); 508 } 509 if (vp->v_type == VREG) { 510 if (pcp->pc_flags & PC_CHG) 511 pcp->pc_entry.pcd_attr |= PCA_ARCH; 512 pcp->pc_entry.pcd_size = htoli(pcp->pc_size); 513 } 514 pc_setstartcluster(fsp, &pcp->pc_entry, pcp->pc_scluster); 515 *((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry; 516 bwrite2(bp); 517 error = geterror(bp); 518 if (error) 519 error = EIO; 520 brelse(bp); 521 if (error) { 522 PC_DPRINTF0(1, "pc_nodeupdate ERROR\n"); 523 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp)); 524 } 525 pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC); 526 return (error); 527 } 528 529 /* 530 * Verify that the disk in the drive is the same one that we 531 * got the pcnode from. 532 * MUST be called with node unlocked. 533 */ 534 /* ARGSUSED */ 535 int 536 pc_verify(struct pcfs *fsp) 537 { 538 int fdstatus = 0; 539 int error = 0; 540 541 if (!fsp || fsp->pcfs_flags & PCFS_IRRECOV) 542 return (EIO); 543 544 if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) { 545 PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp); 546 error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev, 547 FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE|FKIOCTL, 548 NULL, NULL); 549 550 if (error) { 551 if (error == ENOTTY || error == ENXIO) { 552 error = 0; 553 } else { 554 PC_DPRINTF1(1, 555 "pc_verify: FDGETCHANGE ioctl failed: %d\n", 556 error); 557 pc_mark_irrecov(fsp); 558 } 559 } else if (fsp->pcfs_fatjustread) { 560 /* 561 * Ignore the results of the ioctl if we just 562 * read the FAT. There is a good chance that 563 * the disk changed bit will be on, because 564 * we've just mounted and we don't want to 565 * give a false positive that the sky is falling. 566 */ 567 fsp->pcfs_fatjustread = 0; 568 } else { 569 /* 570 * Oddly enough we can't check just one flag here. The 571 * x86 floppy driver sets a different flag 572 * (FDGC_DETECTED) than the sparc driver does. 573 * I think this MAY be a bug, and I filed 4165938 574 * to get someone to look at the behavior 575 * a bit more closely. In the meantime, my testing and 576 * code examination seem to indicate it is safe to 577 * check for either bit being set. 578 */ 579 if (fdstatus & (FDGC_HISTORY | FDGC_DETECTED)) { 580 PC_DPRINTF0(1, "pc_verify: change detected\n"); 581 pc_mark_irrecov(fsp); 582 } 583 } 584 } 585 if (!(error || fsp->pcfs_fatp)) { 586 error = pc_getfat(fsp); 587 } 588 589 return (error); 590 } 591 592 /* 593 * The disk has changed, pulling the rug out from beneath us. 594 * Mark the FS as being in an irrecoverable state. 595 * In a short while we'll clean up. 596 */ 597 void 598 pc_mark_irrecov(struct pcfs *fsp) 599 { 600 if (!(fsp->pcfs_flags & PCFS_NOCHK)) { 601 if (pc_lockfs(fsp, 1, 0)) { 602 /* 603 * Locking failed, which currently would 604 * only happen if the FS were already 605 * marked as hosed. If another reason for 606 * failure were to arise in the future, this 607 * routine would have to change. 608 */ 609 return; 610 } 611 612 fsp->pcfs_flags |= PCFS_IRRECOV; 613 cmn_err(CE_WARN, 614 "Disk was changed during an update or\n" 615 "an irrecoverable error was encountered.\n" 616 "File damage is possible. To prevent further\n" 617 "damage, this pcfs instance will now be frozen.\n" 618 "Use umount(1M) to release the instance.\n"); 619 (void) pc_unlockfs(fsp); 620 } 621 } 622 623 /* 624 * The disk has been changed! 625 */ 626 void 627 pc_diskchanged(struct pcfs *fsp) 628 { 629 struct pcnode *pcp, *npcp = NULL; 630 struct pchead *hp; 631 struct vnode *vp; 632 extern vfs_t EIO_vfs; 633 struct vfs *vfsp; 634 635 /* 636 * Eliminate all pcnodes (dir & file) associated with this fs. 637 * If the node is internal, ie, no references outside of 638 * pcfs itself, then release the associated vnode structure. 639 * Invalidate the in core FAT. 640 * Invalidate cached data blocks and blocks waiting for I/O. 641 */ 642 PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp); 643 644 vfsp = PCFSTOVFS(fsp); 645 646 for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) { 647 for (pcp = hp->pch_forw; 648 pcp != (struct pcnode *)hp; pcp = npcp) { 649 npcp = pcp -> pc_forw; 650 vp = PCTOV(pcp); 651 if ((vp->v_vfsp == vfsp) && 652 !(pcp->pc_flags & PC_RELEHOLD)) { 653 mutex_enter(&(vp)->v_lock); 654 if (vp->v_count > 0) { 655 mutex_exit(&(vp)->v_lock); 656 continue; 657 } 658 mutex_exit(&(vp)->v_lock); 659 VN_HOLD(vp); 660 remque(pcp); 661 vp->v_data = NULL; 662 vp->v_vfsp = &EIO_vfs; 663 vp->v_type = VBAD; 664 VN_RELE(vp); 665 if (!(pcp->pc_flags & PC_EXTERNAL)) { 666 (void) pvn_vplist_dirty(vp, 667 (u_offset_t)0, pcfs_putapage, 668 B_INVAL | B_TRUNC, 669 (struct cred *)NULL); 670 vn_free(vp); 671 } 672 kmem_free(pcp, sizeof (struct pcnode)); 673 fsp->pcfs_nrefs --; 674 VFS_RELE(vfsp); 675 } 676 } 677 } 678 for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) { 679 for (pcp = hp->pch_forw; fsp->pcfs_frefs && 680 pcp != (struct pcnode *)hp; pcp = npcp) { 681 npcp = pcp -> pc_forw; 682 vp = PCTOV(pcp); 683 if ((vp->v_vfsp == vfsp) && 684 !(pcp->pc_flags & PC_RELEHOLD)) { 685 mutex_enter(&(vp)->v_lock); 686 if (vp->v_count > 0) { 687 mutex_exit(&(vp)->v_lock); 688 continue; 689 } 690 mutex_exit(&(vp)->v_lock); 691 VN_HOLD(vp); 692 remque(pcp); 693 vp->v_data = NULL; 694 vp->v_vfsp = &EIO_vfs; 695 vp->v_type = VBAD; 696 VN_RELE(vp); 697 if (!(pcp->pc_flags & PC_EXTERNAL)) { 698 (void) pvn_vplist_dirty(vp, 699 (u_offset_t)0, pcfs_putapage, 700 B_INVAL | B_TRUNC, 701 (struct cred *)NULL); 702 vn_free(vp); 703 } 704 kmem_free(pcp, sizeof (struct pcnode)); 705 fsp->pcfs_frefs --; 706 fsp->pcfs_nrefs --; 707 VFS_RELE(vfsp); 708 } 709 } 710 } 711 #ifdef undef 712 if (fsp->pcfs_frefs) { 713 rw_exit(&pcnodes_lock); 714 panic("pc_diskchanged: frefs"); 715 } 716 if (fsp->pcfs_nrefs) { 717 rw_exit(&pcnodes_lock); 718 panic("pc_diskchanged: nrefs"); 719 } 720 #endif 721 if (!(vfsp->vfs_flag & VFS_UNMOUNTED) && 722 fsp->pcfs_fatp != (uchar_t *)0) { 723 pc_invalfat(fsp); 724 } else { 725 binval(fsp->pcfs_xdev); 726 } 727 } 728