diff mbox series

[v1] xhci: dbc: fix handling ClearFeature Halt requests

Message ID 20240722150738.61486-1-ukaszb@chromium.org
State New
Headers show
Series [v1] xhci: dbc: fix handling ClearFeature Halt requests | expand

Commit Message

Łukasz Bartosik July 22, 2024, 3:07 p.m. UTC
DbC driver does not handle ClearFeature Halt requests correctly
which in turn may lead to dropping packets on the receive path.

Below is a trace capture where due to incorrect handling of
ClearFeature Halt packet gets dropped on the receive path.

/sys/kernel/debug/tracing # cat trace
1) kworker/10:3-514   [010] d..1.  2925.601843: xhci_dbc_handle_event:
	EVENT: TRB 000000019588c0e0 status 'Stall Error' len 0 slot 1 ep 2
	type 'Transfer Event' flags e:C

2) kworker/10:3-514   [010] d..1.  2925.613285: xhci_dbc_handle_event:
	EVENT: TRB 000000019588bc80 status 'Stall Error' len 1024 slot 1
	ep 3 type 'Transfer Event' flags e:C

3) kworker/10:3-514   [010] d..1.  2925.619024: xhci_dbc_handle_transfer:
	BULK: Buffer 0000000244b49800 length 1024 TD size 0 intr 0 type
	'Normal' flags b:i:I:c:s:i:e:C

4) kworker/10:3-514   [010] d..1.  2925.619025: xhci_dbc_giveback_request:
	bulk-in: req 00000000a70b5ad2 length 0/1024 ==> -6

5) kworker/10:3-514   [010] dNs2.  2925.623820: xhci_dbc_gadget_ep_queue:
	BULK: Buffer 0000000244b49800 length 1024 TD size 0 intr 0 type
	'Normal' flags b:i:I:c:s:i:e:c

6) kworker/10:3-514   [010] dNs1.  2925.623823: xhci_dbc_queue_request:
	bulk-in: req 00000000a70b5ad2 length 0/1024 ==> -115

7) kworker/10:3-514   [010] d..1.  2925.629865: xhci_dbc_handle_event:
	EVENT: TRB 000000019588bc80 status 'Short Packet' len 1000 slot 1
	ep 3 type 'Transfer Event' flags e:C

8) kworker/10:3-514   [010] d..1.  2925.635540: xhci_dbc_handle_event:
	EVENT: TRB 000000019588bc90 status 'Short Packet' len 763 slot 1
	ep 3 type 'Transfer Event' flags e:C

9) kworker/10:3-514   [010] d..1.  2925.635540: xhci_dbc_handle_transfer:
	BULK: Buffer 0000000244b49c00 length 1024 TD size 0 intr 0 type
	'Normal' flags b:i:I:c:s:i:e:C

10) kworker/10:3-514  [010] d..1.  2925.635541: xhci_dbc_giveback_request:
	bulk-in: req 00000000b4ec77d7 length 261/1024 ==> 0

11) kworker/10:3-514  [010] dNs2.  2925.635561: xhci_dbc_gadget_ep_queue:
	BULK: Buffer 0000000244b49c00 length 1024 TD size 0 intr 0 type
	'Normal' flags b:i:I:c:s:i:e:c

12) kworker/10:3-514  [010] dNs1.  2925.635562: xhci_dbc_queue_request:
	bulk-in: req 00000000b4ec77d7 length 0/1024 ==> -115

Lines 1 and 2 are Endpoints OUT and IN responses to receiving ClearFeature
Halt requests.

Line 7 notifies of reception of 24 bytes packet.

Line 8 notifies of reception of 261 bytes packet

In Lines [9, 12] 261 bytes packet is being processed.

However 24 bytes packet gets dropped. The kernel log includes entry which
is an indication of a packet drop:
[  127.651845] xhci_hcd 0000:00:0d.0: no matched request

This fix adds correct handling of ClearFeature Halt requests
by restarting an endpoint which received the request.

Fixes: dfba2174dc42 ("usb: xhci: Add DbC support in xHCI driver")
Signed-off-by: Łukasz Bartosik <ukaszb@chromium.org>
---
 drivers/usb/host/xhci-dbgcap.c | 29 +++++++++++++++++++----------
 drivers/usb/host/xhci-dbgtty.c |  5 +++++
 2 files changed, 24 insertions(+), 10 deletions(-)

Comments

Łukasz Bartosik July 22, 2024, 11:17 p.m. UTC | #1
On Mon, Jul 22, 2024 at 6:31 PM Greg Kroah-Hartman
<gregkh@linuxfoundation.org> wrote:
>
> On Mon, Jul 22, 2024 at 03:07:38PM +0000, Łukasz Bartosik wrote:
> > DbC driver does not handle ClearFeature Halt requests correctly
> > which in turn may lead to dropping packets on the receive path.
> >
> > Below is a trace capture where due to incorrect handling of
> > ClearFeature Halt packet gets dropped on the receive path.
> >
> > /sys/kernel/debug/tracing # cat trace
> > 1) kworker/10:3-514   [010] d..1.  2925.601843: xhci_dbc_handle_event:
> >       EVENT: TRB 000000019588c0e0 status 'Stall Error' len 0 slot 1 ep 2
> >       type 'Transfer Event' flags e:C
> >
> > 2) kworker/10:3-514   [010] d..1.  2925.613285: xhci_dbc_handle_event:
> >       EVENT: TRB 000000019588bc80 status 'Stall Error' len 1024 slot 1
> >       ep 3 type 'Transfer Event' flags e:C
> >
> > 3) kworker/10:3-514   [010] d..1.  2925.619024: xhci_dbc_handle_transfer:
> >       BULK: Buffer 0000000244b49800 length 1024 TD size 0 intr 0 type
> >       'Normal' flags b:i:I:c:s:i:e:C
> >
> > 4) kworker/10:3-514   [010] d..1.  2925.619025: xhci_dbc_giveback_request:
> >       bulk-in: req 00000000a70b5ad2 length 0/1024 ==> -6
> >
> > 5) kworker/10:3-514   [010] dNs2.  2925.623820: xhci_dbc_gadget_ep_queue:
> >       BULK: Buffer 0000000244b49800 length 1024 TD size 0 intr 0 type
> >       'Normal' flags b:i:I:c:s:i:e:c
> >
> > 6) kworker/10:3-514   [010] dNs1.  2925.623823: xhci_dbc_queue_request:
> >       bulk-in: req 00000000a70b5ad2 length 0/1024 ==> -115
> >
> > 7) kworker/10:3-514   [010] d..1.  2925.629865: xhci_dbc_handle_event:
> >       EVENT: TRB 000000019588bc80 status 'Short Packet' len 1000 slot 1
> >       ep 3 type 'Transfer Event' flags e:C
> >
> > 8) kworker/10:3-514   [010] d..1.  2925.635540: xhci_dbc_handle_event:
> >       EVENT: TRB 000000019588bc90 status 'Short Packet' len 763 slot 1
> >       ep 3 type 'Transfer Event' flags e:C
> >
> > 9) kworker/10:3-514   [010] d..1.  2925.635540: xhci_dbc_handle_transfer:
> >       BULK: Buffer 0000000244b49c00 length 1024 TD size 0 intr 0 type
> >       'Normal' flags b:i:I:c:s:i:e:C
> >
> > 10) kworker/10:3-514  [010] d..1.  2925.635541: xhci_dbc_giveback_request:
> >       bulk-in: req 00000000b4ec77d7 length 261/1024 ==> 0
> >
> > 11) kworker/10:3-514  [010] dNs2.  2925.635561: xhci_dbc_gadget_ep_queue:
> >       BULK: Buffer 0000000244b49c00 length 1024 TD size 0 intr 0 type
> >       'Normal' flags b:i:I:c:s:i:e:c
> >
> > 12) kworker/10:3-514  [010] dNs1.  2925.635562: xhci_dbc_queue_request:
> >       bulk-in: req 00000000b4ec77d7 length 0/1024 ==> -115
> >
> > Lines 1 and 2 are Endpoints OUT and IN responses to receiving ClearFeature
> > Halt requests.
> >
> > Line 7 notifies of reception of 24 bytes packet.
> >
> > Line 8 notifies of reception of 261 bytes packet
> >
> > In Lines [9, 12] 261 bytes packet is being processed.
> >
> > However 24 bytes packet gets dropped. The kernel log includes entry which
> > is an indication of a packet drop:
> > [  127.651845] xhci_hcd 0000:00:0d.0: no matched request
> >
> > This fix adds correct handling of ClearFeature Halt requests
> > by restarting an endpoint which received the request.
> >
> > Fixes: dfba2174dc42 ("usb: xhci: Add DbC support in xHCI driver")
> > Signed-off-by: Łukasz Bartosik <ukaszb@chromium.org>
> > ---
> >  drivers/usb/host/xhci-dbgcap.c | 29 +++++++++++++++++++----------
> >  drivers/usb/host/xhci-dbgtty.c |  5 +++++
> >  2 files changed, 24 insertions(+), 10 deletions(-)
> >
> > diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
> > index 872d9cddbcef..b7af3a5ac98a 100644
> > --- a/drivers/usb/host/xhci-dbgcap.c
> > +++ b/drivers/usb/host/xhci-dbgcap.c
> > @@ -173,7 +173,7 @@ static void xhci_dbc_giveback(struct dbc_request *req, int status)
> >       spin_lock(&dbc->lock);
> >  }
> >
> > -static void xhci_dbc_flush_single_request(struct dbc_request *req)
> > +static void xhci_dbc_flush_single_request(struct dbc_request *req, int status)
>
>
> So now we need to look up what "status" is here for every place this is
> called?  That's going to be a pain, as you didn't even document it :(
>

The xhci_dbc_flush_single_request is called only in the function
xhci_dbc_flush_endpoint_requests which follows
xhci_dbc_flush_single_request but I will document it.

Thanks,
Lukasz

> thanks,
>
> greg k-h
diff mbox series

Patch

diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 872d9cddbcef..b7af3a5ac98a 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -173,7 +173,7 @@  static void xhci_dbc_giveback(struct dbc_request *req, int status)
 	spin_lock(&dbc->lock);
 }
 
-static void xhci_dbc_flush_single_request(struct dbc_request *req)
+static void xhci_dbc_flush_single_request(struct dbc_request *req, int status)
 {
 	union xhci_trb	*trb = req->trb;
 
@@ -183,21 +183,27 @@  static void xhci_dbc_flush_single_request(struct dbc_request *req)
 	trb->generic.field[3]	&= cpu_to_le32(TRB_CYCLE);
 	trb->generic.field[3]	|= cpu_to_le32(TRB_TYPE(TRB_TR_NOOP));
 
-	xhci_dbc_giveback(req, -ESHUTDOWN);
+	xhci_dbc_giveback(req, status);
 }
 
-static void xhci_dbc_flush_endpoint_requests(struct dbc_ep *dep)
+static void xhci_dbc_flush_endpoint_requests(struct dbc_ep *dep, bool restart)
 {
+	struct list_head	*list = &dep->list_pending;
 	struct dbc_request	*req, *tmp;
+	int			status = -ESHUTDOWN;
+
+	list_for_each_entry_safe(req, tmp, list, list_pending) {
+		if (restart && list_is_last(&req->list_pending, list))
+			status = -ERESTART;
 
-	list_for_each_entry_safe(req, tmp, &dep->list_pending, list_pending)
-		xhci_dbc_flush_single_request(req);
+		xhci_dbc_flush_single_request(req, status);
+	}
 }
 
 static void xhci_dbc_flush_requests(struct xhci_dbc *dbc)
 {
-	xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_OUT]);
-	xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_IN]);
+	xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_OUT], false);
+	xhci_dbc_flush_endpoint_requests(&dbc->eps[BULK_IN], false);
 }
 
 struct dbc_request *
@@ -718,10 +724,13 @@  static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event)
 	case COMP_TRB_ERROR:
 	case COMP_BABBLE_DETECTED_ERROR:
 	case COMP_USB_TRANSACTION_ERROR:
-	case COMP_STALL_ERROR:
 		dev_warn(dbc->dev, "tx error %d detected\n", comp_code);
 		status = -comp_code;
 		break;
+	case COMP_STALL_ERROR:
+		/* Restart endpoint */
+		xhci_dbc_flush_endpoint_requests(dep, true);
+		return;
 	default:
 		dev_err(dbc->dev, "unknown tx error %d\n", comp_code);
 		status = -comp_code;
@@ -823,12 +832,12 @@  static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc)
 
 			if (ctrl & DBC_CTRL_HALT_IN_TR) {
 				dep = get_in_ep(dbc);
-				xhci_dbc_flush_endpoint_requests(dep);
+				xhci_dbc_flush_endpoint_requests(dep, false);
 			}
 
 			if (ctrl & DBC_CTRL_HALT_OUT_TR) {
 				dep = get_out_ep(dbc);
-				xhci_dbc_flush_endpoint_requests(dep);
+				xhci_dbc_flush_endpoint_requests(dep, false);
 			}
 
 			return EVT_DONE;
diff --git a/drivers/usb/host/xhci-dbgtty.c b/drivers/usb/host/xhci-dbgtty.c
index b74e98e94393..bdf80aa2b28c 100644
--- a/drivers/usb/host/xhci-dbgtty.c
+++ b/drivers/usb/host/xhci-dbgtty.c
@@ -121,6 +121,7 @@  static void dbc_write_complete(struct xhci_dbc *dbc, struct dbc_request *req)
 	list_add(&req->list_pool, &port->write_pool);
 	switch (req->status) {
 	case 0:
+	case -ERESTART:
 		dbc_start_tx(port);
 		break;
 	case -ESHUTDOWN:
@@ -318,6 +319,10 @@  static void dbc_rx_push(struct tasklet_struct *t)
 		case -ESHUTDOWN:
 			disconnect = true;
 			break;
+		case -ERESTART:
+			disconnect = false;
+			req->actual = 0;
+			break;
 		default:
 			pr_warn("ttyDBC0: unexpected RX status %d\n",
 				req->status);