diff options
Diffstat (limited to 'block/genhd.c')
-rw-r--r-- | block/genhd.c | 129 |
1 files changed, 109 insertions, 20 deletions
diff --git a/block/genhd.c b/block/genhd.c index e1cb96fb883e..c2b14aa69d58 100644 --- a/block/genhd.c +++ b/block/genhd.c | |||
@@ -52,14 +52,21 @@ static struct device_type disk_type; | |||
52 | */ | 52 | */ |
53 | struct hd_struct *disk_get_part(struct gendisk *disk, int partno) | 53 | struct hd_struct *disk_get_part(struct gendisk *disk, int partno) |
54 | { | 54 | { |
55 | struct hd_struct *part; | 55 | struct hd_struct *part = NULL; |
56 | struct disk_part_tbl *ptbl; | ||
56 | 57 | ||
57 | if (unlikely(partno < 0 || partno >= disk_max_parts(disk))) | 58 | if (unlikely(partno < 0)) |
58 | return NULL; | 59 | return NULL; |
60 | |||
59 | rcu_read_lock(); | 61 | rcu_read_lock(); |
60 | part = rcu_dereference(disk->__part[partno]); | 62 | |
61 | if (part) | 63 | ptbl = rcu_dereference(disk->part_tbl); |
62 | get_device(part_to_dev(part)); | 64 | if (likely(partno < ptbl->len)) { |
65 | part = rcu_dereference(ptbl->part[partno]); | ||
66 | if (part) | ||
67 | get_device(part_to_dev(part)); | ||
68 | } | ||
69 | |||
63 | rcu_read_unlock(); | 70 | rcu_read_unlock(); |
64 | 71 | ||
65 | return part; | 72 | return part; |
@@ -80,17 +87,24 @@ EXPORT_SYMBOL_GPL(disk_get_part); | |||
80 | void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk, | 87 | void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk, |
81 | unsigned int flags) | 88 | unsigned int flags) |
82 | { | 89 | { |
90 | struct disk_part_tbl *ptbl; | ||
91 | |||
92 | rcu_read_lock(); | ||
93 | ptbl = rcu_dereference(disk->part_tbl); | ||
94 | |||
83 | piter->disk = disk; | 95 | piter->disk = disk; |
84 | piter->part = NULL; | 96 | piter->part = NULL; |
85 | 97 | ||
86 | if (flags & DISK_PITER_REVERSE) | 98 | if (flags & DISK_PITER_REVERSE) |
87 | piter->idx = disk_max_parts(piter->disk) - 1; | 99 | piter->idx = ptbl->len - 1; |
88 | else if (flags & DISK_PITER_INCL_PART0) | 100 | else if (flags & DISK_PITER_INCL_PART0) |
89 | piter->idx = 0; | 101 | piter->idx = 0; |
90 | else | 102 | else |
91 | piter->idx = 1; | 103 | piter->idx = 1; |
92 | 104 | ||
93 | piter->flags = flags; | 105 | piter->flags = flags; |
106 | |||
107 | rcu_read_unlock(); | ||
94 | } | 108 | } |
95 | EXPORT_SYMBOL_GPL(disk_part_iter_init); | 109 | EXPORT_SYMBOL_GPL(disk_part_iter_init); |
96 | 110 | ||
@@ -105,13 +119,16 @@ EXPORT_SYMBOL_GPL(disk_part_iter_init); | |||
105 | */ | 119 | */ |
106 | struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) | 120 | struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) |
107 | { | 121 | { |
122 | struct disk_part_tbl *ptbl; | ||
108 | int inc, end; | 123 | int inc, end; |
109 | 124 | ||
110 | /* put the last partition */ | 125 | /* put the last partition */ |
111 | disk_put_part(piter->part); | 126 | disk_put_part(piter->part); |
112 | piter->part = NULL; | 127 | piter->part = NULL; |
113 | 128 | ||
129 | /* get part_tbl */ | ||
114 | rcu_read_lock(); | 130 | rcu_read_lock(); |
131 | ptbl = rcu_dereference(piter->disk->part_tbl); | ||
115 | 132 | ||
116 | /* determine iteration parameters */ | 133 | /* determine iteration parameters */ |
117 | if (piter->flags & DISK_PITER_REVERSE) { | 134 | if (piter->flags & DISK_PITER_REVERSE) { |
@@ -122,14 +139,14 @@ struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) | |||
122 | end = 0; | 139 | end = 0; |
123 | } else { | 140 | } else { |
124 | inc = 1; | 141 | inc = 1; |
125 | end = disk_max_parts(piter->disk); | 142 | end = ptbl->len; |
126 | } | 143 | } |
127 | 144 | ||
128 | /* iterate to the next partition */ | 145 | /* iterate to the next partition */ |
129 | for (; piter->idx != end; piter->idx += inc) { | 146 | for (; piter->idx != end; piter->idx += inc) { |
130 | struct hd_struct *part; | 147 | struct hd_struct *part; |
131 | 148 | ||
132 | part = rcu_dereference(piter->disk->__part[piter->idx]); | 149 | part = rcu_dereference(ptbl->part[piter->idx]); |
133 | if (!part) | 150 | if (!part) |
134 | continue; | 151 | continue; |
135 | if (!(piter->flags & DISK_PITER_INCL_EMPTY) && !part->nr_sects) | 152 | if (!(piter->flags & DISK_PITER_INCL_EMPTY) && !part->nr_sects) |
@@ -180,10 +197,13 @@ EXPORT_SYMBOL_GPL(disk_part_iter_exit); | |||
180 | */ | 197 | */ |
181 | struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) | 198 | struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) |
182 | { | 199 | { |
200 | struct disk_part_tbl *ptbl; | ||
183 | int i; | 201 | int i; |
184 | 202 | ||
185 | for (i = 1; i < disk_max_parts(disk); i++) { | 203 | ptbl = rcu_dereference(disk->part_tbl); |
186 | struct hd_struct *part = rcu_dereference(disk->__part[i]); | 204 | |
205 | for (i = 1; i < ptbl->len; i++) { | ||
206 | struct hd_struct *part = rcu_dereference(ptbl->part[i]); | ||
187 | 207 | ||
188 | if (part && part->start_sect <= sector && | 208 | if (part && part->start_sect <= sector && |
189 | sector < part->start_sect + part->nr_sects) | 209 | sector < part->start_sect + part->nr_sects) |
@@ -798,12 +818,86 @@ static struct attribute_group *disk_attr_groups[] = { | |||
798 | NULL | 818 | NULL |
799 | }; | 819 | }; |
800 | 820 | ||
821 | static void disk_free_ptbl_rcu_cb(struct rcu_head *head) | ||
822 | { | ||
823 | struct disk_part_tbl *ptbl = | ||
824 | container_of(head, struct disk_part_tbl, rcu_head); | ||
825 | |||
826 | kfree(ptbl); | ||
827 | } | ||
828 | |||
829 | /** | ||
830 | * disk_replace_part_tbl - replace disk->part_tbl in RCU-safe way | ||
831 | * @disk: disk to replace part_tbl for | ||
832 | * @new_ptbl: new part_tbl to install | ||
833 | * | ||
834 | * Replace disk->part_tbl with @new_ptbl in RCU-safe way. The | ||
835 | * original ptbl is freed using RCU callback. | ||
836 | * | ||
837 | * LOCKING: | ||
838 | * Matching bd_mutx locked. | ||
839 | */ | ||
840 | static void disk_replace_part_tbl(struct gendisk *disk, | ||
841 | struct disk_part_tbl *new_ptbl) | ||
842 | { | ||
843 | struct disk_part_tbl *old_ptbl = disk->part_tbl; | ||
844 | |||
845 | rcu_assign_pointer(disk->part_tbl, new_ptbl); | ||
846 | if (old_ptbl) | ||
847 | call_rcu(&old_ptbl->rcu_head, disk_free_ptbl_rcu_cb); | ||
848 | } | ||
849 | |||
850 | /** | ||
851 | * disk_expand_part_tbl - expand disk->part_tbl | ||
852 | * @disk: disk to expand part_tbl for | ||
853 | * @partno: expand such that this partno can fit in | ||
854 | * | ||
855 | * Expand disk->part_tbl such that @partno can fit in. disk->part_tbl | ||
856 | * uses RCU to allow unlocked dereferencing for stats and other stuff. | ||
857 | * | ||
858 | * LOCKING: | ||
859 | * Matching bd_mutex locked, might sleep. | ||
860 | * | ||
861 | * RETURNS: | ||
862 | * 0 on success, -errno on failure. | ||
863 | */ | ||
864 | int disk_expand_part_tbl(struct gendisk *disk, int partno) | ||
865 | { | ||
866 | struct disk_part_tbl *old_ptbl = disk->part_tbl; | ||
867 | struct disk_part_tbl *new_ptbl; | ||
868 | int len = old_ptbl ? old_ptbl->len : 0; | ||
869 | int target = partno + 1; | ||
870 | size_t size; | ||
871 | int i; | ||
872 | |||
873 | /* disk_max_parts() is zero during initialization, ignore if so */ | ||
874 | if (disk_max_parts(disk) && target > disk_max_parts(disk)) | ||
875 | return -EINVAL; | ||
876 | |||
877 | if (target <= len) | ||
878 | return 0; | ||
879 | |||
880 | size = sizeof(*new_ptbl) + target * sizeof(new_ptbl->part[0]); | ||
881 | new_ptbl = kzalloc_node(size, GFP_KERNEL, disk->node_id); | ||
882 | if (!new_ptbl) | ||
883 | return -ENOMEM; | ||
884 | |||
885 | INIT_RCU_HEAD(&new_ptbl->rcu_head); | ||
886 | new_ptbl->len = target; | ||
887 | |||
888 | for (i = 0; i < len; i++) | ||
889 | rcu_assign_pointer(new_ptbl->part[i], old_ptbl->part[i]); | ||
890 | |||
891 | disk_replace_part_tbl(disk, new_ptbl); | ||
892 | return 0; | ||
893 | } | ||
894 | |||
801 | static void disk_release(struct device *dev) | 895 | static void disk_release(struct device *dev) |
802 | { | 896 | { |
803 | struct gendisk *disk = dev_to_disk(dev); | 897 | struct gendisk *disk = dev_to_disk(dev); |
804 | 898 | ||
805 | kfree(disk->random); | 899 | kfree(disk->random); |
806 | kfree(disk->__part); | 900 | disk_replace_part_tbl(disk, NULL); |
807 | free_part_stats(&disk->part0); | 901 | free_part_stats(&disk->part0); |
808 | kfree(disk); | 902 | kfree(disk); |
809 | } | 903 | } |
@@ -948,22 +1042,16 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) | |||
948 | disk = kmalloc_node(sizeof(struct gendisk), | 1042 | disk = kmalloc_node(sizeof(struct gendisk), |
949 | GFP_KERNEL | __GFP_ZERO, node_id); | 1043 | GFP_KERNEL | __GFP_ZERO, node_id); |
950 | if (disk) { | 1044 | if (disk) { |
951 | int tot_minors = minors + ext_minors; | ||
952 | int size = tot_minors * sizeof(struct hd_struct *); | ||
953 | |||
954 | if (!init_part_stats(&disk->part0)) { | 1045 | if (!init_part_stats(&disk->part0)) { |
955 | kfree(disk); | 1046 | kfree(disk); |
956 | return NULL; | 1047 | return NULL; |
957 | } | 1048 | } |
958 | 1049 | if (disk_expand_part_tbl(disk, 0)) { | |
959 | disk->__part = kmalloc_node(size, GFP_KERNEL | __GFP_ZERO, | 1050 | free_part_stats(&disk->part0); |
960 | node_id); | ||
961 | if (!disk->__part) { | ||
962 | free_part_stats(&disk->part0); | ||
963 | kfree(disk); | 1051 | kfree(disk); |
964 | return NULL; | 1052 | return NULL; |
965 | } | 1053 | } |
966 | disk->__part[0] = &disk->part0; | 1054 | disk->part_tbl->part[0] = &disk->part0; |
967 | 1055 | ||
968 | disk->minors = minors; | 1056 | disk->minors = minors; |
969 | disk->ext_minors = ext_minors; | 1057 | disk->ext_minors = ext_minors; |
@@ -973,6 +1061,7 @@ struct gendisk *alloc_disk_ext_node(int minors, int ext_minors, int node_id) | |||
973 | device_initialize(disk_to_dev(disk)); | 1061 | device_initialize(disk_to_dev(disk)); |
974 | INIT_WORK(&disk->async_notify, | 1062 | INIT_WORK(&disk->async_notify, |
975 | media_change_notify_thread); | 1063 | media_change_notify_thread); |
1064 | disk->node_id = node_id; | ||
976 | } | 1065 | } |
977 | return disk; | 1066 | return disk; |
978 | } | 1067 | } |