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