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 thoughts.
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 domain
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.
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->disable();
next = next->next;
}
}
}
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.
Comments, anyone?
--
Anders