vfs_subr.c

来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 1,613 行 · 第 1/3 页

C
1,613
字号
{	if (bp->b_vp)		panic("bgetvp: not free");	VHOLD(vp);	bp->b_vp = vp;	if (vp->v_type == VBLK || vp->v_type == VCHR)		bp->b_dev = vp->v_rdev;	else		bp->b_dev = NODEV;	/*	 * Insert onto list for new vnode.	 */	bufinsvn(bp, &vp->v_cleanblkhd);}/* * Disassociate a buffer from a vnode. */voidbrelvp(bp)	register struct buf *bp;{	struct vnode *vp;	if (bp->b_vp == (struct vnode *) 0)		panic("brelvp: NULL");	/*	 * Delete from old vnode list, if on one.	 */	if (bp->b_vnbufs.le_next != NOLIST)		bufremvn(bp);	vp = bp->b_vp;	bp->b_vp = (struct vnode *) 0;	HOLDRELE(vp);}/* * Reassign a buffer from one vnode to another. * Used to assign file specific control information * (indirect blocks) to the vnode to which they belong. */voidreassignbuf(bp, newvp)	register struct buf *bp;	register struct vnode *newvp;{	register struct buflists *listheadp;	if (newvp == NULL) {		printf("reassignbuf: NULL");		return;	}	/*	 * Delete from old vnode list, if on one.	 */	if (bp->b_vnbufs.le_next != NOLIST)		bufremvn(bp);	/*	 * If dirty, put on list of dirty buffers;	 * otherwise insert onto list of clean buffers.	 */	if (bp->b_flags & B_DELWRI)		listheadp = &newvp->v_dirtyblkhd;	else		listheadp = &newvp->v_cleanblkhd;	bufinsvn(bp, listheadp);}/* * Create a vnode for a block device. * Used for root filesystem, argdev, and swap areas. * Also used for memory file system special devices. */intbdevvp(dev, vpp)	dev_t dev;	struct vnode **vpp;{	return (getdevvp(dev, vpp, VBLK));}/* * Create a vnode for a character device. * Used for kernfs and some console handling. */intcdevvp(dev, vpp)	dev_t dev;	struct vnode **vpp;{	return (getdevvp(dev, vpp, VCHR));}/* * Create a vnode for a device. * Used by bdevvp (block device) for root file system etc., * and by cdevvp (character device) for console and kernfs. */intgetdevvp(dev, vpp, type)	dev_t dev;	struct vnode **vpp;	enum vtype type;{	register struct vnode *vp;	struct vnode *nvp;	int error;	if (dev == NODEV)		return (0);	error = getnewvnode(VT_NON, NULL, spec_vnodeop_p, &nvp);	if (error) {		*vpp = NULLVP;		return (error);	}	vp = nvp;	vp->v_type = type;	if ((nvp = checkalias(vp, dev, NULL)) != 0) {		vput(vp);		vp = nvp;	}	*vpp = vp;	return (0);}/* * Check to see if the new vnode represents a special device * for which we already have a vnode (either because of * bdevvp() or because of a different vnode representing * the same block device). If such an alias exists, deallocate * the existing contents and return the aliased vnode. The * caller is responsible for filling it with its new contents. */struct vnode *checkalias(nvp, nvp_rdev, mp)	register struct vnode *nvp;	dev_t nvp_rdev;	struct mount *mp;{	register struct vnode *vp;	struct vnode **vpp;	if (nvp->v_type != VBLK && nvp->v_type != VCHR)		return (NULLVP);	vpp = &speclisth[SPECHASH(nvp_rdev)];loop:	for (vp = *vpp; vp; vp = vp->v_specnext) {		if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type)			continue;		/*		 * Alias, but not in use, so flush it out.		 */		if (vp->v_usecount == 0) {			vgone(vp);			goto loop;		}		if (vget(vp, 1))			goto loop;		break;	}	if (vp == NULL || vp->v_tag != VT_NON || vp->v_type != VBLK) {		MALLOC(nvp->v_specinfo, struct specinfo *,			sizeof(struct specinfo), M_VNODE, M_WAITOK);		nvp->v_rdev = nvp_rdev;		nvp->v_hashchain = vpp;		nvp->v_specnext = *vpp;		nvp->v_specflags = 0;		*vpp = nvp;		if (vp != NULL) {			nvp->v_flag |= VALIASED;			vp->v_flag |= VALIASED;			vput(vp);		}		return (NULLVP);	}	VOP_UNLOCK(vp);	vclean(vp, 0);	vp->v_op = nvp->v_op;	vp->v_tag = nvp->v_tag;	nvp->v_type = VNON;	insmntque(vp, mp);	return (vp);}/* * Grab a particular vnode from the free list, increment its * reference count and lock it. The vnode lock bit is set the * vnode is being eliminated in vgone. The process is awakened * when the transition is completed, and an error returned to * indicate that the vnode is no longer usable (possibly having * been changed to a new file system type). */intvget(vp, lockflag)	register struct vnode *vp;	int lockflag;{	/*	 * If the vnode is in the process of being cleaned out for	 * another use, we wait for the cleaning to finish and then	 * return failure. Cleaning is determined either by checking	 * that the VXLOCK flag is set, or that the use count is	 * zero with the back pointer set to show that it has been	 * removed from the free list by getnewvnode. The VXLOCK	 * flag may not have been set yet because vclean is blocked in	 * the VOP_LOCK call waiting for the VOP_INACTIVE to complete.	 */	if ((vp->v_flag & VXLOCK) ||	    (vp->v_usecount == 0 &&	     vp->v_freelist.tqe_prev == (struct vnode **)0xdeadb)) {		vp->v_flag |= VXWANT;		tsleep((caddr_t)vp, PINOD, "vget", 0);		return (1);	}	if (vp->v_usecount == 0)		TAILQ_REMOVE(&vnode_free_list, vp, v_freelist);	vp->v_usecount++;	if (lockflag)		VOP_LOCK(vp);	return (0);}/* * Vnode reference, just increment the count */voidvref(vp)	struct vnode *vp;{	if (vp->v_usecount <= 0)		panic("vref used where vget required");	vp->v_usecount++;}/* * vput(), just unlock and vrele() */voidvput(vp)	register struct vnode *vp;{	VOP_UNLOCK(vp);	vrele(vp);}/* * Vnode release. * If count drops to zero, call inactive routine and return to freelist. */voidvrele(vp)	register struct vnode *vp;{#ifdef DIAGNOSTIC	if (vp == NULL)		panic("vrele: null vp");#endif	vp->v_usecount--;	if (vp->v_usecount > 0)		return;#ifdef DIAGNOSTIC	if (vp->v_usecount != 0 || vp->v_writecount != 0) {		vprint("vrele: bad ref count", vp);		panic("vrele: ref cnt");	}#endif	/*	 * insert at tail of LRU list	 */	TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_freelist);	VOP_INACTIVE(vp);}/* * Page or buffer structure gets a reference. */voidvhold(vp)	register struct vnode *vp;{	vp->v_holdcnt++;}/* * Page or buffer structure frees a reference. */voidholdrele(vp)	register struct vnode *vp;{	if (vp->v_holdcnt <= 0)		panic("holdrele: holdcnt");	vp->v_holdcnt--;}/* * Remove any vnodes in the vnode table belonging to mount point mp. * * If MNT_NOFORCE is specified, there should not be any active ones, * return error if any are found (nb: this is a user error, not a * system error). If MNT_FORCE is specified, detach any active vnodes * that are found. */#ifdef DEBUGint busyprt = 0;	/* print out busy vnodes */struct ctldebug debug1 = { "busyprt", &busyprt };#endifintvflush(mp, skipvp, flags)	struct mount *mp;	struct vnode *skipvp;	int flags;{	register struct vnode *vp, *nvp;	int busy = 0;	if ((mp->mnt_flag & MNT_MPBUSY) == 0)		panic("vflush: not busy");loop:	for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) {		if (vp->v_mount != mp)			goto loop;		nvp = vp->v_mntvnodes.le_next;		/*		 * Skip over a selected vnode.		 */		if (vp == skipvp)			continue;		/*		 * Skip over a vnodes marked VSYSTEM.		 */		if ((flags & SKIPSYSTEM) && (vp->v_flag & VSYSTEM))			continue;		/*		 * If WRITECLOSE is set, only flush out regular file		 * vnodes open for writing.		 */		if ((flags & WRITECLOSE) &&		    (vp->v_writecount == 0 || vp->v_type != VREG))			continue;		/*		 * With v_usecount == 0, all we need to do is clear		 * out the vnode data structures and we are done.		 */		if (vp->v_usecount == 0) {			vgone(vp);			continue;		}		/*		 * If FORCECLOSE is set, forcibly close the vnode.		 * For block or character devices, revert to an		 * anonymous device. For all other files, just kill them.		 */		if (flags & FORCECLOSE) {			if (vp->v_type != VBLK && vp->v_type != VCHR) {				vgone(vp);			} else {				vclean(vp, 0);				vp->v_op = spec_vnodeop_p;				insmntque(vp, (struct mount *)0);			}			continue;		}#ifdef DEBUG		if (busyprt)			vprint("vflush: busy vnode", vp);#endif		busy++;	}	if (busy)		return (EBUSY);	return (0);}/* * Disassociate the underlying file system from a vnode. */voidvclean(vp, flags)	register struct vnode *vp;	int flags;{	int active;	/*	 * Check to see if the vnode is in use.	 * If so we have to reference it before we clean it out	 * so that its count cannot fall to zero and generate a	 * race against ourselves to recycle it.	 */	if ((active = vp->v_usecount) != 0)		VREF(vp);	/*	 * Even if the count is zero, the VOP_INACTIVE routine may still	 * have the object locked while it cleans it out. The VOP_LOCK	 * ensures that the VOP_INACTIVE routine is done with its work.	 * For active vnodes, it ensures that no other activity can	 * occur while the underlying object is being cleaned out.	 */	VOP_LOCK(vp);	/*	 * Prevent the vnode from being recycled or	 * brought into use while we clean it out.	 */	if (vp->v_flag & VXLOCK)		panic("vclean: deadlock");	vp->v_flag |= VXLOCK;	/*	 * Clean out any buffers associated with the vnode.	 */	if (flags & DOCLOSE)		vinvalbuf(vp, V_SAVE, NOCRED, NULL, 0, 0);	/*	 * Any other processes trying to obtain this lock must first	 * wait for VXLOCK to clear, then call the new lock operation.	 */	VOP_UNLOCK(vp);	/*	 * If purging an active vnode, it must be closed and	 * deactivated before being reclaimed.	 */	if (active) {		if (flags & DOCLOSE)			VOP_CLOSE(vp, FNONBLOCK, NOCRED, NULL);		VOP_INACTIVE(vp);	}	/*	 * Reclaim the vnode.	 */	if (VOP_RECLAIM(vp))		panic("vclean: cannot reclaim");	if (active)		vrele(vp);	/*	 * Done with purge, notify sleepers of the grim news.	 */	vp->v_op = dead_vnodeop_p;	vp->v_tag = VT_NON;	vp->v_flag &= ~VXLOCK;	if (vp->v_flag & VXWANT) {		vp->v_flag &= ~VXWANT;		wakeup((caddr_t)vp);	}}/* * Eliminate all activity associated with  the requested vnode * and with all vnodes aliased to the requested vnode. */voidvgoneall(vp)	register struct vnode *vp;{	register struct vnode *vq;	if (vp->v_flag & VALIASED) {		/*		 * If a vgone (or vclean) is already in progress,		 * wait until it is done and return.		 */		if (vp->v_flag & VXLOCK) {			vp->v_flag |= VXWANT;			tsleep((caddr_t)vp, PINOD, "vgoneall", 0);			return;		}		/*		 * Ensure that vp will not be vgone'd while we		 * are eliminating its aliases.		 */		vp->v_flag |= VXLOCK;		while (vp->v_flag & VALIASED) {			for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) {				if (vq->v_rdev != vp->v_rdev ||				    vq->v_type != vp->v_type || vp == vq)					continue;				vgone(vq);				break;			}		}		/*		 * Remove the lock so that vgone below will		 * really eliminate the vnode after which time		 * vgone will awaken any sleepers.		 */		vp->v_flag &= ~VXLOCK;	}	vgone(vp);}/* * Eliminate all activity associated with a vnode * in preparation for reuse. */voidvgone(vp)	register struct vnode *vp;{	register struct vnode *vq;	struct vnode *vx;	/*	 * If a vgone (or vclean) is already in progress,	 * wait until it is done and return.	 */	if (vp->v_flag & VXLOCK) {		vp->v_flag |= VXWANT;		tsleep((caddr_t)vp, PINOD, "vgone", 0);		return;	}	/*	 * Clean out the filesystem specific data.	 */	vclean(vp, DOCLOSE);	/*	 * Delete from old mount point vnode list, if on one.	 */	insmntque(vp, (struct mount *)0);	/*	 * If special device, remove it from special device alias list.	 */	if (vp->v_type == VBLK || vp->v_type == VCHR) {		if (*vp->v_hashchain == vp) {			*vp->v_hashchain = vp->v_specnext;		} else {			for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) {				if (vq->v_specnext != vp)

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?