2012-10-20 18:32:28

by Hannes Frederic Sowa

[permalink] [raw]
Subject: [PATCH 0/4] brd: sanity checks and dynamic ram disk creation/destroying

Hi!

While toying around with a library which needs small block devices for
quorum management, I came up with these patches to dynamically create
and destroy ram disks (I wanted to have non-swappable backed devices).

Feedback is appreciated!

Change log (since RFC patch <[email protected]>):
1) module parameters(rd_nr, rd_size, max_part) are now unsigned ints
2) check for active users of ram disk before BLKFLSBUFing
3) switched from lists to idrs for device management

The patches apply against v3.6.

Documentation/ioctl/ioctl-number.txt | 1 +
block/partition-generic.c | 1 +
drivers/block/brd.c | 360 +++++++++++++++++++++++++++++------
include/linux/Kbuild | 1 +
include/linux/brd.h | 12 ++
include/linux/miscdevice.h | 1 +
6 files changed, 313 insertions(+), 63 deletions(-)

Greetings,

Hannes


2012-10-20 18:32:31

by Hannes Frederic Sowa

[permalink] [raw]
Subject: [PATCH 4/4] brd: add miscdevice to control creation and deletion of ram disks

This patch adds the miscdevice /dev/brd-control with two ioctls:

1) BRD_CTL_ADD: Instantiates a new ram disk with a given size as
parameter. This parameter is filled in with the new minor id
on return.

2) BRD_CTL_DEL: Deletes a ram disk. Takes the minor id as parameter.

Cc: Nick Piggin <[email protected]>
Signed-off-by: Hannes Frederic Sowa <[email protected]>
---
Documentation/ioctl/ioctl-number.txt | 1 +
drivers/block/brd.c | 170 ++++++++++++++++++++++++++++++++---
include/linux/Kbuild | 1 +
include/linux/brd.h | 12 +++
include/linux/miscdevice.h | 1 +
5 files changed, 173 insertions(+), 12 deletions(-)
create mode 100644 include/linux/brd.h

diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
index 849b771..b8827eb 100644
--- a/Documentation/ioctl/ioctl-number.txt
+++ b/Documentation/ioctl/ioctl-number.txt
@@ -326,4 +326,5 @@ Code Seq#(hex) Include File Comments
<mailto:[email protected]>
0xF6 all LTTng Linux Trace Toolkit Next Generation
<mailto:[email protected]>
+0xF7 00-01 drivers/block/brd.c block ram device control driver
0xFD all linux/dm-ioctl.h
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index 1d0016b..5ad25af 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -20,6 +20,9 @@
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/idr.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/brd.h>

#include <asm/uaccess.h>

@@ -478,7 +481,7 @@ __setup("ramdisk_size=", ramdisk_size);
*/
static DEFINE_MUTEX(brd_devices_mutex);

-static struct brd_device *brd_alloc(int i)
+static struct brd_device *brd_alloc(int i, unsigned int size_kb)
{
struct brd_device *brd;
struct gendisk *disk;
@@ -511,7 +514,7 @@ static struct brd_device *brd_alloc(int i)
disk->queue = brd->brd_queue;
disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
sprintf(disk->disk_name, "ram%d", i);
- set_capacity(disk, rd_size * 2);
+ set_capacity(disk, size_kb * 2);

return brd;

@@ -584,7 +587,7 @@ static struct brd_device *brd_new(int id)
err = brd_new_specific_id(id);
if (err < 0)
return ERR_PTR(err);
- brd = brd_alloc(id);
+ brd = brd_alloc(id, rd_size);
if (!brd) {
p = ERR_PTR(-ENOMEM);
goto out;
@@ -605,6 +608,139 @@ out:
return p;
}

+static int brd_control_add(unsigned int size_kb)
+{
+ int val, err;
+ struct brd_device *brd, *p;
+
+ lockdep_assert_held(&brd_devices_mutex);
+
+ val = brd_new_id(0);
+ if (val < 0)
+ return val;
+ if (val >= (MINORMASK >> part_shift)) {
+ err = -EINVAL;
+ goto out;
+ }
+ brd = brd_alloc(val, size_kb);
+ if (!brd) {
+ err = -ENOMEM;
+ goto out;
+ }
+ p = idr_replace(&brd_idr, brd, val);
+ if (IS_ERR(p)) {
+ err = PTR_ERR(p);
+ goto out2;
+ }
+ if (p != DUMMY) {
+ WARN_ON(1);
+ err = -EBUSY;
+ goto out2;
+ }
+ add_disk(brd->brd_disk);
+ return val;
+out2:
+ brd_free(brd);
+out:
+ idr_remove(&brd_idr, val);
+ return err;
+}
+
+static int brd_control_del(unsigned int val)
+{
+ int err = 0;
+ struct brd_device *brd;
+ struct block_device *bdev;
+
+ lockdep_assert_held(&brd_devices_mutex);
+
+ brd = idr_find(&brd_idr, val);
+ if (!brd)
+ return -ENODEV;
+
+ mutex_lock(&brd_mutex);
+ bdev = bdget_disk(brd->brd_disk, 0);
+ if (!bdev) {
+ err = -ENODEV;
+ goto out;
+ }
+ mutex_lock(&bdev->bd_mutex);
+ err = brd_blkdev_in_use(bdev);
+ if (err) {
+ err = -EBUSY;
+ } else {
+ idr_remove(&brd_idr, val);
+ brd_del_one(brd);
+ err = 0;
+ }
+ mutex_unlock(&bdev->bd_mutex);
+out:
+ mutex_unlock(&brd_mutex);
+ return err;
+}
+
+static long brd_control_ioctl(struct file *file, unsigned int cmd,
+ unsigned long param)
+{
+ int err = -ENOSYS;
+ int val;
+ unsigned int size_kb;
+
+ if (rd_nr)
+ return -EINVAL;
+
+ mutex_lock(&brd_devices_mutex);
+ switch (cmd) {
+ case BRD_CTL_ADD:
+ err = get_user(size_kb, (unsigned int __user *)param);
+ if (err < 0)
+ break;
+ val = brd_control_add(size_kb);
+ if (val < 0) {
+ err = val;
+ break;
+ }
+ err = put_user(val << part_shift, (unsigned int __user *)param);
+ if (err < 0) {
+ brd_control_del(val);
+ break;
+ }
+ err = 0;
+ break;
+ case BRD_CTL_DEL:
+ err = get_user(val, (unsigned int __user *)param);
+ if (err < 0)
+ break;
+ if ((val & max_part) != 0) {
+ err = -EINVAL;
+ break;
+ }
+ val >>= part_shift;
+ err = brd_control_del(val);
+ break;
+ }
+ mutex_unlock(&brd_devices_mutex);
+ return err;
+}
+
+static const struct file_operations brd_ctl_fops = {
+ .open = nonseekable_open,
+ .unlocked_ioctl = brd_control_ioctl,
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = brd_control_ioctl,
+#endif
+};
+
+static struct miscdevice brd_misc = {
+ .minor = BRD_CTRL_MINOR,
+ .name = "brd-control",
+ .fops = &brd_ctl_fops,
+};
+MODULE_ALIAS_MISCDEV(BRD_CTRL_MINOR);
+MODULE_ALIAS("devname:brd-control");
+
static int brd_add_disk_idr(int id, void *p, void *data)
{
struct brd_device *brd = (struct brd_device *)p;
@@ -691,14 +827,21 @@ static int __init brd_init(void)
range = 1UL << MINORBITS;
}

- if (register_blkdev(RAMDISK_MAJOR, "ramdisk"))
- return -EIO;
+ err = misc_register(&brd_misc);
+ if (err)
+ return err;
+
+ err = register_blkdev(RAMDISK_MAJOR, "ramdisk");
+ if (err) {
+ err = -EIO;
+ goto out;
+ }

for (i = 0; i < nr; i++) {
brd = brd_new(i);
if (IS_ERR(brd)) {
err = PTR_RET(brd);
- goto out_free;
+ goto out2;
}
}

@@ -712,13 +855,15 @@ static int __init brd_init(void)
printk(KERN_INFO "brd: module loaded\n");
return 0;

-out_free:
+out2:
+ unregister_blkdev(RAMDISK_MAJOR, "ramdisk");
+out:
+ misc_deregister(&brd_misc);
+
idr_for_each(&brd_idr, brd_free_idr, NULL);
idr_remove_all(&brd_idr);
idr_destroy(&brd_idr);

- unregister_blkdev(RAMDISK_MAJOR, "ramdisk");
-
return err;
}

@@ -728,12 +873,13 @@ static void __exit brd_exit(void)

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

+ blk_unregister_region(MKDEV(RAMDISK_MAJOR, 0), range);
+ unregister_blkdev(RAMDISK_MAJOR, "ramdisk");
+ misc_deregister(&brd_misc);
+
idr_for_each(&brd_idr, brd_del_one_idr, NULL);
idr_remove_all(&brd_idr);
idr_destroy(&brd_idr);
-
- blk_unregister_region(MKDEV(RAMDISK_MAJOR, 0), range);
- unregister_blkdev(RAMDISK_MAJOR, "ramdisk");
}

module_init(brd_init);
diff --git a/include/linux/Kbuild b/include/linux/Kbuild
index fa21760..05b0d3f 100644
--- a/include/linux/Kbuild
+++ b/include/linux/Kbuild
@@ -78,6 +78,7 @@ header-y += blk_types.h
header-y += blkpg.h
header-y += blktrace_api.h
header-y += bpqether.h
+header-y += brd.h
header-y += bsg.h
header-y += can.h
header-y += capability.h
diff --git a/include/linux/brd.h b/include/linux/brd.h
new file mode 100644
index 0000000..8a789136
--- /dev/null
+++ b/include/linux/brd.h
@@ -0,0 +1,12 @@
+#ifndef __LINUX_BRD_H__
+#define __LINUX_BRD_H__
+
+#include <linux/ioctl.h>
+
+#define BRD_CTL_IOCTL_MAGIC 0xF7
+
+/* /dev/brd-control ioctl commands */
+#define BRD_CTL_ADD _IOWR(BRD_CTL_IOCTL_MAGIC, 0, unsigned int)
+#define BRD_CTL_DEL _IOR(BRD_CTL_IOCTL_MAGIC, 1, unsigned int)
+
+#endif /* __LINUX_BRD_H__ */
diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h
index e0deeb2..45df076 100644
--- a/include/linux/miscdevice.h
+++ b/include/linux/miscdevice.h
@@ -44,6 +44,7 @@
#define MAPPER_CTRL_MINOR 236
#define LOOP_CTRL_MINOR 237
#define VHOST_NET_MINOR 238
+#define BRD_CTRL_MINOR 239
#define MISC_DYNAMIC_MINOR 255

struct device;
--
1.7.12.4

2012-10-20 18:32:53

by Hannes Frederic Sowa

[permalink] [raw]
Subject: [PATCH 1/4] brd: change module parameters rd_size, rd_nr, max_part to unsigned

All parameters of the module brd are now converted to unsigneds. This
saves some less-than-zero checks.

As the external user of rd_size (arch/arm/kernel/setup.c:setup_ramdisk)
seems to be gone in v3.7-rc1 this variable can later be declared static.

While at it, move the (perhaps automatically) placed brd_mutex out of
the silly constellation between function prelude comment and function.

Cc: Nick Piggin <[email protected]>
Signed-off-by: Hannes Frederic Sowa <[email protected]>
---
drivers/block/brd.c | 44 +++++++++++++++++++++-----------------------
1 file changed, 21 insertions(+), 23 deletions(-)

diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index 531ceb3..c4965f5 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -48,10 +48,11 @@ struct brd_device {
struct radix_tree_root brd_pages;
};

+static DEFINE_MUTEX(brd_mutex);
+
/*
* Look up and return a brd's page for a given sector.
*/
-static DEFINE_MUTEX(brd_mutex);
static struct page *brd_lookup_page(struct brd_device *brd, sector_t sector)
{
pgoff_t idx;
@@ -429,15 +430,15 @@ static const struct block_device_operations brd_fops = {
/*
* And now the modules code and kernel interface.
*/
-static int rd_nr;
-int rd_size = CONFIG_BLK_DEV_RAM_SIZE;
-static int max_part;
-static int part_shift;
-module_param(rd_nr, int, S_IRUGO);
+static unsigned int rd_nr;
+unsigned int rd_size = CONFIG_BLK_DEV_RAM_SIZE;
+static unsigned int max_part;
+static unsigned int part_shift;
+module_param(rd_nr, uint, S_IRUGO);
MODULE_PARM_DESC(rd_nr, "Maximum number of brd devices");
-module_param(rd_size, int, S_IRUGO);
+module_param(rd_size, uint, S_IRUGO);
MODULE_PARM_DESC(rd_size, "Size of each RAM disk in kbytes.");
-module_param(max_part, int, S_IRUGO);
+module_param(max_part, uint, S_IRUGO);
MODULE_PARM_DESC(max_part, "Maximum number of partitions per RAM disk");
MODULE_LICENSE("GPL");
MODULE_ALIAS_BLOCKDEV_MAJOR(RAMDISK_MAJOR);
@@ -447,7 +448,7 @@ MODULE_ALIAS("rd");
/* Legacy boot options - nonmodular */
static int __init ramdisk_size(char *str)
{
- rd_size = simple_strtol(str, NULL, 0);
+ rd_size = simple_strtoul(str, NULL, 0);
return 1;
}
__setup("ramdisk_size=", ramdisk_size);
@@ -555,7 +556,7 @@ static struct kobject *brd_probe(dev_t dev, int *part, void *data)

static int __init brd_init(void)
{
- int i, nr;
+ unsigned int i, nr;
unsigned long range;
struct brd_device *brd, *next;

@@ -574,20 +575,17 @@ static int __init brd_init(void)
* kernel automatically instantiate actual device on-demand.
*/

- part_shift = 0;
- if (max_part > 0) {
- part_shift = fls(max_part);
+ part_shift = fls(max_part);

- /*
- * Adjust max_part according to part_shift as it is exported
- * to user space so that user can decide correct minor number
- * if [s]he want to create more devices.
- *
- * Note that -1 is required because partition 0 is reserved
- * for the whole disk.
- */
- max_part = (1UL << part_shift) - 1;
- }
+ /*
+ * Adjust max_part according to part_shift as it is exported
+ * to user space so that user can decide correct minor number
+ * if [s]he want to create more devices.
+ *
+ * Note that -1 is required because partition 0 is reserved
+ * for the whole disk.
+ */
+ max_part = (1UL << part_shift) - 1;

if ((1UL << part_shift) > DISK_MAX_PARTS)
return -EINVAL;
--
1.7.12.4

2012-10-20 18:32:52

by Hannes Frederic Sowa

[permalink] [raw]
Subject: [PATCH 2/4] brd: check for open partitions when BLKFLSBUFing a ram disk

Users of partitions on ram disks could accidentally flush the block
device with ioctl(BLKFLSBUF) while it is in use. This patch prevents
this from happening.

This patch also adds a call to rescan_partitions after BLKFLSBUFing,
for which a EXPORT_SYMBOL_GPL statement was needed. Otherwise some kind
of use-after-free could happen.

After this change BLKFLSBUFing on partitions of a ram disk is never
possible.

Cc: Nick Piggin <[email protected]>
Signed-off-by: Hannes Frederic Sowa <[email protected]>
---
block/partition-generic.c | 1 +
drivers/block/brd.c | 20 +++++++++++++++++---
2 files changed, 18 insertions(+), 3 deletions(-)

diff --git a/block/partition-generic.c b/block/partition-generic.c
index f1d1451..cd7bc10 100644
--- a/block/partition-generic.c
+++ b/block/partition-generic.c
@@ -528,6 +528,7 @@ rescan:
kfree(state);
return 0;
}
+EXPORT_SYMBOL_GPL(rescan_partitions);

int invalidate_partitions(struct gendisk *disk, struct block_device *bdev)
{
diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index c4965f5..ce1255c 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -385,6 +385,13 @@ static int brd_direct_access(struct block_device *bdev, sector_t sector,
}
#endif

+static int brd_blkdev_in_use(struct block_device *bdev)
+{
+ lockdep_assert_held(&bdev->bd_mutex);
+ return bdev->bd_part_count > 0
+ || bdev->bd_openers > 1;
+}
+
static int brd_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
@@ -399,9 +406,15 @@ static int brd_ioctl(struct block_device *bdev, fmode_t mode,
* release and destroy the ramdisk data.
*/
mutex_lock(&brd_mutex);
+ if (bdget_disk(brd->brd_disk, 0) != bdev) {
+ error = -EBUSY;
+ goto out;
+ }
mutex_lock(&bdev->bd_mutex);
- error = -EBUSY;
- if (bdev->bd_openers <= 1) {
+ error = brd_blkdev_in_use(bdev);
+ if (error) {
+ error = -EBUSY;
+ } else {
/*
* Kill the cache first, so it isn't written back to the
* device.
@@ -411,9 +424,10 @@ static int brd_ioctl(struct block_device *bdev, fmode_t mode,
*/
kill_bdev(bdev);
brd_free_pages(brd);
- error = 0;
+ error = rescan_partitions(brd->brd_disk, bdev);
}
mutex_unlock(&bdev->bd_mutex);
+out:
mutex_unlock(&brd_mutex);

return error;
--
1.7.12.4

2012-10-20 18:32:50

by Hannes Frederic Sowa

[permalink] [raw]
Subject: [PATCH 3/4] brd: replace list with brd_devices by an idr indexed by minor ids

This patch replaces the list of brd_devices with an idr, thus enabling
easier block ram disk creation and deletion.

Cc: Nick Piggin <[email protected]>
Signed-off-by: Hannes Frederic Sowa <[email protected]>
---
drivers/block/brd.c | 136 ++++++++++++++++++++++++++++++++++++++++------------
1 file changed, 106 insertions(+), 30 deletions(-)

diff --git a/drivers/block/brd.c b/drivers/block/brd.c
index ce1255c..1d0016b 100644
--- a/drivers/block/brd.c
+++ b/drivers/block/brd.c
@@ -19,6 +19,7 @@
#include <linux/radix-tree.h>
#include <linux/fs.h>
#include <linux/slab.h>
+#include <linux/idr.h>

#include <asm/uaccess.h>

@@ -34,11 +35,8 @@
* device).
*/
struct brd_device {
- int brd_number;
-
struct request_queue *brd_queue;
struct gendisk *brd_disk;
- struct list_head brd_list;

/*
* Backing store of pages and lock to protect it. This is the contents
@@ -50,6 +48,10 @@ struct brd_device {

static DEFINE_MUTEX(brd_mutex);

+static char dummy = -1;
+#define DUMMY ((struct brd_device *)&dummy)
+static DEFINE_IDR(brd_idr);
+
/*
* Look up and return a brd's page for a given sector.
*/
@@ -458,6 +460,8 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_BLOCKDEV_MAJOR(RAMDISK_MAJOR);
MODULE_ALIAS("rd");

+static struct brd_device *brd_new(int id);
+
#ifndef MODULE
/* Legacy boot options - nonmodular */
static int __init ramdisk_size(char *str)
@@ -472,7 +476,6 @@ __setup("ramdisk_size=", ramdisk_size);
* The device scheme is derived from loop.c. Keep them in synch where possible
* (should share code eventually).
*/
-static LIST_HEAD(brd_devices);
static DEFINE_MUTEX(brd_devices_mutex);

static struct brd_device *brd_alloc(int i)
@@ -483,7 +486,6 @@ static struct brd_device *brd_alloc(int i)
brd = kzalloc(sizeof(*brd), GFP_KERNEL);
if (!brd)
goto out;
- brd->brd_number = i;
spin_lock_init(&brd->brd_lock);
INIT_RADIX_TREE(&brd->brd_pages, GFP_ATOMIC);

@@ -533,27 +535,100 @@ static struct brd_device *brd_init_one(int i)
{
struct brd_device *brd;

- list_for_each_entry(brd, &brd_devices, brd_list) {
- if (brd->brd_number == i)
- goto out;
- }
+ brd = idr_find(&brd_idr, i);
+ if (brd)
+ return brd;

- brd = brd_alloc(i);
- if (brd) {
+ brd = brd_new(i);
+ if (!IS_ERR(brd))
add_disk(brd->brd_disk);
- list_add_tail(&brd->brd_list, &brd_devices);
- }
-out:
return brd;
}

static void brd_del_one(struct brd_device *brd)
{
- list_del(&brd->brd_list);
del_gendisk(brd->brd_disk);
brd_free(brd);
}

+static int brd_new_id(int min)
+{
+ int id, err;
+ err = idr_pre_get(&brd_idr, GFP_KERNEL);
+ if (!err)
+ return -ENOMEM;
+ err = idr_get_new_above(&brd_idr, DUMMY, min, &id);
+ if (err < 0)
+ return err;
+ return id;
+}
+
+static int brd_new_specific_id(int id)
+{
+ int n;
+ n = brd_new_id(id);
+ if (n < 0)
+ return n;
+ if (id != n) {
+ idr_remove(&brd_idr, n);
+ return -EBUSY;
+ }
+ return n;
+}
+
+static struct brd_device *brd_new(int id)
+{
+ int err;
+ struct brd_device *brd, *p;
+
+ err = brd_new_specific_id(id);
+ if (err < 0)
+ return ERR_PTR(err);
+ brd = brd_alloc(id);
+ if (!brd) {
+ p = ERR_PTR(-ENOMEM);
+ goto out;
+ }
+ p = idr_replace(&brd_idr, brd, id);
+ if (IS_ERR(p))
+ goto out2;
+ if (p != DUMMY) {
+ WARN_ON(1);
+ p = ERR_PTR(-EBUSY);
+ goto out2;
+ }
+ return brd;
+out2:
+ brd_free(brd);
+out:
+ idr_remove(&brd_idr, id);
+ return p;
+}
+
+static int brd_add_disk_idr(int id, void *p, void *data)
+{
+ struct brd_device *brd = (struct brd_device *)p;
+ WARN_ON(brd == DUMMY);
+ add_disk(brd->brd_disk);
+ return 0;
+}
+
+static int brd_free_idr(int id, void *p, void *data)
+{
+ struct brd_device *brd = (struct brd_device *)p;
+ WARN_ON(brd == DUMMY);
+ brd_free(brd);
+ return 0;
+}
+
+static int brd_del_one_idr(int id, void *p, void *data)
+{
+ struct brd_device *brd = (struct brd_device *)p;
+ WARN_ON(brd == DUMMY);
+ brd_del_one(brd);
+ return 0;
+}
+
static struct kobject *brd_probe(dev_t dev, int *part, void *data)
{
struct brd_device *brd;
@@ -561,7 +636,7 @@ static struct kobject *brd_probe(dev_t dev, int *part, void *data)

mutex_lock(&brd_devices_mutex);
brd = brd_init_one(MINOR(dev) >> part_shift);
- kobj = brd ? get_disk(brd->brd_disk) : ERR_PTR(-ENOMEM);
+ kobj = IS_ERR(brd) ? ERR_PTR(PTR_ERR(brd)) : get_disk(brd->brd_disk);
mutex_unlock(&brd_devices_mutex);

*part = 0;
@@ -570,9 +645,10 @@ static struct kobject *brd_probe(dev_t dev, int *part, void *data)

static int __init brd_init(void)
{
+ int err;
unsigned int i, nr;
unsigned long range;
- struct brd_device *brd, *next;
+ struct brd_device *brd;

/*
* brd module now has a feature to instantiate underlying device
@@ -619,16 +695,16 @@ static int __init brd_init(void)
return -EIO;

for (i = 0; i < nr; i++) {
- brd = brd_alloc(i);
- if (!brd)
+ brd = brd_new(i);
+ if (IS_ERR(brd)) {
+ err = PTR_RET(brd);
goto out_free;
- list_add_tail(&brd->brd_list, &brd_devices);
+ }
}

/* point of no return */

- list_for_each_entry(brd, &brd_devices, brd_list)
- add_disk(brd->brd_disk);
+ idr_for_each(&brd_idr, brd_add_disk_idr, NULL);

blk_register_region(MKDEV(RAMDISK_MAJOR, 0), range,
THIS_MODULE, brd_probe, NULL, NULL);
@@ -637,24 +713,24 @@ static int __init brd_init(void)
return 0;

out_free:
- list_for_each_entry_safe(brd, next, &brd_devices, brd_list) {
- list_del(&brd->brd_list);
- brd_free(brd);
- }
+ idr_for_each(&brd_idr, brd_free_idr, NULL);
+ idr_remove_all(&brd_idr);
+ idr_destroy(&brd_idr);
+
unregister_blkdev(RAMDISK_MAJOR, "ramdisk");

- return -ENOMEM;
+ return err;
}

static void __exit brd_exit(void)
{
unsigned long range;
- struct brd_device *brd, *next;

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

- list_for_each_entry_safe(brd, next, &brd_devices, brd_list)
- brd_del_one(brd);
+ idr_for_each(&brd_idr, brd_del_one_idr, NULL);
+ idr_remove_all(&brd_idr);
+ idr_destroy(&brd_idr);

blk_unregister_region(MKDEV(RAMDISK_MAJOR, 0), range);
unregister_blkdev(RAMDISK_MAJOR, "ramdisk");
--
1.7.12.4