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 .
*/
# include <linux/raid/linear.h>
/*
* find which device holds a particular offset
*/
static inline dev_info_t * which_dev ( mddev_t * mddev , sector_t sector )
{
dev_info_t * hash ;
linear_conf_t * conf = mddev_to_conf ( mddev ) ;
/*
* sector_div ( a , b ) returns the remainer and sets a to a / b
*/
2008-10-13 04:55:12 +04:00
sector > > = conf - > sector_shift ;
( void ) sector_div ( sector , conf - > spacing ) ;
hash = conf - > hash_table [ sector ] ;
2005-04-17 02:20:36 +04:00
2008-10-13 04:55:12 +04:00
while ( sector > = hash - > num_sectors + hash - > start_sector )
2005-04-17 02:20:36 +04:00
hash + + ;
return hash ;
}
/**
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
dev0 = which_dev ( mddev , sector ) ;
2008-10-13 04:55:12 +04:00
maxsectors = dev0 - > num_sectors - ( sector - dev0 - > start_sector ) ;
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 ;
linear_conf_t * conf = mddev_to_conf ( mddev ) ;
int i ;
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
}
}
2006-10-03 12:15:53 +04:00
static int linear_congested ( void * data , int bits )
{
mddev_t * mddev = data ;
linear_conf_t * conf = mddev_to_conf ( mddev ) ;
int i , ret = 0 ;
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 ) ;
}
return ret ;
}
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 ;
dev_info_t * * table ;
mdk_rdev_t * rdev ;
int i , nb_zone , cnt ;
2008-10-13 04:55:12 +04:00
sector_t min_sectors ;
2008-10-13 04:55:12 +04:00
sector_t curr_sector ;
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 ;
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 ;
blk_queue_stack_limits ( mddev - > queue ,
rdev - > bdev - > bd_disk - > queue ) ;
/* 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 & &
mddev - > queue - > max_sectors > ( PAGE_SIZE > > 9 ) )
blk_queue_max_sectors ( mddev - > queue , PAGE_SIZE > > 9 ) ;
2008-10-13 04:55:12 +04:00
disk - > num_sectors = rdev - > size * 2 ;
2008-07-21 11:05:25 +04:00
conf - > array_sectors + = rdev - > size * 2 ;
2005-04-17 02:20:36 +04:00
cnt + + ;
}
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 ;
}
2008-10-13 04:55:12 +04:00
min_sectors = conf - > array_sectors ;
sector_div ( min_sectors , PAGE_SIZE / sizeof ( struct dev_info * ) ) ;
2008-11-06 11:41:24 +03:00
if ( min_sectors = = 0 )
min_sectors = 1 ;
2005-09-10 03:23:47 +04:00
2008-10-13 04:55:12 +04:00
/* min_sectors is the minimum spacing that will fit the hash
2005-09-10 03:23:47 +04:00
* table in one PAGE . This may be much smaller than needed .
* We find the smallest non - terminal set of consecutive devices
2008-10-13 04:55:12 +04:00
* that is larger than min_sectors and use the size of that as
2005-09-10 03:23:47 +04:00
* the actual spacing
*/
2008-10-13 04:55:12 +04:00
conf - > spacing = conf - > array_sectors ;
2005-09-10 03:23:47 +04:00
for ( i = 0 ; i < cnt - 1 ; i + + ) {
2008-10-13 04:55:12 +04:00
sector_t tmp = 0 ;
2005-09-10 03:23:47 +04:00
int j ;
2008-10-13 04:55:12 +04:00
for ( j = i ; j < cnt - 1 & & tmp < min_sectors ; j + + )
tmp + = conf - > disks [ j ] . num_sectors ;
2008-10-13 04:55:12 +04:00
if ( tmp > = min_sectors & & tmp < conf - > spacing )
conf - > spacing = tmp ;
2005-09-10 03:23:47 +04:00
}
2008-10-13 04:55:12 +04:00
/* spacing may be too large for sector_div to work with,
2005-09-10 03:23:47 +04:00
* so we might need to pre - shift
*/
2008-10-13 04:55:12 +04:00
conf - > sector_shift = 0 ;
2005-09-10 03:23:47 +04:00
if ( sizeof ( sector_t ) > sizeof ( u32 ) ) {
2008-10-13 04:55:12 +04:00
sector_t space = conf - > spacing ;
2005-09-10 03:23:47 +04:00
while ( space > ( sector_t ) ( ~ ( u32 ) 0 ) ) {
space > > = 1 ;
2008-10-13 04:55:12 +04:00
conf - > sector_shift + + ;
2005-09-10 03:23:47 +04:00
}
}
2005-04-17 02:20:36 +04:00
/*
* This code was restructured to work around a gcc - 2.95 .3 internal
* compiler error . Alter it with care .
*/
{
sector_t sz ;
unsigned round ;
unsigned long base ;
2008-10-13 04:55:12 +04:00
sz = conf - > array_sectors > > conf - > sector_shift ;
2005-09-10 03:23:47 +04:00
sz + = 1 ; /* force round-up */
2008-10-13 04:55:12 +04:00
base = conf - > spacing > > conf - > sector_shift ;
2005-04-17 02:20:36 +04:00
round = sector_div ( sz , base ) ;
2005-09-10 03:23:47 +04:00
nb_zone = sz + ( round ? 1 : 0 ) ;
2005-04-17 02:20:36 +04:00
}
2005-09-10 03:23:47 +04:00
BUG_ON ( nb_zone > PAGE_SIZE / sizeof ( struct dev_info * ) ) ;
conf - > hash_table = kmalloc ( sizeof ( struct dev_info * ) * nb_zone ,
2005-04-17 02:20:36 +04:00
GFP_KERNEL ) ;
if ( ! conf - > hash_table )
goto out ;
/*
* Here we generate the linear hash table
2005-09-10 03:23:47 +04:00
* First calculate the device offsets .
2005-04-17 02:20:36 +04:00
*/
2008-10-13 04:55:12 +04:00
conf - > disks [ 0 ] . start_sector = 0 ;
2007-05-24 00:58:10 +04:00
for ( i = 1 ; i < raid_disks ; i + + )
2008-10-13 04:55:12 +04:00
conf - > disks [ i ] . start_sector =
conf - > disks [ i - 1 ] . start_sector +
conf - > disks [ i - 1 ] . num_sectors ;
2005-09-10 03:23:47 +04:00
2005-04-17 02:20:36 +04:00
table = conf - > hash_table ;
2005-09-10 03:23:47 +04:00
i = 0 ;
2008-10-13 04:55:12 +04:00
for ( curr_sector = 0 ;
curr_sector < conf - > array_sectors ;
2008-10-13 04:55:12 +04:00
curr_sector + = conf - > spacing ) {
2005-04-17 02:20:36 +04:00
2007-05-24 00:58:10 +04:00
while ( i < raid_disks - 1 & &
2008-10-13 04:55:12 +04:00
curr_sector > = conf - > disks [ i + 1 ] . start_sector )
2005-09-10 03:23:47 +04:00
i + + ;
2005-04-17 02:20:36 +04:00
2005-09-10 03:23:47 +04:00
* table + + = conf - > disks + i ;
}
2008-10-13 04:55:12 +04:00
if ( conf - > sector_shift ) {
conf - > spacing > > = conf - > sector_shift ;
/* round spacing up so that when we divide by it,
2005-09-10 03:23:47 +04:00
* we err on the side of " too-low " , which is safest .
2005-04-17 02:20:36 +04:00
*/
2008-10-13 04:55:12 +04:00
conf - > spacing + + ;
2005-04-17 02:20:36 +04:00
}
2005-09-10 03:23:47 +04:00
BUG_ON ( table - conf - > hash_table > nb_zone ) ;
2005-04-17 02:20:36 +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 ;
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 ;
2008-07-21 11:05:25 +04:00
mddev - > array_sectors = conf - > array_sectors ;
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 ;
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
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 .
*/
linear_conf_t * newconf ;
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 ;
newconf - > prev = mddev_to_conf ( mddev ) ;
mddev - > private = newconf ;
mddev - > raid_disks + + ;
2008-07-21 11:05:25 +04:00
mddev - > array_sectors = newconf - > array_sectors ;
2008-07-21 11:05:22 +04:00
set_capacity ( mddev - > gendisk , mddev - > array_sectors ) ;
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 )
{
linear_conf_t * conf = mddev_to_conf ( mddev ) ;
blk_sync_queue ( mddev - > queue ) ; /* the unplug fn references 'conf'*/
2006-06-26 11:27:41 +04:00
do {
linear_conf_t * t = conf - > prev ;
kfree ( conf - > hash_table ) ;
kfree ( conf ) ;
conf = t ;
} while ( 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 ;
2008-08-25 14:47:21 +04:00
int cpu ;
2005-04-17 02:20:36 +04:00
2005-09-10 03:23:41 +04:00
if ( unlikely ( bio_barrier ( bio ) ) ) {
2007-09-27 14:47:43 +04:00
bio_endio ( bio , - EOPNOTSUPP ) ;
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
tmp_dev = which_dev ( mddev , bio - > bi_sector ) ;
2008-10-13 04:55:12 +04:00
if ( unlikely ( bio - > bi_sector > = ( tmp_dev - > num_sectors +
tmp_dev - > start_sector )
| | ( bio - > bi_sector <
tmp_dev - > 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 ) ,
2008-10-13 04:55:12 +04:00
( unsigned long long ) tmp_dev - > num_sectors ,
( unsigned long long ) tmp_dev - > start_sector ) ;
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 ) >
2008-10-13 04:55:12 +04:00
tmp_dev - > start_sector + tmp_dev - > num_sectors ) ) {
2005-04-17 02:20:36 +04:00
/* This bio crosses a device boundary, so we have to
* split it .
*/
struct bio_pair * bp ;
2008-10-13 04:55:12 +04:00
2008-10-09 10:57:05 +04:00
bp = bio_split ( bio ,
2008-10-13 04:55:12 +04:00
tmp_dev - > start_sector + tmp_dev - > num_sectors
- bio - > bi_sector ) ;
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 ;
2008-10-13 04:55:12 +04:00
bio - > bi_sector = bio - > bi_sector - tmp_dev - > start_sector
+ tmp_dev - > rdev - > data_offset ;
2005-04-17 02:20:36 +04:00
return 1 ;
}
static void linear_status ( struct seq_file * seq , mddev_t * mddev )
{
seq_printf ( seq , " %dk rounding " , mddev - > chunk_size / 1024 ) ;
}
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 ,
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 " ) ;
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 " ) ;