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.