Skip to content

Commit

Permalink
Illumos 5981 - Deadlock in dmu_objset_find_dp
Browse files Browse the repository at this point in the history
5981 Deadlock in dmu_objset_find_dp
Reviewed by: Matthew Ahrens <[email protected]>
Reviewed by: Dan McDonald <[email protected]>
Approved by: Robert Mustacchi <[email protected]>

References:
  https://www.illumos.org/issues/5981
  illumos/illumos-gate@1d3f896

Ported-by: kernelOfTruth [email protected]
Signed-off-by: Brian Behlendorf <[email protected]>
Closes #3553
  • Loading branch information
Arne Jansen authored and behlendorf committed Jul 6, 2015
1 parent 71e2fe4 commit 5e8cd5d
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 4 deletions.
1 change: 1 addition & 0 deletions include/sys/dsl_pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions include/sys/rrwlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
10 changes: 9 additions & 1 deletion module/zfs/dmu_objset.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
7 changes: 7 additions & 0 deletions module/zfs/dsl_pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
25 changes: 22 additions & 3 deletions module/zfs/rrwlock.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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);

Expand All @@ -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)
{
Expand Down

0 comments on commit 5e8cd5d

Please sign in to comment.