aboutsummaryrefslogtreecommitdiffstats
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
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>
-rw-r--r--drivers/md/dm-ioctl.c65
-rw-r--r--drivers/md/dm.c32
-rw-r--r--drivers/md/dm.h2
-rw-r--r--include/linux/dm-ioctl.h6
4 files changed, 81 insertions, 24 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);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 952c49c3b9aa..6982f86db6dc 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -64,12 +64,14 @@ union map_info *dm_get_mapinfo(struct bio *bio)
64#define DMF_SUSPENDED 1 64#define DMF_SUSPENDED 1
65#define DMF_FROZEN 2 65#define DMF_FROZEN 2
66#define DMF_FREEING 3 66#define DMF_FREEING 3
67#define DMF_DELETING 4
67 68
68struct mapped_device { 69struct mapped_device {
69 struct rw_semaphore io_lock; 70 struct rw_semaphore io_lock;
70 struct semaphore suspend_lock; 71 struct semaphore suspend_lock;
71 rwlock_t map_lock; 72 rwlock_t map_lock;
72 atomic_t holders; 73 atomic_t holders;
74 atomic_t open_count;
73 75
74 unsigned long flags; 76 unsigned long flags;
75 77
@@ -228,12 +230,14 @@ static int dm_blk_open(struct inode *inode, struct file *file)
228 if (!md) 230 if (!md)
229 goto out; 231 goto out;
230 232
231 if (test_bit(DMF_FREEING, &md->flags)) { 233 if (test_bit(DMF_FREEING, &md->flags) ||
234 test_bit(DMF_DELETING, &md->flags)) {
232 md = NULL; 235 md = NULL;
233 goto out; 236 goto out;
234 } 237 }
235 238
236 dm_get(md); 239 dm_get(md);
240 atomic_inc(&md->open_count);
237 241
238out: 242out:
239 spin_unlock(&_minor_lock); 243 spin_unlock(&_minor_lock);
@@ -246,10 +250,35 @@ static int dm_blk_close(struct inode *inode, struct file *file)
246 struct mapped_device *md; 250 struct mapped_device *md;
247 251
248 md = inode->i_bdev->bd_disk->private_data; 252 md = inode->i_bdev->bd_disk->private_data;
253 atomic_dec(&md->open_count);
249 dm_put(md); 254 dm_put(md);
250 return 0; 255 return 0;
251} 256}
252 257
258int dm_open_count(struct mapped_device *md)
259{
260 return atomic_read(&md->open_count);
261}
262
263/*
264 * Guarantees nothing is using the device before it's deleted.
265 */
266int dm_lock_for_deletion(struct mapped_device *md)
267{
268 int r = 0;
269
270 spin_lock(&_minor_lock);
271
272 if (dm_open_count(md))
273 r = -EBUSY;
274 else
275 set_bit(DMF_DELETING, &md->flags);
276
277 spin_unlock(&_minor_lock);
278
279 return r;
280}
281
253static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) 282static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
254{ 283{
255 struct mapped_device *md = bdev->bd_disk->private_data; 284 struct mapped_device *md = bdev->bd_disk->private_data;
@@ -867,6 +896,7 @@ static struct mapped_device *alloc_dev(int minor)
867 init_MUTEX(&md->suspend_lock); 896 init_MUTEX(&md->suspend_lock);
868 rwlock_init(&md->map_lock); 897 rwlock_init(&md->map_lock);
869 atomic_set(&md->holders, 1); 898 atomic_set(&md->holders, 1);
899 atomic_set(&md->open_count, 0);
870 atomic_set(&md->event_nr, 0); 900 atomic_set(&md->event_nr, 0);
871 901
872 md->queue = blk_alloc_queue(GFP_KERNEL); 902 md->queue = blk_alloc_queue(GFP_KERNEL);
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 71ddd1e23007..9ebb24b037bc 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -123,5 +123,7 @@ void dm_stripe_exit(void);
123 123
124void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); 124void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size);
125union map_info *dm_get_mapinfo(struct bio *bio); 125union map_info *dm_get_mapinfo(struct bio *bio);
126int dm_open_count(struct mapped_device *md);
127int dm_lock_for_deletion(struct mapped_device *md);
126 128
127#endif 129#endif
diff --git a/include/linux/dm-ioctl.h b/include/linux/dm-ioctl.h
index c67c6786612a..9623bb625090 100644
--- a/include/linux/dm-ioctl.h
+++ b/include/linux/dm-ioctl.h
@@ -285,9 +285,9 @@ typedef char ioctl_struct[308];
285#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl) 285#define DM_DEV_SET_GEOMETRY _IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
286 286
287#define DM_VERSION_MAJOR 4 287#define DM_VERSION_MAJOR 4
288#define DM_VERSION_MINOR 6 288#define DM_VERSION_MINOR 7
289#define DM_VERSION_PATCHLEVEL 0 289#define DM_VERSION_PATCHLEVEL 0
290#define DM_VERSION_EXTRA "-ioctl (2006-02-17)" 290#define DM_VERSION_EXTRA "-ioctl (2006-06-24)"
291 291
292/* Status bits */ 292/* Status bits */
293#define DM_READONLY_FLAG (1 << 0) /* In/Out */ 293#define DM_READONLY_FLAG (1 << 0) /* In/Out */
@@ -314,7 +314,7 @@ typedef char ioctl_struct[308];
314#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */ 314#define DM_BUFFER_FULL_FLAG (1 << 8) /* Out */
315 315
316/* 316/*
317 * Set this to improve performance when you aren't going to use open_count. 317 * This flag is now ignored.
318 */ 318 */
319#define DM_SKIP_BDGET_FLAG (1 << 9) /* In */ 319#define DM_SKIP_BDGET_FLAG (1 << 9) /* In */
320 320