#ifndef _LINUX_DEVICE_MAPPER_H
#define _LINUX_DEVICE_MAPPER_H
-#ifdef __KERNEL__
-
typedef unsigned long sector_t;
struct dm_target;
* In the constructor the target parameter will already have the
* table, type, begin and len fields filled in.
*/
-typedef int (*dm_ctr_fn) (struct dm_target *target, int argc, char **argv);
+typedef int (*dm_ctr_fn) (struct dm_target * target, int argc, char **argv);
/*
* The destructor doesn't need to free the dm_target, just
* anything hidden ti->private.
*/
-typedef void (*dm_dtr_fn) (struct dm_target *ti);
+typedef void (*dm_dtr_fn) (struct dm_target * ti);
/*
* The map function must return:
* = 0: The target will handle the io by resubmitting it later
* > 0: simple remap complete
*/
-typedef int (*dm_map_fn) (struct dm_target *ti, struct buffer_head *bh, int rw);
-typedef int (*dm_status_fn) (struct dm_target *ti, status_type_t status_type,
+typedef int (*dm_map_fn) (struct dm_target * ti, struct buffer_head * bh,
+ int rw, void **map_context);
+
+/*
+ * Returns:
+ * < 0 : error (currently ignored)
+ * 0 : ended successfully
+ * 1 : for some reason the io has still not completed (eg,
+ * multipath target might want to requeue a failed io).
+ */
+typedef int (*dm_endio_fn) (struct dm_target * ti, struct buffer_head * bh,
+ int rw, int error, void *map_context);
+typedef int (*dm_status_fn) (struct dm_target * ti, status_type_t status_type,
char *result, int maxlen);
void dm_error(const char *message);
dm_ctr_fn ctr;
dm_dtr_fn dtr;
dm_map_fn map;
+ dm_endio_fn end_io;
dm_status_fn status;
};
int dm_register_target(struct target_type *t);
int dm_unregister_target(struct target_type *t);
-#endif /* __KERNEL__ */
-
#endif /* _LINUX_DEVICE_MAPPER_H */
kfree(lc);
}
-static int linear_map(struct dm_target *ti, struct buffer_head *bh, int rw)
+static int linear_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+ void **map_context)
{
struct linear_c *lc = (struct linear_c *) ti->private;
*/
#include <linux/config.h>
+#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
struct dm_snapshot *s;
unsigned long chunk_size;
int r = -EINVAL;
- char *persistent;
+ char persistent;
char *origin_path;
char *cow_path;
char *value;
origin_path = argv[0];
cow_path = argv[1];
- persistent = argv[2];
+ persistent = toupper(*argv[2]);
- if ((*persistent & 0x5f) != 'P' && (*persistent & 0x5f) != 'N') {
+ if (persistent != 'P' && persistent != 'N') {
ti->error = "Persistent flag is not P or N";
r = -EINVAL;
goto bad;
s = kmalloc(sizeof(*s), GFP_KERNEL);
if (s == NULL) {
ti->error = "Cannot allocate snapshot context private "
- "structure";
+ "structure";
r = -ENOMEM;
goto bad;
}
s->chunk_size = chunk_size;
s->chunk_mask = chunk_size - 1;
- s->type = *persistent;
+ s->type = persistent;
for (s->chunk_shift = 0; chunk_size;
s->chunk_shift++, chunk_size >>= 1)
;
*/
s->store.snap = s;
- if ((*persistent & 0x5f) == 'P')
+ if (persistent == 'P')
r = dm_create_persistent(&s->store, s->chunk_size);
else
r = dm_create_transient(&s->store, s, blocksize);
(bh->b_rsector & s->chunk_mask);
}
-static int snapshot_map(struct dm_target *ti, struct buffer_head *bh, int rw)
+static int snapshot_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+ void **map_context)
{
struct exception *e;
struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
dm_put_device(ti, dev);
}
-static int origin_map(struct dm_target *ti, struct buffer_head *bh, int rw)
+static int origin_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+ void **map_context)
{
struct dm_dev *dev = (struct dm_dev *) ti->private;
bh->b_rdev = dev->dev;
return -EINVAL;
}
+ /*
+ * chunk_size is a power of two
+ */
+ if (!chunk_size || (chunk_size & (chunk_size - 1))) {
+ ti->error = "dm-stripe: Invalid chunk size";
+ return -EINVAL;
+ }
+
if (!multiple(ti->len, stripes, &width)) {
ti->error = "dm-stripe: Target length not divisable by "
"number of stripes";
return -EINVAL;
}
+ /*
+ * Do we have enough arguments for that many stripes ?
+ */
+ if (argc != (2 + 2 * stripes)) {
+ ti->error = "dm-stripe: Not enough destinations specified";
+ return -EINVAL;
+ }
+
sc = alloc_context(stripes);
if (!sc) {
ti->error = "dm-stripe: Memory allocation for striped context "
- "failed";
+ "failed";
return -ENOMEM;
}
sc->stripes = stripes;
sc->stripe_width = width;
- /*
- * chunk_size is a power of two
- */
- if (!chunk_size || (chunk_size & (chunk_size - 1))) {
- ti->error = "dm-stripe: Invalid chunk size";
- kfree(sc);
- return -EINVAL;
- }
-
sc->chunk_mask = ((sector_t) chunk_size) - 1;
for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++)
chunk_size >>= 1;
* Get the stripe destinations.
*/
for (i = 0; i < stripes; i++) {
- if (argc < 2) {
- ti->error = "dm-stripe: Not enough destinations "
- "specified";
- kfree(sc);
- return -EINVAL;
- }
-
argv += 2;
r = get_stripe(ti, sc, i, argv);
if (r < 0) {
ti->error = "dm-stripe: Couldn't parse stripe "
- "destination";
+ "destination";
while (i--)
dm_put_device(ti, sc->stripe[i].dev);
kfree(sc);
kfree(sc);
}
-static int stripe_map(struct dm_target *ti, struct buffer_head *bh, int rw)
+static int stripe_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+ void **context)
{
struct stripe_c *sc = (struct stripe_c *) ti->private;
offset = snprintf(result, maxlen, "%d " SECTOR_FORMAT,
sc->stripes, sc->chunk_mask + 1);
for (i = 0; i < sc->stripes; i++) {
- offset += snprintf(result + offset, maxlen - offset,
- " %s " SECTOR_FORMAT,
- kdevname(to_kdev_t
- (sc->stripe[i].dev->bdev->bd_dev)),
- sc->stripe[i].physical_start);
+ offset +=
+ snprintf(result + offset, maxlen - offset,
+ " %s " SECTOR_FORMAT,
+ kdevname(to_kdev_t(sc->stripe[i].dev->bdev->bd_dev)),
+ sc->stripe[i].physical_start);
}
break;
}
/* free the targets */
for (i = 0; i < t->num_targets; i++) {
- struct dm_target *tgt = &t->targets[i];
-
- dm_put_target_type(t->targets[i].type);
+ struct dm_target *tgt = t->targets + i;
if (tgt->type->dtr)
tgt->type->dtr(tgt);
+
+ dm_put_target_type(tgt->type);
}
vfree(t->highs);
int dm_table_add_target(struct dm_table *t, const char *type,
sector_t start, sector_t len, char *params)
{
- int r, argc;
+ int r = -EINVAL, argc;
char *argv[32];
- struct target_type *tt;
struct dm_target *tgt;
if ((r = check_space(t)))
tgt = t->targets + t->num_targets;
memset(tgt, 0, sizeof(*tgt));
- tt = dm_get_target_type(type);
- if (!tt) {
+ tgt->type = dm_get_target_type(type);
+ if (!tgt->type) {
tgt->error = "unknown target type";
- return -EINVAL;
+ goto bad;
}
tgt->table = t;
- tgt->type = tt;
tgt->begin = start;
tgt->len = len;
tgt->error = "Unknown error";
* Does this target adjoin the previous one ?
*/
if (!adjoin(t, tgt)) {
- DMERR("Gap in table");
- dm_put_target_type(tt);
- return -EINVAL;
+ tgt->error = "Gap in table";
+ goto bad;
}
r = split_args(ARRAY_SIZE(argv), &argc, argv, params);
if (r) {
tgt->error = "couldn't split parameters";
- dm_put_target_type(tt);
- return r;
+ goto bad;
}
- r = tt->ctr(tgt, argc, argv);
- if (r) {
- dm_put_target_type(tt);
- return r;
- }
+ r = tgt->type->ctr(tgt, argc, argv);
+ if (r)
+ goto bad;
t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
return 0;
+
+ bad:
+ printk(KERN_ERR DM_NAME ": %s\n", tgt->error);
+ dm_put_target_type(tgt->type);
+ return r;
}
static int setup_indexes(struct dm_table *t)
EXPORT_SYMBOL(dm_get_device);
EXPORT_SYMBOL(dm_put_device);
EXPORT_SYMBOL(dm_table_event);
+EXPORT_SYMBOL(dm_table_get_mode);
};
static LIST_HEAD(_targets);
-static rwlock_t _lock = RW_LOCK_UNLOCKED;
+static DECLARE_RWSEM(_lock);
#define DM_MOD_NAME_SIZE 32
{
struct tt_internal *ti;
- read_lock(&_lock);
+ down_read(&_lock);
ti = __find_target_type(name);
if (ti) {
__MOD_INC_USE_COUNT(ti->tt.module);
ti->use++;
}
- read_unlock(&_lock);
+ up_read(&_lock);
return ti;
}
strcat(module_name, name);
request_module(module_name);
-
- return;
}
struct target_type *dm_get_target_type(const char *name)
{
struct tt_internal *ti = (struct tt_internal *) t;
- read_lock(&_lock);
+ down_read(&_lock);
if (--ti->use == 0 && ti->tt.module)
__MOD_DEC_USE_COUNT(ti->tt.module);
if (ti->use < 0)
BUG();
- read_unlock(&_lock);
+ up_read(&_lock);
return;
}
if (!ti)
return -ENOMEM;
- write_lock(&_lock);
+ down_write(&_lock);
if (__find_target_type(t->name))
rv = -EEXIST;
else
list_add(&ti->list, &_targets);
- write_unlock(&_lock);
+ up_write(&_lock);
return rv;
}
{
struct tt_internal *ti;
- write_lock(&_lock);
+ down_write(&_lock);
if (!(ti = __find_target_type(t->name))) {
- write_unlock(&_lock);
+ up_write(&_lock);
return -EINVAL;
}
if (ti->use) {
- write_unlock(&_lock);
+ up_write(&_lock);
return -ETXTBSY;
}
list_del(&ti->list);
kfree(ti);
- write_unlock(&_lock);
+ up_write(&_lock);
return 0;
}
static void io_err_dtr(struct dm_target *ti)
{
/* empty */
- return;
}
-static int io_err_map(struct dm_target *ti, struct buffer_head *bh, int rw)
+static int io_err_map(struct dm_target *ti, struct buffer_head *bh, int rw,
+ void **map_context)
{
- buffer_IO_error(bh);
- return 0;
+ return -EIO;
}
static struct target_type error_target = {
struct dm_io {
struct mapped_device *md;
+ struct dm_target *ti;
+ int rw;
+ void *map_context;
void (*end_io) (struct buffer_head * bh, int uptodate);
void *context;
};
* The current mapping.
*/
struct dm_table *map;
+
+ /*
+ * io objects are allocated from here.
+ */
+ mempool_t *io_pool;
};
#define MIN_IOS 256
static kmem_cache_t *_io_cache;
-static mempool_t *_io_pool;
/* block device arrays */
static int _block_size[MAX_DEVICES];
static int dm_request(request_queue_t *q, int rw, struct buffer_head *bh);
static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb);
-
static __init int local_init(void)
{
int r;
if (!_io_cache)
return -ENOMEM;
- _io_pool = mempool_create(MIN_IOS, mempool_alloc_slab,
- mempool_free_slab, _io_cache);
- if (!_io_pool) {
- kmem_cache_destroy(_io_cache);
- return -ENOMEM;
- }
-
_major = major;
r = register_blkdev(_major, _name, &dm_blk_dops);
if (r < 0) {
DMERR("register_blkdev failed");
- mempool_destroy(_io_pool);
kmem_cache_destroy(_io_cache);
return r;
}
static void local_exit(void)
{
- mempool_destroy(_io_pool);
kmem_cache_destroy(_io_cache);
if (unregister_blkdev(_major, _name) < 0)
return 0;
}
-static inline struct dm_io *alloc_io(void)
+static inline struct dm_io *alloc_io(struct mapped_device *md)
{
- return mempool_alloc(_io_pool, GFP_NOIO);
+ return mempool_alloc(md->io_pool, GFP_NOIO);
}
-static inline void free_io(struct dm_io *io)
+static inline void free_io(struct mapped_device *md, struct dm_io *io)
{
- mempool_free(io, _io_pool);
+ mempool_free(io, md->io_pool);
}
static inline struct deferred_io *alloc_deferred(void)
down_write(&md->lock);
- if (!test_bit(DMF_SUSPENDED, &md->flags)) {
+ if (!test_bit(DMF_BLOCK_IO, &md->flags)) {
up_write(&md->lock);
free_deferred(di);
return 1;
*/
static void dec_pending(struct buffer_head *bh, int uptodate)
{
+ int r;
struct dm_io *io = bh->b_private;
+ dm_endio_fn endio = io->ti->type->end_io;
+
+ if (endio) {
+ r = endio(io->ti, bh, io->rw, uptodate ? 0 : -EIO,
+ io->map_context);
+ if (r < 0)
+ uptodate = 0;
+
+ else if (r > 0)
+ /* the target wants another shot at the io */
+ return;
+ }
if (atomic_dec_and_test(&io->md->pending))
/* nudge anyone waiting on suspend queue */
bh->b_end_io = io->end_io;
bh->b_private = io->context;
- free_io(io);
+ free_io(io->md, io);
bh->b_end_io(bh, uptodate);
}
* Do the bh mapping for a given leaf
*/
static inline int __map_buffer(struct mapped_device *md,
- int rw, struct buffer_head *bh)
+ int rw, struct buffer_head *bh, struct dm_io *io)
{
int r;
- struct dm_io *io;
struct dm_target *ti;
ti = dm_table_find_target(md->map, bh->b_rsector);
if (!ti)
return -EINVAL;
- io = alloc_io();
- if (!io)
- return -ENOMEM;
-
- io->md = md;
- io->end_io = bh->b_end_io;
- io->context = bh->b_private;
-
- r = ti->type->map(ti, bh, rw);
+ r = ti->type->map(ti, bh, rw, &io->map_context);
- if (r > 0) {
+ if (r >= 0) {
/* hook the end io request fn */
atomic_inc(&md->pending);
+ io->md = md;
+ io->ti = ti;
+ io->rw = rw;
+ io->end_io = bh->b_end_io;
+ io->context = bh->b_private;
bh->b_end_io = dec_pending;
bh->b_private = io;
-
- } else
- /* we don't need to hook */
- free_io(io);
+ }
return r;
}
static int dm_request(request_queue_t *q, int rw, struct buffer_head *bh)
{
int r;
+ struct dm_io *io;
struct mapped_device *md;
md = get_kdev(bh->b_rdev);
return 0;
}
+ io = alloc_io(md);
down_read(&md->lock);
r = __deferring(md, rw, bh);
else if (!r) {
/* not deferring */
- r = __map_buffer(md, rw, bh);
+ r = __map_buffer(md, rw, bh, io);
if (r < 0)
goto bad;
} else
return r;
bad:
+ free_io(md, io);
buffer_IO_error(bh);
up_read(&md->lock);
dm_put(md);
{
struct buffer_head bh;
struct dm_target *ti;
+ void *map_context;
int r;
if (test_bit(DMF_BLOCK_IO, &md->flags)) {
ti = dm_table_find_target(md->map, bh.b_rsector);
/* do the mapping */
- r = ti->type->map(ti, &bh, READ);
+ r = ti->type->map(ti, &bh, READ, &map_context);
+ ti->type->end_io(ti, &bh, READ, 0, map_context);
if (!r) {
*r_dev = bh.b_rdev;
}
memset(md, 0, sizeof(*md));
+
+ md->io_pool = mempool_create(MIN_IOS, mempool_alloc_slab,
+ mempool_free_slab, _io_cache);
+ if (!md->io_pool) {
+ free_minor(minor);
+ kfree(md);
+ return NULL;
+ }
+
md->dev = mk_kdev(_major, minor);
init_rwsem(&md->lock);
atomic_set(&md->holders, 1);
static void free_dev(struct mapped_device *md)
{
free_minor(minor(md->dev));
+ mempool_destroy(md->io_pool);
kfree(md);
}
}
set_bit(DMF_BLOCK_IO, &md->flags);
+ add_wait_queue(&md->wait, &wait);
up_write(&md->lock);
/*
* Then we wait for the already mapped ios to
* complete.
*/
- down_read(&md->lock);
-
- add_wait_queue(&md->wait, &wait);
+ run_task_queue(&tq_disk);
while (1) {
set_current_state(TASK_INTERRUPTIBLE);
}
current->state = TASK_RUNNING;
- remove_wait_queue(&md->wait, &wait);
- up_read(&md->lock);
- /* set_bit is atomic */
+ down_write(&md->lock);
+ remove_wait_queue(&md->wait, &wait);
set_bit(DMF_SUSPENDED, &md->flags);
+ up_write(&md->lock);
return 0;
}
struct deferred_io *def;
down_write(&md->lock);
- if (!test_bit(DMF_SUSPENDED, &md->flags) ||
- !dm_table_get_size(md->map)) {
+ if (!test_bit(DMF_SUSPENDED, &md->flags) || !dm_table_get_size(md->map)) {
up_write(&md->lock);
return -EINVAL;
}
return 0;
bad:
- while (i--)
+ while (i--) {
+ UnlockPage(_pages_array[i]);
__free_page(_pages_array[i]);
+ }
return -ENOMEM;
}
p = block >> job->bpp_shift;
block &= job->bpp_mask;
- bh->b_dev = B_FREE;
bh->b_size = job->block_size;
set_bh_page(bh, job->pages[p], ((block << job->block_shift) +
job->offset) << SECTOR_SHIFT);
init_buffer(bh, end_bh, job);
bh->b_dev = job->disk.dev;
- bh->b_state = ((1 << BH_Mapped) | (1 << BH_Lock) | (1 << BH_Req));
+ atomic_set(&bh->b_count, 1);
+
+ bh->b_state = ((1 << BH_Uptodate) | (1 << BH_Mapped) |
+ (1 << BH_Lock) | (1 << BH_Req));
- set_bit(BH_Uptodate, &bh->b_state);
if (job->rw == WRITE)
clear_bit(BH_Dirty, &bh->b_state);
}
if (r == -ENOMEM)
- /* can complete now */
+ /* can't complete now */
return 1;
return r;
{
struct copy_info *info = (struct copy_info *) job->context;
- if (job->err && info->notify) {
- info->notify(job->err, job->context);
+ if (job->err) {
+ if (info->notify)
+ info->notify(job->err, job->context);
+
kcopyd_free_job(job);
free_copy_info(info);
return;
job->rw = WRITE;
memcpy(&job->disk, &info->to, sizeof(job->disk));
job->callback = copy_complete;
- job->context = info;
/*
* Queue the write.
memcpy(&job->disk, &info->to, sizeof(job->disk));
job->offset = offset;
- calc_block_sizes(job);
job->callback = page_write_complete;
job->context = info;
memcpy(&job->disk, from, sizeof(*from));
job->offset = 0;
- calc_block_sizes(job);
job->callback = copy_write;
job->context = info;
* Set this to ensure you are notified when the job has
* completed. 'context' is for callback to use.
*/
- void (*callback) (struct kcopyd_job *job);
+ void (*callback) (struct kcopyd_job * job);
void *context;
};
#include <linux/wait.h>
#include <linux/blk.h>
#include <linux/slab.h>
+
#include <asm/uaccess.h>
#define DM_DRIVER_EMAIL "dm@uk.sistina.com"
return 0;
}
-
/*-----------------------------------------------------------------
* Implementation of the ioctl commands
*---------------------------------------------------------------*/
* All the ioctl commands get dispatched to functions with this
* prototype.
*/
-typedef int (*ioctl_fn)(struct dm_ioctl *param, struct dm_ioctl *user);
+typedef int (*ioctl_fn) (struct dm_ioctl * param, struct dm_ioctl * user);
/*
* Check a string doesn't overrun the chunk of
return -EINVAL;
}
+ /*
+ * You may ask the interface to drop its reference to an
+ * in use device. This is no different to unlinking a
+ * file that someone still has open. The device will not
+ * actually be destroyed until the last opener closes it.
+ * The name and uuid of the device (both are interface
+ * properties) will be available for reuse immediately.
+ *
+ * You don't want to drop a _suspended_ device from the
+ * interface, since that will leave you with no way of
+ * resuming it.
+ */
+ if (dm_suspended(hc->md)) {
+ DMWARN("refusing to remove a suspended device.");
+ up_write(&_hash_lock);
+ return -EPERM;
+ }
+
__hash_remove(hc);
up_write(&_hash_lock);
return 0;
dm_table_put(t);
return r;
}
+ dm_table_put(t); /* md will have taken its own reference */
dev = dm_kdev(md);
set_device_ro(dev, (param->flags & DM_READONLY_FLAG));
return dm_hash_rename(param->name, new_name);
}
-
/*-----------------------------------------------------------------
* Implementation of open/close/ioctl on the special char
* device.
}
static struct file_operations _ctl_fops = {
- .ioctl = ctl_ioctl,
- .owner = THIS_MODULE,
+ .ioctl = ctl_ioctl,
+ .owner = THIS_MODULE,
};
static devfs_handle_t _ctl_handle;
return 0;
failed:
- dm_hash_exit();
misc_deregister(&_dm_misc);
+ dm_hash_exit();
return r;
}
void dm_interface_exit(void)
{
- dm_hash_exit();
-
if (misc_deregister(&_dm_misc) < 0)
DMERR("misc_deregister failed for control device");
+
+ dm_hash_exit();
}
#define DM_VERSION_MAJOR 1
#define DM_VERSION_MINOR 0
-#define DM_VERSION_PATCHLEVEL 8
-#define DM_VERSION_EXTRA "-ioctl-cvs (2002-11-21)"
+#define DM_VERSION_PATCHLEVEL 9
+#define DM_VERSION_EXTRA "-ioctl (2003-01-21)"
/* Status bits */
#define DM_READONLY_FLAG 0x00000001