Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/kern Replace the rwlock based implementation with passiv...



details:   https://anonhg.NetBSD.org/src/rev/7f8f3f55cca1
branches:  trunk
changeset: 784182:7f8f3f55cca1
user:      hannken <hannken%NetBSD.org@localhost>
date:      Mon Jan 21 09:14:01 2013 +0000

description:
Replace the rwlock based implementation with passive serialization
from pserialize(9) and mutex / condvar.

The fast paths (fstrans_start/fstrans_done on a file system not
suspended or suspending and fscow_run with no change pending) now
run without locks or other atomic operations.  Suspension and cow
handler insertion and removal is done with mutex / condvars.

The API remains unchanged.

diffstat:

 sys/kern/vfs_trans.c |  722 +++++++++++++++++++++++++++++++++-----------------
 1 files changed, 477 insertions(+), 245 deletions(-)

diffs (truncated from 964 to 300 lines):

diff -r 68b23e791aac -r 7f8f3f55cca1 sys/kern/vfs_trans.c
--- a/sys/kern/vfs_trans.c      Mon Jan 21 08:02:01 2013 +0000
+++ b/sys/kern/vfs_trans.c      Mon Jan 21 09:14:01 2013 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: vfs_trans.c,v 1.25 2009/05/12 11:42:12 yamt Exp $      */
+/*     $NetBSD: vfs_trans.c,v 1.26 2013/01/21 09:14:01 hannken Exp $   */
 
 /*-
  * Copyright (c) 2007 The NetBSD Foundation, Inc.
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.25 2009/05/12 11:42:12 yamt Exp $");
+__KERNEL_RCSID(0, "$NetBSD: vfs_trans.c,v 1.26 2013/01/21 09:14:01 hannken Exp $");
 
 /*
  * File system transaction operations.
@@ -38,16 +38,13 @@
 
 #include "opt_ddb.h"
 
-#if defined(DDB)
-#define _LWP_API_PRIVATE       /* Need _lwp_getspecific_by_lwp() */
-#endif
-
 #include <sys/param.h>
 #include <sys/systm.h>
+#include <sys/atomic.h>
 #include <sys/buf.h>
 #include <sys/kmem.h>
 #include <sys/mount.h>
-#include <sys/rwlock.h>
+#include <sys/pserialize.h>
 #include <sys/vnode.h>
 #define _FSTRANS_API_PRIVATE
 #include <sys/fstrans.h>
@@ -57,34 +54,47 @@
 #include <miscfs/syncfs/syncfs.h>
 
 struct fscow_handler {
-       SLIST_ENTRY(fscow_handler) ch_list;
+       LIST_ENTRY(fscow_handler) ch_list;
        int (*ch_func)(void *, struct buf *, bool);
        void *ch_arg;
 };
 struct fstrans_lwp_info {
        struct fstrans_lwp_info *fli_succ;
+       struct lwp *fli_self;
        struct mount *fli_mount;
        int fli_trans_cnt;
        int fli_cow_cnt;
        enum fstrans_lock_type fli_lock_type;
+       LIST_ENTRY(fstrans_lwp_info) fli_list;
 };
 struct fstrans_mount_info {
        enum fstrans_state fmi_state;
-       krwlock_t fmi_shared_lock;
-       krwlock_t fmi_lazy_lock;
-       krwlock_t fmi_cow_lock;
-       SLIST_HEAD(, fscow_handler) fmi_cow_handler;
+       unsigned int fmi_ref_cnt;
+       bool fmi_cow_change;
+       LIST_HEAD(, fscow_handler) fmi_cow_handler;
 };
 
-static specificdata_key_t lwp_data_key;
+static specificdata_key_t lwp_data_key;        /* Our specific data key. */
 static kmutex_t vfs_suspend_lock;      /* Serialize suspensions. */
-static pool_cache_t fstrans_cache;
+static kmutex_t fstrans_lock;          /* Fstrans big lock. */
+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_cache;     /* Pool of struct fstrans_lwp_info. */
 
 static void fstrans_lwp_dtor(void *);
-static struct fstrans_lwp_info *fstrans_get_lwp_info(struct mount *);
+static void fstrans_mount_dtor(struct mount *);
+static struct fstrans_lwp_info *fstrans_get_lwp_info(struct mount *, bool);
+static bool grant_lock(const enum fstrans_state, const enum fstrans_lock_type);
+static bool state_change_done(const struct mount *);
+static bool cow_state_change_done(const struct mount *);
+static void cow_change_enter(const struct mount *);
+static void cow_change_done(const struct mount *);
 
 /*
- * Initialize
+ * Initialize.
  */
 void
 fstrans_init(void)
@@ -95,120 +105,183 @@
        KASSERT(error == 0);
 
        mutex_init(&vfs_suspend_lock, MUTEX_DEFAULT, IPL_NONE);
+       mutex_init(&fstrans_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_cache = pool_cache_init(sizeof(struct fstrans_lwp_info), 0, 0,
            0, "fstrans", NULL, IPL_NONE, NULL, NULL, NULL);
 }
 
 /*
- * Deallocate lwp state
+ * Deallocate lwp state.
  */
 static void
 fstrans_lwp_dtor(void *arg)
 {
        struct fstrans_lwp_info *fli, *fli_next;
 
+       mutex_enter(&fstrans_lock);
        for (fli = arg; fli; fli = fli_next) {
                KASSERT(fli->fli_trans_cnt == 0);
                KASSERT(fli->fli_cow_cnt == 0);
+               if (fli->fli_mount != NULL)
+                       fstrans_mount_dtor(fli->fli_mount);
                fli_next = fli->fli_succ;
+               LIST_REMOVE(fli, fli_list);
                pool_cache_put(fstrans_cache, fli);
        }
+       mutex_exit(&fstrans_lock);
 }
 
 /*
- * Allocate mount state
+ * Dereference mount state.
+ */
+static void
+fstrans_mount_dtor(struct mount *mp)
+{
+       struct fstrans_mount_info *fmi;
+
+       fmi = mp->mnt_transinfo;
+       if (atomic_dec_uint_nv(&fmi->fmi_ref_cnt) > 0)
+               return;
+
+       KASSERT(fmi->fmi_state == FSTRANS_NORMAL);
+       KASSERT(LIST_FIRST(&fmi->fmi_cow_handler) == NULL);
+
+       kmem_free(fmi, sizeof(*fmi));
+       mp->mnt_iflag &= ~IMNT_HAS_TRANS;
+       mp->mnt_transinfo = NULL;
+
+       vfs_destroy(mp);
+}
+
+/*
+ * Allocate mount state.
  */
 int
 fstrans_mount(struct mount *mp)
 {
+       int error;
        struct fstrans_mount_info *new;
 
+       error = vfs_busy(mp, NULL);
+       if (error)
+               return error;
        if ((new = kmem_alloc(sizeof(*new), KM_SLEEP)) == NULL)
                return ENOMEM;
        new->fmi_state = FSTRANS_NORMAL;
-       rw_init(&new->fmi_lazy_lock);
-       rw_init(&new->fmi_shared_lock);
-       SLIST_INIT(&new->fmi_cow_handler);
-       rw_init(&new->fmi_cow_lock);
+       new->fmi_ref_cnt = 1;
+       LIST_INIT(&new->fmi_cow_handler);
+       new->fmi_cow_change = false;
 
        mp->mnt_transinfo = new;
        mp->mnt_iflag |= IMNT_HAS_TRANS;
 
+       vfs_unbusy(mp, true, NULL);
+
        return 0;
 }
 
 /*
- * Deallocate mount state
+ * Deallocate mount state.
  */
 void
 fstrans_unmount(struct mount *mp)
 {
-       struct fstrans_mount_info *fmi;
-       struct fscow_handler *hp;
-
-       if ((fmi = mp->mnt_transinfo) == NULL)
-               return;
 
-       KASSERT(fmi->fmi_state == FSTRANS_NORMAL);
-       rw_destroy(&fmi->fmi_lazy_lock);
-       rw_destroy(&fmi->fmi_shared_lock);
-       rw_enter(&fmi->fmi_cow_lock, RW_WRITER);
-       while ((hp = SLIST_FIRST(&fmi->fmi_cow_handler)) != NULL) {
-               SLIST_REMOVE(&fmi->fmi_cow_handler, hp, fscow_handler, ch_list);
-               kmem_free(hp, sizeof(*hp));
-       }
-       rw_exit(&fmi->fmi_cow_lock);
-       rw_destroy(&fmi->fmi_cow_lock);
-       kmem_free(fmi, sizeof(*fmi));
-       mp->mnt_iflag &= ~IMNT_HAS_TRANS;
-       mp->mnt_transinfo = NULL;
+       KASSERT(mp->mnt_transinfo != NULL);
+
+       fstrans_mount_dtor(mp);
 }
 
 /*
- * Retrieve the per lwp info for this mount
+ * Retrieve the per lwp info for this mount allocating if necessary.
  */
 static struct fstrans_lwp_info *
-fstrans_get_lwp_info(struct mount *mp)
+fstrans_get_lwp_info(struct mount *mp, bool do_alloc)
 {
-       struct fstrans_lwp_info *fli, *new_fli;
+       struct fstrans_lwp_info *fli, *res;
+       struct fstrans_mount_info *fmi;
 
-       new_fli = NULL;
+       /*
+        * Scan our list for a match clearing entries whose mount is gone.
+        */
+       res = NULL;
        for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
-               if (fli->fli_mount == mp)
-                       return fli;
-               else if (fli->fli_trans_cnt == 0 && fli->fli_cow_cnt == 0 &&
-                   new_fli == NULL)
-                       new_fli = fli;
+               if (fli->fli_mount == mp) {
+                       KASSERT(res == NULL);
+                       res = fli;
+               } else if (fli->fli_mount != NULL &&
+                   (fli->fli_mount->mnt_iflag & IMNT_GONE) != 0 &&
+                   fli->fli_trans_cnt == 0 && fli->fli_cow_cnt == 0) {
+                       fstrans_mount_dtor(fli->fli_mount);
+                       fli->fli_mount = NULL;
+               }
+       }
+       if (__predict_true(res != NULL))
+               return res;
+
+       if (! do_alloc)
+               return NULL;
+
+       /*
+        * Try to reuse a cleared entry or allocate a new one.
+        */
+       for (fli = lwp_getspecific(lwp_data_key); fli; fli = fli->fli_succ) {
+               if (fli->fli_mount == NULL) {
+                       KASSERT(fli->fli_trans_cnt == 0);
+                       KASSERT(fli->fli_cow_cnt == 0);
+                       break;
+               }
+       }
+       if (fli == NULL) {
+               fli = pool_cache_get(fstrans_cache, PR_WAITOK);
+               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 = lwp_getspecific(lwp_data_key);
+               lwp_setspecific(lwp_data_key, fli);
        }
 
-       if (new_fli == NULL) {
-               new_fli = pool_cache_get(fstrans_cache, PR_WAITOK);
-               new_fli->fli_trans_cnt = 0;
-               new_fli->fli_cow_cnt = 0;
-               new_fli->fli_succ = lwp_getspecific(lwp_data_key);
-               lwp_setspecific(lwp_data_key, new_fli);
-       }
+       /*
+        * Attach the entry to the mount.
+        */
+       fmi = mp->mnt_transinfo;
+       fli->fli_mount = mp;
+       atomic_inc_uint(&fmi->fmi_ref_cnt);
+
+       return fli;
+}
 
-       KASSERT(new_fli->fli_trans_cnt == 0);
-       KASSERT(new_fli->fli_cow_cnt == 0);
+/*
+ * Check if this lock type is granted at this state.
+ */
+static bool
+grant_lock(const enum fstrans_state state, const enum fstrans_lock_type type)
+{
 



Home | Main Index | Thread Index | Old Index