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 | |
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>
-rw-r--r-- | drivers/md/dm-ioctl.c | 36 | ||||
-rw-r--r-- | drivers/md/dm.c | 47 | ||||
-rw-r--r-- | drivers/md/dm.h | 13 | ||||
-rw-r--r-- | include/uapi/linux/dm-ioctl.h | 15 |
4 files changed, 99 insertions, 12 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index afe08146f73e..51521429fb59 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c | |||
@@ -57,7 +57,7 @@ struct vers_iter { | |||
57 | static struct list_head _name_buckets[NUM_BUCKETS]; | 57 | static struct list_head _name_buckets[NUM_BUCKETS]; |
58 | static struct list_head _uuid_buckets[NUM_BUCKETS]; | 58 | static struct list_head _uuid_buckets[NUM_BUCKETS]; |
59 | 59 | ||
60 | static void dm_hash_remove_all(int keep_open_devices); | 60 | static void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred); |
61 | 61 | ||
62 | /* | 62 | /* |
63 | * Guards access to both hash tables. | 63 | * Guards access to both hash tables. |
@@ -86,7 +86,7 @@ static int dm_hash_init(void) | |||
86 | 86 | ||
87 | static void dm_hash_exit(void) | 87 | static void dm_hash_exit(void) |
88 | { | 88 | { |
89 | dm_hash_remove_all(0); | 89 | dm_hash_remove_all(false, false, false); |
90 | } | 90 | } |
91 | 91 | ||
92 | /*----------------------------------------------------------------- | 92 | /*----------------------------------------------------------------- |
@@ -276,7 +276,7 @@ static struct dm_table *__hash_remove(struct hash_cell *hc) | |||
276 | return table; | 276 | return table; |
277 | } | 277 | } |
278 | 278 | ||
279 | static void dm_hash_remove_all(int keep_open_devices) | 279 | static void dm_hash_remove_all(bool keep_open_devices, bool mark_deferred, bool only_deferred) |
280 | { | 280 | { |
281 | int i, dev_skipped; | 281 | int i, dev_skipped; |
282 | struct hash_cell *hc; | 282 | struct hash_cell *hc; |
@@ -293,7 +293,8 @@ retry: | |||
293 | md = hc->md; | 293 | md = hc->md; |
294 | dm_get(md); | 294 | dm_get(md); |
295 | 295 | ||
296 | if (keep_open_devices && dm_lock_for_deletion(md)) { | 296 | if (keep_open_devices && |
297 | dm_lock_for_deletion(md, mark_deferred, only_deferred)) { | ||
297 | dm_put(md); | 298 | dm_put(md); |
298 | dev_skipped++; | 299 | dev_skipped++; |
299 | continue; | 300 | continue; |
@@ -450,6 +451,11 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, | |||
450 | return md; | 451 | return md; |
451 | } | 452 | } |
452 | 453 | ||
454 | void dm_deferred_remove(void) | ||
455 | { | ||
456 | dm_hash_remove_all(true, false, true); | ||
457 | } | ||
458 | |||
453 | /*----------------------------------------------------------------- | 459 | /*----------------------------------------------------------------- |
454 | * Implementation of the ioctl commands | 460 | * Implementation of the ioctl commands |
455 | *---------------------------------------------------------------*/ | 461 | *---------------------------------------------------------------*/ |
@@ -461,7 +467,7 @@ typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size); | |||
461 | 467 | ||
462 | static int remove_all(struct dm_ioctl *param, size_t param_size) | 468 | static int remove_all(struct dm_ioctl *param, size_t param_size) |
463 | { | 469 | { |
464 | dm_hash_remove_all(1); | 470 | dm_hash_remove_all(true, !!(param->flags & DM_DEFERRED_REMOVE), false); |
465 | param->data_size = 0; | 471 | param->data_size = 0; |
466 | return 0; | 472 | return 0; |
467 | } | 473 | } |
@@ -683,6 +689,9 @@ static void __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
683 | if (dm_suspended_md(md)) | 689 | if (dm_suspended_md(md)) |
684 | param->flags |= DM_SUSPEND_FLAG; | 690 | param->flags |= DM_SUSPEND_FLAG; |
685 | 691 | ||
692 | if (dm_test_deferred_remove_flag(md)) | ||
693 | param->flags |= DM_DEFERRED_REMOVE; | ||
694 | |||
686 | param->dev = huge_encode_dev(disk_devt(disk)); | 695 | param->dev = huge_encode_dev(disk_devt(disk)); |
687 | 696 | ||
688 | /* | 697 | /* |
@@ -832,8 +841,13 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size) | |||
832 | /* | 841 | /* |
833 | * Ensure the device is not open and nothing further can open it. | 842 | * Ensure the device is not open and nothing further can open it. |
834 | */ | 843 | */ |
835 | r = dm_lock_for_deletion(md); | 844 | r = dm_lock_for_deletion(md, !!(param->flags & DM_DEFERRED_REMOVE), false); |
836 | if (r) { | 845 | if (r) { |
846 | if (r == -EBUSY && param->flags & DM_DEFERRED_REMOVE) { | ||
847 | up_write(&_hash_lock); | ||
848 | dm_put(md); | ||
849 | return 0; | ||
850 | } | ||
837 | DMDEBUG_LIMIT("unable to remove open device %s", hc->name); | 851 | DMDEBUG_LIMIT("unable to remove open device %s", hc->name); |
838 | up_write(&_hash_lock); | 852 | up_write(&_hash_lock); |
839 | dm_put(md); | 853 | dm_put(md); |
@@ -848,6 +862,8 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size) | |||
848 | dm_table_destroy(t); | 862 | dm_table_destroy(t); |
849 | } | 863 | } |
850 | 864 | ||
865 | param->flags &= ~DM_DEFERRED_REMOVE; | ||
866 | |||
851 | if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr)) | 867 | if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr)) |
852 | param->flags |= DM_UEVENT_GENERATED_FLAG; | 868 | param->flags |= DM_UEVENT_GENERATED_FLAG; |
853 | 869 | ||
@@ -1469,6 +1485,14 @@ static int message_for_md(struct mapped_device *md, unsigned argc, char **argv, | |||
1469 | if (**argv != '@') | 1485 | if (**argv != '@') |
1470 | return 2; /* no '@' prefix, deliver to target */ | 1486 | return 2; /* no '@' prefix, deliver to target */ |
1471 | 1487 | ||
1488 | if (!strcasecmp(argv[0], "@cancel_deferred_remove")) { | ||
1489 | if (argc != 1) { | ||
1490 | DMERR("Invalid arguments for @cancel_deferred_remove"); | ||
1491 | return -EINVAL; | ||
1492 | } | ||
1493 | return dm_cancel_deferred_remove(md); | ||
1494 | } | ||
1495 | |||
1472 | r = dm_stats_message(md, argc, argv, result, maxlen); | 1496 | r = dm_stats_message(md, argc, argv, result, maxlen); |
1473 | if (r < 2) | 1497 | if (r < 2) |
1474 | return r; | 1498 | return r; |
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)); |
diff --git a/drivers/md/dm.h b/drivers/md/dm.h index 1d1ad7b7e527..c57ba550f69e 100644 --- a/drivers/md/dm.h +++ b/drivers/md/dm.h | |||
@@ -129,6 +129,16 @@ int dm_deleting_md(struct mapped_device *md); | |||
129 | int dm_suspended_md(struct mapped_device *md); | 129 | int dm_suspended_md(struct mapped_device *md); |
130 | 130 | ||
131 | /* | 131 | /* |
132 | * Test if the device is scheduled for deferred remove. | ||
133 | */ | ||
134 | int dm_test_deferred_remove_flag(struct mapped_device *md); | ||
135 | |||
136 | /* | ||
137 | * Try to remove devices marked for deferred removal. | ||
138 | */ | ||
139 | void dm_deferred_remove(void); | ||
140 | |||
141 | /* | ||
132 | * The device-mapper can be driven through one of two interfaces; | 142 | * The device-mapper can be driven through one of two interfaces; |
133 | * ioctl or filesystem, depending which patch you have applied. | 143 | * ioctl or filesystem, depending which patch you have applied. |
134 | */ | 144 | */ |
@@ -158,7 +168,8 @@ void dm_stripe_exit(void); | |||
158 | void dm_destroy(struct mapped_device *md); | 168 | void dm_destroy(struct mapped_device *md); |
159 | void dm_destroy_immediate(struct mapped_device *md); | 169 | void dm_destroy_immediate(struct mapped_device *md); |
160 | int dm_open_count(struct mapped_device *md); | 170 | int dm_open_count(struct mapped_device *md); |
161 | int dm_lock_for_deletion(struct mapped_device *md); | 171 | int dm_lock_for_deletion(struct mapped_device *md, bool mark_deferred, bool only_deferred); |
172 | int dm_cancel_deferred_remove(struct mapped_device *md); | ||
162 | int dm_request_based(struct mapped_device *md); | 173 | int dm_request_based(struct mapped_device *md); |
163 | sector_t dm_get_size(struct mapped_device *md); | 174 | sector_t dm_get_size(struct mapped_device *md); |
164 | struct dm_stats *dm_get_stats(struct mapped_device *md); | 175 | struct dm_stats *dm_get_stats(struct mapped_device *md); |
diff --git a/include/uapi/linux/dm-ioctl.h b/include/uapi/linux/dm-ioctl.h index f1e12bd40b3b..c8a4302093a3 100644 --- a/include/uapi/linux/dm-ioctl.h +++ b/include/uapi/linux/dm-ioctl.h | |||
@@ -267,9 +267,9 @@ enum { | |||
267 | #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) | 267 | #define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) |
268 | 268 | ||
269 | #define DM_VERSION_MAJOR 4 | 269 | #define DM_VERSION_MAJOR 4 |
270 | #define DM_VERSION_MINOR 26 | 270 | #define DM_VERSION_MINOR 27 |
271 | #define DM_VERSION_PATCHLEVEL 0 | 271 | #define DM_VERSION_PATCHLEVEL 0 |
272 | #define DM_VERSION_EXTRA "-ioctl (2013-08-15)" | 272 | #define DM_VERSION_EXTRA "-ioctl (2013-10-30)" |
273 | 273 | ||
274 | /* Status bits */ | 274 | /* Status bits */ |
275 | #define DM_READONLY_FLAG (1 << 0) /* In/Out */ | 275 | #define DM_READONLY_FLAG (1 << 0) /* In/Out */ |
@@ -341,4 +341,15 @@ enum { | |||
341 | */ | 341 | */ |
342 | #define DM_DATA_OUT_FLAG (1 << 16) /* Out */ | 342 | #define DM_DATA_OUT_FLAG (1 << 16) /* Out */ |
343 | 343 | ||
344 | /* | ||
345 | * If set with DM_DEV_REMOVE or DM_REMOVE_ALL this indicates that if | ||
346 | * the device cannot be removed immediately because it is still in use | ||
347 | * it should instead be scheduled for removal when it gets closed. | ||
348 | * | ||
349 | * On return from DM_DEV_REMOVE, DM_DEV_STATUS or other ioctls, this | ||
350 | * flag indicates that the device is scheduled to be removed when it | ||
351 | * gets closed. | ||
352 | */ | ||
353 | #define DM_DEFERRED_REMOVE (1 << 17) /* In/Out */ | ||
354 | |||
344 | #endif /* _LINUX_DM_IOCTL_H */ | 355 | #endif /* _LINUX_DM_IOCTL_H */ |