To begin with, the whole point of this RFC was to show that moving the 
entire IRQ handler (or even a large part of it) to a tasklet would have 
been at least as simple as moving the givebacks alone.

Now that I realize the hrtimer and unlink pathways would have to be
changed too, it's not quite so simple as it seemed at first.  Still, I
think it would be no worse than the work you did.  It also is more
typical of the way people expect the work to be divided between a top
half and a bottom half -- usually the top half does little more than 
acknowledge the interrupt.

Since the proposal was meant only as a proof of principle, I'm not 
going to develope it any farther.  But I will respond to the points you 
raised in your review.

On Sat, 24 Aug 2013, Ming Lei wrote:

> But the current interrupt from EHCI isn't be acked(USBSTS isn't cleared),
> so EHCI HW don't know the irq has been handled, then it is reasonable
> that the EHCI interrupt still interrupts CPU.
> 
> EHCI spec(4.15) also states the point clearly:
> 
>         the host controller is not required to de-assert a currently active
>         interrupt condition when software sets the interrupt enables (in the
>         USBINR register, see Section 2.3.3) to a zero.  The only reliable
>         method software should use for acknowledging an interrupt is by
>         transitioning the appropriate status bits in the USBSTS register
>        (Section 2.3.2) from a one to a zero.

Ooh, you're right!  I had completely forgotten about this.  It's a 
serious error in the RFC patch.  Fixing it would be pretty easy, 
however.

> > Not handling interrupts right away is the price we pay for using a
> > bottom half.  Your tasklet implementation isn't very different.
> > Although the interrupt handler may realize quickly that a transfer has
> > finished, there will still be a delay before the URB's completion
> > handler can run.  And the length of that delay will depend on the
> > tasklet schedule delay.
> 
> In my tasklet implementation, the next EHCI interrupt can be handled
> after the current hard interrupt is handled, but in this patch the EHCI
> hard irq can't be handled until the tasklet is handled, right?

Right.  But in the end, it doesn't matter.

Remember, more than 99% of the work an HCD does is handling URBs.  
(HCDs do a couple of other things, such as detecting connections and
disconnection or handling wakeups, but they are very small compared to
the number of URBs it handles.)  Consider also that an URB isn't done
until it is given back to the driver that submitted it.

Therefore, when measuring performance or latency of an HCD, what 
matters is how long the driver has to wait from URB submission to URB 
completion.  If any part of that processing takes place in a tasklet, 
the tasklet delay will add in to the total time.  It doesn't matter 
whether the tasklet handles only the giveback or some of the HCD's 
internal work as well.

> >> Thirdly, fatal error should have been handled immediately inside hard
> >> interrupt handler.
> >
> > Why?  What difference does it make?
> 
> At least, EHCI HW generates the fatal irq ASAP, "This interrupt is not
> delayed to the next interrupt threshold."(4.15.2.4), so it is reasonable for
> software to handle it asap.

That doesn't seem like a good reason.  (Probably it just reflects the
fact that once a fatal error has occurred, the controller hardware
can't reliably determine when the next interrupt threshold will occur.)  
But of course, it would be easy to put that part of the code into the
top half of the handler.

> >> Finally, tasklet schedule should have been optimized here if the tasklet
> >> is running on another CPU, otherwise the current CPU may spin on
> >> the tasklet lock until the same tasklet is completed on another CPU.
> >
> > That could be added in.  But doesn't the core kernel's tasklet
> > implementation already take care of this?  The tasklet_hi_action()
> > routine uses tasklet_trylock(), so it doesn't spin.
> 
> OK, but extra tasklet schedule delay might be caused.

Like I said, this feature also could be added easily.

> > There's no point in enabling interrupts before they can be handled.  As
> > long as the tasklet is running, it doesn't do any good to receive more
> > IRQs.
> 
> Why doesn't any good to receive more IRWQs? And there should be
> other interrupts(non transfer complete irq, such as port change, fatal
> error, ...)
> comes.

There's no reason to handle those events any more quickly than ordinary 
completion IRQs.  Of course, the code _could_ be structured to leave 
interrupts unmasked while the tasklet runs.  The tasklet would end up 
doing the same amount of work; the only difference would be that more 
interrupts occur while the tasklet runs, thereby wasting CPU time.

To put it another way, which would you prefer: To have three interrupts 
while the tasklet is running, or one interrupt as soon as it finishes?

> >> Also if the latest value of irqs_are_masked isn't see by ehci_irq
> >> immediately, it will cause CPU interrupted by extra and unnecessary
> >> irq signal.
> >>
> >> > +       smp_wmb();              /* Make sure ehci_irq() sees that 
> >> > assignment */
> >
> > That's why I put in this memory barrier.  It guarantees that
> > irqs_are_masked _will_ be seen by ehci_irq.
> 
> memory barrier only orders memory load/store, and it can't
> make sure CPU always sees the latest value, also what is
> the two memory load/store to order with the smp_wmb()? And
> where is the smp_rmb() pair?

Whenever the CPU gets interrupted, the main system-level interrupt
handler executes at least one memory barrier.  That's what pairs with
the barrier above.  Thus:

        The smp_wmb() forces the irqs_are_masked assignment to be
        ordered before the write to the intr_enable register.

        If an interrupt then occurs, the memory barrier in the 
        system's interrupt code will cause the assignment to visible
        to the interrupt service routine.

On the other hand, I realized later that ehci_irq() can sometimes be 
called in other contexts, not from within a hardirq.  This means the 
smp_wmb is insufficient after all, and a spinlock is indeed needed.

> The same problem might exist for reading/writing 'irqs_are_masked'
> in ehci_irq: the flag is set in ehci_irq() on CPU0, but it might not be
> seen in another ehci_irq() on CPU1.

An interrupt can't occur on CPU1 until the handler on CPU0 returns.  
The memory barriers in the system-level interrupt routines mean that
everything done on CPU0 before the handler returns would be visible to
the handler on CPU1.

Alan Stern

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to