vserver 1.9.5.x5
[linux-2.6.git] / drivers / md / dm.c
index bdfcbe0..7f1af52 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
+ * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
  *
  * This file is released under the GPL.
  */
@@ -15,9 +16,9 @@
 #include <linux/buffer_head.h>
 #include <linux/mempool.h>
 #include <linux/slab.h>
+#include <linux/idr.h>
 
 static const char *_name = DM_NAME;
-#define MAX_DEVICES 1024
 
 static unsigned int major = 0;
 static unsigned int _major = 0;
@@ -59,6 +60,8 @@ struct mapped_device {
        request_queue_t *queue;
        struct gendisk *disk;
 
+       void *interface_ptr;
+
        /*
         * A list of ios that arrived while we were suspended.
         */
@@ -80,7 +83,7 @@ struct mapped_device {
        /*
         * Event handling.
         */
-       uint32_t event_nr;
+       atomic_t event_nr;
        wait_queue_head_t eventq;
 
        /*
@@ -93,7 +96,7 @@ struct mapped_device {
 static kmem_cache_t *_io_cache;
 static kmem_cache_t *_tio_cache;
 
-static __init int local_init(void)
+static int __init local_init(void)
 {
        int r;
 
@@ -138,23 +141,20 @@ static void local_exit(void)
        DMINFO("cleaned up");
 }
 
-/*
- * We have a lot of init/exit functions, so it seems easier to
- * store them in an array.  The disposable macro 'xx'
- * expands a prefix into a pair of function names.
- */
-static struct {
-       int (*init) (void);
-       void (*exit) (void);
-
-} _inits[] = {
-#define xx(n) {n ## _init, n ## _exit},
-       xx(local)
-       xx(dm_target)
-       xx(dm_linear)
-       xx(dm_stripe)
-       xx(dm_interface)
-#undef xx
+int (*_inits[])(void) __initdata = {
+       local_init,
+       dm_target_init,
+       dm_linear_init,
+       dm_stripe_init,
+       dm_interface_init,
+};
+
+void (*_exits[])(void) = {
+       local_exit,
+       dm_target_exit,
+       dm_linear_exit,
+       dm_stripe_exit,
+       dm_interface_exit,
 };
 
 static int __init dm_init(void)
@@ -164,7 +164,7 @@ static int __init dm_init(void)
        int r, i;
 
        for (i = 0; i < count; i++) {
-               r = _inits[i].init();
+               r = _inits[i]();
                if (r)
                        goto bad;
        }
@@ -173,17 +173,17 @@ static int __init dm_init(void)
 
       bad:
        while (i--)
-               _inits[i].exit();
+               _exits[i]();
 
        return r;
 }
 
 static void __exit dm_exit(void)
 {
-       int i = ARRAY_SIZE(_inits);
+       int i = ARRAY_SIZE(_exits);
 
        while (i--)
-               _inits[i].exit();
+               _exits[i]();
 }
 
 /*
@@ -331,8 +331,8 @@ static sector_t max_io_len(struct mapped_device *md,
         */
        if (ti->split_io) {
                sector_t boundary;
-               boundary = dm_round_up(offset + 1, ti->split_io) - offset;
-
+               boundary = ((offset + ti->split_io) & ~(ti->split_io - 1))
+                          - offset;
                if (len > boundary)
                        len = boundary;
        }
@@ -369,6 +369,7 @@ static void __map_bio(struct dm_target *ti, struct bio *clone,
                struct dm_io *io = tio->io;
                free_tio(tio->io->md, tio);
                dec_pending(io, -EIO);
+               bio_put(clone);
        }
 }
 
@@ -393,7 +394,7 @@ static struct bio *split_bvec(struct bio *bio, sector_t sector,
        struct bio_vec *bv = bio->bi_io_vec + idx;
 
        clone = bio_alloc(GFP_NOIO, 1);
-       memcpy(clone->bi_io_vec, bv, sizeof(*bv));
+       *clone->bi_io_vec = *bv;
 
        clone->bi_sector = sector;
        clone->bi_bdev = bio->bi_bdev;
@@ -585,6 +586,21 @@ static int dm_request(request_queue_t *q, struct bio *bio)
        return 0;
 }
 
+static int dm_flush_all(request_queue_t *q, struct gendisk *disk,
+                       sector_t *error_sector)
+{
+       struct mapped_device *md = q->queuedata;
+       struct dm_table *map = dm_get_table(md);
+       int ret = -ENXIO;
+
+       if (map) {
+               ret = dm_table_flush_all(md->map);
+               dm_table_put(map);
+       }
+
+       return ret;
+}
+
 static void dm_unplug_all(request_queue_t *q)
 {
        struct mapped_device *md = q->queuedata;
@@ -612,56 +628,90 @@ static int dm_any_congested(void *congested_data, int bdi_bits)
 }
 
 /*-----------------------------------------------------------------
- * A bitset is used to keep track of allocated minor numbers.
+ * An IDR is used to keep track of allocated minor numbers.
  *---------------------------------------------------------------*/
-static spinlock_t _minor_lock = SPIN_LOCK_UNLOCKED;
-static unsigned long _minor_bits[MAX_DEVICES / BITS_PER_LONG];
+static DECLARE_MUTEX(_minor_lock);
+static DEFINE_IDR(_minor_idr);
 
 static void free_minor(unsigned int minor)
 {
-       spin_lock(&_minor_lock);
-       clear_bit(minor, _minor_bits);
-       spin_unlock(&_minor_lock);
+       down(&_minor_lock);
+       idr_remove(&_minor_idr, minor);
+       up(&_minor_lock);
 }
 
 /*
  * See if the device with a specific minor # is free.
  */
-static int specific_minor(unsigned int minor)
+static int specific_minor(struct mapped_device *md, unsigned int minor)
 {
-       int r = -EBUSY;
+       int r, m;
 
-       if (minor >= MAX_DEVICES) {
-               DMWARN("request for a mapped_device beyond MAX_DEVICES (%d)",
-                      MAX_DEVICES);
+       if (minor >= (1 << MINORBITS))
                return -EINVAL;
+
+       down(&_minor_lock);
+
+       if (idr_find(&_minor_idr, minor)) {
+               r = -EBUSY;
+               goto out;
        }
 
-       spin_lock(&_minor_lock);
-       if (!test_and_set_bit(minor, _minor_bits))
-               r = 0;
-       spin_unlock(&_minor_lock);
+       r = idr_pre_get(&_minor_idr, GFP_KERNEL);
+       if (!r) {
+               r = -ENOMEM;
+               goto out;
+       }
+
+       r = idr_get_new_above(&_minor_idr, md, minor, &m);
+       if (r) {
+               goto out;
+       }
+
+       if (m != minor) {
+               idr_remove(&_minor_idr, m);
+               r = -EBUSY;
+               goto out;
+       }
 
+out:
+       up(&_minor_lock);
        return r;
 }
 
-static int next_free_minor(unsigned int *minor)
+static int next_free_minor(struct mapped_device *md, unsigned int *minor)
 {
-       int r = -EBUSY;
+       int r;
        unsigned int m;
 
-       spin_lock(&_minor_lock);
-       m = find_first_zero_bit(_minor_bits, MAX_DEVICES);
-       if (m != MAX_DEVICES) {
-               set_bit(m, _minor_bits);
-               *minor = m;
-               r = 0;
+       down(&_minor_lock);
+
+       r = idr_pre_get(&_minor_idr, GFP_KERNEL);
+       if (!r) {
+               r = -ENOMEM;
+               goto out;
+       }
+
+       r = idr_get_new(&_minor_idr, md, &m);
+       if (r) {
+               goto out;
        }
-       spin_unlock(&_minor_lock);
 
+       if (m >= (1 << MINORBITS)) {
+               idr_remove(&_minor_idr, m);
+               r = -ENOSPC;
+               goto out;
+       }
+
+       *minor = m;
+
+out:
+       up(&_minor_lock);
        return r;
 }
 
+static struct block_device_operations dm_blk_dops;
+
 /*
  * Allocate and initialise a blank device with a given minor.
  */
@@ -676,7 +726,7 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
        }
 
        /* get a minor number for the dev */
-       r = persistent ? specific_minor(minor) : next_free_minor(&minor);
+       r = persistent ? specific_minor(md, minor) : next_free_minor(md, &minor);
        if (r < 0)
                goto bad1;
 
@@ -684,6 +734,7 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
        init_rwsem(&md->lock);
        rwlock_init(&md->map_lock);
        atomic_set(&md->holders, 1);
+       atomic_set(&md->event_nr, 0);
 
        md->queue = blk_alloc_queue(GFP_KERNEL);
        if (!md->queue)
@@ -694,6 +745,7 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
        md->queue->backing_dev_info.congested_data = md;
        blk_queue_make_request(md->queue, dm_request);
        md->queue->unplug_fn = dm_unplug_all;
+       md->queue->issue_flush_fn = dm_flush_all;
 
        md->io_pool = mempool_create(MIN_IOS, mempool_alloc_slab,
                                     mempool_free_slab, _io_cache);
@@ -753,10 +805,8 @@ static void event_callback(void *context)
 {
        struct mapped_device *md = (struct mapped_device *) context;
 
-       down_write(&md->lock);
-       md->event_nr++;
+       atomic_inc(&md->event_nr);
        wake_up(&md->eventq);
-       up_write(&md->lock);
 }
 
 static void __set_size(struct gendisk *disk, sector_t size)
@@ -833,6 +883,32 @@ int dm_create_with_minor(unsigned int minor, struct mapped_device **result)
        return create_aux(minor, 1, result);
 }
 
+void *dm_get_mdptr(dev_t dev)
+{
+       struct mapped_device *md;
+       void *mdptr = NULL;
+       unsigned minor = MINOR(dev);
+
+       if (MAJOR(dev) != _major || minor >= (1 << MINORBITS))
+               return NULL;
+
+       down(&_minor_lock);
+
+       md = idr_find(&_minor_idr, minor);
+
+       if (md && (dm_disk(md)->first_minor == minor))
+               mdptr = md->interface_ptr;
+
+       up(&_minor_lock);
+
+       return mdptr;
+}
+
+void dm_set_mdptr(struct mapped_device *md, void *ptr)
+{
+       md->interface_ptr = ptr;
+}
+
 void dm_get(struct mapped_device *md)
 {
        atomic_inc(&md->holders);
@@ -843,8 +919,10 @@ void dm_put(struct mapped_device *md)
        struct dm_table *map = dm_get_table(md);
 
        if (atomic_dec_and_test(&md->holders)) {
-               if (!test_bit(DMF_SUSPENDED, &md->flags) && map)
-                       dm_table_suspend_targets(map);
+               if (!test_bit(DMF_SUSPENDED, &md->flags) && map) {
+                       dm_table_presuspend_targets(map);
+                       dm_table_postsuspend_targets(map);
+               }
                __unbind(md);
                free_dev(md);
        }
@@ -956,7 +1034,11 @@ int dm_suspend(struct mapped_device *md)
                return -EINVAL;
        }
 
+       map = dm_get_table(md);
+       if (map)
+               dm_table_presuspend_targets(map);
        __lock_fs(md);
+
        up_read(&md->lock);
 
        /*
@@ -979,7 +1061,6 @@ int dm_suspend(struct mapped_device *md)
        up_write(&md->lock);
 
        /* unplug */
-       map = dm_get_table(md);
        if (map) {
                dm_table_unplug_all(map);
                dm_table_put(map);
@@ -1014,7 +1095,7 @@ int dm_suspend(struct mapped_device *md)
 
        map = dm_get_table(md);
        if (map)
-               dm_table_suspend_targets(map);
+               dm_table_postsuspend_targets(map);
        dm_table_put(map);
        up_write(&md->lock);
 
@@ -1054,35 +1135,13 @@ int dm_resume(struct mapped_device *md)
  *---------------------------------------------------------------*/
 uint32_t dm_get_event_nr(struct mapped_device *md)
 {
-       uint32_t r;
-
-       down_read(&md->lock);
-       r = md->event_nr;
-       up_read(&md->lock);
-
-       return r;
+       return atomic_read(&md->event_nr);
 }
 
-int dm_add_wait_queue(struct mapped_device *md, wait_queue_t *wq,
-                     uint32_t event_nr)
+int dm_wait_event(struct mapped_device *md, int event_nr)
 {
-       down_write(&md->lock);
-       if (event_nr != md->event_nr) {
-               up_write(&md->lock);
-               return 1;
-       }
-
-       add_wait_queue(&md->eventq, wq);
-       up_write(&md->lock);
-
-       return 0;
-}
-
-void dm_remove_wait_queue(struct mapped_device *md, wait_queue_t *wq)
-{
-       down_write(&md->lock);
-       remove_wait_queue(&md->eventq, wq);
-       up_write(&md->lock);
+       return wait_event_interruptible(md->eventq,
+                       (event_nr != atomic_read(&md->event_nr)));
 }
 
 /*
@@ -1099,7 +1158,7 @@ int dm_suspended(struct mapped_device *md)
        return test_bit(DMF_SUSPENDED, &md->flags);
 }
 
-struct block_device_operations dm_blk_dops = {
+static struct block_device_operations dm_blk_dops = {
        .open = dm_blk_open,
        .release = dm_blk_close,
        .owner = THIS_MODULE
@@ -1114,5 +1173,5 @@ module_exit(dm_exit);
 module_param(major, uint, 0);
 MODULE_PARM_DESC(major, "The major number of the device mapper");
 MODULE_DESCRIPTION(DM_NAME " driver");
-MODULE_AUTHOR("Joe Thornber <thornber@sistina.com>");
+MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
 MODULE_LICENSE("GPL");