[PATCH printk v2 09/11] panic: Add atomic write enforcement to oops

From: John Ogness
Date: Tue Sep 19 2023 - 19:10:08 EST


Invoke the atomic write enforcement functions for oops to
ensure that the information gets out to the consoles.

Since there is no single general function that calls both
oops_enter() and oops_exit(), the nesting feature of atomic
write sections is taken advantage of in order to guarantee
full coverage between the first oops_enter() and the last
oops_exit().

It is important to note that if there are any legacy consoles
registered, they will be attempting to directly print from the
printk-caller context, which may jeopardize the reliability of
the atomic consoles. Optimally there should be no legacy
consoles registered.

Signed-off-by: John Ogness <john.ogness@xxxxxxxxxxxxx>
---
kernel/panic.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)

diff --git a/kernel/panic.c b/kernel/panic.c
index 86ed71ba8c4d..e2879098645d 100644
--- a/kernel/panic.c
+++ b/kernel/panic.c
@@ -614,6 +614,10 @@ bool oops_may_print(void)
return pause_on_oops_flag == 0;
}

+static atomic_t oops_cpu = ATOMIC_INIT(-1);
+static int oops_nesting;
+static enum nbcon_prio oops_prev_prio;
+
/*
* Called when the architecture enters its oops handler, before it prints
* anything. If this is the first CPU to oops, and it's oopsing the first
@@ -630,6 +634,36 @@ bool oops_may_print(void)
*/
void oops_enter(void)
{
+ enum nbcon_prio prev_prio;
+ int cpu = -1;
+
+ /*
+ * If this turns out to be the first CPU in oops, this is the
+ * beginning of the outermost atomic section. Otherwise it is
+ * the beginning of an inner atomic section.
+ */
+ prev_prio = nbcon_atomic_enter(NBCON_PRIO_EMERGENCY);
+
+ if (atomic_try_cmpxchg_relaxed(&oops_cpu, &cpu, smp_processor_id())) {
+ /*
+ * This is the first CPU in oops. Save the outermost
+ * @prev_prio in order to restore it on the outermost
+ * matching oops_exit(), when @oops_nesting == 0.
+ */
+ oops_prev_prio = prev_prio;
+
+ /*
+ * Enter an inner atomic section that ends at the end of this
+ * function. In this case, the nbcon_atomic_enter() above
+ * began the outermost atomic section.
+ */
+ prev_prio = nbcon_atomic_enter(NBCON_PRIO_EMERGENCY);
+ }
+
+ /* Track nesting when this CPU is the owner. */
+ if (cpu == -1 || cpu == smp_processor_id())
+ oops_nesting++;
+
tracing_off();
/* can't trust the integrity of the kernel anymore: */
debug_locks_off();
@@ -637,6 +671,9 @@ void oops_enter(void)

if (sysctl_oops_all_cpu_backtrace)
trigger_all_cpu_backtrace();
+
+ /* Exit inner atomic section. */
+ nbcon_atomic_exit(NBCON_PRIO_EMERGENCY, prev_prio);
}

static void print_oops_end_marker(void)
@@ -652,6 +689,18 @@ void oops_exit(void)
{
do_oops_enter_exit();
print_oops_end_marker();
+
+ if (atomic_read(&oops_cpu) == smp_processor_id()) {
+ oops_nesting--;
+ if (oops_nesting == 0) {
+ atomic_set(&oops_cpu, -1);
+
+ /* Exit outmost atomic section. */
+ nbcon_atomic_exit(NBCON_PRIO_EMERGENCY, oops_prev_prio);
+ }
+ }
+ put_cpu();
+
kmsg_dump(KMSG_DUMP_OOPS);
}

--
2.39.2