summaryrefslogtreecommitdiffstats
path: root/kernel/printk
diff options
context:
space:
mode:
authorJohn Ogness <john.ogness@linutronix.de>2022-11-16 17:21:15 +0100
committerPetr Mladek <pmladek@suse.com>2022-12-02 11:24:59 +0100
commit6c4afa79147e4aa86665795695ac4a5f25e73176 (patch)
tree303743239f481d5eeb6ae90f33ec164de2e48a3a /kernel/printk
parentprintk: Convert console_drivers list to hlist (diff)
downloadlinux-6c4afa79147e4aa86665795695ac4a5f25e73176.tar.xz
linux-6c4afa79147e4aa86665795695ac4a5f25e73176.zip
printk: Prepare for SRCU console list protection
Provide an NMI-safe SRCU protected variant to walk the console list. Note that all console fields are now set before adding the console to the list to avoid the console becoming visible by SCRU readers before being fully initialized. This is a preparatory change for a new console infrastructure which operates independent of the console BKL. Suggested-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: John Ogness <john.ogness@linutronix.de> Acked-by: Miguel Ojeda <ojeda@kernel.org> Reviewed-by: Paul E. McKenney <paulmck@kernel.org> Reviewed-by: Petr Mladek <pmladek@suse.com> Signed-off-by: Petr Mladek <pmladek@suse.com> Link: https://lore.kernel.org/r/20221116162152.193147-4-john.ogness@linutronix.de
Diffstat (limited to 'kernel/printk')
-rw-r--r--kernel/printk/printk.c87
1 files changed, 72 insertions, 15 deletions
diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c
index e6f0832e71f0..173f46a29252 100644
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
@@ -85,6 +85,7 @@ EXPORT_SYMBOL(oops_in_progress);
static DEFINE_SEMAPHORE(console_sem);
HLIST_HEAD(console_list);
EXPORT_SYMBOL_GPL(console_list);
+DEFINE_STATIC_SRCU(console_srcu);
/*
* System may need to suppress printk message under certain
@@ -104,6 +105,13 @@ static struct lockdep_map console_lock_dep_map = {
};
#endif
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+bool console_srcu_read_lock_is_held(void)
+{
+ return srcu_read_lock_held(&console_srcu);
+}
+#endif
+
enum devkmsg_log_bits {
__DEVKMSG_LOG_BIT_ON = 0,
__DEVKMSG_LOG_BIT_OFF,
@@ -219,6 +227,32 @@ int devkmsg_sysctl_set_loglvl(struct ctl_table *table, int write,
}
#endif /* CONFIG_PRINTK && CONFIG_SYSCTL */
+/**
+ * console_srcu_read_lock - Register a new reader for the
+ * SRCU-protected console list
+ *
+ * Use for_each_console_srcu() to iterate the console list
+ *
+ * Context: Any context.
+ */
+int console_srcu_read_lock(void)
+{
+ return srcu_read_lock_nmisafe(&console_srcu);
+}
+EXPORT_SYMBOL(console_srcu_read_lock);
+
+/**
+ * console_srcu_read_unlock - Unregister an old reader from
+ * the SRCU-protected console list
+ *
+ * Counterpart to console_srcu_read_lock()
+ */
+void console_srcu_read_unlock(int cookie)
+{
+ srcu_read_unlock_nmisafe(&console_srcu, cookie);
+}
+EXPORT_SYMBOL(console_srcu_read_unlock);
+
/*
* Helper macros to handle lockdep when locking/unlocking console_sem. We use
* macros instead of functions so that _RET_IP_ contains useful information.
@@ -2989,6 +3023,14 @@ void console_stop(struct console *console)
console_lock();
console->flags &= ~CON_ENABLED;
console_unlock();
+
+ /*
+ * Ensure that all SRCU list walks have completed. All contexts must
+ * be able to see that this console is disabled so that (for example)
+ * the caller can suspend the port without risk of another context
+ * using the port.
+ */
+ synchronize_srcu(&console_srcu);
}
EXPORT_SYMBOL(console_stop);
@@ -3179,6 +3221,17 @@ void register_console(struct console *newcon)
newcon->flags &= ~CON_PRINTBUFFER;
}
+ newcon->dropped = 0;
+ if (newcon->flags & CON_PRINTBUFFER) {
+ /* Get a consistent copy of @syslog_seq. */
+ mutex_lock(&syslog_lock);
+ newcon->seq = syslog_seq;
+ mutex_unlock(&syslog_lock);
+ } else {
+ /* Begin with next message. */
+ newcon->seq = prb_next_seq(prb);
+ }
+
/*
* Put this console in the list - keep the
* preferred driver at the head of the list.
@@ -3187,28 +3240,24 @@ void register_console(struct console *newcon)
if (hlist_empty(&console_list)) {
/* Ensure CON_CONSDEV is always set for the head. */
newcon->flags |= CON_CONSDEV;
- hlist_add_head(&newcon->node, &console_list);
+ hlist_add_head_rcu(&newcon->node, &console_list);
} else if (newcon->flags & CON_CONSDEV) {
/* Only the new head can have CON_CONSDEV set. */
console_first()->flags &= ~CON_CONSDEV;
- hlist_add_head(&newcon->node, &console_list);
+ hlist_add_head_rcu(&newcon->node, &console_list);
} else {
- hlist_add_behind(&newcon->node, console_list.first);
- }
-
- newcon->dropped = 0;
- if (newcon->flags & CON_PRINTBUFFER) {
- /* Get a consistent copy of @syslog_seq. */
- mutex_lock(&syslog_lock);
- newcon->seq = syslog_seq;
- mutex_unlock(&syslog_lock);
- } else {
- /* Begin with next message. */
- newcon->seq = prb_next_seq(prb);
+ hlist_add_behind_rcu(&newcon->node, console_list.first);
}
console_unlock();
+
+ /*
+ * No need to synchronize SRCU here! The caller does not rely
+ * on all contexts being able to see the new console before
+ * register_console() completes.
+ */
+
console_sysfs_notify();
/*
@@ -3254,7 +3303,7 @@ int unregister_console(struct console *console)
return -ENODEV;
}
- hlist_del_init(&console->node);
+ hlist_del_init_rcu(&console->node);
/*
* <HISTORICAL>
@@ -3269,6 +3318,14 @@ int unregister_console(struct console *console)
console_first()->flags |= CON_CONSDEV;
console_unlock();
+
+ /*
+ * Ensure that all SRCU list walks have completed. All contexts
+ * must not be able to see this console in the list so that any
+ * exit/cleanup routines can be performed safely.
+ */
+ synchronize_srcu(&console_srcu);
+
console_sysfs_notify();
if (console->exit)