aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md/dm-ioctl.c
diff options
context:
space:
mode:
authorAlasdair G Kergon <agk@redhat.com>2006-06-26 03:27:34 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-06-26 12:58:36 -0400
commit5c6bd75d06db512515a3781aa97e42df2faf0815 (patch)
tree43d68c6d3174e1ae8a778acd834665c122f8b2a4 /drivers/md/dm-ioctl.c
parentc2ade42dd35466d90aa6fc7cc717f396e165492f (diff)
[PATCH] dm: prevent removal if open
If you misuse the device-mapper interface (or there's a bug in your userspace tools) it's possible to end up with 'unlinked' mapped devices that cannot be removed until you reboot (along with uninterruptible processes). This patch prevents you from removing a device that is still open. It introduces dm_lock_for_deletion() which is called when a device is about to be removed to ensure that nothing has it open and nothing further can open it. It uses a private open_count for this which also lets us remove one of the problematic bdget_disk() calls elsewhere. Signed-off-by: Alasdair G Kergon <agk@redhat.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r--drivers/md/dm-ioctl.c65
1 files changed, 45 insertions, 20 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index c826b3e1799a..c6ce13b18747 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -48,7 +48,7 @@ struct vers_iter {
48static struct list_head _name_buckets[NUM_BUCKETS]; 48static struct list_head _name_buckets[NUM_BUCKETS];
49static struct list_head _uuid_buckets[NUM_BUCKETS]; 49static struct list_head _uuid_buckets[NUM_BUCKETS];
50 50
51static void dm_hash_remove_all(void); 51static void dm_hash_remove_all(int keep_open_devices);
52 52
53/* 53/*
54 * Guards access to both hash tables. 54 * Guards access to both hash tables.
@@ -73,7 +73,7 @@ static int dm_hash_init(void)
73 73
74static void dm_hash_exit(void) 74static void dm_hash_exit(void)
75{ 75{
76 dm_hash_remove_all(); 76 dm_hash_remove_all(0);
77 devfs_remove(DM_DIR); 77 devfs_remove(DM_DIR);
78} 78}
79 79
@@ -260,19 +260,41 @@ static void __hash_remove(struct hash_cell *hc)
260 free_cell(hc); 260 free_cell(hc);
261} 261}
262 262
263static void dm_hash_remove_all(void) 263static void dm_hash_remove_all(int keep_open_devices)
264{ 264{
265 int i; 265 int i, dev_skipped, dev_removed;
266 struct hash_cell *hc; 266 struct hash_cell *hc;
267 struct list_head *tmp, *n; 267 struct list_head *tmp, *n;
268 268
269 down_write(&_hash_lock); 269 down_write(&_hash_lock);
270
271retry:
272 dev_skipped = dev_removed = 0;
270 for (i = 0; i < NUM_BUCKETS; i++) { 273 for (i = 0; i < NUM_BUCKETS; i++) {
271 list_for_each_safe (tmp, n, _name_buckets + i) { 274 list_for_each_safe (tmp, n, _name_buckets + i) {
272 hc = list_entry(tmp, struct hash_cell, name_list); 275 hc = list_entry(tmp, struct hash_cell, name_list);
276
277 if (keep_open_devices &&
278 dm_lock_for_deletion(hc->md)) {
279 dev_skipped++;
280 continue;
281 }
273 __hash_remove(hc); 282 __hash_remove(hc);
283 dev_removed = 1;
274 } 284 }
275 } 285 }
286
287 /*
288 * Some mapped devices may be using other mapped devices, so if any
289 * still exist, repeat until we make no further progress.
290 */
291 if (dev_skipped) {
292 if (dev_removed)
293 goto retry;
294
295 DMWARN("remove_all left %d open device(s)", dev_skipped);
296 }
297
276 up_write(&_hash_lock); 298 up_write(&_hash_lock);
277} 299}
278 300
@@ -355,7 +377,7 @@ typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size);
355 377
356static int remove_all(struct dm_ioctl *param, size_t param_size) 378static int remove_all(struct dm_ioctl *param, size_t param_size)
357{ 379{
358 dm_hash_remove_all(); 380 dm_hash_remove_all(1);
359 param->data_size = 0; 381 param->data_size = 0;
360 return 0; 382 return 0;
361} 383}
@@ -535,7 +557,6 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
535{ 557{
536 struct gendisk *disk = dm_disk(md); 558 struct gendisk *disk = dm_disk(md);
537 struct dm_table *table; 559 struct dm_table *table;
538 struct block_device *bdev;
539 560
540 param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | 561 param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
541 DM_ACTIVE_PRESENT_FLAG); 562 DM_ACTIVE_PRESENT_FLAG);
@@ -545,20 +566,12 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
545 566
546 param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); 567 param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor));
547 568
548 if (!(param->flags & DM_SKIP_BDGET_FLAG)) { 569 /*
549 bdev = bdget_disk(disk, 0); 570 * Yes, this will be out of date by the time it gets back
550 if (!bdev) 571 * to userland, but it is still very useful for
551 return -ENXIO; 572 * debugging.
552 573 */
553 /* 574 param->open_count = dm_open_count(md);
554 * Yes, this will be out of date by the time it gets back
555 * to userland, but it is still very useful for
556 * debugging.
557 */
558 param->open_count = bdev->bd_openers;
559 bdput(bdev);
560 } else
561 param->open_count = -1;
562 575
563 if (disk->policy) 576 if (disk->policy)
564 param->flags |= DM_READONLY_FLAG; 577 param->flags |= DM_READONLY_FLAG;
@@ -661,6 +674,7 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
661{ 674{
662 struct hash_cell *hc; 675 struct hash_cell *hc;
663 struct mapped_device *md; 676 struct mapped_device *md;
677 int r;
664 678
665 down_write(&_hash_lock); 679 down_write(&_hash_lock);
666 hc = __find_device_hash_cell(param); 680 hc = __find_device_hash_cell(param);
@@ -673,6 +687,17 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
673 687
674 md = hc->md; 688 md = hc->md;
675 689
690 /*
691 * Ensure the device is not open and nothing further can open it.
692 */
693 r = dm_lock_for_deletion(md);
694 if (r) {
695 DMWARN("unable to remove open device %s", hc->name);
696 up_write(&_hash_lock);
697 dm_put(md);
698 return r;
699 }
700
676 __hash_remove(hc); 701 __hash_remove(hc);
677 up_write(&_hash_lock); 702 up_write(&_hash_lock);
678 dm_put(md); 703 dm_put(md);