X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fmd%2Fdm.c;h=01794ed391a29a2794f4c3d983ad9b4635264567;hb=9bf4aaab3e101692164d49b7ca357651eb691cb6;hp=03381e0036073f7daf4586a2ae71363f5da43944;hpb=db216c3d5e4c040e557a50f8f5d35d5c415e8c1c;p=linux-2.6.git diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 03381e003..01794ed39 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -17,11 +17,13 @@ #include 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;