vserver 1.9.3
[linux-2.6.git] / drivers / md / dm.c
index 01794ed..4d3c1d3 100644 (file)
 #include <linux/buffer_head.h>
 #include <linux/mempool.h>
 #include <linux/slab.h>
+#include <linux/idr.h>
 
 static const char *_name = DM_NAME;
 
 static unsigned int major = 0;
 static unsigned int _major = 0;
 
-static int realloc_minor_bits(unsigned long requested_minor);
-static void free_minor_bits(void);
-
 /*
  * One of these is allocated per bio.
  */
@@ -113,19 +111,11 @@ static int __init local_init(void)
                return -ENOMEM;
        }
 
-       r = realloc_minor_bits(1024);
-       if (r < 0) {
-               kmem_cache_destroy(_tio_cache);
-               kmem_cache_destroy(_io_cache);
-               return r;
-       }
-
        _major = major;
        r = register_blkdev(_major, _name);
        if (r < 0) {
                kmem_cache_destroy(_tio_cache);
                kmem_cache_destroy(_io_cache);
-               free_minor_bits();
                return r;
        }
 
@@ -139,7 +129,6 @@ static void local_exit(void)
 {
        kmem_cache_destroy(_tio_cache);
        kmem_cache_destroy(_io_cache);
-       free_minor_bits();
 
        if (unregister_blkdev(_major, _name) < 0)
                DMERR("devfs_unregister_blkdev failed");
@@ -597,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;
@@ -624,59 +628,15 @@ 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 DECLARE_MUTEX(_minor_lock);
-static unsigned long *_minor_bits = NULL;
-static unsigned long _max_minors = 0;
-
-#define MINORS_SIZE(minors) ((minors / BITS_PER_LONG) * sizeof(unsigned long))
-
-static int realloc_minor_bits(unsigned long requested_minor)
-{
-       unsigned long max_minors;
-       unsigned long *minor_bits, *tmp;
-
-       if (requested_minor < _max_minors)
-               return -EINVAL;
-
-       /* Round up the requested minor to the next power-of-2. */
-       max_minors = 1 << fls(requested_minor - 1);
-       if (max_minors > (1 << MINORBITS))
-               return -EINVAL;
-
-       minor_bits = kmalloc(MINORS_SIZE(max_minors), GFP_KERNEL);
-       if (!minor_bits)
-               return -ENOMEM;
-       memset(minor_bits, 0, MINORS_SIZE(max_minors));
-
-       /* Copy the existing bit-set to the new one. */
-       if (_minor_bits)
-               memcpy(minor_bits, _minor_bits, MINORS_SIZE(_max_minors));
-
-       tmp = _minor_bits;
-       _minor_bits = minor_bits;
-       _max_minors = max_minors;
-       if (tmp)
-               kfree(tmp);
-
-       return 0;
-}
-
-static void free_minor_bits(void)
-{
-       down(&_minor_lock);
-       kfree(_minor_bits);
-       _minor_bits = NULL;
-       _max_minors = 0;
-       up(&_minor_lock);
-}
+static DEFINE_IDR(_minor_idr);
 
 static void free_minor(unsigned int minor)
 {
        down(&_minor_lock);
-       if (minor < _max_minors)
-               clear_bit(minor, _minor_bits);
+       idr_remove(&_minor_idr, minor);
        up(&_minor_lock);
 }
 
@@ -685,24 +645,37 @@ static void free_minor(unsigned int minor)
  */
 static int specific_minor(unsigned int minor)
 {
-       int r = 0;
+       int r, m;
 
-       if (minor > (1 << MINORBITS))
+       if (minor >= (1 << MINORBITS))
                return -EINVAL;
 
        down(&_minor_lock);
-       if (minor >= _max_minors) {
-               r = realloc_minor_bits(minor);
-               if (r) {
-                       up(&_minor_lock);
-                       return r;
-               }
+
+       if (idr_find(&_minor_idr, minor)) {
+               r = -EBUSY;
+               goto out;
+       }
+
+       r = idr_pre_get(&_minor_idr, GFP_KERNEL);
+       if (!r) {
+               r = -ENOMEM;
+               goto out;
+       }
+
+       r = idr_get_new_above(&_minor_idr, specific_minor, minor, &m);
+       if (r) {
+               goto out;
        }
 
-       if (test_and_set_bit(minor, _minor_bits))
+       if (m != minor) {
+               idr_remove(&_minor_idr, m);
                r = -EBUSY;
-       up(&_minor_lock);
+               goto out;
+       }
 
+out:
+       up(&_minor_lock);
        return r;
 }
 
@@ -712,21 +685,29 @@ static int next_free_minor(unsigned int *minor)
        unsigned int m;
 
        down(&_minor_lock);
-       m = find_first_zero_bit(_minor_bits, _max_minors);
-       if (m >= _max_minors) {
-               r = realloc_minor_bits(_max_minors * 2);
-               if (r) {
-                       up(&_minor_lock);
-                       return r;
-               }
-               m = find_first_zero_bit(_minor_bits, _max_minors);
+
+       r = idr_pre_get(&_minor_idr, GFP_KERNEL);
+       if (!r) {
+               r = -ENOMEM;
+               goto out;
+       }
+
+       r = idr_get_new(&_minor_idr, next_free_minor, &m);
+       if (r) {
+               goto out;
+       }
+
+       if (m >= (1 << MINORBITS)) {
+               idr_remove(&_minor_idr, m);
+               r = -ENOSPC;
+               goto out;
        }
 
-       set_bit(m, _minor_bits);
        *minor = m;
-       up(&_minor_lock);
 
-       return 0;
+out:
+       up(&_minor_lock);
+       return r;
 }
 
 static struct block_device_operations dm_blk_dops;
@@ -764,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);