summaryrefslogtreecommitdiff
path: root/patches/0019-console-add-write_atomic-interface.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/0019-console-add-write_atomic-interface.patch')
-rw-r--r--patches/0019-console-add-write_atomic-interface.patch218
1 files changed, 212 insertions, 6 deletions
diff --git a/patches/0019-console-add-write_atomic-interface.patch b/patches/0019-console-add-write_atomic-interface.patch
index 2c772981c4e2..b147cbebbed7 100644
--- a/patches/0019-console-add-write_atomic-interface.patch
+++ b/patches/0019-console-add-write_atomic-interface.patch
@@ -1,6 +1,6 @@
From: John Ogness <john.ogness@linutronix.de>
Date: Mon, 30 Nov 2020 01:42:01 +0106
-Subject: [PATCH 19/28] console: add write_atomic interface
+Subject: [PATCH 19/29] console: add write_atomic interface
Add a write_atomic() callback to the console. This is an optional
function for console drivers. The function must be atomic (including
@@ -21,13 +21,106 @@ are provided:
These functions synchronize using a processor-reentrant spinlock
(called a cpulock).
+kgdb makes use of its own cpulock (@dbg_master_lock, @kgdb_active).
+This will conflict with the printk cpulock. Therefore, a CPU must
+ensure that it is not holding the printk cpulock when calling
+kgdb_cpu_enter(). If it is, it must allow its printk context to
+complete first.
+
Signed-off-by: John Ogness <john.ogness@linutronix.de>
Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
- include/linux/console.h | 4 +
- kernel/printk/printk.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++
- 2 files changed, 104 insertions(+)
+ arch/powerpc/include/asm/smp.h | 1
+ arch/powerpc/kernel/kgdb.c | 11 +++
+ arch/powerpc/kernel/smp.c | 5 +
+ arch/x86/kernel/kgdb.c | 10 ++-
+ include/linux/console.h | 5 +
+ include/linux/kgdb.h | 3 +
+ kernel/debug/debug_core.c | 45 ++++++++-------
+ kernel/printk/printk.c | 123 +++++++++++++++++++++++++++++++++++++++++
+ 8 files changed, 180 insertions(+), 23 deletions(-)
+--- a/arch/powerpc/include/asm/smp.h
++++ b/arch/powerpc/include/asm/smp.h
+@@ -57,6 +57,7 @@ struct smp_ops_t {
+
+ extern int smp_send_nmi_ipi(int cpu, void (*fn)(struct pt_regs *), u64 delay_us);
+ extern int smp_send_safe_nmi_ipi(int cpu, void (*fn)(struct pt_regs *), u64 delay_us);
++extern void smp_send_debugger_break_cpu(unsigned int cpu);
+ extern void smp_send_debugger_break(void);
+ extern void start_secondary_resume(void);
+ extern void smp_generic_give_timebase(void);
+--- a/arch/powerpc/kernel/kgdb.c
++++ b/arch/powerpc/kernel/kgdb.c
+@@ -20,6 +20,7 @@
+ #include <linux/signal.h>
+ #include <linux/ptrace.h>
+ #include <linux/kdebug.h>
++#include <linux/console.h>
+ #include <asm/current.h>
+ #include <asm/processor.h>
+ #include <asm/machdep.h>
+@@ -120,11 +121,19 @@ int kgdb_skipexception(int exception, st
+
+ static int kgdb_debugger_ipi(struct pt_regs *regs)
+ {
+- kgdb_nmicallback(raw_smp_processor_id(), regs);
++ int cpu = raw_smp_processor_id();
++
++ if (!console_atomic_kgdb_cpu_delay(cpu))
++ kgdb_nmicallback(cpu, regs);
+ return 0;
+ }
+
+ #ifdef CONFIG_SMP
++void kgdb_roundup_cpu(unsigned int cpu)
++{
++ smp_send_debugger_break_cpu(cpu);
++}
++
+ void kgdb_roundup_cpus(void)
+ {
+ smp_send_debugger_break();
+--- a/arch/powerpc/kernel/smp.c
++++ b/arch/powerpc/kernel/smp.c
+@@ -582,6 +582,11 @@ static void debugger_ipi_callback(struct
+ debugger_ipi(regs);
+ }
+
++void smp_send_debugger_break_cpu(unsigned int cpu)
++{
++ smp_send_nmi_ipi(cpu, debugger_ipi_callback, 1000000);
++}
++
+ void smp_send_debugger_break(void)
+ {
+ smp_send_nmi_ipi(NMI_IPI_ALL_OTHERS, debugger_ipi_callback, 1000000);
+--- a/arch/x86/kernel/kgdb.c
++++ b/arch/x86/kernel/kgdb.c
+@@ -32,6 +32,7 @@
+ #include <linux/kgdb.h>
+ #include <linux/smp.h>
+ #include <linux/nmi.h>
++#include <linux/console.h>
+ #include <linux/hw_breakpoint.h>
+ #include <linux/uaccess.h>
+ #include <linux/memory.h>
+@@ -502,9 +503,12 @@ static int kgdb_nmi_handler(unsigned int
+ if (atomic_read(&kgdb_active) != -1) {
+ /* KGDB CPU roundup */
+ cpu = raw_smp_processor_id();
+- kgdb_nmicallback(cpu, regs);
+- set_bit(cpu, was_in_debug_nmi);
+- touch_nmi_watchdog();
++
++ if (!console_atomic_kgdb_cpu_delay(cpu)) {
++ kgdb_nmicallback(cpu, regs);
++ set_bit(cpu, was_in_debug_nmi);
++ touch_nmi_watchdog();
++ }
+
+ return NMI_HANDLED;
+ }
--- a/include/linux/console.h
+++ b/include/linux/console.h
@@ -140,6 +140,7 @@ static inline int con_debug_leave(void)
@@ -38,17 +131,108 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
int (*read)(struct console *, char *, unsigned);
struct tty_driver *(*device)(struct console *, int *);
void (*unblank)(void);
-@@ -229,4 +230,7 @@ extern void console_init(void);
+@@ -229,4 +230,8 @@ extern void console_init(void);
void dummycon_register_output_notifier(struct notifier_block *nb);
void dummycon_unregister_output_notifier(struct notifier_block *nb);
+extern void console_atomic_lock(unsigned int *flags);
+extern void console_atomic_unlock(unsigned int flags);
++extern bool console_atomic_kgdb_cpu_delay(unsigned int cpu);
+
#endif /* _LINUX_CONSOLE_H */
+--- a/include/linux/kgdb.h
++++ b/include/linux/kgdb.h
+@@ -212,6 +212,8 @@ extern void kgdb_call_nmi_hook(void *ign
+ */
+ extern void kgdb_roundup_cpus(void);
+
++extern void kgdb_roundup_cpu(unsigned int cpu);
++
+ /**
+ * kgdb_arch_set_pc - Generic call back to the program counter
+ * @regs: Current &struct pt_regs.
+@@ -365,5 +367,6 @@ extern void kgdb_free_init_mem(void);
+ #define dbg_late_init()
+ static inline void kgdb_panic(const char *msg) {}
+ static inline void kgdb_free_init_mem(void) { }
++static inline void kgdb_roundup_cpu(unsigned int cpu) {}
+ #endif /* ! CONFIG_KGDB */
+ #endif /* _KGDB_H_ */
+--- a/kernel/debug/debug_core.c
++++ b/kernel/debug/debug_core.c
+@@ -241,35 +241,42 @@ NOKPROBE_SYMBOL(kgdb_call_nmi_hook);
+ static DEFINE_PER_CPU(call_single_data_t, kgdb_roundup_csd) =
+ CSD_INIT(kgdb_call_nmi_hook, NULL);
+
+-void __weak kgdb_roundup_cpus(void)
++void __weak kgdb_roundup_cpu(unsigned int cpu)
+ {
+ call_single_data_t *csd;
++ int ret;
++
++ csd = &per_cpu(kgdb_roundup_csd, cpu);
++
++ /*
++ * If it didn't round up last time, don't try again
++ * since smp_call_function_single_async() will block.
++ *
++ * If rounding_up is false then we know that the
++ * previous call must have at least started and that
++ * means smp_call_function_single_async() won't block.
++ */
++ if (kgdb_info[cpu].rounding_up)
++ return;
++ kgdb_info[cpu].rounding_up = true;
++
++ ret = smp_call_function_single_async(cpu, csd);
++ if (ret)
++ kgdb_info[cpu].rounding_up = false;
++}
++NOKPROBE_SYMBOL(kgdb_roundup_cpu);
++
++void __weak kgdb_roundup_cpus(void)
++{
+ int this_cpu = raw_smp_processor_id();
+ int cpu;
+- int ret;
+
+ for_each_online_cpu(cpu) {
+ /* No need to roundup ourselves */
+ if (cpu == this_cpu)
+ continue;
+
+- csd = &per_cpu(kgdb_roundup_csd, cpu);
+-
+- /*
+- * If it didn't round up last time, don't try again
+- * since smp_call_function_single_async() will block.
+- *
+- * If rounding_up is false then we know that the
+- * previous call must have at least started and that
+- * means smp_call_function_single_async() won't block.
+- */
+- if (kgdb_info[cpu].rounding_up)
+- continue;
+- kgdb_info[cpu].rounding_up = true;
+-
+- ret = smp_call_function_single_async(cpu, csd);
+- if (ret)
+- kgdb_info[cpu].rounding_up = false;
++ kgdb_roundup_cpu(cpu);
+ }
+ }
+ NOKPROBE_SYMBOL(kgdb_roundup_cpus);
--- a/kernel/printk/printk.c
+++ b/kernel/printk/printk.c
-@@ -3546,3 +3546,103 @@ void kmsg_dump_rewind(struct kmsg_dumper
+@@ -44,6 +44,7 @@
+ #include <linux/irq_work.h>
+ #include <linux/ctype.h>
+ #include <linux/uio.h>
++#include <linux/kgdb.h>
+ #include <linux/sched/clock.h>
+ #include <linux/sched/debug.h>
+ #include <linux/sched/task_stack.h>
+@@ -3558,3 +3559,125 @@ void kmsg_dump_rewind(struct kmsg_dump_i
EXPORT_SYMBOL_GPL(kmsg_dump_rewind);
#endif
@@ -65,6 +249,8 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ .irqflags = &_##name##_percpu_irqflags, \
+}
+
++static unsigned int kgdb_cpu = -1;
++
+static bool __prb_trylock(struct prb_cpulock *cpu_lock,
+ unsigned int *cpu_store)
+{
@@ -125,10 +311,15 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ */
+static void prb_unlock(struct prb_cpulock *cpu_lock, unsigned int cpu_store)
+{
++ bool trigger_kgdb = false;
+ unsigned long *flags;
+ unsigned int cpu;
+
+ cpu = atomic_read(&cpu_lock->owner);
++ if (cpu == kgdb_cpu && cpu_store == -1) {
++ trigger_kgdb = true;
++ kgdb_cpu = -1;
++ }
+ atomic_set_release(&cpu_lock->owner, cpu_store);
+
+ if (cpu_store == -1) {
@@ -137,6 +328,11 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ }
+
+ put_cpu();
++
++ if (trigger_kgdb) {
++ pr_warn("re-triggering kgdb roundup for CPU#%d\n", cpu);
++ kgdb_roundup_cpu(cpu);
++ }
+}
+
+DECLARE_STATIC_PRINTKRB_CPULOCK(printk_cpulock);
@@ -152,3 +348,13 @@ Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ prb_unlock(&printk_cpulock, flags);
+}
+EXPORT_SYMBOL(console_atomic_unlock);
++
++bool console_atomic_kgdb_cpu_delay(unsigned int cpu)
++{
++ if (cpu != atomic_read(&printk_cpulock.owner))
++ return false;
++
++ kgdb_cpu = cpu;
++ return true;
++}
++EXPORT_SYMBOL(console_atomic_kgdb_cpu_delay);