diff options
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r-- | drivers/md/dm-ioctl.c | 145 |
1 files changed, 106 insertions, 39 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index a67942931582..d7500e1c26f2 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c | |||
@@ -56,6 +56,11 @@ static void dm_hash_remove_all(int keep_open_devices); | |||
56 | */ | 56 | */ |
57 | static DECLARE_RWSEM(_hash_lock); | 57 | static DECLARE_RWSEM(_hash_lock); |
58 | 58 | ||
59 | /* | ||
60 | * Protects use of mdptr to obtain hash cell name and uuid from mapped device. | ||
61 | */ | ||
62 | static DEFINE_MUTEX(dm_hash_cells_mutex); | ||
63 | |||
59 | static void init_buckets(struct list_head *buckets) | 64 | static void init_buckets(struct list_head *buckets) |
60 | { | 65 | { |
61 | unsigned int i; | 66 | unsigned int i; |
@@ -206,7 +211,9 @@ static int dm_hash_insert(const char *name, const char *uuid, struct mapped_devi | |||
206 | list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); | 211 | list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid)); |
207 | } | 212 | } |
208 | dm_get(md); | 213 | dm_get(md); |
214 | mutex_lock(&dm_hash_cells_mutex); | ||
209 | dm_set_mdptr(md, cell); | 215 | dm_set_mdptr(md, cell); |
216 | mutex_unlock(&dm_hash_cells_mutex); | ||
210 | up_write(&_hash_lock); | 217 | up_write(&_hash_lock); |
211 | 218 | ||
212 | return 0; | 219 | return 0; |
@@ -224,9 +231,11 @@ static void __hash_remove(struct hash_cell *hc) | |||
224 | /* remove from the dev hash */ | 231 | /* remove from the dev hash */ |
225 | list_del(&hc->uuid_list); | 232 | list_del(&hc->uuid_list); |
226 | list_del(&hc->name_list); | 233 | list_del(&hc->name_list); |
234 | mutex_lock(&dm_hash_cells_mutex); | ||
227 | dm_set_mdptr(hc->md, NULL); | 235 | dm_set_mdptr(hc->md, NULL); |
236 | mutex_unlock(&dm_hash_cells_mutex); | ||
228 | 237 | ||
229 | table = dm_get_table(hc->md); | 238 | table = dm_get_live_table(hc->md); |
230 | if (table) { | 239 | if (table) { |
231 | dm_table_event(table); | 240 | dm_table_event(table); |
232 | dm_table_put(table); | 241 | dm_table_put(table); |
@@ -276,7 +285,8 @@ retry: | |||
276 | up_write(&_hash_lock); | 285 | up_write(&_hash_lock); |
277 | } | 286 | } |
278 | 287 | ||
279 | static int dm_hash_rename(uint32_t cookie, const char *old, const char *new) | 288 | static int dm_hash_rename(uint32_t cookie, uint32_t *flags, const char *old, |
289 | const char *new) | ||
280 | { | 290 | { |
281 | char *new_name, *old_name; | 291 | char *new_name, *old_name; |
282 | struct hash_cell *hc; | 292 | struct hash_cell *hc; |
@@ -321,19 +331,22 @@ static int dm_hash_rename(uint32_t cookie, const char *old, const char *new) | |||
321 | */ | 331 | */ |
322 | list_del(&hc->name_list); | 332 | list_del(&hc->name_list); |
323 | old_name = hc->name; | 333 | old_name = hc->name; |
334 | mutex_lock(&dm_hash_cells_mutex); | ||
324 | hc->name = new_name; | 335 | hc->name = new_name; |
336 | mutex_unlock(&dm_hash_cells_mutex); | ||
325 | list_add(&hc->name_list, _name_buckets + hash_str(new_name)); | 337 | list_add(&hc->name_list, _name_buckets + hash_str(new_name)); |
326 | 338 | ||
327 | /* | 339 | /* |
328 | * Wake up any dm event waiters. | 340 | * Wake up any dm event waiters. |
329 | */ | 341 | */ |
330 | table = dm_get_table(hc->md); | 342 | table = dm_get_live_table(hc->md); |
331 | if (table) { | 343 | if (table) { |
332 | dm_table_event(table); | 344 | dm_table_event(table); |
333 | dm_table_put(table); | 345 | dm_table_put(table); |
334 | } | 346 | } |
335 | 347 | ||
336 | dm_kobject_uevent(hc->md, KOBJ_CHANGE, cookie); | 348 | if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, cookie)) |
349 | *flags |= DM_UEVENT_GENERATED_FLAG; | ||
337 | 350 | ||
338 | dm_put(hc->md); | 351 | dm_put(hc->md); |
339 | up_write(&_hash_lock); | 352 | up_write(&_hash_lock); |
@@ -512,8 +525,6 @@ static int list_versions(struct dm_ioctl *param, size_t param_size) | |||
512 | return 0; | 525 | return 0; |
513 | } | 526 | } |
514 | 527 | ||
515 | |||
516 | |||
517 | static int check_name(const char *name) | 528 | static int check_name(const char *name) |
518 | { | 529 | { |
519 | if (strchr(name, '/')) { | 530 | if (strchr(name, '/')) { |
@@ -525,6 +536,40 @@ static int check_name(const char *name) | |||
525 | } | 536 | } |
526 | 537 | ||
527 | /* | 538 | /* |
539 | * On successful return, the caller must not attempt to acquire | ||
540 | * _hash_lock without first calling dm_table_put, because dm_table_destroy | ||
541 | * waits for this dm_table_put and could be called under this lock. | ||
542 | */ | ||
543 | static struct dm_table *dm_get_inactive_table(struct mapped_device *md) | ||
544 | { | ||
545 | struct hash_cell *hc; | ||
546 | struct dm_table *table = NULL; | ||
547 | |||
548 | down_read(&_hash_lock); | ||
549 | hc = dm_get_mdptr(md); | ||
550 | if (!hc || hc->md != md) { | ||
551 | DMWARN("device has been removed from the dev hash table."); | ||
552 | goto out; | ||
553 | } | ||
554 | |||
555 | table = hc->new_map; | ||
556 | if (table) | ||
557 | dm_table_get(table); | ||
558 | |||
559 | out: | ||
560 | up_read(&_hash_lock); | ||
561 | |||
562 | return table; | ||
563 | } | ||
564 | |||
565 | static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md, | ||
566 | struct dm_ioctl *param) | ||
567 | { | ||
568 | return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ? | ||
569 | dm_get_inactive_table(md) : dm_get_live_table(md); | ||
570 | } | ||
571 | |||
572 | /* | ||
528 | * Fills in a dm_ioctl structure, ready for sending back to | 573 | * Fills in a dm_ioctl structure, ready for sending back to |
529 | * userland. | 574 | * userland. |
530 | */ | 575 | */ |
@@ -536,7 +581,7 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
536 | param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | | 581 | param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | |
537 | DM_ACTIVE_PRESENT_FLAG); | 582 | DM_ACTIVE_PRESENT_FLAG); |
538 | 583 | ||
539 | if (dm_suspended(md)) | 584 | if (dm_suspended_md(md)) |
540 | param->flags |= DM_SUSPEND_FLAG; | 585 | param->flags |= DM_SUSPEND_FLAG; |
541 | 586 | ||
542 | param->dev = huge_encode_dev(disk_devt(disk)); | 587 | param->dev = huge_encode_dev(disk_devt(disk)); |
@@ -548,18 +593,30 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
548 | */ | 593 | */ |
549 | param->open_count = dm_open_count(md); | 594 | param->open_count = dm_open_count(md); |
550 | 595 | ||
551 | if (get_disk_ro(disk)) | ||
552 | param->flags |= DM_READONLY_FLAG; | ||
553 | |||
554 | param->event_nr = dm_get_event_nr(md); | 596 | param->event_nr = dm_get_event_nr(md); |
597 | param->target_count = 0; | ||
555 | 598 | ||
556 | table = dm_get_table(md); | 599 | table = dm_get_live_table(md); |
557 | if (table) { | 600 | if (table) { |
558 | param->flags |= DM_ACTIVE_PRESENT_FLAG; | 601 | if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) { |
559 | param->target_count = dm_table_get_num_targets(table); | 602 | if (get_disk_ro(disk)) |
603 | param->flags |= DM_READONLY_FLAG; | ||
604 | param->target_count = dm_table_get_num_targets(table); | ||
605 | } | ||
560 | dm_table_put(table); | 606 | dm_table_put(table); |
561 | } else | 607 | |
562 | param->target_count = 0; | 608 | param->flags |= DM_ACTIVE_PRESENT_FLAG; |
609 | } | ||
610 | |||
611 | if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) { | ||
612 | table = dm_get_inactive_table(md); | ||
613 | if (table) { | ||
614 | if (!(dm_table_get_mode(table) & FMODE_WRITE)) | ||
615 | param->flags |= DM_READONLY_FLAG; | ||
616 | param->target_count = dm_table_get_num_targets(table); | ||
617 | dm_table_put(table); | ||
618 | } | ||
619 | } | ||
563 | 620 | ||
564 | return 0; | 621 | return 0; |
565 | } | 622 | } |
@@ -634,9 +691,9 @@ static struct mapped_device *find_device(struct dm_ioctl *param) | |||
634 | * Sneakily write in both the name and the uuid | 691 | * Sneakily write in both the name and the uuid |
635 | * while we have the cell. | 692 | * while we have the cell. |
636 | */ | 693 | */ |
637 | strncpy(param->name, hc->name, sizeof(param->name)); | 694 | strlcpy(param->name, hc->name, sizeof(param->name)); |
638 | if (hc->uuid) | 695 | if (hc->uuid) |
639 | strncpy(param->uuid, hc->uuid, sizeof(param->uuid)-1); | 696 | strlcpy(param->uuid, hc->uuid, sizeof(param->uuid)); |
640 | else | 697 | else |
641 | param->uuid[0] = '\0'; | 698 | param->uuid[0] = '\0'; |
642 | 699 | ||
@@ -681,10 +738,10 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size) | |||
681 | __hash_remove(hc); | 738 | __hash_remove(hc); |
682 | up_write(&_hash_lock); | 739 | up_write(&_hash_lock); |
683 | 740 | ||
684 | dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr); | 741 | if (!dm_kobject_uevent(md, KOBJ_REMOVE, param->event_nr)) |
742 | param->flags |= DM_UEVENT_GENERATED_FLAG; | ||
685 | 743 | ||
686 | dm_put(md); | 744 | dm_put(md); |
687 | param->data_size = 0; | ||
688 | return 0; | 745 | return 0; |
689 | } | 746 | } |
690 | 747 | ||
@@ -718,7 +775,9 @@ static int dev_rename(struct dm_ioctl *param, size_t param_size) | |||
718 | return r; | 775 | return r; |
719 | 776 | ||
720 | param->data_size = 0; | 777 | param->data_size = 0; |
721 | return dm_hash_rename(param->event_nr, param->name, new_name); | 778 | |
779 | return dm_hash_rename(param->event_nr, ¶m->flags, param->name, | ||
780 | new_name); | ||
722 | } | 781 | } |
723 | 782 | ||
724 | static int dev_set_geometry(struct dm_ioctl *param, size_t param_size) | 783 | static int dev_set_geometry(struct dm_ioctl *param, size_t param_size) |
@@ -784,7 +843,7 @@ static int do_suspend(struct dm_ioctl *param) | |||
784 | if (param->flags & DM_NOFLUSH_FLAG) | 843 | if (param->flags & DM_NOFLUSH_FLAG) |
785 | suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; | 844 | suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; |
786 | 845 | ||
787 | if (!dm_suspended(md)) | 846 | if (!dm_suspended_md(md)) |
788 | r = dm_suspend(md, suspend_flags); | 847 | r = dm_suspend(md, suspend_flags); |
789 | 848 | ||
790 | if (!r) | 849 | if (!r) |
@@ -800,7 +859,7 @@ static int do_resume(struct dm_ioctl *param) | |||
800 | unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG; | 859 | unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG; |
801 | struct hash_cell *hc; | 860 | struct hash_cell *hc; |
802 | struct mapped_device *md; | 861 | struct mapped_device *md; |
803 | struct dm_table *new_map; | 862 | struct dm_table *new_map, *old_map = NULL; |
804 | 863 | ||
805 | down_write(&_hash_lock); | 864 | down_write(&_hash_lock); |
806 | 865 | ||
@@ -826,14 +885,14 @@ static int do_resume(struct dm_ioctl *param) | |||
826 | suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; | 885 | suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; |
827 | if (param->flags & DM_NOFLUSH_FLAG) | 886 | if (param->flags & DM_NOFLUSH_FLAG) |
828 | suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; | 887 | suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; |
829 | if (!dm_suspended(md)) | 888 | if (!dm_suspended_md(md)) |
830 | dm_suspend(md, suspend_flags); | 889 | dm_suspend(md, suspend_flags); |
831 | 890 | ||
832 | r = dm_swap_table(md, new_map); | 891 | old_map = dm_swap_table(md, new_map); |
833 | if (r) { | 892 | if (IS_ERR(old_map)) { |
834 | dm_table_destroy(new_map); | 893 | dm_table_destroy(new_map); |
835 | dm_put(md); | 894 | dm_put(md); |
836 | return r; | 895 | return PTR_ERR(old_map); |
837 | } | 896 | } |
838 | 897 | ||
839 | if (dm_table_get_mode(new_map) & FMODE_WRITE) | 898 | if (dm_table_get_mode(new_map) & FMODE_WRITE) |
@@ -842,14 +901,17 @@ static int do_resume(struct dm_ioctl *param) | |||
842 | set_disk_ro(dm_disk(md), 1); | 901 | set_disk_ro(dm_disk(md), 1); |
843 | } | 902 | } |
844 | 903 | ||
845 | if (dm_suspended(md)) | 904 | if (dm_suspended_md(md)) { |
846 | r = dm_resume(md); | 905 | r = dm_resume(md); |
906 | if (!r && !dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr)) | ||
907 | param->flags |= DM_UEVENT_GENERATED_FLAG; | ||
908 | } | ||
847 | 909 | ||
910 | if (old_map) | ||
911 | dm_table_destroy(old_map); | ||
848 | 912 | ||
849 | if (!r) { | 913 | if (!r) |
850 | dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr); | ||
851 | r = __dev_status(md, param); | 914 | r = __dev_status(md, param); |
852 | } | ||
853 | 915 | ||
854 | dm_put(md); | 916 | dm_put(md); |
855 | return r; | 917 | return r; |
@@ -982,7 +1044,7 @@ static int dev_wait(struct dm_ioctl *param, size_t param_size) | |||
982 | if (r) | 1044 | if (r) |
983 | goto out; | 1045 | goto out; |
984 | 1046 | ||
985 | table = dm_get_table(md); | 1047 | table = dm_get_live_or_inactive_table(md, param); |
986 | if (table) { | 1048 | if (table) { |
987 | retrieve_status(table, param, param_size); | 1049 | retrieve_status(table, param, param_size); |
988 | dm_table_put(table); | 1050 | dm_table_put(table); |
@@ -1215,7 +1277,7 @@ static int table_deps(struct dm_ioctl *param, size_t param_size) | |||
1215 | if (r) | 1277 | if (r) |
1216 | goto out; | 1278 | goto out; |
1217 | 1279 | ||
1218 | table = dm_get_table(md); | 1280 | table = dm_get_live_or_inactive_table(md, param); |
1219 | if (table) { | 1281 | if (table) { |
1220 | retrieve_deps(table, param, param_size); | 1282 | retrieve_deps(table, param, param_size); |
1221 | dm_table_put(table); | 1283 | dm_table_put(table); |
@@ -1244,13 +1306,13 @@ static int table_status(struct dm_ioctl *param, size_t param_size) | |||
1244 | if (r) | 1306 | if (r) |
1245 | goto out; | 1307 | goto out; |
1246 | 1308 | ||
1247 | table = dm_get_table(md); | 1309 | table = dm_get_live_or_inactive_table(md, param); |
1248 | if (table) { | 1310 | if (table) { |
1249 | retrieve_status(table, param, param_size); | 1311 | retrieve_status(table, param, param_size); |
1250 | dm_table_put(table); | 1312 | dm_table_put(table); |
1251 | } | 1313 | } |
1252 | 1314 | ||
1253 | out: | 1315 | out: |
1254 | dm_put(md); | 1316 | dm_put(md); |
1255 | return r; | 1317 | return r; |
1256 | } | 1318 | } |
@@ -1288,10 +1350,15 @@ static int target_message(struct dm_ioctl *param, size_t param_size) | |||
1288 | goto out; | 1350 | goto out; |
1289 | } | 1351 | } |
1290 | 1352 | ||
1291 | table = dm_get_table(md); | 1353 | table = dm_get_live_table(md); |
1292 | if (!table) | 1354 | if (!table) |
1293 | goto out_argv; | 1355 | goto out_argv; |
1294 | 1356 | ||
1357 | if (dm_deleting_md(md)) { | ||
1358 | r = -ENXIO; | ||
1359 | goto out_table; | ||
1360 | } | ||
1361 | |||
1295 | ti = dm_table_find_target(table, tmsg->sector); | 1362 | ti = dm_table_find_target(table, tmsg->sector); |
1296 | if (!dm_target_is_valid(ti)) { | 1363 | if (!dm_target_is_valid(ti)) { |
1297 | DMWARN("Target message sector outside device."); | 1364 | DMWARN("Target message sector outside device."); |
@@ -1303,6 +1370,7 @@ static int target_message(struct dm_ioctl *param, size_t param_size) | |||
1303 | r = -EINVAL; | 1370 | r = -EINVAL; |
1304 | } | 1371 | } |
1305 | 1372 | ||
1373 | out_table: | ||
1306 | dm_table_put(table); | 1374 | dm_table_put(table); |
1307 | out_argv: | 1375 | out_argv: |
1308 | kfree(argv); | 1376 | kfree(argv); |
@@ -1413,6 +1481,7 @@ static int validate_params(uint cmd, struct dm_ioctl *param) | |||
1413 | { | 1481 | { |
1414 | /* Always clear this flag */ | 1482 | /* Always clear this flag */ |
1415 | param->flags &= ~DM_BUFFER_FULL_FLAG; | 1483 | param->flags &= ~DM_BUFFER_FULL_FLAG; |
1484 | param->flags &= ~DM_UEVENT_GENERATED_FLAG; | ||
1416 | 1485 | ||
1417 | /* Ignores parameters */ | 1486 | /* Ignores parameters */ |
1418 | if (cmd == DM_REMOVE_ALL_CMD || | 1487 | if (cmd == DM_REMOVE_ALL_CMD || |
@@ -1582,8 +1651,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) | |||
1582 | if (!md) | 1651 | if (!md) |
1583 | return -ENXIO; | 1652 | return -ENXIO; |
1584 | 1653 | ||
1585 | dm_get(md); | 1654 | mutex_lock(&dm_hash_cells_mutex); |
1586 | down_read(&_hash_lock); | ||
1587 | hc = dm_get_mdptr(md); | 1655 | hc = dm_get_mdptr(md); |
1588 | if (!hc || hc->md != md) { | 1656 | if (!hc || hc->md != md) { |
1589 | r = -ENXIO; | 1657 | r = -ENXIO; |
@@ -1596,8 +1664,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) | |||
1596 | strcpy(uuid, hc->uuid ? : ""); | 1664 | strcpy(uuid, hc->uuid ? : ""); |
1597 | 1665 | ||
1598 | out: | 1666 | out: |
1599 | up_read(&_hash_lock); | 1667 | mutex_unlock(&dm_hash_cells_mutex); |
1600 | dm_put(md); | ||
1601 | 1668 | ||
1602 | return r; | 1669 | return r; |
1603 | } | 1670 | } |