1/* 2 linear.c : Multiple Devices driver for Linux 3 Copyright (C) 1994-96 Marc ZYNGIER 4 <zyngier@ufr-info-p7.ibp.fr> or 5 <maz@gloups.fdn.fr> 6 7 Linear mode management functions. 8 9 This program is free software; you can redistribute it and/or modify 10 it under the terms of the GNU General Public License as published by 11 the Free Software Foundation; either version 2, or (at your option) 12 any later version. 13 14 You should have received a copy of the GNU General Public License 15 (for example /usr/src/linux/COPYING); if not, write to the Free 16 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17*/ 18 19#include <linux/blkdev.h> 20#include <linux/raid/md_u.h> 21#include <linux/seq_file.h> 22#include <linux/module.h> 23#include <linux/slab.h> 24#include "md.h" 25#include "linear.h" 26 27/* 28 * find which device holds a particular offset 29 */ 30static 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 55static int linear_congested(struct mddev *mddev, int bits) 56{ 57 struct linear_conf *conf; 58 int i, ret = 0; 59 60 conf = mddev->private; 61 62 for (i = 0; i < mddev->raid_disks && !ret ; i++) { 63 struct request_queue *q = bdev_get_queue(conf->disks[i].rdev->bdev); 64 ret |= bdi_congested(&q->backing_dev_info, bits); 65 } 66 67 return ret; 68} 69 70static sector_t linear_size(struct mddev *mddev, sector_t sectors, int raid_disks) 71{ 72 struct linear_conf *conf; 73 sector_t array_sectors; 74 75 conf = mddev->private; 76 WARN_ONCE(sectors || raid_disks, 77 "%s does not support generic reshape\n", __func__); 78 array_sectors = conf->array_sectors; 79 80 return array_sectors; 81} 82 83static struct linear_conf *linear_conf(struct mddev *mddev, int raid_disks) 84{ 85 struct linear_conf *conf; 86 struct md_rdev *rdev; 87 int i, cnt; 88 bool discard_supported = false; 89 90 conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(struct dev_info), 91 GFP_KERNEL); 92 if (!conf) 93 return NULL; 94 95 cnt = 0; 96 conf->array_sectors = 0; 97 98 rdev_for_each(rdev, mddev) { 99 int j = rdev->raid_disk; 100 struct dev_info *disk = conf->disks + j; 101 sector_t sectors; 102 103 if (j < 0 || j >= raid_disks || disk->rdev) { 104 printk(KERN_ERR "md/linear:%s: disk numbering problem. Aborting!\n", 105 mdname(mddev)); 106 goto out; 107 } 108 109 disk->rdev = rdev; 110 if (mddev->chunk_sectors) { 111 sectors = rdev->sectors; 112 sector_div(sectors, mddev->chunk_sectors); 113 rdev->sectors = sectors * mddev->chunk_sectors; 114 } 115 116 disk_stack_limits(mddev->gendisk, rdev->bdev, 117 rdev->data_offset << 9); 118 119 conf->array_sectors += rdev->sectors; 120 cnt++; 121 122 if (blk_queue_discard(bdev_get_queue(rdev->bdev))) 123 discard_supported = true; 124 } 125 if (cnt != raid_disks) { 126 printk(KERN_ERR "md/linear:%s: not enough drives present. Aborting!\n", 127 mdname(mddev)); 128 goto out; 129 } 130 131 if (!discard_supported) 132 queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); 133 else 134 queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, mddev->queue); 135 136 /* 137 * Here we calculate the device offsets. 138 */ 139 conf->disks[0].end_sector = conf->disks[0].rdev->sectors; 140 141 for (i = 1; i < raid_disks; i++) 142 conf->disks[i].end_sector = 143 conf->disks[i-1].end_sector + 144 conf->disks[i].rdev->sectors; 145 146 return conf; 147 148out: 149 kfree(conf); 150 return NULL; 151} 152 153static int linear_run (struct mddev *mddev) 154{ 155 struct linear_conf *conf; 156 int ret; 157 158 if (md_check_no_bitmap(mddev)) 159 return -EINVAL; 160 conf = linear_conf(mddev, mddev->raid_disks); 161 162 if (!conf) 163 return 1; 164 mddev->private = conf; 165 md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); 166 167 ret = md_integrity_register(mddev); 168 if (ret) { 169 kfree(conf); 170 mddev->private = NULL; 171 } 172 return ret; 173} 174 175static int linear_add(struct mddev *mddev, struct md_rdev *rdev) 176{ 177 /* Adding a drive to a linear array allows the array to grow. 178 * It is permitted if the new drive has a matching superblock 179 * already on it, with raid_disk equal to raid_disks. 180 * It is achieved by creating a new linear_private_data structure 181 * and swapping it in in-place of the current one. 182 * The current one is never freed until the array is stopped. 183 * This avoids races. 184 */ 185 struct linear_conf *newconf, *oldconf; 186 187 if (rdev->saved_raid_disk != mddev->raid_disks) 188 return -EINVAL; 189 190 rdev->raid_disk = rdev->saved_raid_disk; 191 rdev->saved_raid_disk = -1; 192 193 newconf = linear_conf(mddev,mddev->raid_disks+1); 194 195 if (!newconf) 196 return -ENOMEM; 197 198 mddev_suspend(mddev); 199 oldconf = mddev->private; 200 mddev->raid_disks++; 201 mddev->private = newconf; 202 md_set_array_sectors(mddev, linear_size(mddev, 0, 0)); 203 set_capacity(mddev->gendisk, mddev->array_sectors); 204 mddev_resume(mddev); 205 revalidate_disk(mddev->gendisk); 206 kfree(oldconf); 207 return 0; 208} 209 210static void linear_free(struct mddev *mddev, void *priv) 211{ 212 struct linear_conf *conf = priv; 213 214 kfree(conf); 215} 216 217static void linear_make_request(struct mddev *mddev, struct bio *bio) 218{ 219 char b[BDEVNAME_SIZE]; 220 struct dev_info *tmp_dev; 221 struct bio *split; 222 sector_t start_sector, end_sector, data_offset; 223 224 if (unlikely(bio->bi_rw & REQ_FLUSH)) { 225 md_flush_request(mddev, bio); 226 return; 227 } 228 229 do { 230 tmp_dev = which_dev(mddev, bio->bi_iter.bi_sector); 231 start_sector = tmp_dev->end_sector - tmp_dev->rdev->sectors; 232 end_sector = tmp_dev->end_sector; 233 data_offset = tmp_dev->rdev->data_offset; 234 bio->bi_bdev = tmp_dev->rdev->bdev; 235 236 if (unlikely(bio->bi_iter.bi_sector >= end_sector || 237 bio->bi_iter.bi_sector < start_sector)) 238 goto out_of_bounds; 239 240 if (unlikely(bio_end_sector(bio) > end_sector)) { 241 /* This bio crosses a device boundary, so we have to 242 * split it. 243 */ 244 split = bio_split(bio, end_sector - 245 bio->bi_iter.bi_sector, 246 GFP_NOIO, fs_bio_set); 247 bio_chain(split, bio); 248 } else { 249 split = bio; 250 } 251 252 split->bi_iter.bi_sector = split->bi_iter.bi_sector - 253 start_sector + data_offset; 254 255 if (unlikely((split->bi_rw & REQ_DISCARD) && 256 !blk_queue_discard(bdev_get_queue(split->bi_bdev)))) { 257 /* Just ignore it */ 258 bio_endio(split); 259 } else 260 generic_make_request(split); 261 } while (split != bio); 262 return; 263 264out_of_bounds: 265 printk(KERN_ERR 266 "md/linear:%s: make_request: Sector %llu out of bounds on " 267 "dev %s: %llu sectors, offset %llu\n", 268 mdname(mddev), 269 (unsigned long long)bio->bi_iter.bi_sector, 270 bdevname(tmp_dev->rdev->bdev, b), 271 (unsigned long long)tmp_dev->rdev->sectors, 272 (unsigned long long)start_sector); 273 bio_io_error(bio); 274} 275 276static void linear_status (struct seq_file *seq, struct mddev *mddev) 277{ 278 279 seq_printf(seq, " %dk rounding", mddev->chunk_sectors / 2); 280} 281 282static void linear_quiesce(struct mddev *mddev, int state) 283{ 284} 285 286static struct md_personality linear_personality = 287{ 288 .name = "linear", 289 .level = LEVEL_LINEAR, 290 .owner = THIS_MODULE, 291 .make_request = linear_make_request, 292 .run = linear_run, 293 .free = linear_free, 294 .status = linear_status, 295 .hot_add_disk = linear_add, 296 .size = linear_size, 297 .quiesce = linear_quiesce, 298 .congested = linear_congested, 299}; 300 301static int __init linear_init (void) 302{ 303 return register_md_personality (&linear_personality); 304} 305 306static void linear_exit (void) 307{ 308 unregister_md_personality (&linear_personality); 309} 310 311module_init(linear_init); 312module_exit(linear_exit); 313MODULE_LICENSE("GPL"); 314MODULE_DESCRIPTION("Linear device concatenation personality for MD"); 315MODULE_ALIAS("md-personality-1"); /* LINEAR - deprecated*/ 316MODULE_ALIAS("md-linear"); 317MODULE_ALIAS("md-level--1"); 318