]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
s390/wti: Add wti accounting for missed grace periods
authorTobias Huschle <huschle@linux.ibm.com>
Mon, 12 Aug 2024 11:39:29 +0000 (13:39 +0200)
committerVasily Gorbik <gor@linux.ibm.com>
Thu, 29 Aug 2024 20:56:34 +0000 (22:56 +0200)
A virtual CPU that has received a warning-track interrupt may fail to
acknowledge the interrupt within the warning-track grace period.
While this is usually not a problem, it will become necessary to
investigate if there is a large number of such missed warning-track
interrupts. Therefore, it is necessary to track these events.
The information is tracked through the s390 debug facility and can be
found under /sys/kernel/debug/s390dbf/wti/.

The hex_ascii output is formatted as:
 <pid> <symbol>

The values pid and current psw are collected when a warning track
interrupt is received. Symbol is either the kernel symbol matching the
collected psw or redacted to <user> when running in user space.

Each line represents the currently executing process when a warning
track interrupt was received which was then not acknowledged within its
grace period.

Acked-by: Heiko Carstens <hca@linux.ibm.com>
Reviewed-by: Mete Durlu <meted@linux.ibm.com>
Signed-off-by: Tobias Huschle <huschle@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
arch/s390/kernel/wti.c

index 3abae3908e65f0df5fc3626694e60094b23f44d0..da8bbffbed9d91683fc444b506da9c9f9dc7eb04 100644 (file)
@@ -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);