diff mbox series

[1/5] usb: gadget: Introduce usb_request->is_last field

Message ID 456f864b86a72ab9490ce095d5ba3f59b39d6a09.1588025916.git.thinhn@synopsys.com (mailing list archive)
State Superseded
Headers show
Series usb: dwc3: gadget: Handle streams | expand

Commit Message

Thinh Nguyen April 27, 2020, 10:27 p.m. UTC
A transfer is composed of one or more usb_requests. Currently, only the
function driver knows this based on its implementation and its class
protocol. However, some usb controllers need to know this to update its
resources. For example, the DWC3 controller needs this info to update
its internal resources and initiate different streams.

Introduce a new field is_last to usb_request to inform the controller
driver whether the request is the last of its transfer.

Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
---
 include/linux/usb/gadget.h | 2 ++
 1 file changed, 2 insertions(+)

Comments

Peter Chen April 29, 2020, 3:15 a.m. UTC | #1
On 20-04-27 15:27:41, Thinh Nguyen wrote:
> A transfer is composed of one or more usb_requests. Currently, only the
> function driver knows this based on its implementation and its class
> protocol. However, some usb controllers need to know this to update its
> resources. For example, the DWC3 controller needs this info to update
> its internal resources and initiate different streams.
> 
> Introduce a new field is_last to usb_request to inform the controller
> driver whether the request is the last of its transfer.
> 
> Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
> ---
>  include/linux/usb/gadget.h | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
> index e959c09a97c9..742c52f7e470 100644
> --- a/include/linux/usb/gadget.h
> +++ b/include/linux/usb/gadget.h
> @@ -50,6 +50,7 @@ struct usb_ep;
>   * @short_not_ok: When reading data, makes short packets be
>   *     treated as errors (queue stops advancing till cleanup).
>   * @dma_mapped: Indicates if request has been mapped to DMA (internal)
> + * @is_last: Indicates if this request is the last of a transfer.

Would you please describe the use case for it, and what's 'transfer'
and 'request' in your use case?

Peter

>   * @complete: Function called when request completes, so this request and
>   *	its buffer may be re-used.  The function will always be called with
>   *	interrupts disabled, and it must not sleep.
> @@ -108,6 +109,7 @@ struct usb_request {
>  	unsigned		zero:1;
>  	unsigned		short_not_ok:1;
>  	unsigned		dma_mapped:1;
> +	unsigned		is_last:1;
>  
>  	void			(*complete)(struct usb_ep *ep,
>  					struct usb_request *req);
> -- 
> 2.11.0
>
Thinh Nguyen April 30, 2020, 1 a.m. UTC | #2
Hi,

Peter Chen wrote:
> On 20-04-27 15:27:41, Thinh Nguyen wrote:
>> A transfer is composed of one or more usb_requests. Currently, only the
>> function driver knows this based on its implementation and its class
>> protocol. However, some usb controllers need to know this to update its
>> resources. For example, the DWC3 controller needs this info to update
>> its internal resources and initiate different streams.
>>
>> Introduce a new field is_last to usb_request to inform the controller
>> driver whether the request is the last of its transfer.
>>
>> Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
>> ---
>>   include/linux/usb/gadget.h | 2 ++
>>   1 file changed, 2 insertions(+)
>>
>> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
>> index e959c09a97c9..742c52f7e470 100644
>> --- a/include/linux/usb/gadget.h
>> +++ b/include/linux/usb/gadget.h
>> @@ -50,6 +50,7 @@ struct usb_ep;
>>    * @short_not_ok: When reading data, makes short packets be
>>    *     treated as errors (queue stops advancing till cleanup).
>>    * @dma_mapped: Indicates if request has been mapped to DMA (internal)
>> + * @is_last: Indicates if this request is the last of a transfer.
> Would you please describe the use case for it, and what's 'transfer'
> and 'request' in your use case?
>

The transfer size is defined by a class protocol. The function driver 
will determine how many usb_requests are needed to fulfill that transfer.

For example, in MSC, a READ/WRITE command can request for a transfer 
size up to (512 * 2^31-1) bytes. It'd be too large for a single 
usb_request, which is why the mass_storage function driver limits each 
request to 16KB max by default. A MSC command itself is a transfer, same 
with its status.

This is a similar case for UASP. However, the f_tcm and the target 
drivers current implementation only use a single request per transfer. 
(This needs to be fixed, along with some other issues in f_tcm).

The use case here is that DWC3x controller needs to update its resources 
whenever a transfer is completed. This is a requirement for streams when 
it needs to free up some resources for different stream transfers. The 
function driver must pass this information to the controller driver for 
streams to work properly.

Potentially, another use case for this is to update the documentation on 
usb_dequeue_request(). By providing the concept of a transfer, we can 
say that dequeuing an in-flight request will cancel the transfer, and 
any incomplete request within the transfer must be given back to the 
function driver. This would make sense for controllers that use TRB ring 
buffer and dequeue/enqueue TRB pointers. But this idea still needs more 
thoughts.

BR,
Thinh
Peter Chen April 30, 2020, 3:57 a.m. UTC | #3
> > On 20-04-27 15:27:41, Thinh Nguyen wrote:
> >> A transfer is composed of one or more usb_requests. Currently, only
> >> the function driver knows this based on its implementation and its
> >> class protocol. However, some usb controllers need to know this to
> >> update its resources. For example, the DWC3 controller needs this
> >> info to update its internal resources and initiate different streams.
> >>
> >> Introduce a new field is_last to usb_request to inform the controller
> >> driver whether the request is the last of its transfer.
> >>
> >> Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
> >> ---
> >>   include/linux/usb/gadget.h | 2 ++
> >>   1 file changed, 2 insertions(+)
> >>
> >> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
> >> index e959c09a97c9..742c52f7e470 100644
> >> --- a/include/linux/usb/gadget.h
> >> +++ b/include/linux/usb/gadget.h
> >> @@ -50,6 +50,7 @@ struct usb_ep;
> >>    * @short_not_ok: When reading data, makes short packets be
> >>    *     treated as errors (queue stops advancing till cleanup).
> >>    * @dma_mapped: Indicates if request has been mapped to DMA
> >> (internal)
> >> + * @is_last: Indicates if this request is the last of a transfer.
> > Would you please describe the use case for it, and what's 'transfer'
> > and 'request' in your use case?
> >
> 
> The transfer size is defined by a class protocol. The function driver will determine
> how many usb_requests are needed to fulfill that transfer.
> 

Thanks for your information.

If 'transfer size' here is software concept, why controller needs to know? The general
controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.

> For example, in MSC, a READ/WRITE command can request for a transfer size up
> to (512 * 2^31-1) bytes. It'd be too large for a single usb_request, which is why the
> mass_storage function driver limits each request to 16KB max by default. A MSC
> command itself is a transfer, same with its status.
> 
> This is a similar case for UASP. However, the f_tcm and the target drivers current
> implementation only use a single request per transfer.
> (This needs to be fixed, along with some other issues in f_tcm).
> 
> The use case here is that DWC3x controller needs to update its resources
> whenever a transfer is completed. This is a requirement for streams when it needs
> to free up some resources for different stream transfers. The function driver must
> pass this information to the controller driver for streams to work properly.
> 

Does the controller case about stream information or the transfer information?

> Potentially, another use case for this is to update the documentation on
> usb_dequeue_request(). By providing the concept of a transfer, we can say that
> dequeuing an in-flight request will cancel the transfer, and any incomplete request
> within the transfer must be given back to the function driver. This would make
> sense for controllers that use TRB ring buffer and dequeue/enqueue TRB pointers.
> But this idea still needs more thoughts.
> 
 
I think class driver needs to consider that, the controller driver only needs to handle
request and related TRBs.

Peter
Thinh Nguyen April 30, 2020, 4:54 a.m. UTC | #4
Peter Chen wrote:
>   
>>> On 20-04-27 15:27:41, Thinh Nguyen wrote:
>>>> A transfer is composed of one or more usb_requests. Currently, only
>>>> the function driver knows this based on its implementation and its
>>>> class protocol. However, some usb controllers need to know this to
>>>> update its resources. For example, the DWC3 controller needs this
>>>> info to update its internal resources and initiate different streams.
>>>>
>>>> Introduce a new field is_last to usb_request to inform the controller
>>>> driver whether the request is the last of its transfer.
>>>>
>>>> Signed-off-by: Thinh Nguyen <thinhn@synopsys.com>
>>>> ---
>>>>    include/linux/usb/gadget.h | 2 ++
>>>>    1 file changed, 2 insertions(+)
>>>>
>>>> diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
>>>> index e959c09a97c9..742c52f7e470 100644
>>>> --- a/include/linux/usb/gadget.h
>>>> +++ b/include/linux/usb/gadget.h
>>>> @@ -50,6 +50,7 @@ struct usb_ep;
>>>>     * @short_not_ok: When reading data, makes short packets be
>>>>     *     treated as errors (queue stops advancing till cleanup).
>>>>     * @dma_mapped: Indicates if request has been mapped to DMA
>>>> (internal)
>>>> + * @is_last: Indicates if this request is the last of a transfer.
>>> Would you please describe the use case for it, and what's 'transfer'
>>> and 'request' in your use case?
>>>
>> The transfer size is defined by a class protocol. The function driver will determine
>> how many usb_requests are needed to fulfill that transfer.
>>
> Thanks for your information.
>
> If 'transfer size' here is software concept, why controller needs to know? The general
> controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.

While some controllers don't have the concept of this, DWC_usb3x does. 
It has a notion of starting and ending a transfer. While a transfer is 
started, the endpoint uses a resource. It releases that resource when 
the transfer completes. So far, dwc3 implemented in such a way that bulk 
transfers are always in-progress and don't complete. That's fine so far, 
but it's not the case with streams.

>
>> For example, in MSC, a READ/WRITE command can request for a transfer size up
>> to (512 * 2^31-1) bytes. It'd be too large for a single usb_request, which is why the
>> mass_storage function driver limits each request to 16KB max by default. A MSC
>> command itself is a transfer, same with its status.
>>
>> This is a similar case for UASP. However, the f_tcm and the target drivers current
>> implementation only use a single request per transfer.
>> (This needs to be fixed, along with some other issues in f_tcm).
>>
>> The use case here is that DWC3x controller needs to update its resources
>> whenever a transfer is completed. This is a requirement for streams when it needs
>> to free up some resources for different stream transfers. The function driver must
>> pass this information to the controller driver for streams to work properly.
>>
> Does the controller case about stream information or the transfer information?

For streams, each stransfer has a stream ID, and each transfer uses a 
resource.

>
>> Potentially, another use case for this is to update the documentation on
>> usb_dequeue_request(). By providing the concept of a transfer, we can say that
>> dequeuing an in-flight request will cancel the transfer, and any incomplete request
>> within the transfer must be given back to the function driver. This would make
>> sense for controllers that use TRB ring buffer and dequeue/enqueue TRB pointers.
>> But this idea still needs more thoughts.
>>
>   
> I think class driver needs to consider that, the controller driver only needs to handle
> request and related TRBs.

It maybe true for some controllers, but DWC_usb3x needs this information.

BR,
Thinh
Alan Stern April 30, 2020, 2:21 p.m. UTC | #5
On Thu, 30 Apr 2020, Thinh Nguyen wrote:

> Peter Chen wrote:

> > If 'transfer size' here is software concept, why controller needs to know? The general
> > controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.
> 
> While some controllers don't have the concept of this, DWC_usb3x does. 
> It has a notion of starting and ending a transfer. While a transfer is 
> started, the endpoint uses a resource. It releases that resource when 
> the transfer completes. So far, dwc3 implemented in such a way that bulk 
> transfers are always in-progress and don't complete. That's fine so far, 
> but it's not the case with streams.

This is peculiar.  I haven't heard of any other controller doing this.

What does the controller use this resource for?  Would anything go 
wrong if you told the controller that each transfer was a single 
usb_request?

Alan Stern
Thinh Nguyen April 30, 2020, 5:17 p.m. UTC | #6
Hi,

Alan Stern wrote:
> On Thu, 30 Apr 2020, Thinh Nguyen wrote:
>
>> Peter Chen wrote:
>>> If 'transfer size' here is software concept, why controller needs to know? The general
>>> controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.
>> While some controllers don't have the concept of this, DWC_usb3x does.
>> It has a notion of starting and ending a transfer. While a transfer is
>> started, the endpoint uses a resource. It releases that resource when
>> the transfer completes. So far, dwc3 implemented in such a way that bulk
>> transfers are always in-progress and don't complete. That's fine so far,
>> but it's not the case with streams.
> This is peculiar.  I haven't heard of any other controller doing this.
>
> What does the controller use this resource for?  Would anything go
> wrong if you told the controller that each transfer was a single
> usb_request?

It's no problem. Each transfer can be a single request. Just set the 
request->is_last. (Refer to [patch 2/5] for f_tcm).

The issue here is that the controller needs to know when a stream 
completes so it can start on a different stream. In the controller 
driver, this is done by setting a certain control bit in the TRB 
indicating the last TRB of a transfer. This knowledge can only come from 
the function driver, which is why we need this "is_last" field.

BR,
Thinh
Alan Stern April 30, 2020, 6:22 p.m. UTC | #7
On Thu, 30 Apr 2020, Thinh Nguyen wrote:

> Hi,
> 
> Alan Stern wrote:
> > On Thu, 30 Apr 2020, Thinh Nguyen wrote:
> >
> >> Peter Chen wrote:
> >>> If 'transfer size' here is software concept, why controller needs to know? The general
> >>> controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.
> >> While some controllers don't have the concept of this, DWC_usb3x does.
> >> It has a notion of starting and ending a transfer. While a transfer is
> >> started, the endpoint uses a resource. It releases that resource when
> >> the transfer completes. So far, dwc3 implemented in such a way that bulk
> >> transfers are always in-progress and don't complete. That's fine so far,
> >> but it's not the case with streams.
> > This is peculiar.  I haven't heard of any other controller doing this.
> >
> > What does the controller use this resource for?  Would anything go
> > wrong if you told the controller that each transfer was a single
> > usb_request?
> 
> It's no problem. Each transfer can be a single request. Just set the 
> request->is_last. (Refer to [patch 2/5] for f_tcm).
> 
> The issue here is that the controller needs to know when a stream 
> completes so it can start on a different stream. In the controller 

Why does it need to know this?  Why can't it start on a different 
stream at any time?

> driver, this is done by setting a certain control bit in the TRB 
> indicating the last TRB of a transfer. This knowledge can only come from 
> the function driver, which is why we need this "is_last" field.

What's wrong with just assuming _every_ usb_request is the last one of 
its transfer?  Then you wouldn't have to add a new flag or change the 
existing function drivers.

Alan Stern
Thinh Nguyen April 30, 2020, 6:36 p.m. UTC | #8
Alan Stern wrote:
> On Thu, 30 Apr 2020, Thinh Nguyen wrote:
>
>> Hi,
>>
>> Alan Stern wrote:
>>> On Thu, 30 Apr 2020, Thinh Nguyen wrote:
>>>
>>>> Peter Chen wrote:
>>>>> If 'transfer size' here is software concept, why controller needs to know? The general
>>>>> controller internal logic doesn't care class protocol, it only cares TRB chain software prepares.
>>>> While some controllers don't have the concept of this, DWC_usb3x does.
>>>> It has a notion of starting and ending a transfer. While a transfer is
>>>> started, the endpoint uses a resource. It releases that resource when
>>>> the transfer completes. So far, dwc3 implemented in such a way that bulk
>>>> transfers are always in-progress and don't complete. That's fine so far,
>>>> but it's not the case with streams.
>>> This is peculiar.  I haven't heard of any other controller doing this.
>>>
>>> What does the controller use this resource for?  Would anything go
>>> wrong if you told the controller that each transfer was a single
>>> usb_request?
>> It's no problem. Each transfer can be a single request. Just set the
>> request->is_last. (Refer to [patch 2/5] for f_tcm).
>>
>> The issue here is that the controller needs to know when a stream
>> completes so it can start on a different stream. In the controller
> Why does it need to know this?  Why can't it start on a different
> stream at any time?

By design, whenever the controller needs to start on a different stream, 
it will issue a START_TRANSFER command along with a stream ID. It cannot 
issue START_TRANSFER command again until the previous transfer completes 
or ended.

>
>> driver, this is done by setting a certain control bit in the TRB
>> indicating the last TRB of a transfer. This knowledge can only come from
>> the function driver, which is why we need this "is_last" field.
> What's wrong with just assuming _every_ usb_request is the last one of
> its transfer?  Then you wouldn't have to add a new flag or change the
> existing function drivers.

That will affect performance. The driver will then need to service 
multiple interrupts and restart the transfer multiple times.

BR,
Thinh
diff mbox series

Patch

diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index e959c09a97c9..742c52f7e470 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -50,6 +50,7 @@  struct usb_ep;
  * @short_not_ok: When reading data, makes short packets be
  *     treated as errors (queue stops advancing till cleanup).
  * @dma_mapped: Indicates if request has been mapped to DMA (internal)
+ * @is_last: Indicates if this request is the last of a transfer.
  * @complete: Function called when request completes, so this request and
  *	its buffer may be re-used.  The function will always be called with
  *	interrupts disabled, and it must not sleep.
@@ -108,6 +109,7 @@  struct usb_request {
 	unsigned		zero:1;
 	unsigned		short_not_ok:1;
 	unsigned		dma_mapped:1;
+	unsigned		is_last:1;
 
 	void			(*complete)(struct usb_ep *ep,
 					struct usb_request *req);