diff options
Diffstat (limited to 'patches/0019-console-add-write_atomic-interface.patch')
-rw-r--r-- | patches/0019-console-add-write_atomic-interface.patch | 218 |
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); |