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