blob: a145ddde7687a6e5cf15f12c948e680979559fc7 [file] [log] [blame]
/*
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 02139, USA.
*/
#include <linux/module.h>
#include <linux/raid/md.h>
#include <linux/slab.h>
#include <linux/raid/linear.h>
#define MAJOR_NR MD_MAJOR
#define MD_DRIVER
#define MD_PERSONALITY
static int linear_run (mddev_t *mddev)
{
linear_conf_t *conf;
struct linear_hash *table;
mdk_rdev_t *rdev;
int size, i, j, nb_zone;
unsigned int curr_offset;
MOD_INC_USE_COUNT;
conf = kmalloc (sizeof (*conf), GFP_KERNEL);
if (!conf)
goto out;
mddev->private = conf;
if (md_check_ordering(mddev)) {
printk("linear: disks are not ordered, aborting!\n");
goto out;
}
/*
* Find the smallest device.
*/
conf->smallest = NULL;
curr_offset = 0;
ITERATE_RDEV_ORDERED(mddev,rdev,j) {
dev_info_t *disk = conf->disks + j;
disk->dev = rdev->dev;
disk->size = rdev->size;
disk->offset = curr_offset;
curr_offset += disk->size;
if (!conf->smallest || (disk->size < conf->smallest->size))
conf->smallest = disk;
}
nb_zone = conf->nr_zones =
md_size[mdidx(mddev)] / conf->smallest->size +
((md_size[mdidx(mddev)] % conf->smallest->size) ? 1 : 0);
conf->hash_table = kmalloc (sizeof (struct linear_hash) * nb_zone,
GFP_KERNEL);
if (!conf->hash_table)
goto out;
/*
* Here we generate the linear hash table
*/
table = conf->hash_table;
i = 0;
size = 0;
for (j = 0; j < mddev->nb_dev; j++) {
dev_info_t *disk = conf->disks + j;
if (size < 0) {
table[-1].dev1 = disk;
}
size += disk->size;
while (size>0) {
table->dev0 = disk;
table->dev1 = NULL;
size -= conf->smallest->size;
table++;
}
}
if (table-conf->hash_table != nb_zone)
BUG();
return 0;
out:
if (conf)
kfree(conf);
MOD_DEC_USE_COUNT;
return 1;
}
static int linear_stop (mddev_t *mddev)
{
linear_conf_t *conf = mddev_to_conf(mddev);
kfree(conf->hash_table);
kfree(conf);
MOD_DEC_USE_COUNT;
return 0;
}
static int linear_make_request (mddev_t *mddev,
int rw, struct buffer_head * bh)
{
linear_conf_t *conf = mddev_to_conf(mddev);
struct linear_hash *hash;
dev_info_t *tmp_dev;
long block;
block = bh->b_rsector >> 1;
hash = conf->hash_table + (block / conf->smallest->size);
if (block >= (hash->dev0->size + hash->dev0->offset)) {
if (!hash->dev1) {
printk ("linear_make_request : hash->dev1==NULL for block %ld\n",
block);
buffer_IO_error(bh);
return 0;
}
tmp_dev = hash->dev1;
} else
tmp_dev = hash->dev0;
if (block >= (tmp_dev->size + tmp_dev->offset)
|| block < tmp_dev->offset) {
printk ("linear_make_request: Block %ld out of bounds on dev %s size %ld offset %ld\n", block, kdevname(tmp_dev->dev), tmp_dev->size, tmp_dev->offset);
buffer_IO_error(bh);
return 0;
}
bh->b_rdev = tmp_dev->dev;
bh->b_rsector = bh->b_rsector - (tmp_dev->offset << 1);
return 1;
}
static void linear_status (struct seq_file *seq, mddev_t *mddev)
{
#undef MD_DEBUG
#ifdef MD_DEBUG
int j;
linear_conf_t *conf = mddev_to_conf(mddev);
seq_printf(seq, " ");
for (j = 0; j < conf->nr_zones; j++)
{
seq_printf(seq, "[%s",
partition_name(conf->hash_table[j].dev0->dev));
if (conf->hash_table[j].dev1)
seq_printf(seq, "/%s] ",
partition_name(conf->hash_table[j].dev1->dev));
else
seq_printf(seq, "] ");
}
seq_printf(seq, "\n");
#endif
seq_printf(seq, " %dk rounding", mddev->param.chunk_size/1024);
}
static mdk_personality_t linear_personality=
{
name: "linear",
make_request: linear_make_request,
run: linear_run,
stop: linear_stop,
status: linear_status,
};
static int md__init linear_init (void)
{
return register_md_personality (LINEAR, &linear_personality);
}
static void linear_exit (void)
{
unregister_md_personality (LINEAR);
}
module_init(linear_init);
module_exit(linear_exit);
MODULE_LICENSE("GPL");