Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Tobias Huschle and committed by
Vasily Gorbik
42419bcd cafeff5a

+50 -1
+50 -1
arch/s390/kernel/wti.c
··· 5 5 * Copyright IBM Corp. 2023 6 6 */ 7 7 8 + #include <linux/kallsyms.h> 8 9 #include <linux/smpboot.h> 9 10 #include <linux/irq.h> 10 11 #include <uapi/linux/sched/types.h> 12 + #include <asm/debug.h> 11 13 #include <asm/diag.h> 12 14 #include <asm/sclp.h> 13 15 16 + #define WTI_DBF_LEN 64 17 + 18 + struct wti_debug { 19 + unsigned long missed; 20 + unsigned long addr; 21 + pid_t pid; 22 + }; 23 + 14 24 struct wti_state { 25 + /* debug data for s390dbf */ 26 + struct wti_debug dbg; 15 27 /* 16 28 * Represents the real-time thread responsible to 17 29 * acknowledge the warning-track interrupt and trigger ··· 38 26 }; 39 27 40 28 static DEFINE_PER_CPU(struct wti_state, wti_state); 29 + 30 + static debug_info_t *wti_dbg; 41 31 42 32 /* 43 33 * During a warning-track grace period, interrupts are disabled ··· 75 61 local_irq_restore(flags); 76 62 } 77 63 64 + static void store_debug_data(struct wti_state *st) 65 + { 66 + struct pt_regs *regs = get_irq_regs(); 67 + 68 + st->dbg.pid = current->pid; 69 + st->dbg.addr = 0; 70 + if (!user_mode(regs)) 71 + st->dbg.addr = regs->psw.addr; 72 + } 73 + 78 74 static void wti_interrupt(struct ext_code ext_code, 79 75 unsigned int param32, unsigned long param64) 80 76 { ··· 92 68 93 69 inc_irq_stat(IRQEXT_WTI); 94 70 wti_irq_disable(); 71 + store_debug_data(st); 95 72 st->pending = true; 96 73 wake_up_process(st->thread); 97 74 } ··· 102 77 struct wti_state *st = per_cpu_ptr(&wti_state, cpu); 103 78 104 79 return st->pending; 80 + } 81 + 82 + static void wti_dbf_grace_period(struct wti_state *st) 83 + { 84 + struct wti_debug *wdi = &st->dbg; 85 + char buf[WTI_DBF_LEN]; 86 + 87 + if (wdi->addr) 88 + snprintf(buf, sizeof(buf), "%d %pS", wdi->pid, (void *)wdi->addr); 89 + else 90 + snprintf(buf, sizeof(buf), "%d <user>", wdi->pid); 91 + debug_text_event(wti_dbg, 2, buf); 92 + wdi->missed++; 105 93 } 106 94 107 95 static void wti_thread_fn(unsigned int cpu) ··· 127 89 * resumes when hypervisor decides to dispatch CPU 128 90 * to this LPAR again. 129 91 */ 130 - diag49c(DIAG49C_SUBC_ACK); 92 + if (diag49c(DIAG49C_SUBC_ACK)) 93 + wti_dbf_grace_period(st); 131 94 wti_irq_enable(); 132 95 } 133 96 ··· 168 129 rc = -EOPNOTSUPP; 169 130 goto out_subclass; 170 131 } 132 + wti_dbg = debug_register("wti", 1, 1, WTI_DBF_LEN); 133 + if (!wti_dbg) { 134 + rc = -ENOMEM; 135 + goto out_debug_register; 136 + } 137 + rc = debug_register_view(wti_dbg, &debug_hex_ascii_view); 138 + if (rc) 139 + goto out_debug_register; 171 140 goto out; 141 + out_debug_register: 142 + debug_unregister(wti_dbg); 172 143 out_subclass: 173 144 irq_subclass_unregister(IRQ_SUBCLASS_WARNING_TRACK); 174 145 unregister_external_irq(EXT_IRQ_WARNING_TRACK, wti_interrupt);