From 5e8cd5d17f21200beb5f6fae8e8be64c0491195d Mon Sep 17 00:00:00 2001 From: Arne Jansen Date: Thu, 2 Jul 2015 17:58:17 +0200 Subject: [PATCH] Illumos 5981 - Deadlock in dmu_objset_find_dp 5981 Deadlock in dmu_objset_find_dp Reviewed by: Matthew Ahrens Reviewed by: Dan McDonald Approved by: Robert Mustacchi 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 Closes #3553 --- include/sys/dsl_pool.h | 1 + include/sys/rrwlock.h | 1 + module/zfs/dmu_objset.c | 10 +++++++++- module/zfs/dsl_pool.c | 7 +++++++ module/zfs/rrwlock.c | 25 ++++++++++++++++++++++--- 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/include/sys/dsl_pool.h b/include/sys/dsl_pool.h index b2b9128e5..48b12e8eb 100644 --- a/include/sys/dsl_pool.h +++ b/include/sys/dsl_pool.h @@ -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); diff --git a/include/sys/rrwlock.h b/include/sys/rrwlock.h index 25c8a5246..d2bdff495 100644 --- a/include/sys/rrwlock.h +++ b/include/sys/rrwlock.h @@ -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); diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index bc1aa1286..823a15677 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -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); diff --git a/module/zfs/dsl_pool.c b/module/zfs/dsl_pool.c index 5d804352d..23cf43862 100644 --- a/module/zfs/dsl_pool.c +++ b/module/zfs/dsl_pool.c @@ -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) { diff --git a/module/zfs/rrwlock.c b/module/zfs/rrwlock.c index 8e80166c7..29a22534e 100644 --- a/module/zfs/rrwlock.c +++ b/module/zfs/rrwlock.c @@ -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) {