Subject: Re: Audio interrupt latency
To: Neil A. Carson <neil@causality.com>
From: Charles M. Hannum <mycroft@mit.edu>
List: port-arm32
Date: 08/17/1998 10:46:01
> > BTW, I looked at the way interrupts are handled, and I must say, the
> > port would benefit a great deal from using a software masking scheme
> > like the i386 port uses. Particularly the grotesque ISA IRQ handling.
>
> Are you sure that whta you mention is applicable? Beware that interrupts
> are handled quite differently. Eg SA interrupts are level rather than
> edge sensitive. I don't know if this is applicable to the i386 or not.
The hack as implemented on the i386 works for both edge and level
triggered interrupts. (It would actually be simpler if I could assume
edge trigger! I wouldn't have to actually mask any interrupts in the
PICs.)
The important difference on the Shark is the wired-or configuration
for ISA interrupts, rather than the traditional 8259 handshake that
reports an interrupt vector to the CPU.
My main concern with the existing Shark code is that it's awfully
expensive. It requires a tremendous number of cycles on interrupt
entry, and a fair number on each spl*(). And even then it doesn't
actually enforce the interrupt hierarchy between interrupts that occur
close together.
I would suggest something more like this:
* Maintain a pair of 256-byte lookup tables that map IRR value to
highest interrupt level (IPL_*) activated.
* On interrupt entry:
* Read the IRRs and or the interrupts into the set of active
interrupts. Mask them in the PICs (8259s).
* Mask off the currently blocked interrupts, then use the
aforementioned tables to figure out the highest IPL corresponding
to the new interrupts. Set the IPL and the set of currently
blocked interrupts accordingly. (If there were no unblocked
interrupts, exit the interrupt handler immediately.)
* Enable interrupts.
* Search backwards from the new IPL to the previous IPL+1. For each
level, run any interrupts at that level that are in the set of
currently active interrupts, then decrement the IPL and update the
set of currently blocked interrupts.
(Note that there's a race condition between this and the previous
rules that requires rereading the set of active interrupts from
memory on each iteration. Also, between when we decide there are
no interrupts at this level and we decrement the IPL, interrupts
must be blocked. If disabling interrupts is expensive, we could
instead decrement the IPL, check again for any interrupts at this
level, and if there are any, increment the IPL again and go back
into the main loop. This sounds hairy, but it's actually not that
bad.)
* On spl*(), merely update the IPL and the set of currently blocked
interrupts; do not update the hardware interrupt masks.
I admit this hits 2 or 3 more cache lines, but compared to executing
several hundred extra instructions it's a good tradeoff.