Skip to content

Commit

Permalink
loop: replace linked list of allocated devices with an idr index
Browse files Browse the repository at this point in the history
Replace the linked list, that keeps track of allocated devices, with an
idr index to allow a more efficient lookup of devices.

Cc: Tejun Heo <tj@kernel.org>
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
  • Loading branch information
Kay Sievers authored and Jens Axboe committed Jul 31, 2011
1 parent aa387cc commit 34dd82a
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 73 deletions.
152 changes: 80 additions & 72 deletions drivers/block/loop.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,8 @@

#include <asm/uaccess.h>

static LIST_HEAD(loop_devices);
static DEFINE_MUTEX(loop_devices_mutex);
static DEFINE_IDR(loop_index_idr);
static DEFINE_MUTEX(loop_index_mutex);

static int max_part;
static int part_shift;
Expand Down Expand Up @@ -722,17 +722,10 @@ static inline int is_loop_device(struct file *file)
static ssize_t loop_attr_show(struct device *dev, char *page,
ssize_t (*callback)(struct loop_device *, char *))
{
struct loop_device *l, *lo = NULL;

mutex_lock(&loop_devices_mutex);
list_for_each_entry(l, &loop_devices, lo_list)
if (disk_to_dev(l->lo_disk) == dev) {
lo = l;
break;
}
mutex_unlock(&loop_devices_mutex);
struct gendisk *disk = dev_to_disk(dev);
struct loop_device *lo = disk->private_data;

return lo ? callback(lo, page) : -EIO;
return callback(lo, page);
}

#define LOOP_ATTR_RO(_name) \
Expand Down Expand Up @@ -1557,40 +1550,64 @@ int loop_register_transfer(struct loop_func_table *funcs)
return 0;
}

static int unregister_transfer_cb(int id, void *ptr, void *data)
{
struct loop_device *lo = ptr;
struct loop_func_table *xfer = data;

mutex_lock(&lo->lo_ctl_mutex);
if (lo->lo_encryption == xfer)
loop_release_xfer(lo);
mutex_unlock(&lo->lo_ctl_mutex);
return 0;
}

int loop_unregister_transfer(int number)
{
unsigned int n = number;
struct loop_device *lo;
struct loop_func_table *xfer;

if (n == 0 || n >= MAX_LO_CRYPT || (xfer = xfer_funcs[n]) == NULL)
return -EINVAL;

xfer_funcs[n] = NULL;

list_for_each_entry(lo, &loop_devices, lo_list) {
mutex_lock(&lo->lo_ctl_mutex);

if (lo->lo_encryption == xfer)
loop_release_xfer(lo);

mutex_unlock(&lo->lo_ctl_mutex);
}

idr_for_each(&loop_index_idr, &unregister_transfer_cb, xfer);
return 0;
}

EXPORT_SYMBOL(loop_register_transfer);
EXPORT_SYMBOL(loop_unregister_transfer);

static struct loop_device *loop_alloc(int i)
static int loop_add(struct loop_device **l, int i)
{
struct loop_device *lo;
struct gendisk *disk;
int err;

lo = kzalloc(sizeof(*lo), GFP_KERNEL);
if (!lo)
if (!lo) {
err = -ENOMEM;
goto out;
}

err = idr_pre_get(&loop_index_idr, GFP_KERNEL);
if (err < 0)
goto out_free_dev;

if (i >= 0) {
int m;

/* create specific i in the index */
err = idr_get_new_above(&loop_index_idr, lo, i, &m);
if (err >= 0 && i != m) {
idr_remove(&loop_index_idr, m);
err = -EEXIST;
}
} else {
err = -EINVAL;
}
if (err < 0)
goto out_free_dev;

lo->lo_queue = blk_alloc_queue(GFP_KERNEL);
if (!lo->lo_queue)
Expand All @@ -1611,56 +1628,54 @@ static struct loop_device *loop_alloc(int i)
disk->private_data = lo;
disk->queue = lo->lo_queue;
sprintf(disk->disk_name, "loop%d", i);
return lo;
add_disk(disk);
*l = lo;
return lo->lo_number;

out_free_queue:
blk_cleanup_queue(lo->lo_queue);
out_free_dev:
kfree(lo);
out:
return NULL;
return err;
}

static void loop_free(struct loop_device *lo)
static void loop_remove(struct loop_device *lo)
{
del_gendisk(lo->lo_disk);
blk_cleanup_queue(lo->lo_queue);
put_disk(lo->lo_disk);
list_del(&lo->lo_list);
kfree(lo);
}

static struct loop_device *loop_init_one(int i)
static int loop_lookup(struct loop_device **l, int i)
{
struct loop_device *lo;
int ret = -ENODEV;

list_for_each_entry(lo, &loop_devices, lo_list) {
if (lo->lo_number == i)
return lo;
}

lo = loop_alloc(i);
lo = idr_find(&loop_index_idr, i);
if (lo) {
add_disk(lo->lo_disk);
list_add_tail(&lo->lo_list, &loop_devices);
*l = lo;
ret = lo->lo_number;
}
return lo;
}

static void loop_del_one(struct loop_device *lo)
{
del_gendisk(lo->lo_disk);
loop_free(lo);
return ret;
}

static struct kobject *loop_probe(dev_t dev, int *part, void *data)
{
struct loop_device *lo;
struct kobject *kobj;
int err;

mutex_lock(&loop_devices_mutex);
lo = loop_init_one(MINOR(dev) >> part_shift);
kobj = lo ? get_disk(lo->lo_disk) : ERR_PTR(-ENOMEM);
mutex_unlock(&loop_devices_mutex);
mutex_lock(&loop_index_mutex);
err = loop_lookup(&lo, MINOR(dev) >> part_shift);
if (err < 0)
err = loop_add(&lo, MINOR(dev) >> part_shift);
if (err < 0)
kobj = ERR_PTR(err);
else
kobj = get_disk(lo->lo_disk);
mutex_unlock(&loop_index_mutex);

*part = 0;
return kobj;
Expand All @@ -1670,7 +1685,7 @@ static int __init loop_init(void)
{
int i, nr;
unsigned long range;
struct loop_device *lo, *next;
struct loop_device *lo;

/*
* loop module now has a feature to instantiate underlying device
Expand Down Expand Up @@ -1719,43 +1734,36 @@ static int __init loop_init(void)
if (register_blkdev(LOOP_MAJOR, "loop"))
return -EIO;

for (i = 0; i < nr; i++) {
lo = loop_alloc(i);
if (!lo)
goto Enomem;
list_add_tail(&lo->lo_list, &loop_devices);
}

/* point of no return */

list_for_each_entry(lo, &loop_devices, lo_list)
add_disk(lo->lo_disk);

blk_register_region(MKDEV(LOOP_MAJOR, 0), range,
THIS_MODULE, loop_probe, NULL, NULL);

/* pre-create number devices of devices given by config or max_loop */
mutex_lock(&loop_index_mutex);
for (i = 0; i < nr; i++)
loop_add(&lo, i);
mutex_unlock(&loop_index_mutex);

printk(KERN_INFO "loop: module loaded\n");
return 0;
}

Enomem:
printk(KERN_INFO "loop: out of memory\n");

list_for_each_entry_safe(lo, next, &loop_devices, lo_list)
loop_free(lo);
static int loop_exit_cb(int id, void *ptr, void *data)
{
struct loop_device *lo = ptr;

unregister_blkdev(LOOP_MAJOR, "loop");
return -ENOMEM;
loop_remove(lo);
return 0;
}

static void __exit loop_exit(void)
{
unsigned long range;
struct loop_device *lo, *next;

range = max_loop ? max_loop << part_shift : 1UL << MINORBITS;

list_for_each_entry_safe(lo, next, &loop_devices, lo_list)
loop_del_one(lo);
idr_for_each(&loop_index_idr, &loop_exit_cb, NULL);
idr_remove_all(&loop_index_idr);
idr_destroy(&loop_index_idr);

blk_unregister_region(MKDEV(LOOP_MAJOR, 0), range);
unregister_blkdev(LOOP_MAJOR, "loop");
Expand Down
1 change: 0 additions & 1 deletion include/linux/loop.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ struct loop_device {

struct request_queue *lo_queue;
struct gendisk *lo_disk;
struct list_head lo_list;
};

#endif /* __KERNEL__ */
Expand Down

0 comments on commit 34dd82a

Please sign in to comment.