Commit 83d2e628 authored by Kevin Corry's avatar Kevin Corry Committed by Linus Torvalds

[PATCH] devicemapper: use an IDR tree for tracking minors

Keep track of allocated minor numbers with an IDR instead of a bit-set.
Signed-off-by: default avatarKevin Corry <kevcorry@us.ibm.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent ae144966
...@@ -15,15 +15,13 @@ ...@@ -15,15 +15,13 @@
#include <linux/buffer_head.h> #include <linux/buffer_head.h>
#include <linux/mempool.h> #include <linux/mempool.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/idr.h>
static const char *_name = DM_NAME; static const char *_name = DM_NAME;
static unsigned int major = 0; static unsigned int major = 0;
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. * One of these is allocated per bio.
*/ */
...@@ -113,19 +111,11 @@ static int __init local_init(void) ...@@ -113,19 +111,11 @@ static int __init local_init(void)
return -ENOMEM; return -ENOMEM;
} }
r = realloc_minor_bits(1024);
if (r < 0) {
kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache);
return r;
}
_major = major; _major = major;
r = register_blkdev(_major, _name); r = register_blkdev(_major, _name);
if (r < 0) { if (r < 0) {
kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache); kmem_cache_destroy(_io_cache);
free_minor_bits();
return r; return r;
} }
...@@ -139,7 +129,6 @@ static void local_exit(void) ...@@ -139,7 +129,6 @@ static void local_exit(void)
{ {
kmem_cache_destroy(_tio_cache); kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache); kmem_cache_destroy(_io_cache);
free_minor_bits();
if (unregister_blkdev(_major, _name) < 0) if (unregister_blkdev(_major, _name) < 0)
DMERR("devfs_unregister_blkdev failed"); DMERR("devfs_unregister_blkdev failed");
...@@ -639,59 +628,15 @@ static int dm_any_congested(void *congested_data, int bdi_bits) ...@@ -639,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 DECLARE_MUTEX(_minor_lock);
static unsigned long *_minor_bits = NULL; static DEFINE_IDR(_minor_idr);
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) static void free_minor(unsigned int minor)
{ {
down(&_minor_lock); down(&_minor_lock);
if (minor < _max_minors) idr_remove(&_minor_idr, minor);
clear_bit(minor, _minor_bits);
up(&_minor_lock); up(&_minor_lock);
} }
...@@ -700,24 +645,37 @@ static void free_minor(unsigned int minor) ...@@ -700,24 +645,37 @@ static void free_minor(unsigned int minor)
*/ */
static int specific_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; return -EINVAL;
down(&_minor_lock); down(&_minor_lock);
if (minor >= _max_minors) {
r = realloc_minor_bits(minor); if (idr_find(&_minor_idr, minor)) {
if (r) { r = -EBUSY;
up(&_minor_lock); goto out;
return r;
}
} }
if (test_and_set_bit(minor, _minor_bits)) 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 (m != minor) {
idr_remove(&_minor_idr, m);
r = -EBUSY; r = -EBUSY;
up(&_minor_lock); goto out;
}
out:
up(&_minor_lock);
return r; return r;
} }
...@@ -727,21 +685,29 @@ static int next_free_minor(unsigned int *minor) ...@@ -727,21 +685,29 @@ static int next_free_minor(unsigned int *minor)
unsigned int m; unsigned int m;
down(&_minor_lock); down(&_minor_lock);
m = find_first_zero_bit(_minor_bits, _max_minors);
if (m >= _max_minors) { r = idr_pre_get(&_minor_idr, GFP_KERNEL);
r = realloc_minor_bits(_max_minors * 2); if (!r) {
if (r) { r = -ENOMEM;
up(&_minor_lock); goto out;
return r; }
}
m = find_first_zero_bit(_minor_bits, _max_minors); 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; *minor = m;
up(&_minor_lock);
return 0; out:
up(&_minor_lock);
return r;
} }
static struct block_device_operations dm_blk_dops; static struct block_device_operations dm_blk_dops;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment