public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Tzung-Bi Shih <tzungbi@kernel.org>
To: Arnd Bergmann <arnd@arndb.de>,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Benson Leung <bleung@chromium.org>,
	tzungbi@kernel.org, linux-kernel@vger.kernel.org,
	chrome-platform@lists.linux.dev,
	"Rafael J. Wysocki" <rafael@kernel.org>,
	Danilo Krummrich <dakr@kernel.org>,
	Jonathan Corbet <corbet@lwn.net>, Shuah Khan <shuah@kernel.org>,
	Laurent Pinchart <laurent.pinchart@ideasonboard.com>,
	Wolfram Sang <wsa+renesas@sang-engineering.com>,
	Jason Gunthorpe <jgg@nvidia.com>, Johan Hovold <johan@kernel.org>,
	"Paul E . McKenney" <paulmck@kernel.org>,
	Dan Williams <dan.j.williams@intel.com>
Subject: [PATCH 4/8] char: misc: Use SRCU to protect list traversal
Date: Mon, 27 Apr 2026 21:46:55 +0800	[thread overview]
Message-ID: <20260427134659.95181-5-tzungbi@kernel.org> (raw)
In-Reply-To: <20260427134659.95181-1-tzungbi@kernel.org>

Replace the global mutex with SRCU to protect list traversal in the
file operations.  This allows concurrent file operations on misc devices
without contending for a global lock.

Writers (registration and deregistration) continue to be serialized
by a mutex when modifying the lists.

Signed-off-by: Tzung-Bi Shih <tzungbi@kernel.org>
---
 drivers/char/misc.c | 84 ++++++++++++++++++++++++++-------------------
 1 file changed, 48 insertions(+), 36 deletions(-)

diff --git a/drivers/char/misc.c b/drivers/char/misc.c
index 87f47bdc7afb..8514287b8ff9 100644
--- a/drivers/char/misc.c
+++ b/drivers/char/misc.c
@@ -45,6 +45,7 @@
 #include <linux/mutex.h>
 #include <linux/proc_fs.h>
 #include <linux/seq_file.h>
+#include <linux/srcu.h>
 #include <linux/stat.h>
 #include <linux/init.h>
 #include <linux/device.h>
@@ -58,6 +59,7 @@
 static LIST_HEAD(misc_list);
 static LIST_HEAD(misc_sync_ctx_list);
 static DEFINE_MUTEX(misc_mtx);
+DEFINE_STATIC_SRCU(misc_srcu);
 
 /*
  * Assigned numbers.
@@ -86,23 +88,23 @@ static void misc_minor_free(int minor)
 #ifdef CONFIG_PROC_FS
 static void *misc_seq_start(struct seq_file *seq, loff_t *pos)
 {
-	mutex_lock(&misc_mtx);
-	return seq_list_start(&misc_list, *pos);
+	seq->private = (void *)(long)srcu_read_lock(&misc_srcu);
+	return seq_list_start_rcu(&misc_list, *pos);
 }
 
 static void *misc_seq_next(struct seq_file *seq, void *v, loff_t *pos)
 {
-	return seq_list_next(v, &misc_list, pos);
+	return seq_list_next_rcu(v, &misc_list, pos);
 }
 
 static void misc_seq_stop(struct seq_file *seq, void *v)
 {
-	mutex_unlock(&misc_mtx);
+	srcu_read_unlock(&misc_srcu, (int)(long)seq->private);
 }
 
 static int misc_seq_show(struct seq_file *seq, void *v)
 {
-	const struct miscdevice *p = list_entry(v, struct miscdevice, list);
+	const struct miscdevice *p = list_entry_rcu(v, struct miscdevice, list);
 
 	seq_printf(seq, "%3i %s\n", p->minor, p->name ? p->name : "");
 	return 0;
@@ -121,9 +123,8 @@ static struct miscdevice *misc_find(int minor)
 {
 	struct miscdevice *iter;
 
-	lockdep_assert_held(&misc_mtx);
-
-	list_for_each_entry(iter, &misc_list, list) {
+	list_for_each_entry_srcu(iter, &misc_list, list,
+				 srcu_read_lock_held(&misc_srcu)) {
 		if (iter->minor == minor)
 			return iter;
 	}
@@ -136,7 +137,7 @@ static struct miscdevice *misc_find(int minor)
 	{								\
 		struct miscdevice *c;					\
 									\
-		guard(mutex)(&misc_mtx);				\
+		guard(srcu)(&misc_srcu);				\
 									\
 		c = misc_find(iminor(filp->f_inode));			\
 		if (!c)							\
@@ -160,7 +161,9 @@ static void misc_sync_ctx_release(struct kref *kref)
 	struct miscdevice_sync_ctx *ctx = container_of(kref, typeof(*ctx), kref);
 
 	misc_minor_free(ctx->minor);
-	list_del(&ctx->list);
+	scoped_guard(mutex, &misc_mtx)
+		list_del_rcu(&ctx->list);
+	synchronize_srcu(&misc_srcu);
 	kfree(ctx);
 }
 
@@ -170,23 +173,24 @@ static int misc_sync_release(struct inode *inode, struct file *filp)
 	struct miscdevice *c;
 	struct miscdevice_sync_ctx *iter, *ctx = NULL;
 
-	guard(mutex)(&misc_mtx);
-
-	c = misc_find(minor);
-	if (c) {
-		/* The miscdevice is still registered. */
-		ctx = c->sync_ctx;
-	} else {
-		/* The miscdeivce is unregistered.  Search in the list. */
-		list_for_each_entry(iter, &misc_sync_ctx_list, list) {
-			if (iter->minor == minor) {
-				ctx = iter;
-				break;
+	scoped_guard(srcu, &misc_srcu) {
+		c = misc_find(minor);
+		if (c) {
+			/* The miscdevice is still registered. */
+			ctx = c->sync_ctx;
+		} else {
+			/* The miscdeivce is unregistered.  Search in the list. */
+			list_for_each_entry_srcu(iter, &misc_sync_ctx_list,
+					list, srcu_read_lock_held(&misc_srcu)) {
+				if (iter->minor == minor) {
+					ctx = iter;
+					break;
+				}
+			}
+			if (!ctx) {
+				pr_err("Cannot find miscdevice_sync_ctx\n");
+				return -ENOENT;
 			}
-		}
-		if (!ctx) {
-			pr_err("Cannot find miscdevice_sync_ctx\n");
-			return -ENOENT;
 		}
 	}
 
@@ -206,8 +210,9 @@ static int misc_open(struct inode *inode, struct file *file)
 	struct miscdevice *c = NULL;
 	int err = -ENODEV;
 	const struct file_operations *new_fops = NULL;
+	int idx;
 
-	mutex_lock(&misc_mtx);
+	idx = srcu_read_lock(&misc_srcu);
 
 	c = misc_find(minor);
 	if (c)
@@ -215,9 +220,9 @@ static int misc_open(struct inode *inode, struct file *file)
 
 	/* Only request module for fixed minor code */
 	if (!new_fops && minor < MISC_DYNAMIC_MINOR) {
-		mutex_unlock(&misc_mtx);
+		srcu_read_unlock(&misc_srcu, idx);
 		request_module("char-major-%d-%d", MISC_MAJOR, minor);
-		mutex_lock(&misc_mtx);
+		idx = srcu_read_lock(&misc_srcu);
 
 		c = misc_find(minor);
 		if (c)
@@ -243,7 +248,7 @@ static int misc_open(struct inode *inode, struct file *file)
 	if (file->f_op->open)
 		err = file->f_op->open(inode, file);
 fail:
-	mutex_unlock(&misc_mtx);
+	srcu_read_unlock(&misc_srcu, idx);
 	return err;
 }
 
@@ -311,8 +316,10 @@ int misc_register(struct miscdevice *misc)
 	} else {
 		int i;
 
-		if (misc_find(misc->minor))
-			return -EBUSY;
+		scoped_guard(srcu, &misc_srcu) {
+			if (misc_find(misc->minor))
+				return -EBUSY;
+		}
 
 		i = misc_minor_alloc(misc->minor);
 		if (i < 0)
@@ -336,7 +343,7 @@ int misc_register(struct miscdevice *misc)
 	 * Add it to the front, so that later devices can "override"
 	 * earlier defaults
 	 */
-	list_add(&misc->list, &misc_list);
+	list_add_rcu(&misc->list, &misc_list);
 	return 0;
 }
 EXPORT_SYMBOL(misc_register);
@@ -351,15 +358,20 @@ EXPORT_SYMBOL(misc_register);
 
 void misc_deregister(struct miscdevice *misc)
 {
-	guard(mutex)(&misc_mtx);
-	list_del_init(&misc->list);
+	scoped_guard(mutex, &misc_mtx)
+		list_del_rcu(&misc->list);
+	synchronize_srcu(&misc_srcu);
+	INIT_LIST_HEAD(&misc->list);
+
 	device_destroy(&misc_class, MKDEV(MISC_MAJOR, misc->minor));
 
 	/* Defer to free the minor number for sync fops */
 	if (!misc->sync_ctx) {
 		misc_minor_free(misc->minor);
 	} else {
-		list_add(&misc->sync_ctx->list, &misc_sync_ctx_list);
+		scoped_guard(mutex, &misc_mtx)
+			list_add_rcu(&misc->sync_ctx->list,
+				     &misc_sync_ctx_list);
 		kref_put(&misc->sync_ctx->kref, misc_sync_ctx_release);
 	}
 
-- 
2.51.0


  parent reply	other threads:[~2026-04-27 13:47 UTC|newest]

Thread overview: 10+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-27 13:46 [PATCH 0/8] char: misc: Introduce misc_sync to fix UAF Tzung-Bi Shih
2026-04-27 13:46 ` [PATCH 1/8] char: misc: Simplify locking with guard() Tzung-Bi Shih
2026-04-27 13:46 ` [PATCH 2/8] char: misc: Introduce misc_find() helper Tzung-Bi Shih
2026-04-27 13:46 ` [PATCH 3/8] char: misc: Introduce misc_sync_register() Tzung-Bi Shih
2026-04-28 16:09   ` Jason Gunthorpe
2026-04-27 13:46 ` Tzung-Bi Shih [this message]
2026-04-27 13:46 ` [PATCH 5/8] platform/chrome: cros_ec_chardev: Introduce chardev_data Tzung-Bi Shih
2026-04-27 13:46 ` [PATCH 6/8] platform/chrome: cros_ec_chardev: Move data to chardev_pdata Tzung-Bi Shih
2026-04-27 13:46 ` [PATCH 7/8] platform/chrome: cros_ec_chardev: Add event relayer Tzung-Bi Shih
2026-04-27 13:46 ` [PATCH 8/8] platform/chrome: cros_ec_chardev: Use misc_sync_register() Tzung-Bi Shih

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260427134659.95181-5-tzungbi@kernel.org \
    --to=tzungbi@kernel.org \
    --cc=arnd@arndb.de \
    --cc=bleung@chromium.org \
    --cc=chrome-platform@lists.linux.dev \
    --cc=corbet@lwn.net \
    --cc=dakr@kernel.org \
    --cc=dan.j.williams@intel.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=jgg@nvidia.com \
    --cc=johan@kernel.org \
    --cc=laurent.pinchart@ideasonboard.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=paulmck@kernel.org \
    --cc=rafael@kernel.org \
    --cc=shuah@kernel.org \
    --cc=wsa+renesas@sang-engineering.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox