diff options
author | Philipp Reisner <philipp.reisner@linbit.com> | 2012-08-21 14:34:07 -0400 |
---|---|---|
committer | Philipp Reisner <philipp.reisner@linbit.com> | 2012-11-09 08:08:20 -0500 |
commit | c1fd29a11f433ca8ae37723768016ffe6cdd487b (patch) | |
tree | d297c9ee730f292a2a789f9bbeceffde075afc26 /drivers/block/drbd | |
parent | 0ee98e2eb0c85f27b6f24a15d59fb54f99a93840 (diff) |
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')
-rw-r--r-- | drivers/block/drbd/drbd_int.h | 27 | ||||
-rw-r--r-- | drivers/block/drbd/drbd_state.c | 14 |
2 files changed, 33 insertions, 8 deletions
diff --git a/drivers/block/drbd/drbd_int.h b/drivers/block/drbd/drbd_int.h index 37ae87e468ae..1c1576b942b6 100644 --- a/drivers/block/drbd/drbd_int.h +++ b/drivers/block/drbd/drbd_int.h | |||
@@ -2301,3 +2301,30 @@ static inline void drbd_md_flush(struct drbd_conf *mdev) | |||
2301 | } | 2301 | } |
2302 | 2302 | ||
2303 | #endif | 2303 | #endif |
2304 | |||
2305 | /* This is defined in drivers/md/md.h as well. Should go into wait.h */ | ||
2306 | #define __wait_event_lock_irq(wq, condition, lock, cmd) \ | ||
2307 | do { \ | ||
2308 | wait_queue_t __wait; \ | ||
2309 | init_waitqueue_entry(&__wait, current); \ | ||
2310 | \ | ||
2311 | add_wait_queue(&wq, &__wait); \ | ||
2312 | for (;;) { \ | ||
2313 | set_current_state(TASK_UNINTERRUPTIBLE); \ | ||
2314 | if (condition) \ | ||
2315 | break; \ | ||
2316 | spin_unlock_irq(&lock); \ | ||
2317 | cmd; \ | ||
2318 | schedule(); \ | ||
2319 | spin_lock_irq(&lock); \ | ||
2320 | } \ | ||
2321 | current->state = TASK_RUNNING; \ | ||
2322 | remove_wait_queue(&wq, &__wait); \ | ||
2323 | } while (0) | ||
2324 | |||
2325 | #define wait_event_lock_irq(wq, condition, lock, cmd) \ | ||
2326 | do { \ | ||
2327 | if (condition) \ | ||
2328 | break; \ | ||
2329 | __wait_event_lock_irq(wq, condition, lock, cmd); \ | ||
2330 | } while (0) | ||
diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c index 4fda4e2024ec..ce1495187f02 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 | |||
1710 | if (test_and_clear_bit(CONN_WD_ST_CHG_FAIL, &tconn->flags)) | 1710 | if (test_and_clear_bit(CONN_WD_ST_CHG_FAIL, &tconn->flags)) |
1711 | return SS_CW_FAILED_BY_PEER; | 1711 | return SS_CW_FAILED_BY_PEER; |
1712 | 1712 | ||
1713 | spin_lock_irq(&tconn->req_lock); | ||
1714 | rv = tconn->cstate != C_WF_REPORT_PARAMS ? SS_CW_NO_NEED : SS_UNKNOWN_ERROR; | 1713 | rv = tconn->cstate != C_WF_REPORT_PARAMS ? SS_CW_NO_NEED : SS_UNKNOWN_ERROR; |
1715 | 1714 | ||
1716 | if (rv == SS_UNKNOWN_ERROR) | 1715 | if (rv == SS_UNKNOWN_ERROR) |
@@ -1719,8 +1718,6 @@ _conn_rq_cond(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state | |||
1719 | if (rv == SS_SUCCESS) | 1718 | if (rv == SS_SUCCESS) |
1720 | rv = SS_UNKNOWN_ERROR; /* cont waiting, otherwise fail. */ | 1719 | rv = SS_UNKNOWN_ERROR; /* cont waiting, otherwise fail. */ |
1721 | 1720 | ||
1722 | spin_unlock_irq(&tconn->req_lock); | ||
1723 | |||
1724 | return rv; | 1721 | return rv; |
1725 | } | 1722 | } |
1726 | 1723 | ||
@@ -1736,21 +1733,22 @@ conn_cl_wide(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state v | |||
1736 | set_bit(CONN_WD_ST_CHG_REQ, &tconn->flags); | 1733 | set_bit(CONN_WD_ST_CHG_REQ, &tconn->flags); |
1737 | if (conn_send_state_req(tconn, mask, val)) { | 1734 | if (conn_send_state_req(tconn, mask, val)) { |
1738 | clear_bit(CONN_WD_ST_CHG_REQ, &tconn->flags); | 1735 | clear_bit(CONN_WD_ST_CHG_REQ, &tconn->flags); |
1739 | rv = SS_CW_FAILED_BY_PEER; | ||
1740 | /* if (f & CS_VERBOSE) | 1736 | /* if (f & CS_VERBOSE) |
1741 | print_st_err(mdev, os, ns, rv); */ | 1737 | print_st_err(mdev, os, ns, rv); */ |
1742 | goto abort; | 1738 | mutex_unlock(&tconn->cstate_mutex); |
1739 | spin_lock_irq(&tconn->req_lock); | ||
1740 | return SS_CW_FAILED_BY_PEER; | ||
1743 | } | 1741 | } |
1744 | 1742 | ||
1745 | if (val.conn == C_DISCONNECTING) | 1743 | if (val.conn == C_DISCONNECTING) |
1746 | set_bit(DISCONNECT_SENT, &tconn->flags); | 1744 | set_bit(DISCONNECT_SENT, &tconn->flags); |
1747 | 1745 | ||
1748 | wait_event(tconn->ping_wait, (rv = _conn_rq_cond(tconn, mask, val))); | 1746 | spin_lock_irq(&tconn->req_lock); |
1747 | |||
1748 | wait_event_lock_irq(tconn->ping_wait, (rv = _conn_rq_cond(tconn, mask, val)), tconn->req_lock,); | ||
1749 | clear_bit(CONN_WD_ST_CHG_REQ, &tconn->flags); | 1749 | clear_bit(CONN_WD_ST_CHG_REQ, &tconn->flags); |
1750 | 1750 | ||
1751 | abort: | ||
1752 | mutex_unlock(&tconn->cstate_mutex); | 1751 | mutex_unlock(&tconn->cstate_mutex); |
1753 | spin_lock_irq(&tconn->req_lock); | ||
1754 | 1752 | ||
1755 | return rv; | 1753 | return rv; |
1756 | } | 1754 | } |