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
This commit is contained in:
Konstantin Belousov 2011-04-24 10:47:56 +00:00
parent 16a174b5c5
commit d9ca1af7ed
3 changed files with 31 additions and 23 deletions

View File

@ -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;

View File

@ -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 *);

View File

@ -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.