---
name: "intr_event"
section: "9"
platform: "freebsd"
package: "base"
version: "16.0-CURRENT-20261027-61c70dc16a79"
release: "FreeBSD 16.0-CURRENT (2026-10-27)"
platforms: ["freebsd"]
url: "https://new.carta.tech/intr_event"
---

# intr\_event(9)

kernel interrupt handler and thread API

## NAME

**intr\_event\_add\_handler**, **intr\_event\_create**, **intr\_event\_destroy**, **intr\_event\_handle**, **intr\_event\_remove\_handler**, **intr\_priority** – kernel interrupt handler and thread API

## SYNOPSIS

**#include \<sys/param.h\>**

**#include \<sys/bus.h\>**

**#include \<sys/interrupt.h\>**

*int*\

**intr\_event\_add\_handler**(*struct intr\_event \*ie*, *const char \*name*, *driver\_filter\_t filter*, *driver\_intr\_t handler*, *void \*arg*, *u\_char pri*, *enum intr\_type flags*, *void \*\*cookiep*);

*int*\

**intr\_event\_create**(*struct intr\_event \*\*event*, *void \*source*, *int flags*, *int irq*, *void (\*pre\_ithread)(void \*)*, *void (\*post\_ithread)(void \*)*, *void (\*post\_filter)(void \*)*, *int (\*assign\_cpu)(void \*, int)*, *const char \*fmt*, *...*);

*int*\

**intr\_event\_destroy**(*struct intr\_event \*ie*);

*int*\

**intr\_event\_handle**(*struct intr\_event \*ie*, *struct trapframe \*frame*);

*int*\

**intr\_event\_remove\_handler**(*void \*cookie*);

*u\_char*\

**intr\_priority**(*enum intr\_type flags*);

## DESCRIPTION

The interrupt event API provides methods to manage the registration and execution of interrupt handlers and their associated thread contexts.

Each interrupt event in the system corresponds to a single hardware or software interrupt source. Each interrupt event maintains a list of interrupt handlers, sorted by priority, which will be invoked when handling the event. An interrupt event will typically, but not always, have an associated [kthread(9)](/kthread/9.md), known as the interrupt thread. Finally, each event contains optional callback functions which will be invoked before and after the handler functions themselves.

An interrupt handler contains two distinct handler functions: the *filter* and the thread *handler*. The *filter* function is run from interrupt context and is intended to perform quick handling such as acknowledging or masking a hardware interrupt, and queueing work for the ensuing thread *handler*. Both functions are optional; each interrupt handler may choose to register a filter, a thread handler, or both. Each interrupt handler also consists of a name, a set of flags, and an opaque argument which will be passed to both the *filter* and *handler* functions.

### Handler Constraints

The *filter* function is executed inside a [critical(9)](/critical/9.md) section. Therefore, filters may not yield the CPU for any reason, and may only use spin locks to access shared data. Allocating memory within a filter is not permitted.

The *handler* function executes from the context of the associated interrupt kernel thread. Sleeping is not permitted, but the interrupt thread may be preempted by higher priority threads. Thus, threaded handler functions may obtain non-sleepable locks, as described in [locking(9)](/locking/9.md). Any memory or zone allocations in an interrupt thread must specify the M\_NOWAIT flag, and any allocation errors must be handled.

The exception to these constraints is software interrupt threads, which are allowed to sleep but should be allocated and scheduled using the [swi(9)](/swi/9.md) interface.

### Function Descriptions

The **intr\_event\_create**() function creates a new interrupt event. The *event* argument points to a *struct* *intr\_event* pointer that will reference the newly created event upon success. The *source* argument is an opaque pointer which will be passed to the *pre\_ithread*, *post\_ithread*, and *post\_filter* callbacks. The *flags* argument is a mask of properties of this thread. The only valid flag currently for **intr\_event\_create**() is IE\_SOFT to specify that this interrupt thread is a software interrupt. The *enable* and *disable* arguments specify optional functions used to enable and disable this interrupt thread's interrupt source. The *irq* argument is the unique interrupt vector number corresponding to the event. The *pre\_ithread*, *post\_ithread*, and *post\_filter* arguments are callback functions that are invoked at different points while handling an interrupt. This is described in more detail in the Handler Callbacks section, below. They may be *NULL* to specify no callback. The *assign\_cpu* argument points to a callback function that will be invoked when binding an interrupt to a particular CPU. It may be *NULL* if binding is unsupported. The remaining arguments form a [printf(9)](/printf/9.md) argument list that is used to build the base name of the new interrupt thread. The full name of an interrupt thread is formed by concatenating the base name of the interrupt thread with the names of all of its interrupt handlers.

The **intr\_event\_destroy**() function destroys a previously created interrupt event by releasing its resources. An interrupt event can only be destroyed if it has no handlers remaining.

The **intr\_event\_add\_handler**() function adds a new handler to an existing interrupt event specified by *ie*. The *name* argument specifies a name for this handler. The *filter* argument provide the filter function to execute. The *handler* argument provides the handler function to be executed from the event's interrupt thread. The *arg* argument will be passed to the *filter* and *handler* functions when they are invoked. The *pri* argument specifies the priority of this handler, corresponding to the values defined in ⟨sys/priority.h⟩. It determines the order this handler is called relative to the other handlers for this event, as well as the scheduling priority of the backing kernel thread. *flags* argument can be used to specify properties of this handler as defined in ⟨sys/bus.h⟩. If *cookiep* is not NULL, then it will be assigned a cookie that can be used later to remove this handler.

The **intr\_event\_handle**() function is the main entry point into the interrupt handling code. It must be called from an interrupt context. The function will execute all filter handlers associated with the interrupt event *ie*, and schedule the associated interrupt thread to run, if applicable. The *frame* argument is used to pass a pointer to the *struct* *trapframe* containing the machine state at the time of the interrupt. The main body of this function runs within a [critical(9)](/critical/9.md) section.

The **intr\_event\_remove\_handler**() function removes an interrupt handler from the interrupt event specified by *ie*. The *cookie* argument, obtained from **intr\_event\_add\_handler**(), identifies the handler to remove.

The **intr\_priority**() function translates the INTR\_TYPE\_\* interrupt flags into interrupt thread scheduling priorities.

The interrupt flags not related to the type of a particular interrupt (INTR\_TYPE\_\*) can be used to specify additional properties of both hardware and software interrupt handlers. The INTR\_EXCL flag specifies that this handler cannot share an interrupt thread with another handler. The INTR\_MPSAFE flag specifies that this handler is MP safe in that it does not need the Giant mutex to be held while it is executed. The INTR\_ENTROPY flag specifies that the interrupt source this handler is tied to is a good source of entropy, and thus that entropy should be gathered when an interrupt from the handler's source triggers. Presently, the INTR\_ENTROPY flag is not valid for software interrupt handlers. The INTR\_SLEEPABLE flag specifies that the interrupt ithread may sleep. Presently, the INTR\_SLEEPABLE flag requires the INTR\_EXCL flag to be set.

### Handler Callbacks

Each *struct* *intr\_event* is assigned three optional callback functions when it is created: *pre\_ithread*, *post\_ithread*, and *post\_filter*. These callbacks are intended to be defined by the interrupt controller driver, to allow for actions such as masking and unmasking hardware interrupt signals.

When an interrupt is triggered, all filters are run to determine if any threaded interrupt handlers should be scheduled for execution by the associated interrupt thread. If no threaded handlers are scheduled, the *post\_filter* callback is invoked which should acknowledge the interrupt and permit it to trigger in the future. If any threaded handlers are scheduled, the *pre\_ithread* callback is invoked instead. This handler should acknowledge the interrupt, but it should also ensure that the interrupt will not fire continuously until after the threaded handlers have executed. Typically this callback masks level-triggered interrupts in an interrupt controller while leaving edge-triggered interrupts alone. Once all threaded handlers have executed, the *post\_ithread* callback is invoked from the interrupt thread to enable future interrupts. Typically this callback unmasks level-triggered interrupts in an interrupt controller.

## RETURN VALUES

The **intr\_event\_add\_handler**(), **intr\_event\_create**(), **intr\_event\_destroy**(), **intr\_event\_handle**(), and **intr\_event\_remove\_handler**() functions return zero on success and non-zero on failure. The **intr\_priority**() function returns a process priority corresponding to the passed in interrupt flags.

## EXAMPLES

The [swi\_add(9)](/swi_add/9.md) function demonstrates the use of **intr\_event\_create**() and **intr\_event\_add\_handler**().

```
int
swi_add(struct intr_event **eventp, const char *name, driver_intr_t handler,
    void *arg, int pri, enum intr_type flags, void **cookiep)
{
	struct intr_event *ie;
	int error = 0;
	if (flags & INTR_ENTROPY)
		return (EINVAL);
	ie = (eventp != NULL) ? *eventp : NULL;
	if (ie != NULL) {
		if (!(ie->ie_flags & IE_SOFT))
			return (EINVAL);
	} else {
		error = intr_event_create(&ie, NULL, IE_SOFT, 0,
		    NULL, NULL, NULL, swi_assign_cpu, "swi%d:", pri);
		if (error)
			return (error);
		if (eventp != NULL)
			*eventp = ie;
	}
	if (handler != NULL) {
		error = intr_event_add_handler(ie, name, NULL, handler, arg,
		    PI_SWI(pri), flags, cookiep);
	}
	return (error);
}
```

## ERRORS

The **intr\_event\_add\_handler**() function will fail if:

- \[EINVAL\]

  The *ie* or *name* arguments are NULL.

- \[EINVAL\]

  The *handler* and *filter* arguments are both NULL.

- \[EINVAL\]

  The IH\_EXCLUSIVE flag is specified and the interrupt thread *ie* already has at least one handler, or the interrupt thread *ie* already has an exclusive handler.

The **intr\_event\_create**() function will fail if:

- \[EINVAL\]

  A flag other than IE\_SOFT was specified in the *flags* parameter.

The **intr\_event\_destroy**() function will fail if:

- \[EINVAL\]

  The *ie* argument is NULL.

- \[EBUSY\]

  The interrupt event pointed to by *ie* has at least one handler which has not been removed with **intr\_event\_remove\_handler**().

The **intr\_event\_handle**() function will fail if:

- \[EINVAL\]

  The *ie* argument is NULL.

- \[EINVAL\]

  There are no interrupt handlers assigned to *ie*.

- \[EINVAL\]

  The interrupt was not acknowledged by any filter and has no associated thread handler.

The **intr\_event\_remove\_handler**() function will fail if:

- \[EINVAL\]

  The *cookie* argument is NULL.

## SEE ALSO

[critical(9)](/critical/9.md), [kthread(9)](/kthread/9.md), [locking(9)](/locking/9.md), [malloc(9)](/malloc/9.md), [swi(9)](/swi/9.md), [uma(9)](/uma/9.md)

## HISTORY

Interrupt threads and their corresponding API first appeared in FreeBSD 5.0.
