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
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
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
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
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