diff options
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/dm-ioctl.c | 65 | ||||
-rw-r--r-- | drivers/md/dm.c | 32 | ||||
-rw-r--r-- | drivers/md/dm.h | 2 |
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 { | |||
48 | static struct list_head _name_buckets[NUM_BUCKETS]; | 48 | static struct list_head _name_buckets[NUM_BUCKETS]; |
49 | static struct list_head _uuid_buckets[NUM_BUCKETS]; | 49 | static struct list_head _uuid_buckets[NUM_BUCKETS]; |
50 | 50 | ||
51 | static void dm_hash_remove_all(void); | 51 | static 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 | ||
74 | static void dm_hash_exit(void) | 74 | static 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 | ||
263 | static void dm_hash_remove_all(void) | 263 | static 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 | |||
271 | retry: | ||
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 | ||
356 | static int remove_all(struct dm_ioctl *param, size_t param_size) | 378 | static 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 | ||
68 | struct mapped_device { | 69 | struct 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 | ||
238 | out: | 242 | out: |
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 | ||
258 | int 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 | */ | ||
266 | int 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 | |||
253 | static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo) | 282 | static 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 | ||
124 | void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); | 124 | void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size); |
125 | union map_info *dm_get_mapinfo(struct bio *bio); | 125 | union map_info *dm_get_mapinfo(struct bio *bio); |
126 | int dm_open_count(struct mapped_device *md); | ||
127 | int dm_lock_for_deletion(struct mapped_device *md); | ||
126 | 128 | ||
127 | #endif | 129 | #endif |