diff options
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r-- | drivers/md/dm-ioctl.c | 139 |
1 files changed, 74 insertions, 65 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 8edd6435414d..d13bb15a8a02 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 | */ |
@@ -13,12 +13,12 @@ | |||
13 | #include <linux/init.h> | 13 | #include <linux/init.h> |
14 | #include <linux/wait.h> | 14 | #include <linux/wait.h> |
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/devfs_fs_kernel.h> | ||
17 | #include <linux/dm-ioctl.h> | 16 | #include <linux/dm-ioctl.h> |
18 | #include <linux/hdreg.h> | 17 | #include <linux/hdreg.h> |
19 | 18 | ||
20 | #include <asm/uaccess.h> | 19 | #include <asm/uaccess.h> |
21 | 20 | ||
21 | #define DM_MSG_PREFIX "ioctl" | ||
22 | #define DM_DRIVER_EMAIL "dm-devel@redhat.com" | 22 | #define DM_DRIVER_EMAIL "dm-devel@redhat.com" |
23 | 23 | ||
24 | /*----------------------------------------------------------------- | 24 | /*----------------------------------------------------------------- |
@@ -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. |
@@ -67,14 +67,12 @@ static int dm_hash_init(void) | |||
67 | { | 67 | { |
68 | init_buckets(_name_buckets); | 68 | init_buckets(_name_buckets); |
69 | init_buckets(_uuid_buckets); | 69 | init_buckets(_uuid_buckets); |
70 | devfs_mk_dir(DM_DIR); | ||
71 | return 0; | 70 | return 0; |
72 | } | 71 | } |
73 | 72 | ||
74 | static void dm_hash_exit(void) | 73 | static void dm_hash_exit(void) |
75 | { | 74 | { |
76 | dm_hash_remove_all(); | 75 | dm_hash_remove_all(0); |
77 | devfs_remove(DM_DIR); | ||
78 | } | 76 | } |
79 | 77 | ||
80 | /*----------------------------------------------------------------- | 78 | /*----------------------------------------------------------------- |
@@ -102,8 +100,10 @@ static struct hash_cell *__get_name_cell(const char *str) | |||
102 | unsigned int h = hash_str(str); | 100 | unsigned int h = hash_str(str); |
103 | 101 | ||
104 | list_for_each_entry (hc, _name_buckets + h, name_list) | 102 | list_for_each_entry (hc, _name_buckets + h, name_list) |
105 | if (!strcmp(hc->name, str)) | 103 | if (!strcmp(hc->name, str)) { |
104 | dm_get(hc->md); | ||
106 | return hc; | 105 | return hc; |
106 | } | ||
107 | 107 | ||
108 | return NULL; | 108 | return NULL; |
109 | } | 109 | } |
@@ -114,8 +114,10 @@ static struct hash_cell *__get_uuid_cell(const char *str) | |||
114 | unsigned int h = hash_str(str); | 114 | unsigned int h = hash_str(str); |
115 | 115 | ||
116 | list_for_each_entry (hc, _uuid_buckets + h, uuid_list) | 116 | list_for_each_entry (hc, _uuid_buckets + h, uuid_list) |
117 | if (!strcmp(hc->uuid, str)) | 117 | if (!strcmp(hc->uuid, str)) { |
118 | dm_get(hc->md); | ||
118 | return hc; | 119 | return hc; |
120 | } | ||
119 | 121 | ||
120 | return NULL; | 122 | return NULL; |
121 | } | 123 | } |
@@ -167,31 +169,12 @@ static void free_cell(struct hash_cell *hc) | |||
167 | } | 169 | } |
168 | 170 | ||
169 | /* | 171 | /* |
170 | * devfs stuff. | ||
171 | */ | ||
172 | static int register_with_devfs(struct hash_cell *hc) | ||
173 | { | ||
174 | struct gendisk *disk = dm_disk(hc->md); | ||
175 | |||
176 | devfs_mk_bdev(MKDEV(disk->major, disk->first_minor), | ||
177 | S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP, | ||
178 | DM_DIR "/%s", hc->name); | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static int unregister_with_devfs(struct hash_cell *hc) | ||
183 | { | ||
184 | devfs_remove(DM_DIR"/%s", hc->name); | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | /* | ||
189 | * The kdev_t and uuid of a device can never change once it is | 172 | * The kdev_t and uuid of a device can never change once it is |
190 | * initially inserted. | 173 | * initially inserted. |
191 | */ | 174 | */ |
192 | static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) | 175 | static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md) |
193 | { | 176 | { |
194 | struct hash_cell *cell; | 177 | struct hash_cell *cell, *hc; |
195 | 178 | ||
196 | /* | 179 | /* |
197 | * Allocate the new cells. | 180 | * Allocate the new cells. |
@@ -204,19 +187,23 @@ static int dm_hash_insert(const char *name, const char *uuid, struct mapped_devi | |||
204 | * Insert the cell into both hash tables. | 187 | * Insert the cell into both hash tables. |
205 | */ | 188 | */ |
206 | down_write(&_hash_lock); | 189 | down_write(&_hash_lock); |
207 | if (__get_name_cell(name)) | 190 | hc = __get_name_cell(name); |
191 | if (hc) { | ||
192 | dm_put(hc->md); | ||
208 | goto bad; | 193 | goto bad; |
194 | } | ||
209 | 195 | ||
210 | list_add(&cell->name_list, _name_buckets + hash_str(name)); | 196 | list_add(&cell->name_list, _name_buckets + hash_str(name)); |
211 | 197 | ||
212 | if (uuid) { | 198 | if (uuid) { |
213 | if (__get_uuid_cell(uuid)) { | 199 | hc = __get_uuid_cell(uuid); |
200 | if (hc) { | ||
214 | list_del(&cell->name_list); | 201 | list_del(&cell->name_list); |
202 | dm_put(hc->md); | ||
215 | goto bad; | 203 | goto bad; |
216 | } | 204 | } |
217 | list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); | 205 | list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); |
218 | } | 206 | } |
219 | register_with_devfs(cell); | ||
220 | dm_get(md); | 207 | dm_get(md); |
221 | dm_set_mdptr(md, cell); | 208 | dm_set_mdptr(md, cell); |
222 | up_write(&_hash_lock); | 209 | up_write(&_hash_lock); |
@@ -236,7 +223,6 @@ static void __hash_remove(struct hash_cell *hc) | |||
236 | /* remove from the dev hash */ | 223 | /* remove from the dev hash */ |
237 | list_del(&hc->uuid_list); | 224 | list_del(&hc->uuid_list); |
238 | list_del(&hc->name_list); | 225 | list_del(&hc->name_list); |
239 | unregister_with_devfs(hc); | ||
240 | dm_set_mdptr(hc->md, NULL); | 226 | dm_set_mdptr(hc->md, NULL); |
241 | 227 | ||
242 | table = dm_get_table(hc->md); | 228 | table = dm_get_table(hc->md); |
@@ -251,19 +237,41 @@ static void __hash_remove(struct hash_cell *hc) | |||
251 | free_cell(hc); | 237 | free_cell(hc); |
252 | } | 238 | } |
253 | 239 | ||
254 | static void dm_hash_remove_all(void) | 240 | static void dm_hash_remove_all(int keep_open_devices) |
255 | { | 241 | { |
256 | int i; | 242 | int i, dev_skipped, dev_removed; |
257 | struct hash_cell *hc; | 243 | struct hash_cell *hc; |
258 | struct list_head *tmp, *n; | 244 | struct list_head *tmp, *n; |
259 | 245 | ||
260 | down_write(&_hash_lock); | 246 | down_write(&_hash_lock); |
247 | |||
248 | retry: | ||
249 | dev_skipped = dev_removed = 0; | ||
261 | for (i = 0; i < NUM_BUCKETS; i++) { | 250 | for (i = 0; i < NUM_BUCKETS; i++) { |
262 | list_for_each_safe (tmp, n, _name_buckets + i) { | 251 | list_for_each_safe (tmp, n, _name_buckets + i) { |
263 | hc = list_entry(tmp, struct hash_cell, name_list); | 252 | hc = list_entry(tmp, struct hash_cell, name_list); |
253 | |||
254 | if (keep_open_devices && | ||
255 | dm_lock_for_deletion(hc->md)) { | ||
256 | dev_skipped++; | ||
257 | continue; | ||
258 | } | ||
264 | __hash_remove(hc); | 259 | __hash_remove(hc); |
260 | dev_removed = 1; | ||
265 | } | 261 | } |
266 | } | 262 | } |
263 | |||
264 | /* | ||
265 | * Some mapped devices may be using other mapped devices, so if any | ||
266 | * still exist, repeat until we make no further progress. | ||
267 | */ | ||
268 | if (dev_skipped) { | ||
269 | if (dev_removed) | ||
270 | goto retry; | ||
271 | |||
272 | DMWARN("remove_all left %d open device(s)", dev_skipped); | ||
273 | } | ||
274 | |||
267 | up_write(&_hash_lock); | 275 | up_write(&_hash_lock); |
268 | } | 276 | } |
269 | 277 | ||
@@ -289,6 +297,7 @@ static int dm_hash_rename(const char *old, const char *new) | |||
289 | if (hc) { | 297 | if (hc) { |
290 | DMWARN("asked to rename to an already existing name %s -> %s", | 298 | DMWARN("asked to rename to an already existing name %s -> %s", |
291 | old, new); | 299 | old, new); |
300 | dm_put(hc->md); | ||
292 | up_write(&_hash_lock); | 301 | up_write(&_hash_lock); |
293 | kfree(new_name); | 302 | kfree(new_name); |
294 | return -EBUSY; | 303 | return -EBUSY; |
@@ -309,16 +318,11 @@ static int dm_hash_rename(const char *old, const char *new) | |||
309 | /* | 318 | /* |
310 | * rename and move the name cell. | 319 | * rename and move the name cell. |
311 | */ | 320 | */ |
312 | unregister_with_devfs(hc); | ||
313 | |||
314 | list_del(&hc->name_list); | 321 | list_del(&hc->name_list); |
315 | old_name = hc->name; | 322 | old_name = hc->name; |
316 | hc->name = new_name; | 323 | hc->name = new_name; |
317 | list_add(&hc->name_list, _name_buckets + hash_str(new_name)); | 324 | list_add(&hc->name_list, _name_buckets + hash_str(new_name)); |
318 | 325 | ||
319 | /* rename the device node in devfs */ | ||
320 | register_with_devfs(hc); | ||
321 | |||
322 | /* | 326 | /* |
323 | * Wake up any dm event waiters. | 327 | * Wake up any dm event waiters. |
324 | */ | 328 | */ |
@@ -328,6 +332,7 @@ static int dm_hash_rename(const char *old, const char *new) | |||
328 | dm_table_put(table); | 332 | dm_table_put(table); |
329 | } | 333 | } |
330 | 334 | ||
335 | dm_put(hc->md); | ||
331 | up_write(&_hash_lock); | 336 | up_write(&_hash_lock); |
332 | kfree(old_name); | 337 | kfree(old_name); |
333 | return 0; | 338 | return 0; |
@@ -344,7 +349,7 @@ typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size); | |||
344 | 349 | ||
345 | static int remove_all(struct dm_ioctl *param, size_t param_size) | 350 | static int remove_all(struct dm_ioctl *param, size_t param_size) |
346 | { | 351 | { |
347 | dm_hash_remove_all(); | 352 | dm_hash_remove_all(1); |
348 | param->data_size = 0; | 353 | param->data_size = 0; |
349 | return 0; | 354 | return 0; |
350 | } | 355 | } |
@@ -524,7 +529,6 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
524 | { | 529 | { |
525 | struct gendisk *disk = dm_disk(md); | 530 | struct gendisk *disk = dm_disk(md); |
526 | struct dm_table *table; | 531 | struct dm_table *table; |
527 | struct block_device *bdev; | ||
528 | 532 | ||
529 | param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | | 533 | param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | |
530 | DM_ACTIVE_PRESENT_FLAG); | 534 | DM_ACTIVE_PRESENT_FLAG); |
@@ -534,20 +538,12 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
534 | 538 | ||
535 | param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); | 539 | param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor)); |
536 | 540 | ||
537 | if (!(param->flags & DM_SKIP_BDGET_FLAG)) { | 541 | /* |
538 | bdev = bdget_disk(disk, 0); | 542 | * Yes, this will be out of date by the time it gets back |
539 | if (!bdev) | 543 | * to userland, but it is still very useful for |
540 | return -ENXIO; | 544 | * debugging. |
541 | 545 | */ | |
542 | /* | 546 | 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 | 547 | ||
552 | if (disk->policy) | 548 | if (disk->policy) |
553 | param->flags |= DM_READONLY_FLAG; | 549 | param->flags |= DM_READONLY_FLAG; |
@@ -567,7 +563,7 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
567 | 563 | ||
568 | static int dev_create(struct dm_ioctl *param, size_t param_size) | 564 | static int dev_create(struct dm_ioctl *param, size_t param_size) |
569 | { | 565 | { |
570 | int r; | 566 | int r, m = DM_ANY_MINOR; |
571 | struct mapped_device *md; | 567 | struct mapped_device *md; |
572 | 568 | ||
573 | r = check_name(param->name); | 569 | r = check_name(param->name); |
@@ -575,10 +571,9 @@ static int dev_create(struct dm_ioctl *param, size_t param_size) | |||
575 | return r; | 571 | return r; |
576 | 572 | ||
577 | if (param->flags & DM_PERSISTENT_DEV_FLAG) | 573 | if (param->flags & DM_PERSISTENT_DEV_FLAG) |
578 | r = dm_create_with_minor(MINOR(huge_decode_dev(param->dev)), &md); | 574 | m = MINOR(huge_decode_dev(param->dev)); |
579 | else | ||
580 | r = dm_create(&md); | ||
581 | 575 | ||
576 | r = dm_create(m, &md); | ||
582 | if (r) | 577 | if (r) |
583 | return r; | 578 | return r; |
584 | 579 | ||
@@ -611,10 +606,8 @@ static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param) | |||
611 | return __get_name_cell(param->name); | 606 | return __get_name_cell(param->name); |
612 | 607 | ||
613 | md = dm_get_md(huge_decode_dev(param->dev)); | 608 | md = dm_get_md(huge_decode_dev(param->dev)); |
614 | if (md) { | 609 | if (md) |
615 | mdptr = dm_get_mdptr(md); | 610 | mdptr = dm_get_mdptr(md); |
616 | dm_put(md); | ||
617 | } | ||
618 | 611 | ||
619 | return mdptr; | 612 | return mdptr; |
620 | } | 613 | } |
@@ -628,7 +621,6 @@ static struct mapped_device *find_device(struct dm_ioctl *param) | |||
628 | hc = __find_device_hash_cell(param); | 621 | hc = __find_device_hash_cell(param); |
629 | if (hc) { | 622 | if (hc) { |
630 | md = hc->md; | 623 | md = hc->md; |
631 | dm_get(md); | ||
632 | 624 | ||
633 | /* | 625 | /* |
634 | * Sneakily write in both the name and the uuid | 626 | * Sneakily write in both the name and the uuid |
@@ -653,6 +645,8 @@ static struct mapped_device *find_device(struct dm_ioctl *param) | |||
653 | static int dev_remove(struct dm_ioctl *param, size_t param_size) | 645 | static int dev_remove(struct dm_ioctl *param, size_t param_size) |
654 | { | 646 | { |
655 | struct hash_cell *hc; | 647 | struct hash_cell *hc; |
648 | struct mapped_device *md; | ||
649 | int r; | ||
656 | 650 | ||
657 | down_write(&_hash_lock); | 651 | down_write(&_hash_lock); |
658 | hc = __find_device_hash_cell(param); | 652 | hc = __find_device_hash_cell(param); |
@@ -663,8 +657,22 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size) | |||
663 | return -ENXIO; | 657 | return -ENXIO; |
664 | } | 658 | } |
665 | 659 | ||
660 | md = hc->md; | ||
661 | |||
662 | /* | ||
663 | * Ensure the device is not open and nothing further can open it. | ||
664 | */ | ||
665 | r = dm_lock_for_deletion(md); | ||
666 | if (r) { | ||
667 | DMWARN("unable to remove open device %s", hc->name); | ||
668 | up_write(&_hash_lock); | ||
669 | dm_put(md); | ||
670 | return r; | ||
671 | } | ||
672 | |||
666 | __hash_remove(hc); | 673 | __hash_remove(hc); |
667 | up_write(&_hash_lock); | 674 | up_write(&_hash_lock); |
675 | dm_put(md); | ||
668 | param->data_size = 0; | 676 | param->data_size = 0; |
669 | return 0; | 677 | return 0; |
670 | } | 678 | } |
@@ -790,7 +798,6 @@ static int do_resume(struct dm_ioctl *param) | |||
790 | } | 798 | } |
791 | 799 | ||
792 | md = hc->md; | 800 | md = hc->md; |
793 | dm_get(md); | ||
794 | 801 | ||
795 | new_map = hc->new_map; | 802 | new_map = hc->new_map; |
796 | hc->new_map = NULL; | 803 | hc->new_map = NULL; |
@@ -1078,6 +1085,7 @@ static int table_clear(struct dm_ioctl *param, size_t param_size) | |||
1078 | { | 1085 | { |
1079 | int r; | 1086 | int r; |
1080 | struct hash_cell *hc; | 1087 | struct hash_cell *hc; |
1088 | struct mapped_device *md; | ||
1081 | 1089 | ||
1082 | down_write(&_hash_lock); | 1090 | down_write(&_hash_lock); |
1083 | 1091 | ||
@@ -1096,7 +1104,9 @@ static int table_clear(struct dm_ioctl *param, size_t param_size) | |||
1096 | param->flags &= ~DM_INACTIVE_PRESENT_FLAG; | 1104 | param->flags &= ~DM_INACTIVE_PRESENT_FLAG; |
1097 | 1105 | ||
1098 | r = __dev_status(hc->md, param); | 1106 | r = __dev_status(hc->md, param); |
1107 | md = hc->md; | ||
1099 | up_write(&_hash_lock); | 1108 | up_write(&_hash_lock); |
1109 | dm_put(md); | ||
1100 | return r; | 1110 | return r; |
1101 | } | 1111 | } |
1102 | 1112 | ||
@@ -1462,7 +1472,6 @@ static struct file_operations _ctl_fops = { | |||
1462 | static struct miscdevice _dm_misc = { | 1472 | static struct miscdevice _dm_misc = { |
1463 | .minor = MISC_DYNAMIC_MINOR, | 1473 | .minor = MISC_DYNAMIC_MINOR, |
1464 | .name = DM_NAME, | 1474 | .name = DM_NAME, |
1465 | .devfs_name = "mapper/control", | ||
1466 | .fops = &_ctl_fops | 1475 | .fops = &_ctl_fops |
1467 | }; | 1476 | }; |
1468 | 1477 | ||