1
0
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:
Alasdair Kergon 2001-12-06 14:20:38 +00:00
parent a9c5b0dee6
commit a8bc68638c
26 changed files with 0 additions and 4011 deletions

View File

@ -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.

View File

@ -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:
*/

View File

@ -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, &params) :
next_target(spec, end, &spec, &params);
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, &param, 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");
}

View File

@ -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

View File

@ -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");

View File

@ -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");

View File

@ -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);

View File

@ -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);

View File

@ -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);