On Thu, 21 Feb 2008, Misha Zhilin wrote:

> Alan,
> I've got a bit more time now, so let me give you more detailed
> explanation of what's going on.
> 
> First of all we are talking about large URBs. These are URBs that
> transfers more than 20KB of data. In other words, they require more than
> one qtd.
> 
> So, how it used to be up until 2.6.24:
> Short transfer was detected inside qh_completions. EREMOTEIO status was
> set to urb_status. By means of qtd_copy_status function and urb_status
> field, the EREMOTEIO status was carried through all of qtd belonging to
> the URB. As EREMOTEIO is a "error" status, all subsequent qtd of the
> same URB even with QTD_STS_ACTIVE (!!!) were unlinked.

That's the key point: -EREMOTEIO is an "error" status.  But 0 is an 
unusual status also; the "normal" status is -EINPROGRESS.

> And ehci_urb_done
> was eventually called (calling in turn usb_hcd_giveback_urb).
> 
> After your patch:
> Short transfer was detected in qh_completions. EREMOTEIO "error" status
> was set to last_status. Then it was almost immediately replaced by
> "normal" EINPROGRESS status.

Yes.  However the "if ((token & QTD_STS_HALT) != 0)" test should 
succeed, causing "stopped" to be set to 1.

> All subsequent qtd of the same URB have
> QTD_STS_ACTIVE flag set... forever.

That shouldn't matter.  Since "stopped" is set and last_status isn't 
-EINPROGRESS, the QTD shouldn't be skipped over; it should be removed 
from the list.

> So they stay stuck in the list and
> ehci_urb_done is newer called. All the modifications you did in
> usb_hcd_giveback_urb won't help to get it called.

It still ought to be called.  Perhaps the problem is here:

                /* clean up any state from previous QTD ...*/
                if (last) {
                        if (likely (last->urb != urb)) {
                                ehci_urb_done(ehci, last->urb, last_status);
                                count++;
                        }
                        ehci_qtd_free (ehci, last);
                        last = NULL;
                        last_status = -EINPROGRESS;
                }

In the case where (last->urb == urb) we are handling one of these 
still-active QTDs following a bad one.  In this case we shouldn't set 
last_status back to -EINPROGRESS.  So perhaps we need to move that 
statement into the "if" block:

                if (last) {
                        if (likely (last->urb != urb)) {
                                ehci_urb_done(ehci, last->urb, last_status);
                                count++;
                                last_status = -EINPROGRESS;
                        }
                        ehci_qtd_free (ehci, last);
                        last = NULL;
                }

Does that suffice to fix your problem?

> You probably didn't notice it as it's quite unique situation. I've
> noticed it because we have hardware that doing large URB transfers and
> as part of normal operations we use short reads. Our driver worked fined
> through 2.4.x and 2.6.x up until EREMOTEIO "optimization" in
> qh_completions.

That could indeed explain why I didn't see the problem in my testing.

Alan Stern

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

Reply via email to