diff options
author | kib <kib@FreeBSD.org> | 2011-04-24 10:47:56 +0000 |
---|---|---|
committer | kib <kib@FreeBSD.org> | 2011-04-24 10:47:56 +0000 |
commit | 38508e2c9106805f11c061609135bbe11e934d67 (patch) | |
tree | 0d608ac56aebd6ed577365a58f9e733bb4b382ab | |
parent | 96e417f7418b0ca583aab76927500e0e94afb15b (diff) | |
download | FreeBSD-src-38508e2c9106805f11c061609135bbe11e934d67.zip FreeBSD-src-38508e2c9106805f11c061609135bbe11e934d67.tar.gz |
VFS sometimes is unable to inactivate a vnode when vnode use count
goes to zero. E.g., the vnode might be only shared-locked at the time of
vput() call. Such vnodes are kept in the hash, so they can be found later.
If ffs_valloc() allocated an inode that has its vnode cached in hash, and
still owing the inactivation, then vget() call from ffs_valloc() clears
VI_OWEINACT, and then the vnode is reused for the newly allocated inode.
The problem is, the vnode is not reclaimed before it is put to the new
use. ffs_valloc() recycles vnode vm object, but this is not enough.
In particular, at least v_vflag should be cleared, and several bits of
UFS state need to be removed.
It is very inconvenient to call vgone() at this point. Instead, move
some parts of ufs_reclaim() into helper function ufs_prepare_reclaim(),
and call the helper from VOP_RECLAIM and ffs_valloc().
Reviewed by: mckusick
Tested by: pho
MFC after: 3 weeks
-rw-r--r-- | sys/ufs/ffs/ffs_alloc.c | 3 | ||||
-rw-r--r-- | sys/ufs/ufs/ufs_extern.h | 1 | ||||
-rw-r--r-- | sys/ufs/ufs/ufs_inode.c | 50 |
3 files changed, 31 insertions, 23 deletions
diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index 1795161..e60514d 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -1012,8 +1012,9 @@ dup_alloc: ip->i_din2->di_birthtime = ts.tv_sec; ip->i_din2->di_birthnsec = ts.tv_nsec; } + ufs_prepare_reclaim(*vpp); ip->i_flag = 0; - vnode_destroy_vobject(*vpp); + (*vpp)->v_vflag = 0; (*vpp)->v_type = VNON; if (fs->fs_magic == FS_UFS2_MAGIC) (*vpp)->v_op = &ffs_vnodeops2; diff --git a/sys/ufs/ufs/ufs_extern.h b/sys/ufs/ufs/ufs_extern.h index 6658b66..528b03b 100644 --- a/sys/ufs/ufs/ufs_extern.h +++ b/sys/ufs/ufs/ufs_extern.h @@ -76,6 +76,7 @@ int ufs_inactive(struct vop_inactive_args *); int ufs_init(struct vfsconf *); void ufs_itimes(struct vnode *vp); int ufs_lookup(struct vop_cachedlookup_args *); +void ufs_prepare_reclaim(struct vnode *vp); int ufs_readdir(struct vop_readdir_args *); int ufs_reclaim(struct vop_reclaim_args *); void ffs_snapgone(struct inode *); diff --git a/sys/ufs/ufs/ufs_inode.c b/sys/ufs/ufs/ufs_inode.c index 3b7509f..7ddbe97 100644 --- a/sys/ufs/ufs/ufs_inode.c +++ b/sys/ufs/ufs/ufs_inode.c @@ -172,6 +172,31 @@ out: return (error); } +void +ufs_prepare_reclaim(struct vnode *vp) +{ + struct inode *ip; +#ifdef QUOTA + int i; +#endif + + ip = VTOI(vp); + + vnode_destroy_vobject(vp); +#ifdef QUOTA + for (i = 0; i < MAXQUOTAS; i++) { + if (ip->i_dquot[i] != NODQUOT) { + dqrele(vp, ip->i_dquot[i]); + ip->i_dquot[i] = NODQUOT; + } + } +#endif +#ifdef UFS_DIRHASH + if (ip->i_dirhash != NULL) + ufsdirhash_free(ip); +#endif +} + /* * Reclaim an inode so that it can be used for other purposes. */ @@ -185,14 +210,9 @@ ufs_reclaim(ap) struct vnode *vp = ap->a_vp; struct inode *ip = VTOI(vp); struct ufsmount *ump = ip->i_ump; -#ifdef QUOTA - int i; -#endif - /* - * Destroy the vm object and flush associated pages. - */ - vnode_destroy_vobject(vp); + ufs_prepare_reclaim(vp); + if (ip->i_flag & IN_LAZYMOD) ip->i_flag |= IN_MODIFIED; UFS_UPDATE(vp, 0); @@ -200,21 +220,7 @@ ufs_reclaim(ap) * Remove the inode from its hash chain. */ vfs_hash_remove(vp); - /* - * Purge old data structures associated with the inode. - */ -#ifdef QUOTA - for (i = 0; i < MAXQUOTAS; i++) { - if (ip->i_dquot[i] != NODQUOT) { - dqrele(vp, ip->i_dquot[i]); - ip->i_dquot[i] = NODQUOT; - } - } -#endif -#ifdef UFS_DIRHASH - if (ip->i_dirhash != NULL) - ufsdirhash_free(ip); -#endif + /* * Lock the clearing of v_data so ffs_lock() can inspect it * prior to obtaining the lock. |