162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci   drbd_state.c
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci   This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci   Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
862306a36Sopenharmony_ci   Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
962306a36Sopenharmony_ci   Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci   Thanks to Carter Burden, Bart Grantham and Gennadiy Nerubayev
1262306a36Sopenharmony_ci   from Logicworks, Inc. for making SDP replication support possible.
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/drbd_limits.h>
1762306a36Sopenharmony_ci#include "drbd_int.h"
1862306a36Sopenharmony_ci#include "drbd_protocol.h"
1962306a36Sopenharmony_ci#include "drbd_req.h"
2062306a36Sopenharmony_ci#include "drbd_state_change.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct after_state_chg_work {
2362306a36Sopenharmony_ci	struct drbd_work w;
2462306a36Sopenharmony_ci	struct drbd_device *device;
2562306a36Sopenharmony_ci	union drbd_state os;
2662306a36Sopenharmony_ci	union drbd_state ns;
2762306a36Sopenharmony_ci	enum chg_state_flags flags;
2862306a36Sopenharmony_ci	struct completion *done;
2962306a36Sopenharmony_ci	struct drbd_state_change *state_change;
3062306a36Sopenharmony_ci};
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cienum sanitize_state_warnings {
3362306a36Sopenharmony_ci	NO_WARNING,
3462306a36Sopenharmony_ci	ABORTED_ONLINE_VERIFY,
3562306a36Sopenharmony_ci	ABORTED_RESYNC,
3662306a36Sopenharmony_ci	CONNECTION_LOST_NEGOTIATING,
3762306a36Sopenharmony_ci	IMPLICITLY_UPGRADED_DISK,
3862306a36Sopenharmony_ci	IMPLICITLY_UPGRADED_PDSK,
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void count_objects(struct drbd_resource *resource,
4262306a36Sopenharmony_ci			  unsigned int *n_devices,
4362306a36Sopenharmony_ci			  unsigned int *n_connections)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	struct drbd_device *device;
4662306a36Sopenharmony_ci	struct drbd_connection *connection;
4762306a36Sopenharmony_ci	int vnr;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	*n_devices = 0;
5062306a36Sopenharmony_ci	*n_connections = 0;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	idr_for_each_entry(&resource->devices, device, vnr)
5362306a36Sopenharmony_ci		(*n_devices)++;
5462306a36Sopenharmony_ci	for_each_connection(connection, resource)
5562306a36Sopenharmony_ci		(*n_connections)++;
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic struct drbd_state_change *alloc_state_change(unsigned int n_devices, unsigned int n_connections, gfp_t gfp)
5962306a36Sopenharmony_ci{
6062306a36Sopenharmony_ci	struct drbd_state_change *state_change;
6162306a36Sopenharmony_ci	unsigned int size, n;
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci	size = sizeof(struct drbd_state_change) +
6462306a36Sopenharmony_ci	       n_devices * sizeof(struct drbd_device_state_change) +
6562306a36Sopenharmony_ci	       n_connections * sizeof(struct drbd_connection_state_change) +
6662306a36Sopenharmony_ci	       n_devices * n_connections * sizeof(struct drbd_peer_device_state_change);
6762306a36Sopenharmony_ci	state_change = kmalloc(size, gfp);
6862306a36Sopenharmony_ci	if (!state_change)
6962306a36Sopenharmony_ci		return NULL;
7062306a36Sopenharmony_ci	state_change->n_devices = n_devices;
7162306a36Sopenharmony_ci	state_change->n_connections = n_connections;
7262306a36Sopenharmony_ci	state_change->devices = (void *)(state_change + 1);
7362306a36Sopenharmony_ci	state_change->connections = (void *)&state_change->devices[n_devices];
7462306a36Sopenharmony_ci	state_change->peer_devices = (void *)&state_change->connections[n_connections];
7562306a36Sopenharmony_ci	state_change->resource->resource = NULL;
7662306a36Sopenharmony_ci	for (n = 0; n < n_devices; n++)
7762306a36Sopenharmony_ci		state_change->devices[n].device = NULL;
7862306a36Sopenharmony_ci	for (n = 0; n < n_connections; n++)
7962306a36Sopenharmony_ci		state_change->connections[n].connection = NULL;
8062306a36Sopenharmony_ci	return state_change;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistruct drbd_state_change *remember_old_state(struct drbd_resource *resource, gfp_t gfp)
8462306a36Sopenharmony_ci{
8562306a36Sopenharmony_ci	struct drbd_state_change *state_change;
8662306a36Sopenharmony_ci	struct drbd_device *device;
8762306a36Sopenharmony_ci	unsigned int n_devices;
8862306a36Sopenharmony_ci	struct drbd_connection *connection;
8962306a36Sopenharmony_ci	unsigned int n_connections;
9062306a36Sopenharmony_ci	int vnr;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	struct drbd_device_state_change *device_state_change;
9362306a36Sopenharmony_ci	struct drbd_peer_device_state_change *peer_device_state_change;
9462306a36Sopenharmony_ci	struct drbd_connection_state_change *connection_state_change;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	/* Caller holds req_lock spinlock.
9762306a36Sopenharmony_ci	 * No state, no device IDR, no connections lists can change. */
9862306a36Sopenharmony_ci	count_objects(resource, &n_devices, &n_connections);
9962306a36Sopenharmony_ci	state_change = alloc_state_change(n_devices, n_connections, gfp);
10062306a36Sopenharmony_ci	if (!state_change)
10162306a36Sopenharmony_ci		return NULL;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	kref_get(&resource->kref);
10462306a36Sopenharmony_ci	state_change->resource->resource = resource;
10562306a36Sopenharmony_ci	state_change->resource->role[OLD] =
10662306a36Sopenharmony_ci		conn_highest_role(first_connection(resource));
10762306a36Sopenharmony_ci	state_change->resource->susp[OLD] = resource->susp;
10862306a36Sopenharmony_ci	state_change->resource->susp_nod[OLD] = resource->susp_nod;
10962306a36Sopenharmony_ci	state_change->resource->susp_fen[OLD] = resource->susp_fen;
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	connection_state_change = state_change->connections;
11262306a36Sopenharmony_ci	for_each_connection(connection, resource) {
11362306a36Sopenharmony_ci		kref_get(&connection->kref);
11462306a36Sopenharmony_ci		connection_state_change->connection = connection;
11562306a36Sopenharmony_ci		connection_state_change->cstate[OLD] =
11662306a36Sopenharmony_ci			connection->cstate;
11762306a36Sopenharmony_ci		connection_state_change->peer_role[OLD] =
11862306a36Sopenharmony_ci			conn_highest_peer(connection);
11962306a36Sopenharmony_ci		connection_state_change++;
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	device_state_change = state_change->devices;
12362306a36Sopenharmony_ci	peer_device_state_change = state_change->peer_devices;
12462306a36Sopenharmony_ci	idr_for_each_entry(&resource->devices, device, vnr) {
12562306a36Sopenharmony_ci		kref_get(&device->kref);
12662306a36Sopenharmony_ci		device_state_change->device = device;
12762306a36Sopenharmony_ci		device_state_change->disk_state[OLD] = device->state.disk;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci		/* The peer_devices for each device have to be enumerated in
13062306a36Sopenharmony_ci		   the order of the connections. We may not use for_each_peer_device() here. */
13162306a36Sopenharmony_ci		for_each_connection(connection, resource) {
13262306a36Sopenharmony_ci			struct drbd_peer_device *peer_device;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci			peer_device = conn_peer_device(connection, device->vnr);
13562306a36Sopenharmony_ci			peer_device_state_change->peer_device = peer_device;
13662306a36Sopenharmony_ci			peer_device_state_change->disk_state[OLD] =
13762306a36Sopenharmony_ci				device->state.pdsk;
13862306a36Sopenharmony_ci			peer_device_state_change->repl_state[OLD] =
13962306a36Sopenharmony_ci				max_t(enum drbd_conns,
14062306a36Sopenharmony_ci				      C_WF_REPORT_PARAMS, device->state.conn);
14162306a36Sopenharmony_ci			peer_device_state_change->resync_susp_user[OLD] =
14262306a36Sopenharmony_ci				device->state.user_isp;
14362306a36Sopenharmony_ci			peer_device_state_change->resync_susp_peer[OLD] =
14462306a36Sopenharmony_ci				device->state.peer_isp;
14562306a36Sopenharmony_ci			peer_device_state_change->resync_susp_dependency[OLD] =
14662306a36Sopenharmony_ci				device->state.aftr_isp;
14762306a36Sopenharmony_ci			peer_device_state_change++;
14862306a36Sopenharmony_ci		}
14962306a36Sopenharmony_ci		device_state_change++;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	return state_change;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic void remember_new_state(struct drbd_state_change *state_change)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct drbd_resource_state_change *resource_state_change;
15862306a36Sopenharmony_ci	struct drbd_resource *resource;
15962306a36Sopenharmony_ci	unsigned int n;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (!state_change)
16262306a36Sopenharmony_ci		return;
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	resource_state_change = &state_change->resource[0];
16562306a36Sopenharmony_ci	resource = resource_state_change->resource;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	resource_state_change->role[NEW] =
16862306a36Sopenharmony_ci		conn_highest_role(first_connection(resource));
16962306a36Sopenharmony_ci	resource_state_change->susp[NEW] = resource->susp;
17062306a36Sopenharmony_ci	resource_state_change->susp_nod[NEW] = resource->susp_nod;
17162306a36Sopenharmony_ci	resource_state_change->susp_fen[NEW] = resource->susp_fen;
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	for (n = 0; n < state_change->n_devices; n++) {
17462306a36Sopenharmony_ci		struct drbd_device_state_change *device_state_change =
17562306a36Sopenharmony_ci			&state_change->devices[n];
17662306a36Sopenharmony_ci		struct drbd_device *device = device_state_change->device;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci		device_state_change->disk_state[NEW] = device->state.disk;
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	for (n = 0; n < state_change->n_connections; n++) {
18262306a36Sopenharmony_ci		struct drbd_connection_state_change *connection_state_change =
18362306a36Sopenharmony_ci			&state_change->connections[n];
18462306a36Sopenharmony_ci		struct drbd_connection *connection =
18562306a36Sopenharmony_ci			connection_state_change->connection;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci		connection_state_change->cstate[NEW] = connection->cstate;
18862306a36Sopenharmony_ci		connection_state_change->peer_role[NEW] =
18962306a36Sopenharmony_ci			conn_highest_peer(connection);
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	for (n = 0; n < state_change->n_devices * state_change->n_connections; n++) {
19362306a36Sopenharmony_ci		struct drbd_peer_device_state_change *peer_device_state_change =
19462306a36Sopenharmony_ci			&state_change->peer_devices[n];
19562306a36Sopenharmony_ci		struct drbd_device *device =
19662306a36Sopenharmony_ci			peer_device_state_change->peer_device->device;
19762306a36Sopenharmony_ci		union drbd_dev_state state = device->state;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci		peer_device_state_change->disk_state[NEW] = state.pdsk;
20062306a36Sopenharmony_ci		peer_device_state_change->repl_state[NEW] =
20162306a36Sopenharmony_ci			max_t(enum drbd_conns, C_WF_REPORT_PARAMS, state.conn);
20262306a36Sopenharmony_ci		peer_device_state_change->resync_susp_user[NEW] =
20362306a36Sopenharmony_ci			state.user_isp;
20462306a36Sopenharmony_ci		peer_device_state_change->resync_susp_peer[NEW] =
20562306a36Sopenharmony_ci			state.peer_isp;
20662306a36Sopenharmony_ci		peer_device_state_change->resync_susp_dependency[NEW] =
20762306a36Sopenharmony_ci			state.aftr_isp;
20862306a36Sopenharmony_ci	}
20962306a36Sopenharmony_ci}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_civoid copy_old_to_new_state_change(struct drbd_state_change *state_change)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	struct drbd_resource_state_change *resource_state_change = &state_change->resource[0];
21462306a36Sopenharmony_ci	unsigned int n_device, n_connection, n_peer_device, n_peer_devices;
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci#define OLD_TO_NEW(x) \
21762306a36Sopenharmony_ci	(x[NEW] = x[OLD])
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	OLD_TO_NEW(resource_state_change->role);
22062306a36Sopenharmony_ci	OLD_TO_NEW(resource_state_change->susp);
22162306a36Sopenharmony_ci	OLD_TO_NEW(resource_state_change->susp_nod);
22262306a36Sopenharmony_ci	OLD_TO_NEW(resource_state_change->susp_fen);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	for (n_connection = 0; n_connection < state_change->n_connections; n_connection++) {
22562306a36Sopenharmony_ci		struct drbd_connection_state_change *connection_state_change =
22662306a36Sopenharmony_ci				&state_change->connections[n_connection];
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci		OLD_TO_NEW(connection_state_change->peer_role);
22962306a36Sopenharmony_ci		OLD_TO_NEW(connection_state_change->cstate);
23062306a36Sopenharmony_ci	}
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	for (n_device = 0; n_device < state_change->n_devices; n_device++) {
23362306a36Sopenharmony_ci		struct drbd_device_state_change *device_state_change =
23462306a36Sopenharmony_ci			&state_change->devices[n_device];
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		OLD_TO_NEW(device_state_change->disk_state);
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	n_peer_devices = state_change->n_devices * state_change->n_connections;
24062306a36Sopenharmony_ci	for (n_peer_device = 0; n_peer_device < n_peer_devices; n_peer_device++) {
24162306a36Sopenharmony_ci		struct drbd_peer_device_state_change *p =
24262306a36Sopenharmony_ci			&state_change->peer_devices[n_peer_device];
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		OLD_TO_NEW(p->disk_state);
24562306a36Sopenharmony_ci		OLD_TO_NEW(p->repl_state);
24662306a36Sopenharmony_ci		OLD_TO_NEW(p->resync_susp_user);
24762306a36Sopenharmony_ci		OLD_TO_NEW(p->resync_susp_peer);
24862306a36Sopenharmony_ci		OLD_TO_NEW(p->resync_susp_dependency);
24962306a36Sopenharmony_ci	}
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci#undef OLD_TO_NEW
25262306a36Sopenharmony_ci}
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_civoid forget_state_change(struct drbd_state_change *state_change)
25562306a36Sopenharmony_ci{
25662306a36Sopenharmony_ci	unsigned int n;
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	if (!state_change)
25962306a36Sopenharmony_ci		return;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (state_change->resource->resource)
26262306a36Sopenharmony_ci		kref_put(&state_change->resource->resource->kref, drbd_destroy_resource);
26362306a36Sopenharmony_ci	for (n = 0; n < state_change->n_devices; n++) {
26462306a36Sopenharmony_ci		struct drbd_device *device = state_change->devices[n].device;
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci		if (device)
26762306a36Sopenharmony_ci			kref_put(&device->kref, drbd_destroy_device);
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci	for (n = 0; n < state_change->n_connections; n++) {
27062306a36Sopenharmony_ci		struct drbd_connection *connection =
27162306a36Sopenharmony_ci			state_change->connections[n].connection;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		if (connection)
27462306a36Sopenharmony_ci			kref_put(&connection->kref, drbd_destroy_connection);
27562306a36Sopenharmony_ci	}
27662306a36Sopenharmony_ci	kfree(state_change);
27762306a36Sopenharmony_ci}
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_cistatic int w_after_state_ch(struct drbd_work *w, int unused);
28062306a36Sopenharmony_cistatic void after_state_ch(struct drbd_device *device, union drbd_state os,
28162306a36Sopenharmony_ci			   union drbd_state ns, enum chg_state_flags flags,
28262306a36Sopenharmony_ci			   struct drbd_state_change *);
28362306a36Sopenharmony_cistatic enum drbd_state_rv is_valid_state(struct drbd_device *, union drbd_state);
28462306a36Sopenharmony_cistatic enum drbd_state_rv is_valid_soft_transition(union drbd_state, union drbd_state, struct drbd_connection *);
28562306a36Sopenharmony_cistatic enum drbd_state_rv is_valid_transition(union drbd_state os, union drbd_state ns);
28662306a36Sopenharmony_cistatic union drbd_state sanitize_state(struct drbd_device *device, union drbd_state os,
28762306a36Sopenharmony_ci				       union drbd_state ns, enum sanitize_state_warnings *warn);
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_cistatic inline bool is_susp(union drbd_state s)
29062306a36Sopenharmony_ci{
29162306a36Sopenharmony_ci        return s.susp || s.susp_nod || s.susp_fen;
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cibool conn_all_vols_unconf(struct drbd_connection *connection)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
29762306a36Sopenharmony_ci	bool rv = true;
29862306a36Sopenharmony_ci	int vnr;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	rcu_read_lock();
30162306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
30262306a36Sopenharmony_ci		struct drbd_device *device = peer_device->device;
30362306a36Sopenharmony_ci		if (device->state.disk != D_DISKLESS ||
30462306a36Sopenharmony_ci		    device->state.conn != C_STANDALONE ||
30562306a36Sopenharmony_ci		    device->state.role != R_SECONDARY) {
30662306a36Sopenharmony_ci			rv = false;
30762306a36Sopenharmony_ci			break;
30862306a36Sopenharmony_ci		}
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci	rcu_read_unlock();
31162306a36Sopenharmony_ci
31262306a36Sopenharmony_ci	return rv;
31362306a36Sopenharmony_ci}
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci/* Unfortunately the states where not correctly ordered, when
31662306a36Sopenharmony_ci   they where defined. therefore can not use max_t() here. */
31762306a36Sopenharmony_cistatic enum drbd_role max_role(enum drbd_role role1, enum drbd_role role2)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	if (role1 == R_PRIMARY || role2 == R_PRIMARY)
32062306a36Sopenharmony_ci		return R_PRIMARY;
32162306a36Sopenharmony_ci	if (role1 == R_SECONDARY || role2 == R_SECONDARY)
32262306a36Sopenharmony_ci		return R_SECONDARY;
32362306a36Sopenharmony_ci	return R_UNKNOWN;
32462306a36Sopenharmony_ci}
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic enum drbd_role min_role(enum drbd_role role1, enum drbd_role role2)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	if (role1 == R_UNKNOWN || role2 == R_UNKNOWN)
32962306a36Sopenharmony_ci		return R_UNKNOWN;
33062306a36Sopenharmony_ci	if (role1 == R_SECONDARY || role2 == R_SECONDARY)
33162306a36Sopenharmony_ci		return R_SECONDARY;
33262306a36Sopenharmony_ci	return R_PRIMARY;
33362306a36Sopenharmony_ci}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_cienum drbd_role conn_highest_role(struct drbd_connection *connection)
33662306a36Sopenharmony_ci{
33762306a36Sopenharmony_ci	enum drbd_role role = R_SECONDARY;
33862306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
33962306a36Sopenharmony_ci	int vnr;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	rcu_read_lock();
34262306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
34362306a36Sopenharmony_ci		struct drbd_device *device = peer_device->device;
34462306a36Sopenharmony_ci		role = max_role(role, device->state.role);
34562306a36Sopenharmony_ci	}
34662306a36Sopenharmony_ci	rcu_read_unlock();
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return role;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_cienum drbd_role conn_highest_peer(struct drbd_connection *connection)
35262306a36Sopenharmony_ci{
35362306a36Sopenharmony_ci	enum drbd_role peer = R_UNKNOWN;
35462306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
35562306a36Sopenharmony_ci	int vnr;
35662306a36Sopenharmony_ci
35762306a36Sopenharmony_ci	rcu_read_lock();
35862306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
35962306a36Sopenharmony_ci		struct drbd_device *device = peer_device->device;
36062306a36Sopenharmony_ci		peer = max_role(peer, device->state.peer);
36162306a36Sopenharmony_ci	}
36262306a36Sopenharmony_ci	rcu_read_unlock();
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	return peer;
36562306a36Sopenharmony_ci}
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_cienum drbd_disk_state conn_highest_disk(struct drbd_connection *connection)
36862306a36Sopenharmony_ci{
36962306a36Sopenharmony_ci	enum drbd_disk_state disk_state = D_DISKLESS;
37062306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
37162306a36Sopenharmony_ci	int vnr;
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci	rcu_read_lock();
37462306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
37562306a36Sopenharmony_ci		struct drbd_device *device = peer_device->device;
37662306a36Sopenharmony_ci		disk_state = max_t(enum drbd_disk_state, disk_state, device->state.disk);
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci	rcu_read_unlock();
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	return disk_state;
38162306a36Sopenharmony_ci}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_cienum drbd_disk_state conn_lowest_disk(struct drbd_connection *connection)
38462306a36Sopenharmony_ci{
38562306a36Sopenharmony_ci	enum drbd_disk_state disk_state = D_MASK;
38662306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
38762306a36Sopenharmony_ci	int vnr;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	rcu_read_lock();
39062306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
39162306a36Sopenharmony_ci		struct drbd_device *device = peer_device->device;
39262306a36Sopenharmony_ci		disk_state = min_t(enum drbd_disk_state, disk_state, device->state.disk);
39362306a36Sopenharmony_ci	}
39462306a36Sopenharmony_ci	rcu_read_unlock();
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	return disk_state;
39762306a36Sopenharmony_ci}
39862306a36Sopenharmony_ci
39962306a36Sopenharmony_cienum drbd_disk_state conn_highest_pdsk(struct drbd_connection *connection)
40062306a36Sopenharmony_ci{
40162306a36Sopenharmony_ci	enum drbd_disk_state disk_state = D_DISKLESS;
40262306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
40362306a36Sopenharmony_ci	int vnr;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	rcu_read_lock();
40662306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
40762306a36Sopenharmony_ci		struct drbd_device *device = peer_device->device;
40862306a36Sopenharmony_ci		disk_state = max_t(enum drbd_disk_state, disk_state, device->state.pdsk);
40962306a36Sopenharmony_ci	}
41062306a36Sopenharmony_ci	rcu_read_unlock();
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	return disk_state;
41362306a36Sopenharmony_ci}
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_cienum drbd_conns conn_lowest_conn(struct drbd_connection *connection)
41662306a36Sopenharmony_ci{
41762306a36Sopenharmony_ci	enum drbd_conns conn = C_MASK;
41862306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
41962306a36Sopenharmony_ci	int vnr;
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci	rcu_read_lock();
42262306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
42362306a36Sopenharmony_ci		struct drbd_device *device = peer_device->device;
42462306a36Sopenharmony_ci		conn = min_t(enum drbd_conns, conn, device->state.conn);
42562306a36Sopenharmony_ci	}
42662306a36Sopenharmony_ci	rcu_read_unlock();
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_ci	return conn;
42962306a36Sopenharmony_ci}
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_cistatic bool no_peer_wf_report_params(struct drbd_connection *connection)
43262306a36Sopenharmony_ci{
43362306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
43462306a36Sopenharmony_ci	int vnr;
43562306a36Sopenharmony_ci	bool rv = true;
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci	rcu_read_lock();
43862306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
43962306a36Sopenharmony_ci		if (peer_device->device->state.conn == C_WF_REPORT_PARAMS) {
44062306a36Sopenharmony_ci			rv = false;
44162306a36Sopenharmony_ci			break;
44262306a36Sopenharmony_ci		}
44362306a36Sopenharmony_ci	rcu_read_unlock();
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	return rv;
44662306a36Sopenharmony_ci}
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_cistatic void wake_up_all_devices(struct drbd_connection *connection)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
45162306a36Sopenharmony_ci	int vnr;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	rcu_read_lock();
45462306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
45562306a36Sopenharmony_ci		wake_up(&peer_device->device->state_wait);
45662306a36Sopenharmony_ci	rcu_read_unlock();
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci/**
46262306a36Sopenharmony_ci * cl_wide_st_chg() - true if the state change is a cluster wide one
46362306a36Sopenharmony_ci * @device:	DRBD device.
46462306a36Sopenharmony_ci * @os:		old (current) state.
46562306a36Sopenharmony_ci * @ns:		new (wanted) state.
46662306a36Sopenharmony_ci */
46762306a36Sopenharmony_cistatic int cl_wide_st_chg(struct drbd_device *device,
46862306a36Sopenharmony_ci			  union drbd_state os, union drbd_state ns)
46962306a36Sopenharmony_ci{
47062306a36Sopenharmony_ci	return (os.conn >= C_CONNECTED && ns.conn >= C_CONNECTED &&
47162306a36Sopenharmony_ci		 ((os.role != R_PRIMARY && ns.role == R_PRIMARY) ||
47262306a36Sopenharmony_ci		  (os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) ||
47362306a36Sopenharmony_ci		  (os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S) ||
47462306a36Sopenharmony_ci		  (os.disk != D_FAILED && ns.disk == D_FAILED))) ||
47562306a36Sopenharmony_ci		(os.conn >= C_CONNECTED && ns.conn == C_DISCONNECTING) ||
47662306a36Sopenharmony_ci		(os.conn == C_CONNECTED && ns.conn == C_VERIFY_S) ||
47762306a36Sopenharmony_ci		(os.conn == C_CONNECTED && ns.conn == C_WF_REPORT_PARAMS);
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic union drbd_state
48162306a36Sopenharmony_ciapply_mask_val(union drbd_state os, union drbd_state mask, union drbd_state val)
48262306a36Sopenharmony_ci{
48362306a36Sopenharmony_ci	union drbd_state ns;
48462306a36Sopenharmony_ci	ns.i = (os.i & ~mask.i) | val.i;
48562306a36Sopenharmony_ci	return ns;
48662306a36Sopenharmony_ci}
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_cienum drbd_state_rv
48962306a36Sopenharmony_cidrbd_change_state(struct drbd_device *device, enum chg_state_flags f,
49062306a36Sopenharmony_ci		  union drbd_state mask, union drbd_state val)
49162306a36Sopenharmony_ci{
49262306a36Sopenharmony_ci	unsigned long flags;
49362306a36Sopenharmony_ci	union drbd_state ns;
49462306a36Sopenharmony_ci	enum drbd_state_rv rv;
49562306a36Sopenharmony_ci
49662306a36Sopenharmony_ci	spin_lock_irqsave(&device->resource->req_lock, flags);
49762306a36Sopenharmony_ci	ns = apply_mask_val(drbd_read_state(device), mask, val);
49862306a36Sopenharmony_ci	rv = _drbd_set_state(device, ns, f, NULL);
49962306a36Sopenharmony_ci	spin_unlock_irqrestore(&device->resource->req_lock, flags);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	return rv;
50262306a36Sopenharmony_ci}
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/**
50562306a36Sopenharmony_ci * drbd_force_state() - Impose a change which happens outside our control on our state
50662306a36Sopenharmony_ci * @device:	DRBD device.
50762306a36Sopenharmony_ci * @mask:	mask of state bits to change.
50862306a36Sopenharmony_ci * @val:	value of new state bits.
50962306a36Sopenharmony_ci */
51062306a36Sopenharmony_civoid drbd_force_state(struct drbd_device *device,
51162306a36Sopenharmony_ci	union drbd_state mask, union drbd_state val)
51262306a36Sopenharmony_ci{
51362306a36Sopenharmony_ci	drbd_change_state(device, CS_HARD, mask, val);
51462306a36Sopenharmony_ci}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_cistatic enum drbd_state_rv
51762306a36Sopenharmony_ci_req_st_cond(struct drbd_device *device, union drbd_state mask,
51862306a36Sopenharmony_ci	     union drbd_state val)
51962306a36Sopenharmony_ci{
52062306a36Sopenharmony_ci	union drbd_state os, ns;
52162306a36Sopenharmony_ci	unsigned long flags;
52262306a36Sopenharmony_ci	enum drbd_state_rv rv;
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (test_and_clear_bit(CL_ST_CHG_SUCCESS, &device->flags))
52562306a36Sopenharmony_ci		return SS_CW_SUCCESS;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	if (test_and_clear_bit(CL_ST_CHG_FAIL, &device->flags))
52862306a36Sopenharmony_ci		return SS_CW_FAILED_BY_PEER;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	spin_lock_irqsave(&device->resource->req_lock, flags);
53162306a36Sopenharmony_ci	os = drbd_read_state(device);
53262306a36Sopenharmony_ci	ns = sanitize_state(device, os, apply_mask_val(os, mask, val), NULL);
53362306a36Sopenharmony_ci	rv = is_valid_transition(os, ns);
53462306a36Sopenharmony_ci	if (rv >= SS_SUCCESS)
53562306a36Sopenharmony_ci		rv = SS_UNKNOWN_ERROR;  /* cont waiting, otherwise fail. */
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	if (!cl_wide_st_chg(device, os, ns))
53862306a36Sopenharmony_ci		rv = SS_CW_NO_NEED;
53962306a36Sopenharmony_ci	if (rv == SS_UNKNOWN_ERROR) {
54062306a36Sopenharmony_ci		rv = is_valid_state(device, ns);
54162306a36Sopenharmony_ci		if (rv >= SS_SUCCESS) {
54262306a36Sopenharmony_ci			rv = is_valid_soft_transition(os, ns, first_peer_device(device)->connection);
54362306a36Sopenharmony_ci			if (rv >= SS_SUCCESS)
54462306a36Sopenharmony_ci				rv = SS_UNKNOWN_ERROR; /* cont waiting, otherwise fail. */
54562306a36Sopenharmony_ci		}
54662306a36Sopenharmony_ci	}
54762306a36Sopenharmony_ci	spin_unlock_irqrestore(&device->resource->req_lock, flags);
54862306a36Sopenharmony_ci
54962306a36Sopenharmony_ci	return rv;
55062306a36Sopenharmony_ci}
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci/**
55362306a36Sopenharmony_ci * drbd_req_state() - Perform an eventually cluster wide state change
55462306a36Sopenharmony_ci * @device:	DRBD device.
55562306a36Sopenharmony_ci * @mask:	mask of state bits to change.
55662306a36Sopenharmony_ci * @val:	value of new state bits.
55762306a36Sopenharmony_ci * @f:		flags
55862306a36Sopenharmony_ci *
55962306a36Sopenharmony_ci * Should not be called directly, use drbd_request_state() or
56062306a36Sopenharmony_ci * _drbd_request_state().
56162306a36Sopenharmony_ci */
56262306a36Sopenharmony_cistatic enum drbd_state_rv
56362306a36Sopenharmony_cidrbd_req_state(struct drbd_device *device, union drbd_state mask,
56462306a36Sopenharmony_ci	       union drbd_state val, enum chg_state_flags f)
56562306a36Sopenharmony_ci{
56662306a36Sopenharmony_ci	struct completion done;
56762306a36Sopenharmony_ci	unsigned long flags;
56862306a36Sopenharmony_ci	union drbd_state os, ns;
56962306a36Sopenharmony_ci	enum drbd_state_rv rv;
57062306a36Sopenharmony_ci	void *buffer = NULL;
57162306a36Sopenharmony_ci
57262306a36Sopenharmony_ci	init_completion(&done);
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (f & CS_SERIALIZE)
57562306a36Sopenharmony_ci		mutex_lock(device->state_mutex);
57662306a36Sopenharmony_ci	if (f & CS_INHIBIT_MD_IO)
57762306a36Sopenharmony_ci		buffer = drbd_md_get_buffer(device, __func__);
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	spin_lock_irqsave(&device->resource->req_lock, flags);
58062306a36Sopenharmony_ci	os = drbd_read_state(device);
58162306a36Sopenharmony_ci	ns = sanitize_state(device, os, apply_mask_val(os, mask, val), NULL);
58262306a36Sopenharmony_ci	rv = is_valid_transition(os, ns);
58362306a36Sopenharmony_ci	if (rv < SS_SUCCESS) {
58462306a36Sopenharmony_ci		spin_unlock_irqrestore(&device->resource->req_lock, flags);
58562306a36Sopenharmony_ci		goto abort;
58662306a36Sopenharmony_ci	}
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci	if (cl_wide_st_chg(device, os, ns)) {
58962306a36Sopenharmony_ci		rv = is_valid_state(device, ns);
59062306a36Sopenharmony_ci		if (rv == SS_SUCCESS)
59162306a36Sopenharmony_ci			rv = is_valid_soft_transition(os, ns, first_peer_device(device)->connection);
59262306a36Sopenharmony_ci		spin_unlock_irqrestore(&device->resource->req_lock, flags);
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci		if (rv < SS_SUCCESS) {
59562306a36Sopenharmony_ci			if (f & CS_VERBOSE)
59662306a36Sopenharmony_ci				print_st_err(device, os, ns, rv);
59762306a36Sopenharmony_ci			goto abort;
59862306a36Sopenharmony_ci		}
59962306a36Sopenharmony_ci
60062306a36Sopenharmony_ci		if (drbd_send_state_req(first_peer_device(device), mask, val)) {
60162306a36Sopenharmony_ci			rv = SS_CW_FAILED_BY_PEER;
60262306a36Sopenharmony_ci			if (f & CS_VERBOSE)
60362306a36Sopenharmony_ci				print_st_err(device, os, ns, rv);
60462306a36Sopenharmony_ci			goto abort;
60562306a36Sopenharmony_ci		}
60662306a36Sopenharmony_ci
60762306a36Sopenharmony_ci		wait_event(device->state_wait,
60862306a36Sopenharmony_ci			(rv = _req_st_cond(device, mask, val)));
60962306a36Sopenharmony_ci
61062306a36Sopenharmony_ci		if (rv < SS_SUCCESS) {
61162306a36Sopenharmony_ci			if (f & CS_VERBOSE)
61262306a36Sopenharmony_ci				print_st_err(device, os, ns, rv);
61362306a36Sopenharmony_ci			goto abort;
61462306a36Sopenharmony_ci		}
61562306a36Sopenharmony_ci		spin_lock_irqsave(&device->resource->req_lock, flags);
61662306a36Sopenharmony_ci		ns = apply_mask_val(drbd_read_state(device), mask, val);
61762306a36Sopenharmony_ci		rv = _drbd_set_state(device, ns, f, &done);
61862306a36Sopenharmony_ci	} else {
61962306a36Sopenharmony_ci		rv = _drbd_set_state(device, ns, f, &done);
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	spin_unlock_irqrestore(&device->resource->req_lock, flags);
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci	if (f & CS_WAIT_COMPLETE && rv == SS_SUCCESS) {
62562306a36Sopenharmony_ci		D_ASSERT(device, current != first_peer_device(device)->connection->worker.task);
62662306a36Sopenharmony_ci		wait_for_completion(&done);
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci
62962306a36Sopenharmony_ciabort:
63062306a36Sopenharmony_ci	if (buffer)
63162306a36Sopenharmony_ci		drbd_md_put_buffer(device);
63262306a36Sopenharmony_ci	if (f & CS_SERIALIZE)
63362306a36Sopenharmony_ci		mutex_unlock(device->state_mutex);
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return rv;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci/**
63962306a36Sopenharmony_ci * _drbd_request_state() - Request a state change (with flags)
64062306a36Sopenharmony_ci * @device:	DRBD device.
64162306a36Sopenharmony_ci * @mask:	mask of state bits to change.
64262306a36Sopenharmony_ci * @val:	value of new state bits.
64362306a36Sopenharmony_ci * @f:		flags
64462306a36Sopenharmony_ci *
64562306a36Sopenharmony_ci * Cousin of drbd_request_state(), useful with the CS_WAIT_COMPLETE
64662306a36Sopenharmony_ci * flag, or when logging of failed state change requests is not desired.
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_cienum drbd_state_rv
64962306a36Sopenharmony_ci_drbd_request_state(struct drbd_device *device, union drbd_state mask,
65062306a36Sopenharmony_ci		    union drbd_state val, enum chg_state_flags f)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	enum drbd_state_rv rv;
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_ci	wait_event(device->state_wait,
65562306a36Sopenharmony_ci		   (rv = drbd_req_state(device, mask, val, f)) != SS_IN_TRANSIENT_STATE);
65662306a36Sopenharmony_ci
65762306a36Sopenharmony_ci	return rv;
65862306a36Sopenharmony_ci}
65962306a36Sopenharmony_ci
66062306a36Sopenharmony_ci/*
66162306a36Sopenharmony_ci * We grab drbd_md_get_buffer(), because we don't want to "fail" the disk while
66262306a36Sopenharmony_ci * there is IO in-flight: the transition into D_FAILED for detach purposes
66362306a36Sopenharmony_ci * may get misinterpreted as actual IO error in a confused endio function.
66462306a36Sopenharmony_ci *
66562306a36Sopenharmony_ci * We wrap it all into wait_event(), to retry in case the drbd_req_state()
66662306a36Sopenharmony_ci * returns SS_IN_TRANSIENT_STATE.
66762306a36Sopenharmony_ci *
66862306a36Sopenharmony_ci * To avoid potential deadlock with e.g. the receiver thread trying to grab
66962306a36Sopenharmony_ci * drbd_md_get_buffer() while trying to get out of the "transient state", we
67062306a36Sopenharmony_ci * need to grab and release the meta data buffer inside of that wait_event loop.
67162306a36Sopenharmony_ci */
67262306a36Sopenharmony_cistatic enum drbd_state_rv
67362306a36Sopenharmony_cirequest_detach(struct drbd_device *device)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	return drbd_req_state(device, NS(disk, D_FAILED),
67662306a36Sopenharmony_ci			CS_VERBOSE | CS_ORDERED | CS_INHIBIT_MD_IO);
67762306a36Sopenharmony_ci}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ciint drbd_request_detach_interruptible(struct drbd_device *device)
68062306a36Sopenharmony_ci{
68162306a36Sopenharmony_ci	int ret, rv;
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_ci	drbd_suspend_io(device); /* so no-one is stuck in drbd_al_begin_io */
68462306a36Sopenharmony_ci	wait_event_interruptible(device->state_wait,
68562306a36Sopenharmony_ci		(rv = request_detach(device)) != SS_IN_TRANSIENT_STATE);
68662306a36Sopenharmony_ci	drbd_resume_io(device);
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	ret = wait_event_interruptible(device->misc_wait,
68962306a36Sopenharmony_ci			device->state.disk != D_FAILED);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (rv == SS_IS_DISKLESS)
69262306a36Sopenharmony_ci		rv = SS_NOTHING_TO_DO;
69362306a36Sopenharmony_ci	if (ret)
69462306a36Sopenharmony_ci		rv = ERR_INTR;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	return rv;
69762306a36Sopenharmony_ci}
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_cienum drbd_state_rv
70062306a36Sopenharmony_ci_drbd_request_state_holding_state_mutex(struct drbd_device *device, union drbd_state mask,
70162306a36Sopenharmony_ci		    union drbd_state val, enum chg_state_flags f)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	enum drbd_state_rv rv;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	BUG_ON(f & CS_SERIALIZE);
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	wait_event_cmd(device->state_wait,
70862306a36Sopenharmony_ci		       (rv = drbd_req_state(device, mask, val, f)) != SS_IN_TRANSIENT_STATE,
70962306a36Sopenharmony_ci		       mutex_unlock(device->state_mutex),
71062306a36Sopenharmony_ci		       mutex_lock(device->state_mutex));
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	return rv;
71362306a36Sopenharmony_ci}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_cistatic void print_st(struct drbd_device *device, const char *name, union drbd_state ns)
71662306a36Sopenharmony_ci{
71762306a36Sopenharmony_ci	drbd_err(device, " %s = { cs:%s ro:%s/%s ds:%s/%s %c%c%c%c%c%c }\n",
71862306a36Sopenharmony_ci	    name,
71962306a36Sopenharmony_ci	    drbd_conn_str(ns.conn),
72062306a36Sopenharmony_ci	    drbd_role_str(ns.role),
72162306a36Sopenharmony_ci	    drbd_role_str(ns.peer),
72262306a36Sopenharmony_ci	    drbd_disk_str(ns.disk),
72362306a36Sopenharmony_ci	    drbd_disk_str(ns.pdsk),
72462306a36Sopenharmony_ci	    is_susp(ns) ? 's' : 'r',
72562306a36Sopenharmony_ci	    ns.aftr_isp ? 'a' : '-',
72662306a36Sopenharmony_ci	    ns.peer_isp ? 'p' : '-',
72762306a36Sopenharmony_ci	    ns.user_isp ? 'u' : '-',
72862306a36Sopenharmony_ci	    ns.susp_fen ? 'F' : '-',
72962306a36Sopenharmony_ci	    ns.susp_nod ? 'N' : '-'
73062306a36Sopenharmony_ci	    );
73162306a36Sopenharmony_ci}
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_civoid print_st_err(struct drbd_device *device, union drbd_state os,
73462306a36Sopenharmony_ci	          union drbd_state ns, enum drbd_state_rv err)
73562306a36Sopenharmony_ci{
73662306a36Sopenharmony_ci	if (err == SS_IN_TRANSIENT_STATE)
73762306a36Sopenharmony_ci		return;
73862306a36Sopenharmony_ci	drbd_err(device, "State change failed: %s\n", drbd_set_st_err_str(err));
73962306a36Sopenharmony_ci	print_st(device, " state", os);
74062306a36Sopenharmony_ci	print_st(device, "wanted", ns);
74162306a36Sopenharmony_ci}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_cistatic long print_state_change(char *pb, union drbd_state os, union drbd_state ns,
74462306a36Sopenharmony_ci			       enum chg_state_flags flags)
74562306a36Sopenharmony_ci{
74662306a36Sopenharmony_ci	char *pbp;
74762306a36Sopenharmony_ci	pbp = pb;
74862306a36Sopenharmony_ci	*pbp = 0;
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	if (ns.role != os.role && flags & CS_DC_ROLE)
75162306a36Sopenharmony_ci		pbp += sprintf(pbp, "role( %s -> %s ) ",
75262306a36Sopenharmony_ci			       drbd_role_str(os.role),
75362306a36Sopenharmony_ci			       drbd_role_str(ns.role));
75462306a36Sopenharmony_ci	if (ns.peer != os.peer && flags & CS_DC_PEER)
75562306a36Sopenharmony_ci		pbp += sprintf(pbp, "peer( %s -> %s ) ",
75662306a36Sopenharmony_ci			       drbd_role_str(os.peer),
75762306a36Sopenharmony_ci			       drbd_role_str(ns.peer));
75862306a36Sopenharmony_ci	if (ns.conn != os.conn && flags & CS_DC_CONN)
75962306a36Sopenharmony_ci		pbp += sprintf(pbp, "conn( %s -> %s ) ",
76062306a36Sopenharmony_ci			       drbd_conn_str(os.conn),
76162306a36Sopenharmony_ci			       drbd_conn_str(ns.conn));
76262306a36Sopenharmony_ci	if (ns.disk != os.disk && flags & CS_DC_DISK)
76362306a36Sopenharmony_ci		pbp += sprintf(pbp, "disk( %s -> %s ) ",
76462306a36Sopenharmony_ci			       drbd_disk_str(os.disk),
76562306a36Sopenharmony_ci			       drbd_disk_str(ns.disk));
76662306a36Sopenharmony_ci	if (ns.pdsk != os.pdsk && flags & CS_DC_PDSK)
76762306a36Sopenharmony_ci		pbp += sprintf(pbp, "pdsk( %s -> %s ) ",
76862306a36Sopenharmony_ci			       drbd_disk_str(os.pdsk),
76962306a36Sopenharmony_ci			       drbd_disk_str(ns.pdsk));
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ci	return pbp - pb;
77262306a36Sopenharmony_ci}
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_cistatic void drbd_pr_state_change(struct drbd_device *device, union drbd_state os, union drbd_state ns,
77562306a36Sopenharmony_ci				 enum chg_state_flags flags)
77662306a36Sopenharmony_ci{
77762306a36Sopenharmony_ci	char pb[300];
77862306a36Sopenharmony_ci	char *pbp = pb;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	pbp += print_state_change(pbp, os, ns, flags ^ CS_DC_MASK);
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ci	if (ns.aftr_isp != os.aftr_isp)
78362306a36Sopenharmony_ci		pbp += sprintf(pbp, "aftr_isp( %d -> %d ) ",
78462306a36Sopenharmony_ci			       os.aftr_isp,
78562306a36Sopenharmony_ci			       ns.aftr_isp);
78662306a36Sopenharmony_ci	if (ns.peer_isp != os.peer_isp)
78762306a36Sopenharmony_ci		pbp += sprintf(pbp, "peer_isp( %d -> %d ) ",
78862306a36Sopenharmony_ci			       os.peer_isp,
78962306a36Sopenharmony_ci			       ns.peer_isp);
79062306a36Sopenharmony_ci	if (ns.user_isp != os.user_isp)
79162306a36Sopenharmony_ci		pbp += sprintf(pbp, "user_isp( %d -> %d ) ",
79262306a36Sopenharmony_ci			       os.user_isp,
79362306a36Sopenharmony_ci			       ns.user_isp);
79462306a36Sopenharmony_ci
79562306a36Sopenharmony_ci	if (pbp != pb)
79662306a36Sopenharmony_ci		drbd_info(device, "%s\n", pb);
79762306a36Sopenharmony_ci}
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_cistatic void conn_pr_state_change(struct drbd_connection *connection, union drbd_state os, union drbd_state ns,
80062306a36Sopenharmony_ci				 enum chg_state_flags flags)
80162306a36Sopenharmony_ci{
80262306a36Sopenharmony_ci	char pb[300];
80362306a36Sopenharmony_ci	char *pbp = pb;
80462306a36Sopenharmony_ci
80562306a36Sopenharmony_ci	pbp += print_state_change(pbp, os, ns, flags);
80662306a36Sopenharmony_ci
80762306a36Sopenharmony_ci	if (is_susp(ns) != is_susp(os) && flags & CS_DC_SUSP)
80862306a36Sopenharmony_ci		pbp += sprintf(pbp, "susp( %d -> %d ) ",
80962306a36Sopenharmony_ci			       is_susp(os),
81062306a36Sopenharmony_ci			       is_susp(ns));
81162306a36Sopenharmony_ci
81262306a36Sopenharmony_ci	if (pbp != pb)
81362306a36Sopenharmony_ci		drbd_info(connection, "%s\n", pb);
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci
81762306a36Sopenharmony_ci/**
81862306a36Sopenharmony_ci * is_valid_state() - Returns an SS_ error code if ns is not valid
81962306a36Sopenharmony_ci * @device:	DRBD device.
82062306a36Sopenharmony_ci * @ns:		State to consider.
82162306a36Sopenharmony_ci */
82262306a36Sopenharmony_cistatic enum drbd_state_rv
82362306a36Sopenharmony_ciis_valid_state(struct drbd_device *device, union drbd_state ns)
82462306a36Sopenharmony_ci{
82562306a36Sopenharmony_ci	/* See drbd_state_sw_errors in drbd_strings.c */
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	enum drbd_fencing_p fp;
82862306a36Sopenharmony_ci	enum drbd_state_rv rv = SS_SUCCESS;
82962306a36Sopenharmony_ci	struct net_conf *nc;
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	rcu_read_lock();
83262306a36Sopenharmony_ci	fp = FP_DONT_CARE;
83362306a36Sopenharmony_ci	if (get_ldev(device)) {
83462306a36Sopenharmony_ci		fp = rcu_dereference(device->ldev->disk_conf)->fencing;
83562306a36Sopenharmony_ci		put_ldev(device);
83662306a36Sopenharmony_ci	}
83762306a36Sopenharmony_ci
83862306a36Sopenharmony_ci	nc = rcu_dereference(first_peer_device(device)->connection->net_conf);
83962306a36Sopenharmony_ci	if (nc) {
84062306a36Sopenharmony_ci		if (!nc->two_primaries && ns.role == R_PRIMARY) {
84162306a36Sopenharmony_ci			if (ns.peer == R_PRIMARY)
84262306a36Sopenharmony_ci				rv = SS_TWO_PRIMARIES;
84362306a36Sopenharmony_ci			else if (conn_highest_peer(first_peer_device(device)->connection) == R_PRIMARY)
84462306a36Sopenharmony_ci				rv = SS_O_VOL_PEER_PRI;
84562306a36Sopenharmony_ci		}
84662306a36Sopenharmony_ci	}
84762306a36Sopenharmony_ci
84862306a36Sopenharmony_ci	if (rv <= 0)
84962306a36Sopenharmony_ci		goto out; /* already found a reason to abort */
85062306a36Sopenharmony_ci	else if (ns.role == R_SECONDARY && device->open_cnt)
85162306a36Sopenharmony_ci		rv = SS_DEVICE_IN_USE;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	else if (ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.disk < D_UP_TO_DATE)
85462306a36Sopenharmony_ci		rv = SS_NO_UP_TO_DATE_DISK;
85562306a36Sopenharmony_ci
85662306a36Sopenharmony_ci	else if (fp >= FP_RESOURCE &&
85762306a36Sopenharmony_ci		 ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.pdsk >= D_UNKNOWN)
85862306a36Sopenharmony_ci		rv = SS_PRIMARY_NOP;
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	else if (ns.role == R_PRIMARY && ns.disk <= D_INCONSISTENT && ns.pdsk <= D_INCONSISTENT)
86162306a36Sopenharmony_ci		rv = SS_NO_UP_TO_DATE_DISK;
86262306a36Sopenharmony_ci
86362306a36Sopenharmony_ci	else if (ns.conn > C_CONNECTED && ns.disk < D_INCONSISTENT)
86462306a36Sopenharmony_ci		rv = SS_NO_LOCAL_DISK;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	else if (ns.conn > C_CONNECTED && ns.pdsk < D_INCONSISTENT)
86762306a36Sopenharmony_ci		rv = SS_NO_REMOTE_DISK;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	else if (ns.conn > C_CONNECTED && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE)
87062306a36Sopenharmony_ci		rv = SS_NO_UP_TO_DATE_DISK;
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	else if ((ns.conn == C_CONNECTED ||
87362306a36Sopenharmony_ci		  ns.conn == C_WF_BITMAP_S ||
87462306a36Sopenharmony_ci		  ns.conn == C_SYNC_SOURCE ||
87562306a36Sopenharmony_ci		  ns.conn == C_PAUSED_SYNC_S) &&
87662306a36Sopenharmony_ci		  ns.disk == D_OUTDATED)
87762306a36Sopenharmony_ci		rv = SS_CONNECTED_OUTDATES;
87862306a36Sopenharmony_ci
87962306a36Sopenharmony_ci	else if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
88062306a36Sopenharmony_ci		 (nc->verify_alg[0] == 0))
88162306a36Sopenharmony_ci		rv = SS_NO_VERIFY_ALG;
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	else if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
88462306a36Sopenharmony_ci		  first_peer_device(device)->connection->agreed_pro_version < 88)
88562306a36Sopenharmony_ci		rv = SS_NOT_SUPPORTED;
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_ci	else if (ns.role == R_PRIMARY && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE)
88862306a36Sopenharmony_ci		rv = SS_NO_UP_TO_DATE_DISK;
88962306a36Sopenharmony_ci
89062306a36Sopenharmony_ci	else if ((ns.conn == C_STARTING_SYNC_S || ns.conn == C_STARTING_SYNC_T) &&
89162306a36Sopenharmony_ci                 ns.pdsk == D_UNKNOWN)
89262306a36Sopenharmony_ci		rv = SS_NEED_CONNECTION;
89362306a36Sopenharmony_ci
89462306a36Sopenharmony_ci	else if (ns.conn >= C_CONNECTED && ns.pdsk == D_UNKNOWN)
89562306a36Sopenharmony_ci		rv = SS_CONNECTED_OUTDATES;
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ciout:
89862306a36Sopenharmony_ci	rcu_read_unlock();
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	return rv;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci/**
90462306a36Sopenharmony_ci * is_valid_soft_transition() - Returns an SS_ error code if the state transition is not possible
90562306a36Sopenharmony_ci * This function limits state transitions that may be declined by DRBD. I.e.
90662306a36Sopenharmony_ci * user requests (aka soft transitions).
90762306a36Sopenharmony_ci * @os:		old state.
90862306a36Sopenharmony_ci * @ns:		new state.
90962306a36Sopenharmony_ci * @connection:  DRBD connection.
91062306a36Sopenharmony_ci */
91162306a36Sopenharmony_cistatic enum drbd_state_rv
91262306a36Sopenharmony_ciis_valid_soft_transition(union drbd_state os, union drbd_state ns, struct drbd_connection *connection)
91362306a36Sopenharmony_ci{
91462306a36Sopenharmony_ci	enum drbd_state_rv rv = SS_SUCCESS;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	if ((ns.conn == C_STARTING_SYNC_T || ns.conn == C_STARTING_SYNC_S) &&
91762306a36Sopenharmony_ci	    os.conn > C_CONNECTED)
91862306a36Sopenharmony_ci		rv = SS_RESYNC_RUNNING;
91962306a36Sopenharmony_ci
92062306a36Sopenharmony_ci	if (ns.conn == C_DISCONNECTING && os.conn == C_STANDALONE)
92162306a36Sopenharmony_ci		rv = SS_ALREADY_STANDALONE;
92262306a36Sopenharmony_ci
92362306a36Sopenharmony_ci	if (ns.disk > D_ATTACHING && os.disk == D_DISKLESS)
92462306a36Sopenharmony_ci		rv = SS_IS_DISKLESS;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	if (ns.conn == C_WF_CONNECTION && os.conn < C_UNCONNECTED)
92762306a36Sopenharmony_ci		rv = SS_NO_NET_CONFIG;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if (ns.disk == D_OUTDATED && os.disk < D_OUTDATED && os.disk != D_ATTACHING)
93062306a36Sopenharmony_ci		rv = SS_LOWER_THAN_OUTDATED;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (ns.conn == C_DISCONNECTING && os.conn == C_UNCONNECTED)
93362306a36Sopenharmony_ci		rv = SS_IN_TRANSIENT_STATE;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	/* While establishing a connection only allow cstate to change.
93662306a36Sopenharmony_ci	   Delay/refuse role changes, detach attach etc... (they do not touch cstate) */
93762306a36Sopenharmony_ci	if (test_bit(STATE_SENT, &connection->flags) &&
93862306a36Sopenharmony_ci	    !((ns.conn == C_WF_REPORT_PARAMS && os.conn == C_WF_CONNECTION) ||
93962306a36Sopenharmony_ci	      (ns.conn >= C_CONNECTED && os.conn == C_WF_REPORT_PARAMS)))
94062306a36Sopenharmony_ci		rv = SS_IN_TRANSIENT_STATE;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	/* Do not promote during resync handshake triggered by "force primary".
94362306a36Sopenharmony_ci	 * This is a hack. It should really be rejected by the peer during the
94462306a36Sopenharmony_ci	 * cluster wide state change request. */
94562306a36Sopenharmony_ci	if (os.role != R_PRIMARY && ns.role == R_PRIMARY
94662306a36Sopenharmony_ci		&& ns.pdsk == D_UP_TO_DATE
94762306a36Sopenharmony_ci		&& ns.disk != D_UP_TO_DATE && ns.disk != D_DISKLESS
94862306a36Sopenharmony_ci		&& (ns.conn <= C_WF_SYNC_UUID || ns.conn != os.conn))
94962306a36Sopenharmony_ci			rv = SS_IN_TRANSIENT_STATE;
95062306a36Sopenharmony_ci
95162306a36Sopenharmony_ci	if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && os.conn < C_CONNECTED)
95262306a36Sopenharmony_ci		rv = SS_NEED_CONNECTION;
95362306a36Sopenharmony_ci
95462306a36Sopenharmony_ci	if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
95562306a36Sopenharmony_ci	    ns.conn != os.conn && os.conn > C_CONNECTED)
95662306a36Sopenharmony_ci		rv = SS_RESYNC_RUNNING;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	if ((ns.conn == C_STARTING_SYNC_S || ns.conn == C_STARTING_SYNC_T) &&
95962306a36Sopenharmony_ci	    os.conn < C_CONNECTED)
96062306a36Sopenharmony_ci		rv = SS_NEED_CONNECTION;
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	if ((ns.conn == C_SYNC_TARGET || ns.conn == C_SYNC_SOURCE)
96362306a36Sopenharmony_ci	    && os.conn < C_WF_REPORT_PARAMS)
96462306a36Sopenharmony_ci		rv = SS_NEED_CONNECTION; /* No NetworkFailure -> SyncTarget etc... */
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (ns.conn == C_DISCONNECTING && ns.pdsk == D_OUTDATED &&
96762306a36Sopenharmony_ci	    os.conn < C_CONNECTED && os.pdsk > D_OUTDATED)
96862306a36Sopenharmony_ci		rv = SS_OUTDATE_WO_CONN;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	return rv;
97162306a36Sopenharmony_ci}
97262306a36Sopenharmony_ci
97362306a36Sopenharmony_cistatic enum drbd_state_rv
97462306a36Sopenharmony_ciis_valid_conn_transition(enum drbd_conns oc, enum drbd_conns nc)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	/* no change -> nothing to do, at least for the connection part */
97762306a36Sopenharmony_ci	if (oc == nc)
97862306a36Sopenharmony_ci		return SS_NOTHING_TO_DO;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci	/* disconnect of an unconfigured connection does not make sense */
98162306a36Sopenharmony_ci	if (oc == C_STANDALONE && nc == C_DISCONNECTING)
98262306a36Sopenharmony_ci		return SS_ALREADY_STANDALONE;
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	/* from C_STANDALONE, we start with C_UNCONNECTED */
98562306a36Sopenharmony_ci	if (oc == C_STANDALONE && nc != C_UNCONNECTED)
98662306a36Sopenharmony_ci		return SS_NEED_CONNECTION;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	/* When establishing a connection we need to go through WF_REPORT_PARAMS!
98962306a36Sopenharmony_ci	   Necessary to do the right thing upon invalidate-remote on a disconnected resource */
99062306a36Sopenharmony_ci	if (oc < C_WF_REPORT_PARAMS && nc >= C_CONNECTED)
99162306a36Sopenharmony_ci		return SS_NEED_CONNECTION;
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	/* After a network error only C_UNCONNECTED or C_DISCONNECTING may follow. */
99462306a36Sopenharmony_ci	if (oc >= C_TIMEOUT && oc <= C_TEAR_DOWN && nc != C_UNCONNECTED && nc != C_DISCONNECTING)
99562306a36Sopenharmony_ci		return SS_IN_TRANSIENT_STATE;
99662306a36Sopenharmony_ci
99762306a36Sopenharmony_ci	/* After C_DISCONNECTING only C_STANDALONE may follow */
99862306a36Sopenharmony_ci	if (oc == C_DISCONNECTING && nc != C_STANDALONE)
99962306a36Sopenharmony_ci		return SS_IN_TRANSIENT_STATE;
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci	return SS_SUCCESS;
100262306a36Sopenharmony_ci}
100362306a36Sopenharmony_ci
100462306a36Sopenharmony_ci
100562306a36Sopenharmony_ci/**
100662306a36Sopenharmony_ci * is_valid_transition() - Returns an SS_ error code if the state transition is not possible
100762306a36Sopenharmony_ci * This limits hard state transitions. Hard state transitions are facts there are
100862306a36Sopenharmony_ci * imposed on DRBD by the environment. E.g. disk broke or network broke down.
100962306a36Sopenharmony_ci * But those hard state transitions are still not allowed to do everything.
101062306a36Sopenharmony_ci * @ns:		new state.
101162306a36Sopenharmony_ci * @os:		old state.
101262306a36Sopenharmony_ci */
101362306a36Sopenharmony_cistatic enum drbd_state_rv
101462306a36Sopenharmony_ciis_valid_transition(union drbd_state os, union drbd_state ns)
101562306a36Sopenharmony_ci{
101662306a36Sopenharmony_ci	enum drbd_state_rv rv;
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ci	rv = is_valid_conn_transition(os.conn, ns.conn);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ci	/* we cannot fail (again) if we already detached */
102162306a36Sopenharmony_ci	if (ns.disk == D_FAILED && os.disk == D_DISKLESS)
102262306a36Sopenharmony_ci		rv = SS_IS_DISKLESS;
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	return rv;
102562306a36Sopenharmony_ci}
102662306a36Sopenharmony_ci
102762306a36Sopenharmony_cistatic void print_sanitize_warnings(struct drbd_device *device, enum sanitize_state_warnings warn)
102862306a36Sopenharmony_ci{
102962306a36Sopenharmony_ci	static const char *msg_table[] = {
103062306a36Sopenharmony_ci		[NO_WARNING] = "",
103162306a36Sopenharmony_ci		[ABORTED_ONLINE_VERIFY] = "Online-verify aborted.",
103262306a36Sopenharmony_ci		[ABORTED_RESYNC] = "Resync aborted.",
103362306a36Sopenharmony_ci		[CONNECTION_LOST_NEGOTIATING] = "Connection lost while negotiating, no data!",
103462306a36Sopenharmony_ci		[IMPLICITLY_UPGRADED_DISK] = "Implicitly upgraded disk",
103562306a36Sopenharmony_ci		[IMPLICITLY_UPGRADED_PDSK] = "Implicitly upgraded pdsk",
103662306a36Sopenharmony_ci	};
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_ci	if (warn != NO_WARNING)
103962306a36Sopenharmony_ci		drbd_warn(device, "%s\n", msg_table[warn]);
104062306a36Sopenharmony_ci}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci/**
104362306a36Sopenharmony_ci * sanitize_state() - Resolves implicitly necessary additional changes to a state transition
104462306a36Sopenharmony_ci * @device:	DRBD device.
104562306a36Sopenharmony_ci * @os:		old state.
104662306a36Sopenharmony_ci * @ns:		new state.
104762306a36Sopenharmony_ci * @warn:	placeholder for returned state warning.
104862306a36Sopenharmony_ci *
104962306a36Sopenharmony_ci * When we loose connection, we have to set the state of the peers disk (pdsk)
105062306a36Sopenharmony_ci * to D_UNKNOWN. This rule and many more along those lines are in this function.
105162306a36Sopenharmony_ci */
105262306a36Sopenharmony_cistatic union drbd_state sanitize_state(struct drbd_device *device, union drbd_state os,
105362306a36Sopenharmony_ci				       union drbd_state ns, enum sanitize_state_warnings *warn)
105462306a36Sopenharmony_ci{
105562306a36Sopenharmony_ci	enum drbd_fencing_p fp;
105662306a36Sopenharmony_ci	enum drbd_disk_state disk_min, disk_max, pdsk_min, pdsk_max;
105762306a36Sopenharmony_ci
105862306a36Sopenharmony_ci	if (warn)
105962306a36Sopenharmony_ci		*warn = NO_WARNING;
106062306a36Sopenharmony_ci
106162306a36Sopenharmony_ci	fp = FP_DONT_CARE;
106262306a36Sopenharmony_ci	if (get_ldev(device)) {
106362306a36Sopenharmony_ci		rcu_read_lock();
106462306a36Sopenharmony_ci		fp = rcu_dereference(device->ldev->disk_conf)->fencing;
106562306a36Sopenharmony_ci		rcu_read_unlock();
106662306a36Sopenharmony_ci		put_ldev(device);
106762306a36Sopenharmony_ci	}
106862306a36Sopenharmony_ci
106962306a36Sopenharmony_ci	/* Implications from connection to peer and peer_isp */
107062306a36Sopenharmony_ci	if (ns.conn < C_CONNECTED) {
107162306a36Sopenharmony_ci		ns.peer_isp = 0;
107262306a36Sopenharmony_ci		ns.peer = R_UNKNOWN;
107362306a36Sopenharmony_ci		if (ns.pdsk > D_UNKNOWN || ns.pdsk < D_INCONSISTENT)
107462306a36Sopenharmony_ci			ns.pdsk = D_UNKNOWN;
107562306a36Sopenharmony_ci	}
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	/* Clear the aftr_isp when becoming unconfigured */
107862306a36Sopenharmony_ci	if (ns.conn == C_STANDALONE && ns.disk == D_DISKLESS && ns.role == R_SECONDARY)
107962306a36Sopenharmony_ci		ns.aftr_isp = 0;
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	/* An implication of the disk states onto the connection state */
108262306a36Sopenharmony_ci	/* Abort resync if a disk fails/detaches */
108362306a36Sopenharmony_ci	if (ns.conn > C_CONNECTED && (ns.disk <= D_FAILED || ns.pdsk <= D_FAILED)) {
108462306a36Sopenharmony_ci		if (warn)
108562306a36Sopenharmony_ci			*warn = ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T ?
108662306a36Sopenharmony_ci				ABORTED_ONLINE_VERIFY : ABORTED_RESYNC;
108762306a36Sopenharmony_ci		ns.conn = C_CONNECTED;
108862306a36Sopenharmony_ci	}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	/* Connection breaks down before we finished "Negotiating" */
109162306a36Sopenharmony_ci	if (ns.conn < C_CONNECTED && ns.disk == D_NEGOTIATING &&
109262306a36Sopenharmony_ci	    get_ldev_if_state(device, D_NEGOTIATING)) {
109362306a36Sopenharmony_ci		if (device->ed_uuid == device->ldev->md.uuid[UI_CURRENT]) {
109462306a36Sopenharmony_ci			ns.disk = device->new_state_tmp.disk;
109562306a36Sopenharmony_ci			ns.pdsk = device->new_state_tmp.pdsk;
109662306a36Sopenharmony_ci		} else {
109762306a36Sopenharmony_ci			if (warn)
109862306a36Sopenharmony_ci				*warn = CONNECTION_LOST_NEGOTIATING;
109962306a36Sopenharmony_ci			ns.disk = D_DISKLESS;
110062306a36Sopenharmony_ci			ns.pdsk = D_UNKNOWN;
110162306a36Sopenharmony_ci		}
110262306a36Sopenharmony_ci		put_ldev(device);
110362306a36Sopenharmony_ci	}
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	/* D_CONSISTENT and D_OUTDATED vanish when we get connected */
110662306a36Sopenharmony_ci	if (ns.conn >= C_CONNECTED && ns.conn < C_AHEAD) {
110762306a36Sopenharmony_ci		if (ns.disk == D_CONSISTENT || ns.disk == D_OUTDATED)
110862306a36Sopenharmony_ci			ns.disk = D_UP_TO_DATE;
110962306a36Sopenharmony_ci		if (ns.pdsk == D_CONSISTENT || ns.pdsk == D_OUTDATED)
111062306a36Sopenharmony_ci			ns.pdsk = D_UP_TO_DATE;
111162306a36Sopenharmony_ci	}
111262306a36Sopenharmony_ci
111362306a36Sopenharmony_ci	/* Implications of the connection state on the disk states */
111462306a36Sopenharmony_ci	disk_min = D_DISKLESS;
111562306a36Sopenharmony_ci	disk_max = D_UP_TO_DATE;
111662306a36Sopenharmony_ci	pdsk_min = D_INCONSISTENT;
111762306a36Sopenharmony_ci	pdsk_max = D_UNKNOWN;
111862306a36Sopenharmony_ci	switch ((enum drbd_conns)ns.conn) {
111962306a36Sopenharmony_ci	case C_WF_BITMAP_T:
112062306a36Sopenharmony_ci	case C_PAUSED_SYNC_T:
112162306a36Sopenharmony_ci	case C_STARTING_SYNC_T:
112262306a36Sopenharmony_ci	case C_WF_SYNC_UUID:
112362306a36Sopenharmony_ci	case C_BEHIND:
112462306a36Sopenharmony_ci		disk_min = D_INCONSISTENT;
112562306a36Sopenharmony_ci		disk_max = D_OUTDATED;
112662306a36Sopenharmony_ci		pdsk_min = D_UP_TO_DATE;
112762306a36Sopenharmony_ci		pdsk_max = D_UP_TO_DATE;
112862306a36Sopenharmony_ci		break;
112962306a36Sopenharmony_ci	case C_VERIFY_S:
113062306a36Sopenharmony_ci	case C_VERIFY_T:
113162306a36Sopenharmony_ci		disk_min = D_UP_TO_DATE;
113262306a36Sopenharmony_ci		disk_max = D_UP_TO_DATE;
113362306a36Sopenharmony_ci		pdsk_min = D_UP_TO_DATE;
113462306a36Sopenharmony_ci		pdsk_max = D_UP_TO_DATE;
113562306a36Sopenharmony_ci		break;
113662306a36Sopenharmony_ci	case C_CONNECTED:
113762306a36Sopenharmony_ci		disk_min = D_DISKLESS;
113862306a36Sopenharmony_ci		disk_max = D_UP_TO_DATE;
113962306a36Sopenharmony_ci		pdsk_min = D_DISKLESS;
114062306a36Sopenharmony_ci		pdsk_max = D_UP_TO_DATE;
114162306a36Sopenharmony_ci		break;
114262306a36Sopenharmony_ci	case C_WF_BITMAP_S:
114362306a36Sopenharmony_ci	case C_PAUSED_SYNC_S:
114462306a36Sopenharmony_ci	case C_STARTING_SYNC_S:
114562306a36Sopenharmony_ci	case C_AHEAD:
114662306a36Sopenharmony_ci		disk_min = D_UP_TO_DATE;
114762306a36Sopenharmony_ci		disk_max = D_UP_TO_DATE;
114862306a36Sopenharmony_ci		pdsk_min = D_INCONSISTENT;
114962306a36Sopenharmony_ci		pdsk_max = D_CONSISTENT; /* D_OUTDATED would be nice. But explicit outdate necessary*/
115062306a36Sopenharmony_ci		break;
115162306a36Sopenharmony_ci	case C_SYNC_TARGET:
115262306a36Sopenharmony_ci		disk_min = D_INCONSISTENT;
115362306a36Sopenharmony_ci		disk_max = D_INCONSISTENT;
115462306a36Sopenharmony_ci		pdsk_min = D_UP_TO_DATE;
115562306a36Sopenharmony_ci		pdsk_max = D_UP_TO_DATE;
115662306a36Sopenharmony_ci		break;
115762306a36Sopenharmony_ci	case C_SYNC_SOURCE:
115862306a36Sopenharmony_ci		disk_min = D_UP_TO_DATE;
115962306a36Sopenharmony_ci		disk_max = D_UP_TO_DATE;
116062306a36Sopenharmony_ci		pdsk_min = D_INCONSISTENT;
116162306a36Sopenharmony_ci		pdsk_max = D_INCONSISTENT;
116262306a36Sopenharmony_ci		break;
116362306a36Sopenharmony_ci	case C_STANDALONE:
116462306a36Sopenharmony_ci	case C_DISCONNECTING:
116562306a36Sopenharmony_ci	case C_UNCONNECTED:
116662306a36Sopenharmony_ci	case C_TIMEOUT:
116762306a36Sopenharmony_ci	case C_BROKEN_PIPE:
116862306a36Sopenharmony_ci	case C_NETWORK_FAILURE:
116962306a36Sopenharmony_ci	case C_PROTOCOL_ERROR:
117062306a36Sopenharmony_ci	case C_TEAR_DOWN:
117162306a36Sopenharmony_ci	case C_WF_CONNECTION:
117262306a36Sopenharmony_ci	case C_WF_REPORT_PARAMS:
117362306a36Sopenharmony_ci	case C_MASK:
117462306a36Sopenharmony_ci		break;
117562306a36Sopenharmony_ci	}
117662306a36Sopenharmony_ci	if (ns.disk > disk_max)
117762306a36Sopenharmony_ci		ns.disk = disk_max;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	if (ns.disk < disk_min) {
118062306a36Sopenharmony_ci		if (warn)
118162306a36Sopenharmony_ci			*warn = IMPLICITLY_UPGRADED_DISK;
118262306a36Sopenharmony_ci		ns.disk = disk_min;
118362306a36Sopenharmony_ci	}
118462306a36Sopenharmony_ci	if (ns.pdsk > pdsk_max)
118562306a36Sopenharmony_ci		ns.pdsk = pdsk_max;
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	if (ns.pdsk < pdsk_min) {
118862306a36Sopenharmony_ci		if (warn)
118962306a36Sopenharmony_ci			*warn = IMPLICITLY_UPGRADED_PDSK;
119062306a36Sopenharmony_ci		ns.pdsk = pdsk_min;
119162306a36Sopenharmony_ci	}
119262306a36Sopenharmony_ci
119362306a36Sopenharmony_ci	if (fp == FP_STONITH &&
119462306a36Sopenharmony_ci	    (ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.pdsk > D_OUTDATED) &&
119562306a36Sopenharmony_ci	    !(os.role == R_PRIMARY && os.conn < C_CONNECTED && os.pdsk > D_OUTDATED))
119662306a36Sopenharmony_ci		ns.susp_fen = 1; /* Suspend IO while fence-peer handler runs (peer lost) */
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	if (device->resource->res_opts.on_no_data == OND_SUSPEND_IO &&
119962306a36Sopenharmony_ci	    (ns.role == R_PRIMARY && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE) &&
120062306a36Sopenharmony_ci	    !(os.role == R_PRIMARY && os.disk < D_UP_TO_DATE && os.pdsk < D_UP_TO_DATE))
120162306a36Sopenharmony_ci		ns.susp_nod = 1; /* Suspend IO while no data available (no accessible data available) */
120262306a36Sopenharmony_ci
120362306a36Sopenharmony_ci	if (ns.aftr_isp || ns.peer_isp || ns.user_isp) {
120462306a36Sopenharmony_ci		if (ns.conn == C_SYNC_SOURCE)
120562306a36Sopenharmony_ci			ns.conn = C_PAUSED_SYNC_S;
120662306a36Sopenharmony_ci		if (ns.conn == C_SYNC_TARGET)
120762306a36Sopenharmony_ci			ns.conn = C_PAUSED_SYNC_T;
120862306a36Sopenharmony_ci	} else {
120962306a36Sopenharmony_ci		if (ns.conn == C_PAUSED_SYNC_S)
121062306a36Sopenharmony_ci			ns.conn = C_SYNC_SOURCE;
121162306a36Sopenharmony_ci		if (ns.conn == C_PAUSED_SYNC_T)
121262306a36Sopenharmony_ci			ns.conn = C_SYNC_TARGET;
121362306a36Sopenharmony_ci	}
121462306a36Sopenharmony_ci
121562306a36Sopenharmony_ci	return ns;
121662306a36Sopenharmony_ci}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_civoid drbd_resume_al(struct drbd_device *device)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	if (test_and_clear_bit(AL_SUSPENDED, &device->flags))
122162306a36Sopenharmony_ci		drbd_info(device, "Resumed AL updates\n");
122262306a36Sopenharmony_ci}
122362306a36Sopenharmony_ci
122462306a36Sopenharmony_ci/* helper for _drbd_set_state */
122562306a36Sopenharmony_cistatic void set_ov_position(struct drbd_peer_device *peer_device, enum drbd_conns cs)
122662306a36Sopenharmony_ci{
122762306a36Sopenharmony_ci	struct drbd_device *device = peer_device->device;
122862306a36Sopenharmony_ci
122962306a36Sopenharmony_ci	if (peer_device->connection->agreed_pro_version < 90)
123062306a36Sopenharmony_ci		device->ov_start_sector = 0;
123162306a36Sopenharmony_ci	device->rs_total = drbd_bm_bits(device);
123262306a36Sopenharmony_ci	device->ov_position = 0;
123362306a36Sopenharmony_ci	if (cs == C_VERIFY_T) {
123462306a36Sopenharmony_ci		/* starting online verify from an arbitrary position
123562306a36Sopenharmony_ci		 * does not fit well into the existing protocol.
123662306a36Sopenharmony_ci		 * on C_VERIFY_T, we initialize ov_left and friends
123762306a36Sopenharmony_ci		 * implicitly in receive_DataRequest once the
123862306a36Sopenharmony_ci		 * first P_OV_REQUEST is received */
123962306a36Sopenharmony_ci		device->ov_start_sector = ~(sector_t)0;
124062306a36Sopenharmony_ci	} else {
124162306a36Sopenharmony_ci		unsigned long bit = BM_SECT_TO_BIT(device->ov_start_sector);
124262306a36Sopenharmony_ci		if (bit >= device->rs_total) {
124362306a36Sopenharmony_ci			device->ov_start_sector =
124462306a36Sopenharmony_ci				BM_BIT_TO_SECT(device->rs_total - 1);
124562306a36Sopenharmony_ci			device->rs_total = 1;
124662306a36Sopenharmony_ci		} else
124762306a36Sopenharmony_ci			device->rs_total -= bit;
124862306a36Sopenharmony_ci		device->ov_position = device->ov_start_sector;
124962306a36Sopenharmony_ci	}
125062306a36Sopenharmony_ci	device->ov_left = device->rs_total;
125162306a36Sopenharmony_ci}
125262306a36Sopenharmony_ci
125362306a36Sopenharmony_ci/**
125462306a36Sopenharmony_ci * _drbd_set_state() - Set a new DRBD state
125562306a36Sopenharmony_ci * @device:	DRBD device.
125662306a36Sopenharmony_ci * @ns:		new state.
125762306a36Sopenharmony_ci * @flags:	Flags
125862306a36Sopenharmony_ci * @done:	Optional completion, that will get completed after the after_state_ch() finished
125962306a36Sopenharmony_ci *
126062306a36Sopenharmony_ci * Caller needs to hold req_lock. Do not call directly.
126162306a36Sopenharmony_ci */
126262306a36Sopenharmony_cienum drbd_state_rv
126362306a36Sopenharmony_ci_drbd_set_state(struct drbd_device *device, union drbd_state ns,
126462306a36Sopenharmony_ci	        enum chg_state_flags flags, struct completion *done)
126562306a36Sopenharmony_ci{
126662306a36Sopenharmony_ci	struct drbd_peer_device *peer_device = first_peer_device(device);
126762306a36Sopenharmony_ci	struct drbd_connection *connection = peer_device ? peer_device->connection : NULL;
126862306a36Sopenharmony_ci	union drbd_state os;
126962306a36Sopenharmony_ci	enum drbd_state_rv rv = SS_SUCCESS;
127062306a36Sopenharmony_ci	enum sanitize_state_warnings ssw;
127162306a36Sopenharmony_ci	struct after_state_chg_work *ascw;
127262306a36Sopenharmony_ci	struct drbd_state_change *state_change;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	os = drbd_read_state(device);
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	ns = sanitize_state(device, os, ns, &ssw);
127762306a36Sopenharmony_ci	if (ns.i == os.i)
127862306a36Sopenharmony_ci		return SS_NOTHING_TO_DO;
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci	rv = is_valid_transition(os, ns);
128162306a36Sopenharmony_ci	if (rv < SS_SUCCESS)
128262306a36Sopenharmony_ci		return rv;
128362306a36Sopenharmony_ci
128462306a36Sopenharmony_ci	if (!(flags & CS_HARD)) {
128562306a36Sopenharmony_ci		/*  pre-state-change checks ; only look at ns  */
128662306a36Sopenharmony_ci		/* See drbd_state_sw_errors in drbd_strings.c */
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci		rv = is_valid_state(device, ns);
128962306a36Sopenharmony_ci		if (rv < SS_SUCCESS) {
129062306a36Sopenharmony_ci			/* If the old state was illegal as well, then let
129162306a36Sopenharmony_ci			   this happen...*/
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_ci			if (is_valid_state(device, os) == rv)
129462306a36Sopenharmony_ci				rv = is_valid_soft_transition(os, ns, connection);
129562306a36Sopenharmony_ci		} else
129662306a36Sopenharmony_ci			rv = is_valid_soft_transition(os, ns, connection);
129762306a36Sopenharmony_ci	}
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	if (rv < SS_SUCCESS) {
130062306a36Sopenharmony_ci		if (flags & CS_VERBOSE)
130162306a36Sopenharmony_ci			print_st_err(device, os, ns, rv);
130262306a36Sopenharmony_ci		return rv;
130362306a36Sopenharmony_ci	}
130462306a36Sopenharmony_ci
130562306a36Sopenharmony_ci	print_sanitize_warnings(device, ssw);
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_ci	drbd_pr_state_change(device, os, ns, flags);
130862306a36Sopenharmony_ci
130962306a36Sopenharmony_ci	/* Display changes to the susp* flags that where caused by the call to
131062306a36Sopenharmony_ci	   sanitize_state(). Only display it here if we where not called from
131162306a36Sopenharmony_ci	   _conn_request_state() */
131262306a36Sopenharmony_ci	if (!(flags & CS_DC_SUSP))
131362306a36Sopenharmony_ci		conn_pr_state_change(connection, os, ns,
131462306a36Sopenharmony_ci				     (flags & ~CS_DC_MASK) | CS_DC_SUSP);
131562306a36Sopenharmony_ci
131662306a36Sopenharmony_ci	/* if we are going -> D_FAILED or D_DISKLESS, grab one extra reference
131762306a36Sopenharmony_ci	 * on the ldev here, to be sure the transition -> D_DISKLESS resp.
131862306a36Sopenharmony_ci	 * drbd_ldev_destroy() won't happen before our corresponding
131962306a36Sopenharmony_ci	 * after_state_ch works run, where we put_ldev again. */
132062306a36Sopenharmony_ci	if ((os.disk != D_FAILED && ns.disk == D_FAILED) ||
132162306a36Sopenharmony_ci	    (os.disk != D_DISKLESS && ns.disk == D_DISKLESS))
132262306a36Sopenharmony_ci		atomic_inc(&device->local_cnt);
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci	if (!is_sync_state(os.conn) && is_sync_state(ns.conn))
132562306a36Sopenharmony_ci		clear_bit(RS_DONE, &device->flags);
132662306a36Sopenharmony_ci
132762306a36Sopenharmony_ci	/* FIXME: Have any flags been set earlier in this function already? */
132862306a36Sopenharmony_ci	state_change = remember_old_state(device->resource, GFP_ATOMIC);
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci	/* changes to local_cnt and device flags should be visible before
133162306a36Sopenharmony_ci	 * changes to state, which again should be visible before anything else
133262306a36Sopenharmony_ci	 * depending on that change happens. */
133362306a36Sopenharmony_ci	smp_wmb();
133462306a36Sopenharmony_ci	device->state.i = ns.i;
133562306a36Sopenharmony_ci	device->resource->susp = ns.susp;
133662306a36Sopenharmony_ci	device->resource->susp_nod = ns.susp_nod;
133762306a36Sopenharmony_ci	device->resource->susp_fen = ns.susp_fen;
133862306a36Sopenharmony_ci	smp_wmb();
133962306a36Sopenharmony_ci
134062306a36Sopenharmony_ci	remember_new_state(state_change);
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	/* put replicated vs not-replicated requests in seperate epochs */
134362306a36Sopenharmony_ci	if (drbd_should_do_remote((union drbd_dev_state)os.i) !=
134462306a36Sopenharmony_ci	    drbd_should_do_remote((union drbd_dev_state)ns.i))
134562306a36Sopenharmony_ci		start_new_tl_epoch(connection);
134662306a36Sopenharmony_ci
134762306a36Sopenharmony_ci	if (os.disk == D_ATTACHING && ns.disk >= D_NEGOTIATING)
134862306a36Sopenharmony_ci		drbd_print_uuids(device, "attached to UUIDs");
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	/* Wake up role changes, that were delayed because of connection establishing */
135162306a36Sopenharmony_ci	if (os.conn == C_WF_REPORT_PARAMS && ns.conn != C_WF_REPORT_PARAMS &&
135262306a36Sopenharmony_ci	    no_peer_wf_report_params(connection)) {
135362306a36Sopenharmony_ci		clear_bit(STATE_SENT, &connection->flags);
135462306a36Sopenharmony_ci		wake_up_all_devices(connection);
135562306a36Sopenharmony_ci	}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	wake_up(&device->misc_wait);
135862306a36Sopenharmony_ci	wake_up(&device->state_wait);
135962306a36Sopenharmony_ci	wake_up(&connection->ping_wait);
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	/* Aborted verify run, or we reached the stop sector.
136262306a36Sopenharmony_ci	 * Log the last position, unless end-of-device. */
136362306a36Sopenharmony_ci	if ((os.conn == C_VERIFY_S || os.conn == C_VERIFY_T) &&
136462306a36Sopenharmony_ci	    ns.conn <= C_CONNECTED) {
136562306a36Sopenharmony_ci		device->ov_start_sector =
136662306a36Sopenharmony_ci			BM_BIT_TO_SECT(drbd_bm_bits(device) - device->ov_left);
136762306a36Sopenharmony_ci		if (device->ov_left)
136862306a36Sopenharmony_ci			drbd_info(device, "Online Verify reached sector %llu\n",
136962306a36Sopenharmony_ci				(unsigned long long)device->ov_start_sector);
137062306a36Sopenharmony_ci	}
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	if ((os.conn == C_PAUSED_SYNC_T || os.conn == C_PAUSED_SYNC_S) &&
137362306a36Sopenharmony_ci	    (ns.conn == C_SYNC_TARGET  || ns.conn == C_SYNC_SOURCE)) {
137462306a36Sopenharmony_ci		drbd_info(device, "Syncer continues.\n");
137562306a36Sopenharmony_ci		device->rs_paused += (long)jiffies
137662306a36Sopenharmony_ci				  -(long)device->rs_mark_time[device->rs_last_mark];
137762306a36Sopenharmony_ci		if (ns.conn == C_SYNC_TARGET)
137862306a36Sopenharmony_ci			mod_timer(&device->resync_timer, jiffies);
137962306a36Sopenharmony_ci	}
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_ci	if ((os.conn == C_SYNC_TARGET  || os.conn == C_SYNC_SOURCE) &&
138262306a36Sopenharmony_ci	    (ns.conn == C_PAUSED_SYNC_T || ns.conn == C_PAUSED_SYNC_S)) {
138362306a36Sopenharmony_ci		drbd_info(device, "Resync suspended\n");
138462306a36Sopenharmony_ci		device->rs_mark_time[device->rs_last_mark] = jiffies;
138562306a36Sopenharmony_ci	}
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	if (os.conn == C_CONNECTED &&
138862306a36Sopenharmony_ci	    (ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T)) {
138962306a36Sopenharmony_ci		unsigned long now = jiffies;
139062306a36Sopenharmony_ci		int i;
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci		set_ov_position(peer_device, ns.conn);
139362306a36Sopenharmony_ci		device->rs_start = now;
139462306a36Sopenharmony_ci		device->rs_last_sect_ev = 0;
139562306a36Sopenharmony_ci		device->ov_last_oos_size = 0;
139662306a36Sopenharmony_ci		device->ov_last_oos_start = 0;
139762306a36Sopenharmony_ci
139862306a36Sopenharmony_ci		for (i = 0; i < DRBD_SYNC_MARKS; i++) {
139962306a36Sopenharmony_ci			device->rs_mark_left[i] = device->ov_left;
140062306a36Sopenharmony_ci			device->rs_mark_time[i] = now;
140162306a36Sopenharmony_ci		}
140262306a36Sopenharmony_ci
140362306a36Sopenharmony_ci		drbd_rs_controller_reset(peer_device);
140462306a36Sopenharmony_ci
140562306a36Sopenharmony_ci		if (ns.conn == C_VERIFY_S) {
140662306a36Sopenharmony_ci			drbd_info(device, "Starting Online Verify from sector %llu\n",
140762306a36Sopenharmony_ci					(unsigned long long)device->ov_position);
140862306a36Sopenharmony_ci			mod_timer(&device->resync_timer, jiffies);
140962306a36Sopenharmony_ci		}
141062306a36Sopenharmony_ci	}
141162306a36Sopenharmony_ci
141262306a36Sopenharmony_ci	if (get_ldev(device)) {
141362306a36Sopenharmony_ci		u32 mdf = device->ldev->md.flags & ~(MDF_CONSISTENT|MDF_PRIMARY_IND|
141462306a36Sopenharmony_ci						 MDF_CONNECTED_IND|MDF_WAS_UP_TO_DATE|
141562306a36Sopenharmony_ci						 MDF_PEER_OUT_DATED|MDF_CRASHED_PRIMARY);
141662306a36Sopenharmony_ci
141762306a36Sopenharmony_ci		mdf &= ~MDF_AL_CLEAN;
141862306a36Sopenharmony_ci		if (test_bit(CRASHED_PRIMARY, &device->flags))
141962306a36Sopenharmony_ci			mdf |= MDF_CRASHED_PRIMARY;
142062306a36Sopenharmony_ci		if (device->state.role == R_PRIMARY ||
142162306a36Sopenharmony_ci		    (device->state.pdsk < D_INCONSISTENT && device->state.peer == R_PRIMARY))
142262306a36Sopenharmony_ci			mdf |= MDF_PRIMARY_IND;
142362306a36Sopenharmony_ci		if (device->state.conn > C_WF_REPORT_PARAMS)
142462306a36Sopenharmony_ci			mdf |= MDF_CONNECTED_IND;
142562306a36Sopenharmony_ci		if (device->state.disk > D_INCONSISTENT)
142662306a36Sopenharmony_ci			mdf |= MDF_CONSISTENT;
142762306a36Sopenharmony_ci		if (device->state.disk > D_OUTDATED)
142862306a36Sopenharmony_ci			mdf |= MDF_WAS_UP_TO_DATE;
142962306a36Sopenharmony_ci		if (device->state.pdsk <= D_OUTDATED && device->state.pdsk >= D_INCONSISTENT)
143062306a36Sopenharmony_ci			mdf |= MDF_PEER_OUT_DATED;
143162306a36Sopenharmony_ci		if (mdf != device->ldev->md.flags) {
143262306a36Sopenharmony_ci			device->ldev->md.flags = mdf;
143362306a36Sopenharmony_ci			drbd_md_mark_dirty(device);
143462306a36Sopenharmony_ci		}
143562306a36Sopenharmony_ci		if (os.disk < D_CONSISTENT && ns.disk >= D_CONSISTENT)
143662306a36Sopenharmony_ci			drbd_set_ed_uuid(device, device->ldev->md.uuid[UI_CURRENT]);
143762306a36Sopenharmony_ci		put_ldev(device);
143862306a36Sopenharmony_ci	}
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	/* Peer was forced D_UP_TO_DATE & R_PRIMARY, consider to resync */
144162306a36Sopenharmony_ci	if (os.disk == D_INCONSISTENT && os.pdsk == D_INCONSISTENT &&
144262306a36Sopenharmony_ci	    os.peer == R_SECONDARY && ns.peer == R_PRIMARY)
144362306a36Sopenharmony_ci		set_bit(CONSIDER_RESYNC, &device->flags);
144462306a36Sopenharmony_ci
144562306a36Sopenharmony_ci	/* Receiver should clean up itself */
144662306a36Sopenharmony_ci	if (os.conn != C_DISCONNECTING && ns.conn == C_DISCONNECTING)
144762306a36Sopenharmony_ci		drbd_thread_stop_nowait(&connection->receiver);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	/* Now the receiver finished cleaning up itself, it should die */
145062306a36Sopenharmony_ci	if (os.conn != C_STANDALONE && ns.conn == C_STANDALONE)
145162306a36Sopenharmony_ci		drbd_thread_stop_nowait(&connection->receiver);
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	/* Upon network failure, we need to restart the receiver. */
145462306a36Sopenharmony_ci	if (os.conn > C_WF_CONNECTION &&
145562306a36Sopenharmony_ci	    ns.conn <= C_TEAR_DOWN && ns.conn >= C_TIMEOUT)
145662306a36Sopenharmony_ci		drbd_thread_restart_nowait(&connection->receiver);
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_ci	/* Resume AL writing if we get a connection */
145962306a36Sopenharmony_ci	if (os.conn < C_CONNECTED && ns.conn >= C_CONNECTED) {
146062306a36Sopenharmony_ci		drbd_resume_al(device);
146162306a36Sopenharmony_ci		connection->connect_cnt++;
146262306a36Sopenharmony_ci	}
146362306a36Sopenharmony_ci
146462306a36Sopenharmony_ci	/* remember last attach time so request_timer_fn() won't
146562306a36Sopenharmony_ci	 * kill newly established sessions while we are still trying to thaw
146662306a36Sopenharmony_ci	 * previously frozen IO */
146762306a36Sopenharmony_ci	if ((os.disk == D_ATTACHING || os.disk == D_NEGOTIATING) &&
146862306a36Sopenharmony_ci	    ns.disk > D_NEGOTIATING)
146962306a36Sopenharmony_ci		device->last_reattach_jif = jiffies;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	ascw = kmalloc(sizeof(*ascw), GFP_ATOMIC);
147262306a36Sopenharmony_ci	if (ascw) {
147362306a36Sopenharmony_ci		ascw->os = os;
147462306a36Sopenharmony_ci		ascw->ns = ns;
147562306a36Sopenharmony_ci		ascw->flags = flags;
147662306a36Sopenharmony_ci		ascw->w.cb = w_after_state_ch;
147762306a36Sopenharmony_ci		ascw->device = device;
147862306a36Sopenharmony_ci		ascw->done = done;
147962306a36Sopenharmony_ci		ascw->state_change = state_change;
148062306a36Sopenharmony_ci		drbd_queue_work(&connection->sender_work,
148162306a36Sopenharmony_ci				&ascw->w);
148262306a36Sopenharmony_ci	} else {
148362306a36Sopenharmony_ci		drbd_err(device, "Could not kmalloc an ascw\n");
148462306a36Sopenharmony_ci	}
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	return rv;
148762306a36Sopenharmony_ci}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_cistatic int w_after_state_ch(struct drbd_work *w, int unused)
149062306a36Sopenharmony_ci{
149162306a36Sopenharmony_ci	struct after_state_chg_work *ascw =
149262306a36Sopenharmony_ci		container_of(w, struct after_state_chg_work, w);
149362306a36Sopenharmony_ci	struct drbd_device *device = ascw->device;
149462306a36Sopenharmony_ci
149562306a36Sopenharmony_ci	after_state_ch(device, ascw->os, ascw->ns, ascw->flags, ascw->state_change);
149662306a36Sopenharmony_ci	forget_state_change(ascw->state_change);
149762306a36Sopenharmony_ci	if (ascw->flags & CS_WAIT_COMPLETE)
149862306a36Sopenharmony_ci		complete(ascw->done);
149962306a36Sopenharmony_ci	kfree(ascw);
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	return 0;
150262306a36Sopenharmony_ci}
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_cistatic void abw_start_sync(struct drbd_device *device, int rv)
150562306a36Sopenharmony_ci{
150662306a36Sopenharmony_ci	if (rv) {
150762306a36Sopenharmony_ci		drbd_err(device, "Writing the bitmap failed not starting resync.\n");
150862306a36Sopenharmony_ci		_drbd_request_state(device, NS(conn, C_CONNECTED), CS_VERBOSE);
150962306a36Sopenharmony_ci		return;
151062306a36Sopenharmony_ci	}
151162306a36Sopenharmony_ci
151262306a36Sopenharmony_ci	switch (device->state.conn) {
151362306a36Sopenharmony_ci	case C_STARTING_SYNC_T:
151462306a36Sopenharmony_ci		_drbd_request_state(device, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE);
151562306a36Sopenharmony_ci		break;
151662306a36Sopenharmony_ci	case C_STARTING_SYNC_S:
151762306a36Sopenharmony_ci		drbd_start_resync(device, C_SYNC_SOURCE);
151862306a36Sopenharmony_ci		break;
151962306a36Sopenharmony_ci	}
152062306a36Sopenharmony_ci}
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ciint drbd_bitmap_io_from_worker(struct drbd_device *device,
152362306a36Sopenharmony_ci		int (*io_fn)(struct drbd_device *, struct drbd_peer_device *),
152462306a36Sopenharmony_ci		char *why, enum bm_flag flags,
152562306a36Sopenharmony_ci		struct drbd_peer_device *peer_device)
152662306a36Sopenharmony_ci{
152762306a36Sopenharmony_ci	int rv;
152862306a36Sopenharmony_ci
152962306a36Sopenharmony_ci	D_ASSERT(device, current == first_peer_device(device)->connection->worker.task);
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	/* open coded non-blocking drbd_suspend_io(device); */
153262306a36Sopenharmony_ci	atomic_inc(&device->suspend_cnt);
153362306a36Sopenharmony_ci
153462306a36Sopenharmony_ci	drbd_bm_lock(device, why, flags);
153562306a36Sopenharmony_ci	rv = io_fn(device, peer_device);
153662306a36Sopenharmony_ci	drbd_bm_unlock(device);
153762306a36Sopenharmony_ci
153862306a36Sopenharmony_ci	drbd_resume_io(device);
153962306a36Sopenharmony_ci
154062306a36Sopenharmony_ci	return rv;
154162306a36Sopenharmony_ci}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ciint notify_resource_state_change(struct sk_buff *skb,
154462306a36Sopenharmony_ci				  unsigned int seq,
154562306a36Sopenharmony_ci				  struct drbd_resource_state_change *resource_state_change,
154662306a36Sopenharmony_ci				  enum drbd_notification_type type)
154762306a36Sopenharmony_ci{
154862306a36Sopenharmony_ci	struct drbd_resource *resource = resource_state_change->resource;
154962306a36Sopenharmony_ci	struct resource_info resource_info = {
155062306a36Sopenharmony_ci		.res_role = resource_state_change->role[NEW],
155162306a36Sopenharmony_ci		.res_susp = resource_state_change->susp[NEW],
155262306a36Sopenharmony_ci		.res_susp_nod = resource_state_change->susp_nod[NEW],
155362306a36Sopenharmony_ci		.res_susp_fen = resource_state_change->susp_fen[NEW],
155462306a36Sopenharmony_ci	};
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	return notify_resource_state(skb, seq, resource, &resource_info, type);
155762306a36Sopenharmony_ci}
155862306a36Sopenharmony_ci
155962306a36Sopenharmony_ciint notify_connection_state_change(struct sk_buff *skb,
156062306a36Sopenharmony_ci				    unsigned int seq,
156162306a36Sopenharmony_ci				    struct drbd_connection_state_change *connection_state_change,
156262306a36Sopenharmony_ci				    enum drbd_notification_type type)
156362306a36Sopenharmony_ci{
156462306a36Sopenharmony_ci	struct drbd_connection *connection = connection_state_change->connection;
156562306a36Sopenharmony_ci	struct connection_info connection_info = {
156662306a36Sopenharmony_ci		.conn_connection_state = connection_state_change->cstate[NEW],
156762306a36Sopenharmony_ci		.conn_role = connection_state_change->peer_role[NEW],
156862306a36Sopenharmony_ci	};
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_ci	return notify_connection_state(skb, seq, connection, &connection_info, type);
157162306a36Sopenharmony_ci}
157262306a36Sopenharmony_ci
157362306a36Sopenharmony_ciint notify_device_state_change(struct sk_buff *skb,
157462306a36Sopenharmony_ci				unsigned int seq,
157562306a36Sopenharmony_ci				struct drbd_device_state_change *device_state_change,
157662306a36Sopenharmony_ci				enum drbd_notification_type type)
157762306a36Sopenharmony_ci{
157862306a36Sopenharmony_ci	struct drbd_device *device = device_state_change->device;
157962306a36Sopenharmony_ci	struct device_info device_info = {
158062306a36Sopenharmony_ci		.dev_disk_state = device_state_change->disk_state[NEW],
158162306a36Sopenharmony_ci	};
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	return notify_device_state(skb, seq, device, &device_info, type);
158462306a36Sopenharmony_ci}
158562306a36Sopenharmony_ci
158662306a36Sopenharmony_ciint notify_peer_device_state_change(struct sk_buff *skb,
158762306a36Sopenharmony_ci				     unsigned int seq,
158862306a36Sopenharmony_ci				     struct drbd_peer_device_state_change *p,
158962306a36Sopenharmony_ci				     enum drbd_notification_type type)
159062306a36Sopenharmony_ci{
159162306a36Sopenharmony_ci	struct drbd_peer_device *peer_device = p->peer_device;
159262306a36Sopenharmony_ci	struct peer_device_info peer_device_info = {
159362306a36Sopenharmony_ci		.peer_repl_state = p->repl_state[NEW],
159462306a36Sopenharmony_ci		.peer_disk_state = p->disk_state[NEW],
159562306a36Sopenharmony_ci		.peer_resync_susp_user = p->resync_susp_user[NEW],
159662306a36Sopenharmony_ci		.peer_resync_susp_peer = p->resync_susp_peer[NEW],
159762306a36Sopenharmony_ci		.peer_resync_susp_dependency = p->resync_susp_dependency[NEW],
159862306a36Sopenharmony_ci	};
159962306a36Sopenharmony_ci
160062306a36Sopenharmony_ci	return notify_peer_device_state(skb, seq, peer_device, &peer_device_info, type);
160162306a36Sopenharmony_ci}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_cistatic void broadcast_state_change(struct drbd_state_change *state_change)
160462306a36Sopenharmony_ci{
160562306a36Sopenharmony_ci	struct drbd_resource_state_change *resource_state_change = &state_change->resource[0];
160662306a36Sopenharmony_ci	bool resource_state_has_changed;
160762306a36Sopenharmony_ci	unsigned int n_device, n_connection, n_peer_device, n_peer_devices;
160862306a36Sopenharmony_ci	int (*last_func)(struct sk_buff *, unsigned int, void *,
160962306a36Sopenharmony_ci			  enum drbd_notification_type) = NULL;
161062306a36Sopenharmony_ci	void *last_arg = NULL;
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_ci#define HAS_CHANGED(state) ((state)[OLD] != (state)[NEW])
161362306a36Sopenharmony_ci#define FINAL_STATE_CHANGE(type) \
161462306a36Sopenharmony_ci	({ if (last_func) \
161562306a36Sopenharmony_ci		last_func(NULL, 0, last_arg, type); \
161662306a36Sopenharmony_ci	})
161762306a36Sopenharmony_ci#define REMEMBER_STATE_CHANGE(func, arg, type) \
161862306a36Sopenharmony_ci	({ FINAL_STATE_CHANGE(type | NOTIFY_CONTINUES); \
161962306a36Sopenharmony_ci	   last_func = (typeof(last_func))func; \
162062306a36Sopenharmony_ci	   last_arg = arg; \
162162306a36Sopenharmony_ci	 })
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	mutex_lock(&notification_mutex);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	resource_state_has_changed =
162662306a36Sopenharmony_ci	    HAS_CHANGED(resource_state_change->role) ||
162762306a36Sopenharmony_ci	    HAS_CHANGED(resource_state_change->susp) ||
162862306a36Sopenharmony_ci	    HAS_CHANGED(resource_state_change->susp_nod) ||
162962306a36Sopenharmony_ci	    HAS_CHANGED(resource_state_change->susp_fen);
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	if (resource_state_has_changed)
163262306a36Sopenharmony_ci		REMEMBER_STATE_CHANGE(notify_resource_state_change,
163362306a36Sopenharmony_ci				      resource_state_change, NOTIFY_CHANGE);
163462306a36Sopenharmony_ci
163562306a36Sopenharmony_ci	for (n_connection = 0; n_connection < state_change->n_connections; n_connection++) {
163662306a36Sopenharmony_ci		struct drbd_connection_state_change *connection_state_change =
163762306a36Sopenharmony_ci				&state_change->connections[n_connection];
163862306a36Sopenharmony_ci
163962306a36Sopenharmony_ci		if (HAS_CHANGED(connection_state_change->peer_role) ||
164062306a36Sopenharmony_ci		    HAS_CHANGED(connection_state_change->cstate))
164162306a36Sopenharmony_ci			REMEMBER_STATE_CHANGE(notify_connection_state_change,
164262306a36Sopenharmony_ci					      connection_state_change, NOTIFY_CHANGE);
164362306a36Sopenharmony_ci	}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	for (n_device = 0; n_device < state_change->n_devices; n_device++) {
164662306a36Sopenharmony_ci		struct drbd_device_state_change *device_state_change =
164762306a36Sopenharmony_ci			&state_change->devices[n_device];
164862306a36Sopenharmony_ci
164962306a36Sopenharmony_ci		if (HAS_CHANGED(device_state_change->disk_state))
165062306a36Sopenharmony_ci			REMEMBER_STATE_CHANGE(notify_device_state_change,
165162306a36Sopenharmony_ci					      device_state_change, NOTIFY_CHANGE);
165262306a36Sopenharmony_ci	}
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	n_peer_devices = state_change->n_devices * state_change->n_connections;
165562306a36Sopenharmony_ci	for (n_peer_device = 0; n_peer_device < n_peer_devices; n_peer_device++) {
165662306a36Sopenharmony_ci		struct drbd_peer_device_state_change *p =
165762306a36Sopenharmony_ci			&state_change->peer_devices[n_peer_device];
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_ci		if (HAS_CHANGED(p->disk_state) ||
166062306a36Sopenharmony_ci		    HAS_CHANGED(p->repl_state) ||
166162306a36Sopenharmony_ci		    HAS_CHANGED(p->resync_susp_user) ||
166262306a36Sopenharmony_ci		    HAS_CHANGED(p->resync_susp_peer) ||
166362306a36Sopenharmony_ci		    HAS_CHANGED(p->resync_susp_dependency))
166462306a36Sopenharmony_ci			REMEMBER_STATE_CHANGE(notify_peer_device_state_change,
166562306a36Sopenharmony_ci					      p, NOTIFY_CHANGE);
166662306a36Sopenharmony_ci	}
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	FINAL_STATE_CHANGE(NOTIFY_CHANGE);
166962306a36Sopenharmony_ci	mutex_unlock(&notification_mutex);
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci#undef HAS_CHANGED
167262306a36Sopenharmony_ci#undef FINAL_STATE_CHANGE
167362306a36Sopenharmony_ci#undef REMEMBER_STATE_CHANGE
167462306a36Sopenharmony_ci}
167562306a36Sopenharmony_ci
167662306a36Sopenharmony_ci/* takes old and new peer disk state */
167762306a36Sopenharmony_cistatic bool lost_contact_to_peer_data(enum drbd_disk_state os, enum drbd_disk_state ns)
167862306a36Sopenharmony_ci{
167962306a36Sopenharmony_ci	if ((os >= D_INCONSISTENT && os != D_UNKNOWN && os != D_OUTDATED)
168062306a36Sopenharmony_ci	&&  (ns < D_INCONSISTENT || ns == D_UNKNOWN || ns == D_OUTDATED))
168162306a36Sopenharmony_ci		return true;
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ci	/* Scenario, starting with normal operation
168462306a36Sopenharmony_ci	 * Connected Primary/Secondary UpToDate/UpToDate
168562306a36Sopenharmony_ci	 * NetworkFailure Primary/Unknown UpToDate/DUnknown (frozen)
168662306a36Sopenharmony_ci	 * ...
168762306a36Sopenharmony_ci	 * Connected Primary/Secondary UpToDate/Diskless (resumed; needs to bump uuid!)
168862306a36Sopenharmony_ci	 */
168962306a36Sopenharmony_ci	if (os == D_UNKNOWN
169062306a36Sopenharmony_ci	&&  (ns == D_DISKLESS || ns == D_FAILED || ns == D_OUTDATED))
169162306a36Sopenharmony_ci		return true;
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	return false;
169462306a36Sopenharmony_ci}
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci/**
169762306a36Sopenharmony_ci * after_state_ch() - Perform after state change actions that may sleep
169862306a36Sopenharmony_ci * @device:	DRBD device.
169962306a36Sopenharmony_ci * @os:		old state.
170062306a36Sopenharmony_ci * @ns:		new state.
170162306a36Sopenharmony_ci * @flags:	Flags
170262306a36Sopenharmony_ci * @state_change: state change to broadcast
170362306a36Sopenharmony_ci */
170462306a36Sopenharmony_cistatic void after_state_ch(struct drbd_device *device, union drbd_state os,
170562306a36Sopenharmony_ci			   union drbd_state ns, enum chg_state_flags flags,
170662306a36Sopenharmony_ci			   struct drbd_state_change *state_change)
170762306a36Sopenharmony_ci{
170862306a36Sopenharmony_ci	struct drbd_resource *resource = device->resource;
170962306a36Sopenharmony_ci	struct drbd_peer_device *peer_device = first_peer_device(device);
171062306a36Sopenharmony_ci	struct drbd_connection *connection = peer_device ? peer_device->connection : NULL;
171162306a36Sopenharmony_ci	struct sib_info sib;
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	broadcast_state_change(state_change);
171462306a36Sopenharmony_ci
171562306a36Sopenharmony_ci	sib.sib_reason = SIB_STATE_CHANGE;
171662306a36Sopenharmony_ci	sib.os = os;
171762306a36Sopenharmony_ci	sib.ns = ns;
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	if ((os.disk != D_UP_TO_DATE || os.pdsk != D_UP_TO_DATE)
172062306a36Sopenharmony_ci	&&  (ns.disk == D_UP_TO_DATE && ns.pdsk == D_UP_TO_DATE)) {
172162306a36Sopenharmony_ci		clear_bit(CRASHED_PRIMARY, &device->flags);
172262306a36Sopenharmony_ci		if (device->p_uuid)
172362306a36Sopenharmony_ci			device->p_uuid[UI_FLAGS] &= ~((u64)2);
172462306a36Sopenharmony_ci	}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	/* Inform userspace about the change... */
172762306a36Sopenharmony_ci	drbd_bcast_event(device, &sib);
172862306a36Sopenharmony_ci
172962306a36Sopenharmony_ci	if (!(os.role == R_PRIMARY && os.disk < D_UP_TO_DATE && os.pdsk < D_UP_TO_DATE) &&
173062306a36Sopenharmony_ci	    (ns.role == R_PRIMARY && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE))
173162306a36Sopenharmony_ci		drbd_khelper(device, "pri-on-incon-degr");
173262306a36Sopenharmony_ci
173362306a36Sopenharmony_ci	/* Here we have the actions that are performed after a
173462306a36Sopenharmony_ci	   state change. This function might sleep */
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	if (ns.susp_nod) {
173762306a36Sopenharmony_ci		enum drbd_req_event what = NOTHING;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci		spin_lock_irq(&device->resource->req_lock);
174062306a36Sopenharmony_ci		if (os.conn < C_CONNECTED && conn_lowest_conn(connection) >= C_CONNECTED)
174162306a36Sopenharmony_ci			what = RESEND;
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci		if ((os.disk == D_ATTACHING || os.disk == D_NEGOTIATING) &&
174462306a36Sopenharmony_ci		    conn_lowest_disk(connection) == D_UP_TO_DATE)
174562306a36Sopenharmony_ci			what = RESTART_FROZEN_DISK_IO;
174662306a36Sopenharmony_ci
174762306a36Sopenharmony_ci		if (resource->susp_nod && what != NOTHING) {
174862306a36Sopenharmony_ci			_tl_restart(connection, what);
174962306a36Sopenharmony_ci			_conn_request_state(connection,
175062306a36Sopenharmony_ci					    (union drbd_state) { { .susp_nod = 1 } },
175162306a36Sopenharmony_ci					    (union drbd_state) { { .susp_nod = 0 } },
175262306a36Sopenharmony_ci					    CS_VERBOSE);
175362306a36Sopenharmony_ci		}
175462306a36Sopenharmony_ci		spin_unlock_irq(&device->resource->req_lock);
175562306a36Sopenharmony_ci	}
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	if (ns.susp_fen) {
175862306a36Sopenharmony_ci		spin_lock_irq(&device->resource->req_lock);
175962306a36Sopenharmony_ci		if (resource->susp_fen && conn_lowest_conn(connection) >= C_CONNECTED) {
176062306a36Sopenharmony_ci			/* case2: The connection was established again: */
176162306a36Sopenharmony_ci			struct drbd_peer_device *peer_device;
176262306a36Sopenharmony_ci			int vnr;
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci			rcu_read_lock();
176562306a36Sopenharmony_ci			idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
176662306a36Sopenharmony_ci				clear_bit(NEW_CUR_UUID, &peer_device->device->flags);
176762306a36Sopenharmony_ci			rcu_read_unlock();
176862306a36Sopenharmony_ci
176962306a36Sopenharmony_ci			/* We should actively create a new uuid, _before_
177062306a36Sopenharmony_ci			 * we resume/resent, if the peer is diskless
177162306a36Sopenharmony_ci			 * (recovery from a multiple error scenario).
177262306a36Sopenharmony_ci			 * Currently, this happens with a slight delay
177362306a36Sopenharmony_ci			 * below when checking lost_contact_to_peer_data() ...
177462306a36Sopenharmony_ci			 */
177562306a36Sopenharmony_ci			_tl_restart(connection, RESEND);
177662306a36Sopenharmony_ci			_conn_request_state(connection,
177762306a36Sopenharmony_ci					    (union drbd_state) { { .susp_fen = 1 } },
177862306a36Sopenharmony_ci					    (union drbd_state) { { .susp_fen = 0 } },
177962306a36Sopenharmony_ci					    CS_VERBOSE);
178062306a36Sopenharmony_ci		}
178162306a36Sopenharmony_ci		spin_unlock_irq(&device->resource->req_lock);
178262306a36Sopenharmony_ci	}
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	/* Became sync source.  With protocol >= 96, we still need to send out
178562306a36Sopenharmony_ci	 * the sync uuid now. Need to do that before any drbd_send_state, or
178662306a36Sopenharmony_ci	 * the other side may go "paused sync" before receiving the sync uuids,
178762306a36Sopenharmony_ci	 * which is unexpected. */
178862306a36Sopenharmony_ci	if ((os.conn != C_SYNC_SOURCE && os.conn != C_PAUSED_SYNC_S) &&
178962306a36Sopenharmony_ci	    (ns.conn == C_SYNC_SOURCE || ns.conn == C_PAUSED_SYNC_S) &&
179062306a36Sopenharmony_ci	    connection->agreed_pro_version >= 96 && get_ldev(device)) {
179162306a36Sopenharmony_ci		drbd_gen_and_send_sync_uuid(peer_device);
179262306a36Sopenharmony_ci		put_ldev(device);
179362306a36Sopenharmony_ci	}
179462306a36Sopenharmony_ci
179562306a36Sopenharmony_ci	/* Do not change the order of the if above and the two below... */
179662306a36Sopenharmony_ci	if (os.pdsk == D_DISKLESS &&
179762306a36Sopenharmony_ci	    ns.pdsk > D_DISKLESS && ns.pdsk != D_UNKNOWN) {      /* attach on the peer */
179862306a36Sopenharmony_ci		/* we probably will start a resync soon.
179962306a36Sopenharmony_ci		 * make sure those things are properly reset. */
180062306a36Sopenharmony_ci		device->rs_total = 0;
180162306a36Sopenharmony_ci		device->rs_failed = 0;
180262306a36Sopenharmony_ci		atomic_set(&device->rs_pending_cnt, 0);
180362306a36Sopenharmony_ci		drbd_rs_cancel_all(device);
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci		drbd_send_uuids(peer_device);
180662306a36Sopenharmony_ci		drbd_send_state(peer_device, ns);
180762306a36Sopenharmony_ci	}
180862306a36Sopenharmony_ci	/* No point in queuing send_bitmap if we don't have a connection
180962306a36Sopenharmony_ci	 * anymore, so check also the _current_ state, not only the new state
181062306a36Sopenharmony_ci	 * at the time this work was queued. */
181162306a36Sopenharmony_ci	if (os.conn != C_WF_BITMAP_S && ns.conn == C_WF_BITMAP_S &&
181262306a36Sopenharmony_ci	    device->state.conn == C_WF_BITMAP_S)
181362306a36Sopenharmony_ci		drbd_queue_bitmap_io(device, &drbd_send_bitmap, NULL,
181462306a36Sopenharmony_ci				"send_bitmap (WFBitMapS)",
181562306a36Sopenharmony_ci				BM_LOCKED_TEST_ALLOWED, peer_device);
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	/* Lost contact to peer's copy of the data */
181862306a36Sopenharmony_ci	if (lost_contact_to_peer_data(os.pdsk, ns.pdsk)) {
181962306a36Sopenharmony_ci		if (get_ldev(device)) {
182062306a36Sopenharmony_ci			if ((ns.role == R_PRIMARY || ns.peer == R_PRIMARY) &&
182162306a36Sopenharmony_ci			    device->ldev->md.uuid[UI_BITMAP] == 0 && ns.disk >= D_UP_TO_DATE) {
182262306a36Sopenharmony_ci				if (drbd_suspended(device)) {
182362306a36Sopenharmony_ci					set_bit(NEW_CUR_UUID, &device->flags);
182462306a36Sopenharmony_ci				} else {
182562306a36Sopenharmony_ci					drbd_uuid_new_current(device);
182662306a36Sopenharmony_ci					drbd_send_uuids(peer_device);
182762306a36Sopenharmony_ci				}
182862306a36Sopenharmony_ci			}
182962306a36Sopenharmony_ci			put_ldev(device);
183062306a36Sopenharmony_ci		}
183162306a36Sopenharmony_ci	}
183262306a36Sopenharmony_ci
183362306a36Sopenharmony_ci	if (ns.pdsk < D_INCONSISTENT && get_ldev(device)) {
183462306a36Sopenharmony_ci		if (os.peer != R_PRIMARY && ns.peer == R_PRIMARY &&
183562306a36Sopenharmony_ci		    device->ldev->md.uuid[UI_BITMAP] == 0 && ns.disk >= D_UP_TO_DATE) {
183662306a36Sopenharmony_ci			drbd_uuid_new_current(device);
183762306a36Sopenharmony_ci			drbd_send_uuids(peer_device);
183862306a36Sopenharmony_ci		}
183962306a36Sopenharmony_ci		/* D_DISKLESS Peer becomes secondary */
184062306a36Sopenharmony_ci		if (os.peer == R_PRIMARY && ns.peer == R_SECONDARY)
184162306a36Sopenharmony_ci			/* We may still be Primary ourselves.
184262306a36Sopenharmony_ci			 * No harm done if the bitmap still changes,
184362306a36Sopenharmony_ci			 * redirtied pages will follow later. */
184462306a36Sopenharmony_ci			drbd_bitmap_io_from_worker(device, &drbd_bm_write,
184562306a36Sopenharmony_ci				"demote diskless peer", BM_LOCKED_SET_ALLOWED, peer_device);
184662306a36Sopenharmony_ci		put_ldev(device);
184762306a36Sopenharmony_ci	}
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	/* Write out all changed bits on demote.
185062306a36Sopenharmony_ci	 * Though, no need to da that just yet
185162306a36Sopenharmony_ci	 * if there is a resync going on still */
185262306a36Sopenharmony_ci	if (os.role == R_PRIMARY && ns.role == R_SECONDARY &&
185362306a36Sopenharmony_ci		device->state.conn <= C_CONNECTED && get_ldev(device)) {
185462306a36Sopenharmony_ci		/* No changes to the bitmap expected this time, so assert that,
185562306a36Sopenharmony_ci		 * even though no harm was done if it did change. */
185662306a36Sopenharmony_ci		drbd_bitmap_io_from_worker(device, &drbd_bm_write,
185762306a36Sopenharmony_ci				"demote", BM_LOCKED_TEST_ALLOWED, peer_device);
185862306a36Sopenharmony_ci		put_ldev(device);
185962306a36Sopenharmony_ci	}
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	/* Last part of the attaching process ... */
186262306a36Sopenharmony_ci	if (ns.conn >= C_CONNECTED &&
186362306a36Sopenharmony_ci	    os.disk == D_ATTACHING && ns.disk == D_NEGOTIATING) {
186462306a36Sopenharmony_ci		drbd_send_sizes(peer_device, 0, 0);  /* to start sync... */
186562306a36Sopenharmony_ci		drbd_send_uuids(peer_device);
186662306a36Sopenharmony_ci		drbd_send_state(peer_device, ns);
186762306a36Sopenharmony_ci	}
186862306a36Sopenharmony_ci
186962306a36Sopenharmony_ci	/* We want to pause/continue resync, tell peer. */
187062306a36Sopenharmony_ci	if (ns.conn >= C_CONNECTED &&
187162306a36Sopenharmony_ci	     ((os.aftr_isp != ns.aftr_isp) ||
187262306a36Sopenharmony_ci	      (os.user_isp != ns.user_isp)))
187362306a36Sopenharmony_ci		drbd_send_state(peer_device, ns);
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	/* In case one of the isp bits got set, suspend other devices. */
187662306a36Sopenharmony_ci	if ((!os.aftr_isp && !os.peer_isp && !os.user_isp) &&
187762306a36Sopenharmony_ci	    (ns.aftr_isp || ns.peer_isp || ns.user_isp))
187862306a36Sopenharmony_ci		suspend_other_sg(device);
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	/* Make sure the peer gets informed about eventual state
188162306a36Sopenharmony_ci	   changes (ISP bits) while we were in WFReportParams. */
188262306a36Sopenharmony_ci	if (os.conn == C_WF_REPORT_PARAMS && ns.conn >= C_CONNECTED)
188362306a36Sopenharmony_ci		drbd_send_state(peer_device, ns);
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	if (os.conn != C_AHEAD && ns.conn == C_AHEAD)
188662306a36Sopenharmony_ci		drbd_send_state(peer_device, ns);
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	/* We are in the progress to start a full sync... */
188962306a36Sopenharmony_ci	if ((os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) ||
189062306a36Sopenharmony_ci	    (os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S))
189162306a36Sopenharmony_ci		/* no other bitmap changes expected during this phase */
189262306a36Sopenharmony_ci		drbd_queue_bitmap_io(device,
189362306a36Sopenharmony_ci			&drbd_bmio_set_n_write, &abw_start_sync,
189462306a36Sopenharmony_ci			"set_n_write from StartingSync", BM_LOCKED_TEST_ALLOWED,
189562306a36Sopenharmony_ci			peer_device);
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	/* first half of local IO error, failure to attach,
189862306a36Sopenharmony_ci	 * or administrative detach */
189962306a36Sopenharmony_ci	if (os.disk != D_FAILED && ns.disk == D_FAILED) {
190062306a36Sopenharmony_ci		enum drbd_io_error_p eh = EP_PASS_ON;
190162306a36Sopenharmony_ci		int was_io_error = 0;
190262306a36Sopenharmony_ci		/* corresponding get_ldev was in _drbd_set_state, to serialize
190362306a36Sopenharmony_ci		 * our cleanup here with the transition to D_DISKLESS.
190462306a36Sopenharmony_ci		 * But is is still not save to dreference ldev here, since
190562306a36Sopenharmony_ci		 * we might come from an failed Attach before ldev was set. */
190662306a36Sopenharmony_ci		if (device->ldev) {
190762306a36Sopenharmony_ci			rcu_read_lock();
190862306a36Sopenharmony_ci			eh = rcu_dereference(device->ldev->disk_conf)->on_io_error;
190962306a36Sopenharmony_ci			rcu_read_unlock();
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci			was_io_error = test_and_clear_bit(WAS_IO_ERROR, &device->flags);
191262306a36Sopenharmony_ci
191362306a36Sopenharmony_ci			/* Intentionally call this handler first, before drbd_send_state().
191462306a36Sopenharmony_ci			 * See: 2932204 drbd: call local-io-error handler early
191562306a36Sopenharmony_ci			 * People may chose to hard-reset the box from this handler.
191662306a36Sopenharmony_ci			 * It is useful if this looks like a "regular node crash". */
191762306a36Sopenharmony_ci			if (was_io_error && eh == EP_CALL_HELPER)
191862306a36Sopenharmony_ci				drbd_khelper(device, "local-io-error");
191962306a36Sopenharmony_ci
192062306a36Sopenharmony_ci			/* Immediately allow completion of all application IO,
192162306a36Sopenharmony_ci			 * that waits for completion from the local disk,
192262306a36Sopenharmony_ci			 * if this was a force-detach due to disk_timeout
192362306a36Sopenharmony_ci			 * or administrator request (drbdsetup detach --force).
192462306a36Sopenharmony_ci			 * Do NOT abort otherwise.
192562306a36Sopenharmony_ci			 * Aborting local requests may cause serious problems,
192662306a36Sopenharmony_ci			 * if requests are completed to upper layers already,
192762306a36Sopenharmony_ci			 * and then later the already submitted local bio completes.
192862306a36Sopenharmony_ci			 * This can cause DMA into former bio pages that meanwhile
192962306a36Sopenharmony_ci			 * have been re-used for other things.
193062306a36Sopenharmony_ci			 * So aborting local requests may cause crashes,
193162306a36Sopenharmony_ci			 * or even worse, silent data corruption.
193262306a36Sopenharmony_ci			 */
193362306a36Sopenharmony_ci			if (test_and_clear_bit(FORCE_DETACH, &device->flags))
193462306a36Sopenharmony_ci				tl_abort_disk_io(device);
193562306a36Sopenharmony_ci
193662306a36Sopenharmony_ci			/* current state still has to be D_FAILED,
193762306a36Sopenharmony_ci			 * there is only one way out: to D_DISKLESS,
193862306a36Sopenharmony_ci			 * and that may only happen after our put_ldev below. */
193962306a36Sopenharmony_ci			if (device->state.disk != D_FAILED)
194062306a36Sopenharmony_ci				drbd_err(device,
194162306a36Sopenharmony_ci					"ASSERT FAILED: disk is %s during detach\n",
194262306a36Sopenharmony_ci					drbd_disk_str(device->state.disk));
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci			if (ns.conn >= C_CONNECTED)
194562306a36Sopenharmony_ci				drbd_send_state(peer_device, ns);
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_ci			drbd_rs_cancel_all(device);
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_ci			/* In case we want to get something to stable storage still,
195062306a36Sopenharmony_ci			 * this may be the last chance.
195162306a36Sopenharmony_ci			 * Following put_ldev may transition to D_DISKLESS. */
195262306a36Sopenharmony_ci			drbd_md_sync(device);
195362306a36Sopenharmony_ci		}
195462306a36Sopenharmony_ci		put_ldev(device);
195562306a36Sopenharmony_ci	}
195662306a36Sopenharmony_ci
195762306a36Sopenharmony_ci	/* second half of local IO error, failure to attach,
195862306a36Sopenharmony_ci	 * or administrative detach,
195962306a36Sopenharmony_ci	 * after local_cnt references have reached zero again */
196062306a36Sopenharmony_ci	if (os.disk != D_DISKLESS && ns.disk == D_DISKLESS) {
196162306a36Sopenharmony_ci		/* We must still be diskless,
196262306a36Sopenharmony_ci		 * re-attach has to be serialized with this! */
196362306a36Sopenharmony_ci		if (device->state.disk != D_DISKLESS)
196462306a36Sopenharmony_ci			drbd_err(device,
196562306a36Sopenharmony_ci				 "ASSERT FAILED: disk is %s while going diskless\n",
196662306a36Sopenharmony_ci				 drbd_disk_str(device->state.disk));
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci		if (ns.conn >= C_CONNECTED)
196962306a36Sopenharmony_ci			drbd_send_state(peer_device, ns);
197062306a36Sopenharmony_ci		/* corresponding get_ldev in __drbd_set_state
197162306a36Sopenharmony_ci		 * this may finally trigger drbd_ldev_destroy. */
197262306a36Sopenharmony_ci		put_ldev(device);
197362306a36Sopenharmony_ci	}
197462306a36Sopenharmony_ci
197562306a36Sopenharmony_ci	/* Notify peer that I had a local IO error, and did not detached.. */
197662306a36Sopenharmony_ci	if (os.disk == D_UP_TO_DATE && ns.disk == D_INCONSISTENT && ns.conn >= C_CONNECTED)
197762306a36Sopenharmony_ci		drbd_send_state(peer_device, ns);
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	/* Disks got bigger while they were detached */
198062306a36Sopenharmony_ci	if (ns.disk > D_NEGOTIATING && ns.pdsk > D_NEGOTIATING &&
198162306a36Sopenharmony_ci	    test_and_clear_bit(RESYNC_AFTER_NEG, &device->flags)) {
198262306a36Sopenharmony_ci		if (ns.conn == C_CONNECTED)
198362306a36Sopenharmony_ci			resync_after_online_grow(device);
198462306a36Sopenharmony_ci	}
198562306a36Sopenharmony_ci
198662306a36Sopenharmony_ci	/* A resync finished or aborted, wake paused devices... */
198762306a36Sopenharmony_ci	if ((os.conn > C_CONNECTED && ns.conn <= C_CONNECTED) ||
198862306a36Sopenharmony_ci	    (os.peer_isp && !ns.peer_isp) ||
198962306a36Sopenharmony_ci	    (os.user_isp && !ns.user_isp))
199062306a36Sopenharmony_ci		resume_next_sg(device);
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	/* sync target done with resync.  Explicitly notify peer, even though
199362306a36Sopenharmony_ci	 * it should (at least for non-empty resyncs) already know itself. */
199462306a36Sopenharmony_ci	if (os.disk < D_UP_TO_DATE && os.conn >= C_SYNC_SOURCE && ns.conn == C_CONNECTED)
199562306a36Sopenharmony_ci		drbd_send_state(peer_device, ns);
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	/* Verify finished, or reached stop sector.  Peer did not know about
199862306a36Sopenharmony_ci	 * the stop sector, and we may even have changed the stop sector during
199962306a36Sopenharmony_ci	 * verify to interrupt/stop early.  Send the new state. */
200062306a36Sopenharmony_ci	if (os.conn == C_VERIFY_S && ns.conn == C_CONNECTED
200162306a36Sopenharmony_ci	&& verify_can_do_stop_sector(device))
200262306a36Sopenharmony_ci		drbd_send_state(peer_device, ns);
200362306a36Sopenharmony_ci
200462306a36Sopenharmony_ci	/* This triggers bitmap writeout of potentially still unwritten pages
200562306a36Sopenharmony_ci	 * if the resync finished cleanly, or aborted because of peer disk
200662306a36Sopenharmony_ci	 * failure, or on transition from resync back to AHEAD/BEHIND.
200762306a36Sopenharmony_ci	 *
200862306a36Sopenharmony_ci	 * Connection loss is handled in drbd_disconnected() by the receiver.
200962306a36Sopenharmony_ci	 *
201062306a36Sopenharmony_ci	 * For resync aborted because of local disk failure, we cannot do
201162306a36Sopenharmony_ci	 * any bitmap writeout anymore.
201262306a36Sopenharmony_ci	 *
201362306a36Sopenharmony_ci	 * No harm done if some bits change during this phase.
201462306a36Sopenharmony_ci	 */
201562306a36Sopenharmony_ci	if ((os.conn > C_CONNECTED && os.conn < C_AHEAD) &&
201662306a36Sopenharmony_ci	    (ns.conn == C_CONNECTED || ns.conn >= C_AHEAD) && get_ldev(device)) {
201762306a36Sopenharmony_ci		drbd_queue_bitmap_io(device, &drbd_bm_write_copy_pages, NULL,
201862306a36Sopenharmony_ci			"write from resync_finished", BM_LOCKED_CHANGE_ALLOWED,
201962306a36Sopenharmony_ci			peer_device);
202062306a36Sopenharmony_ci		put_ldev(device);
202162306a36Sopenharmony_ci	}
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	if (ns.disk == D_DISKLESS &&
202462306a36Sopenharmony_ci	    ns.conn == C_STANDALONE &&
202562306a36Sopenharmony_ci	    ns.role == R_SECONDARY) {
202662306a36Sopenharmony_ci		if (os.aftr_isp != ns.aftr_isp)
202762306a36Sopenharmony_ci			resume_next_sg(device);
202862306a36Sopenharmony_ci	}
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	drbd_md_sync(device);
203162306a36Sopenharmony_ci}
203262306a36Sopenharmony_ci
203362306a36Sopenharmony_cistruct after_conn_state_chg_work {
203462306a36Sopenharmony_ci	struct drbd_work w;
203562306a36Sopenharmony_ci	enum drbd_conns oc;
203662306a36Sopenharmony_ci	union drbd_state ns_min;
203762306a36Sopenharmony_ci	union drbd_state ns_max; /* new, max state, over all devices */
203862306a36Sopenharmony_ci	enum chg_state_flags flags;
203962306a36Sopenharmony_ci	struct drbd_connection *connection;
204062306a36Sopenharmony_ci	struct drbd_state_change *state_change;
204162306a36Sopenharmony_ci};
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_cistatic int w_after_conn_state_ch(struct drbd_work *w, int unused)
204462306a36Sopenharmony_ci{
204562306a36Sopenharmony_ci	struct after_conn_state_chg_work *acscw =
204662306a36Sopenharmony_ci		container_of(w, struct after_conn_state_chg_work, w);
204762306a36Sopenharmony_ci	struct drbd_connection *connection = acscw->connection;
204862306a36Sopenharmony_ci	enum drbd_conns oc = acscw->oc;
204962306a36Sopenharmony_ci	union drbd_state ns_max = acscw->ns_max;
205062306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
205162306a36Sopenharmony_ci	int vnr;
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci	broadcast_state_change(acscw->state_change);
205462306a36Sopenharmony_ci	forget_state_change(acscw->state_change);
205562306a36Sopenharmony_ci	kfree(acscw);
205662306a36Sopenharmony_ci
205762306a36Sopenharmony_ci	/* Upon network configuration, we need to start the receiver */
205862306a36Sopenharmony_ci	if (oc == C_STANDALONE && ns_max.conn == C_UNCONNECTED)
205962306a36Sopenharmony_ci		drbd_thread_start(&connection->receiver);
206062306a36Sopenharmony_ci
206162306a36Sopenharmony_ci	if (oc == C_DISCONNECTING && ns_max.conn == C_STANDALONE) {
206262306a36Sopenharmony_ci		struct net_conf *old_conf;
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci		mutex_lock(&notification_mutex);
206562306a36Sopenharmony_ci		idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
206662306a36Sopenharmony_ci			notify_peer_device_state(NULL, 0, peer_device, NULL,
206762306a36Sopenharmony_ci						 NOTIFY_DESTROY | NOTIFY_CONTINUES);
206862306a36Sopenharmony_ci		notify_connection_state(NULL, 0, connection, NULL, NOTIFY_DESTROY);
206962306a36Sopenharmony_ci		mutex_unlock(&notification_mutex);
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci		mutex_lock(&connection->resource->conf_update);
207262306a36Sopenharmony_ci		old_conf = connection->net_conf;
207362306a36Sopenharmony_ci		connection->my_addr_len = 0;
207462306a36Sopenharmony_ci		connection->peer_addr_len = 0;
207562306a36Sopenharmony_ci		RCU_INIT_POINTER(connection->net_conf, NULL);
207662306a36Sopenharmony_ci		conn_free_crypto(connection);
207762306a36Sopenharmony_ci		mutex_unlock(&connection->resource->conf_update);
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci		kvfree_rcu_mightsleep(old_conf);
208062306a36Sopenharmony_ci	}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci	if (ns_max.susp_fen) {
208362306a36Sopenharmony_ci		/* case1: The outdate peer handler is successful: */
208462306a36Sopenharmony_ci		if (ns_max.pdsk <= D_OUTDATED) {
208562306a36Sopenharmony_ci			rcu_read_lock();
208662306a36Sopenharmony_ci			idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
208762306a36Sopenharmony_ci				struct drbd_device *device = peer_device->device;
208862306a36Sopenharmony_ci				if (test_bit(NEW_CUR_UUID, &device->flags)) {
208962306a36Sopenharmony_ci					drbd_uuid_new_current(device);
209062306a36Sopenharmony_ci					clear_bit(NEW_CUR_UUID, &device->flags);
209162306a36Sopenharmony_ci				}
209262306a36Sopenharmony_ci			}
209362306a36Sopenharmony_ci			rcu_read_unlock();
209462306a36Sopenharmony_ci			spin_lock_irq(&connection->resource->req_lock);
209562306a36Sopenharmony_ci			_tl_restart(connection, CONNECTION_LOST_WHILE_PENDING);
209662306a36Sopenharmony_ci			_conn_request_state(connection,
209762306a36Sopenharmony_ci					    (union drbd_state) { { .susp_fen = 1 } },
209862306a36Sopenharmony_ci					    (union drbd_state) { { .susp_fen = 0 } },
209962306a36Sopenharmony_ci					    CS_VERBOSE);
210062306a36Sopenharmony_ci			spin_unlock_irq(&connection->resource->req_lock);
210162306a36Sopenharmony_ci		}
210262306a36Sopenharmony_ci	}
210362306a36Sopenharmony_ci	conn_md_sync(connection);
210462306a36Sopenharmony_ci	kref_put(&connection->kref, drbd_destroy_connection);
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	return 0;
210762306a36Sopenharmony_ci}
210862306a36Sopenharmony_ci
210962306a36Sopenharmony_cistatic void conn_old_common_state(struct drbd_connection *connection, union drbd_state *pcs, enum chg_state_flags *pf)
211062306a36Sopenharmony_ci{
211162306a36Sopenharmony_ci	enum chg_state_flags flags = ~0;
211262306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
211362306a36Sopenharmony_ci	int vnr, first_vol = 1;
211462306a36Sopenharmony_ci	union drbd_dev_state os, cs = {
211562306a36Sopenharmony_ci		{ .role = R_SECONDARY,
211662306a36Sopenharmony_ci		  .peer = R_UNKNOWN,
211762306a36Sopenharmony_ci		  .conn = connection->cstate,
211862306a36Sopenharmony_ci		  .disk = D_DISKLESS,
211962306a36Sopenharmony_ci		  .pdsk = D_UNKNOWN,
212062306a36Sopenharmony_ci		} };
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_ci	rcu_read_lock();
212362306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
212462306a36Sopenharmony_ci		struct drbd_device *device = peer_device->device;
212562306a36Sopenharmony_ci		os = device->state;
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci		if (first_vol) {
212862306a36Sopenharmony_ci			cs = os;
212962306a36Sopenharmony_ci			first_vol = 0;
213062306a36Sopenharmony_ci			continue;
213162306a36Sopenharmony_ci		}
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_ci		if (cs.role != os.role)
213462306a36Sopenharmony_ci			flags &= ~CS_DC_ROLE;
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci		if (cs.peer != os.peer)
213762306a36Sopenharmony_ci			flags &= ~CS_DC_PEER;
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci		if (cs.conn != os.conn)
214062306a36Sopenharmony_ci			flags &= ~CS_DC_CONN;
214162306a36Sopenharmony_ci
214262306a36Sopenharmony_ci		if (cs.disk != os.disk)
214362306a36Sopenharmony_ci			flags &= ~CS_DC_DISK;
214462306a36Sopenharmony_ci
214562306a36Sopenharmony_ci		if (cs.pdsk != os.pdsk)
214662306a36Sopenharmony_ci			flags &= ~CS_DC_PDSK;
214762306a36Sopenharmony_ci	}
214862306a36Sopenharmony_ci	rcu_read_unlock();
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	*pf |= CS_DC_MASK;
215162306a36Sopenharmony_ci	*pf &= flags;
215262306a36Sopenharmony_ci	(*pcs).i = cs.i;
215362306a36Sopenharmony_ci}
215462306a36Sopenharmony_ci
215562306a36Sopenharmony_cistatic enum drbd_state_rv
215662306a36Sopenharmony_ciconn_is_valid_transition(struct drbd_connection *connection, union drbd_state mask, union drbd_state val,
215762306a36Sopenharmony_ci			 enum chg_state_flags flags)
215862306a36Sopenharmony_ci{
215962306a36Sopenharmony_ci	enum drbd_state_rv rv = SS_SUCCESS;
216062306a36Sopenharmony_ci	union drbd_state ns, os;
216162306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
216262306a36Sopenharmony_ci	int vnr;
216362306a36Sopenharmony_ci
216462306a36Sopenharmony_ci	rcu_read_lock();
216562306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
216662306a36Sopenharmony_ci		struct drbd_device *device = peer_device->device;
216762306a36Sopenharmony_ci		os = drbd_read_state(device);
216862306a36Sopenharmony_ci		ns = sanitize_state(device, os, apply_mask_val(os, mask, val), NULL);
216962306a36Sopenharmony_ci
217062306a36Sopenharmony_ci		if (flags & CS_IGN_OUTD_FAIL && ns.disk == D_OUTDATED && os.disk < D_OUTDATED)
217162306a36Sopenharmony_ci			ns.disk = os.disk;
217262306a36Sopenharmony_ci
217362306a36Sopenharmony_ci		if (ns.i == os.i)
217462306a36Sopenharmony_ci			continue;
217562306a36Sopenharmony_ci
217662306a36Sopenharmony_ci		rv = is_valid_transition(os, ns);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci		if (rv >= SS_SUCCESS && !(flags & CS_HARD)) {
217962306a36Sopenharmony_ci			rv = is_valid_state(device, ns);
218062306a36Sopenharmony_ci			if (rv < SS_SUCCESS) {
218162306a36Sopenharmony_ci				if (is_valid_state(device, os) == rv)
218262306a36Sopenharmony_ci					rv = is_valid_soft_transition(os, ns, connection);
218362306a36Sopenharmony_ci			} else
218462306a36Sopenharmony_ci				rv = is_valid_soft_transition(os, ns, connection);
218562306a36Sopenharmony_ci		}
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci		if (rv < SS_SUCCESS) {
218862306a36Sopenharmony_ci			if (flags & CS_VERBOSE)
218962306a36Sopenharmony_ci				print_st_err(device, os, ns, rv);
219062306a36Sopenharmony_ci			break;
219162306a36Sopenharmony_ci		}
219262306a36Sopenharmony_ci	}
219362306a36Sopenharmony_ci	rcu_read_unlock();
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	return rv;
219662306a36Sopenharmony_ci}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_cistatic void
219962306a36Sopenharmony_ciconn_set_state(struct drbd_connection *connection, union drbd_state mask, union drbd_state val,
220062306a36Sopenharmony_ci	       union drbd_state *pns_min, union drbd_state *pns_max, enum chg_state_flags flags)
220162306a36Sopenharmony_ci{
220262306a36Sopenharmony_ci	union drbd_state ns, os, ns_max = { };
220362306a36Sopenharmony_ci	union drbd_state ns_min = {
220462306a36Sopenharmony_ci		{ .role = R_MASK,
220562306a36Sopenharmony_ci		  .peer = R_MASK,
220662306a36Sopenharmony_ci		  .conn = val.conn,
220762306a36Sopenharmony_ci		  .disk = D_MASK,
220862306a36Sopenharmony_ci		  .pdsk = D_MASK
220962306a36Sopenharmony_ci		} };
221062306a36Sopenharmony_ci	struct drbd_peer_device *peer_device;
221162306a36Sopenharmony_ci	enum drbd_state_rv rv;
221262306a36Sopenharmony_ci	int vnr, number_of_volumes = 0;
221362306a36Sopenharmony_ci
221462306a36Sopenharmony_ci	if (mask.conn == C_MASK) {
221562306a36Sopenharmony_ci		/* remember last connect time so request_timer_fn() won't
221662306a36Sopenharmony_ci		 * kill newly established sessions while we are still trying to thaw
221762306a36Sopenharmony_ci		 * previously frozen IO */
221862306a36Sopenharmony_ci		if (connection->cstate != C_WF_REPORT_PARAMS && val.conn == C_WF_REPORT_PARAMS)
221962306a36Sopenharmony_ci			connection->last_reconnect_jif = jiffies;
222062306a36Sopenharmony_ci
222162306a36Sopenharmony_ci		connection->cstate = val.conn;
222262306a36Sopenharmony_ci	}
222362306a36Sopenharmony_ci
222462306a36Sopenharmony_ci	rcu_read_lock();
222562306a36Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
222662306a36Sopenharmony_ci		struct drbd_device *device = peer_device->device;
222762306a36Sopenharmony_ci		number_of_volumes++;
222862306a36Sopenharmony_ci		os = drbd_read_state(device);
222962306a36Sopenharmony_ci		ns = apply_mask_val(os, mask, val);
223062306a36Sopenharmony_ci		ns = sanitize_state(device, os, ns, NULL);
223162306a36Sopenharmony_ci
223262306a36Sopenharmony_ci		if (flags & CS_IGN_OUTD_FAIL && ns.disk == D_OUTDATED && os.disk < D_OUTDATED)
223362306a36Sopenharmony_ci			ns.disk = os.disk;
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci		rv = _drbd_set_state(device, ns, flags, NULL);
223662306a36Sopenharmony_ci		BUG_ON(rv < SS_SUCCESS);
223762306a36Sopenharmony_ci		ns.i = device->state.i;
223862306a36Sopenharmony_ci		ns_max.role = max_role(ns.role, ns_max.role);
223962306a36Sopenharmony_ci		ns_max.peer = max_role(ns.peer, ns_max.peer);
224062306a36Sopenharmony_ci		ns_max.conn = max_t(enum drbd_conns, ns.conn, ns_max.conn);
224162306a36Sopenharmony_ci		ns_max.disk = max_t(enum drbd_disk_state, ns.disk, ns_max.disk);
224262306a36Sopenharmony_ci		ns_max.pdsk = max_t(enum drbd_disk_state, ns.pdsk, ns_max.pdsk);
224362306a36Sopenharmony_ci
224462306a36Sopenharmony_ci		ns_min.role = min_role(ns.role, ns_min.role);
224562306a36Sopenharmony_ci		ns_min.peer = min_role(ns.peer, ns_min.peer);
224662306a36Sopenharmony_ci		ns_min.conn = min_t(enum drbd_conns, ns.conn, ns_min.conn);
224762306a36Sopenharmony_ci		ns_min.disk = min_t(enum drbd_disk_state, ns.disk, ns_min.disk);
224862306a36Sopenharmony_ci		ns_min.pdsk = min_t(enum drbd_disk_state, ns.pdsk, ns_min.pdsk);
224962306a36Sopenharmony_ci	}
225062306a36Sopenharmony_ci	rcu_read_unlock();
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	if (number_of_volumes == 0) {
225362306a36Sopenharmony_ci		ns_min = ns_max = (union drbd_state) { {
225462306a36Sopenharmony_ci				.role = R_SECONDARY,
225562306a36Sopenharmony_ci				.peer = R_UNKNOWN,
225662306a36Sopenharmony_ci				.conn = val.conn,
225762306a36Sopenharmony_ci				.disk = D_DISKLESS,
225862306a36Sopenharmony_ci				.pdsk = D_UNKNOWN
225962306a36Sopenharmony_ci			} };
226062306a36Sopenharmony_ci	}
226162306a36Sopenharmony_ci
226262306a36Sopenharmony_ci	ns_min.susp = ns_max.susp = connection->resource->susp;
226362306a36Sopenharmony_ci	ns_min.susp_nod = ns_max.susp_nod = connection->resource->susp_nod;
226462306a36Sopenharmony_ci	ns_min.susp_fen = ns_max.susp_fen = connection->resource->susp_fen;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	*pns_min = ns_min;
226762306a36Sopenharmony_ci	*pns_max = ns_max;
226862306a36Sopenharmony_ci}
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_cistatic enum drbd_state_rv
227162306a36Sopenharmony_ci_conn_rq_cond(struct drbd_connection *connection, union drbd_state mask, union drbd_state val)
227262306a36Sopenharmony_ci{
227362306a36Sopenharmony_ci	enum drbd_state_rv err, rv = SS_UNKNOWN_ERROR; /* continue waiting */;
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci	if (test_and_clear_bit(CONN_WD_ST_CHG_OKAY, &connection->flags))
227662306a36Sopenharmony_ci		rv = SS_CW_SUCCESS;
227762306a36Sopenharmony_ci
227862306a36Sopenharmony_ci	if (test_and_clear_bit(CONN_WD_ST_CHG_FAIL, &connection->flags))
227962306a36Sopenharmony_ci		rv = SS_CW_FAILED_BY_PEER;
228062306a36Sopenharmony_ci
228162306a36Sopenharmony_ci	err = conn_is_valid_transition(connection, mask, val, 0);
228262306a36Sopenharmony_ci	if (err == SS_SUCCESS && connection->cstate == C_WF_REPORT_PARAMS)
228362306a36Sopenharmony_ci		return rv;
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	return err;
228662306a36Sopenharmony_ci}
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_cienum drbd_state_rv
228962306a36Sopenharmony_ci_conn_request_state(struct drbd_connection *connection, union drbd_state mask, union drbd_state val,
229062306a36Sopenharmony_ci		    enum chg_state_flags flags)
229162306a36Sopenharmony_ci{
229262306a36Sopenharmony_ci	enum drbd_state_rv rv = SS_SUCCESS;
229362306a36Sopenharmony_ci	struct after_conn_state_chg_work *acscw;
229462306a36Sopenharmony_ci	enum drbd_conns oc = connection->cstate;
229562306a36Sopenharmony_ci	union drbd_state ns_max, ns_min, os;
229662306a36Sopenharmony_ci	bool have_mutex = false;
229762306a36Sopenharmony_ci	struct drbd_state_change *state_change;
229862306a36Sopenharmony_ci
229962306a36Sopenharmony_ci	if (mask.conn) {
230062306a36Sopenharmony_ci		rv = is_valid_conn_transition(oc, val.conn);
230162306a36Sopenharmony_ci		if (rv < SS_SUCCESS)
230262306a36Sopenharmony_ci			goto abort;
230362306a36Sopenharmony_ci	}
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	rv = conn_is_valid_transition(connection, mask, val, flags);
230662306a36Sopenharmony_ci	if (rv < SS_SUCCESS)
230762306a36Sopenharmony_ci		goto abort;
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	if (oc == C_WF_REPORT_PARAMS && val.conn == C_DISCONNECTING &&
231062306a36Sopenharmony_ci	    !(flags & (CS_LOCAL_ONLY | CS_HARD))) {
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci		/* This will be a cluster-wide state change.
231362306a36Sopenharmony_ci		 * Need to give up the spinlock, grab the mutex,
231462306a36Sopenharmony_ci		 * then send the state change request, ... */
231562306a36Sopenharmony_ci		spin_unlock_irq(&connection->resource->req_lock);
231662306a36Sopenharmony_ci		mutex_lock(&connection->cstate_mutex);
231762306a36Sopenharmony_ci		have_mutex = true;
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci		set_bit(CONN_WD_ST_CHG_REQ, &connection->flags);
232062306a36Sopenharmony_ci		if (conn_send_state_req(connection, mask, val)) {
232162306a36Sopenharmony_ci			/* sending failed. */
232262306a36Sopenharmony_ci			clear_bit(CONN_WD_ST_CHG_REQ, &connection->flags);
232362306a36Sopenharmony_ci			rv = SS_CW_FAILED_BY_PEER;
232462306a36Sopenharmony_ci			/* need to re-aquire the spin lock, though */
232562306a36Sopenharmony_ci			goto abort_unlocked;
232662306a36Sopenharmony_ci		}
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci		if (val.conn == C_DISCONNECTING)
232962306a36Sopenharmony_ci			set_bit(DISCONNECT_SENT, &connection->flags);
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci		/* ... and re-aquire the spinlock.
233262306a36Sopenharmony_ci		 * If _conn_rq_cond() returned >= SS_SUCCESS, we must call
233362306a36Sopenharmony_ci		 * conn_set_state() within the same spinlock. */
233462306a36Sopenharmony_ci		spin_lock_irq(&connection->resource->req_lock);
233562306a36Sopenharmony_ci		wait_event_lock_irq(connection->ping_wait,
233662306a36Sopenharmony_ci				(rv = _conn_rq_cond(connection, mask, val)),
233762306a36Sopenharmony_ci				connection->resource->req_lock);
233862306a36Sopenharmony_ci		clear_bit(CONN_WD_ST_CHG_REQ, &connection->flags);
233962306a36Sopenharmony_ci		if (rv < SS_SUCCESS)
234062306a36Sopenharmony_ci			goto abort;
234162306a36Sopenharmony_ci	}
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	state_change = remember_old_state(connection->resource, GFP_ATOMIC);
234462306a36Sopenharmony_ci	conn_old_common_state(connection, &os, &flags);
234562306a36Sopenharmony_ci	flags |= CS_DC_SUSP;
234662306a36Sopenharmony_ci	conn_set_state(connection, mask, val, &ns_min, &ns_max, flags);
234762306a36Sopenharmony_ci	conn_pr_state_change(connection, os, ns_max, flags);
234862306a36Sopenharmony_ci	remember_new_state(state_change);
234962306a36Sopenharmony_ci
235062306a36Sopenharmony_ci	acscw = kmalloc(sizeof(*acscw), GFP_ATOMIC);
235162306a36Sopenharmony_ci	if (acscw) {
235262306a36Sopenharmony_ci		acscw->oc = os.conn;
235362306a36Sopenharmony_ci		acscw->ns_min = ns_min;
235462306a36Sopenharmony_ci		acscw->ns_max = ns_max;
235562306a36Sopenharmony_ci		acscw->flags = flags;
235662306a36Sopenharmony_ci		acscw->w.cb = w_after_conn_state_ch;
235762306a36Sopenharmony_ci		kref_get(&connection->kref);
235862306a36Sopenharmony_ci		acscw->connection = connection;
235962306a36Sopenharmony_ci		acscw->state_change = state_change;
236062306a36Sopenharmony_ci		drbd_queue_work(&connection->sender_work, &acscw->w);
236162306a36Sopenharmony_ci	} else {
236262306a36Sopenharmony_ci		drbd_err(connection, "Could not kmalloc an acscw\n");
236362306a36Sopenharmony_ci	}
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci abort:
236662306a36Sopenharmony_ci	if (have_mutex) {
236762306a36Sopenharmony_ci		/* mutex_unlock() "... must not be used in interrupt context.",
236862306a36Sopenharmony_ci		 * so give up the spinlock, then re-aquire it */
236962306a36Sopenharmony_ci		spin_unlock_irq(&connection->resource->req_lock);
237062306a36Sopenharmony_ci abort_unlocked:
237162306a36Sopenharmony_ci		mutex_unlock(&connection->cstate_mutex);
237262306a36Sopenharmony_ci		spin_lock_irq(&connection->resource->req_lock);
237362306a36Sopenharmony_ci	}
237462306a36Sopenharmony_ci	if (rv < SS_SUCCESS && flags & CS_VERBOSE) {
237562306a36Sopenharmony_ci		drbd_err(connection, "State change failed: %s\n", drbd_set_st_err_str(rv));
237662306a36Sopenharmony_ci		drbd_err(connection, " mask = 0x%x val = 0x%x\n", mask.i, val.i);
237762306a36Sopenharmony_ci		drbd_err(connection, " old_conn:%s wanted_conn:%s\n", drbd_conn_str(oc), drbd_conn_str(val.conn));
237862306a36Sopenharmony_ci	}
237962306a36Sopenharmony_ci	return rv;
238062306a36Sopenharmony_ci}
238162306a36Sopenharmony_ci
238262306a36Sopenharmony_cienum drbd_state_rv
238362306a36Sopenharmony_ciconn_request_state(struct drbd_connection *connection, union drbd_state mask, union drbd_state val,
238462306a36Sopenharmony_ci		   enum chg_state_flags flags)
238562306a36Sopenharmony_ci{
238662306a36Sopenharmony_ci	enum drbd_state_rv rv;
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	spin_lock_irq(&connection->resource->req_lock);
238962306a36Sopenharmony_ci	rv = _conn_request_state(connection, mask, val, flags);
239062306a36Sopenharmony_ci	spin_unlock_irq(&connection->resource->req_lock);
239162306a36Sopenharmony_ci
239262306a36Sopenharmony_ci	return rv;
239362306a36Sopenharmony_ci}
2394