Commit 77c556f6 authored by Andreas Gruenbacher's avatar Andreas Gruenbacher Committed by Philipp Reisner

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: default avatarAndreas Gruenbacher <agruen@linbit.com>
Signed-off-by: default avatarPhilipp Reisner <philipp.reisner@linbit.com>
parent 05a10ec7
...@@ -105,7 +105,7 @@ struct drbd_connection; ...@@ -105,7 +105,7 @@ struct drbd_connection;
#define DEV (disk_to_dev(device->vdisk)) #define DEV (disk_to_dev(device->vdisk))
#define conn_printk(LEVEL, TCONN, FMT, ARGS...) \ #define conn_printk(LEVEL, TCONN, FMT, ARGS...) \
printk(LEVEL "d-con %s: " FMT, TCONN->name , ## ARGS) printk(LEVEL "d-con %s: " FMT, TCONN->resource->name , ## ARGS)
#define conn_alert(TCONN, FMT, ARGS...) conn_printk(KERN_ALERT, TCONN, FMT, ## ARGS) #define conn_alert(TCONN, FMT, ARGS...) conn_printk(KERN_ALERT, TCONN, FMT, ## ARGS)
#define conn_crit(TCONN, FMT, ARGS...) conn_printk(KERN_CRIT, TCONN, FMT, ## ARGS) #define conn_crit(TCONN, FMT, ARGS...) conn_printk(KERN_CRIT, TCONN, FMT, ## ARGS)
#define conn_err(TCONN, FMT, ARGS...) conn_printk(KERN_ERR, TCONN, FMT, ## ARGS) #define conn_err(TCONN, FMT, ARGS...) conn_printk(KERN_ERR, TCONN, FMT, ## ARGS)
...@@ -167,7 +167,7 @@ drbd_insert_fault(struct drbd_device *device, unsigned int type) { ...@@ -167,7 +167,7 @@ drbd_insert_fault(struct drbd_device *device, unsigned int type) {
extern struct ratelimit_state drbd_ratelimit_state; extern struct ratelimit_state drbd_ratelimit_state;
extern struct idr drbd_devices; /* RCU, updates: genl_lock() */ extern struct idr drbd_devices; /* RCU, updates: genl_lock() */
extern struct list_head drbd_connections; /* RCU, updates: genl_lock() */ extern struct list_head drbd_resources; /* RCU, updates: genl_lock() */
extern const char *cmdname(enum drbd_packet cmd); extern const char *cmdname(enum drbd_packet cmd);
...@@ -536,9 +536,16 @@ enum { ...@@ -536,9 +536,16 @@ enum {
DISCONNECT_SENT, DISCONNECT_SENT,
}; };
struct drbd_connection { /* is a resource from the config file */ struct drbd_resource {
char *name; /* Resource name */ char *name;
struct list_head connections; /* linked on global drbd_connections */ struct kref kref;
struct list_head connections;
struct list_head resources;
};
struct drbd_connection {
struct list_head connections;
struct drbd_resource *resource;
struct kref kref; struct kref kref;
struct idr volumes; /* <connection, vnr> to device mapping */ struct idr volumes; /* <connection, vnr> to device mapping */
enum drbd_conns cstate; /* Only C_STANDALONE to C_WF_REPORT_PARAMS */ enum drbd_conns cstate; /* Only C_STANDALONE to C_WF_REPORT_PARAMS */
...@@ -779,6 +786,24 @@ static inline struct drbd_peer_device *first_peer_device(struct drbd_device *dev ...@@ -779,6 +786,24 @@ static inline struct drbd_peer_device *first_peer_device(struct drbd_device *dev
return list_first_entry(&device->peer_devices, struct drbd_peer_device, peer_devices); return list_first_entry(&device->peer_devices, struct drbd_peer_device, peer_devices);
} }
#define for_each_resource(resource, _resources) \
list_for_each_entry(resource, _resources, resources)
#define for_each_resource_rcu(resource, _resources) \
list_for_each_entry_rcu(resource, _resources, resources)
#define for_each_resource_safe(resource, tmp, _resources) \
list_for_each_entry_safe(resource, tmp, _resources, resources)
#define for_each_connection(connection, resource) \
list_for_each_entry(connection, &resource->connections, connections)
#define for_each_connection_rcu(connection, resource) \
list_for_each_entry_rcu(connection, &resource->connections, connections)
#define for_each_connection_safe(connection, tmp, resource) \
list_for_each_entry_safe(connection, tmp, &resource->connections, connections)
#define for_each_peer_device(peer_device, device) \ #define for_each_peer_device(peer_device, device) \
list_for_each_entry(peer_device, &device->peer_devices, peer_devices) list_for_each_entry(peer_device, &device->peer_devices, peer_devices)
...@@ -1177,12 +1202,16 @@ extern int conn_lowest_minor(struct drbd_connection *connection); ...@@ -1177,12 +1202,16 @@ extern int conn_lowest_minor(struct drbd_connection *connection);
enum drbd_ret_code drbd_create_minor(struct drbd_connection *connection, unsigned int minor, int vnr); enum drbd_ret_code drbd_create_minor(struct drbd_connection *connection, unsigned int minor, int vnr);
extern void drbd_destroy_device(struct kref *kref); extern void drbd_destroy_device(struct kref *kref);
extern struct drbd_resource *drbd_create_resource(const char *name);
extern void drbd_free_resource(struct drbd_resource *resource);
extern int set_resource_options(struct drbd_connection *connection, struct res_opts *res_opts); extern int set_resource_options(struct drbd_connection *connection, struct res_opts *res_opts);
extern struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts); extern struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts);
extern void drbd_destroy_connection(struct kref *kref); extern void drbd_destroy_connection(struct kref *kref);
struct drbd_connection *conn_get_by_name(const char *name); struct drbd_connection *conn_get_by_name(const char *name);
extern struct drbd_connection *conn_get_by_addrs(void *my_addr, int my_addr_len, extern struct drbd_connection *conn_get_by_addrs(void *my_addr, int my_addr_len,
void *peer_addr, int peer_addr_len); void *peer_addr, int peer_addr_len);
extern void drbd_destroy_resource(struct kref *kref);
extern void conn_free_crypto(struct drbd_connection *connection); extern void conn_free_crypto(struct drbd_connection *connection);
extern int proc_details; extern int proc_details;
...@@ -2082,4 +2111,10 @@ static inline void drbd_md_flush(struct drbd_device *device) ...@@ -2082,4 +2111,10 @@ static inline void drbd_md_flush(struct drbd_device *device)
} }
} }
static inline struct drbd_connection *first_connection(struct drbd_resource *resource)
{
return list_first_entry(&resource->connections,
struct drbd_connection, connections);
}
#endif #endif
...@@ -118,7 +118,7 @@ module_param_string(usermode_helper, usermode_helper, sizeof(usermode_helper), 0 ...@@ -118,7 +118,7 @@ module_param_string(usermode_helper, usermode_helper, sizeof(usermode_helper), 0
* as member "struct gendisk *vdisk;" * as member "struct gendisk *vdisk;"
*/ */
struct idr drbd_devices; struct idr drbd_devices;
struct list_head drbd_connections; /* list of struct drbd_connection */ struct list_head drbd_resources;
struct kmem_cache *drbd_request_cache; struct kmem_cache *drbd_request_cache;
struct kmem_cache *drbd_ee_cache; /* peer requests */ struct kmem_cache *drbd_ee_cache; /* peer requests */
...@@ -330,7 +330,8 @@ static int drbd_thread_setup(void *arg) ...@@ -330,7 +330,8 @@ static int drbd_thread_setup(void *arg)
int retval; int retval;
snprintf(current->comm, sizeof(current->comm), "drbd_%c_%s", snprintf(current->comm, sizeof(current->comm), "drbd_%c_%s",
thi->name[0], thi->connection->name); thi->name[0],
thi->connection->resource->name);
restart: restart:
retval = thi->function(thi); retval = thi->function(thi);
...@@ -411,7 +412,7 @@ int drbd_thread_start(struct drbd_thread *thi) ...@@ -411,7 +412,7 @@ int drbd_thread_start(struct drbd_thread *thi)
flush_signals(current); /* otherw. may get -ERESTARTNOINTR */ flush_signals(current); /* otherw. may get -ERESTARTNOINTR */
nt = kthread_create(drbd_thread_setup, (void *) thi, nt = kthread_create(drbd_thread_setup, (void *) thi,
"drbd_%c_%s", thi->name[0], thi->connection->name); "drbd_%c_%s", thi->name[0], thi->connection->resource->name);
if (IS_ERR(nt)) { if (IS_ERR(nt)) {
conn_err(connection, "Couldn't start thread\n"); conn_err(connection, "Couldn't start thread\n");
...@@ -2276,12 +2277,31 @@ void drbd_restart_request(struct drbd_request *req) ...@@ -2276,12 +2277,31 @@ void drbd_restart_request(struct drbd_request *req)
queue_work(retry.wq, &retry.worker); queue_work(retry.wq, &retry.worker);
} }
void drbd_destroy_resource(struct kref *kref)
{
struct drbd_resource *resource =
container_of(kref, struct drbd_resource, kref);
kfree(resource->name);
kfree(resource);
}
void drbd_free_resource(struct drbd_resource *resource)
{
struct drbd_connection *connection, *tmp;
for_each_connection_safe(connection, tmp, resource) {
list_del(&connection->connections);
kref_put(&connection->kref, drbd_destroy_connection);
}
kref_put(&resource->kref, drbd_destroy_resource);
}
static void drbd_cleanup(void) static void drbd_cleanup(void)
{ {
unsigned int i; unsigned int i;
struct drbd_device *device; struct drbd_device *device;
struct drbd_connection *connection, *tmp; struct drbd_resource *resource, *tmp;
unregister_reboot_notifier(&drbd_notifier); unregister_reboot_notifier(&drbd_notifier);
...@@ -2311,10 +2331,9 @@ static void drbd_cleanup(void) ...@@ -2311,10 +2331,9 @@ static void drbd_cleanup(void)
} }
/* not _rcu since, no other updater anymore. Genl already unregistered */ /* not _rcu since, no other updater anymore. Genl already unregistered */
list_for_each_entry_safe(connection, tmp, &drbd_connections, connections) { for_each_resource_safe(resource, tmp, &drbd_resources) {
list_del(&connection->connections); /* not _rcu no proc, not other threads */ list_del(&resource->resources);
/* synchronize_rcu(); */ drbd_free_resource(resource);
kref_put(&connection->kref, drbd_destroy_connection);
} }
drbd_destroy_mempools(); drbd_destroy_mempools();
...@@ -2391,13 +2410,15 @@ static void drbd_init_workqueue(struct drbd_work_queue* wq) ...@@ -2391,13 +2410,15 @@ static void drbd_init_workqueue(struct drbd_work_queue* wq)
struct drbd_connection *conn_get_by_name(const char *name) struct drbd_connection *conn_get_by_name(const char *name)
{ {
struct drbd_connection *connection; struct drbd_connection *connection;
struct drbd_resource *resource;
if (!name || !name[0]) if (!name || !name[0])
return NULL; return NULL;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(connection, &drbd_connections, connections) { for_each_resource_rcu(resource, &drbd_resources) {
if (!strcmp(connection->name, name)) { if (!strcmp(resource->name, name)) {
connection = first_connection(resource);
kref_get(&connection->kref); kref_get(&connection->kref);
goto found; goto found;
} }
...@@ -2411,10 +2432,12 @@ struct drbd_connection *conn_get_by_name(const char *name) ...@@ -2411,10 +2432,12 @@ struct drbd_connection *conn_get_by_name(const char *name)
struct drbd_connection *conn_get_by_addrs(void *my_addr, int my_addr_len, struct drbd_connection *conn_get_by_addrs(void *my_addr, int my_addr_len,
void *peer_addr, int peer_addr_len) void *peer_addr, int peer_addr_len)
{ {
struct drbd_resource *resource;
struct drbd_connection *connection; struct drbd_connection *connection;
rcu_read_lock(); rcu_read_lock();
list_for_each_entry_rcu(connection, &drbd_connections, connections) { for_each_resource_rcu(resource, &drbd_resources) {
for_each_connection_rcu(connection, resource) {
if (connection->my_addr_len == my_addr_len && if (connection->my_addr_len == my_addr_len &&
connection->peer_addr_len == peer_addr_len && connection->peer_addr_len == peer_addr_len &&
!memcmp(&connection->my_addr, my_addr, my_addr_len) && !memcmp(&connection->my_addr, my_addr, my_addr_len) &&
...@@ -2423,6 +2446,7 @@ struct drbd_connection *conn_get_by_addrs(void *my_addr, int my_addr_len, ...@@ -2423,6 +2446,7 @@ struct drbd_connection *conn_get_by_addrs(void *my_addr, int my_addr_len,
goto found; goto found;
} }
} }
}
connection = NULL; connection = NULL;
found: found:
rcu_read_unlock(); rcu_read_unlock();
...@@ -2506,19 +2530,34 @@ int set_resource_options(struct drbd_connection *connection, struct res_opts *re ...@@ -2506,19 +2530,34 @@ int set_resource_options(struct drbd_connection *connection, struct res_opts *re
} }
struct drbd_resource *drbd_create_resource(const char *name)
{
struct drbd_resource *resource;
resource = kmalloc(sizeof(struct drbd_resource), GFP_KERNEL);
if (!resource)
return NULL;
resource->name = kstrdup(name, GFP_KERNEL);
if (!resource->name) {
kfree(resource);
return NULL;
}
kref_init(&resource->kref);
INIT_LIST_HEAD(&resource->connections);
list_add_tail_rcu(&resource->resources, &drbd_resources);
return resource;
}
/* caller must be under genl_lock() */ /* caller must be under genl_lock() */
struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts) struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts)
{ {
struct drbd_resource *resource;
struct drbd_connection *connection; struct drbd_connection *connection;
connection = kzalloc(sizeof(struct drbd_connection), GFP_KERNEL); connection = kzalloc(sizeof(struct drbd_connection), GFP_KERNEL);
if (!connection) if (!connection)
return NULL; return NULL;
connection->name = kstrdup(name, GFP_KERNEL);
if (!connection->name)
goto fail;
if (drbd_alloc_socket(&connection->data)) if (drbd_alloc_socket(&connection->data))
goto fail; goto fail;
if (drbd_alloc_socket(&connection->meta)) if (drbd_alloc_socket(&connection->meta))
...@@ -2545,6 +2584,10 @@ struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts) ...@@ -2545,6 +2584,10 @@ struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts)
connection->send.current_epoch_nr = 0; connection->send.current_epoch_nr = 0;
connection->send.current_epoch_writes = 0; connection->send.current_epoch_writes = 0;
resource = drbd_create_resource(name);
if (!resource)
goto fail;
connection->cstate = C_STANDALONE; connection->cstate = C_STANDALONE;
mutex_init(&connection->cstate_mutex); mutex_init(&connection->cstate_mutex);
spin_lock_init(&connection->req_lock); spin_lock_init(&connection->req_lock);
...@@ -2561,7 +2604,10 @@ struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts) ...@@ -2561,7 +2604,10 @@ struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts)
drbd_thread_init(connection, &connection->asender, drbd_asender, "asender"); drbd_thread_init(connection, &connection->asender, drbd_asender, "asender");
kref_init(&connection->kref); kref_init(&connection->kref);
list_add_tail_rcu(&connection->connections, &drbd_connections);
kref_get(&resource->kref);
connection->resource = resource;
list_add_tail_rcu(&connection->connections, &resource->connections);
return connection; return connection;
...@@ -2570,7 +2616,6 @@ struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts) ...@@ -2570,7 +2616,6 @@ struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts)
free_cpumask_var(connection->cpu_mask); free_cpumask_var(connection->cpu_mask);
drbd_free_socket(&connection->meta); drbd_free_socket(&connection->meta);
drbd_free_socket(&connection->data); drbd_free_socket(&connection->data);
kfree(connection->name);
kfree(connection); kfree(connection);
return NULL; return NULL;
...@@ -2579,6 +2624,7 @@ struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts) ...@@ -2579,6 +2624,7 @@ struct drbd_connection *conn_create(const char *name, struct res_opts *res_opts)
void drbd_destroy_connection(struct kref *kref) void drbd_destroy_connection(struct kref *kref)
{ {
struct drbd_connection *connection = container_of(kref, struct drbd_connection, kref); struct drbd_connection *connection = container_of(kref, struct drbd_connection, kref);
struct drbd_resource *resource = connection->resource;
if (atomic_read(&connection->current_epoch->epoch_size) != 0) if (atomic_read(&connection->current_epoch->epoch_size) != 0)
conn_err(connection, "epoch_size:%d\n", atomic_read(&connection->current_epoch->epoch_size)); conn_err(connection, "epoch_size:%d\n", atomic_read(&connection->current_epoch->epoch_size));
...@@ -2589,10 +2635,10 @@ void drbd_destroy_connection(struct kref *kref) ...@@ -2589,10 +2635,10 @@ void drbd_destroy_connection(struct kref *kref)
free_cpumask_var(connection->cpu_mask); free_cpumask_var(connection->cpu_mask);
drbd_free_socket(&connection->meta); drbd_free_socket(&connection->meta);
drbd_free_socket(&connection->data); drbd_free_socket(&connection->data);
kfree(connection->name);
kfree(connection->int_dig_in); kfree(connection->int_dig_in);
kfree(connection->int_dig_vv); kfree(connection->int_dig_vv);
kfree(connection); kfree(connection);
kref_put(&resource->kref, drbd_destroy_resource);
} }
static int init_submitter(struct drbd_device *device) static int init_submitter(struct drbd_device *device)
...@@ -2775,7 +2821,7 @@ int __init drbd_init(void) ...@@ -2775,7 +2821,7 @@ int __init drbd_init(void)
idr_init(&drbd_devices); idr_init(&drbd_devices);
rwlock_init(&global_state_lock); rwlock_init(&global_state_lock);
INIT_LIST_HEAD(&drbd_connections); INIT_LIST_HEAD(&drbd_resources);
err = drbd_genl_register(); err = drbd_genl_register();
if (err) { if (err) {
......
...@@ -249,7 +249,7 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info, ...@@ -249,7 +249,7 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
first_peer_device(adm_ctx.device)->connection != adm_ctx.connection) { 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", pr_warning("request: minor=%u, resource=%s; but that minor belongs to connection %s\n",
adm_ctx.minor, adm_ctx.resource_name, 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"); drbd_msg_put_info("minor exists in different resource");
return ERR_INVALID_REQUEST; return ERR_INVALID_REQUEST;
} }
...@@ -258,7 +258,8 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info, ...@@ -258,7 +258,8 @@ static int drbd_adm_prepare(struct sk_buff *skb, struct genl_info *info,
adm_ctx.volume != adm_ctx.device->vnr) { adm_ctx.volume != adm_ctx.device->vnr) {
pr_warning("request: minor=%u, volume=%u; but that minor is volume %u in %s\n", pr_warning("request: minor=%u, volume=%u; but that minor is volume %u in %s\n",
adm_ctx.minor, adm_ctx.volume, 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"); drbd_msg_put_info("minor exists as different volume");
return ERR_INVALID_REQUEST; return ERR_INVALID_REQUEST;
} }
...@@ -371,23 +372,24 @@ static int conn_khelper(struct drbd_connection *connection, char *cmd) ...@@ -371,23 +372,24 @@ static int conn_khelper(struct drbd_connection *connection, char *cmd)
(char[20]) { }, /* address family */ (char[20]) { }, /* address family */
(char[60]) { }, /* address */ (char[60]) { }, /* address */
NULL }; 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; int ret;
setup_khelper_env(connection, envp); setup_khelper_env(connection, envp);
conn_md_sync(connection); 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() ?? */ /* TODO: conn_bcast_event() ?? */
ret = call_usermodehelper(usermode_helper, argv, envp, UMH_WAIT_PROC); ret = call_usermodehelper(usermode_helper, argv, envp, UMH_WAIT_PROC);
if (ret) if (ret)
conn_warn(connection, "helper command: %s %s %s exit code %u (0x%x)\n", 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); (ret >> 8) & 0xff, ret);
else else
conn_info(connection, "helper command: %s %s %s exit code %u (0x%x)\n", 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); (ret >> 8) & 0xff, ret);
/* TODO: conn_bcast_event() ?? */ /* TODO: conn_bcast_event() ?? */
...@@ -2143,6 +2145,7 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info) ...@@ -2143,6 +2145,7 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info)
struct drbd_device *device; struct drbd_device *device;
struct net_conf *old_conf, *new_conf = NULL; struct net_conf *old_conf, *new_conf = NULL;
struct crypto crypto = { }; struct crypto crypto = { };
struct drbd_resource *resource;
struct drbd_connection *connection; struct drbd_connection *connection;
enum drbd_ret_code retcode; enum drbd_ret_code retcode;
int i; int i;
...@@ -2163,19 +2166,23 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info) ...@@ -2163,19 +2166,23 @@ int drbd_adm_connect(struct sk_buff *skb, struct genl_info *info)
/* No need for _rcu here. All reconfiguration is /* No need for _rcu here. All reconfiguration is
* strictly serialized on genl_lock(). We are protected against * strictly serialized on genl_lock(). We are protected against
* concurrent reconfiguration/addition/deletion */ * concurrent reconfiguration/addition/deletion */
list_for_each_entry(connection, &drbd_connections, connections) { for_each_resource(resource, &drbd_resources) {
for_each_connection(connection, resource) {
if (nla_len(adm_ctx.my_addr) == connection->my_addr_len && 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)) { !memcmp(nla_data(adm_ctx.my_addr), &connection->my_addr,
connection->my_addr_len)) {
retcode = ERR_LOCAL_ADDR; retcode = ERR_LOCAL_ADDR;
goto out; goto out;
} }
if (nla_len(adm_ctx.peer_addr) == connection->peer_addr_len && 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)) { !memcmp(nla_data(adm_ctx.peer_addr), &connection->peer_addr,
connection->peer_addr_len)) {
retcode = ERR_PEER_ADDR; retcode = ERR_PEER_ADDR;
goto out; goto out;
} }
} }
}
connection = adm_ctx.connection; connection = adm_ctx.connection;
conn_reconfig_start(connection); conn_reconfig_start(connection);
...@@ -2736,7 +2743,7 @@ static int nla_put_drbd_cfg_context(struct sk_buff *skb, struct drbd_connection ...@@ -2736,7 +2743,7 @@ static int nla_put_drbd_cfg_context(struct sk_buff *skb, struct drbd_connection
if (vnr != VOLUME_UNSPECIFIED && if (vnr != VOLUME_UNSPECIFIED &&
nla_put_u32(skb, T_ctx_volume, vnr)) nla_put_u32(skb, T_ctx_volume, vnr))
goto nla_put_failure; 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; goto nla_put_failure;
if (connection->my_addr_len && if (connection->my_addr_len &&
nla_put(skb, T_ctx_my_addr, connection->my_addr_len, &connection->my_addr)) nla_put(skb, T_ctx_my_addr, connection->my_addr_len, &connection->my_addr))
...@@ -2899,18 +2906,20 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2899,18 +2906,20 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb)
{ {
struct drbd_device *device; struct drbd_device *device;
struct drbd_genlmsghdr *dh; struct drbd_genlmsghdr *dh;
struct drbd_connection *pos = (struct drbd_connection *)cb->args[0]; struct drbd_resource *pos = (struct drbd_resource *)cb->args[0];
struct drbd_connection *connection = NULL; struct drbd_resource *resource = NULL;
struct drbd_connection *tmp; struct drbd_connection *connection;
struct drbd_resource *tmp;
unsigned volume = cb->args[1]; unsigned volume = cb->args[1];
/* Open coded, deferred, iteration: /* 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) { * 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]; * and i is cb->args[1];
* *
* cb->args[2] indicates if we shall loop over all resources, * cb->args[2] indicates if we shall loop over all resources,
...@@ -2927,36 +2936,37 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2927,36 +2936,37 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb)
/* synchronize with conn_create()/drbd_destroy_connection() */ /* synchronize with conn_create()/drbd_destroy_connection() */
rcu_read_lock(); rcu_read_lock();
/* revalidate iterator position */ /* revalidate iterator position */
list_for_each_entry_rcu(tmp, &drbd_connections, connections) { for_each_resource_rcu(tmp, &drbd_resources) {
if (pos == NULL) { if (pos == NULL) {
/* first iteration */ /* first iteration */
pos = tmp; pos = tmp;
connection = pos; resource = pos;
break; break;
} }
if (tmp == pos) { if (tmp == pos) {
connection = pos; resource = pos;
break; break;
} }
} }
if (connection) { if (resource) {
next_connection: next_resource:
connection = first_connection(resource);
device = idr_get_next(&connection->volumes, &volume); device = idr_get_next(&connection->volumes, &volume);
if (!device) { if (!device) {
/* No more volumes to dump on this connection. /* No more volumes to dump on this resource.
* Advance connection iterator. */ * Advance resource iterator. */
pos = list_entry_rcu(connection->connections.next, pos = list_entry_rcu(resource->resources.next,
struct drbd_connection, connections); struct drbd_resource, resources);
/* Did we dump any volume on this connection yet? */ /* Did we dump any volume of this resource yet? */
if (volume != 0) { if (volume != 0) {
/* If we reached the end of the list, /* If we reached the end of the list,
* or only a single resource dump was requested, * or only a single resource dump was requested,
* we are done. */ * we are done. */
if (&pos->connections == &drbd_connections || cb->args[2]) if (&pos->resources == &drbd_resources || cb->args[2])
goto out; goto out;
volume = 0; volume = 0;
connection = pos; resource = pos;
goto next_connection; goto next_resource;
} }
} }
...@@ -3000,9 +3010,9 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -3000,9 +3010,9 @@ static int get_one_status(struct sk_buff *skb, struct netlink_callback *cb)
rcu_read_unlock(); rcu_read_unlock();
/* where to start the next iteration */ /* where to start the next iteration */
cb->args[0] = (long)pos; 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. */ * Which will terminate the dump. */
return skb->len; return skb->len;
} }
...@@ -3399,9 +3409,11 @@ int drbd_adm_down(struct sk_buff *skb, struct genl_info *info) ...@@ -3399,9 +3409,11 @@ int drbd_adm_down(struct sk_buff *skb, struct genl_info *info)
/* delete connection */ /* delete connection */
if (conn_lowest_minor(adm_ctx.connection) < 0) { 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(); synchronize_rcu();
kref_put(&adm_ctx.connection->kref, drbd_destroy_connection); drbd_free_resource(resource);
retcode = NO_ERROR; retcode = NO_ERROR;
} else { } else {
...@@ -3417,6 +3429,8 @@ int drbd_adm_down(struct sk_buff *skb, struct genl_info *info) ...@@ -3417,6 +3429,8 @@ int drbd_adm_down(struct sk_buff *skb, struct genl_info *info)
int drbd_adm_del_resource(struct sk_buff *skb, struct genl_info *info) 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; enum drbd_ret_code retcode;
retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE); retcode = drbd_adm_prepare(skb, info, DRBD_ADM_NEED_RESOURCE);
...@@ -3425,18 +3439,24 @@ int drbd_adm_del_resource(struct sk_buff *skb, struct genl_info *info) ...@@ -3425,18 +3439,24 @@ int drbd_adm_del_resource(struct sk_buff *skb, struct genl_info *info)
if (retcode != NO_ERROR) if (retcode != NO_ERROR)
goto out; goto out;
if (conn_lowest_minor(adm_ctx.connection) < 0) { resource = adm_ctx.resource;
list_del_rcu(&adm_ctx.connection->connections); for_each_connection(connection, resource) {
synchronize_rcu(); if (connection->cstate > C_STANDALONE) {
kref_put(&adm_ctx.connection->kref, drbd_destroy_connection); retcode = ERR_NET_CONFIGURED;
goto out;
retcode = NO_ERROR; }
} else { }
if (!idr_is_empty(&resource->devices)) {
retcode = ERR_RES_IN_USE; retcode = ERR_RES_IN_USE;
goto out;
} }
if (retcode == NO_ERROR) list_del_rcu(&resource->resources);
drbd_thread_stop(&adm_ctx.connection->worker); for_each_connection(connection, resource)
drbd_thread_stop(&connection->worker);
synchronize_rcu();
drbd_free_resource(resource);
retcode = NO_ERROR;
out: out:
drbd_adm_finish(info, retcode); drbd_adm_finish(info, retcode);
return 0; return 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment