Jan Kiszka wrote:
Anders Blomdell wrote:

For the last few days, I have tried to figure out a good way to share
interrupts between RT and non-RT domains. This has included looking
through Dmitry's patch, correcting bugs and testing what is possible in
my specific case. I'll therefore try to summarize at least a few of my

1. When looking through Dmitry's patch I get the impression that the
iack handler has very little to do with each interrupt (the test
'prev->iack != intr->iack' is a dead giveaway), but is more of a
domain-specific function (or perhaps even just a placeholder for the
hijacked Linux ack-function).

2. Somewhat inspired by the figure in "Life with Adeos", I have
identified the following cases:

 irq K  | ----------- | ---o    |   // Linux only
 irq L  | ---o        |         |   // RT-only
 irq M  | ---o------- | ---o    |   // Shared between domains
 irq N  | ---o---o--- |         |   // Shared inside single domain
 irq O  | ---o---o--- | ---o    |   // Shared between and inside single

Xenomai currently handles the K & L cases, Dmitrys patch addresses the N
case, with edge triggered interrupts the M (and O after Dmitry's patch)
case(s) might be handled by returning RT_INTR_CHAINED | RT_INTR_ENABLE
from the interrupt handler, for level triggered interrupt the M and O
cases can't be handled.

I guess you mean it the other way around: for the edge-triggered
cross-domain case we would actually have to loop over both the RT and
the Linux handlers until we are sure, that the IRQ line was released once.
I obviously has misunderstood edge triggered :-(

Luckily, I never saw such a scenario which were unavoidable (it hits you
with ISA hardware which tend to have nice IRQ jumpers or other means -
it's just that you often cannot divide several controllers on the same
extension card IRQ-wise apart).

If one looks more closely at the K case (Linux only interrupt), it works
by when an interrupt occurs, the call to irq_end is postponed until the
Linux interrupt handler has run, i.e. further interrupts are disabled.
This can be seen as a lazy version of Philippe's idea of disabling all
non-RT interrupts until the RT-domain is idle, i.e. the interrupt is
disabled only if it indeed occurs.

If this idea should be generalized to the M (and O) case(s), one can't
rely on postponing the irq_end call (since the interrupt is still needed
in the RT-domain), but has to rely on some function that disables all
non-RT hardware that generates interrupts on that irq-line; such a
function naturally has to have intimate knowledge of all hardware that
can generate interrupts in order to be able to disable those interrupt
sources that are non-RT.

If we then take Jan's observation about the many (Linux-only) interrupts
present in an ordinary PC and add it to Philippe's idea of disabling all
non-RT interrupts while executing in the RT-domain, I think that the
following is a workable (and fairly efficient) way of handling this:

Add hardware dependent enable/disable functions, where the enable is
called just before normal execution in a domain starts (i.e. when
playing back interrupts, the disable is still in effect), and disable is
called when normal domain execution end. This does effectively handle
the K case above, with the added benefit that NO non-RT interrupts will
occur during RT execution.

In the 8259 case, the disable function could look something like:

 domain_irq_disable(uint irqmask) {
   if (irqmask & 0xff00 != 0xff00) {
     irqmask &= ~0x0004; // Cascaded interrupt is still needed
     outb(irqmask >> 8, PIC_SLAVE_IMR);
   outb(irqmask, PIC_MASTER_IMR);

If we should extend this to handle the M (and O) case(s), the disable
function could look like:

 domain_irq_disable(uint irqmask, shared_irq_t *shared[]) {
   int i;

   for (i = 0 ; i < MAX_IRQ ; i++) {
     if (shared[i]) {
       shared_irq_t *next = shared[i];
       irqmask &= ~(1<<i);
       while (next) {
     next = next->next;

This obviously means that all non-RT IRQ handlers sharing a line with
the RT domain would have to be registered in that shared[]-list. This
gets close to my old suggestion. Just raises the question how to
organise these interface, both on the RT and the Linux side.

   if (irqmask & 0xff00 != 0xff00) {
     irqmask &= ~0x0004; // Cascaded interrupt is still needed
     outb(irqmask >> 8, PIC_SLAVE_IMR);
   outb(irqmask, PIC_MASTER_IMR);

An obvious optimization of the above scheme, is to never call the
disable (or enable) function for the RT-domain, since there all
interrupt processing is protected by the hardware.

Another point is to avoid that looping over disable handlers for IRQs of
the K case. Otherwise, too many device-specific disable handlers had to
be implemented even if only a single Linux device hogs a RT IRQ.
You only have to spin over those IRQ that are actually shared across domains (probably just a few in most cases), those IRQ that stays in one domain fets handled in the interrupt controller. Setting up the appropriate [per domain] lists can be done as irqhandlers gets registered/unregistred (which is usually quite seldom).

Comments, anyone?

I see your need, we just have to keep in mind that we are currently
discussing the corner case (shared RT/non-RT IRQs) of the corner case
(shared RT IRQs). Whatever will be defined, the normal use case should
define how an efficient interface has to look like. Or we have to add a
lot of #ifdef's...
My current need can simply be handled by disabling the UART interrupt and have a low priority RT task polling the interrupt and handing it over to Linux. It was just that I thought that now might be a good time to try to bring together those three somewhat overlapping issues:

  1. Shared RT-interrupts (which Dimitry has already done)
  2. Philippe's suggestion of disabling all interrupts for lower priority
  3. Interrupts shared between domains

As previously said, my current needs are covered by #3, but since I have RT plans for some of my ordinary PCs as well, I feel that #2 is worth some effort as well. My feeling right now, is that execution time of #2 will be shorter than it takes to handle a single non-RT interrupt in the current codebase, and hence it ought to improve RT performance (avoiding I-cache pressure from non-RT handlers for instance), and #3 is a small addition once #2 is in place.



Reply via email to