Remove the limitation of 1024 DM devices. Each time the minor_bits array fills up, allocate a new minor_bits array double the size of the existing one. --- diff/drivers/md/dm.c 2004-06-21 16:17:16.000000000 -0500 +++ source/drivers/md/dm.c 2004-06-21 17:37:56.407152088 -0500 @@ -17,11 +17,13 @@ #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 unsigned long *_minor_bits = NULL; +static int realloc_minor_bits(void); + /* * One of these is allocated per bio. */ @@ -111,11 +113,19 @@ return -ENOMEM; } + r = realloc_minor_bits(); + 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); + kfree(_minor_bits); return r; } @@ -129,6 +139,7 @@ { kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_io_cache); + kfree(_minor_bits); if (unregister_blkdev(_major, _name) < 0) DMERR("devfs_unregister_blkdev failed"); @@ -615,14 +626,48 @@ /*----------------------------------------------------------------- * 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 _max_devices = 0; + +static int realloc_minor_bits(void) +{ + unsigned long max_devices; + unsigned long *minor_bits, *tmp; + + /* Start with 1024 bits, then keep doubling the size of + * the bit-set. Can't go beyond 2^20 devices. + */ + max_devices = (_max_devices) ? (_max_devices * 2) : 1024; + if (max_devices > (1 << MINORBITS)) + return -EINVAL; + + minor_bits = kmalloc((max_devices / BITS_PER_LONG) * + sizeof(unsigned long), GFP_KERNEL); + if (!minor_bits) + return -ENOMEM; + memset(minor_bits, 0, + (max_devices / BITS_PER_LONG) * sizeof(unsigned long)); + + /* Copy the existing bit-set to the new one. */ + if (_minor_bits) + memcpy(minor_bits, _minor_bits, + ((_max_devices / BITS_PER_LONG) * sizeof(unsigned long))); + + tmp = _minor_bits; + _minor_bits = minor_bits; + _max_devices = max_devices; + kfree(tmp); + + DMINFO("Allocated new minor_bits array for %lu devices", _max_devices); + return 0; +} 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_devices) + clear_bit(minor, _minor_bits); + up(&_minor_lock); } /* @@ -630,37 +675,46 @@ */ 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); - return -EINVAL; + down(&_minor_lock); + while (minor >= _max_devices) { + r = realloc_minor_bits(); + if (r) { + DMWARN("request for a mapped_device beyond " + "max_devices (%lu)", _max_devices); + 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); + while ((m = find_first_zero_bit(_minor_bits, _max_devices)) + >= _max_devices) { + r = realloc_minor_bits(); + if (r) { + up(&_minor_lock); + return r; + } } - 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;