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 pc_cluster32_t ncl = 0; 162 163 scluster = fsp->pcfs_rdirstart; 164 if (pc_fileclsize(fsp, scluster, &ncl)) { 165 PC_DPRINTF1(2, "cluster chain " 166 "corruption, scluster=%d\n", 167 scluster); 168 pcp->pc_flags |= PC_INVAL; 169 } 170 pcp->pc_size = fsp->pcfs_clsize * ncl; 171 } else { 172 pcp->pc_size = 173 fsp->pcfs_rdirsec * fsp->pcfs_secsize; 174 } 175 } else { 176 pc_cluster32_t ncl = 0; 177 178 if (pc_fileclsize(fsp, scluster, &ncl)) { 179 PC_DPRINTF1(2, "cluster chain corruption, " 180 "scluster=%d\n", scluster); 181 pcp->pc_flags |= PC_INVAL; 182 } 183 pcp->pc_size = fsp->pcfs_clsize * ncl; 184 } 185 } else { 186 vn_setops(vp, pcfs_fvnodeops); 187 vp->v_type = VREG; 188 vp->v_flag = VNOSWAP; 189 fsp->pcfs_frefs++; 190 pcp->pc_size = ltohi(ep->pcd_size); 191 } 192 fsp->pcfs_nrefs++; 193 VFS_HOLD(PCFSTOVFS(fsp)); 194 vp->v_data = (caddr_t)pcp; 195 vp->v_vfsp = PCFSTOVFS(fsp); 196 vn_exists(vp); 197 rw_enter(&pcnodes_lock, RW_WRITER); 198 insque(pcp, hp); 199 rw_exit(&pcnodes_lock); 200 return (pcp); 201 } 202 203 int 204 syncpcp(struct pcnode *pcp, int flags) 205 { 206 int err; 207 if (!vn_has_cached_data(PCTOV(pcp))) 208 err = 0; 209 else 210 err = VOP_PUTPAGE(PCTOV(pcp), (offset_t)0, (uint_t)0, 211 flags, (struct cred *)0); 212 213 return (err); 214 } 215 216 void 217 pc_rele(struct pcnode *pcp) 218 { 219 struct pcfs *fsp; 220 struct vnode *vp; 221 int err; 222 223 vp = PCTOV(pcp); 224 PC_DPRINTF1(8, "pc_rele vp=0x%p\n", (void *)vp); 225 226 fsp = VFSTOPCFS(vp->v_vfsp); 227 ASSERT(fsp->pcfs_flags & PCFS_LOCKED); 228 229 rw_enter(&pcnodes_lock, RW_WRITER); 230 pcp->pc_flags |= PC_RELEHOLD; 231 232 retry: 233 if (vp->v_type != VDIR && (pcp->pc_flags & PC_INVAL) == 0) { 234 /* 235 * If the file was removed while active it may be safely 236 * truncated now. 237 */ 238 239 if (pcp->pc_entry.pcd_filename[0] == PCD_ERASED) { 240 (void) pc_truncate(pcp, 0); 241 } else if (pcp->pc_flags & PC_CHG) { 242 (void) pc_nodeupdate(pcp); 243 } 244 err = syncpcp(pcp, B_INVAL); 245 if (err) { 246 (void) syncpcp(pcp, B_INVAL|B_FORCE); 247 } 248 } 249 if (vn_has_cached_data(vp)) { 250 /* 251 * pvn_vplist_dirty will abort all old pages 252 */ 253 (void) pvn_vplist_dirty(vp, (u_offset_t)0, 254 pcfs_putapage, B_INVAL, (struct cred *)NULL); 255 } 256 257 (void) pc_syncfat(fsp); 258 mutex_enter(&vp->v_lock); 259 if (vn_has_cached_data(vp)) { 260 mutex_exit(&vp->v_lock); 261 goto retry; 262 } 263 ASSERT(!vn_has_cached_data(vp)); 264 265 vp->v_count--; /* release our hold from vn_rele */ 266 if (vp->v_count > 0) { /* Is this check still needed? */ 267 PC_DPRINTF1(3, "pc_rele: pcp=0x%p HELD AGAIN!\n", (void *)pcp); 268 mutex_exit(&vp->v_lock); 269 pcp->pc_flags &= ~PC_RELEHOLD; 270 rw_exit(&pcnodes_lock); 271 return; 272 } 273 274 remque(pcp); 275 rw_exit(&pcnodes_lock); 276 /* 277 * XXX - old code had a check for !(pcp->pc_flags & PC_INVAL) 278 * here. Seems superfluous/incorrect, but then earlier on PC_INVAL 279 * was never set anywhere in PCFS. Now it is, and we _have_ to drop 280 * the file reference here. Else, we'd screw up umount/modunload. 281 */ 282 if ((vp->v_type == VREG)) { 283 fsp->pcfs_frefs--; 284 } 285 fsp->pcfs_nrefs--; 286 VFS_RELE(vp->v_vfsp); 287 288 if (fsp->pcfs_nrefs < 0) { 289 panic("pc_rele: nrefs count"); 290 } 291 if (fsp->pcfs_frefs < 0) { 292 panic("pc_rele: frefs count"); 293 } 294 295 mutex_exit(&vp->v_lock); 296 vn_invalid(vp); 297 vn_free(vp); 298 kmem_free(pcp, sizeof (struct pcnode)); 299 } 300 301 /* 302 * Mark a pcnode as modified with the current time. 303 */ 304 void 305 pc_mark_mod(struct pcnode *pcp) 306 { 307 timestruc_t now; 308 309 if (PCTOV(pcp)->v_type == VREG) { 310 gethrestime(&now); 311 if (pc_tvtopct(&now, &pcp->pc_entry.pcd_mtime)) 312 PC_DPRINTF1(2, "pc_mark_mod failed timestamp " 313 "conversion, curtime = %lld\n", 314 (long long)now.tv_sec); 315 pcp->pc_flags |= PC_CHG; 316 } 317 } 318 319 /* 320 * Mark a pcnode as accessed with the current time. 321 */ 322 void 323 pc_mark_acc(struct pcnode *pcp) 324 { 325 struct pctime pt = { 0, 0 }; 326 timestruc_t now; 327 328 if (PCTOV(pcp)->v_type == VREG) { 329 gethrestime(&now); 330 if (pc_tvtopct(&now, &pt)) 331 PC_DPRINTF1(2, "pc_mark_acc failed timestamp " 332 "conversion, curtime = %lld\n", 333 (long long)now.tv_sec); 334 pcp->pc_entry.pcd_ladate = pt.pct_date; 335 pcp->pc_flags |= PC_CHG; 336 } 337 } 338 339 /* 340 * Truncate a file to a length. 341 * Node must be locked. 342 */ 343 int 344 pc_truncate(struct pcnode *pcp, uint_t length) 345 { 346 struct pcfs *fsp; 347 struct vnode *vp; 348 int error = 0; 349 350 PC_DPRINTF3(4, "pc_truncate pcp=0x%p, len=%u, size=%u\n", 351 (void *)pcp, length, pcp->pc_size); 352 vp = PCTOV(pcp); 353 if (pcp->pc_flags & PC_INVAL) 354 return (EIO); 355 fsp = VFSTOPCFS(vp->v_vfsp); 356 /* 357 * directories are always truncated to zero and are not marked 358 */ 359 if (vp->v_type == VDIR) { 360 error = pc_bfree(pcp, 0); 361 return (error); 362 } 363 /* 364 * If length is the same as the current size 365 * just mark the pcnode and return. 366 */ 367 if (length > pcp->pc_size) { 368 daddr_t bno; 369 uint_t llcn; 370 371 /* 372 * We are extending a file. 373 * Extend it with _one_ call to pc_balloc (no holes) 374 * since we don't need to use the block number(s). 375 */ 376 if ((daddr_t)howmany((offset_t)pcp->pc_size, fsp->pcfs_clsize) < 377 (llcn = (daddr_t)howmany((offset_t)length, 378 fsp->pcfs_clsize))) { 379 error = pc_balloc(pcp, (daddr_t)(llcn - 1), 1, &bno); 380 } 381 if (error) { 382 pc_cluster32_t ncl = 0; 383 PC_DPRINTF1(2, "pc_truncate: error=%d\n", error); 384 /* 385 * probably ran out disk space; 386 * determine current file size 387 */ 388 if (pc_fileclsize(fsp, pcp->pc_scluster, &ncl)) { 389 PC_DPRINTF1(2, "cluster chain corruption, " 390 "scluster=%d\n", pcp->pc_scluster); 391 pcp->pc_flags |= PC_INVAL; 392 } 393 pcp->pc_size = fsp->pcfs_clsize * ncl; 394 } else 395 pcp->pc_size = length; 396 397 } else if (length < pcp->pc_size) { 398 /* 399 * We are shrinking a file. 400 * Free blocks after the block that length points to. 401 */ 402 if (pc_blkoff(fsp, length) == 0) { 403 /* 404 * Truncation to a block (cluster size) boundary only 405 * requires us to invalidate everything after the new 406 * end of the file. 407 */ 408 (void) pvn_vplist_dirty(PCTOV(pcp), (u_offset_t)length, 409 pcfs_putapage, B_INVAL | B_TRUNC, CRED()); 410 } else { 411 /* 412 * pvn_vpzero() cannot deal with more than MAXBSIZE 413 * chunks. Since the FAT clustersize can get larger 414 * than that, we'll zero from the new length to the 415 * end of the cluster for clustersizes smaller than 416 * MAXBSIZE - or the end of the MAXBSIZE block in 417 * case we've got a large clustersize. 418 */ 419 size_t nbytes = 420 roundup(length, MIN(fsp->pcfs_clsize, MAXBSIZE)) - 421 length; 422 423 pvn_vpzero(PCTOV(pcp), (u_offset_t)length, nbytes); 424 (void) pvn_vplist_dirty(PCTOV(pcp), 425 (u_offset_t)length + nbytes, 426 pcfs_putapage, B_INVAL | B_TRUNC, CRED()); 427 } 428 error = pc_bfree(pcp, 429 (pc_cluster32_t)howmany((offset_t)length, 430 fsp->pcfs_clsize)); 431 pcp->pc_size = length; 432 } 433 pc_mark_mod(pcp); 434 return (error); 435 } 436 437 /* 438 * Get block for entry. 439 */ 440 static int 441 pc_getentryblock(struct pcnode *pcp, struct buf **bpp) 442 { 443 struct pcfs *fsp; 444 445 PC_DPRINTF0(7, "pc_getentryblock "); 446 fsp = VFSTOPCFS(PCTOV(pcp)->v_vfsp); 447 if (pcp->pc_eblkno >= fsp->pcfs_datastart || 448 (pcp->pc_eblkno - fsp->pcfs_rdirstart) < 449 (fsp->pcfs_rdirsec & ~(fsp->pcfs_spcl - 1))) { 450 *bpp = bread(fsp->pcfs_xdev, 451 pc_dbdaddr(fsp, pcp->pc_eblkno), fsp->pcfs_clsize); 452 } else { 453 *bpp = bread(fsp->pcfs_xdev, 454 pc_dbdaddr(fsp, pcp->pc_eblkno), 455 (int)(fsp->pcfs_datastart-pcp->pc_eblkno) * 456 fsp->pcfs_secsize); 457 } 458 if ((*bpp)->b_flags & B_ERROR) { 459 PC_DPRINTF0(1, "pc_getentryblock: error "); 460 brelse(*bpp); 461 pc_mark_irrecov(fsp); 462 return (EIO); 463 } 464 return (0); 465 } 466 467 /* 468 * Sync all data associated with a file. 469 * Flush all the blocks in the buffer cache out to disk, sync the FAT and 470 * update the directory entry. 471 */ 472 int 473 pc_nodesync(struct pcnode *pcp) 474 { 475 struct pcfs *fsp; 476 int err; 477 struct vnode *vp; 478 479 PC_DPRINTF1(7, "pc_nodesync pcp=0x%p\n", (void *)pcp); 480 vp = PCTOV(pcp); 481 fsp = VFSTOPCFS(vp->v_vfsp); 482 err = 0; 483 if (pcp->pc_flags & PC_MOD) { 484 /* 485 * Flush all data blocks from buffer cache and 486 * update the FAT which points to the data. 487 */ 488 if (err = syncpcp(pcp, 0)) { /* %% ?? how to handle error? */ 489 if (err == ENOMEM) 490 return (err); 491 else { 492 pc_mark_irrecov(fsp); 493 return (EIO); 494 } 495 } 496 pcp->pc_flags &= ~PC_MOD; 497 } 498 /* 499 * update the directory entry 500 */ 501 if (pcp->pc_flags & PC_CHG) 502 (void) pc_nodeupdate(pcp); 503 return (err); 504 } 505 506 /* 507 * Update the node's directory entry. 508 */ 509 int 510 pc_nodeupdate(struct pcnode *pcp) 511 { 512 struct buf *bp; 513 int error; 514 struct vnode *vp; 515 struct pcfs *fsp; 516 517 vp = PCTOV(pcp); 518 fsp = VFSTOPCFS(vp->v_vfsp); 519 if (IS_FAT32(fsp) && (vp->v_flag & VROOT)) { 520 /* no node to update */ 521 pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC); 522 return (0); 523 } 524 if (vp->v_flag & VROOT) { 525 panic("pc_nodeupdate"); 526 } 527 if (pcp->pc_flags & PC_INVAL) 528 return (0); 529 PC_DPRINTF3(7, "pc_nodeupdate pcp=0x%p, bn=%ld, off=%d\n", (void *)pcp, 530 pcp->pc_eblkno, pcp->pc_eoffset); 531 532 if (error = pc_getentryblock(pcp, &bp)) { 533 return (error); 534 } 535 if (vp->v_type == VREG) { 536 if (pcp->pc_flags & PC_CHG) 537 pcp->pc_entry.pcd_attr |= PCA_ARCH; 538 pcp->pc_entry.pcd_size = htoli(pcp->pc_size); 539 } 540 pc_setstartcluster(fsp, &pcp->pc_entry, pcp->pc_scluster); 541 *((struct pcdir *)(bp->b_un.b_addr + pcp->pc_eoffset)) = pcp->pc_entry; 542 bwrite2(bp); 543 error = geterror(bp); 544 if (error) 545 error = EIO; 546 brelse(bp); 547 if (error) { 548 PC_DPRINTF0(1, "pc_nodeupdate ERROR\n"); 549 pc_mark_irrecov(VFSTOPCFS(vp->v_vfsp)); 550 } 551 pcp->pc_flags &= ~(PC_CHG | PC_MOD | PC_ACC); 552 return (error); 553 } 554 555 /* 556 * Verify that the disk in the drive is the same one that we 557 * got the pcnode from. 558 * MUST be called with node unlocked. 559 */ 560 /* ARGSUSED */ 561 int 562 pc_verify(struct pcfs *fsp) 563 { 564 int fdstatus = 0; 565 int error = 0; 566 567 if (!fsp || fsp->pcfs_flags & PCFS_IRRECOV) 568 return (EIO); 569 570 if (!(fsp->pcfs_flags & PCFS_NOCHK) && fsp->pcfs_fatp) { 571 PC_DPRINTF1(4, "pc_verify fsp=0x%p\n", (void *)fsp); 572 error = cdev_ioctl(fsp->pcfs_vfs->vfs_dev, 573 FDGETCHANGE, (intptr_t)&fdstatus, FNATIVE|FKIOCTL, 574 NULL, NULL); 575 576 if (error) { 577 if (error == ENOTTY || error == ENXIO) { 578 error = 0; 579 } else { 580 PC_DPRINTF1(1, 581 "pc_verify: FDGETCHANGE ioctl failed: %d\n", 582 error); 583 pc_mark_irrecov(fsp); 584 } 585 } else if (fsp->pcfs_fatjustread) { 586 /* 587 * Ignore the results of the ioctl if we just 588 * read the FAT. There is a good chance that 589 * the disk changed bit will be on, because 590 * we've just mounted and we don't want to 591 * give a false positive that the sky is falling. 592 */ 593 fsp->pcfs_fatjustread = 0; 594 } else { 595 /* 596 * Oddly enough we can't check just one flag here. The 597 * x86 floppy driver sets a different flag 598 * (FDGC_DETECTED) than the sparc driver does. 599 * I think this MAY be a bug, and I filed 4165938 600 * to get someone to look at the behavior 601 * a bit more closely. In the meantime, my testing and 602 * code examination seem to indicate it is safe to 603 * check for either bit being set. 604 */ 605 if (fdstatus & (FDGC_HISTORY | FDGC_DETECTED)) { 606 PC_DPRINTF0(1, "pc_verify: change detected\n"); 607 pc_mark_irrecov(fsp); 608 } 609 } 610 } 611 if (!(error || fsp->pcfs_fatp)) { 612 error = pc_getfat(fsp); 613 } 614 615 return (error); 616 } 617 618 /* 619 * The disk has changed, pulling the rug out from beneath us. 620 * Mark the FS as being in an irrecoverable state. 621 * In a short while we'll clean up. 622 */ 623 void 624 pc_mark_irrecov(struct pcfs *fsp) 625 { 626 if (!(fsp->pcfs_flags & PCFS_NOCHK)) { 627 if (pc_lockfs(fsp, 1, 0)) { 628 /* 629 * Locking failed, which currently would 630 * only happen if the FS were already 631 * marked as hosed. If another reason for 632 * failure were to arise in the future, this 633 * routine would have to change. 634 */ 635 return; 636 } 637 638 fsp->pcfs_flags |= PCFS_IRRECOV; 639 cmn_err(CE_WARN, 640 "Disk was changed during an update or\n" 641 "an irrecoverable error was encountered.\n" 642 "File damage is possible. To prevent further\n" 643 "damage, this pcfs instance will now be frozen.\n" 644 "Use umount(1M) to release the instance.\n"); 645 (void) pc_unlockfs(fsp); 646 } 647 } 648 649 /* 650 * The disk has been changed! 651 */ 652 void 653 pc_diskchanged(struct pcfs *fsp) 654 { 655 struct pcnode *pcp, *npcp = NULL; 656 struct pchead *hp; 657 struct vnode *vp; 658 extern vfs_t EIO_vfs; 659 struct vfs *vfsp; 660 661 /* 662 * Eliminate all pcnodes (dir & file) associated with this fs. 663 * If the node is internal, ie, no references outside of 664 * pcfs itself, then release the associated vnode structure. 665 * Invalidate the in core FAT. 666 * Invalidate cached data blocks and blocks waiting for I/O. 667 */ 668 PC_DPRINTF1(1, "pc_diskchanged fsp=0x%p\n", (void *)fsp); 669 670 vfsp = PCFSTOVFS(fsp); 671 672 for (hp = pcdhead; hp < &pcdhead[NPCHASH]; hp++) { 673 for (pcp = hp->pch_forw; 674 pcp != (struct pcnode *)hp; pcp = npcp) { 675 npcp = pcp -> pc_forw; 676 vp = PCTOV(pcp); 677 if ((vp->v_vfsp == vfsp) && 678 !(pcp->pc_flags & PC_RELEHOLD)) { 679 mutex_enter(&(vp)->v_lock); 680 if (vp->v_count > 0) { 681 mutex_exit(&(vp)->v_lock); 682 continue; 683 } 684 mutex_exit(&(vp)->v_lock); 685 VN_HOLD(vp); 686 remque(pcp); 687 vp->v_data = NULL; 688 vp->v_vfsp = &EIO_vfs; 689 vp->v_type = VBAD; 690 VN_RELE(vp); 691 if (!(pcp->pc_flags & PC_EXTERNAL)) { 692 (void) pvn_vplist_dirty(vp, 693 (u_offset_t)0, pcfs_putapage, 694 B_INVAL | B_TRUNC, 695 (struct cred *)NULL); 696 vn_free(vp); 697 } 698 kmem_free(pcp, sizeof (struct pcnode)); 699 fsp->pcfs_nrefs --; 700 VFS_RELE(vfsp); 701 } 702 } 703 } 704 for (hp = pcfhead; fsp->pcfs_frefs && hp < &pcfhead[NPCHASH]; hp++) { 705 for (pcp = hp->pch_forw; fsp->pcfs_frefs && 706 pcp != (struct pcnode *)hp; pcp = npcp) { 707 npcp = pcp -> pc_forw; 708 vp = PCTOV(pcp); 709 if ((vp->v_vfsp == vfsp) && 710 !(pcp->pc_flags & PC_RELEHOLD)) { 711 mutex_enter(&(vp)->v_lock); 712 if (vp->v_count > 0) { 713 mutex_exit(&(vp)->v_lock); 714 continue; 715 } 716 mutex_exit(&(vp)->v_lock); 717 VN_HOLD(vp); 718 remque(pcp); 719 vp->v_data = NULL; 720 vp->v_vfsp = &EIO_vfs; 721 vp->v_type = VBAD; 722 VN_RELE(vp); 723 if (!(pcp->pc_flags & PC_EXTERNAL)) { 724 (void) pvn_vplist_dirty(vp, 725 (u_offset_t)0, pcfs_putapage, 726 B_INVAL | B_TRUNC, 727 (struct cred *)NULL); 728 vn_free(vp); 729 } 730 kmem_free(pcp, sizeof (struct pcnode)); 731 fsp->pcfs_frefs--; 732 fsp->pcfs_nrefs--; 733 VFS_RELE(vfsp); 734 } 735 } 736 } 737 #ifdef undef 738 if (fsp->pcfs_frefs) { 739 rw_exit(&pcnodes_lock); 740 panic("pc_diskchanged: frefs"); 741 } 742 if (fsp->pcfs_nrefs) { 743 rw_exit(&pcnodes_lock); 744 panic("pc_diskchanged: nrefs"); 745 } 746 #endif 747 if (!(vfsp->vfs_flag & VFS_UNMOUNTED) && 748 fsp->pcfs_fatp != (uchar_t *)0) { 749 pc_invalfat(fsp); 750 } else { 751 binval(fsp->pcfs_xdev); 752 } 753 } 754