]> sourceware.org Git - dm.git/commitdiff
Hold devices in a hash table.
authorAlasdair Kergon <agk@redhat.com>
Mon, 14 Oct 2002 11:07:24 +0000 (11:07 +0000)
committerAlasdair Kergon <agk@redhat.com>
Mon, 14 Oct 2002 11:07:24 +0000 (11:07 +0000)
VERSION
kernel/Makefile.in
kernel/common/dm-hash.c [new file with mode: 0644]
kernel/common/dm.c
kernel/common/dm.h
kernel/ioctl/dm-ioctl.c
kernel/ioctl/dm-ioctl.h

diff --git a/VERSION b/VERSION
index 07a427fb83a47636bb3e9a24a97acd9c825c67a1..b2922a64379817115e7e0b8819a0ceae6586861f 100644 (file)
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.96.05-cvs (2002-09-24)
+0.96.05-cvs (2002-10-14)
index 3c509bac70ed5efab29727a0f3f9d435cf40e178..6cc6087b3e5d91c4c7edd58728b562c6ea5a48f3 100644 (file)
@@ -34,7 +34,7 @@ FS=dmfs-error.c dmfs-lv.c dmfs-root.c dmfs-status.c \
 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
 
diff --git a/kernel/common/dm-hash.c b/kernel/common/dm-hash.c
new file mode 100644 (file)
index 0000000..d411208
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ * 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;
+}
index 65bae2c1e7f2eaed14e67bfc5db8922cc240f7bf..7a35bcc9014f8446e2e225c5d30543a71838f71a 100644 (file)
@@ -30,16 +30,6 @@ struct io_hook {
 
 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];
@@ -50,162 +40,10 @@ 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);
 
-/*
- * 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",
@@ -267,6 +105,7 @@ static struct {
 } _inits[] = {
 #define xx(n) {n ## _init, n ## _exit},
        xx(local)
+       xx(dm_hash)
        xx(dm_target)
        xx(dm_linear)
        xx(dm_stripe)
@@ -693,52 +532,48 @@ static int dm_user_bmap(struct inode *inode, struct lv_bmap *lvb)
 }
 
 /*
- * 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.");
@@ -746,37 +581,64 @@ static struct mapped_device *alloc_dev(const char *name, const char *uuid,
        }
 
        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)
@@ -855,34 +717,9 @@ static void __unbind(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;
@@ -897,86 +734,60 @@ int dm_create(const char *name, const char *uuid, int minor, int ro,
        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;
@@ -985,13 +796,14 @@ int dm_destroy(struct mapped_device *md)
        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;
 }
 
@@ -1011,12 +823,10 @@ void dm_destroy_all(void)
                                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);
 }
@@ -1069,70 +879,82 @@ int dm_swap_table(struct mapped_device *md, struct dm_table *table)
 
 /*
  * 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;
 }
 
index c4fad1b60da40b3cffbd8d77548f7baca9dddb51..8ef241689d7c870f2688b6b86036479037fa0997 100644 (file)
@@ -99,11 +99,14 @@ struct dm_table {
  * 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;
 
@@ -131,30 +134,46 @@ void dm_target_exit(void);
  */
 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);
 
 /*
@@ -172,8 +191,8 @@ int dm_swap_table(struct mapped_device *md, struct dm_table *t);
 /*
  * 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);
@@ -214,6 +233,14 @@ static inline int array_too_big(unsigned long fixed, unsigned long obj,
        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
  */
index 4109bf43cc9e375c6efcc643d3750d7e18d6e6c9..c339b6adccfd14b4ceb67e3d611aa25f005fa61b 100644 (file)
@@ -210,14 +210,18 @@ static void __info(struct mapped_device *md, struct dm_ioctl *param)
 /*
  * 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)
@@ -238,7 +242,7 @@ static int info(struct dm_ioctl *param, struct dm_ioctl *user)
 
        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.
@@ -365,7 +369,7 @@ static int get_status(struct dm_ioctl *param, struct dm_ioctl *user)
        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.
@@ -408,7 +412,7 @@ static int wait_device_event(struct dm_ioctl *param, struct dm_ioctl *user)
        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.
@@ -445,7 +449,7 @@ static int dep(struct dm_ioctl *param, struct dm_ioctl *user)
        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;
 
@@ -500,31 +504,36 @@ static int remove(struct dm_ioctl *param, struct dm_ioctl *user)
        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)
@@ -543,7 +552,7 @@ 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;
@@ -567,14 +576,18 @@ static int rename(struct dm_ioctl *param, struct dm_ioctl *user)
 {
        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);
 }
 
 
@@ -799,7 +812,9 @@ static int __init dm_devfs_init(void) {
        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;
index d1a07a7b538a1905da5ecf7d1063be3e93be6bc2..7830aeae17b8df772da34a5d5660466e4c85d115 100644 (file)
@@ -127,8 +127,8 @@ enum {
 
 #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
This page took 0.05817 seconds and 5 git commands to generate.