svn commit: r288570 - in stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs: . sys

Alexander Motin mav at FreeBSD.org
Sat Oct 3 07:59:28 UTC 2015


Author: mav
Date: Sat Oct  3 07:59:25 2015
New Revision: 288570
URL: https://svnweb.freebsd.org/changeset/base/288570

Log:
  MFC r286689: 5981 Deadlock in dmu_objset_find_dp
  
  illumos/illumos-gate at 1d3f896f5469c69c1339890ec3d68e9feddb0343
  
  https://www.illumos.org/issues/5981
    When dmu_objset_find_dp gets called with a read lock held, it fans out
    the work to the task queue. Each task in turn acquires its own read
    lock before calling the callback. If during this process anyone tries
    to a acquire a write lock, it will stall all read lock requests.Thus
    the tasks will never finish, the read lock of the caller will never
    get freed and the write lock never acquired.  deadlock.
  
  Reviewed by: Matthew Ahrens <mahrens at delphix.com>
  Reviewed by: Dan McDonald <danmcd at omniti.com>
  Approved by: Robert Mustacchi <rm at joyent.com>
  Author: Arne Jansen <jansen at webgods.de>

Modified:
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_pool.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/rrwlock.c
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_pool.h
  stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/rrwlock.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c
==============================================================================
--- stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c	Sat Oct  3 07:58:28 2015	(r288569)
+++ stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c	Sat Oct  3 07:59:25 2015	(r288570)
@@ -1746,7 +1746,15 @@ dmu_objset_find_dp_cb(void *arg)
 	dmu_objset_find_ctx_t *dcp = arg;
 	dsl_pool_t *dp = dcp->dc_dp;
 
-	dsl_pool_config_enter(dp, FTAG);
+	/*
+	 * We need to get a pool_config_lock here, as there are several
+	 * asssert(pool_config_held) down the stack. Getting a lock via
+	 * dsl_pool_config_enter is risky, as it might be stalled by a
+	 * pending writer. This would deadlock, as the write lock can
+	 * only be granted when our parent thread gives up the lock.
+	 * The _prio interface gives us priority over a pending writer.
+	 */
+	dsl_pool_config_enter_prio(dp, FTAG);
 
 	dmu_objset_find_dp_impl(dcp);
 

Modified: stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_pool.c
==============================================================================
--- stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_pool.c	Sat Oct  3 07:58:28 2015	(r288569)
+++ stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_pool.c	Sat Oct  3 07:59:25 2015	(r288570)
@@ -1143,6 +1143,13 @@ dsl_pool_config_enter(dsl_pool_t *dp, vo
 }
 
 void
+dsl_pool_config_enter_prio(dsl_pool_t *dp, void *tag)
+{
+	ASSERT(!rrw_held(&dp->dp_config_rwlock, RW_READER));
+	rrw_enter_read_prio(&dp->dp_config_rwlock, tag);
+}
+
+void
 dsl_pool_config_exit(dsl_pool_t *dp, void *tag)
 {
 	rrw_exit(&dp->dp_config_rwlock, tag);

Modified: stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/rrwlock.c
==============================================================================
--- stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/rrwlock.c	Sat Oct  3 07:58:28 2015	(r288569)
+++ stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/rrwlock.c	Sat Oct  3 07:59:25 2015	(r288570)
@@ -159,8 +159,8 @@ rrw_destroy(rrwlock_t *rrl)
 	refcount_destroy(&rrl->rr_linked_rcount);
 }
 
-void
-rrw_enter_read(rrwlock_t *rrl, void *tag)
+static void
+rrw_enter_read_impl(rrwlock_t *rrl, boolean_t prio, void *tag)
 {
 	mutex_enter(&rrl->rr_lock);
 #if !defined(DEBUG) && defined(_KERNEL)
@@ -176,7 +176,7 @@ rrw_enter_read(rrwlock_t *rrl, void *tag
 	ASSERT(refcount_count(&rrl->rr_anon_rcount) >= 0);
 
 	while (rrl->rr_writer != NULL || (rrl->rr_writer_wanted &&
-	    refcount_is_zero(&rrl->rr_anon_rcount) &&
+	    refcount_is_zero(&rrl->rr_anon_rcount) && !prio &&
 	    rrn_find(rrl) == NULL))
 		cv_wait(&rrl->rr_cv, &rrl->rr_lock);
 
@@ -192,6 +192,25 @@ rrw_enter_read(rrwlock_t *rrl, void *tag
 }
 
 void
+rrw_enter_read(rrwlock_t *rrl, void *tag)
+{
+	rrw_enter_read_impl(rrl, B_FALSE, tag);
+}
+
+/*
+ * take a read lock even if there are pending write lock requests. if we want
+ * to take a lock reentrantly, but from different threads (that have a
+ * relationship to each other), the normal detection mechanism to overrule
+ * the pending writer does not work, so we have to give an explicit hint here.
+ */
+void
+rrw_enter_read_prio(rrwlock_t *rrl, void *tag)
+{
+	rrw_enter_read_impl(rrl, B_TRUE, tag);
+}
+
+
+void
 rrw_enter_write(rrwlock_t *rrl)
 {
 	mutex_enter(&rrl->rr_lock);

Modified: stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_pool.h
==============================================================================
--- stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_pool.h	Sat Oct  3 07:58:28 2015	(r288569)
+++ stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dsl_pool.h	Sat Oct  3 07:59:25 2015	(r288570)
@@ -152,6 +152,7 @@ void dsl_pool_upgrade_dir_clones(dsl_poo
 void dsl_pool_mos_diduse_space(dsl_pool_t *dp,
     int64_t used, int64_t comp, int64_t uncomp);
 void dsl_pool_config_enter(dsl_pool_t *dp, void *tag);
+void dsl_pool_config_enter_prio(dsl_pool_t *dp, void *tag);
 void dsl_pool_config_exit(dsl_pool_t *dp, void *tag);
 boolean_t dsl_pool_config_held(dsl_pool_t *dp);
 boolean_t dsl_pool_config_held_writer(dsl_pool_t *dp);

Modified: stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/rrwlock.h
==============================================================================
--- stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/rrwlock.h	Sat Oct  3 07:58:28 2015	(r288569)
+++ stable/10/sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/rrwlock.h	Sat Oct  3 07:59:25 2015	(r288570)
@@ -69,6 +69,7 @@ void rrw_init(rrwlock_t *rrl, boolean_t 
 void rrw_destroy(rrwlock_t *rrl);
 void rrw_enter(rrwlock_t *rrl, krw_t rw, void *tag);
 void rrw_enter_read(rrwlock_t *rrl, void *tag);
+void rrw_enter_read_prio(rrwlock_t *rrl, void *tag);
 void rrw_enter_write(rrwlock_t *rrl);
 void rrw_exit(rrwlock_t *rrl, void *tag);
 boolean_t rrw_held(rrwlock_t *rrl, krw_t rw);


More information about the svn-src-stable-10 mailing list