1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Support for warning track interruption
4 *
5 * Copyright IBM Corp. 2023
6 */
7
8 #include <linux/cpu.h>
9 #include <linux/debugfs.h>
10 #include <linux/kallsyms.h>
11 #include <linux/smpboot.h>
12 #include <linux/irq.h>
13 #include <uapi/linux/sched/types.h>
14 #include <asm/debug.h>
15 #include <asm/diag.h>
16 #include <asm/sclp.h>
17
18 #define WTI_DBF_LEN 64
19
20 struct wti_debug {
21 unsigned long missed;
22 unsigned long addr;
23 pid_t pid;
24 };
25
26 struct wti_state {
27 /* debug data for s390dbf */
28 struct wti_debug dbg;
29 /*
30 * Represents the real-time thread responsible to
31 * acknowledge the warning-track interrupt and trigger
32 * preliminary and postliminary precautions.
33 */
34 struct task_struct *thread;
35 /*
36 * If pending is true, the real-time thread must be scheduled.
37 * If not, a wake up of that thread will remain a noop.
38 */
39 bool pending;
40 };
41
42 static DEFINE_PER_CPU(struct wti_state, wti_state);
43
44 static debug_info_t *wti_dbg;
45
46 /*
47 * During a warning-track grace period, interrupts are disabled
48 * to prevent delays of the warning-track acknowledgment.
49 *
50 * Once the CPU is physically dispatched again, interrupts are
51 * re-enabled.
52 */
53
wti_irq_disable(void)54 static void wti_irq_disable(void)
55 {
56 unsigned long flags;
57 struct ctlreg cr6;
58
59 local_irq_save(flags);
60 local_ctl_store(6, &cr6);
61 /* disable all I/O interrupts */
62 cr6.val &= ~0xff000000UL;
63 local_ctl_load(6, &cr6);
64 local_irq_restore(flags);
65 }
66
wti_irq_enable(void)67 static void wti_irq_enable(void)
68 {
69 unsigned long flags;
70 struct ctlreg cr6;
71
72 local_irq_save(flags);
73 local_ctl_store(6, &cr6);
74 /* enable all I/O interrupts */
75 cr6.val |= 0xff000000UL;
76 local_ctl_load(6, &cr6);
77 local_irq_restore(flags);
78 }
79
store_debug_data(struct wti_state * st)80 static void store_debug_data(struct wti_state *st)
81 {
82 struct pt_regs *regs = get_irq_regs();
83
84 st->dbg.pid = current->pid;
85 st->dbg.addr = 0;
86 if (!user_mode(regs))
87 st->dbg.addr = regs->psw.addr;
88 }
89
wti_interrupt(struct ext_code ext_code,unsigned int param32,unsigned long param64)90 static void wti_interrupt(struct ext_code ext_code,
91 unsigned int param32, unsigned long param64)
92 {
93 struct wti_state *st = this_cpu_ptr(&wti_state);
94
95 inc_irq_stat(IRQEXT_WTI);
96 wti_irq_disable();
97 store_debug_data(st);
98 st->pending = true;
99 wake_up_process(st->thread);
100 }
101
wti_pending(unsigned int cpu)102 static int wti_pending(unsigned int cpu)
103 {
104 struct wti_state *st = per_cpu_ptr(&wti_state, cpu);
105
106 return st->pending;
107 }
108
wti_dbf_grace_period(struct wti_state * st)109 static void wti_dbf_grace_period(struct wti_state *st)
110 {
111 struct wti_debug *wdi = &st->dbg;
112 char buf[WTI_DBF_LEN];
113
114 if (wdi->addr)
115 snprintf(buf, sizeof(buf), "%d %pS", wdi->pid, (void *)wdi->addr);
116 else
117 snprintf(buf, sizeof(buf), "%d <user>", wdi->pid);
118 debug_text_event(wti_dbg, 2, buf);
119 wdi->missed++;
120 }
121
wti_show(struct seq_file * seq,void * v)122 static int wti_show(struct seq_file *seq, void *v)
123 {
124 struct wti_state *st;
125 int cpu;
126
127 cpus_read_lock();
128 seq_puts(seq, " ");
129 for_each_online_cpu(cpu)
130 seq_printf(seq, "CPU%-8d", cpu);
131 seq_putc(seq, '\n');
132 for_each_online_cpu(cpu) {
133 st = per_cpu_ptr(&wti_state, cpu);
134 seq_printf(seq, " %10lu", st->dbg.missed);
135 }
136 seq_putc(seq, '\n');
137 cpus_read_unlock();
138 return 0;
139 }
140 DEFINE_SHOW_ATTRIBUTE(wti);
141
wti_thread_fn(unsigned int cpu)142 static void wti_thread_fn(unsigned int cpu)
143 {
144 struct wti_state *st = per_cpu_ptr(&wti_state, cpu);
145
146 st->pending = false;
147 /*
148 * Yield CPU voluntarily to the hypervisor. Control
149 * resumes when hypervisor decides to dispatch CPU
150 * to this LPAR again.
151 */
152 if (diag49c(DIAG49C_SUBC_ACK))
153 wti_dbf_grace_period(st);
154 wti_irq_enable();
155 }
156
157 static struct smp_hotplug_thread wti_threads = {
158 .store = &wti_state.thread,
159 .thread_should_run = wti_pending,
160 .thread_fn = wti_thread_fn,
161 .thread_comm = "cpuwti/%u",
162 .selfparking = false,
163 };
164
wti_init(void)165 static int __init wti_init(void)
166 {
167 struct sched_param wti_sched_param = { .sched_priority = MAX_RT_PRIO - 1 };
168 struct dentry *wti_dir;
169 struct wti_state *st;
170 int cpu, rc;
171
172 rc = -EOPNOTSUPP;
173 if (!sclp.has_wti)
174 goto out;
175 rc = smpboot_register_percpu_thread(&wti_threads);
176 if (WARN_ON(rc))
177 goto out;
178 for_each_online_cpu(cpu) {
179 st = per_cpu_ptr(&wti_state, cpu);
180 sched_setscheduler(st->thread, SCHED_FIFO, &wti_sched_param);
181 }
182 rc = register_external_irq(EXT_IRQ_WARNING_TRACK, wti_interrupt);
183 if (rc) {
184 pr_warn("Couldn't request external interrupt 0x1007\n");
185 goto out_thread;
186 }
187 irq_subclass_register(IRQ_SUBCLASS_WARNING_TRACK);
188 rc = diag49c(DIAG49C_SUBC_REG);
189 if (rc) {
190 pr_warn("Failed to register warning track interrupt through DIAG 49C\n");
191 rc = -EOPNOTSUPP;
192 goto out_subclass;
193 }
194 wti_dir = debugfs_create_dir("wti", arch_debugfs_dir);
195 debugfs_create_file("stat", 0400, wti_dir, NULL, &wti_fops);
196 wti_dbg = debug_register("wti", 1, 1, WTI_DBF_LEN);
197 if (!wti_dbg) {
198 rc = -ENOMEM;
199 goto out_debug_register;
200 }
201 rc = debug_register_view(wti_dbg, &debug_hex_ascii_view);
202 if (rc)
203 goto out_debug_register;
204 goto out;
205 out_debug_register:
206 debug_unregister(wti_dbg);
207 out_subclass:
208 irq_subclass_unregister(IRQ_SUBCLASS_WARNING_TRACK);
209 unregister_external_irq(EXT_IRQ_WARNING_TRACK, wti_interrupt);
210 out_thread:
211 smpboot_unregister_percpu_thread(&wti_threads);
212 out:
213 return rc;
214 }
215 late_initcall(wti_init);
216