Synthetic IRQs

The pipeline introduces an additional type of interrupts, which are purely software-originated, with no hardware involvement. These IRQs can be triggered by any kernel code. Synthetic IRQs are inherently per-CPU events. Because the common pipeline flow applies to synthetic interrupts, it is possible to attach them to out-of-band and/or in-band handlers, just like device interrupts.

Synthetic interrupts abide by the normal rules with respect to interrupt masking: such IRQs may be deferred until the stage they should be handled from is unstalled.

Synthetic interrupts and regular softirqs differ in essence: the latter only exist in the in-band context, and therefore cannot trigger out-of-band activities. Synthetic interrupts used to be called virtual IRQs (or virq for short) by the legacy I-pipe implementation, Dovetail’s ancestor; such rename clears the confusion with the way abstract interrupt numbers defined within interrupt domains may be called elsewhere in the kernel code base (i.e. virtual interrupts too).

Allocating synthetic interrupts

Synthetic interrupt vectors are allocated from the synthetic_irq_domain, using the irq_create_direct_mapping() routine.

A synthetic interrupt handler can be installed for running on the root stage upon a scheduling request (i.e. being posted) from an out-of-band context as follows:

#include <linux/irq_pipeline.h>

static irqreturn_t sirq_handler(int sirq, void *dev_id)
{
	do_in_band_work();

	return IRQ_HANDLED;
}

static struct irqaction sirq_action = {
        .handler = sirq_handler,
        .name = "In-band synthetic interrupt",
        .flags = IRQF_NO_THREAD,
};

unsigned int alloc_sirq(void)
{
	unsigned int sirq;

	sirq = irq_create_direct_mapping(synthetic_irq_domain);
	if (!sirq)
		return 0;
	
	setup_percpu_irq(sirq, &sirq_action);

	return sirq;
}

A synthetic interrupt handler can be installed for running from the head stage upon a trigger from an in-band context as follows:

static irqreturn_t sirq_oob_handler(int sirq, void *dev_id)
{
	do_out_of_band_work();

	return IRQ_HANDLED;
}

unsigned int alloc_sirq(void)
{
	unsigned int sirq;

	sirq  = irq_create_direct_mapping(synthetic_irq_domain);
	if (!sirq)
		return 0;
     
	ret = __request_percpu_irq(sirq, sirq_oob_handler,
                                   IRQF_OOB,
                                   "Out-of-band synthetic interrupt",
                                   dev_id);
	if (ret) {
        	irq_dispose_mapping(sirq);
		return 0;
	}

	return sirq;
}

Scheduling in-band execution of a synthetic interrupt handler

The execution of sirq_handler() in the in-band context can be scheduled (or posted) from the out-of-band context in two different ways:

Using the common injection service

	irq_pipeline_inject(sirq);

Using the lightweight injection method (requires interrupts to be disabled in the CPU)

	unsigned long flags = hard_local_irqsave();
	irq_stage_post_root(sirq);
	hard_local_irqrestore(flags);

Assuming that no interrupt may be pending in the event log for the head stage at the time this code runs, the second method relies on the invariant that in a pipeline interrupt model, IRQs pending for the root stage will have to wait for the head stage to quiesce before they can be handled. Therefore, it is pointless to check for synchronizing the interrupts pending for the root stage from the head stage, which the irq_pipeline_inject() service would do systematically. irq_stage_post_root() simply marks the event as pending in the event log of the root stage for the current CPU, then returns. This event would be played as a result of synchronizing the log automatically when the current CPU switches back to the root stage.

It is also valid to post a synthetic interrupt to be handled on the root stage from an in-band context, using irq_pipeline_inject(). In such a case, the normal rules of interrupt delivery apply, depending on the state of the virtual interrupt disable flag for the root stage: the IRQ is immediately delivered, with the call to irq_pipeline_inject() returning only after the handler has run.

Triggering out-of-band execution of a synthetic interrupt handler

Conversely, the execution of sirq_handler() on the head stage can be triggered from the in-band context as follows:

	irq_pipeline_inject(sirq);

Since the head stage has precedence over the root stage for execution of any pending event, this IRQ is immediately delivered, with the call to irq_pipeline_inject() returning only after the handler has run.

It is also valid to post a synthetic interrupt to be handled on the head stage from an out-of-band context, using irq_pipeline_inject(). In such a case, the normal rules of interrupt delivery apply, depending on the state of the virtual interrupt disable flag for the head stage.

Calling irq_stage_post_head(sirq) from the root stage to trigger an out-of-band event is most often not the right way to do this, because this service would not synchronize the interrupt log before returning. In other words, the sirq event would still be pending for the head stage despite the fact that it should have preempted the root stage before returning to the caller.