Commit cb703454 authored by Philipp Reisner's avatar Philipp Reisner

drbd: Converted drbd_try_outdate_peer() from mdev to tconn

Signed-off-by: default avatarPhilipp Reisner <philipp.reisner@linbit.com>
Signed-off-by: default avatarLars Ellenberg <lars.ellenberg@linbit.com>
parent a02d1240
...@@ -1472,8 +1472,8 @@ extern void drbd_reconsider_max_bio_size(struct drbd_conf *mdev); ...@@ -1472,8 +1472,8 @@ extern void drbd_reconsider_max_bio_size(struct drbd_conf *mdev);
extern enum drbd_state_rv drbd_set_role(struct drbd_conf *mdev, extern enum drbd_state_rv drbd_set_role(struct drbd_conf *mdev,
enum drbd_role new_role, enum drbd_role new_role,
int force); int force);
extern enum drbd_disk_state drbd_try_outdate_peer(struct drbd_conf *mdev); extern bool conn_try_outdate_peer(struct drbd_tconn *tconn);
extern void drbd_try_outdate_peer_async(struct drbd_conf *mdev); extern void conn_try_outdate_peer_async(struct drbd_tconn *tconn);
extern int drbd_khelper(struct drbd_conf *mdev, char *cmd); extern int drbd_khelper(struct drbd_conf *mdev, char *cmd);
/* drbd_worker.c */ /* drbd_worker.c */
......
...@@ -366,116 +366,122 @@ int conn_khelper(struct drbd_tconn *tconn, char *cmd) ...@@ -366,116 +366,122 @@ int conn_khelper(struct drbd_tconn *tconn, char *cmd)
return ret; return ret;
} }
enum drbd_disk_state drbd_try_outdate_peer(struct drbd_conf *mdev) static enum drbd_fencing_p highest_fencing_policy(struct drbd_tconn *tconn)
{ {
enum drbd_fencing_p fp = FP_NOT_AVAIL;
struct drbd_conf *mdev;
int vnr;
idr_for_each_entry(&tconn->volumes, mdev, vnr) {
if (get_ldev_if_state(mdev, D_CONSISTENT)) {
fp = max_t(enum drbd_fencing_p, fp, mdev->ldev->dc.fencing);
put_ldev(mdev);
}
}
return fp;
}
bool conn_try_outdate_peer(struct drbd_tconn *tconn)
{
union drbd_state mask = { };
union drbd_state val = { };
enum drbd_fencing_p fp;
char *ex_to_string; char *ex_to_string;
int r; int r;
enum drbd_disk_state nps;
enum drbd_fencing_p fp;
D_ASSERT(mdev->state.pdsk == D_UNKNOWN); if (tconn->cstate >= C_WF_REPORT_PARAMS) {
conn_err(tconn, "Expected cstate < C_WF_REPORT_PARAMS\n");
return false;
}
if (get_ldev_if_state(mdev, D_CONSISTENT)) { fp = highest_fencing_policy(tconn);
fp = mdev->ldev->dc.fencing; switch (fp) {
put_ldev(mdev); case FP_NOT_AVAIL:
} else { conn_warn(tconn, "Not fencing peer, I'm not even Consistent myself.\n");
dev_warn(DEV, "Not fencing peer, I'm not even Consistent myself.\n");
nps = mdev->state.pdsk;
goto out; goto out;
case FP_DONT_CARE:
return true;
default: ;
} }
r = drbd_khelper(mdev, "fence-peer"); r = conn_khelper(tconn, "fence-peer");
switch ((r>>8) & 0xff) { switch ((r>>8) & 0xff) {
case 3: /* peer is inconsistent */ case 3: /* peer is inconsistent */
ex_to_string = "peer is inconsistent or worse"; ex_to_string = "peer is inconsistent or worse";
nps = D_INCONSISTENT; mask.pdsk = D_MASK;
val.pdsk = D_INCONSISTENT;
break; break;
case 4: /* peer got outdated, or was already outdated */ case 4: /* peer got outdated, or was already outdated */
ex_to_string = "peer was fenced"; ex_to_string = "peer was fenced";
nps = D_OUTDATED; mask.pdsk = D_MASK;
val.pdsk = D_OUTDATED;
break; break;
case 5: /* peer was down */ case 5: /* peer was down */
if (mdev->state.disk == D_UP_TO_DATE) { if (conn_highest_disk(tconn) == D_UP_TO_DATE) {
/* we will(have) create(d) a new UUID anyways... */ /* we will(have) create(d) a new UUID anyways... */
ex_to_string = "peer is unreachable, assumed to be dead"; ex_to_string = "peer is unreachable, assumed to be dead";
nps = D_OUTDATED; mask.pdsk = D_MASK;
val.pdsk = D_OUTDATED;
} else { } else {
ex_to_string = "peer unreachable, doing nothing since disk != UpToDate"; ex_to_string = "peer unreachable, doing nothing since disk != UpToDate";
nps = mdev->state.pdsk;
} }
break; break;
case 6: /* Peer is primary, voluntarily outdate myself. case 6: /* Peer is primary, voluntarily outdate myself.
* This is useful when an unconnected R_SECONDARY is asked to * This is useful when an unconnected R_SECONDARY is asked to
* become R_PRIMARY, but finds the other peer being active. */ * become R_PRIMARY, but finds the other peer being active. */
ex_to_string = "peer is active"; ex_to_string = "peer is active";
dev_warn(DEV, "Peer is primary, outdating myself.\n"); conn_warn(tconn, "Peer is primary, outdating myself.\n");
nps = D_UNKNOWN; mask.disk = D_MASK;
_drbd_request_state(mdev, NS(disk, D_OUTDATED), CS_WAIT_COMPLETE); val.disk = D_OUTDATED;
break; break;
case 7: case 7:
if (fp != FP_STONITH) if (fp != FP_STONITH)
dev_err(DEV, "fence-peer() = 7 && fencing != Stonith !!!\n"); conn_err(tconn, "fence-peer() = 7 && fencing != Stonith !!!\n");
ex_to_string = "peer was stonithed"; ex_to_string = "peer was stonithed";
nps = D_OUTDATED; mask.pdsk = D_MASK;
val.pdsk = D_OUTDATED;
break; break;
default: default:
/* The script is broken ... */ /* The script is broken ... */
nps = D_UNKNOWN; conn_err(tconn, "fence-peer helper broken, returned %d\n", (r>>8)&0xff);
dev_err(DEV, "fence-peer helper broken, returned %d\n", (r>>8)&0xff); return false; /* Eventually leave IO frozen */
return nps;
} }
dev_info(DEV, "fence-peer helper returned %d (%s)\n", conn_info(tconn, "fence-peer helper returned %d (%s)\n",
(r>>8) & 0xff, ex_to_string); (r>>8) & 0xff, ex_to_string);
out: out:
if (mdev->state.susp_fen && nps >= D_UNKNOWN) {
/* The handler was not successful... unfreeze here, the
state engine can not unfreeze... */
_drbd_request_state(mdev, NS(susp_fen, 0), CS_VERBOSE);
}
return nps; /* Not using
conn_request_state(tconn, mask, val, CS_VERBOSE);
here, because we might were able to re-establish the connection in the
meantime. */
spin_lock_irq(&tconn->req_lock);
if (tconn->cstate < C_WF_REPORT_PARAMS)
_conn_request_state(tconn, mask, val, CS_VERBOSE);
spin_unlock_irq(&tconn->req_lock);
return conn_highest_pdsk(tconn) <= D_OUTDATED;
} }
static int _try_outdate_peer_async(void *data) static int _try_outdate_peer_async(void *data)
{ {
struct drbd_conf *mdev = (struct drbd_conf *)data; struct drbd_tconn *tconn = (struct drbd_tconn *)data;
enum drbd_disk_state nps;
union drbd_state ns;
nps = drbd_try_outdate_peer(mdev); conn_try_outdate_peer(tconn);
/* Not using
drbd_request_state(mdev, NS(pdsk, nps));
here, because we might were able to re-establish the connection
in the meantime. This can only partially be solved in the state's
engine is_valid_state() and is_valid_state_transition()
functions.
nps can be D_INCONSISTENT, D_OUTDATED or D_UNKNOWN.
pdsk == D_INCONSISTENT while conn >= C_CONNECTED is valid,
therefore we have to have the pre state change check here.
*/
spin_lock_irq(&mdev->tconn->req_lock);
ns = mdev->state;
if (ns.conn < C_WF_REPORT_PARAMS) {
ns.pdsk = nps;
_drbd_set_state(mdev, ns, CS_VERBOSE, NULL);
}
spin_unlock_irq(&mdev->tconn->req_lock);
return 0; return 0;
} }
void drbd_try_outdate_peer_async(struct drbd_conf *mdev) void conn_try_outdate_peer_async(struct drbd_tconn *tconn)
{ {
struct task_struct *opa; struct task_struct *opa;
opa = kthread_run(_try_outdate_peer_async, mdev, "drbd%d_a_helper", mdev_to_minor(mdev)); opa = kthread_run(_try_outdate_peer_async, tconn, "drbd_async_h");
if (IS_ERR(opa)) if (IS_ERR(opa))
dev_err(DEV, "out of mem, failed to invoke fence-peer helper\n"); conn_err(tconn, "out of mem, failed to invoke fence-peer helper\n");
} }
enum drbd_state_rv enum drbd_state_rv
...@@ -486,7 +492,6 @@ drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force) ...@@ -486,7 +492,6 @@ drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force)
int try = 0; int try = 0;
int forced = 0; int forced = 0;
union drbd_state mask, val; union drbd_state mask, val;
enum drbd_disk_state nps;
if (new_role == R_PRIMARY) if (new_role == R_PRIMARY)
request_ping(mdev->tconn); /* Detect a dead peer ASAP */ request_ping(mdev->tconn); /* Detect a dead peer ASAP */
...@@ -519,32 +524,23 @@ drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force) ...@@ -519,32 +524,23 @@ drbd_set_role(struct drbd_conf *mdev, enum drbd_role new_role, int force)
if (rv == SS_NO_UP_TO_DATE_DISK && if (rv == SS_NO_UP_TO_DATE_DISK &&
mdev->state.disk == D_CONSISTENT && mask.pdsk == 0) { mdev->state.disk == D_CONSISTENT && mask.pdsk == 0) {
D_ASSERT(mdev->state.pdsk == D_UNKNOWN); D_ASSERT(mdev->state.pdsk == D_UNKNOWN);
nps = drbd_try_outdate_peer(mdev);
if (nps == D_OUTDATED || nps == D_INCONSISTENT) { if (conn_try_outdate_peer(mdev->tconn)) {
val.disk = D_UP_TO_DATE; val.disk = D_UP_TO_DATE;
mask.disk = D_MASK; mask.disk = D_MASK;
} }
val.pdsk = nps;
mask.pdsk = D_MASK;
continue; continue;
} }
if (rv == SS_NOTHING_TO_DO) if (rv == SS_NOTHING_TO_DO)
goto out; goto out;
if (rv == SS_PRIMARY_NOP && mask.pdsk == 0) { if (rv == SS_PRIMARY_NOP && mask.pdsk == 0) {
nps = drbd_try_outdate_peer(mdev); if (!conn_try_outdate_peer(mdev->tconn) && force) {
if (force && nps > D_OUTDATED) {
dev_warn(DEV, "Forced into split brain situation!\n"); dev_warn(DEV, "Forced into split brain situation!\n");
nps = D_OUTDATED; mask.pdsk = D_MASK;
} val.pdsk = D_OUTDATED;
mask.pdsk = D_MASK;
val.pdsk = nps;
}
continue; continue;
} }
if (rv == SS_TWO_PRIMARIES) { if (rv == SS_TWO_PRIMARIES) {
......
...@@ -4030,9 +4030,11 @@ static void drbd_disconnect(struct drbd_tconn *tconn) ...@@ -4030,9 +4030,11 @@ static void drbd_disconnect(struct drbd_tconn *tconn)
drbd_free_sock(tconn); drbd_free_sock(tconn);
idr_for_each(&tconn->volumes, drbd_disconnected, tconn); idr_for_each(&tconn->volumes, drbd_disconnected, tconn);
conn_info(tconn, "Connection closed\n"); conn_info(tconn, "Connection closed\n");
if (conn_highest_role(tconn) == R_PRIMARY && conn_highest_pdsk(tconn) >= D_UNKNOWN)
conn_try_outdate_peer_async(tconn);
spin_lock_irq(&tconn->req_lock); spin_lock_irq(&tconn->req_lock);
oc = tconn->cstate; oc = tconn->cstate;
if (oc >= C_UNCONNECTED) if (oc >= C_UNCONNECTED)
...@@ -4109,9 +4111,6 @@ static int drbd_disconnected(int vnr, void *p, void *data) ...@@ -4109,9 +4111,6 @@ static int drbd_disconnected(int vnr, void *p, void *data)
put_ldev(mdev); put_ldev(mdev);
} }
if (mdev->state.role == R_PRIMARY && fp >= FP_RESOURCE && mdev->state.pdsk >= D_UNKNOWN)
drbd_try_outdate_peer_async(mdev);
/* serialize with bitmap writeout triggered by the state change, /* serialize with bitmap writeout triggered by the state change,
* if any. */ * if any. */
wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags)); wait_event(mdev->misc_wait, !test_bit(BITMAP_IO, &mdev->flags));
......
...@@ -61,6 +61,73 @@ bool conn_all_vols_unconf(struct drbd_tconn *tconn) ...@@ -61,6 +61,73 @@ bool conn_all_vols_unconf(struct drbd_tconn *tconn)
return true; return true;
} }
/* Unfortunately the states where not correctly ordered, when
they where defined. therefore can not use max_t() here. */
static enum drbd_role max_role(enum drbd_role role1, enum drbd_role role2)
{
if (role1 == R_PRIMARY || role2 == R_PRIMARY)
return R_PRIMARY;
if (role1 == R_SECONDARY || role2 == R_SECONDARY)
return R_SECONDARY;
return R_UNKNOWN;
}
static enum drbd_role min_role(enum drbd_role role1, enum drbd_role role2)
{
if (role1 == R_UNKNOWN || role2 == R_UNKNOWN)
return R_UNKNOWN;
if (role1 == R_SECONDARY || role2 == R_SECONDARY)
return R_SECONDARY;
return R_PRIMARY;
}
enum drbd_role conn_highest_role(struct drbd_tconn *tconn)
{
enum drbd_role role = R_UNKNOWN;
struct drbd_conf *mdev;
int vnr;
idr_for_each_entry(&tconn->volumes, mdev, vnr)
role = max_role(role, mdev->state.role);
return role;
}
enum drbd_role conn_highest_peer(struct drbd_tconn *tconn)
{
enum drbd_role peer = R_UNKNOWN;
struct drbd_conf *mdev;
int vnr;
idr_for_each_entry(&tconn->volumes, mdev, vnr)
peer = max_role(peer, mdev->state.peer);
return peer;
}
enum drbd_disk_state conn_highest_disk(struct drbd_tconn *tconn)
{
enum drbd_disk_state ds = D_DISKLESS;
struct drbd_conf *mdev;
int vnr;
idr_for_each_entry(&tconn->volumes, mdev, vnr)
ds = max_t(enum drbd_disk_state, ds, mdev->state.disk);
return ds;
}
enum drbd_disk_state conn_highest_pdsk(struct drbd_tconn *tconn)
{
enum drbd_disk_state ds = D_DISKLESS;
struct drbd_conf *mdev;
int vnr;
idr_for_each_entry(&tconn->volumes, mdev, vnr)
ds = max_t(enum drbd_disk_state, ds, mdev->state.pdsk);
return ds;
}
/** /**
* cl_wide_st_chg() - true if the state change is a cluster wide one * cl_wide_st_chg() - true if the state change is a cluster wide one
* @mdev: DRBD device. * @mdev: DRBD device.
...@@ -329,18 +396,6 @@ static void print_state_change(struct drbd_conf *mdev, union drbd_state os, unio ...@@ -329,18 +396,6 @@ static void print_state_change(struct drbd_conf *mdev, union drbd_state os, unio
dev_info(DEV, "%s\n", pb); dev_info(DEV, "%s\n", pb);
} }
static bool vol_has_primary_peer(struct drbd_tconn *tconn)
{
struct drbd_conf *mdev;
int vnr;
idr_for_each_entry(&tconn->volumes, mdev, vnr) {
if (mdev->state.peer == R_PRIMARY)
return true;
}
return false;
}
/** /**
* is_valid_state() - Returns an SS_ error code if ns is not valid * is_valid_state() - Returns an SS_ error code if ns is not valid
* @mdev: DRBD device. * @mdev: DRBD device.
...@@ -364,7 +419,7 @@ is_valid_state(struct drbd_conf *mdev, union drbd_state ns) ...@@ -364,7 +419,7 @@ is_valid_state(struct drbd_conf *mdev, union drbd_state ns)
if (!mdev->tconn->net_conf->two_primaries && ns.role == R_PRIMARY) { if (!mdev->tconn->net_conf->two_primaries && ns.role == R_PRIMARY) {
if (ns.peer == R_PRIMARY) if (ns.peer == R_PRIMARY)
rv = SS_TWO_PRIMARIES; rv = SS_TWO_PRIMARIES;
else if (vol_has_primary_peer(mdev->tconn)) else if (conn_highest_peer(mdev->tconn) == R_PRIMARY)
rv = SS_O_VOL_PEER_PRI; rv = SS_O_VOL_PEER_PRI;
} }
put_net_conf(mdev->tconn); put_net_conf(mdev->tconn);
...@@ -1390,8 +1445,8 @@ static int _set_state_itr_fn(int vnr, void *p, void *data) ...@@ -1390,8 +1445,8 @@ static int _set_state_itr_fn(int vnr, void *p, void *data)
rv = __drbd_set_state(mdev, ns, flags, NULL); rv = __drbd_set_state(mdev, ns, flags, NULL);
ms.role = max_t(enum drbd_role, mdev->state.role, ms.role); ms.role = max_role(ns.role, ms.role);
ms.peer = max_t(enum drbd_role, mdev->state.peer, ms.peer); ms.peer = max_role(ns.peer, ms.peer);
ms.disk = max_t(enum drbd_role, mdev->state.disk, ms.disk); ms.disk = max_t(enum drbd_role, mdev->state.disk, ms.disk);
ms.pdsk = max_t(enum drbd_role, mdev->state.pdsk, ms.pdsk); ms.pdsk = max_t(enum drbd_role, mdev->state.pdsk, ms.pdsk);
params->ms = ms; params->ms = ms;
......
...@@ -110,4 +110,9 @@ static inline int drbd_request_state(struct drbd_conf *mdev, ...@@ -110,4 +110,9 @@ static inline int drbd_request_state(struct drbd_conf *mdev,
return _drbd_request_state(mdev, mask, val, CS_VERBOSE + CS_ORDERED); return _drbd_request_state(mdev, mask, val, CS_VERBOSE + CS_ORDERED);
} }
enum drbd_role conn_highest_role(struct drbd_tconn *tconn);
enum drbd_role conn_highest_peer(struct drbd_tconn *tconn);
enum drbd_disk_state conn_highest_disk(struct drbd_tconn *tconn);
enum drbd_disk_state conn_highest_pdsk(struct drbd_tconn *tconn);
#endif #endif
...@@ -65,7 +65,8 @@ enum drbd_io_error_p { ...@@ -65,7 +65,8 @@ enum drbd_io_error_p {
}; };
enum drbd_fencing_p { enum drbd_fencing_p {
FP_DONT_CARE, FP_NOT_AVAIL = -1, /* Not a policy */
FP_DONT_CARE = 0,
FP_RESOURCE, FP_RESOURCE,
FP_STONITH FP_STONITH
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment