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