static int drbd_nl_disconnect(struct drbd_conf *mdev, struct drbd_nl_cfg_req *nlp,
struct drbd_nl_cfg_reply *reply)
{
+ struct drbd_tconn *tconn = mdev->tconn;
int retcode;
struct disconnect dc;
}
if (dc.force) {
- spin_lock_irq(&mdev->tconn->req_lock);
- if (mdev->state.conn >= C_WF_CONNECTION)
- _drbd_set_state(_NS(mdev, conn, C_DISCONNECTING), CS_HARD, NULL);
- spin_unlock_irq(&mdev->tconn->req_lock);
+ spin_lock_irq(&tconn->req_lock);
+ if (tconn->cstate >= C_WF_CONNECTION)
+ _conn_request_state(tconn, NS(conn, C_DISCONNECTING), CS_HARD);
+ spin_unlock_irq(&tconn->req_lock);
goto done;
}
- retcode = _drbd_request_state(mdev, NS(conn, C_DISCONNECTING), CS_ORDERED);
+ retcode = conn_request_state(tconn, NS(conn, C_DISCONNECTING), 0);
if (retcode == SS_NOTHING_TO_DO)
goto done;
else if (retcode == SS_ALREADY_STANDALONE)
goto done;
else if (retcode == SS_PRIMARY_NOP) {
- /* Our statche checking code wants to see the peer outdated. */
- retcode = drbd_request_state(mdev, NS2(conn, C_DISCONNECTING,
- pdsk, D_OUTDATED));
+ /* Our state checking code wants to see the peer outdated. */
+ retcode = conn_request_state(tconn, NS2(conn, C_DISCONNECTING,
+ pdsk, D_OUTDATED), CS_VERBOSE);
} else if (retcode == SS_CW_FAILED_BY_PEER) {
/* The peer probably wants to see us outdated. */
- retcode = _drbd_request_state(mdev, NS2(conn, C_DISCONNECTING,
- disk, D_OUTDATED),
- CS_ORDERED);
+ retcode = conn_request_state(tconn, NS2(conn, C_DISCONNECTING,
+ disk, D_OUTDATED), 0);
if (retcode == SS_IS_DISKLESS || retcode == SS_LOWER_THAN_OUTDATED) {
- drbd_force_state(mdev, NS(conn, C_DISCONNECTING));
+ conn_request_state(tconn, NS(conn, C_DISCONNECTING), CS_HARD);
retcode = SS_SUCCESS;
}
}
if (retcode < SS_SUCCESS)
goto fail;
- if (wait_event_interruptible(mdev->state_wait,
- mdev->state.conn != C_DISCONNECTING)) {
+ if (wait_event_interruptible(tconn->ping_wait,
+ tconn->cstate != C_DISCONNECTING)) {
/* Do not test for mdev->state.conn == C_STANDALONE, since
someone else might connect us in the mean time! */
retcode = ERR_INTR;
return 0;
}
+static enum drbd_state_rv
+_conn_rq_cond(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state val)
+{
+ struct _is_valid_itr_params params;
+ enum drbd_state_rv rv;
+
+ if (test_and_clear_bit(CONN_WD_ST_CHG_OKAY, &tconn->flags))
+ return SS_CW_SUCCESS;
+
+ if (test_and_clear_bit(CONN_WD_ST_CHG_FAIL, &tconn->flags))
+ return SS_CW_FAILED_BY_PEER;
+
+ params.flags = CS_NO_CSTATE_CHG; /* ΓΆΓΆ think */
+ params.mask = mask;
+ params.val = val;
+
+ 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)
+ rv = idr_for_each(&tconn->volumes, _is_valid_itr_fn, ¶ms);
+
+ if (rv == 0) /* idr_for_each semantics */
+ rv = SS_UNKNOWN_ERROR; /* cont waiting, otherwise fail. */
+
+ spin_unlock_irq(&tconn->req_lock);
+
+ return rv;
+}
+
+static enum drbd_state_rv
+conn_cl_wide(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state val,
+ enum chg_state_flags f)
+{
+ enum drbd_state_rv rv;
+
+ spin_unlock_irq(&tconn->req_lock);
+ mutex_lock(&tconn->cstate_mutex);
+
+ if (!conn_send_state_req(tconn, mask, val)) {
+ rv = SS_CW_FAILED_BY_PEER;
+ /* if (f & CS_VERBOSE)
+ print_st_err(mdev, os, ns, rv); */
+ goto abort;
+ }
+
+ wait_event(tconn->ping_wait, (rv = _conn_rq_cond(tconn, mask, val)));
+
+abort:
+ mutex_unlock(&tconn->cstate_mutex);
+ spin_lock_irq(&tconn->req_lock);
+
+ return rv;
+}
+
enum drbd_state_rv
_conn_request_state(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state val,
enum chg_state_flags flags)
if (rv < SS_SUCCESS)
goto abort;
+ if (oc == C_WF_REPORT_PARAMS && val.conn == C_DISCONNECTING &&
+ !(flags & (CS_LOCAL_ONLY | CS_HARD))) {
+ rv = conn_cl_wide(tconn, mask, val, flags);
+ if (rv < SS_SUCCESS)
+ goto abort;
+ }
+
if (params.oc_state == OC_CONSISTENT) {
oc = params.oc;
print_conn_state_change(tconn, oc, val.conn);