[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <aL88Gb6R5M3zhMTb@mozart.vkv.me>
Date: Mon, 8 Sep 2025 13:27:05 -0700
From: Calvin Owens <calvin@...nvd.org>
To: John Ogness <john.ogness@...utronix.de>,
Breno Leitao <leitao@...ian.org>
Cc: Mike Galbraith <efault@....de>, Simon Horman <horms@...nel.org>,
kuba@...nel.org, Pavel Begunkov <asml.silence@...il.com>,
Johannes Berg <johannes@...solutions.net>, paulmck@...nel.org,
LKML <linux-kernel@...r.kernel.org>, netdev@...r.kernel.org,
boqun.feng@...il.com
Subject: Re: netconsole: HARDIRQ-safe -> HARDIRQ-unsafe lock order warning
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 <calvin@...nvd.org>
Subject: [PATCH] Quick and dirty testcase for netconsole (and other consoles)
Signed-off-by: Calvin Owens <calvin@...nvd.org>
---
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@...nvd.org>");
+MODULE_DESCRIPTION("Empirical testcase for console crash output");
+MODULE_LICENSE("GPL");
--
2.47.2
Powered by blists - more mailing lists