diff options
| -rw-r--r-- | drivers/md/dm-cache-metadata.c | 101 |
1 files changed, 95 insertions, 6 deletions
diff --git a/drivers/md/dm-cache-metadata.c b/drivers/md/dm-cache-metadata.c index 9fc616c2755e..21b156242e42 100644 --- a/drivers/md/dm-cache-metadata.c +++ b/drivers/md/dm-cache-metadata.c | |||
| @@ -94,6 +94,9 @@ struct cache_disk_superblock { | |||
| 94 | } __packed; | 94 | } __packed; |
| 95 | 95 | ||
| 96 | struct dm_cache_metadata { | 96 | struct dm_cache_metadata { |
| 97 | atomic_t ref_count; | ||
| 98 | struct list_head list; | ||
| 99 | |||
| 97 | struct block_device *bdev; | 100 | struct block_device *bdev; |
| 98 | struct dm_block_manager *bm; | 101 | struct dm_block_manager *bm; |
| 99 | struct dm_space_map *metadata_sm; | 102 | struct dm_space_map *metadata_sm; |
| @@ -669,10 +672,10 @@ static void unpack_value(__le64 value_le, dm_oblock_t *block, unsigned *flags) | |||
| 669 | 672 | ||
| 670 | /*----------------------------------------------------------------*/ | 673 | /*----------------------------------------------------------------*/ |
| 671 | 674 | ||
| 672 | struct dm_cache_metadata *dm_cache_metadata_open(struct block_device *bdev, | 675 | static struct dm_cache_metadata *metadata_open(struct block_device *bdev, |
| 673 | sector_t data_block_size, | 676 | sector_t data_block_size, |
| 674 | bool may_format_device, | 677 | bool may_format_device, |
| 675 | size_t policy_hint_size) | 678 | size_t policy_hint_size) |
| 676 | { | 679 | { |
| 677 | int r; | 680 | int r; |
| 678 | struct dm_cache_metadata *cmd; | 681 | struct dm_cache_metadata *cmd; |
| @@ -683,6 +686,7 @@ struct dm_cache_metadata *dm_cache_metadata_open(struct block_device *bdev, | |||
| 683 | return NULL; | 686 | return NULL; |
| 684 | } | 687 | } |
| 685 | 688 | ||
| 689 | atomic_set(&cmd->ref_count, 1); | ||
| 686 | init_rwsem(&cmd->root_lock); | 690 | init_rwsem(&cmd->root_lock); |
| 687 | cmd->bdev = bdev; | 691 | cmd->bdev = bdev; |
| 688 | cmd->data_block_size = data_block_size; | 692 | cmd->data_block_size = data_block_size; |
| @@ -705,10 +709,95 @@ struct dm_cache_metadata *dm_cache_metadata_open(struct block_device *bdev, | |||
| 705 | return cmd; | 709 | return cmd; |
| 706 | } | 710 | } |
| 707 | 711 | ||
| 712 | /* | ||
| 713 | * We keep a little list of ref counted metadata objects to prevent two | ||
| 714 | * different target instances creating separate bufio instances. This is | ||
| 715 | * an issue if a table is reloaded before the suspend. | ||
| 716 | */ | ||
| 717 | static DEFINE_MUTEX(table_lock); | ||
| 718 | static LIST_HEAD(table); | ||
| 719 | |||
| 720 | static struct dm_cache_metadata *lookup(struct block_device *bdev) | ||
| 721 | { | ||
| 722 | struct dm_cache_metadata *cmd; | ||
| 723 | |||
| 724 | list_for_each_entry(cmd, &table, list) | ||
| 725 | if (cmd->bdev == bdev) { | ||
| 726 | atomic_inc(&cmd->ref_count); | ||
| 727 | return cmd; | ||
| 728 | } | ||
| 729 | |||
| 730 | return NULL; | ||
| 731 | } | ||
| 732 | |||
| 733 | static struct dm_cache_metadata *lookup_or_open(struct block_device *bdev, | ||
| 734 | sector_t data_block_size, | ||
| 735 | bool may_format_device, | ||
| 736 | size_t policy_hint_size) | ||
| 737 | { | ||
| 738 | struct dm_cache_metadata *cmd, *cmd2; | ||
| 739 | |||
| 740 | mutex_lock(&table_lock); | ||
| 741 | cmd = lookup(bdev); | ||
| 742 | mutex_unlock(&table_lock); | ||
| 743 | |||
| 744 | if (cmd) | ||
| 745 | return cmd; | ||
| 746 | |||
| 747 | cmd = metadata_open(bdev, data_block_size, may_format_device, policy_hint_size); | ||
| 748 | if (cmd) { | ||
| 749 | mutex_lock(&table_lock); | ||
| 750 | cmd2 = lookup(bdev); | ||
| 751 | if (cmd2) { | ||
| 752 | mutex_unlock(&table_lock); | ||
| 753 | __destroy_persistent_data_objects(cmd); | ||
| 754 | kfree(cmd); | ||
| 755 | return cmd2; | ||
| 756 | } | ||
| 757 | list_add(&cmd->list, &table); | ||
| 758 | mutex_unlock(&table_lock); | ||
| 759 | } | ||
| 760 | |||
| 761 | return cmd; | ||
| 762 | } | ||
| 763 | |||
| 764 | static bool same_params(struct dm_cache_metadata *cmd, sector_t data_block_size) | ||
| 765 | { | ||
| 766 | if (cmd->data_block_size != data_block_size) { | ||
| 767 | DMERR("data_block_size (%llu) different from that in metadata (%llu)\n", | ||
| 768 | (unsigned long long) data_block_size, | ||
| 769 | (unsigned long long) cmd->data_block_size); | ||
| 770 | return false; | ||
| 771 | } | ||
| 772 | |||
| 773 | return true; | ||
| 774 | } | ||
| 775 | |||
| 776 | struct dm_cache_metadata *dm_cache_metadata_open(struct block_device *bdev, | ||
| 777 | sector_t data_block_size, | ||
| 778 | bool may_format_device, | ||
| 779 | size_t policy_hint_size) | ||
| 780 | { | ||
| 781 | struct dm_cache_metadata *cmd = lookup_or_open(bdev, data_block_size, | ||
| 782 | may_format_device, policy_hint_size); | ||
| 783 | if (cmd && !same_params(cmd, data_block_size)) { | ||
| 784 | dm_cache_metadata_close(cmd); | ||
| 785 | return NULL; | ||
| 786 | } | ||
| 787 | |||
| 788 | return cmd; | ||
| 789 | } | ||
| 790 | |||
| 708 | void dm_cache_metadata_close(struct dm_cache_metadata *cmd) | 791 | void dm_cache_metadata_close(struct dm_cache_metadata *cmd) |
| 709 | { | 792 | { |
| 710 | __destroy_persistent_data_objects(cmd); | 793 | if (atomic_dec_and_test(&cmd->ref_count)) { |
| 711 | kfree(cmd); | 794 | mutex_lock(&table_lock); |
| 795 | list_del(&cmd->list); | ||
| 796 | mutex_unlock(&table_lock); | ||
| 797 | |||
| 798 | __destroy_persistent_data_objects(cmd); | ||
| 799 | kfree(cmd); | ||
| 800 | } | ||
| 712 | } | 801 | } |
| 713 | 802 | ||
| 714 | /* | 803 | /* |
