Module Name:    src
Committed By:   ad
Date:           Sun May 17 19:34:07 UTC 2020

Modified Files:
        src/sys/kern: vfs_trans.c

Log Message:
Reorganise the locking and allocation of fstrans_lwp_info slightly, to
reduce contention.  "please go ahead" hannken@.


To generate a diff of this commit:
cvs rdiff -u -r1.62 -r1.63 src/sys/kern/vfs_trans.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_trans.c
diff -u src/sys/kern/vfs_trans.c:1.62 src/sys/kern/vfs_trans.c:1.63
--- src/sys/kern/vfs_trans.c:1.62	Wed May 13 09:21:30 2020
+++ src/sys/kern/vfs_trans.c	Sun May 17 19:34:07 2020
@@ -1,7 +1,7 @@
-/*	$NetBSD: vfs_trans.c,v 1.62 2020/05/13 09:21:30 hannken Exp $	*/
+/*	$NetBSD: vfs_trans.c,v 1.63 2020/05/17 19:34:07 ad Exp $	*/
 
 /*-
- * Copyright (c) 2007 The NetBSD Foundation, Inc.
+ * Copyright (c) 2007, 2020 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.62 2020/05/13 09:21:30 hannken Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.63 2020/05/17 19:34:07 ad Exp $");
 
 /*
  * File system transaction operations.
@@ -50,6 +50,7 @@ __KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,
 #include <sys/vnode.h>
 #include <sys/fstrans.h>
 #include <sys/proc.h>
+#include <sys/pool.h>
 
 #include <miscfs/specfs/specdev.h>
 
@@ -85,14 +86,17 @@ struct fstrans_mount_info {
 	struct lwp *fmi_owner;
 };
 
-static kmutex_t vfs_suspend_lock;	/* Serialize suspensions. */
-static kmutex_t fstrans_lock;		/* Fstrans big lock. */
-static kmutex_t fstrans_mount_lock;	/* Fstrans mount big lock. */
+static kmutex_t vfs_suspend_lock	/* Serialize suspensions. */
+    __cacheline_aligned;
+static kmutex_t fstrans_lock		/* Fstrans big lock. */
+    __cacheline_aligned;
 static kcondvar_t fstrans_state_cv;	/* Fstrans or cow state changed. */
 static kcondvar_t fstrans_count_cv;	/* Fstrans or cow count changed. */
 static pserialize_t fstrans_psz;	/* Pserialize state. */
 static LIST_HEAD(fstrans_lwp_head, fstrans_lwp_info) fstrans_fli_head;
 					/* List of all fstrans_lwp_info. */
+static pool_cache_t fstrans_lwp_cache;	/* Cache of fstrans_lwp_info. */
+
 static int fstrans_gone_count;		/* Number of fstrans_mount_info gone. */
 
 static void fstrans_mount_dtor(struct fstrans_mount_info *);
@@ -100,6 +104,8 @@ static void fstrans_clear_lwp_info(void)
 static inline struct fstrans_lwp_info *
     fstrans_get_lwp_info(struct mount *, bool);
 static struct fstrans_lwp_info *fstrans_alloc_lwp_info(struct mount *);
+static int fstrans_lwp_pcc(void *, void *, int);
+static void fstrans_lwp_pcd(void *, void *);
 static inline int _fstrans_start(struct mount *, enum fstrans_lock_type, int);
 static bool grant_lock(const struct fstrans_mount_info *,
     const enum fstrans_lock_type);
@@ -125,12 +131,12 @@ fstrans_debug_mount(struct mount *mp)
 {
 	struct fstrans_debug_mount *fdm, *new;
 
-	KASSERT(mutex_owned(&fstrans_mount_lock));
+	KASSERT(mutex_owned(&fstrans_lock));
 
-	mutex_exit(&fstrans_mount_lock);
+	mutex_exit(&fstrans_lock);
 	new = kmem_alloc(sizeof(*new), KM_SLEEP);
 	new->fdm_mount = mp;
-	mutex_enter(&fstrans_mount_lock);
+	mutex_enter(&fstrans_lock);
 
 	SLIST_FOREACH(fdm, &fstrans_debug_mount_head, fdm_list)
 		KASSERT(fdm->fdm_mount != mp);
@@ -142,7 +148,7 @@ fstrans_debug_unmount(struct mount *mp)
 {
 	struct fstrans_debug_mount *fdm;
 
-	KASSERT(mutex_owned(&fstrans_mount_lock));
+	KASSERT(mutex_owned(&fstrans_lock));
 
 	SLIST_FOREACH(fdm, &fstrans_debug_mount_head, fdm_list)
 		if (fdm->fdm_mount == mp)
@@ -158,7 +164,7 @@ fstrans_debug_validate_mount(struct moun
 {
 	struct fstrans_debug_mount *fdm;
 
-	KASSERT(mutex_owned(&fstrans_mount_lock));
+	KASSERT(mutex_owned(&fstrans_lock));
 
 	SLIST_FOREACH(fdm, &fstrans_debug_mount_head, fdm_list)
 		if (fdm->fdm_mount == mp)
@@ -183,11 +189,45 @@ fstrans_init(void)
 
 	mutex_init(&vfs_suspend_lock, MUTEX_DEFAULT, IPL_NONE);
 	mutex_init(&fstrans_lock, MUTEX_DEFAULT, IPL_NONE);
-	mutex_init(&fstrans_mount_lock, MUTEX_DEFAULT, IPL_NONE);
 	cv_init(&fstrans_state_cv, "fstchg");
 	cv_init(&fstrans_count_cv, "fstcnt");
 	fstrans_psz = pserialize_create();
 	LIST_INIT(&fstrans_fli_head);
+	fstrans_lwp_cache = pool_cache_init(sizeof(struct fstrans_lwp_info),
+	    coherency_unit, 0, 0, "fstlwp", NULL, IPL_NONE,
+	    fstrans_lwp_pcc, fstrans_lwp_pcd, NULL);
+	KASSERT(fstrans_lwp_cache != NULL);
+}
+
+/*
+ * pool_cache constructor for fstrans_lwp_info.  Updating the global list
+ * produces cache misses on MP.  Minimise by keeping free entries on list.
+ */
+int
+fstrans_lwp_pcc(void *arg, void *obj, int flags)
+{
+	struct fstrans_lwp_info *fli = obj;
+
+	memset(fli, 0, sizeof(*fli));
+
+	mutex_enter(&fstrans_lock);
+	LIST_INSERT_HEAD(&fstrans_fli_head, fli, fli_list);
+	mutex_exit(&fstrans_lock);
+
+	return 0;
+}
+
+/*
+ * pool_cache destructor
+ */
+void
+fstrans_lwp_pcd(void *arg, void *obj)
+{
+	struct fstrans_lwp_info *fli = obj;
+
+	mutex_enter(&fstrans_lock);
+	LIST_REMOVE(fli, fli_list);
+	mutex_exit(&fstrans_lock);
 }
 
 /*
@@ -198,6 +238,10 @@ fstrans_lwp_dtor(lwp_t *l)
 {
 	struct fstrans_lwp_info *fli, *fli_next;
 
+	if (l->l_fstrans == NULL)
+		return;
+
+	mutex_enter(&fstrans_lock);
 	for (fli = l->l_fstrans; fli; fli = fli_next) {
 		KASSERT(fli->fli_trans_cnt == 0);
 		KASSERT(fli->fli_cow_cnt == 0);
@@ -209,10 +253,14 @@ fstrans_lwp_dtor(lwp_t *l)
 		fli->fli_mount = NULL;
 		fli->fli_alias = NULL;
 		fli->fli_mountinfo = NULL;
-		membar_sync();
 		fli->fli_self = NULL;
 	}
+	mutex_exit(&fstrans_lock);
 
+	for (fli = l->l_fstrans; fli; fli = fli_next) {
+		fli_next = fli->fli_succ;
+		pool_cache_put(fstrans_lwp_cache, fli);
+	}
 	l->l_fstrans = NULL;
 }
 
@@ -223,12 +271,11 @@ static void
 fstrans_mount_dtor(struct fstrans_mount_info *fmi)
 {
 
-	mutex_enter(&fstrans_mount_lock);
+	KASSERT(mutex_owned(&fstrans_lock));
 
 	KASSERT(fmi != NULL);
 	fmi->fmi_ref_cnt -= 1;
-	if (fmi->fmi_ref_cnt > 0) {
-		mutex_exit(&fstrans_mount_lock);
+	if (__predict_true(fmi->fmi_ref_cnt > 0)) {
 		return;
 	}
 
@@ -239,8 +286,6 @@ fstrans_mount_dtor(struct fstrans_mount_
 	KASSERT(fstrans_gone_count > 0);
 	fstrans_gone_count -= 1;
 
-	mutex_exit(&fstrans_mount_lock);
-
 	kmem_free(fmi->fmi_mount, sizeof(*fmi->fmi_mount));
 	kmem_free(fmi, sizeof(*fmi));
 }
@@ -262,10 +307,10 @@ fstrans_mount(struct mount *mp)
 	newfmi->fmi_mount = mp;
 	newfmi->fmi_owner = NULL;
 
-	mutex_enter(&fstrans_mount_lock);
+	mutex_enter(&fstrans_lock);
 	mp->mnt_transinfo = newfmi;
 	fstrans_debug_mount(mp);
-	mutex_exit(&fstrans_mount_lock);
+	mutex_exit(&fstrans_lock);
 
 	return 0;
 }
@@ -280,14 +325,13 @@ fstrans_unmount(struct mount *mp)
 
 	KASSERT(fmi != NULL);
 
-	mutex_enter(&fstrans_mount_lock);
+	mutex_enter(&fstrans_lock);
 	fstrans_debug_unmount(mp);
 	fmi->fmi_gone = true;
 	mp->mnt_transinfo = NULL;
 	fstrans_gone_count += 1;
-	mutex_exit(&fstrans_mount_lock);
-
 	fstrans_mount_dtor(fmi);
+	mutex_exit(&fstrans_lock);
 }
 
 /*
@@ -296,11 +340,12 @@ fstrans_unmount(struct mount *mp)
 static void
 fstrans_clear_lwp_info(void)
 {
-	struct fstrans_lwp_info **p, *fli;
+	struct fstrans_lwp_info **p, *fli, *tofree = NULL;
 
 	/*
 	 * Scan our list clearing entries whose mount is gone.
 	 */
+	mutex_enter(&fstrans_lock);
 	for (p = &curlwp->l_fstrans; *p; ) {
 		fli = *p;
 		if (fli->fli_mount != NULL &&
@@ -317,9 +362,10 @@ fstrans_clear_lwp_info(void)
 			fli->fli_mount = NULL;
 			fli->fli_alias = NULL;
 			fli->fli_mountinfo = NULL;
-			membar_sync();
 			fli->fli_self = NULL;
 			p = &curlwp->l_fstrans;
+			fli->fli_succ = tofree;
+			tofree = fli;
 		} else {
 			p = &(*p)->fli_succ;
 		}
@@ -329,6 +375,13 @@ fstrans_clear_lwp_info(void)
 		if (fli->fli_alias != NULL)
 			KASSERT(fli->fli_alias->fli_self == curlwp);
 #endif /* DIAGNOSTIC */
+	mutex_exit(&fstrans_lock);
+
+	while (tofree != NULL) {
+		fli = tofree;
+		tofree = fli->fli_succ;
+		pool_cache_put(fstrans_lwp_cache, fli);
+	}
 }
 
 /*
@@ -346,40 +399,25 @@ fstrans_alloc_lwp_info(struct mount *mp)
 	}
 
 	/*
-	 * Try to reuse a cleared entry or allocate a new one.
+	 * Allocate a new entry.
 	 */
-	mutex_enter(&fstrans_lock);
-	LIST_FOREACH(fli, &fstrans_fli_head, fli_list) {
-		membar_sync();
-		if (fli->fli_self == NULL) {
-			KASSERT(fli->fli_mount == NULL);
-			KASSERT(fli->fli_trans_cnt == 0);
-			KASSERT(fli->fli_cow_cnt == 0);
-			KASSERT(fli->fli_alias_cnt == 0);
-			fli->fli_self = curlwp;
-			fli->fli_succ = curlwp->l_fstrans;
-			curlwp->l_fstrans = fli;
-			break;
-		}
-	}
-	mutex_exit(&fstrans_lock);
-
-	if (fli == NULL) {
-		fli = kmem_alloc(sizeof(*fli), KM_SLEEP);
-		mutex_enter(&fstrans_lock);
-		memset(fli, 0, sizeof(*fli));
-		fli->fli_self = curlwp;
-		LIST_INSERT_HEAD(&fstrans_fli_head, fli, fli_list);
-		mutex_exit(&fstrans_lock);
-		fli->fli_succ = curlwp->l_fstrans;
-		curlwp->l_fstrans = fli;
-	}
+	fli = pool_cache_get(fstrans_lwp_cache, PR_WAITOK);
+	KASSERT(fli->fli_trans_cnt == 0);
+	KASSERT(fli->fli_cow_cnt == 0);
+	KASSERT(fli->fli_alias_cnt == 0);
+	KASSERT(fli->fli_mount == NULL);
+	KASSERT(fli->fli_alias == NULL);
+	KASSERT(fli->fli_mountinfo == NULL);
+	KASSERT(fli->fli_self == NULL);
+	fli->fli_succ = curlwp->l_fstrans;
+	curlwp->l_fstrans = fli;
 
 	/*
 	 * Attach the entry to the mount if its mnt_transinfo is valid.
 	 */
 
-	mutex_enter(&fstrans_mount_lock);
+	mutex_enter(&fstrans_lock);
+	fli->fli_self = curlwp;
 	fstrans_debug_validate_mount(mp);
 	fmi = mp->mnt_transinfo;
 	KASSERT(fmi != NULL);
@@ -389,7 +427,7 @@ fstrans_alloc_lwp_info(struct mount *mp)
 	do {
 		mp = mp->mnt_lower;
 	} while (mp && mp->mnt_lower);
-	mutex_exit(&fstrans_mount_lock);
+	mutex_exit(&fstrans_lock);
 
 	if (mp) {
 		fli->fli_alias = fstrans_alloc_lwp_info(mp);
@@ -819,11 +857,11 @@ fscow_establish(struct mount *mp, int (*
 
 	KASSERT(mp != dead_rootmount);
 
-	mutex_enter(&fstrans_mount_lock);
+	mutex_enter(&fstrans_lock);
 	fmi = mp->mnt_transinfo;
 	KASSERT(fmi != NULL);
 	fmi->fmi_ref_cnt += 1;
-	mutex_exit(&fstrans_mount_lock);
+	mutex_exit(&fstrans_lock);
 
 	newch = kmem_alloc(sizeof(*newch), KM_SLEEP);
 	newch->ch_func = func;
@@ -859,9 +897,8 @@ fscow_disestablish(struct mount *mp, int
 		LIST_REMOVE(hp, ch_list);
 		kmem_free(hp, sizeof(*hp));
 	}
-	cow_change_done(fmi);
-
 	fstrans_mount_dtor(fmi);
+	cow_change_done(fmi);
 
 	return hp ? 0 : EINVAL;
 }

Reply via email to