[PATCH] block: refactor register_blkdev() to compare major number when allocating unused major number

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Currently when specifying major number as 0,
register_blkdev() will try to alloc any unused
major number in the range. However, the allocating
logic does not accuretaly compare major number
with existing entries, so even we have plenty of
available major numbers but still might fail with
-EBUSY in extreme case.

Signed-off-by: Chengguang Xu <cgxu519@xxxxxxx>
---
 block/genhd.c | 103 ++++++++++++++++++++++++++------------------------
 1 file changed, 54 insertions(+), 49 deletions(-)

diff --git a/block/genhd.c b/block/genhd.c
index 1dd8fd6613b8..80b788ed17d1 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -330,6 +330,40 @@ void blkdev_show(struct seq_file *seqf, off_t offset)
 }
 #endif /* CONFIG_PROC_FS */
 
+static int __register_blkdev(unsigned int major, const char *name,
+			     struct blk_major_name *new)
+{
+	struct blk_major_name *n, *p = NULL;
+	int index = major_to_index(major);
+
+	for (n = major_names[index]; n; n = n->next) {
+		if (n->major == major)
+			return -EBUSY;
+		p = n;
+	}
+
+	new->major = major;
+	if (p == NULL)
+		major_names[index] = new;
+	else
+		p->next = new;
+
+	return 0;
+}
+
+static int alloc_blkdev(unsigned int major, const char *name,
+			struct blk_major_name *new)
+{
+	int index;
+
+	for (index = ARRAY_SIZE(major_names) - 1; index; index--) {
+		if (__register_blkdev(index, name, new) == 0)
+			return index;
+	}
+
+	return -EBUSY;
+}
+
 /**
  * register_blkdev - register a new block device
  *
@@ -345,73 +379,44 @@ void blkdev_show(struct seq_file *seqf, off_t offset)
  *    then the function returns zero on success, or a negative error code
  *  - if any unused major number was requested with @major = 0 parameter
  *    then the return value is the allocated major number in range
- *    [1..BLKDEV_MAJOR_MAX-1] or a negative error code otherwise
+ *    [1..BLKDEV_MAJOR_HASH_SIZE-1] or a negative error code otherwise
  *
  * See Documentation/admin-guide/devices.txt for the list of allocated
  * major numbers.
  */
 int register_blkdev(unsigned int major, const char *name)
 {
-	struct blk_major_name **n, *p;
-	int index, ret = 0;
-
-	mutex_lock(&block_class_lock);
-
-	/* temporary */
-	if (major == 0) {
-		for (index = ARRAY_SIZE(major_names)-1; index > 0; index--) {
-			if (major_names[index] == NULL)
-				break;
-		}
-
-		if (index == 0) {
-			printk("register_blkdev: failed to get major for %s\n",
-			       name);
-			ret = -EBUSY;
-			goto out;
-		}
-		major = index;
-		ret = major;
-	}
+	struct blk_major_name *new;
+	int ret;
 
 	if (major >= BLKDEV_MAJOR_MAX) {
-		pr_err("register_blkdev: major requested (%u) is greater than the maximum (%u) for %s\n",
-		       major, BLKDEV_MAJOR_MAX-1, name);
-
-		ret = -EINVAL;
-		goto out;
+		pr_err("%s: major requested (%u) is greater than the maximum (%u) for %s\n",
+			__func__, major, BLKDEV_MAJOR_MAX-1, name);
+		return -EINVAL;
 	}
 
-	p = kmalloc(sizeof(struct blk_major_name), GFP_KERNEL);
-	if (p == NULL) {
-		ret = -ENOMEM;
-		goto out;
-	}
+	new = kmalloc(sizeof(struct blk_major_name), GFP_KERNEL);
+	if (new == NULL)
+		return -ENOMEM;
 
-	p->major = major;
-	strlcpy(p->name, name, sizeof(p->name));
-	p->next = NULL;
-	index = major_to_index(major);
+	strlcpy(new->name, name, sizeof(new->name));
+	new->next = NULL;
 
-	for (n = &major_names[index]; *n; n = &(*n)->next) {
-		if ((*n)->major == major)
-			break;
-	}
-	if (!*n)
-		*n = p;
+	mutex_lock(&block_class_lock);
+	if (major == 0)
+		ret = alloc_blkdev(major, name, new);
 	else
-		ret = -EBUSY;
+		ret = __register_blkdev(major, name, new);
+	mutex_unlock(&block_class_lock);
 
 	if (ret < 0) {
-		printk("register_blkdev: cannot get major %u for %s\n",
-		       major, name);
-		kfree(p);
+		kfree(new);
+		pr_err("%s: cannot get major for %s, major requested (%u)\n",
+			__func__, name, major);
 	}
-out:
-	mutex_unlock(&block_class_lock);
+
 	return ret;
 }
-
 EXPORT_SYMBOL(register_blkdev);
 
 void unregister_blkdev(unsigned int major, const char *name)
-- 
2.20.1




[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux