xref: /illumos-gate/usr/src/uts/common/fs/pcfs/pc_node.c (revision 264a6e74)
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