Source-Changes-HG archive

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

[src/trunk]: src/external/cddl/osnet/dist/uts/common/fs/zfs Do reference coun...



details:   https://anonhg.NetBSD.org/src/rev/cfdf8fb30623
branches:  trunk
changeset: 782093:cfdf8fb30623
user:      riastradh <riastradh%NetBSD.org@localhost>
date:      Mon Oct 15 14:03:06 2012 +0000

description:
Do reference counting for zfs dirlock waiters.

Solaris relies on cv_broadcast(&cv); cv_destroy(&cv) working, but
that hoses our cv_wait, which needs to continue using cv after it is
woken.  Solaris's idiom is an abuse of the condvar abstraction, but
we can get the same effect with reference counting.

diffstat:

 external/cddl/osnet/dist/uts/common/fs/zfs/sys/zfs_znode.h |   1 +
 external/cddl/osnet/dist/uts/common/fs/zfs/zfs_dir.c       |  54 ++++++++++++-
 2 files changed, 49 insertions(+), 6 deletions(-)

diffs (97 lines):

diff -r 49aa1d9451c4 -r cfdf8fb30623 external/cddl/osnet/dist/uts/common/fs/zfs/sys/zfs_znode.h
--- a/external/cddl/osnet/dist/uts/common/fs/zfs/sys/zfs_znode.h        Mon Oct 15 13:38:52 2012 +0000
+++ b/external/cddl/osnet/dist/uts/common/fs/zfs/sys/zfs_znode.h        Mon Oct 15 14:03:06 2012 +0000
@@ -186,6 +186,7 @@
        uint32_t        dl_sharecnt;    /* 0 if exclusive, > 0 if shared */
        uint8_t         dl_namelock;    /* 1 if z_name_lock is NOT held */
        uint16_t        dl_namesize;    /* set if dl_name was allocated */
+       unsigned long   dl_refcnt;      /* reference count */
        kcondvar_t      dl_cv;          /* wait for entry to be unlocked */
        struct znode    *dl_dzp;        /* directory znode */
        struct zfs_dirlock *dl_next;    /* next in z_dirlocks list */
diff -r 49aa1d9451c4 -r cfdf8fb30623 external/cddl/osnet/dist/uts/common/fs/zfs/zfs_dir.c
--- a/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_dir.c      Mon Oct 15 13:38:52 2012 +0000
+++ b/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_dir.c      Mon Oct 15 14:03:06 2012 +0000
@@ -96,6 +96,43 @@
 }
 
 /*
+ * Reference counting for dirlocks.  Solaris destroys the condvar as
+ * soon as it broadcasts, which works for them because cv_wait doesn't
+ * need to use the condvar after it is woken, but which is too fast and
+ * loose with the abstraction for us in NetBSD.
+ */
+
+static int
+zfs_dirlock_hold(zfs_dirlock_t *dl, znode_t *dzp)
+{
+
+       (void)dzp;              /* ignore */
+       KASSERT(mutex_owned(&dzp->z_lock));
+
+       if (dl->dl_refcnt >= ULONG_MAX) /* XXX Name this constant.  */
+               return (ENFILE);        /* XXX What to do?  */
+
+       dl->dl_refcnt++;
+       return (0);
+}
+
+static void
+zfs_dirlock_rele(zfs_dirlock_t *dl, znode_t *dzp)
+{
+
+       (void)dzp;              /* ignore */
+       KASSERT(mutex_owned(&dzp->z_lock));
+       KASSERT(dl->dl_refcnt > 0);
+
+       if (--dl->dl_refcnt == 0) {
+               if (dl->dl_namesize != 0)
+                       kmem_free(dl->dl_name, dl->dl_namesize);
+               cv_destroy(&dl->dl_cv);
+               kmem_free(dl, sizeof(*dl));
+       }
+}
+
+/*
  * Lock a directory entry.  A dirlock on <dzp, name> protects that name
  * in dzp's directory zap object.  As long as you hold a dirlock, you can
  * assume two things: (1) dzp cannot be reaped, and (2) no other thread
@@ -246,14 +283,23 @@
                        dl->dl_sharecnt = 0;
                        dl->dl_namelock = 0;
                        dl->dl_namesize = 0;
+                       dl->dl_refcnt = 1;
                        dl->dl_dzp = dzp;
                        dl->dl_next = dzp->z_dirlocks;
                        dzp->z_dirlocks = dl;
                        break;
-               } 
+               }
                if ((flag & ZSHARED) && dl->dl_sharecnt != 0)
                        break;
+               error = zfs_dirlock_hold(dl, dzp);
+               if (error) {
+                       mutex_exit(&dzp->z_lock);
+                       if (!(flag & ZHAVELOCK))
+                               rw_exit(&dzp->z_name_lock);
+                       return (error);
+               }
                cv_wait(&dl->dl_cv, &dzp->z_lock);
+               zfs_dirlock_rele(dl, dzp);
        }
 
        /*
@@ -355,12 +401,8 @@
                prev_dl = &cur_dl->dl_next;
        *prev_dl = dl->dl_next;
        cv_broadcast(&dl->dl_cv);
+       zfs_dirlock_rele(dl, dzp);
        mutex_exit(&dzp->z_lock);
-
-       if (dl->dl_namesize != 0)
-               kmem_free(dl->dl_name, dl->dl_namesize);
-       cv_destroy(&dl->dl_cv);
-       kmem_free(dl, sizeof (*dl));
 }
 
 /*



Home | Main Index | Thread Index | Old Index