VServer 1.9.2 (patch-2.6.8.1-vs1.9.2.diff)
[linux-2.6.git] / drivers / md / dm.c
index 03381e0..01794ed 100644 (file)
 #include <linux/slab.h>
 
 static const char *_name = DM_NAME;
-#define MAX_DEVICES 1024
 
 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.
  */
@@ -111,11 +113,19 @@ 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;
        }
 
@@ -129,6 +139,7 @@ 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");
@@ -394,7 +405,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;
@@ -615,14 +626,58 @@ static int dm_any_congested(void *congested_data, int bdi_bits)
 /*-----------------------------------------------------------------
  * A bitset 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 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 void free_minor(unsigned int minor)
 {
-       spin_lock(&_minor_lock);
-       clear_bit(minor, _minor_bits);
-       spin_unlock(&_minor_lock);
+       down(&_minor_lock);
+       if (minor < _max_minors)
+               clear_bit(minor, _minor_bits);
+       up(&_minor_lock);
 }
 
 /*
@@ -630,37 +685,48 @@ static void free_minor(unsigned int minor)
  */
 static int specific_minor(unsigned int minor)
 {
-       int r = -EBUSY;
+       int r = 0;
 
-       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 (minor >= _max_minors) {
+               r = realloc_minor_bits(minor);
+               if (r) {
+                       up(&_minor_lock);
+                       return r;
+               }
        }
 
-       spin_lock(&_minor_lock);
-       if (!test_and_set_bit(minor, _minor_bits))
-               r = 0;
-       spin_unlock(&_minor_lock);
+       if (test_and_set_bit(minor, _minor_bits))
+               r = -EBUSY;
+       up(&_minor_lock);
 
        return r;
 }
 
 static int next_free_minor(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);
+       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);
        }
-       spin_unlock(&_minor_lock);
 
-       return r;
+       set_bit(m, _minor_bits);
+       *minor = m;
+       up(&_minor_lock);
+
+       return 0;
 }
 
 static struct block_device_operations dm_blk_dops;