All of lore.kernel.org
 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 v6 11/40] printk: introduce console_list_lock
Date: Mon, 21 Nov 2022 12:16:12 +0106	[thread overview]
Message-ID: <87sficwokr.fsf@jogness.linutronix.de> (raw)
In-Reply-To: <20221116162152.193147-12-john.ogness@linutronix.de>

Currently there exist races in register_console(), where the types
of registered consoles are checked (without holding the console_lock)
and then after acquiring the console_lock, it is assumed that the list
has not changed. Also, some code that performs console_unregister()
make similar assumptions.

It might be possible to fix these races using the console_lock. But
it would require a complex analysis of all console drivers to make
sure that the console_lock is not taken in match() and setup()
callbacks. And we really prefer to split up and reduce the
responsibilities of console_lock rather than expand its complexity.
Therefore, introduce a new console_list_lock to provide full
synchronization for any console list changes.

In addition, also use console_list_lock for synchronization of
console->flags updates. All flags are either static or modified only
during the console registration. There are only two exceptions.

The first exception is CON_ENABLED, which is also modified by
console_start()/console_stop(). Therefore, these functions must
also take the console_list_lock.

The second exception is when the flags are modified by the console
driver init code before the console is registered. These will be
ignored because they are not visible to the rest of the system
via the console_drivers list.

Note that one of the various responsibilities of the console_lock is
also intended to provide console list and console->flags
synchronization. Later changes will update call sites relying on the
console_lock for these purposes. Once all call sites have been
updated, the console_lock will be relieved of synchronizing
console_list and console->flags updates.

Suggested-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: John Ogness <john.ogness@linutronix.de>
Reviewed-by: Petr Mladek <pmladek@suse.com>
---
 Stephen reported [0] a build failure with linux-next. The problem was a
 missing EXPORT of @lockdep_assert_console_list_lock_held for when
 drivers are built as modules.
 
 [0] https://lore.kernel.org/lkml/20221121110041.1d2c635b@canb.auug.org.au

 include/linux/console.h | 23 +++++++++--
 kernel/printk/printk.c  | 89 ++++++++++++++++++++++++++++++++++++-----
 2 files changed, 100 insertions(+), 12 deletions(-)

diff --git a/include/linux/console.h b/include/linux/console.h
index f4f0c9523835..24d83e24840b 100644
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -158,6 +158,14 @@ struct console {
 	struct hlist_node node;
 };
 
+#ifdef CONFIG_LOCKDEP
+extern void lockdep_assert_console_list_lock_held(void);
+#else
+static inline void lockdep_assert_console_list_lock_held(void)
+{
+}
+#endif
+
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 extern bool console_srcu_read_lock_is_held(void);
 #else
@@ -170,6 +178,9 @@ static inline bool console_srcu_read_lock_is_held(void)
 extern int console_srcu_read_lock(void);
 extern void console_srcu_read_unlock(int cookie);
 
+extern void console_list_lock(void) __acquires(console_mutex);
+extern void console_list_unlock(void) __releases(console_mutex);
+
 extern struct hlist_head console_list;
 
 /**
@@ -186,10 +197,16 @@ extern struct hlist_head console_list;
 	hlist_for_each_entry_srcu(con, &console_list, node,		\
 				  console_srcu_read_lock_is_held())
 
-/*
- * for_each_console() allows you to iterate on each console
+/**
+ * for_each_console() - Iterator over registered consoles
+ * @con:	struct console pointer used as loop cursor
+ *
+ * The console list and the console->flags are immutable while iterating.
+ *
+ * Requires console_list_lock to be held.
  */
-#define for_each_console(con) \
+#define for_each_console(con)						\
+	lockdep_assert_console_list_lock_held();			\
 	hlist_for_each_entry(con, &console_list, node)
 
 extern int console_set_on_cmdline;
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index c84654444a02..2b4506673a86 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -78,6 +78,13 @@ EXPORT_SYMBOL(ignore_console_lock_warning);
 int oops_in_progress;
 EXPORT_SYMBOL(oops_in_progress);
 
+/*
+ * console_mutex protects console_list updates and console->flags updates.
+ * The flags are synchronized only for consoles that are registered, i.e.
+ * accessible via the console list.
+ */
+static DEFINE_MUTEX(console_mutex);
+
 /*
  * console_sem protects console_list and console->flags updates, and also
  * provides serialization for access to the entire console driver system.
@@ -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);
+}
+EXPORT_SYMBOL(lockdep_assert_console_list_lock_held);
 #endif
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
@@ -227,6 +240,40 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write,
 }
 #endif /* CONFIG_PRINTK && CONFIG_SYSCTL */
 
+/**
+ * console_list_lock - Lock the console list
+ *
+ * For console list or console->flags updates
+ */
+void console_list_lock(void)
+{
+	/*
+	 * In unregister_console(), synchronize_srcu() is called with the
+	 * console_list_lock held. Therefore it is not allowed that the
+	 * console_list_lock is taken with the srcu_lock held.
+	 *
+	 * Detecting if this context is really in the read-side critical
+	 * section is only possible if the appropriate debug options are
+	 * enabled.
+	 */
+	WARN_ON_ONCE(debug_lockdep_rcu_enabled() &&
+		     srcu_read_lock_held(&console_srcu));
+
+	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);
+
 /**
  * console_srcu_read_lock - Register a new reader for the
  *	SRCU-protected console list
@@ -3020,9 +3067,11 @@ 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();
 
 	/*
 	 * Ensure that all SRCU list walks have completed. All contexts must
@@ -3036,9 +3085,11 @@ 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);
@@ -3187,6 +3238,8 @@ static void console_init_seq(struct console *newcon, bool bootcon_registered)
 #define console_first()				\
 	hlist_entry(console_list.first, struct console, node)
 
+static int unregister_console_locked(struct console *console);
+
 /*
  * The console driver calls this routine during kernel initialization
  * to register the console printing procedure with printk() and to
@@ -3213,13 +3266,14 @@ void register_console(struct console *newcon)
 	bool realcon_registered = false;
 	int err;
 
+	console_list_lock();
+
 	for_each_console(con) {
 		if (WARN(con == newcon, "console '%s%d' already registered\n",
-					 con->name, con->index))
-			return;
-	}
+					 con->name, con->index)) {
+			goto unlock;
+		}
 
-	for_each_console(con) {
 		if (con->flags & CON_BOOT)
 			bootcon_registered = true;
 		else
@@ -3230,7 +3284,7 @@ void register_console(struct console *newcon)
 	if ((newcon->flags & CON_BOOT) && realcon_registered) {
 		pr_info("Too late to register bootconsole %s%d\n",
 			newcon->name, newcon->index);
-		return;
+		goto unlock;
 	}
 
 	/*
@@ -3261,7 +3315,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,
@@ -3320,16 +3374,21 @@ void register_console(struct console *newcon)
 
 		hlist_for_each_entry_safe(con, tmp, &console_list, node) {
 			if (con->flags & CON_BOOT)
-				unregister_console(con);
+				unregister_console_locked(con);
 		}
 	}
+unlock:
+	console_list_unlock();
 }
 EXPORT_SYMBOL(register_console);
 
-int unregister_console(struct console *console)
+/* Must be called under console_list_lock(). */
+static int unregister_console_locked(struct console *console)
 {
 	int res;
 
+	lockdep_assert_console_list_lock_held();
+
 	con_printk(KERN_INFO, console, "disabled\n");
 
 	res = _braille_unregister_console(console);
@@ -3378,6 +3437,16 @@ int unregister_console(struct console *console)
 
 	return res;
 }
+
+int unregister_console(struct console *console)
+{
+	int res;
+
+	console_list_lock();
+	res = unregister_console_locked(console);
+	console_list_unlock();
+	return res;
+}
 EXPORT_SYMBOL(unregister_console);
 
 /*
@@ -3430,6 +3499,7 @@ static int __init printk_late_init(void)
 	struct console *con;
 	int ret;
 
+	console_list_lock();
 	hlist_for_each_entry_safe(con, tmp, &console_list, node) {
 		if (!(con->flags & CON_BOOT))
 			continue;
@@ -3447,9 +3517,10 @@ 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);
+			unregister_console_locked(con);
 		}
 	}
+	console_list_unlock();
 
 	ret = cpuhp_setup_state_nocalls(CPUHP_PRINTK_DEAD, "printk:dead", NULL,
 					console_cpu_notify);
-- 
2.30.2

  reply	other threads:[~2022-11-21 11:15 UTC|newest]

Thread overview: 83+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-11-16 16:21 [PATCH printk v5 00/40] reduce console_lock scope John Ogness
2022-11-16 16:21 ` John Ogness
2022-11-16 16:21 ` John Ogness
2022-11-16 16:21 ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 01/40] serial: kgdboc: Lock console list in probe function John Ogness
2022-11-16 16:21 ` [PATCH printk v5 02/40] printk: Convert console_drivers list to hlist John Ogness
2022-11-16 16:21 ` [PATCH printk v5 03/40] printk: Prepare for SRCU console list protection John Ogness
2022-12-01 18:22   ` Nathan Chancellor
2022-12-01 21:36     ` John Ogness
2022-12-01 21:56       ` Paul E. McKenney
2022-12-01 22:12         ` John Ogness
2022-12-02  0:21           ` Paul E. McKenney
2022-12-02 10:53             ` Petr Mladek
2022-11-16 16:21 ` [PATCH printk v5 04/40] printk: register_console: use "registered" for variable names John Ogness
2022-11-16 16:21 ` [PATCH printk v5 05/40] printk: move @seq initialization to helper John Ogness
2022-11-18  9:56   ` Petr Mladek
2022-11-16 16:21 ` [PATCH printk v5 06/40] printk: fix setting first seq for consoles John Ogness
2022-11-18 10:23   ` Petr Mladek
2022-11-18 10:52     ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 07/40] um: kmsg_dump: only dump when no output console available John Ogness
2022-11-16 16:21   ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 08/40] tty: serial: kgdboc: document console_lock usage John Ogness
2022-11-16 16:21 ` [PATCH printk v5 09/40] tty: tty_io: " John Ogness
2022-11-16 16:21 ` [PATCH printk v5 10/40] proc: consoles: " John Ogness
2022-11-16 16:21 ` [PATCH printk v5 11/40] printk: introduce console_list_lock John Ogness
2022-11-21 11:10   ` John Ogness [this message]
2022-11-21 13:36     ` [PATCH printk v6 " Petr Mladek
2022-11-16 16:21 ` [PATCH printk v5 12/40] console: introduce wrappers to read/write console flags John Ogness
2022-11-16 16:21 ` [PATCH printk v5 13/40] um: kmsg_dumper: use srcu console list iterator John Ogness
2022-11-16 16:21   ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 14/40] kdb: " John Ogness
2022-11-17  0:59   ` Doug Anderson
2022-11-16 16:21 ` [PATCH printk v5 15/40] printk: console_flush_all: " John Ogness
2022-11-16 16:21 ` [PATCH printk v5 16/40] printk: __pr_flush: " John Ogness
2022-11-16 16:21 ` [PATCH printk v5 17/40] printk: console_is_usable: use console_srcu_read_flags John Ogness
2022-11-16 16:21 ` [PATCH printk v5 18/40] printk: console_unblank: use srcu console list iterator John Ogness
2022-11-16 16:21 ` [PATCH printk v5 19/40] printk: console_flush_on_panic: " John Ogness
2022-11-16 16:21 ` [PATCH printk v5 20/40] printk: console_device: " John Ogness
2022-11-16 16:21 ` [PATCH printk v5 21/40] console: introduce console_is_registered() John Ogness
2022-11-16 16:21 ` [PATCH printk v5 22/40] serial_core: replace uart_console_enabled() with uart_console_registered() John Ogness
2022-11-16 16:21 ` [PATCH printk v5 23/40] tty: nfcon: use console_is_registered() John Ogness
2022-11-17  8:18   ` Geert Uytterhoeven
2022-11-16 16:21 ` [PATCH printk v5 24/40] efi: earlycon: " John Ogness
2022-11-16 16:21 ` [PATCH printk v5 25/40] tty: hvc: " John Ogness
2022-11-16 16:21   ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 26/40] tty: serial: earlycon: " John Ogness
2022-11-16 16:21 ` [PATCH printk v5 27/40] tty: serial: pic32_uart: " John Ogness
2022-11-16 16:21 ` [PATCH printk v5 28/40] tty: serial: samsung_tty: " John Ogness
2022-11-16 16:21   ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 29/40] tty: serial: xilinx_uartps: " John Ogness
2022-11-16 16:21   ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 30/40] usb: early: xhci-dbc: " John Ogness
2022-11-16 16:21 ` [PATCH printk v5 31/40] netconsole: avoid CON_ENABLED misuse to track registration John Ogness
2022-11-16 16:21 ` [PATCH printk v5 32/40] printk, xen: fbfront: create/use safe function for forcing preferred John Ogness
2022-11-16 16:21   ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 33/40] tty: tty_io: use console_list_lock for list synchronization John Ogness
2022-11-16 16:21 ` [PATCH printk v5 34/40] proc: consoles: use console_list_lock for list iteration John Ogness
2022-11-16 16:21 ` [PATCH printk v5 35/40] tty: serial: kgdboc: use srcu console list iterator John Ogness
2022-11-17  0:59   ` Doug Anderson
2022-11-17  9:32     ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 36/40] tty: serial: kgdboc: use console_list_lock for list traversal John Ogness
2022-11-17  0:59   ` Doug Anderson
2022-11-16 16:21 ` [PATCH printk v5 37/40] tty: serial: kgdboc: synchronize tty_find_polling_driver() and register_console() John Ogness
2022-11-17  0:59   ` Doug Anderson
2022-11-17 10:00     ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 38/40] tty: serial: kgdboc: use console_list_lock to trap exit John Ogness
2022-11-17  0:56   ` Doug Anderson
2022-11-17 11:08     ` John Ogness
2022-11-16 16:21 ` [PATCH printk v5 39/40] printk: relieve console_lock of list synchronization duties John Ogness
2022-11-18 11:07   ` Petr Mladek
2022-11-16 16:21 ` [PATCH printk v5 40/40] tty: serial: sh-sci: use setup() callback for early console John Ogness
2022-11-18 11:22 ` [PATCH printk v5 00/40] reduce console_lock scope Petr Mladek
2022-11-18 11:22   ` Petr Mladek
2022-11-18 11:22   ` Petr Mladek
2022-11-18 11:22   ` Petr Mladek
2022-11-18 14:55   ` Petr Mladek
2022-11-18 14:55     ` Petr Mladek
2022-11-18 14:55     ` Petr Mladek
2022-11-18 14:55     ` Petr Mladek
2022-11-22 16:43 ` Greg Kroah-Hartman
2022-11-22 16:43   ` Greg Kroah-Hartman
2022-11-22 16:43   ` Greg Kroah-Hartman
2022-11-22 16:43   ` Greg Kroah-Hartman

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=87sficwokr.fsf@jogness.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.