~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/s390/kernel/wti.c

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

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

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php