Module Name:    src
Committed By:   riastradh
Date:           Mon Oct 15 14:03:06 UTC 2012

Modified Files:
        src/external/cddl/osnet/dist/uts/common/fs/zfs: zfs_dir.c
        src/external/cddl/osnet/dist/uts/common/fs/zfs/sys: zfs_znode.h

Log Message:
Do reference counting for zfs range lock waiters.

Avoid cv_broadcast(&cv); cv_destroy(&cv); which works in Solaris only
by abuse of the condvar abstraction.

There are parts of this code that should be factored into smaller
subroutines, mainly range lock allocation and initialization, but
that would make it harder to merge newer versions of zfs, so for now
I've just expanded those parts further in-line.


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 \
    src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_dir.c
cvs rdiff -u -r1.4 -r1.5 \
    src/external/cddl/osnet/dist/uts/common/fs/zfs/sys/zfs_znode.h

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_dir.c
diff -u src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_dir.c:1.5 src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_dir.c:1.6
--- src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_dir.c:1.5	Sat Feb 27 23:43:53 2010
+++ src/external/cddl/osnet/dist/uts/common/fs/zfs/zfs_dir.c	Mon Oct 15 14:03:06 2012
@@ -96,6 +96,43 @@ zfs_match_find(zfsvfs_t *zfsvfs, znode_t
 }
 
 /*
+ * 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 @@ zfs_dirent_lock(zfs_dirlock_t **dlpp, zn
 			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 @@ zfs_dirent_unlock(zfs_dirlock_t *dl)
 		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));
 }
 
 /*

Index: src/external/cddl/osnet/dist/uts/common/fs/zfs/sys/zfs_znode.h
diff -u src/external/cddl/osnet/dist/uts/common/fs/zfs/sys/zfs_znode.h:1.4 src/external/cddl/osnet/dist/uts/common/fs/zfs/sys/zfs_znode.h:1.5
--- src/external/cddl/osnet/dist/uts/common/fs/zfs/sys/zfs_znode.h:1.4	Sat Feb 27 23:43:53 2010
+++ src/external/cddl/osnet/dist/uts/common/fs/zfs/sys/zfs_znode.h	Mon Oct 15 14:03:06 2012
@@ -186,6 +186,7 @@ typedef struct zfs_dirlock {
 	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 */

Reply via email to