drbd: fix race between role change and handshake
Symptoms: If DRBD was "cleanly shut down" (all in sync, both Secondary before disconnect, identical data generation uuids), and then one side was promoted *during* the next connection handshake, the role change could confuse the handshake. The Primary would get stuck in WFBitmapS, the Secondary would log unexpected cstate (Connected) in receive_bitmap and get stuck in WFBitmapT. Fix: The test in is_valid_soft_transition wrong. It works because the not allowed actions (promote/attach) do not touch the cstate. The previous condition failed to demand a cstate change in one clause. In order to avoid deadlocks give up the state_mutex while waiting for the transient state to go away. Conflicts: drbd/drbd_state.c drbd/drbd_state.h drbd/drbd_wrappers.h Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com> Signed-off-by: Lars Ellenberg <lars.ellenberg@linbit.com> Signed-off-by: Jens Axboe <axboe@fb.com>
This commit is contained in:
Родитель
f221f4bcc5
Коммит
a88215312c
|
@ -588,7 +588,7 @@ drbd_set_role(struct drbd_device *const device, enum drbd_role new_role, int for
|
|||
val.i = 0; val.role = new_role;
|
||||
|
||||
while (try++ < max_tries) {
|
||||
rv = _drbd_request_state(device, mask, val, CS_WAIT_COMPLETE);
|
||||
rv = _drbd_request_state_holding_state_mutex(device, mask, val, CS_WAIT_COMPLETE);
|
||||
|
||||
/* in case we first succeeded to outdate,
|
||||
* but now suddenly could establish a connection */
|
||||
|
|
|
@ -215,6 +215,18 @@ static bool no_peer_wf_report_params(struct drbd_connection *connection)
|
|||
return rv;
|
||||
}
|
||||
|
||||
static void wake_up_all_devices(struct drbd_connection *connection)
|
||||
{
|
||||
struct drbd_peer_device *peer_device;
|
||||
int vnr;
|
||||
|
||||
rcu_read_lock();
|
||||
idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
|
||||
wake_up(&peer_device->device->state_wait);
|
||||
rcu_read_unlock();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* cl_wide_st_chg() - true if the state change is a cluster wide one
|
||||
|
@ -410,6 +422,22 @@ _drbd_request_state(struct drbd_device *device, union drbd_state mask,
|
|||
return rv;
|
||||
}
|
||||
|
||||
enum drbd_state_rv
|
||||
_drbd_request_state_holding_state_mutex(struct drbd_device *device, union drbd_state mask,
|
||||
union drbd_state val, enum chg_state_flags f)
|
||||
{
|
||||
enum drbd_state_rv rv;
|
||||
|
||||
BUG_ON(f & CS_SERIALIZE);
|
||||
|
||||
wait_event_cmd(device->state_wait,
|
||||
(rv = drbd_req_state(device, mask, val, f)) != SS_IN_TRANSIENT_STATE,
|
||||
mutex_unlock(device->state_mutex),
|
||||
mutex_lock(device->state_mutex));
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static void print_st(struct drbd_device *device, const char *name, union drbd_state ns)
|
||||
{
|
||||
drbd_err(device, " %s = { cs:%s ro:%s/%s ds:%s/%s %c%c%c%c%c%c }\n",
|
||||
|
@ -629,14 +657,11 @@ is_valid_soft_transition(union drbd_state os, union drbd_state ns, struct drbd_c
|
|||
if (ns.conn == C_DISCONNECTING && os.conn == C_UNCONNECTED)
|
||||
rv = SS_IN_TRANSIENT_STATE;
|
||||
|
||||
/* if (ns.conn == os.conn && ns.conn == C_WF_REPORT_PARAMS)
|
||||
rv = SS_IN_TRANSIENT_STATE; */
|
||||
|
||||
/* While establishing a connection only allow cstate to change.
|
||||
Delay/refuse role changes, detach attach etc... */
|
||||
Delay/refuse role changes, detach attach etc... (they do not touch cstate) */
|
||||
if (test_bit(STATE_SENT, &connection->flags) &&
|
||||
!(os.conn == C_WF_REPORT_PARAMS ||
|
||||
(ns.conn == C_WF_REPORT_PARAMS && os.conn == C_WF_CONNECTION)))
|
||||
!((ns.conn == C_WF_REPORT_PARAMS && os.conn == C_WF_CONNECTION) ||
|
||||
(ns.conn >= C_CONNECTED && os.conn == C_WF_REPORT_PARAMS)))
|
||||
rv = SS_IN_TRANSIENT_STATE;
|
||||
|
||||
if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && os.conn < C_CONNECTED)
|
||||
|
@ -1032,8 +1057,10 @@ __drbd_set_state(struct drbd_device *device, union drbd_state ns,
|
|||
|
||||
/* Wake up role changes, that were delayed because of connection establishing */
|
||||
if (os.conn == C_WF_REPORT_PARAMS && ns.conn != C_WF_REPORT_PARAMS &&
|
||||
no_peer_wf_report_params(connection))
|
||||
no_peer_wf_report_params(connection)) {
|
||||
clear_bit(STATE_SENT, &connection->flags);
|
||||
wake_up_all_devices(connection);
|
||||
}
|
||||
|
||||
wake_up(&device->misc_wait);
|
||||
wake_up(&device->state_wait);
|
||||
|
|
|
@ -117,6 +117,11 @@ extern enum drbd_state_rv _drbd_request_state(struct drbd_device *,
|
|||
union drbd_state,
|
||||
union drbd_state,
|
||||
enum chg_state_flags);
|
||||
|
||||
extern enum drbd_state_rv
|
||||
_drbd_request_state_holding_state_mutex(struct drbd_device *, union drbd_state,
|
||||
union drbd_state, enum chg_state_flags);
|
||||
|
||||
extern enum drbd_state_rv __drbd_set_state(struct drbd_device *, union drbd_state,
|
||||
enum chg_state_flags,
|
||||
struct completion *done);
|
||||
|
|
Загрузка…
Ссылка в новой задаче