diff options
author | Mikulas Patocka <mpatocka@redhat.com> | 2013-11-01 18:27:41 -0400 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2013-11-09 18:20:22 -0500 |
commit | 2c140a246dc0bc085b98eddde978060fcec1080c (patch) | |
tree | 73d747f4422fe202868495d08ea5d189bc16bd79 /drivers/md/dm.c | |
parent | 7833b08e18241a1c35c09ef38be840cbf6c58acf (diff) |
dm: allow remove to be deferred
This patch allows the removal of an open device to be deferred until
it is closed. (Previously such a removal attempt would fail.)
The deferred remove functionality is enabled by setting the flag
DM_DEFERRED_REMOVE in the ioctl structure on DM_DEV_REMOVE or
DM_REMOVE_ALL ioctl.
On return from DM_DEV_REMOVE, the flag DM_DEFERRED_REMOVE indicates if
the device was removed immediately or flagged to be removed on close -
if the flag is clear, the device was removed.
On return from DM_DEV_STATUS and other ioctls, the flag
DM_DEFERRED_REMOVE is set if the device is scheduled to be removed on
closure.
A device that is scheduled to be deleted can be revived using the
message "@cancel_deferred_remove". This message clears the
DMF_DEFERRED_REMOVE flag so that the device won't be deleted on close.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Diffstat (limited to 'drivers/md/dm.c')
-rw-r--r-- | drivers/md/dm.c | 47 |
1 files changed, 44 insertions, 3 deletions
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index b3e26c7d1417..0704c523a76b 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c | |||
@@ -49,6 +49,11 @@ static unsigned int _major = 0; | |||
49 | static DEFINE_IDR(_minor_idr); | 49 | static DEFINE_IDR(_minor_idr); |
50 | 50 | ||
51 | static DEFINE_SPINLOCK(_minor_lock); | 51 | static DEFINE_SPINLOCK(_minor_lock); |
52 | |||
53 | static void do_deferred_remove(struct work_struct *w); | ||
54 | |||
55 | static DECLARE_WORK(deferred_remove_work, do_deferred_remove); | ||
56 | |||
52 | /* | 57 | /* |
53 | * For bio-based dm. | 58 | * For bio-based dm. |
54 | * One of these is allocated per bio. | 59 | * One of these is allocated per bio. |
@@ -116,6 +121,7 @@ EXPORT_SYMBOL_GPL(dm_get_rq_mapinfo); | |||
116 | #define DMF_DELETING 4 | 121 | #define DMF_DELETING 4 |
117 | #define DMF_NOFLUSH_SUSPENDING 5 | 122 | #define DMF_NOFLUSH_SUSPENDING 5 |
118 | #define DMF_MERGE_IS_OPTIONAL 6 | 123 | #define DMF_MERGE_IS_OPTIONAL 6 |
124 | #define DMF_DEFERRED_REMOVE 7 | ||
119 | 125 | ||
120 | /* | 126 | /* |
121 | * A dummy definition to make RCU happy. | 127 | * A dummy definition to make RCU happy. |
@@ -299,6 +305,8 @@ out_free_io_cache: | |||
299 | 305 | ||
300 | static void local_exit(void) | 306 | static void local_exit(void) |
301 | { | 307 | { |
308 | flush_scheduled_work(); | ||
309 | |||
302 | kmem_cache_destroy(_rq_tio_cache); | 310 | kmem_cache_destroy(_rq_tio_cache); |
303 | kmem_cache_destroy(_io_cache); | 311 | kmem_cache_destroy(_io_cache); |
304 | unregister_blkdev(_major, _name); | 312 | unregister_blkdev(_major, _name); |
@@ -404,7 +412,10 @@ static void dm_blk_close(struct gendisk *disk, fmode_t mode) | |||
404 | 412 | ||
405 | spin_lock(&_minor_lock); | 413 | spin_lock(&_minor_lock); |
406 | 414 | ||
407 | atomic_dec(&md->open_count); | 415 | if (atomic_dec_and_test(&md->open_count) && |
416 | (test_bit(DMF_DEFERRED_REMOVE, &md->flags))) | ||
417 | schedule_work(&deferred_remove_work); | ||
418 | |||
408 | dm_put(md); | 419 | dm_put(md); |
409 | 420 | ||
410 | spin_unlock(&_minor_lock); | 421 | spin_unlock(&_minor_lock); |
@@ -418,14 +429,18 @@ int dm_open_count(struct mapped_device *md) | |||
418 | /* | 429 | /* |
419 | * Guarantees nothing is using the device before it's deleted. | 430 | * Guarantees nothing is using the device before it's deleted. |
420 | */ | 431 | */ |
421 | int dm_lock_for_deletion(struct mapped_device *md) | 432 | int dm_lock_for_deletion(struct mapped_device *md, bool mark_deferred, bool only_deferred) |
422 | { | 433 | { |
423 | int r = 0; | 434 | int r = 0; |
424 | 435 | ||
425 | spin_lock(&_minor_lock); | 436 | spin_lock(&_minor_lock); |
426 | 437 | ||
427 | if (dm_open_count(md)) | 438 | if (dm_open_count(md)) { |
428 | r = -EBUSY; | 439 | r = -EBUSY; |
440 | if (mark_deferred) | ||
441 | set_bit(DMF_DEFERRED_REMOVE, &md->flags); | ||
442 | } else if (only_deferred && !test_bit(DMF_DEFERRED_REMOVE, &md->flags)) | ||
443 | r = -EEXIST; | ||
429 | else | 444 | else |
430 | set_bit(DMF_DELETING, &md->flags); | 445 | set_bit(DMF_DELETING, &md->flags); |
431 | 446 | ||
@@ -434,6 +449,27 @@ int dm_lock_for_deletion(struct mapped_device *md) | |||
434 | return r; | 449 | return r; |
435 | } | 450 | } |
436 | 451 | ||
452 | int dm_cancel_deferred_remove(struct mapped_device *md) | ||
453 | { | ||
454 | int r = 0; | ||
455 | |||
456 | spin_lock(&_minor_lock); | ||
457 | |||
458 | if (test_bit(DMF_DELETING, &md->flags)) | ||
459 | r = -EBUSY; | ||
460 | else | ||
461 | clear_bit(DMF_DEFERRED_REMOVE, &md->flags); | ||
462 | |||
463 | spin_unlock(&_minor_lock); | ||
464 | |||
465 | return r; | ||
466 | } | ||
467 | |||
468 | static void do_deferred_remove(struct work_struct *w) | ||
469 | { | ||
470 | dm_deferred_remove(); | ||
471 | } | ||
472 | |||
437 | sector_t dm_get_size(struct mapped_device *md) | 473 | sector_t dm_get_size(struct mapped_device *md) |
438 | { | 474 | { |
439 | return get_capacity(md->disk); | 475 | return get_capacity(md->disk); |
@@ -2894,6 +2930,11 @@ int dm_suspended_md(struct mapped_device *md) | |||
2894 | return test_bit(DMF_SUSPENDED, &md->flags); | 2930 | return test_bit(DMF_SUSPENDED, &md->flags); |
2895 | } | 2931 | } |
2896 | 2932 | ||
2933 | int dm_test_deferred_remove_flag(struct mapped_device *md) | ||
2934 | { | ||
2935 | return test_bit(DMF_DEFERRED_REMOVE, &md->flags); | ||
2936 | } | ||
2937 | |||
2897 | int dm_suspended(struct dm_target *ti) | 2938 | int dm_suspended(struct dm_target *ti) |
2898 | { | 2939 | { |
2899 | return dm_suspended_md(dm_table_get_md(ti->table)); | 2940 | return dm_suspended_md(dm_table_get_md(ti->table)); |