diff options
author | Ilya Dryomov <idryomov@gmail.com> | 2017-04-13 06:17:38 -0400 |
---|---|---|
committer | Ilya Dryomov <idryomov@gmail.com> | 2017-05-04 03:19:23 -0400 |
commit | 5769ed0cb12dcd135251e546863196cec0b58e34 (patch) | |
tree | cb030108daaddd6f624152df7e99fd6c20a33385 | |
parent | fd22aef8b47cfc068448df65c1183698b0abd815 (diff) |
rbd: fix error handling around rbd_init_disk()
add_disk() takes an extra reference on disk->queue, which is put in
put_disk() -> disk_release(). Avoiding blk_cleanup_queue() (which also
puts the queue) until add_disk() sets GENHD_FL_UP works for the queue
itself, but leaks various queue internals. Conditioning tag_set freeing
on GENHD_FL_UP is wrong too: all error paths after rbd_init_disk() leak
the tag_set.
Move the final "announce" steps out of rbd_dev_device_setup() so that
it can be unwound like any other function. Leave "announce" steps to
do_rbd_add/remove().
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
Reviewed-by: Jason Dillaman <dillaman@redhat.com>
-rw-r--r-- | drivers/block/rbd.c | 87 |
1 files changed, 44 insertions, 43 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c index b299ed0315f8..50395af7a9a6 100644 --- a/drivers/block/rbd.c +++ b/drivers/block/rbd.c | |||
@@ -4114,19 +4114,10 @@ static int rbd_queue_rq(struct blk_mq_hw_ctx *hctx, | |||
4114 | 4114 | ||
4115 | static void rbd_free_disk(struct rbd_device *rbd_dev) | 4115 | static void rbd_free_disk(struct rbd_device *rbd_dev) |
4116 | { | 4116 | { |
4117 | struct gendisk *disk = rbd_dev->disk; | 4117 | blk_cleanup_queue(rbd_dev->disk->queue); |
4118 | 4118 | blk_mq_free_tag_set(&rbd_dev->tag_set); | |
4119 | if (!disk) | 4119 | put_disk(rbd_dev->disk); |
4120 | return; | ||
4121 | |||
4122 | rbd_dev->disk = NULL; | 4120 | rbd_dev->disk = NULL; |
4123 | if (disk->flags & GENHD_FL_UP) { | ||
4124 | del_gendisk(disk); | ||
4125 | if (disk->queue) | ||
4126 | blk_cleanup_queue(disk->queue); | ||
4127 | blk_mq_free_tag_set(&rbd_dev->tag_set); | ||
4128 | } | ||
4129 | put_disk(disk); | ||
4130 | } | 4121 | } |
4131 | 4122 | ||
4132 | static int rbd_obj_read_sync(struct rbd_device *rbd_dev, | 4123 | static int rbd_obj_read_sync(struct rbd_device *rbd_dev, |
@@ -4385,8 +4376,12 @@ static int rbd_init_disk(struct rbd_device *rbd_dev) | |||
4385 | if (!ceph_test_opt(rbd_dev->rbd_client->client, NOCRC)) | 4376 | if (!ceph_test_opt(rbd_dev->rbd_client->client, NOCRC)) |
4386 | q->backing_dev_info->capabilities |= BDI_CAP_STABLE_WRITES; | 4377 | q->backing_dev_info->capabilities |= BDI_CAP_STABLE_WRITES; |
4387 | 4378 | ||
4379 | /* | ||
4380 | * disk_release() expects a queue ref from add_disk() and will | ||
4381 | * put it. Hold an extra ref until add_disk() is called. | ||
4382 | */ | ||
4383 | WARN_ON(!blk_get_queue(q)); | ||
4388 | disk->queue = q; | 4384 | disk->queue = q; |
4389 | |||
4390 | q->queuedata = rbd_dev; | 4385 | q->queuedata = rbd_dev; |
4391 | 4386 | ||
4392 | rbd_dev->disk = disk; | 4387 | rbd_dev->disk = disk; |
@@ -5875,6 +5870,15 @@ out_err: | |||
5875 | return ret; | 5870 | return ret; |
5876 | } | 5871 | } |
5877 | 5872 | ||
5873 | static void rbd_dev_device_release(struct rbd_device *rbd_dev) | ||
5874 | { | ||
5875 | clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags); | ||
5876 | rbd_dev_mapping_clear(rbd_dev); | ||
5877 | rbd_free_disk(rbd_dev); | ||
5878 | if (!single_major) | ||
5879 | unregister_blkdev(rbd_dev->major, rbd_dev->name); | ||
5880 | } | ||
5881 | |||
5878 | /* | 5882 | /* |
5879 | * rbd_dev->header_rwsem must be locked for write and will be unlocked | 5883 | * rbd_dev->header_rwsem must be locked for write and will be unlocked |
5880 | * upon return. | 5884 | * upon return. |
@@ -5910,26 +5914,13 @@ static int rbd_dev_device_setup(struct rbd_device *rbd_dev) | |||
5910 | set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE); | 5914 | set_capacity(rbd_dev->disk, rbd_dev->mapping.size / SECTOR_SIZE); |
5911 | set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only); | 5915 | set_disk_ro(rbd_dev->disk, rbd_dev->mapping.read_only); |
5912 | 5916 | ||
5913 | dev_set_name(&rbd_dev->dev, "%d", rbd_dev->dev_id); | 5917 | ret = dev_set_name(&rbd_dev->dev, "%d", rbd_dev->dev_id); |
5914 | ret = device_add(&rbd_dev->dev); | ||
5915 | if (ret) | 5918 | if (ret) |
5916 | goto err_out_mapping; | 5919 | goto err_out_mapping; |
5917 | 5920 | ||
5918 | /* Everything's ready. Announce the disk to the world. */ | ||
5919 | |||
5920 | set_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags); | 5921 | set_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags); |
5921 | up_write(&rbd_dev->header_rwsem); | 5922 | up_write(&rbd_dev->header_rwsem); |
5922 | 5923 | return 0; | |
5923 | spin_lock(&rbd_dev_list_lock); | ||
5924 | list_add_tail(&rbd_dev->node, &rbd_dev_list); | ||
5925 | spin_unlock(&rbd_dev_list_lock); | ||
5926 | |||
5927 | add_disk(rbd_dev->disk); | ||
5928 | pr_info("%s: capacity %llu features 0x%llx\n", rbd_dev->disk->disk_name, | ||
5929 | (unsigned long long)get_capacity(rbd_dev->disk) << SECTOR_SHIFT, | ||
5930 | rbd_dev->header.features); | ||
5931 | |||
5932 | return ret; | ||
5933 | 5924 | ||
5934 | err_out_mapping: | 5925 | err_out_mapping: |
5935 | rbd_dev_mapping_clear(rbd_dev); | 5926 | rbd_dev_mapping_clear(rbd_dev); |
@@ -6131,11 +6122,30 @@ static ssize_t do_rbd_add(struct bus_type *bus, | |||
6131 | if (rc) | 6122 | if (rc) |
6132 | goto err_out_image_probe; | 6123 | goto err_out_image_probe; |
6133 | 6124 | ||
6125 | /* Everything's ready. Announce the disk to the world. */ | ||
6126 | |||
6127 | rc = device_add(&rbd_dev->dev); | ||
6128 | if (rc) | ||
6129 | goto err_out_device_setup; | ||
6130 | |||
6131 | add_disk(rbd_dev->disk); | ||
6132 | /* see rbd_init_disk() */ | ||
6133 | blk_put_queue(rbd_dev->disk->queue); | ||
6134 | |||
6135 | spin_lock(&rbd_dev_list_lock); | ||
6136 | list_add_tail(&rbd_dev->node, &rbd_dev_list); | ||
6137 | spin_unlock(&rbd_dev_list_lock); | ||
6138 | |||
6139 | pr_info("%s: capacity %llu features 0x%llx\n", rbd_dev->disk->disk_name, | ||
6140 | (unsigned long long)get_capacity(rbd_dev->disk) << SECTOR_SHIFT, | ||
6141 | rbd_dev->header.features); | ||
6134 | rc = count; | 6142 | rc = count; |
6135 | out: | 6143 | out: |
6136 | module_put(THIS_MODULE); | 6144 | module_put(THIS_MODULE); |
6137 | return rc; | 6145 | return rc; |
6138 | 6146 | ||
6147 | err_out_device_setup: | ||
6148 | rbd_dev_device_release(rbd_dev); | ||
6139 | err_out_image_probe: | 6149 | err_out_image_probe: |
6140 | rbd_dev_image_release(rbd_dev); | 6150 | rbd_dev_image_release(rbd_dev); |
6141 | err_out_rbd_dev: | 6151 | err_out_rbd_dev: |
@@ -6165,21 +6175,6 @@ static ssize_t rbd_add_single_major(struct bus_type *bus, | |||
6165 | return do_rbd_add(bus, buf, count); | 6175 | return do_rbd_add(bus, buf, count); |
6166 | } | 6176 | } |
6167 | 6177 | ||
6168 | static void rbd_dev_device_release(struct rbd_device *rbd_dev) | ||
6169 | { | ||
6170 | rbd_free_disk(rbd_dev); | ||
6171 | |||
6172 | spin_lock(&rbd_dev_list_lock); | ||
6173 | list_del_init(&rbd_dev->node); | ||
6174 | spin_unlock(&rbd_dev_list_lock); | ||
6175 | |||
6176 | clear_bit(RBD_DEV_FLAG_EXISTS, &rbd_dev->flags); | ||
6177 | device_del(&rbd_dev->dev); | ||
6178 | rbd_dev_mapping_clear(rbd_dev); | ||
6179 | if (!single_major) | ||
6180 | unregister_blkdev(rbd_dev->major, rbd_dev->name); | ||
6181 | } | ||
6182 | |||
6183 | static void rbd_dev_remove_parent(struct rbd_device *rbd_dev) | 6178 | static void rbd_dev_remove_parent(struct rbd_device *rbd_dev) |
6184 | { | 6179 | { |
6185 | while (rbd_dev->parent) { | 6180 | while (rbd_dev->parent) { |
@@ -6266,6 +6261,12 @@ static ssize_t do_rbd_remove(struct bus_type *bus, | |||
6266 | blk_set_queue_dying(rbd_dev->disk->queue); | 6261 | blk_set_queue_dying(rbd_dev->disk->queue); |
6267 | } | 6262 | } |
6268 | 6263 | ||
6264 | del_gendisk(rbd_dev->disk); | ||
6265 | spin_lock(&rbd_dev_list_lock); | ||
6266 | list_del_init(&rbd_dev->node); | ||
6267 | spin_unlock(&rbd_dev_list_lock); | ||
6268 | device_del(&rbd_dev->dev); | ||
6269 | |||
6269 | down_write(&rbd_dev->lock_rwsem); | 6270 | down_write(&rbd_dev->lock_rwsem); |
6270 | if (__rbd_is_lock_owner(rbd_dev)) | 6271 | if (__rbd_is_lock_owner(rbd_dev)) |
6271 | rbd_unlock(rbd_dev); | 6272 | rbd_unlock(rbd_dev); |