[PATCH v2] xhci: dbc: fix handling ClearFeature Halt requests

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

 



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@xxxxxxxxxxxx>
---
Changes v2->v1:
- Documented both xhci_dbc_flush_single_request and 
xhci_dbc_flush_endpoint_requests functions.
---
 drivers/usb/host/xhci-dbgcap.c | 48 +++++++++++++++++++++++++++-------
 drivers/usb/host/xhci-dbgtty.c |  5 ++++
 2 files changed, 43 insertions(+), 10 deletions(-)

diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c
index 872d9cddbcef..36ec3242e096 100644
--- a/drivers/usb/host/xhci-dbgcap.c
+++ b/drivers/usb/host/xhci-dbgcap.c
@@ -173,7 +173,17 @@ 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)
+/**
+ * xhci_dbc_flush_single_request - flushes single request
+ * @req: request to flush
+ * @status: 0 or -ERESTART - after the request is flushed it will be queued
+ *          back to the endpoint
+ *
+ *          -ESHUTDOWN - after the request is flushed it won't be queued back
+ *          to the endpoint and if it was last endpoint's request the endpoint
+ *          will stay shut
+ */
+static void xhci_dbc_flush_single_request(struct dbc_request *req, int status)
 {
 	union xhci_trb	*trb = req->trb;
 
@@ -183,21 +193,36 @@ 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)
+/**
+ * xhci_dbc_flush_endpoint_requests - flushes endpoint's requests
+ * @dep: endpoint to flush requests
+ * @restart: true - after being flushed, the requests will be queued back
+ *           to the endpoint and its operation will resume
+ *
+ *           false - after flushing last endpoint's request the endpoint will
+ *           stay shut
+ */
+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, &dep->list_pending, list_pending)
-		xhci_dbc_flush_single_request(req);
+	list_for_each_entry_safe(req, tmp, list, list_pending) {
+		if (restart && list_is_last(&req->list_pending, list))
+			status = -ERESTART;
+
+		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 +743,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 +851,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);
-- 
2.45.2.1089.g2a221341d9-goog





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

  Powered by Linux