xhci: store TD status in the td struct instead of passing it along
[ Upstream commit a6ccd1fd4bd4fca37eaa3d76bef940d6332919bc ] In cases where the TD can't be given back in current handler we want to be able to store it until its time to return the TD. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://lore.kernel.org/r/20210129130044.206855-19-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Stable-dep-of: a1575120972e ("xhci: Prevent infinite loop in transaction errors recovery for streams") Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:

committed by
Greg Kroah-Hartman

parent
9b63a80c45
commit
cad965cedb
@@ -774,7 +774,7 @@ static void xhci_unmap_td_bounce_buffer(struct xhci_hcd *xhci,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
|
static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||||
struct xhci_ring *ep_ring, int *status)
|
struct xhci_ring *ep_ring, int status)
|
||||||
{
|
{
|
||||||
struct urb *urb = NULL;
|
struct urb *urb = NULL;
|
||||||
|
|
||||||
@@ -793,7 +793,7 @@ static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||||||
xhci_warn(xhci, "URB req %u and actual %u transfer length mismatch\n",
|
xhci_warn(xhci, "URB req %u and actual %u transfer length mismatch\n",
|
||||||
urb->transfer_buffer_length, urb->actual_length);
|
urb->transfer_buffer_length, urb->actual_length);
|
||||||
urb->actual_length = 0;
|
urb->actual_length = 0;
|
||||||
*status = 0;
|
status = 0;
|
||||||
}
|
}
|
||||||
list_del_init(&td->td_list);
|
list_del_init(&td->td_list);
|
||||||
/* Was this TD slated to be cancelled but completed anyway? */
|
/* Was this TD slated to be cancelled but completed anyway? */
|
||||||
@@ -805,15 +805,15 @@ static int xhci_td_cleanup(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||||||
if (last_td_in_urb(td)) {
|
if (last_td_in_urb(td)) {
|
||||||
if ((urb->actual_length != urb->transfer_buffer_length &&
|
if ((urb->actual_length != urb->transfer_buffer_length &&
|
||||||
(urb->transfer_flags & URB_SHORT_NOT_OK)) ||
|
(urb->transfer_flags & URB_SHORT_NOT_OK)) ||
|
||||||
(*status != 0 && !usb_endpoint_xfer_isoc(&urb->ep->desc)))
|
(status != 0 && !usb_endpoint_xfer_isoc(&urb->ep->desc)))
|
||||||
xhci_dbg(xhci, "Giveback URB %p, len = %d, expected = %d, status = %d\n",
|
xhci_dbg(xhci, "Giveback URB %p, len = %d, expected = %d, status = %d\n",
|
||||||
urb, urb->actual_length,
|
urb, urb->actual_length,
|
||||||
urb->transfer_buffer_length, *status);
|
urb->transfer_buffer_length, status);
|
||||||
|
|
||||||
/* set isoc urb status to 0 just as EHCI, UHCI, and OHCI */
|
/* set isoc urb status to 0 just as EHCI, UHCI, and OHCI */
|
||||||
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
|
if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS)
|
||||||
*status = 0;
|
status = 0;
|
||||||
xhci_giveback_urb_in_irq(xhci, td, *status);
|
xhci_giveback_urb_in_irq(xhci, td, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -2060,8 +2060,7 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||||
struct xhci_transfer_event *event,
|
struct xhci_transfer_event *event, struct xhci_virt_ep *ep)
|
||||||
struct xhci_virt_ep *ep, int *status)
|
|
||||||
{
|
{
|
||||||
struct xhci_ep_ctx *ep_ctx;
|
struct xhci_ep_ctx *ep_ctx;
|
||||||
struct xhci_ring *ep_ring;
|
struct xhci_ring *ep_ring;
|
||||||
@@ -2104,7 +2103,7 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||||||
inc_deq(xhci, ep_ring);
|
inc_deq(xhci, ep_ring);
|
||||||
}
|
}
|
||||||
|
|
||||||
return xhci_td_cleanup(xhci, td, ep_ring, status);
|
return xhci_td_cleanup(xhci, td, ep_ring, td->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* sum trb lengths from ring dequeue up to stop_trb, _excluding_ stop_trb */
|
/* sum trb lengths from ring dequeue up to stop_trb, _excluding_ stop_trb */
|
||||||
@@ -2127,7 +2126,7 @@ static int sum_trb_lengths(struct xhci_hcd *xhci, struct xhci_ring *ring,
|
|||||||
*/
|
*/
|
||||||
static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||||
union xhci_trb *ep_trb, struct xhci_transfer_event *event,
|
union xhci_trb *ep_trb, struct xhci_transfer_event *event,
|
||||||
struct xhci_virt_ep *ep, int *status)
|
struct xhci_virt_ep *ep)
|
||||||
{
|
{
|
||||||
struct xhci_ep_ctx *ep_ctx;
|
struct xhci_ep_ctx *ep_ctx;
|
||||||
u32 trb_comp_code;
|
u32 trb_comp_code;
|
||||||
@@ -2145,13 +2144,13 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||||||
if (trb_type != TRB_STATUS) {
|
if (trb_type != TRB_STATUS) {
|
||||||
xhci_warn(xhci, "WARN: Success on ctrl %s TRB without IOC set?\n",
|
xhci_warn(xhci, "WARN: Success on ctrl %s TRB without IOC set?\n",
|
||||||
(trb_type == TRB_DATA) ? "data" : "setup");
|
(trb_type == TRB_DATA) ? "data" : "setup");
|
||||||
*status = -ESHUTDOWN;
|
td->status = -ESHUTDOWN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
*status = 0;
|
td->status = 0;
|
||||||
break;
|
break;
|
||||||
case COMP_SHORT_PACKET:
|
case COMP_SHORT_PACKET:
|
||||||
*status = 0;
|
td->status = 0;
|
||||||
break;
|
break;
|
||||||
case COMP_STOPPED_SHORT_PACKET:
|
case COMP_STOPPED_SHORT_PACKET:
|
||||||
if (trb_type == TRB_DATA || trb_type == TRB_NORMAL)
|
if (trb_type == TRB_DATA || trb_type == TRB_NORMAL)
|
||||||
@@ -2215,7 +2214,7 @@ static int process_ctrl_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||||||
td->urb->actual_length = requested;
|
td->urb->actual_length = requested;
|
||||||
|
|
||||||
finish_td:
|
finish_td:
|
||||||
return finish_td(xhci, td, event, ep, status);
|
return finish_td(xhci, td, event, ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2223,7 +2222,7 @@ finish_td:
|
|||||||
*/
|
*/
|
||||||
static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||||
union xhci_trb *ep_trb, struct xhci_transfer_event *event,
|
union xhci_trb *ep_trb, struct xhci_transfer_event *event,
|
||||||
struct xhci_virt_ep *ep, int *status)
|
struct xhci_virt_ep *ep)
|
||||||
{
|
{
|
||||||
struct urb_priv *urb_priv;
|
struct urb_priv *urb_priv;
|
||||||
int idx;
|
int idx;
|
||||||
@@ -2300,11 +2299,11 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||||||
|
|
||||||
td->urb->actual_length += frame->actual_length;
|
td->urb->actual_length += frame->actual_length;
|
||||||
|
|
||||||
return finish_td(xhci, td, event, ep, status);
|
return finish_td(xhci, td, event, ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||||
struct xhci_virt_ep *ep, int *status)
|
struct xhci_virt_ep *ep, int status)
|
||||||
{
|
{
|
||||||
struct urb_priv *urb_priv;
|
struct urb_priv *urb_priv;
|
||||||
struct usb_iso_packet_descriptor *frame;
|
struct usb_iso_packet_descriptor *frame;
|
||||||
@@ -2333,7 +2332,7 @@ static int skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||||||
*/
|
*/
|
||||||
static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||||
union xhci_trb *ep_trb, struct xhci_transfer_event *event,
|
union xhci_trb *ep_trb, struct xhci_transfer_event *event,
|
||||||
struct xhci_virt_ep *ep, int *status)
|
struct xhci_virt_ep *ep)
|
||||||
{
|
{
|
||||||
struct xhci_slot_ctx *slot_ctx;
|
struct xhci_slot_ctx *slot_ctx;
|
||||||
struct xhci_ring *ep_ring;
|
struct xhci_ring *ep_ring;
|
||||||
@@ -2357,13 +2356,13 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||||||
td->urb->ep->desc.bEndpointAddress,
|
td->urb->ep->desc.bEndpointAddress,
|
||||||
requested, remaining);
|
requested, remaining);
|
||||||
}
|
}
|
||||||
*status = 0;
|
td->status = 0;
|
||||||
break;
|
break;
|
||||||
case COMP_SHORT_PACKET:
|
case COMP_SHORT_PACKET:
|
||||||
xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n",
|
xhci_dbg(xhci, "ep %#x - asked for %d bytes, %d bytes untransferred\n",
|
||||||
td->urb->ep->desc.bEndpointAddress,
|
td->urb->ep->desc.bEndpointAddress,
|
||||||
requested, remaining);
|
requested, remaining);
|
||||||
*status = 0;
|
td->status = 0;
|
||||||
break;
|
break;
|
||||||
case COMP_STOPPED_SHORT_PACKET:
|
case COMP_STOPPED_SHORT_PACKET:
|
||||||
td->urb->actual_length = remaining;
|
td->urb->actual_length = remaining;
|
||||||
@@ -2378,7 +2377,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
|||||||
(ep_ring->err_count++ > MAX_SOFT_RETRY) ||
|
(ep_ring->err_count++ > MAX_SOFT_RETRY) ||
|
||||||
le32_to_cpu(slot_ctx->tt_info) & TT_SLOT)
|
le32_to_cpu(slot_ctx->tt_info) & TT_SLOT)
|
||||||
break;
|
break;
|
||||||
*status = 0;
|
|
||||||
|
td->status = 0;
|
||||||
xhci_cleanup_halted_endpoint(xhci, ep, ep_ring->stream_id, td,
|
xhci_cleanup_halted_endpoint(xhci, ep, ep_ring->stream_id, td,
|
||||||
EP_SOFT_RESET);
|
EP_SOFT_RESET);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -2399,7 +2399,7 @@ finish_td:
|
|||||||
remaining);
|
remaining);
|
||||||
td->urb->actual_length = 0;
|
td->urb->actual_length = 0;
|
||||||
}
|
}
|
||||||
return finish_td(xhci, td, event, ep, status);
|
return finish_td(xhci, td, event, ep);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2701,7 +2701,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||||||
return -ESHUTDOWN;
|
return -ESHUTDOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
skip_isoc_td(xhci, td, ep, &status);
|
skip_isoc_td(xhci, td, ep, status);
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
if (trb_comp_code == COMP_SHORT_PACKET)
|
if (trb_comp_code == COMP_SHORT_PACKET)
|
||||||
@@ -2729,6 +2729,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||||||
* endpoint. Otherwise, the endpoint remains stalled
|
* endpoint. Otherwise, the endpoint remains stalled
|
||||||
* indefinitely.
|
* indefinitely.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (trb_is_noop(ep_trb)) {
|
if (trb_is_noop(ep_trb)) {
|
||||||
if (trb_comp_code == COMP_STALL_ERROR ||
|
if (trb_comp_code == COMP_STALL_ERROR ||
|
||||||
xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
|
xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
|
||||||
@@ -2739,14 +2740,15 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
|||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td->status = status;
|
||||||
|
|
||||||
/* update the urb's actual_length and give back to the core */
|
/* update the urb's actual_length and give back to the core */
|
||||||
if (usb_endpoint_xfer_control(&td->urb->ep->desc))
|
if (usb_endpoint_xfer_control(&td->urb->ep->desc))
|
||||||
process_ctrl_td(xhci, td, ep_trb, event, ep, &status);
|
process_ctrl_td(xhci, td, ep_trb, event, ep);
|
||||||
else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
|
else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc))
|
||||||
process_isoc_td(xhci, td, ep_trb, event, ep, &status);
|
process_isoc_td(xhci, td, ep_trb, event, ep);
|
||||||
else
|
else
|
||||||
process_bulk_intr_td(xhci, td, ep_trb, event, ep,
|
process_bulk_intr_td(xhci, td, ep_trb, event, ep);
|
||||||
&status);
|
|
||||||
cleanup:
|
cleanup:
|
||||||
handling_skipped_tds = ep->skip &&
|
handling_skipped_tds = ep->skip &&
|
||||||
trb_comp_code != COMP_MISSED_SERVICE_ERROR &&
|
trb_comp_code != COMP_MISSED_SERVICE_ERROR &&
|
||||||
|
@@ -1544,6 +1544,7 @@ struct xhci_segment {
|
|||||||
struct xhci_td {
|
struct xhci_td {
|
||||||
struct list_head td_list;
|
struct list_head td_list;
|
||||||
struct list_head cancelled_td_list;
|
struct list_head cancelled_td_list;
|
||||||
|
int status;
|
||||||
struct urb *urb;
|
struct urb *urb;
|
||||||
struct xhci_segment *start_seg;
|
struct xhci_segment *start_seg;
|
||||||
union xhci_trb *first_trb;
|
union xhci_trb *first_trb;
|
||||||
|
Reference in New Issue
Block a user