-0.96.05-cvs (2002-09-24)
+0.96.05-cvs (2002-10-14)
COMMON=dm-linear.c dm-stripe.c \
dm-snapshot.c kcopyd.c \
dm-table.c dm-target.c dm.c dm.h dm-snapshot.h \
- dm-exception-store.c kcopyd.h
+ dm-exception-store.c kcopyd.h dm-hash.c
IOCTL=dm-ioctl.c
--- /dev/null
+/*
+ * Copyright (C) 2002 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+/*
+ * We need to be able to quickly return the struct mapped_device,
+ * whether it is looked up by name, uuid or by kdev_t. Note that
+ * multiple major numbers are now supported, so we cannot keep
+ * things simple by putting them in an array.
+ *
+ * Instead this will be implemented as a trio of closely coupled
+ * hash tables.
+ */
+
+#include <linux/list.h>
+#include <linux/rwsem.h>
+#include <linux/slab.h>
+
+#include "dm.h"
+
+struct hash_cell {
+ struct list_head list;
+ struct mapped_device *md;
+};
+
+#define NUM_BUCKETS 64
+#define MASK_BUCKETS (NUM_BUCKETS - 1)
+#define HASH_MULT 2654435387U
+static struct list_head *_dev_buckets;
+static struct list_head *_name_buckets;
+static struct list_head *_uuid_buckets;
+
+/*
+ * Guards access to all three tables.
+ */
+static DECLARE_RWSEM(_hash_lock);
+
+
+/*-----------------------------------------------------------------
+ * Init/exit code
+ *---------------------------------------------------------------*/
+void dm_hash_exit(void)
+{
+ if (_dev_buckets) {
+ kfree(_dev_buckets);
+ _dev_buckets = NULL;
+ }
+
+ if (_name_buckets) {
+ kfree(_name_buckets);
+ _name_buckets = NULL;
+ }
+
+ if (_uuid_buckets) {
+ kfree(_uuid_buckets);
+ _uuid_buckets = NULL;
+ }
+}
+
+struct list_head *alloc_buckets(void)
+{
+ struct list_head *buckets;
+ unsigned int i, len;
+
+ len = NUM_BUCKETS * sizeof(struct list_head);
+ buckets = kmalloc(len, GFP_KERNEL);
+ if (buckets)
+ for (i = 0; i < NUM_BUCKETS; i++)
+ INIT_LIST_HEAD(buckets + i);
+
+ return buckets;
+}
+
+int dm_hash_init(void)
+{
+ _dev_buckets = alloc_buckets();
+ if (!_dev_buckets)
+ goto bad;
+
+ _name_buckets = alloc_buckets();
+ if (!_name_buckets)
+ goto bad;
+
+ _uuid_buckets = alloc_buckets();
+ if (!_uuid_buckets)
+ goto bad;
+
+ return 0;
+
+ bad:
+ dm_hash_exit();
+ return -ENOMEM;
+}
+
+
+/*-----------------------------------------------------------------
+ * Hash functions
+ *---------------------------------------------------------------*/
+static inline unsigned int hash_dev(kdev_t dev)
+{
+ return (HASHDEV(dev) * HASH_MULT) & MASK_BUCKETS;
+}
+
+/*
+ * We're not really concerned with the str hash function being
+ * fast since it's only used by the ioctl interface.
+ */
+static unsigned int hash_str(const char *str)
+{
+ unsigned int h = 0;
+
+ while (*str)
+ h = (h + (unsigned int) *str++) * HASH_MULT;
+
+ return h & MASK_BUCKETS;
+}
+
+
+/*-----------------------------------------------------------------
+ * Code for looking up the device by kdev_t.
+ *---------------------------------------------------------------*/
+static struct hash_cell *__get_dev_cell(kdev_t dev)
+{
+ struct list_head *tmp;
+ struct hash_cell *hc;
+ unsigned int h = hash_dev(dev);
+
+ list_for_each (tmp, _dev_buckets + h) {
+ hc = list_entry(tmp, struct hash_cell, list);
+ if (kdev_same(hc->md->dev, dev))
+ return hc;
+ }
+
+ return NULL;
+}
+
+struct mapped_device *dm_get_r(kdev_t dev)
+{
+ struct hash_cell *hc;
+ struct mapped_device *md = NULL;
+
+ down_read(&_hash_lock);
+
+ hc = __get_dev_cell(dev);
+ if (!hc)
+ goto out;
+
+ down_read(&hc->md->lock);
+ if (hc->md->invalid) {
+ up_read(&hc->md->lock);
+ goto out;
+ }
+
+ md = hc->md;
+
+ out:
+ up_read(&_hash_lock);
+ return md;
+}
+
+struct mapped_device *dm_get_w(kdev_t dev)
+{
+ struct hash_cell *hc;
+ struct mapped_device *md = NULL;
+
+ down_read(&_hash_lock);
+
+ hc = __get_dev_cell(dev);
+ if (!hc)
+ goto out;
+
+ down_write(&hc->md->lock);
+ if (hc->md->invalid) {
+ up_write(&hc->md->lock);
+ goto out;
+ }
+
+ md = hc->md;
+
+ out:
+ up_read(&_hash_lock);
+ return md;
+}
+
+
+/*-----------------------------------------------------------------
+ * Code for looking up a device by name
+ *---------------------------------------------------------------*/
+static int namecmp(struct mapped_device *md, const char *str, int uuid)
+{
+ if (!uuid)
+ return strcmp(md->name, str);
+
+ if (!md->uuid)
+ return -1; /* never equal */
+
+ return strcmp(md->uuid, str);
+}
+
+static struct hash_cell *__get_str_cell(const char *str, int uuid)
+{
+ struct list_head *tmp, *buckets;
+ struct hash_cell *hc;
+ unsigned int h = hash_str(str);
+
+ buckets = uuid ? _uuid_buckets : _name_buckets;
+ list_for_each (tmp, buckets + h) {
+ hc = list_entry(tmp, struct hash_cell, list);
+ if (!namecmp(hc->md, str, uuid))
+ return hc;
+ }
+
+ return NULL;
+}
+
+static inline struct mapped_device *get_name(const char *str, int uuid,
+ int write)
+{
+ struct hash_cell *hc;
+ struct mapped_device *md = NULL;
+
+ down_read(&_hash_lock);
+
+ hc = __get_str_cell(str, uuid);
+ if (!hc)
+ goto out;
+
+ if (write)
+ down_write(&hc->md->lock);
+ else
+ down_read(&hc->md->lock);
+
+ if (hc->md->invalid) {
+ if (write)
+ up_write(&hc->md->lock);
+ else
+ up_read(&hc->md->lock);
+ goto out;
+ }
+
+ md = hc->md;
+
+ out:
+ up_read(&_hash_lock);
+
+ return md;
+}
+
+struct mapped_device *dm_get_name_r(const char *name)
+{
+ return get_name(name, 0, 0);
+}
+
+struct mapped_device *dm_get_name_w(const char *name)
+{
+ return get_name(name, 0, 1);
+}
+
+struct mapped_device *dm_get_uuid_r(const char *uuid)
+{
+ return get_name(uuid, 1, 0);
+}
+
+struct mapped_device *dm_get_uuid_w(const char *uuid)
+{
+ return get_name(uuid, 1, 1);
+}
+
+/*-----------------------------------------------------------------
+ * Inserting and removing and renaming a device.
+ *---------------------------------------------------------------*/
+static struct hash_cell *alloc_cell(struct mapped_device *md)
+{
+ struct hash_cell *hc = kmalloc(sizeof(*hc), GFP_KERNEL);
+ if (hc) {
+ INIT_LIST_HEAD(&hc->list);
+ hc->md = md;
+ }
+
+ return hc;
+}
+
+/*
+ * The kdev_t and uuid of a device can never change once it is
+ * initially inserted.
+ */
+int dm_hash_insert(struct mapped_device *md)
+{
+ struct hash_cell *dev_cell, *name_cell, *uuid_cell;
+
+ /*
+ * Allocate the new cells.
+ */
+ dev_cell = name_cell = uuid_cell = NULL;
+ if (!(dev_cell = alloc_cell(md)) ||
+ !(name_cell = alloc_cell(md)) ||
+ !(uuid_cell = alloc_cell(md))) {
+ if (uuid_cell)
+ kfree(uuid_cell);
+ if (name_cell)
+ kfree(name_cell);
+ if (dev_cell)
+ kfree(dev_cell);
+
+ return -ENOMEM;
+ }
+
+ /*
+ * Insert the cell into all three hash tables.
+ */
+ down_write(&_hash_lock);
+ if (__get_dev_cell(md->dev))
+ goto bad;
+
+ list_add(&dev_cell->list, _dev_buckets + hash_dev(md->dev));
+
+ if (__get_str_cell(md->name, 0)) {
+ list_del(&dev_cell->list);
+ goto bad;
+ }
+ list_add(&name_cell->list, _name_buckets + hash_str(md->name));
+
+ if (md->uuid) {
+ if (__get_str_cell(md->uuid, 1)) {
+ list_del(&name_cell->list);
+ list_del(&dev_cell->list);
+ goto bad;
+ }
+ list_add(&uuid_cell->list, _uuid_buckets + hash_str(md->uuid));
+ }
+ up_write(&_hash_lock);
+
+ if (!md->uuid)
+ kfree(uuid_cell);
+
+ return 0;
+
+ bad:
+ up_write(&_hash_lock);
+ kfree(uuid_cell);
+ kfree(name_cell);
+ kfree(dev_cell);
+ return -EBUSY;
+}
+
+static void dispose_cell(struct hash_cell *hc)
+{
+ list_del(&hc->list);
+ kfree(hc);
+}
+
+/*
+ * md should already have the write lock and md->invalid is already set.
+ */
+void dm_hash_remove(struct mapped_device *md)
+{
+ struct hash_cell *hc;
+
+ /*
+ * Ensure that anything else waiting for the lock gets it and
+ * promptly releases it because md->invalid has now been set.
+ * Acquiring _hash_lock exclusively prevents anything else
+ * starting a search for an md until our md is completely removed.
+ */
+ up_write(&md->lock);
+ down_write(&_hash_lock);
+ down_write(&md->lock);
+
+ /* remove from the dev hash */
+ hc = __get_dev_cell(md->dev);
+ if (!hc)
+ DMWARN("device doesn't appear to be in the dev hash table.");
+ else
+ dispose_cell(hc);
+
+ /* remove from the name hash */
+ hc = __get_str_cell(md->name, 0);
+ if (!hc)
+ DMWARN("device doesn't appear to be in the name hash table.");
+ else
+ dispose_cell(hc);
+
+ /* remove from the uuid hash, if it has a uuid */
+ if (md->uuid) {
+ hc = __get_str_cell(md->uuid, 1);
+ if (!hc)
+ DMWARN("device doesn't appear to be in the uuid "
+ "hash table.");
+ else
+ dispose_cell(hc);
+ }
+
+ up_write(&_hash_lock);
+}
+
+int dm_hash_rename(const char *old, const char *new)
+{
+ char *new_name, *old_name;
+ struct hash_cell *hc;
+
+ /*
+ * duplicate new.
+ */
+ new_name = dm_strdup(new);
+ if (!new_name)
+ return -ENOMEM;
+
+ down_write(&_hash_lock);
+
+ /*
+ * Is new free ?
+ */
+ hc = __get_str_cell(new, 0);
+ if (hc) {
+ DMWARN("asked to rename to an already existing name %s -> %s",
+ old, new);
+ up_write(&_hash_lock);
+ return -EBUSY;
+ }
+
+ /*
+ * Is there such a device as 'old' ?
+ */
+ hc = __get_str_cell(old, 0);
+ if (!hc) {
+ DMWARN("asked to rename a non existent device %s -> %s",
+ old, new);
+ up_write(&_hash_lock);
+ return -ENXIO;
+ }
+
+ /*
+ * rename and move the name cell.
+ */
+ list_del(&hc->list);
+ old_name = hc->md->name;
+ hc->md->name = new_name;
+ list_add(&hc->list, _name_buckets + hash_str(new_name));
+
+ up_write(&_hash_lock);
+ kfree(old_name);
+ return 0;
+}
static kmem_cache_t *_io_hook_cache;
-static struct mapped_device *_devs[MAX_DEVICES];
-static struct rw_semaphore _dev_locks[MAX_DEVICES];
-
-/*
- * This lock is only held by dm_create and dm_set_name to avoid
- * race conditions where someone else may create a device with
- * the same name.
- */
-static spinlock_t _create_lock = SPIN_LOCK_UNLOCKED;
-
/* block device arrays */
static int _block_size[MAX_DEVICES];
static int _blksize_size[MAX_DEVICES];
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);
-/*
- * Protect the mapped_devices referenced from _dev[]
- */
-struct mapped_device *dm_get_r(kdev_t dev)
-{
- struct mapped_device *md;
- int minor = MINOR(dev);
-
- if (minor >= MAX_DEVICES)
- return NULL;
-
- down_read(_dev_locks + minor);
- md = _devs[minor];
- if (!md)
- up_read(_dev_locks + minor);
- return md;
-}
-
-struct mapped_device *dm_get_w(kdev_t dev)
-{
- struct mapped_device *md;
- int minor = MINOR(dev);
-
- if (minor >= MAX_DEVICES)
- return NULL;
-
- down_write(_dev_locks + minor);
- md = _devs[minor];
- if (!md)
- up_write(_dev_locks + minor);
- return md;
-}
-
-static int namecmp(struct mapped_device *md, const char *name, int nametype)
-{
- switch (nametype) {
- case DM_LOOKUP_BY_NAME:
- return strcmp(md->name, name);
- break;
-
- case DM_LOOKUP_BY_UUID:
- if (!md->uuid)
- return -1; /* never equal */
-
- return strcmp(md->uuid, name);
- break;
-
- default:
- DMWARN("Unknown comparison type in namecmp: %d", nametype);
- BUG();
- }
-
- return -1;
-}
-
-/*
- * The interface (eg, ioctl) will probably access the devices
- * through these slow 'by name' locks, this needs improving at
- * some point if people start playing with *large* numbers of dm
- * devices.
- */
-struct mapped_device *dm_get_name_r(const char *name, int nametype)
-{
- int i;
- struct mapped_device *md;
-
- for (i = 0; i < MAX_DEVICES; i++) {
- md = dm_get_r(mk_kdev(_major, i));
- if (md) {
- if (!namecmp(md, name, nametype))
- return md;
-
- dm_put_r(md);
- }
- }
-
- return NULL;
-}
-
-struct mapped_device *dm_get_name_w(const char *name, int nametype)
-{
- int i;
- struct mapped_device *md;
-
- /*
- * To avoid getting write locks on all the devices we try
- * and promote a read lock to a write lock, this can
- * fail, in which case we just start again.
- */
-
- restart:
- for (i = 0; i < MAX_DEVICES; i++) {
- md = dm_get_r(mk_kdev(_major, i));
- if (!md)
- continue;
-
- if (namecmp(md, name, nametype)) {
- dm_put_r(md);
- continue;
- }
-
- /* found it */
- dm_put_r(md);
-
- md = dm_get_w(mk_kdev(_major, i));
- if (!md)
- goto restart;
-
- if (namecmp(md, name, nametype)) {
- dm_put_w(md);
- goto restart;
- }
-
- return md;
- }
-
- return NULL;
-}
-
-void dm_put_r(struct mapped_device *md)
-{
- int minor = MINOR(md->dev);
-
- if (minor >= MAX_DEVICES)
- return;
-
- up_read(_dev_locks + minor);
-}
-
-void dm_put_w(struct mapped_device *md)
-{
- int minor = MINOR(md->dev);
-
- if (minor >= MAX_DEVICES)
- return;
-
- up_write(_dev_locks + minor);
-}
-
-/*
- * Setup and tear down the driver
- */
-static __init void init_locks(void)
-{
- int i;
-
- for (i = 0; i < MAX_DEVICES; i++)
- init_rwsem(_dev_locks + i);
-}
-
static __init int local_init(void)
{
int r;
- init_locks();
-
/* allocate a slab for the io-hooks */
if (!_io_hook_cache &&
!(_io_hook_cache = kmem_cache_create("dm io hooks",
} _inits[] = {
#define xx(n) {n ## _init, n ## _exit},
xx(local)
+ xx(dm_hash)
xx(dm_target)
xx(dm_linear)
xx(dm_stripe)
}
/*
- * See if the device with a specific minor # is free. The write
- * lock is held when it returns successfully.
+ * See if the device with a specific minor # is free. Inserts
+ * the device into the hashes.
*/
static inline int specific_dev(int minor, struct mapped_device *md)
{
if (minor >= MAX_DEVICES) {
DMWARN("request for a mapped_device beyond MAX_DEVICES (%d)",
MAX_DEVICES);
- return -1;
+ return -EINVAL;
}
- down_write(_dev_locks + minor);
- if (_devs[minor]) {
+ md->dev = mk_kdev(_major, minor);
+ if (dm_hash_insert(md))
/* in use */
- up_write(_dev_locks + minor);
- return -1;
- }
+ return -EBUSY;
return minor;
}
/*
- * Find the first free device. Again the write lock is held on
- * success.
+ * Find the first free device.
*/
static int any_old_dev(struct mapped_device *md)
{
int i;
for (i = 0; i < MAX_DEVICES; i++)
- if (specific_dev(i, md) != -1)
+ if (specific_dev(i, md) >= 0)
return i;
- return -1;
+ return -EBUSY;
}
/*
- * Allocate and initialise a blank device.
- * Caller must ensure uuid is null-terminated.
+ * Allocate and initialise a blank device, then insert it into
+ * the hash tables. Caller must ensure uuid is null-terminated.
* Device is returned with a write lock held.
*/
static struct mapped_device *alloc_dev(const char *name, const char *uuid,
int minor)
{
struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL);
- int len;
if (!md) {
DMWARN("unable to allocate device, out of memory.");
}
memset(md, 0, sizeof(*md));
+ init_rwsem(&md->lock);
+ down_write(&md->lock);
/*
- * This grabs the write lock if it succeeds.
+ * Copy in the name.
*/
- minor = (minor < 0) ? any_old_dev(md) : specific_dev(minor, md);
- if (minor < 0) {
- kfree(md);
- return NULL;
- }
-
- md->dev = mk_kdev(_major, minor);
- md->suspended = 0;
-
- strncpy(md->name, name, sizeof(md->name) - 1);
- md->name[sizeof(md->name) - 1] = '\0';
+ md->name = dm_strdup(name);
+ if (!md->name)
+ goto bad;
/*
* Copy in the uuid.
*/
if (uuid && *uuid) {
- len = strlen(uuid) + 1;
- if (!(md->uuid = kmalloc(len, GFP_KERNEL))) {
+ md->uuid = dm_strdup(uuid);
+ if (!md->uuid) {
DMWARN("unable to allocate uuid - out of memory.");
- kfree(md);
- return NULL;
+ goto bad;
}
- strcpy(md->uuid, uuid);
}
+ /*
+ * This will have inserted the device into the hashes iff
+ * it succeeded.
+ */
+ minor = (minor < 0) ? any_old_dev(md) : specific_dev(minor, md);
+ if (minor < 0)
+ goto bad;
+
+ md->suspended = 0;
+ md->invalid = 0;
+ md->use_count = 0;
+ md->deferred = NULL;
+
+ md->pending = (atomic_t) ATOMIC_INIT(0);
init_waitqueue_head(&md->wait);
return md;
+
+ bad:
+ if (md->name)
+ kfree(md->name);
+
+ if (md->uuid)
+ kfree(md->uuid);
+
+ kfree(md);
+ return NULL;
+}
+
+static void free_dev(struct mapped_device *md)
+{
+ dm_hash_remove(md);
+ kfree(md->name);
+
+ if (md->uuid)
+ kfree(md->uuid);
+
+ kfree(md);
}
static int __register_device(struct mapped_device *md)
static int check_name(const char *name)
{
- struct mapped_device *md;
-
- if (strchr(name, '/') || strlen(name) > DM_NAME_LEN) {
+ if (strchr(name, '/')) {
DMWARN("invalid device name");
- return -1;
- }
-
- md = dm_get_name_r(name, DM_LOOKUP_BY_NAME);
- if (md) {
- dm_put_r(md);
- DMWARN("device name already in use");
- return -1;
- }
-
- return 0;
-}
-
-static int check_uuid(const char *uuid)
-{
- struct mapped_device *md;
-
- if (uuid) {
- md = dm_get_name_r(uuid, DM_LOOKUP_BY_UUID);
- if (md) {
- dm_put_r(md);
- DMWARN("device uuid already in use");
- return -1;
- }
+ return -EINVAL;
}
return 0;
int r;
struct mapped_device *md;
- spin_lock(&_create_lock);
- if (check_name(name) || check_uuid(uuid)) {
- spin_unlock(&_create_lock);
- return -EINVAL;
- }
+ r = check_name(name);
+ if (r)
+ return r;
md = alloc_dev(name, uuid, minor);
- if (!md) {
- spin_unlock(&_create_lock);
+ if (!md)
return -ENXIO;
- }
- minor = MINOR(md->dev);
- _devs[minor] = md;
r = __register_device(md);
if (r)
- goto err;
+ goto bad;
r = __bind(md, table);
if (r)
- goto err;
+ goto bad;
dm_set_ro(md, ro);
-
- spin_unlock(&_create_lock);
dm_put_w(md);
return 0;
- err:
- _devs[minor] = NULL;
- if (md->uuid)
- kfree(md->uuid);
-
+ bad:
dm_put_w(md);
- kfree(md);
- spin_unlock(&_create_lock);
+ free_dev(md);
return r;
}
/*
* Renames the device. No lock held.
*/
-int dm_set_name(const char *name, int nametype, const char *newname)
+int dm_set_name(const char *name, const char *new_name)
{
int r;
struct mapped_device *md;
- spin_lock(&_create_lock);
- if (check_name(newname) < 0) {
- spin_unlock(&_create_lock);
- return -EINVAL;
- }
-
- md = dm_get_name_w(name, nametype);
- if (!md) {
- spin_unlock(&_create_lock);
- return -ENXIO;
- }
-
- r = __unregister_device(md);
+ r = dm_hash_rename(name, new_name);
if (r)
- goto out;
-
- strcpy(md->name, newname);
- r = __register_device(md);
+ return r;
- out:
+ md = dm_get_name_w(new_name);
+ r = __unregister_device(md);
+ if (!r)
+ r = __register_device(md);
dm_put_w(md);
- spin_unlock(&_create_lock);
return r;
}
/*
- * Destructor for the device. You cannot destroy an open
- * device. Write lock must be held before calling.
- * Caller must dm_put_w(md) then kfree(md) if call was successful.
+ * Destructor for the device. You cannot destroy an open device.
+ * Write lock must be held before calling. md will have been
+ * freed if call was successful.
*/
int dm_destroy(struct mapped_device *md)
{
- int minor, r;
+ int r;
if (md->use_count)
return -EPERM;
if (r)
return r;
- minor = MINOR(md->dev);
- _devs[minor] = NULL;
- __unbind(md);
-
- if (md->uuid)
- kfree(md->uuid);
+ /*
+ * Signal that this md is now invalid so that nothing further
+ * can acquire its lock.
+ */
+ md->invalid = 1;
+ __unbind(md);
+ free_dev(md);
return 0;
}
continue;
r = dm_destroy(md);
- dm_put_w(md);
-
- if (!r) {
- kfree(md);
+ if (r)
+ dm_put_w(md);
+ else
some_destroyed = 1;
- }
}
} while (some_destroyed);
}
/*
* We need to be able to change a mapping table under a mounted
- * filesystem. for example we might want to move some data in
+ * 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. Write lock must be held.
*/
-int dm_suspend(struct mapped_device *md)
+int dm_suspend(kdev_t dev)
{
- int minor = MINOR(md->dev);
+ struct mapped_device *md;
DECLARE_WAITQUEUE(wait, current);
- if (md->suspended)
+ /*
+ * First we set the suspend flag so no more ios will be
+ * mapped.
+ */
+ md = dm_get_w(dev);
+ if (!md)
+ return -ENXIO;
+
+ if (md->suspended) {
+ dm_put_w(md);
return -EINVAL;
+ }
md->suspended = 1;
dm_put_w(md);
- /* wait for all the pending io to flush */
+ /*
+ * Then we wait for wait for the already mapped ios to
+ * complete.
+ */
+ md = dm_get_r(dev);
+ if (!md)
+ return -ENXIO;
+ if (!md->suspended)
+ return -EINVAL;
+
add_wait_queue(&md->wait, &wait);
current->state = TASK_UNINTERRUPTIBLE;
do {
- md = dm_get_w(md->dev);
- if (!md) {
- /* Caller expects to free this lock. Yuck. */
- down_write(_dev_locks + minor);
- return -ENXIO;
- }
-
if (!atomic_read(&md->pending))
break;
- dm_put_w(md);
schedule();
} while (1);
current->state = TASK_RUNNING;
remove_wait_queue(&md->wait, &wait);
+ dm_put_r(md);
return 0;
}
-int dm_resume(struct mapped_device *md)
+int dm_resume(kdev_t dev)
{
- int minor = MINOR(md->dev);
+ struct mapped_device *md;
struct deferred_io *def;
- if (!md->suspended || !md->map->num_targets)
+ md = dm_get_w(dev);
+ if (!md)
+ return -ENXIO;
+
+ if (!md->suspended || !md->map->num_targets) {
+ dm_put_w(md);
return -EINVAL;
+ }
md->suspended = 0;
def = md->deferred;
md->deferred = NULL;
-
dm_put_w(md);
+
flush_deferred_io(def);
run_task_queue(&tq_disk);
- if (!dm_get_w(md->dev)) {
- /* FIXME: yuck */
- down_write(_dev_locks + minor);
- return -ENXIO;
- }
-
return 0;
}
* The actual device struct
*/
struct mapped_device {
+ struct rw_semaphore lock;
+
kdev_t dev;
- char name[DM_NAME_LEN];
+ char *name;
char *uuid;
int use_count;
+ int invalid;
int suspended;
int read_only;
*/
int split_args(int max, int *argc, char **argv, char *input);
-/* dm.c */
-struct mapped_device *dm_get_r(kdev_t dev);
-struct mapped_device *dm_get_w(kdev_t dev);
+/*
+ * dm-hash manages the lookup of devices by dev/name/uuid.
+ */
+int dm_hash_init(void);
+void dm_hash_exit(void);
+
+int dm_hash_insert(struct mapped_device *md);
+void dm_hash_remove(struct mapped_device *md);
+int dm_hash_rename(const char *old, const char *new);
/*
- * There are two ways to lookup a device.
+ * There are three ways to lookup a device: by kdev_t, by name
+ * and by uuid. A code path (eg an ioctl) should only ever get
+ * one device at any time.
*/
-enum {
- DM_LOOKUP_BY_NAME,
- DM_LOOKUP_BY_UUID
-};
+struct mapped_device *dm_get_r(kdev_t dev);
+struct mapped_device *dm_get_w(kdev_t dev);
-struct mapped_device *dm_get_name_r(const char *name, int nametype);
-struct mapped_device *dm_get_name_w(const char *name, int nametype);
+struct mapped_device *dm_get_name_r(const char *name);
+struct mapped_device *dm_get_name_w(const char *name);
-void dm_put_r(struct mapped_device *md);
-void dm_put_w(struct mapped_device *md);
+struct mapped_device *dm_get_uuid_r(const char *uuid);
+struct mapped_device *dm_get_uuid_w(const char *uuid);
+
+static inline void dm_put_r(struct mapped_device *md)
+{
+ up_read(&md->lock);
+}
+
+static inline void dm_put_w(struct mapped_device *md)
+{
+ up_write(&md->lock);
+}
/*
* Call with no lock.
*/
int dm_create(const char *name, const char *uuid, int minor, int ro,
struct dm_table *table);
-int dm_set_name(const char *name, int nametype, const char *newname);
+int dm_set_name(const char *name, const char *newname);
void dm_destroy_all(void);
/*
/*
* A device can still be used while suspended, but I/O is deferred.
*/
-int dm_suspend(struct mapped_device *md);
-int dm_resume(struct mapped_device *md);
+int dm_suspend(kdev_t dev);
+int dm_resume(kdev_t dev);
/* dm-table.c */
int dm_table_create(struct dm_table **result, int mode);
return (num > (ULONG_MAX - fixed) / obj);
}
+static inline char *dm_strdup(const char *str)
+{
+ char *r = kmalloc(strlen(str) + 1, GFP_KERNEL);
+ if (r)
+ strcpy(r, str);
+ return r;
+}
+
/*
* Targets
*/
/*
* Always use UUID for lookups if it's present, otherwise use name.
*/
-static inline char *lookup_name(struct dm_ioctl *param)
+static inline struct mapped_device *find_device_r(struct dm_ioctl *param)
{
- return (*param->uuid) ? param->uuid : param->name;
+ return (*param->uuid ?
+ dm_get_uuid_r(param->uuid) :
+ dm_get_name_r(param->name));
}
-static inline int lookup_type(struct dm_ioctl *param)
+static inline struct mapped_device *find_device_w(struct dm_ioctl *param)
{
- return (*param->uuid) ? DM_LOOKUP_BY_UUID : DM_LOOKUP_BY_NAME;
+ return (*param->uuid ?
+ dm_get_uuid_w(param->uuid) :
+ dm_get_name_w(param->name));
}
#define ALIGNMENT sizeof(int)
param->flags = 0;
- md = dm_get_name_r(lookup_name(param), lookup_type(param));
+ md = find_device_r(param);
if (!md)
/*
* Device not found - returns cleared exists flag.
int ret;
char *outbuf = NULL;
- md = dm_get_name_r(lookup_name(param), lookup_type(param));
+ md = find_device_r(param);
if (!md)
/*
* Device not found - returns cleared exists flag.
struct mapped_device *md;
DECLARE_WAITQUEUE(wq, current);
- md = dm_get_name_r(lookup_name(param), lookup_type(param));
+ md = find_device_r(param);
if (!md)
/*
* Device not found - returns cleared exists flag.
size_t len = 0;
struct dm_target_deps *deps = NULL;
- md = dm_get_name_r(lookup_name(param), lookup_type(param));
+ md = find_device_r(param);
if (!md)
goto out;
int r;
struct mapped_device *md;
- md = dm_get_name_w(lookup_name(param), lookup_type(param));
+ md = find_device_w(param);
if (!md)
return -ENXIO;
+ /*
+ * This unlocks and deallocates md.
+ */
r = dm_destroy(md);
- dm_put_w(md);
- if (!r)
- kfree(md);
+ if (r) {
+ dm_put_w(md);
+ return r;
+ }
- return r;
+ return 0;
}
static int suspend(struct dm_ioctl *param, struct dm_ioctl *user)
{
- int r;
struct mapped_device *md;
+ kdev_t dev;
- md = dm_get_name_w(lookup_name(param), lookup_type(param));
+ md = find_device_r(param);
if (!md)
return -ENXIO;
- r = (param->flags & DM_SUSPEND_FLAG) ? dm_suspend(md) : dm_resume(md);
- dm_put_w(md);
+ dev = md->dev;
+ dm_put_r(md);
- return r;
+ return (param->flags & DM_SUSPEND_FLAG) ?
+ dm_suspend(dev) : dm_resume(dev);
}
static int reload(struct dm_ioctl *param, struct dm_ioctl *user)
return r;
}
- md = dm_get_name_w(lookup_name(param), lookup_type(param));
+ md = find_device_w(param);
if (!md) {
dm_table_destroy(t);
return -ENXIO;
{
char *newname = (char *) param + param->data_start;
+ if (!param->name) {
+ DMWARN("Invalid old logical volume name supplied.");
+ return -EINVAL;
+ }
+
if (valid_str(newname, (void *) param,
- (void *) param + param->data_size) ||
- dm_set_name(lookup_name(param), lookup_type(param), newname)) {
+ (void *) param + param->data_size)) {
DMWARN("Invalid new logical volume name supplied.");
return -EINVAL;
}
- return 0;
+ return dm_set_name(param->name, newname);
}
return 0;
}
-/* Create misc character device and link to DM_DIR/control */
+/*
+ * Create misc character device and link to DM_DIR/control.
+ */
int __init dm_interface_init(void)
{
int r;
#define DM_VERSION_MAJOR 1
#define DM_VERSION_MINOR 0
-#define DM_VERSION_PATCHLEVEL 4
-#define DM_VERSION_EXTRA "-ioctl-cvs (2002-09-24)"
+#define DM_VERSION_PATCHLEVEL 5
+#define DM_VERSION_EXTRA "-ioctl-cvs (2002-10-14)"
/* Status bits */
#define DM_READONLY_FLAG 0x00000001