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 | } |
