diff options
-rw-r--r-- | arch/s390/kernel/wti.c | 51 |
1 files changed, 50 insertions, 1 deletions
diff --git a/arch/s390/kernel/wti.c b/arch/s390/kernel/wti.c index 3abae3908e65..da8bbffbed9d 100644 --- a/arch/s390/kernel/wti.c +++ b/arch/s390/kernel/wti.c @@ -5,13 +5,25 @@ * Copyright IBM Corp. 2023 */ +#include <linux/kallsyms.h> #include <linux/smpboot.h> #include <linux/irq.h> #include <uapi/linux/sched/types.h> +#include <asm/debug.h> #include <asm/diag.h> #include <asm/sclp.h> +#define WTI_DBF_LEN 64 + +struct wti_debug { + unsigned long missed; + unsigned long addr; + pid_t pid; +}; + struct wti_state { + /* debug data for s390dbf */ + struct wti_debug dbg; /* * Represents the real-time thread responsible to * acknowledge the warning-track interrupt and trigger @@ -27,6 +39,8 @@ struct wti_state { static DEFINE_PER_CPU(struct wti_state, wti_state); +static debug_info_t *wti_dbg; + /* * During a warning-track grace period, interrupts are disabled * to prevent delays of the warning-track acknowledgment. @@ -61,6 +75,16 @@ static void wti_irq_enable(void) local_irq_restore(flags); } +static void store_debug_data(struct wti_state *st) +{ + struct pt_regs *regs = get_irq_regs(); + + st->dbg.pid = current->pid; + st->dbg.addr = 0; + if (!user_mode(regs)) + st->dbg.addr = regs->psw.addr; +} + static void wti_interrupt(struct ext_code ext_code, unsigned int param32, unsigned long param64) { @@ -68,6 +92,7 @@ static void wti_interrupt(struct ext_code ext_code, inc_irq_stat(IRQEXT_WTI); wti_irq_disable(); + store_debug_data(st); st->pending = true; wake_up_process(st->thread); } @@ -79,6 +104,19 @@ static int wti_pending(unsigned int cpu) return st->pending; } +static void wti_dbf_grace_period(struct wti_state *st) +{ + struct wti_debug *wdi = &st->dbg; + char buf[WTI_DBF_LEN]; + + if (wdi->addr) + snprintf(buf, sizeof(buf), "%d %pS", wdi->pid, (void *)wdi->addr); + else + snprintf(buf, sizeof(buf), "%d <user>", wdi->pid); + debug_text_event(wti_dbg, 2, buf); + wdi->missed++; +} + static void wti_thread_fn(unsigned int cpu) { struct wti_state *st = per_cpu_ptr(&wti_state, cpu); @@ -89,7 +127,8 @@ static void wti_thread_fn(unsigned int cpu) * resumes when hypervisor decides to dispatch CPU * to this LPAR again. */ - diag49c(DIAG49C_SUBC_ACK); + if (diag49c(DIAG49C_SUBC_ACK)) + wti_dbf_grace_period(st); wti_irq_enable(); } @@ -129,7 +168,17 @@ static int __init wti_init(void) rc = -EOPNOTSUPP; goto out_subclass; } + wti_dbg = debug_register("wti", 1, 1, WTI_DBF_LEN); + if (!wti_dbg) { + rc = -ENOMEM; + goto out_debug_register; + } + rc = debug_register_view(wti_dbg, &debug_hex_ascii_view); + if (rc) + goto out_debug_register; goto out; +out_debug_register: + debug_unregister(wti_dbg); out_subclass: irq_subclass_unregister(IRQ_SUBCLASS_WARNING_TRACK); unregister_external_irq(EXT_IRQ_WARNING_TRACK, wti_interrupt); |