Module Name: src
Committed By: hannken
Date: Thu May 26 11:09:55 UTC 2016
Modified Files:
src/sys/kern: vfs_vnode.c
src/sys/sys: vnode.h
Log Message:
Use vnode state to replace VI_MARKER, VI_CHANGING, VI_XLOCK and VI_CLEAN.
Presented on tech-kern@
To generate a diff of this commit:
cvs rdiff -u -r1.51 -r1.52 src/sys/kern/vfs_vnode.c
cvs rdiff -u -r1.261 -r1.262 src/sys/sys/vnode.h
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.51 src/sys/kern/vfs_vnode.c:1.52
--- src/sys/kern/vfs_vnode.c:1.51 Thu May 26 11:08:44 2016
+++ src/sys/kern/vfs_vnode.c Thu May 26 11:09:55 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: vfs_vnode.c,v 1.51 2016/05/26 11:08:44 hannken Exp $ */
+/* $NetBSD: vfs_vnode.c,v 1.52 2016/05/26 11:09:55 hannken Exp $ */
/*-
* Copyright (c) 1997-2011 The NetBSD Foundation, Inc.
@@ -92,6 +92,49 @@
* or cleaned via vclean(9), which calls VOP_RECLAIM(9) to disassociate
* underlying file system from the vnode, and finally destroyed.
*
+ * Vnode state
+ *
+ * Vnode is always in one of six states:
+ * - MARKER This is a marker vnode to help list traversal. It
+ * will never change its state.
+ * - LOADING Vnode is associating underlying file system and not
+ * yet ready to use.
+ * - ACTIVE Vnode has associated underlying file system and is
+ * ready to use.
+ * - BLOCKED Vnode is active but cannot get new references.
+ * - RECLAIMING Vnode is disassociating from the underlying file
+ * system.
+ * - RECLAIMED Vnode has disassociated from underlying file system
+ * and is dead.
+ *
+ * Valid state changes are:
+ * LOADING -> ACTIVE
+ * Vnode has been initialised in vcache_get() or
+ * vcache_new() and is ready to use.
+ * ACTIVE -> RECLAIMING
+ * Vnode starts disassociation from underlying file
+ * system in vclean().
+ * RECLAIMING -> RECLAIMED
+ * Vnode finished disassociation from underlying file
+ * system in vclean().
+ * ACTIVE -> BLOCKED
+ * Either vcache_rekey*() is changing the vnode key or
+ * vrelel() is about to call VOP_INACTIVE().
+ * BLOCKED -> ACTIVE
+ * The block condition is over.
+ * LOADING -> RECLAIMED
+ * Either vcache_get() or vcache_new() failed to
+ * associate the underlying file system or vcache_rekey*()
+ * drops a vnode used as placeholder.
+ *
+ * Of these states LOADING, BLOCKED and RECLAIMING are intermediate
+ * and it is possible to wait for state change.
+ *
+ * State is protected with v_interlock with one exception:
+ * to change from LOADING both v_interlock and vcache.lock must be held
+ * so it is possible to check "state == LOADING" without holding
+ * v_interlock. See vcache_get() for details.
+ *
* Reference counting
*
* Vnode is considered active, if reference count (vnode_t::v_usecount)
@@ -109,14 +152,10 @@
* Changing the usecount from a non-zero value to a non-zero value can
* safely be done using atomic operations, without the interlock held.
*
- * Note: if VI_CLEAN is set, vnode_t::v_interlock will be released while
- * mntvnode_lock is still held.
- *
- * See PR 41374.
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,v 1.51 2016/05/26 11:08:44 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,v 1.52 2016/05/26 11:09:55 hannken Exp $");
#define _VFS_VNODE_PRIVATE
@@ -146,7 +185,6 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,
/* Flags to vrelel. */
#define VRELEL_ASYNC_RELE 0x0001 /* Always defer to vrele thread. */
-#define VRELEL_CHANGING_SET 0x0002 /* VI_CHANGING set by caller. */
enum vcache_state {
VN_MARKER, /* Stable, used as marker. Will not change. */
@@ -162,10 +200,9 @@ struct vcache_key {
size_t vk_key_len;
};
struct vcache_node {
- struct vnode vn_data;
+ struct vnode vn_vnode;
enum vcache_state vn_state;
SLIST_ENTRY(vcache_node) vn_hash;
- struct vnode *vn_vnode;
struct vcache_key vn_key;
};
@@ -211,7 +248,6 @@ static void vdrain_thread(void *);
static void vrele_thread(void *);
static void vnpanic(vnode_t *, const char *, ...)
__printflike(2, 3);
-static void vwait(vnode_t *, int);
/* Routines having to do with the management of the vnode table. */
extern struct mount *dead_rootmount;
@@ -253,7 +289,7 @@ vstate_name(enum vcache_state state)
#define VSTATE_ASSERT(vp, state) \
vstate_assert((vp), (state), __func__, __LINE__)
-static void __unused
+static void
vstate_assert(vnode_t *vp, enum vcache_state state, const char *func, int line)
{
struct vcache_node *node = VP_TO_VN(vp);
@@ -266,7 +302,7 @@ vstate_assert(vnode_t *vp, enum vcache_s
vstate_name(node->vn_state), vstate_name(state), func, line);
}
-static enum vcache_state __unused
+static enum vcache_state
vstate_assert_get(vnode_t *vp, const char *func, int line)
{
struct vcache_node *node = VP_TO_VN(vp);
@@ -279,7 +315,7 @@ vstate_assert_get(vnode_t *vp, const cha
return node->vn_state;
}
-static void __unused
+static void
vstate_assert_wait_stable(vnode_t *vp, const char *func, int line)
{
struct vcache_node *node = VP_TO_VN(vp);
@@ -297,7 +333,7 @@ vstate_assert_wait_stable(vnode_t *vp, c
vstate_name(node->vn_state), func, line);
}
-static void __unused
+static void
vstate_assert_change(vnode_t *vp, enum vcache_state from, enum vcache_state to,
const char *func, int line)
{
@@ -334,7 +370,7 @@ vstate_assert_change(vnode_t *vp, enum v
vstate_wait_stable((vp))
#define VSTATE_ASSERT(vp, state)
-static void __unused
+static void
vstate_wait_stable(vnode_t *vp)
{
struct vcache_node *node = VP_TO_VN(vp);
@@ -343,7 +379,7 @@ vstate_wait_stable(vnode_t *vp)
cv_wait(&vp->v_cv, vp->v_interlock);
}
-static void __unused
+static void
vstate_change(vnode_t *vp, enum vcache_state from, enum vcache_state to)
{
struct vcache_node *node = VP_TO_VN(vp);
@@ -399,7 +435,7 @@ vnalloc_marker(struct mount *mp)
uvm_obj_init(&vp->v_uobj, &uvm_vnodeops, true, 0);
vp->v_mount = mp;
vp->v_type = VBAD;
- vp->v_iflag = VI_MARKER;
+ node->vn_state = VN_MARKER;
return vp;
}
@@ -413,7 +449,7 @@ vnfree_marker(vnode_t *vp)
struct vcache_node *node;
node = VP_TO_VN(vp);
- KASSERT(ISSET(vp->v_iflag, VI_MARKER));
+ KASSERT(node->vn_state == VN_MARKER);
uvm_obj_destroy(&vp->v_uobj, true);
pool_cache_put(vcache.pool, node);
}
@@ -425,7 +461,7 @@ bool
vnis_marker(vnode_t *vp)
{
- return (ISSET(vp->v_iflag, VI_MARKER));
+ return (VP_TO_VN(vp)->vn_state == VN_MARKER);
}
/*
@@ -452,7 +488,6 @@ try_nextlist:
* lists.
*/
KASSERT(vp->v_usecount == 0);
- KASSERT((vp->v_iflag & VI_CLEAN) == 0);
KASSERT(vp->v_freelisthd == listhd);
if (vn_lock(vp, LK_EXCLUSIVE | LK_NOWAIT) != 0)
@@ -461,7 +496,6 @@ try_nextlist:
VOP_UNLOCK(vp);
continue;
}
- KASSERT((vp->v_iflag & VI_XLOCK) == 0);
mp = vp->v_mount;
if (fstrans_start_nowait(mp, FSTRANS_SHARED) != 0) {
mutex_exit(vp->v_interlock);
@@ -493,10 +527,8 @@ try_nextlist:
* before doing this.
*/
vp->v_usecount = 1;
- KASSERT((vp->v_iflag & VI_CHANGING) == 0);
- vp->v_iflag |= VI_CHANGING;
vclean(vp);
- vrelel(vp, VRELEL_CHANGING_SET);
+ vrelel(vp, 0);
fstrans_done(mp);
return 0;
@@ -552,21 +584,22 @@ vremfree(vnode_t *vp)
/*
* vget: get a particular vnode from the free list, increment its reference
- * count and lock it.
+ * count and return it.
*
- * => Should be called with v_interlock held.
+ * => Must be called with v_interlock held.
*
- * If VI_CHANGING is set, the vnode may be eliminated in vgone()/vclean().
+ * If state is VN_RECLAIMING, the vnode may be eliminated in vgone()/vclean().
* In that case, we cannot grab the vnode, so the process is awakened when
* the transition is completed, and an error returned to indicate that the
* vnode is no longer usable.
+ *
+ * If state is VN_LOADING or VN_BLOCKED, wait until the vnode enters a
+ * stable state (VN_ACTIVE or VN_RECLAIMED).
*/
int
vget(vnode_t *vp, int flags, bool waitok)
{
- int error = 0;
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
KASSERT(mutex_owned(vp->v_interlock));
KASSERT((flags & ~LK_NOWAIT) == 0);
KASSERT(waitok == ((flags & LK_NOWAIT) == 0));
@@ -587,24 +620,24 @@ vget(vnode_t *vp, int flags, bool waitok
* for the change to complete and take care not to return
* a clean vnode.
*/
- if ((vp->v_iflag & VI_CHANGING) != 0) {
- if ((flags & LK_NOWAIT) != 0) {
- vrelel(vp, 0);
- return EBUSY;
- }
- vwait(vp, VI_CHANGING);
- if ((vp->v_iflag & VI_CLEAN) != 0) {
- vrelel(vp, 0);
- return ENOENT;
- }
+ if (! ISSET(flags, LK_NOWAIT))
+ VSTATE_WAIT_STABLE(vp);
+ if (VSTATE_GET(vp) == VN_RECLAIMED) {
+ vrelel(vp, 0);
+ return ENOENT;
+ } else if (VSTATE_GET(vp) != VN_ACTIVE) {
+ KASSERT(ISSET(flags, LK_NOWAIT));
+ vrelel(vp, 0);
+ return EBUSY;
}
/*
* Ok, we got it in good shape.
*/
- KASSERT((vp->v_iflag & VI_CLEAN) == 0);
+ VSTATE_ASSERT(vp, VN_ACTIVE);
mutex_exit(vp->v_interlock);
- return error;
+
+ return 0;
}
/*
@@ -614,8 +647,6 @@ void
vput(vnode_t *vp)
{
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
-
VOP_UNLOCK(vp);
vrele(vp);
}
@@ -652,11 +683,10 @@ vrelel(vnode_t *vp, int flags)
int error;
KASSERT(mutex_owned(vp->v_interlock));
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
KASSERT(vp->v_freelisthd == NULL);
if (__predict_false(vp->v_op == dead_vnodeop_p &&
- (vp->v_iflag & (VI_CLEAN|VI_XLOCK)) == 0)) {
+ VSTATE_GET(vp) != VN_RECLAIMED)) {
vnpanic(vp, "dead but not clean");
}
@@ -665,11 +695,6 @@ vrelel(vnode_t *vp, int flags)
* and unlock.
*/
if (vtryrele(vp)) {
- if ((flags & VRELEL_CHANGING_SET) != 0) {
- KASSERT((vp->v_iflag & VI_CHANGING) != 0);
- vp->v_iflag &= ~VI_CHANGING;
- cv_broadcast(&vp->v_cv);
- }
mutex_exit(vp->v_interlock);
return;
}
@@ -677,8 +702,6 @@ vrelel(vnode_t *vp, int flags)
vnpanic(vp, "%s: bad ref count", __func__);
}
- KASSERT((vp->v_iflag & VI_XLOCK) == 0);
-
#ifdef DIAGNOSTIC
if ((vp->v_type == VBLK || vp->v_type == VCHR) &&
vp->v_specnode != NULL && vp->v_specnode->sn_opencnt != 0) {
@@ -690,7 +713,7 @@ vrelel(vnode_t *vp, int flags)
* If not clean, deactivate the vnode, but preserve
* our reference across the call to VOP_INACTIVE().
*/
- if ((vp->v_iflag & VI_CLEAN) == 0) {
+ if (VSTATE_GET(vp) != VN_RECLAIMED) {
recycle = false;
/*
@@ -729,11 +752,6 @@ vrelel(vnode_t *vp, int flags)
* Defer reclaim to the kthread; it's not safe to
* clean it here. We donate it our last reference.
*/
- if ((flags & VRELEL_CHANGING_SET) != 0) {
- KASSERT((vp->v_iflag & VI_CHANGING) != 0);
- vp->v_iflag &= ~VI_CHANGING;
- cv_broadcast(&vp->v_cv);
- }
mutex_enter(&vrele_lock);
TAILQ_INSERT_TAIL(&vrele_list, vp, v_freelist);
if (++vrele_pending > (desiredvnodes >> 8))
@@ -749,28 +767,17 @@ vrelel(vnode_t *vp, int flags)
*/
if (__predict_false(vtryrele(vp))) {
VOP_UNLOCK(vp);
- if ((flags & VRELEL_CHANGING_SET) != 0) {
- KASSERT((vp->v_iflag & VI_CHANGING) != 0);
- vp->v_iflag &= ~VI_CHANGING;
- cv_broadcast(&vp->v_cv);
- }
mutex_exit(vp->v_interlock);
return;
}
-
- if ((flags & VRELEL_CHANGING_SET) == 0) {
- KASSERT((vp->v_iflag & VI_CHANGING) == 0);
- vp->v_iflag |= VI_CHANGING;
- }
+ VSTATE_CHANGE(vp, VN_ACTIVE, VN_BLOCKED);
mutex_exit(vp->v_interlock);
/*
- * The vnode can gain another reference while being
+ * The vnode must not gain another reference while being
* deactivated. If VOP_INACTIVE() indicates that
* the described file has been deleted, then recycle
- * the vnode irrespective of additional references.
- * Another thread may be waiting to re-use the on-disk
- * inode.
+ * the vnode.
*
* Note that VOP_INACTIVE() will drop the vnode lock.
*/
@@ -781,11 +788,9 @@ vrelel(vnode_t *vp, int flags)
recycle = false;
}
mutex_enter(vp->v_interlock);
+ VSTATE_CHANGE(vp, VN_BLOCKED, VN_ACTIVE);
if (!recycle) {
if (vtryrele(vp)) {
- KASSERT((vp->v_iflag & VI_CHANGING) != 0);
- vp->v_iflag &= ~VI_CHANGING;
- cv_broadcast(&vp->v_cv);
mutex_exit(vp->v_interlock);
return;
}
@@ -806,26 +811,19 @@ vrelel(vnode_t *vp, int flags)
* otherwise just free it.
*/
if (recycle) {
+ VSTATE_ASSERT(vp, VN_ACTIVE);
vclean(vp);
}
KASSERT(vp->v_usecount > 0);
- } else { /* vnode was already clean */
- if ((flags & VRELEL_CHANGING_SET) == 0) {
- KASSERT((vp->v_iflag & VI_CHANGING) == 0);
- vp->v_iflag |= VI_CHANGING;
- }
}
if (atomic_dec_uint_nv(&vp->v_usecount) != 0) {
/* Gained another reference while being reclaimed. */
- KASSERT((vp->v_iflag & VI_CHANGING) != 0);
- vp->v_iflag &= ~VI_CHANGING;
- cv_broadcast(&vp->v_cv);
mutex_exit(vp->v_interlock);
return;
}
- if ((vp->v_iflag & VI_CLEAN) != 0) {
+ if (VSTATE_GET(vp) == VN_RECLAIMED) {
/*
* It's clean so destroy it. It isn't referenced
* anywhere since it has been reclaimed.
@@ -852,9 +850,6 @@ vrelel(vnode_t *vp, int flags)
}
TAILQ_INSERT_TAIL(vp->v_freelisthd, vp, v_freelist);
mutex_exit(&vnode_free_list_lock);
- KASSERT((vp->v_iflag & VI_CHANGING) != 0);
- vp->v_iflag &= ~VI_CHANGING;
- cv_broadcast(&vp->v_cv);
mutex_exit(vp->v_interlock);
}
}
@@ -863,8 +858,6 @@ void
vrele(vnode_t *vp)
{
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
-
if (vtryrele(vp)) {
return;
}
@@ -879,8 +872,6 @@ void
vrele_async(vnode_t *vp)
{
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
-
if (vtryrele(vp)) {
return;
}
@@ -948,7 +939,6 @@ void
vref(vnode_t *vp)
{
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
KASSERT(vp->v_usecount != 0);
atomic_inc_uint(&vp->v_usecount);
@@ -963,7 +953,6 @@ vholdl(vnode_t *vp)
{
KASSERT(mutex_owned(vp->v_interlock));
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
if (vp->v_holdcnt++ == 0 && vp->v_usecount == 0) {
mutex_enter(&vnode_free_list_lock);
@@ -984,7 +973,6 @@ holdrelel(vnode_t *vp)
{
KASSERT(mutex_owned(vp->v_interlock));
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
if (vp->v_holdcnt <= 0) {
vnpanic(vp, "%s: holdcnt vp %p", __func__, vp);
@@ -1017,8 +1005,6 @@ vclean(vnode_t *vp)
KASSERT((vp->v_vflag & VV_LOCKSWORK) == 0 ||
VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
KASSERT(mutex_owned(vp->v_interlock));
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
- KASSERT((vp->v_iflag & (VI_XLOCK | VI_CLEAN)) == 0);
KASSERT(vp->v_usecount != 0);
active = (vp->v_usecount > 1);
@@ -1026,7 +1012,7 @@ vclean(vnode_t *vp)
* Prevent the vnode from being recycled or brought into use
* while we clean it out.
*/
- vp->v_iflag |= VI_XLOCK;
+ VSTATE_CHANGE(vp, VN_ACTIVE, VN_RECLAIMING);
if (vp->v_iflag & VI_EXECMAP) {
atomic_add_int(&uvmexp.execpages, -vp->v_uobj.uo_npages);
atomic_add_int(&uvmexp.filepages, vp->v_uobj.uo_npages);
@@ -1056,7 +1042,7 @@ vclean(vnode_t *vp)
} else {
/*
* Any other processes trying to obtain this lock must first
- * wait for VI_XLOCK to clear, then call the new lock operation.
+ * wait for VN_RECLAIMED, then call the new lock operation.
*/
VOP_UNLOCK(vp);
}
@@ -1086,11 +1072,9 @@ vclean(vnode_t *vp)
mutex_enter(vp->v_interlock);
vp->v_op = dead_vnodeop_p;
vp->v_vflag |= VV_LOCKSWORK;
- vp->v_iflag |= VI_CLEAN;
+ VSTATE_CHANGE(vp, VN_RECLAIMING, VN_RECLAIMED);
vp->v_tag = VT_NON;
KNOTE(&vp->v_klist, NOTE_REVOKE);
- vp->v_iflag &= ~VI_XLOCK;
- cv_broadcast(&vp->v_cv);
KASSERT((vp->v_iflag & VI_ONWORKLST) == 0);
}
@@ -1107,24 +1091,13 @@ vrecycle(vnode_t *vp)
mutex_enter(vp->v_interlock);
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
-
- if (vp->v_usecount != 1) {
- mutex_exit(vp->v_interlock);
- VOP_UNLOCK(vp);
- return false;
- }
- if ((vp->v_iflag & VI_CHANGING) != 0)
- vwait(vp, VI_CHANGING);
if (vp->v_usecount != 1) {
mutex_exit(vp->v_interlock);
VOP_UNLOCK(vp);
return false;
}
- KASSERT((vp->v_iflag & VI_CLEAN) == 0);
- vp->v_iflag |= VI_CHANGING;
vclean(vp);
- vrelel(vp, VRELEL_CHANGING_SET);
+ vrelel(vp, 0);
return true;
}
@@ -1142,7 +1115,8 @@ vrevoke(vnode_t *vp)
KASSERT(vp->v_usecount > 0);
mutex_enter(vp->v_interlock);
- if ((vp->v_iflag & VI_CLEAN) != 0) {
+ VSTATE_WAIT_STABLE(vp);
+ if (VSTATE_GET(vp) == VN_RECLAIMED) {
mutex_exit(vp->v_interlock);
return;
} else if (vp->v_type != VBLK && vp->v_type != VCHR) {
@@ -1170,16 +1144,13 @@ vgone(vnode_t *vp)
{
if (vn_lock(vp, LK_EXCLUSIVE) != 0) {
- KASSERT((vp->v_iflag & VI_CLEAN) != 0);
+ VSTATE_ASSERT(vp, VN_RECLAIMED);
vrele(vp);
}
mutex_enter(vp->v_interlock);
- if ((vp->v_iflag & VI_CHANGING) != 0)
- vwait(vp, VI_CHANGING);
- vp->v_iflag |= VI_CHANGING;
vclean(vp);
- vrelel(vp, VRELEL_CHANGING_SET);
+ vrelel(vp, 0);
}
static inline uint32_t
@@ -1300,7 +1271,6 @@ vcache_free(struct vcache_node *node)
vp = VN_TO_VP(node);
KASSERT(vp->v_usecount == 0);
- KASSERT((vp->v_iflag & VI_MARKER) == 0);
rw_destroy(&vp->v_lock);
mutex_enter(&vnode_free_list_lock);
@@ -1339,8 +1309,21 @@ again:
node = vcache_hash_lookup(&vcache_key, hash);
/* If found, take a reference or retry. */
- if (__predict_true(node != NULL && node->vn_vnode != NULL)) {
- vp = node->vn_vnode;
+ if (__predict_true(node != NULL)) {
+ /*
+ * If the vnode is loading we cannot take the v_interlock
+ * here as it might change during load (see uvm_obj_setlock()).
+ * As changing state from VN_LOADING requires both vcache.lock
+ * and v_interlock it is safe to test with vcache.lock held.
+ *
+ * Wait for vnodes changing state from VN_LOADING and retry.
+ */
+ if (__predict_false(node->vn_state == VN_LOADING)) {
+ cv_wait(&vcache.cv, &vcache.lock);
+ mutex_exit(&vcache.lock);
+ goto again;
+ }
+ vp = VN_TO_VP(node);
mutex_enter(vp->v_interlock);
mutex_exit(&vcache.lock);
error = vget(vp, 0, true /* wait */);
@@ -1351,14 +1334,6 @@ again:
KASSERT((error != 0) == (*vpp == NULL));
return error;
}
-
- /* If another thread loads this node, wait and retry. */
- if (node != NULL) {
- KASSERT(node->vn_vnode == NULL);
- mutex_exit(&vcache.lock);
- kpause("vcache", false, mstohz(20), NULL);
- goto again;
- }
mutex_exit(&vcache.lock);
/* Allocate and initialize a new vcache / vnode pair. */
@@ -1375,28 +1350,28 @@ again:
new_node, vn_hash);
node = new_node;
}
- mutex_exit(&vcache.lock);
/* If another thread beat us inserting this node, retry. */
if (node != new_node) {
- KASSERT(vp->v_usecount == 1);
- vp->v_usecount = 0;
- vcache_free(new_node);
+ mutex_enter(vp->v_interlock);
+ VSTATE_CHANGE(vp, VN_LOADING, VN_RECLAIMED);
+ mutex_exit(&vcache.lock);
+ vrelel(vp, 0);
vfs_unbusy(mp, false, NULL);
goto again;
}
+ mutex_exit(&vcache.lock);
- /* Load the fs node. Exclusive as new_node->vn_vnode is NULL. */
- vp->v_iflag |= VI_CHANGING;
+ /* Load the fs node. Exclusive as new_node is VN_LOADING. */
error = VFS_LOADVNODE(mp, vp, key, key_len, &new_key);
if (error) {
mutex_enter(&vcache.lock);
SLIST_REMOVE(&vcache.hashtab[hash & vcache.hashmask],
new_node, vcache_node, vn_hash);
+ mutex_enter(vp->v_interlock);
+ VSTATE_CHANGE(vp, VN_LOADING, VN_RECLAIMED);
mutex_exit(&vcache.lock);
- KASSERT(vp->v_usecount == 1);
- vp->v_usecount = 0;
- vcache_free(new_node);
+ vrelel(vp, 0);
vfs_unbusy(mp, false, NULL);
KASSERT(*vpp == NULL);
return error;
@@ -1412,12 +1387,10 @@ again:
/* Finished loading, finalize node. */
mutex_enter(&vcache.lock);
new_node->vn_key.vk_key = new_key;
- new_node->vn_vnode = vp;
- mutex_exit(&vcache.lock);
mutex_enter(vp->v_interlock);
- vp->v_iflag &= ~VI_CHANGING;
- cv_broadcast(&vp->v_cv);
+ VSTATE_CHANGE(vp, VN_LOADING, VN_ACTIVE);
mutex_exit(vp->v_interlock);
+ mutex_exit(&vcache.lock);
*vpp = vp;
return 0;
}
@@ -1431,7 +1404,7 @@ vcache_new(struct mount *mp, struct vnod
{
int error;
uint32_t hash;
- struct vnode *vp;
+ struct vnode *ovp, *vp;
struct vcache_node *new_node;
struct vcache_node *old_node __diagused;
@@ -1446,13 +1419,14 @@ vcache_new(struct mount *mp, struct vnod
vp = VN_TO_VP(new_node);
/* Create and load the fs node. */
- vp->v_iflag |= VI_CHANGING;
error = VFS_NEWVNODE(mp, dvp, vp, vap, cred,
&new_node->vn_key.vk_key_len, &new_node->vn_key.vk_key);
if (error) {
- KASSERT(vp->v_usecount == 1);
- vp->v_usecount = 0;
- vcache_free(VP_TO_VN(vp));
+ mutex_enter(&vcache.lock);
+ mutex_enter(vp->v_interlock);
+ VSTATE_CHANGE(vp, VN_LOADING, VN_RECLAIMED);
+ mutex_exit(&vcache.lock);
+ vrelel(vp, 0);
vfs_unbusy(mp, false, NULL);
KASSERT(*vpp == NULL);
return error;
@@ -1464,16 +1438,11 @@ vcache_new(struct mount *mp, struct vnod
/* Wait for previous instance to be reclaimed, then insert new node. */
mutex_enter(&vcache.lock);
while ((old_node = vcache_hash_lookup(&new_node->vn_key, hash))) {
-#ifdef DIAGNOSTIC
- if (old_node->vn_vnode != NULL)
- mutex_enter(old_node->vn_vnode->v_interlock);
- KASSERT(old_node->vn_vnode == NULL ||
- (old_node->vn_vnode->v_iflag & (VI_XLOCK | VI_CLEAN)) != 0);
- if (old_node->vn_vnode != NULL)
- mutex_exit(old_node->vn_vnode->v_interlock);
-#endif
+ ovp = VN_TO_VP(old_node);
+ mutex_enter(ovp->v_interlock);
mutex_exit(&vcache.lock);
- kpause("vcache", false, mstohz(20), NULL);
+ error = vget(ovp, 0, true /* wait */);
+ KASSERT(error == ENOENT);
mutex_enter(&vcache.lock);
}
SLIST_INSERT_HEAD(&vcache.hashtab[hash & vcache.hashmask],
@@ -1486,11 +1455,9 @@ vcache_new(struct mount *mp, struct vnod
/* Finished loading, finalize node. */
mutex_enter(&vcache.lock);
- new_node->vn_vnode = vp;
- mutex_exit(&vcache.lock);
mutex_enter(vp->v_interlock);
- vp->v_iflag &= ~VI_CHANGING;
- cv_broadcast(&vp->v_cv);
+ VSTATE_CHANGE(vp, VN_LOADING, VN_ACTIVE);
+ mutex_exit(&vcache.lock);
mutex_exit(vp->v_interlock);
*vpp = vp;
return 0;
@@ -1508,6 +1475,7 @@ vcache_rekey_enter(struct mount *mp, str
uint32_t old_hash, new_hash;
struct vcache_key old_vcache_key, new_vcache_key;
struct vcache_node *node, *new_node;
+ struct vnode *tvp;
old_vcache_key.vk_mount = mp;
old_vcache_key.vk_key = old_key;
@@ -1521,16 +1489,16 @@ vcache_rekey_enter(struct mount *mp, str
new_node = vcache_alloc();
new_node->vn_key = new_vcache_key;
-
- mutex_enter(&vcache.lock);
+ tvp = VN_TO_VP(new_node);
/* Insert locked new node used as placeholder. */
+ mutex_enter(&vcache.lock);
node = vcache_hash_lookup(&new_vcache_key, new_hash);
if (node != NULL) {
+ mutex_enter(tvp->v_interlock);
+ VSTATE_CHANGE(tvp, VN_LOADING, VN_RECLAIMED);
mutex_exit(&vcache.lock);
- KASSERT(VN_TO_VP(new_node)->v_usecount == 1);
- VN_TO_VP(new_node)->v_usecount = 0;
- vcache_free(new_node);
+ vrelel(tvp, 0);
return EEXIST;
}
SLIST_INSERT_HEAD(&vcache.hashtab[new_hash & vcache.hashmask],
@@ -1539,9 +1507,11 @@ vcache_rekey_enter(struct mount *mp, str
/* Lock old node. */
node = vcache_hash_lookup(&old_vcache_key, old_hash);
KASSERT(node != NULL);
- KASSERT(node->vn_vnode == vp);
- node->vn_vnode = NULL;
+ KASSERT(VN_TO_VP(node) == vp);
+ mutex_enter(vp->v_interlock);
+ VSTATE_CHANGE(vp, VN_ACTIVE, VN_BLOCKED);
node->vn_key = old_vcache_key;
+ mutex_exit(vp->v_interlock);
mutex_exit(&vcache.lock);
return 0;
}
@@ -1557,6 +1527,7 @@ vcache_rekey_exit(struct mount *mp, stru
uint32_t old_hash, new_hash;
struct vcache_key old_vcache_key, new_vcache_key;
struct vcache_node *old_node, *new_node;
+ struct vnode *tvp;
old_vcache_key.vk_mount = mp;
old_vcache_key.vk_key = old_key;
@@ -1573,13 +1544,18 @@ vcache_rekey_exit(struct mount *mp, stru
/* Lookup old and new node. */
old_node = vcache_hash_lookup(&old_vcache_key, old_hash);
KASSERT(old_node != NULL);
- KASSERT(old_node->vn_vnode == NULL);
+ KASSERT(VN_TO_VP(old_node) == vp);
+ mutex_enter(vp->v_interlock);
+ VSTATE_ASSERT(vp, VN_BLOCKED);
+
new_node = vcache_hash_lookup(&new_vcache_key, new_hash);
- KASSERT(new_node != NULL && new_node->vn_vnode == NULL);
+ KASSERT(new_node != NULL);
KASSERT(new_node->vn_key.vk_key_len == new_key_len);
+ tvp = VN_TO_VP(new_node);
+ mutex_enter(tvp->v_interlock);
+ VSTATE_ASSERT(VN_TO_VP(new_node), VN_LOADING);
/* Rekey old node and put it onto its new hashlist. */
- old_node->vn_vnode = vp;
old_node->vn_key = new_vcache_key;
if (old_hash != new_hash) {
SLIST_REMOVE(&vcache.hashtab[old_hash & vcache.hashmask],
@@ -1587,14 +1563,15 @@ vcache_rekey_exit(struct mount *mp, stru
SLIST_INSERT_HEAD(&vcache.hashtab[new_hash & vcache.hashmask],
old_node, vn_hash);
}
+ VSTATE_CHANGE(vp, VN_BLOCKED, VN_ACTIVE);
+ mutex_exit(vp->v_interlock);
/* Remove new node used as placeholder. */
SLIST_REMOVE(&vcache.hashtab[new_hash & vcache.hashmask],
new_node, vcache_node, vn_hash);
+ VSTATE_CHANGE(tvp, VN_LOADING, VN_RECLAIMED);
mutex_exit(&vcache.lock);
- KASSERT(VN_TO_VP(new_node)->v_usecount == 1);
- VN_TO_VP(new_node)->v_usecount = 0;
- vcache_free(new_node);
+ vrelel(tvp, 0);
}
/*
@@ -1675,30 +1652,18 @@ vdead_check(struct vnode *vp, int flags)
{
KASSERT(mutex_owned(vp->v_interlock));
- if (ISSET(vp->v_iflag, VI_XLOCK)) {
- if (ISSET(flags, VDEAD_NOWAIT))
- return EBUSY;
- vwait(vp, VI_XLOCK);
- KASSERT(ISSET(vp->v_iflag, VI_CLEAN));
- }
- if (ISSET(vp->v_iflag, VI_CLEAN))
- return ENOENT;
- return 0;
-}
-/*
- * Wait for a vnode (typically with VI_XLOCK set) to be cleaned or
- * recycled.
- */
-static void
-vwait(vnode_t *vp, int flags)
-{
+ if (! ISSET(flags, VDEAD_NOWAIT))
+ VSTATE_WAIT_STABLE(vp);
- KASSERT(mutex_owned(vp->v_interlock));
- KASSERT(vp->v_usecount != 0);
+ if (VSTATE_GET(vp) == VN_RECLAIMING) {
+ KASSERT(ISSET(flags, VDEAD_NOWAIT));
+ return EBUSY;
+ } else if (VSTATE_GET(vp) == VN_RECLAIMED) {
+ return ENOENT;
+ }
- while ((vp->v_iflag & flags) != 0)
- cv_wait(&vp->v_cv, vp->v_interlock);
+ return 0;
}
int
Index: src/sys/sys/vnode.h
diff -u src/sys/sys/vnode.h:1.261 src/sys/sys/vnode.h:1.262
--- src/sys/sys/vnode.h:1.261 Thu May 26 11:07:33 2016
+++ src/sys/sys/vnode.h Thu May 26 11:09:55 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: vnode.h,v 1.261 2016/05/26 11:07:33 hannken Exp $ */
+/* $NetBSD: vnode.h,v 1.262 2016/05/26 11:09:55 hannken Exp $ */
/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -204,17 +204,7 @@ typedef struct vnode vnode_t;
#define VI_EXECMAP 0x00000200 /* might have PROT_EXEC mappings */
#define VI_WRMAP 0x00000400 /* might have PROT_WRITE u. mappings */
#define VI_WRMAPDIRTY 0x00000800 /* might have dirty pages */
-#ifdef _VFS_VNODE_PRIVATE
-#define VI_XLOCK 0x00001000 /* vnode is locked to change type */
-#endif /* _VFS_VNODE_PRIVATE */
#define VI_ONWORKLST 0x00004000 /* On syncer work-list */
-#ifdef _VFS_VNODE_PRIVATE
-#define VI_MARKER 0x00008000 /* Dummy marker vnode */
-#endif /* _VFS_VNODE_PRIVATE */
-#ifdef _VFS_VNODE_PRIVATE
-#define VI_CLEAN 0x00080000 /* has been reclaimed */
-#define VI_CHANGING 0x00100000 /* vnode changes state */
-#endif /* _VFS_VNODE_PRIVATE */
/*
* The third set are locked by the underlying file system.
@@ -223,8 +213,7 @@ typedef struct vnode vnode_t;
#define VNODE_FLAGBITS \
"\20\1ROOT\2SYSTEM\3ISTTY\4MAPPED\5MPSAFE\6LOCKSWORK\11TEXT\12EXECMAP" \
- "\13WRMAP\14WRMAPDIRTY\15XLOCK\17ONWORKLST\20MARKER" \
- "\24CLEAN\25CHANGING\31DIROP"
+ "\13WRMAP\14WRMAPDIRTY\17ONWORKLST\31DIROP"
#define VSIZENOTSET ((voff_t)-1)