mirror of
git://sourceware.org/git/lvm2.git
synced 2025-01-03 05:18:29 +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);
|
|
||||||
static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* setup and teardown the driver
|
|
||||||
*/
|
|
||||||
static int __init dm_init(void)
|
|
||||||
{
|
|
||||||
int ret = -ENOMEM;
|
|
||||||
|
|
||||||
init_rwsem(&_dev_lock);
|
|
||||||
|
|
||||||
_io_hook_cache = kmem_cache_create("dm io hooks",
|
|
||||||
sizeof(struct io_hook),
|
|
||||||
0, 0, NULL, NULL);
|
|
||||||
|
|
||||||
if (!_io_hook_cache)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
ret = dm_target_init();
|
|
||||||
if (ret < 0)
|
|
||||||
goto err_cache_free;
|
|
||||||
|
|
||||||
ret = dm_interface_init();
|
|
||||||
if (ret < 0)
|
|
||||||
goto err_cache_free;
|
|
||||||
|
|
||||||
ret = devfs_register_blkdev(major, _name, &dm_blk_dops);
|
|
||||||
if (ret < 0)
|
|
||||||
goto err_blkdev;
|
|
||||||
|
|
||||||
if (major == 0)
|
|
||||||
major = ret;
|
|
||||||
|
|
||||||
/* set up the arrays */
|
|
||||||
read_ahead[major] = DEFAULT_READ_AHEAD;
|
|
||||||
blk_size[major] = _block_size;
|
|
||||||
blksize_size[major] = _blksize_size;
|
|
||||||
hardsect_size[major] = _hardsect_size;
|
|
||||||
|
|
||||||
blk_queue_make_request(BLK_DEFAULT_QUEUE(major), request);
|
|
||||||
|
|
||||||
_dev_dir = devfs_mk_dir(0, DM_DIR, NULL);
|
|
||||||
|
|
||||||
printk(KERN_INFO "%s %d.%d.%d initialised\n", _name,
|
|
||||||
_version[0], _version[1], _version[2]);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
err_blkdev:
|
|
||||||
printk(KERN_ERR "%s -- register_blkdev failed\n", _name);
|
|
||||||
dm_interface_exit();
|
|
||||||
err_cache_free:
|
|
||||||
kmem_cache_destroy(_io_hook_cache);
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __exit dm_exit(void)
|
|
||||||
{
|
|
||||||
dm_interface_exit();
|
|
||||||
|
|
||||||
if (kmem_cache_destroy(_io_hook_cache))
|
|
||||||
WARN("it looks like there are still some io_hooks allocated");
|
|
||||||
|
|
||||||
_io_hook_cache = NULL;
|
|
||||||
|
|
||||||
if (devfs_unregister_blkdev(major, _name) < 0)
|
|
||||||
printk(KERN_ERR "%s -- unregister_blkdev failed\n", _name);
|
|
||||||
|
|
||||||
read_ahead[major] = 0;
|
|
||||||
blk_size[major] = NULL;
|
|
||||||
blksize_size[major] = NULL;
|
|
||||||
hardsect_size[major] = NULL;
|
|
||||||
|
|
||||||
printk(KERN_INFO "%s %d.%d.%d cleaned up\n", _name,
|
|
||||||
_version[0], _version[1], _version[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* block device functions
|
|
||||||
*/
|
|
||||||
static int dm_blk_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
int minor = MINOR(inode->i_rdev);
|
|
||||||
struct mapped_device *md;
|
|
||||||
|
|
||||||
if (minor >= MAX_DEVICES)
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
wl;
|
|
||||||
md = _devs[minor];
|
|
||||||
|
|
||||||
if (!md) {
|
|
||||||
wu;
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
md->use_count++;
|
|
||||||
wu;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dm_blk_close(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
int minor = MINOR(inode->i_rdev);
|
|
||||||
struct mapped_device *md;
|
|
||||||
|
|
||||||
if (minor >= MAX_DEVICES)
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
wl;
|
|
||||||
md = _devs[minor];
|
|
||||||
if (!md || md->use_count < 1) {
|
|
||||||
WARN("reference count in mapped_device incorrect");
|
|
||||||
wu;
|
|
||||||
return -ENXIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
md->use_count--;
|
|
||||||
wu;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* In 512-byte units */
|
|
||||||
#define VOLUME_SIZE(minor) (_block_size[(minor)] << 1)
|
|
||||||
|
|
||||||
static int dm_blk_ioctl(struct inode *inode, struct file *file,
|
|
||||||
uint command, ulong a)
|
|
||||||
{
|
|
||||||
int minor = MINOR(inode->i_rdev);
|
|
||||||
long size;
|
|
||||||
|
|
||||||
if (minor >= MAX_DEVICES)
|
|
||||||
return -ENXIO;
|
|
||||||
|
|
||||||
switch (command) {
|
|
||||||
case BLKSSZGET:
|
|
||||||
case BLKBSZGET:
|
|
||||||
case BLKROGET:
|
|
||||||
case BLKROSET:
|
|
||||||
case BLKRASET:
|
|
||||||
case BLKRAGET:
|
|
||||||
case BLKFLSBUF:
|
|
||||||
#if 0
|
|
||||||
case BLKELVSET:
|
|
||||||
case BLKELVGET:
|
|
||||||
#endif
|
|
||||||
return blk_ioctl(inode->i_rdev, command, a);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BLKGETSIZE:
|
|
||||||
size = VOLUME_SIZE(minor);
|
|
||||||
if (copy_to_user((void *) a, &size, sizeof (long)))
|
|
||||||
return -EFAULT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BLKGETSIZE64:
|
|
||||||
size = VOLUME_SIZE(minor);
|
|
||||||
if (put_user((u64)size, (u64 *)a))
|
|
||||||
return -EFAULT;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BLKRRPART:
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
case LV_BMAP:
|
|
||||||
return dm_user_bmap(inode, (struct lv_bmap *) a);
|
|
||||||
|
|
||||||
default:
|
|
||||||
WARN("unknown block ioctl %d", command);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct io_hook *alloc_io_hook(void)
|
|
||||||
{
|
|
||||||
return kmem_cache_alloc(_io_hook_cache, GFP_NOIO);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void free_io_hook(struct io_hook *ih)
|
|
||||||
{
|
|
||||||
kmem_cache_free(_io_hook_cache, ih);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: need to decide if deferred_io's need
|
|
||||||
* their own slab, I say no for now since they are
|
|
||||||
* only used when the device is suspended.
|
|
||||||
*/
|
|
||||||
static inline struct deferred_io *alloc_deferred(void)
|
|
||||||
{
|
|
||||||
return kmalloc(sizeof(struct deferred_io), GFP_NOIO);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void free_deferred(struct deferred_io *di)
|
|
||||||
{
|
|
||||||
kfree(di);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* call a targets optional error function if
|
|
||||||
* an io failed.
|
|
||||||
*/
|
|
||||||
static inline int call_err_fn(struct io_hook *ih, struct buffer_head *bh)
|
|
||||||
{
|
|
||||||
dm_err_fn err = ih->target->type->err;
|
|
||||||
if (err)
|
|
||||||
return err(bh, ih->rw, ih->target->private);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* bh->b_end_io routine that decrements the
|
|
||||||
* pending count and then calls the original
|
|
||||||
* bh->b_end_io fn.
|
|
||||||
*/
|
|
||||||
static void dec_pending(struct buffer_head *bh, int uptodate)
|
|
||||||
{
|
|
||||||
struct io_hook *ih = bh->b_private;
|
|
||||||
|
|
||||||
if (!uptodate && call_err_fn(ih, bh))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (atomic_dec_and_test(&ih->md->pending))
|
|
||||||
/* nudge anyone waiting on suspend queue */
|
|
||||||
wake_up(&ih->md->wait);
|
|
||||||
|
|
||||||
bh->b_end_io = ih->end_io;
|
|
||||||
bh->b_private = ih->context;
|
|
||||||
free_io_hook(ih);
|
|
||||||
|
|
||||||
bh->b_end_io(bh, uptodate);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* add the bh to the list of deferred io.
|
|
||||||
*/
|
|
||||||
static int queue_io(struct mapped_device *md, struct buffer_head *bh, int rw)
|
|
||||||
{
|
|
||||||
struct deferred_io *di = alloc_deferred();
|
|
||||||
|
|
||||||
if (!di)
|
|
||||||
return -ENOMEM;
|
|
||||||
|
|
||||||
wl;
|
|
||||||
if (!md->suspended) {
|
|
||||||
wu;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
di->bh = bh;
|
|
||||||
di->rw = rw;
|
|
||||||
di->next = md->deferred;
|
|
||||||
md->deferred = di;
|
|
||||||
wu;
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* do the bh mapping for a given leaf
|
|
||||||
*/
|
|
||||||
static inline int __map_buffer(struct mapped_device *md,
|
|
||||||
struct buffer_head *bh, int rw, int leaf)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
dm_map_fn fn;
|
|
||||||
void *context;
|
|
||||||
struct io_hook *ih = NULL;
|
|
||||||
struct target *ti = md->map->targets + leaf;
|
|
||||||
|
|
||||||
fn = ti->type->map;
|
|
||||||
context = ti->private;
|
|
||||||
|
|
||||||
ih = alloc_io_hook();
|
|
||||||
|
|
||||||
if (!ih)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ih->md = md;
|
|
||||||
ih->rw = rw;
|
|
||||||
ih->target = ti;
|
|
||||||
ih->end_io = bh->b_end_io;
|
|
||||||
ih->context = bh->b_private;
|
|
||||||
|
|
||||||
r = fn(bh, rw, context);
|
|
||||||
|
|
||||||
if (r > 0) {
|
|
||||||
/* hook the end io request fn */
|
|
||||||
atomic_inc(&md->pending);
|
|
||||||
bh->b_end_io = dec_pending;
|
|
||||||
bh->b_private = ih;
|
|
||||||
|
|
||||||
} else if (r == 0)
|
|
||||||
/* we don't need to hook */
|
|
||||||
free_io_hook(ih);
|
|
||||||
|
|
||||||
else if (r < 0) {
|
|
||||||
free_io_hook(ih);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* search the btree for the correct target.
|
|
||||||
*/
|
|
||||||
static inline int __find_node(struct dm_table *t, struct buffer_head *bh)
|
|
||||||
{
|
|
||||||
int l, n = 0, k = 0;
|
|
||||||
offset_t *node;
|
|
||||||
|
|
||||||
for (l = 0; l < t->depth; l++) {
|
|
||||||
n = get_child(n, k);
|
|
||||||
node = get_node(t, l, n);
|
|
||||||
|
|
||||||
for (k = 0; k < KEYS_PER_NODE; k++)
|
|
||||||
if (node[k] >= bh->b_rsector)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (KEYS_PER_NODE * n) + k;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int request(request_queue_t *q, int rw, struct buffer_head *bh)
|
|
||||||
{
|
|
||||||
struct mapped_device *md;
|
|
||||||
int r, minor = MINOR(bh->b_rdev);
|
|
||||||
|
|
||||||
if (minor >= MAX_DEVICES)
|
|
||||||
goto bad_no_lock;
|
|
||||||
|
|
||||||
rl;
|
|
||||||
md = _devs[minor];
|
|
||||||
|
|
||||||
if (!md)
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If we're suspended we have to queue
|
|
||||||
* this io for later.
|
|
||||||
*/
|
|
||||||
while (md->suspended) {
|
|
||||||
ru;
|
|
||||||
|
|
||||||
if (rw == READA)
|
|
||||||
goto bad_no_lock;
|
|
||||||
|
|
||||||
r = queue_io(md, bh, rw);
|
|
||||||
|
|
||||||
if (r < 0)
|
|
||||||
goto bad_no_lock;
|
|
||||||
|
|
||||||
else if (r > 0)
|
|
||||||
return 0; /* deferred successfully */
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We're in a while loop, because
|
|
||||||
* someone could suspend before we
|
|
||||||
* get to the following read
|
|
||||||
* lock
|
|
||||||
*/
|
|
||||||
rl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!__map_buffer(md, bh, rw, __find_node(md->map, bh)))
|
|
||||||
goto bad;
|
|
||||||
|
|
||||||
ru;
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
bad:
|
|
||||||
ru;
|
|
||||||
|
|
||||||
bad_no_lock:
|
|
||||||
buffer_IO_error(bh);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int check_dev_size(int minor, unsigned long block)
|
|
||||||
{
|
|
||||||
/* FIXME: check this */
|
|
||||||
unsigned long max_sector = (_block_size[minor] << 1) + 1;
|
|
||||||
unsigned long sector = (block + 1) * (_blksize_size[minor] >> 9);
|
|
||||||
|
|
||||||
return (sector > max_sector) ? 0 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* creates a dummy buffer head and maps it (for lilo).
|
|
||||||
*/
|
|
||||||
static int do_bmap(kdev_t dev, unsigned long block,
|
|
||||||
kdev_t *r_dev, unsigned long *r_block)
|
|
||||||
{
|
|
||||||
struct mapped_device *md;
|
|
||||||
struct buffer_head bh;
|
|
||||||
int minor = MINOR(dev), r;
|
|
||||||
struct target *t;
|
|
||||||
|
|
||||||
rl;
|
|
||||||
if ((minor >= MAX_DEVICES) || !(md = _devs[minor]) || md->suspended) {
|
|
||||||
r = -ENXIO;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!check_dev_size(minor, block)) {
|
|
||||||
r = -EINVAL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* setup dummy bh */
|
|
||||||
memset(&bh, 0, sizeof(bh));
|
|
||||||
bh.b_blocknr = block;
|
|
||||||
bh.b_dev = bh.b_rdev = dev;
|
|
||||||
bh.b_size = _blksize_size[minor];
|
|
||||||
bh.b_rsector = block * (bh.b_size >> 9);
|
|
||||||
|
|
||||||
/* find target */
|
|
||||||
t = md->map->targets + __find_node(md->map, &bh);
|
|
||||||
|
|
||||||
/* do the mapping */
|
|
||||||
r = t->type->map(&bh, READ, t->private);
|
|
||||||
|
|
||||||
*r_dev = bh.b_rdev;
|
|
||||||
*r_block = bh.b_rsector / (bh.b_size >> 9);
|
|
||||||
|
|
||||||
out:
|
|
||||||
ru;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* marshals arguments and results between user and
|
|
||||||
* kernel space.
|
|
||||||
*/
|
|
||||||
static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb)
|
|
||||||
{
|
|
||||||
unsigned long block, r_block;
|
|
||||||
kdev_t r_dev;
|
|
||||||
int r;
|
|
||||||
|
|
||||||
if (get_user(block, &lvb->lv_block))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
if ((r = do_bmap(inode->i_rdev, block, &r_dev, &r_block)))
|
|
||||||
return r;
|
|
||||||
|
|
||||||
if (put_user(kdev_t_to_nr(r_dev), &lvb->lv_dev) ||
|
|
||||||
put_user(r_block, &lvb->lv_block))
|
|
||||||
return -EFAULT;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* see if the device with a specific minor # is
|
|
||||||
* free.
|
|
||||||
*/
|
|
||||||
static inline int __specific_dev(int minor)
|
|
||||||
{
|
|
||||||
if (minor > MAX_DEVICES) {
|
|
||||||
WARN("request for a mapped_device > than MAX_DEVICES");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_devs[minor])
|
|
||||||
return minor;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* find the first free device.
|
|
||||||
*/
|
|
||||||
static inline int __any_old_dev(void)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_DEVICES; i++)
|
|
||||||
if (!_devs[i])
|
|
||||||
return i;
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* allocate and initialise a blank device.
|
|
||||||
*/
|
|
||||||
static struct mapped_device *alloc_dev(int minor)
|
|
||||||
{
|
|
||||||
struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL);
|
|
||||||
|
|
||||||
if (!md)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
memset(md, 0, sizeof (*md));
|
|
||||||
|
|
||||||
wl;
|
|
||||||
minor = (minor < 0) ? __any_old_dev() : __specific_dev(minor);
|
|
||||||
|
|
||||||
if (minor < 0) {
|
|
||||||
WARN("no free devices available");
|
|
||||||
wu;
|
|
||||||
kfree(md);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
md->dev = MKDEV(major, minor);
|
|
||||||
md->name[0] = '\0';
|
|
||||||
md->suspended = 0;
|
|
||||||
|
|
||||||
init_waitqueue_head(&md->wait);
|
|
||||||
|
|
||||||
_devs[minor] = md;
|
|
||||||
wu;
|
|
||||||
|
|
||||||
return md;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_dev(struct mapped_device *md)
|
|
||||||
{
|
|
||||||
kfree(md);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int register_device(struct mapped_device *md)
|
|
||||||
{
|
|
||||||
md->devfs_entry =
|
|
||||||
devfs_register(_dev_dir, md->name, DEVFS_FL_CURRENT_OWNER,
|
|
||||||
MAJOR(md->dev), MINOR(md->dev),
|
|
||||||
S_IFBLK | S_IRUSR | S_IWUSR | S_IRGRP,
|
|
||||||
&dm_blk_dops, NULL);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int unregister_device(struct mapped_device *md)
|
|
||||||
{
|
|
||||||
devfs_unregister(md->devfs_entry);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* the hardsect size for a mapped device is the
|
|
||||||
* smallest hard sect size from the devices it
|
|
||||||
* maps onto.
|
|
||||||
*/
|
|
||||||
static int __find_hardsect_size(struct list_head *devices)
|
|
||||||
{
|
|
||||||
int result = INT_MAX, size;
|
|
||||||
struct list_head *tmp;
|
|
||||||
|
|
||||||
list_for_each(tmp, devices) {
|
|
||||||
struct dm_dev *dd = list_entry(tmp, struct dm_dev, list);
|
|
||||||
size = get_hardsect_size(dd->dev);
|
|
||||||
if (size < result)
|
|
||||||
result = size;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Bind a table to the device.
|
|
||||||
*/
|
|
||||||
static int __bind(struct mapped_device *md, struct dm_table *t)
|
|
||||||
{
|
|
||||||
int minor = MINOR(md->dev);
|
|
||||||
|
|
||||||
md->map = t;
|
|
||||||
|
|
||||||
if (!t->num_targets) {
|
|
||||||
_block_size[minor] = 0;
|
|
||||||
_blksize_size[minor] = BLOCK_SIZE;
|
|
||||||
_hardsect_size[minor] = 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* in k */
|
|
||||||
_block_size[minor] = (t->highs[t->num_targets - 1] + 1) >> 1;
|
|
||||||
|
|
||||||
_blksize_size[minor] = BLOCK_SIZE;
|
|
||||||
_hardsect_size[minor] = __find_hardsect_size(&t->devices);
|
|
||||||
register_disk(NULL, md->dev, 1, &dm_blk_dops, _block_size[minor]);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void __unbind(struct mapped_device *md)
|
|
||||||
{
|
|
||||||
int minor = MINOR(md->dev);
|
|
||||||
|
|
||||||
dm_table_destroy(md->map);
|
|
||||||
md->map = NULL;
|
|
||||||
|
|
||||||
_block_size[minor] = 0;
|
|
||||||
_blksize_size[minor] = 0;
|
|
||||||
_hardsect_size[minor] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static struct mapped_device *__get_by_name(const char *name)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < MAX_DEVICES; i++)
|
|
||||||
if (_devs[i] && !strcmp(_devs[i]->name, name))
|
|
||||||
return _devs[i];
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int check_name(const char *name)
|
|
||||||
{
|
|
||||||
if (strchr(name, '/')) {
|
|
||||||
WARN("invalid device name");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (__get_by_name(name)) {
|
|
||||||
WARN("device name already in use");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* constructor for a new device
|
|
||||||
*/
|
|
||||||
struct mapped_device *dm_create(const char *name, int minor,
|
|
||||||
struct dm_table *table)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
struct mapped_device *md;
|
|
||||||
|
|
||||||
if (minor >= MAX_DEVICES)
|
|
||||||
return ERR_PTR(-ENXIO);
|
|
||||||
|
|
||||||
if (!(md = alloc_dev(minor)))
|
|
||||||
return ERR_PTR(-ENXIO);
|
|
||||||
|
|
||||||
wl;
|
|
||||||
if (!check_name(name)) {
|
|
||||||
wu;
|
|
||||||
free_dev(md);
|
|
||||||
return ERR_PTR(-EINVAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
strcpy(md->name, name);
|
|
||||||
_devs[minor] = md;
|
|
||||||
if ((r = register_device(md))) {
|
|
||||||
wu;
|
|
||||||
free_dev(md);
|
|
||||||
return ERR_PTR(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((r = __bind(md, table))) {
|
|
||||||
wu;
|
|
||||||
free_dev(md);
|
|
||||||
return ERR_PTR(r);
|
|
||||||
}
|
|
||||||
wu;
|
|
||||||
|
|
||||||
return md;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Destructor for the device. You cannot destroy
|
|
||||||
* a suspended device.
|
|
||||||
*/
|
|
||||||
int dm_destroy(struct mapped_device *md)
|
|
||||||
{
|
|
||||||
int minor, r;
|
|
||||||
|
|
||||||
rl;
|
|
||||||
if (md->suspended || md->use_count) {
|
|
||||||
ru;
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
fsync_dev(md->dev);
|
|
||||||
ru;
|
|
||||||
|
|
||||||
wl;
|
|
||||||
if (md->use_count) {
|
|
||||||
wu;
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((r = unregister_device(md))) {
|
|
||||||
wu;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
minor = MINOR(md->dev);
|
|
||||||
_devs[minor] = 0;
|
|
||||||
__unbind(md);
|
|
||||||
|
|
||||||
wu;
|
|
||||||
|
|
||||||
free_dev(md);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* requeue the deferred buffer_heads by calling
|
|
||||||
* generic_make_request.
|
|
||||||
*/
|
|
||||||
static void flush_deferred_io(struct deferred_io *c)
|
|
||||||
{
|
|
||||||
struct deferred_io *n;
|
|
||||||
|
|
||||||
while (c) {
|
|
||||||
n = c->next;
|
|
||||||
generic_make_request(c->rw, c->bh);
|
|
||||||
free_deferred(c);
|
|
||||||
c = n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Swap in a new table (destroying old one).
|
|
||||||
*/
|
|
||||||
int dm_swap_table(struct mapped_device *md, struct dm_table *table)
|
|
||||||
{
|
|
||||||
int r;
|
|
||||||
|
|
||||||
wl;
|
|
||||||
|
|
||||||
/* device must be suspended */
|
|
||||||
if (!md->suspended) {
|
|
||||||
wu;
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
__unbind(md);
|
|
||||||
|
|
||||||
if ((r = __bind(md, table))) {
|
|
||||||
wu;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
wu;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to be able to change a mapping table
|
|
||||||
* under a mounted filesystem. for example we
|
|
||||||
* might want to move some data in the background.
|
|
||||||
* Before the table can be swapped with
|
|
||||||
* dm_bind_table, dm_suspend must be called to
|
|
||||||
* flush any in flight buffer_heads and ensure
|
|
||||||
* that any further io gets deferred.
|
|
||||||
*/
|
|
||||||
int dm_suspend(struct mapped_device *md)
|
|
||||||
{
|
|
||||||
DECLARE_WAITQUEUE(wait, current);
|
|
||||||
|
|
||||||
wl;
|
|
||||||
if (md->suspended) {
|
|
||||||
wu;
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
md->suspended = 1;
|
|
||||||
wu;
|
|
||||||
|
|
||||||
/* wait for all the pending io to flush */
|
|
||||||
add_wait_queue(&md->wait, &wait);
|
|
||||||
current->state = TASK_UNINTERRUPTIBLE;
|
|
||||||
do {
|
|
||||||
wl;
|
|
||||||
if (!atomic_read(&md->pending))
|
|
||||||
break;
|
|
||||||
|
|
||||||
wu;
|
|
||||||
schedule();
|
|
||||||
|
|
||||||
} while (1);
|
|
||||||
|
|
||||||
current->state = TASK_RUNNING;
|
|
||||||
remove_wait_queue(&md->wait, &wait);
|
|
||||||
wu;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_resume(struct mapped_device *md)
|
|
||||||
{
|
|
||||||
struct deferred_io *def;
|
|
||||||
|
|
||||||
wl;
|
|
||||||
if (!md->suspended) {
|
|
||||||
wu;
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
md->suspended = 0;
|
|
||||||
def = md->deferred;
|
|
||||||
md->deferred = NULL;
|
|
||||||
wu;
|
|
||||||
|
|
||||||
flush_deferred_io(def);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Search for a device with a particular name.
|
|
||||||
*/
|
|
||||||
struct mapped_device *dm_get(const char *name)
|
|
||||||
{
|
|
||||||
struct mapped_device *md;
|
|
||||||
|
|
||||||
rl;
|
|
||||||
md = __get_by_name(name);
|
|
||||||
ru;
|
|
||||||
|
|
||||||
return md;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct block_device_operations dm_blk_dops = {
|
|
||||||
open: dm_blk_open,
|
|
||||||
release: dm_blk_close,
|
|
||||||
ioctl: dm_blk_ioctl,
|
|
||||||
owner: THIS_MODULE,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* module hooks
|
|
||||||
*/
|
|
||||||
module_init(dm_init);
|
|
||||||
module_exit(dm_exit);
|
|
||||||
|
|
||||||
MODULE_PARM(major, "i");
|
|
||||||
MODULE_PARM_DESC(major, "The major number of the device mapper");
|
|
||||||
MODULE_DESCRIPTION("device-mapper driver");
|
|
||||||
MODULE_AUTHOR("Joe Thornber <thornber@sistina.com>");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
|
|
@ -1,243 +0,0 @@
|
|||||||
/*
|
|
||||||
* dm.h
|
|
||||||
*
|
|
||||||
* Copyright (C) 2001 Sistina Software
|
|
||||||
*
|
|
||||||
* This file is released under the GPL.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Internal header file for device mapper
|
|
||||||
*
|
|
||||||
* Changelog
|
|
||||||
*
|
|
||||||
* 16/08/2001 - First version [Joe Thornber]
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This driver attempts to provide a generic way of specifying logical
|
|
||||||
* devices which are mapped onto other devices.
|
|
||||||
*
|
|
||||||
* It does this by mapping sections of 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.
|
|
||||||
*
|
|
||||||
* (dm-table.c) A btree like structure is used to hold the sector
|
|
||||||
* range -> target mapping. Because 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). Each
|
|
||||||
* node of the btree is 1 level cache line in size, this gives a small
|
|
||||||
* performance boost.
|
|
||||||
*
|
|
||||||
* A userland test program for the btree gave the following results on a
|
|
||||||
* 1 Gigahertz Athlon machine:
|
|
||||||
*
|
|
||||||
* entries in btree lookups per second
|
|
||||||
* ---------------- ------------------
|
|
||||||
* 5 25,000,000
|
|
||||||
* 1000 7,700,000
|
|
||||||
* 10,000,000 3,800,000
|
|
||||||
*
|
|
||||||
* Of course these results should be taken with a pinch of salt; the
|
|
||||||
* lookups were sequential and there were no other applications (other
|
|
||||||
* than X + emacs) running to give any pressure on the level 1 cache.
|
|
||||||
*
|
|
||||||
* Typical LVM users would find they have very few targets for each
|
|
||||||
* LV (probably less than 10).
|
|
||||||
*
|
|
||||||
* (dm-target.c) 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 LVM features (omitting striped volumes
|
|
||||||
* and snapshots).
|
|
||||||
*
|
|
||||||
* (dm-fs.c) The driver is 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>
|
|
||||||
*
|
|
||||||
* /proc/device-mapper/<device name> accepts the mapping table:
|
|
||||||
*
|
|
||||||
* begin
|
|
||||||
* <sector start> <length> <target name> <target args>...
|
|
||||||
* ...
|
|
||||||
* end
|
|
||||||
*
|
|
||||||
* The begin/end lines are nasty, they should be handled by open/close
|
|
||||||
* for the file.
|
|
||||||
*
|
|
||||||
* 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 (reflector ?); 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. Great for
|
|
||||||
* implementing pvmove. Not sure how userland would be notified that
|
|
||||||
* the copying process had completed. Possibly by reading a /proc entry
|
|
||||||
* for the LV. Could also use poll() for this kind of thing.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
#ifndef DM_INTERNAL_H
|
|
||||||
#define DM_INTERNAL_H
|
|
||||||
|
|
||||||
#include <linux/version.h>
|
|
||||||
#include <linux/major.h>
|
|
||||||
#include <linux/iobuf.h>
|
|
||||||
#include <linux/module.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/vmalloc.h>
|
|
||||||
#include <linux/compatmac.h>
|
|
||||||
#include <linux/cache.h>
|
|
||||||
#include <linux/devfs_fs_kernel.h>
|
|
||||||
#include <linux/ctype.h>
|
|
||||||
#include <linux/device-mapper.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
|
|
||||||
#define MAX_DEPTH 16
|
|
||||||
#define NODE_SIZE L1_CACHE_BYTES
|
|
||||||
#define KEYS_PER_NODE (NODE_SIZE / sizeof(offset_t))
|
|
||||||
#define CHILDREN_PER_NODE (KEYS_PER_NODE + 1)
|
|
||||||
#define DM_NAME_LEN 128
|
|
||||||
|
|
||||||
/*
|
|
||||||
* list of devices that a metadevice uses
|
|
||||||
* and hence should open/close.
|
|
||||||
*/
|
|
||||||
struct dm_dev {
|
|
||||||
atomic_t count;
|
|
||||||
struct list_head list;
|
|
||||||
|
|
||||||
kdev_t dev;
|
|
||||||
struct block_device *bd;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* io that had to be deferred while we were
|
|
||||||
* suspended
|
|
||||||
*/
|
|
||||||
struct deferred_io {
|
|
||||||
int rw;
|
|
||||||
struct buffer_head *bh;
|
|
||||||
struct deferred_io *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* btree leaf, these do the actual mapping
|
|
||||||
*/
|
|
||||||
struct target {
|
|
||||||
struct target_type *type;
|
|
||||||
void *private;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* the btree
|
|
||||||
*/
|
|
||||||
struct dm_table {
|
|
||||||
/* btree table */
|
|
||||||
int depth;
|
|
||||||
int counts[MAX_DEPTH]; /* in nodes */
|
|
||||||
offset_t *index[MAX_DEPTH];
|
|
||||||
|
|
||||||
int num_targets;
|
|
||||||
int num_allocated;
|
|
||||||
offset_t *highs;
|
|
||||||
struct target *targets;
|
|
||||||
|
|
||||||
/* a list of devices used by this table */
|
|
||||||
struct list_head devices;
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* the actual device struct
|
|
||||||
*/
|
|
||||||
struct mapped_device {
|
|
||||||
kdev_t dev;
|
|
||||||
char name[DM_NAME_LEN];
|
|
||||||
|
|
||||||
int use_count;
|
|
||||||
int suspended;
|
|
||||||
|
|
||||||
/* a list of io's that arrived while we were suspended */
|
|
||||||
atomic_t pending;
|
|
||||||
wait_queue_head_t wait;
|
|
||||||
struct deferred_io *deferred;
|
|
||||||
|
|
||||||
struct dm_table *map;
|
|
||||||
|
|
||||||
/* used by dm-fs.c */
|
|
||||||
devfs_handle_t devfs_entry;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern struct block_device_operations dm_blk_dops;
|
|
||||||
|
|
||||||
|
|
||||||
/* dm-target.c */
|
|
||||||
int dm_target_init(void);
|
|
||||||
struct target_type *dm_get_target_type(const char *name);
|
|
||||||
void dm_put_target_type(struct target_type *t);
|
|
||||||
|
|
||||||
/* dm.c */
|
|
||||||
struct mapped_device *dm_find_by_minor(int minor);
|
|
||||||
struct mapped_device *dm_get(const char *name);
|
|
||||||
struct mapped_device *dm_create(const char *name, int minor, struct dm_table *);int dm_destroy(struct mapped_device *md);
|
|
||||||
int dm_swap_table(struct mapped_device *md, struct dm_table *t);
|
|
||||||
int dm_suspend(struct mapped_device *md);
|
|
||||||
int dm_resume(struct mapped_device *md);
|
|
||||||
|
|
||||||
/* dm-table.c */
|
|
||||||
struct dm_table *dm_table_create(void);
|
|
||||||
void dm_table_destroy(struct dm_table *t);
|
|
||||||
|
|
||||||
int dm_table_add_target(struct dm_table *t, offset_t high,
|
|
||||||
struct target_type *type, void *private);
|
|
||||||
int dm_table_complete(struct dm_table *t);
|
|
||||||
|
|
||||||
#define WARN(f, x...) printk(KERN_WARNING "device-mapper: " f "\n" , ## x)
|
|
||||||
|
|
||||||
/*
|
|
||||||
* calculate the index of the child node of the
|
|
||||||
* n'th node k'th key.
|
|
||||||
*/
|
|
||||||
static inline int get_child(int n, int k)
|
|
||||||
{
|
|
||||||
return (n * CHILDREN_PER_NODE) + k;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* returns the n'th node of level l from table t.
|
|
||||||
*/
|
|
||||||
static inline offset_t *get_node(struct dm_table *t, int l, int n)
|
|
||||||
{
|
|
||||||
return t->index[l] + (n * KEYS_PER_NODE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int dm_interface_init(void) __init;
|
|
||||||
void dm_interface_exit(void) __exit;
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,122 +0,0 @@
|
|||||||
/*
|
|
||||||
* dmfs-error.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2001 Sistina Software
|
|
||||||
*
|
|
||||||
* This software 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.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with GNU CC; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/config.h>
|
|
||||||
#include <linux/list.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
|
|
||||||
#include "dm.h"
|
|
||||||
#include "dmfs.h"
|
|
||||||
|
|
||||||
struct dmfs_error {
|
|
||||||
struct list_head list;
|
|
||||||
unsigned len;
|
|
||||||
char *msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct dmfs_error oom_error;
|
|
||||||
|
|
||||||
static struct list_head oom_list = {
|
|
||||||
next: &oom_error.list,
|
|
||||||
prev: &oom_error.list,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct dmfs_error oom_error = {
|
|
||||||
list: { next: &oom_list, prev: &oom_list },
|
|
||||||
len: 39,
|
|
||||||
msg: "Out of memory during creation of table\n",
|
|
||||||
};
|
|
||||||
|
|
||||||
void dmfs_add_error(struct inode *inode, unsigned num, char *str)
|
|
||||||
{
|
|
||||||
struct dmfs_i *dmi = DMFS_I(inode);
|
|
||||||
int len = strlen(str) + sizeof(struct dmfs_error) + 12;
|
|
||||||
struct dmfs_error *e = kmalloc(len, GFP_KERNEL);
|
|
||||||
if (e) {
|
|
||||||
e->msg = (char *)(e + 1);
|
|
||||||
e->len = sprintf(e->msg, "%8u: %s\n", num, str);
|
|
||||||
list_add(&e->list, &dmi->errors);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void dmfs_zap_errors(struct inode *inode)
|
|
||||||
{
|
|
||||||
struct dmfs_i *dmi = DMFS_I(inode);
|
|
||||||
struct dmfs_error *e;
|
|
||||||
|
|
||||||
while(!list_empty(&dmi->errors)) {
|
|
||||||
e = list_entry(dmi->errors.next, struct dmfs_error, list);
|
|
||||||
list_del(&e->list);
|
|
||||||
kfree(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *e_start(struct seq_file *e, loff_t *pos)
|
|
||||||
{
|
|
||||||
struct list_head *p;
|
|
||||||
loff_t n = *pos;
|
|
||||||
struct dmfs_i *dmi = e->context;
|
|
||||||
|
|
||||||
down(&dmi->sem);
|
|
||||||
if (dmi->status) {
|
|
||||||
list_for_each(p, &oom_list)
|
|
||||||
if (n-- == 0)
|
|
||||||
return list_entry(p, struct dmfs_error, list);
|
|
||||||
} else {
|
|
||||||
list_for_each(p, &dmi->errors)
|
|
||||||
if (n-- == 0)
|
|
||||||
return list_entry(p, struct dmfs_error, list);
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *e_next(struct seq_file *e, void *v, loff_t *pos)
|
|
||||||
{
|
|
||||||
struct dmfs_i *dmi = e->context;
|
|
||||||
struct list_head *p = ((struct dmfs_error *)v)->list.next;
|
|
||||||
(*pos)++;
|
|
||||||
return (p == &dmi->errors) || (p == &oom_list) ? NULL
|
|
||||||
: list_entry(p, struct dmfs_error, list);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void e_stop(struct seq_file *e, void *v)
|
|
||||||
{
|
|
||||||
struct dmfs_i *dmi = e->context;
|
|
||||||
up(&dmi->sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int show_error(struct seq_file *e, void *v)
|
|
||||||
{
|
|
||||||
struct dmfs_error *d = v;
|
|
||||||
seq_puts(e, d->msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct seq_operations dmfs_error_seq_ops = {
|
|
||||||
start: e_start,
|
|
||||||
next: e_next,
|
|
||||||
stop: e_stop,
|
|
||||||
show: show_error,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
@ -1,236 +0,0 @@
|
|||||||
/*
|
|
||||||
* dmfs-lv.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2001 Sistina Software
|
|
||||||
*
|
|
||||||
* This software 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.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with GNU CC; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Heavily based upon ramfs */
|
|
||||||
|
|
||||||
#include <linux/config.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
|
|
||||||
#include "dm.h"
|
|
||||||
#include "dmfs.h"
|
|
||||||
|
|
||||||
struct dmfs_inode_info {
|
|
||||||
const char *name;
|
|
||||||
struct inode *(*create)(struct inode *, int, struct seq_operations *, int);
|
|
||||||
struct seq_operations *seq_ops;
|
|
||||||
int type;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DMFS_SEQ(inode) ((struct seq_operations *)(inode)->u.generic_ip)
|
|
||||||
|
|
||||||
extern struct inode *dmfs_create_table(struct inode *, int, struct seq_operations *, int);
|
|
||||||
extern struct seq_operations dmfs_error_seq_ops;
|
|
||||||
extern struct seq_operations dmfs_status_seq_ops;
|
|
||||||
extern struct seq_operations dmfs_suspend_seq_ops;
|
|
||||||
extern ssize_t dmfs_suspend_write(struct file *file, const char *buf, size_t size, loff_t *ppos);
|
|
||||||
|
|
||||||
static int dmfs_seq_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
int ret = seq_open(file, DMFS_SEQ(inode));
|
|
||||||
if (ret >= 0) {
|
|
||||||
struct seq_file *seq = file->private_data;
|
|
||||||
seq->context = DMFS_I(file->f_dentry->d_parent->d_inode);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_no_fsync(struct file *file, struct dentry *dentry, int datasync)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct file_operations dmfs_suspend_file_operations = {
|
|
||||||
open: dmfs_seq_open,
|
|
||||||
read: seq_read,
|
|
||||||
llseek: seq_lseek,
|
|
||||||
release: seq_release,
|
|
||||||
write: dmfs_suspend_write,
|
|
||||||
fsync: dmfs_no_fsync,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct inode_operations dmfs_null_inode_operations = {
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct file_operations dmfs_seq_ro_file_operations = {
|
|
||||||
open: dmfs_seq_open,
|
|
||||||
read: seq_read,
|
|
||||||
llseek: seq_lseek,
|
|
||||||
release: seq_release,
|
|
||||||
fsync: dmfs_no_fsync,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct inode *dmfs_create_seq_ro(struct inode *dir, int mode, struct seq_operations *seq_ops, int dev)
|
|
||||||
{
|
|
||||||
struct inode *inode = dmfs_new_inode(dir->i_sb, mode | S_IFREG);
|
|
||||||
if (inode) {
|
|
||||||
inode->i_fop = &dmfs_seq_ro_file_operations;
|
|
||||||
inode->i_op = &dmfs_null_inode_operations;
|
|
||||||
DMFS_SEQ(inode) = seq_ops;
|
|
||||||
}
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct inode *dmfs_create_device(struct inode *dir, int mode, struct seq_operations *seq_ops, int dev)
|
|
||||||
{
|
|
||||||
struct inode *inode = dmfs_new_inode(dir->i_sb, mode | S_IFBLK);
|
|
||||||
if (inode) {
|
|
||||||
init_special_inode(inode, mode | S_IFBLK, dev);
|
|
||||||
}
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct inode *dmfs_create_suspend(struct inode *dir, int mode, struct seq_operations *seq_ops, int dev)
|
|
||||||
{
|
|
||||||
struct inode *inode = dmfs_create_seq_ro(dir, mode, seq_ops, dev);
|
|
||||||
if (inode) {
|
|
||||||
inode->i_fop = &dmfs_suspend_file_operations;
|
|
||||||
}
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_lv_unlink(struct inode *dir, struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct inode *inode = dentry->d_inode;
|
|
||||||
|
|
||||||
inode->i_mapping = &inode->i_data;
|
|
||||||
inode->i_nlink--;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct dmfs_inode_info dmfs_ii[] = {
|
|
||||||
{ ".", NULL, NULL, DT_DIR },
|
|
||||||
{ "..", NULL, NULL, DT_DIR },
|
|
||||||
{ "table", dmfs_create_table, NULL, DT_REG },
|
|
||||||
{ "error", dmfs_create_seq_ro, &dmfs_error_seq_ops, DT_REG },
|
|
||||||
{ "status", dmfs_create_seq_ro, &dmfs_status_seq_ops, DT_REG },
|
|
||||||
{ "device", dmfs_create_device, NULL, DT_BLK },
|
|
||||||
{ "suspend", dmfs_create_suspend, &dmfs_suspend_seq_ops, DT_REG },
|
|
||||||
};
|
|
||||||
|
|
||||||
#define NR_DMFS_II (sizeof(dmfs_ii)/sizeof(struct dmfs_inode_info))
|
|
||||||
|
|
||||||
static struct dmfs_inode_info *dmfs_find_by_name(const char *n, int len)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for(i = 2; i < NR_DMFS_II; i++) {
|
|
||||||
if (strlen(dmfs_ii[i].name) != len)
|
|
||||||
continue;
|
|
||||||
if (memcmp(dmfs_ii[i].name, n, len) == 0)
|
|
||||||
return &dmfs_ii[i];
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct dentry *dmfs_lv_lookup(struct inode *dir, struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct inode *inode = NULL;
|
|
||||||
struct dmfs_inode_info *ii;
|
|
||||||
|
|
||||||
ii = dmfs_find_by_name(dentry->d_name.name, dentry->d_name.len);
|
|
||||||
if (ii) {
|
|
||||||
int dev = kdev_t_to_nr(DMFS_I(dir)->md->dev);
|
|
||||||
inode = ii->create(dir, 0600, ii->seq_ops, dev);
|
|
||||||
}
|
|
||||||
|
|
||||||
d_add(dentry, inode);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_inum(int entry, struct dentry *dentry)
|
|
||||||
{
|
|
||||||
if (entry == 0)
|
|
||||||
return dentry->d_inode->i_ino;
|
|
||||||
if (entry == 1)
|
|
||||||
return dentry->d_parent->d_inode->i_ino;
|
|
||||||
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_lv_readdir(struct file *filp, void *dirent, filldir_t filldir)
|
|
||||||
{
|
|
||||||
struct dentry *dentry = filp->f_dentry;
|
|
||||||
struct dmfs_inode_info *ii;
|
|
||||||
|
|
||||||
while (filp->f_pos < NR_DMFS_II) {
|
|
||||||
ii = &dmfs_ii[filp->f_pos];
|
|
||||||
if (filldir(dirent, ii->name, strlen(ii->name), filp->f_pos,
|
|
||||||
dmfs_inum(filp->f_pos, dentry), ii->type) < 0)
|
|
||||||
break;
|
|
||||||
filp->f_pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int dmfs_lv_sync(struct file *file, struct dentry *dentry, int datasync)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct file_operations dmfs_lv_file_operations = {
|
|
||||||
read: generic_read_dir,
|
|
||||||
readdir: dmfs_lv_readdir,
|
|
||||||
fsync: dmfs_lv_sync,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct inode_operations dmfs_lv_inode_operations = {
|
|
||||||
lookup: dmfs_lv_lookup,
|
|
||||||
unlink: dmfs_lv_unlink,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct inode *dmfs_create_lv(struct super_block *sb, int mode, struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct inode *inode = dmfs_new_private_inode(sb, mode | S_IFDIR);
|
|
||||||
struct mapped_device *md;
|
|
||||||
const char *name = dentry->d_name.name;
|
|
||||||
char tmp_name[DM_NAME_LEN + 1];
|
|
||||||
struct dm_table *table;
|
|
||||||
int ret = -ENOMEM;
|
|
||||||
|
|
||||||
if (inode) {
|
|
||||||
table = dm_table_create();
|
|
||||||
ret = PTR_ERR(table);
|
|
||||||
if (!IS_ERR(table)) {
|
|
||||||
ret = dm_table_complete(table);
|
|
||||||
if (ret == 0) {
|
|
||||||
inode->i_fop = &dmfs_lv_file_operations;
|
|
||||||
inode->i_op = &dmfs_lv_inode_operations;
|
|
||||||
memcpy(tmp_name, name, dentry->d_name.len);
|
|
||||||
tmp_name[dentry->d_name.len] = 0;
|
|
||||||
md = dm_create(tmp_name, -1, table);
|
|
||||||
if (!IS_ERR(md)) {
|
|
||||||
DMFS_I(inode)->md = md;
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
ret = PTR_ERR(md);
|
|
||||||
}
|
|
||||||
dm_table_destroy(table);
|
|
||||||
}
|
|
||||||
iput(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ERR_PTR(ret);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,159 +0,0 @@
|
|||||||
/*
|
|
||||||
* dmfs-root.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2001 Sistina Software
|
|
||||||
*
|
|
||||||
* This software 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.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with GNU CC; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Heavily based upon ramfs */
|
|
||||||
|
|
||||||
#include <linux/config.h>
|
|
||||||
#include <linux/ctype.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
|
|
||||||
#include "dm.h"
|
|
||||||
#include "dmfs.h"
|
|
||||||
|
|
||||||
extern struct inode *dmfs_create_lv(struct super_block *sb, int mode, struct dentry *dentry);
|
|
||||||
|
|
||||||
static int is_identifier(const char *str, int len)
|
|
||||||
{
|
|
||||||
while(len--) {
|
|
||||||
if (!isalnum(*str) && *str != '_')
|
|
||||||
return 0;
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_root_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|
||||||
{
|
|
||||||
struct inode *inode;
|
|
||||||
int rv = -ENOSPC;
|
|
||||||
|
|
||||||
if (dentry->d_name.len >= DM_NAME_LEN)
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
if (!is_identifier(dentry->d_name.name, dentry->d_name.len))
|
|
||||||
return -EPERM;
|
|
||||||
|
|
||||||
if (dentry->d_name.name[0] == '.')
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
inode = dmfs_create_lv(dir->i_sb, mode, dentry);
|
|
||||||
if (!IS_ERR(inode)) {
|
|
||||||
d_instantiate(dentry, inode);
|
|
||||||
dget(dentry);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return PTR_ERR(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* if u.generic_ip is not NULL, then it indicates an inode which
|
|
||||||
* represents a table. If it is NULL then the inode is a virtual
|
|
||||||
* file and should be deleted along with the directory.
|
|
||||||
*/
|
|
||||||
static inline int positive(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
return dentry->d_inode && !d_unhashed(dentry);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int empty(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct list_head *list;
|
|
||||||
|
|
||||||
spin_lock(&dcache_lock);
|
|
||||||
list = dentry->d_subdirs.next;
|
|
||||||
|
|
||||||
while(list != &dentry->d_subdirs) {
|
|
||||||
struct dentry *de = list_entry(list, struct dentry, d_child);
|
|
||||||
|
|
||||||
if (positive(de)) {
|
|
||||||
spin_unlock(&dcache_lock);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
list = list->next;
|
|
||||||
}
|
|
||||||
spin_unlock(&dcache_lock);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_root_rmdir(struct inode *dir, struct dentry *dentry)
|
|
||||||
{
|
|
||||||
int ret = -ENOTEMPTY;
|
|
||||||
|
|
||||||
if (empty(dentry)) {
|
|
||||||
struct inode *inode = dentry->d_inode;
|
|
||||||
ret = dm_destroy(DMFS_I(inode)->md);
|
|
||||||
if (ret == 0) {
|
|
||||||
DMFS_I(inode)->md = NULL;
|
|
||||||
inode->i_nlink--;
|
|
||||||
dput(dentry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct dentry *dmfs_root_lookup(struct inode *dir, struct dentry *dentry)
|
|
||||||
{
|
|
||||||
d_add(dentry, NULL);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_root_rename(struct inode *old_dir, struct dentry *old_dentry,
|
|
||||||
struct inode *new_dir, struct dentry *new_dentry)
|
|
||||||
{
|
|
||||||
/* Can only rename - not move between directories! */
|
|
||||||
if (old_dir != new_dir)
|
|
||||||
return -EPERM;
|
|
||||||
|
|
||||||
return -EINVAL; /* FIXME: a change of LV name here */
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_root_sync(struct file *file, struct dentry *dentry, int datasync)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct file_operations dmfs_root_file_operations = {
|
|
||||||
read: generic_read_dir,
|
|
||||||
readdir: dcache_readdir,
|
|
||||||
fsync: dmfs_root_sync,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct inode_operations dmfs_root_inode_operations = {
|
|
||||||
lookup: dmfs_root_lookup,
|
|
||||||
mkdir: dmfs_root_mkdir,
|
|
||||||
rmdir: dmfs_root_rmdir,
|
|
||||||
rename: dmfs_root_rename,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct inode *dmfs_create_root(struct super_block *sb, int mode)
|
|
||||||
{
|
|
||||||
struct inode *inode = dmfs_new_inode(sb, mode | S_IFDIR);
|
|
||||||
|
|
||||||
if (inode) {
|
|
||||||
inode->i_fop = &dmfs_root_file_operations;
|
|
||||||
inode->i_op = &dmfs_root_inode_operations;
|
|
||||||
}
|
|
||||||
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* dmfs-status.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2001 Sistina Software
|
|
||||||
*
|
|
||||||
* This software 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.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with GNU CC; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/config.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
|
|
||||||
#include "dm.h"
|
|
||||||
#include "dmfs.h"
|
|
||||||
|
|
||||||
static void *s_start(struct seq_file *s, loff_t *pos)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *s_next(struct seq_file *s, void *v, loff_t *pos)
|
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void s_stop(struct seq_file *s, void *v)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static int s_show(struct seq_file *s, void *v)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct seq_operations dmfs_status_seq_ops = {
|
|
||||||
start: s_start,
|
|
||||||
next: s_next,
|
|
||||||
stop: s_stop,
|
|
||||||
show: s_show,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
@ -1,160 +0,0 @@
|
|||||||
/*
|
|
||||||
* dmfs-super.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2001 Sistina Software
|
|
||||||
*
|
|
||||||
* This software 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.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with GNU CC; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/config.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/kmod.h>
|
|
||||||
|
|
||||||
#include "dmfs.h"
|
|
||||||
#include "dm.h"
|
|
||||||
|
|
||||||
#define DMFS_MAGIC 0x444D4653
|
|
||||||
|
|
||||||
extern struct inode *dmfs_create_root(struct super_block *sb, int);
|
|
||||||
|
|
||||||
static int dmfs_statfs(struct super_block *sb, struct statfs *buf)
|
|
||||||
{
|
|
||||||
buf->f_type = sb->s_magic;
|
|
||||||
buf->f_bsize = sb->s_blocksize;
|
|
||||||
buf->f_namelen = DM_NAME_LEN - 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dmfs_delete_inode(struct inode *inode)
|
|
||||||
{
|
|
||||||
if (S_ISDIR(inode->i_mode)) {
|
|
||||||
struct dmfs_i *dmi = DMFS_I(inode);
|
|
||||||
|
|
||||||
if (dmi) {
|
|
||||||
if (dmi->md)
|
|
||||||
BUG();
|
|
||||||
if (!list_empty(&dmi->errors))
|
|
||||||
dmfs_zap_errors(inode);
|
|
||||||
kfree(dmi);
|
|
||||||
MOD_DEC_USE_COUNT; /* Don't remove */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inode->u.generic_ip = NULL;
|
|
||||||
clear_inode(inode);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct super_operations dmfs_super_operations = {
|
|
||||||
statfs: dmfs_statfs,
|
|
||||||
put_inode: force_delete,
|
|
||||||
delete_inode: dmfs_delete_inode,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct super_block *dmfs_read_super(struct super_block *sb, void *data, int silent)
|
|
||||||
{
|
|
||||||
struct inode *inode;
|
|
||||||
struct dentry *root;
|
|
||||||
|
|
||||||
sb->s_blocksize = PAGE_CACHE_SIZE;
|
|
||||||
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
|
|
||||||
sb->s_magic = DMFS_MAGIC;
|
|
||||||
sb->s_op = &dmfs_super_operations;
|
|
||||||
sb->s_maxbytes = MAX_NON_LFS;
|
|
||||||
|
|
||||||
inode = dmfs_create_root(sb, 0755);
|
|
||||||
if (IS_ERR(inode))
|
|
||||||
return NULL;
|
|
||||||
root = d_alloc_root(inode);
|
|
||||||
if (!root) {
|
|
||||||
iput(inode);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
sb->s_root = root;
|
|
||||||
|
|
||||||
return sb;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct inode *dmfs_new_inode(struct super_block *sb, int mode)
|
|
||||||
{
|
|
||||||
struct inode *inode = new_inode(sb);
|
|
||||||
|
|
||||||
if (inode) {
|
|
||||||
inode->i_mode = mode;
|
|
||||||
inode->i_uid = current->fsuid;
|
|
||||||
inode->i_gid = current->fsgid;
|
|
||||||
inode->i_blksize = PAGE_CACHE_SIZE;
|
|
||||||
inode->i_blocks = 0;
|
|
||||||
inode->i_rdev = NODEV;
|
|
||||||
inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
|
|
||||||
}
|
|
||||||
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct inode *dmfs_new_private_inode(struct super_block *sb, int mode)
|
|
||||||
{
|
|
||||||
struct inode *inode = dmfs_new_inode(sb, mode);
|
|
||||||
struct dmfs_i *dmi;
|
|
||||||
|
|
||||||
if (inode) {
|
|
||||||
dmi = kmalloc(sizeof(struct dmfs_i), GFP_KERNEL);
|
|
||||||
if (dmi == NULL) {
|
|
||||||
iput(inode);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
memset(dmi, 0, sizeof(struct dmfs_i));
|
|
||||||
init_MUTEX(&dmi->sem);
|
|
||||||
INIT_LIST_HEAD(&dmi->errors);
|
|
||||||
inode->u.generic_ip = dmi;
|
|
||||||
MOD_INC_USE_COUNT; /* Don't remove */
|
|
||||||
}
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
static DECLARE_FSTYPE(dmfs_fstype, "dmfs", dmfs_read_super, FS_SINGLE);
|
|
||||||
static struct vfsmount *dmfs_mnt;
|
|
||||||
|
|
||||||
int __init dm_interface_init(void)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = register_filesystem(&dmfs_fstype);
|
|
||||||
if (ret < 0)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
dmfs_mnt = kern_mount(&dmfs_fstype);
|
|
||||||
if (IS_ERR(dmfs_mnt)) {
|
|
||||||
ret = PTR_ERR(dmfs_mnt);
|
|
||||||
unregister_filesystem(&dmfs_fstype);
|
|
||||||
} else {
|
|
||||||
MOD_DEC_USE_COUNT; /* Yes, this really is correct... */
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void __exit dm_interface_exit(void)
|
|
||||||
{
|
|
||||||
MOD_INC_USE_COUNT; /* So that it lands up being zero */
|
|
||||||
|
|
||||||
do_umount(dmfs_mnt, 0);
|
|
||||||
|
|
||||||
unregister_filesystem(&dmfs_fstype);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,95 +0,0 @@
|
|||||||
/*
|
|
||||||
* dmfs-suspend.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2001 Sistina Software
|
|
||||||
*
|
|
||||||
* This software 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.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with GNU CC; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/config.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/seq_file.h>
|
|
||||||
|
|
||||||
#include "dm.h"
|
|
||||||
#include "dmfs.h"
|
|
||||||
|
|
||||||
|
|
||||||
static void *s_start(struct seq_file *s, loff_t *pos)
|
|
||||||
{
|
|
||||||
struct dmfs_i *dmi = s->context;
|
|
||||||
if (*pos > 0)
|
|
||||||
return NULL;
|
|
||||||
down(&dmi->sem);
|
|
||||||
return (void *)1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *s_next(struct seq_file *s, void *v, loff_t *pos)
|
|
||||||
{
|
|
||||||
(*pos)++;
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void s_stop(struct seq_file *s, void *v)
|
|
||||||
{
|
|
||||||
struct dmfs_i *dmi = s->context;
|
|
||||||
up(&dmi->sem);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int s_show(struct seq_file *s, void *v)
|
|
||||||
{
|
|
||||||
struct dmfs_i *dmi = s->context;
|
|
||||||
char msg[3] = "1\n";
|
|
||||||
if (dmi->md->suspended == 0) {
|
|
||||||
msg[0] = '0';
|
|
||||||
}
|
|
||||||
seq_puts(s, msg);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct seq_operations dmfs_suspend_seq_ops = {
|
|
||||||
start: s_start,
|
|
||||||
next: s_next,
|
|
||||||
stop: s_stop,
|
|
||||||
show: s_show,
|
|
||||||
};
|
|
||||||
|
|
||||||
ssize_t dmfs_suspend_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
|
|
||||||
{
|
|
||||||
struct inode *dir = file->f_dentry->d_parent->d_inode;
|
|
||||||
struct dmfs_i *dmi = DMFS_I(dir);
|
|
||||||
int written = 0;
|
|
||||||
|
|
||||||
if (count == 0)
|
|
||||||
goto out;
|
|
||||||
if (count != 1 && count != 2)
|
|
||||||
return -EINVAL;
|
|
||||||
if (buf[0] != '0' && buf[0] != '1')
|
|
||||||
return -EINVAL;
|
|
||||||
|
|
||||||
down(&dmi->sem);
|
|
||||||
if (buf[0] == '0')
|
|
||||||
written = dm_resume(dmi->md);
|
|
||||||
if (buf[0] == '1')
|
|
||||||
written = dm_suspend(dmi->md);
|
|
||||||
if (written >= 0)
|
|
||||||
written = count;
|
|
||||||
up(&dmi->sem);
|
|
||||||
|
|
||||||
out:
|
|
||||||
return written;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,367 +0,0 @@
|
|||||||
/*
|
|
||||||
* dmfs-table.c
|
|
||||||
*
|
|
||||||
* Copyright (C) 2001 Sistina Software
|
|
||||||
*
|
|
||||||
* This software 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.
|
|
||||||
*
|
|
||||||
* This software is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with GNU CC; see the file COPYING. If not, write to
|
|
||||||
* the Free Software Foundation, 59 Temple Place - Suite 330,
|
|
||||||
* Boston, MA 02111-1307, USA.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <linux/config.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/mm.h>
|
|
||||||
|
|
||||||
#include "dm.h"
|
|
||||||
#include "dmfs.h"
|
|
||||||
|
|
||||||
static offset_t start_of_next_range(struct dm_table *t)
|
|
||||||
{
|
|
||||||
offset_t n = 0;
|
|
||||||
if (t->num_targets) {
|
|
||||||
n = t->highs[t->num_targets - 1] + 1;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *dmfs_parse_line(struct dm_table *t, char *str)
|
|
||||||
{
|
|
||||||
offset_t start, size, high;
|
|
||||||
void *context;
|
|
||||||
struct target_type *ttype;
|
|
||||||
int rv = 0;
|
|
||||||
char *msg;
|
|
||||||
int pos = 0;
|
|
||||||
char target[33];
|
|
||||||
|
|
||||||
static char *err_table[] = {
|
|
||||||
"Missing/Invalid start argument",
|
|
||||||
"Missing/Invalid size argument",
|
|
||||||
"Missing target type"
|
|
||||||
};
|
|
||||||
/* printk("dmfs_parse_line: (%s)\n", str); */
|
|
||||||
|
|
||||||
rv = sscanf(str, "%d %d %32s%n", &start, &size, target, &pos);
|
|
||||||
if (rv < 3) {
|
|
||||||
msg = err_table[rv];
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
str += pos;
|
|
||||||
while(*str && isspace(*str))
|
|
||||||
str++;
|
|
||||||
|
|
||||||
msg = "Gap in table";
|
|
||||||
if (start != start_of_next_range(t))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
msg = "Target type unknown";
|
|
||||||
ttype = dm_get_target_type(target);
|
|
||||||
if (ttype) {
|
|
||||||
msg = "This message should never appear (constructor error)";
|
|
||||||
rv = ttype->ctr(t, start, size, str, &context);
|
|
||||||
msg = context;
|
|
||||||
if (rv == 0) {
|
|
||||||
#if 0
|
|
||||||
printk("dmfs_parse: %u %u %s %s\n", start, size,
|
|
||||||
ttype->name,
|
|
||||||
ttype->print ? ttype->print(context) : "-");
|
|
||||||
#endif
|
|
||||||
msg = "Error adding target to table";
|
|
||||||
high = start + (size - 1);
|
|
||||||
if (dm_table_add_target(t, high, ttype, context) == 0)
|
|
||||||
return NULL;
|
|
||||||
ttype->dtr(t, context);
|
|
||||||
}
|
|
||||||
dm_put_target_type(ttype);
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
return msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static int dmfs_copy(char *dst, int dstlen, char *src, int srclen, int *flag)
|
|
||||||
{
|
|
||||||
int len = min(dstlen, srclen);
|
|
||||||
char *start = dst;
|
|
||||||
|
|
||||||
while(len) {
|
|
||||||
*dst = *src++;
|
|
||||||
if (*dst == '\n')
|
|
||||||
goto end_of_line;
|
|
||||||
dst++;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
out:
|
|
||||||
return (dst - start);
|
|
||||||
end_of_line:
|
|
||||||
dst++;
|
|
||||||
*flag = 1;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_line_is_not_comment(char *str)
|
|
||||||
{
|
|
||||||
while(*str) {
|
|
||||||
if (*str == '#')
|
|
||||||
break;
|
|
||||||
if (!isspace(*str))
|
|
||||||
return 1;
|
|
||||||
str++;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct dmfs_desc {
|
|
||||||
struct dm_table *table;
|
|
||||||
struct inode *inode;
|
|
||||||
char *tmp;
|
|
||||||
loff_t tmpl;
|
|
||||||
unsigned long lnum;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int dmfs_read_actor(read_descriptor_t *desc, struct page *page, unsigned long offset, unsigned long size)
|
|
||||||
{
|
|
||||||
char *buf, *msg;
|
|
||||||
unsigned long count = desc->count, len, copied;
|
|
||||||
struct dmfs_desc *d = (struct dmfs_desc *)desc->buf;
|
|
||||||
|
|
||||||
if (size > count)
|
|
||||||
size = count;
|
|
||||||
|
|
||||||
len = size;
|
|
||||||
buf = kmap(page);
|
|
||||||
do {
|
|
||||||
int flag = 0;
|
|
||||||
copied = dmfs_copy(d->tmp + d->tmpl, PAGE_SIZE - d->tmpl - 1,
|
|
||||||
buf + offset, len, &flag);
|
|
||||||
offset += copied;
|
|
||||||
len -= copied;
|
|
||||||
if (d->tmpl + copied == PAGE_SIZE - 1)
|
|
||||||
goto line_too_long;
|
|
||||||
d->tmpl += copied;
|
|
||||||
if (flag || (len == 0 && count == size)) {
|
|
||||||
*(d->tmp + d->tmpl) = 0;
|
|
||||||
if (dmfs_line_is_not_comment(d->tmp)) {
|
|
||||||
msg = dmfs_parse_line(d->table, d->tmp);
|
|
||||||
if (msg) {
|
|
||||||
dmfs_add_error(d->inode, d->lnum, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d->lnum++;
|
|
||||||
d->tmpl = 0;
|
|
||||||
}
|
|
||||||
} while(len > 0);
|
|
||||||
kunmap(page);
|
|
||||||
|
|
||||||
desc->count = count - size;
|
|
||||||
desc->written += size;
|
|
||||||
|
|
||||||
return size;
|
|
||||||
|
|
||||||
line_too_long:
|
|
||||||
printk(KERN_INFO "dmfs_read_actor: Line %lu too long\n", d->lnum);
|
|
||||||
kunmap(page);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct dm_table *dmfs_parse(struct inode *inode, struct file *filp)
|
|
||||||
{
|
|
||||||
struct dm_table *t = NULL;
|
|
||||||
unsigned long page;
|
|
||||||
struct dmfs_desc d;
|
|
||||||
loff_t pos = 0;
|
|
||||||
|
|
||||||
if (inode->i_size == 0)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
page = __get_free_page(GFP_NOFS);
|
|
||||||
if (page) {
|
|
||||||
t = dm_table_create();
|
|
||||||
if (t) {
|
|
||||||
read_descriptor_t desc;
|
|
||||||
|
|
||||||
desc.written = 0;
|
|
||||||
desc.count = inode->i_size;
|
|
||||||
desc.buf = (char *)&d;
|
|
||||||
d.table = t;
|
|
||||||
d.inode = inode;
|
|
||||||
d.tmp = (char *)page;
|
|
||||||
d.tmpl = 0;
|
|
||||||
d.lnum = 1;
|
|
||||||
|
|
||||||
do_generic_file_read(filp, &pos, &desc, dmfs_read_actor);
|
|
||||||
if (desc.written != inode->i_size) {
|
|
||||||
dm_table_destroy(t);
|
|
||||||
t = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
free_page(page);
|
|
||||||
}
|
|
||||||
if (!list_empty(&DMFS_I(inode)->errors)) {
|
|
||||||
dm_table_destroy(t);
|
|
||||||
t = NULL;
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_table_release(struct inode *inode, struct file *f)
|
|
||||||
{
|
|
||||||
struct dentry *dentry = f->f_dentry;
|
|
||||||
struct inode *parent = dentry->d_parent->d_inode;
|
|
||||||
struct dmfs_i *dmi = DMFS_I(parent);
|
|
||||||
struct dm_table *table;
|
|
||||||
|
|
||||||
if (f->f_mode & FMODE_WRITE) {
|
|
||||||
|
|
||||||
down(&dmi->sem);
|
|
||||||
dmfs_zap_errors(dentry->d_parent->d_inode);
|
|
||||||
table = dmfs_parse(dentry->d_parent->d_inode, f);
|
|
||||||
|
|
||||||
if (table) {
|
|
||||||
struct mapped_device *md = dmi->md;
|
|
||||||
int need_resume = 0;
|
|
||||||
|
|
||||||
if (md->suspended == 0) {
|
|
||||||
dm_suspend(md);
|
|
||||||
need_resume = 1;
|
|
||||||
}
|
|
||||||
dm_swap_table(md, table);
|
|
||||||
if (need_resume) {
|
|
||||||
dm_resume(md);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
up(&dmi->sem);
|
|
||||||
|
|
||||||
put_write_access(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_readpage(struct file *file, struct page *page)
|
|
||||||
{
|
|
||||||
if (!Page_Uptodate(page)) {
|
|
||||||
memset(kmap(page), 0, PAGE_CACHE_SIZE);
|
|
||||||
kunmap(page);
|
|
||||||
flush_dcache_page(page);
|
|
||||||
SetPageUptodate(page);
|
|
||||||
}
|
|
||||||
UnlockPage(page);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_prepare_write(struct file *file, struct page *page,
|
|
||||||
unsigned offset, unsigned to)
|
|
||||||
{
|
|
||||||
void *addr = kmap(page);
|
|
||||||
if (!Page_Uptodate(page)) {
|
|
||||||
memset(addr, 0, PAGE_CACHE_SIZE);
|
|
||||||
flush_dcache_page(page);
|
|
||||||
SetPageUptodate(page);
|
|
||||||
}
|
|
||||||
SetPageDirty(page);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_commit_write(struct file *file, struct page *page,
|
|
||||||
unsigned offset, unsigned to)
|
|
||||||
{
|
|
||||||
struct inode *inode = page->mapping->host;
|
|
||||||
loff_t pos = ((loff_t) page->index << PAGE_CACHE_SHIFT) + to;
|
|
||||||
|
|
||||||
kunmap(page);
|
|
||||||
if (pos > inode->i_size)
|
|
||||||
inode->i_size = pos;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* There is a small race here in that two processes might call this at
|
|
||||||
* the same time and both fail. So its a fail safe race :-) This should
|
|
||||||
* move into namei.c (and thus use the spinlock and do this properly)
|
|
||||||
* at some stage if we continue to use this set of functions for ensuring
|
|
||||||
* exclusive write access to the file
|
|
||||||
*/
|
|
||||||
static int get_exclusive_write_access(struct inode *inode)
|
|
||||||
{
|
|
||||||
if (get_write_access(inode))
|
|
||||||
return -1;
|
|
||||||
if (atomic_read(&inode->i_writecount) != 1) {
|
|
||||||
put_write_access(inode);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_table_open(struct inode *inode, struct file *file)
|
|
||||||
{
|
|
||||||
struct dentry *dentry = file->f_dentry;
|
|
||||||
struct inode *parent = dentry->d_parent->d_inode;
|
|
||||||
|
|
||||||
if (file->f_mode & FMODE_WRITE) {
|
|
||||||
if (get_exclusive_write_access(parent))
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_table_sync(struct file *file, struct dentry *dentry, int datasync)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dmfs_table_revalidate(struct dentry *dentry)
|
|
||||||
{
|
|
||||||
struct inode *inode = dentry->d_inode;
|
|
||||||
struct inode *parent = dentry->d_parent->d_inode;
|
|
||||||
|
|
||||||
inode->i_size = parent->i_size;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct address_space_operations dmfs_address_space_operations = {
|
|
||||||
readpage: dmfs_readpage,
|
|
||||||
writepage: fail_writepage,
|
|
||||||
prepare_write: dmfs_prepare_write,
|
|
||||||
commit_write: dmfs_commit_write,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct file_operations dmfs_table_file_operations = {
|
|
||||||
llseek: generic_file_llseek,
|
|
||||||
read: generic_file_read,
|
|
||||||
write: generic_file_write,
|
|
||||||
open: dmfs_table_open,
|
|
||||||
release: dmfs_table_release,
|
|
||||||
fsync: dmfs_table_sync,
|
|
||||||
};
|
|
||||||
|
|
||||||
static struct inode_operations dmfs_table_inode_operations = {
|
|
||||||
revalidate: dmfs_table_revalidate,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct inode *dmfs_create_table(struct inode *dir, int mode)
|
|
||||||
{
|
|
||||||
struct inode *inode = dmfs_new_inode(dir->i_sb, mode | S_IFREG);
|
|
||||||
|
|
||||||
if (inode) {
|
|
||||||
inode->i_mapping = dir->i_mapping;
|
|
||||||
inode->i_mapping->a_ops = &dmfs_address_space_operations;
|
|
||||||
inode->i_fop = &dmfs_table_file_operations;
|
|
||||||
inode->i_op = &dmfs_table_inode_operations;
|
|
||||||
}
|
|
||||||
|
|
||||||
return inode;
|
|
||||||
}
|
|
||||||
|
|
@ -1,22 +0,0 @@
|
|||||||
#ifndef LINUX_DMFS_H
|
|
||||||
#define LINUX_DMFS_H
|
|
||||||
|
|
||||||
struct dmfs_i {
|
|
||||||
struct semaphore sem;
|
|
||||||
struct mapped_device *md;
|
|
||||||
struct list_head errors;
|
|
||||||
int status;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define DMFS_I(inode) ((struct dmfs_i *)(inode)->u.generic_ip)
|
|
||||||
|
|
||||||
|
|
||||||
extern struct inode *dmfs_new_inode(struct super_block *sb, int mode);
|
|
||||||
extern struct inode *dmfs_new_private_inode(struct super_block *sb, int mode);
|
|
||||||
|
|
||||||
extern void dmfs_add_error(struct inode *inode, unsigned num, char *str);
|
|
||||||
extern void dmfs_zap_errors(struct inode *inode);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* LINUX_DMFS_H */
|
|
@ -1,11 +0,0 @@
|
|||||||
diff -ru linux-2.4.16/drivers/md/Config.in linux/drivers/md/Config.in
|
|
||||||
--- linux-2.4.16/drivers/md/Config.in Mon Sep 24 16:29:14 2001
|
|
||||||
+++ linux/drivers/md/Config.in Thu Nov 29 10:27:35 2001
|
|
||||||
@@ -14,5 +14,7 @@
|
|
||||||
dep_tristate ' Multipath I/O support' CONFIG_MD_MULTIPATH $CONFIG_BLK_DEV_MD
|
|
||||||
|
|
||||||
dep_tristate ' Logical volume manager (LVM) support' CONFIG_BLK_DEV_LVM $CONFIG_MD
|
|
||||||
+dep_tristate ' Device mapper support' CONFIG_BLK_DEV_DM $CONFIG_MD
|
|
||||||
+dep_tristate ' Device mapper linear target' CONFIG_BLK_DEV_DM_LINEAR $CONFIG_BLK_DEV_DM
|
|
||||||
|
|
||||||
endmenu
|
|
@ -1,13 +0,0 @@
|
|||||||
--- uml_build/arch/um/config.in.orig Tue Jan 2 14:33:42 2001
|
|
||||||
+++ uml_build/arch/um/config.in Tue Jan 2 14:35:42 2001
|
|
||||||
@@ -15,6 +15,8 @@
|
|
||||||
bool 'Prompt for development and/or incomplete code/drivers' CONFIG_EXPERIMENTAL
|
|
||||||
endmenu
|
|
||||||
|
|
||||||
+source drivers/md/Config.in
|
|
||||||
+
|
|
||||||
mainmenu_option next_comment
|
|
||||||
comment 'Processor features'
|
|
||||||
bool 'Symmetric multi-processing support' CONFIG_SMP
|
|
||||||
|
|
||||||
|
|
@ -1,24 +0,0 @@
|
|||||||
diff -ru linux-2.4.16/fs/namespace.c linux/fs/namespace.c
|
|
||||||
--- linux-2.4.16/fs/namespace.c Tue Nov 27 15:56:03 2001
|
|
||||||
+++ linux/fs/namespace.c Thu Nov 29 10:27:27 2001
|
|
||||||
@@ -332,7 +332,7 @@
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-static int do_umount(struct vfsmount *mnt, int flags)
|
|
||||||
+int do_umount(struct vfsmount *mnt, int flags)
|
|
||||||
{
|
|
||||||
struct super_block * sb = mnt->mnt_sb;
|
|
||||||
int retval = 0;
|
|
||||||
Only in linux/include/linux: device-mapper.h
|
|
||||||
diff -ru linux-2.4.16/include/linux/fs.h linux/include/linux/fs.h
|
|
||||||
--- linux-2.4.16/include/linux/fs.h Tue Nov 27 15:56:14 2001
|
|
||||||
+++ linux/include/linux/fs.h Thu Nov 29 10:33:35 2001
|
|
||||||
@@ -980,6 +980,7 @@
|
|
||||||
extern struct vfsmount *kern_mount(struct file_system_type *);
|
|
||||||
extern int may_umount(struct vfsmount *);
|
|
||||||
extern long do_mount(char *, char *, char *, unsigned long, void *);
|
|
||||||
+extern int do_umount(struct vfsmount *mnt, int flags);
|
|
||||||
|
|
||||||
#define kern_umount mntput
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
|||||||
diff -ru linux-2.4.16/kernel/ksyms.c linux/kernel/ksyms.c
|
|
||||||
--- linux-2.4.16/kernel/ksyms.c Tue Nov 27 15:56:08 2001
|
|
||||||
+++ linux/kernel/ksyms.c Thu Nov 29 10:51:49 2001
|
|
||||||
@@ -46,6 +46,7 @@
|
|
||||||
#include <linux/tty.h>
|
|
||||||
#include <linux/in6.h>
|
|
||||||
#include <linux/completion.h>
|
|
||||||
+#include <linux/seq_file.h>
|
|
||||||
#include <asm/checksum.h>
|
|
||||||
|
|
||||||
#if defined(CONFIG_PROC_FS)
|
|
||||||
@@ -323,6 +324,7 @@
|
|
||||||
EXPORT_SYMBOL(register_filesystem);
|
|
||||||
EXPORT_SYMBOL(unregister_filesystem);
|
|
||||||
EXPORT_SYMBOL(kern_mount);
|
|
||||||
+EXPORT_SYMBOL(do_umount);
|
|
||||||
EXPORT_SYMBOL(__mntput);
|
|
||||||
EXPORT_SYMBOL(may_umount);
|
|
||||||
|
|
||||||
@@ -348,6 +350,12 @@
|
|
||||||
EXPORT_SYMBOL(proc_doulongvec_ms_jiffies_minmax);
|
|
||||||
EXPORT_SYMBOL(proc_doulongvec_minmax);
|
|
||||||
|
|
||||||
+/* seq_file */
|
|
||||||
+EXPORT_SYMBOL(seq_read);
|
|
||||||
+EXPORT_SYMBOL(seq_lseek);
|
|
||||||
+EXPORT_SYMBOL(seq_open);
|
|
||||||
+EXPORT_SYMBOL(seq_release);
|
|
||||||
+
|
|
||||||
/* interrupt handling */
|
|
||||||
EXPORT_SYMBOL(add_timer);
|
|
||||||
EXPORT_SYMBOL(del_timer);
|
|
||||||
@@ -522,6 +530,7 @@
|
|
||||||
|
|
||||||
/* binfmt_aout */
|
|
||||||
EXPORT_SYMBOL(get_write_access);
|
|
||||||
+EXPORT_SYMBOL(deny_write_access);
|
|
||||||
|
|
||||||
/* time */
|
|
||||||
EXPORT_SYMBOL(get_fast_time);
|
|
@ -1,32 +0,0 @@
|
|||||||
diff -ru linux-2.4.16/drivers/md/Makefile linux/drivers/md/Makefile
|
|
||||||
--- linux-2.4.16/drivers/md/Makefile Tue Nov 27 15:55:57 2001
|
|
||||||
+++ linux/drivers/md/Makefile Thu Nov 29 10:30:29 2001
|
|
||||||
@@ -4,9 +4,12 @@
|
|
||||||
|
|
||||||
O_TARGET := mddev.o
|
|
||||||
|
|
||||||
-export-objs := md.o xor.o
|
|
||||||
+export-objs := md.o xor.o dm-table.o dm-target.o
|
|
||||||
list-multi := lvm-mod.o
|
|
||||||
lvm-mod-objs := lvm.o lvm-snap.o lvm-fs.o
|
|
||||||
+dm-mod-objs := dm.o dm-table.o dm-target.o dmfs-super.o dmfs-root.o \
|
|
||||||
+ dmfs-lv.o dmfs-table.o dmfs-error.o dmfs-status.o \
|
|
||||||
+ dmfs-active.o
|
|
||||||
|
|
||||||
# Note: link order is important. All raid personalities
|
|
||||||
# and xor.o must come before md.o, as they each initialise
|
|
||||||
@@ -20,8 +23,14 @@
|
|
||||||
obj-$(CONFIG_MD_MULTIPATH) += multipath.o
|
|
||||||
obj-$(CONFIG_BLK_DEV_MD) += md.o
|
|
||||||
obj-$(CONFIG_BLK_DEV_LVM) += lvm-mod.o
|
|
||||||
+obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o
|
|
||||||
+obj-$(CONFIG_BLK_DEV_DM_LINEAR) += dm-linear.o
|
|
||||||
|
|
||||||
include $(TOPDIR)/Rules.make
|
|
||||||
|
|
||||||
lvm-mod.o: $(lvm-mod-objs)
|
|
||||||
$(LD) -r -o $@ $(lvm-mod-objs)
|
|
||||||
+
|
|
||||||
+dm-mod.o: $(dm-mod-objs)
|
|
||||||
+ $(LD) -r -o $@ $(dm-mod-objs)
|
|
||||||
+
|
|
@ -1,11 +0,0 @@
|
|||||||
diff -ru linux-2.4.16/include/linux/seq_file.h linux/include/linux/seq_file.h
|
|
||||||
--- linux-2.4.16/include/linux/seq_file.h Tue Nov 27 15:56:08 2001
|
|
||||||
+++ linux/include/linux/seq_file.h Thu Nov 29 10:38:37 2001
|
|
||||||
@@ -12,6 +12,7 @@
|
|
||||||
loff_t index;
|
|
||||||
struct semaphore sem;
|
|
||||||
struct seq_operations *op;
|
|
||||||
+ void *context;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct seq_operations {
|
|
@ -1,13 +0,0 @@
|
|||||||
00_latest Latest source - I only tend to update this before
|
|
||||||
making a release. So if you got this from CVS copy
|
|
||||||
or link the source files in by hand.
|
|
||||||
|
|
||||||
00_config Add device-mapper to the MD section
|
|
||||||
|
|
||||||
00_config_uml only apply for uml, turns on the md section
|
|
||||||
|
|
||||||
00_makefile Add device-mapper to the MD Makefile.
|
|
||||||
|
|
||||||
00_bh-async-3 AA's async bh patch so we can hook b_end_io
|
|
||||||
to keep track of pending io.
|
|
||||||
|
|
@ -1,28 +0,0 @@
|
|||||||
List of projects, ideas and pending bug fixes (no particular order)
|
|
||||||
===============================================
|
|
||||||
|
|
||||||
o Verify that table parsing code is still doing the right thing.
|
|
||||||
|
|
||||||
o Verify ioctl() interface is still working the same way as it used to
|
|
||||||
|
|
||||||
o Error handling in dm-ioctl.c:parse_table()
|
|
||||||
|
|
||||||
o Locking on mapped_devices in dm-ioctl.c
|
|
||||||
|
|
||||||
o Port to 2.5 kernels (new block I/O layer)
|
|
||||||
|
|
||||||
o Investigate char device number allocation for dm-ioctl.c (maybe use misc
|
|
||||||
device ?)
|
|
||||||
|
|
||||||
o dm-table.c:dm_table_get_device() only appears to check the sizes when
|
|
||||||
allocating new devices
|
|
||||||
|
|
||||||
o Investigate overhead of vmalloc()ed tables and if required look at ways
|
|
||||||
of using "normal" kernel memory instead.
|
|
||||||
|
|
||||||
o Add ability to change the blocksize of logical volumes
|
|
||||||
|
|
||||||
o General code tidy
|
|
||||||
|
|
||||||
o Testing, testing, testing....
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user