Example of using InterruptAttachEvent() to handle sigevents on a specific CPU

Updated: April 19, 2023

For an IST (Interrupt Service Thread), when you call InterruptAttachEvent() to associate a sigevent with a specific interrupt, the sigevent is delivered to the calling thread by default. You may want the handler to run on a specific CPU (or core) for various reasons (e.g., reliability). To accomplish this, you call ThreadCtl() with the argument _NTO_TCTL_RUNMASK_GET_AND_SET and the runmask to specify the CPU for a thread before you set up the event [call InterruptAttachEvent()]. The runmask specifies the core or CPU that the sigevent runs on. It’s important to note that you may incur a system performance impact when you use this design and it can also result in partition inversion if you use adaptive partitions (APS).

For ISTs, you'd still typically want to change the PIC routing as in the Example of modifying the interrupt controller in a BSP when using InterruptAttach() as well as setting the runmask. Without the routing change, the hardware will deliver the interrupt to CPU X and then the kernel will have to IPI CPU Y to run the IST code.

Warning: Considerable cross-core activity and increase latency can occur if you set the runmask of the IST to a CPU other than the one to which the interrupt is routed.
The following sample code handle interrupts on CPU1 (hard-coded in this example):
#include <stdio.h>
#include <sys/neutrino.h>

#define HW_SERIAL_IRQ      3
#define REG_RX             0
#define REG_II             2
#define REG_LS             5
#define REG_MS             6
#define IIR_MASK           0x07
#define IIR_MSR            0x00
#define IIR_THE            0x02
#define IIR_RX             0x04
#define IIR_LSR            0x06
#define IIR_MASK           0x07

static int base_reg = 0x2f8;

int
main (int argc, char **argv)
{
    int  intId;         // interrupt id
    int  iir;           // interrupt identification register
    int  serial_msr;    // saved contents of Modem Status Reg
    int  serial_rx;     // saved contents of RX register
    int  serial_lsr;    // saved contents of Line Status Reg
    struct sigevent event;

/* Initialize the sigevent */
    SIGEV_INTR_INIT(&event);

    /* Set interrupt thread to run on CPU 1 */
    int cpu = 1;
    unsigned runmask = (1 << cpu);

    /* Set runmask so that the interrupt always runs on the specified CPU */
    if (ThreadCtl(_NTO_TCTL_RUNMASK_GET_AND_SET, &runmask) == -1) {
        printf("Unable to specify runmask for CPU %d", cpu);
        return -1;
    }

    // Set up the event
    intId = InterruptAttachEvent (HW_SERIAL_IRQ, &event, 0);

    for (;;) {

        // Wait for an interrupt event (could use MsgReceive() instead)
        // Note: If you want to use MsgReceive(), you must make the sigevent a pulse
        // sigevent, create a private channel, and then create a connection to
        // the channel to send the pulse (to yourself). For production code,
        // consider also using MsgRegisterEvent()
        InterruptWait (0, NULL);

        /*
         * Determine the source of the interrupt
         * reading the Interrupt Identification Register
         * to clear it
         *

        iir = in8 (base_reg + REG_II) & IIR_MASK;

        // Unmask the interrupt, so that we can get the next event
        InterruptUnmask (HW_SERIAL_IRQ, intId);

        /* no interrupt? */
        if (iir & 1) {
            /* Then wait again for next */
            continue;
        }

        /*
         * Determine which interrupt source caused the interrupt
         * and determine if we need to do with it
        */

        switch (iir) {
        case    IIR_MSR:
            serial_msr = in8 (base_reg + REG_MS);

            /*
             * Perform whatever processing you would've done in
             * the other example...
            */
            break;

        case    IIR_THE:
            /* do nothing */
           break;

        case    IIR_RX:
            /* Note the character */
            serial_rx = in8 (base_reg + REG_RX);
            break;

        case    IIR_LSR:
            /* Note the line status reg. */
            serial_lsr = in8 (base_reg + REG_LS);
            break;
        }
    }

    /* You won't get here. */
    return (0);
}