drbd: Add struct drbd_resource
In a first step, each resource has exactly one connection, and both objects are
allocated at the same time. The final result will be one resource and zero or
more connections.
Only allow to delete a resource if all its connections are C_STANDALONE.
Stop the worker threads of all connections early enough.
Signed-off-by: Andreas Gruenbacher <agruen@linbit.com>
Signed-off-by: Philipp Reisner <philipp.reisner@linbit.com>
diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c
index 83d8c18..b8eaccc 100644
--- a/drivers/block/drbd/drbd_nl.c
+++ b/drivers/block/drbd/drbd_nl.c
@@ -249,7 +249,7 @@
first_peer_device(adm_ctx.device)->connection != adm_ctx.connection) {
pr_warning("request: minor=%u, resource=%s; but that minor belongs to connection %s\n",
adm_ctx.minor, adm_ctx.resource_name,
- first_peer_device(adm_ctx.device)->connection->name);
+ first_peer_device(adm_ctx.device)->connection->resource->name);
drbd_msg_put_info("minor exists in different resource");
return ERR_INVALID_REQUEST;
}
@@ -258,7 +258,8 @@
adm_ctx.volume != adm_ctx.device->vnr) {
pr_warning("request: minor=%u, volume=%u; but that minor is volume %u in %s\n",
adm_ctx.minor, adm_ctx.volume,
- adm_ctx.device->vnr, first_peer_device(adm_ctx.device)->connection->name);
+ adm_ctx.device->vnr,
+ first_peer_device(adm_ctx.device)->connection->resource->name);
drbd_msg_put_info("minor exists as different volume");
return ERR_INVALID_REQUEST;
}
@@ -371,23 +372,24 @@
(char[20]) { }, /* address family */
(char[60]) { }, /* address */
NULL };
- char *argv[] = {usermode_helper, cmd, connection->name, NULL };
+ char *resource_name = connection->resource->name;
+ char *argv[] = {usermode_helper, cmd, resource_name, NULL };
int ret;
setup_khelper_env(connection, envp);
conn_md_sync(connection);
- conn_info(connection, "helper command: %s %s %s\n", usermode_helper, cmd, connection->name);
+ conn_info(connection, "helper command: %s %s %s\n", usermode_helper, cmd, resource_name);
/* TODO: conn_bcast_event() ?? */
ret = call_usermodehelper(usermode_helper, argv, envp, UMH_WAIT_PROC);
if (ret)
conn_warn(connection, "helper command: %s %s %s exit code %u (0x%x)\n",
- usermode_helper, cmd, connection->name,
+ usermode_helper, cmd, resource_name,
(ret >> 8) & 0xff, ret);
else
conn_info(connection, "helper command: %s %s %s exit code %u (0x%x)\n",
- usermode_helper, cmd, connection->name,
+ usermode_helper, cmd, resource_name,
(ret >> 8) & 0xff, ret);
/* TODO: conn_bcast_event() ?? */
@@ -2143,6 +2145,7 @@
struct drbd_device *device;
struct net_conf *old_conf, *new_conf = NULL;
struct crypto crypto = { };
+ struct drbd_resource *resource;
struct drbd_connection *connection;
enum drbd_ret_code retcode;
int i;
@@ -2163,17 +2166,21 @@
/* No need for _rcu here. All reconfiguration is
* strictly serialized on genl_lock(). We are protected against
* concurrent reconfiguration/addition/deletion */
- list_for_each_entry(connection, &drbd_connections, connections) {
- if (nla_len(adm_ctx.my_addr) == connection->my_addr_len &&
- !memcmp(nla_data(adm_ctx.my_addr), &connection->my_addr, connection->my_addr_len)) {
- retcode = ERR_LOCAL_ADDR;
- goto out;
- }
+ for_each_resource(resource, &drbd_resources) {
+ for_each_connection(connection, resource) {
+ if (nla_len(adm_ctx.my_addr) == connection->my_addr_len &&
+ !memcmp(nla_data(adm_ctx.my_addr), &connection->my_addr,
+ connection->my_addr_len)) {
+ retcode = ERR_LOCAL_ADDR;
+ goto out;
+ }
- if (nla_len(adm_ctx.peer_addr) == connection->peer_addr_len &&
- !memcmp(nla_data(adm_ctx.peer_addr), &connection->peer_addr, connection->peer_addr_len)) {
- retcode = ERR_PEER_ADDR;
- goto out;
+ if (nla_len(adm_ctx.peer_addr) == connection->peer_addr_len &&
+ !memcmp(nla_data(adm_ctx.peer_addr), &connection->peer_addr,
+ connection->peer_addr_len)) {
+ retcode = ERR_PEER_ADDR;
+ goto out;
+ }
}
}
@@ -2736,7 +2743,7 @@
if (vnr != VOLUME_UNSPECIFIED &&
nla_put_u32(skb, T_ctx_volume, vnr))
goto nla_put_failure;
- if (nla_put_string(skb, T_ctx_resource_name, connection->name))
+ if (nla_put_string(skb, T_ctx_resource_name, connection->resource->name))
goto nla_put_failure;
if (connection->my_addr_len &&
nla_put(skb, T_ctx_my_addr, connection->my_addr_len, &connection->my_addr))
@@ -2899,18 +2906,20 @@
{
struct drbd_device *device;
struct drbd_genlmsghdr *dh;
- struct drbd_connection *pos = (struct drbd_connection *)cb->args[0];
- struct drbd_connection *connection = NULL;
- struct drbd_connection *tmp;
+ struct drbd_resource *pos = (struct drbd_resource *)cb->args[0];
+ struct drbd_resource *resource = NULL;
+ struct drbd_connection *connection;
+ struct drbd_resource *tmp;
unsigned volume = cb->args[1];
/* Open coded, deferred, iteration:
- * list_for_each_entry_safe(connection, tmp, &drbd_connections, connections) {
+ * for_each_resource_safe(resource, tmp, &drbd_resources) {
+ * connection = "first connection of resource";
* idr_for_each_entry(&connection->volumes, device, i) {
* ...
* }
* }
- * where connection is cb->args[0];
+ * where resource is cb->args[0];
* and i is cb->args[1];
*
* cb->args[2] indicates if we shall loop over all resources,
@@ -2927,36 +2936,37 @@
/* synchronize with conn_create()/drbd_destroy_connection() */
rcu_read_lock();
/* revalidate iterator position */
- list_for_each_entry_rcu(tmp, &drbd_connections, connections) {
+ for_each_resource_rcu(tmp, &drbd_resources) {
if (pos == NULL) {
/* first iteration */
pos = tmp;
- connection = pos;
+ resource = pos;
break;
}
if (tmp == pos) {
- connection = pos;
+ resource = pos;
break;
}
}
- if (connection) {
-next_connection:
+ if (resource) {
+next_resource:
+ connection = first_connection(resource);
device = idr_get_next(&connection->volumes, &volume);
if (!device) {
- /* No more volumes to dump on this connection.
- * Advance connection iterator. */
- pos = list_entry_rcu(connection->connections.next,
- struct drbd_connection, connections);
- /* Did we dump any volume on this connection yet? */
+ /* No more volumes to dump on this resource.
+ * Advance resource iterator. */
+ pos = list_entry_rcu(resource->resources.next,
+ struct drbd_resource, resources);
+ /* Did we dump any volume of this resource yet? */
if (volume != 0) {
/* If we reached the end of the list,
* or only a single resource dump was requested,
* we are done. */
- if (&pos->connections == &drbd_connections || cb->args[2])
+ if (&pos->resources == &drbd_resources || cb->args[2])
goto out;
volume = 0;
- connection = pos;
- goto next_connection;
+ resource = pos;
+ goto next_resource;
}
}
@@ -3000,9 +3010,9 @@
rcu_read_unlock();
/* where to start the next iteration */
cb->args[0] = (long)pos;
- cb->args[1] = (pos == connection) ? volume + 1 : 0;
+ cb->args[1] = (pos == resource) ? volume + 1 : 0;
- /* No more connections/volumes/minors found results in an empty skb.
+ /* No more resources/volumes/minors found results in an empty skb.
* Which will terminate the dump. */
return skb->len;
}
@@ -3399,9 +3409,11 @@
/* delete connection */
if (conn_lowest_minor(adm_ctx.connection) < 0) {
- list_del_rcu(&adm_ctx.connection->connections);
+ struct drbd_resource *resource = adm_ctx.connection->resource;
+
+ list_del_rcu(&resource->resources);
synchronize_rcu();
- kref_put(&adm_ctx.connection->kref, drbd_destroy_connection);
+ drbd_free_resource(resource);
retcode = NO_ERROR;
} else {
@@ -3417,6 +3429,8 @@
int drbd_adm_del_resource(struct sk_buff *skb, struct genl_info *info)
{
+ struct drbd_resource *resource;
+ struct drbd_connection *connection;
enum drbd_ret_code retcode;
retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE);
@@ -3425,18 +3439,24 @@
if (retcode != NO_ERROR)
goto out;
- if (conn_lowest_minor(adm_ctx.connection) < 0) {
- list_del_rcu(&adm_ctx.connection->connections);
- synchronize_rcu();
- kref_put(&adm_ctx.connection->kref, drbd_destroy_connection);
-
- retcode = NO_ERROR;
- } else {
+ resource = adm_ctx.resource;
+ for_each_connection(connection, resource) {
+ if (connection->cstate > C_STANDALONE) {
+ retcode = ERR_NET_CONFIGURED;
+ goto out;
+ }
+ }
+ if (!idr_is_empty(&resource->devices)) {
retcode = ERR_RES_IN_USE;
+ goto out;
}
- if (retcode == NO_ERROR)
- drbd_thread_stop(&adm_ctx.connection->worker);
+ list_del_rcu(&resource->resources);
+ for_each_connection(connection, resource)
+ drbd_thread_stop(&connection->worker);
+ synchronize_rcu();
+ drbd_free_resource(resource);
+ retcode = NO_ERROR;
out:
drbd_adm_finish(info, retcode);
return 0;