1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | linear.c : Multiple Devices driver for Linux |
4 | Copyright (C) 1994-96 Marc ZYNGIER |
5 | <zyngier@ufr-info-p7.ibp.fr> or |
6 | <maz@gloups.fdn.fr> |
7 | |
8 | Linear mode management functions. |
9 | |
10 | */ |
11 | |
12 | #include <linux/blkdev.h> |
13 | #include <linux/raid/md_u.h> |
14 | #include <linux/seq_file.h> |
15 | #include <linux/module.h> |
16 | #include <linux/slab.h> |
17 | #include <trace/events/block.h> |
18 | #include "md.h" |
19 | #include "md-linear.h" |
20 | |
21 | /* |
22 | * find which device holds a particular offset |
23 | */ |
24 | static inline struct dev_info *which_dev(struct mddev *mddev, sector_t sector) |
25 | { |
26 | int lo, mid, hi; |
27 | struct linear_conf *conf; |
28 | |
29 | lo = 0; |
30 | hi = mddev->raid_disks - 1; |
31 | conf = mddev->private; |
32 | |
33 | /* |
34 | * Binary Search |
35 | */ |
36 | |
37 | while (hi > lo) { |
38 | |
39 | mid = (hi + lo) / 2; |
40 | if (sector < conf->disks[mid].end_sector) |
41 | hi = mid; |
42 | else |
43 | lo = mid + 1; |
44 | } |
45 | |
46 | return conf->disks + lo; |
47 | } |
48 | |
49 | static sector_t linear_size(struct mddev *mddev, sector_t sectors, int raid_disks) |
50 | { |
51 | struct linear_conf *conf; |
52 | sector_t array_sectors; |
53 | |
54 | conf = mddev->private; |
55 | WARN_ONCE(sectors || raid_disks, |
56 | "%s does not support generic reshape\n" , __func__); |
57 | array_sectors = conf->array_sectors; |
58 | |
59 | return array_sectors; |
60 | } |
61 | |
62 | static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) |
63 | { |
64 | struct linear_conf *conf; |
65 | struct md_rdev *rdev; |
66 | int i, cnt; |
67 | |
68 | conf = kzalloc(struct_size(conf, disks, raid_disks), GFP_KERNEL); |
69 | if (!conf) |
70 | return NULL; |
71 | |
72 | /* |
73 | * conf->raid_disks is copy of mddev->raid_disks. The reason to |
74 | * keep a copy of mddev->raid_disks in struct linear_conf is, |
75 | * mddev->raid_disks may not be consistent with pointers number of |
76 | * conf->disks[] when it is updated in linear_add() and used to |
77 | * iterate old conf->disks[] earray in linear_congested(). |
78 | * Here conf->raid_disks is always consitent with number of |
79 | * pointers in conf->disks[] array, and mddev->private is updated |
80 | * with rcu_assign_pointer() in linear_addr(), such race can be |
81 | * avoided. |
82 | */ |
83 | conf->raid_disks = raid_disks; |
84 | |
85 | cnt = 0; |
86 | conf->array_sectors = 0; |
87 | |
88 | rdev_for_each(rdev, mddev) { |
89 | int j = rdev->raid_disk; |
90 | struct dev_info *disk = conf->disks + j; |
91 | sector_t sectors; |
92 | |
93 | if (j < 0 || j >= raid_disks || disk->rdev) { |
94 | pr_warn("md/linear:%s: disk numbering problem. Aborting!\n" , |
95 | mdname(mddev)); |
96 | goto out; |
97 | } |
98 | |
99 | disk->rdev = rdev; |
100 | if (mddev->chunk_sectors) { |
101 | sectors = rdev->sectors; |
102 | sector_div(sectors, mddev->chunk_sectors); |
103 | rdev->sectors = sectors * mddev->chunk_sectors; |
104 | } |
105 | |
106 | disk_stack_limits(disk: mddev->gendisk, bdev: rdev->bdev, |
107 | offset: rdev->data_offset << 9); |
108 | |
109 | conf->array_sectors += rdev->sectors; |
110 | cnt++; |
111 | } |
112 | if (cnt != raid_disks) { |
113 | pr_warn("md/linear:%s: not enough drives present. Aborting!\n" , |
114 | mdname(mddev)); |
115 | goto out; |
116 | } |
117 | |
118 | /* |
119 | * Here we calculate the device offsets. |
120 | */ |
121 | conf->disks[0].end_sector = conf->disks[0].rdev->sectors; |
122 | |
123 | for (i = 1; i < raid_disks; i++) |
124 | conf->disks[i].end_sector = |
125 | conf->disks[i-1].end_sector + |
126 | conf->disks[i].rdev->sectors; |
127 | |
128 | return conf; |
129 | |
130 | out: |
131 | kfree(objp: conf); |
132 | return NULL; |
133 | } |
134 | |
135 | static int linear_run (struct mddev *mddev) |
136 | { |
137 | struct linear_conf *conf; |
138 | int ret; |
139 | |
140 | if (md_check_no_bitmap(mddev)) |
141 | return -EINVAL; |
142 | conf = linear_conf(mddev, raid_disks: mddev->raid_disks); |
143 | |
144 | if (!conf) |
145 | return 1; |
146 | mddev->private = conf; |
147 | md_set_array_sectors(mddev, array_sectors: linear_size(mddev, sectors: 0, raid_disks: 0)); |
148 | |
149 | ret = md_integrity_register(mddev); |
150 | if (ret) { |
151 | kfree(objp: conf); |
152 | mddev->private = NULL; |
153 | } |
154 | return ret; |
155 | } |
156 | |
157 | static int linear_add(struct mddev *mddev, struct md_rdev *rdev) |
158 | { |
159 | /* Adding a drive to a linear array allows the array to grow. |
160 | * It is permitted if the new drive has a matching superblock |
161 | * already on it, with raid_disk equal to raid_disks. |
162 | * It is achieved by creating a new linear_private_data structure |
163 | * and swapping it in in-place of the current one. |
164 | * The current one is never freed until the array is stopped. |
165 | * This avoids races. |
166 | */ |
167 | struct linear_conf *newconf, *oldconf; |
168 | |
169 | if (rdev->saved_raid_disk != mddev->raid_disks) |
170 | return -EINVAL; |
171 | |
172 | rdev->raid_disk = rdev->saved_raid_disk; |
173 | rdev->saved_raid_disk = -1; |
174 | |
175 | newconf = linear_conf(mddev,raid_disks: mddev->raid_disks+1); |
176 | |
177 | if (!newconf) |
178 | return -ENOMEM; |
179 | |
180 | /* newconf->raid_disks already keeps a copy of * the increased |
181 | * value of mddev->raid_disks, WARN_ONCE() is just used to make |
182 | * sure of this. It is possible that oldconf is still referenced |
183 | * in linear_congested(), therefore kfree_rcu() is used to free |
184 | * oldconf until no one uses it anymore. |
185 | */ |
186 | oldconf = rcu_dereference_protected(mddev->private, |
187 | lockdep_is_held(&mddev->reconfig_mutex)); |
188 | mddev->raid_disks++; |
189 | WARN_ONCE(mddev->raid_disks != newconf->raid_disks, |
190 | "copied raid_disks doesn't match mddev->raid_disks" ); |
191 | rcu_assign_pointer(mddev->private, newconf); |
192 | md_set_array_sectors(mddev, array_sectors: linear_size(mddev, sectors: 0, raid_disks: 0)); |
193 | set_capacity_and_notify(disk: mddev->gendisk, size: mddev->array_sectors); |
194 | kfree_rcu(oldconf, rcu); |
195 | return 0; |
196 | } |
197 | |
198 | static void linear_free(struct mddev *mddev, void *priv) |
199 | { |
200 | struct linear_conf *conf = priv; |
201 | |
202 | kfree(objp: conf); |
203 | } |
204 | |
205 | static bool linear_make_request(struct mddev *mddev, struct bio *bio) |
206 | { |
207 | struct dev_info *tmp_dev; |
208 | sector_t start_sector, end_sector, data_offset; |
209 | sector_t bio_sector = bio->bi_iter.bi_sector; |
210 | |
211 | if (unlikely(bio->bi_opf & REQ_PREFLUSH) |
212 | && md_flush_request(mddev, bio)) |
213 | return true; |
214 | |
215 | tmp_dev = which_dev(mddev, sector: bio_sector); |
216 | start_sector = tmp_dev->end_sector - tmp_dev->rdev->sectors; |
217 | end_sector = tmp_dev->end_sector; |
218 | data_offset = tmp_dev->rdev->data_offset; |
219 | |
220 | if (unlikely(bio_sector >= end_sector || |
221 | bio_sector < start_sector)) |
222 | goto out_of_bounds; |
223 | |
224 | if (unlikely(is_rdev_broken(tmp_dev->rdev))) { |
225 | md_error(mddev, rdev: tmp_dev->rdev); |
226 | bio_io_error(bio); |
227 | return true; |
228 | } |
229 | |
230 | if (unlikely(bio_end_sector(bio) > end_sector)) { |
231 | /* This bio crosses a device boundary, so we have to split it */ |
232 | struct bio *split = bio_split(bio, sectors: end_sector - bio_sector, |
233 | GFP_NOIO, bs: &mddev->bio_set); |
234 | bio_chain(split, bio); |
235 | submit_bio_noacct(bio); |
236 | bio = split; |
237 | } |
238 | |
239 | md_account_bio(mddev, bio: &bio); |
240 | bio_set_dev(bio, bdev: tmp_dev->rdev->bdev); |
241 | bio->bi_iter.bi_sector = bio->bi_iter.bi_sector - |
242 | start_sector + data_offset; |
243 | |
244 | if (unlikely((bio_op(bio) == REQ_OP_DISCARD) && |
245 | !bdev_max_discard_sectors(bio->bi_bdev))) { |
246 | /* Just ignore it */ |
247 | bio_endio(bio); |
248 | } else { |
249 | if (mddev->gendisk) |
250 | trace_block_bio_remap(bio, dev: disk_devt(disk: mddev->gendisk), |
251 | from: bio_sector); |
252 | mddev_check_write_zeroes(mddev, bio); |
253 | submit_bio_noacct(bio); |
254 | } |
255 | return true; |
256 | |
257 | out_of_bounds: |
258 | pr_err("md/linear:%s: make_request: Sector %llu out of bounds on dev %pg: %llu sectors, offset %llu\n" , |
259 | mdname(mddev), |
260 | (unsigned long long)bio->bi_iter.bi_sector, |
261 | tmp_dev->rdev->bdev, |
262 | (unsigned long long)tmp_dev->rdev->sectors, |
263 | (unsigned long long)start_sector); |
264 | bio_io_error(bio); |
265 | return true; |
266 | } |
267 | |
268 | static void linear_status (struct seq_file *seq, struct mddev *mddev) |
269 | { |
270 | seq_printf(m: seq, fmt: " %dk rounding" , mddev->chunk_sectors / 2); |
271 | } |
272 | |
273 | static void linear_error(struct mddev *mddev, struct md_rdev *rdev) |
274 | { |
275 | if (!test_and_set_bit(nr: MD_BROKEN, addr: &mddev->flags)) { |
276 | char *md_name = mdname(mddev); |
277 | |
278 | pr_crit("md/linear%s: Disk failure on %pg detected, failing array.\n" , |
279 | md_name, rdev->bdev); |
280 | } |
281 | } |
282 | |
283 | static void linear_quiesce(struct mddev *mddev, int state) |
284 | { |
285 | } |
286 | |
287 | static struct md_personality linear_personality = |
288 | { |
289 | .name = "linear" , |
290 | .level = LEVEL_LINEAR, |
291 | .owner = THIS_MODULE, |
292 | .make_request = linear_make_request, |
293 | .run = linear_run, |
294 | .free = linear_free, |
295 | .status = linear_status, |
296 | .hot_add_disk = linear_add, |
297 | .size = linear_size, |
298 | .quiesce = linear_quiesce, |
299 | .error_handler = linear_error, |
300 | }; |
301 | |
302 | static int __init linear_init (void) |
303 | { |
304 | return register_md_personality (p: &linear_personality); |
305 | } |
306 | |
307 | static void linear_exit (void) |
308 | { |
309 | unregister_md_personality (p: &linear_personality); |
310 | } |
311 | |
312 | module_init(linear_init); |
313 | module_exit(linear_exit); |
314 | MODULE_LICENSE("GPL" ); |
315 | MODULE_DESCRIPTION("Linear device concatenation personality for MD (deprecated)" ); |
316 | MODULE_ALIAS("md-personality-1" ); /* LINEAR - deprecated*/ |
317 | MODULE_ALIAS("md-linear" ); |
318 | MODULE_ALIAS("md-level--1" ); |
319 | |