shared_intr.c revision 1.25 1 1.25 thorpej /* $NetBSD: shared_intr.c,v 1.25 2020/09/25 03:40:11 thorpej Exp $ */
2 1.25 thorpej
3 1.25 thorpej /*
4 1.25 thorpej * Copyright (c) 2020 The NetBSD Foundation, Inc.
5 1.25 thorpej * All rights reserved.
6 1.25 thorpej *
7 1.25 thorpej * This code is derived from software contributed to The NetBSD Foundation
8 1.25 thorpej * by Jason R. Thorpe.
9 1.25 thorpej *
10 1.25 thorpej * Redistribution and use in source and binary forms, with or without
11 1.25 thorpej * modification, are permitted provided that the following conditions
12 1.25 thorpej * are met:
13 1.25 thorpej * 1. Redistributions of source code must retain the above copyright
14 1.25 thorpej * notice, this list of conditions and the following disclaimer.
15 1.25 thorpej * 2. Redistributions in binary form must reproduce the above copyright
16 1.25 thorpej * notice, this list of conditions and the following disclaimer in the
17 1.25 thorpej * documentation and/or other materials provided with the distribution.
18 1.25 thorpej *
19 1.25 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.25 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.25 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.25 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.25 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.25 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.25 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.25 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.25 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.25 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.25 thorpej * POSSIBILITY OF SUCH DAMAGE.
30 1.25 thorpej */
31 1.1 cgd
32 1.1 cgd /*
33 1.1 cgd * Copyright (c) 1996 Carnegie-Mellon University.
34 1.1 cgd * All rights reserved.
35 1.1 cgd *
36 1.1 cgd * Authors: Chris G. Demetriou
37 1.21 matt *
38 1.1 cgd * Permission to use, copy, modify and distribute this software and
39 1.1 cgd * its documentation is hereby granted, provided that both the copyright
40 1.1 cgd * notice and this permission notice appear in all copies of the
41 1.1 cgd * software, derivative works or modified versions, and any portions
42 1.1 cgd * thereof, and that both notices appear in supporting documentation.
43 1.21 matt *
44 1.21 matt * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
45 1.21 matt * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
46 1.1 cgd * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
47 1.21 matt *
48 1.1 cgd * Carnegie Mellon requests users of this software to return to
49 1.1 cgd *
50 1.1 cgd * Software Distribution Coordinator or Software.Distribution (at) CS.CMU.EDU
51 1.1 cgd * School of Computer Science
52 1.1 cgd * Carnegie Mellon University
53 1.1 cgd * Pittsburgh PA 15213-3890
54 1.1 cgd *
55 1.1 cgd * any improvements or extensions that they make and grant Carnegie the
56 1.1 cgd * rights to redistribute these changes.
57 1.1 cgd */
58 1.1 cgd
59 1.1 cgd /*
60 1.1 cgd * Common shared-interrupt-line functionality.
61 1.1 cgd */
62 1.2 cgd
63 1.3 cgd #include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
64 1.3 cgd
65 1.25 thorpej __KERNEL_RCSID(0, "$NetBSD: shared_intr.c,v 1.25 2020/09/25 03:40:11 thorpej Exp $");
66 1.1 cgd
67 1.1 cgd #include <sys/param.h>
68 1.7 thorpej #include <sys/kernel.h>
69 1.25 thorpej #include <sys/cpu.h>
70 1.25 thorpej #include <sys/kmem.h>
71 1.25 thorpej #include <sys/kmem.h>
72 1.1 cgd #include <sys/systm.h>
73 1.1 cgd #include <sys/syslog.h>
74 1.1 cgd #include <sys/queue.h>
75 1.19 ad #include <sys/atomic.h>
76 1.19 ad #include <sys/intr.h>
77 1.25 thorpej #include <sys/xcall.h>
78 1.1 cgd
79 1.20 dsl static const char *intr_typename(int);
80 1.1 cgd
81 1.1 cgd static const char *
82 1.14 thorpej intr_typename(int type)
83 1.1 cgd {
84 1.1 cgd
85 1.1 cgd switch (type) {
86 1.1 cgd case IST_UNUSABLE:
87 1.1 cgd return ("disabled");
88 1.1 cgd case IST_NONE:
89 1.1 cgd return ("none");
90 1.1 cgd case IST_PULSE:
91 1.1 cgd return ("pulsed");
92 1.1 cgd case IST_EDGE:
93 1.1 cgd return ("edge-triggered");
94 1.1 cgd case IST_LEVEL:
95 1.1 cgd return ("level-triggered");
96 1.1 cgd }
97 1.1 cgd panic("intr_typename: unknown type %d", type);
98 1.1 cgd }
99 1.1 cgd
100 1.1 cgd struct alpha_shared_intr *
101 1.15 thorpej alpha_shared_intr_alloc(unsigned int n, unsigned int namesize)
102 1.1 cgd {
103 1.1 cgd struct alpha_shared_intr *intr;
104 1.1 cgd unsigned int i;
105 1.1 cgd
106 1.25 thorpej intr = kmem_alloc(n * sizeof(*intr), KM_SLEEP);
107 1.1 cgd for (i = 0; i < n; i++) {
108 1.1 cgd TAILQ_INIT(&intr[i].intr_q);
109 1.1 cgd intr[i].intr_sharetype = IST_NONE;
110 1.1 cgd intr[i].intr_dfltsharetype = IST_NONE;
111 1.1 cgd intr[i].intr_nstrays = 0;
112 1.1 cgd intr[i].intr_maxstrays = 5;
113 1.11 mjacob intr[i].intr_private = NULL;
114 1.25 thorpej intr[i].intr_cpu = NULL;
115 1.15 thorpej if (namesize != 0) {
116 1.25 thorpej intr[i].intr_string = kmem_zalloc(namesize, KM_SLEEP);
117 1.25 thorpej } else {
118 1.15 thorpej intr[i].intr_string = NULL;
119 1.25 thorpej }
120 1.1 cgd }
121 1.1 cgd
122 1.1 cgd return (intr);
123 1.1 cgd }
124 1.1 cgd
125 1.1 cgd int
126 1.14 thorpej alpha_shared_intr_dispatch(struct alpha_shared_intr *intr, unsigned int num)
127 1.1 cgd {
128 1.1 cgd struct alpha_shared_intrhand *ih;
129 1.1 cgd int rv, handled;
130 1.1 cgd
131 1.19 ad atomic_add_long(&intr[num].intr_evcnt.ev_count, 1);
132 1.15 thorpej
133 1.1 cgd ih = intr[num].intr_q.tqh_first;
134 1.1 cgd handled = 0;
135 1.1 cgd while (ih != NULL) {
136 1.1 cgd
137 1.1 cgd /*
138 1.1 cgd * The handler returns one of three values:
139 1.1 cgd * 0: This interrupt wasn't for me.
140 1.1 cgd * 1: This interrupt was for me.
141 1.1 cgd * -1: This interrupt might have been for me, but I can't say
142 1.1 cgd * for sure.
143 1.1 cgd */
144 1.23 thorpej
145 1.24 thorpej rv = (*ih->ih_fn)(ih->ih_arg);
146 1.1 cgd
147 1.1 cgd handled = handled || (rv != 0);
148 1.1 cgd ih = ih->ih_q.tqe_next;
149 1.1 cgd }
150 1.1 cgd
151 1.1 cgd return (handled);
152 1.1 cgd }
153 1.1 cgd
154 1.24 thorpej static int
155 1.24 thorpej alpha_shared_intr_wrapper(void * const arg)
156 1.24 thorpej {
157 1.24 thorpej struct alpha_shared_intrhand * const ih = arg;
158 1.24 thorpej int rv;
159 1.24 thorpej
160 1.24 thorpej KERNEL_LOCK(1, NULL);
161 1.24 thorpej rv = (*ih->ih_real_fn)(ih->ih_real_arg);
162 1.24 thorpej KERNEL_UNLOCK_ONE(NULL);
163 1.24 thorpej
164 1.24 thorpej return rv;
165 1.24 thorpej }
166 1.24 thorpej
167 1.25 thorpej struct alpha_shared_intrhand *
168 1.25 thorpej alpha_shared_intr_alloc_intrhand(struct alpha_shared_intr *intr,
169 1.25 thorpej unsigned int num, int type, int level, int flags,
170 1.23 thorpej int (*fn)(void *), void *arg, const char *basename)
171 1.1 cgd {
172 1.1 cgd struct alpha_shared_intrhand *ih;
173 1.1 cgd
174 1.1 cgd if (intr[num].intr_sharetype == IST_UNUSABLE) {
175 1.25 thorpej printf("%s: %s %d: unusable\n", __func__,
176 1.1 cgd basename, num);
177 1.1 cgd return NULL;
178 1.1 cgd }
179 1.1 cgd
180 1.25 thorpej KASSERT(type != IST_NONE);
181 1.25 thorpej
182 1.25 thorpej ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
183 1.25 thorpej
184 1.25 thorpej ih->ih_intrhead = intr;
185 1.25 thorpej ih->ih_fn = ih->ih_real_fn = fn;
186 1.25 thorpej ih->ih_arg = ih->ih_real_arg = arg;
187 1.25 thorpej ih->ih_level = level;
188 1.25 thorpej ih->ih_type = type;
189 1.25 thorpej ih->ih_num = num;
190 1.25 thorpej
191 1.25 thorpej /*
192 1.25 thorpej * Non-MPSAFE interrupts get a wrapper that takes the
193 1.25 thorpej * KERNEL_LOCK.
194 1.25 thorpej */
195 1.25 thorpej if ((flags & ALPHA_INTR_MPSAFE) == 0) {
196 1.25 thorpej ih->ih_fn = alpha_shared_intr_wrapper;
197 1.25 thorpej ih->ih_arg = ih;
198 1.25 thorpej }
199 1.25 thorpej
200 1.25 thorpej return (ih);
201 1.25 thorpej }
202 1.25 thorpej
203 1.25 thorpej void
204 1.25 thorpej alpha_shared_intr_free_intrhand(struct alpha_shared_intrhand *ih)
205 1.25 thorpej {
206 1.25 thorpej
207 1.25 thorpej kmem_free(ih, sizeof(*ih));
208 1.25 thorpej }
209 1.25 thorpej
210 1.25 thorpej static void
211 1.25 thorpej alpha_shared_intr_link_unlink_xcall(void *arg1, void *arg2)
212 1.25 thorpej {
213 1.25 thorpej struct alpha_shared_intrhand *ih = arg1;
214 1.25 thorpej struct alpha_shared_intr *intr = ih->ih_intrhead;
215 1.25 thorpej struct cpu_info *ci = intr->intr_cpu;
216 1.25 thorpej unsigned int num = ih->ih_num;
217 1.25 thorpej
218 1.25 thorpej KASSERT(ci == curcpu() || !mp_online);
219 1.25 thorpej KASSERT(!cpu_intr_p());
220 1.25 thorpej
221 1.25 thorpej const unsigned long psl = alpha_pal_swpipl(ALPHA_PSL_IPL_HIGH);
222 1.25 thorpej
223 1.25 thorpej if (arg2 != NULL) {
224 1.25 thorpej TAILQ_INSERT_TAIL(&intr[num].intr_q, ih, ih_q);
225 1.25 thorpej ci->ci_nintrhand++;
226 1.25 thorpej } else {
227 1.25 thorpej TAILQ_REMOVE(&intr[num].intr_q, ih, ih_q);
228 1.25 thorpej ci->ci_nintrhand--;
229 1.25 thorpej }
230 1.25 thorpej
231 1.25 thorpej alpha_pal_swpipl(psl);
232 1.25 thorpej }
233 1.25 thorpej
234 1.25 thorpej bool
235 1.25 thorpej alpha_shared_intr_link(struct alpha_shared_intr *intr,
236 1.25 thorpej struct alpha_shared_intrhand *ih, const char *basename)
237 1.25 thorpej {
238 1.25 thorpej int type = ih->ih_type;
239 1.25 thorpej unsigned int num = ih->ih_num;
240 1.25 thorpej
241 1.25 thorpej KASSERT(mutex_owned(&cpu_lock));
242 1.25 thorpej KASSERT(ih->ih_intrhead == intr);
243 1.1 cgd
244 1.1 cgd switch (intr[num].intr_sharetype) {
245 1.1 cgd case IST_EDGE:
246 1.1 cgd case IST_LEVEL:
247 1.1 cgd if (type == intr[num].intr_sharetype)
248 1.1 cgd break;
249 1.1 cgd case IST_PULSE:
250 1.1 cgd if (type != IST_NONE) {
251 1.10 thorpej if (intr[num].intr_q.tqh_first == NULL) {
252 1.1 cgd printf("alpha_shared_intr_establish: %s %d: warning: using %s on %s\n",
253 1.1 cgd basename, num, intr_typename(type),
254 1.1 cgd intr_typename(intr[num].intr_sharetype));
255 1.1 cgd type = intr[num].intr_sharetype;
256 1.1 cgd } else {
257 1.25 thorpej printf("alpha_shared_intr_establish: %s %d: can't share %s with %s\n",
258 1.1 cgd basename, num, intr_typename(type),
259 1.1 cgd intr_typename(intr[num].intr_sharetype));
260 1.25 thorpej return (false);
261 1.1 cgd }
262 1.1 cgd }
263 1.1 cgd break;
264 1.1 cgd
265 1.1 cgd case IST_NONE:
266 1.1 cgd /* not currently used; safe */
267 1.1 cgd break;
268 1.1 cgd }
269 1.1 cgd
270 1.25 thorpej intr[num].intr_sharetype = type;
271 1.24 thorpej
272 1.24 thorpej /*
273 1.25 thorpej * If a CPU hasn't been assigned yet, just give it to the
274 1.25 thorpej * primary.
275 1.24 thorpej */
276 1.25 thorpej if (intr->intr_cpu == NULL) {
277 1.25 thorpej intr->intr_cpu = &cpu_info_primary;
278 1.24 thorpej }
279 1.1 cgd
280 1.25 thorpej kpreempt_disable();
281 1.25 thorpej if (intr->intr_cpu == curcpu() || !mp_online) {
282 1.25 thorpej alpha_shared_intr_link_unlink_xcall(ih, intr);
283 1.25 thorpej } else {
284 1.25 thorpej uint64_t where = xc_unicast(XC_HIGHPRI,
285 1.25 thorpej alpha_shared_intr_link_unlink_xcall, ih, intr,
286 1.25 thorpej intr->intr_cpu);
287 1.25 thorpej xc_wait(where);
288 1.25 thorpej }
289 1.25 thorpej kpreempt_enable();
290 1.1 cgd
291 1.25 thorpej return (true);
292 1.6 thorpej }
293 1.6 thorpej
294 1.6 thorpej void
295 1.25 thorpej alpha_shared_intr_unlink(struct alpha_shared_intr *intr,
296 1.25 thorpej struct alpha_shared_intrhand *ih, const char *basename)
297 1.6 thorpej {
298 1.6 thorpej
299 1.25 thorpej KASSERT(mutex_owned(&cpu_lock));
300 1.25 thorpej
301 1.25 thorpej kpreempt_disable();
302 1.25 thorpej if (intr->intr_cpu == curcpu() || !mp_online) {
303 1.25 thorpej alpha_shared_intr_link_unlink_xcall(ih, NULL);
304 1.25 thorpej } else {
305 1.25 thorpej uint64_t where = xc_unicast(XC_HIGHPRI,
306 1.25 thorpej alpha_shared_intr_link_unlink_xcall, ih, NULL,
307 1.25 thorpej intr->intr_cpu);
308 1.25 thorpej xc_wait(where);
309 1.25 thorpej }
310 1.25 thorpej kpreempt_enable();
311 1.1 cgd }
312 1.1 cgd
313 1.1 cgd int
314 1.14 thorpej alpha_shared_intr_get_sharetype(struct alpha_shared_intr *intr,
315 1.14 thorpej unsigned int num)
316 1.1 cgd {
317 1.1 cgd
318 1.1 cgd return (intr[num].intr_sharetype);
319 1.1 cgd }
320 1.1 cgd
321 1.1 cgd int
322 1.14 thorpej alpha_shared_intr_isactive(struct alpha_shared_intr *intr, unsigned int num)
323 1.1 cgd {
324 1.1 cgd
325 1.1 cgd return (intr[num].intr_q.tqh_first != NULL);
326 1.16 thorpej }
327 1.16 thorpej
328 1.16 thorpej int
329 1.16 thorpej alpha_shared_intr_firstactive(struct alpha_shared_intr *intr, unsigned int num)
330 1.16 thorpej {
331 1.16 thorpej
332 1.16 thorpej return (intr[num].intr_q.tqh_first != NULL &&
333 1.16 thorpej intr[num].intr_q.tqh_first->ih_q.tqe_next == NULL);
334 1.1 cgd }
335 1.1 cgd
336 1.1 cgd void
337 1.14 thorpej alpha_shared_intr_set_dfltsharetype(struct alpha_shared_intr *intr,
338 1.14 thorpej unsigned int num, int newdfltsharetype)
339 1.1 cgd {
340 1.1 cgd
341 1.1 cgd #ifdef DIAGNOSTIC
342 1.1 cgd if (alpha_shared_intr_isactive(intr, num))
343 1.1 cgd panic("alpha_shared_intr_set_dfltsharetype on active intr");
344 1.1 cgd #endif
345 1.1 cgd
346 1.1 cgd intr[num].intr_dfltsharetype = newdfltsharetype;
347 1.1 cgd intr[num].intr_sharetype = intr[num].intr_dfltsharetype;
348 1.1 cgd }
349 1.1 cgd
350 1.1 cgd void
351 1.14 thorpej alpha_shared_intr_set_maxstrays(struct alpha_shared_intr *intr,
352 1.14 thorpej unsigned int num, int newmaxstrays)
353 1.1 cgd {
354 1.12 mjacob int s = splhigh();
355 1.1 cgd intr[num].intr_maxstrays = newmaxstrays;
356 1.1 cgd intr[num].intr_nstrays = 0;
357 1.12 mjacob splx(s);
358 1.1 cgd }
359 1.1 cgd
360 1.1 cgd void
361 1.17 thorpej alpha_shared_intr_reset_strays(struct alpha_shared_intr *intr,
362 1.17 thorpej unsigned int num)
363 1.17 thorpej {
364 1.17 thorpej
365 1.17 thorpej /*
366 1.17 thorpej * Don't bother blocking interrupts; this doesn't have to be
367 1.17 thorpej * precise, but it does need to be fast.
368 1.17 thorpej */
369 1.17 thorpej intr[num].intr_nstrays = 0;
370 1.17 thorpej }
371 1.17 thorpej
372 1.17 thorpej void
373 1.14 thorpej alpha_shared_intr_stray(struct alpha_shared_intr *intr, unsigned int num,
374 1.14 thorpej const char *basename)
375 1.1 cgd {
376 1.1 cgd
377 1.1 cgd intr[num].intr_nstrays++;
378 1.5 thorpej
379 1.5 thorpej if (intr[num].intr_maxstrays == 0)
380 1.5 thorpej return;
381 1.5 thorpej
382 1.1 cgd if (intr[num].intr_nstrays <= intr[num].intr_maxstrays)
383 1.1 cgd log(LOG_ERR, "stray %s %d%s\n", basename, num,
384 1.1 cgd intr[num].intr_nstrays >= intr[num].intr_maxstrays ?
385 1.1 cgd "; stopped logging" : "");
386 1.8 thorpej }
387 1.8 thorpej
388 1.8 thorpej void
389 1.14 thorpej alpha_shared_intr_set_private(struct alpha_shared_intr *intr,
390 1.14 thorpej unsigned int num, void *v)
391 1.8 thorpej {
392 1.8 thorpej
393 1.8 thorpej intr[num].intr_private = v;
394 1.8 thorpej }
395 1.8 thorpej
396 1.8 thorpej void *
397 1.14 thorpej alpha_shared_intr_get_private(struct alpha_shared_intr *intr,
398 1.14 thorpej unsigned int num)
399 1.8 thorpej {
400 1.8 thorpej
401 1.8 thorpej return (intr[num].intr_private);
402 1.15 thorpej }
403 1.15 thorpej
404 1.25 thorpej void
405 1.25 thorpej alpha_shared_intr_set_cpu(struct alpha_shared_intr *intr, unsigned int num,
406 1.25 thorpej struct cpu_info *ci)
407 1.25 thorpej {
408 1.25 thorpej
409 1.25 thorpej intr[num].intr_cpu = ci;
410 1.25 thorpej }
411 1.25 thorpej
412 1.25 thorpej struct cpu_info *
413 1.25 thorpej alpha_shared_intr_get_cpu(struct alpha_shared_intr *intr, unsigned int num)
414 1.25 thorpej {
415 1.25 thorpej
416 1.25 thorpej return (intr[num].intr_cpu);
417 1.25 thorpej }
418 1.25 thorpej
419 1.15 thorpej struct evcnt *
420 1.15 thorpej alpha_shared_intr_evcnt(struct alpha_shared_intr *intr,
421 1.15 thorpej unsigned int num)
422 1.15 thorpej {
423 1.15 thorpej
424 1.15 thorpej return (&intr[num].intr_evcnt);
425 1.15 thorpej }
426 1.15 thorpej
427 1.15 thorpej char *
428 1.15 thorpej alpha_shared_intr_string(struct alpha_shared_intr *intr,
429 1.15 thorpej unsigned int num)
430 1.15 thorpej {
431 1.15 thorpej
432 1.15 thorpej return (intr[num].intr_string);
433 1.1 cgd }
434