Re: netconsole: HARDIRQ-safe -> HARDIRQ-unsafe lock order warning
From: Calvin Owens <hidden>
Date: 2025-09-08 20:27:08
Also in:
lkml
Subsystem:
the rest, tty layer and serial drivers · Maintainers:
Linus Torvalds, Greg Kroah-Hartman, Jiri Slaby
On Friday 09/05 at 14:54 +0206, John Ogness wrote:
<snip> NBCON is meant to deprecate @oops_in_progress. However, it is true that consoles not implementing ->write_atomic() will never print panic output.
Below is a silly little testcase that makes it more convenient to test if crashes are getting out in a few canned cases, in case anyone else finds it useful. Testing this on 6.17-rc5 on a Pi 4b, I don't get any netconsole output at all for any crash case over wifi, so that already doesn't work. All the cases currently work over ethernet. ----8<---- From: Calvin Owens <redacted> Subject: [PATCH] Quick and dirty testcase for netconsole (and other consoles) Signed-off-by: Calvin Owens <redacted> --- drivers/tty/Kconfig | 9 ++ drivers/tty/Makefile | 1 + drivers/tty/crashtest.c | 178 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 188 insertions(+) create mode 100644 drivers/tty/crashtest.c
diff --git a/drivers/tty/Kconfig b/drivers/tty/Kconfig
index 149f3d53b760..c0f58943202c 100644
--- a/drivers/tty/Kconfig
+++ b/drivers/tty/Kconfig@@ -424,6 +424,15 @@ config RPMSG_TTY To compile this driver as a module, choose M here: the module will be called rpmsg_tty. +config CRASHTEST + tristate "Empirical testcase for console crash output" + help + Say Y to expose a file at /sys/kernel/debug/crashtest which allows + the kernel to be deliberately crashed in various execution contexts to + prove crash traces can be successfully emitted on the console. + + To compile this driver as a module, choose M here. If unsure, say N. + endif # TTY source "drivers/tty/serdev/Kconfig"
diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile
index 07aca5184a55..0448b8285079 100644
--- a/drivers/tty/Makefile
+++ b/drivers/tty/Makefile@@ -27,5 +27,6 @@ obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o obj-$(CONFIG_VCC) += vcc.o obj-$(CONFIG_RPMSG_TTY) += rpmsg_tty.o +obj-$(CONFIG_CRASHTEST) += crashtest.o obj-y += ipwireless/
diff --git a/drivers/tty/crashtest.c b/drivers/tty/crashtest.c
new file mode 100644
index 000000000000..a7b90300d906
--- /dev/null
+++ b/drivers/tty/crashtest.c@@ -0,0 +1,178 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * # cat /sys/kernel/debug/crashtest + * crash_user + * crash_user_nobh + * crash_user_noirq + * crash_user_conlock + * crash_user_rtnllock + * crash_bh + * crash_irq + * # echo "crash_user" > /sys/kernel/debug/crashtest + */ + +#include <linux/console.h> +#include <linux/debugfs.h> +#include <linux/irq_work.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/rtnetlink.h> +#include <linux/string.h> + +static ssize_t __crash(void) +{ + pr_emerg("BANG!\n"); + *(volatile unsigned char *)NULL = '!'; + return -ENOSYS; +} + +static void __crash_irq_work(struct irq_work *work) +{ + __crash(); +} + +static struct irq_work irq_crash_work; + +static ssize_t crash_irq(void) +{ + if (!irq_work_queue(&irq_crash_work)) + return -EBUSY; + + irq_work_sync(&irq_crash_work); + return -ENOSYS; +} + +static void __crash_bh_work(struct work_struct *work) +{ + __crash(); +} + +static struct work_struct bh_crash_work; + +static ssize_t crash_bh(void) +{ + if (!queue_work(system_bh_wq, &bh_crash_work)) + return -EBUSY; + + flush_work(&bh_crash_work); + return -ENOSYS; +} + +static ssize_t crash_user(void) +{ + return __crash(); +} + +static ssize_t crash_user_nobh(void) +{ + local_bh_disable(); + return crash_user(); +} + +static ssize_t crash_user_noirq(void) +{ + local_irq_disable(); + return crash_user(); +} + +static ssize_t crash_user_conlock(void) +{ + console_lock(); + return crash_user(); +} + +static ssize_t crash_user_rtnllock(void) +{ + rtnl_lock(); + return crash_user(); +} + +struct crashtest_case { + ssize_t (*fn)(void); + const char *str; +}; + +#define CRASHTEST_CASE(fn_) \ + (struct crashtest_case){.fn = fn_, .str = #fn_ "\n"} + +static const struct crashtest_case tests[] = { + CRASHTEST_CASE(crash_user), + CRASHTEST_CASE(crash_user_nobh), + CRASHTEST_CASE(crash_user_noirq), + CRASHTEST_CASE(crash_user_conlock), + CRASHTEST_CASE(crash_user_rtnllock), + CRASHTEST_CASE(crash_bh), + CRASHTEST_CASE(crash_irq), +}; + +static ssize_t crashtest_write(struct file *file, const char __user *u_buf, + size_t u_len, loff_t *u_off) +{ + char buf[32] = {0}; + unsigned i; + + if (copy_from_user(buf, u_buf, min(u_len, sizeof(buf) - 1)) != 0) + return -EFAULT; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) + if (!strcmp(tests[i].str, buf)) + return tests[i].fn(); + + return -EEXIST; +} + +static ssize_t crashtest_read(struct file *file, char __user *u_buf, + size_t u_len, loff_t *u_off) +{ + ssize_t ret = 0; + unsigned i; + + if (*u_off > 0) + return 0; + + for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) { + unsigned long len, done; + + len = strlen(tests[i].str); + done = len - copy_to_user(u_buf, tests[i].str, min(u_len, len)); + u_len -= done; + u_buf += done; + ret += done; + if (done != len) + break; + } + + *u_off += ret; + return ret; +} + +static const struct file_operations crashtest_fops = { + .write = crashtest_write, + .read = crashtest_read, +}; + +static struct dentry *crashtest_dentry; + +static int __init setup_crashtest(void) +{ + INIT_WORK(&bh_crash_work, __crash_bh_work); + init_irq_work(&irq_crash_work, __crash_irq_work); + crashtest_dentry = debugfs_create_file("crashtest", 0600, NULL, NULL, + &crashtest_fops); + if (IS_ERR(crashtest_dentry)) + return PTR_ERR(crashtest_dentry); + + return 0; +} + +static void __exit cleanup_crashtest(void) +{ + debugfs_remove(crashtest_dentry); +} + +late_initcall(setup_crashtest); +module_exit(cleanup_crashtest); + +MODULE_AUTHOR("Calvin Owens <calvin@wbinvd.org>"); +MODULE_DESCRIPTION("Empirical testcase for console crash output"); +MODULE_LICENSE("GPL");
--
2.47.2