Module Name: src Committed By: hannken Date: Thu Mar 30 09:14:59 UTC 2017
Modified Files: src/sys/kern: vfs_vnode.c Log Message: Change vrelel() to defer the test for a reclaimed vnode until we hold both the interlock and the vnode lock. Add a common operation to deallocate a vnode in state LOADING. To generate a diff of this commit: cvs rdiff -u -r1.78 -r1.79 src/sys/kern/vfs_vnode.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/vfs_vnode.c diff -u src/sys/kern/vfs_vnode.c:1.78 src/sys/kern/vfs_vnode.c:1.79 --- src/sys/kern/vfs_vnode.c:1.78 Thu Mar 30 09:14:08 2017 +++ src/sys/kern/vfs_vnode.c Thu Mar 30 09:14:59 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: vfs_vnode.c,v 1.78 2017/03/30 09:14:08 hannken Exp $ */ +/* $NetBSD: vfs_vnode.c,v 1.79 2017/03/30 09:14:59 hannken Exp $ */ /*- * Copyright (c) 1997-2011 The NetBSD Foundation, Inc. @@ -156,7 +156,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,v 1.78 2017/03/30 09:14:08 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,v 1.79 2017/03/30 09:14:59 hannken Exp $"); #include <sys/param.h> #include <sys/kernel.h> @@ -212,6 +212,7 @@ static pool_cache_t vcache_pool; static void lru_requeue(vnode_t *, vnodelst_t *); static vnodelst_t * lru_which(vnode_t *); static vnode_impl_t * vcache_alloc(void); +static void vcache_dealloc(vnode_impl_t *); static void vcache_free(vnode_impl_t *); static void vcache_init(void); static void vcache_reinit(void); @@ -662,6 +663,8 @@ vtryrele(vnode_t *vp) static void vrelel(vnode_t *vp, int flags) { + const bool async = ((flags & VRELEL_ASYNC_RELE) != 0); + const bool force = ((flags & VRELEL_FORCE_RELE) != 0); bool recycle, defer; int error; @@ -692,62 +695,48 @@ vrelel(vnode_t *vp, int flags) #endif /* - * If not clean, deactivate the vnode, but preserve - * our reference across the call to VOP_INACTIVE(). + * First try to get the vnode locked for VOP_INACTIVE(). + * Defer vnode release to vdrain_thread if caller requests + * it explicitly, is the pagedaemon or the lock failed. */ - if (VSTATE_GET(vp) != VS_RECLAIMED) { - recycle = false; - + if ((curlwp == uvm.pagedaemon_lwp) || async) { + defer = true; + } else { + mutex_exit(vp->v_interlock); + error = vn_lock(vp, + LK_EXCLUSIVE | LK_RETRY | (force ? 0 : LK_NOWAIT)); + defer = (error != 0); + mutex_enter(vp->v_interlock); + } + KASSERT(mutex_owned(vp->v_interlock)); + KASSERT(! (force && defer)); + if (defer) { /* - * XXX This ugly block can be largely eliminated if - * locking is pushed down into the file systems. - * - * Defer vnode release to vdrain_thread if caller - * requests it explicitly or is the pagedaemon. + * Defer reclaim to the kthread; it's not safe to + * clean it here. We donate it our last reference. */ - if ((curlwp == uvm.pagedaemon_lwp) || - (flags & VRELEL_ASYNC_RELE) != 0) { - defer = true; - } else if ((flags & VRELEL_FORCE_RELE) != 0) { - /* - * We have to try harder. - */ - mutex_exit(vp->v_interlock); - error = vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); - KASSERTMSG((error == 0), "vn_lock failed: %d", error); - mutex_enter(vp->v_interlock); - defer = false; - } else { - /* If we can't acquire the lock, then defer. */ - mutex_exit(vp->v_interlock); - error = vn_lock(vp, - LK_EXCLUSIVE | LK_RETRY | LK_NOWAIT); - defer = (error != 0); - mutex_enter(vp->v_interlock); - } - - KASSERT(mutex_owned(vp->v_interlock)); - KASSERT(! ((flags & VRELEL_FORCE_RELE) != 0 && defer)); + lru_requeue(vp, &lru_vrele_list); + mutex_exit(vp->v_interlock); + return; + } - if (defer) { - /* - * Defer reclaim to the kthread; it's not safe to - * clean it here. We donate it our last reference. - */ - lru_requeue(vp, &lru_vrele_list); - mutex_exit(vp->v_interlock); - return; - } + /* + * If the node got another reference while we + * released the interlock, don't try to inactivate it yet. + */ + if (__predict_false(vtryrele(vp))) { + VOP_UNLOCK(vp); + mutex_exit(vp->v_interlock); + return; + } - /* - * If the node got another reference while we - * released the interlock, don't try to inactivate it yet. - */ - if (__predict_false(vtryrele(vp))) { - VOP_UNLOCK(vp); - mutex_exit(vp->v_interlock); - return; - } + /* + * If not clean, deactivate the vnode, but preserve + * our reference across the call to VOP_INACTIVE(). + */ + if (VSTATE_GET(vp) == VS_RECLAIMED) { + VOP_UNLOCK(vp); + } else { VSTATE_CHANGE(vp, VS_ACTIVE, VS_BLOCKED); mutex_exit(vp->v_interlock); @@ -759,6 +748,7 @@ vrelel(vnode_t *vp, int flags) * * Note that VOP_INACTIVE() will drop the vnode lock. */ + recycle = false; VOP_INACTIVE(vp, &recycle); if (recycle) { /* vcache_reclaim() below will drop the lock. */ @@ -1097,6 +1087,26 @@ vcache_alloc(void) } /* + * Deallocate a vcache node in state VS_LOADING. + * + * vcache_lock held on entry and released on return. + */ +static void +vcache_dealloc(vnode_impl_t *vip) +{ + vnode_t *vp; + + KASSERT(mutex_owned(&vcache_lock)); + + vp = VIMPL_TO_VNODE(vip); + mutex_enter(vp->v_interlock); + vp->v_op = dead_vnodeop_p; + VSTATE_CHANGE(vp, VS_LOADING, VS_RECLAIMED); + mutex_exit(&vcache_lock); + vrelel(vp, 0); +} + +/* * Free an unused, unreferenced vcache node. * v_interlock locked on entry. */ @@ -1260,10 +1270,7 @@ again: /* If another thread beat us inserting this node, retry. */ if (vip != new_vip) { - mutex_enter(vp->v_interlock); - VSTATE_CHANGE(vp, VS_LOADING, VS_RECLAIMED); - mutex_exit(&vcache_lock); - vrelel(vp, 0); + vcache_dealloc(new_vip); vfs_unbusy(mp, false, NULL); goto again; } @@ -1275,10 +1282,7 @@ again: mutex_enter(&vcache_lock); SLIST_REMOVE(&vcache_hashtab[hash & vcache_hashmask], new_vip, vnode_impl, vi_hash); - mutex_enter(vp->v_interlock); - VSTATE_CHANGE(vp, VS_LOADING, VS_RECLAIMED); - mutex_exit(&vcache_lock); - vrelel(vp, 0); + vcache_dealloc(new_vip); vfs_unbusy(mp, false, NULL); KASSERT(*vpp == NULL); return error; @@ -1329,10 +1333,7 @@ vcache_new(struct mount *mp, struct vnod &vip->vi_key.vk_key_len, &vip->vi_key.vk_key); if (error) { mutex_enter(&vcache_lock); - mutex_enter(vp->v_interlock); - VSTATE_CHANGE(vp, VS_LOADING, VS_RECLAIMED); - mutex_exit(&vcache_lock); - vrelel(vp, 0); + vcache_dealloc(vip); vfs_unbusy(mp, false, NULL); KASSERT(*vpp == NULL); return error; @@ -1381,7 +1382,6 @@ vcache_rekey_enter(struct mount *mp, str uint32_t old_hash, new_hash; struct vcache_key old_vcache_key, new_vcache_key; vnode_impl_t *vip, *new_vip; - struct vnode *new_vp; old_vcache_key.vk_mount = mp; old_vcache_key.vk_key = old_key; @@ -1395,16 +1395,12 @@ vcache_rekey_enter(struct mount *mp, str new_vip = vcache_alloc(); new_vip->vi_key = new_vcache_key; - new_vp = VIMPL_TO_VNODE(new_vip); /* Insert locked new node used as placeholder. */ mutex_enter(&vcache_lock); vip = vcache_hash_lookup(&new_vcache_key, new_hash); if (vip != NULL) { - mutex_enter(new_vp->v_interlock); - VSTATE_CHANGE(new_vp, VS_LOADING, VS_RECLAIMED); - mutex_exit(&vcache_lock); - vrelel(new_vp, 0); + vcache_dealloc(new_vip); return EEXIST; } SLIST_INSERT_HEAD(&vcache_hashtab[new_hash & vcache_hashmask], @@ -1456,6 +1452,7 @@ vcache_rekey_exit(struct mount *mp, stru new_vp = VIMPL_TO_VNODE(new_vip); mutex_enter(new_vp->v_interlock); VSTATE_ASSERT(VIMPL_TO_VNODE(new_vip), VS_LOADING); + mutex_exit(new_vp->v_interlock); /* Rekey old node and put it onto its new hashlist. */ vip->vi_key = new_vcache_key; @@ -1469,9 +1466,7 @@ vcache_rekey_exit(struct mount *mp, stru /* Remove new node used as placeholder. */ SLIST_REMOVE(&vcache_hashtab[new_hash & vcache_hashmask], new_vip, vnode_impl, vi_hash); - VSTATE_CHANGE(new_vp, VS_LOADING, VS_RECLAIMED); - mutex_exit(&vcache_lock); - vrelel(new_vp, 0); + vcache_dealloc(new_vip); } /*