mirror of
git://sourceware.org/git/lvm2.git
synced 2025-02-08 09:57:55 +03:00
Moved across to device-mapper repository.
This commit is contained in:
parent
a9c5b0dee6
commit
a8bc68638c
@ -1,104 +0,0 @@
|
||||
The main goal of this driver is to support volume management in
|
||||
general, not just for LVM. The kernel should provide general
|
||||
services, not support specific applications. eg, The driver has no
|
||||
concept of volume groups.
|
||||
|
||||
The driver does this by mapping sector ranges for the logical device
|
||||
onto 'targets'.
|
||||
|
||||
When the logical device is accessed, the make_request function looks
|
||||
up the correct target for the given sector, and then asks this target
|
||||
to do the remapping.
|
||||
|
||||
A btree structure is used to hold the sector range -> target mapping.
|
||||
Since we know all the entries in the btree in advance we can make a
|
||||
very compact tree, omitting pointers to child nodes, (child nodes
|
||||
locations can be calculated). Typical users would find they only have
|
||||
a handful of targets for each logical volume LV.
|
||||
|
||||
Benchmarking with bonnie++ suggests that this is certainly no slower
|
||||
than current LVM.
|
||||
|
||||
|
||||
Target types are not hard coded, instead the register_mapping_type
|
||||
function should be called. A target type is specified using three
|
||||
functions (see the header):
|
||||
|
||||
dm_ctr_fn - takes a string and contructs a target specific piece of
|
||||
context data.
|
||||
dm_dtr_fn - destroy contexts.
|
||||
dm_map_fn - function that takes a buffer_head and some previously
|
||||
constructed context and performs the remapping.
|
||||
|
||||
Currently there are two two trivial mappers, which are automatically
|
||||
registered: 'linear', and 'io_error'. Linear alone is enough to
|
||||
implement most of LVM.
|
||||
|
||||
|
||||
I do not like ioctl interfaces so this driver is currently controlled
|
||||
through a /proc interface. /proc/device-mapper/control allows you to
|
||||
create and remove devices by 'cat'ing a line of the following format:
|
||||
|
||||
create <device name> [minor no]
|
||||
remove <device name>
|
||||
|
||||
If you're not using devfs you'll have to do the mknod'ing yourself,
|
||||
otherwise the device will appear in /dev/device-mapper automatically.
|
||||
|
||||
/proc/device-mapper/<device name> accepts the mapping table:
|
||||
|
||||
begin
|
||||
<sector start> <length> <target name> <target args>...
|
||||
...
|
||||
end
|
||||
|
||||
where <target args> are specific to the target type, eg. for a linear
|
||||
mapping:
|
||||
|
||||
<sector start> <length> linear <major> <minor> <start>
|
||||
|
||||
and the io-err mapping:
|
||||
|
||||
<sector start> <length> io-err
|
||||
|
||||
The begin/end lines around the table are nasty, they should be handled
|
||||
by open/close of the file.
|
||||
|
||||
The interface is far from complete, currently loading a table either
|
||||
succeeds or fails, you have no way of knowing which line of the
|
||||
mapping table was erroneous. Also there is no way to get status
|
||||
information out, though this should be easy to add, either as another
|
||||
/proc file, or just by reading the same /proc/device-mapper/<device>
|
||||
file. I will be seperating the loading and validation of a table from
|
||||
the binding of a valid table to a device.
|
||||
|
||||
It has been suggested that I should implement a little custom
|
||||
filesystem rather than labouring with /proc. For example doing a
|
||||
mkdir foo in /wherever/device-mapper would create a new device. People
|
||||
waiting for a status change (eg, a mirror operation to complete) could
|
||||
poll a file. Does the community find this an acceptable way to go ?
|
||||
|
||||
|
||||
At the moment the table assumes 32 bit keys (sectors), the move to 64
|
||||
bits will involve no interface changes, since the tables will be read
|
||||
in as ascii data. A different table implementation can therefor be
|
||||
provided at another time. Either just by changing offset_t to 64
|
||||
bits, or maybe implementing a structure which looks up the keys in
|
||||
stages (ie, 32 bits at a time).
|
||||
|
||||
|
||||
More interesting targets:
|
||||
|
||||
striped mapping; given a stripe size and a number of device regions
|
||||
this would stripe data across the regions. Especially useful, since
|
||||
we could limit each striped region to a 32 bit area and then avoid
|
||||
nasty 64 bit %'s.
|
||||
|
||||
mirror mapping; would set off a kernel thread slowly copying data from
|
||||
one region to another, ensuring that any new writes got copied to both
|
||||
destinations correctly. Enabling us to implement a live pvmove
|
||||
correctly.
|
||||
|
||||
|
||||
|
||||
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* device-mapper.h
|
||||
*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Changelog
|
||||
*
|
||||
* 14/08/2001 - First version [Joe Thornber]
|
||||
*/
|
||||
|
||||
#ifndef DEVICE_MAPPER_H
|
||||
#define DEVICE_MAPPER_H
|
||||
|
||||
#define DM_DIR "device-mapper"
|
||||
#define DM_MAX_TYPE_NAME 16
|
||||
|
||||
struct dm_table;
|
||||
struct dm_dev;
|
||||
typedef unsigned int offset_t;
|
||||
|
||||
typedef void (*dm_error_fn)(const char *message, void *private);
|
||||
|
||||
/*
|
||||
* constructor, destructor and map fn types
|
||||
*/
|
||||
typedef int (*dm_ctr_fn)(struct dm_table *t, offset_t b, offset_t l,
|
||||
char *args, void **context);
|
||||
|
||||
typedef void (*dm_dtr_fn)(struct dm_table *t, void *c);
|
||||
typedef int (*dm_map_fn)(struct buffer_head *bh, int rw, void *context);
|
||||
typedef int (*dm_err_fn)(struct buffer_head *bh, int rw, void *context);
|
||||
typedef char *(*dm_print_fn)(void *context);
|
||||
|
||||
/*
|
||||
* Contructors should call this to make sure any
|
||||
* destination devices are handled correctly
|
||||
* (ie. opened/closed).
|
||||
*/
|
||||
int dm_table_get_device(struct dm_table *t, const char *path,
|
||||
offset_t start, offset_t len,
|
||||
struct dm_dev **result);
|
||||
void dm_table_put_device(struct dm_table *table, struct dm_dev *d);
|
||||
|
||||
/*
|
||||
* information about a target type
|
||||
*/
|
||||
struct target_type {
|
||||
const char *name;
|
||||
struct module *module;
|
||||
dm_ctr_fn ctr;
|
||||
dm_dtr_fn dtr;
|
||||
dm_map_fn map;
|
||||
dm_err_fn err;
|
||||
dm_print_fn print;
|
||||
};
|
||||
|
||||
int dm_register_target(struct target_type *t);
|
||||
int dm_unregister_target(struct target_type *t);
|
||||
|
||||
#endif /* DEVICE_MAPPER_H */
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* c-file-style: "linux"
|
||||
* End:
|
||||
*/
|
@ -1,331 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#include "dm.h"
|
||||
#include <linux/dm-ioctl.h>
|
||||
|
||||
static void free_params(struct dm_ioctl *p)
|
||||
{
|
||||
vfree(p);
|
||||
}
|
||||
|
||||
static int copy_params(struct dm_ioctl *user, struct dm_ioctl **result)
|
||||
{
|
||||
struct dm_ioctl tmp, *dmi;
|
||||
|
||||
if (copy_from_user(&tmp, user, sizeof(tmp)))
|
||||
return -EFAULT;
|
||||
|
||||
if (!(dmi = vmalloc(tmp.data_size)))
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(dmi, user, tmp.data_size))
|
||||
return -EFAULT;
|
||||
|
||||
*result = dmi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* check a string doesn't overrun the chunk of
|
||||
* memory we copied from userland.
|
||||
*/
|
||||
static int valid_str(char *str, void *end)
|
||||
{
|
||||
while (((void *) str < end) && *str)
|
||||
str++;
|
||||
|
||||
return *str ? 0 : 1;
|
||||
}
|
||||
|
||||
static int first_target(struct dm_ioctl *a, void *end,
|
||||
struct dm_target_spec **spec, char **params)
|
||||
{
|
||||
*spec = (struct dm_target_spec *) (a + 1);
|
||||
*params = (char *) (*spec + 1);
|
||||
|
||||
return valid_str(*params, end);
|
||||
}
|
||||
|
||||
static int next_target(struct dm_target_spec *last, void *end,
|
||||
struct dm_target_spec **spec, char **params)
|
||||
{
|
||||
*spec = (struct dm_target_spec *)
|
||||
(((unsigned char *) last) + last->next);
|
||||
*params = (char *) (*spec + 1);
|
||||
|
||||
return valid_str(*params, end);
|
||||
}
|
||||
|
||||
void err_fn(const char *message, void *private)
|
||||
{
|
||||
printk(KERN_WARNING "%s\n", message);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks to see if there's a gap in the table.
|
||||
* Returns true iff there is a gap.
|
||||
*/
|
||||
static int gap(struct dm_table *table, struct dm_target_spec *spec)
|
||||
{
|
||||
if (!table->num_targets)
|
||||
return (spec->sector_start > 0) ? 1 : 0;
|
||||
|
||||
if (spec->sector_start != table->highs[table->num_targets - 1] + 1)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int populate_table(struct dm_table *table, struct dm_ioctl *args)
|
||||
{
|
||||
int i = 0, r, first = 1;
|
||||
struct dm_target_spec *spec;
|
||||
char *params;
|
||||
struct target_type *ttype;
|
||||
void *context, *end;
|
||||
offset_t high = 0;
|
||||
|
||||
if (!args->target_count) {
|
||||
WARN("No targets specified");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
end = ((void *) args) + args->data_size;
|
||||
|
||||
#define PARSE_ERROR(msg) {err_fn(msg, NULL); return -EINVAL;}
|
||||
|
||||
for (i = 0; i < args->target_count; i++) {
|
||||
|
||||
r = first ? first_target(args, end, &spec, ¶ms) :
|
||||
next_target(spec, end, &spec, ¶ms);
|
||||
|
||||
if (!r)
|
||||
PARSE_ERROR("unable to find target");
|
||||
|
||||
/* lookup the target type */
|
||||
if (!(ttype = dm_get_target_type(spec->target_type)))
|
||||
PARSE_ERROR("unable to find target type");
|
||||
|
||||
if (gap(table, spec))
|
||||
PARSE_ERROR("gap in target ranges");
|
||||
|
||||
/* build the target */
|
||||
if (ttype->ctr(table, spec->sector_start, spec->length, params,
|
||||
&context))
|
||||
PARSE_ERROR(context);
|
||||
|
||||
/* add the target to the table */
|
||||
high = spec->sector_start + (spec->length - 1);
|
||||
if (dm_table_add_target(table, high, ttype, context))
|
||||
PARSE_ERROR("internal error adding target to table");
|
||||
|
||||
first = 0;
|
||||
}
|
||||
|
||||
#undef PARSE_ERROR
|
||||
|
||||
r = dm_table_complete(table);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* Copies device info back to user space, used by
|
||||
* the create and info ioctls.
|
||||
*/
|
||||
static int info(const char *name, struct dm_ioctl *user)
|
||||
{
|
||||
struct dm_ioctl param;
|
||||
struct mapped_device *md = dm_get(name);
|
||||
|
||||
if (!md) {
|
||||
param.exists = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
param.data_size = 0;
|
||||
strncpy(param.name, md->name, sizeof(param.name));
|
||||
param.exists = 1;
|
||||
param.suspend = md->suspended;
|
||||
param.open_count = md->use_count;
|
||||
param.major = MAJOR(md->dev);
|
||||
param.minor = MINOR(md->dev);
|
||||
param.target_count = md->map->num_targets;
|
||||
|
||||
out:
|
||||
return copy_to_user(user, ¶m, sizeof(param));
|
||||
}
|
||||
|
||||
static int create(struct dm_ioctl *param, struct dm_ioctl *user)
|
||||
{
|
||||
int r;
|
||||
struct mapped_device *md;
|
||||
struct dm_table *t;
|
||||
|
||||
t = dm_table_create();
|
||||
r = PTR_ERR(t);
|
||||
if (IS_ERR(t))
|
||||
goto bad;
|
||||
|
||||
if ((r = populate_table(t, param)))
|
||||
goto bad;
|
||||
|
||||
md = dm_create(param->name, param->minor, t);
|
||||
r = PTR_ERR(md);
|
||||
if (IS_ERR(md))
|
||||
goto bad;
|
||||
|
||||
if ((r = info(param->name, user))) {
|
||||
dm_destroy(md);
|
||||
goto bad;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
bad:
|
||||
dm_table_destroy(t);
|
||||
return r;
|
||||
}
|
||||
|
||||
static int remove(struct dm_ioctl *param)
|
||||
{
|
||||
struct mapped_device *md = dm_get(param->name);
|
||||
|
||||
if (!md)
|
||||
return -ENXIO;
|
||||
|
||||
return dm_destroy(md);
|
||||
}
|
||||
|
||||
static int suspend(struct dm_ioctl *param)
|
||||
{
|
||||
struct mapped_device *md = dm_get(param->name);
|
||||
|
||||
if (!md)
|
||||
return -ENXIO;
|
||||
|
||||
return param->suspend ? dm_suspend(md) : dm_resume(md);
|
||||
}
|
||||
|
||||
static int reload(struct dm_ioctl *param)
|
||||
{
|
||||
int r;
|
||||
struct mapped_device *md = dm_get(param->name);
|
||||
struct dm_table *t;
|
||||
|
||||
if (!md)
|
||||
return -ENXIO;
|
||||
|
||||
t = dm_table_create();
|
||||
if (IS_ERR(t))
|
||||
return PTR_ERR(t);
|
||||
|
||||
if ((r = populate_table(t, param))) {
|
||||
dm_table_destroy(t);
|
||||
return r;
|
||||
}
|
||||
|
||||
if ((r = dm_swap_table(md, t))) {
|
||||
dm_table_destroy(t);
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctl_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
/* only root can open this */
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EACCES;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ctl_close(struct inode *inode, struct file *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int ctl_ioctl(struct inode *inode, struct file *file,
|
||||
uint command, ulong a)
|
||||
{
|
||||
int r;
|
||||
struct dm_ioctl *p;
|
||||
|
||||
if ((r = copy_params((struct dm_ioctl *) a, &p)))
|
||||
return r;
|
||||
|
||||
switch (command) {
|
||||
case DM_CREATE:
|
||||
r = create(p, (struct dm_ioctl *) a);
|
||||
break;
|
||||
|
||||
case DM_REMOVE:
|
||||
r = remove(p);
|
||||
break;
|
||||
|
||||
case DM_SUSPEND:
|
||||
r = suspend(p);
|
||||
break;
|
||||
|
||||
case DM_RELOAD:
|
||||
r = reload(p);
|
||||
break;
|
||||
|
||||
case DM_INFO:
|
||||
r = info(p->name, (struct dm_ioctl *) a);
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN("dm_ctl_ioctl: unknown command 0x%x\n", command);
|
||||
r = -EINVAL;
|
||||
}
|
||||
|
||||
free_params(p);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
static struct file_operations _ctl_fops = {
|
||||
open: ctl_open,
|
||||
release: ctl_close,
|
||||
ioctl: ctl_ioctl,
|
||||
owner: THIS_MODULE,
|
||||
};
|
||||
|
||||
|
||||
static devfs_handle_t _ctl_handle;
|
||||
|
||||
int dm_interface_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = devfs_register_chrdev(DM_CHAR_MAJOR, DM_DIR,
|
||||
&_ctl_fops)) < 0) {
|
||||
WARN("devfs_register_chrdev failed for dm control dev");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
_ctl_handle = devfs_register(0 , DM_DIR "/control", 0,
|
||||
DM_CHAR_MAJOR, 0,
|
||||
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
|
||||
&_ctl_fops, NULL);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
void dm_interface_exit(void)
|
||||
{
|
||||
// FIXME: remove control device
|
||||
|
||||
if (devfs_unregister_chrdev(DM_CHAR_MAJOR, DM_DIR) < 0)
|
||||
WARN("devfs_unregister_chrdev failed for dm control device");
|
||||
}
|
||||
|
@ -1,57 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#ifndef _DM_IOCTL_H
|
||||
#define _DM_IOCTL_H
|
||||
|
||||
#include "device-mapper.h"
|
||||
|
||||
/*
|
||||
* Implements a traditional ioctl interface to the
|
||||
* device mapper. Yuck.
|
||||
*/
|
||||
|
||||
struct dm_target_spec {
|
||||
int32_t status; /* used when reading from kernel only */
|
||||
unsigned long long sector_start;
|
||||
unsigned long long length;
|
||||
|
||||
char target_type[DM_MAX_TYPE_NAME];
|
||||
|
||||
unsigned long next; /* offset in bytes to next target_spec */
|
||||
|
||||
/*
|
||||
* Parameter string starts immediately
|
||||
* after this object. Be careful to add
|
||||
* padding after string to ensure correct
|
||||
* alignment of subsequent dm_target_spec.
|
||||
*/
|
||||
};
|
||||
|
||||
struct dm_ioctl {
|
||||
unsigned long data_size; /* the size of this structure */
|
||||
char name[DM_NAME_LEN];
|
||||
|
||||
int exists; /* out */
|
||||
int suspend; /* in/out */
|
||||
int open_count; /* out */
|
||||
int major; /* out */
|
||||
int minor; /* in/out */
|
||||
|
||||
int target_count; /* in/out */
|
||||
};
|
||||
|
||||
/* FIXME: find own numbers, 109 is pinched from LVM */
|
||||
#define DM_IOCTL 0xfd
|
||||
#define DM_CHAR_MAJOR 124
|
||||
|
||||
#define DM_CREATE _IOWR(DM_IOCTL, 0x00, struct dm_ioctl)
|
||||
#define DM_REMOVE _IOW(DM_IOCTL, 0x01, struct dm_ioctl)
|
||||
#define DM_SUSPEND _IOW(DM_IOCTL, 0x02, struct dm_ioctl)
|
||||
#define DM_RELOAD _IOWR(DM_IOCTL, 0x03, struct dm_ioctl)
|
||||
#define DM_INFO _IOWR(DM_IOCTL, 0x04, struct dm_ioctl)
|
||||
|
||||
#endif
|
@ -1,146 +0,0 @@
|
||||
/*
|
||||
* dm-linear.c
|
||||
*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/device-mapper.h>
|
||||
|
||||
#include "dm.h"
|
||||
|
||||
/*
|
||||
* linear: maps a linear range of a device.
|
||||
*/
|
||||
struct linear_c {
|
||||
long delta; /* FIXME: we need a signed offset type */
|
||||
struct dm_dev *dev;
|
||||
};
|
||||
|
||||
static inline char *next_token(char **p)
|
||||
{
|
||||
static const char *delim = " \t";
|
||||
char *r;
|
||||
|
||||
do {
|
||||
r = strsep(p, delim);
|
||||
} while(r && *r == 0);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* construct a linear mapping.
|
||||
* <dev_path> <offset>
|
||||
*/
|
||||
static int linear_ctr(struct dm_table *t, offset_t b, offset_t l,
|
||||
char *args, void **context)
|
||||
{
|
||||
struct linear_c *lc;
|
||||
unsigned int start;
|
||||
int r = -EINVAL;
|
||||
char *tok;
|
||||
char *path;
|
||||
char *p = args;
|
||||
|
||||
*context = "No device path given";
|
||||
path = next_token(&p);
|
||||
if (!path)
|
||||
goto bad;
|
||||
|
||||
*context = "No initial offset given";
|
||||
tok = next_token(&p);
|
||||
if (!tok)
|
||||
goto bad;
|
||||
start = simple_strtoul(tok, NULL, 10);
|
||||
|
||||
*context = "Cannot allocate linear context private structure";
|
||||
lc = kmalloc(sizeof(lc), GFP_KERNEL);
|
||||
if (lc == NULL)
|
||||
goto bad;
|
||||
|
||||
*context = "Cannot get target device";
|
||||
r = dm_table_get_device(t, path, start, l, &lc->dev);
|
||||
if (r)
|
||||
goto bad_free;
|
||||
|
||||
lc->delta = (int) start - (int) b;
|
||||
*context = lc;
|
||||
return 0;
|
||||
|
||||
bad_free:
|
||||
kfree(lc);
|
||||
bad:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void linear_dtr(struct dm_table *t, void *c)
|
||||
{
|
||||
struct linear_c *lc = (struct linear_c *) c;
|
||||
dm_table_put_device(t, lc->dev);
|
||||
kfree(c);
|
||||
}
|
||||
|
||||
static int linear_map(struct buffer_head *bh, int rw, void *context)
|
||||
{
|
||||
struct linear_c *lc = (struct linear_c *) context;
|
||||
|
||||
bh->b_rdev = lc->dev->dev;
|
||||
bh->b_rsector = bh->b_rsector + lc->delta;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Debugging use only.
|
||||
*/
|
||||
static char *linear_print(void *context)
|
||||
{
|
||||
struct linear_c *lc = (struct linear_c *)context;
|
||||
static char buf[256];
|
||||
sprintf(buf, " %lu", lc->delta);
|
||||
return buf;
|
||||
}
|
||||
|
||||
static struct target_type linear_target = {
|
||||
name: "linear",
|
||||
module: THIS_MODULE,
|
||||
ctr: linear_ctr,
|
||||
dtr: linear_dtr,
|
||||
map: linear_map,
|
||||
print: linear_print,
|
||||
};
|
||||
|
||||
static int __init linear_init(void)
|
||||
{
|
||||
int r = dm_register_target(&linear_target);
|
||||
|
||||
if (r < 0)
|
||||
printk(KERN_ERR
|
||||
"Device mapper: Linear: register failed %d\n", r);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __exit linear_exit(void)
|
||||
{
|
||||
int r = dm_unregister_target(&linear_target);
|
||||
|
||||
if (r < 0)
|
||||
printk(KERN_ERR
|
||||
"Device mapper: Linear: unregister failed %d\n", r);
|
||||
}
|
||||
|
||||
module_init(linear_init);
|
||||
module_exit(linear_exit);
|
||||
|
||||
MODULE_AUTHOR("Joe Thornber <thornber@uk.sistina.com>");
|
||||
MODULE_DESCRIPTION("Device Mapper: Linear mapping");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
@ -1,185 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include <linux/config.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/device-mapper.h>
|
||||
|
||||
#include "dm.h"
|
||||
|
||||
struct stripe {
|
||||
struct dm_dev *dev;
|
||||
offset_t physical_start;
|
||||
};
|
||||
|
||||
struct stripe_c {
|
||||
offset_t logical_start;
|
||||
uint32_t stripes;
|
||||
|
||||
/* The size of this target / num. stripes */
|
||||
uint32_t stripe_width;
|
||||
|
||||
/* eg, we stripe in 64k chunks */
|
||||
uint32_t chunk_shift;
|
||||
offset_t chunk_mask;
|
||||
|
||||
struct stripe stripe[0];
|
||||
};
|
||||
|
||||
|
||||
static inline struct stripe_c *alloc_context(int stripes)
|
||||
{
|
||||
size_t len = sizeof(struct stripe_c) +
|
||||
(sizeof(struct stripe) * stripes);
|
||||
return kmalloc(len, GFP_KERNEL);
|
||||
}
|
||||
|
||||
/*
|
||||
* parses a single <dev> <sector> pair.
|
||||
*/
|
||||
static int get_stripe(struct dm_table *t, struct stripe_c *sc,
|
||||
int stripe, char *args)
|
||||
{
|
||||
int n, r;
|
||||
char path[256]; /* FIXME: buffer overrun risk */
|
||||
unsigned long start;
|
||||
|
||||
if (sscanf(args, "%s %lu %n", path, &start, &n) != 2)
|
||||
return -EINVAL;
|
||||
|
||||
if ((r = dm_table_get_device(t, path, start, sc->stripe_width,
|
||||
&sc->stripe[stripe].dev)))
|
||||
return -ENXIO;
|
||||
|
||||
sc->stripe[stripe].physical_start = start;
|
||||
return n;
|
||||
}
|
||||
|
||||
/*
|
||||
* construct a striped mapping.
|
||||
* <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+
|
||||
*/
|
||||
static int stripe_ctr(struct dm_table *t, offset_t b, offset_t l,
|
||||
char *args, void **context)
|
||||
{
|
||||
struct stripe_c *sc;
|
||||
uint32_t stripes;
|
||||
uint32_t chunk_size;
|
||||
int n, i;
|
||||
|
||||
*context = "couldn't parse <stripes> <chunk size>";
|
||||
if (sscanf(args, "%u %u %n", &stripes, &chunk_size, &n) != 2) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*context = "target length is not divisable by the number of stripes";
|
||||
if (l % stripes) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*context = "couldn't allocate memory for striped context";
|
||||
if (!(sc = alloc_context(stripes))) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sc->logical_start = b;
|
||||
sc->stripes = stripes;
|
||||
sc->stripe_width = l / stripes;
|
||||
|
||||
/*
|
||||
* chunk_size is a power of two. We only
|
||||
* that power and the mask.
|
||||
*/
|
||||
*context = "invalid chunk size";
|
||||
if (!chunk_size) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
sc->chunk_mask = chunk_size - 1;
|
||||
for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++)
|
||||
chunk_size >>= 1;
|
||||
sc->chunk_shift--;
|
||||
|
||||
/*
|
||||
* Get the stripe destinations.
|
||||
*/
|
||||
for (i = 0; i < stripes; i++) {
|
||||
args += n;
|
||||
n = get_stripe(t, sc, i, args);
|
||||
|
||||
*context = "couldn't parse stripe destination";
|
||||
if (n < 0) {
|
||||
kfree(sc);
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
*context = sc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void stripe_dtr(struct dm_table *t, void *c)
|
||||
{
|
||||
unsigned int i;
|
||||
struct stripe_c *sc = (struct stripe_c *) c;
|
||||
|
||||
for (i = 0; i < sc->stripes; i++)
|
||||
dm_table_put_device(t, sc->stripe[i].dev);
|
||||
|
||||
kfree(sc);
|
||||
}
|
||||
|
||||
static int stripe_map(struct buffer_head *bh, int rw, void *context)
|
||||
{
|
||||
struct stripe_c *sc = (struct stripe_c *) context;
|
||||
|
||||
offset_t offset = bh->b_rsector - sc->logical_start;
|
||||
uint32_t chunk = (uint32_t) (offset >> sc->chunk_shift);
|
||||
uint32_t stripe = chunk % sc->stripes; /* 32bit modulus */
|
||||
chunk = chunk / sc->stripes;
|
||||
|
||||
bh->b_rdev = sc->stripe[stripe].dev->dev;
|
||||
bh->b_rsector = sc->stripe[stripe].physical_start +
|
||||
(chunk << sc->chunk_shift) +
|
||||
(offset & sc->chunk_mask);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static struct target_type stripe_target = {
|
||||
name: "striped",
|
||||
module: THIS_MODULE,
|
||||
ctr: stripe_ctr,
|
||||
dtr: stripe_dtr,
|
||||
map: stripe_map,
|
||||
};
|
||||
|
||||
static int __init stripe_init(void)
|
||||
{
|
||||
int r;
|
||||
|
||||
if ((r = dm_register_target(&stripe_target)) < 0)
|
||||
WARN("linear target register failed");
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void __exit stripe_exit(void)
|
||||
{
|
||||
if (dm_unregister_target(&stripe_target))
|
||||
WARN("striped target unregister failed");
|
||||
}
|
||||
|
||||
module_init(stripe_init);
|
||||
module_exit(stripe_exit);
|
||||
|
||||
MODULE_AUTHOR("Joe Thornber <thornber@sistina.com>");
|
||||
MODULE_DESCRIPTION("Device Mapper: Striped mapping");
|
||||
MODULE_LICENSE("GPL");
|
@ -1,407 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited.
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "dm.h"
|
||||
|
||||
#include <linux/blkdev.h>
|
||||
|
||||
|
||||
/* ceiling(n / size) * size */
|
||||
static inline ulong round_up(ulong n, ulong size)
|
||||
{
|
||||
ulong r = n % size;
|
||||
return n + (r ? (size - r) : 0);
|
||||
}
|
||||
|
||||
/* ceiling(n / size) */
|
||||
static inline ulong div_up(ulong n, ulong size)
|
||||
{
|
||||
return round_up(n, size) / size;
|
||||
}
|
||||
|
||||
/* similar to ceiling(log_size(n)) */
|
||||
static uint int_log(ulong n, ulong base)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
while (n > 1) {
|
||||
n = div_up(n, base);
|
||||
result++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* return the highest key that you could lookup
|
||||
* from the n'th node on level l of the btree.
|
||||
*/
|
||||
static offset_t high(struct dm_table *t, int l, int n)
|
||||
{
|
||||
for (; l < t->depth - 1; l++)
|
||||
n = get_child(n, CHILDREN_PER_NODE - 1);
|
||||
|
||||
if (n >= t->counts[l])
|
||||
return (offset_t) -1;
|
||||
|
||||
return get_node(t, l, n)[KEYS_PER_NODE - 1];
|
||||
}
|
||||
|
||||
/*
|
||||
* fills in a level of the btree based on the
|
||||
* highs of the level below it.
|
||||
*/
|
||||
static int setup_btree_index(int l, struct dm_table *t)
|
||||
{
|
||||
int n, k;
|
||||
offset_t *node;
|
||||
|
||||
for (n = 0; n < t->counts[l]; n++) {
|
||||
node = get_node(t, l, n);
|
||||
|
||||
for (k = 0; k < KEYS_PER_NODE; k++)
|
||||
node[k] = high(t, l + 1, get_child(n, k));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* highs, and targets are managed as dynamic
|
||||
* arrays during a table load.
|
||||
*/
|
||||
static int alloc_targets(struct dm_table *t, int num)
|
||||
{
|
||||
offset_t *n_highs;
|
||||
struct target *n_targets;
|
||||
int n = t->num_targets;
|
||||
int size = (sizeof(struct target) + sizeof(offset_t)) * num;
|
||||
|
||||
n_highs = vmalloc(size);
|
||||
if (!n_highs)
|
||||
return -ENOMEM;
|
||||
|
||||
n_targets = (struct target *) (n_highs + num);
|
||||
|
||||
if (n) {
|
||||
memcpy(n_highs, t->highs, sizeof(*n_highs) * n);
|
||||
memcpy(n_targets, t->targets, sizeof(*n_targets) * n);
|
||||
}
|
||||
|
||||
memset(n_highs + n , -1, sizeof(*n_highs) * (num - n));
|
||||
vfree(t->highs);
|
||||
|
||||
t->num_allocated = num;
|
||||
t->highs = n_highs;
|
||||
t->targets = n_targets;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dm_table *dm_table_create(void)
|
||||
{
|
||||
struct dm_table *t = kmalloc(sizeof(struct dm_table), GFP_NOIO);
|
||||
|
||||
if (!t)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
memset(t, 0, sizeof(*t));
|
||||
INIT_LIST_HEAD(&t->devices);
|
||||
|
||||
/* allocate a single nodes worth of targets to
|
||||
begin with */
|
||||
if (alloc_targets(t, KEYS_PER_NODE)) {
|
||||
kfree(t);
|
||||
t = ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void free_devices(struct list_head *devices)
|
||||
{
|
||||
struct list_head *tmp, *next;
|
||||
|
||||
for (tmp = devices->next; tmp != devices; tmp = next) {
|
||||
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
|
||||
next = tmp->next;
|
||||
kfree(dd);
|
||||
}
|
||||
}
|
||||
|
||||
void dm_table_destroy(struct dm_table *t)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* free the indexes (see dm_table_complete) */
|
||||
if (t->depth >= 2)
|
||||
vfree(t->index[t->depth - 2]);
|
||||
|
||||
/* free the targets */
|
||||
for (i = 0; i < t->num_targets; i++) {
|
||||
struct target *tgt = &t->targets[i];
|
||||
|
||||
if (tgt->type->dtr)
|
||||
tgt->type->dtr(t, tgt->private);
|
||||
|
||||
dm_put_target_type(t->targets[i].type);
|
||||
}
|
||||
|
||||
vfree(t->highs);
|
||||
|
||||
/* free the device list */
|
||||
if (t->devices.next != &t->devices) {
|
||||
WARN("there are still devices present, someone isn't "
|
||||
"calling dm_table_remove_device");
|
||||
|
||||
free_devices(&t->devices);
|
||||
}
|
||||
|
||||
kfree(t);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks to see if we need to extend
|
||||
* highs or targets.
|
||||
*/
|
||||
static inline int check_space(struct dm_table *t)
|
||||
{
|
||||
if (t->num_targets >= t->num_allocated)
|
||||
return alloc_targets(t, t->num_allocated * 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* convert a device path to a kdev_t.
|
||||
*/
|
||||
int lookup_device(const char *path, kdev_t *dev)
|
||||
{
|
||||
int r;
|
||||
struct nameidata nd;
|
||||
struct inode *inode;
|
||||
|
||||
if (!path_init(path, LOOKUP_FOLLOW, &nd))
|
||||
return 0;
|
||||
|
||||
if ((r = path_walk(path, &nd)))
|
||||
goto bad;
|
||||
|
||||
inode = nd.dentry->d_inode;
|
||||
if (!inode) {
|
||||
r = -ENOENT;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
if (!S_ISBLK(inode->i_mode)) {
|
||||
r = -EINVAL;
|
||||
goto bad;
|
||||
}
|
||||
|
||||
*dev = inode->i_rdev;
|
||||
|
||||
bad:
|
||||
path_release(&nd);
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* see if we've already got a device in the list.
|
||||
*/
|
||||
static struct dm_dev *find_device(struct list_head *l, kdev_t dev)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
|
||||
list_for_each(tmp, l) {
|
||||
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
|
||||
if (dd->dev == dev)
|
||||
return dd;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* open a device so we can use it as a map
|
||||
* destination.
|
||||
*/
|
||||
static int open_dev(struct dm_dev *d)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (d->bd)
|
||||
BUG();
|
||||
|
||||
if (!(d->bd = bdget(kdev_t_to_nr(d->dev))))
|
||||
return -ENOMEM;
|
||||
|
||||
if ((err = blkdev_get(d->bd, FMODE_READ|FMODE_WRITE, 0, BDEV_FILE)))
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* close a device that we've been using.
|
||||
*/
|
||||
static void close_dev(struct dm_dev *d)
|
||||
{
|
||||
if (!d->bd)
|
||||
return;
|
||||
|
||||
blkdev_put(d->bd, BDEV_FILE);
|
||||
d->bd = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* If possible (ie. blk_size[major] is set), this
|
||||
* checks an area of a destination device is
|
||||
* valid.
|
||||
*/
|
||||
static int check_device_area(kdev_t dev, offset_t start, offset_t len)
|
||||
{
|
||||
int *sizes;
|
||||
offset_t dev_size;
|
||||
|
||||
if (!(sizes = blk_size[MAJOR(dev)]) || !(dev_size = sizes[MINOR(dev)]))
|
||||
/* we don't know the device details,
|
||||
* so give the benefit of the doubt */
|
||||
return 1;
|
||||
|
||||
/* convert to 512-byte sectors */
|
||||
dev_size <<= 1;
|
||||
|
||||
return ((start < dev_size) && (len <= (dev_size - start)));
|
||||
}
|
||||
|
||||
/*
|
||||
* add a device to the list, or just increment the
|
||||
* usage count if it's already present.
|
||||
*/
|
||||
int dm_table_get_device(struct dm_table *t, const char *path,
|
||||
offset_t start, offset_t len,
|
||||
struct dm_dev **result)
|
||||
{
|
||||
int r;
|
||||
kdev_t dev;
|
||||
struct dm_dev *dd;
|
||||
|
||||
/* convert the path to a device */
|
||||
if ((r = lookup_device(path, &dev)))
|
||||
return r;
|
||||
|
||||
dd = find_device(&t->devices, dev);
|
||||
if (!dd) {
|
||||
dd = kmalloc(sizeof(*dd), GFP_KERNEL);
|
||||
if (!dd)
|
||||
return -ENOMEM;
|
||||
|
||||
dd->dev = dev;
|
||||
dd->bd = 0;
|
||||
|
||||
if ((r = open_dev(dd))) {
|
||||
kfree(dd);
|
||||
return r;
|
||||
}
|
||||
|
||||
atomic_set(&dd->count, 0);
|
||||
list_add(&dd->list, &t->devices);
|
||||
}
|
||||
atomic_inc(&dd->count);
|
||||
|
||||
if (!check_device_area(dd->dev, start, len)) {
|
||||
WARN("device '%s' not large enough for target", path);
|
||||
dm_table_put_device(t, dd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*result = dd;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* decrement a devices use count and remove it if
|
||||
* neccessary.
|
||||
*/
|
||||
void dm_table_put_device(struct dm_table *t, struct dm_dev *dd)
|
||||
{
|
||||
if (atomic_dec_and_test(&dd->count)) {
|
||||
close_dev(dd);
|
||||
list_del(&dd->list);
|
||||
kfree(dd);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* adds a target to the map
|
||||
*/
|
||||
int dm_table_add_target(struct dm_table *t, offset_t high,
|
||||
struct target_type *type, void *private)
|
||||
{
|
||||
int r, n;
|
||||
|
||||
if ((r = check_space(t)))
|
||||
return r;
|
||||
|
||||
n = t->num_targets++;
|
||||
t->highs[n] = high;
|
||||
t->targets[n].type = type;
|
||||
t->targets[n].private = private;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int setup_indexes(struct dm_table *t)
|
||||
{
|
||||
int i, total = 0;
|
||||
offset_t *indexes;
|
||||
|
||||
/* allocate the space for *all* the indexes */
|
||||
for (i = t->depth - 2; i >= 0; i--) {
|
||||
t->counts[i] = div_up(t->counts[i + 1], CHILDREN_PER_NODE);
|
||||
total += t->counts[i];
|
||||
}
|
||||
|
||||
if (!(indexes = vmalloc(NODE_SIZE * total)))
|
||||
return -ENOMEM;
|
||||
|
||||
/* set up internal nodes, bottom-up */
|
||||
for (i = t->depth - 2, total = 0; i >= 0; i--) {
|
||||
t->index[i] = indexes;
|
||||
indexes += (KEYS_PER_NODE * t->counts[i]);
|
||||
setup_btree_index(i, t);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* builds the btree to index the map
|
||||
*/
|
||||
int dm_table_complete(struct dm_table *t)
|
||||
{
|
||||
int leaf_nodes, r = 0;
|
||||
|
||||
/* how many indexes will the btree have ? */
|
||||
leaf_nodes = div_up(t->num_targets, KEYS_PER_NODE);
|
||||
t->depth = 1 + int_log(leaf_nodes, CHILDREN_PER_NODE);
|
||||
|
||||
/* leaf layer has already been set up */
|
||||
t->counts[t->depth - 1] = leaf_nodes;
|
||||
t->index[t->depth - 1] = t->highs;
|
||||
|
||||
if (t->depth >= 2)
|
||||
r = setup_indexes(t);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(dm_table_get_device);
|
||||
EXPORT_SYMBOL(dm_table_put_device);
|
@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software (UK) Limited
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "dm.h"
|
||||
#include <linux/kmod.h>
|
||||
|
||||
struct tt_internal {
|
||||
struct target_type tt;
|
||||
|
||||
struct list_head list;
|
||||
long use;
|
||||
};
|
||||
|
||||
static LIST_HEAD(_targets);
|
||||
static rwlock_t _lock = RW_LOCK_UNLOCKED;
|
||||
|
||||
#define DM_MOD_NAME_SIZE 32
|
||||
|
||||
static inline struct tt_internal *__find_target_type(const char *name)
|
||||
{
|
||||
struct list_head *tmp;
|
||||
struct tt_internal *ti;
|
||||
|
||||
list_for_each(tmp, &_targets) {
|
||||
ti = list_entry(tmp, struct tt_internal, list);
|
||||
|
||||
if (!strcmp(name, ti->tt.name))
|
||||
return ti;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct tt_internal *get_target_type(const char *name)
|
||||
{
|
||||
struct tt_internal *ti;
|
||||
|
||||
read_lock(&_lock);
|
||||
ti = __find_target_type(name);
|
||||
|
||||
if (ti) {
|
||||
if (ti->use == 0 && ti->tt.module)
|
||||
__MOD_INC_USE_COUNT(ti->tt.module);
|
||||
ti->use++;
|
||||
}
|
||||
read_unlock(&_lock);
|
||||
|
||||
return ti;
|
||||
}
|
||||
|
||||
static void load_module(const char *name)
|
||||
{
|
||||
char module_name[DM_MOD_NAME_SIZE] = "dm-";
|
||||
|
||||
/* Length check for strcat() below */
|
||||
if (strlen(name) > (DM_MOD_NAME_SIZE - 4))
|
||||
return;
|
||||
|
||||
strcat(module_name, name);
|
||||
request_module(module_name);
|
||||
}
|
||||
|
||||
struct target_type *dm_get_target_type(const char *name)
|
||||
{
|
||||
struct tt_internal *ti = get_target_type(name);
|
||||
|
||||
if (!ti) {
|
||||
load_module(name);
|
||||
ti = get_target_type(name);
|
||||
}
|
||||
|
||||
return ti ? &ti->tt : NULL;
|
||||
}
|
||||
|
||||
void dm_put_target_type(struct target_type *t)
|
||||
{
|
||||
struct tt_internal *ti = (struct tt_internal *) t;
|
||||
|
||||
read_lock(&_lock);
|
||||
if (--ti->use == 0 && ti->tt.module)
|
||||
__MOD_DEC_USE_COUNT(ti->tt.module);
|
||||
|
||||
if (ti->use < 0)
|
||||
BUG();
|
||||
read_unlock(&_lock);
|
||||
}
|
||||
|
||||
static struct tt_internal *alloc_target(struct target_type *t)
|
||||
{
|
||||
struct tt_internal *ti = kmalloc(sizeof(*ti), GFP_KERNEL);
|
||||
|
||||
if (ti) {
|
||||
memset(ti, 0, sizeof(*ti));
|
||||
ti->tt = *t;
|
||||
}
|
||||
|
||||
return ti;
|
||||
}
|
||||
|
||||
int dm_register_target(struct target_type *t)
|
||||
{
|
||||
int rv = 0;
|
||||
struct tt_internal *ti = alloc_target(t);
|
||||
|
||||
if (!ti)
|
||||
return -ENOMEM;
|
||||
|
||||
write_lock(&_lock);
|
||||
if (__find_target_type(t->name))
|
||||
rv = -EEXIST;
|
||||
else
|
||||
list_add(&ti->list, &_targets);
|
||||
|
||||
write_unlock(&_lock);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int dm_unregister_target(struct target_type *t)
|
||||
{
|
||||
struct tt_internal *ti;
|
||||
|
||||
write_lock(&_lock);
|
||||
if (!(ti = __find_target_type(t->name))) {
|
||||
write_unlock(&_lock);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (ti->use) {
|
||||
write_unlock(&_lock);
|
||||
return -ETXTBSY;
|
||||
}
|
||||
|
||||
list_del(&ti->list);
|
||||
kfree(ti);
|
||||
|
||||
write_unlock(&_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* io-err: always fails an io, useful for bringing
|
||||
* up LV's that have holes in them.
|
||||
*/
|
||||
static int io_err_ctr(struct dm_table *t, offset_t b, offset_t l,
|
||||
char *args, void **context)
|
||||
{
|
||||
*context = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void io_err_dtr(struct dm_table *t, void *c)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
static int io_err_map(struct buffer_head *bh, int rw, void *context)
|
||||
{
|
||||
buffer_IO_error(bh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct target_type error_target = {
|
||||
name: "error",
|
||||
ctr: io_err_ctr,
|
||||
dtr: io_err_dtr,
|
||||
map: io_err_map
|
||||
};
|
||||
|
||||
|
||||
int dm_target_init(void)
|
||||
{
|
||||
return dm_register_target(&error_target);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(dm_register_target);
|
||||
EXPORT_SYMBOL(dm_unregister_target);
|
||||
|
@ -1,900 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2001 Sistina Software
|
||||
*
|
||||
* This file is released under the GPL.
|
||||
*/
|
||||
|
||||
#include "dm.h"
|
||||
|
||||
#include <linux/blk.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/blkpg.h>
|
||||
#include <linux/kmod.h>
|
||||
|
||||
/* we only need this for the lv_bmap struct definition, not happy */
|
||||
#include <linux/lvm.h>
|
||||
|
||||
#define MAX_DEVICES 64
|
||||
#define DEFAULT_READ_AHEAD 64
|
||||
#define DEVICE_NAME "device-mapper"
|
||||
|
||||
static const char *_name = DEVICE_NAME;
|
||||
static int _version[3] = {0, 1, 0};
|
||||
static int major = 0;
|
||||
|
||||
struct io_hook {
|
||||
struct mapped_device *md;
|
||||
struct target *target;
|
||||
int rw;
|
||||
|
||||
void (*end_io)(struct buffer_head * bh, int uptodate);
|
||||
void *context;
|
||||
};
|
||||
|
||||
static kmem_cache_t *_io_hook_cache;
|
||||
|
||||
#define rl down_read(&_dev_lock)
|
||||
#define ru up_read(&_dev_lock)
|
||||
#define wl down_write(&_dev_lock)
|
||||
#define wu up_write(&_dev_lock)
|
||||
|
||||
static struct rw_semaphore _dev_lock;
|
||||
static struct mapped_device *_devs[MAX_DEVICES];
|
||||
|
||||
/* block device arrays */
|
||||
static int _block_size[MAX_DEVICES];
|
||||
static int _blksize_size[MAX_DEVICES];
|
||||
static int _hardsect_size[MAX_DEVICES];
|
||||
|
||||
static devfs_handle_t _dev_dir;
|
||||
|
||||
static int request(request_queue_t *q, int rw, struct buffer_head *bh);
|
||||
|