Module Name:    src
Committed By:   ad
Date:           Fri Jan 24 16:05:23 UTC 2020

Modified Files:
        src/sys/kern [ad-namecache]: vfs_subr.c vfs_vnode.c
        src/sys/miscfs/genfs [ad-namecache]: genfs_vnops.c
        src/sys/sys [ad-namecache]: vnode.h vnode_impl.h

Log Message:
vnodes:

- Have own v_usecount again, don't share the uvm_object's refcount.
- Cluster the members of vnode_t and vnode_impl_t in a cache-concious way.
- Go back to having vi_lock directly in vnode_impl_t.
- Go back to having v_usecount adjusted with atomics.
- Start adjusting v_holdcnt with atomics, too.
- Put all the namecache stuff back into vnode_impl_t.


To generate a diff of this commit:
cvs rdiff -u -r1.478.2.1 -r1.478.2.2 src/sys/kern/vfs_subr.c
cvs rdiff -u -r1.105.2.4 -r1.105.2.5 src/sys/kern/vfs_vnode.c
cvs rdiff -u -r1.200.2.2 -r1.200.2.3 src/sys/miscfs/genfs/genfs_vnops.c
cvs rdiff -u -r1.286 -r1.286.2.1 src/sys/sys/vnode.h
cvs rdiff -u -r1.19.2.4 -r1.19.2.5 src/sys/sys/vnode_impl.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_subr.c
diff -u src/sys/kern/vfs_subr.c:1.478.2.1 src/sys/kern/vfs_subr.c:1.478.2.2
--- src/sys/kern/vfs_subr.c:1.478.2.1	Fri Jan 17 21:47:35 2020
+++ src/sys/kern/vfs_subr.c	Fri Jan 24 16:05:22 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: vfs_subr.c,v 1.478.2.1 2020/01/17 21:47:35 ad Exp $	*/
+/*	$NetBSD: vfs_subr.c,v 1.478.2.2 2020/01/24 16:05:22 ad Exp $	*/
 
 /*-
  * Copyright (c) 1997, 1998, 2004, 2005, 2007, 2008, 2019
@@ -69,7 +69,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_subr.c,v 1.478.2.1 2020/01/17 21:47:35 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_subr.c,v 1.478.2.2 2020/01/24 16:05:22 ad Exp $");
 
 #ifdef _KERNEL_OPT
 #include "opt_ddb.h"
@@ -1111,7 +1111,7 @@ vprint_common(struct vnode *vp, const ch
 	    vp->v_usecount, vp->v_writecount, vp->v_holdcnt);
 	(*pr)("%ssize %" PRIx64 " writesize %" PRIx64 " numoutput %d\n",
 	    prefix, vp->v_size, vp->v_writesize, vp->v_numoutput);
-	(*pr)("%sdata %p lock %p\n", prefix, vp->v_data, vip->vi_lock);
+	(*pr)("%sdata %p lock %p\n", prefix, vp->v_data, &vip->vi_lock);
 
 	(*pr)("%sstate %s key(%p %zd)", prefix, vstate_name(vip->vi_state),
 	    vip->vi_key.vk_mount, vip->vi_key.vk_key_len);
@@ -1544,7 +1544,7 @@ vfs_vnode_lock_print(void *vlock, int fu
 
 	for (mp = _mountlist_next(NULL); mp; mp = _mountlist_next(mp)) {
 		TAILQ_FOREACH(vip, &mp->mnt_vnodelist, vi_mntvnodes) {
-			if (vip->vi_lock == vlock ||
+			if (&vip->vi_lock == vlock ||
 			    VIMPL_TO_VNODE(vip)->v_interlock == vlock)
 				vfs_vnode_print(VIMPL_TO_VNODE(vip), full, pr);
 		}

Index: src/sys/kern/vfs_vnode.c
diff -u src/sys/kern/vfs_vnode.c:1.105.2.4 src/sys/kern/vfs_vnode.c:1.105.2.5
--- src/sys/kern/vfs_vnode.c:1.105.2.4	Thu Jan 23 19:28:39 2020
+++ src/sys/kern/vfs_vnode.c	Fri Jan 24 16:05:22 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: vfs_vnode.c,v 1.105.2.4 2020/01/23 19:28:39 ad Exp $	*/
+/*	$NetBSD: vfs_vnode.c,v 1.105.2.5 2020/01/24 16:05:22 ad Exp $	*/
 
 /*-
  * Copyright (c) 1997-2011, 2019 The NetBSD Foundation, Inc.
@@ -142,10 +142,19 @@
  *	as vput(9), routines.  Common points holding references are e.g.
  *	file openings, current working directory, mount points, etc.  
  *
+ * Note on v_usecount & v_holdcnt and their locking
+ *
+ *	At nearly all points it is known that the counts could be zero,
+ *	the vnode_t::v_interlock will be held.  To change the counts away
+ *	from zero, the interlock must be held.  To change from a non-zero
+ *	value to zero, again the interlock must be held.
+ *
+ *	Changing the usecount from a non-zero value to a non-zero value can
+ *	safely be done using atomic operations, without the interlock held.
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,v 1.105.2.4 2020/01/23 19:28:39 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,v 1.105.2.5 2020/01/24 16:05:22 ad Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -669,6 +678,27 @@ vdrain_thread(void *cookie)
 }
 
 /*
+ * Try to drop reference on a vnode.  Abort if we are releasing the
+ * last reference.  Note: this _must_ succeed if not the last reference.
+ */
+static bool
+vtryrele(vnode_t *vp)
+{
+	u_int use, next;
+
+	for (use = vp->v_usecount;; use = next) {
+		if (__predict_false(use == 1)) {
+			return false;
+		}
+		KASSERT(use > 1);
+		next = atomic_cas_uint(&vp->v_usecount, use, use - 1);
+		if (__predict_true(next == use)) {
+			return true;
+		}
+	}
+}
+
+/*
  * vput: unlock and release the reference.
  */
 void
@@ -676,7 +706,20 @@ vput(vnode_t *vp)
 {
 	int lktype;
 
-	if ((vp->v_vflag & VV_LOCKSWORK) == 0) {
+	/*
+	 * Do an unlocked check of v_usecount.  If it looks like we're not
+	 * about to drop the last reference, then unlock the vnode and try
+	 * to drop the reference.  If it ends up being the last reference
+	 * after all, we dropped the lock when we shouldn't have.  vrelel()
+	 * can fix it all up.  Most of the time this will all go to plan.
+	 */
+	if (vp->v_usecount > 1) {
+		VOP_UNLOCK(vp);
+		if (vtryrele(vp)) {
+			return;
+		}
+		lktype = LK_NONE;
+	} else if ((vp->v_vflag & VV_LOCKSWORK) == 0) {
 		lktype = LK_EXCLUSIVE;
 	} else {
 		lktype = VOP_ISLOCKED(vp);
@@ -708,11 +751,10 @@ vrelel(vnode_t *vp, int flags, int lktyp
 	 * If not the last reference, just drop the reference count
 	 * and unlock.
 	 */
-	if (vp->v_usecount > 1) {
+	if (vtryrele(vp)) {
 		if (lktype != LK_NONE) {
 			VOP_UNLOCK(vp);
 		}
-		vp->v_usecount--;
 		mutex_exit(vp->v_interlock);
 		return;
 	}
@@ -792,8 +834,7 @@ vrelel(vnode_t *vp, int flags, int lktyp
 		mutex_enter(vp->v_interlock);
 		if (!recycle) {
 			VOP_UNLOCK(vp);
-			if (vp->v_usecount > 1) {
-				vp->v_usecount--;
+			if (vtryrele(vp)) {
 				mutex_exit(vp->v_interlock);
 				return;
 			}
@@ -820,8 +861,7 @@ vrelel(vnode_t *vp, int flags, int lktyp
 		KASSERT(vp->v_usecount > 0);
 	}
 
-	vp->v_usecount--;
-	if (vp->v_usecount != 0) {
+	if (atomic_dec_uint_nv(&vp->v_usecount) != 0) {
 		/* Gained another reference while being reclaimed. */
 		mutex_exit(vp->v_interlock);
 		return;
@@ -848,6 +888,9 @@ void
 vrele(vnode_t *vp)
 {
 
+	if (vtryrele(vp)) {
+		return;
+	}
 	mutex_enter(vp->v_interlock);
 	vrelel(vp, 0, LK_NONE);
 }
@@ -859,6 +902,9 @@ void
 vrele_async(vnode_t *vp)
 {
 
+	if (vtryrele(vp)) {
+		return;
+	}
 	mutex_enter(vp->v_interlock);
 	vrelel(vp, VRELEL_ASYNC, LK_NONE);
 }
@@ -873,9 +919,7 @@ vref(vnode_t *vp)
 
 	KASSERT(vp->v_usecount != 0);
 
-	mutex_enter(vp->v_interlock);
-	vp->v_usecount++;
-	mutex_exit(vp->v_interlock);
+	atomic_inc_uint(&vp->v_usecount);
 }
 
 /*
@@ -888,11 +932,34 @@ vholdl(vnode_t *vp)
 
 	KASSERT(mutex_owned(vp->v_interlock));
 
-	if (vp->v_holdcnt++ == 0 && vp->v_usecount == 0)
+	if (atomic_inc_uint_nv(&vp->v_holdcnt) == 1 && vp->v_usecount == 0)
 		lru_requeue(vp, lru_which(vp));
 }
 
 /*
+ * Page or buffer structure gets a reference.
+ */
+void
+vhold(vnode_t *vp)
+{
+	int hold, next;
+
+	for (hold = vp->v_holdcnt;; hold = next) {
+		if (__predict_false(hold == 0)) {
+			break;
+		}
+		next = atomic_cas_uint(&vp->v_holdcnt, hold, hold + 1);
+		if (__predict_true(next == hold)) {
+			return;
+		}
+	}
+
+	mutex_enter(vp->v_interlock);
+	vholdl(vp);
+	mutex_exit(vp->v_interlock);
+}
+
+/*
  * Page or buffer structure frees a reference.
  * Called with v_interlock held.
  */
@@ -906,12 +973,35 @@ holdrelel(vnode_t *vp)
 		vnpanic(vp, "%s: holdcnt vp %p", __func__, vp);
 	}
 
-	vp->v_holdcnt--;
-	if (vp->v_holdcnt == 0 && vp->v_usecount == 0)
+	if (atomic_dec_uint_nv(&vp->v_holdcnt) == 0 && vp->v_usecount == 0)
 		lru_requeue(vp, lru_which(vp));
 }
 
 /*
+ * Page or buffer structure frees a reference.
+ */
+void
+holdrele(vnode_t *vp)
+{
+	int hold, next;
+
+	for (hold = vp->v_holdcnt;; hold = next) {
+		if (__predict_false(hold == 1)) {
+			break;
+		}
+		KASSERT(hold > 1);
+		next = atomic_cas_uint(&vp->v_holdcnt, hold, hold - 1);
+		if (__predict_true(next == hold)) {
+			return;
+		}
+	}
+
+	mutex_enter(vp->v_interlock);
+	holdrelel(vp);
+	mutex_exit(vp->v_interlock);
+}
+
+/*
  * Recycle an unused vnode if caller holds the last reference.
  */
 bool
@@ -1013,7 +1103,7 @@ vrevoke(vnode_t *vp)
 	if (VSTATE_GET(vp) == VS_RECLAIMED) {
 		mutex_exit(vp->v_interlock);
 	} else if (vp->v_type != VBLK && vp->v_type != VCHR) {
-		vp->v_usecount++;
+		atomic_inc_uint(&vp->v_usecount);
 		mutex_exit(vp->v_interlock);
 		vgone(vp);
 	} else {
@@ -1068,8 +1158,8 @@ static void
 vcache_init(void)
 {
 
-	vcache_pool = pool_cache_init(sizeof(vnode_impl_t), 0, 0, 0,
-	    "vcachepl", NULL, IPL_NONE, NULL, NULL, NULL);
+	vcache_pool = pool_cache_init(sizeof(vnode_impl_t), coherency_unit,
+	    0, 0, "vcachepl", NULL, IPL_NONE, NULL, NULL, NULL);
 	KASSERT(vcache_pool != NULL);
 	mutex_init(&vcache_lock, MUTEX_DEFAULT, IPL_NONE);
 	cv_init(&vcache_cv, "vcache");
@@ -1139,7 +1229,7 @@ vcache_alloc(void)
 	vip = pool_cache_get(vcache_pool, PR_WAITOK);
 	memset(vip, 0, sizeof(*vip));
 
-	vip->vi_lock = rw_obj_alloc();
+	rw_init(&vip->vi_lock);
 
 	vp = VIMPL_TO_VNODE(vip);
 	uvm_obj_init(&vp->v_uobj, &uvm_vnodeops, true, 0);
@@ -1201,7 +1291,7 @@ vcache_free(vnode_impl_t *vip)
 	if (vp->v_type == VBLK || vp->v_type == VCHR)
 		spec_node_destroy(vp);
 
-	rw_obj_free(vip->vi_lock);
+	rw_destroy(&vip->vi_lock);
 	uvm_obj_destroy(&vp->v_uobj, true);
 	cv_destroy(&vp->v_cv);
 	cache_vnode_fini(vp);
@@ -1226,8 +1316,10 @@ vcache_tryvget(vnode_t *vp)
 		error = ENOENT;
 	else if (__predict_false(VSTATE_GET(vp) != VS_LOADED))
 		error = EBUSY;
+	else if (vp->v_usecount == 0)
+		vp->v_usecount = 1;
 	else
-		vp->v_usecount++;
+		atomic_inc_uint(&vp->v_usecount);
 
 	mutex_exit(vp->v_interlock);
 
@@ -1261,7 +1353,10 @@ vcache_vget(vnode_t *vp)
 		return ENOENT;
 	}
 	VSTATE_ASSERT(vp, VS_LOADED);
-	vp->v_usecount++;
+	if (vp->v_usecount == 0)
+		vp->v_usecount = 1;
+	else
+		atomic_inc_uint(&vp->v_usecount);
 	mutex_exit(vp->v_interlock);
 
 	return 0;

Index: src/sys/miscfs/genfs/genfs_vnops.c
diff -u src/sys/miscfs/genfs/genfs_vnops.c:1.200.2.2 src/sys/miscfs/genfs/genfs_vnops.c:1.200.2.3
--- src/sys/miscfs/genfs/genfs_vnops.c:1.200.2.2	Wed Jan 22 12:00:18 2020
+++ src/sys/miscfs/genfs/genfs_vnops.c	Fri Jan 24 16:05:22 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: genfs_vnops.c,v 1.200.2.2 2020/01/22 12:00:18 ad Exp $	*/
+/*	$NetBSD: genfs_vnops.c,v 1.200.2.3 2020/01/24 16:05:22 ad Exp $	*/
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -57,7 +57,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: genfs_vnops.c,v 1.200.2.2 2020/01/22 12:00:18 ad Exp $");
+__KERNEL_RCSID(0, "$NetBSD: genfs_vnops.c,v 1.200.2.3 2020/01/24 16:05:22 ad Exp $");
 
 #include <sys/param.h>
 #include <sys/systm.h>
@@ -293,19 +293,19 @@ genfs_deadlock(void *v)
 		return ENOENT;
 
 	if (ISSET(flags, LK_DOWNGRADE)) {
-		rw_downgrade(vip->vi_lock);
+		rw_downgrade(&vip->vi_lock);
 	} else if (ISSET(flags, LK_UPGRADE)) {
 		KASSERT(ISSET(flags, LK_NOWAIT));
-		if (!rw_tryupgrade(vip->vi_lock)) {
+		if (!rw_tryupgrade(&vip->vi_lock)) {
 			return EBUSY;
 		}
 	} else if ((flags & (LK_EXCLUSIVE | LK_SHARED)) != 0) {
 		op = (ISSET(flags, LK_EXCLUSIVE) ? RW_WRITER : RW_READER);
 		if (ISSET(flags, LK_NOWAIT)) {
-			if (!rw_tryenter(vip->vi_lock, op))
+			if (!rw_tryenter(&vip->vi_lock, op))
 				return EBUSY;
 		} else {
-			rw_enter(vip->vi_lock, op);
+			rw_enter(&vip->vi_lock, op);
 		}
 	}
 	VSTATE_ASSERT_UNLOCKED(vp, VS_RECLAIMED);
@@ -324,7 +324,7 @@ genfs_deadunlock(void *v)
 	vnode_t *vp = ap->a_vp;
 	vnode_impl_t *vip = VNODE_TO_VIMPL(vp);
 
-	rw_exit(vip->vi_lock);
+	rw_exit(&vip->vi_lock);
 
 	return 0;
 }
@@ -345,19 +345,19 @@ genfs_lock(void *v)
 	krw_t op;
 
 	if (ISSET(flags, LK_DOWNGRADE)) {
-		rw_downgrade(vip->vi_lock);
+		rw_downgrade(&vip->vi_lock);
 	} else if (ISSET(flags, LK_UPGRADE)) {
 		KASSERT(ISSET(flags, LK_NOWAIT));
-		if (!rw_tryupgrade(vip->vi_lock)) {
+		if (!rw_tryupgrade(&vip->vi_lock)) {
 			return EBUSY;
 		}
 	} else if ((flags & (LK_EXCLUSIVE | LK_SHARED)) != 0) {
 		op = (ISSET(flags, LK_EXCLUSIVE) ? RW_WRITER : RW_READER);
 		if (ISSET(flags, LK_NOWAIT)) {
-			if (!rw_tryenter(vip->vi_lock, op))
+			if (!rw_tryenter(&vip->vi_lock, op))
 				return EBUSY;
 		} else {
-			rw_enter(vip->vi_lock, op);
+			rw_enter(&vip->vi_lock, op);
 		}
 	}
 	VSTATE_ASSERT_UNLOCKED(vp, VS_ACTIVE);
@@ -376,7 +376,7 @@ genfs_unlock(void *v)
 	vnode_t *vp = ap->a_vp;
 	vnode_impl_t *vip = VNODE_TO_VIMPL(vp);
 
-	rw_exit(vip->vi_lock);
+	rw_exit(&vip->vi_lock);
 
 	return 0;
 }
@@ -393,10 +393,10 @@ genfs_islocked(void *v)
 	vnode_t *vp = ap->a_vp;
 	vnode_impl_t *vip = VNODE_TO_VIMPL(vp);
 
-	if (rw_write_held(vip->vi_lock))
+	if (rw_write_held(&vip->vi_lock))
 		return LK_EXCLUSIVE;
 
-	if (rw_read_held(vip->vi_lock))
+	if (rw_read_held(&vip->vi_lock))
 		return LK_SHARED;
 
 	return 0;

Index: src/sys/sys/vnode.h
diff -u src/sys/sys/vnode.h:1.286 src/sys/sys/vnode.h:1.286.2.1
--- src/sys/sys/vnode.h:1.286	Sun Dec 22 19:47:34 2019
+++ src/sys/sys/vnode.h	Fri Jan 24 16:05:23 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: vnode.h,v 1.286 2019/12/22 19:47:34 ad Exp $	*/
+/*	$NetBSD: vnode.h,v 1.286.2.1 2020/01/24 16:05:23 ad Exp $	*/
 
 /*-
  * Copyright (c) 2008 The NetBSD Foundation, Inc.
@@ -134,20 +134,35 @@ LIST_HEAD(buflists, buf);
  * it from v_data.
  */
 struct vnode {
+	/*
+	 * VM system related items.
+	 */
 	struct uvm_object v_uobj;		/* i: the VM object */
-	kcondvar_t	v_cv;			/* i: synchronization */
 	voff_t		v_size;			/* i: size of file */
 	voff_t		v_writesize;		/* i: new size after write */
+
+	/*
+	 * Unstable items get their own cache line.
+	 * On _LP64 this fills the space nicely.
+	 */
+	kcondvar_t	v_cv			/* i: synchronization */
+	    __aligned(COHERENCY_UNIT);
 	int		v_iflag;		/* i: VI_* flags */
-	int		v_vflag;		/* v: VV_* flags */
 	int		v_uflag;		/* u: VU_* flags */
 	int		v_numoutput;		/* i: # of pending writes */
 	int		v_writecount;		/* i: ref count of writers */
+	int		v_usecount;		/* i: use count */
 	int		v_holdcnt;		/* i: page & buffer refs */
-	struct mount	*v_mount;		/* v: ptr to vfs we are in */
-	int		(**v_op)(void *);	/* :: vnode operations vector */
 	struct buflists	v_cleanblkhd;		/* x: clean blocklist head */
 	struct buflists	v_dirtyblkhd;		/* x: dirty blocklist head */
+
+	/*
+	 * The remaining items are largely stable.
+	 */
+	int		v_vflag			/* v: VV_* flags */
+	    __aligned(COHERENCY_UNIT);
+	struct mount	*v_mount;		/* v: ptr to vfs we are in */
+	int		(**v_op)(void *);	/* :: vnode operations vector */
 	union {
 		struct mount	*vu_mountedhere;/* v: ptr to vfs (VDIR) */
 		struct socket	*vu_socket;	/* v: unix ipc (VSOCK) */
@@ -160,7 +175,6 @@ struct vnode {
 	void 		*v_data;		/* :: private data for fs */
 	struct klist	v_klist;		/* i: notes attached to vnode */
 };
-#define	v_usecount	v_uobj.uo_refs
 #define	v_interlock	v_uobj.vmobjlock
 #define	v_mountedhere	v_un.vu_mountedhere
 #define	v_socket	v_un.vu_socket
@@ -320,36 +334,11 @@ extern const int	vttoif_tab[];
 #define VDEAD_NOWAIT	0x0001		/* vdead_check: do not sleep */
 
 void holdrelel(struct vnode *);
+void holdrele(struct vnode *);
 void vholdl(struct vnode *);
+void vhold(struct vnode *);
 void vref(struct vnode *);
 
-static __inline void holdrele(struct vnode *) __unused;
-static __inline void vhold(struct vnode *) __unused;
-
-/*
- * decrease buf or page ref
- */
-static __inline void
-holdrele(struct vnode *vp)
-{
-
-	mutex_enter(vp->v_interlock);
-	holdrelel(vp);
-	mutex_exit(vp->v_interlock);
-}
-
-/*
- * increase buf or page ref
- */
-static __inline void
-vhold(struct vnode *vp)
-{
-
-	mutex_enter(vp->v_interlock);
-	vholdl(vp);
-	mutex_exit(vp->v_interlock);
-}
-
 #define	NULLVP	((struct vnode *)NULL)
 
 static __inline void

Index: src/sys/sys/vnode_impl.h
diff -u src/sys/sys/vnode_impl.h:1.19.2.4 src/sys/sys/vnode_impl.h:1.19.2.5
--- src/sys/sys/vnode_impl.h:1.19.2.4	Fri Jan 17 22:26:26 2020
+++ src/sys/sys/vnode_impl.h	Fri Jan 24 16:05:23 2020
@@ -1,4 +1,4 @@
-/*	$NetBSD: vnode_impl.h,v 1.19.2.4 2020/01/17 22:26:26 ad Exp $	*/
+/*	$NetBSD: vnode_impl.h,v 1.19.2.5 2020/01/24 16:05:23 ad Exp $	*/
 
 /*-
  * Copyright (c) 2016, 2019, 2020 The NetBSD Foundation, Inc.
@@ -57,26 +57,58 @@ struct vcache_key {
  * Reading or writing any of these items requires holding the appropriate
  * lock.  Field markings and the corresponding locks:
  *
+ *	-	stable throughout the life of the vnode
  *	c	vcache_lock
  *	d	vdrain_lock
  *	i	v_interlock
+ *	l	vi_nc_listlock
  *	m	mnt_vnodelock
- *	n	managed by vfs_cache: see above
+ *	n	vi_nc_lock
+ *	n,l	vi_nc_lock + vi_nc_listlock to modify
  *	s	syncer_data_lock
  */
 struct vnode_impl {
 	struct vnode vi_vnode;
-	enum vnode_state vi_state;		/* i: current state */
-	struct vnodelst *vi_lrulisthd;		/* d: current lru list head */
-	TAILQ_ENTRY(vnode_impl) vi_lrulist;	/* d: lru list */
-	struct nchnode *vi_ncache;		/* n: namecache state */
-	int vi_synclist_slot;			/* s: synclist slot index */
-	int vi_lrulisttm;			/* i: time of lru enqueue */
-	TAILQ_ENTRY(vnode_impl) vi_synclist;	/* s: vnodes with dirty bufs */
-	TAILQ_ENTRY(vnode_impl) vi_mntvnodes;	/* m: vnodes for mount point */
-	SLIST_ENTRY(vnode_impl) vi_hash;	/* c: vnode cache list */
-	krwlock_t *vi_lock;			/* -: lock for this vnode */
-	struct vcache_key vi_key;		/* c: vnode cache key */
+
+	/*
+	 * Largely stable data.
+	 */
+	struct vcache_key vi_key;		/* c   vnode cache key */
+
+	/*
+	 * Namecache.  Give it a separate line so activity doesn't impinge
+	 * on the stable stuff.
+	 */
+	rb_tree_t	vi_nc_tree		/* n   namecache tree */
+	    __aligned(COHERENCY_UNIT);
+	TAILQ_HEAD(,namecache) vi_nc_list;	/* l   namecaches (parent) */
+	mode_t		vi_nc_mode;		/* n,l cached mode or VNOVAL */
+	uid_t		vi_nc_uid;		/* n,l cached UID or VNOVAL */
+	gid_t		vi_nc_gid;		/* n,l cached GID or VNOVAL */
+	uint32_t	vi_nc_spare;		/* -   spare (padding) */
+
+	/*
+	 * vnode cache, LRU and syncer.  This all changes with some
+	 * regularity so keep it together.
+	 */
+	struct vnodelst	*vi_lrulisthd		/* d   current lru list head */
+	    __aligned(COHERENCY_UNIT);
+	TAILQ_ENTRY(vnode_impl) vi_lrulist;	/* d   lru list */
+	int 		vi_synclist_slot;	/* s   synclist slot index */
+	int 		vi_lrulisttm;		/* i   time of lru enqueue */
+	TAILQ_ENTRY(vnode_impl) vi_synclist;	/* s   vnodes with dirty bufs */
+	SLIST_ENTRY(vnode_impl) vi_hash;	/* c   vnode cache list */
+	enum vnode_state vi_state;		/* i   current state */
+
+	/*
+	 * Locks and expensive to access items which can be expected to
+	 * generate a cache miss.
+	 */
+	krwlock_t	vi_lock			/* -   lock for this vnode */
+	    __aligned(COHERENCY_UNIT);
+	krwlock_t	vi_nc_lock;		/* -   lock on node */
+	krwlock_t	vi_nc_listlock;		/* -   lock on nn_list */
+	TAILQ_ENTRY(vnode_impl) vi_mntvnodes;	/* m   vnodes for mount point */
 };
 typedef struct vnode_impl vnode_impl_t;
 

Reply via email to