block: refactor register_blkdev() to compare major number when allocating unused major number
diff mbox series

Message ID 20190211032619.29208-1-cgxu519@gmx.com
State New
Headers show
Series
  • block: refactor register_blkdev() to compare major number when allocating unused major number
Related show

Commit Message

Chengguang Xu Feb. 11, 2019, 3:26 a.m. UTC
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@gmx.com>
---
 block/genhd.c | 103 ++++++++++++++++++++++++++------------------------
 1 file changed, 54 insertions(+), 49 deletions(-)

Patch
diff mbox series

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)