From 88ef594ed7367a76e53f4a33b3d866f4dfb1d4ff Mon Sep 17 00:00:00 2001 From: Philipp Reisner Date: Fri, 25 Mar 2011 14:31:11 +0100 Subject: [PATCH] drbd: Fixed logging of old connection state During a disconnect the oc variable in _conn_request_state() could become outdated. Determin the common old state after sleeping. While at it, I implemented that for all parts of the state Signed-off-by: Philipp Reisner Signed-off-by: Lars Ellenberg --- drivers/block/drbd/drbd_state.c | 117 ++++++++++++++++++++++++-------- 1 file changed, 87 insertions(+), 30 deletions(-) diff --git a/drivers/block/drbd/drbd_state.c b/drivers/block/drbd/drbd_state.c index 85cfbeadd513..2a170bf7cc29 100644 --- a/drivers/block/drbd/drbd_state.c +++ b/drivers/block/drbd/drbd_state.c @@ -1377,42 +1377,100 @@ static void print_conn_state_change(struct drbd_tconn *tconn, enum drbd_conns oc conn_info(tconn, "%s\n", pb); } -struct _is_valid_itr_params { - enum drbd_conns oc; - enum { - OC_UNINITIALIZED, - OC_CONSISTENT, - OC_INCONSISTENT, - } oc_state; -}; +enum sp_state { + OC_UNINITIALIZED, + OC_CONSISTENT, + OC_INCONSISTENT, +} oc_state; + +static void common_state_part(enum sp_state *sps, int *sp, int nsp) +{ + switch (*sps) { + case OC_UNINITIALIZED: + *sp = nsp; + *sps = OC_CONSISTENT; + break; + case OC_CONSISTENT: + if (*sp != nsp) + *sps = OC_INCONSISTENT; + break; + case OC_INCONSISTENT: + break; + } +} + +void conn_old_common_state(struct drbd_tconn *tconn, union drbd_state *pcs, union drbd_state *pmask) +{ + union drbd_state css = {}; /* common state state */ + union drbd_state os, cs = {}; /* old_state, common_state */ + union drbd_state mask = {}; + enum sp_state sps; /* state part state */ + int sp; /* state part */ + struct drbd_conf *mdev; + int vnr; + + idr_for_each_entry(&tconn->volumes, mdev, vnr) { + os = mdev->state; + + sps = css.role; + sp = cs.role; + common_state_part(&sps, &sp, os.role); + css.role = sps; + cs.role = sp; + + sps = css.peer; + sp = cs.peer; + common_state_part(&sps, &sp, os.peer); + css.peer = sps; + cs.peer = sp; + + sps = css.conn; + sp = cs.conn; + common_state_part(&sps, &sp, os.conn); + css.conn = sps; + cs.conn = sp; + + sps = css.disk; + sp = cs.disk; + common_state_part(&sps, &sp, os.disk); + css.disk = sps; + cs.disk = sp; + + sps = css.pdsk; + sp = cs.pdsk; + common_state_part(&sps, &sp, os.pdsk); + css.pdsk = sps; + cs.pdsk = sp; + } + + if (css.role == OC_CONSISTENT) + mask.role = R_MASK; + if (css.peer == OC_CONSISTENT) + mask.peer = R_MASK; + if (css.conn == OC_CONSISTENT) + mask.conn = C_MASK; + if (css.disk == OC_CONSISTENT) + mask.disk = D_MASK; + if (css.pdsk == OC_CONSISTENT) + mask.pdsk = D_MASK; + + *pcs = cs; + *pmask = mask; +} static enum drbd_state_rv conn_is_valid_transition(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state val, - enum chg_state_flags flags, struct _is_valid_itr_params *params) + enum chg_state_flags flags) { enum drbd_state_rv rv = SS_SUCCESS; union drbd_state ns, os; struct drbd_conf *mdev; int vnr; - params->oc_state = OC_UNINITIALIZED; idr_for_each_entry(&tconn->volumes, mdev, vnr) { os = mdev->state; ns = sanitize_state(mdev, apply_mask_val(os, mask, val), NULL); - switch (params->oc_state) { - case OC_UNINITIALIZED: - params->oc = os.conn; - params->oc_state = OC_CONSISTENT; - break; - case OC_CONSISTENT: - if (params->oc != os.conn) - params->oc_state = OC_INCONSISTENT; - break; - case OC_INCONSISTENT: - break; - } - if (ns.i == os.i) continue; @@ -1471,7 +1529,6 @@ conn_set_state(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state 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)) @@ -1484,7 +1541,7 @@ _conn_rq_cond(struct drbd_tconn *tconn, union drbd_state mask, union drbd_state rv = tconn->cstate != C_WF_REPORT_PARAMS ? SS_CW_NO_NEED : SS_UNKNOWN_ERROR; if (rv == SS_UNKNOWN_ERROR) - rv = conn_is_valid_transition(tconn, mask, val, CS_NO_CSTATE_CHG, ¶ms); + rv = conn_is_valid_transition(tconn, mask, val, CS_NO_CSTATE_CHG); if (rv == SS_SUCCESS) rv = SS_UNKNOWN_ERROR; /* cont waiting, otherwise fail. */ @@ -1524,16 +1581,15 @@ _conn_request_state(struct drbd_tconn *tconn, union drbd_state mask, union drbd_ enum chg_state_flags flags) { enum drbd_state_rv rv = SS_SUCCESS; - struct _is_valid_itr_params params; struct after_conn_state_chg_work *acscw; enum drbd_conns oc = tconn->cstate; - union drbd_state ms; + union drbd_state ms, os_val, os_mask; rv = is_valid_conn_transition(oc, val.conn); if (rv < SS_SUCCESS) goto abort; - rv = conn_is_valid_transition(tconn, mask, val, flags, ¶ms); + rv = conn_is_valid_transition(tconn, mask, val, flags); if (rv < SS_SUCCESS) goto abort; @@ -1544,8 +1600,9 @@ _conn_request_state(struct drbd_tconn *tconn, union drbd_state mask, union drbd_ goto abort; } - if (params.oc_state == OC_CONSISTENT) { - oc = params.oc; + conn_old_common_state(tconn, &os_val, &os_mask); + if (os_mask.conn == C_MASK) { + oc = os_val.conn; print_conn_state_change(tconn, oc, val.conn); flags |= CS_NO_CSTATE_CHG; }