]> Git Repo - J-linux.git/blob - arch/s390/kernel/wti.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / arch / s390 / kernel / wti.c
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
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
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
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
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
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
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
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
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
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);
This page took 0.0372 seconds and 4 git commands to generate.