diff options
author | Tejun Heo <tj@kernel.org> | 2010-11-13 05:55:17 -0500 |
---|---|---|
committer | Tejun Heo <tj@kernel.org> | 2010-11-13 05:55:17 -0500 |
commit | e09b457bdb7e8d23fc54dcef0930ac697d8de895 (patch) | |
tree | 41035bf61a8acb08292d01c0f66e5450502688af /fs/block_dev.c | |
parent | 37004c42f7240035bc2726c340c4efa726b4818e (diff) |
block: simplify holder symlink handling
Code to manage symlinks in /sys/block/*/{holders|slaves} are overly
complex with multiple holder considerations, redundant extra
references to all involved kobjects, unused generic kobject holder
support and unnecessary mixup with bd_claim/release functionalities.
Strip it down to what's necessary (single gendisk holder) and make it
use a separate interface. This is a step for cleaning up
bd_claim/release. This patch makes dm-table slightly more complex but
it will be simplified again with further changes.
Signed-off-by: Tejun Heo <tj@kernel.org>
Acked-by: Neil Brown <neilb@suse.de>
Acked-by: Mike Snitzer <snitzer@redhat.com>
Cc: dm-devel@redhat.com
Diffstat (limited to 'fs/block_dev.c')
-rw-r--r-- | fs/block_dev.c | 322 |
1 files changed, 44 insertions, 278 deletions
diff --git a/fs/block_dev.c b/fs/block_dev.c index 06e8ff12b97c..9329068684d2 100644 --- a/fs/block_dev.c +++ b/fs/block_dev.c | |||
@@ -426,9 +426,6 @@ static void init_once(void *foo) | |||
426 | mutex_init(&bdev->bd_mutex); | 426 | mutex_init(&bdev->bd_mutex); |
427 | INIT_LIST_HEAD(&bdev->bd_inodes); | 427 | INIT_LIST_HEAD(&bdev->bd_inodes); |
428 | INIT_LIST_HEAD(&bdev->bd_list); | 428 | INIT_LIST_HEAD(&bdev->bd_list); |
429 | #ifdef CONFIG_SYSFS | ||
430 | INIT_LIST_HEAD(&bdev->bd_holder_list); | ||
431 | #endif | ||
432 | inode_init_once(&ei->vfs_inode); | 429 | inode_init_once(&ei->vfs_inode); |
433 | /* Initialize mutex for freeze. */ | 430 | /* Initialize mutex for freeze. */ |
434 | mutex_init(&bdev->bd_fsfreeze_mutex); | 431 | mutex_init(&bdev->bd_fsfreeze_mutex); |
@@ -881,314 +878,83 @@ void bd_release(struct block_device *bdev) | |||
881 | EXPORT_SYMBOL(bd_release); | 878 | EXPORT_SYMBOL(bd_release); |
882 | 879 | ||
883 | #ifdef CONFIG_SYSFS | 880 | #ifdef CONFIG_SYSFS |
884 | /* | ||
885 | * Functions for bd_claim_by_kobject / bd_release_from_kobject | ||
886 | * | ||
887 | * If a kobject is passed to bd_claim_by_kobject() | ||
888 | * and the kobject has a parent directory, | ||
889 | * following symlinks are created: | ||
890 | * o from the kobject to the claimed bdev | ||
891 | * o from "holders" directory of the bdev to the parent of the kobject | ||
892 | * bd_release_from_kobject() removes these symlinks. | ||
893 | * | ||
894 | * Example: | ||
895 | * If /dev/dm-0 maps to /dev/sda, kobject corresponding to | ||
896 | * /sys/block/dm-0/slaves is passed to bd_claim_by_kobject(), then: | ||
897 | * /sys/block/dm-0/slaves/sda --> /sys/block/sda | ||
898 | * /sys/block/sda/holders/dm-0 --> /sys/block/dm-0 | ||
899 | */ | ||
900 | |||
901 | static int add_symlink(struct kobject *from, struct kobject *to) | 881 | static int add_symlink(struct kobject *from, struct kobject *to) |
902 | { | 882 | { |
903 | if (!from || !to) | ||
904 | return 0; | ||
905 | return sysfs_create_link(from, to, kobject_name(to)); | 883 | return sysfs_create_link(from, to, kobject_name(to)); |
906 | } | 884 | } |
907 | 885 | ||
908 | static void del_symlink(struct kobject *from, struct kobject *to) | 886 | static void del_symlink(struct kobject *from, struct kobject *to) |
909 | { | 887 | { |
910 | if (!from || !to) | ||
911 | return; | ||
912 | sysfs_remove_link(from, kobject_name(to)); | 888 | sysfs_remove_link(from, kobject_name(to)); |
913 | } | 889 | } |
914 | 890 | ||
915 | /* | ||
916 | * 'struct bd_holder' contains pointers to kobjects symlinked by | ||
917 | * bd_claim_by_kobject. | ||
918 | * It's connected to bd_holder_list which is protected by bdev->bd_sem. | ||
919 | */ | ||
920 | struct bd_holder { | ||
921 | struct list_head list; /* chain of holders of the bdev */ | ||
922 | int count; /* references from the holder */ | ||
923 | struct kobject *sdir; /* holder object, e.g. "/block/dm-0/slaves" */ | ||
924 | struct kobject *hdev; /* e.g. "/block/dm-0" */ | ||
925 | struct kobject *hdir; /* e.g. "/block/sda/holders" */ | ||
926 | struct kobject *sdev; /* e.g. "/block/sda" */ | ||
927 | }; | ||
928 | |||
929 | /* | ||
930 | * Get references of related kobjects at once. | ||
931 | * Returns 1 on success. 0 on failure. | ||
932 | * | ||
933 | * Should call bd_holder_release_dirs() after successful use. | ||
934 | */ | ||
935 | static int bd_holder_grab_dirs(struct block_device *bdev, | ||
936 | struct bd_holder *bo) | ||
937 | { | ||
938 | if (!bdev || !bo) | ||
939 | return 0; | ||
940 | |||
941 | bo->sdir = kobject_get(bo->sdir); | ||
942 | if (!bo->sdir) | ||
943 | return 0; | ||
944 | |||
945 | bo->hdev = kobject_get(bo->sdir->parent); | ||
946 | if (!bo->hdev) | ||
947 | goto fail_put_sdir; | ||
948 | |||
949 | bo->sdev = kobject_get(&part_to_dev(bdev->bd_part)->kobj); | ||
950 | if (!bo->sdev) | ||
951 | goto fail_put_hdev; | ||
952 | |||
953 | bo->hdir = kobject_get(bdev->bd_part->holder_dir); | ||
954 | if (!bo->hdir) | ||
955 | goto fail_put_sdev; | ||
956 | |||
957 | return 1; | ||
958 | |||
959 | fail_put_sdev: | ||
960 | kobject_put(bo->sdev); | ||
961 | fail_put_hdev: | ||
962 | kobject_put(bo->hdev); | ||
963 | fail_put_sdir: | ||
964 | kobject_put(bo->sdir); | ||
965 | |||
966 | return 0; | ||
967 | } | ||
968 | |||
969 | /* Put references of related kobjects at once. */ | ||
970 | static void bd_holder_release_dirs(struct bd_holder *bo) | ||
971 | { | ||
972 | kobject_put(bo->hdir); | ||
973 | kobject_put(bo->sdev); | ||
974 | kobject_put(bo->hdev); | ||
975 | kobject_put(bo->sdir); | ||
976 | } | ||
977 | |||
978 | static struct bd_holder *alloc_bd_holder(struct kobject *kobj) | ||
979 | { | ||
980 | struct bd_holder *bo; | ||
981 | |||
982 | bo = kzalloc(sizeof(*bo), GFP_KERNEL); | ||
983 | if (!bo) | ||
984 | return NULL; | ||
985 | |||
986 | bo->count = 1; | ||
987 | bo->sdir = kobj; | ||
988 | |||
989 | return bo; | ||
990 | } | ||
991 | |||
992 | static void free_bd_holder(struct bd_holder *bo) | ||
993 | { | ||
994 | kfree(bo); | ||
995 | } | ||
996 | |||
997 | /** | 891 | /** |
998 | * find_bd_holder - find matching struct bd_holder from the block device | 892 | * bd_link_disk_holder - create symlinks between holding disk and slave bdev |
893 | * @bdev: the claimed slave bdev | ||
894 | * @disk: the holding disk | ||
999 | * | 895 | * |
1000 | * @bdev: struct block device to be searched | 896 | * This functions creates the following sysfs symlinks. |
1001 | * @bo: target struct bd_holder | ||
1002 | * | 897 | * |
1003 | * Returns matching entry with @bo in @bdev->bd_holder_list. | 898 | * - from "slaves" directory of the holder @disk to the claimed @bdev |
1004 | * If found, increment the reference count and return the pointer. | 899 | * - from "holders" directory of the @bdev to the holder @disk |
1005 | * If not found, returns NULL. | ||
1006 | */ | ||
1007 | static struct bd_holder *find_bd_holder(struct block_device *bdev, | ||
1008 | struct bd_holder *bo) | ||
1009 | { | ||
1010 | struct bd_holder *tmp; | ||
1011 | |||
1012 | list_for_each_entry(tmp, &bdev->bd_holder_list, list) | ||
1013 | if (tmp->sdir == bo->sdir) { | ||
1014 | tmp->count++; | ||
1015 | return tmp; | ||
1016 | } | ||
1017 | |||
1018 | return NULL; | ||
1019 | } | ||
1020 | |||
1021 | /** | ||
1022 | * add_bd_holder - create sysfs symlinks for bd_claim() relationship | ||
1023 | * | 900 | * |
1024 | * @bdev: block device to be bd_claimed | 901 | * For example, if /dev/dm-0 maps to /dev/sda and disk for dm-0 is |
1025 | * @bo: preallocated and initialized by alloc_bd_holder() | 902 | * passed to bd_link_disk_holder(), then: |
1026 | * | 903 | * |
1027 | * Add @bo to @bdev->bd_holder_list, create symlinks. | 904 | * /sys/block/dm-0/slaves/sda --> /sys/block/sda |
905 | * /sys/block/sda/holders/dm-0 --> /sys/block/dm-0 | ||
1028 | * | 906 | * |
1029 | * Returns 0 if symlinks are created. | 907 | * The caller must have claimed @bdev before calling this function and |
1030 | * Returns -ve if something fails. | 908 | * ensure that both @bdev and @disk are valid during the creation and |
1031 | */ | 909 | * lifetime of these symlinks. |
1032 | static int add_bd_holder(struct block_device *bdev, struct bd_holder *bo) | ||
1033 | { | ||
1034 | int err; | ||
1035 | |||
1036 | if (!bo) | ||
1037 | return -EINVAL; | ||
1038 | |||
1039 | if (!bd_holder_grab_dirs(bdev, bo)) | ||
1040 | return -EBUSY; | ||
1041 | |||
1042 | err = add_symlink(bo->sdir, bo->sdev); | ||
1043 | if (err) | ||
1044 | return err; | ||
1045 | |||
1046 | err = add_symlink(bo->hdir, bo->hdev); | ||
1047 | if (err) { | ||
1048 | del_symlink(bo->sdir, bo->sdev); | ||
1049 | return err; | ||
1050 | } | ||
1051 | |||
1052 | list_add_tail(&bo->list, &bdev->bd_holder_list); | ||
1053 | return 0; | ||
1054 | } | ||
1055 | |||
1056 | /** | ||
1057 | * del_bd_holder - delete sysfs symlinks for bd_claim() relationship | ||
1058 | * | ||
1059 | * @bdev: block device to be bd_claimed | ||
1060 | * @kobj: holder's kobject | ||
1061 | * | ||
1062 | * If there is matching entry with @kobj in @bdev->bd_holder_list | ||
1063 | * and no other bd_claim() from the same kobject, | ||
1064 | * remove the struct bd_holder from the list, delete symlinks for it. | ||
1065 | * | ||
1066 | * Returns a pointer to the struct bd_holder when it's removed from the list | ||
1067 | * and ready to be freed. | ||
1068 | * Returns NULL if matching claim isn't found or there is other bd_claim() | ||
1069 | * by the same kobject. | ||
1070 | */ | ||
1071 | static struct bd_holder *del_bd_holder(struct block_device *bdev, | ||
1072 | struct kobject *kobj) | ||
1073 | { | ||
1074 | struct bd_holder *bo; | ||
1075 | |||
1076 | list_for_each_entry(bo, &bdev->bd_holder_list, list) { | ||
1077 | if (bo->sdir == kobj) { | ||
1078 | bo->count--; | ||
1079 | BUG_ON(bo->count < 0); | ||
1080 | if (!bo->count) { | ||
1081 | list_del(&bo->list); | ||
1082 | del_symlink(bo->sdir, bo->sdev); | ||
1083 | del_symlink(bo->hdir, bo->hdev); | ||
1084 | bd_holder_release_dirs(bo); | ||
1085 | return bo; | ||
1086 | } | ||
1087 | break; | ||
1088 | } | ||
1089 | } | ||
1090 | |||
1091 | return NULL; | ||
1092 | } | ||
1093 | |||
1094 | /** | ||
1095 | * bd_claim_by_kobject - bd_claim() with additional kobject signature | ||
1096 | * | ||
1097 | * @bdev: block device to be claimed | ||
1098 | * @holder: holder's signature | ||
1099 | * @kobj: holder's kobject | ||
1100 | * | 910 | * |
1101 | * Do bd_claim() and if it succeeds, create sysfs symlinks between | 911 | * CONTEXT: |
1102 | * the bdev and the holder's kobject. | 912 | * Might sleep. |
1103 | * Use bd_release_from_kobject() when relesing the claimed bdev. | ||
1104 | * | 913 | * |
1105 | * Returns 0 on success. (same as bd_claim()) | 914 | * RETURNS: |
1106 | * Returns errno on failure. | 915 | * 0 on success, -errno on failure. |
1107 | */ | 916 | */ |
1108 | static int bd_claim_by_kobject(struct block_device *bdev, void *holder, | 917 | int bd_link_disk_holder(struct block_device *bdev, struct gendisk *disk) |
1109 | struct kobject *kobj) | ||
1110 | { | 918 | { |
1111 | int err; | 919 | int ret = 0; |
1112 | struct bd_holder *bo, *found; | ||
1113 | |||
1114 | if (!kobj) | ||
1115 | return -EINVAL; | ||
1116 | |||
1117 | bo = alloc_bd_holder(kobj); | ||
1118 | if (!bo) | ||
1119 | return -ENOMEM; | ||
1120 | 920 | ||
1121 | mutex_lock(&bdev->bd_mutex); | 921 | mutex_lock(&bdev->bd_mutex); |
1122 | 922 | ||
1123 | err = bd_claim(bdev, holder); | 923 | WARN_ON_ONCE(!bdev->bd_holder || bdev->bd_holder_disk); |
1124 | if (err) | ||
1125 | goto fail; | ||
1126 | 924 | ||
1127 | found = find_bd_holder(bdev, bo); | 925 | /* FIXME: remove the following once add_disk() handles errors */ |
1128 | if (found) | 926 | if (WARN_ON(!disk->slave_dir || !bdev->bd_part->holder_dir)) |
1129 | goto fail; | 927 | goto out_unlock; |
1130 | 928 | ||
1131 | err = add_bd_holder(bdev, bo); | 929 | ret = add_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); |
1132 | if (err) | 930 | if (ret) |
1133 | bd_release(bdev); | 931 | goto out_unlock; |
1134 | else | ||
1135 | bo = NULL; | ||
1136 | fail: | ||
1137 | mutex_unlock(&bdev->bd_mutex); | ||
1138 | free_bd_holder(bo); | ||
1139 | return err; | ||
1140 | } | ||
1141 | 932 | ||
1142 | /** | 933 | ret = add_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj); |
1143 | * bd_release_from_kobject - bd_release() with additional kobject signature | 934 | if (ret) { |
1144 | * | 935 | del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); |
1145 | * @bdev: block device to be released | 936 | goto out_unlock; |
1146 | * @kobj: holder's kobject | 937 | } |
1147 | * | ||
1148 | * Do bd_release() and remove sysfs symlinks created by bd_claim_by_kobject(). | ||
1149 | */ | ||
1150 | static void bd_release_from_kobject(struct block_device *bdev, | ||
1151 | struct kobject *kobj) | ||
1152 | { | ||
1153 | if (!kobj) | ||
1154 | return; | ||
1155 | 938 | ||
1156 | mutex_lock(&bdev->bd_mutex); | 939 | bdev->bd_holder_disk = disk; |
1157 | bd_release(bdev); | 940 | out_unlock: |
1158 | free_bd_holder(del_bd_holder(bdev, kobj)); | ||
1159 | mutex_unlock(&bdev->bd_mutex); | 941 | mutex_unlock(&bdev->bd_mutex); |
942 | return ret; | ||
1160 | } | 943 | } |
944 | EXPORT_SYMBOL_GPL(bd_link_disk_holder); | ||
1161 | 945 | ||
1162 | /** | 946 | void bd_unlink_disk_holder(struct block_device *bdev) |
1163 | * bd_claim_by_disk - wrapper function for bd_claim_by_kobject() | ||
1164 | * | ||
1165 | * @bdev: block device to be claimed | ||
1166 | * @holder: holder's signature | ||
1167 | * @disk: holder's gendisk | ||
1168 | * | ||
1169 | * Call bd_claim_by_kobject() with getting @disk->slave_dir. | ||
1170 | */ | ||
1171 | int bd_claim_by_disk(struct block_device *bdev, void *holder, | ||
1172 | struct gendisk *disk) | ||
1173 | { | 947 | { |
1174 | return bd_claim_by_kobject(bdev, holder, kobject_get(disk->slave_dir)); | 948 | struct gendisk *disk = bdev->bd_holder_disk; |
1175 | } | ||
1176 | EXPORT_SYMBOL_GPL(bd_claim_by_disk); | ||
1177 | 949 | ||
1178 | /** | 950 | bdev->bd_holder_disk = NULL; |
1179 | * bd_release_from_disk - wrapper function for bd_release_from_kobject() | 951 | if (!disk) |
1180 | * | 952 | return; |
1181 | * @bdev: block device to be claimed | 953 | |
1182 | * @disk: holder's gendisk | 954 | del_symlink(disk->slave_dir, &part_to_dev(bdev->bd_part)->kobj); |
1183 | * | 955 | del_symlink(bdev->bd_part->holder_dir, &disk_to_dev(disk)->kobj); |
1184 | * Call bd_release_from_kobject() and put @disk->slave_dir. | ||
1185 | */ | ||
1186 | void bd_release_from_disk(struct block_device *bdev, struct gendisk *disk) | ||
1187 | { | ||
1188 | bd_release_from_kobject(bdev, disk->slave_dir); | ||
1189 | kobject_put(disk->slave_dir); | ||
1190 | } | 956 | } |
1191 | EXPORT_SYMBOL_GPL(bd_release_from_disk); | 957 | EXPORT_SYMBOL_GPL(bd_unlink_disk_holder); |
1192 | #endif | 958 | #endif |
1193 | 959 | ||
1194 | /* | 960 | /* |