aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Elder <elder@inktank.com>2013-01-14 13:43:31 -0500
committerSage Weil <sage@inktank.com>2013-02-13 21:29:10 -0500
commitb82d167be64b3e88d9434d8a98ce83c83a07aa48 (patch)
tree45f23409f8412ad4c536b1002553a8f8dd7a5081
parent6d292906f80170f4647079dd503df18b737750af (diff)
rbd: prevent open for image being removed
An open request for a mapped rbd image can arrive while removal of that mapping is underway. We need to prevent such an open request from succeeding. (It appears that Maciej Galkiewicz ran into this problem.) Define and use a "removing" flag to indicate a mapping is getting removed. Set it in the remove path after verifying nothing holds the device open. And check it in the open path before allowing the open to proceed. Acquire the rbd device's lock around each of these spots to avoid any races accessing the flags and open_count fields. This addresses: http://tracker.newdream.net/issues/3427 Reported-by: Maciej Galkiewicz <maciejgalkiewicz@ragnarson.com> Signed-off-by: Alex Elder <elder@inktank.com> Reviewed-by: Josh Durgin <josh.durgin@inktank.com>
-rw-r--r--drivers/block/rbd.c43
1 files changed, 33 insertions, 10 deletions
diff --git a/drivers/block/rbd.c b/drivers/block/rbd.c
index 8c90a39c2a91..ed0c91d81063 100644
--- a/drivers/block/rbd.c
+++ b/drivers/block/rbd.c
@@ -261,10 +261,10 @@ struct rbd_device {
261 261
262 char name[DEV_NAME_LEN]; /* blkdev name, e.g. rbd3 */ 262 char name[DEV_NAME_LEN]; /* blkdev name, e.g. rbd3 */
263 263
264 spinlock_t lock; /* queue lock */ 264 spinlock_t lock; /* queue, flags, open_count */
265 265
266 struct rbd_image_header header; 266 struct rbd_image_header header;
267 unsigned long flags; 267 unsigned long flags; /* possibly lock protected */
268 struct rbd_spec *spec; 268 struct rbd_spec *spec;
269 269
270 char *header_name; 270 char *header_name;
@@ -289,13 +289,19 @@ struct rbd_device {
289 289
290 /* sysfs related */ 290 /* sysfs related */
291 struct device dev; 291 struct device dev;
292 unsigned long open_count; 292 unsigned long open_count; /* protected by lock */
293}; 293};
294 294
295/* Flag bits for rbd_dev->flags */ 295/*
296 296 * Flag bits for rbd_dev->flags. If atomicity is required,
297 * rbd_dev->lock is used to protect access.
298 *
299 * Currently, only the "removing" flag (which is coupled with the
300 * "open_count" field) requires atomic access.
301 */
297enum rbd_dev_flags { 302enum rbd_dev_flags {
298 RBD_DEV_FLAG_EXISTS, /* mapped snapshot has not been deleted */ 303 RBD_DEV_FLAG_EXISTS, /* mapped snapshot has not been deleted */
304 RBD_DEV_FLAG_REMOVING, /* this mapping is being removed */
299}; 305};
300 306
301static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */ 307static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */
@@ -383,14 +389,23 @@ static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev, u64 *hver);
383static int rbd_open(struct block_device *bdev, fmode_t mode) 389static int rbd_open(struct block_device *bdev, fmode_t mode)
384{ 390{
385 struct rbd_device *rbd_dev = bdev->bd_disk->private_data; 391 struct rbd_device *rbd_dev = bdev->bd_disk->private_data;
392 bool removing = false;
386 393
387 if ((mode & FMODE_WRITE) && rbd_dev->mapping.read_only) 394 if ((mode & FMODE_WRITE) && rbd_dev->mapping.read_only)
388 return -EROFS; 395 return -EROFS;
389 396
397 spin_lock(&rbd_dev->lock);
398 if (test_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags))
399 removing = true;
400 else
401 rbd_dev->open_count++;
402 spin_unlock(&rbd_dev->lock);
403 if (removing)
404 return -ENOENT;
405
390 mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); 406 mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
391 (void) get_device(&rbd_dev->dev); 407 (void) get_device(&rbd_dev->dev);
392 set_device_ro(bdev, rbd_dev->mapping.read_only); 408 set_device_ro(bdev, rbd_dev->mapping.read_only);
393 rbd_dev->open_count++;
394 mutex_unlock(&ctl_mutex); 409 mutex_unlock(&ctl_mutex);
395 410
396 return 0; 411 return 0;
@@ -399,10 +414,14 @@ static int rbd_open(struct block_device *bdev, fmode_t mode)
399static int rbd_release(struct gendisk *disk, fmode_t mode) 414static int rbd_release(struct gendisk *disk, fmode_t mode)
400{ 415{
401 struct rbd_device *rbd_dev = disk->private_data; 416 struct rbd_device *rbd_dev = disk->private_data;
417 unsigned long open_count_before;
418
419 spin_lock(&rbd_dev->lock);
420 open_count_before = rbd_dev->open_count--;
421 spin_unlock(&rbd_dev->lock);
422 rbd_assert(open_count_before > 0);
402 423
403 mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); 424 mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING);
404 rbd_assert(rbd_dev->open_count > 0);
405 rbd_dev->open_count--;
406 put_device(&rbd_dev->dev); 425 put_device(&rbd_dev->dev);
407 mutex_unlock(&ctl_mutex); 426 mutex_unlock(&ctl_mutex);
408 427
@@ -4083,10 +4102,14 @@ static ssize_t rbd_remove(struct bus_type *bus,
4083 goto done; 4102 goto done;
4084 } 4103 }
4085 4104
4086 if (rbd_dev->open_count) { 4105 spin_lock(&rbd_dev->lock);
4106 if (rbd_dev->open_count)
4087 ret = -EBUSY; 4107 ret = -EBUSY;
4108 else
4109 set_bit(RBD_DEV_FLAG_REMOVING, &rbd_dev->flags);
4110 spin_unlock(&rbd_dev->lock);
4111 if (ret < 0)
4088 goto done; 4112 goto done;
4089 }
4090 4113
4091 rbd_remove_all_snaps(rbd_dev); 4114 rbd_remove_all_snaps(rbd_dev);
4092 rbd_bus_del_dev(rbd_dev); 4115 rbd_bus_del_dev(rbd_dev);