aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikulas Patocka <mpatocka@redhat.com>2013-11-01 18:27:41 -0400
committerMike Snitzer <snitzer@redhat.com>2013-11-09 18:20:22 -0500
commit2c140a246dc0bc085b98eddde978060fcec1080c (patch)
tree73d747f4422fe202868495d08ea5d189bc16bd79
parent7833b08e18241a1c35c09ef38be840cbf6c58acf (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.c36
-rw-r--r--drivers/md/dm.c47
-rw-r--r--drivers/md/dm.h13
-rw-r--r--include/uapi/linux/dm-ioctl.h15
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 {
57static struct list_head _name_buckets[NUM_BUCKETS]; 57static struct list_head _name_buckets[NUM_BUCKETS];
58static struct list_head _uuid_buckets[NUM_BUCKETS]; 58static struct list_head _uuid_buckets[NUM_BUCKETS];
59 59
60static void dm_hash_remove_all(int keep_open_devices); 60static 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
87static void dm_hash_exit(void) 87static 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
279static void dm_hash_remove_all(int keep_open_devices) 279static 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
454void 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
462static int remove_all(struct dm_ioctl *param, size_t param_size) 468static 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;
49static DEFINE_IDR(_minor_idr); 49static DEFINE_IDR(_minor_idr);
50 50
51static DEFINE_SPINLOCK(_minor_lock); 51static DEFINE_SPINLOCK(_minor_lock);
52
53static void do_deferred_remove(struct work_struct *w);
54
55static 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
300static void local_exit(void) 306static 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 */
421int dm_lock_for_deletion(struct mapped_device *md) 432int 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
452int 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
468static void do_deferred_remove(struct work_struct *w)
469{
470 dm_deferred_remove();
471}
472
437sector_t dm_get_size(struct mapped_device *md) 473sector_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
2933int dm_test_deferred_remove_flag(struct mapped_device *md)
2934{
2935 return test_bit(DMF_DEFERRED_REMOVE, &md->flags);
2936}
2937
2897int dm_suspended(struct dm_target *ti) 2938int 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);
129int dm_suspended_md(struct mapped_device *md); 129int dm_suspended_md(struct mapped_device *md);
130 130
131/* 131/*
132 * Test if the device is scheduled for deferred remove.
133 */
134int dm_test_deferred_remove_flag(struct mapped_device *md);
135
136/*
137 * Try to remove devices marked for deferred removal.
138 */
139void 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);
158void dm_destroy(struct mapped_device *md); 168void dm_destroy(struct mapped_device *md);
159void dm_destroy_immediate(struct mapped_device *md); 169void dm_destroy_immediate(struct mapped_device *md);
160int dm_open_count(struct mapped_device *md); 170int dm_open_count(struct mapped_device *md);
161int dm_lock_for_deletion(struct mapped_device *md); 171int dm_lock_for_deletion(struct mapped_device *md, bool mark_deferred, bool only_deferred);
172int dm_cancel_deferred_remove(struct mapped_device *md);
162int dm_request_based(struct mapped_device *md); 173int dm_request_based(struct mapped_device *md);
163sector_t dm_get_size(struct mapped_device *md); 174sector_t dm_get_size(struct mapped_device *md);
164struct dm_stats *dm_get_stats(struct mapped_device *md); 175struct 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 */