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