summaryrefslogtreecommitdiff
path: root/drivers/block/drbd/drbd_state.c
diff options
context:
space:
mode:
authorPhilipp Reisner <philipp.reisner@linbit.com>2012-08-21 18:34:07 (GMT)
committerPhilipp Reisner <philipp.reisner@linbit.com>2012-11-09 13:08:20 (GMT)
commitc1fd29a11f433ca8ae37723768016ffe6cdd487b (patch)
treed297c9ee730f292a2a789f9bbeceffde075afc26 /drivers/block/drbd/drbd_state.c
parent0ee98e2eb0c85f27b6f24a15d59fb54f99a93840 (diff)
downloadlinux-c1fd29a11f433ca8ae37723768016ffe6cdd487b.tar.xz
drbd: Fix a race condition that can lead to a BUG()
If the preconditions for a state change change after the wait_event() we might hit the BUG() statement in conn_set_state(). With holding the spin_lock while evaluating the condition AND until the actual state change we ensure the the preconditions can not change anymore. Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com>
Diffstat (limited to 'drivers/block/drbd/drbd_state.c')
-rw-r--r--drivers/block/drbd/drbd_state.c14
1 files changed, 6 insertions, 8 deletions
diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c
index 4fda4e2..ce14951 100644
--- a/drivers/block/drbd/drbd_state.c
+++ b/drivers/block/drbd/drbd_state.c
@@ -1710,7 +1710,6 @@ _conn_rq_cond(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state
if (test_and_clear_bit(CONN_WD_ST_CHG_FAIL, &tconn->flags))
return SS_CW_FAILED_BY_PEER;
- spin_lock_irq(&tconn->req_lock);
rv = tconn->cstate != C_WF_REPORT_PARAMS ? SS_CW_NO_NEED : SS_UNKNOWN_ERROR;
if (rv == SS_UNKNOWN_ERROR)
@@ -1719,8 +1718,6 @@ _conn_rq_cond(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state
if (rv == SS_SUCCESS)
rv = SS_UNKNOWN_ERROR; /* cont waiting, otherwise fail. */
- spin_unlock_irq(&tconn->req_lock);
-
return rv;
}
@@ -1736,21 +1733,22 @@ conn_cl_wide(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state v
set_bit(CONN_WD_ST_CHG_REQ, &tconn->flags);
if (conn_send_state_req(tconn, mask, val)) {
clear_bit(CONN_WD_ST_CHG_REQ, &tconn->flags);
- rv = SS_CW_FAILED_BY_PEER;
/* if (f & CS_VERBOSE)
print_st_err(mdev, os, ns, rv); */
- goto abort;
+ mutex_unlock(&tconn->cstate_mutex);
+ spin_lock_irq(&tconn->req_lock);
+ return SS_CW_FAILED_BY_PEER;
}
if (val.conn == C_DISCONNECTING)
set_bit(DISCONNECT_SENT, &tconn->flags);
- wait_event(tconn->ping_wait, (rv = _conn_rq_cond(tconn, mask, val)));
+ spin_lock_irq(&tconn->req_lock);
+
+ wait_event_lock_irq(tconn->ping_wait, (rv = _conn_rq_cond(tconn, mask, val)), tconn->req_lock,);
clear_bit(CONN_WD_ST_CHG_REQ, &tconn->flags);
-abort:
mutex_unlock(&tconn->cstate_mutex);
- spin_lock_irq(&tconn->req_lock);
return rv;
}