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); |
