linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: John Ogness <john.ogness@linutronix.de>
To: Petr Mladek <pmladek@suse.com>
Cc: Sergey Senozhatsky <senozhatsky@chromium.org>,
	Steven Rostedt <rostedt@goodmis.org>,
	Thomas Gleixner <tglx@linutronix.de>,
	linux-kernel@vger.kernel.org,
	Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Subject: [PATCH printk 06/18] printk: Protect [un]register_console() with a mutex
Date: Sat, 24 Sep 2022 02:10:42 +0206	[thread overview]
Message-ID: <20220924000454.3319186-7-john.ogness@linutronix.de> (raw)
In-Reply-To: <20220924000454.3319186-1-john.ogness@linutronix.de>

From: Thomas Gleixner <tglx@linutronix.de>

Unprotected list walks are a brilliant idea. Especially in the context of
hotpluggable consoles.

The new list lock provides not only synchronization for console list
manipulation, but also for manipulation of console->flags:

    console_list_lock();
    console_lock();

    /* may now manipulate the console list and/or console->flags */

    console_unlock();
    console_list_unlock();

Therefore it is safe to iterate the console list and read console->flags
if holding either the console lock or the console list lock.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: John Ogness <john.ogness@linutronix.de>
---
 include/linux/console.h | 30 +++++++++++++--
 kernel/printk/printk.c  | 84 ++++++++++++++++++++++++++++++++++-------
 2 files changed, 98 insertions(+), 16 deletions(-)

diff --git a/include/linux/console.h b/include/linux/console.h
index 8c1686e2c233..24344f9b0bc1 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -157,10 +157,34 @@ struct console {
 	struct	 console *next;
 };
 
-/*
- * for_each_console() allows you to iterate on each console
+#ifdef CONFIG_LOCKDEP
+extern void lockdep_assert_console_list_lock_held(void);
+#else
+static inline void lockdep_assert_console_list_lock_held(void) { }
+#endif
+
+extern void console_list_lock(void) __acquires(console_mutex);
+extern void console_list_unlock(void) __releases(console_mutex);
+
+/**
+ * for_each_registered_console() - Iterator over registered consoles
+ * @con:	struct console pointer used as loop cursor
+ *
+ * Requires console_list_lock to be held. Can only be invoked from
+ * preemptible context.
+ */
+#define for_each_registered_console(con)				\
+	lockdep_assert_console_list_lock_held();			\
+	for (con = console_drivers; con != NULL; con = con->next)
+
+/**
+ * for_each_console() - Iterator over registered consoles
+ * @con:	struct console pointer used as loop cursor
+ *
+ * Requires console_lock to be held which guarantees that the
+ * list is immutable.
  */
-#define for_each_console(con) \
+#define for_each_console(con)						\
 	for (con = console_drivers; con != NULL; con = con->next)
 
 extern int console_set_on_cmdline;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index e4f1e7478b52..8c56f6071873 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -79,10 +79,17 @@ int oops_in_progress;
 EXPORT_SYMBOL(oops_in_progress);
 
 /*
- * console_sem protects the console_drivers list, and also
- * provides serialisation for access to the entire console
- * driver system.
+ * console_sem protects the console_drivers list, and also provides
+ * serialization for access to the entire console driver system.
+ *
+ * console_mutex serializes register/unregister.
+ *
+ * console_sem must be taken inside a console_mutex locked section
+ * for any list manipulation in order to keep the console BKL
+ * machinery happy. This requirement also applies to manipulation
+ * of console->flags.
  */
+static DEFINE_MUTEX(console_mutex);
 static DEFINE_SEMAPHORE(console_sem);
 struct console *console_drivers;
 EXPORT_SYMBOL_GPL(console_drivers);
@@ -103,6 +110,12 @@ static int __read_mostly suppress_panic_printk;
 static struct lockdep_map console_lock_dep_map = {
 	.name = "console_lock"
 };
+
+void lockdep_assert_console_list_lock_held(void)
+{
+	lockdep_assert_held(&console_mutex);
+}
+
 #endif
 
 enum devkmsg_log_bits {
@@ -220,6 +233,28 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write,
 }
 #endif /* CONFIG_PRINTK && CONFIG_SYSCTL */
 
+/**
+ * console_list_lock - Lock the console list
+ *
+ * For non-console related list walks, e.g. procfs, sysfs...
+ */
+void console_list_lock(void)
+{
+	mutex_lock(&console_mutex);
+}
+EXPORT_SYMBOL(console_list_lock);
+
+/**
+ * console_list_unlock - Unlock the console list
+ *
+ * Counterpart to console_list_lock()
+ */
+void console_list_unlock(void)
+{
+	mutex_unlock(&console_mutex);
+}
+EXPORT_SYMBOL(console_list_unlock);
+
 /*
  * Helper macros to handle lockdep when locking/unlocking console_sem. We use
  * macros instead of functions so that _RET_IP_ contains useful information.
@@ -2978,17 +3013,21 @@ struct tty_driver *console_device(int *index)
 void console_stop(struct console *console)
 {
 	__pr_flush(console, 1000, true);
+	console_list_lock();
 	console_lock();
 	console->flags &= ~CON_ENABLED;
 	console_unlock();
+	console_list_unlock();
 }
 EXPORT_SYMBOL(console_stop);
 
 void console_start(struct console *console)
 {
+	console_list_lock();
 	console_lock();
 	console->flags |= CON_ENABLED;
 	console_unlock();
+	console_list_unlock();
 	__pr_flush(console, 1000, true);
 }
 EXPORT_SYMBOL(console_start);
@@ -3081,6 +3120,8 @@ static void try_enable_default_console(struct console *newcon)
 	       (con->flags & CON_BOOT) ? "boot" : "",	\
 	       con->name, con->index, ##__VA_ARGS__)
 
+static int console_unregister_locked(struct console *console);
+
 /*
  * The console driver calls this routine during kernel initialization
  * to register the console printing procedure with printk() and to
@@ -3107,13 +3148,14 @@ void register_console(struct console *newcon)
 	bool realcon_enabled = false;
 	int err;
 
-	for_each_console(con) {
+	console_list_lock();
+	for_each_registered_console(con) {
 		if (WARN(con == newcon, "console '%s%d' already registered\n",
 					 con->name, con->index))
-			return;
+			goto unlock;
 	}
 
-	for_each_console(con) {
+	for_each_registered_console(con) {
 		if (con->flags & CON_BOOT)
 			bootcon_enabled = true;
 		else
@@ -3124,7 +3166,7 @@ void register_console(struct console *newcon)
 	if (newcon->flags & CON_BOOT && realcon_enabled) {
 		pr_info("Too late to register bootconsole %s%d\n",
 			newcon->name, newcon->index);
-		return;
+		goto unlock;
 	}
 
 	/*
@@ -3155,7 +3197,7 @@ void register_console(struct console *newcon)
 
 	/* printk() messages are not printed to the Braille console. */
 	if (err || newcon->flags & CON_BRL)
-		return;
+		goto unlock;
 
 	/*
 	 * If we have a bootconsole, and are switching to a real console,
@@ -3209,14 +3251,17 @@ void register_console(struct console *newcon)
 	if (bootcon_enabled &&
 	    ((newcon->flags & (CON_CONSDEV | CON_BOOT)) == CON_CONSDEV) &&
 	    !keep_bootcon) {
-		for_each_console(con)
+		for_each_console(con) {
 			if (con->flags & CON_BOOT)
-				unregister_console(con);
+				console_unregister_locked(con);
+		}
 	}
+unlock:
+	console_list_unlock();
 }
 EXPORT_SYMBOL(register_console);
 
-int unregister_console(struct console *console)
+static int console_unregister_locked(struct console *console)
 {
 	struct console *con;
 	int res;
@@ -3269,6 +3314,16 @@ int unregister_console(struct console *console)
 
 	return res;
 }
+
+int unregister_console(struct console *console)
+{
+	int res;
+
+	console_list_lock();
+	res = console_unregister_locked(console);
+	console_list_unlock();
+	return res;
+}
 EXPORT_SYMBOL(unregister_console);
 
 /*
@@ -3320,7 +3375,8 @@ static int __init printk_late_init(void)
 	struct console *con;
 	int ret;
 
-	for_each_console(con) {
+	console_list_lock();
+	for_each_registered_console(con) {
 		if (!(con->flags & CON_BOOT))
 			continue;
 
@@ -3337,9 +3393,11 @@ static int __init printk_late_init(void)
 			 */
 			pr_warn("bootconsole [%s%d] uses init memory and must be disabled even before the real one is ready\n",
 				con->name, con->index);
-			unregister_console(con);
+			console_unregister_locked(con);
 		}
 	}
+	console_list_unlock();
+
 	ret = cpuhp_setup_state_nocalls(CPUHP_PRINTK_DEAD, "printk:dead", NULL,
 					console_cpu_notify);
 	WARN_ON(ret < 0);
-- 
2.30.2


  parent reply	other threads:[~2022-09-24  0:05 UTC|newest]

Thread overview: 75+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-09-24  0:04 [PATCH printk 00/18] preparation for threaded/atomic printing John Ogness
2022-09-24  0:04 ` [PATCH printk 01/18] printk: Make pr_flush() static John Ogness
2022-09-26 14:12   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 02/18] printk: Declare log_wait properly John Ogness
2022-09-26 14:22   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 03/18] printk: Remove write only variable nr_ext_console_drivers John Ogness
2022-09-26 14:25   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 04/18] printk: Remove bogus comment vs. boot consoles John Ogness
2022-09-26 14:26   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 05/18] printk: Mark __printk percpu data ready __ro_after_init John Ogness
2022-09-26 14:27   ` Petr Mladek
2022-09-24  0:04 ` John Ogness [this message]
2022-09-24  9:31   ` [PATCH printk 06/18] printk: Protect [un]register_console() with a mutex Sergey Senozhatsky
2022-09-27 15:16   ` Petr Mladek
2022-09-28  9:46     ` Sergey Senozhatsky
2022-09-28 23:42     ` John Ogness
2022-09-29 15:43       ` Petr Mladek
2022-09-30  9:24         ` Petr Mladek
2022-09-30 14:16           ` John Ogness
2022-09-30 18:04             ` Petr Mladek
2022-09-30 20:26               ` John Ogness
2022-10-03 14:37                 ` Petr Mladek
2022-10-03 19:35                   ` John Ogness
2022-10-04  2:06                     ` Sergey Senozhatsky
2022-10-04  7:28                     ` Petr Mladek
2022-09-30 13:30         ` John Ogness
2022-09-24  0:04 ` [PATCH printk 07/18] printk: Convert console list walks for readers to list lock John Ogness
2022-09-27 14:07   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 08/18] parisc: Put console abuse into one place John Ogness
2022-09-24  0:20   ` Steven Rostedt
2022-09-30  7:54   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 09/18] serial: kgdboc: Lock console list in probe function John Ogness
2022-09-28 23:32   ` Doug Anderson
2022-09-30  8:07   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 10/18] kgbd: Pretend that console list walk is safe John Ogness
2022-09-26  9:33   ` Aaron Tomlin
2022-09-28 23:32   ` Doug Anderson
2022-09-30  8:39     ` Petr Mladek
2022-09-30 13:44       ` John Ogness
2022-09-30 17:27         ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 11/18] printk: Convert console_drivers list to hlist John Ogness
2022-09-24 10:53   ` Sergey Senozhatsky
2022-09-24 17:20   ` Helge Deller
2022-09-25  0:43     ` Sergey Senozhatsky
2022-09-24 17:27   ` Helge Deller
2022-09-30 14:20   ` Petr Mladek
2022-09-30 16:53     ` Helge Deller
2022-09-30 19:46       ` John Ogness
2022-09-30 22:41         ` Helge Deller
2022-09-24  0:04 ` [PATCH printk 12/18] printk: Prepare for SCRU console list protection John Ogness
2022-09-24 10:58   ` Sergey Senozhatsky
2022-09-24  0:04 ` [PATCH printk 13/18] printk: Move buffer size defines John Ogness
2022-09-24 11:01   ` Sergey Senozhatsky
2022-10-07  9:11   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 14/18] printk: Document struct console John Ogness
2022-09-24 11:08   ` Sergey Senozhatsky
2022-10-07 11:57   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 15/18] printk: Add struct cons_text_buf John Ogness
2022-09-24 11:09   ` Sergey Senozhatsky
2022-10-07 15:15   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 16/18] printk: Use " John Ogness
2022-09-24 11:34   ` Sergey Senozhatsky
2022-10-10 10:11   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 17/18] printk: Use an output descriptor struct for emit John Ogness
2022-10-10 15:40   ` Petr Mladek
2022-09-24  0:04 ` [PATCH printk 18/18] printk: Handle dropped message smarter John Ogness
2022-09-26  4:19   ` Sergey Senozhatsky
2022-09-26  7:54     ` John Ogness
2022-09-26  9:18       ` Sergey Senozhatsky
2022-10-10 16:07       ` Petr Mladek
2022-09-26  9:22   ` Sergey Senozhatsky
2022-09-24  6:44 ` [PATCH printk 00/18] preparation for threaded/atomic printing Greg Kroah-Hartman
2022-09-25 15:23   ` John Ogness
2022-09-24  9:47 ` Sergey Senozhatsky
2022-09-29 16:33 ` Petr Mladek

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=20220924000454.3319186-7-john.ogness@linutronix.de \
    --to=john.ogness@linutronix.de \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=pmladek@suse.com \
    --cc=rostedt@goodmis.org \
    --cc=senozhatsky@chromium.org \
    --cc=tglx@linutronix.de \
    /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;
as well as URLs for NNTP newsgroup(s).