Linux-Block Archive on lore.kernel.org
 help / Atom feed
* [PATCH] block: refactor register_blkdev() to compare major number when allocating unused major number
@ 2019-02-11  3:26 Chengguang Xu
  0 siblings, 0 replies; 1+ messages in thread
From: Chengguang Xu @ 2019-02-11  3:26 UTC (permalink / raw)
  To: axboe; +Cc: linux-block, Chengguang Xu

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

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


^ permalink raw reply	[flat|nested] 1+ messages in thread

only message in thread, back to index

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-11  3:26 [PATCH] block: refactor register_blkdev() to compare major number when allocating unused major number Chengguang Xu

Linux-Block Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-block/0 linux-block/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-block linux-block/ https://lore.kernel.org/linux-block \
		linux-block@vger.kernel.org linux-block@archiver.kernel.org
	public-inbox-index linux-block


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-block


AGPL code for this site: git clone https://public-inbox.org/ public-inbox