aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/md
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/md')
-rw-r--r--drivers/md/dm-ioctl.c65
-rw-r--r--drivers/md/dm.c32
-rw-r--r--drivers/md/dm.h2
3 files changed, 78 insertions, 21 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