diff --git a/drivers/block/drbd/drbd_main.c b/drivers/block/drbd/drbd_main.c index b00a8d74f6cb..54df98fa2881 100644 --- a/drivers/block/drbd/drbd_main.c +++ b/drivers/block/drbd/drbd_main.c @@ -2661,9 +2661,9 @@ static int init_submitter(struct drbd_device *device) enum drbd_ret_code drbd_create_device(struct drbd_resource *resource, unsigned int minor, int vnr) { - struct drbd_connection *connection = first_connection(resource); + struct drbd_connection *connection; struct drbd_device *device; - struct drbd_peer_device *peer_device; + struct drbd_peer_device *peer_device, *tmp_peer_device; struct gendisk *disk; struct request_queue *q; int id; @@ -2679,18 +2679,8 @@ enum drbd_ret_code drbd_create_device(struct drbd_resource *resource, unsigned i return ERR_NOMEM; kref_init(&device->kref); - peer_device = kzalloc(sizeof(struct drbd_peer_device), GFP_KERNEL); - if (!peer_device) - goto out_no_peer_device; - - INIT_LIST_HEAD(&device->peer_devices); - list_add(&peer_device->peer_devices, &device->peer_devices); kref_get(&resource->kref); device->resource = resource; - kref_get(&connection->kref); - peer_device->connection = connection; - peer_device->device = device; - device->minor = minor; device->vnr = vnr; @@ -2761,15 +2751,27 @@ enum drbd_ret_code drbd_create_device(struct drbd_resource *resource, unsigned i } kref_get(&device->kref); - id = idr_alloc(&connection->peer_devices, peer_device, vnr, vnr + 1, GFP_KERNEL); - if (id < 0) { - if (id == -ENOSPC) { - err = ERR_INVALID_REQUEST; - drbd_msg_put_info("requested volume exists already"); + INIT_LIST_HEAD(&device->peer_devices); + for_each_connection(connection, resource) { + peer_device = kzalloc(sizeof(struct drbd_peer_device), GFP_KERNEL); + if (!peer_device) + goto out_idr_remove_from_resource; + peer_device->connection = connection; + peer_device->device = device; + + list_add(&peer_device->peer_devices, &device->peer_devices); + kref_get(&device->kref); + + id = idr_alloc(&connection->peer_devices, peer_device, vnr, vnr + 1, GFP_KERNEL); + if (id < 0) { + if (id == -ENOSPC) { + err = ERR_INVALID_REQUEST; + drbd_msg_put_info("requested volume exists already"); + } + goto out_idr_remove_from_resource; } - goto out_idr_remove_from_resource; + kref_get(&connection->kref); } - kref_get(&device->kref); if (init_submitter(device)) { err = ERR_NOMEM; @@ -2780,7 +2782,7 @@ enum drbd_ret_code drbd_create_device(struct drbd_resource *resource, unsigned i add_disk(disk); /* inherit the connection state */ - device->state.conn = connection->cstate; + device->state.conn = first_connection(resource)->cstate; if (device->state.conn == C_WF_REPORT_PARAMS) drbd_connected(device); @@ -2789,6 +2791,17 @@ enum drbd_ret_code drbd_create_device(struct drbd_resource *resource, unsigned i out_idr_remove_vol: idr_remove(&connection->peer_devices, vnr); out_idr_remove_from_resource: + for_each_connection(connection, resource) { + peer_device = idr_find(&connection->peer_devices, vnr); + if (peer_device) { + idr_remove(&connection->peer_devices, vnr); + kref_put(&connection->kref, drbd_destroy_connection); + } + } + for_each_peer_device_safe(peer_device, tmp_peer_device, device) { + list_del(&peer_device->peer_devices); + kfree(peer_device); + } idr_remove(&resource->devices, vnr); out_idr_remove_minor: idr_remove(&drbd_devices, minor); @@ -2802,9 +2815,7 @@ enum drbd_ret_code drbd_create_device(struct drbd_resource *resource, unsigned i out_no_disk: blk_cleanup_queue(q); out_no_q: - kref_put(&connection->kref, drbd_destroy_connection); kref_put(&resource->kref, drbd_destroy_resource); -out_no_peer_device: kfree(device); return err; } diff --git a/drivers/block/drbd/drbd_nl.c b/drivers/block/drbd/drbd_nl.c index 33bc23fc55bb..8ebcf88b0c1b 100644 --- a/drivers/block/drbd/drbd_nl.c +++ b/drivers/block/drbd/drbd_nl.c @@ -560,8 +560,16 @@ drbd_set_role(struct drbd_device *device, enum drbd_role new_role, int force) 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); @@ -3387,8 +3395,10 @@ int drbd_adm_del_minor(struct sk_buff *skb, struct genl_info *info) 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); @@ -3397,24 +3407,29 @@ int drbd_adm_down(struct sk_buff *skb, struct genl_info *info) 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; @@ -3424,13 +3439,14 @@ int drbd_adm_down(struct sk_buff *skb, struct genl_info *info) /* 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"); @@ -3438,21 +3454,11 @@ int drbd_adm_down(struct sk_buff *skb, struct genl_info *info) } } - /* 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); + retcode = NO_ERROR; - list_del_rcu(&resource->resources); - synchronize_rcu(); - drbd_free_resource(resource); - - 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;