On Monday 21 June 2004 5:57 pm, Kevin Corry wrote: > 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. New version based on feedback from agk. Remove the limitation of 1024 DM devices. --- diff/drivers/md/dm.c 2004-06-29 13:22:25.000000000 -0500 +++ source/drivers/md/dm.c 2004-06-29 14:27:32.000000000 -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 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 @@ 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 @@ { kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_io_cache); + free_minor_bits(); if (unregister_blkdev(_major, _name) < 0) DMERR("devfs_unregister_blkdev failed"); @@ -615,14 +626,75 @@ /*----------------------------------------------------------------- * 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; + +unsigned long pow2_round_up(unsigned long value) +{ + unsigned long mask = 1; + + if ((value & (value - 1)) == 0) + return value; + + value <<= 1; + while (value & (value - 1)) { + value &= ~mask; + mask <<= 1; + } + + return value; +} + +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 = pow2_round_up(requested_minor); + if (max_minors > (1 << MINORBITS)) + return -EINVAL; + + minor_bits = kmalloc((max_minors / BITS_PER_LONG) * + sizeof(unsigned long), GFP_KERNEL); + if (!minor_bits) + return -ENOMEM; + memset(minor_bits, 0, + (max_minors / BITS_PER_LONG) * sizeof(unsigned long)); + + /* Copy the existing bit-set to the new one. */ + if (_minor_bits) + memcpy(minor_bits, _minor_bits, + ((_max_minors / BITS_PER_LONG) * sizeof(unsigned long))); + + 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 +702,48 @@ */ 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; + 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;