Linux USB
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Mathias Nyman" <mathias.nyman@linux.intel.com>
Cc: linux-usb@vger.kernel.org
Subject: Re: [PATCH v2] xhci: give back TDs and clean up ep0 after disconnect
Date: Thu, 29 Oct 2026 22:11:41 +0000	[thread overview]
Message-ID: <sashiko-outbox-190053@kernel.org> (raw)
In-Reply-To: <20261029204324.658845-1-mathias.nyman@linux.intel.com>

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] xhci: stale rhub_port status across reset re-triggers EP_DROP_PENDING
--

commit 28b1e3f2ba757ac7bec832f907ed7d9693752c9e
Author: Yiyu Zhou <yiyu@yiyuzhou.io>

xhci: give back TDs and clean up ep0 after disconnect

This commit cancels and gives back TDs on halted endpoints behind
disconnected or link-inactive ports, hard-resets and cleans up ep0 so it
can survive brief disconnects during enumeration, and blocks new URB
submissions in xhci_urb_enqueue() while EP_DROP_PENDING is set.

Closes: https://lore.kernel.org/all/20261010071811.20483-1-yiyu@yiyuzhou.io/
Closes: https://lore.kernel.org/linux-usb/4MjB9K-2SoiA85WbI9iEig@dsine.de/

> diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
> index c23434001e9cb..a1fa188680f58 100644
> --- a/drivers/usb/host/xhci-ring.c
> +++ b/drivers/usb/host/xhci-ring.c

[ ... ]

> @@ -993,25 +1012,53 @@ static int xhci_handle_halted_endpoint(struct xhci_hcd *xhci,
>  {
>  	struct xhci_port *rhub_port = ep->vdev->rhub_port;
>  	unsigned int slot_id = ep->vdev->slot_id;
> +	bool disconnected = false;
> +	struct xhci_ring *ring;
>  	int err;
>  
> -	/*
> -	 * Avoid resetting endpoint if link is inactive or device disconnected.
> -	 * Can cause host hang.
> -	 * Device will be reset to recover an inactive link, so don't do anything
> -	 */
> +	/* link is inactive or device disconnected */
>  	if (rhub_port->link_inactive || !rhub_port->connected) {
>  		ep->ep_state |= EP_DROP_PENDING;
> -		return -ENODEV;
> +		disconnected = true;
> +		if (!td && ep->ep_index != 0) {
> +			xhci_kill_endpoint_urbs(xhci, slot_id, ep->ep_index);
> +			return -ENODEV;
> +		}
>  	}

[ ... ]

> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index 30ac1bc4559b7..b50a582be1fbf 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -1665,12 +1665,6 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag

[ ... ]

> @@ -1680,6 +1674,13 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
>  
>  	ep_state = &xhci->devs[slot_id]->eps[ep_index].ep_state;
>  
> +	if (xhci->devs[slot_id]->rhub_port->link_inactive ||
> +	    *ep_state & EP_DROP_PENDING) {
> +		xhci_dbg(xhci, "Can't queue urb, port disconnect or link inactive\n");
> +		ret = -ENODEV;
> +		goto free_priv;
> +	}

[Severity: High]
Can a stale rhub_port->connected or rhub_port->link_inactive state across a
port reset cause xhci_urb_enqueue() to permanently reject ep0 URBs with
-ENODEV after a brief disconnect?

In drivers/usb/host/xhci-ring.c, port->connected and port->link_inactive are
only updated in handle_port_status() when a Port Status Change Event TRB is
received:

	if (!(portsc & PORT_RESET)) {
		port->link_inactive = (pls == XDEV_INACTIVE);
		port->connected = !!(portsc & PORT_CONNECT);
	}

Per xHCI specification section 4.19.2, the xHC only generates a Port Status
Change Event TRB when the logical OR of all PORTSC status-change bits
(CSC | PEC | OCC | PRC | WRC | PLC | CEC) transitions from 0 to 1.

When a device briefly disconnects during enumeration in hub_port_init() (or
enters XDEV_INACTIVE), PORT_CSC (or PORT_PLC) transitions from 0 to 1 and
handle_port_status() sets rhub_port->connected = 0 (or
rhub_port->link_inactive = 1). Because hub_wq is busy in hub_port_init(),
port_event() does not run to clear PORT_CSC.

When the device reconnects and hub_port_reset() resets the port to recover
it, PORT_CSC (or PORT_PLC) is already 1 when reconnect and PORT_RC occur:

1. On USB 2.0 ports, the first reset attempt sets PORT_RC = 1 while PORT_CSC
   is still 1 (no 0-to-1 transition of the change-bit OR).
   hub_port_wait_reset() sees USB_PORT_STAT_C_CONNECTION, clears PORT_CSC
   while PORT_RC remains 1, and returns -EAGAIN before hub_port_reset()
   clears PORT_RC. The second reset attempt then completes while PORT_RC is
   already 1 and clears PORT_RC to 0, so no Port Status Change Event TRB is
   generated after the reconnect.

2. On USB 3.0 ports, hub_port_wait_reset() ignores
   USB_PORT_STAT_C_CONNECTION, so the reset completes while PORT_CSC or
   PORT_PLC is still 1 (no 0-to-1 transition) and hub_port_reset() then
   clears PORT_RC, PORT_WRC, PORT_PLC, and PORT_CSC.

At the end of the reset, xhci_discover_or_reset_device() clears
EP_DROP_PENDING without refreshing rhub_port->connected or
rhub_port->link_inactive:

command_cleanup:
	for (i = 0; i < EP_CTX_PER_DEV; i++)
		virt_dev->eps[i].ep_state &= ~EP_DROP_PENDING;

If rhub_port->link_inactive is still 1, xhci_urb_enqueue() will immediately
reject all URBs with -ENODEV.

If rhub_port->connected is still 0, then as soon as ep0 hits any normal
protocol stall (COMP_STALL_ERROR, such as fetching USB_DT_DEVICE_QUALIFIER,
USB_DT_BOS, or HID_REQ_SET_IDLE) or an URB unlink via
xhci_handle_cmd_stop_ep(), xhci_handle_halted_endpoint() sees
!rhub_port->connected, overwrites td->status from -EPIPE to -EPROTO, and
sets ep->ep_state |= EP_DROP_PENDING on ep0. Because EP_DROP_PENDING is
never cleared by xhci_handle_cmd_reset_ep() or xhci_handle_cmd_set_deq(),
wouldn't xhci_urb_enqueue() then reject all subsequent ep0 control transfers
with -ENODEV?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261029204324.658845-1-mathias.nyman@linux.intel.com?part=1

      parent reply	other threads:[~2026-10-29 22:11 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-29 20:43 [PATCH v2] xhci: give back TDs and clean up ep0 after disconnect Mathias Nyman
2026-10-29 20:43 ` Mathias Nyman
2026-10-29 22:08   ` sashiko-bot
2026-10-29 22:11 ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-190053@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mathias.nyman@linux.intel.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox