diff options
Diffstat (limited to 'fs/block_dev.c')
-rw-r--r-- | fs/block_dev.c | 93 |
1 files changed, 75 insertions, 18 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c index fe3f59c14a02..333a7bb4cb9c 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
@@ -432,6 +432,9 @@ static void init_once(void *foo) | |||
432 | mutex_init(&bdev->bd_mutex); | 432 | mutex_init(&bdev->bd_mutex); |
433 | INIT_LIST_HEAD(&bdev->bd_inodes); | 433 | INIT_LIST_HEAD(&bdev->bd_inodes); |
434 | INIT_LIST_HEAD(&bdev->bd_list); | 434 | INIT_LIST_HEAD(&bdev->bd_list); |
435 | #ifdef CONFIG_SYSFS | ||
436 | INIT_LIST_HEAD(&bdev->bd_holder_disks); | ||
437 | #endif | ||
435 | inode_init_once(&ei->vfs_inode); | 438 | inode_init_once(&ei->vfs_inode); |
436 | /* Initialize mutex for freeze. */ | 439 | /* Initialize mutex for freeze. */ |
437 | mutex_init(&bdev->bd_fsfreeze_mutex); | 440 | mutex_init(&bdev->bd_fsfreeze_mutex); |
@@ -779,6 +782,23 @@ static struct block_device *bd_start_claiming(struct block_device *bdev, | |||
779 | } | 782 | } |
780 | 783 | ||
781 | #ifdef CONFIG_SYSFS | 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 | static int add_symlink(struct kobject *from, struct kobject *to) | 802 | static int add_symlink(struct kobject *from, struct kobject *to) |
783 | { | 803 | { |
784 | return sysfs_create_link(from, to, kobject_name(to)); | 804 | return sysfs_create_link(from, to, kobject_name(to)); |
@@ -794,6 +814,8 @@ static void del_symlink(struct kobject *from, struct kobject *to) | |||
794 | * @bdev: the claimed slave bdev | 814 | * @bdev: the claimed slave bdev |
795 | * @disk: the holding disk | 815 | * @disk: the holding disk |
796 | * | 816 | * |
817 | * DON'T USE THIS UNLESS YOU'RE ALREADY USING IT. | ||
818 | * | ||
797 | * This functions creates the following sysfs symlinks. | 819 | * This functions creates the following sysfs symlinks. |
798 | * | 820 | * |
799 | * - from "slaves" directory of the holder @disk to the claimed @bdev | 821 | * - from "slaves" directory of the holder @disk to the claimed @bdev |
@@ -817,47 +839,83 @@ static void del_symlink(struct kobject *from, struct kobject *to) | |||
817 | */ | 839 | */ |
818 | int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk) | 840 | int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk) |
819 | { | 841 | { |
842 | struct bd_holder_disk *holder; | ||
820 | int ret = 0; | 843 | int ret = 0; |
821 | 844 | ||
822 | mutex_lock(&bdev->bd_mutex); | 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 | /* FIXME: remove the following once add_disk() handles errors */ | 849 | /* FIXME: remove the following once add_disk() handles errors */ |
827 | if (WARN_ON(!disk->slave_dir || !bdev->bd_part->holder_dir)) | 850 | if (WARN_ON(!disk->slave_dir || !bdev->bd_part->holder_dir)) |
828 | goto out_unlock; | 851 | goto out_unlock; |
829 | 852 | ||
830 | ret = add_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); | 853 | holder = bd_find_holder_disk(bdev, disk); |
831 | if (ret) | 854 | if (holder) { |
855 | holder->refcnt++; | ||
832 | goto out_unlock; | 856 | goto out_unlock; |
857 | } | ||
833 | 858 | ||
834 | ret = add_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj); | 859 | holder = kzalloc(sizeof(*holder), GFP_KERNEL); |
835 | if (ret) { | 860 | if (!holder) { |
836 | del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); | 861 | ret = -ENOMEM; |
837 | goto out_unlock; | 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 | out_unlock: | 884 | out_unlock: |
842 | mutex_unlock(&bdev->bd_mutex); | 885 | mutex_unlock(&bdev->bd_mutex); |
843 | return ret; | 886 | return ret; |
844 | } | 887 | } |
845 | EXPORT_SYMBOL_GPL(bd_link_disk_holder); | 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; | 904 | mutex_lock(&bdev->bd_mutex); |
852 | if (!disk) | ||
853 | return; | ||
854 | 905 | ||
855 | del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); | 906 | holder = bd_find_holder_disk(bdev, disk); |
856 | del_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj); | 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 | 918 | EXPORT_SYMBOL_GPL(bd_unlink_disk_holder); |
859 | static inline void bd_unlink_disk_holder(struct block_device *bdev) | ||
860 | { } | ||
861 | #endif | 919 | #endif |
862 | 920 | ||
863 | /** | 921 | /** |
@@ -1380,7 +1438,6 @@ int blkdev_put(struct block_device *bdev, fmode_t mode) | |||
1380 | * unblock evpoll if it was a write holder. | 1438 | * unblock evpoll if it was a write holder. |
1381 | */ | 1439 | */ |
1382 | if (bdev_free) { | 1440 | if (bdev_free) { |
1383 | bd_unlink_disk_holder(bdev); | ||
1384 | if (bdev->bd_write_holder) { | 1441 | if (bdev->bd_write_holder) { |
1385 | disk_unblock_events(bdev->bd_disk); | 1442 | disk_unblock_events(bdev->bd_disk); |
1386 | bdev->bd_write_holder = false; | 1443 | bdev->bd_write_holder = false; |