commit 9044ad57b60b0556d42b6f8aa218a68865e810a4 upstream Backport targeted specifically for linux-6.6.y stable kernel. Resolve minor conflict due to 10-patch dbc cleanup series in 6.8
Don't flush all pending DbC data requests when an endpoint halts.
An endpoint may halt and xHC DbC triggers a STALL error event if there's an issue with a bulk data transfer. The transfer should restart once xHC DbC receives a ClearFeature(ENDPOINT_HALT) request from the host.
Once xHC DbC restarts it will start from the TRB pointed to by dequeue field in the endpoint context, which might be the same TRB we got the STALL event for. Turn the TRB to a no-op in this case to make sure xHC DbC doesn't reuse and tries to retransmit this same TRB after we already handled it, and gave its corresponding data request back.
Other STALL events might be completely bogus. Lukasz Bartosik discovered that xHC DbC might issue spurious STALL events if hosts sends a ClearFeature(ENDPOINT_HALT) request to non-halted endpoints even without any active bulk transfers.
Assume STALL event is spurious if it reports 0 bytes transferred, and the endpoint stopped on the STALLED TRB. Don't give back the data request corresponding to the TRB in this case.
The halted status is per endpoint. Track it with a per endpoint flag instead of the driver invented DbC wide DS_STALLED state. DbC remains in DbC-Configured state even if endpoints halt. There is no Stalled state in the DbC Port state Machine (xhci section 7.6.6)
Reported-by: Łukasz Bartosik ukaszb@chromium.org Closes: https://lore.kernel.org/linux-usb/20240725074857.623299-1-ukaszb@chromium.or... Tested-by: Łukasz Bartosik ukaszb@chromium.org Signed-off-by: Mathias Nyman mathias.nyman@linux.intel.com --- drivers/usb/host/xhci-dbgcap.c | 132 ++++++++++++++++++++------------- drivers/usb/host/xhci-dbgcap.h | 2 +- 2 files changed, 83 insertions(+), 51 deletions(-)
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c index b40d9238d447..69067015f0d5 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c @@ -158,16 +158,18 @@ 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 trb_to_noop(union xhci_trb *trb) { - union xhci_trb *trb = req->trb; - trb->generic.field[0] = 0; trb->generic.field[1] = 0; trb->generic.field[2] = 0; trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE); trb->generic.field[3] |= cpu_to_le32(TRB_TYPE(TRB_TR_NOOP)); +}
+static void xhci_dbc_flush_single_request(struct dbc_request *req) +{ + trb_to_noop(req->trb); xhci_dbc_giveback(req, -ESHUTDOWN); }
@@ -637,7 +639,6 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) case DS_DISABLED: return; case DS_CONFIGURED: - case DS_STALLED: if (dbc->driver->disconnect) dbc->driver->disconnect(dbc); break; @@ -657,6 +658,23 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) } }
+static void +handle_ep_halt_changes(struct xhci_dbc *dbc, struct dbc_ep *dep, bool halted) +{ + if (halted) { + dev_info(dbc->dev, "DbC Endpoint halted\n"); + dep->halted = 1; + + } else if (dep->halted) { + dev_info(dbc->dev, "DbC Endpoint halt cleared\n"); + dep->halted = 0; + + if (!list_empty(&dep->list_pending)) + writel(DBC_DOOR_BELL_TARGET(dep->direction), + &dbc->regs->doorbell); + } +} + static void dbc_handle_port_status(struct xhci_dbc *dbc, union xhci_trb *event) { @@ -685,6 +703,7 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) struct xhci_ring *ring; int ep_id; int status; + struct xhci_ep_ctx *ep_ctx; u32 comp_code; size_t remain_length; struct dbc_request *req = NULL, *r; @@ -694,8 +713,30 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) ep_id = TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3])); dep = (ep_id == EPID_OUT) ? get_out_ep(dbc) : get_in_ep(dbc); + ep_ctx = (ep_id == EPID_OUT) ? + dbc_bulkout_ctx(dbc) : dbc_bulkin_ctx(dbc); ring = dep->ring;
+ /* Match the pending request: */ + list_for_each_entry(r, &dep->list_pending, list_pending) { + if (r->trb_dma == event->trans_event.buffer) { + req = r; + break; + } + if (r->status == -COMP_STALL_ERROR) { + dev_warn(dbc->dev, "Give back stale stalled req\n"); + ring->num_trbs_free++; + xhci_dbc_giveback(r, 0); + } + } + + if (!req) { + dev_warn(dbc->dev, "no matched request\n"); + return; + } + + trace_xhci_dbc_handle_transfer(ring, &req->trb->generic); + switch (comp_code) { case COMP_SUCCESS: remain_length = 0; @@ -706,31 +747,49 @@ 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: + dev_warn(dbc->dev, "Stall error at bulk TRB %llx, remaining %zu, ep deq %llx\n", + event->trans_event.buffer, remain_length, ep_ctx->deq); + status = 0; + dep->halted = 1; + + /* + * xHC DbC may trigger a STALL bulk xfer event when host sends a + * ClearFeature(ENDPOINT_HALT) request even if there wasn't an + * active bulk transfer. + * + * Don't give back this transfer request as hardware will later + * start processing TRBs starting from this 'STALLED' TRB, + * causing TRBs and requests to be out of sync. + * + * If STALL event shows some bytes were transferred then assume + * it's an actual transfer issue and give back the request. + * In this case mark the TRB as No-Op to avoid hw from using the + * TRB again. + */ + + if ((ep_ctx->deq & ~TRB_CYCLE) == event->trans_event.buffer) { + dev_dbg(dbc->dev, "Ep stopped on Stalled TRB\n"); + if (remain_length == req->length) { + dev_dbg(dbc->dev, "Spurious stall event, keep req\n"); + req->status = -COMP_STALL_ERROR; + req->actual = 0; + return; + } + dev_dbg(dbc->dev, "Give back stalled req, but turn TRB to No-op\n"); + trb_to_noop(req->trb); + } + break; + default: dev_err(dbc->dev, "unknown tx error %d\n", comp_code); status = -comp_code; break; }
- /* Match the pending request: */ - list_for_each_entry(r, &dep->list_pending, list_pending) { - if (r->trb_dma == event->trans_event.buffer) { - req = r; - break; - } - } - - if (!req) { - dev_warn(dbc->dev, "no matched request\n"); - return; - } - - trace_xhci_dbc_handle_transfer(ring, &req->trb->generic); - ring->num_trbs_free++; req->actual = req->length - remain_length; xhci_dbc_giveback(req, status); @@ -750,7 +809,6 @@ static void inc_evt_deq(struct xhci_ring *ring) static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) { dma_addr_t deq; - struct dbc_ep *dep; union xhci_trb *evt; u32 ctrl, portsc; bool update_erdp = false; @@ -802,43 +860,17 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) return EVT_DISC; }
- /* Handle endpoint stall event: */ + /* Check and handle changes in endpoint halt status */ ctrl = readl(&dbc->regs->control); - if ((ctrl & DBC_CTRL_HALT_IN_TR) || - (ctrl & DBC_CTRL_HALT_OUT_TR)) { - dev_info(dbc->dev, "DbC Endpoint stall\n"); - dbc->state = DS_STALLED; - - if (ctrl & DBC_CTRL_HALT_IN_TR) { - dep = get_in_ep(dbc); - xhci_dbc_flush_endpoint_requests(dep); - } - - if (ctrl & DBC_CTRL_HALT_OUT_TR) { - dep = get_out_ep(dbc); - xhci_dbc_flush_endpoint_requests(dep); - } - - return EVT_DONE; - } + handle_ep_halt_changes(dbc, get_in_ep(dbc), ctrl & DBC_CTRL_HALT_IN_TR); + handle_ep_halt_changes(dbc, get_out_ep(dbc), ctrl & DBC_CTRL_HALT_OUT_TR);
/* Clear DbC run change bit: */ if (ctrl & DBC_CTRL_DBC_RUN_CHANGE) { writel(ctrl, &dbc->regs->control); ctrl = readl(&dbc->regs->control); } - break; - case DS_STALLED: - ctrl = readl(&dbc->regs->control); - if (!(ctrl & DBC_CTRL_HALT_IN_TR) && - !(ctrl & DBC_CTRL_HALT_OUT_TR) && - (ctrl & DBC_CTRL_DBC_RUN)) { - dbc->state = DS_CONFIGURED; - break; - } - - return EVT_DONE; default: dev_err(dbc->dev, "Unknown DbC state %d\n", dbc->state); break; diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h index 76170d7a7e7c..2de0dc49a3e9 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -81,7 +81,6 @@ enum dbc_state { DS_ENABLED, DS_CONNECTED, DS_CONFIGURED, - DS_STALLED, };
struct dbc_ep { @@ -89,6 +88,7 @@ struct dbc_ep { struct list_head list_pending; struct xhci_ring *ring; unsigned int direction:1; + unsigned int halted:1; };
#define DBC_QUEUE_SIZE 16
[ Sasha's backport helper bot ]
Hi,
The upstream commit SHA1 provided is correct: 9044ad57b60b0556d42b6f8aa218a68865e810a4
Status in newer kernel trees: 6.12.y | Present (exact SHA1)
Note: The patch differs from the upstream commit: --- --- - 2024-11-22 11:40:04.900767226 -0500 +++ /tmp/tmp.eypkTL84u1 2024-11-22 11:40:04.892785429 -0500 @@ -28,18 +28,16 @@ Closes: https://lore.kernel.org/linux-usb/20240725074857.623299-1-ukaszb@chromium.or... Tested-by: Łukasz Bartosik ukaszb@chromium.org Signed-off-by: Mathias Nyman mathias.nyman@linux.intel.com -Link: https://lore.kernel.org/r/20240905143300.1959279-2-mathias.nyman@linux.intel... -Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- - drivers/usb/host/xhci-dbgcap.c | 133 ++++++++++++++++++++------------- + drivers/usb/host/xhci-dbgcap.c | 132 ++++++++++++++++++++------------- drivers/usb/host/xhci-dbgcap.h | 2 +- - 2 files changed, 83 insertions(+), 52 deletions(-) + 2 files changed, 83 insertions(+), 51 deletions(-)
diff --git a/drivers/usb/host/xhci-dbgcap.c b/drivers/usb/host/xhci-dbgcap.c -index 161c09953c4e0..241d7aa1fbc20 100644 +index b40d9238d447..69067015f0d5 100644 --- a/drivers/usb/host/xhci-dbgcap.c +++ b/drivers/usb/host/xhci-dbgcap.c -@@ -173,16 +173,18 @@ static void xhci_dbc_giveback(struct dbc_request *req, int status) +@@ -158,16 +158,18 @@ static void xhci_dbc_giveback(struct dbc_request *req, int status) spin_lock(&dbc->lock); }
@@ -61,7 +59,7 @@ xhci_dbc_giveback(req, -ESHUTDOWN); }
-@@ -649,7 +651,6 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) +@@ -637,7 +639,6 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) case DS_DISABLED: return; case DS_CONFIGURED: @@ -69,8 +67,8 @@ if (dbc->driver->disconnect) dbc->driver->disconnect(dbc); break; -@@ -669,6 +670,23 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) - pm_runtime_put_sync(dbc->dev); /* note, was self.controller */ +@@ -657,6 +658,23 @@ static void xhci_dbc_stop(struct xhci_dbc *dbc) + } }
+static void @@ -93,7 +91,7 @@ static void dbc_handle_port_status(struct xhci_dbc *dbc, union xhci_trb *event) { -@@ -697,6 +715,7 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) +@@ -685,6 +703,7 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) struct xhci_ring *ring; int ep_id; int status; @@ -101,7 +99,7 @@ u32 comp_code; size_t remain_length; struct dbc_request *req = NULL, *r; -@@ -706,8 +725,30 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) +@@ -694,8 +713,30 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) ep_id = TRB_TO_EP_ID(le32_to_cpu(event->generic.field[3])); dep = (ep_id == EPID_OUT) ? get_out_ep(dbc) : get_in_ep(dbc); @@ -132,7 +130,7 @@ switch (comp_code) { case COMP_SUCCESS: remain_length = 0; -@@ -718,31 +759,49 @@ static void dbc_handle_xfer_event(struct xhci_dbc *dbc, union xhci_trb *event) +@@ -706,31 +747,49 @@ 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: @@ -198,7 +196,7 @@ ring->num_trbs_free++; req->actual = req->length - remain_length; xhci_dbc_giveback(req, status); -@@ -762,7 +821,6 @@ static void inc_evt_deq(struct xhci_ring *ring) +@@ -750,7 +809,6 @@ static void inc_evt_deq(struct xhci_ring *ring) static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) { dma_addr_t deq; @@ -206,7 +204,7 @@ union xhci_trb *evt; u32 ctrl, portsc; bool update_erdp = false; -@@ -814,43 +872,17 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) +@@ -802,43 +860,17 @@ static enum evtreturn xhci_dbc_do_handle_events(struct xhci_dbc *dbc) return EVT_DISC; }
@@ -253,16 +251,8 @@ default: dev_err(dbc->dev, "Unknown DbC state %d\n", dbc->state); break; -@@ -939,7 +971,6 @@ static const char * const dbc_state_strings[DS_MAX] = { - [DS_ENABLED] = "enabled", - [DS_CONNECTED] = "connected", - [DS_CONFIGURED] = "configured", -- [DS_STALLED] = "stalled", - }; - - static ssize_t dbc_show(struct device *dev, diff --git a/drivers/usb/host/xhci-dbgcap.h b/drivers/usb/host/xhci-dbgcap.h -index 0118c6288a3cc..97c5dc290138b 100644 +index 76170d7a7e7c..2de0dc49a3e9 100644 --- a/drivers/usb/host/xhci-dbgcap.h +++ b/drivers/usb/host/xhci-dbgcap.h @@ -81,7 +81,6 @@ enum dbc_state { @@ -270,10 +260,10 @@ DS_CONNECTED, DS_CONFIGURED, - DS_STALLED, - DS_MAX };
-@@ -90,6 +89,7 @@ struct dbc_ep { + struct dbc_ep { +@@ -89,6 +88,7 @@ struct dbc_ep { struct list_head list_pending; struct xhci_ring *ring; unsigned int direction:1; @@ -281,3 +271,6 @@ };
#define DBC_QUEUE_SIZE 16 +-- +2.25.1 + ---
Results of testing on various branches:
| Branch | Patch Apply | Build Test | |---------------------------|-------------|------------| | stable/linux-6.12.y | Failed | N/A | | stable/linux-6.11.y | Failed | N/A | | stable/linux-6.6.y | Success | Failed | | stable/linux-6.1.y | Success | Failed | | stable/linux-5.15.y | Success | Failed | | stable/linux-5.10.y | Success | Failed | | stable/linux-5.4.y | Failed | N/A | | stable/linux-4.19.y | Failed | N/A |
Build Errors: Build error for stable/linux-6.6.y: lib/test_dhry.o: warning: objtool: dhry() falls through to next function dhry_run_set.cold() drivers/usb/host/xhci-dbgcap.c: In function 'dbc_show': drivers/usb/host/xhci-dbgcap.c:976:14: error: 'DS_STALLED' undeclared (first use in this function); did you mean 'DS_ENABLED'? 976 | case DS_STALLED: | ^~~~~~~~~~ | DS_ENABLED drivers/usb/host/xhci-dbgcap.c:976:14: note: each undeclared identifier is reported only once for each function it appears in make[5]: *** [scripts/Makefile.build:243: drivers/usb/host/xhci-dbgcap.o] Error 1 make[5]: Target 'drivers/usb/host/' not remade because of errors. make[4]: *** [scripts/Makefile.build:480: drivers/usb/host] Error 2 make[4]: Target 'drivers/usb/' not remade because of errors. make[3]: *** [scripts/Makefile.build:480: drivers/usb] Error 2 make[3]: Target 'drivers/' not remade because of errors. make[2]: *** [scripts/Makefile.build:480: drivers] Error 2 make[2]: Target './' not remade because of errors. make[1]: *** [/home/sasha/build/linus-next/Makefile:1921: .] Error 2 make[1]: Target '__all' not remade because of errors. make: *** [Makefile:234: __sub-make] Error 2 make: Target '__all' not remade because of errors.
Build error for stable/linux-6.1.y: drivers/usb/host/xhci-dbgcap.c: In function 'dbc_show': drivers/usb/host/xhci-dbgcap.c:976:14: error: 'DS_STALLED' undeclared (first use in this function); did you mean 'DS_ENABLED'? 976 | case DS_STALLED: | ^~~~~~~~~~ | DS_ENABLED drivers/usb/host/xhci-dbgcap.c:976:14: note: each undeclared identifier is reported only once for each function it appears in make[4]: *** [scripts/Makefile.build:250: drivers/usb/host/xhci-dbgcap.o] Error 1 make[4]: Target 'drivers/usb/host/' not remade because of errors. make[3]: *** [scripts/Makefile.build:503: drivers/usb/host] Error 2 make[3]: Target 'drivers/usb/' not remade because of errors. make[2]: *** [scripts/Makefile.build:503: drivers/usb] Error 2 make[2]: Target 'drivers/' not remade because of errors. make[1]: *** [scripts/Makefile.build:503: drivers] Error 2 make[1]: Target './' not remade because of errors. make: *** [Makefile:2009: .] Error 2 make: Target '__all' not remade because of errors.
Build error for stable/linux-5.15.y: drivers/usb/host/xhci-dbgcap.c: In function 'dbc_show': drivers/usb/host/xhci-dbgcap.c:976:14: error: 'DS_STALLED' undeclared (first use in this function); did you mean 'DS_ENABLED'? 976 | case DS_STALLED: | ^~~~~~~~~~ | DS_ENABLED drivers/usb/host/xhci-dbgcap.c:976:14: note: each undeclared identifier is reported only once for each function it appears in make[3]: *** [scripts/Makefile.build:289: drivers/usb/host/xhci-dbgcap.o] Error 1 make[3]: Target '__build' not remade because of errors. make[2]: *** [scripts/Makefile.build:552: drivers/usb/host] Error 2 make[2]: Target '__build' not remade because of errors. make[1]: *** [scripts/Makefile.build:552: drivers/usb] Error 2 make[1]: Target '__build' not remade because of errors. make: *** [Makefile:1906: drivers] Error 2 make: Target '__all' not remade because of errors.
Build error for stable/linux-5.10.y: In file included from ./include/linux/kernel.h:15, from ./include/linux/list.h:9, from ./include/linux/kobject.h:19, from ./include/linux/of.h:17, from ./include/linux/clk-provider.h:9, from drivers/clk/qcom/clk-rpmh.c:6: drivers/clk/qcom/clk-rpmh.c: In function 'clk_rpmh_bcm_send_cmd': ./include/linux/minmax.h:20:35: warning: comparison of distinct pointer types lacks a cast [-Wcompare-distinct-pointer-types] 20 | (!!(sizeof((typeof(x) *)1 == (typeof(y) *)1))) | ^~ ./include/linux/minmax.h:26:18: note: in expansion of macro '__typecheck' 26 | (__typecheck(x, y) && __no_side_effects(x, y)) | ^~~~~~~~~~~ ./include/linux/minmax.h:36:31: note: in expansion of macro '__safe_cmp' 36 | __builtin_choose_expr(__safe_cmp(x, y), \ | ^~~~~~~~~~ ./include/linux/minmax.h:45:25: note: in expansion of macro '__careful_cmp' 45 | #define min(x, y) __careful_cmp(x, y, <) | ^~~~~~~~~~~~~ drivers/clk/qcom/clk-rpmh.c:273:21: note: in expansion of macro 'min' 273 | cmd_state = min(cmd_state, BCM_TCS_CMD_VOTE_MASK); | ^~~ In file included from ./include/linux/mm.h:30, from ./include/linux/pagemap.h:8, from ./include/linux/buffer_head.h:14, from fs/udf/udfdecl.h:12, from fs/udf/super.c:41: fs/udf/super.c: In function 'udf_fill_partdesc_info': ./include/linux/overflow.h:70:22: warning: comparison of distinct pointer types lacks a cast [-Wcompare-distinct-pointer-types] 70 | (void) (&__a == &__b); \ | ^~ fs/udf/super.c:1155:21: note: in expansion of macro 'check_add_overflow' 1155 | if (check_add_overflow(map->s_partition_len, | ^~~~~~~~~~~~~~~~~~ drivers/usb/host/xhci-dbgcap.c: In function 'dbc_show': drivers/usb/host/xhci-dbgcap.c:1029:14: error: 'DS_STALLED' undeclared (first use in this function); did you mean 'DS_ENABLED'? 1029 | case DS_STALLED: | ^~~~~~~~~~ | DS_ENABLED drivers/usb/host/xhci-dbgcap.c:1029:14: note: each undeclared identifier is reported only once for each function it appears in make[3]: *** [scripts/Makefile.build:286: drivers/usb/host/xhci-dbgcap.o] Error 1 make[3]: Target '__build' not remade because of errors. make[2]: *** [scripts/Makefile.build:503: drivers/usb/host] Error 2 make[2]: Target '__build' not remade because of errors. make[1]: *** [scripts/Makefile.build:503: drivers/usb] Error 2 make[1]: Target '__build' not remade because of errors. make: *** [Makefile:1832: drivers] Error 2 make: Target '__all' not remade because of errors.
On 2.12.2024 11.13, Greg KH wrote:
On Fri, Nov 22, 2024 at 05:41:46PM +0200, Mathias Nyman wrote:
commit 9044ad57b60b0556d42b6f8aa218a68865e810a4 upstream Backport targeted specifically for linux-6.6.y stable kernel. Resolve minor conflict due to 10-patch dbc cleanup series in 6.8
Breaks the build :(
Sorry about that, turns out I had removed DbC from kernel config in my stable repo.
I'll send an updated version
Thanks Mathias
linux-stable-mirror@lists.linaro.org