diff options
| author | Mikulas Patocka <mpatocka@redhat.com> | 2013-07-10 18:41:18 -0400 |
|---|---|---|
| committer | Alasdair G Kergon <agk@redhat.com> | 2013-07-10 18:41:18 -0400 |
| commit | 83d5e5b0af907d46d241a86d9e44003b3f0accbd (patch) | |
| tree | 46349d07c2090da15c250af3bac40833eb96e9f0 | |
| parent | 2480945cd44b50ba8b1646544eec2db21f064f12 (diff) | |
dm: optimize use SRCU and RCU
This patch removes "io_lock" and "map_lock" in struct mapped_device and
"holders" in struct dm_table and replaces these mechanisms with
sleepable-rcu.
Previously, the code would call "dm_get_live_table" and "dm_table_put" to
get and release table. Now, the code is changed to call "dm_get_live_table"
and "dm_put_live_table". dm_get_live_table locks sleepable-rcu and
dm_put_live_table unlocks it.
dm_get_live_table_fast/dm_put_live_table_fast can be used instead of
dm_get_live_table/dm_put_live_table. These *_fast functions use
non-sleepable RCU, so the caller must not block between them.
If the code changes active or inactive dm table, it must call
dm_sync_table before destroying the old table.
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Jun'ichi Nomura <j-nomura@ce.jp.nec.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
| -rw-r--r-- | drivers/md/dm-ioctl.c | 122 | ||||
| -rw-r--r-- | drivers/md/dm-table.c | 35 | ||||
| -rw-r--r-- | drivers/md/dm.c | 160 | ||||
| -rw-r--r-- | include/linux/device-mapper.h | 6 | ||||
| -rw-r--r-- | include/uapi/linux/dm-ioctl.h | 4 |
5 files changed, 180 insertions, 147 deletions
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 12f04868e899..f1b758675ec7 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c | |||
| @@ -36,6 +36,14 @@ struct hash_cell { | |||
| 36 | struct dm_table *new_map; | 36 | struct dm_table *new_map; |
| 37 | }; | 37 | }; |
| 38 | 38 | ||
| 39 | /* | ||
| 40 | * A dummy definition to make RCU happy. | ||
| 41 | * struct dm_table should never be dereferenced in this file. | ||
| 42 | */ | ||
| 43 | struct dm_table { | ||
| 44 | int undefined__; | ||
| 45 | }; | ||
| 46 | |||
| 39 | struct vers_iter { | 47 | struct vers_iter { |
| 40 | size_t param_size; | 48 | size_t param_size; |
| 41 | struct dm_target_versions *vers, *old_vers; | 49 | struct dm_target_versions *vers, *old_vers; |
| @@ -242,9 +250,10 @@ static int dm_hash_insert(const char *name, const char *uuid, struct mapped_devi | |||
| 242 | return -EBUSY; | 250 | return -EBUSY; |
| 243 | } | 251 | } |
| 244 | 252 | ||
| 245 | static void __hash_remove(struct hash_cell *hc) | 253 | static struct dm_table *__hash_remove(struct hash_cell *hc) |
| 246 | { | 254 | { |
| 247 | struct dm_table *table; | 255 | struct dm_table *table; |
| 256 | int srcu_idx; | ||
| 248 | 257 | ||
| 249 | /* remove from the dev hash */ | 258 | /* remove from the dev hash */ |
| 250 | list_del(&hc->uuid_list); | 259 | list_del(&hc->uuid_list); |
| @@ -253,16 +262,18 @@ static void __hash_remove(struct hash_cell *hc) | |||
| 253 | dm_set_mdptr(hc->md, NULL); | 262 | dm_set_mdptr(hc->md, NULL); |
| 254 | mutex_unlock(&dm_hash_cells_mutex); | 263 | mutex_unlock(&dm_hash_cells_mutex); |
| 255 | 264 | ||
| 256 | table = dm_get_live_table(hc->md); | 265 | table = dm_get_live_table(hc->md, &srcu_idx); |
| 257 | if (table) { | 266 | if (table) |
| 258 | dm_table_event(table); | 267 | dm_table_event(table); |
| 259 | dm_table_put(table); | 268 | dm_put_live_table(hc->md, srcu_idx); |
| 260 | } | ||
| 261 | 269 | ||
| 270 | table = NULL; | ||
| 262 | if (hc->new_map) | 271 | if (hc->new_map) |
| 263 | dm_table_destroy(hc->new_map); | 272 | table = hc->new_map; |
| 264 | dm_put(hc->md); | 273 | dm_put(hc->md); |
| 265 | free_cell(hc); | 274 | free_cell(hc); |
| 275 | |||
| 276 | return table; | ||
| 266 | } | 277 | } |
| 267 | 278 | ||
| 268 | static void dm_hash_remove_all(int keep_open_devices) | 279 | static void dm_hash_remove_all(int keep_open_devices) |
| @@ -270,6 +281,7 @@ static void dm_hash_remove_all(int keep_open_devices) | |||
| 270 | int i, dev_skipped; | 281 | int i, dev_skipped; |
| 271 | struct hash_cell *hc; | 282 | struct hash_cell *hc; |
| 272 | struct mapped_device *md; | 283 | struct mapped_device *md; |
| 284 | struct dm_table *t; | ||
| 273 | 285 | ||
| 274 | retry: | 286 | retry: |
| 275 | dev_skipped = 0; | 287 | dev_skipped = 0; |
| @@ -287,10 +299,14 @@ retry: | |||
| 287 | continue; | 299 | continue; |
| 288 | } | 300 | } |
| 289 | 301 | ||
| 290 | __hash_remove(hc); | 302 | t = __hash_remove(hc); |
| 291 | 303 | ||
| 292 | up_write(&_hash_lock); | 304 | up_write(&_hash_lock); |
| 293 | 305 | ||
| 306 | if (t) { | ||
| 307 | dm_sync_table(md); | ||
| 308 | dm_table_destroy(t); | ||
| 309 | } | ||
| 294 | dm_put(md); | 310 | dm_put(md); |
| 295 | if (likely(keep_open_devices)) | 311 | if (likely(keep_open_devices)) |
| 296 | dm_destroy(md); | 312 | dm_destroy(md); |
| @@ -356,6 +372,7 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, | |||
| 356 | struct dm_table *table; | 372 | struct dm_table *table; |
| 357 | struct mapped_device *md; | 373 | struct mapped_device *md; |
| 358 | unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; | 374 | unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0; |
| 375 | int srcu_idx; | ||
| 359 | 376 | ||
| 360 | /* | 377 | /* |
| 361 | * duplicate new. | 378 | * duplicate new. |
| @@ -418,11 +435,10 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param, | |||
| 418 | /* | 435 | /* |
| 419 | * Wake up any dm event waiters. | 436 | * Wake up any dm event waiters. |
| 420 | */ | 437 | */ |
| 421 | table = dm_get_live_table(hc->md); | 438 | table = dm_get_live_table(hc->md, &srcu_idx); |
| 422 | if (table) { | 439 | if (table) |
| 423 | dm_table_event(table); | 440 | dm_table_event(table); |
| 424 | dm_table_put(table); | 441 | dm_put_live_table(hc->md, srcu_idx); |
| 425 | } | ||
| 426 | 442 | ||
| 427 | if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr)) | 443 | if (!dm_kobject_uevent(hc->md, KOBJ_CHANGE, param->event_nr)) |
| 428 | param->flags |= DM_UEVENT_GENERATED_FLAG; | 444 | param->flags |= DM_UEVENT_GENERATED_FLAG; |
| @@ -620,11 +636,14 @@ static int check_name(const char *name) | |||
| 620 | * _hash_lock without first calling dm_table_put, because dm_table_destroy | 636 | * _hash_lock without first calling dm_table_put, because dm_table_destroy |
| 621 | * waits for this dm_table_put and could be called under this lock. | 637 | * waits for this dm_table_put and could be called under this lock. |
| 622 | */ | 638 | */ |
| 623 | static struct dm_table *dm_get_inactive_table(struct mapped_device *md) | 639 | static struct dm_table *dm_get_inactive_table(struct mapped_device *md, int *srcu_idx) |
| 624 | { | 640 | { |
| 625 | struct hash_cell *hc; | 641 | struct hash_cell *hc; |
| 626 | struct dm_table *table = NULL; | 642 | struct dm_table *table = NULL; |
| 627 | 643 | ||
| 644 | /* increment rcu count, we don't care about the table pointer */ | ||
| 645 | dm_get_live_table(md, srcu_idx); | ||
| 646 | |||
| 628 | down_read(&_hash_lock); | 647 | down_read(&_hash_lock); |
| 629 | hc = dm_get_mdptr(md); | 648 | hc = dm_get_mdptr(md); |
| 630 | if (!hc || hc->md != md) { | 649 | if (!hc || hc->md != md) { |
| @@ -633,8 +652,6 @@ static struct dm_table *dm_get_inactive_table(struct mapped_device *md) | |||
| 633 | } | 652 | } |
| 634 | 653 | ||
| 635 | table = hc->new_map; | 654 | table = hc->new_map; |
| 636 | if (table) | ||
| 637 | dm_table_get(table); | ||
| 638 | 655 | ||
| 639 | out: | 656 | out: |
| 640 | up_read(&_hash_lock); | 657 | up_read(&_hash_lock); |
| @@ -643,10 +660,11 @@ out: | |||
| 643 | } | 660 | } |
| 644 | 661 | ||
| 645 | static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md, | 662 | static struct dm_table *dm_get_live_or_inactive_table(struct mapped_device *md, |
| 646 | struct dm_ioctl *param) | 663 | struct dm_ioctl *param, |
| 664 | int *srcu_idx) | ||
| 647 | { | 665 | { |
| 648 | return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ? | 666 | return (param->flags & DM_QUERY_INACTIVE_TABLE_FLAG) ? |
| 649 | dm_get_inactive_table(md) : dm_get_live_table(md); | 667 | dm_get_inactive_table(md, srcu_idx) : dm_get_live_table(md, srcu_idx); |
| 650 | } | 668 | } |
| 651 | 669 | ||
| 652 | /* | 670 | /* |
| @@ -657,6 +675,7 @@ static void __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
| 657 | { | 675 | { |
| 658 | struct gendisk *disk = dm_disk(md); | 676 | struct gendisk *disk = dm_disk(md); |
| 659 | struct dm_table *table; | 677 | struct dm_table *table; |
| 678 | int srcu_idx; | ||
| 660 | 679 | ||
| 661 | param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | | 680 | param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG | |
| 662 | DM_ACTIVE_PRESENT_FLAG); | 681 | DM_ACTIVE_PRESENT_FLAG); |
| @@ -676,26 +695,27 @@ static void __dev_status(struct mapped_device *md, struct dm_ioctl *param) | |||
| 676 | param->event_nr = dm_get_event_nr(md); | 695 | param->event_nr = dm_get_event_nr(md); |
| 677 | param->target_count = 0; | 696 | param->target_count = 0; |
| 678 | 697 | ||
| 679 | table = dm_get_live_table(md); | 698 | table = dm_get_live_table(md, &srcu_idx); |
| 680 | if (table) { | 699 | if (table) { |
| 681 | if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) { | 700 | if (!(param->flags & DM_QUERY_INACTIVE_TABLE_FLAG)) { |
| 682 | if (get_disk_ro(disk)) | 701 | if (get_disk_ro(disk)) |
| 683 | param->flags |= DM_READONLY_FLAG; | 702 | param->flags |= DM_READONLY_FLAG; |
