s390/wti: Add wti accounting for missed grace periods

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>
This commit is contained in:
Tobias Huschle 2024-08-12 13:39:29 +02:00 committed by Vasily Gorbik
parent cafeff5a03
commit 42419bcdfd

View 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);