Commit 49731baa41df404c2c3f44555869ab387363af43
Committed by
Jens Axboe
1 parent
c553f8e335
Exists in
master
and in
39 other branches
block: restore multiple bd_link_disk_holder() support
Commit e09b457b (block: simplify holder symlink handling) incorrectly assumed that there is only one link at maximum. dm may use multiple links and expects block layer to track reference count for each link, which is different from and unrelated to the exclusive device holder identified by @holder when the device is opened. Remove the single holder assumption and automatic removal of the link and revive the per-link reference count tracking. The code essentially behaves the same as before commit e09b457b sans the unnecessary kobject reference count dancing. While at it, note that this facility should not be used by anyone else than the current ones. Sysfs symlinks shouldn't be abused like this and the whole thing doesn't belong in the block layer at all. Signed-off-by: Tejun Heo <tj@kernel.org> Reported-by: Milan Broz <mbroz@redhat.com> Cc: Jun'ichi Nomura <j-nomura@ce.jp.nec.com> Cc: Neil Brown <neilb@suse.de> Cc: linux-raid@vger.kernel.org Cc: Kay Sievers <kay.sievers@vrfy.org> Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Showing 4 changed files with 84 additions and 19 deletions Side-by-side Diff
drivers/md/dm-table.c
drivers/md/md.c
fs/block_dev.c
... | ... | @@ -432,6 +432,9 @@ |
432 | 432 | mutex_init(&bdev->bd_mutex); |
433 | 433 | INIT_LIST_HEAD(&bdev->bd_inodes); |
434 | 434 | INIT_LIST_HEAD(&bdev->bd_list); |
435 | +#ifdef CONFIG_SYSFS | |
436 | + INIT_LIST_HEAD(&bdev->bd_holder_disks); | |
437 | +#endif | |
435 | 438 | inode_init_once(&ei->vfs_inode); |
436 | 439 | /* Initialize mutex for freeze. */ |
437 | 440 | mutex_init(&bdev->bd_fsfreeze_mutex); |
... | ... | @@ -779,6 +782,23 @@ |
779 | 782 | } |
780 | 783 | |
781 | 784 | #ifdef CONFIG_SYSFS |
785 | +struct bd_holder_disk { | |
786 | + struct list_head list; | |
787 | + struct gendisk *disk; | |
788 | + int refcnt; | |
789 | +}; | |
790 | + | |
791 | +static struct bd_holder_disk *bd_find_holder_disk(struct block_device *bdev, | |
792 | + struct gendisk *disk) | |
793 | +{ | |
794 | + struct bd_holder_disk *holder; | |
795 | + | |
796 | + list_for_each_entry(holder, &bdev->bd_holder_disks, list) | |
797 | + if (holder->disk == disk) | |
798 | + return holder; | |
799 | + return NULL; | |
800 | +} | |
801 | + | |
782 | 802 | static int add_symlink(struct kobject *from, struct kobject *to) |
783 | 803 | { |
784 | 804 | return sysfs_create_link(from, to, kobject_name(to)); |
... | ... | @@ -794,6 +814,8 @@ |
794 | 814 | * @bdev: the claimed slave bdev |
795 | 815 | * @disk: the holding disk |
796 | 816 | * |
817 | + * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT. | |
818 | + * | |
797 | 819 | * This functions creates the following sysfs symlinks. |
798 | 820 | * |
799 | 821 | * - from "slaves" directory of the holder @disk to the claimed @bdev |
800 | 822 | |
801 | 823 | |
802 | 824 | |
803 | 825 | |
804 | 826 | |
805 | 827 | |
806 | 828 | |
807 | 829 | |
808 | 830 | |
809 | 831 | |
... | ... | @@ -817,47 +839,83 @@ |
817 | 839 | */ |
818 | 840 | int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk) |
819 | 841 | { |
842 | + struct bd_holder_disk *holder; | |
820 | 843 | int ret = 0; |
821 | 844 | |
822 | 845 | mutex_lock(&bdev->bd_mutex); |
823 | 846 | |
824 | - WARN_ON_ONCE(!bdev->bd_holder || bdev->bd_holder_disk); | |
847 | + WARN_ON_ONCE(!bdev->bd_holder); | |
825 | 848 | |
826 | 849 | /* FIXME: remove the following once add_disk() handles errors */ |
827 | 850 | if (WARN_ON(!disk->slave_dir || !bdev->bd_part->holder_dir)) |
828 | 851 | goto out_unlock; |
829 | 852 | |
830 | - ret = add_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); | |
831 | - if (ret) | |
853 | + holder = bd_find_holder_disk(bdev, disk); | |
854 | + if (holder) { | |
855 | + holder->refcnt++; | |
832 | 856 | goto out_unlock; |
857 | + } | |
833 | 858 | |
834 | - ret = add_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj); | |
835 | - if (ret) { | |
836 | - del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); | |
859 | + holder = kzalloc(sizeof(*holder), GFP_KERNEL); | |
860 | + if (!holder) { | |
861 | + ret = -ENOMEM; | |
837 | 862 | goto out_unlock; |
838 | 863 | } |
839 | 864 | |
840 | - bdev->bd_holder_disk = disk; | |
865 | + INIT_LIST_HEAD(&holder->list); | |
866 | + holder->disk = disk; | |
867 | + holder->refcnt = 1; | |
868 | + | |
869 | + ret = add_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); | |
870 | + if (ret) | |
871 | + goto out_free; | |
872 | + | |
873 | + ret = add_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj); | |
874 | + if (ret) | |
875 | + goto out_del; | |
876 | + | |
877 | + list_add(&holder->list, &bdev->bd_holder_disks); | |
878 | + goto out_unlock; | |
879 | + | |
880 | +out_del: | |
881 | + del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); | |
882 | +out_free: | |
883 | + kfree(holder); | |
841 | 884 | out_unlock: |
842 | 885 | mutex_unlock(&bdev->bd_mutex); |
843 | 886 | return ret; |
844 | 887 | } |
845 | 888 | EXPORT_SYMBOL_GPL(bd_link_disk_holder); |
846 | 889 | |
847 | -static void bd_unlink_disk_holder(struct block_device *bdev) | |
890 | +/** | |
891 | + * bd_unlink_disk_holder - destroy symlinks created by bd_link_disk_holder() | |
892 | + * @bdev: the calimed slave bdev | |
893 | + * @disk: the holding disk | |
894 | + * | |
895 | + * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT. | |
896 | + * | |
897 | + * CONTEXT: | |
898 | + * Might sleep. | |
899 | + */ | |
900 | +void bd_unlink_disk_holder(struct block_device *bdev, struct gendisk *disk) | |
848 | 901 | { |
849 | - struct gendisk *disk = bdev->bd_holder_disk; | |
902 | + struct bd_holder_disk *holder; | |
850 | 903 | |
851 | - bdev->bd_holder_disk = NULL; | |
852 | - if (!disk) | |
853 | - return; | |
904 | + mutex_lock(&bdev->bd_mutex); | |
854 | 905 | |
855 | - del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); | |
856 | - del_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj); | |
906 | + holder = bd_find_holder_disk(bdev, disk); | |
907 | + | |
908 | + if (!WARN_ON_ONCE(holder == NULL) && !--holder->refcnt) { | |
909 | + del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); | |
910 | + del_symlink(bdev->bd_part->holder_dir, | |
911 | + &disk_to_dev(disk)->kobj); | |
912 | + list_del_init(&holder->list); | |
913 | + kfree(holder); | |
914 | + } | |
915 | + | |
916 | + mutex_unlock(&bdev->bd_mutex); | |
857 | 917 | } |
858 | -#else | |
859 | -static inline void bd_unlink_disk_holder(struct block_device *bdev) | |
860 | -{ } | |
918 | +EXPORT_SYMBOL_GPL(bd_unlink_disk_holder); | |
861 | 919 | #endif |
862 | 920 | |
863 | 921 | /** |
... | ... | @@ -1380,7 +1438,6 @@ |
1380 | 1438 | * unblock evpoll if it was a write holder. |
1381 | 1439 | */ |
1382 | 1440 | if (bdev_free) { |
1383 | - bd_unlink_disk_holder(bdev); | |
1384 | 1441 | if (bdev->bd_write_holder) { |
1385 | 1442 | disk_unblock_events(bdev->bd_disk); |
1386 | 1443 | bdev->bd_write_holder = false; |
include/linux/fs.h
... | ... | @@ -666,7 +666,7 @@ |
666 | 666 | int bd_holders; |
667 | 667 | bool bd_write_holder; |
668 | 668 | #ifdef CONFIG_SYSFS |
669 | - struct gendisk * bd_holder_disk; /* for sysfs slave linkng */ | |
669 | + struct list_head bd_holder_disks; | |
670 | 670 | #endif |
671 | 671 | struct block_device * bd_contains; |
672 | 672 | unsigned bd_block_size; |
673 | 673 | |
... | ... | @@ -2058,11 +2058,17 @@ |
2058 | 2058 | extern int blkdev_put(struct block_device *bdev, fmode_t mode); |
2059 | 2059 | #ifdef CONFIG_SYSFS |
2060 | 2060 | extern int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk); |
2061 | +extern void bd_unlink_disk_holder(struct block_device *bdev, | |
2062 | + struct gendisk *disk); | |
2061 | 2063 | #else |
2062 | 2064 | static inline int bd_link_disk_holder(struct block_device *bdev, |
2063 | 2065 | struct gendisk *disk) |
2064 | 2066 | { |
2065 | 2067 | return 0; |
2068 | +} | |
2069 | +static inline void bd_unlink_disk_holder(struct block_device *bdev, | |
2070 | + struct gendisk *disk) | |
2071 | +{ | |
2066 | 2072 | } |
2067 | 2073 | #endif |
2068 | 2074 | #endif |