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 @@
#include <linux/buffer_head.h>
#include <linux/mempool.h>
#include <linux/slab.h>
#include <linux/idr.h>
static const char *_name = DM_NAME;
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.
*/
......@@ -113,19 +111,11 @@ 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;
}
......@@ -139,7 +129,6 @@ 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");
......@@ -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 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 DEFINE_IDR(_minor_idr);
static void free_minor(unsigned int minor)
{
down(&_minor_lock);
if (minor < _max_minors)
clear_bit(minor, _minor_bits);
idr_remove(&_minor_idr, minor);
up(&_minor_lock);
}
......@@ -700,24 +645,37 @@ static void free_minor(unsigned int minor)
*/
static int specific_minor(unsigned int minor)
{
int r = 0;
int r, m;
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;
}
if (idr_find(&_minor_idr, minor)) {
r = -EBUSY;
goto out;
}
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;
up(&_minor_lock);
goto out;
}
out:
up(&_minor_lock);
return r;
}
......@@ -727,21 +685,29 @@ static int next_free_minor(unsigned int *minor)
unsigned int m;
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);
r = idr_pre_get(&_minor_idr, GFP_KERNEL);
if (!r) {
r = -ENOMEM;
goto out;
}
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;
up(&_minor_lock);
return 0;
out:
up(&_minor_lock);
return r;
}
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