* [RFT v2] tty/sysrq: Make sysrq handler NMI aware
@ 2022-02-28 7:53 Sumit Garg
2022-02-28 12:07 ` Peter Zijlstra
0 siblings, 1 reply; 3+ messages in thread
From: Sumit Garg @ 2022-02-28 7:53 UTC (permalink / raw)
To: linux-serial, hasegawa-hitomi
Cc: gregkh, jirislaby, jason.wessel, daniel.thompson, dianders,
linux-kernel, kgdb-bugreport, arnd, peterz, Sumit Garg
Allow a magic sysrq to be triggered from an NMI context. This is done
via marking some sysrq actions as NMI safe. Safe actions will be allowed
to run from NMI context whilst that cannot run from an NMI will be queued
as irq_work for later processing.
A particular sysrq handler is only marked as NMI safe in case the handler
isn't contending for any synchronization primitives as in NMI context
they are expected to cause deadlocks. Note that the debug sysrq do not
contend for any synchronization primitives. It does call kgdb_breakpoint()
to provoke a trap but that trap handler should be NMI safe on
architectures that implement an NMI.
Signed-off-by: Sumit Garg <sumit.garg@linaro.org>
---
Hi Hitomi Hasegawa,
Give this patch a try with diagnostic pseudo NMI interrupt on A64FX and
let me know if it works for you. Also, feel free to include this patch
along with your driver patch.
-Sumit
Changes in v2:
- Rebased to 5.17-rc5.
- Separate this patch from complete patch-set [1] as its relevant for
other diagnostic NMI interrupts [2] as well apart from uart NMI
interrupts.
- Incorporated suggestions from Doug.
[1] https://lore.kernel.org/linux-arm-kernel/CAFA6WYOWHgmYYt=KGXDh2hKiuy_rQbJfi279ev0+s-Qh7L21kA@mail.gmail.com/t/#m2b5006f08581448020eb24566927a104d0b95c44
[2] https://lore.kernel.org/all/Yhi0rrkSR63ZhjX1@kroah.com/T/
drivers/tty/sysrq.c | 51 ++++++++++++++++++++++++++++++++++++++-
include/linux/sysrq.h | 1 +
kernel/debug/debug_core.c | 1 +
3 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
index bbfd004449b5..0ce944591036 100644
--- a/drivers/tty/sysrq.c
+++ b/drivers/tty/sysrq.c
@@ -51,6 +51,8 @@
#include <linux/syscalls.h>
#include <linux/of.h>
#include <linux/rcupdate.h>
+#include <linux/irq_work.h>
+#include <linux/kfifo.h>
#include <asm/ptrace.h>
#include <asm/irq_regs.h>
@@ -112,6 +114,7 @@ static const struct sysrq_key_op sysrq_loglevel_op = {
.help_msg = "loglevel(0-9)",
.action_msg = "Changing Loglevel",
.enable_mask = SYSRQ_ENABLE_LOG,
+ .nmi_safe = true,
};
#ifdef CONFIG_VT
@@ -159,6 +162,7 @@ static const struct sysrq_key_op sysrq_crash_op = {
.help_msg = "crash(c)",
.action_msg = "Trigger a crash",
.enable_mask = SYSRQ_ENABLE_DUMP,
+ .nmi_safe = true,
};
static void sysrq_handle_reboot(int key)
@@ -172,6 +176,7 @@ static const struct sysrq_key_op sysrq_reboot_op = {
.help_msg = "reboot(b)",
.action_msg = "Resetting",
.enable_mask = SYSRQ_ENABLE_BOOT,
+ .nmi_safe = true,
};
const struct sysrq_key_op *__sysrq_reboot_op = &sysrq_reboot_op;
@@ -219,6 +224,7 @@ static const struct sysrq_key_op sysrq_showlocks_op = {
.handler = sysrq_handle_showlocks,
.help_msg = "show-all-locks(d)",
.action_msg = "Show Locks Held",
+ .nmi_safe = true,
};
#else
#define sysrq_showlocks_op (*(const struct sysrq_key_op *)NULL)
@@ -291,6 +297,7 @@ static const struct sysrq_key_op sysrq_showregs_op = {
.help_msg = "show-registers(p)",
.action_msg = "Show Regs",
.enable_mask = SYSRQ_ENABLE_DUMP,
+ .nmi_safe = true,
};
static void sysrq_handle_showstate(int key)
@@ -328,6 +335,7 @@ static const struct sysrq_key_op sysrq_ftrace_dump_op = {
.help_msg = "dump-ftrace-buffer(z)",
.action_msg = "Dump ftrace buffer",
.enable_mask = SYSRQ_ENABLE_DUMP,
+ .nmi_safe = true,
};
#else
#define sysrq_ftrace_dump_op (*(const struct sysrq_key_op *)NULL)
@@ -566,6 +574,37 @@ static void __sysrq_put_key_op(int key, const struct sysrq_key_op *op_p)
sysrq_key_table[i] = op_p;
}
+#define SYSRQ_NMI_FIFO_SIZE 2
+static DEFINE_KFIFO(sysrq_nmi_fifo, int, SYSRQ_NMI_FIFO_SIZE);
+
+static void sysrq_do_nmi_work(struct irq_work *work)
+{
+ const struct sysrq_key_op *op_p;
+ int orig_suppress_printk;
+ int key;
+
+ orig_suppress_printk = suppress_printk;
+ suppress_printk = 0;
+
+ rcu_sysrq_start();
+ rcu_read_lock();
+
+ if (kfifo_peek(&sysrq_nmi_fifo, &key)) {
+ op_p = __sysrq_get_key_op(key);
+ if (op_p)
+ op_p->handler(key);
+ }
+
+ rcu_read_unlock();
+ rcu_sysrq_end();
+
+ suppress_printk = orig_suppress_printk;
+
+ kfifo_reset_out(&sysrq_nmi_fifo);
+}
+
+static DEFINE_IRQ_WORK(sysrq_nmi_work, sysrq_do_nmi_work);
+
void __handle_sysrq(int key, bool check_mask)
{
const struct sysrq_key_op *op_p;
@@ -573,6 +612,10 @@ void __handle_sysrq(int key, bool check_mask)
int orig_suppress_printk;
int i;
+ /* Skip sysrq handling if one already in progress */
+ if (!kfifo_is_empty(&sysrq_nmi_fifo))
+ return;
+
orig_suppress_printk = suppress_printk;
suppress_printk = 0;
@@ -596,7 +639,13 @@ void __handle_sysrq(int key, bool check_mask)
if (!check_mask || sysrq_on_mask(op_p->enable_mask)) {
pr_info("%s\n", op_p->action_msg);
console_loglevel = orig_log_level;
- op_p->handler(key);
+
+ if (in_nmi() && !op_p->nmi_safe) {
+ kfifo_put(&sysrq_nmi_fifo, key);
+ irq_work_queue(&sysrq_nmi_work);
+ } else {
+ op_p->handler(key);
+ }
} else {
pr_info("This sysrq operation is disabled.\n");
console_loglevel = orig_log_level;
diff --git a/include/linux/sysrq.h b/include/linux/sysrq.h
index 3a582ec7a2f1..630b5b9dc225 100644
--- a/include/linux/sysrq.h
+++ b/include/linux/sysrq.h
@@ -34,6 +34,7 @@ struct sysrq_key_op {
const char * const help_msg;
const char * const action_msg;
const int enable_mask;
+ const bool nmi_safe;
};
#ifdef CONFIG_MAGIC_SYSRQ
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c
index da06a5553835..53b56114f59b 100644
--- a/kernel/debug/debug_core.c
+++ b/kernel/debug/debug_core.c
@@ -978,6 +978,7 @@ static const struct sysrq_key_op sysrq_dbg_op = {
.handler = sysrq_handle_dbg,
.help_msg = "debug(g)",
.action_msg = "DEBUG",
+ .nmi_safe = true,
};
#endif
--
2.25.1
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFT v2] tty/sysrq: Make sysrq handler NMI aware
2022-02-28 7:53 [RFT v2] tty/sysrq: Make sysrq handler NMI aware Sumit Garg
@ 2022-02-28 12:07 ` Peter Zijlstra
2022-02-28 13:28 ` Sumit Garg
0 siblings, 1 reply; 3+ messages in thread
From: Peter Zijlstra @ 2022-02-28 12:07 UTC (permalink / raw)
To: Sumit Garg
Cc: linux-serial, hasegawa-hitomi, gregkh, jirislaby, jason.wessel,
daniel.thompson, dianders, linux-kernel, kgdb-bugreport, arnd
On Mon, Feb 28, 2022 at 01:23:51PM +0530, Sumit Garg wrote:
> Allow a magic sysrq to be triggered from an NMI context. This is done
*why* though?
> +#define SYSRQ_NMI_FIFO_SIZE 2
> +static DEFINE_KFIFO(sysrq_nmi_fifo, int, SYSRQ_NMI_FIFO_SIZE);
> +
> +static void sysrq_do_nmi_work(struct irq_work *work)
That naming don't make sense, it does the !NMI work, from IRQ context.
> +{
> + const struct sysrq_key_op *op_p;
> + int orig_suppress_printk;
> + int key;
> +
> + orig_suppress_printk = suppress_printk;
> + suppress_printk = 0;
> +
> + rcu_sysrq_start();
> + rcu_read_lock();
> +
> + if (kfifo_peek(&sysrq_nmi_fifo, &key)) {
> + op_p = __sysrq_get_key_op(key);
> + if (op_p)
> + op_p->handler(key);
> + }
> +
> + rcu_read_unlock();
> + rcu_sysrq_end();
> +
> + suppress_printk = orig_suppress_printk;
> +
> + kfifo_reset_out(&sysrq_nmi_fifo);
> +}
> +
> +static DEFINE_IRQ_WORK(sysrq_nmi_work, sysrq_do_nmi_work);
> +
> void __handle_sysrq(int key, bool check_mask)
> {
> const struct sysrq_key_op *op_p;
> @@ -573,6 +612,10 @@ void __handle_sysrq(int key, bool check_mask)
> int orig_suppress_printk;
> int i;
>
> + /* Skip sysrq handling if one already in progress */
> + if (!kfifo_is_empty(&sysrq_nmi_fifo))
> + return;
> +
> orig_suppress_printk = suppress_printk;
> suppress_printk = 0;
>
> @@ -596,7 +639,13 @@ void __handle_sysrq(int key, bool check_mask)
> if (!check_mask || sysrq_on_mask(op_p->enable_mask)) {
> pr_info("%s\n", op_p->action_msg);
> console_loglevel = orig_log_level;
> - op_p->handler(key);
> +
> + if (in_nmi() && !op_p->nmi_safe) {
> + kfifo_put(&sysrq_nmi_fifo, key);
> + irq_work_queue(&sysrq_nmi_work);
> + } else {
> + op_p->handler(key);
> + }
> } else {
> pr_info("This sysrq operation is disabled.\n");
> console_loglevel = orig_log_level;
I'm missing the point of that kfifo stuff; afaict it only ever buffers
_1_ key, might as well use a simple variable, no?
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [RFT v2] tty/sysrq: Make sysrq handler NMI aware
2022-02-28 12:07 ` Peter Zijlstra
@ 2022-02-28 13:28 ` Sumit Garg
0 siblings, 0 replies; 3+ messages in thread
From: Sumit Garg @ 2022-02-28 13:28 UTC (permalink / raw)
To: Peter Zijlstra
Cc: linux-serial, hasegawa-hitomi, gregkh, jirislaby, jason.wessel,
daniel.thompson, dianders, linux-kernel, kgdb-bugreport, arnd
Hi Peter,
Thanks for your review.
On Mon, 28 Feb 2022 at 17:37, Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Mon, Feb 28, 2022 at 01:23:51PM +0530, Sumit Garg wrote:
> > Allow a magic sysrq to be triggered from an NMI context. This is done
>
> *why* though?
>
I should have copied the reasoning from v1 cover letter [1] to this
single patch as well. Will do it in v3. The basic idea is to enhance
kernel's NMI debuggability for CPUs stuck in hard lockups. As an
example, one should be able to launch kdb as well as other diagnostics
offered by magic sysrq in NMI context.
[1] https://lore.kernel.org/linux-arm-kernel/1595333413-30052-1-git-send-email-sumit.garg@linaro.org/
>
> > +#define SYSRQ_NMI_FIFO_SIZE 2
> > +static DEFINE_KFIFO(sysrq_nmi_fifo, int, SYSRQ_NMI_FIFO_SIZE);
> > +
> > +static void sysrq_do_nmi_work(struct irq_work *work)
>
> That naming don't make sense, it does the !NMI work, from IRQ context.
>
Will rename it to sysrq_do_irq_work().
> > +{
> > + const struct sysrq_key_op *op_p;
> > + int orig_suppress_printk;
> > + int key;
> > +
> > + orig_suppress_printk = suppress_printk;
> > + suppress_printk = 0;
> > +
> > + rcu_sysrq_start();
> > + rcu_read_lock();
> > +
> > + if (kfifo_peek(&sysrq_nmi_fifo, &key)) {
> > + op_p = __sysrq_get_key_op(key);
> > + if (op_p)
> > + op_p->handler(key);
> > + }
> > +
> > + rcu_read_unlock();
> > + rcu_sysrq_end();
> > +
> > + suppress_printk = orig_suppress_printk;
> > +
> > + kfifo_reset_out(&sysrq_nmi_fifo);
> > +}
> > +
> > +static DEFINE_IRQ_WORK(sysrq_nmi_work, sysrq_do_nmi_work);
> > +
> > void __handle_sysrq(int key, bool check_mask)
> > {
> > const struct sysrq_key_op *op_p;
> > @@ -573,6 +612,10 @@ void __handle_sysrq(int key, bool check_mask)
> > int orig_suppress_printk;
> > int i;
> >
> > + /* Skip sysrq handling if one already in progress */
> > + if (!kfifo_is_empty(&sysrq_nmi_fifo))
> > + return;
> > +
> > orig_suppress_printk = suppress_printk;
> > suppress_printk = 0;
> >
> > @@ -596,7 +639,13 @@ void __handle_sysrq(int key, bool check_mask)
> > if (!check_mask || sysrq_on_mask(op_p->enable_mask)) {
> > pr_info("%s\n", op_p->action_msg);
> > console_loglevel = orig_log_level;
> > - op_p->handler(key);
> > +
> > + if (in_nmi() && !op_p->nmi_safe) {
> > + kfifo_put(&sysrq_nmi_fifo, key);
> > + irq_work_queue(&sysrq_nmi_work);
> > + } else {
> > + op_p->handler(key);
> > + }
> > } else {
> > pr_info("This sysrq operation is disabled.\n");
> > console_loglevel = orig_log_level;
>
> I'm missing the point of that kfifo stuff; afaict it only ever buffers
> _1_ key, might as well use a simple variable, no?
Yeah you are right, using a single key buffer should also suffice. The
original idea was to queue multiple sysrq and handle them one by one
but that turned out to be unsafe.
-Sumit
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2022-02-28 13:28 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-28 7:53 [RFT v2] tty/sysrq: Make sysrq handler NMI aware Sumit Garg
2022-02-28 12:07 ` Peter Zijlstra
2022-02-28 13:28 ` Sumit Garg
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®