aboutsummaryrefslogtreecommitdiffstats
path: root/block/genhd.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/genhd.c')
-rw-r--r--block/genhd.c129
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 */
53struct hd_struct *disk_get_part(struct gendisk *disk, int partno) 53struct 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);
80void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk, 87void 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}
95EXPORT_SYMBOL_GPL(disk_part_iter_init); 109EXPORT_SYMBOL_GPL(disk_part_iter_init);
96 110
@@ -105,13 +119,16 @@ EXPORT_SYMBOL_GPL(disk_part_iter_init);
105 */ 119 */
106struct hd_struct *disk_part_iter_next(struct disk_part_iter *piter) 120struct 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 */
181struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector) 198struct 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
821static 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 */
840static 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 */
864int 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
801static void disk_release(struct device *dev) 895static 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}