2005-04-17 02:20:36 +04:00
/*
linear . c : Multiple Devices driver for Linux
Copyright ( C ) 1994 - 96 Marc ZYNGIER
< zyngier @ ufr - info - p7 . ibp . fr > or
< maz @ gloups . fdn . fr >
Linear mode management functions .
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 , or ( at your option )
any later version .
You should have received a copy of the GNU General Public License
( for example / usr / src / linux / COPYING ) ; if not , write to the Free
Software Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2009-03-31 07:33:13 +04:00
# include <linux/blkdev.h>
# include <linux/raid/md_u.h>
# include <linux/seq_file.h>
2009-03-31 07:33:13 +04:00
# include "md.h"
2009-03-31 07:27:03 +04:00
# include "linear.h"
2005-04-17 02:20:36 +04:00
/*
* find which device holds a particular offset
*/
static inline dev_info_t * which_dev ( mddev_t * mddev , sector_t sector )
{
2009-06-16 10:57:08 +04:00
int lo , mid , hi ;
2009-06-18 02:49:35 +04:00
linear_conf_t * conf ;
2005-04-17 02:20:36 +04:00
2009-06-16 10:57:08 +04:00
lo = 0 ;
hi = mddev - > raid_disks - 1 ;
2009-06-18 02:49:35 +04:00
conf = rcu_dereference ( mddev - > private ) ;
2005-04-17 02:20:36 +04:00
2009-06-16 10:57:08 +04:00
/*
* Binary Search
*/
while ( hi > lo ) {
mid = ( hi + lo ) / 2 ;
if ( sector < conf - > disks [ mid ] . end_sector )
hi = mid ;
else
lo = mid + 1 ;
}
return conf - > disks + lo ;
2005-04-17 02:20:36 +04:00
}
/**
2005-09-10 03:23:47 +04:00
* linear_mergeable_bvec - - tell bio layer if two requests can be merged
2005-04-17 02:20:36 +04:00
* @ q : request queue
2008-07-03 11:53:43 +04:00
* @ bvm : properties of new bio
2005-04-17 02:20:36 +04:00
* @ biovec : the request that could be merged to it .
*
* Return amount of bytes we can take at this offset
*/
2008-07-03 11:53:43 +04:00
static int linear_mergeable_bvec ( struct request_queue * q ,
struct bvec_merge_data * bvm ,
struct bio_vec * biovec )
2005-04-17 02:20:36 +04:00
{
mddev_t * mddev = q - > queuedata ;
dev_info_t * dev0 ;
2008-07-03 11:53:43 +04:00
unsigned long maxsectors , bio_sectors = bvm - > bi_size > > 9 ;
sector_t sector = bvm - > bi_sector + get_start_sect ( bvm - > bi_bdev ) ;
2005-04-17 02:20:36 +04:00
2009-06-18 02:49:35 +04:00
rcu_read_lock ( ) ;
2005-04-17 02:20:36 +04:00
dev0 = which_dev ( mddev , sector ) ;
2009-06-16 10:56:13 +04:00
maxsectors = dev0 - > end_sector - sector ;
2009-06-18 02:49:35 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
if ( maxsectors < bio_sectors )
maxsectors = 0 ;
else
maxsectors - = bio_sectors ;
if ( maxsectors < = ( PAGE_SIZE > > 9 ) & & bio_sectors = = 0 )
return biovec - > bv_len ;
/* The bytes available at this offset could be really big,
* so we cap at 2 ^ 31 to avoid overflow */
if ( maxsectors > ( 1 < < ( 31 - 9 ) ) )
return 1 < < 31 ;
return maxsectors < < 9 ;
}
2007-07-24 11:28:11 +04:00
static void linear_unplug ( struct request_queue * q )
2005-04-17 02:20:36 +04:00
{
mddev_t * mddev = q - > queuedata ;
2009-06-18 02:49:35 +04:00
linear_conf_t * conf ;
2005-04-17 02:20:36 +04:00
int i ;
2009-06-18 02:49:35 +04:00
rcu_read_lock ( ) ;
conf = rcu_dereference ( mddev - > private ) ;
2005-04-17 02:20:36 +04:00
for ( i = 0 ; i < mddev - > raid_disks ; i + + ) {
2007-07-24 11:28:11 +04:00
struct request_queue * r_queue = bdev_get_queue ( conf - > disks [ i ] . rdev - > bdev ) ;
2007-11-07 22:26:56 +03:00
blk_unplug ( r_queue ) ;
2005-04-17 02:20:36 +04:00
}
2009-06-18 02:49:35 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
}
2006-10-03 12:15:53 +04:00
static int linear_congested ( void * data , int bits )
{
mddev_t * mddev = data ;
2009-06-18 02:49:35 +04:00
linear_conf_t * conf ;
2006-10-03 12:15:53 +04:00
int i , ret = 0 ;
2009-09-23 12:10:29 +04:00
if ( mddev_congested ( mddev , bits ) )
return 1 ;
2009-06-18 02:49:35 +04:00
rcu_read_lock ( ) ;
conf = rcu_dereference ( mddev - > private ) ;
2006-10-03 12:15:53 +04:00
for ( i = 0 ; i < mddev - > raid_disks & & ! ret ; i + + ) {
2007-07-24 11:28:11 +04:00
struct request_queue * q = bdev_get_queue ( conf - > disks [ i ] . rdev - > bdev ) ;
2006-10-03 12:15:53 +04:00
ret | = bdi_congested ( & q - > backing_dev_info , bits ) ;
}
2009-06-18 02:49:35 +04:00
rcu_read_unlock ( ) ;
2006-10-03 12:15:53 +04:00
return ret ;
}
2009-03-18 04:10:40 +03:00
static sector_t linear_size ( mddev_t * mddev , sector_t sectors , int raid_disks )
{
2009-06-18 02:49:35 +04:00
linear_conf_t * conf ;
sector_t array_sectors ;
2009-03-18 04:10:40 +03:00
2009-06-18 02:49:35 +04:00
rcu_read_lock ( ) ;
conf = rcu_dereference ( mddev - > private ) ;
2009-03-18 04:10:40 +03:00
WARN_ONCE ( sectors | | raid_disks ,
" %s does not support generic reshape \n " , __func__ ) ;
2009-06-18 02:49:35 +04:00
array_sectors = conf - > array_sectors ;
rcu_read_unlock ( ) ;
2009-03-18 04:10:40 +03:00
2009-06-18 02:49:35 +04:00
return array_sectors ;
2009-03-18 04:10:40 +03:00
}
2006-06-26 11:27:41 +04:00
static linear_conf_t * linear_conf ( mddev_t * mddev , int raid_disks )
2005-04-17 02:20:36 +04:00
{
linear_conf_t * conf ;
mdk_rdev_t * rdev ;
2009-06-16 10:55:26 +04:00
int i , cnt ;
2005-04-17 02:20:36 +04:00
2006-06-26 11:27:41 +04:00
conf = kzalloc ( sizeof ( * conf ) + raid_disks * sizeof ( dev_info_t ) ,
2005-04-17 02:20:36 +04:00
GFP_KERNEL ) ;
if ( ! conf )
2006-06-26 11:27:41 +04:00
return NULL ;
2005-04-17 02:20:36 +04:00
cnt = 0 ;
2008-07-21 11:05:25 +04:00
conf - > array_sectors = 0 ;
2005-04-17 02:20:36 +04:00
2009-01-09 00:31:08 +03:00
list_for_each_entry ( rdev , & mddev - > disks , same_set ) {
2005-04-17 02:20:36 +04:00
int j = rdev - > raid_disk ;
dev_info_t * disk = conf - > disks + j ;
2009-06-18 02:48:55 +04:00
sector_t sectors ;
2005-04-17 02:20:36 +04:00
2008-06-28 02:31:19 +04:00
if ( j < 0 | | j > = raid_disks | | disk - > rdev ) {
2005-04-17 02:20:36 +04:00
printk ( " linear: disk numbering problem. Aborting! \n " ) ;
goto out ;
}
disk - > rdev = rdev ;
2009-06-18 02:48:55 +04:00
if ( mddev - > chunk_sectors ) {
sectors = rdev - > sectors ;
sector_div ( sectors , mddev - > chunk_sectors ) ;
rdev - > sectors = sectors * mddev - > chunk_sectors ;
}
2005-04-17 02:20:36 +04:00
2009-07-01 05:13:45 +04:00
disk_stack_limits ( mddev - > gendisk , rdev - > bdev ,
rdev - > data_offset < < 9 ) ;
2005-04-17 02:20:36 +04:00
/* as we don't honour merge_bvec_fn, we must never risk
* violating it , so limit - > max_sector to one PAGE , as
* a one page request is never in violation .
*/
if ( rdev - > bdev - > bd_disk - > queue - > merge_bvec_fn & &
2009-05-23 01:17:50 +04:00
queue_max_sectors ( mddev - > queue ) > ( PAGE_SIZE > > 9 ) )
2005-04-17 02:20:36 +04:00
blk_queue_max_sectors ( mddev - > queue , PAGE_SIZE > > 9 ) ;
2009-03-31 07:33:13 +04:00
conf - > array_sectors + = rdev - > sectors ;
2005-04-17 02:20:36 +04:00
cnt + + ;
2009-06-16 10:56:13 +04:00
2005-04-17 02:20:36 +04:00
}
2006-06-26 11:27:41 +04:00
if ( cnt ! = raid_disks ) {
2005-04-17 02:20:36 +04:00
printk ( " linear: not enough drives present. Aborting! \n " ) ;
goto out ;
}
/*
2009-06-16 10:55:26 +04:00
* Here we calculate the device offsets .
2005-04-17 02:20:36 +04:00
*/
2009-06-16 10:56:13 +04:00
conf - > disks [ 0 ] . end_sector = conf - > disks [ 0 ] . rdev - > sectors ;
2007-05-24 00:58:10 +04:00
for ( i = 1 ; i < raid_disks ; i + + )
2009-06-16 10:56:13 +04:00
conf - > disks [ i ] . end_sector =
conf - > disks [ i - 1 ] . end_sector +
conf - > disks [ i ] . rdev - > sectors ;
2005-09-10 03:23:47 +04:00
2006-06-26 11:27:41 +04:00
return conf ;
out :
kfree ( conf ) ;
return NULL ;
}
static int linear_run ( mddev_t * mddev )
{
linear_conf_t * conf ;
2009-06-18 02:49:23 +04:00
if ( md_check_no_bitmap ( mddev ) )
return - EINVAL ;
2008-05-15 03:05:54 +04:00
mddev - > queue - > queue_lock = & mddev - > queue - > __queue_lock ;
2006-06-26 11:27:41 +04:00
conf = linear_conf ( mddev , mddev - > raid_disks ) ;
if ( ! conf )
return 1 ;
mddev - > private = conf ;
2009-03-31 07:59:03 +04:00
md_set_array_sectors ( mddev , linear_size ( mddev , 0 , 0 ) ) ;
2006-06-26 11:27:41 +04:00
2005-04-17 02:20:36 +04:00
blk_queue_merge_bvec ( mddev - > queue , linear_mergeable_bvec ) ;
mddev - > queue - > unplug_fn = linear_unplug ;
2006-10-03 12:15:53 +04:00
mddev - > queue - > backing_dev_info . congested_fn = linear_congested ;
mddev - > queue - > backing_dev_info . congested_data = mddev ;
2009-08-03 04:59:47 +04:00
md_integrity_register ( mddev ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
2006-06-26 11:27:41 +04:00
}
2005-04-17 02:20:36 +04:00
2009-06-18 02:49:42 +04:00
static void free_conf ( struct rcu_head * head )
{
linear_conf_t * conf = container_of ( head , linear_conf_t , rcu ) ;
kfree ( conf ) ;
}
2006-06-26 11:27:41 +04:00
static int linear_add ( mddev_t * mddev , mdk_rdev_t * rdev )
{
/* Adding a drive to a linear array allows the array to grow.
* It is permitted if the new drive has a matching superblock
* already on it , with raid_disk equal to raid_disks .
* It is achieved by creating a new linear_private_data structure
* and swapping it in in - place of the current one .
* The current one is never freed until the array is stopped .
* This avoids races .
*/
2009-06-18 02:49:42 +04:00
linear_conf_t * newconf , * oldconf ;
2006-06-26 11:27:41 +04:00
2007-05-24 00:58:10 +04:00
if ( rdev - > saved_raid_disk ! = mddev - > raid_disks )
2006-06-26 11:27:41 +04:00
return - EINVAL ;
2007-05-24 00:58:10 +04:00
rdev - > raid_disk = rdev - > saved_raid_disk ;
2006-06-26 11:27:41 +04:00
newconf = linear_conf ( mddev , mddev - > raid_disks + 1 ) ;
if ( ! newconf )
return - ENOMEM ;
2009-06-18 02:49:42 +04:00
oldconf = rcu_dereference ( mddev - > private ) ;
2006-06-26 11:27:41 +04:00
mddev - > raid_disks + + ;
2009-06-18 02:49:35 +04:00
rcu_assign_pointer ( mddev - > private , newconf ) ;
2009-03-31 07:59:03 +04:00
md_set_array_sectors ( mddev , linear_size ( mddev , 0 , 0 ) ) ;
2008-07-21 11:05:22 +04:00
set_capacity ( mddev - > gendisk , mddev - > array_sectors ) ;
2009-08-03 04:59:58 +04:00
revalidate_disk ( mddev - > gendisk ) ;
2009-06-18 02:49:42 +04:00
call_rcu ( & oldconf - > rcu , free_conf ) ;
2006-06-26 11:27:41 +04:00
return 0 ;
2005-04-17 02:20:36 +04:00
}
static int linear_stop ( mddev_t * mddev )
{
2009-06-16 10:54:21 +04:00
linear_conf_t * conf = mddev - > private ;
2009-06-18 02:49:35 +04:00
/*
* We do not require rcu protection here since
* we hold reconfig_mutex for both linear_add and
* linear_stop , so they cannot race .
2009-06-18 02:49:42 +04:00
* We should make sure any old ' conf ' s are properly
* freed though .
2009-06-18 02:49:35 +04:00
*/
2009-06-18 02:49:42 +04:00
rcu_barrier ( ) ;
2005-04-17 02:20:36 +04:00
blk_sync_queue ( mddev - > queue ) ; /* the unplug fn references 'conf'*/
2009-06-18 02:49:42 +04:00
kfree ( conf ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2007-07-24 11:28:11 +04:00
static int linear_make_request ( struct request_queue * q , struct bio * bio )
2005-04-17 02:20:36 +04:00
{
2005-11-01 11:26:16 +03:00
const int rw = bio_data_dir ( bio ) ;
2005-04-17 02:20:36 +04:00
mddev_t * mddev = q - > queuedata ;
dev_info_t * tmp_dev ;
2009-06-16 10:56:13 +04:00
sector_t start_sector ;
2008-08-25 14:47:21 +04:00
int cpu ;
2005-04-17 02:20:36 +04:00
2009-09-11 16:32:04 +04:00
if ( unlikely ( bio_rw_flagged ( bio , BIO_RW_BARRIER ) ) ) {
2009-12-14 04:49:49 +03:00
md_barrier_request ( mddev , bio ) ;
2005-09-10 03:23:41 +04:00
return 0 ;
}
2008-08-25 14:56:14 +04:00
cpu = part_stat_lock ( ) ;
part_stat_inc ( cpu , & mddev - > gendisk - > part0 , ios [ rw ] ) ;
part_stat_add ( cpu , & mddev - > gendisk - > part0 , sectors [ rw ] ,
bio_sectors ( bio ) ) ;
part_stat_unlock ( ) ;
2005-04-17 02:20:36 +04:00
2009-06-18 02:49:35 +04:00
rcu_read_lock ( ) ;
2005-04-17 02:20:36 +04:00
tmp_dev = which_dev ( mddev , bio - > bi_sector ) ;
2009-06-16 10:56:13 +04:00
start_sector = tmp_dev - > end_sector - tmp_dev - > rdev - > sectors ;
2009-06-18 02:49:35 +04:00
2009-06-16 10:56:13 +04:00
if ( unlikely ( bio - > bi_sector > = ( tmp_dev - > end_sector )
| | ( bio - > bi_sector < start_sector ) ) ) {
2005-04-17 02:20:36 +04:00
char b [ BDEVNAME_SIZE ] ;
2008-10-13 04:55:12 +04:00
printk ( " linear_make_request: Sector %llu out of bounds on "
" dev %s: %llu sectors, offset %llu \n " ,
( unsigned long long ) bio - > bi_sector ,
2005-04-17 02:20:36 +04:00
bdevname ( tmp_dev - > rdev - > bdev , b ) ,
2009-06-16 10:56:13 +04:00
( unsigned long long ) tmp_dev - > rdev - > sectors ,
( unsigned long long ) start_sector ) ;
2009-06-18 02:49:35 +04:00
rcu_read_unlock ( ) ;
2007-09-27 14:47:43 +04:00
bio_io_error ( bio ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
if ( unlikely ( bio - > bi_sector + ( bio - > bi_size > > 9 ) >
2009-06-16 10:56:13 +04:00
tmp_dev - > end_sector ) ) {
2005-04-17 02:20:36 +04:00
/* This bio crosses a device boundary, so we have to
* split it .
*/
struct bio_pair * bp ;
2009-06-18 02:49:35 +04:00
sector_t end_sector = tmp_dev - > end_sector ;
rcu_read_unlock ( ) ;
2008-10-13 04:55:12 +04:00
2009-06-18 02:49:35 +04:00
bp = bio_split ( bio , end_sector - bio - > bi_sector ) ;
2008-10-13 04:55:12 +04:00
2005-04-17 02:20:36 +04:00
if ( linear_make_request ( q , & bp - > bio1 ) )
generic_make_request ( & bp - > bio1 ) ;
if ( linear_make_request ( q , & bp - > bio2 ) )
generic_make_request ( & bp - > bio2 ) ;
bio_pair_release ( bp ) ;
return 0 ;
}
bio - > bi_bdev = tmp_dev - > rdev - > bdev ;
2009-06-16 10:56:13 +04:00
bio - > bi_sector = bio - > bi_sector - start_sector
2008-10-13 04:55:12 +04:00
+ tmp_dev - > rdev - > data_offset ;
2009-06-18 02:49:35 +04:00
rcu_read_unlock ( ) ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
static void linear_status ( struct seq_file * seq , mddev_t * mddev )
{
2009-06-18 02:45:01 +04:00
seq_printf ( seq , " %dk rounding " , mddev - > chunk_sectors / 2 ) ;
2005-04-17 02:20:36 +04:00
}
2006-01-06 11:20:36 +03:00
static struct mdk_personality linear_personality =
2005-04-17 02:20:36 +04:00
{
. name = " linear " ,
2006-01-06 11:20:36 +03:00
. level = LEVEL_LINEAR ,
2005-04-17 02:20:36 +04:00
. owner = THIS_MODULE ,
. make_request = linear_make_request ,
. run = linear_run ,
. stop = linear_stop ,
. status = linear_status ,
2006-06-26 11:27:41 +04:00
. hot_add_disk = linear_add ,
2009-03-18 04:10:40 +03:00
. size = linear_size ,
2005-04-17 02:20:36 +04:00
} ;
static int __init linear_init ( void )
{
2006-01-06 11:20:36 +03:00
return register_md_personality ( & linear_personality ) ;
2005-04-17 02:20:36 +04:00
}
static void linear_exit ( void )
{
2006-01-06 11:20:36 +03:00
unregister_md_personality ( & linear_personality ) ;
2005-04-17 02:20:36 +04:00
}
module_init ( linear_init ) ;
module_exit ( linear_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
2009-12-14 04:49:58 +03:00
MODULE_DESCRIPTION ( " Linear device concatenation personality for MD " ) ;
2006-01-06 11:20:51 +03:00
MODULE_ALIAS ( " md-personality-1 " ) ; /* LINEAR - deprecated*/
MODULE_ALIAS ( " md-linear " ) ;
2006-01-06 11:20:36 +03:00
MODULE_ALIAS ( " md-level--1 " ) ;