#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.
*/
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;
}
{
kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache);
- free_minor_bits();
if (unregister_blkdev(_major, _name) < 0)
DMERR("devfs_unregister_blkdev failed");
return 0;
}
+static int dm_flush_all(request_queue_t *q, struct gendisk *disk,
+ sector_t *error_sector)
+{
+ struct mapped_device *md = q->queuedata;
+ struct dm_table *map = dm_get_table(md);
+ int ret = -ENXIO;
+
+ if (map) {
+ ret = dm_table_flush_all(md->map);
+ dm_table_put(map);
+ }
+
+ return ret;
+}
+
static void dm_unplug_all(request_queue_t *q)
{
struct mapped_device *md = q->queuedata;
}
/*-----------------------------------------------------------------
- * 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);
}
*/
static int specific_minor(unsigned int minor)
{
- int r = 0;
+ int r, m;
- if (minor > (1 << MINORBITS))
+ 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;
+ }
+
+ 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 (test_and_set_bit(minor, _minor_bits))
+ if (m != minor) {
+ idr_remove(&_minor_idr, m);
r = -EBUSY;
- up(&_minor_lock);
+ goto out;
+ }
+out:
+ up(&_minor_lock);
return r;
}
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;
md->queue->backing_dev_info.congested_data = md;
blk_queue_make_request(md->queue, dm_request);
md->queue->unplug_fn = dm_unplug_all;
+ md->queue->issue_flush_fn = dm_flush_all;
md->io_pool = mempool_create(MIN_IOS, mempool_alloc_slab,
mempool_free_slab, _io_cache);