18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci   drbd_state.c
48c2ecf20Sopenharmony_ci
58c2ecf20Sopenharmony_ci   This file is part of DRBD by Philipp Reisner and Lars Ellenberg.
68c2ecf20Sopenharmony_ci
78c2ecf20Sopenharmony_ci   Copyright (C) 2001-2008, LINBIT Information Technologies GmbH.
88c2ecf20Sopenharmony_ci   Copyright (C) 1999-2008, Philipp Reisner <philipp.reisner@linbit.com>.
98c2ecf20Sopenharmony_ci   Copyright (C) 2002-2008, Lars Ellenberg <lars.ellenberg@linbit.com>.
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci   Thanks to Carter Burden, Bart Grantham and Gennadiy Nerubayev
128c2ecf20Sopenharmony_ci   from Logicworks, Inc. for making SDP replication support possible.
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/drbd_limits.h>
178c2ecf20Sopenharmony_ci#include "drbd_int.h"
188c2ecf20Sopenharmony_ci#include "drbd_protocol.h"
198c2ecf20Sopenharmony_ci#include "drbd_req.h"
208c2ecf20Sopenharmony_ci#include "drbd_state_change.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct after_state_chg_work {
238c2ecf20Sopenharmony_ci	struct drbd_work w;
248c2ecf20Sopenharmony_ci	struct drbd_device *device;
258c2ecf20Sopenharmony_ci	union drbd_state os;
268c2ecf20Sopenharmony_ci	union drbd_state ns;
278c2ecf20Sopenharmony_ci	enum chg_state_flags flags;
288c2ecf20Sopenharmony_ci	struct completion *done;
298c2ecf20Sopenharmony_ci	struct drbd_state_change *state_change;
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cienum sanitize_state_warnings {
338c2ecf20Sopenharmony_ci	NO_WARNING,
348c2ecf20Sopenharmony_ci	ABORTED_ONLINE_VERIFY,
358c2ecf20Sopenharmony_ci	ABORTED_RESYNC,
368c2ecf20Sopenharmony_ci	CONNECTION_LOST_NEGOTIATING,
378c2ecf20Sopenharmony_ci	IMPLICITLY_UPGRADED_DISK,
388c2ecf20Sopenharmony_ci	IMPLICITLY_UPGRADED_PDSK,
398c2ecf20Sopenharmony_ci};
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic void count_objects(struct drbd_resource *resource,
428c2ecf20Sopenharmony_ci			  unsigned int *n_devices,
438c2ecf20Sopenharmony_ci			  unsigned int *n_connections)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct drbd_device *device;
468c2ecf20Sopenharmony_ci	struct drbd_connection *connection;
478c2ecf20Sopenharmony_ci	int vnr;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	*n_devices = 0;
508c2ecf20Sopenharmony_ci	*n_connections = 0;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	idr_for_each_entry(&resource->devices, device, vnr)
538c2ecf20Sopenharmony_ci		(*n_devices)++;
548c2ecf20Sopenharmony_ci	for_each_connection(connection, resource)
558c2ecf20Sopenharmony_ci		(*n_connections)++;
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct drbd_state_change *alloc_state_change(unsigned int n_devices, unsigned int n_connections, gfp_t gfp)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci	struct drbd_state_change *state_change;
618c2ecf20Sopenharmony_ci	unsigned int size, n;
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	size = sizeof(struct drbd_state_change) +
648c2ecf20Sopenharmony_ci	       n_devices * sizeof(struct drbd_device_state_change) +
658c2ecf20Sopenharmony_ci	       n_connections * sizeof(struct drbd_connection_state_change) +
668c2ecf20Sopenharmony_ci	       n_devices * n_connections * sizeof(struct drbd_peer_device_state_change);
678c2ecf20Sopenharmony_ci	state_change = kmalloc(size, gfp);
688c2ecf20Sopenharmony_ci	if (!state_change)
698c2ecf20Sopenharmony_ci		return NULL;
708c2ecf20Sopenharmony_ci	state_change->n_devices = n_devices;
718c2ecf20Sopenharmony_ci	state_change->n_connections = n_connections;
728c2ecf20Sopenharmony_ci	state_change->devices = (void *)(state_change + 1);
738c2ecf20Sopenharmony_ci	state_change->connections = (void *)&state_change->devices[n_devices];
748c2ecf20Sopenharmony_ci	state_change->peer_devices = (void *)&state_change->connections[n_connections];
758c2ecf20Sopenharmony_ci	state_change->resource->resource = NULL;
768c2ecf20Sopenharmony_ci	for (n = 0; n < n_devices; n++)
778c2ecf20Sopenharmony_ci		state_change->devices[n].device = NULL;
788c2ecf20Sopenharmony_ci	for (n = 0; n < n_connections; n++)
798c2ecf20Sopenharmony_ci		state_change->connections[n].connection = NULL;
808c2ecf20Sopenharmony_ci	return state_change;
818c2ecf20Sopenharmony_ci}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_cistruct drbd_state_change *remember_old_state(struct drbd_resource *resource, gfp_t gfp)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct drbd_state_change *state_change;
868c2ecf20Sopenharmony_ci	struct drbd_device *device;
878c2ecf20Sopenharmony_ci	unsigned int n_devices;
888c2ecf20Sopenharmony_ci	struct drbd_connection *connection;
898c2ecf20Sopenharmony_ci	unsigned int n_connections;
908c2ecf20Sopenharmony_ci	int vnr;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	struct drbd_device_state_change *device_state_change;
938c2ecf20Sopenharmony_ci	struct drbd_peer_device_state_change *peer_device_state_change;
948c2ecf20Sopenharmony_ci	struct drbd_connection_state_change *connection_state_change;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/* Caller holds req_lock spinlock.
978c2ecf20Sopenharmony_ci	 * No state, no device IDR, no connections lists can change. */
988c2ecf20Sopenharmony_ci	count_objects(resource, &n_devices, &n_connections);
998c2ecf20Sopenharmony_ci	state_change = alloc_state_change(n_devices, n_connections, gfp);
1008c2ecf20Sopenharmony_ci	if (!state_change)
1018c2ecf20Sopenharmony_ci		return NULL;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	kref_get(&resource->kref);
1048c2ecf20Sopenharmony_ci	state_change->resource->resource = resource;
1058c2ecf20Sopenharmony_ci	state_change->resource->role[OLD] =
1068c2ecf20Sopenharmony_ci		conn_highest_role(first_connection(resource));
1078c2ecf20Sopenharmony_ci	state_change->resource->susp[OLD] = resource->susp;
1088c2ecf20Sopenharmony_ci	state_change->resource->susp_nod[OLD] = resource->susp_nod;
1098c2ecf20Sopenharmony_ci	state_change->resource->susp_fen[OLD] = resource->susp_fen;
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	connection_state_change = state_change->connections;
1128c2ecf20Sopenharmony_ci	for_each_connection(connection, resource) {
1138c2ecf20Sopenharmony_ci		kref_get(&connection->kref);
1148c2ecf20Sopenharmony_ci		connection_state_change->connection = connection;
1158c2ecf20Sopenharmony_ci		connection_state_change->cstate[OLD] =
1168c2ecf20Sopenharmony_ci			connection->cstate;
1178c2ecf20Sopenharmony_ci		connection_state_change->peer_role[OLD] =
1188c2ecf20Sopenharmony_ci			conn_highest_peer(connection);
1198c2ecf20Sopenharmony_ci		connection_state_change++;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	device_state_change = state_change->devices;
1238c2ecf20Sopenharmony_ci	peer_device_state_change = state_change->peer_devices;
1248c2ecf20Sopenharmony_ci	idr_for_each_entry(&resource->devices, device, vnr) {
1258c2ecf20Sopenharmony_ci		kref_get(&device->kref);
1268c2ecf20Sopenharmony_ci		device_state_change->device = device;
1278c2ecf20Sopenharmony_ci		device_state_change->disk_state[OLD] = device->state.disk;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		/* The peer_devices for each device have to be enumerated in
1308c2ecf20Sopenharmony_ci		   the order of the connections. We may not use for_each_peer_device() here. */
1318c2ecf20Sopenharmony_ci		for_each_connection(connection, resource) {
1328c2ecf20Sopenharmony_ci			struct drbd_peer_device *peer_device;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci			peer_device = conn_peer_device(connection, device->vnr);
1358c2ecf20Sopenharmony_ci			peer_device_state_change->peer_device = peer_device;
1368c2ecf20Sopenharmony_ci			peer_device_state_change->disk_state[OLD] =
1378c2ecf20Sopenharmony_ci				device->state.pdsk;
1388c2ecf20Sopenharmony_ci			peer_device_state_change->repl_state[OLD] =
1398c2ecf20Sopenharmony_ci				max_t(enum drbd_conns,
1408c2ecf20Sopenharmony_ci				      C_WF_REPORT_PARAMS, device->state.conn);
1418c2ecf20Sopenharmony_ci			peer_device_state_change->resync_susp_user[OLD] =
1428c2ecf20Sopenharmony_ci				device->state.user_isp;
1438c2ecf20Sopenharmony_ci			peer_device_state_change->resync_susp_peer[OLD] =
1448c2ecf20Sopenharmony_ci				device->state.peer_isp;
1458c2ecf20Sopenharmony_ci			peer_device_state_change->resync_susp_dependency[OLD] =
1468c2ecf20Sopenharmony_ci				device->state.aftr_isp;
1478c2ecf20Sopenharmony_ci			peer_device_state_change++;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci		device_state_change++;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	return state_change;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cistatic void remember_new_state(struct drbd_state_change *state_change)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	struct drbd_resource_state_change *resource_state_change;
1588c2ecf20Sopenharmony_ci	struct drbd_resource *resource;
1598c2ecf20Sopenharmony_ci	unsigned int n;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	if (!state_change)
1628c2ecf20Sopenharmony_ci		return;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	resource_state_change = &state_change->resource[0];
1658c2ecf20Sopenharmony_ci	resource = resource_state_change->resource;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	resource_state_change->role[NEW] =
1688c2ecf20Sopenharmony_ci		conn_highest_role(first_connection(resource));
1698c2ecf20Sopenharmony_ci	resource_state_change->susp[NEW] = resource->susp;
1708c2ecf20Sopenharmony_ci	resource_state_change->susp_nod[NEW] = resource->susp_nod;
1718c2ecf20Sopenharmony_ci	resource_state_change->susp_fen[NEW] = resource->susp_fen;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	for (n = 0; n < state_change->n_devices; n++) {
1748c2ecf20Sopenharmony_ci		struct drbd_device_state_change *device_state_change =
1758c2ecf20Sopenharmony_ci			&state_change->devices[n];
1768c2ecf20Sopenharmony_ci		struct drbd_device *device = device_state_change->device;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci		device_state_change->disk_state[NEW] = device->state.disk;
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	for (n = 0; n < state_change->n_connections; n++) {
1828c2ecf20Sopenharmony_ci		struct drbd_connection_state_change *connection_state_change =
1838c2ecf20Sopenharmony_ci			&state_change->connections[n];
1848c2ecf20Sopenharmony_ci		struct drbd_connection *connection =
1858c2ecf20Sopenharmony_ci			connection_state_change->connection;
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		connection_state_change->cstate[NEW] = connection->cstate;
1888c2ecf20Sopenharmony_ci		connection_state_change->peer_role[NEW] =
1898c2ecf20Sopenharmony_ci			conn_highest_peer(connection);
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	for (n = 0; n < state_change->n_devices * state_change->n_connections; n++) {
1938c2ecf20Sopenharmony_ci		struct drbd_peer_device_state_change *peer_device_state_change =
1948c2ecf20Sopenharmony_ci			&state_change->peer_devices[n];
1958c2ecf20Sopenharmony_ci		struct drbd_device *device =
1968c2ecf20Sopenharmony_ci			peer_device_state_change->peer_device->device;
1978c2ecf20Sopenharmony_ci		union drbd_dev_state state = device->state;
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci		peer_device_state_change->disk_state[NEW] = state.pdsk;
2008c2ecf20Sopenharmony_ci		peer_device_state_change->repl_state[NEW] =
2018c2ecf20Sopenharmony_ci			max_t(enum drbd_conns, C_WF_REPORT_PARAMS, state.conn);
2028c2ecf20Sopenharmony_ci		peer_device_state_change->resync_susp_user[NEW] =
2038c2ecf20Sopenharmony_ci			state.user_isp;
2048c2ecf20Sopenharmony_ci		peer_device_state_change->resync_susp_peer[NEW] =
2058c2ecf20Sopenharmony_ci			state.peer_isp;
2068c2ecf20Sopenharmony_ci		peer_device_state_change->resync_susp_dependency[NEW] =
2078c2ecf20Sopenharmony_ci			state.aftr_isp;
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_civoid copy_old_to_new_state_change(struct drbd_state_change *state_change)
2128c2ecf20Sopenharmony_ci{
2138c2ecf20Sopenharmony_ci	struct drbd_resource_state_change *resource_state_change = &state_change->resource[0];
2148c2ecf20Sopenharmony_ci	unsigned int n_device, n_connection, n_peer_device, n_peer_devices;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci#define OLD_TO_NEW(x) \
2178c2ecf20Sopenharmony_ci	(x[NEW] = x[OLD])
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	OLD_TO_NEW(resource_state_change->role);
2208c2ecf20Sopenharmony_ci	OLD_TO_NEW(resource_state_change->susp);
2218c2ecf20Sopenharmony_ci	OLD_TO_NEW(resource_state_change->susp_nod);
2228c2ecf20Sopenharmony_ci	OLD_TO_NEW(resource_state_change->susp_fen);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	for (n_connection = 0; n_connection < state_change->n_connections; n_connection++) {
2258c2ecf20Sopenharmony_ci		struct drbd_connection_state_change *connection_state_change =
2268c2ecf20Sopenharmony_ci				&state_change->connections[n_connection];
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci		OLD_TO_NEW(connection_state_change->peer_role);
2298c2ecf20Sopenharmony_ci		OLD_TO_NEW(connection_state_change->cstate);
2308c2ecf20Sopenharmony_ci	}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	for (n_device = 0; n_device < state_change->n_devices; n_device++) {
2338c2ecf20Sopenharmony_ci		struct drbd_device_state_change *device_state_change =
2348c2ecf20Sopenharmony_ci			&state_change->devices[n_device];
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci		OLD_TO_NEW(device_state_change->disk_state);
2378c2ecf20Sopenharmony_ci	}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	n_peer_devices = state_change->n_devices * state_change->n_connections;
2408c2ecf20Sopenharmony_ci	for (n_peer_device = 0; n_peer_device < n_peer_devices; n_peer_device++) {
2418c2ecf20Sopenharmony_ci		struct drbd_peer_device_state_change *p =
2428c2ecf20Sopenharmony_ci			&state_change->peer_devices[n_peer_device];
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci		OLD_TO_NEW(p->disk_state);
2458c2ecf20Sopenharmony_ci		OLD_TO_NEW(p->repl_state);
2468c2ecf20Sopenharmony_ci		OLD_TO_NEW(p->resync_susp_user);
2478c2ecf20Sopenharmony_ci		OLD_TO_NEW(p->resync_susp_peer);
2488c2ecf20Sopenharmony_ci		OLD_TO_NEW(p->resync_susp_dependency);
2498c2ecf20Sopenharmony_ci	}
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci#undef OLD_TO_NEW
2528c2ecf20Sopenharmony_ci}
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_civoid forget_state_change(struct drbd_state_change *state_change)
2558c2ecf20Sopenharmony_ci{
2568c2ecf20Sopenharmony_ci	unsigned int n;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (!state_change)
2598c2ecf20Sopenharmony_ci		return;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	if (state_change->resource->resource)
2628c2ecf20Sopenharmony_ci		kref_put(&state_change->resource->resource->kref, drbd_destroy_resource);
2638c2ecf20Sopenharmony_ci	for (n = 0; n < state_change->n_devices; n++) {
2648c2ecf20Sopenharmony_ci		struct drbd_device *device = state_change->devices[n].device;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		if (device)
2678c2ecf20Sopenharmony_ci			kref_put(&device->kref, drbd_destroy_device);
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci	for (n = 0; n < state_change->n_connections; n++) {
2708c2ecf20Sopenharmony_ci		struct drbd_connection *connection =
2718c2ecf20Sopenharmony_ci			state_change->connections[n].connection;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		if (connection)
2748c2ecf20Sopenharmony_ci			kref_put(&connection->kref, drbd_destroy_connection);
2758c2ecf20Sopenharmony_ci	}
2768c2ecf20Sopenharmony_ci	kfree(state_change);
2778c2ecf20Sopenharmony_ci}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_cistatic int w_after_state_ch(struct drbd_work *w, int unused);
2808c2ecf20Sopenharmony_cistatic void after_state_ch(struct drbd_device *device, union drbd_state os,
2818c2ecf20Sopenharmony_ci			   union drbd_state ns, enum chg_state_flags flags,
2828c2ecf20Sopenharmony_ci			   struct drbd_state_change *);
2838c2ecf20Sopenharmony_cistatic enum drbd_state_rv is_valid_state(struct drbd_device *, union drbd_state);
2848c2ecf20Sopenharmony_cistatic enum drbd_state_rv is_valid_soft_transition(union drbd_state, union drbd_state, struct drbd_connection *);
2858c2ecf20Sopenharmony_cistatic enum drbd_state_rv is_valid_transition(union drbd_state os, union drbd_state ns);
2868c2ecf20Sopenharmony_cistatic union drbd_state sanitize_state(struct drbd_device *device, union drbd_state os,
2878c2ecf20Sopenharmony_ci				       union drbd_state ns, enum sanitize_state_warnings *warn);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_cistatic inline bool is_susp(union drbd_state s)
2908c2ecf20Sopenharmony_ci{
2918c2ecf20Sopenharmony_ci        return s.susp || s.susp_nod || s.susp_fen;
2928c2ecf20Sopenharmony_ci}
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_cibool conn_all_vols_unconf(struct drbd_connection *connection)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
2978c2ecf20Sopenharmony_ci	bool rv = true;
2988c2ecf20Sopenharmony_ci	int vnr;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	rcu_read_lock();
3018c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
3028c2ecf20Sopenharmony_ci		struct drbd_device *device = peer_device->device;
3038c2ecf20Sopenharmony_ci		if (device->state.disk != D_DISKLESS ||
3048c2ecf20Sopenharmony_ci		    device->state.conn != C_STANDALONE ||
3058c2ecf20Sopenharmony_ci		    device->state.role != R_SECONDARY) {
3068c2ecf20Sopenharmony_ci			rv = false;
3078c2ecf20Sopenharmony_ci			break;
3088c2ecf20Sopenharmony_ci		}
3098c2ecf20Sopenharmony_ci	}
3108c2ecf20Sopenharmony_ci	rcu_read_unlock();
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	return rv;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci/* Unfortunately the states where not correctly ordered, when
3168c2ecf20Sopenharmony_ci   they where defined. therefore can not use max_t() here. */
3178c2ecf20Sopenharmony_cistatic enum drbd_role max_role(enum drbd_role role1, enum drbd_role role2)
3188c2ecf20Sopenharmony_ci{
3198c2ecf20Sopenharmony_ci	if (role1 == R_PRIMARY || role2 == R_PRIMARY)
3208c2ecf20Sopenharmony_ci		return R_PRIMARY;
3218c2ecf20Sopenharmony_ci	if (role1 == R_SECONDARY || role2 == R_SECONDARY)
3228c2ecf20Sopenharmony_ci		return R_SECONDARY;
3238c2ecf20Sopenharmony_ci	return R_UNKNOWN;
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic enum drbd_role min_role(enum drbd_role role1, enum drbd_role role2)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	if (role1 == R_UNKNOWN || role2 == R_UNKNOWN)
3298c2ecf20Sopenharmony_ci		return R_UNKNOWN;
3308c2ecf20Sopenharmony_ci	if (role1 == R_SECONDARY || role2 == R_SECONDARY)
3318c2ecf20Sopenharmony_ci		return R_SECONDARY;
3328c2ecf20Sopenharmony_ci	return R_PRIMARY;
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cienum drbd_role conn_highest_role(struct drbd_connection *connection)
3368c2ecf20Sopenharmony_ci{
3378c2ecf20Sopenharmony_ci	enum drbd_role role = R_SECONDARY;
3388c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
3398c2ecf20Sopenharmony_ci	int vnr;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	rcu_read_lock();
3428c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
3438c2ecf20Sopenharmony_ci		struct drbd_device *device = peer_device->device;
3448c2ecf20Sopenharmony_ci		role = max_role(role, device->state.role);
3458c2ecf20Sopenharmony_ci	}
3468c2ecf20Sopenharmony_ci	rcu_read_unlock();
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	return role;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_cienum drbd_role conn_highest_peer(struct drbd_connection *connection)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	enum drbd_role peer = R_UNKNOWN;
3548c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
3558c2ecf20Sopenharmony_ci	int vnr;
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_ci	rcu_read_lock();
3588c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
3598c2ecf20Sopenharmony_ci		struct drbd_device *device = peer_device->device;
3608c2ecf20Sopenharmony_ci		peer = max_role(peer, device->state.peer);
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci	rcu_read_unlock();
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	return peer;
3658c2ecf20Sopenharmony_ci}
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_cienum drbd_disk_state conn_highest_disk(struct drbd_connection *connection)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	enum drbd_disk_state disk_state = D_DISKLESS;
3708c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
3718c2ecf20Sopenharmony_ci	int vnr;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	rcu_read_lock();
3748c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
3758c2ecf20Sopenharmony_ci		struct drbd_device *device = peer_device->device;
3768c2ecf20Sopenharmony_ci		disk_state = max_t(enum drbd_disk_state, disk_state, device->state.disk);
3778c2ecf20Sopenharmony_ci	}
3788c2ecf20Sopenharmony_ci	rcu_read_unlock();
3798c2ecf20Sopenharmony_ci
3808c2ecf20Sopenharmony_ci	return disk_state;
3818c2ecf20Sopenharmony_ci}
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cienum drbd_disk_state conn_lowest_disk(struct drbd_connection *connection)
3848c2ecf20Sopenharmony_ci{
3858c2ecf20Sopenharmony_ci	enum drbd_disk_state disk_state = D_MASK;
3868c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
3878c2ecf20Sopenharmony_ci	int vnr;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	rcu_read_lock();
3908c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
3918c2ecf20Sopenharmony_ci		struct drbd_device *device = peer_device->device;
3928c2ecf20Sopenharmony_ci		disk_state = min_t(enum drbd_disk_state, disk_state, device->state.disk);
3938c2ecf20Sopenharmony_ci	}
3948c2ecf20Sopenharmony_ci	rcu_read_unlock();
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci	return disk_state;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cienum drbd_disk_state conn_highest_pdsk(struct drbd_connection *connection)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	enum drbd_disk_state disk_state = D_DISKLESS;
4028c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
4038c2ecf20Sopenharmony_ci	int vnr;
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	rcu_read_lock();
4068c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
4078c2ecf20Sopenharmony_ci		struct drbd_device *device = peer_device->device;
4088c2ecf20Sopenharmony_ci		disk_state = max_t(enum drbd_disk_state, disk_state, device->state.pdsk);
4098c2ecf20Sopenharmony_ci	}
4108c2ecf20Sopenharmony_ci	rcu_read_unlock();
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	return disk_state;
4138c2ecf20Sopenharmony_ci}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_cienum drbd_conns conn_lowest_conn(struct drbd_connection *connection)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	enum drbd_conns conn = C_MASK;
4188c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
4198c2ecf20Sopenharmony_ci	int vnr;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	rcu_read_lock();
4228c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
4238c2ecf20Sopenharmony_ci		struct drbd_device *device = peer_device->device;
4248c2ecf20Sopenharmony_ci		conn = min_t(enum drbd_conns, conn, device->state.conn);
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci	rcu_read_unlock();
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	return conn;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_cistatic bool no_peer_wf_report_params(struct drbd_connection *connection)
4328c2ecf20Sopenharmony_ci{
4338c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
4348c2ecf20Sopenharmony_ci	int vnr;
4358c2ecf20Sopenharmony_ci	bool rv = true;
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	rcu_read_lock();
4388c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
4398c2ecf20Sopenharmony_ci		if (peer_device->device->state.conn == C_WF_REPORT_PARAMS) {
4408c2ecf20Sopenharmony_ci			rv = false;
4418c2ecf20Sopenharmony_ci			break;
4428c2ecf20Sopenharmony_ci		}
4438c2ecf20Sopenharmony_ci	rcu_read_unlock();
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	return rv;
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic void wake_up_all_devices(struct drbd_connection *connection)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
4518c2ecf20Sopenharmony_ci	int vnr;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	rcu_read_lock();
4548c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
4558c2ecf20Sopenharmony_ci		wake_up(&peer_device->device->state_wait);
4568c2ecf20Sopenharmony_ci	rcu_read_unlock();
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci/**
4628c2ecf20Sopenharmony_ci * cl_wide_st_chg() - true if the state change is a cluster wide one
4638c2ecf20Sopenharmony_ci * @device:	DRBD device.
4648c2ecf20Sopenharmony_ci * @os:		old (current) state.
4658c2ecf20Sopenharmony_ci * @ns:		new (wanted) state.
4668c2ecf20Sopenharmony_ci */
4678c2ecf20Sopenharmony_cistatic int cl_wide_st_chg(struct drbd_device *device,
4688c2ecf20Sopenharmony_ci			  union drbd_state os, union drbd_state ns)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	return (os.conn >= C_CONNECTED && ns.conn >= C_CONNECTED &&
4718c2ecf20Sopenharmony_ci		 ((os.role != R_PRIMARY && ns.role == R_PRIMARY) ||
4728c2ecf20Sopenharmony_ci		  (os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) ||
4738c2ecf20Sopenharmony_ci		  (os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S) ||
4748c2ecf20Sopenharmony_ci		  (os.disk != D_FAILED && ns.disk == D_FAILED))) ||
4758c2ecf20Sopenharmony_ci		(os.conn >= C_CONNECTED && ns.conn == C_DISCONNECTING) ||
4768c2ecf20Sopenharmony_ci		(os.conn == C_CONNECTED && ns.conn == C_VERIFY_S) ||
4778c2ecf20Sopenharmony_ci		(os.conn == C_CONNECTED && ns.conn == C_WF_REPORT_PARAMS);
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic union drbd_state
4818c2ecf20Sopenharmony_ciapply_mask_val(union drbd_state os, union drbd_state mask, union drbd_state val)
4828c2ecf20Sopenharmony_ci{
4838c2ecf20Sopenharmony_ci	union drbd_state ns;
4848c2ecf20Sopenharmony_ci	ns.i = (os.i & ~mask.i) | val.i;
4858c2ecf20Sopenharmony_ci	return ns;
4868c2ecf20Sopenharmony_ci}
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_cienum drbd_state_rv
4898c2ecf20Sopenharmony_cidrbd_change_state(struct drbd_device *device, enum chg_state_flags f,
4908c2ecf20Sopenharmony_ci		  union drbd_state mask, union drbd_state val)
4918c2ecf20Sopenharmony_ci{
4928c2ecf20Sopenharmony_ci	unsigned long flags;
4938c2ecf20Sopenharmony_ci	union drbd_state ns;
4948c2ecf20Sopenharmony_ci	enum drbd_state_rv rv;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	spin_lock_irqsave(&device->resource->req_lock, flags);
4978c2ecf20Sopenharmony_ci	ns = apply_mask_val(drbd_read_state(device), mask, val);
4988c2ecf20Sopenharmony_ci	rv = _drbd_set_state(device, ns, f, NULL);
4998c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&device->resource->req_lock, flags);
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci	return rv;
5028c2ecf20Sopenharmony_ci}
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci/**
5058c2ecf20Sopenharmony_ci * drbd_force_state() - Impose a change which happens outside our control on our state
5068c2ecf20Sopenharmony_ci * @device:	DRBD device.
5078c2ecf20Sopenharmony_ci * @mask:	mask of state bits to change.
5088c2ecf20Sopenharmony_ci * @val:	value of new state bits.
5098c2ecf20Sopenharmony_ci */
5108c2ecf20Sopenharmony_civoid drbd_force_state(struct drbd_device *device,
5118c2ecf20Sopenharmony_ci	union drbd_state mask, union drbd_state val)
5128c2ecf20Sopenharmony_ci{
5138c2ecf20Sopenharmony_ci	drbd_change_state(device, CS_HARD, mask, val);
5148c2ecf20Sopenharmony_ci}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_cistatic enum drbd_state_rv
5178c2ecf20Sopenharmony_ci_req_st_cond(struct drbd_device *device, union drbd_state mask,
5188c2ecf20Sopenharmony_ci	     union drbd_state val)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	union drbd_state os, ns;
5218c2ecf20Sopenharmony_ci	unsigned long flags;
5228c2ecf20Sopenharmony_ci	enum drbd_state_rv rv;
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	if (test_and_clear_bit(CL_ST_CHG_SUCCESS, &device->flags))
5258c2ecf20Sopenharmony_ci		return SS_CW_SUCCESS;
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	if (test_and_clear_bit(CL_ST_CHG_FAIL, &device->flags))
5288c2ecf20Sopenharmony_ci		return SS_CW_FAILED_BY_PEER;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	spin_lock_irqsave(&device->resource->req_lock, flags);
5318c2ecf20Sopenharmony_ci	os = drbd_read_state(device);
5328c2ecf20Sopenharmony_ci	ns = sanitize_state(device, os, apply_mask_val(os, mask, val), NULL);
5338c2ecf20Sopenharmony_ci	rv = is_valid_transition(os, ns);
5348c2ecf20Sopenharmony_ci	if (rv >= SS_SUCCESS)
5358c2ecf20Sopenharmony_ci		rv = SS_UNKNOWN_ERROR;  /* cont waiting, otherwise fail. */
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	if (!cl_wide_st_chg(device, os, ns))
5388c2ecf20Sopenharmony_ci		rv = SS_CW_NO_NEED;
5398c2ecf20Sopenharmony_ci	if (rv == SS_UNKNOWN_ERROR) {
5408c2ecf20Sopenharmony_ci		rv = is_valid_state(device, ns);
5418c2ecf20Sopenharmony_ci		if (rv >= SS_SUCCESS) {
5428c2ecf20Sopenharmony_ci			rv = is_valid_soft_transition(os, ns, first_peer_device(device)->connection);
5438c2ecf20Sopenharmony_ci			if (rv >= SS_SUCCESS)
5448c2ecf20Sopenharmony_ci				rv = SS_UNKNOWN_ERROR; /* cont waiting, otherwise fail. */
5458c2ecf20Sopenharmony_ci		}
5468c2ecf20Sopenharmony_ci	}
5478c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&device->resource->req_lock, flags);
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	return rv;
5508c2ecf20Sopenharmony_ci}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci/**
5538c2ecf20Sopenharmony_ci * drbd_req_state() - Perform an eventually cluster wide state change
5548c2ecf20Sopenharmony_ci * @device:	DRBD device.
5558c2ecf20Sopenharmony_ci * @mask:	mask of state bits to change.
5568c2ecf20Sopenharmony_ci * @val:	value of new state bits.
5578c2ecf20Sopenharmony_ci * @f:		flags
5588c2ecf20Sopenharmony_ci *
5598c2ecf20Sopenharmony_ci * Should not be called directly, use drbd_request_state() or
5608c2ecf20Sopenharmony_ci * _drbd_request_state().
5618c2ecf20Sopenharmony_ci */
5628c2ecf20Sopenharmony_cistatic enum drbd_state_rv
5638c2ecf20Sopenharmony_cidrbd_req_state(struct drbd_device *device, union drbd_state mask,
5648c2ecf20Sopenharmony_ci	       union drbd_state val, enum chg_state_flags f)
5658c2ecf20Sopenharmony_ci{
5668c2ecf20Sopenharmony_ci	struct completion done;
5678c2ecf20Sopenharmony_ci	unsigned long flags;
5688c2ecf20Sopenharmony_ci	union drbd_state os, ns;
5698c2ecf20Sopenharmony_ci	enum drbd_state_rv rv;
5708c2ecf20Sopenharmony_ci	void *buffer = NULL;
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci	init_completion(&done);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	if (f & CS_SERIALIZE)
5758c2ecf20Sopenharmony_ci		mutex_lock(device->state_mutex);
5768c2ecf20Sopenharmony_ci	if (f & CS_INHIBIT_MD_IO)
5778c2ecf20Sopenharmony_ci		buffer = drbd_md_get_buffer(device, __func__);
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	spin_lock_irqsave(&device->resource->req_lock, flags);
5808c2ecf20Sopenharmony_ci	os = drbd_read_state(device);
5818c2ecf20Sopenharmony_ci	ns = sanitize_state(device, os, apply_mask_val(os, mask, val), NULL);
5828c2ecf20Sopenharmony_ci	rv = is_valid_transition(os, ns);
5838c2ecf20Sopenharmony_ci	if (rv < SS_SUCCESS) {
5848c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&device->resource->req_lock, flags);
5858c2ecf20Sopenharmony_ci		goto abort;
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	if (cl_wide_st_chg(device, os, ns)) {
5898c2ecf20Sopenharmony_ci		rv = is_valid_state(device, ns);
5908c2ecf20Sopenharmony_ci		if (rv == SS_SUCCESS)
5918c2ecf20Sopenharmony_ci			rv = is_valid_soft_transition(os, ns, first_peer_device(device)->connection);
5928c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&device->resource->req_lock, flags);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci		if (rv < SS_SUCCESS) {
5958c2ecf20Sopenharmony_ci			if (f & CS_VERBOSE)
5968c2ecf20Sopenharmony_ci				print_st_err(device, os, ns, rv);
5978c2ecf20Sopenharmony_ci			goto abort;
5988c2ecf20Sopenharmony_ci		}
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci		if (drbd_send_state_req(first_peer_device(device), mask, val)) {
6018c2ecf20Sopenharmony_ci			rv = SS_CW_FAILED_BY_PEER;
6028c2ecf20Sopenharmony_ci			if (f & CS_VERBOSE)
6038c2ecf20Sopenharmony_ci				print_st_err(device, os, ns, rv);
6048c2ecf20Sopenharmony_ci			goto abort;
6058c2ecf20Sopenharmony_ci		}
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci		wait_event(device->state_wait,
6088c2ecf20Sopenharmony_ci			(rv = _req_st_cond(device, mask, val)));
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci		if (rv < SS_SUCCESS) {
6118c2ecf20Sopenharmony_ci			if (f & CS_VERBOSE)
6128c2ecf20Sopenharmony_ci				print_st_err(device, os, ns, rv);
6138c2ecf20Sopenharmony_ci			goto abort;
6148c2ecf20Sopenharmony_ci		}
6158c2ecf20Sopenharmony_ci		spin_lock_irqsave(&device->resource->req_lock, flags);
6168c2ecf20Sopenharmony_ci		ns = apply_mask_val(drbd_read_state(device), mask, val);
6178c2ecf20Sopenharmony_ci		rv = _drbd_set_state(device, ns, f, &done);
6188c2ecf20Sopenharmony_ci	} else {
6198c2ecf20Sopenharmony_ci		rv = _drbd_set_state(device, ns, f, &done);
6208c2ecf20Sopenharmony_ci	}
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&device->resource->req_lock, flags);
6238c2ecf20Sopenharmony_ci
6248c2ecf20Sopenharmony_ci	if (f & CS_WAIT_COMPLETE && rv == SS_SUCCESS) {
6258c2ecf20Sopenharmony_ci		D_ASSERT(device, current != first_peer_device(device)->connection->worker.task);
6268c2ecf20Sopenharmony_ci		wait_for_completion(&done);
6278c2ecf20Sopenharmony_ci	}
6288c2ecf20Sopenharmony_ci
6298c2ecf20Sopenharmony_ciabort:
6308c2ecf20Sopenharmony_ci	if (buffer)
6318c2ecf20Sopenharmony_ci		drbd_md_put_buffer(device);
6328c2ecf20Sopenharmony_ci	if (f & CS_SERIALIZE)
6338c2ecf20Sopenharmony_ci		mutex_unlock(device->state_mutex);
6348c2ecf20Sopenharmony_ci
6358c2ecf20Sopenharmony_ci	return rv;
6368c2ecf20Sopenharmony_ci}
6378c2ecf20Sopenharmony_ci
6388c2ecf20Sopenharmony_ci/**
6398c2ecf20Sopenharmony_ci * _drbd_request_state() - Request a state change (with flags)
6408c2ecf20Sopenharmony_ci * @device:	DRBD device.
6418c2ecf20Sopenharmony_ci * @mask:	mask of state bits to change.
6428c2ecf20Sopenharmony_ci * @val:	value of new state bits.
6438c2ecf20Sopenharmony_ci * @f:		flags
6448c2ecf20Sopenharmony_ci *
6458c2ecf20Sopenharmony_ci * Cousin of drbd_request_state(), useful with the CS_WAIT_COMPLETE
6468c2ecf20Sopenharmony_ci * flag, or when logging of failed state change requests is not desired.
6478c2ecf20Sopenharmony_ci */
6488c2ecf20Sopenharmony_cienum drbd_state_rv
6498c2ecf20Sopenharmony_ci_drbd_request_state(struct drbd_device *device, union drbd_state mask,
6508c2ecf20Sopenharmony_ci		    union drbd_state val, enum chg_state_flags f)
6518c2ecf20Sopenharmony_ci{
6528c2ecf20Sopenharmony_ci	enum drbd_state_rv rv;
6538c2ecf20Sopenharmony_ci
6548c2ecf20Sopenharmony_ci	wait_event(device->state_wait,
6558c2ecf20Sopenharmony_ci		   (rv = drbd_req_state(device, mask, val, f)) != SS_IN_TRANSIENT_STATE);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	return rv;
6588c2ecf20Sopenharmony_ci}
6598c2ecf20Sopenharmony_ci
6608c2ecf20Sopenharmony_ci/*
6618c2ecf20Sopenharmony_ci * We grab drbd_md_get_buffer(), because we don't want to "fail" the disk while
6628c2ecf20Sopenharmony_ci * there is IO in-flight: the transition into D_FAILED for detach purposes
6638c2ecf20Sopenharmony_ci * may get misinterpreted as actual IO error in a confused endio function.
6648c2ecf20Sopenharmony_ci *
6658c2ecf20Sopenharmony_ci * We wrap it all into wait_event(), to retry in case the drbd_req_state()
6668c2ecf20Sopenharmony_ci * returns SS_IN_TRANSIENT_STATE.
6678c2ecf20Sopenharmony_ci *
6688c2ecf20Sopenharmony_ci * To avoid potential deadlock with e.g. the receiver thread trying to grab
6698c2ecf20Sopenharmony_ci * drbd_md_get_buffer() while trying to get out of the "transient state", we
6708c2ecf20Sopenharmony_ci * need to grab and release the meta data buffer inside of that wait_event loop.
6718c2ecf20Sopenharmony_ci */
6728c2ecf20Sopenharmony_cistatic enum drbd_state_rv
6738c2ecf20Sopenharmony_cirequest_detach(struct drbd_device *device)
6748c2ecf20Sopenharmony_ci{
6758c2ecf20Sopenharmony_ci	return drbd_req_state(device, NS(disk, D_FAILED),
6768c2ecf20Sopenharmony_ci			CS_VERBOSE | CS_ORDERED | CS_INHIBIT_MD_IO);
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_ciint drbd_request_detach_interruptible(struct drbd_device *device)
6808c2ecf20Sopenharmony_ci{
6818c2ecf20Sopenharmony_ci	int ret, rv;
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci	drbd_suspend_io(device); /* so no-one is stuck in drbd_al_begin_io */
6848c2ecf20Sopenharmony_ci	wait_event_interruptible(device->state_wait,
6858c2ecf20Sopenharmony_ci		(rv = request_detach(device)) != SS_IN_TRANSIENT_STATE);
6868c2ecf20Sopenharmony_ci	drbd_resume_io(device);
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	ret = wait_event_interruptible(device->misc_wait,
6898c2ecf20Sopenharmony_ci			device->state.disk != D_FAILED);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	if (rv == SS_IS_DISKLESS)
6928c2ecf20Sopenharmony_ci		rv = SS_NOTHING_TO_DO;
6938c2ecf20Sopenharmony_ci	if (ret)
6948c2ecf20Sopenharmony_ci		rv = ERR_INTR;
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci	return rv;
6978c2ecf20Sopenharmony_ci}
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_cienum drbd_state_rv
7008c2ecf20Sopenharmony_ci_drbd_request_state_holding_state_mutex(struct drbd_device *device, union drbd_state mask,
7018c2ecf20Sopenharmony_ci		    union drbd_state val, enum chg_state_flags f)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	enum drbd_state_rv rv;
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci	BUG_ON(f & CS_SERIALIZE);
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci	wait_event_cmd(device->state_wait,
7088c2ecf20Sopenharmony_ci		       (rv = drbd_req_state(device, mask, val, f)) != SS_IN_TRANSIENT_STATE,
7098c2ecf20Sopenharmony_ci		       mutex_unlock(device->state_mutex),
7108c2ecf20Sopenharmony_ci		       mutex_lock(device->state_mutex));
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci	return rv;
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_cistatic void print_st(struct drbd_device *device, const char *name, union drbd_state ns)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	drbd_err(device, " %s = { cs:%s ro:%s/%s ds:%s/%s %c%c%c%c%c%c }\n",
7188c2ecf20Sopenharmony_ci	    name,
7198c2ecf20Sopenharmony_ci	    drbd_conn_str(ns.conn),
7208c2ecf20Sopenharmony_ci	    drbd_role_str(ns.role),
7218c2ecf20Sopenharmony_ci	    drbd_role_str(ns.peer),
7228c2ecf20Sopenharmony_ci	    drbd_disk_str(ns.disk),
7238c2ecf20Sopenharmony_ci	    drbd_disk_str(ns.pdsk),
7248c2ecf20Sopenharmony_ci	    is_susp(ns) ? 's' : 'r',
7258c2ecf20Sopenharmony_ci	    ns.aftr_isp ? 'a' : '-',
7268c2ecf20Sopenharmony_ci	    ns.peer_isp ? 'p' : '-',
7278c2ecf20Sopenharmony_ci	    ns.user_isp ? 'u' : '-',
7288c2ecf20Sopenharmony_ci	    ns.susp_fen ? 'F' : '-',
7298c2ecf20Sopenharmony_ci	    ns.susp_nod ? 'N' : '-'
7308c2ecf20Sopenharmony_ci	    );
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_civoid print_st_err(struct drbd_device *device, union drbd_state os,
7348c2ecf20Sopenharmony_ci	          union drbd_state ns, enum drbd_state_rv err)
7358c2ecf20Sopenharmony_ci{
7368c2ecf20Sopenharmony_ci	if (err == SS_IN_TRANSIENT_STATE)
7378c2ecf20Sopenharmony_ci		return;
7388c2ecf20Sopenharmony_ci	drbd_err(device, "State change failed: %s\n", drbd_set_st_err_str(err));
7398c2ecf20Sopenharmony_ci	print_st(device, " state", os);
7408c2ecf20Sopenharmony_ci	print_st(device, "wanted", ns);
7418c2ecf20Sopenharmony_ci}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_cistatic long print_state_change(char *pb, union drbd_state os, union drbd_state ns,
7448c2ecf20Sopenharmony_ci			       enum chg_state_flags flags)
7458c2ecf20Sopenharmony_ci{
7468c2ecf20Sopenharmony_ci	char *pbp;
7478c2ecf20Sopenharmony_ci	pbp = pb;
7488c2ecf20Sopenharmony_ci	*pbp = 0;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	if (ns.role != os.role && flags & CS_DC_ROLE)
7518c2ecf20Sopenharmony_ci		pbp += sprintf(pbp, "role( %s -> %s ) ",
7528c2ecf20Sopenharmony_ci			       drbd_role_str(os.role),
7538c2ecf20Sopenharmony_ci			       drbd_role_str(ns.role));
7548c2ecf20Sopenharmony_ci	if (ns.peer != os.peer && flags & CS_DC_PEER)
7558c2ecf20Sopenharmony_ci		pbp += sprintf(pbp, "peer( %s -> %s ) ",
7568c2ecf20Sopenharmony_ci			       drbd_role_str(os.peer),
7578c2ecf20Sopenharmony_ci			       drbd_role_str(ns.peer));
7588c2ecf20Sopenharmony_ci	if (ns.conn != os.conn && flags & CS_DC_CONN)
7598c2ecf20Sopenharmony_ci		pbp += sprintf(pbp, "conn( %s -> %s ) ",
7608c2ecf20Sopenharmony_ci			       drbd_conn_str(os.conn),
7618c2ecf20Sopenharmony_ci			       drbd_conn_str(ns.conn));
7628c2ecf20Sopenharmony_ci	if (ns.disk != os.disk && flags & CS_DC_DISK)
7638c2ecf20Sopenharmony_ci		pbp += sprintf(pbp, "disk( %s -> %s ) ",
7648c2ecf20Sopenharmony_ci			       drbd_disk_str(os.disk),
7658c2ecf20Sopenharmony_ci			       drbd_disk_str(ns.disk));
7668c2ecf20Sopenharmony_ci	if (ns.pdsk != os.pdsk && flags & CS_DC_PDSK)
7678c2ecf20Sopenharmony_ci		pbp += sprintf(pbp, "pdsk( %s -> %s ) ",
7688c2ecf20Sopenharmony_ci			       drbd_disk_str(os.pdsk),
7698c2ecf20Sopenharmony_ci			       drbd_disk_str(ns.pdsk));
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	return pbp - pb;
7728c2ecf20Sopenharmony_ci}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic void drbd_pr_state_change(struct drbd_device *device, union drbd_state os, union drbd_state ns,
7758c2ecf20Sopenharmony_ci				 enum chg_state_flags flags)
7768c2ecf20Sopenharmony_ci{
7778c2ecf20Sopenharmony_ci	char pb[300];
7788c2ecf20Sopenharmony_ci	char *pbp = pb;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	pbp += print_state_change(pbp, os, ns, flags ^ CS_DC_MASK);
7818c2ecf20Sopenharmony_ci
7828c2ecf20Sopenharmony_ci	if (ns.aftr_isp != os.aftr_isp)
7838c2ecf20Sopenharmony_ci		pbp += sprintf(pbp, "aftr_isp( %d -> %d ) ",
7848c2ecf20Sopenharmony_ci			       os.aftr_isp,
7858c2ecf20Sopenharmony_ci			       ns.aftr_isp);
7868c2ecf20Sopenharmony_ci	if (ns.peer_isp != os.peer_isp)
7878c2ecf20Sopenharmony_ci		pbp += sprintf(pbp, "peer_isp( %d -> %d ) ",
7888c2ecf20Sopenharmony_ci			       os.peer_isp,
7898c2ecf20Sopenharmony_ci			       ns.peer_isp);
7908c2ecf20Sopenharmony_ci	if (ns.user_isp != os.user_isp)
7918c2ecf20Sopenharmony_ci		pbp += sprintf(pbp, "user_isp( %d -> %d ) ",
7928c2ecf20Sopenharmony_ci			       os.user_isp,
7938c2ecf20Sopenharmony_ci			       ns.user_isp);
7948c2ecf20Sopenharmony_ci
7958c2ecf20Sopenharmony_ci	if (pbp != pb)
7968c2ecf20Sopenharmony_ci		drbd_info(device, "%s\n", pb);
7978c2ecf20Sopenharmony_ci}
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_cistatic void conn_pr_state_change(struct drbd_connection *connection, union drbd_state os, union drbd_state ns,
8008c2ecf20Sopenharmony_ci				 enum chg_state_flags flags)
8018c2ecf20Sopenharmony_ci{
8028c2ecf20Sopenharmony_ci	char pb[300];
8038c2ecf20Sopenharmony_ci	char *pbp = pb;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci	pbp += print_state_change(pbp, os, ns, flags);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci	if (is_susp(ns) != is_susp(os) && flags & CS_DC_SUSP)
8088c2ecf20Sopenharmony_ci		pbp += sprintf(pbp, "susp( %d -> %d ) ",
8098c2ecf20Sopenharmony_ci			       is_susp(os),
8108c2ecf20Sopenharmony_ci			       is_susp(ns));
8118c2ecf20Sopenharmony_ci
8128c2ecf20Sopenharmony_ci	if (pbp != pb)
8138c2ecf20Sopenharmony_ci		drbd_info(connection, "%s\n", pb);
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci
8178c2ecf20Sopenharmony_ci/**
8188c2ecf20Sopenharmony_ci * is_valid_state() - Returns an SS_ error code if ns is not valid
8198c2ecf20Sopenharmony_ci * @device:	DRBD device.
8208c2ecf20Sopenharmony_ci * @ns:		State to consider.
8218c2ecf20Sopenharmony_ci */
8228c2ecf20Sopenharmony_cistatic enum drbd_state_rv
8238c2ecf20Sopenharmony_ciis_valid_state(struct drbd_device *device, union drbd_state ns)
8248c2ecf20Sopenharmony_ci{
8258c2ecf20Sopenharmony_ci	/* See drbd_state_sw_errors in drbd_strings.c */
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	enum drbd_fencing_p fp;
8288c2ecf20Sopenharmony_ci	enum drbd_state_rv rv = SS_SUCCESS;
8298c2ecf20Sopenharmony_ci	struct net_conf *nc;
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_ci	rcu_read_lock();
8328c2ecf20Sopenharmony_ci	fp = FP_DONT_CARE;
8338c2ecf20Sopenharmony_ci	if (get_ldev(device)) {
8348c2ecf20Sopenharmony_ci		fp = rcu_dereference(device->ldev->disk_conf)->fencing;
8358c2ecf20Sopenharmony_ci		put_ldev(device);
8368c2ecf20Sopenharmony_ci	}
8378c2ecf20Sopenharmony_ci
8388c2ecf20Sopenharmony_ci	nc = rcu_dereference(first_peer_device(device)->connection->net_conf);
8398c2ecf20Sopenharmony_ci	if (nc) {
8408c2ecf20Sopenharmony_ci		if (!nc->two_primaries && ns.role == R_PRIMARY) {
8418c2ecf20Sopenharmony_ci			if (ns.peer == R_PRIMARY)
8428c2ecf20Sopenharmony_ci				rv = SS_TWO_PRIMARIES;
8438c2ecf20Sopenharmony_ci			else if (conn_highest_peer(first_peer_device(device)->connection) == R_PRIMARY)
8448c2ecf20Sopenharmony_ci				rv = SS_O_VOL_PEER_PRI;
8458c2ecf20Sopenharmony_ci		}
8468c2ecf20Sopenharmony_ci	}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_ci	if (rv <= 0)
8498c2ecf20Sopenharmony_ci		goto out; /* already found a reason to abort */
8508c2ecf20Sopenharmony_ci	else if (ns.role == R_SECONDARY && device->open_cnt)
8518c2ecf20Sopenharmony_ci		rv = SS_DEVICE_IN_USE;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	else if (ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.disk < D_UP_TO_DATE)
8548c2ecf20Sopenharmony_ci		rv = SS_NO_UP_TO_DATE_DISK;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	else if (fp >= FP_RESOURCE &&
8578c2ecf20Sopenharmony_ci		 ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.pdsk >= D_UNKNOWN)
8588c2ecf20Sopenharmony_ci		rv = SS_PRIMARY_NOP;
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_ci	else if (ns.role == R_PRIMARY && ns.disk <= D_INCONSISTENT && ns.pdsk <= D_INCONSISTENT)
8618c2ecf20Sopenharmony_ci		rv = SS_NO_UP_TO_DATE_DISK;
8628c2ecf20Sopenharmony_ci
8638c2ecf20Sopenharmony_ci	else if (ns.conn > C_CONNECTED && ns.disk < D_INCONSISTENT)
8648c2ecf20Sopenharmony_ci		rv = SS_NO_LOCAL_DISK;
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	else if (ns.conn > C_CONNECTED && ns.pdsk < D_INCONSISTENT)
8678c2ecf20Sopenharmony_ci		rv = SS_NO_REMOTE_DISK;
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_ci	else if (ns.conn > C_CONNECTED && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE)
8708c2ecf20Sopenharmony_ci		rv = SS_NO_UP_TO_DATE_DISK;
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci	else if ((ns.conn == C_CONNECTED ||
8738c2ecf20Sopenharmony_ci		  ns.conn == C_WF_BITMAP_S ||
8748c2ecf20Sopenharmony_ci		  ns.conn == C_SYNC_SOURCE ||
8758c2ecf20Sopenharmony_ci		  ns.conn == C_PAUSED_SYNC_S) &&
8768c2ecf20Sopenharmony_ci		  ns.disk == D_OUTDATED)
8778c2ecf20Sopenharmony_ci		rv = SS_CONNECTED_OUTDATES;
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	else if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
8808c2ecf20Sopenharmony_ci		 (nc->verify_alg[0] == 0))
8818c2ecf20Sopenharmony_ci		rv = SS_NO_VERIFY_ALG;
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci	else if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
8848c2ecf20Sopenharmony_ci		  first_peer_device(device)->connection->agreed_pro_version < 88)
8858c2ecf20Sopenharmony_ci		rv = SS_NOT_SUPPORTED;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	else if (ns.role == R_PRIMARY && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE)
8888c2ecf20Sopenharmony_ci		rv = SS_NO_UP_TO_DATE_DISK;
8898c2ecf20Sopenharmony_ci
8908c2ecf20Sopenharmony_ci	else if ((ns.conn == C_STARTING_SYNC_S || ns.conn == C_STARTING_SYNC_T) &&
8918c2ecf20Sopenharmony_ci                 ns.pdsk == D_UNKNOWN)
8928c2ecf20Sopenharmony_ci		rv = SS_NEED_CONNECTION;
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	else if (ns.conn >= C_CONNECTED && ns.pdsk == D_UNKNOWN)
8958c2ecf20Sopenharmony_ci		rv = SS_CONNECTED_OUTDATES;
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ciout:
8988c2ecf20Sopenharmony_ci	rcu_read_unlock();
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	return rv;
9018c2ecf20Sopenharmony_ci}
9028c2ecf20Sopenharmony_ci
9038c2ecf20Sopenharmony_ci/**
9048c2ecf20Sopenharmony_ci * is_valid_soft_transition() - Returns an SS_ error code if the state transition is not possible
9058c2ecf20Sopenharmony_ci * This function limits state transitions that may be declined by DRBD. I.e.
9068c2ecf20Sopenharmony_ci * user requests (aka soft transitions).
9078c2ecf20Sopenharmony_ci * @device:	DRBD device.
9088c2ecf20Sopenharmony_ci * @ns:		new state.
9098c2ecf20Sopenharmony_ci * @os:		old state.
9108c2ecf20Sopenharmony_ci */
9118c2ecf20Sopenharmony_cistatic enum drbd_state_rv
9128c2ecf20Sopenharmony_ciis_valid_soft_transition(union drbd_state os, union drbd_state ns, struct drbd_connection *connection)
9138c2ecf20Sopenharmony_ci{
9148c2ecf20Sopenharmony_ci	enum drbd_state_rv rv = SS_SUCCESS;
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci	if ((ns.conn == C_STARTING_SYNC_T || ns.conn == C_STARTING_SYNC_S) &&
9178c2ecf20Sopenharmony_ci	    os.conn > C_CONNECTED)
9188c2ecf20Sopenharmony_ci		rv = SS_RESYNC_RUNNING;
9198c2ecf20Sopenharmony_ci
9208c2ecf20Sopenharmony_ci	if (ns.conn == C_DISCONNECTING && os.conn == C_STANDALONE)
9218c2ecf20Sopenharmony_ci		rv = SS_ALREADY_STANDALONE;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	if (ns.disk > D_ATTACHING && os.disk == D_DISKLESS)
9248c2ecf20Sopenharmony_ci		rv = SS_IS_DISKLESS;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci	if (ns.conn == C_WF_CONNECTION && os.conn < C_UNCONNECTED)
9278c2ecf20Sopenharmony_ci		rv = SS_NO_NET_CONFIG;
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	if (ns.disk == D_OUTDATED && os.disk < D_OUTDATED && os.disk != D_ATTACHING)
9308c2ecf20Sopenharmony_ci		rv = SS_LOWER_THAN_OUTDATED;
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_ci	if (ns.conn == C_DISCONNECTING && os.conn == C_UNCONNECTED)
9338c2ecf20Sopenharmony_ci		rv = SS_IN_TRANSIENT_STATE;
9348c2ecf20Sopenharmony_ci
9358c2ecf20Sopenharmony_ci	/* While establishing a connection only allow cstate to change.
9368c2ecf20Sopenharmony_ci	   Delay/refuse role changes, detach attach etc... (they do not touch cstate) */
9378c2ecf20Sopenharmony_ci	if (test_bit(STATE_SENT, &connection->flags) &&
9388c2ecf20Sopenharmony_ci	    !((ns.conn == C_WF_REPORT_PARAMS && os.conn == C_WF_CONNECTION) ||
9398c2ecf20Sopenharmony_ci	      (ns.conn >= C_CONNECTED && os.conn == C_WF_REPORT_PARAMS)))
9408c2ecf20Sopenharmony_ci		rv = SS_IN_TRANSIENT_STATE;
9418c2ecf20Sopenharmony_ci
9428c2ecf20Sopenharmony_ci	/* Do not promote during resync handshake triggered by "force primary".
9438c2ecf20Sopenharmony_ci	 * This is a hack. It should really be rejected by the peer during the
9448c2ecf20Sopenharmony_ci	 * cluster wide state change request. */
9458c2ecf20Sopenharmony_ci	if (os.role != R_PRIMARY && ns.role == R_PRIMARY
9468c2ecf20Sopenharmony_ci		&& ns.pdsk == D_UP_TO_DATE
9478c2ecf20Sopenharmony_ci		&& ns.disk != D_UP_TO_DATE && ns.disk != D_DISKLESS
9488c2ecf20Sopenharmony_ci		&& (ns.conn <= C_WF_SYNC_UUID || ns.conn != os.conn))
9498c2ecf20Sopenharmony_ci			rv = SS_IN_TRANSIENT_STATE;
9508c2ecf20Sopenharmony_ci
9518c2ecf20Sopenharmony_ci	if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) && os.conn < C_CONNECTED)
9528c2ecf20Sopenharmony_ci		rv = SS_NEED_CONNECTION;
9538c2ecf20Sopenharmony_ci
9548c2ecf20Sopenharmony_ci	if ((ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T) &&
9558c2ecf20Sopenharmony_ci	    ns.conn != os.conn && os.conn > C_CONNECTED)
9568c2ecf20Sopenharmony_ci		rv = SS_RESYNC_RUNNING;
9578c2ecf20Sopenharmony_ci
9588c2ecf20Sopenharmony_ci	if ((ns.conn == C_STARTING_SYNC_S || ns.conn == C_STARTING_SYNC_T) &&
9598c2ecf20Sopenharmony_ci	    os.conn < C_CONNECTED)
9608c2ecf20Sopenharmony_ci		rv = SS_NEED_CONNECTION;
9618c2ecf20Sopenharmony_ci
9628c2ecf20Sopenharmony_ci	if ((ns.conn == C_SYNC_TARGET || ns.conn == C_SYNC_SOURCE)
9638c2ecf20Sopenharmony_ci	    && os.conn < C_WF_REPORT_PARAMS)
9648c2ecf20Sopenharmony_ci		rv = SS_NEED_CONNECTION; /* No NetworkFailure -> SyncTarget etc... */
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci	if (ns.conn == C_DISCONNECTING && ns.pdsk == D_OUTDATED &&
9678c2ecf20Sopenharmony_ci	    os.conn < C_CONNECTED && os.pdsk > D_OUTDATED)
9688c2ecf20Sopenharmony_ci		rv = SS_OUTDATE_WO_CONN;
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	return rv;
9718c2ecf20Sopenharmony_ci}
9728c2ecf20Sopenharmony_ci
9738c2ecf20Sopenharmony_cistatic enum drbd_state_rv
9748c2ecf20Sopenharmony_ciis_valid_conn_transition(enum drbd_conns oc, enum drbd_conns nc)
9758c2ecf20Sopenharmony_ci{
9768c2ecf20Sopenharmony_ci	/* no change -> nothing to do, at least for the connection part */
9778c2ecf20Sopenharmony_ci	if (oc == nc)
9788c2ecf20Sopenharmony_ci		return SS_NOTHING_TO_DO;
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci	/* disconnect of an unconfigured connection does not make sense */
9818c2ecf20Sopenharmony_ci	if (oc == C_STANDALONE && nc == C_DISCONNECTING)
9828c2ecf20Sopenharmony_ci		return SS_ALREADY_STANDALONE;
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci	/* from C_STANDALONE, we start with C_UNCONNECTED */
9858c2ecf20Sopenharmony_ci	if (oc == C_STANDALONE && nc != C_UNCONNECTED)
9868c2ecf20Sopenharmony_ci		return SS_NEED_CONNECTION;
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci	/* When establishing a connection we need to go through WF_REPORT_PARAMS!
9898c2ecf20Sopenharmony_ci	   Necessary to do the right thing upon invalidate-remote on a disconnected resource */
9908c2ecf20Sopenharmony_ci	if (oc < C_WF_REPORT_PARAMS && nc >= C_CONNECTED)
9918c2ecf20Sopenharmony_ci		return SS_NEED_CONNECTION;
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci	/* After a network error only C_UNCONNECTED or C_DISCONNECTING may follow. */
9948c2ecf20Sopenharmony_ci	if (oc >= C_TIMEOUT && oc <= C_TEAR_DOWN && nc != C_UNCONNECTED && nc != C_DISCONNECTING)
9958c2ecf20Sopenharmony_ci		return SS_IN_TRANSIENT_STATE;
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/* After C_DISCONNECTING only C_STANDALONE may follow */
9988c2ecf20Sopenharmony_ci	if (oc == C_DISCONNECTING && nc != C_STANDALONE)
9998c2ecf20Sopenharmony_ci		return SS_IN_TRANSIENT_STATE;
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci	return SS_SUCCESS;
10028c2ecf20Sopenharmony_ci}
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci
10058c2ecf20Sopenharmony_ci/**
10068c2ecf20Sopenharmony_ci * is_valid_transition() - Returns an SS_ error code if the state transition is not possible
10078c2ecf20Sopenharmony_ci * This limits hard state transitions. Hard state transitions are facts there are
10088c2ecf20Sopenharmony_ci * imposed on DRBD by the environment. E.g. disk broke or network broke down.
10098c2ecf20Sopenharmony_ci * But those hard state transitions are still not allowed to do everything.
10108c2ecf20Sopenharmony_ci * @ns:		new state.
10118c2ecf20Sopenharmony_ci * @os:		old state.
10128c2ecf20Sopenharmony_ci */
10138c2ecf20Sopenharmony_cistatic enum drbd_state_rv
10148c2ecf20Sopenharmony_ciis_valid_transition(union drbd_state os, union drbd_state ns)
10158c2ecf20Sopenharmony_ci{
10168c2ecf20Sopenharmony_ci	enum drbd_state_rv rv;
10178c2ecf20Sopenharmony_ci
10188c2ecf20Sopenharmony_ci	rv = is_valid_conn_transition(os.conn, ns.conn);
10198c2ecf20Sopenharmony_ci
10208c2ecf20Sopenharmony_ci	/* we cannot fail (again) if we already detached */
10218c2ecf20Sopenharmony_ci	if (ns.disk == D_FAILED && os.disk == D_DISKLESS)
10228c2ecf20Sopenharmony_ci		rv = SS_IS_DISKLESS;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	return rv;
10258c2ecf20Sopenharmony_ci}
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_cistatic void print_sanitize_warnings(struct drbd_device *device, enum sanitize_state_warnings warn)
10288c2ecf20Sopenharmony_ci{
10298c2ecf20Sopenharmony_ci	static const char *msg_table[] = {
10308c2ecf20Sopenharmony_ci		[NO_WARNING] = "",
10318c2ecf20Sopenharmony_ci		[ABORTED_ONLINE_VERIFY] = "Online-verify aborted.",
10328c2ecf20Sopenharmony_ci		[ABORTED_RESYNC] = "Resync aborted.",
10338c2ecf20Sopenharmony_ci		[CONNECTION_LOST_NEGOTIATING] = "Connection lost while negotiating, no data!",
10348c2ecf20Sopenharmony_ci		[IMPLICITLY_UPGRADED_DISK] = "Implicitly upgraded disk",
10358c2ecf20Sopenharmony_ci		[IMPLICITLY_UPGRADED_PDSK] = "Implicitly upgraded pdsk",
10368c2ecf20Sopenharmony_ci	};
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	if (warn != NO_WARNING)
10398c2ecf20Sopenharmony_ci		drbd_warn(device, "%s\n", msg_table[warn]);
10408c2ecf20Sopenharmony_ci}
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci/**
10438c2ecf20Sopenharmony_ci * sanitize_state() - Resolves implicitly necessary additional changes to a state transition
10448c2ecf20Sopenharmony_ci * @device:	DRBD device.
10458c2ecf20Sopenharmony_ci * @os:		old state.
10468c2ecf20Sopenharmony_ci * @ns:		new state.
10478c2ecf20Sopenharmony_ci * @warn_sync_abort:
10488c2ecf20Sopenharmony_ci *
10498c2ecf20Sopenharmony_ci * When we loose connection, we have to set the state of the peers disk (pdsk)
10508c2ecf20Sopenharmony_ci * to D_UNKNOWN. This rule and many more along those lines are in this function.
10518c2ecf20Sopenharmony_ci */
10528c2ecf20Sopenharmony_cistatic union drbd_state sanitize_state(struct drbd_device *device, union drbd_state os,
10538c2ecf20Sopenharmony_ci				       union drbd_state ns, enum sanitize_state_warnings *warn)
10548c2ecf20Sopenharmony_ci{
10558c2ecf20Sopenharmony_ci	enum drbd_fencing_p fp;
10568c2ecf20Sopenharmony_ci	enum drbd_disk_state disk_min, disk_max, pdsk_min, pdsk_max;
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci	if (warn)
10598c2ecf20Sopenharmony_ci		*warn = NO_WARNING;
10608c2ecf20Sopenharmony_ci
10618c2ecf20Sopenharmony_ci	fp = FP_DONT_CARE;
10628c2ecf20Sopenharmony_ci	if (get_ldev(device)) {
10638c2ecf20Sopenharmony_ci		rcu_read_lock();
10648c2ecf20Sopenharmony_ci		fp = rcu_dereference(device->ldev->disk_conf)->fencing;
10658c2ecf20Sopenharmony_ci		rcu_read_unlock();
10668c2ecf20Sopenharmony_ci		put_ldev(device);
10678c2ecf20Sopenharmony_ci	}
10688c2ecf20Sopenharmony_ci
10698c2ecf20Sopenharmony_ci	/* Implications from connection to peer and peer_isp */
10708c2ecf20Sopenharmony_ci	if (ns.conn < C_CONNECTED) {
10718c2ecf20Sopenharmony_ci		ns.peer_isp = 0;
10728c2ecf20Sopenharmony_ci		ns.peer = R_UNKNOWN;
10738c2ecf20Sopenharmony_ci		if (ns.pdsk > D_UNKNOWN || ns.pdsk < D_INCONSISTENT)
10748c2ecf20Sopenharmony_ci			ns.pdsk = D_UNKNOWN;
10758c2ecf20Sopenharmony_ci	}
10768c2ecf20Sopenharmony_ci
10778c2ecf20Sopenharmony_ci	/* Clear the aftr_isp when becoming unconfigured */
10788c2ecf20Sopenharmony_ci	if (ns.conn == C_STANDALONE && ns.disk == D_DISKLESS && ns.role == R_SECONDARY)
10798c2ecf20Sopenharmony_ci		ns.aftr_isp = 0;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	/* An implication of the disk states onto the connection state */
10828c2ecf20Sopenharmony_ci	/* Abort resync if a disk fails/detaches */
10838c2ecf20Sopenharmony_ci	if (ns.conn > C_CONNECTED && (ns.disk <= D_FAILED || ns.pdsk <= D_FAILED)) {
10848c2ecf20Sopenharmony_ci		if (warn)
10858c2ecf20Sopenharmony_ci			*warn = ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T ?
10868c2ecf20Sopenharmony_ci				ABORTED_ONLINE_VERIFY : ABORTED_RESYNC;
10878c2ecf20Sopenharmony_ci		ns.conn = C_CONNECTED;
10888c2ecf20Sopenharmony_ci	}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	/* Connection breaks down before we finished "Negotiating" */
10918c2ecf20Sopenharmony_ci	if (ns.conn < C_CONNECTED && ns.disk == D_NEGOTIATING &&
10928c2ecf20Sopenharmony_ci	    get_ldev_if_state(device, D_NEGOTIATING)) {
10938c2ecf20Sopenharmony_ci		if (device->ed_uuid == device->ldev->md.uuid[UI_CURRENT]) {
10948c2ecf20Sopenharmony_ci			ns.disk = device->new_state_tmp.disk;
10958c2ecf20Sopenharmony_ci			ns.pdsk = device->new_state_tmp.pdsk;
10968c2ecf20Sopenharmony_ci		} else {
10978c2ecf20Sopenharmony_ci			if (warn)
10988c2ecf20Sopenharmony_ci				*warn = CONNECTION_LOST_NEGOTIATING;
10998c2ecf20Sopenharmony_ci			ns.disk = D_DISKLESS;
11008c2ecf20Sopenharmony_ci			ns.pdsk = D_UNKNOWN;
11018c2ecf20Sopenharmony_ci		}
11028c2ecf20Sopenharmony_ci		put_ldev(device);
11038c2ecf20Sopenharmony_ci	}
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci	/* D_CONSISTENT and D_OUTDATED vanish when we get connected */
11068c2ecf20Sopenharmony_ci	if (ns.conn >= C_CONNECTED && ns.conn < C_AHEAD) {
11078c2ecf20Sopenharmony_ci		if (ns.disk == D_CONSISTENT || ns.disk == D_OUTDATED)
11088c2ecf20Sopenharmony_ci			ns.disk = D_UP_TO_DATE;
11098c2ecf20Sopenharmony_ci		if (ns.pdsk == D_CONSISTENT || ns.pdsk == D_OUTDATED)
11108c2ecf20Sopenharmony_ci			ns.pdsk = D_UP_TO_DATE;
11118c2ecf20Sopenharmony_ci	}
11128c2ecf20Sopenharmony_ci
11138c2ecf20Sopenharmony_ci	/* Implications of the connection state on the disk states */
11148c2ecf20Sopenharmony_ci	disk_min = D_DISKLESS;
11158c2ecf20Sopenharmony_ci	disk_max = D_UP_TO_DATE;
11168c2ecf20Sopenharmony_ci	pdsk_min = D_INCONSISTENT;
11178c2ecf20Sopenharmony_ci	pdsk_max = D_UNKNOWN;
11188c2ecf20Sopenharmony_ci	switch ((enum drbd_conns)ns.conn) {
11198c2ecf20Sopenharmony_ci	case C_WF_BITMAP_T:
11208c2ecf20Sopenharmony_ci	case C_PAUSED_SYNC_T:
11218c2ecf20Sopenharmony_ci	case C_STARTING_SYNC_T:
11228c2ecf20Sopenharmony_ci	case C_WF_SYNC_UUID:
11238c2ecf20Sopenharmony_ci	case C_BEHIND:
11248c2ecf20Sopenharmony_ci		disk_min = D_INCONSISTENT;
11258c2ecf20Sopenharmony_ci		disk_max = D_OUTDATED;
11268c2ecf20Sopenharmony_ci		pdsk_min = D_UP_TO_DATE;
11278c2ecf20Sopenharmony_ci		pdsk_max = D_UP_TO_DATE;
11288c2ecf20Sopenharmony_ci		break;
11298c2ecf20Sopenharmony_ci	case C_VERIFY_S:
11308c2ecf20Sopenharmony_ci	case C_VERIFY_T:
11318c2ecf20Sopenharmony_ci		disk_min = D_UP_TO_DATE;
11328c2ecf20Sopenharmony_ci		disk_max = D_UP_TO_DATE;
11338c2ecf20Sopenharmony_ci		pdsk_min = D_UP_TO_DATE;
11348c2ecf20Sopenharmony_ci		pdsk_max = D_UP_TO_DATE;
11358c2ecf20Sopenharmony_ci		break;
11368c2ecf20Sopenharmony_ci	case C_CONNECTED:
11378c2ecf20Sopenharmony_ci		disk_min = D_DISKLESS;
11388c2ecf20Sopenharmony_ci		disk_max = D_UP_TO_DATE;
11398c2ecf20Sopenharmony_ci		pdsk_min = D_DISKLESS;
11408c2ecf20Sopenharmony_ci		pdsk_max = D_UP_TO_DATE;
11418c2ecf20Sopenharmony_ci		break;
11428c2ecf20Sopenharmony_ci	case C_WF_BITMAP_S:
11438c2ecf20Sopenharmony_ci	case C_PAUSED_SYNC_S:
11448c2ecf20Sopenharmony_ci	case C_STARTING_SYNC_S:
11458c2ecf20Sopenharmony_ci	case C_AHEAD:
11468c2ecf20Sopenharmony_ci		disk_min = D_UP_TO_DATE;
11478c2ecf20Sopenharmony_ci		disk_max = D_UP_TO_DATE;
11488c2ecf20Sopenharmony_ci		pdsk_min = D_INCONSISTENT;
11498c2ecf20Sopenharmony_ci		pdsk_max = D_CONSISTENT; /* D_OUTDATED would be nice. But explicit outdate necessary*/
11508c2ecf20Sopenharmony_ci		break;
11518c2ecf20Sopenharmony_ci	case C_SYNC_TARGET:
11528c2ecf20Sopenharmony_ci		disk_min = D_INCONSISTENT;
11538c2ecf20Sopenharmony_ci		disk_max = D_INCONSISTENT;
11548c2ecf20Sopenharmony_ci		pdsk_min = D_UP_TO_DATE;
11558c2ecf20Sopenharmony_ci		pdsk_max = D_UP_TO_DATE;
11568c2ecf20Sopenharmony_ci		break;
11578c2ecf20Sopenharmony_ci	case C_SYNC_SOURCE:
11588c2ecf20Sopenharmony_ci		disk_min = D_UP_TO_DATE;
11598c2ecf20Sopenharmony_ci		disk_max = D_UP_TO_DATE;
11608c2ecf20Sopenharmony_ci		pdsk_min = D_INCONSISTENT;
11618c2ecf20Sopenharmony_ci		pdsk_max = D_INCONSISTENT;
11628c2ecf20Sopenharmony_ci		break;
11638c2ecf20Sopenharmony_ci	case C_STANDALONE:
11648c2ecf20Sopenharmony_ci	case C_DISCONNECTING:
11658c2ecf20Sopenharmony_ci	case C_UNCONNECTED:
11668c2ecf20Sopenharmony_ci	case C_TIMEOUT:
11678c2ecf20Sopenharmony_ci	case C_BROKEN_PIPE:
11688c2ecf20Sopenharmony_ci	case C_NETWORK_FAILURE:
11698c2ecf20Sopenharmony_ci	case C_PROTOCOL_ERROR:
11708c2ecf20Sopenharmony_ci	case C_TEAR_DOWN:
11718c2ecf20Sopenharmony_ci	case C_WF_CONNECTION:
11728c2ecf20Sopenharmony_ci	case C_WF_REPORT_PARAMS:
11738c2ecf20Sopenharmony_ci	case C_MASK:
11748c2ecf20Sopenharmony_ci		break;
11758c2ecf20Sopenharmony_ci	}
11768c2ecf20Sopenharmony_ci	if (ns.disk > disk_max)
11778c2ecf20Sopenharmony_ci		ns.disk = disk_max;
11788c2ecf20Sopenharmony_ci
11798c2ecf20Sopenharmony_ci	if (ns.disk < disk_min) {
11808c2ecf20Sopenharmony_ci		if (warn)
11818c2ecf20Sopenharmony_ci			*warn = IMPLICITLY_UPGRADED_DISK;
11828c2ecf20Sopenharmony_ci		ns.disk = disk_min;
11838c2ecf20Sopenharmony_ci	}
11848c2ecf20Sopenharmony_ci	if (ns.pdsk > pdsk_max)
11858c2ecf20Sopenharmony_ci		ns.pdsk = pdsk_max;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci	if (ns.pdsk < pdsk_min) {
11888c2ecf20Sopenharmony_ci		if (warn)
11898c2ecf20Sopenharmony_ci			*warn = IMPLICITLY_UPGRADED_PDSK;
11908c2ecf20Sopenharmony_ci		ns.pdsk = pdsk_min;
11918c2ecf20Sopenharmony_ci	}
11928c2ecf20Sopenharmony_ci
11938c2ecf20Sopenharmony_ci	if (fp == FP_STONITH &&
11948c2ecf20Sopenharmony_ci	    (ns.role == R_PRIMARY && ns.conn < C_CONNECTED && ns.pdsk > D_OUTDATED) &&
11958c2ecf20Sopenharmony_ci	    !(os.role == R_PRIMARY && os.conn < C_CONNECTED && os.pdsk > D_OUTDATED))
11968c2ecf20Sopenharmony_ci		ns.susp_fen = 1; /* Suspend IO while fence-peer handler runs (peer lost) */
11978c2ecf20Sopenharmony_ci
11988c2ecf20Sopenharmony_ci	if (device->resource->res_opts.on_no_data == OND_SUSPEND_IO &&
11998c2ecf20Sopenharmony_ci	    (ns.role == R_PRIMARY && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE) &&
12008c2ecf20Sopenharmony_ci	    !(os.role == R_PRIMARY && os.disk < D_UP_TO_DATE && os.pdsk < D_UP_TO_DATE))
12018c2ecf20Sopenharmony_ci		ns.susp_nod = 1; /* Suspend IO while no data available (no accessible data available) */
12028c2ecf20Sopenharmony_ci
12038c2ecf20Sopenharmony_ci	if (ns.aftr_isp || ns.peer_isp || ns.user_isp) {
12048c2ecf20Sopenharmony_ci		if (ns.conn == C_SYNC_SOURCE)
12058c2ecf20Sopenharmony_ci			ns.conn = C_PAUSED_SYNC_S;
12068c2ecf20Sopenharmony_ci		if (ns.conn == C_SYNC_TARGET)
12078c2ecf20Sopenharmony_ci			ns.conn = C_PAUSED_SYNC_T;
12088c2ecf20Sopenharmony_ci	} else {
12098c2ecf20Sopenharmony_ci		if (ns.conn == C_PAUSED_SYNC_S)
12108c2ecf20Sopenharmony_ci			ns.conn = C_SYNC_SOURCE;
12118c2ecf20Sopenharmony_ci		if (ns.conn == C_PAUSED_SYNC_T)
12128c2ecf20Sopenharmony_ci			ns.conn = C_SYNC_TARGET;
12138c2ecf20Sopenharmony_ci	}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci	return ns;
12168c2ecf20Sopenharmony_ci}
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_civoid drbd_resume_al(struct drbd_device *device)
12198c2ecf20Sopenharmony_ci{
12208c2ecf20Sopenharmony_ci	if (test_and_clear_bit(AL_SUSPENDED, &device->flags))
12218c2ecf20Sopenharmony_ci		drbd_info(device, "Resumed AL updates\n");
12228c2ecf20Sopenharmony_ci}
12238c2ecf20Sopenharmony_ci
12248c2ecf20Sopenharmony_ci/* helper for _drbd_set_state */
12258c2ecf20Sopenharmony_cistatic void set_ov_position(struct drbd_device *device, enum drbd_conns cs)
12268c2ecf20Sopenharmony_ci{
12278c2ecf20Sopenharmony_ci	if (first_peer_device(device)->connection->agreed_pro_version < 90)
12288c2ecf20Sopenharmony_ci		device->ov_start_sector = 0;
12298c2ecf20Sopenharmony_ci	device->rs_total = drbd_bm_bits(device);
12308c2ecf20Sopenharmony_ci	device->ov_position = 0;
12318c2ecf20Sopenharmony_ci	if (cs == C_VERIFY_T) {
12328c2ecf20Sopenharmony_ci		/* starting online verify from an arbitrary position
12338c2ecf20Sopenharmony_ci		 * does not fit well into the existing protocol.
12348c2ecf20Sopenharmony_ci		 * on C_VERIFY_T, we initialize ov_left and friends
12358c2ecf20Sopenharmony_ci		 * implicitly in receive_DataRequest once the
12368c2ecf20Sopenharmony_ci		 * first P_OV_REQUEST is received */
12378c2ecf20Sopenharmony_ci		device->ov_start_sector = ~(sector_t)0;
12388c2ecf20Sopenharmony_ci	} else {
12398c2ecf20Sopenharmony_ci		unsigned long bit = BM_SECT_TO_BIT(device->ov_start_sector);
12408c2ecf20Sopenharmony_ci		if (bit >= device->rs_total) {
12418c2ecf20Sopenharmony_ci			device->ov_start_sector =
12428c2ecf20Sopenharmony_ci				BM_BIT_TO_SECT(device->rs_total - 1);
12438c2ecf20Sopenharmony_ci			device->rs_total = 1;
12448c2ecf20Sopenharmony_ci		} else
12458c2ecf20Sopenharmony_ci			device->rs_total -= bit;
12468c2ecf20Sopenharmony_ci		device->ov_position = device->ov_start_sector;
12478c2ecf20Sopenharmony_ci	}
12488c2ecf20Sopenharmony_ci	device->ov_left = device->rs_total;
12498c2ecf20Sopenharmony_ci}
12508c2ecf20Sopenharmony_ci
12518c2ecf20Sopenharmony_ci/**
12528c2ecf20Sopenharmony_ci * _drbd_set_state() - Set a new DRBD state
12538c2ecf20Sopenharmony_ci * @device:	DRBD device.
12548c2ecf20Sopenharmony_ci * @ns:		new state.
12558c2ecf20Sopenharmony_ci * @flags:	Flags
12568c2ecf20Sopenharmony_ci * @done:	Optional completion, that will get completed after the after_state_ch() finished
12578c2ecf20Sopenharmony_ci *
12588c2ecf20Sopenharmony_ci * Caller needs to hold req_lock. Do not call directly.
12598c2ecf20Sopenharmony_ci */
12608c2ecf20Sopenharmony_cienum drbd_state_rv
12618c2ecf20Sopenharmony_ci_drbd_set_state(struct drbd_device *device, union drbd_state ns,
12628c2ecf20Sopenharmony_ci	        enum chg_state_flags flags, struct completion *done)
12638c2ecf20Sopenharmony_ci{
12648c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device = first_peer_device(device);
12658c2ecf20Sopenharmony_ci	struct drbd_connection *connection = peer_device ? peer_device->connection : NULL;
12668c2ecf20Sopenharmony_ci	union drbd_state os;
12678c2ecf20Sopenharmony_ci	enum drbd_state_rv rv = SS_SUCCESS;
12688c2ecf20Sopenharmony_ci	enum sanitize_state_warnings ssw;
12698c2ecf20Sopenharmony_ci	struct after_state_chg_work *ascw;
12708c2ecf20Sopenharmony_ci	struct drbd_state_change *state_change;
12718c2ecf20Sopenharmony_ci
12728c2ecf20Sopenharmony_ci	os = drbd_read_state(device);
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_ci	ns = sanitize_state(device, os, ns, &ssw);
12758c2ecf20Sopenharmony_ci	if (ns.i == os.i)
12768c2ecf20Sopenharmony_ci		return SS_NOTHING_TO_DO;
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	rv = is_valid_transition(os, ns);
12798c2ecf20Sopenharmony_ci	if (rv < SS_SUCCESS)
12808c2ecf20Sopenharmony_ci		return rv;
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci	if (!(flags & CS_HARD)) {
12838c2ecf20Sopenharmony_ci		/*  pre-state-change checks ; only look at ns  */
12848c2ecf20Sopenharmony_ci		/* See drbd_state_sw_errors in drbd_strings.c */
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci		rv = is_valid_state(device, ns);
12878c2ecf20Sopenharmony_ci		if (rv < SS_SUCCESS) {
12888c2ecf20Sopenharmony_ci			/* If the old state was illegal as well, then let
12898c2ecf20Sopenharmony_ci			   this happen...*/
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci			if (is_valid_state(device, os) == rv)
12928c2ecf20Sopenharmony_ci				rv = is_valid_soft_transition(os, ns, connection);
12938c2ecf20Sopenharmony_ci		} else
12948c2ecf20Sopenharmony_ci			rv = is_valid_soft_transition(os, ns, connection);
12958c2ecf20Sopenharmony_ci	}
12968c2ecf20Sopenharmony_ci
12978c2ecf20Sopenharmony_ci	if (rv < SS_SUCCESS) {
12988c2ecf20Sopenharmony_ci		if (flags & CS_VERBOSE)
12998c2ecf20Sopenharmony_ci			print_st_err(device, os, ns, rv);
13008c2ecf20Sopenharmony_ci		return rv;
13018c2ecf20Sopenharmony_ci	}
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci	print_sanitize_warnings(device, ssw);
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	drbd_pr_state_change(device, os, ns, flags);
13068c2ecf20Sopenharmony_ci
13078c2ecf20Sopenharmony_ci	/* Display changes to the susp* flags that where caused by the call to
13088c2ecf20Sopenharmony_ci	   sanitize_state(). Only display it here if we where not called from
13098c2ecf20Sopenharmony_ci	   _conn_request_state() */
13108c2ecf20Sopenharmony_ci	if (!(flags & CS_DC_SUSP))
13118c2ecf20Sopenharmony_ci		conn_pr_state_change(connection, os, ns,
13128c2ecf20Sopenharmony_ci				     (flags & ~CS_DC_MASK) | CS_DC_SUSP);
13138c2ecf20Sopenharmony_ci
13148c2ecf20Sopenharmony_ci	/* if we are going -> D_FAILED or D_DISKLESS, grab one extra reference
13158c2ecf20Sopenharmony_ci	 * on the ldev here, to be sure the transition -> D_DISKLESS resp.
13168c2ecf20Sopenharmony_ci	 * drbd_ldev_destroy() won't happen before our corresponding
13178c2ecf20Sopenharmony_ci	 * after_state_ch works run, where we put_ldev again. */
13188c2ecf20Sopenharmony_ci	if ((os.disk != D_FAILED && ns.disk == D_FAILED) ||
13198c2ecf20Sopenharmony_ci	    (os.disk != D_DISKLESS && ns.disk == D_DISKLESS))
13208c2ecf20Sopenharmony_ci		atomic_inc(&device->local_cnt);
13218c2ecf20Sopenharmony_ci
13228c2ecf20Sopenharmony_ci	if (!is_sync_state(os.conn) && is_sync_state(ns.conn))
13238c2ecf20Sopenharmony_ci		clear_bit(RS_DONE, &device->flags);
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	/* FIXME: Have any flags been set earlier in this function already? */
13268c2ecf20Sopenharmony_ci	state_change = remember_old_state(device->resource, GFP_ATOMIC);
13278c2ecf20Sopenharmony_ci
13288c2ecf20Sopenharmony_ci	/* changes to local_cnt and device flags should be visible before
13298c2ecf20Sopenharmony_ci	 * changes to state, which again should be visible before anything else
13308c2ecf20Sopenharmony_ci	 * depending on that change happens. */
13318c2ecf20Sopenharmony_ci	smp_wmb();
13328c2ecf20Sopenharmony_ci	device->state.i = ns.i;
13338c2ecf20Sopenharmony_ci	device->resource->susp = ns.susp;
13348c2ecf20Sopenharmony_ci	device->resource->susp_nod = ns.susp_nod;
13358c2ecf20Sopenharmony_ci	device->resource->susp_fen = ns.susp_fen;
13368c2ecf20Sopenharmony_ci	smp_wmb();
13378c2ecf20Sopenharmony_ci
13388c2ecf20Sopenharmony_ci	remember_new_state(state_change);
13398c2ecf20Sopenharmony_ci
13408c2ecf20Sopenharmony_ci	/* put replicated vs not-replicated requests in seperate epochs */
13418c2ecf20Sopenharmony_ci	if (drbd_should_do_remote((union drbd_dev_state)os.i) !=
13428c2ecf20Sopenharmony_ci	    drbd_should_do_remote((union drbd_dev_state)ns.i))
13438c2ecf20Sopenharmony_ci		start_new_tl_epoch(connection);
13448c2ecf20Sopenharmony_ci
13458c2ecf20Sopenharmony_ci	if (os.disk == D_ATTACHING && ns.disk >= D_NEGOTIATING)
13468c2ecf20Sopenharmony_ci		drbd_print_uuids(device, "attached to UUIDs");
13478c2ecf20Sopenharmony_ci
13488c2ecf20Sopenharmony_ci	/* Wake up role changes, that were delayed because of connection establishing */
13498c2ecf20Sopenharmony_ci	if (os.conn == C_WF_REPORT_PARAMS && ns.conn != C_WF_REPORT_PARAMS &&
13508c2ecf20Sopenharmony_ci	    no_peer_wf_report_params(connection)) {
13518c2ecf20Sopenharmony_ci		clear_bit(STATE_SENT, &connection->flags);
13528c2ecf20Sopenharmony_ci		wake_up_all_devices(connection);
13538c2ecf20Sopenharmony_ci	}
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	wake_up(&device->misc_wait);
13568c2ecf20Sopenharmony_ci	wake_up(&device->state_wait);
13578c2ecf20Sopenharmony_ci	wake_up(&connection->ping_wait);
13588c2ecf20Sopenharmony_ci
13598c2ecf20Sopenharmony_ci	/* Aborted verify run, or we reached the stop sector.
13608c2ecf20Sopenharmony_ci	 * Log the last position, unless end-of-device. */
13618c2ecf20Sopenharmony_ci	if ((os.conn == C_VERIFY_S || os.conn == C_VERIFY_T) &&
13628c2ecf20Sopenharmony_ci	    ns.conn <= C_CONNECTED) {
13638c2ecf20Sopenharmony_ci		device->ov_start_sector =
13648c2ecf20Sopenharmony_ci			BM_BIT_TO_SECT(drbd_bm_bits(device) - device->ov_left);
13658c2ecf20Sopenharmony_ci		if (device->ov_left)
13668c2ecf20Sopenharmony_ci			drbd_info(device, "Online Verify reached sector %llu\n",
13678c2ecf20Sopenharmony_ci				(unsigned long long)device->ov_start_sector);
13688c2ecf20Sopenharmony_ci	}
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci	if ((os.conn == C_PAUSED_SYNC_T || os.conn == C_PAUSED_SYNC_S) &&
13718c2ecf20Sopenharmony_ci	    (ns.conn == C_SYNC_TARGET  || ns.conn == C_SYNC_SOURCE)) {
13728c2ecf20Sopenharmony_ci		drbd_info(device, "Syncer continues.\n");
13738c2ecf20Sopenharmony_ci		device->rs_paused += (long)jiffies
13748c2ecf20Sopenharmony_ci				  -(long)device->rs_mark_time[device->rs_last_mark];
13758c2ecf20Sopenharmony_ci		if (ns.conn == C_SYNC_TARGET)
13768c2ecf20Sopenharmony_ci			mod_timer(&device->resync_timer, jiffies);
13778c2ecf20Sopenharmony_ci	}
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	if ((os.conn == C_SYNC_TARGET  || os.conn == C_SYNC_SOURCE) &&
13808c2ecf20Sopenharmony_ci	    (ns.conn == C_PAUSED_SYNC_T || ns.conn == C_PAUSED_SYNC_S)) {
13818c2ecf20Sopenharmony_ci		drbd_info(device, "Resync suspended\n");
13828c2ecf20Sopenharmony_ci		device->rs_mark_time[device->rs_last_mark] = jiffies;
13838c2ecf20Sopenharmony_ci	}
13848c2ecf20Sopenharmony_ci
13858c2ecf20Sopenharmony_ci	if (os.conn == C_CONNECTED &&
13868c2ecf20Sopenharmony_ci	    (ns.conn == C_VERIFY_S || ns.conn == C_VERIFY_T)) {
13878c2ecf20Sopenharmony_ci		unsigned long now = jiffies;
13888c2ecf20Sopenharmony_ci		int i;
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci		set_ov_position(device, ns.conn);
13918c2ecf20Sopenharmony_ci		device->rs_start = now;
13928c2ecf20Sopenharmony_ci		device->rs_last_sect_ev = 0;
13938c2ecf20Sopenharmony_ci		device->ov_last_oos_size = 0;
13948c2ecf20Sopenharmony_ci		device->ov_last_oos_start = 0;
13958c2ecf20Sopenharmony_ci
13968c2ecf20Sopenharmony_ci		for (i = 0; i < DRBD_SYNC_MARKS; i++) {
13978c2ecf20Sopenharmony_ci			device->rs_mark_left[i] = device->ov_left;
13988c2ecf20Sopenharmony_ci			device->rs_mark_time[i] = now;
13998c2ecf20Sopenharmony_ci		}
14008c2ecf20Sopenharmony_ci
14018c2ecf20Sopenharmony_ci		drbd_rs_controller_reset(device);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci		if (ns.conn == C_VERIFY_S) {
14048c2ecf20Sopenharmony_ci			drbd_info(device, "Starting Online Verify from sector %llu\n",
14058c2ecf20Sopenharmony_ci					(unsigned long long)device->ov_position);
14068c2ecf20Sopenharmony_ci			mod_timer(&device->resync_timer, jiffies);
14078c2ecf20Sopenharmony_ci		}
14088c2ecf20Sopenharmony_ci	}
14098c2ecf20Sopenharmony_ci
14108c2ecf20Sopenharmony_ci	if (get_ldev(device)) {
14118c2ecf20Sopenharmony_ci		u32 mdf = device->ldev->md.flags & ~(MDF_CONSISTENT|MDF_PRIMARY_IND|
14128c2ecf20Sopenharmony_ci						 MDF_CONNECTED_IND|MDF_WAS_UP_TO_DATE|
14138c2ecf20Sopenharmony_ci						 MDF_PEER_OUT_DATED|MDF_CRASHED_PRIMARY);
14148c2ecf20Sopenharmony_ci
14158c2ecf20Sopenharmony_ci		mdf &= ~MDF_AL_CLEAN;
14168c2ecf20Sopenharmony_ci		if (test_bit(CRASHED_PRIMARY, &device->flags))
14178c2ecf20Sopenharmony_ci			mdf |= MDF_CRASHED_PRIMARY;
14188c2ecf20Sopenharmony_ci		if (device->state.role == R_PRIMARY ||
14198c2ecf20Sopenharmony_ci		    (device->state.pdsk < D_INCONSISTENT && device->state.peer == R_PRIMARY))
14208c2ecf20Sopenharmony_ci			mdf |= MDF_PRIMARY_IND;
14218c2ecf20Sopenharmony_ci		if (device->state.conn > C_WF_REPORT_PARAMS)
14228c2ecf20Sopenharmony_ci			mdf |= MDF_CONNECTED_IND;
14238c2ecf20Sopenharmony_ci		if (device->state.disk > D_INCONSISTENT)
14248c2ecf20Sopenharmony_ci			mdf |= MDF_CONSISTENT;
14258c2ecf20Sopenharmony_ci		if (device->state.disk > D_OUTDATED)
14268c2ecf20Sopenharmony_ci			mdf |= MDF_WAS_UP_TO_DATE;
14278c2ecf20Sopenharmony_ci		if (device->state.pdsk <= D_OUTDATED && device->state.pdsk >= D_INCONSISTENT)
14288c2ecf20Sopenharmony_ci			mdf |= MDF_PEER_OUT_DATED;
14298c2ecf20Sopenharmony_ci		if (mdf != device->ldev->md.flags) {
14308c2ecf20Sopenharmony_ci			device->ldev->md.flags = mdf;
14318c2ecf20Sopenharmony_ci			drbd_md_mark_dirty(device);
14328c2ecf20Sopenharmony_ci		}
14338c2ecf20Sopenharmony_ci		if (os.disk < D_CONSISTENT && ns.disk >= D_CONSISTENT)
14348c2ecf20Sopenharmony_ci			drbd_set_ed_uuid(device, device->ldev->md.uuid[UI_CURRENT]);
14358c2ecf20Sopenharmony_ci		put_ldev(device);
14368c2ecf20Sopenharmony_ci	}
14378c2ecf20Sopenharmony_ci
14388c2ecf20Sopenharmony_ci	/* Peer was forced D_UP_TO_DATE & R_PRIMARY, consider to resync */
14398c2ecf20Sopenharmony_ci	if (os.disk == D_INCONSISTENT && os.pdsk == D_INCONSISTENT &&
14408c2ecf20Sopenharmony_ci	    os.peer == R_SECONDARY && ns.peer == R_PRIMARY)
14418c2ecf20Sopenharmony_ci		set_bit(CONSIDER_RESYNC, &device->flags);
14428c2ecf20Sopenharmony_ci
14438c2ecf20Sopenharmony_ci	/* Receiver should clean up itself */
14448c2ecf20Sopenharmony_ci	if (os.conn != C_DISCONNECTING && ns.conn == C_DISCONNECTING)
14458c2ecf20Sopenharmony_ci		drbd_thread_stop_nowait(&connection->receiver);
14468c2ecf20Sopenharmony_ci
14478c2ecf20Sopenharmony_ci	/* Now the receiver finished cleaning up itself, it should die */
14488c2ecf20Sopenharmony_ci	if (os.conn != C_STANDALONE && ns.conn == C_STANDALONE)
14498c2ecf20Sopenharmony_ci		drbd_thread_stop_nowait(&connection->receiver);
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci	/* Upon network failure, we need to restart the receiver. */
14528c2ecf20Sopenharmony_ci	if (os.conn > C_WF_CONNECTION &&
14538c2ecf20Sopenharmony_ci	    ns.conn <= C_TEAR_DOWN && ns.conn >= C_TIMEOUT)
14548c2ecf20Sopenharmony_ci		drbd_thread_restart_nowait(&connection->receiver);
14558c2ecf20Sopenharmony_ci
14568c2ecf20Sopenharmony_ci	/* Resume AL writing if we get a connection */
14578c2ecf20Sopenharmony_ci	if (os.conn < C_CONNECTED && ns.conn >= C_CONNECTED) {
14588c2ecf20Sopenharmony_ci		drbd_resume_al(device);
14598c2ecf20Sopenharmony_ci		connection->connect_cnt++;
14608c2ecf20Sopenharmony_ci	}
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	/* remember last attach time so request_timer_fn() won't
14638c2ecf20Sopenharmony_ci	 * kill newly established sessions while we are still trying to thaw
14648c2ecf20Sopenharmony_ci	 * previously frozen IO */
14658c2ecf20Sopenharmony_ci	if ((os.disk == D_ATTACHING || os.disk == D_NEGOTIATING) &&
14668c2ecf20Sopenharmony_ci	    ns.disk > D_NEGOTIATING)
14678c2ecf20Sopenharmony_ci		device->last_reattach_jif = jiffies;
14688c2ecf20Sopenharmony_ci
14698c2ecf20Sopenharmony_ci	ascw = kmalloc(sizeof(*ascw), GFP_ATOMIC);
14708c2ecf20Sopenharmony_ci	if (ascw) {
14718c2ecf20Sopenharmony_ci		ascw->os = os;
14728c2ecf20Sopenharmony_ci		ascw->ns = ns;
14738c2ecf20Sopenharmony_ci		ascw->flags = flags;
14748c2ecf20Sopenharmony_ci		ascw->w.cb = w_after_state_ch;
14758c2ecf20Sopenharmony_ci		ascw->device = device;
14768c2ecf20Sopenharmony_ci		ascw->done = done;
14778c2ecf20Sopenharmony_ci		ascw->state_change = state_change;
14788c2ecf20Sopenharmony_ci		drbd_queue_work(&connection->sender_work,
14798c2ecf20Sopenharmony_ci				&ascw->w);
14808c2ecf20Sopenharmony_ci	} else {
14818c2ecf20Sopenharmony_ci		drbd_err(device, "Could not kmalloc an ascw\n");
14828c2ecf20Sopenharmony_ci	}
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci	return rv;
14858c2ecf20Sopenharmony_ci}
14868c2ecf20Sopenharmony_ci
14878c2ecf20Sopenharmony_cistatic int w_after_state_ch(struct drbd_work *w, int unused)
14888c2ecf20Sopenharmony_ci{
14898c2ecf20Sopenharmony_ci	struct after_state_chg_work *ascw =
14908c2ecf20Sopenharmony_ci		container_of(w, struct after_state_chg_work, w);
14918c2ecf20Sopenharmony_ci	struct drbd_device *device = ascw->device;
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci	after_state_ch(device, ascw->os, ascw->ns, ascw->flags, ascw->state_change);
14948c2ecf20Sopenharmony_ci	forget_state_change(ascw->state_change);
14958c2ecf20Sopenharmony_ci	if (ascw->flags & CS_WAIT_COMPLETE)
14968c2ecf20Sopenharmony_ci		complete(ascw->done);
14978c2ecf20Sopenharmony_ci	kfree(ascw);
14988c2ecf20Sopenharmony_ci
14998c2ecf20Sopenharmony_ci	return 0;
15008c2ecf20Sopenharmony_ci}
15018c2ecf20Sopenharmony_ci
15028c2ecf20Sopenharmony_cistatic void abw_start_sync(struct drbd_device *device, int rv)
15038c2ecf20Sopenharmony_ci{
15048c2ecf20Sopenharmony_ci	if (rv) {
15058c2ecf20Sopenharmony_ci		drbd_err(device, "Writing the bitmap failed not starting resync.\n");
15068c2ecf20Sopenharmony_ci		_drbd_request_state(device, NS(conn, C_CONNECTED), CS_VERBOSE);
15078c2ecf20Sopenharmony_ci		return;
15088c2ecf20Sopenharmony_ci	}
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	switch (device->state.conn) {
15118c2ecf20Sopenharmony_ci	case C_STARTING_SYNC_T:
15128c2ecf20Sopenharmony_ci		_drbd_request_state(device, NS(conn, C_WF_SYNC_UUID), CS_VERBOSE);
15138c2ecf20Sopenharmony_ci		break;
15148c2ecf20Sopenharmony_ci	case C_STARTING_SYNC_S:
15158c2ecf20Sopenharmony_ci		drbd_start_resync(device, C_SYNC_SOURCE);
15168c2ecf20Sopenharmony_ci		break;
15178c2ecf20Sopenharmony_ci	}
15188c2ecf20Sopenharmony_ci}
15198c2ecf20Sopenharmony_ci
15208c2ecf20Sopenharmony_ciint drbd_bitmap_io_from_worker(struct drbd_device *device,
15218c2ecf20Sopenharmony_ci		int (*io_fn)(struct drbd_device *),
15228c2ecf20Sopenharmony_ci		char *why, enum bm_flag flags)
15238c2ecf20Sopenharmony_ci{
15248c2ecf20Sopenharmony_ci	int rv;
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci	D_ASSERT(device, current == first_peer_device(device)->connection->worker.task);
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci	/* open coded non-blocking drbd_suspend_io(device); */
15298c2ecf20Sopenharmony_ci	atomic_inc(&device->suspend_cnt);
15308c2ecf20Sopenharmony_ci
15318c2ecf20Sopenharmony_ci	drbd_bm_lock(device, why, flags);
15328c2ecf20Sopenharmony_ci	rv = io_fn(device);
15338c2ecf20Sopenharmony_ci	drbd_bm_unlock(device);
15348c2ecf20Sopenharmony_ci
15358c2ecf20Sopenharmony_ci	drbd_resume_io(device);
15368c2ecf20Sopenharmony_ci
15378c2ecf20Sopenharmony_ci	return rv;
15388c2ecf20Sopenharmony_ci}
15398c2ecf20Sopenharmony_ci
15408c2ecf20Sopenharmony_ciint notify_resource_state_change(struct sk_buff *skb,
15418c2ecf20Sopenharmony_ci				  unsigned int seq,
15428c2ecf20Sopenharmony_ci				  struct drbd_resource_state_change *resource_state_change,
15438c2ecf20Sopenharmony_ci				  enum drbd_notification_type type)
15448c2ecf20Sopenharmony_ci{
15458c2ecf20Sopenharmony_ci	struct drbd_resource *resource = resource_state_change->resource;
15468c2ecf20Sopenharmony_ci	struct resource_info resource_info = {
15478c2ecf20Sopenharmony_ci		.res_role = resource_state_change->role[NEW],
15488c2ecf20Sopenharmony_ci		.res_susp = resource_state_change->susp[NEW],
15498c2ecf20Sopenharmony_ci		.res_susp_nod = resource_state_change->susp_nod[NEW],
15508c2ecf20Sopenharmony_ci		.res_susp_fen = resource_state_change->susp_fen[NEW],
15518c2ecf20Sopenharmony_ci	};
15528c2ecf20Sopenharmony_ci
15538c2ecf20Sopenharmony_ci	return notify_resource_state(skb, seq, resource, &resource_info, type);
15548c2ecf20Sopenharmony_ci}
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ciint notify_connection_state_change(struct sk_buff *skb,
15578c2ecf20Sopenharmony_ci				    unsigned int seq,
15588c2ecf20Sopenharmony_ci				    struct drbd_connection_state_change *connection_state_change,
15598c2ecf20Sopenharmony_ci				    enum drbd_notification_type type)
15608c2ecf20Sopenharmony_ci{
15618c2ecf20Sopenharmony_ci	struct drbd_connection *connection = connection_state_change->connection;
15628c2ecf20Sopenharmony_ci	struct connection_info connection_info = {
15638c2ecf20Sopenharmony_ci		.conn_connection_state = connection_state_change->cstate[NEW],
15648c2ecf20Sopenharmony_ci		.conn_role = connection_state_change->peer_role[NEW],
15658c2ecf20Sopenharmony_ci	};
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci	return notify_connection_state(skb, seq, connection, &connection_info, type);
15688c2ecf20Sopenharmony_ci}
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ciint notify_device_state_change(struct sk_buff *skb,
15718c2ecf20Sopenharmony_ci				unsigned int seq,
15728c2ecf20Sopenharmony_ci				struct drbd_device_state_change *device_state_change,
15738c2ecf20Sopenharmony_ci				enum drbd_notification_type type)
15748c2ecf20Sopenharmony_ci{
15758c2ecf20Sopenharmony_ci	struct drbd_device *device = device_state_change->device;
15768c2ecf20Sopenharmony_ci	struct device_info device_info = {
15778c2ecf20Sopenharmony_ci		.dev_disk_state = device_state_change->disk_state[NEW],
15788c2ecf20Sopenharmony_ci	};
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci	return notify_device_state(skb, seq, device, &device_info, type);
15818c2ecf20Sopenharmony_ci}
15828c2ecf20Sopenharmony_ci
15838c2ecf20Sopenharmony_ciint notify_peer_device_state_change(struct sk_buff *skb,
15848c2ecf20Sopenharmony_ci				     unsigned int seq,
15858c2ecf20Sopenharmony_ci				     struct drbd_peer_device_state_change *p,
15868c2ecf20Sopenharmony_ci				     enum drbd_notification_type type)
15878c2ecf20Sopenharmony_ci{
15888c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device = p->peer_device;
15898c2ecf20Sopenharmony_ci	struct peer_device_info peer_device_info = {
15908c2ecf20Sopenharmony_ci		.peer_repl_state = p->repl_state[NEW],
15918c2ecf20Sopenharmony_ci		.peer_disk_state = p->disk_state[NEW],
15928c2ecf20Sopenharmony_ci		.peer_resync_susp_user = p->resync_susp_user[NEW],
15938c2ecf20Sopenharmony_ci		.peer_resync_susp_peer = p->resync_susp_peer[NEW],
15948c2ecf20Sopenharmony_ci		.peer_resync_susp_dependency = p->resync_susp_dependency[NEW],
15958c2ecf20Sopenharmony_ci	};
15968c2ecf20Sopenharmony_ci
15978c2ecf20Sopenharmony_ci	return notify_peer_device_state(skb, seq, peer_device, &peer_device_info, type);
15988c2ecf20Sopenharmony_ci}
15998c2ecf20Sopenharmony_ci
16008c2ecf20Sopenharmony_cistatic void broadcast_state_change(struct drbd_state_change *state_change)
16018c2ecf20Sopenharmony_ci{
16028c2ecf20Sopenharmony_ci	struct drbd_resource_state_change *resource_state_change = &state_change->resource[0];
16038c2ecf20Sopenharmony_ci	bool resource_state_has_changed;
16048c2ecf20Sopenharmony_ci	unsigned int n_device, n_connection, n_peer_device, n_peer_devices;
16058c2ecf20Sopenharmony_ci	int (*last_func)(struct sk_buff *, unsigned int, void *,
16068c2ecf20Sopenharmony_ci			  enum drbd_notification_type) = NULL;
16078c2ecf20Sopenharmony_ci	void *last_arg = NULL;
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci#define HAS_CHANGED(state) ((state)[OLD] != (state)[NEW])
16108c2ecf20Sopenharmony_ci#define FINAL_STATE_CHANGE(type) \
16118c2ecf20Sopenharmony_ci	({ if (last_func) \
16128c2ecf20Sopenharmony_ci		last_func(NULL, 0, last_arg, type); \
16138c2ecf20Sopenharmony_ci	})
16148c2ecf20Sopenharmony_ci#define REMEMBER_STATE_CHANGE(func, arg, type) \
16158c2ecf20Sopenharmony_ci	({ FINAL_STATE_CHANGE(type | NOTIFY_CONTINUES); \
16168c2ecf20Sopenharmony_ci	   last_func = (typeof(last_func))func; \
16178c2ecf20Sopenharmony_ci	   last_arg = arg; \
16188c2ecf20Sopenharmony_ci	 })
16198c2ecf20Sopenharmony_ci
16208c2ecf20Sopenharmony_ci	mutex_lock(&notification_mutex);
16218c2ecf20Sopenharmony_ci
16228c2ecf20Sopenharmony_ci	resource_state_has_changed =
16238c2ecf20Sopenharmony_ci	    HAS_CHANGED(resource_state_change->role) ||
16248c2ecf20Sopenharmony_ci	    HAS_CHANGED(resource_state_change->susp) ||
16258c2ecf20Sopenharmony_ci	    HAS_CHANGED(resource_state_change->susp_nod) ||
16268c2ecf20Sopenharmony_ci	    HAS_CHANGED(resource_state_change->susp_fen);
16278c2ecf20Sopenharmony_ci
16288c2ecf20Sopenharmony_ci	if (resource_state_has_changed)
16298c2ecf20Sopenharmony_ci		REMEMBER_STATE_CHANGE(notify_resource_state_change,
16308c2ecf20Sopenharmony_ci				      resource_state_change, NOTIFY_CHANGE);
16318c2ecf20Sopenharmony_ci
16328c2ecf20Sopenharmony_ci	for (n_connection = 0; n_connection < state_change->n_connections; n_connection++) {
16338c2ecf20Sopenharmony_ci		struct drbd_connection_state_change *connection_state_change =
16348c2ecf20Sopenharmony_ci				&state_change->connections[n_connection];
16358c2ecf20Sopenharmony_ci
16368c2ecf20Sopenharmony_ci		if (HAS_CHANGED(connection_state_change->peer_role) ||
16378c2ecf20Sopenharmony_ci		    HAS_CHANGED(connection_state_change->cstate))
16388c2ecf20Sopenharmony_ci			REMEMBER_STATE_CHANGE(notify_connection_state_change,
16398c2ecf20Sopenharmony_ci					      connection_state_change, NOTIFY_CHANGE);
16408c2ecf20Sopenharmony_ci	}
16418c2ecf20Sopenharmony_ci
16428c2ecf20Sopenharmony_ci	for (n_device = 0; n_device < state_change->n_devices; n_device++) {
16438c2ecf20Sopenharmony_ci		struct drbd_device_state_change *device_state_change =
16448c2ecf20Sopenharmony_ci			&state_change->devices[n_device];
16458c2ecf20Sopenharmony_ci
16468c2ecf20Sopenharmony_ci		if (HAS_CHANGED(device_state_change->disk_state))
16478c2ecf20Sopenharmony_ci			REMEMBER_STATE_CHANGE(notify_device_state_change,
16488c2ecf20Sopenharmony_ci					      device_state_change, NOTIFY_CHANGE);
16498c2ecf20Sopenharmony_ci	}
16508c2ecf20Sopenharmony_ci
16518c2ecf20Sopenharmony_ci	n_peer_devices = state_change->n_devices * state_change->n_connections;
16528c2ecf20Sopenharmony_ci	for (n_peer_device = 0; n_peer_device < n_peer_devices; n_peer_device++) {
16538c2ecf20Sopenharmony_ci		struct drbd_peer_device_state_change *p =
16548c2ecf20Sopenharmony_ci			&state_change->peer_devices[n_peer_device];
16558c2ecf20Sopenharmony_ci
16568c2ecf20Sopenharmony_ci		if (HAS_CHANGED(p->disk_state) ||
16578c2ecf20Sopenharmony_ci		    HAS_CHANGED(p->repl_state) ||
16588c2ecf20Sopenharmony_ci		    HAS_CHANGED(p->resync_susp_user) ||
16598c2ecf20Sopenharmony_ci		    HAS_CHANGED(p->resync_susp_peer) ||
16608c2ecf20Sopenharmony_ci		    HAS_CHANGED(p->resync_susp_dependency))
16618c2ecf20Sopenharmony_ci			REMEMBER_STATE_CHANGE(notify_peer_device_state_change,
16628c2ecf20Sopenharmony_ci					      p, NOTIFY_CHANGE);
16638c2ecf20Sopenharmony_ci	}
16648c2ecf20Sopenharmony_ci
16658c2ecf20Sopenharmony_ci	FINAL_STATE_CHANGE(NOTIFY_CHANGE);
16668c2ecf20Sopenharmony_ci	mutex_unlock(&notification_mutex);
16678c2ecf20Sopenharmony_ci
16688c2ecf20Sopenharmony_ci#undef HAS_CHANGED
16698c2ecf20Sopenharmony_ci#undef FINAL_STATE_CHANGE
16708c2ecf20Sopenharmony_ci#undef REMEMBER_STATE_CHANGE
16718c2ecf20Sopenharmony_ci}
16728c2ecf20Sopenharmony_ci
16738c2ecf20Sopenharmony_ci/* takes old and new peer disk state */
16748c2ecf20Sopenharmony_cistatic bool lost_contact_to_peer_data(enum drbd_disk_state os, enum drbd_disk_state ns)
16758c2ecf20Sopenharmony_ci{
16768c2ecf20Sopenharmony_ci	if ((os >= D_INCONSISTENT && os != D_UNKNOWN && os != D_OUTDATED)
16778c2ecf20Sopenharmony_ci	&&  (ns < D_INCONSISTENT || ns == D_UNKNOWN || ns == D_OUTDATED))
16788c2ecf20Sopenharmony_ci		return true;
16798c2ecf20Sopenharmony_ci
16808c2ecf20Sopenharmony_ci	/* Scenario, starting with normal operation
16818c2ecf20Sopenharmony_ci	 * Connected Primary/Secondary UpToDate/UpToDate
16828c2ecf20Sopenharmony_ci	 * NetworkFailure Primary/Unknown UpToDate/DUnknown (frozen)
16838c2ecf20Sopenharmony_ci	 * ...
16848c2ecf20Sopenharmony_ci	 * Connected Primary/Secondary UpToDate/Diskless (resumed; needs to bump uuid!)
16858c2ecf20Sopenharmony_ci	 */
16868c2ecf20Sopenharmony_ci	if (os == D_UNKNOWN
16878c2ecf20Sopenharmony_ci	&&  (ns == D_DISKLESS || ns == D_FAILED || ns == D_OUTDATED))
16888c2ecf20Sopenharmony_ci		return true;
16898c2ecf20Sopenharmony_ci
16908c2ecf20Sopenharmony_ci	return false;
16918c2ecf20Sopenharmony_ci}
16928c2ecf20Sopenharmony_ci
16938c2ecf20Sopenharmony_ci/**
16948c2ecf20Sopenharmony_ci * after_state_ch() - Perform after state change actions that may sleep
16958c2ecf20Sopenharmony_ci * @device:	DRBD device.
16968c2ecf20Sopenharmony_ci * @os:		old state.
16978c2ecf20Sopenharmony_ci * @ns:		new state.
16988c2ecf20Sopenharmony_ci * @flags:	Flags
16998c2ecf20Sopenharmony_ci */
17008c2ecf20Sopenharmony_cistatic void after_state_ch(struct drbd_device *device, union drbd_state os,
17018c2ecf20Sopenharmony_ci			   union drbd_state ns, enum chg_state_flags flags,
17028c2ecf20Sopenharmony_ci			   struct drbd_state_change *state_change)
17038c2ecf20Sopenharmony_ci{
17048c2ecf20Sopenharmony_ci	struct drbd_resource *resource = device->resource;
17058c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device = first_peer_device(device);
17068c2ecf20Sopenharmony_ci	struct drbd_connection *connection = peer_device ? peer_device->connection : NULL;
17078c2ecf20Sopenharmony_ci	struct sib_info sib;
17088c2ecf20Sopenharmony_ci
17098c2ecf20Sopenharmony_ci	broadcast_state_change(state_change);
17108c2ecf20Sopenharmony_ci
17118c2ecf20Sopenharmony_ci	sib.sib_reason = SIB_STATE_CHANGE;
17128c2ecf20Sopenharmony_ci	sib.os = os;
17138c2ecf20Sopenharmony_ci	sib.ns = ns;
17148c2ecf20Sopenharmony_ci
17158c2ecf20Sopenharmony_ci	if ((os.disk != D_UP_TO_DATE || os.pdsk != D_UP_TO_DATE)
17168c2ecf20Sopenharmony_ci	&&  (ns.disk == D_UP_TO_DATE && ns.pdsk == D_UP_TO_DATE)) {
17178c2ecf20Sopenharmony_ci		clear_bit(CRASHED_PRIMARY, &device->flags);
17188c2ecf20Sopenharmony_ci		if (device->p_uuid)
17198c2ecf20Sopenharmony_ci			device->p_uuid[UI_FLAGS] &= ~((u64)2);
17208c2ecf20Sopenharmony_ci	}
17218c2ecf20Sopenharmony_ci
17228c2ecf20Sopenharmony_ci	/* Inform userspace about the change... */
17238c2ecf20Sopenharmony_ci	drbd_bcast_event(device, &sib);
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_ci	if (!(os.role == R_PRIMARY && os.disk < D_UP_TO_DATE && os.pdsk < D_UP_TO_DATE) &&
17268c2ecf20Sopenharmony_ci	    (ns.role == R_PRIMARY && ns.disk < D_UP_TO_DATE && ns.pdsk < D_UP_TO_DATE))
17278c2ecf20Sopenharmony_ci		drbd_khelper(device, "pri-on-incon-degr");
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci	/* Here we have the actions that are performed after a
17308c2ecf20Sopenharmony_ci	   state change. This function might sleep */
17318c2ecf20Sopenharmony_ci
17328c2ecf20Sopenharmony_ci	if (ns.susp_nod) {
17338c2ecf20Sopenharmony_ci		enum drbd_req_event what = NOTHING;
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci		spin_lock_irq(&device->resource->req_lock);
17368c2ecf20Sopenharmony_ci		if (os.conn < C_CONNECTED && conn_lowest_conn(connection) >= C_CONNECTED)
17378c2ecf20Sopenharmony_ci			what = RESEND;
17388c2ecf20Sopenharmony_ci
17398c2ecf20Sopenharmony_ci		if ((os.disk == D_ATTACHING || os.disk == D_NEGOTIATING) &&
17408c2ecf20Sopenharmony_ci		    conn_lowest_disk(connection) == D_UP_TO_DATE)
17418c2ecf20Sopenharmony_ci			what = RESTART_FROZEN_DISK_IO;
17428c2ecf20Sopenharmony_ci
17438c2ecf20Sopenharmony_ci		if (resource->susp_nod && what != NOTHING) {
17448c2ecf20Sopenharmony_ci			_tl_restart(connection, what);
17458c2ecf20Sopenharmony_ci			_conn_request_state(connection,
17468c2ecf20Sopenharmony_ci					    (union drbd_state) { { .susp_nod = 1 } },
17478c2ecf20Sopenharmony_ci					    (union drbd_state) { { .susp_nod = 0 } },
17488c2ecf20Sopenharmony_ci					    CS_VERBOSE);
17498c2ecf20Sopenharmony_ci		}
17508c2ecf20Sopenharmony_ci		spin_unlock_irq(&device->resource->req_lock);
17518c2ecf20Sopenharmony_ci	}
17528c2ecf20Sopenharmony_ci
17538c2ecf20Sopenharmony_ci	if (ns.susp_fen) {
17548c2ecf20Sopenharmony_ci		spin_lock_irq(&device->resource->req_lock);
17558c2ecf20Sopenharmony_ci		if (resource->susp_fen && conn_lowest_conn(connection) >= C_CONNECTED) {
17568c2ecf20Sopenharmony_ci			/* case2: The connection was established again: */
17578c2ecf20Sopenharmony_ci			struct drbd_peer_device *peer_device;
17588c2ecf20Sopenharmony_ci			int vnr;
17598c2ecf20Sopenharmony_ci
17608c2ecf20Sopenharmony_ci			rcu_read_lock();
17618c2ecf20Sopenharmony_ci			idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
17628c2ecf20Sopenharmony_ci				clear_bit(NEW_CUR_UUID, &peer_device->device->flags);
17638c2ecf20Sopenharmony_ci			rcu_read_unlock();
17648c2ecf20Sopenharmony_ci
17658c2ecf20Sopenharmony_ci			/* We should actively create a new uuid, _before_
17668c2ecf20Sopenharmony_ci			 * we resume/resent, if the peer is diskless
17678c2ecf20Sopenharmony_ci			 * (recovery from a multiple error scenario).
17688c2ecf20Sopenharmony_ci			 * Currently, this happens with a slight delay
17698c2ecf20Sopenharmony_ci			 * below when checking lost_contact_to_peer_data() ...
17708c2ecf20Sopenharmony_ci			 */
17718c2ecf20Sopenharmony_ci			_tl_restart(connection, RESEND);
17728c2ecf20Sopenharmony_ci			_conn_request_state(connection,
17738c2ecf20Sopenharmony_ci					    (union drbd_state) { { .susp_fen = 1 } },
17748c2ecf20Sopenharmony_ci					    (union drbd_state) { { .susp_fen = 0 } },
17758c2ecf20Sopenharmony_ci					    CS_VERBOSE);
17768c2ecf20Sopenharmony_ci		}
17778c2ecf20Sopenharmony_ci		spin_unlock_irq(&device->resource->req_lock);
17788c2ecf20Sopenharmony_ci	}
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci	/* Became sync source.  With protocol >= 96, we still need to send out
17818c2ecf20Sopenharmony_ci	 * the sync uuid now. Need to do that before any drbd_send_state, or
17828c2ecf20Sopenharmony_ci	 * the other side may go "paused sync" before receiving the sync uuids,
17838c2ecf20Sopenharmony_ci	 * which is unexpected. */
17848c2ecf20Sopenharmony_ci	if ((os.conn != C_SYNC_SOURCE && os.conn != C_PAUSED_SYNC_S) &&
17858c2ecf20Sopenharmony_ci	    (ns.conn == C_SYNC_SOURCE || ns.conn == C_PAUSED_SYNC_S) &&
17868c2ecf20Sopenharmony_ci	    connection->agreed_pro_version >= 96 && get_ldev(device)) {
17878c2ecf20Sopenharmony_ci		drbd_gen_and_send_sync_uuid(peer_device);
17888c2ecf20Sopenharmony_ci		put_ldev(device);
17898c2ecf20Sopenharmony_ci	}
17908c2ecf20Sopenharmony_ci
17918c2ecf20Sopenharmony_ci	/* Do not change the order of the if above and the two below... */
17928c2ecf20Sopenharmony_ci	if (os.pdsk == D_DISKLESS &&
17938c2ecf20Sopenharmony_ci	    ns.pdsk > D_DISKLESS && ns.pdsk != D_UNKNOWN) {      /* attach on the peer */
17948c2ecf20Sopenharmony_ci		/* we probably will start a resync soon.
17958c2ecf20Sopenharmony_ci		 * make sure those things are properly reset. */
17968c2ecf20Sopenharmony_ci		device->rs_total = 0;
17978c2ecf20Sopenharmony_ci		device->rs_failed = 0;
17988c2ecf20Sopenharmony_ci		atomic_set(&device->rs_pending_cnt, 0);
17998c2ecf20Sopenharmony_ci		drbd_rs_cancel_all(device);
18008c2ecf20Sopenharmony_ci
18018c2ecf20Sopenharmony_ci		drbd_send_uuids(peer_device);
18028c2ecf20Sopenharmony_ci		drbd_send_state(peer_device, ns);
18038c2ecf20Sopenharmony_ci	}
18048c2ecf20Sopenharmony_ci	/* No point in queuing send_bitmap if we don't have a connection
18058c2ecf20Sopenharmony_ci	 * anymore, so check also the _current_ state, not only the new state
18068c2ecf20Sopenharmony_ci	 * at the time this work was queued. */
18078c2ecf20Sopenharmony_ci	if (os.conn != C_WF_BITMAP_S && ns.conn == C_WF_BITMAP_S &&
18088c2ecf20Sopenharmony_ci	    device->state.conn == C_WF_BITMAP_S)
18098c2ecf20Sopenharmony_ci		drbd_queue_bitmap_io(device, &drbd_send_bitmap, NULL,
18108c2ecf20Sopenharmony_ci				"send_bitmap (WFBitMapS)",
18118c2ecf20Sopenharmony_ci				BM_LOCKED_TEST_ALLOWED);
18128c2ecf20Sopenharmony_ci
18138c2ecf20Sopenharmony_ci	/* Lost contact to peer's copy of the data */
18148c2ecf20Sopenharmony_ci	if (lost_contact_to_peer_data(os.pdsk, ns.pdsk)) {
18158c2ecf20Sopenharmony_ci		if (get_ldev(device)) {
18168c2ecf20Sopenharmony_ci			if ((ns.role == R_PRIMARY || ns.peer == R_PRIMARY) &&
18178c2ecf20Sopenharmony_ci			    device->ldev->md.uuid[UI_BITMAP] == 0 && ns.disk >= D_UP_TO_DATE) {
18188c2ecf20Sopenharmony_ci				if (drbd_suspended(device)) {
18198c2ecf20Sopenharmony_ci					set_bit(NEW_CUR_UUID, &device->flags);
18208c2ecf20Sopenharmony_ci				} else {
18218c2ecf20Sopenharmony_ci					drbd_uuid_new_current(device);
18228c2ecf20Sopenharmony_ci					drbd_send_uuids(peer_device);
18238c2ecf20Sopenharmony_ci				}
18248c2ecf20Sopenharmony_ci			}
18258c2ecf20Sopenharmony_ci			put_ldev(device);
18268c2ecf20Sopenharmony_ci		}
18278c2ecf20Sopenharmony_ci	}
18288c2ecf20Sopenharmony_ci
18298c2ecf20Sopenharmony_ci	if (ns.pdsk < D_INCONSISTENT && get_ldev(device)) {
18308c2ecf20Sopenharmony_ci		if (os.peer != R_PRIMARY && ns.peer == R_PRIMARY &&
18318c2ecf20Sopenharmony_ci		    device->ldev->md.uuid[UI_BITMAP] == 0 && ns.disk >= D_UP_TO_DATE) {
18328c2ecf20Sopenharmony_ci			drbd_uuid_new_current(device);
18338c2ecf20Sopenharmony_ci			drbd_send_uuids(peer_device);
18348c2ecf20Sopenharmony_ci		}
18358c2ecf20Sopenharmony_ci		/* D_DISKLESS Peer becomes secondary */
18368c2ecf20Sopenharmony_ci		if (os.peer == R_PRIMARY && ns.peer == R_SECONDARY)
18378c2ecf20Sopenharmony_ci			/* We may still be Primary ourselves.
18388c2ecf20Sopenharmony_ci			 * No harm done if the bitmap still changes,
18398c2ecf20Sopenharmony_ci			 * redirtied pages will follow later. */
18408c2ecf20Sopenharmony_ci			drbd_bitmap_io_from_worker(device, &drbd_bm_write,
18418c2ecf20Sopenharmony_ci				"demote diskless peer", BM_LOCKED_SET_ALLOWED);
18428c2ecf20Sopenharmony_ci		put_ldev(device);
18438c2ecf20Sopenharmony_ci	}
18448c2ecf20Sopenharmony_ci
18458c2ecf20Sopenharmony_ci	/* Write out all changed bits on demote.
18468c2ecf20Sopenharmony_ci	 * Though, no need to da that just yet
18478c2ecf20Sopenharmony_ci	 * if there is a resync going on still */
18488c2ecf20Sopenharmony_ci	if (os.role == R_PRIMARY && ns.role == R_SECONDARY &&
18498c2ecf20Sopenharmony_ci		device->state.conn <= C_CONNECTED && get_ldev(device)) {
18508c2ecf20Sopenharmony_ci		/* No changes to the bitmap expected this time, so assert that,
18518c2ecf20Sopenharmony_ci		 * even though no harm was done if it did change. */
18528c2ecf20Sopenharmony_ci		drbd_bitmap_io_from_worker(device, &drbd_bm_write,
18538c2ecf20Sopenharmony_ci				"demote", BM_LOCKED_TEST_ALLOWED);
18548c2ecf20Sopenharmony_ci		put_ldev(device);
18558c2ecf20Sopenharmony_ci	}
18568c2ecf20Sopenharmony_ci
18578c2ecf20Sopenharmony_ci	/* Last part of the attaching process ... */
18588c2ecf20Sopenharmony_ci	if (ns.conn >= C_CONNECTED &&
18598c2ecf20Sopenharmony_ci	    os.disk == D_ATTACHING && ns.disk == D_NEGOTIATING) {
18608c2ecf20Sopenharmony_ci		drbd_send_sizes(peer_device, 0, 0);  /* to start sync... */
18618c2ecf20Sopenharmony_ci		drbd_send_uuids(peer_device);
18628c2ecf20Sopenharmony_ci		drbd_send_state(peer_device, ns);
18638c2ecf20Sopenharmony_ci	}
18648c2ecf20Sopenharmony_ci
18658c2ecf20Sopenharmony_ci	/* We want to pause/continue resync, tell peer. */
18668c2ecf20Sopenharmony_ci	if (ns.conn >= C_CONNECTED &&
18678c2ecf20Sopenharmony_ci	     ((os.aftr_isp != ns.aftr_isp) ||
18688c2ecf20Sopenharmony_ci	      (os.user_isp != ns.user_isp)))
18698c2ecf20Sopenharmony_ci		drbd_send_state(peer_device, ns);
18708c2ecf20Sopenharmony_ci
18718c2ecf20Sopenharmony_ci	/* In case one of the isp bits got set, suspend other devices. */
18728c2ecf20Sopenharmony_ci	if ((!os.aftr_isp && !os.peer_isp && !os.user_isp) &&
18738c2ecf20Sopenharmony_ci	    (ns.aftr_isp || ns.peer_isp || ns.user_isp))
18748c2ecf20Sopenharmony_ci		suspend_other_sg(device);
18758c2ecf20Sopenharmony_ci
18768c2ecf20Sopenharmony_ci	/* Make sure the peer gets informed about eventual state
18778c2ecf20Sopenharmony_ci	   changes (ISP bits) while we were in WFReportParams. */
18788c2ecf20Sopenharmony_ci	if (os.conn == C_WF_REPORT_PARAMS && ns.conn >= C_CONNECTED)
18798c2ecf20Sopenharmony_ci		drbd_send_state(peer_device, ns);
18808c2ecf20Sopenharmony_ci
18818c2ecf20Sopenharmony_ci	if (os.conn != C_AHEAD && ns.conn == C_AHEAD)
18828c2ecf20Sopenharmony_ci		drbd_send_state(peer_device, ns);
18838c2ecf20Sopenharmony_ci
18848c2ecf20Sopenharmony_ci	/* We are in the progress to start a full sync... */
18858c2ecf20Sopenharmony_ci	if ((os.conn != C_STARTING_SYNC_T && ns.conn == C_STARTING_SYNC_T) ||
18868c2ecf20Sopenharmony_ci	    (os.conn != C_STARTING_SYNC_S && ns.conn == C_STARTING_SYNC_S))
18878c2ecf20Sopenharmony_ci		/* no other bitmap changes expected during this phase */
18888c2ecf20Sopenharmony_ci		drbd_queue_bitmap_io(device,
18898c2ecf20Sopenharmony_ci			&drbd_bmio_set_n_write, &abw_start_sync,
18908c2ecf20Sopenharmony_ci			"set_n_write from StartingSync", BM_LOCKED_TEST_ALLOWED);
18918c2ecf20Sopenharmony_ci
18928c2ecf20Sopenharmony_ci	/* first half of local IO error, failure to attach,
18938c2ecf20Sopenharmony_ci	 * or administrative detach */
18948c2ecf20Sopenharmony_ci	if (os.disk != D_FAILED && ns.disk == D_FAILED) {
18958c2ecf20Sopenharmony_ci		enum drbd_io_error_p eh = EP_PASS_ON;
18968c2ecf20Sopenharmony_ci		int was_io_error = 0;
18978c2ecf20Sopenharmony_ci		/* corresponding get_ldev was in _drbd_set_state, to serialize
18988c2ecf20Sopenharmony_ci		 * our cleanup here with the transition to D_DISKLESS.
18998c2ecf20Sopenharmony_ci		 * But is is still not save to dreference ldev here, since
19008c2ecf20Sopenharmony_ci		 * we might come from an failed Attach before ldev was set. */
19018c2ecf20Sopenharmony_ci		if (device->ldev) {
19028c2ecf20Sopenharmony_ci			rcu_read_lock();
19038c2ecf20Sopenharmony_ci			eh = rcu_dereference(device->ldev->disk_conf)->on_io_error;
19048c2ecf20Sopenharmony_ci			rcu_read_unlock();
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci			was_io_error = test_and_clear_bit(WAS_IO_ERROR, &device->flags);
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_ci			/* Intentionally call this handler first, before drbd_send_state().
19098c2ecf20Sopenharmony_ci			 * See: 2932204 drbd: call local-io-error handler early
19108c2ecf20Sopenharmony_ci			 * People may chose to hard-reset the box from this handler.
19118c2ecf20Sopenharmony_ci			 * It is useful if this looks like a "regular node crash". */
19128c2ecf20Sopenharmony_ci			if (was_io_error && eh == EP_CALL_HELPER)
19138c2ecf20Sopenharmony_ci				drbd_khelper(device, "local-io-error");
19148c2ecf20Sopenharmony_ci
19158c2ecf20Sopenharmony_ci			/* Immediately allow completion of all application IO,
19168c2ecf20Sopenharmony_ci			 * that waits for completion from the local disk,
19178c2ecf20Sopenharmony_ci			 * if this was a force-detach due to disk_timeout
19188c2ecf20Sopenharmony_ci			 * or administrator request (drbdsetup detach --force).
19198c2ecf20Sopenharmony_ci			 * Do NOT abort otherwise.
19208c2ecf20Sopenharmony_ci			 * Aborting local requests may cause serious problems,
19218c2ecf20Sopenharmony_ci			 * if requests are completed to upper layers already,
19228c2ecf20Sopenharmony_ci			 * and then later the already submitted local bio completes.
19238c2ecf20Sopenharmony_ci			 * This can cause DMA into former bio pages that meanwhile
19248c2ecf20Sopenharmony_ci			 * have been re-used for other things.
19258c2ecf20Sopenharmony_ci			 * So aborting local requests may cause crashes,
19268c2ecf20Sopenharmony_ci			 * or even worse, silent data corruption.
19278c2ecf20Sopenharmony_ci			 */
19288c2ecf20Sopenharmony_ci			if (test_and_clear_bit(FORCE_DETACH, &device->flags))
19298c2ecf20Sopenharmony_ci				tl_abort_disk_io(device);
19308c2ecf20Sopenharmony_ci
19318c2ecf20Sopenharmony_ci			/* current state still has to be D_FAILED,
19328c2ecf20Sopenharmony_ci			 * there is only one way out: to D_DISKLESS,
19338c2ecf20Sopenharmony_ci			 * and that may only happen after our put_ldev below. */
19348c2ecf20Sopenharmony_ci			if (device->state.disk != D_FAILED)
19358c2ecf20Sopenharmony_ci				drbd_err(device,
19368c2ecf20Sopenharmony_ci					"ASSERT FAILED: disk is %s during detach\n",
19378c2ecf20Sopenharmony_ci					drbd_disk_str(device->state.disk));
19388c2ecf20Sopenharmony_ci
19398c2ecf20Sopenharmony_ci			if (ns.conn >= C_CONNECTED)
19408c2ecf20Sopenharmony_ci				drbd_send_state(peer_device, ns);
19418c2ecf20Sopenharmony_ci
19428c2ecf20Sopenharmony_ci			drbd_rs_cancel_all(device);
19438c2ecf20Sopenharmony_ci
19448c2ecf20Sopenharmony_ci			/* In case we want to get something to stable storage still,
19458c2ecf20Sopenharmony_ci			 * this may be the last chance.
19468c2ecf20Sopenharmony_ci			 * Following put_ldev may transition to D_DISKLESS. */
19478c2ecf20Sopenharmony_ci			drbd_md_sync(device);
19488c2ecf20Sopenharmony_ci		}
19498c2ecf20Sopenharmony_ci		put_ldev(device);
19508c2ecf20Sopenharmony_ci	}
19518c2ecf20Sopenharmony_ci
19528c2ecf20Sopenharmony_ci	/* second half of local IO error, failure to attach,
19538c2ecf20Sopenharmony_ci	 * or administrative detach,
19548c2ecf20Sopenharmony_ci	 * after local_cnt references have reached zero again */
19558c2ecf20Sopenharmony_ci	if (os.disk != D_DISKLESS && ns.disk == D_DISKLESS) {
19568c2ecf20Sopenharmony_ci		/* We must still be diskless,
19578c2ecf20Sopenharmony_ci		 * re-attach has to be serialized with this! */
19588c2ecf20Sopenharmony_ci		if (device->state.disk != D_DISKLESS)
19598c2ecf20Sopenharmony_ci			drbd_err(device,
19608c2ecf20Sopenharmony_ci				 "ASSERT FAILED: disk is %s while going diskless\n",
19618c2ecf20Sopenharmony_ci				 drbd_disk_str(device->state.disk));
19628c2ecf20Sopenharmony_ci
19638c2ecf20Sopenharmony_ci		if (ns.conn >= C_CONNECTED)
19648c2ecf20Sopenharmony_ci			drbd_send_state(peer_device, ns);
19658c2ecf20Sopenharmony_ci		/* corresponding get_ldev in __drbd_set_state
19668c2ecf20Sopenharmony_ci		 * this may finally trigger drbd_ldev_destroy. */
19678c2ecf20Sopenharmony_ci		put_ldev(device);
19688c2ecf20Sopenharmony_ci	}
19698c2ecf20Sopenharmony_ci
19708c2ecf20Sopenharmony_ci	/* Notify peer that I had a local IO error, and did not detached.. */
19718c2ecf20Sopenharmony_ci	if (os.disk == D_UP_TO_DATE && ns.disk == D_INCONSISTENT && ns.conn >= C_CONNECTED)
19728c2ecf20Sopenharmony_ci		drbd_send_state(peer_device, ns);
19738c2ecf20Sopenharmony_ci
19748c2ecf20Sopenharmony_ci	/* Disks got bigger while they were detached */
19758c2ecf20Sopenharmony_ci	if (ns.disk > D_NEGOTIATING && ns.pdsk > D_NEGOTIATING &&
19768c2ecf20Sopenharmony_ci	    test_and_clear_bit(RESYNC_AFTER_NEG, &device->flags)) {
19778c2ecf20Sopenharmony_ci		if (ns.conn == C_CONNECTED)
19788c2ecf20Sopenharmony_ci			resync_after_online_grow(device);
19798c2ecf20Sopenharmony_ci	}
19808c2ecf20Sopenharmony_ci
19818c2ecf20Sopenharmony_ci	/* A resync finished or aborted, wake paused devices... */
19828c2ecf20Sopenharmony_ci	if ((os.conn > C_CONNECTED && ns.conn <= C_CONNECTED) ||
19838c2ecf20Sopenharmony_ci	    (os.peer_isp && !ns.peer_isp) ||
19848c2ecf20Sopenharmony_ci	    (os.user_isp && !ns.user_isp))
19858c2ecf20Sopenharmony_ci		resume_next_sg(device);
19868c2ecf20Sopenharmony_ci
19878c2ecf20Sopenharmony_ci	/* sync target done with resync.  Explicitly notify peer, even though
19888c2ecf20Sopenharmony_ci	 * it should (at least for non-empty resyncs) already know itself. */
19898c2ecf20Sopenharmony_ci	if (os.disk < D_UP_TO_DATE && os.conn >= C_SYNC_SOURCE && ns.conn == C_CONNECTED)
19908c2ecf20Sopenharmony_ci		drbd_send_state(peer_device, ns);
19918c2ecf20Sopenharmony_ci
19928c2ecf20Sopenharmony_ci	/* Verify finished, or reached stop sector.  Peer did not know about
19938c2ecf20Sopenharmony_ci	 * the stop sector, and we may even have changed the stop sector during
19948c2ecf20Sopenharmony_ci	 * verify to interrupt/stop early.  Send the new state. */
19958c2ecf20Sopenharmony_ci	if (os.conn == C_VERIFY_S && ns.conn == C_CONNECTED
19968c2ecf20Sopenharmony_ci	&& verify_can_do_stop_sector(device))
19978c2ecf20Sopenharmony_ci		drbd_send_state(peer_device, ns);
19988c2ecf20Sopenharmony_ci
19998c2ecf20Sopenharmony_ci	/* This triggers bitmap writeout of potentially still unwritten pages
20008c2ecf20Sopenharmony_ci	 * if the resync finished cleanly, or aborted because of peer disk
20018c2ecf20Sopenharmony_ci	 * failure, or on transition from resync back to AHEAD/BEHIND.
20028c2ecf20Sopenharmony_ci	 *
20038c2ecf20Sopenharmony_ci	 * Connection loss is handled in drbd_disconnected() by the receiver.
20048c2ecf20Sopenharmony_ci	 *
20058c2ecf20Sopenharmony_ci	 * For resync aborted because of local disk failure, we cannot do
20068c2ecf20Sopenharmony_ci	 * any bitmap writeout anymore.
20078c2ecf20Sopenharmony_ci	 *
20088c2ecf20Sopenharmony_ci	 * No harm done if some bits change during this phase.
20098c2ecf20Sopenharmony_ci	 */
20108c2ecf20Sopenharmony_ci	if ((os.conn > C_CONNECTED && os.conn < C_AHEAD) &&
20118c2ecf20Sopenharmony_ci	    (ns.conn == C_CONNECTED || ns.conn >= C_AHEAD) && get_ldev(device)) {
20128c2ecf20Sopenharmony_ci		drbd_queue_bitmap_io(device, &drbd_bm_write_copy_pages, NULL,
20138c2ecf20Sopenharmony_ci			"write from resync_finished", BM_LOCKED_CHANGE_ALLOWED);
20148c2ecf20Sopenharmony_ci		put_ldev(device);
20158c2ecf20Sopenharmony_ci	}
20168c2ecf20Sopenharmony_ci
20178c2ecf20Sopenharmony_ci	if (ns.disk == D_DISKLESS &&
20188c2ecf20Sopenharmony_ci	    ns.conn == C_STANDALONE &&
20198c2ecf20Sopenharmony_ci	    ns.role == R_SECONDARY) {
20208c2ecf20Sopenharmony_ci		if (os.aftr_isp != ns.aftr_isp)
20218c2ecf20Sopenharmony_ci			resume_next_sg(device);
20228c2ecf20Sopenharmony_ci	}
20238c2ecf20Sopenharmony_ci
20248c2ecf20Sopenharmony_ci	drbd_md_sync(device);
20258c2ecf20Sopenharmony_ci}
20268c2ecf20Sopenharmony_ci
20278c2ecf20Sopenharmony_cistruct after_conn_state_chg_work {
20288c2ecf20Sopenharmony_ci	struct drbd_work w;
20298c2ecf20Sopenharmony_ci	enum drbd_conns oc;
20308c2ecf20Sopenharmony_ci	union drbd_state ns_min;
20318c2ecf20Sopenharmony_ci	union drbd_state ns_max; /* new, max state, over all devices */
20328c2ecf20Sopenharmony_ci	enum chg_state_flags flags;
20338c2ecf20Sopenharmony_ci	struct drbd_connection *connection;
20348c2ecf20Sopenharmony_ci	struct drbd_state_change *state_change;
20358c2ecf20Sopenharmony_ci};
20368c2ecf20Sopenharmony_ci
20378c2ecf20Sopenharmony_cistatic int w_after_conn_state_ch(struct drbd_work *w, int unused)
20388c2ecf20Sopenharmony_ci{
20398c2ecf20Sopenharmony_ci	struct after_conn_state_chg_work *acscw =
20408c2ecf20Sopenharmony_ci		container_of(w, struct after_conn_state_chg_work, w);
20418c2ecf20Sopenharmony_ci	struct drbd_connection *connection = acscw->connection;
20428c2ecf20Sopenharmony_ci	enum drbd_conns oc = acscw->oc;
20438c2ecf20Sopenharmony_ci	union drbd_state ns_max = acscw->ns_max;
20448c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
20458c2ecf20Sopenharmony_ci	int vnr;
20468c2ecf20Sopenharmony_ci
20478c2ecf20Sopenharmony_ci	broadcast_state_change(acscw->state_change);
20488c2ecf20Sopenharmony_ci	forget_state_change(acscw->state_change);
20498c2ecf20Sopenharmony_ci	kfree(acscw);
20508c2ecf20Sopenharmony_ci
20518c2ecf20Sopenharmony_ci	/* Upon network configuration, we need to start the receiver */
20528c2ecf20Sopenharmony_ci	if (oc == C_STANDALONE && ns_max.conn == C_UNCONNECTED)
20538c2ecf20Sopenharmony_ci		drbd_thread_start(&connection->receiver);
20548c2ecf20Sopenharmony_ci
20558c2ecf20Sopenharmony_ci	if (oc == C_DISCONNECTING && ns_max.conn == C_STANDALONE) {
20568c2ecf20Sopenharmony_ci		struct net_conf *old_conf;
20578c2ecf20Sopenharmony_ci
20588c2ecf20Sopenharmony_ci		mutex_lock(&notification_mutex);
20598c2ecf20Sopenharmony_ci		idr_for_each_entry(&connection->peer_devices, peer_device, vnr)
20608c2ecf20Sopenharmony_ci			notify_peer_device_state(NULL, 0, peer_device, NULL,
20618c2ecf20Sopenharmony_ci						 NOTIFY_DESTROY | NOTIFY_CONTINUES);
20628c2ecf20Sopenharmony_ci		notify_connection_state(NULL, 0, connection, NULL, NOTIFY_DESTROY);
20638c2ecf20Sopenharmony_ci		mutex_unlock(&notification_mutex);
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci		mutex_lock(&connection->resource->conf_update);
20668c2ecf20Sopenharmony_ci		old_conf = connection->net_conf;
20678c2ecf20Sopenharmony_ci		connection->my_addr_len = 0;
20688c2ecf20Sopenharmony_ci		connection->peer_addr_len = 0;
20698c2ecf20Sopenharmony_ci		RCU_INIT_POINTER(connection->net_conf, NULL);
20708c2ecf20Sopenharmony_ci		conn_free_crypto(connection);
20718c2ecf20Sopenharmony_ci		mutex_unlock(&connection->resource->conf_update);
20728c2ecf20Sopenharmony_ci
20738c2ecf20Sopenharmony_ci		synchronize_rcu();
20748c2ecf20Sopenharmony_ci		kfree(old_conf);
20758c2ecf20Sopenharmony_ci	}
20768c2ecf20Sopenharmony_ci
20778c2ecf20Sopenharmony_ci	if (ns_max.susp_fen) {
20788c2ecf20Sopenharmony_ci		/* case1: The outdate peer handler is successful: */
20798c2ecf20Sopenharmony_ci		if (ns_max.pdsk <= D_OUTDATED) {
20808c2ecf20Sopenharmony_ci			rcu_read_lock();
20818c2ecf20Sopenharmony_ci			idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
20828c2ecf20Sopenharmony_ci				struct drbd_device *device = peer_device->device;
20838c2ecf20Sopenharmony_ci				if (test_bit(NEW_CUR_UUID, &device->flags)) {
20848c2ecf20Sopenharmony_ci					drbd_uuid_new_current(device);
20858c2ecf20Sopenharmony_ci					clear_bit(NEW_CUR_UUID, &device->flags);
20868c2ecf20Sopenharmony_ci				}
20878c2ecf20Sopenharmony_ci			}
20888c2ecf20Sopenharmony_ci			rcu_read_unlock();
20898c2ecf20Sopenharmony_ci			spin_lock_irq(&connection->resource->req_lock);
20908c2ecf20Sopenharmony_ci			_tl_restart(connection, CONNECTION_LOST_WHILE_PENDING);
20918c2ecf20Sopenharmony_ci			_conn_request_state(connection,
20928c2ecf20Sopenharmony_ci					    (union drbd_state) { { .susp_fen = 1 } },
20938c2ecf20Sopenharmony_ci					    (union drbd_state) { { .susp_fen = 0 } },
20948c2ecf20Sopenharmony_ci					    CS_VERBOSE);
20958c2ecf20Sopenharmony_ci			spin_unlock_irq(&connection->resource->req_lock);
20968c2ecf20Sopenharmony_ci		}
20978c2ecf20Sopenharmony_ci	}
20988c2ecf20Sopenharmony_ci	conn_md_sync(connection);
20998c2ecf20Sopenharmony_ci	kref_put(&connection->kref, drbd_destroy_connection);
21008c2ecf20Sopenharmony_ci
21018c2ecf20Sopenharmony_ci	return 0;
21028c2ecf20Sopenharmony_ci}
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_cistatic void conn_old_common_state(struct drbd_connection *connection, union drbd_state *pcs, enum chg_state_flags *pf)
21058c2ecf20Sopenharmony_ci{
21068c2ecf20Sopenharmony_ci	enum chg_state_flags flags = ~0;
21078c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
21088c2ecf20Sopenharmony_ci	int vnr, first_vol = 1;
21098c2ecf20Sopenharmony_ci	union drbd_dev_state os, cs = {
21108c2ecf20Sopenharmony_ci		{ .role = R_SECONDARY,
21118c2ecf20Sopenharmony_ci		  .peer = R_UNKNOWN,
21128c2ecf20Sopenharmony_ci		  .conn = connection->cstate,
21138c2ecf20Sopenharmony_ci		  .disk = D_DISKLESS,
21148c2ecf20Sopenharmony_ci		  .pdsk = D_UNKNOWN,
21158c2ecf20Sopenharmony_ci		} };
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci	rcu_read_lock();
21188c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
21198c2ecf20Sopenharmony_ci		struct drbd_device *device = peer_device->device;
21208c2ecf20Sopenharmony_ci		os = device->state;
21218c2ecf20Sopenharmony_ci
21228c2ecf20Sopenharmony_ci		if (first_vol) {
21238c2ecf20Sopenharmony_ci			cs = os;
21248c2ecf20Sopenharmony_ci			first_vol = 0;
21258c2ecf20Sopenharmony_ci			continue;
21268c2ecf20Sopenharmony_ci		}
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_ci		if (cs.role != os.role)
21298c2ecf20Sopenharmony_ci			flags &= ~CS_DC_ROLE;
21308c2ecf20Sopenharmony_ci
21318c2ecf20Sopenharmony_ci		if (cs.peer != os.peer)
21328c2ecf20Sopenharmony_ci			flags &= ~CS_DC_PEER;
21338c2ecf20Sopenharmony_ci
21348c2ecf20Sopenharmony_ci		if (cs.conn != os.conn)
21358c2ecf20Sopenharmony_ci			flags &= ~CS_DC_CONN;
21368c2ecf20Sopenharmony_ci
21378c2ecf20Sopenharmony_ci		if (cs.disk != os.disk)
21388c2ecf20Sopenharmony_ci			flags &= ~CS_DC_DISK;
21398c2ecf20Sopenharmony_ci
21408c2ecf20Sopenharmony_ci		if (cs.pdsk != os.pdsk)
21418c2ecf20Sopenharmony_ci			flags &= ~CS_DC_PDSK;
21428c2ecf20Sopenharmony_ci	}
21438c2ecf20Sopenharmony_ci	rcu_read_unlock();
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci	*pf |= CS_DC_MASK;
21468c2ecf20Sopenharmony_ci	*pf &= flags;
21478c2ecf20Sopenharmony_ci	(*pcs).i = cs.i;
21488c2ecf20Sopenharmony_ci}
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_cistatic enum drbd_state_rv
21518c2ecf20Sopenharmony_ciconn_is_valid_transition(struct drbd_connection *connection, union drbd_state mask, union drbd_state val,
21528c2ecf20Sopenharmony_ci			 enum chg_state_flags flags)
21538c2ecf20Sopenharmony_ci{
21548c2ecf20Sopenharmony_ci	enum drbd_state_rv rv = SS_SUCCESS;
21558c2ecf20Sopenharmony_ci	union drbd_state ns, os;
21568c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
21578c2ecf20Sopenharmony_ci	int vnr;
21588c2ecf20Sopenharmony_ci
21598c2ecf20Sopenharmony_ci	rcu_read_lock();
21608c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
21618c2ecf20Sopenharmony_ci		struct drbd_device *device = peer_device->device;
21628c2ecf20Sopenharmony_ci		os = drbd_read_state(device);
21638c2ecf20Sopenharmony_ci		ns = sanitize_state(device, os, apply_mask_val(os, mask, val), NULL);
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci		if (flags & CS_IGN_OUTD_FAIL && ns.disk == D_OUTDATED && os.disk < D_OUTDATED)
21668c2ecf20Sopenharmony_ci			ns.disk = os.disk;
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_ci		if (ns.i == os.i)
21698c2ecf20Sopenharmony_ci			continue;
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci		rv = is_valid_transition(os, ns);
21728c2ecf20Sopenharmony_ci
21738c2ecf20Sopenharmony_ci		if (rv >= SS_SUCCESS && !(flags & CS_HARD)) {
21748c2ecf20Sopenharmony_ci			rv = is_valid_state(device, ns);
21758c2ecf20Sopenharmony_ci			if (rv < SS_SUCCESS) {
21768c2ecf20Sopenharmony_ci				if (is_valid_state(device, os) == rv)
21778c2ecf20Sopenharmony_ci					rv = is_valid_soft_transition(os, ns, connection);
21788c2ecf20Sopenharmony_ci			} else
21798c2ecf20Sopenharmony_ci				rv = is_valid_soft_transition(os, ns, connection);
21808c2ecf20Sopenharmony_ci		}
21818c2ecf20Sopenharmony_ci
21828c2ecf20Sopenharmony_ci		if (rv < SS_SUCCESS) {
21838c2ecf20Sopenharmony_ci			if (flags & CS_VERBOSE)
21848c2ecf20Sopenharmony_ci				print_st_err(device, os, ns, rv);
21858c2ecf20Sopenharmony_ci			break;
21868c2ecf20Sopenharmony_ci		}
21878c2ecf20Sopenharmony_ci	}
21888c2ecf20Sopenharmony_ci	rcu_read_unlock();
21898c2ecf20Sopenharmony_ci
21908c2ecf20Sopenharmony_ci	return rv;
21918c2ecf20Sopenharmony_ci}
21928c2ecf20Sopenharmony_ci
21938c2ecf20Sopenharmony_cistatic void
21948c2ecf20Sopenharmony_ciconn_set_state(struct drbd_connection *connection, union drbd_state mask, union drbd_state val,
21958c2ecf20Sopenharmony_ci	       union drbd_state *pns_min, union drbd_state *pns_max, enum chg_state_flags flags)
21968c2ecf20Sopenharmony_ci{
21978c2ecf20Sopenharmony_ci	union drbd_state ns, os, ns_max = { };
21988c2ecf20Sopenharmony_ci	union drbd_state ns_min = {
21998c2ecf20Sopenharmony_ci		{ .role = R_MASK,
22008c2ecf20Sopenharmony_ci		  .peer = R_MASK,
22018c2ecf20Sopenharmony_ci		  .conn = val.conn,
22028c2ecf20Sopenharmony_ci		  .disk = D_MASK,
22038c2ecf20Sopenharmony_ci		  .pdsk = D_MASK
22048c2ecf20Sopenharmony_ci		} };
22058c2ecf20Sopenharmony_ci	struct drbd_peer_device *peer_device;
22068c2ecf20Sopenharmony_ci	enum drbd_state_rv rv;
22078c2ecf20Sopenharmony_ci	int vnr, number_of_volumes = 0;
22088c2ecf20Sopenharmony_ci
22098c2ecf20Sopenharmony_ci	if (mask.conn == C_MASK) {
22108c2ecf20Sopenharmony_ci		/* remember last connect time so request_timer_fn() won't
22118c2ecf20Sopenharmony_ci		 * kill newly established sessions while we are still trying to thaw
22128c2ecf20Sopenharmony_ci		 * previously frozen IO */
22138c2ecf20Sopenharmony_ci		if (connection->cstate != C_WF_REPORT_PARAMS && val.conn == C_WF_REPORT_PARAMS)
22148c2ecf20Sopenharmony_ci			connection->last_reconnect_jif = jiffies;
22158c2ecf20Sopenharmony_ci
22168c2ecf20Sopenharmony_ci		connection->cstate = val.conn;
22178c2ecf20Sopenharmony_ci	}
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	rcu_read_lock();
22208c2ecf20Sopenharmony_ci	idr_for_each_entry(&connection->peer_devices, peer_device, vnr) {
22218c2ecf20Sopenharmony_ci		struct drbd_device *device = peer_device->device;
22228c2ecf20Sopenharmony_ci		number_of_volumes++;
22238c2ecf20Sopenharmony_ci		os = drbd_read_state(device);
22248c2ecf20Sopenharmony_ci		ns = apply_mask_val(os, mask, val);
22258c2ecf20Sopenharmony_ci		ns = sanitize_state(device, os, ns, NULL);
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_ci		if (flags & CS_IGN_OUTD_FAIL && ns.disk == D_OUTDATED && os.disk < D_OUTDATED)
22288c2ecf20Sopenharmony_ci			ns.disk = os.disk;
22298c2ecf20Sopenharmony_ci
22308c2ecf20Sopenharmony_ci		rv = _drbd_set_state(device, ns, flags, NULL);
22318c2ecf20Sopenharmony_ci		BUG_ON(rv < SS_SUCCESS);
22328c2ecf20Sopenharmony_ci		ns.i = device->state.i;
22338c2ecf20Sopenharmony_ci		ns_max.role = max_role(ns.role, ns_max.role);
22348c2ecf20Sopenharmony_ci		ns_max.peer = max_role(ns.peer, ns_max.peer);
22358c2ecf20Sopenharmony_ci		ns_max.conn = max_t(enum drbd_conns, ns.conn, ns_max.conn);
22368c2ecf20Sopenharmony_ci		ns_max.disk = max_t(enum drbd_disk_state, ns.disk, ns_max.disk);
22378c2ecf20Sopenharmony_ci		ns_max.pdsk = max_t(enum drbd_disk_state, ns.pdsk, ns_max.pdsk);
22388c2ecf20Sopenharmony_ci
22398c2ecf20Sopenharmony_ci		ns_min.role = min_role(ns.role, ns_min.role);
22408c2ecf20Sopenharmony_ci		ns_min.peer = min_role(ns.peer, ns_min.peer);
22418c2ecf20Sopenharmony_ci		ns_min.conn = min_t(enum drbd_conns, ns.conn, ns_min.conn);
22428c2ecf20Sopenharmony_ci		ns_min.disk = min_t(enum drbd_disk_state, ns.disk, ns_min.disk);
22438c2ecf20Sopenharmony_ci		ns_min.pdsk = min_t(enum drbd_disk_state, ns.pdsk, ns_min.pdsk);
22448c2ecf20Sopenharmony_ci	}
22458c2ecf20Sopenharmony_ci	rcu_read_unlock();
22468c2ecf20Sopenharmony_ci
22478c2ecf20Sopenharmony_ci	if (number_of_volumes == 0) {
22488c2ecf20Sopenharmony_ci		ns_min = ns_max = (union drbd_state) { {
22498c2ecf20Sopenharmony_ci				.role = R_SECONDARY,
22508c2ecf20Sopenharmony_ci				.peer = R_UNKNOWN,
22518c2ecf20Sopenharmony_ci				.conn = val.conn,
22528c2ecf20Sopenharmony_ci				.disk = D_DISKLESS,
22538c2ecf20Sopenharmony_ci				.pdsk = D_UNKNOWN
22548c2ecf20Sopenharmony_ci			} };
22558c2ecf20Sopenharmony_ci	}
22568c2ecf20Sopenharmony_ci
22578c2ecf20Sopenharmony_ci	ns_min.susp = ns_max.susp = connection->resource->susp;
22588c2ecf20Sopenharmony_ci	ns_min.susp_nod = ns_max.susp_nod = connection->resource->susp_nod;
22598c2ecf20Sopenharmony_ci	ns_min.susp_fen = ns_max.susp_fen = connection->resource->susp_fen;
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ci	*pns_min = ns_min;
22628c2ecf20Sopenharmony_ci	*pns_max = ns_max;
22638c2ecf20Sopenharmony_ci}
22648c2ecf20Sopenharmony_ci
22658c2ecf20Sopenharmony_cistatic enum drbd_state_rv
22668c2ecf20Sopenharmony_ci_conn_rq_cond(struct drbd_connection *connection, union drbd_state mask, union drbd_state val)
22678c2ecf20Sopenharmony_ci{
22688c2ecf20Sopenharmony_ci	enum drbd_state_rv err, rv = SS_UNKNOWN_ERROR; /* continue waiting */;
22698c2ecf20Sopenharmony_ci
22708c2ecf20Sopenharmony_ci	if (test_and_clear_bit(CONN_WD_ST_CHG_OKAY, &connection->flags))
22718c2ecf20Sopenharmony_ci		rv = SS_CW_SUCCESS;
22728c2ecf20Sopenharmony_ci
22738c2ecf20Sopenharmony_ci	if (test_and_clear_bit(CONN_WD_ST_CHG_FAIL, &connection->flags))
22748c2ecf20Sopenharmony_ci		rv = SS_CW_FAILED_BY_PEER;
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	err = conn_is_valid_transition(connection, mask, val, 0);
22778c2ecf20Sopenharmony_ci	if (err == SS_SUCCESS && connection->cstate == C_WF_REPORT_PARAMS)
22788c2ecf20Sopenharmony_ci		return rv;
22798c2ecf20Sopenharmony_ci
22808c2ecf20Sopenharmony_ci	return err;
22818c2ecf20Sopenharmony_ci}
22828c2ecf20Sopenharmony_ci
22838c2ecf20Sopenharmony_cienum drbd_state_rv
22848c2ecf20Sopenharmony_ci_conn_request_state(struct drbd_connection *connection, union drbd_state mask, union drbd_state val,
22858c2ecf20Sopenharmony_ci		    enum chg_state_flags flags)
22868c2ecf20Sopenharmony_ci{
22878c2ecf20Sopenharmony_ci	enum drbd_state_rv rv = SS_SUCCESS;
22888c2ecf20Sopenharmony_ci	struct after_conn_state_chg_work *acscw;
22898c2ecf20Sopenharmony_ci	enum drbd_conns oc = connection->cstate;
22908c2ecf20Sopenharmony_ci	union drbd_state ns_max, ns_min, os;
22918c2ecf20Sopenharmony_ci	bool have_mutex = false;
22928c2ecf20Sopenharmony_ci	struct drbd_state_change *state_change;
22938c2ecf20Sopenharmony_ci
22948c2ecf20Sopenharmony_ci	if (mask.conn) {
22958c2ecf20Sopenharmony_ci		rv = is_valid_conn_transition(oc, val.conn);
22968c2ecf20Sopenharmony_ci		if (rv < SS_SUCCESS)
22978c2ecf20Sopenharmony_ci			goto abort;
22988c2ecf20Sopenharmony_ci	}
22998c2ecf20Sopenharmony_ci
23008c2ecf20Sopenharmony_ci	rv = conn_is_valid_transition(connection, mask, val, flags);
23018c2ecf20Sopenharmony_ci	if (rv < SS_SUCCESS)
23028c2ecf20Sopenharmony_ci		goto abort;
23038c2ecf20Sopenharmony_ci
23048c2ecf20Sopenharmony_ci	if (oc == C_WF_REPORT_PARAMS && val.conn == C_DISCONNECTING &&
23058c2ecf20Sopenharmony_ci	    !(flags & (CS_LOCAL_ONLY | CS_HARD))) {
23068c2ecf20Sopenharmony_ci
23078c2ecf20Sopenharmony_ci		/* This will be a cluster-wide state change.
23088c2ecf20Sopenharmony_ci		 * Need to give up the spinlock, grab the mutex,
23098c2ecf20Sopenharmony_ci		 * then send the state change request, ... */
23108c2ecf20Sopenharmony_ci		spin_unlock_irq(&connection->resource->req_lock);
23118c2ecf20Sopenharmony_ci		mutex_lock(&connection->cstate_mutex);
23128c2ecf20Sopenharmony_ci		have_mutex = true;
23138c2ecf20Sopenharmony_ci
23148c2ecf20Sopenharmony_ci		set_bit(CONN_WD_ST_CHG_REQ, &connection->flags);
23158c2ecf20Sopenharmony_ci		if (conn_send_state_req(connection, mask, val)) {
23168c2ecf20Sopenharmony_ci			/* sending failed. */
23178c2ecf20Sopenharmony_ci			clear_bit(CONN_WD_ST_CHG_REQ, &connection->flags);
23188c2ecf20Sopenharmony_ci			rv = SS_CW_FAILED_BY_PEER;
23198c2ecf20Sopenharmony_ci			/* need to re-aquire the spin lock, though */
23208c2ecf20Sopenharmony_ci			goto abort_unlocked;
23218c2ecf20Sopenharmony_ci		}
23228c2ecf20Sopenharmony_ci
23238c2ecf20Sopenharmony_ci		if (val.conn == C_DISCONNECTING)
23248c2ecf20Sopenharmony_ci			set_bit(DISCONNECT_SENT, &connection->flags);
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci		/* ... and re-aquire the spinlock.
23278c2ecf20Sopenharmony_ci		 * If _conn_rq_cond() returned >= SS_SUCCESS, we must call
23288c2ecf20Sopenharmony_ci		 * conn_set_state() within the same spinlock. */
23298c2ecf20Sopenharmony_ci		spin_lock_irq(&connection->resource->req_lock);
23308c2ecf20Sopenharmony_ci		wait_event_lock_irq(connection->ping_wait,
23318c2ecf20Sopenharmony_ci				(rv = _conn_rq_cond(connection, mask, val)),
23328c2ecf20Sopenharmony_ci				connection->resource->req_lock);
23338c2ecf20Sopenharmony_ci		clear_bit(CONN_WD_ST_CHG_REQ, &connection->flags);
23348c2ecf20Sopenharmony_ci		if (rv < SS_SUCCESS)
23358c2ecf20Sopenharmony_ci			goto abort;
23368c2ecf20Sopenharmony_ci	}
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci	state_change = remember_old_state(connection->resource, GFP_ATOMIC);
23398c2ecf20Sopenharmony_ci	conn_old_common_state(connection, &os, &flags);
23408c2ecf20Sopenharmony_ci	flags |= CS_DC_SUSP;
23418c2ecf20Sopenharmony_ci	conn_set_state(connection, mask, val, &ns_min, &ns_max, flags);
23428c2ecf20Sopenharmony_ci	conn_pr_state_change(connection, os, ns_max, flags);
23438c2ecf20Sopenharmony_ci	remember_new_state(state_change);
23448c2ecf20Sopenharmony_ci
23458c2ecf20Sopenharmony_ci	acscw = kmalloc(sizeof(*acscw), GFP_ATOMIC);
23468c2ecf20Sopenharmony_ci	if (acscw) {
23478c2ecf20Sopenharmony_ci		acscw->oc = os.conn;
23488c2ecf20Sopenharmony_ci		acscw->ns_min = ns_min;
23498c2ecf20Sopenharmony_ci		acscw->ns_max = ns_max;
23508c2ecf20Sopenharmony_ci		acscw->flags = flags;
23518c2ecf20Sopenharmony_ci		acscw->w.cb = w_after_conn_state_ch;
23528c2ecf20Sopenharmony_ci		kref_get(&connection->kref);
23538c2ecf20Sopenharmony_ci		acscw->connection = connection;
23548c2ecf20Sopenharmony_ci		acscw->state_change = state_change;
23558c2ecf20Sopenharmony_ci		drbd_queue_work(&connection->sender_work, &acscw->w);
23568c2ecf20Sopenharmony_ci	} else {
23578c2ecf20Sopenharmony_ci		drbd_err(connection, "Could not kmalloc an acscw\n");
23588c2ecf20Sopenharmony_ci	}
23598c2ecf20Sopenharmony_ci
23608c2ecf20Sopenharmony_ci abort:
23618c2ecf20Sopenharmony_ci	if (have_mutex) {
23628c2ecf20Sopenharmony_ci		/* mutex_unlock() "... must not be used in interrupt context.",
23638c2ecf20Sopenharmony_ci		 * so give up the spinlock, then re-aquire it */
23648c2ecf20Sopenharmony_ci		spin_unlock_irq(&connection->resource->req_lock);
23658c2ecf20Sopenharmony_ci abort_unlocked:
23668c2ecf20Sopenharmony_ci		mutex_unlock(&connection->cstate_mutex);
23678c2ecf20Sopenharmony_ci		spin_lock_irq(&connection->resource->req_lock);
23688c2ecf20Sopenharmony_ci	}
23698c2ecf20Sopenharmony_ci	if (rv < SS_SUCCESS && flags & CS_VERBOSE) {
23708c2ecf20Sopenharmony_ci		drbd_err(connection, "State change failed: %s\n", drbd_set_st_err_str(rv));
23718c2ecf20Sopenharmony_ci		drbd_err(connection, " mask = 0x%x val = 0x%x\n", mask.i, val.i);
23728c2ecf20Sopenharmony_ci		drbd_err(connection, " old_conn:%s wanted_conn:%s\n", drbd_conn_str(oc), drbd_conn_str(val.conn));
23738c2ecf20Sopenharmony_ci	}
23748c2ecf20Sopenharmony_ci	return rv;
23758c2ecf20Sopenharmony_ci}
23768c2ecf20Sopenharmony_ci
23778c2ecf20Sopenharmony_cienum drbd_state_rv
23788c2ecf20Sopenharmony_ciconn_request_state(struct drbd_connection *connection, union drbd_state mask, union drbd_state val,
23798c2ecf20Sopenharmony_ci		   enum chg_state_flags flags)
23808c2ecf20Sopenharmony_ci{
23818c2ecf20Sopenharmony_ci	enum drbd_state_rv rv;
23828c2ecf20Sopenharmony_ci
23838c2ecf20Sopenharmony_ci	spin_lock_irq(&connection->resource->req_lock);
23848c2ecf20Sopenharmony_ci	rv = _conn_request_state(connection, mask, val, flags);
23858c2ecf20Sopenharmony_ci	spin_unlock_irq(&connection->resource->req_lock);
23868c2ecf20Sopenharmony_ci
23878c2ecf20Sopenharmony_ci	return rv;
23888c2ecf20Sopenharmony_ci}
2389