From e0092341a30906d2f43d89f76d181dd96ccb9802 Mon Sep 17 00:00:00 2001 From: Matthew Dillon Date: Thu, 14 Jan 2010 20:01:54 -0800 Subject: [PATCH] HAMMER VFS - hammer_inode / vp races * The hammer_inode must be locked when clearing the vp during a reclaim to interlock with a concurrent reclaim. * hammer_get_vnode() must hold the vp while holding the hammer_inode locked to prevent destruction when racing against drops or reclaims. * Remove the HAMMER_INODE_VHELD flag. Backend truncations and the last inode release should acquire the vnode properly now in hammer_inode_unloadable_check(). --- sys/vfs/hammer/hammer.h | 2 +- sys/vfs/hammer/hammer_inode.c | 32 +++++++++++++++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/sys/vfs/hammer/hammer.h b/sys/vfs/hammer/hammer.h index a2e04461f2..e883edf5ba 100644 --- a/sys/vfs/hammer/hammer.h +++ b/sys/vfs/hammer/hammer.h @@ -379,7 +379,7 @@ typedef struct hammer_inode *hammer_inode_t; #define HAMMER_INODE_DELETED 0x0080 /* inode delete (backend) */ #define HAMMER_INODE_DELONDISK 0x0100 /* delete synchronized to disk */ #define HAMMER_INODE_RO 0x0200 /* read-only (because of as-of) */ -#define HAMMER_INODE_VHELD 0x0400 /* vnode held on sync */ +#define HAMMER_INODE_UNUSED0400 0x0400 #define HAMMER_INODE_DONDISK 0x0800 /* data records may be on disk */ #define HAMMER_INODE_BUFS 0x1000 /* dirty high level bps present */ #define HAMMER_INODE_REFLUSH 0x2000 /* flush on dependancy / reflush */ diff --git a/sys/vfs/hammer/hammer_inode.c b/sys/vfs/hammer/hammer_inode.c index c70ac50b66..ed706b3a29 100644 --- a/sys/vfs/hammer/hammer_inode.c +++ b/sys/vfs/hammer/hammer_inode.c @@ -213,6 +213,8 @@ hammer_vop_inactive(struct vop_inactive_args *ap) * * Once the association is lost we are on our own with regards to * flushing the inode. + * + * We must interlock ip->vp so hammer_get_vnode() can avoid races. */ int hammer_vop_reclaim(struct vop_reclaim_args *ap) @@ -225,6 +227,7 @@ hammer_vop_reclaim(struct vop_reclaim_args *ap) if ((ip = vp->v_data) != NULL) { hmp = ip->hmp; + hammer_lock_ex(&ip->lock); vp->v_data = NULL; ip->vp = NULL; @@ -233,6 +236,7 @@ hammer_vop_reclaim(struct vop_reclaim_args *ap) ++hmp->inode_reclaims; ip->flags |= HAMMER_INODE_RECLAIM; } + hammer_unlock(&ip->lock); hammer_rel_inode(ip, 1); } return(0); @@ -321,14 +325,31 @@ hammer_get_vnode(struct hammer_inode *ip, struct vnode **vpp) } /* + * Interlock vnode clearing. This does not prevent the + * vnode from going into a reclaimed state but it does + * prevent it from being destroyed or reused so the vget() + * will properly fail. + */ + hammer_lock_ex(&ip->lock); + if ((vp = ip->vp) == NULL) { + hammer_unlock(&ip->lock); + continue; + } + vhold_interlocked(vp); + hammer_unlock(&ip->lock); + + /* * loop if the vget fails (aka races), or if the vp * no longer matches ip->vp. */ if (vget(vp, LK_EXCLUSIVE) == 0) { - if (vp == ip->vp) + if (vp == ip->vp) { + vdrop(vp); break; + } vput(vp); } + vdrop(vp); } *vpp = vp; return(error); @@ -1932,14 +1953,19 @@ hammer_flush_inode_core(hammer_inode_t ip, hammer_flush_group_t flg, int flags) if (flg->total_count == hammer_autoflush) flags |= HAMMER_FLUSH_SIGNAL; +#if 0 /* * We need to be able to vfsync/truncate from the backend. + * + * XXX Any truncation from the backend will acquire the vnode + * independently. */ KKASSERT((ip->flags & HAMMER_INODE_VHELD) == 0); if (ip->vp && (ip->vp->v_flag & VINACTIVE) == 0) { ip->flags |= HAMMER_INODE_VHELD; vref(ip->vp); } +#endif /* * Figure out how many in-memory records we can actually flush @@ -1994,10 +2020,12 @@ hammer_flush_inode_core(hammer_inode_t ip, hammer_flush_group_t flg, int flags) --flg->total_count; ip->flush_state = HAMMER_FST_SETUP; ip->flush_group = NULL; +#if 0 if (ip->flags & HAMMER_INODE_VHELD) { ip->flags &= ~HAMMER_INODE_VHELD; vrele(ip->vp); } +#endif /* * REFLUSH is needed to trigger dependancy wakeups @@ -2377,6 +2405,7 @@ hammer_flush_inode_done(hammer_inode_t ip, int error) RB_REMOVE(hammer_fls_rb_tree, &ip->flush_group->flush_tree, ip); ip->flush_group = NULL; +#if 0 /* * Clean up the vnode ref and tracking counts. */ @@ -2384,6 +2413,7 @@ hammer_flush_inode_done(hammer_inode_t ip, int error) ip->flags &= ~HAMMER_INODE_VHELD; vrele(ip->vp); } +#endif --hmp->count_iqueued; --hammer_count_iqueued; -- 2.11.4.GIT