INTR_EVENT(9)          FreeBSD Kernel Developer's Manual         INTR_EVENT(9)

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), 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) 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).  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) 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) 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)
     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) 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), kthread(9), locking(9), malloc(9), swi(9), uma(9)

HISTORY

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

FreeBSD 15.1-STABLE-HBSD       January 24, 2025                  INTR_EVENT(9)