Re: [PATCH 2/3] usb: dwc3: gadget: Don't skip updating remaining data

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Felipe,


On 8/1/2018 1:33 AM, Felipe Balbi wrote:
> Felipe Balbi <balbi@xxxxxxxxxx> writes:
>
>> Hi,
>>
>> Felipe Balbi <balbi@xxxxxxxxxx> writes:
>>
>> <snip>
>>
>>>> This is an issue, but it's not the same one.
>>>>
>>>>      irq/16-dwc3-5032  [003] d...    65.404194: dwc3_complete_trb:
>>>> ep1out: trb 00000000890522d5 buf 00000000b8d6d000 size 0 ctrl 3b56446c
>>>> (hlCS:Sc:isoc-first)
>>>>
>>> So this is chained to the next one, that's correct.
>>>
>>>
>>>>      irq/16-dwc3-5032  [003] d...    65.404209: dwc3_complete_trb:
>>>> ep1out: trb 00000000c15f388f buf 0000000037821000 size 1023 ctrl
>>>> 3b564c78 (hlcS:SC:isoc)
>>>>
>>> But here, HWO should've been left as is, because of the short packet.
>>> That's what the databook says on the two notes on section 8.2.3 after
>>> table 8-8 of Databook 2.60a:

My proposal doesn't change how we handle the TRB's HWO currently.

>>>
>>> 1. When a TRB receives whose CSP bit is 0 and CHN bit is 1 receives a
>>> short packet, the chained TRBs that follow it are not written back
>>> (e.g. the BUFSIZ and HWO fields remain the same as the
>>> software-prepared value)
>>>
>>> 2. In the case of an OUT endpoint, if the CHN bit is set (and CSP is
>>> also set), and a short packet is received, the core retires the TRB in
>>> progress and skip past the TRB where CHN=0, accumulating the ISP and
>>> IOC bits from each TRB. If ISP or IOC is set in any TRB, the core
>>> generates an XferInProgress event. Hardware does not set the HWO bit
>>> to 0 in skipped TRBs. If the endpoint type is isochronous, the CHN=0
>>> TRB will also be retired and its buffer size field updated with the
>>> total number of bytes remaining in the BD.
>>>
>>> Note that at most we have confirmation that SIZE will be updated in
>>> case of Isoc endpoints, but there's nothing there about HWO, so I
>>> expected it to remain set according to the rest of the text.
>>>
>>> There's one possibility that "TRB will also be *retired*" means that
>>> it's not skipped, which would mean that HWO is, indeed, set to 0. To

Right. And the programming guide also says that for isoc OUT transfer,
- Any non-first TRBs with CHN=1 that had not already been retired will
not be written back. HWO will still be 1, and software can reclaim them
for another transfer.
- The last TRB of the Buffer Descriptor will be retired with:
    * HWO = 0.
    * BUFSIZ = The total remaining buffer size of the Buffer Descriptor.
 
So we know that the last isoc TRB will have CHN=0 and HWO=0.


>>> patch that, however, we don't need all the extra checking you have
>>> implemented. I'll try to propose a slightly simpler fix if you're
>>> willing to test it out.
>> Something like below:
> and here's another possibility. This makes it clearer that we want to
> skip all TRBs with CHN bit set:
>
> diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
> index 9ba614032d5d..56e2a2ebae66 100644
> --- a/drivers/usb/dwc3/gadget.c
> +++ b/drivers/usb/dwc3/gadget.c
> @@ -2227,6 +2227,7 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
>                 struct dwc3_request *req, struct dwc3_trb *trb,
>                 const struct dwc3_event_depevt *event, int status, int chain)
>  {
> +       const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
>         unsigned int            count;
>  
>         dwc3_ep_inc_deq(dep);
> @@ -2256,6 +2257,16 @@ static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
>                 return 1;
>         }
>  
> +       /*
> +        * In case of ISOC endpoints and Short or Zero packets, the
> +        * last TRB will be retired and its size field will be updated
> +        * to contain the full remaining size; meaning req->remaining
> +        * will be count from the last TRB in the chain.
> +        */
> +       if ((req->zero || req->unaligned) && usb_endpoint_xfer_isoc(desc)
> +                       && chain)
> +               return 0;
> +
>         count = trb->size & DWC3_TRB_SIZE_MASK;
>         req->remaining += count;
>

These patches will not fix the issue.

Let's take a look at the problem again.

static int dwc3_gadget_ep_reclaim_completed_trb(struct dwc3_ep *dep,
        struct dwc3_request *req, struct dwc3_trb *trb,
        const struct dwc3_event_depevt *event, int status, int chain)
{
    unsigned int        count;

    dwc3_ep_inc_deq(dep);

    trace_dwc3_complete_trb(dep, trb);

    /*
     * If we're in the middle of series of chained TRBs and we
     * receive a short transfer along the way, DWC3 will skip
     * through all TRBs including the last TRB in the chain (the
     * where CHN bit is zero. DWC3 will also avoid clearing HWO
     * bit and SW has to do it manually.
     *
     * We're going to do that here to avoid problems of HW trying
     * to use bogus TRBs for transfers.
     */
    if (chain && (trb->ctrl & DWC3_TRB_CTRL_HWO))
        trb->ctrl &= ~DWC3_TRB_CTRL_HWO;

    /*
     * If we're dealing with unaligned size OUT transfer, we will be left
     * with one TRB pending in the ring. We need to manually clear HWO bit
     * from that TRB.
     */
    if ((req->zero || req->unaligned) && (trb->ctrl & DWC3_TRB_CTRL_HWO)) {
                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>>>>>>>>>>
This tries to check for the last TRB and return early. This works in normal
cases and it stops incrementing the req->remaining. But for isoc OUT
transfers,
this case won't hit due to reason mention previously.
<<<<<<<<<<

        trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
        return 1;
    }

    count = trb->size & DWC3_TRB_SIZE_MASK;
    req->remaining += count;
         ^^^^^^^^^^^^^^^^^^^^^^^
>>>>>>>>>>
So, your proposals will still update the req->remaining with the BUFSIZ
count of
the last TRB.
<<<<<<<<<<


    if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN)
        return 1;

    if (event->status & DEPEVT_STATUS_SHORT && !chain)
        return 1;

    if (event->status & DEPEVT_STATUS_IOC)
        return 1;

    return 0;
}



static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
        const struct dwc3_event_depevt *event,
        struct dwc3_request *req, int status)
{
....
    req->request.actual = req->request.length - req->remaining;
                                                    ^^^^^^^^^^^^^^
>>>>>>>>>>
And the calculation is off because req->remaining includes the remaining
of the
last TRB BUFSIZ count.
<<<<<<<<<<
 ....
}

I included the traces. Hopefully it will go out to the mailing list.
Thanks for making the patches, and of course I will test them. :)

Thinh

Attachment: ktraces_isoc_20180801.tar.xz
Description: ktraces_isoc_20180801.tar.xz


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux