* 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
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.
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)
{
inc_irq_stat(IRQEXT_WTI);
wti_irq_disable();
+ store_debug_data(st);
st->pending = true;
wake_up_process(st->thread);
}
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);
* 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();
}
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);