Subject: Re: pmax interrupt problem solved
To: None <port-pmax@netbsd.org>
From: Michael L. Hitch <mhitch@lightning.msu.montana.edu>
List: port-pmax
Date: 03/09/2000 14:03:08
On Wed, 8 Mar 2000, Todd Whitesel wrote:

> Generally, when you have a wide register full of interrupt pending bits,
> the critical path of the interrupt dispatcher is to obtain a bit position
> of a set bit (preferably the highest priority such bit). If your interrupt
> handlers aren't hogging the machine, which _should_ be the case most of
> the time, then usually there will only be one bit set -- just not always.
> 
> So you want to optimize for the single bit case, but still behave correctly
> in the multiple bit case. If the interrupt logic is all level-sensitive
> (and I expect it is), then you can get away with handling one interrupt at
> a time; the hardware will just throw you back into the interrupt handler
> if there are still any bits set. Plus, this method automatically picks the
> highest priority pending interrupt on the next pass. It is false economy to
> check every single bit in the interrupt register on every pass through, and
> process everything before returning; also, that method is less than fair to
> higher priority interrupts which come in just after their bit is checked.

  Hmmm - after looking at this a bit, and thinking about it, I'm not
really convinced that some of these "optimizations" are all that
significant, at least for the NetBSD pmax case.

  As Terry Friedrichsen pointed out to me, my last change to dec_3max.c
added 2(!) instructions to the handling of the baseboard interrupts (DZ,
LANCE, and SCSI).  If you consider how many instructions it takes to get
to that point from an interrupt, and the number of instructions to exit
from the interrupt dispatch (ignoring the instructions to do the
processing of any given interrupt), and the variablilities due to cache
misses and TLB misses, those 2 extra instructions are probably
insignificant.

  Also, only processing one interrupt routine, returning, and taking
another interrupt execption in the case of multiple interrupt sources
pending could be more overhead than checking for all possible interrupts.
It would certainly depend upon how often you would actually get multiple
interrupts pending when you process them, so it's hard to determine the
best way to go.

  In the case of the 5000/200, we are only checking 6 bits for possible
interrupts, and in the case of multiple "if (bitset) process_interrupt()"
statements, egcs only needs 2 instructions per test for the case where the
interrupt bit is not set.  It would seem to me that it's probably
preferable to use straight-forward, simple code to check and dispatch
the interrupts, rather than adding complexity and obscurity to the code
(unless the effort can produce a *signifcant* performance improvment).

> Note also that we go back and check the pending register again until we've
> gone a complete round with nothing in it. This avoids wasting time returning
> all the way back to user mode if we're just going to get interrupted again,
> and it also automatically finds the next highest-priority interrupt that is
> still pending; plus it notices any new ones that show up while we're running!

  I was thinking that the interrupt dispatch could be written something
like this:

	while (1) {
		csr = <read CSR>;
		if (csr & INTBIT_1) {
			CALLINTR(INTR_1); continuye;
		}
		if (csr & INTBIT_2) {
			CALLINTR(INTR_2); continue;
		}
		...
		break;
	}

  Where the first interrupt is the highest priority desired (i.e. DZ or
SCC serial interrupt), and following interrupts in priority order.  This
would not address the possible case where the TurboChannel devices would
need to have some form of dynamic interrupt prioritization, but I'm not
certain how critical that would be at this point.  After each interrupt
source is processed, it goes back and re-reads the CSR and processes any
new interrupt that may have occurred while processing an interrupt.  Also,
doing it this way would eliminates the use of a separate variable to
control the loop, like the older version of the dispatch loop was doing,
but does have to re-check all the interrupt bits.  When you make a pass
through all sources of interrupts, then the loop exits.  As Todd noted
before, a high enough rate of interrupts could cause this to loop - but in
that case you would be looping processing the interrupts no matter how you
handled the interrupt.

  One further optimization that might be made here is to use a local
pointer to intrcnt and intrtab.  That looks like it should make the code
a bit more compact and efficient.  [Loading the address of intrcnt every
place it's referenced, and the same think for intrtab, generates more
code.  Presumably we only got to this interrupt routine because something
actually caused the interrupt, and you will need to access intrcnt/intrtab
at least once, so it shouldn't be any more overhead to have the addresses
in local pointers that can be used by all instances of reference.

--
Michael L. Hitch			mhitch@montana.edu
Computer Consultant
Information Technology Center
Montana State University	Bozeman, MT	USA