diff options
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r-- | drivers/md/dm-ioctl.c | 109 |
1 files changed, 74 insertions, 35 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 8edd6435414d..3edb3477f987 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. | 2 | * Copyright (C) 2001, 2002 Sistina Software (UK) Limited. |
3 | * Copyright (C) 2004 - 2005 Red Hat, Inc. All rights reserved. | 3 | * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved. |
4 | * | 4 | * |
5 | * This file is released under the GPL. | 5 | * This file is released under the GPL. |
6 | */ | 6 | */ |
@@ -19,6 +19,7 @@ | |||
19 | 19 | ||
20 | #include <asm/uaccess.h> | 20 | #include <asm/uaccess.h> |
21 | 21 | ||
22 | #define DM_MSG_PREFIX "ioctl" | ||
22 | #define DM_DRIVER_EMAIL "dm-devel@redhat.com" | 23 | #define DM_DRIVER_EMAIL "dm-devel@redhat.com" |
23 | 24 | ||
24 | /*----------------------------------------------------------------- | 25 | /*----------------------------------------------------------------- |
@@ -48,7 +49,7 @@ struct vers_iter { | |||
48 | static struct list_head _name_buckets[NUM_BUCKETS]; | 49 | static struct list_head _name_buckets[NUM_BUCKETS]; |
49 | static struct list_head _uuid_buckets[NUM_BUCKETS]; | 50 | static struct list_head _uuid_buckets[NUM_BUCKETS]; |
50 | 51 | ||
51 | static void dm_hash_remove_all(void); | 52 | static void dm_hash_remove_all(int keep_open_devices); |
52 | 53 | ||
53 | /* | 54 | /* |
54 | * Guards access to both hash tables. | 55 | * Guards access to both hash tables. |
@@ -73,7 +74,7 @@ static int dm_hash_init(void) | |||
73 | 74 | ||
74 | static void dm_hash_exit(void) | 75 | static void dm_hash_exit(void) |
75 | { | 76 | { |
76 | dm_hash_remove_all(); | 77 | dm_hash_remove_all(0); |
77 | devfs_remove(DM_DIR); | 78 | devfs_remove(DM_DIR); |
78 | } | 79 | } |
79 | 80 | ||
@@ -102,8 +103,10 @@ static struct hash_cell *__get_name_cell(const char *str) | |||
102 | unsigned int h = hash_str(str); | 103 | unsigned int h = hash_str(str); |
103 | 104 | ||
104 | list_for_each_entry (hc, _name_buckets + h, name_list) | 105 | list_for_each_entry (hc, _name_buckets + h, name_list) |
105 | if (!strcmp(hc->name, str)) | 106 | if (!strcmp(hc->name, str)) { |
107 | dm_get(hc->md); | ||
106 | return hc; | 108 | return hc; |
109 | } | ||
107 | 110 | ||
108 | return NULL; | 111 | return NULL; |
109 | } | 112 | } |
@@ -114,8 +117,10 @@ static struct hash_cell *__get_uuid_cell(const char *str) | |||
114 | unsigned int h = hash_str(str); | 117 | unsigned int h = hash_str(str); |
115 | 118 | ||
116 | list_for_each_entry (hc, _uuid_buckets + h, uuid_list) | 119 | list_for_each_entry (hc, _uuid_buckets + h, uuid_list) |
117 | if (!strcmp(hc->uuid, str)) | 120 | if (!strcmp(hc->uuid, str)) { |
121 | dm_get(hc->md); | ||
118 | return hc; | 122 | return hc; |
123 | } | ||
119 | 124 | ||
120 | return NULL; | 125 | return NULL; |
121 | } | 126 | } |
@@ -191,7 +196,7 @@ static int unregister_with_devfs(struct hash_cell *hc) | |||
191 | */ | 196 | */ |
192 | static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) | 197 | static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) |
193 | { | 198 | { |
194 | struct hash_cell *cell; | 199 | struct hash_cell *cell, *hc; |
195 | 200 | ||
196 | /* | 201 | /* |
197 | * Allocate the new cells. | 202 | * Allocate the new cells. |
@@ -204,14 +209,19 @@ static int dm_hash_insert(const char *name, const char *uuid, struct mapped_devi | |||
204 | * Insert the cell into both hash tables. | 209 | * Insert the cell into both hash tables. |
205 | */ | 210 | */ |
206 | down_write(&_hash_lock); | 211 | down_write(&_hash_lock); |
207 | if (__get_name_cell(name)) | 212 | hc = __get_name_cell(name); |
213 | if (hc) { | ||
214 | dm_put(hc->md); | ||
208 | goto bad; | 215 | goto bad; |
216 | } | ||
209 | 217 | ||
210 | list_add(&cell->name_list, _name_buckets + hash_str(name)); | 218 | list_add(&cell->name_list, _name_buckets + hash_str(name)); |
211 | 219 | ||
212 | if (uuid) { | 220 | if (uuid) { |
213 | if (__get_uuid_cell(uuid)) { | 221 | hc = __get_uuid_cell(uuid); |
222 | if (hc) { | ||
214 | list_del(&cell->name_list); | 223 | list_del(&cell->name_list); |
224 | dm_put(hc->md); | ||
215 | goto bad; | 225 | goto bad; |
216 | } | 226 | } |
217 | list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); | 227 | list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); |
@@ -251,19 +261,41 @@ static void __hash_remove(struct hash_cell *hc) | |||
251 | free_cell(hc); | 261 | free_cell(hc); |
252 | } | 262 | } |
253 | 263 | ||
254 | static void dm_hash_remove_all(void) | 264 | static void dm_hash_remove_all(int keep_open_devices) |
255 | { | 265 | { |
256 | int i; | 266 | int i, dev_skipped, dev_removed; |
257 | struct hash_cell *hc; | 267 | struct hash_cell *hc; |
258 | struct list_head *tmp, *n; | 268 | struct list_head *tmp, *n; |
259 | 269 | ||
260 | down_write(&_hash_lock); | 270 | down_write(&_hash_lock); |
271 | |||
272 | retry: | ||
273 | dev_skipped = dev_removed = 0; | ||
261 | for (i = 0; i < NUM_BUCKETS; i++) { | 274 | for (i = 0; i < NUM_BUCKETS; i++) { |
262 | list_for_each_safe (tmp, n, _name_buckets + i) { | 275 | list_for_each_safe (tmp, n, _name_buckets + i) { |
263 | hc = list_entry(tmp, struct hash_cell, name_list); | 276 | hc = list_entry(tmp, struct hash_cell, name_list); |
277 | |||
278 | if (keep_open_devices && | ||
279 | dm_lock_for_deletion(hc->md)) { | ||
280 | dev_skipped++; | ||
281 | continue; | ||
282 | } | ||
264 | __hash_remove(hc); | 283 | __hash_remove(hc); |
284 | dev_removed = 1; | ||
265 | } | 285 | } |
266 | } | 286 | } |
287 | |||
288 | /* | ||
289 | * Some mapped devices may be using other mapped devices, so if any | ||
290 | * still exist, repeat until we make no further progress. | ||
291 | */ | ||
292 | if (dev_skipped) { | ||
293 | if (dev_removed) | ||
294 | goto retry; | ||
295 | |||
296 | DMWARN("remove_all left %d open device(s)", dev_skipped); | ||
297 | } | ||
298 | |||
267 | up_write(&_hash_lock); | 299 | up_write(&_hash_lock); |
268 | } | 300 | } |
269 | 301 | ||
@@ -289,6 +321,7 @@ static int dm_hash_rename(const char *old, const char *new) | |||
289 | if (hc) { | 321 | if (hc) { |
290 | DMWARN("asked to rename to an already existing name %s -> %s", | 322 | DMWARN("asked to rename to an already existing name %s -> %s", |
291 | old, new); | 323 | old, new); |
324 | dm_put(hc->md); | ||
292 | up_write(&_hash_lock); | 325 | up_write(&_hash_lock); |
293 | kfree(new_name); | 326 | kfree(new_name); |
294 | return -EBUSY; | 327 | return -EBUSY; |
@@ -328,6 +361,7 @@ static int dm_hash_rename(const char *old, const char *new) | |||
328 | dm_table_put(table); | 361 | dm_table_put(table); |
329 | } | 362 | } |
330 | 363 | ||
364 | dm_put(hc->md); | ||
331 | up_write(&_hash_lock); | 365 | up_write(&_hash_lock); |
332 | kfree(old_name); | 366 | kfree(old_name); |
333 | return 0; | 367 | return 0; |
@@ -344,7 +378,7 @@ typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size); | |||
344 | 378 | ||
345 | static int remove_all(struct dm_ioctl *param, size_t param_size) | 379 | static int remove_all(struct dm_ioctl *param, size_t param_size) |
346 | { | 380 | { |
347 | dm_hash_remove_all(); | 381 | dm_hash_remove_all(1); |
348 | param->data_size = 0; | 382 | param->data_size = 0; |
349 | return 0; | 383 | return 0; |
350 | } | 384 | } |
@@ -524,7 +558,6 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
524 | { | 558 | { |
525 | struct gendisk *disk = dm_disk(md); | 559 | struct gendisk *disk = dm_disk(md); |
526 | struct dm_table *table; | 560 | struct dm_table *table; |
527 | struct block_device *bdev; | ||
528 | 561 | ||
529 | param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | | 562 | param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | |
530 | DM_ACTIVE_PRESENT_FLAG); | 563 | DM_ACTIVE_PRESENT_FLAG); |
@@ -534,20 +567,12 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
534 | 567 | ||
535 | param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); | 568 | param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); |
536 | 569 | ||
537 | if (!(param->flags & DM_SKIP_BDGET_FLAG)) { | 570 | /* |
538 | bdev = bdget_disk(disk, 0); | 571 | * Yes, this will be out of date by the time it gets back |
539 | if (!bdev) | 572 | * to userland, but it is still very useful for |
540 | return -ENXIO; | 573 | * debugging. |
541 | 574 | */ | |
542 | /* | 575 | param->open_count = dm_open_count(md); |
543 | * Yes, this will be out of date by the time it gets back | ||
544 | * to userland, but it is still very useful for | ||
545 | * debugging. | ||
546 | */ | ||
547 | param->open_count = bdev->bd_openers; | ||
548 | bdput(bdev); | ||
549 | } else | ||
550 | param->open_count = -1; | ||
551 | 576 | ||
552 | if (disk->policy) | 577 | if (disk->policy) |
553 | param->flags |= DM_READONLY_FLAG; | 578 | param->flags |= DM_READONLY_FLAG; |
@@ -567,7 +592,7 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
567 | 592 | ||
568 | static int dev_create(struct dm_ioctl *param, size_t param_size) | 593 | static int dev_create(struct dm_ioctl *param, size_t param_size) |
569 | { | 594 | { |
570 | int r; | 595 | int r, m = DM_ANY_MINOR; |
571 | struct mapped_device *md; | 596 | struct mapped_device *md; |
572 | 597 | ||
573 | r = check_name(param->name); | 598 | r = check_name(param->name); |
@@ -575,10 +600,9 @@ static int dev_create(struct dm_ioctl *param, size_t param_size) | |||
575 | return r; | 600 | return r; |
576 | 601 | ||
577 | if (param->flags & DM_PERSISTENT_DEV_FLAG) | 602 | if (param->flags & DM_PERSISTENT_DEV_FLAG) |
578 | r = dm_create_with_minor(MINOR(huge_decode_dev(param->dev)), &md); | 603 | m = MINOR(huge_decode_dev(param->dev)); |
579 | else | ||
580 | r = dm_create(&md); | ||
581 | 604 | ||
605 | r = dm_create(m, &md); | ||
582 | if (r) | 606 | if (r) |
583 | return r; | 607 | return r; |
584 | 608 | ||
@@ -611,10 +635,8 @@ static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) | |||
611 | return __get_name_cell(param->name); | 635 | return __get_name_cell(param->name); |
612 | 636 | ||
613 | md = dm_get_md(huge_decode_dev(param->dev)); | 637 | md = dm_get_md(huge_decode_dev(param->dev)); |
614 | if (md) { | 638 | if (md) |
615 | mdptr = dm_get_mdptr(md); | 639 | mdptr = dm_get_mdptr(md); |
616 | dm_put(md); | ||
617 | } | ||
618 | 640 | ||
619 | return mdptr; | 641 | return mdptr; |
620 | } | 642 | } |
@@ -628,7 +650,6 @@ static struct mapped_device *find_device(struct dm_ioctl *param) | |||
628 | hc = __find_device_hash_cell(param); | 650 | hc = __find_device_hash_cell(param); |
629 | if (hc) { | 651 | if (hc) { |
630 | md = hc->md; | 652 | md = hc->md; |
631 | dm_get(md); | ||
632 | 653 | ||
633 | /* | 654 | /* |
634 | * Sneakily write in both the name and the uuid | 655 | * Sneakily write in both the name and the uuid |
@@ -653,6 +674,8 @@ static struct mapped_device *find_device(struct dm_ioctl *param) | |||
653 | static int dev_remove(struct dm_ioctl *param, size_t param_size) | 674 | static int dev_remove(struct dm_ioctl *param, size_t param_size) |
654 | { | 675 | { |
655 | struct hash_cell *hc; | 676 | struct hash_cell *hc; |
677 | struct mapped_device *md; | ||
678 | int r; | ||
656 | 679 | ||
657 | down_write(&_hash_lock); | 680 | down_write(&_hash_lock); |
658 | hc = __find_device_hash_cell(param); | 681 | hc = __find_device_hash_cell(param); |
@@ -663,8 +686,22 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size) | |||
663 | return -ENXIO; | 686 | return -ENXIO; |
664 | } | 687 | } |
665 | 688 | ||
689 | md = hc->md; | ||
690 | |||
691 | /* | ||
692 | * Ensure the device is not open and nothing further can open it. | ||
693 | */ | ||
694 | r = dm_lock_for_deletion(md); | ||
695 | if (r) { | ||
696 | DMWARN("unable to remove open device %s", hc->name); | ||
697 | up_write(&_hash_lock); | ||
698 | dm_put(md); | ||
699 | return r; | ||
700 | } | ||
701 | |||
666 | __hash_remove(hc); | 702 | __hash_remove(hc); |
667 | up_write(&_hash_lock); | 703 | up_write(&_hash_lock); |
704 | dm_put(md); | ||
668 | param->data_size = 0; | 705 | param->data_size = 0; |
669 | return 0; | 706 | return 0; |
670 | } | 707 | } |
@@ -790,7 +827,6 @@ static int do_resume(struct dm_ioctl *param) | |||
790 | } | 827 | } |
791 | 828 | ||
792 | md = hc->md; | 829 | md = hc->md; |
793 | dm_get(md); | ||
794 | 830 | ||
795 | new_map = hc->new_map; | 831 | new_map = hc->new_map; |
796 | hc->new_map = NULL; | 832 | hc->new_map = NULL; |
@@ -1078,6 +1114,7 @@ static int table_clear(struct dm_ioctl *param, size_t param_size) | |||
1078 | { | 1114 | { |
1079 | int r; | 1115 | int r; |
1080 | struct hash_cell *hc; | 1116 | struct hash_cell *hc; |
1117 | struct mapped_device *md; | ||
1081 | 1118 | ||
1082 | down_write(&_hash_lock); | 1119 | down_write(&_hash_lock); |
1083 | 1120 | ||
@@ -1096,7 +1133,9 @@ static int table_clear(struct dm_ioctl *param, size_t param_size) | |||
1096 | param->flags &= ~DM_INACTIVE_PRESENT_FLAG; | 1133 | param->flags &= ~DM_INACTIVE_PRESENT_FLAG; |
1097 | 1134 | ||
1098 | r = __dev_status(hc->md, param); | 1135 | r = __dev_status(hc->md, param); |
1136 | md = hc->md; | ||
1099 | up_write(&_hash_lock); | 1137 | up_write(&_hash_lock); |
1138 | dm_put(md); | ||
1100 | return r; | 1139 | return r; |
1101 | } | 1140 | } |
1102 | 1141 | ||