aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/block/drbd
diff options
context:
space:
mode:
authorPhilipp Reisner <philipp.reisner@linbit.com>2012-08-21 14:34:07 -0400
committerPhilipp Reisner <philipp.reisner@linbit.com>2012-11-09 08:08:20 -0500
commitc1fd29a11f433ca8ae37723768016ffe6cdd487b (patch)
treed297c9ee730f292a2a789f9bbeceffde075afc26 /drivers/block/drbd
parent0ee98e2eb0c85f27b6f24a15d59fb54f99a93840 (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.h27
-rw-r--r--drivers/block/drbd/drbd_state.c14
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) \
2307do { \
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) \
2326do { \
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
1751abort:
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}