Subject: Re: results of the IRC debug patch
To: Michael <macallan18@earthlink.net>
From: Tim Kelly <hockey@dialectronics.com>
List: port-macppc
Date: 12/04/2004 12:56:21
At 9:32 AM -0500 12/4/04, Tim Kelly wrote:
>The pcpl indicates what the current IPL level is. In the code to calculate
>the interrupt masks, do both IPL_AUDIO and IPL_BIO consider the virq when
>shared as above? I don't know. There's a lot of ORing that is used for the
>hierarchy, but if virq 25 comes in, does raise the IPL to IPL_AUDIO or
>IPL_BIO. Seems to me this could be a pertinent question, as you've already
>indicated that playing audio can cause the mouse to stop responding.

It seems to me, in the intr_calculatemasks function, it is possible for a
lower device sharing a virq to block irqs at a higher IPL. As a disclaimer,
let me say upfront that I have yet to find something wrong with Tsubai's
code and when I have found something suspect it usually is from my lack of
understanding the code fully.

When the code begins to determine each virq

        /* First, figure out which levels each IRQ uses. */
        for (irq = 0, is = intrsources; irq < NIRQ; irq++, is++) {
                register int levels = 0;
                for (q = is->is_hand; q; q = q->ih_next)
                        levels |= 1 << q->ih_level;
                is->is_level = levels;
        }

so the inference is that sharing virqs with different levels is expected.
From there set up each level's mask of virqs.

        /* Then figure out which IRQs use each level. */
        for (level = 0; level < NIPL; level++) {
                register int irqs = 0;
                for (irq = 0, is = intrsources; irq < NIRQ; irq++, is++)
                        if (is->is_level & (1 << level))
                                irqs |= 1 << irq;
                imask[level] = irqs;
        }

Begin the hierarchy of blocked virqs

        /*
         * IPL_CLOCK should mask clock interrupt even if interrupt handler
         * is not registered.
         */
        imask[IPL_CLOCK] |= 1 << SPL_CLOCK;

        /*
         * Initialize soft interrupt masks to block themselves.
         */
        imask[IPL_SOFTCLOCK] = 1 << SIR_CLOCK;
        imask[IPL_SOFTNET] = 1 << SIR_NET;
        imask[IPL_SOFTSERIAL] = 1 << SIR_SERIAL;


it sets up each imask level with the irqs at the level plus each irq
already contained with a lower IPL. So IPL_AUDIO would contain all irqs
from IPL_BIO, which is reasonable since we don't want choppy audio when
there is a disk access.

        /*
         * Enforce a hierarchy that gives slow devices a better chance at not
         * dropping data.
         */
        imask[IPL_SOFTCLOCK] |= imask[IPL_NONE];
        imask[IPL_SOFTNET] |= imask[IPL_SOFTCLOCK];
        imask[IPL_BIO] |= imask[IPL_SOFTNET];
        imask[IPL_NET] |= imask[IPL_BIO];
        imask[IPL_SOFTSERIAL] |= imask[IPL_NET];
        imask[IPL_TTY] |= imask[IPL_SOFTSERIAL];

        /*
         * There are tty, network and disk drivers that use free() at interrupt
         * time, so imp > (tty | net | bio).
         */
        imask[IPL_VM] |= imask[IPL_TTY];

        imask[IPL_AUDIO] |= imask[IPL_VM];

But when calculating the final mask for the interrupt source

        /* And eventually calculate the complete masks. */
        for (irq = 0, is = intrsources; irq < NIRQ; irq++, is++) {
                register int irqs = 1 << irq;
                for (q = is->is_hand; q; q = q->ih_next)
                        irqs |= imask[q->ih_level];
                is->is_mask = irqs;
        }


the for (q = ... loop interates over each interrupt handler from the
interrupt source and then ORs the interrupt mask for each interrupt handler
level. This leads me to examine the mouse and audio example, and I get that
when the audio's ih_level (IPL_AUDIO) is masked, it masks everything below
it, but the overall interrupt mask source is shared with the mouse if they
have the same virq.

Later, when a mouse based interrupt comes into ext_intr()

        pcpl = ci->ci_cpl;
        msr = mfmsr();

        int_state = gc_read_irq();
        irq = 31 - cntlzw(int_state);
        r_imen = 1 << irq;

this gets the shared virq. From there the interrupt source with interrupt
handlers and levels is retrieved

        is = &intrsources[irq];

        if ((pcpl & r_imen) != 0) {
                ci->ci_ipending |= r_imen;      /* Masked! Mark this as
pending$
                gc_disable_irq(is->is_hwirq);
        } else {
                splraise(is->is_mask);
                mtmsr(msr | PSL_EE);
                KERNEL_LOCK(LK_CANRECURSE|LK_EXCLUSIVE);
                ih = is->is_hand;
                while (ih) {
                        (*ih->ih_fun)(ih->ih_arg);
                        ih = ih->ih_next;
                }
                KERNEL_UNLOCK();
                mtmsr(msr);
                ci->ci_cpl = pcpl;

                uvmexp.intrs++;
                is->is_ev.ev_count++;
        }

the initial mouse event is handled immediately, but the splraise blocks
everything under IPL_AUDIO - including the mouse - and any rentrancy the
ext_intr gets will mask mouse events as pending, as will stuff should not
be blocked like IPL_BIO. Additionally, if the mouse event comes in
rentrantly, the irq for that entire hardware device gets turned off until
do_pending_int turns it back on.

So it seems to me that while processing a shared virq, the highest priority
level is the one that sets the IPL, as opposed to the IPL of the specific
device sharing the virq.

tim

tim