125 lines
4.3 KiB
Diff
125 lines
4.3 KiB
Diff
|
From: John Ogness <john.ogness@linutronix.de>
|
||
|
Date: Tue, 20 Aug 2024 08:35:35 +0206
|
||
|
Subject: [PATCH 09/54] printk: nbcon: Use driver synchronization while
|
||
|
(un)registering
|
||
|
Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/6.11/older/patches-6.11-rt7.tar.xz
|
||
|
|
||
|
Console drivers typically have to deal with access to the
|
||
|
hardware via user input/output (such as an interactive login
|
||
|
shell) and output of kernel messages via printk() calls.
|
||
|
|
||
|
They use some classic driver-specific locking mechanism in most
|
||
|
situations. But console->write_atomic() callbacks, used by nbcon
|
||
|
consoles, are synchronized only by acquiring the console
|
||
|
context.
|
||
|
|
||
|
The synchronization via the console context ownership is possible
|
||
|
only when the console driver is registered. It is when a
|
||
|
particular device driver is connected with a particular console
|
||
|
driver.
|
||
|
|
||
|
The two synchronization mechanisms must be synchronized between
|
||
|
each other. It is tricky because the console context ownership
|
||
|
is quite special. It might be taken over by a higher priority
|
||
|
context. Also CPU migration must be disabled. The most tricky
|
||
|
part is to (dis)connect these two mechanisms during the console
|
||
|
(un)registration.
|
||
|
|
||
|
Use the driver-specific locking callbacks: device_lock(),
|
||
|
device_unlock(). They allow taking the device-specific lock
|
||
|
while the device is being (un)registered by the related console
|
||
|
driver.
|
||
|
|
||
|
For example, these callbacks lock/unlock the port lock for
|
||
|
serial port drivers.
|
||
|
|
||
|
Note that the driver-specific locking is only needed during
|
||
|
(un)register if it is an nbcon console with the write_atomic()
|
||
|
callback implemented. If write_atomic() is not implemented, the
|
||
|
driver should never attempt to access the hardware without
|
||
|
first acquiring its driver-specific lock.
|
||
|
|
||
|
Signed-off-by: John Ogness <john.ogness@linutronix.de>
|
||
|
Reviewed-by: Petr Mladek <pmladek@suse.com>
|
||
|
Link: https://lore.kernel.org/r/20240820063001.36405-10-john.ogness@linutronix.de
|
||
|
Signed-off-by: Petr Mladek <pmladek@suse.com>
|
||
|
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
|
||
|
---
|
||
|
kernel/printk/printk.c | 33 ++++++++++++++++++++++++++++++++-
|
||
|
1 file changed, 32 insertions(+), 1 deletion(-)
|
||
|
|
||
|
--- a/kernel/printk/printk.c
|
||
|
+++ b/kernel/printk/printk.c
|
||
|
@@ -3549,9 +3549,11 @@ static int unregister_console_locked(str
|
||
|
*/
|
||
|
void register_console(struct console *newcon)
|
||
|
{
|
||
|
- struct console *con;
|
||
|
+ bool use_device_lock = (newcon->flags & CON_NBCON) && newcon->write_atomic;
|
||
|
bool bootcon_registered = false;
|
||
|
bool realcon_registered = false;
|
||
|
+ struct console *con;
|
||
|
+ unsigned long flags;
|
||
|
u64 init_seq;
|
||
|
int err;
|
||
|
|
||
|
@@ -3639,6 +3641,19 @@ void register_console(struct console *ne
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
+ * If another context is actively using the hardware of this new
|
||
|
+ * console, it will not be aware of the nbcon synchronization. This
|
||
|
+ * is a risk that two contexts could access the hardware
|
||
|
+ * simultaneously if this new console is used for atomic printing
|
||
|
+ * and the other context is still using the hardware.
|
||
|
+ *
|
||
|
+ * Use the driver synchronization to ensure that the hardware is not
|
||
|
+ * in use while this new console transitions to being registered.
|
||
|
+ */
|
||
|
+ if (use_device_lock)
|
||
|
+ newcon->device_lock(newcon, &flags);
|
||
|
+
|
||
|
+ /*
|
||
|
* Put this console in the list - keep the
|
||
|
* preferred driver at the head of the list.
|
||
|
*/
|
||
|
@@ -3662,6 +3677,10 @@ void register_console(struct console *ne
|
||
|
* register_console() completes.
|
||
|
*/
|
||
|
|
||
|
+ /* This new console is now registered. */
|
||
|
+ if (use_device_lock)
|
||
|
+ newcon->device_unlock(newcon, flags);
|
||
|
+
|
||
|
console_sysfs_notify();
|
||
|
|
||
|
/*
|
||
|
@@ -3690,6 +3709,8 @@ EXPORT_SYMBOL(register_console);
|
||
|
/* Must be called under console_list_lock(). */
|
||
|
static int unregister_console_locked(struct console *console)
|
||
|
{
|
||
|
+ bool use_device_lock = (console->flags & CON_NBCON) && console->write_atomic;
|
||
|
+ unsigned long flags;
|
||
|
int res;
|
||
|
|
||
|
lockdep_assert_console_list_lock_held();
|
||
|
@@ -3708,8 +3729,18 @@ static int unregister_console_locked(str
|
||
|
if (!console_is_registered_locked(console))
|
||
|
return -ENODEV;
|
||
|
|
||
|
+ /*
|
||
|
+ * Use the driver synchronization to ensure that the hardware is not
|
||
|
+ * in use while this console transitions to being unregistered.
|
||
|
+ */
|
||
|
+ if (use_device_lock)
|
||
|
+ console->device_lock(console, &flags);
|
||
|
+
|
||
|
hlist_del_init_rcu(&console->node);
|
||
|
|
||
|
+ if (use_device_lock)
|
||
|
+ console->device_unlock(console, flags);
|
||
|
+
|
||
|
/*
|
||
|
* <HISTORICAL>
|
||
|
* If this isn't the last console and it has CON_CONSDEV set, we
|