]> git.proxmox.com Git - mirror_zfs.git/commitdiff
Illumos 5981 - Deadlock in dmu_objset_find_dp
authorArne Jansen <jansen@webgods.de>
Thu, 2 Jul 2015 15:58:17 +0000 (17:58 +0200)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 6 Jul 2015 16:31:35 +0000 (09:31 -0700)
5981 Deadlock in dmu_objset_find_dp
Reviewed by: Matthew Ahrens <mahrens@delphix.com>
Reviewed by: Dan McDonald <danmcd@omniti.com>
Approved by: Robert Mustacchi <rm@joyent.com>

References:
  https://www.illumos.org/issues/5981
  https://github.com/illumos/illumos-gate/commit/1d3f896

Ported-by: kernelOfTruth kerneloftruth@gmail.com
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #3553

include/sys/dsl_pool.h
include/sys/rrwlock.h
module/zfs/dmu_objset.c
module/zfs/dsl_pool.c
module/zfs/rrwlock.c

index b2b9128e5274e6a474960ed026359ad7f903b8d5..48b12e8eb1346236288f71cf198f6c45023456c7 100644 (file)
@@ -156,6 +156,7 @@ void dsl_pool_mos_diduse_space(dsl_pool_t *dp,
     int64_t used, int64_t comp, int64_t uncomp);
 boolean_t dsl_pool_need_dirty_delay(dsl_pool_t *dp);
 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);
index 25c8a52467e724284f2271e49b9945a00bd73378..d2bdff495cbf6692b4070eff46816318cb4ebf59 100644 (file)
@@ -72,6 +72,7 @@ void rrw_init(rrwlock_t *rrl, boolean_t track_all);
 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);
index bc1aa12867a29291a64e242b958494170f8eadce..823a15677713e083e4c923db8eec4ee02f4bb23e 100644 (file)
@@ -1784,7 +1784,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);
 
index 5d804352ddfc418a3854903723e3eac09775ae34..23cf43862835a48188675d682c40d7c5c3d86c5d 100644 (file)
@@ -1050,6 +1050,13 @@ dsl_pool_config_enter(dsl_pool_t *dp, void *tag)
        rrw_enter(&dp->dp_config_rwlock, RW_READER, tag);
 }
 
+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)
 {
index 8e80166c7d14daae2f9e16528cd6f2f948022133..29a22534e6005bee658d720ebf2a1a5c14ae5493 100644 (file)
@@ -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);
 
@@ -191,6 +191,25 @@ rrw_enter_read(rrwlock_t *rrl, void *tag)
        mutex_exit(&rrl->rr_lock);
 }
 
+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)
 {