diff options
Diffstat (limited to 'drivers/md/dm-ioctl.c')
-rw-r--r-- | drivers/md/dm-ioctl.c | 123 |
1 files changed, 92 insertions, 31 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index a67942931582..1d669322b27c 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); |
@@ -321,13 +330,15 @@ static int dm_hash_rename(uint32_t cookie, const char *old, const char *new) | |||
321 | */ | 330 | */ |
322 | list_del(&hc->name_list); | 331 | list_del(&hc->name_list); |
323 | old_name = hc->name; | 332 | old_name = hc->name; |
333 | mutex_lock(&dm_hash_cells_mutex); | ||
324 | hc->name = new_name; | 334 | hc->name = new_name; |
335 | mutex_unlock(&dm_hash_cells_mutex); | ||
325 | list_add(&hc->name_list, _name_buckets + hash_str(new_name)); | 336 | list_add(&hc->name_list, _name_buckets + hash_str(new_name)); |
326 | 337 | ||
327 | /* | 338 | /* |
328 | * Wake up any dm event waiters. | 339 | * Wake up any dm event waiters. |
329 | */ | 340 | */ |
330 | table = dm_get_table(hc->md); | 341 | table = dm_get_live_table(hc->md); |
331 | if (table) { | 342 | if (table) { |
332 | dm_table_event(table); | 343 | dm_table_event(table); |
333 | dm_table_put(table); | 344 | dm_table_put(table); |
@@ -512,8 +523,6 @@ static int list_versions(struct dm_ioctl *param, size_t param_size) | |||
512 | return 0; | 523 | return 0; |
513 | } | 524 | } |
514 | 525 | ||
515 | |||
516 | |||
517 | static int check_name(const char *name) | 526 | static int check_name(const char *name) |
518 | { | 527 | { |
519 | if (strchr(name, '/')) { | 528 | if (strchr(name, '/')) { |
@@ -525,6 +534,40 @@ static int check_name(const char *name) | |||
525 | } | 534 | } |
526 | 535 | ||
527 | /* | 536 | /* |
537 | * On successful return, the caller must not attempt to acquire | ||
538 | * _hash_lock without first calling dm_table_put, because dm_table_destroy | ||
539 | * waits for this dm_table_put and could be called under this lock. | ||
540 | */ | ||
541 | static struct dm_table *dm_get_inactive_table(struct mapped_device *md) | ||
542 | { | ||
543 | struct hash_cell *hc; | ||
544 | struct dm_table *table = NULL; | ||
545 | |||
546 | down_read(&_hash_lock); | ||
547 | hc = dm_get_mdptr(md); | ||
548 | if (!hc || hc->md != md) { | ||
549 | DMWARN("device has been removed from the dev hash table."); | ||
550 | goto out; | ||
551 | } | ||
552 | |||
553 | table = hc->new_map; | ||
554 | if (table) | ||
555 | dm_table_get(table); | ||
556 | |||
557 | out: | ||
558 | up_read(&_hash_lock); | ||
559 | |||
560 | return table; | ||
561 | } | ||
562 | |||
563 | static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md, | ||
564 | struct dm_ioctl *param) | ||
565 | { | ||
566 | return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ? | ||
567 | dm_get_inactive_table(md) : dm_get_live_table(md); | ||
568 | } | ||
569 | |||
570 | /* | ||
528 | * Fills in a dm_ioctl structure, ready for sending back to | 571 | * Fills in a dm_ioctl structure, ready for sending back to |
529 | * userland. | 572 | * userland. |
530 | */ | 573 | */ |
@@ -536,7 +579,7 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
536 | param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | | 579 | param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | |
537 | DM_ACTIVE_PRESENT_FLAG); | 580 | DM_ACTIVE_PRESENT_FLAG); |
538 | 581 | ||
539 | if (dm_suspended(md)) | 582 | if (dm_suspended_md(md)) |
540 | param->flags |= DM_SUSPEND_FLAG; | 583 | param->flags |= DM_SUSPEND_FLAG; |
541 | 584 | ||
542 | param->dev = huge_encode_dev(disk_devt(disk)); | 585 | param->dev = huge_encode_dev(disk_devt(disk)); |
@@ -548,18 +591,30 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
548 | */ | 591 | */ |
549 | param->open_count = dm_open_count(md); | 592 | param->open_count = dm_open_count(md); |
550 | 593 | ||
551 | if (get_disk_ro(disk)) | ||
552 | param->flags |= DM_READONLY_FLAG; | ||
553 | |||
554 | param->event_nr = dm_get_event_nr(md); | 594 | param->event_nr = dm_get_event_nr(md); |
595 | param->target_count = 0; | ||
555 | 596 | ||
556 | table = dm_get_table(md); | 597 | table = dm_get_live_table(md); |
557 | if (table) { | 598 | if (table) { |
558 | param->flags |= DM_ACTIVE_PRESENT_FLAG; | 599 | if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) { |
559 | param->target_count = dm_table_get_num_targets(table); | 600 | if (get_disk_ro(disk)) |
601 | param->flags |= DM_READONLY_FLAG; | ||
602 | param->target_count = dm_table_get_num_targets(table); | ||
603 | } | ||
560 | dm_table_put(table); | 604 | dm_table_put(table); |
561 | } else | 605 | |
562 | param->target_count = 0; | 606 | param->flags |= DM_ACTIVE_PRESENT_FLAG; |
607 | } | ||
608 | |||
609 | if (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) { | ||
610 | table = dm_get_inactive_table(md); | ||
611 | if (table) { | ||
612 | if (!(dm_table_get_mode(table) & FMODE_WRITE)) | ||
613 | param->flags |= DM_READONLY_FLAG; | ||
614 | param->target_count = dm_table_get_num_targets(table); | ||
615 | dm_table_put(table); | ||
616 | } | ||
617 | } | ||
563 | 618 | ||
564 | return 0; | 619 | return 0; |
565 | } | 620 | } |
@@ -634,9 +689,9 @@ static struct mapped_device *find_device(struct dm_ioctl *param) | |||
634 | * Sneakily write in both the name and the uuid | 689 | * Sneakily write in both the name and the uuid |
635 | * while we have the cell. | 690 | * while we have the cell. |
636 | */ | 691 | */ |
637 | strncpy(param->name, hc->name, sizeof(param->name)); | 692 | strlcpy(param->name, hc->name, sizeof(param->name)); |
638 | if (hc->uuid) | 693 | if (hc->uuid) |
639 | strncpy(param->uuid, hc->uuid, sizeof(param->uuid)-1); | 694 | strlcpy(param->uuid, hc->uuid, sizeof(param->uuid)); |
640 | else | 695 | else |
641 | param->uuid[0] = '\0'; | 696 | param->uuid[0] = '\0'; |
642 | 697 | ||
@@ -784,7 +839,7 @@ static int do_suspend(struct dm_ioctl *param) | |||
784 | if (param->flags & DM_NOFLUSH_FLAG) | 839 | if (param->flags & DM_NOFLUSH_FLAG) |
785 | suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; | 840 | suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; |
786 | 841 | ||
787 | if (!dm_suspended(md)) | 842 | if (!dm_suspended_md(md)) |
788 | r = dm_suspend(md, suspend_flags); | 843 | r = dm_suspend(md, suspend_flags); |
789 | 844 | ||
790 | if (!r) | 845 | if (!r) |
@@ -800,7 +855,7 @@ static int do_resume(struct dm_ioctl *param) | |||
800 | unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG; | 855 | unsigned suspend_flags = DM_SUSPEND_LOCKFS_FLAG; |
801 | struct hash_cell *hc; | 856 | struct hash_cell *hc; |
802 | struct mapped_device *md; | 857 | struct mapped_device *md; |
803 | struct dm_table *new_map; | 858 | struct dm_table *new_map, *old_map = NULL; |
804 | 859 | ||
805 | down_write(&_hash_lock); | 860 | down_write(&_hash_lock); |
806 | 861 | ||
@@ -826,14 +881,14 @@ static int do_resume(struct dm_ioctl *param) | |||
826 | suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; | 881 | suspend_flags &= ~DM_SUSPEND_LOCKFS_FLAG; |
827 | if (param->flags & DM_NOFLUSH_FLAG) | 882 | if (param->flags & DM_NOFLUSH_FLAG) |
828 | suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; | 883 | suspend_flags |= DM_SUSPEND_NOFLUSH_FLAG; |
829 | if (!dm_suspended(md)) | 884 | if (!dm_suspended_md(md)) |
830 | dm_suspend(md, suspend_flags); | 885 | dm_suspend(md, suspend_flags); |
831 | 886 | ||
832 | r = dm_swap_table(md, new_map); | 887 | old_map = dm_swap_table(md, new_map); |
833 | if (r) { | 888 | if (IS_ERR(old_map)) { |
834 | dm_table_destroy(new_map); | 889 | dm_table_destroy(new_map); |
835 | dm_put(md); | 890 | dm_put(md); |
836 | return r; | 891 | return PTR_ERR(old_map); |
837 | } | 892 | } |
838 | 893 | ||
839 | if (dm_table_get_mode(new_map) & FMODE_WRITE) | 894 | if (dm_table_get_mode(new_map) & FMODE_WRITE) |
@@ -842,9 +897,11 @@ static int do_resume(struct dm_ioctl *param) | |||
842 | set_disk_ro(dm_disk(md), 1); | 897 | set_disk_ro(dm_disk(md), 1); |
843 | } | 898 | } |
844 | 899 | ||
845 | if (dm_suspended(md)) | 900 | if (dm_suspended_md(md)) |
846 | r = dm_resume(md); | 901 | r = dm_resume(md); |
847 | 902 | ||
903 | if (old_map) | ||
904 | dm_table_destroy(old_map); | ||
848 | 905 | ||
849 | if (!r) { | 906 | if (!r) { |
850 | dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr); | 907 | dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr); |
@@ -982,7 +1039,7 @@ static int dev_wait(struct dm_ioctl *param, size_t param_size) | |||
982 | if (r) | 1039 | if (r) |
983 | goto out; | 1040 | goto out; |
984 | 1041 | ||
985 | table = dm_get_table(md); | 1042 | table = dm_get_live_or_inactive_table(md, param); |
986 | if (table) { | 1043 | if (table) { |
987 | retrieve_status(table, param, param_size); | 1044 | retrieve_status(table, param, param_size); |
988 | dm_table_put(table); | 1045 | dm_table_put(table); |
@@ -1215,7 +1272,7 @@ static int table_deps(struct dm_ioctl *param, size_t param_size) | |||
1215 | if (r) | 1272 | if (r) |
1216 | goto out; | 1273 | goto out; |
1217 | 1274 | ||
1218 | table = dm_get_table(md); | 1275 | table = dm_get_live_or_inactive_table(md, param); |
1219 | if (table) { | 1276 | if (table) { |
1220 | retrieve_deps(table, param, param_size); | 1277 | retrieve_deps(table, param, param_size); |
1221 | dm_table_put(table); | 1278 | dm_table_put(table); |
@@ -1244,13 +1301,13 @@ static int table_status(struct dm_ioctl *param, size_t param_size) | |||
1244 | if (r) | 1301 | if (r) |
1245 | goto out; | 1302 | goto out; |
1246 | 1303 | ||
1247 | table = dm_get_table(md); | 1304 | table = dm_get_live_or_inactive_table(md, param); |
1248 | if (table) { | 1305 | if (table) { |
1249 | retrieve_status(table, param, param_size); | 1306 | retrieve_status(table, param, param_size); |
1250 | dm_table_put(table); | 1307 | dm_table_put(table); |
1251 | } | 1308 | } |
1252 | 1309 | ||
1253 | out: | 1310 | out: |
1254 | dm_put(md); | 1311 | dm_put(md); |
1255 | return r; | 1312 | return r; |
1256 | } | 1313 | } |
@@ -1288,10 +1345,15 @@ static int target_message(struct dm_ioctl *param, size_t param_size) | |||
1288 | goto out; | 1345 | goto out; |
1289 | } | 1346 | } |
1290 | 1347 | ||
1291 | table = dm_get_table(md); | 1348 | table = dm_get_live_table(md); |
1292 | if (!table) | 1349 | if (!table) |
1293 | goto out_argv; | 1350 | goto out_argv; |
1294 | 1351 | ||
1352 | if (dm_deleting_md(md)) { | ||
1353 | r = -ENXIO; | ||
1354 | goto out_table; | ||
1355 | } | ||
1356 | |||
1295 | ti = dm_table_find_target(table, tmsg->sector); | 1357 | ti = dm_table_find_target(table, tmsg->sector); |
1296 | if (!dm_target_is_valid(ti)) { | 1358 | if (!dm_target_is_valid(ti)) { |
1297 | DMWARN("Target message sector outside device."); | 1359 | DMWARN("Target message sector outside device."); |
@@ -1303,6 +1365,7 @@ static int target_message(struct dm_ioctl *param, size_t param_size) | |||
1303 | r = -EINVAL; | 1365 | r = -EINVAL; |
1304 | } | 1366 | } |
1305 | 1367 | ||
1368 | out_table: | ||
1306 | dm_table_put(table); | 1369 | dm_table_put(table); |
1307 | out_argv: | 1370 | out_argv: |
1308 | kfree(argv); | 1371 | kfree(argv); |
@@ -1582,8 +1645,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) | |||
1582 | if (!md) | 1645 | if (!md) |
1583 | return -ENXIO; | 1646 | return -ENXIO; |
1584 | 1647 | ||
1585 | dm_get(md); | 1648 | mutex_lock(&dm_hash_cells_mutex); |
1586 | down_read(&_hash_lock); | ||
1587 | hc = dm_get_mdptr(md); | 1649 | hc = dm_get_mdptr(md); |
1588 | if (!hc || hc->md != md) { | 1650 | if (!hc || hc->md != md) { |
1589 | r = -ENXIO; | 1651 | r = -ENXIO; |
@@ -1596,8 +1658,7 @@ int dm_copy_name_and_uuid(struct mapped_device *md, char *name, char *uuid) | |||
1596 | strcpy(uuid, hc->uuid ? : ""); | 1658 | strcpy(uuid, hc->uuid ? : ""); |
1597 | 1659 | ||
1598 | out: | 1660 | out: |
1599 | up_read(&_hash_lock); | 1661 | mutex_unlock(&dm_hash_cells_mutex); |
1600 | dm_put(md); | ||
1601 | 1662 | ||
1602 | return r; | 1663 | return r; |
1603 | } | 1664 | } |