summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkib <kib@FreeBSD.org>2014-12-13 16:02:37 +0000
committerkib <kib@FreeBSD.org>2014-12-13 16:02:37 +0000
commitca2d4fd9c1acb3914333668ef0b231d092912fc0 (patch)
tree305f7781d381bc4c432bacd1f107ed6097bce026
parent57f4abab04df8f943853c019a4d2882991f58bf1 (diff)
downloadFreeBSD-src-ca2d4fd9c1acb3914333668ef0b231d092912fc0.zip
FreeBSD-src-ca2d4fd9c1acb3914333668ef0b231d092912fc0.tar.gz
The vinactive() call in vgonel() may start writes for the dirty pages,
creating delayed write buffers belonging to the reclaimed vnode. Put the buffer cleanup code after inactivation. Add asserts that ensure that buffer queues are empty and add BO_DEAD flag for bufobj to check that no buffers are added after the cleanup. BO_DEAD is only used by INVARIANTS-enabled kernels. Reported and tested by: pho (previous version) Sponsored by: The FreeBSD Foundation MFC after: 1 week
-rw-r--r--sys/kern/vfs_subr.c34
-rw-r--r--sys/sys/bufobj.h1
2 files changed, 25 insertions, 10 deletions
diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c
index 95d2451..2920523 100644
--- a/sys/kern/vfs_subr.c
+++ b/sys/kern/vfs_subr.c
@@ -1567,6 +1567,7 @@ buf_vlist_add(struct buf *bp, struct bufobj *bo, b_xflags_t xflags)
int error;
ASSERT_BO_WLOCKED(bo);
+ KASSERT((bo->bo_flag & BO_DEAD) == 0, ("dead bo %p", bo));
KASSERT((bp->b_xflags & (BX_VNDIRTY|BX_VNCLEAN)) == 0,
("buf_vlist_add: Buf %p has existing xflags %d", bp, bp->b_xflags));
bp->b_xflags |= xflags;
@@ -2803,16 +2804,6 @@ vgonel(struct vnode *vp)
vfs_notify_upper(vp, VFS_NOTIFY_UPPER_RECLAIM);
/*
- * Clean out any buffers associated with the vnode.
- * If the flush fails, just toss the buffers.
- */
- mp = NULL;
- if (!TAILQ_EMPTY(&vp->v_bufobj.bo_dirty.bv_hd))
- (void) vn_start_secondary_write(vp, &mp, V_WAIT);
- if (vinvalbuf(vp, V_SAVE, 0, 0) != 0)
- vinvalbuf(vp, 0, 0, 0);
-
- /*
* If purging an active vnode, it must be closed and
* deactivated before being reclaimed.
*/
@@ -2826,6 +2817,29 @@ vgonel(struct vnode *vp)
}
if (vp->v_type == VSOCK)
vfs_unp_reclaim(vp);
+
+ /*
+ * Clean out any buffers associated with the vnode.
+ * If the flush fails, just toss the buffers.
+ */
+ mp = NULL;
+ if (!TAILQ_EMPTY(&vp->v_bufobj.bo_dirty.bv_hd))
+ (void) vn_start_secondary_write(vp, &mp, V_WAIT);
+ if (vinvalbuf(vp, V_SAVE, 0, 0) != 0) {
+ while (vinvalbuf(vp, 0, 0, 0) != 0)
+ ;
+ }
+#ifdef INVARIANTS
+ BO_LOCK(&vp->v_bufobj);
+ KASSERT(TAILQ_EMPTY(&vp->v_bufobj.bo_dirty.bv_hd) &&
+ vp->v_bufobj.bo_dirty.bv_cnt == 0 &&
+ TAILQ_EMPTY(&vp->v_bufobj.bo_clean.bv_hd) &&
+ vp->v_bufobj.bo_clean.bv_cnt == 0,
+ ("vp %p bufobj not invalidated", vp));
+ vp->v_bufobj.bo_flag |= BO_DEAD;
+ BO_UNLOCK(&vp->v_bufobj);
+#endif
+
/*
* Reclaim the vnode.
*/
diff --git a/sys/sys/bufobj.h b/sys/sys/bufobj.h
index 5f8d53d..0fa6c8c 100644
--- a/sys/sys/bufobj.h
+++ b/sys/sys/bufobj.h
@@ -112,6 +112,7 @@ struct bufobj {
*/
#define BO_ONWORKLST (1 << 0) /* On syncer work-list */
#define BO_WWAIT (1 << 1) /* Wait for output to complete */
+#define BO_DEAD (1 << 2) /* Dead; only with INVARIANTS */
#define BO_LOCKPTR(bo) (&(bo)->bo_lock)
#define BO_LOCK(bo) rw_wlock(BO_LOCKPTR((bo)))
OpenPOWER on IntegriCloud