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