diff options
author | Benjamin Marzinski <bmarzins@redhat.com> | 2014-08-13 14:53:43 -0400 |
---|---|---|
committer | Mike Snitzer <snitzer@redhat.com> | 2014-10-05 20:03:35 -0400 |
commit | 86f1152b117a404229fd6f08ec3faca779f37b92 (patch) | |
tree | dceee3703ab97065c3e425b55d42cb4edb9a079b /drivers/md/dm.c | |
parent | 1f271972478d84dd9e4d6dd82f414d70ed9e78ce (diff) |
dm: allow active and inactive tables to share dm_devs
Until this change, when loading a new DM table, DM core would re-open
all of the devices in the DM table. Now, DM core will avoid redundant
device opens (and closes when destroying the old table) if the old
table already has a device open using the same mode. This is achieved
by managing reference counts on the table_devices that DM core now
stores in the mapped_device structure (rather than in the dm_table
structure). So a mapped_device's active and inactive dm_tables' dm_dev
lists now just point to the dm_devs stored in the mapped_device's
table_devices list.
This improvement in DM core's device reference counting has the
side-effect of fixing a long-standing limitation of the multipath
target: a DM multipath table couldn't include any paths that were unusable
(failed). For example: if all paths have failed and you add a new,
working, path to the table; you can't use it since the table load would
fail due to it still containing failed paths. Now a re-load of a
multipath table can include failed devices and when those devices become
active again they can be used instantly.
The device list code in dm.c isn't a straight copy/paste from the code in
dm-table.c, but it's very close (aside from some variable renames). One
subtle difference is that find_table_device for the tables_devices list
will only match devices with the same name and mode. This is because we
don't want to upgrade a device's mode in the active table when an
inactive table is loaded.
Access to the mapped_device structure's tables_devices list requires a
mutex (tables_devices_lock), so that tables cannot be created and
destroyed concurrently.
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
Diffstat (limited to 'drivers/md/dm.c')
-rw-r--r-- | drivers/md/dm.c | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 56a2c74c9a3f..58f3927fd7cc 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c | |||
@@ -142,6 +142,9 @@ struct mapped_device { | |||
142 | */ | 142 | */ |
143 | struct dm_table *map; | 143 | struct dm_table *map; |
144 | 144 | ||
145 | struct list_head table_devices; | ||
146 | struct mutex table_devices_lock; | ||
147 | |||
145 | unsigned long flags; | 148 | unsigned long flags; |
146 | 149 | ||
147 | struct request_queue *queue; | 150 | struct request_queue *queue; |
@@ -212,6 +215,12 @@ struct dm_md_mempools { | |||
212 | struct bio_set *bs; | 215 | struct bio_set *bs; |
213 | }; | 216 | }; |
214 | 217 | ||
218 | struct table_device { | ||
219 | struct list_head list; | ||
220 | atomic_t count; | ||
221 | struct dm_dev dm_dev; | ||
222 | }; | ||
223 | |||
215 | #define RESERVED_BIO_BASED_IOS 16 | 224 | #define RESERVED_BIO_BASED_IOS 16 |
216 | #define RESERVED_REQUEST_BASED_IOS 256 | 225 | #define RESERVED_REQUEST_BASED_IOS 256 |
217 | #define RESERVED_MAX_IOS 1024 | 226 | #define RESERVED_MAX_IOS 1024 |
@@ -670,6 +679,120 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU) | |||
670 | } | 679 | } |
671 | 680 | ||
672 | /* | 681 | /* |
682 | * Open a table device so we can use it as a map destination. | ||
683 | */ | ||
684 | static int open_table_device(struct table_device *td, dev_t dev, | ||
685 | struct mapped_device *md) | ||
686 | { | ||
687 | static char *_claim_ptr = "I belong to device-mapper"; | ||
688 | struct block_device *bdev; | ||
689 | |||
690 | int r; | ||
691 | |||
692 | BUG_ON(td->dm_dev.bdev); | ||
693 | |||
694 | bdev = blkdev_get_by_dev(dev, td->dm_dev.mode | FMODE_EXCL, _claim_ptr); | ||
695 | if (IS_ERR(bdev)) | ||
696 | return PTR_ERR(bdev); | ||
697 | |||
698 | r = bd_link_disk_holder(bdev, dm_disk(md)); | ||
699 | if (r) { | ||
700 | blkdev_put(bdev, td->dm_dev.mode | FMODE_EXCL); | ||
701 | return r; | ||
702 | } | ||
703 | |||
704 | td->dm_dev.bdev = bdev; | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | /* | ||
709 | * Close a table device that we've been using. | ||
710 | */ | ||
711 | static void close_table_device(struct table_device *td, struct mapped_device *md) | ||
712 | { | ||
713 | if (!td->dm_dev.bdev) | ||
714 | return; | ||
715 | |||
716 | bd_unlink_disk_holder(td->dm_dev.bdev, dm_disk(md)); | ||
717 | blkdev_put(td->dm_dev.bdev, td->dm_dev.mode | FMODE_EXCL); | ||
718 | td->dm_dev.bdev = NULL; | ||
719 | } | ||
720 | |||
721 | static struct table_device *find_table_device(struct list_head *l, dev_t dev, | ||
722 | fmode_t mode) { | ||
723 | struct table_device *td; | ||
724 | |||
725 | list_for_each_entry(td, l, list) | ||
726 | if (td->dm_dev.bdev->bd_dev == dev && td->dm_dev.mode == mode) | ||
727 | return td; | ||
728 | |||
729 | return NULL; | ||
730 | } | ||
731 | |||
732 | int dm_get_table_device(struct mapped_device *md, dev_t dev, fmode_t mode, | ||
733 | struct dm_dev **result) { | ||
734 | int r; | ||
735 | struct table_device *td; | ||
736 | |||
737 | mutex_lock(&md->table_devices_lock); | ||
738 | td = find_table_device(&md->table_devices, dev, mode); | ||
739 | if (!td) { | ||
740 | td = kmalloc(sizeof(*td), GFP_KERNEL); | ||
741 | if (!td) { | ||
742 | mutex_unlock(&md->table_devices_lock); | ||
743 | return -ENOMEM; | ||
744 | } | ||
745 | |||
746 | td->dm_dev.mode = mode; | ||
747 | td->dm_dev.bdev = NULL; | ||
748 | |||
749 | if ((r = open_table_device(td, dev, md))) { | ||
750 | mutex_unlock(&md->table_devices_lock); | ||
751 | kfree(td); | ||
752 | return r; | ||
753 | } | ||
754 | |||
755 | format_dev_t(td->dm_dev.name, dev); | ||
756 | |||
757 | atomic_set(&td->count, 0); | ||
758 | list_add(&td->list, &md->table_devices); | ||
759 | } | ||
760 | atomic_inc(&td->count); | ||
761 | mutex_unlock(&md->table_devices_lock); | ||
762 | |||
763 | *result = &td->dm_dev; | ||
764 | return 0; | ||
765 | } | ||
766 | EXPORT_SYMBOL_GPL(dm_get_table_device); | ||
767 | |||
768 | void dm_put_table_device(struct mapped_device *md, struct dm_dev *d) | ||
769 | { | ||
770 | struct table_device *td = container_of(d, struct table_device, dm_dev); | ||
771 | |||
772 | mutex_lock(&md->table_devices_lock); | ||
773 | if (atomic_dec_and_test(&td->count)) { | ||
774 | close_table_device(td, md); | ||
775 | list_del(&td->list); | ||
776 | kfree(td); | ||
777 | } | ||
778 | mutex_unlock(&md->table_devices_lock); | ||
779 | } | ||
780 | EXPORT_SYMBOL(dm_put_table_device); | ||
781 | |||
782 | static void free_table_devices(struct list_head *devices) | ||
783 | { | ||
784 | struct list_head *tmp, *next; | ||
785 | |||
786 | list_for_each_safe(tmp, next, devices) { | ||
787 | struct table_device *td = list_entry(tmp, struct table_device, list); | ||
788 | |||
789 | DMWARN("dm_destroy: %s still exists with %d references", | ||
790 | td->dm_dev.name, atomic_read(&td->count)); | ||
791 | kfree(td); | ||
792 | } | ||
793 | } | ||
794 | |||
795 | /* | ||
673 | * Get the geometry associated with a dm device | 796 | * Get the geometry associated with a dm device |
674 | */ | 797 | */ |
675 | int dm_get_geometry(struct mapped_device *md, struct hd_geometry *geo) | 798 | int dm_get_geometry(struct mapped_device *md, struct hd_geometry *geo) |
@@ -1944,12 +2067,14 @@ static struct mapped_device *alloc_dev(int minor) | |||
1944 | md->type = DM_TYPE_NONE; | 2067 | md->type = DM_TYPE_NONE; |
1945 | mutex_init(&md->suspend_lock); | 2068 | mutex_init(&md->suspend_lock); |
1946 | mutex_init(&md->type_lock); | 2069 | mutex_init(&md->type_lock); |
2070 | mutex_init(&md->table_devices_lock); | ||
1947 | spin_lock_init(&md->deferred_lock); | 2071 | spin_lock_init(&md->deferred_lock); |
1948 | atomic_set(&md->holders, 1); | 2072 | atomic_set(&md->holders, 1); |
1949 | atomic_set(&md->open_count, 0); | 2073 | atomic_set(&md->open_count, 0); |
1950 | atomic_set(&md->event_nr, 0); | 2074 | atomic_set(&md->event_nr, 0); |
1951 | atomic_set(&md->uevent_seq, 0); | 2075 | atomic_set(&md->uevent_seq, 0); |
1952 | INIT_LIST_HEAD(&md->uevent_list); | 2076 | INIT_LIST_HEAD(&md->uevent_list); |
2077 | INIT_LIST_HEAD(&md->table_devices); | ||
1953 | spin_lock_init(&md->uevent_lock); | 2078 | spin_lock_init(&md->uevent_lock); |
1954 | 2079 | ||
1955 | md->queue = blk_alloc_queue(GFP_KERNEL); | 2080 | md->queue = blk_alloc_queue(GFP_KERNEL); |
@@ -2035,6 +2160,7 @@ static void free_dev(struct mapped_device *md) | |||
2035 | blk_integrity_unregister(md->disk); | 2160 | blk_integrity_unregister(md->disk); |
2036 | del_gendisk(md->disk); | 2161 | del_gendisk(md->disk); |
2037 | cleanup_srcu_struct(&md->io_barrier); | 2162 | cleanup_srcu_struct(&md->io_barrier); |
2163 | free_table_devices(&md->table_devices); | ||
2038 | free_minor(minor); | 2164 | free_minor(minor); |
2039 | 2165 | ||
2040 | spin_lock(&_minor_lock); | 2166 | spin_lock(&_minor_lock); |