1.\" Copyright (c) 2001 John H. Baldwin <jhb@FreeBSD.org> 2.\" Copyright (c) 2006 Tom Rhodes <trhodes@FreeBSD.org> 3.\" Copyright (c) 2021 Mitchell Horne <mhorne@FreeBSD.org> 4.\" Copyright (c) 2022 The FreeBSD Foundation 5.\" 6.\" Portions of this documentation were written by Mitchell Horne 7.\" under sponsorship from the FreeBSD Foundation. 8.\" 9.\" Redistribution and use in source and binary forms, with or without 10.\" modification, are permitted provided that the following conditions 11.\" are met: 12.\" 1. Redistributions of source code must retain the above copyright 13.\" notice, this list of conditions and the following disclaimer. 14.\" 2. Redistributions in binary form must reproduce the above copyright 15.\" notice, this list of conditions and the following disclaimer in the 16.\" documentation and/or other materials provided with the distribution. 17.\" 18.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28.\" SUCH DAMAGE. 29.\" 30.Dd October 30, 2022 31.Dt INTR_EVENT 9 32.Os 33.Sh NAME 34.Nm intr_event_add_handler , 35.Nm intr_event_create , 36.Nm intr_event_destroy , 37.Nm intr_event_handle , 38.Nm intr_event_remove_handler , 39.Nm intr_priority 40.Nd "kernel interrupt handler and thread API" 41.Sh SYNOPSIS 42.In sys/param.h 43.In sys/bus.h 44.In sys/interrupt.h 45.Ft int 46.Fo intr_event_add_handler 47.Fa "struct intr_event *ie" 48.Fa "const char *name" 49.Fa "driver_filter_t filter" 50.Fa "driver_intr_t handler" 51.Fa "void *arg" 52.Fa "u_char pri" 53.Fa "enum intr_type flags" 54.Fa "void **cookiep" 55.Fc 56.Ft int 57.Fo intr_event_create 58.Fa "struct intr_event **event" 59.Fa "void *source" 60.Fa "int flags" 61.Fa "int irq" 62.Fa "void (*pre_ithread)(void *)" 63.Fa "void (*post_ithread)(void *)" 64.Fa "void (*post_filter)(void *)" 65.Fa "int (*assign_cpu)(void *, int)" 66.Fa "const char *fmt" 67.Fa "..." 68.Fc 69.Ft int 70.Fn intr_event_destroy "struct intr_event *ie" 71.Ft int 72.Fn intr_event_handle "struct intr_event *ie" "struct trapframe *frame" 73.Ft int 74.Fn intr_event_remove_handler "void *cookie" 75.Ft u_char 76.Fn intr_priority "enum intr_type flags" 77.Sh DESCRIPTION 78The interrupt event API provides methods to manage the registration and 79execution of interrupt handlers and their associated thread contexts. 80.Pp 81Each interrupt event in the system corresponds to a single hardware or software 82interrupt source. 83Each interrupt event maintains a list of interrupt handlers, sorted by 84priority, which will be invoked when handling the event. 85An interrupt event will typically, but not always, have an associated 86.Xr kthread 9 , 87known as the interrupt thread. 88Finally, each event contains optional callback functions which will be 89invoked before and after the handler functions themselves. 90.Pp 91An interrupt handler contains two distinct handler functions: 92the 93.Em filter 94and the thread 95.Em handler . 96The 97.Em filter 98function is run from interrupt context and is intended to perform quick 99handling such as acknowledging or masking a hardware interrupt, 100and queueing work for the ensuing thread 101.Em handler . 102Both functions are optional; each interrupt handler may choose to register a 103filter, a thread handler, or both. 104Each interrupt handler also consists of a name, 105a set of flags, 106and an opaque argument which will be passed to both the 107.Em filter 108and 109.Em handler 110functions. 111.Ss Handler Constraints 112The 113.Em filter 114function is executed inside a 115.Xr critical 9 116section. 117Therefore, filters may not yield the CPU for any reason, and may only use spin 118locks to access shared data. 119Allocating memory within a filter is not permitted. 120.Pp 121The 122.Em handler 123function executes from the context of the associated interrupt kernel thread. 124Sleeping is not permitted, but the interrupt thread may be preempted by higher 125priority threads. 126Thus, threaded handler functions may obtain non-sleepable locks, as described 127in 128.Xr locking 9 . 129Any memory or zone allocations in an interrupt thread must specify the 130.Dv M_NOWAIT 131flag, and any allocation errors must be handled. 132.Pp 133The exception to these constraints is software interrupt threads, which are 134allowed to sleep but should be allocated and scheduled using the 135.Xr swi 9 136interface. 137.Ss Function Descriptions 138The 139.Fn intr_event_create 140function creates a new interrupt event. 141The 142.Fa event 143argument points to a 144.Vt struct intr_event 145pointer that will reference the newly created event upon success. 146The 147.Fa source 148argument is an opaque pointer which will be passed to the 149.Fa pre_ithread , 150.Fa post_ithread , 151and 152.Fa post_filter 153callbacks. 154The 155.Fa flags 156argument is a mask of properties of this thread. 157The only valid flag currently for 158.Fn intr_event_create 159is 160.Dv IE_SOFT 161to specify that this interrupt thread is a software interrupt. 162The 163.Fa enable 164and 165.Fa disable 166arguments specify optional functions used to enable and disable this 167interrupt thread's interrupt source. 168The 169.Fa irq 170argument is the unique interrupt vector number corresponding to the event. 171The 172.Fa pre_ithread , 173.Fa post_ithread , 174and 175.Fa post_filter 176arguments are callback functions that are invoked at different 177points while handling an interrupt. 178This is described in more detail in the 179.Sx Handler Callbacks 180section, below. 181They may be 182.Va NULL 183to specify no callback. 184The 185.Fa assign_cpu 186argument points to a callback function that will be invoked when binding 187an interrupt to a particular CPU. 188It may be 189.Va NULL 190if binding is unsupported. 191The 192remaining arguments form a 193.Xr printf 9 194argument list that is used to build the base name of the new interrupt thread. 195The full name of an interrupt thread is formed by concatenating the base 196name of the interrupt thread with the names of all of its interrupt handlers. 197.Pp 198The 199.Fn intr_event_destroy 200function destroys a previously created interrupt event by releasing its 201resources. 202.\" The following is not true (yet): 203.\"and arranging for the backing kernel thread to terminate. 204An interrupt event can only be destroyed if it has no handlers remaining. 205.Pp 206The 207.Fn intr_event_add_handler 208function adds a new handler to an existing interrupt event specified by 209.Fa ie . 210The 211.Fa name 212argument specifies a name for this handler. 213The 214.Fa filter 215argument provide the filter function to execute. 216The 217.Fa handler 218argument provides the handler function to be executed from the 219event's interrupt thread. 220The 221.Fa arg 222argument will be passed to the 223.Fa filter 224and 225.Fa handler 226functions when they are invoked. 227The 228.Fa pri 229argument specifies the priority of this handler, 230corresponding to the values defined in 231.In sys/priority.h . 232It determines the order this handler is called relative to the other handlers 233for this event, as well as the scheduling priority of of the backing kernel 234thread. 235.Fa flags 236argument can be used to specify properties of this handler as defined in 237.In sys/bus.h . 238If 239.Fa cookiep 240is not 241.Dv NULL , 242then it will be assigned a cookie that can be used later to remove this 243handler. 244.Pp 245The 246.Fn intr_event_handle 247function is the main entry point into the interrupt handling code. 248It must be called from an interrupt context. 249The function will execute all filter handlers associated with the interrupt 250event 251.Fa ie , 252and schedule the associated interrupt thread to run, if applicable. 253The 254.Fa frame 255argument is used to pass a pointer to the 256.Vt struct trapframe 257containing the machine state at the time of the interrupt. 258The main body of this function runs within a 259.Xr critical 9 260section. 261.Pp 262The 263.Fn intr_event_remove_handler 264function removes an interrupt handler from the interrupt event specified by 265.Fa ie . 266The 267.Fa cookie 268argument, obtained from 269.Fn intr_event_add_handler , 270identifies the handler to remove. 271.Pp 272The 273.Fn intr_priority 274function translates the 275.Dv INTR_TYPE_* 276interrupt flags into interrupt thread scheduling priorities. 277.Pp 278The interrupt flags not related to the type of a particular interrupt 279.Pq Dv INTR_TYPE_* 280can be used to specify additional properties of both hardware and software 281interrupt handlers. 282The 283.Dv INTR_EXCL 284flag specifies that this handler cannot share an interrupt thread with 285another handler. 286The 287.Dv INTR_MPSAFE 288flag specifies that this handler is MP safe in that it does not need the 289Giant mutex to be held while it is executed. 290The 291.Dv INTR_ENTROPY 292flag specifies that the interrupt source this handler is tied to is a good 293source of entropy, and thus that entropy should be gathered when an interrupt 294from the handler's source triggers. 295Presently, the 296.Dv INTR_ENTROPY 297flag is not valid for software interrupt handlers. 298.Ss Handler Callbacks 299Each 300.Vt struct intr_event 301is assigned three optional callback functions when it is created: 302.Fa pre_ithread , 303.Fa post_ithread , 304and 305.Fa post_filter . 306These callbacks are intended to be defined by the interrupt controller driver, 307to allow for actions such as masking and unmasking hardware interrupt signals. 308.Pp 309When an interrupt is triggered, all filters are run to determine if any 310threaded interrupt handlers should be scheduled for execution by the associated 311interrupt thread. If no threaded handlers are scheduled, the 312.Fa post_filter 313callback is invoked which should acknowledge the interrupt and permit it to 314trigger in the future. 315If any threaded handlers are scheduled, the 316.Fa pre_ithread 317callback is invoked instead. 318This handler should acknowledge the interrupt, but it should also ensure that 319the interrupt will not fire continuously until after the threaded handlers have 320executed. 321Typically this callback masks level-triggered interrupts in an interrupt 322controller while leaving edge-triggered interrupts alone. 323Once all threaded handlers have executed, 324the 325.Fa post_ithread 326callback is invoked from the interrupt thread to enable future interrupts. 327Typically this callback unmasks level-triggered interrupts in an interrupt 328controller. 329.Sh RETURN VALUES 330The 331.Fn intr_event_add_handler , 332.Fn intr_event_create , 333.Fn intr_event_destroy , 334.Fn intr_event_handle , 335and 336.Fn intr_event_remove_handler 337functions return zero on success and non-zero on failure. 338The 339.Fn intr_priority 340function returns a process priority corresponding to the passed in interrupt 341flags. 342.Sh EXAMPLES 343The 344.Xr swi_add 9 345function demonstrates the use of 346.Fn intr_event_create 347and 348.Fn intr_event_add_handler . 349.Bd -literal -offset indent 350int 351swi_add(struct intr_event **eventp, const char *name, driver_intr_t handler, 352 void *arg, int pri, enum intr_type flags, void **cookiep) 353{ 354 struct intr_event *ie; 355 int error = 0; 356 357 if (flags & INTR_ENTROPY) 358 return (EINVAL); 359 360 ie = (eventp != NULL) ? *eventp : NULL; 361 362 if (ie != NULL) { 363 if (!(ie->ie_flags & IE_SOFT)) 364 return (EINVAL); 365 } else { 366 error = intr_event_create(&ie, NULL, IE_SOFT, 0, 367 NULL, NULL, NULL, swi_assign_cpu, "swi%d:", pri); 368 if (error) 369 return (error); 370 if (eventp != NULL) 371 *eventp = ie; 372 } 373 if (handler != NULL) { 374 error = intr_event_add_handler(ie, name, NULL, handler, arg, 375 PI_SWI(pri), flags, cookiep); 376 } 377 return (error); 378} 379.Ed 380.Sh ERRORS 381The 382.Fn intr_event_add_handler 383function will fail if: 384.Bl -tag -width Er 385.It Bq Er EINVAL 386The 387.Fa ie 388or 389.Fa name 390arguments are 391.Dv NULL . 392.It Bq Er EINVAL 393The 394.Fa handler 395and 396.Fa filter 397arguments are both 398.Dv NULL . 399.It Bq Er EINVAL 400The 401.Dv IH_EXCLUSIVE 402flag is specified and the interrupt thread 403.Fa ie 404already has at least one handler, or the interrupt thread 405.Fa ie 406already has an exclusive handler. 407.El 408.Pp 409The 410.Fn intr_event_create 411function will fail if: 412.Bl -tag -width Er 413.It Bq Er EINVAL 414A flag other than 415.Dv IE_SOFT 416was specified in the 417.Fa flags 418parameter. 419.El 420.Pp 421The 422.Fn intr_event_destroy 423function will fail if: 424.Bl -tag -width Er 425.It Bq Er EINVAL 426The 427.Fa ie 428argument is 429.Dv NULL . 430.It Bq Er EBUSY 431The interrupt event pointed to by 432.Fa ie 433has at least one handler which has not been removed with 434.Fn intr_event_remove_handler . 435.El 436.Pp 437The 438.Fn intr_event_handle 439function will fail if: 440.Bl -tag -width Er 441.It Bq Er EINVAL 442The 443.Fa ie 444argument is 445.Dv NULL . 446.It Bq Er EINVAL 447There are no interrupt handlers assigned to 448.Fa ie . 449.It Bq Er EINVAL 450The interrupt was not acknowledged by any filter and has no associated thread 451handler. 452.El 453.Pp 454The 455.Fn intr_event_remove_handler 456function will fail if: 457.Bl -tag -width Er 458.It Bq Er EINVAL 459The 460.Fa cookie 461argument is 462.Dv NULL . 463.El 464.Sh SEE ALSO 465.Xr critical 9 , 466.Xr kthread 9 , 467.Xr locking 9 , 468.Xr malloc 9 , 469.Xr swi 9 , 470.Xr uma 9 471.Sh HISTORY 472Interrupt threads and their corresponding API first appeared in 473.Fx 5.0 . 474