summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2015-07-01 06:54:25 +0000
committerkib <kib@FreeBSD.org>2015-07-01 06:54:25 +0000
commitfebe4e6e91057701646038ee282ff0d56df65c5d (patch)
treeb43cef5d29f3be05e4455e14d5c703a88dc88cef
parent854f0423fc9eaa8349d5c597f52271f0412cdeb5 (diff)
downloadFreeBSD-src-febe4e6e91057701646038ee282ff0d56df65c5d.zip
FreeBSD-src-febe4e6e91057701646038ee282ff0d56df65c5d.tar.gz
MFC r284495:
Keep a vnode which is freed but still owing inactivation, on the active list. This closes a race where such vnode is not msync-ed until reboot.
-rw-r--r--sys/kern/vfs_subr.c46
-rw-r--r--sys/ufs/ffs/ffs_vfsops.c14
2 files changed, 38 insertions, 22 deletions
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index a03d516..8de6dcf 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -173,6 +173,11 @@ static int reassignbufcalls;
SYSCTL_INT(_vfs, OID_AUTO, reassignbufcalls, CTLFLAG_RW, &reassignbufcalls, 0,
"Number of calls to reassignbuf");
+static u_long free_owe_inact;
+SYSCTL_ULONG(_vfs, OID_AUTO, free_owe_inact, CTLFLAG_RD, &free_owe_inact, 0,
+ "Number of times free vnodes kept on active list due to VFS "
+ "owing inactivation");
+
/*
* Cache for the mount type id assigned to NFS. This is used for
* special checks in nfs/nfs_nqlease.c and vm/vnode_pager.c.
@@ -2361,11 +2366,8 @@ vholdl(struct vnode *vp)
CTR2(KTR_VFS, "%s: vp %p", __func__, vp);
#ifdef INVARIANTS
/* getnewvnode() calls v_incr_usecount() without holding interlock. */
- if (vp->v_type != VNON || vp->v_data != NULL) {
+ if (vp->v_type != VNON || vp->v_data != NULL)
ASSERT_VI_LOCKED(vp, "vholdl");
- VNASSERT(vp->v_holdcnt > 0 || (vp->v_iflag & VI_FREE) != 0,
- vp, ("vholdl: free vnode is held"));
- }
#endif
vp->v_holdcnt++;
if ((vp->v_iflag & VI_FREE) == 0)
@@ -2434,23 +2436,29 @@ vdropl(struct vnode *vp)
VNASSERT(vp->v_holdcnt == 0, vp,
("vdropl: freeing when we shouldn't"));
active = vp->v_iflag & VI_ACTIVE;
- vp->v_iflag &= ~VI_ACTIVE;
- mp = vp->v_mount;
- mtx_lock(&vnode_free_list_mtx);
- if (active) {
- TAILQ_REMOVE(&mp->mnt_activevnodelist, vp,
- v_actfreelist);
- mp->mnt_activevnodelistsize--;
- }
- if (vp->v_iflag & VI_AGE) {
- TAILQ_INSERT_HEAD(&vnode_free_list, vp, v_actfreelist);
+ if ((vp->v_iflag & VI_OWEINACT) == 0) {
+ vp->v_iflag &= ~VI_ACTIVE;
+ mp = vp->v_mount;
+ mtx_lock(&vnode_free_list_mtx);
+ if (active) {
+ TAILQ_REMOVE(&mp->mnt_activevnodelist, vp,
+ v_actfreelist);
+ mp->mnt_activevnodelistsize--;
+ }
+ if (vp->v_iflag & VI_AGE) {
+ TAILQ_INSERT_HEAD(&vnode_free_list, vp,
+ v_actfreelist);
+ } else {
+ TAILQ_INSERT_TAIL(&vnode_free_list, vp,
+ v_actfreelist);
+ }
+ freevnodes++;
+ vp->v_iflag &= ~VI_AGE;
+ vp->v_iflag |= VI_FREE;
+ mtx_unlock(&vnode_free_list_mtx);
} else {
- TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_actfreelist);
+ atomic_add_long(&free_owe_inact, 1);
}
- freevnodes++;
- vp->v_iflag &= ~VI_AGE;
- vp->v_iflag |= VI_FREE;
- mtx_unlock(&vnode_free_list_mtx);
VI_UNLOCK(vp);
return;
}
diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c
index 0bcb084..53d85bd 100644
--- a/sys/ufs/ffs/ffs_vfsops.c
+++ b/sys/ufs/ffs/ffs_vfsops.c
@@ -1409,6 +1409,14 @@ ffs_statfs(mp, sbp)
return (0);
}
+static bool
+sync_doupdate(struct inode *ip)
+{
+
+ return ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED |
+ IN_UPDATE)) != 0);
+}
+
/*
* For a lazy sync, we only care about access times, quotas and the
* superblock. Other filesystem changes are already converted to
@@ -1442,15 +1450,15 @@ ffs_sync_lazy(mp)
* Test also all the other timestamp flags too, to pick up
* any other cases that could be missed.
*/
- if ((ip->i_flag & (IN_ACCESS | IN_CHANGE | IN_MODIFIED |
- IN_UPDATE)) == 0) {
+ if (!sync_doupdate(ip) && (vp->v_iflag & VI_OWEINACT) == 0) {
VI_UNLOCK(vp);
continue;
}
if ((error = vget(vp, LK_EXCLUSIVE | LK_NOWAIT | LK_INTERLOCK,
td)) != 0)
continue;
- error = ffs_update(vp, 0);
+ if (sync_doupdate(ip))
+ error = ffs_update(vp, 0);
if (error != 0)
allerror = error;
vput(vp);
OpenPOWER on IntegriCloud