adm_ctx.device = minor_to_device(d_in->minor);
if (adm_ctx.resource_name) {
adm_ctx.resource = drbd_find_resource(adm_ctx.resource_name);
- if (adm_ctx.resource) {
- adm_ctx.connection = first_connection(adm_ctx.resource);
- kref_get(&adm_ctx.connection->kref);
- }
}
if (!adm_ctx.device && (flags & DRBD_ADM_NEED_MINOR)) {
}
if (flags & DRBD_ADM_NEED_CONNECTION) {
- if (adm_ctx.connection && !(flags & DRBD_ADM_NEED_RESOURCE)) {
+ if (adm_ctx.resource) {
drbd_msg_put_info("no resource name expected");
return ERR_INVALID_REQUEST;
}
int forced = 0;
union drbd_state mask, val;
- if (new_role == R_PRIMARY)
- request_ping(first_peer_device(device)->connection); /* Detect a dead peer ASAP */
+ if (new_role == R_PRIMARY) {
+ struct drbd_connection *connection;
+
+ /* Detect dead peers as soon as possible. */
+
+ rcu_read_lock();
+ for_each_connection(connection, device->resource)
+ request_ping(connection);
+ rcu_read_unlock();
+ }
mutex_lock(device->state_mutex);
}
static enum drbd_ret_code
-_check_net_options(struct drbd_connection *connection, struct net_conf *old_conf, struct net_conf *new_conf)
+_check_net_options(struct drbd_connection *connection, struct net_conf *old_net_conf, struct net_conf *new_net_conf)
{
struct drbd_peer_device *peer_device;
int i;
- if (old_conf && connection->cstate == C_WF_REPORT_PARAMS && connection->agreed_pro_version < 100) {
- if (new_conf->wire_protocol != old_conf->wire_protocol)
+ if (old_net_conf && connection->cstate == C_WF_REPORT_PARAMS && connection->agreed_pro_version < 100) {
+ if (new_net_conf->wire_protocol != old_net_conf->wire_protocol)
return ERR_NEED_APV_100;
- if (new_conf->two_primaries != old_conf->two_primaries)
+ if (new_net_conf->two_primaries != old_net_conf->two_primaries)
return ERR_NEED_APV_100;
- if (strcmp(new_conf->integrity_alg, old_conf->integrity_alg))
+ if (strcmp(new_net_conf->integrity_alg, old_net_conf->integrity_alg))
return ERR_NEED_APV_100;
}
- if (!new_conf->two_primaries &&
+ if (!new_net_conf->two_primaries &&
conn_highest_role(connection) == R_PRIMARY &&
conn_highest_peer(connection) == R_PRIMARY)
return ERR_NEED_ALLOW_TWO_PRI;
- if (new_conf->two_primaries &&
- (new_conf->wire_protocol != DRBD_PROT_C))
+ if (new_net_conf->two_primaries &&
+ (new_net_conf->wire_protocol != DRBD_PROT_C))
return ERR_NOT_PROTO_C;
idr_for_each_entry(&connection->peer_devices, peer_device, i) {
if (get_ldev(device)) {
enum drbd_fencing_p fp = rcu_dereference(device->ldev->disk_conf)->fencing;
put_ldev(device);
- if (new_conf->wire_protocol == DRBD_PROT_A && fp == FP_STONITH)
+ if (new_net_conf->wire_protocol == DRBD_PROT_A && fp == FP_STONITH)
return ERR_STONITH_AND_PROT_A;
}
- if (device->state.role == R_PRIMARY && new_conf->discard_my_data)
+ if (device->state.role == R_PRIMARY && new_net_conf->discard_my_data)
return ERR_DISCARD_IMPOSSIBLE;
}
- if (new_conf->on_congestion != OC_BLOCK && new_conf->wire_protocol != DRBD_PROT_A)
+ if (new_net_conf->on_congestion != OC_BLOCK && new_net_conf->wire_protocol != DRBD_PROT_A)
return ERR_CONG_NOT_PROTO_A;
return NO_ERROR;
}
static enum drbd_ret_code
-check_net_options(struct drbd_connection *connection, struct net_conf *new_conf)
+check_net_options(struct drbd_connection *connection, struct net_conf *new_net_conf)
{
static enum drbd_ret_code rv;
struct drbd_peer_device *peer_device;
int i;
rcu_read_lock();
- rv = _check_net_options(connection, rcu_dereference(connection->net_conf), new_conf);
+ rv = _check_net_options(connection, rcu_dereference(connection->net_conf), new_net_conf);
rcu_read_unlock();
/* connection->volumes protected by genl_lock() here */
}
static enum drbd_ret_code
-alloc_crypto(struct crypto *crypto, struct net_conf *new_conf)
+alloc_crypto(struct crypto *crypto, struct net_conf *new_net_conf)
{
char hmac_name[CRYPTO_MAX_ALG_NAME];
enum drbd_ret_code rv;
- rv = alloc_hash(&crypto->csums_tfm, new_conf->csums_alg,
+ rv = alloc_hash(&crypto->csums_tfm, new_net_conf->csums_alg,
ERR_CSUMS_ALG);
if (rv != NO_ERROR)
return rv;
- rv = alloc_hash(&crypto->verify_tfm, new_conf->verify_alg,
+ rv = alloc_hash(&crypto->verify_tfm, new_net_conf->verify_alg,
ERR_VERIFY_ALG);
if (rv != NO_ERROR)
return rv;
- rv = alloc_hash(&crypto->integrity_tfm, new_conf->integrity_alg,
+ rv = alloc_hash(&crypto->integrity_tfm, new_net_conf->integrity_alg,
ERR_INTEGRITY_ALG);
if (rv != NO_ERROR)
return rv;
- if (new_conf->cram_hmac_alg[0] != 0) {
+ if (new_net_conf->cram_hmac_alg[0] != 0) {
snprintf(hmac_name, CRYPTO_MAX_ALG_NAME, "hmac(%s)",
- new_conf->cram_hmac_alg);
+ new_net_conf->cram_hmac_alg);
rv = alloc_hash(&crypto->cram_hmac_tfm, hmac_name,
ERR_AUTH_ALG);
{
enum drbd_ret_code retcode;
struct drbd_connection *connection;
- struct net_conf *old_conf, *new_conf = NULL;
+ struct net_conf *old_net_conf, *new_net_conf = NULL;
int err;
int ovr; /* online verify running */
int rsr; /* re-sync running */
connection = adm_ctx.connection;
- new_conf = kzalloc(sizeof(struct net_conf), GFP_KERNEL);
- if (!new_conf) {
+ new_net_conf = kzalloc(sizeof(struct net_conf), GFP_KERNEL);
+ if (!new_net_conf) {
retcode = ERR_NOMEM;
goto out;
}
mutex_lock(&connection->data.mutex);
mutex_lock(&connection->conf_update);
- old_conf = connection->net_conf;
+ old_net_conf = connection->net_conf;
- if (!old_conf) {
+ if (!old_net_conf) {
drbd_msg_put_info("net conf missing, try connect");
retcode = ERR_INVALID_REQUEST;
goto fail;
}
- *new_conf = *old_conf;
+ *new_net_conf = *old_net_conf;
if (should_set_defaults(info))
- set_net_conf_defaults(new_conf);
+ set_net_conf_defaults(new_net_conf);
- err = net_conf_from_attrs_for_change(new_conf, info);
+ err = net_conf_from_attrs_for_change(new_net_conf, info);
if (err && err != -ENOMSG) {
retcode = ERR_MANDATORY_TAG;
drbd_msg_put_info(from_attrs_err_to_txt(err));
goto fail;
}
- retcode = check_net_options(connection, new_conf);
+ retcode = check_net_options(connection, new_net_conf);
if (retcode != NO_ERROR)
goto fail;
/* re-sync running */
rsr = conn_resync_running(connection);
- if (rsr && strcmp(new_conf->csums_alg, old_conf->csums_alg)) {
+ if (rsr && strcmp(new_net_conf->csums_alg, old_net_conf->csums_alg)) {
retcode = ERR_CSUMS_RESYNC_RUNNING;
goto fail;
}
/* online verify running */
ovr = conn_ov_running(connection);
- if (ovr && strcmp(new_conf->verify_alg, old_conf->verify_alg)) {
+ if (ovr && strcmp(new_net_conf->verify_alg, old_net_conf->verify_alg)) {
retcode = ERR_VERIFY_RUNNING;
goto fail;
}
- retcode = alloc_crypto(&crypto, new_conf);
+ retcode = alloc_crypto(&crypto, new_net_conf);
if (retcode != NO_ERROR)
goto fail;
- rcu_assign_pointer(connection->net_conf, new_conf);
+ rcu_assign_pointer(connection->net_conf, new_net_conf);
if (!rsr) {
crypto_free_hash(connection->csums_tfm);
mutex_unlock(&connection->conf_update);
mutex_unlock(&connection->data.mutex);
synchronize_rcu();
- kfree(old_conf);
+ kfree(old_net_conf);
if (connection->cstate >= C_WF_REPORT_PARAMS)
drbd_send_sync_param(minor_to_device(conn_lowest_minor(connection)));
mutex_unlock(&connection->conf_update);
mutex_unlock(&connection->data.mutex);
free_crypto(&crypto);
- kfree(new_conf);
+ kfree(new_net_conf);
done:
conn_reconfig_done(connection);
out:
int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info)
{
struct drbd_peer_device *peer_device;
- struct net_conf *old_conf, *new_conf = NULL;
+ struct net_conf *old_net_conf, *new_net_conf = NULL;
struct crypto crypto = { };
struct drbd_resource *resource;
struct drbd_connection *connection;
}
}
- connection = adm_ctx.connection;
+ connection = first_connection(adm_ctx.resource);
conn_reconfig_start(connection);
if (connection->cstate > C_STANDALONE) {
}
/* allocation not in the IO path, drbdsetup / netlink process context */
- new_conf = kzalloc(sizeof(*new_conf), GFP_KERNEL);
- if (!new_conf) {
+ new_net_conf = kzalloc(sizeof(*new_net_conf), GFP_KERNEL);
+ if (!new_net_conf) {
retcode = ERR_NOMEM;
goto fail;
}
- set_net_conf_defaults(new_conf);
+ set_net_conf_defaults(new_net_conf);
- err = net_conf_from_attrs(new_conf, info);
+ err = net_conf_from_attrs(new_net_conf, info);
if (err && err != -ENOMSG) {
retcode = ERR_MANDATORY_TAG;
drbd_msg_put_info(from_attrs_err_to_txt(err));
goto fail;
}
- retcode = check_net_options(connection, new_conf);
+ retcode = check_net_options(connection, new_net_conf);
if (retcode != NO_ERROR)
goto fail;
- retcode = alloc_crypto(&crypto, new_conf);
+ retcode = alloc_crypto(&crypto, new_net_conf);
if (retcode != NO_ERROR)
goto fail;
- ((char *)new_conf->shared_secret)[SHARED_SECRET_MAX-1] = 0;
+ ((char *)new_net_conf->shared_secret)[SHARED_SECRET_MAX-1] = 0;
conn_flush_workqueue(connection);
mutex_lock(&connection->conf_update);
- old_conf = connection->net_conf;
- if (old_conf) {
+ old_net_conf = connection->net_conf;
+ if (old_net_conf) {
retcode = ERR_NET_CONFIGURED;
mutex_unlock(&connection->conf_update);
goto fail;
}
- rcu_assign_pointer(connection->net_conf, new_conf);
+ rcu_assign_pointer(connection->net_conf, new_net_conf);
conn_free_crypto(connection);
connection->cram_hmac_tfm = crypto.cram_hmac_tfm;
fail:
free_crypto(&crypto);
- kfree(new_conf);
+ kfree(new_net_conf);
conn_reconfig_done(connection);
out:
return drbd_adm_simple_request_state(skb, info, NS(disk, D_OUTDATED));
}
-static int nla_put_drbd_cfg_context(struct sk_buff *skb, struct drbd_connection *connection, unsigned vnr)
+static int nla_put_drbd_cfg_context(struct sk_buff *skb,
+ struct drbd_resource *resource,
+ struct drbd_connection *connection,
+ struct drbd_device *device)
{
struct nlattr *nla;
nla = nla_nest_start(skb, DRBD_NLA_CFG_CONTEXT);
if (!nla)
goto nla_put_failure;
- if (vnr != VOLUME_UNSPECIFIED &&
- nla_put_u32(skb, T_ctx_volume, vnr))
+ if (device &&
+ nla_put_u32(skb, T_ctx_volume, device->vnr))
goto nla_put_failure;
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))
- goto nla_put_failure;
- if (connection->peer_addr_len &&
- nla_put(skb, T_ctx_peer_addr, connection->peer_addr_len, &connection->peer_addr))
- goto nla_put_failure;
+ if (connection) {
+ if (connection->my_addr_len &&
+ nla_put(skb, T_ctx_my_addr, connection->my_addr_len, &connection->my_addr))
+ goto nla_put_failure;
+ if (connection->peer_addr_len &&
+ nla_put(skb, T_ctx_peer_addr, connection->peer_addr_len, &connection->peer_addr))
+ goto nla_put_failure;
+ }
nla_nest_end(skb, nla);
return 0;
return -EMSGSIZE;
}
-static int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device,
+/*
+ * Return the connection of @resource if @resource has exactly one connection.
+ */
+static struct drbd_connection *the_only_connection(struct drbd_resource *resource)
+{
+ struct list_head *connections = &resource->connections;
+
+ if (list_empty(connections) || connections->next->next != connections)
+ return NULL;
+ return list_first_entry(&resource->connections, struct drbd_connection, connections);
+}
+
+int nla_put_status_info(struct sk_buff *skb, struct drbd_device *device,
const struct sib_info *sib)
{
+ struct drbd_resource *resource = device->resource;
struct state_info *si = NULL; /* for sizeof(si->member); */
struct nlattr *nla;
int got_ldev;
/* We need to add connection name and volume number information still.
* Minor number is in drbd_genlmsghdr. */
- if (nla_put_drbd_cfg_context(skb, first_peer_device(device)->connection, device->vnr))
+ if (nla_put_drbd_cfg_context(skb, resource, the_only_connection(resource), device))
goto nla_put_failure;
if (res_opts_to_skb(skb, &device->resource->res_opts, exclude_sensitive))
static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb)
{
- struct drbd_peer_device *peer_device;
struct drbd_device *device;
struct drbd_genlmsghdr *dh;
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:
* for_each_resource_safe(resource, tmp, &drbd_resources) {
- * connection = "first connection of resource";
- * idr_for_each_entry(&connection->peer_devices, peer_device, i) {
+ * connection = "first connection of resource or undefined";
+ * idr_for_each_entry(&resource->devices, device, i) {
* ...
* }
* }
}
if (resource) {
next_resource:
- connection = first_connection(resource);
- peer_device = idr_get_next(&connection->peer_devices, &volume);
- if (!peer_device) {
+ device = idr_get_next(&resource->devices, &volume);
+ if (!device) {
/* No more volumes to dump on this resource.
* Advance resource iterator. */
pos = list_entry_rcu(resource->resources.next,
if (!dh)
goto out;
- if (!peer_device) {
+ if (!device) {
/* This is a connection without a single volume.
* Suprisingly enough, it may have a network
* configuration. */
- struct net_conf *nc;
+ struct drbd_connection *connection;
+
dh->minor = -1U;
dh->ret_code = NO_ERROR;
- if (nla_put_drbd_cfg_context(skb, connection, VOLUME_UNSPECIFIED))
- goto cancel;
- nc = rcu_dereference(connection->net_conf);
- if (nc && net_conf_to_skb(skb, nc, 1) != 0)
+ connection = the_only_connection(resource);
+ if (nla_put_drbd_cfg_context(skb, resource, connection, NULL))
goto cancel;
+ if (connection) {
+ struct net_conf *nc;
+
+ nc = rcu_dereference(connection->net_conf);
+ if (nc && net_conf_to_skb(skb, nc, 1) != 0)
+ goto cancel;
+ }
goto done;
}
- device = peer_device->device;
D_ASSERT(device, device->vnr == volume);
- D_ASSERT(device, first_peer_device(device)->connection == connection);
+ D_ASSERT(device, device->resource == resource);
dh->minor = device_to_minor(device);
dh->ret_code = NO_ERROR;
if (retcode != NO_ERROR)
goto out;
- if (adm_ctx.connection) {
+ if (adm_ctx.resource) {
if (info->nlhdr->nlmsg_flags & NLM_F_EXCL) {
retcode = ERR_INVALID_REQUEST;
drbd_msg_put_info("resource exists");
goto out;
}
- retcode = drbd_create_minor(adm_ctx.connection, dh->minor, adm_ctx.volume);
+ retcode = drbd_create_device(adm_ctx.resource, dh->minor, adm_ctx.volume);
out:
drbd_adm_finish(info, retcode);
return 0;
device->state.role == R_SECONDARY) {
_drbd_request_state(device, NS(conn, C_WF_REPORT_PARAMS),
CS_VERBOSE + CS_WAIT_COMPLETE);
- drbd_delete_minor(device);
+ drbd_delete_device(device);
return NO_ERROR;
} else
return ERR_MINOR_CONFIGURED;
int drbd_adm_down(struct sk_buff *skb, struct genl_info *info)
{
+ struct drbd_resource *resource;
+ struct drbd_connection *connection;
+ struct drbd_device *device;
int retcode; /* enum drbd_ret_code rsp. enum drbd_state_rv */
- struct drbd_peer_device *peer_device;
unsigned i;
retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE);
if (retcode != NO_ERROR)
goto out;
+ resource = adm_ctx.resource;
/* demote */
- idr_for_each_entry(&adm_ctx.connection->peer_devices, peer_device, i) {
- retcode = drbd_set_role(peer_device->device, R_SECONDARY, 0);
+ for_each_connection(connection, resource) {
+ struct drbd_peer_device *peer_device;
+
+ idr_for_each_entry(&connection->peer_devices, peer_device, i) {
+ retcode = drbd_set_role(peer_device->device, R_SECONDARY, 0);
+ if (retcode < SS_SUCCESS) {
+ drbd_msg_put_info("failed to demote");
+ goto out;
+ }
+ }
+
+ retcode = conn_try_disconnect(connection, 0);
if (retcode < SS_SUCCESS) {
- drbd_msg_put_info("failed to demote");
+ drbd_msg_put_info("failed to disconnect");
goto out;
}
}
- retcode = conn_try_disconnect(adm_ctx.connection, 0);
- if (retcode < SS_SUCCESS) {
- drbd_msg_put_info("failed to disconnect");
- goto out;
- }
-
/* detach */
- idr_for_each_entry(&adm_ctx.connection->peer_devices, peer_device, i) {
- retcode = adm_detach(peer_device->device, 0);
+ idr_for_each_entry(&resource->devices, device, i) {
+ retcode = adm_detach(device, 0);
if (retcode < SS_SUCCESS || retcode > NO_ERROR) {
drbd_msg_put_info("failed to detach");
goto out;
/* If we reach this, all volumes (of this connection) are Secondary,
* Disconnected, Diskless, aka Unconfigured. Make sure all threads have
* actually stopped, state handling only does drbd_thread_stop_nowait(). */
- drbd_thread_stop(&adm_ctx.connection->worker);
+ for_each_connection(connection, resource)
+ drbd_thread_stop(&connection->worker);
/* Now, nothing can fail anymore */
/* delete volumes */
- idr_for_each_entry(&adm_ctx.connection->peer_devices, peer_device, i) {
- retcode = adm_del_minor(peer_device->device);
+ idr_for_each_entry(&resource->devices, device, i) {
+ retcode = adm_del_minor(device);
if (retcode != NO_ERROR) {
/* "can not happen" */
drbd_msg_put_info("failed to delete volume");
}
}
- /* delete connection */
- if (conn_lowest_minor(adm_ctx.connection) < 0) {
- struct drbd_resource *resource = adm_ctx.connection->resource;
-
- list_del_rcu(&resource->resources);
- synchronize_rcu();
- drbd_free_resource(resource);
+ list_del_rcu(&resource->resources);
+ synchronize_rcu();
+ drbd_free_resource(resource);
+ retcode = NO_ERROR;
- retcode = NO_ERROR;
- } else {
- /* "can not happen" */
- retcode = ERR_RES_IN_USE;
- drbd_msg_put_info("failed to delete connection");
- }
- goto out;
out:
drbd_adm_finish(info, retcode);
return 0;