From nobody Tue Dec 2 02:20:30 2025 Received: from mail-ej1-f53.google.com (mail-ej1-f53.google.com [209.85.218.53]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 6BB633093B2 for ; Wed, 19 Nov 2025 11:02:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.53 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763550179; cv=none; b=eApskflv/yv/W2Cds75/dch+chavd8QCmsLNN5AVBZASoSu0crMJDOlNPmv7Cv8NRpzSQXrOcys0b7Cbfz0qEoKc6yY1mtzSiiyeLmoNXfWB1sqyCqN5MvwQNud76UXN/RWfUsB3/O4kMmOkHKOk7odKeNp5xzJVRBhqz93J/ek= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763550179; c=relaxed/simple; bh=V0DYnRKMnrb8+bWaJoEpsdLkMGo2LxBu9YTMLIkbsPk=; h=Date:From:To:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=P2yveGDE4Oa+giXJ2q7AvMav0XYVbX/i1QcbilJ2ZjeHOWa+HhRFYPRuXuHLUFZU2S1GQRHGNNl+2q/iYlpwJGf3iZRWlh6DK9PdA27gYiuYxe87Q8VNYfeD1wVI2jXY3NK8s3tvdfjcgliBvT+xKN2xtKg5WkTHuLWBFaySX6o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hzRWnHon; arc=none smtp.client-ip=209.85.218.53 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hzRWnHon" Received: by mail-ej1-f53.google.com with SMTP id a640c23a62f3a-b713c7096f9so856031866b.3 for ; Wed, 19 Nov 2025 03:02:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763550176; x=1764154976; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=zxMv2kZg4qEx22u+xZ5qZqSeqxeWZf4xoX9x5+oSd88=; b=hzRWnHonYHccly/JadVVHf6Hab3MhVxLKybONX/30nvywd8TpwEX6dbQ4+tfEa71bK njzAnKUGFXoQI94TKshZz4eX0QN8jZ+O6p+lnXh9z5me676JB6a3SFVP0F5GnGfhB0uz RsCao5uRFoARLl1LwJws/2tg44d+TxIrLLuXgGzrA52gYvwguodh97WzVOktydmbzQ5q bYkVE0PbDmOQ1RVr13PGhAlsThUOx01JQw4FkqesAvnmHlCWTZ0RkGizyMQZpoxzxuQi EzLctDbHXt4pxfY9o5zCmHbSJwu3OIayKEoDvsTV1mqNOS8VESHJta/+imCN4j47uJeq qsrg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763550176; x=1764154976; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=zxMv2kZg4qEx22u+xZ5qZqSeqxeWZf4xoX9x5+oSd88=; b=roCB0HVJCQ633zITAR7dzWyKUDZn34RiNd8bMrt/I+ULwtnPs9WPxCze6jp6iFdEuK XXRLu23Crr8TrXpLHdfxNiEfCQNN6If+jHDT8X6roAulcxeXS6DypeRVBagiuLoxqMXI lPv/hlPXxlS1ngloEOEZy7oGct+rXmNPzGaEmfj6r0P8+8qBwDhd/+RAYgtztesIZurX OlD8sdh/MYCFg2+B+yo7YBupm1Vlfj0YbZdXOn0ElrUixX04T3tMcRDtG8w8iC1ngiIu WPKDXQBrg5+rl1ITjTRUDZHgFivJaIszWHjZs4PnuCU6G5tPt4YPUaaZ8tx5H0e+O7ba zQtQ== X-Forwarded-Encrypted: i=1; AJvYcCUNVj4LNZuUdFY4ONLQ8Zsaln2SnWgptJiMKIBWHOubidYDFV+iKp9xrWBy1sqGUmgV6ppu0SUUW73RQXs=@vger.kernel.org X-Gm-Message-State: AOJu0YxvxDzq1oEcv8mGRlLk9UEaXgs/ACFPQOQaqRf5xf47XWmq6yfJ orCzYI/4v7IRVkkJrPDNsBV4l/suiVS9/0liIPCJGS9uXiO+8tqvozUX3Vmoyw== X-Gm-Gg: ASbGnctEvb19/GGbHVohjlk0T0Sneic/FBWweB8mw9wj8WL8LnbDlU8eIyHS6ipVw0W SYO8ydIHyDWOTOu86Ps3rCctAXfZ8ha5BmgEv/gN9H4/srcN7ZjnY7FZ5b4eLJmnBfYmP4BajGU EHdlG3Quz6nd1i3nJClU1xiFQ0mtKa2770r+YzKYfilglZA8jSq8xMoV5Qu9ijGHW5mjFjhS0Og IZzeNWc1rNs5pKaRQpQigTZJcGxHT7zhdZCEVJEw8gxzLJ19Gld0eMoqzojBeC/XvilXrRMlEQf oEY/gOuHdPTtjafK+lJhYxjMjbxbijFXA+3ClcPC0oFK+1P9eUbmZSj3943alfAPr8Myt2NcIBO x4196pmmmE5pc3pNouUcWmpMmR0DgkKbf9w3DPwCwYM1N3h/bJ+7+oW6sGwz2vk9m3oLOs4Pwqd GXOvz+Idd4ju83f1NCB/ZIDIKTaDpLYVYrdE0ilQleoIs+ X-Google-Smtp-Source: AGHT+IFc/TKsZa7xUbm2Wx9G077Nk+OerGKYYxWpHLbgIy+HjaQWP5BenLaKeSngCdU5CygLpBIVcg== X-Received: by 2002:a17:906:6a06:b0:b73:8639:cd93 with SMTP id a640c23a62f3a-b738639d0ecmr1556659266b.14.1763550175592; Wed, 19 Nov 2025 03:02:55 -0800 (PST) Received: from foxbook (bhd138.neoplus.adsl.tpnet.pl. [83.28.93.138]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b7359bfb238sm1517689666b.14.2025.11.19.03.02.54 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Wed, 19 Nov 2025 03:02:55 -0800 (PST) Date: Wed, 19 Nov 2025 12:02:52 +0100 From: Michal Pecio To: Mathias Nyman , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 1/5] usb: xhci: Track transfer ring dequeue progress properly Message-ID: <20251119120252.74379adb.michal.pecio@gmail.com> In-Reply-To: <20251119120208.6a025eb0.michal.pecio@gmail.com> References: <20251119120208.6a025eb0.michal.pecio@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" xHCI 4.9.2 has clear requirements regarding transfer ring management: Software uses the Dequeue Pointer to determine when a Transfer Ring is full. As it processes Transfer Events, it updates its copy of the Dequeue Pointer with the value of the Transfer Event TRB Pointer field. If advancing the Enqueue Pointer would make it equal to the Dequeue Pointer then the Transfer Ring is full and software shall wait for Transfer Events that will advance the Dequeue Pointer. This and the first two rows of Table 4-2 in 4.6.9 imply that the xHC is not required to atomically advance Dequeue to the next TRB after completing one. The TRB referenced by latest event is still owned by the xHC and not supposed to be reused for new transfers yet. The driver allows such reuse and then worse. When a TD completes with Short Packet, Dequeue is moved past remaining TRBs without waiting for their completion. This opens them for reuse if it happens across segment boundary and complicates handling events for those TRBs. This is due to sloppy historic assumptions that Dequeue points to the next TD to execute. Those assumptions stopped working when unlinking was implemented and have been fully cleaned up last year. Dequeue is now only used for free space tracking and in move_dequeue_past_td(). So let's fix this. When TD is given back, set Dequeue to the current TRB pointer rather than past the TD. Future patch will also update Dequeue when (and if) events are received for the remaining TRBs. Note: updating Dequeue before giveback would break sum_trb_lengths(). Skipping moves Dequeue past the TD because it is triggered by events outside the TD, so we know that the xHC has left it completely. That being said, replace inc_deq() with next_trb() to ensure that Dequeue doesn't get ahead of the xHC (and/or Enqueue) on Link TRBs. Signed-off-by: Michal Pecio --- drivers/usb/host/xhci-ring.c | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index bf077cd13ffa..3d5124912a09 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -927,7 +927,7 @@ static void xhci_dequeue_td(struct xhci_hcd *xhci, stru= ct xhci_td *td, struct xh { ring->dequeue =3D td->end_trb; ring->deq_seg =3D td->end_seg; - inc_deq(xhci, ring); + next_trb(&ring->deq_seg, &ring->dequeue); =20 xhci_td_cleanup(xhci, td, ring, status); } @@ -2229,8 +2229,8 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, u= nsigned int trb_comp_code) } =20 static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, - struct xhci_ring *ep_ring, struct xhci_td *td, - u32 trb_comp_code) + struct xhci_ring *ep_ring, struct xhci_td *td, u32 trb_comp_code, + struct xhci_segment *ep_seg, union xhci_trb *ep_trb) { struct xhci_ep_ctx *ep_ctx; =20 @@ -2267,7 +2267,11 @@ static void finish_td(struct xhci_hcd *xhci, struct = xhci_virt_ep *ep, return; /* xhci_handle_halted_endpoint marked td cancelled */ } =20 - xhci_dequeue_td(xhci, td, ep_ring, td->status); + /* update ring dequeue state */ + ep_ring->deq_seg =3D ep_seg; + ep_ring->dequeue =3D ep_trb; + + xhci_td_cleanup(xhci, td, ep_ring, td->status); } =20 /* sum trb lengths from the first trb up to stop_trb, _excluding_ stop_trb= */ @@ -2289,7 +2293,8 @@ static u32 sum_trb_lengths(struct xhci_td *td, union = xhci_trb *stop_trb) */ static void process_ctrl_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) + struct xhci_segment *ep_seg, union xhci_trb *ep_trb, + struct xhci_transfer_event *event) { struct xhci_ep_ctx *ep_ctx; u32 trb_comp_code; @@ -2373,7 +2378,7 @@ static void process_ctrl_td(struct xhci_hcd *xhci, st= ruct xhci_virt_ep *ep, td->urb->actual_length =3D requested; =20 finish_td: - finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb); } =20 /* @@ -2381,7 +2386,8 @@ static void process_ctrl_td(struct xhci_hcd *xhci, st= ruct xhci_virt_ep *ep, */ static void process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) + struct xhci_segment *ep_seg, union xhci_trb *ep_trb, + struct xhci_transfer_event *event) { struct urb_priv *urb_priv; int idx; @@ -2483,7 +2489,7 @@ static void process_isoc_td(struct xhci_hcd *xhci, st= ruct xhci_virt_ep *ep, td->urb_length_set =3D true; return; } - finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb); } =20 static void skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, @@ -2511,7 +2517,8 @@ static void skip_isoc_td(struct xhci_hcd *xhci, struc= t xhci_td *td, */ static void process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_e= p *ep, struct xhci_ring *ep_ring, struct xhci_td *td, - union xhci_trb *ep_trb, struct xhci_transfer_event *event) + struct xhci_segment *ep_seg, union xhci_trb *ep_trb, + struct xhci_transfer_event *event) { struct xhci_slot_ctx *slot_ctx; u32 trb_comp_code; @@ -2573,7 +2580,7 @@ static void process_bulk_intr_td(struct xhci_hcd *xhc= i, struct xhci_virt_ep *ep, td->urb->actual_length =3D 0; } =20 - finish_td(xhci, ep, ep_ring, td, trb_comp_code); + finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb); } =20 /* Transfer events which don't point to a transfer TRB, see xhci 4.17.4 */ @@ -2941,11 +2948,11 @@ static int handle_tx_event(struct xhci_hcd *xhci, =20 /* update the urb's actual_length and give back to the core */ if (usb_endpoint_xfer_control(&td->urb->ep->desc)) - process_ctrl_td(xhci, ep, ep_ring, td, ep_trb, event); + process_ctrl_td(xhci, ep, ep_ring, td, ep_seg, ep_trb, event); else if (usb_endpoint_xfer_isoc(&td->urb->ep->desc)) - process_isoc_td(xhci, ep, ep_ring, td, ep_trb, event); + process_isoc_td(xhci, ep, ep_ring, td, ep_seg, ep_trb, event); else - process_bulk_intr_td(xhci, ep, ep_ring, td, ep_trb, event); + process_bulk_intr_td(xhci, ep, ep_ring, td, ep_seg, ep_trb, event); return 0; =20 check_endpoint_halted: --=20 2.48.1 From nobody Tue Dec 2 02:20:30 2025 Received: from mail-ej1-f48.google.com (mail-ej1-f48.google.com [209.85.218.48]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 0139D34F476 for ; Wed, 19 Nov 2025 11:03:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.48 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763550237; cv=none; b=TyCZYclBAzUKlpFLPXo+A7hpefBvMrHTDvO4zpl8vXphXZNWLbrNhsN4b2gixHbgc/yI3kt+V6RbCLQKK9vhoFtqCmJa9yY0m9C0r3IlXgEw9I0JyfZxFQRIs26l8Yezv78Jz7X8tQ3TaOTfyzvCzE+F93P0byC0lhkBRyxvj7U= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763550237; c=relaxed/simple; bh=4mSerw4vYazyDfgyl8j1CLDy2pYsgGbR8tF9bgVF6JA=; h=Date:From:To:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=OflfQRkFPQp9o9CCQw0Pa0PYxQOUrJrjkTalko/VWs88zy7pKaoTpY0kxvYmHmfLVn+ZBEjUmiwKCnrzX+D86F0R3KaMBT0IflZxKQqHYW8eA4UPC+nCChkP87ZWfloUBsxCfxkgCAt2Fk2Wh0zJOUFQvcD1n5uT1VxXsL3I46k= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=Pz3s5KyD; arc=none smtp.client-ip=209.85.218.48 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Pz3s5KyD" Received: by mail-ej1-f48.google.com with SMTP id a640c23a62f3a-b728a43e410so1079840066b.1 for ; Wed, 19 Nov 2025 03:03:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763550230; x=1764155030; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=EdPiRm4LzogiWqW/lnu08ToTWmpvxJj4XqcowJynZMA=; b=Pz3s5KyDCiqAc6CmRV1sN6+hWcfnmA6cIwXzPUD+BKO83rwiZs1t/92Q1i1q3XBI8K ZvGT6kRHEW8p4Zmzg9IXIPczj51PMZqiRa5ums3FzJSmQe+SOHMvdZxMoyNoOrP7njFX V4PTlPiQqEpU5S26P0izNsPEhLanqfmuCvu0cn0zxHQIJYX8O+lygKZZeO3jUhwLIlHS ddhz8GXprusbXnLVUn8sNTI/E8qAPt/ZYxBwAXkuyRwQ0YYRBdibTOts6Mzf8xUYEwoA vn7RC7536ClWCBUCG6kqbBOkHRcfBLsCQP6JNB5ileUk6XyYL1zIN4spx1O1C7B9L5e0 v63g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763550230; x=1764155030; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=EdPiRm4LzogiWqW/lnu08ToTWmpvxJj4XqcowJynZMA=; b=Jv/z0mjiJjmkz0vcD7+iWSr9mMvff1PnjkNS1e87Hmdm/mru/vITBDKGpfNVEOJd6Y QI+quo1H6UVOeW++5LLYY0JyOtLxhqx9j09cNNHxA5VqGr+6r6Ic/Yyf3ixvr8uoeSUs woqSIbyxUyev2OPim4Vvg0gbkWcrnl1DplGqA4lXM7Sui1bn5kVjELPbn9uo3RdoQT/Q gd1g9zLJBUGKYNMIbcSHG1MN4Jlxx63ZR76qOi2E5HN/SAhxLJm0UZXSyXJOzrvLf0tk wmooPZ8JCa4V8z1408JMeNphIooyJRY4OutI03XgIJp1LAzgtaI3Jb7qYkd2Q3uogjjZ ew9Q== X-Forwarded-Encrypted: i=1; AJvYcCVIfnjZekHd/oYqDG7NhPgEgdbeiKQduDJOpo4OhcLaQqjNV7/QuTtn1Z61xWAJdSxsLckCp7C+xyQsPuc=@vger.kernel.org X-Gm-Message-State: AOJu0Yx4eweSFh3c5la21XQFMPUCmvrQTe0n1aF+vfmVB092oeUHB5BS 2ldeSLVHDkQa8rsROzhOswTInUJVqaDpMHjPVGhlcC/MiN8ife3BumS9nqASYg== X-Gm-Gg: ASbGnctjHfXjeuhadOgJGXgDd/nvkSgfXTOMtF1Pbvfl4rLjf82TrokYDwQguHXrHu5 JfJFpk4aTc+V1ZypiKS1ZLWCgOy7CjaJHrzWQkHzzXkpjA0G/SghmjxqkcCG9axmaV6VTJx/Ed4 61BWlpApC3YRJkihZgwr4oDaBAMUHRGu/kBHFosRHfbxY0uYVtNYMBObGzpc++8l4wgZTj3bkIj HOML/Ygtax9i17GArcbF6Buin+oWjYfwmc/GFn43fRItiLdjKmjcKytHu+Qt0RDCa+Y0xy1AYGF pq9WGu5Cd7sKPopGY/Tc/Iu6wqQms5fNoyzybMpYNhV4FMlLD91ubq9L9jouO38FzqEY6KGwvFy OMUJAwjhHxHfHfgBS67coQlhlqhqyFs1kUnNelKUrrwM90+KVqGYJ1877m1dx3tKdkW4ruIlJEQ /g99K8n2ex73E/oy55NcYQ+S4= X-Google-Smtp-Source: AGHT+IFgyVTFepwmJ2y3kGahtWZuiVh3MxIYfoCeuZo+rnD6QUmtcBuWMQiB2ayc/7YdDXfEDMrrbg== X-Received: by 2002:a17:906:b205:b0:b73:7a44:b4d5 with SMTP id a640c23a62f3a-b737a44b692mr1432516766b.41.1763550230496; Wed, 19 Nov 2025 03:03:50 -0800 (PST) Received: from foxbook (bhd138.neoplus.adsl.tpnet.pl. [83.28.93.138]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b734fa81296sm1617324866b.6.2025.11.19.03.03.49 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Wed, 19 Nov 2025 03:03:50 -0800 (PST) Date: Wed, 19 Nov 2025 12:03:47 +0100 From: Michal Pecio To: Mathias Nyman , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/5] usb: xhci: Find transfer TRB early in handle_tx_event() Message-ID: <20251119120347.70a02fde.michal.pecio@gmail.com> In-Reply-To: <20251119120208.6a025eb0.michal.pecio@gmail.com> References: <20251119120208.6a025eb0.michal.pecio@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" As soon as we find the transfer ring to which an event belongs, we can proceed to locate the exact TRB referenced by the event. This enables better event handling and diagnostics, even if no TD matches the event. Also set 'ep_seg' and remove its secondary use as a temporary boolean. Bail out if event TRB pointer is not NULL and not a transfer TRB on the endpoint's ring. This indicates that either the HC executes TRBs from a wrong ring (bad Set TR Dequeue command, Link TRB damaged or ignored by the HC) or its internal state is corrupted and the event is bogus. No such event is going to match any TD on td_list and trying to handle it would generally do nothing. On an isochronous endpoint we might skip all pending TDs and create more chaos. Just log this error and get out. Suggested-by: Mathias Nyman Signed-off-by: Michal Pecio --- drivers/usb/host/xhci-ring.c | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 3d5124912a09..531e2f207b17 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -82,6 +82,27 @@ dma_addr_t xhci_trb_virt_to_dma(struct xhci_segment *seg, return seg->dma + (segment_offset * sizeof(*trb)); } =20 +/* + * Look up a TRB by its DMA address, return NULL if not found on the ring. + * Search from start_seg to let callers optimize starting point selection. + * Write actual segment containing returned TRB to seg_out, if provided. + */ +static union xhci_trb *xhci_dma_to_trb(struct xhci_segment *start_seg, dma= _addr_t dma, + struct xhci_segment **seg_out) +{ + struct xhci_segment *seg; + + xhci_for_each_ring_seg(start_seg, seg) { + if (in_range(dma, seg->dma, TRB_SEGMENT_SIZE)) { + if (seg_out) + *seg_out =3D seg; + return seg->trbs + (dma - seg->dma) / sizeof(seg->trbs[0]); + } + } + + return NULL; +} + static bool trb_is_noop(union xhci_trb *trb) { return TRB_TYPE_NOOP_LE32(trb->generic.field[3]); @@ -2672,6 +2693,15 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (!ep_ring) return handle_transferless_tx_event(xhci, ep, trb_comp_code); =20 + /* get the corresponding transfer TRB pointer */ + ep_trb =3D xhci_dma_to_trb(ep_ring->deq_seg, ep_trb_dma, &ep_seg); + if (!ep_trb && ep_trb_dma) { + xhci_warn(xhci, "Ignoring '%s' event out of ring on slot %d ep %d\n", + xhci_trb_comp_code_string(trb_comp_code), slot_id, ep_index); + /* XXX: other ring's TDs may be executing on this EP, should we kill it?= */ + return 0; + } + /* Look for common error cases */ switch (trb_comp_code) { /* Skip codes that require special handling depending on @@ -2846,9 +2876,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, td_list); =20 /* Is this a TRB in the currently executing TD? */ - ep_seg =3D trb_in_td(td, ep_trb_dma); - - if (!ep_seg) { + if (!trb_in_td(td, ep_trb_dma)) { =20 if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { /* this event is unlikely to match any TD, don't skip them all */ @@ -2931,7 +2959,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, if (ring_xrun_event) return 0; =20 - ep_trb =3D &ep_seg->trbs[(ep_trb_dma - ep_seg->dma) / sizeof(*ep_trb)]; trace_xhci_handle_transfer(ep_ring, (struct xhci_generic_trb *) ep_trb, e= p_trb_dma); =20 /* --=20 2.48.1 From nobody Tue Dec 2 02:20:30 2025 Received: from mail-ed1-f50.google.com (mail-ed1-f50.google.com [209.85.208.50]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D37E8350A2C for ; Wed, 19 Nov 2025 11:04:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.50 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763550276; cv=none; b=Epn1Dftyvv0rJhqnhZWwl0JrHlaEtPiXZWXq4Up+PpmvjXHlGBrrinu543vEVVFUmf60wHlNpZxaNwl4o+bC+lFTX0TYLKwHJmM4OdHtF6vcnKOxrYWDx330toqETv8R5iZe8EAJIbWKEyT/ewpLWCN3eK88ZqzGeXoSVz4a+5w= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763550276; c=relaxed/simple; bh=G7JunY/3iWsoDP1AL4Uu9VZnCup1j693fiMcSMDqWug=; h=Date:From:To:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=HZUpMSsnRMGjEbxISHyzP2RvPDYo/+kkyv8IYKqnD/MMYLinYusajMFeTzLOeQP7BXk4qq7M1CQkc0O6idP0aOLKa1L8s696VbMc5boo0PHK6kQiBnGZDH1MKrStXPAOl6574vjMhgoGk+qQBZ9oSv05qALZSyDTDyu7N1Jpwqw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=dDbesPcu; arc=none smtp.client-ip=209.85.208.50 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="dDbesPcu" Received: by mail-ed1-f50.google.com with SMTP id 4fb4d7f45d1cf-640860f97b5so10386999a12.2 for ; Wed, 19 Nov 2025 03:04:34 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763550273; x=1764155073; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=jECqb7FpaGoNZmYZTTPNeoOaaYauInUDkmHh57LIczM=; b=dDbesPcuvxue7WMUgbtZcB7fENPmcsJYO5oP7rnq3aXOnGzg0jiDc151ubwRYbxbqR L6yE2KbZ+9KUfL89pBwWTb7fyZZL1n9vc+w6FxKsDHd3uUWKfQ9WOJsg7CFl3MWQ9Zlf g24QvPs5GtNKfoGkqw5e+zK+4H/6FELJI7HWXgQRs2xsWT54sdc/GrwoWh/Fa3p9iiXK i/I3Hmvxwod81m/OU0uQ3FomPh5wpM0lzrp/yE9LyHGV1BTop4IDepY0NF7bBzpjosYX /gMuAY2DcayDJTEcqD6HTYpYUgW9reDMF4lazuhOH00uoi+Pe/t80GflfLpbReOXkIDc cbyw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763550273; x=1764155073; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=jECqb7FpaGoNZmYZTTPNeoOaaYauInUDkmHh57LIczM=; b=ih8tzXyLz9J9EgYBmXGmXkL1gVbLCOHWW8yAf+axx9lLa83vZQ2yBOZKYqxLdLN2VH SSmlPixQ3qhsNe+qG5hFel8A3KyszKprhDBLfENS8FW2LJvc65aqo7xbvPjH2HU+JluP 2AAo8v7c9JKGB8UZs1WRd6pibeTVuzKdAvS7hOaiTRAN/aCAdW57PimqKIQh1ZbbCOvr Rm/sKb0VcFM7A/Zxiua9ii2upxFiZtevNrBH+TU/LbwylzHIrkSFij3zauwQHyW7ka4w F9FuAFxojf/XcVVtXXa4Wd6PckCc6RtPq6Ia833t8ieYeleS20tZeCkTLcRalBr/INUs udSA== X-Forwarded-Encrypted: i=1; AJvYcCWIr5FEsjiqAeVaVvFmVeedY/x3BrhXHv7wj+TbdoMwaKJRDFkWle5M5ALu3BXghfTR7ivugM3mWpMWWZw=@vger.kernel.org X-Gm-Message-State: AOJu0YwhSUT80ymvu4hOX5xwgup0+msYT9epw0JnS863cfy5t+BPq6eE Mz4TVORdrBv8ELJKf0xEOVHEjxYmKANn60n+3hl9el8vedI5NKTtd6Cf X-Gm-Gg: ASbGnctfZxKddCqIW5lFzmXZg9gn6XBeBwh0cFjaDVFZA1jKrwLu5xrAAZRfXg5sYAI GAMbODFGOEZ+m9iaZXWrdVXuLd73kU0yytcss7mNpAtVyMJe+coDNXaX19bAfzkLplOkAHUNCbG qsfd2xmyUrCDUn5cKKaIuSZeS8fmauFva8V4DHBzBWcbLaDd9LVmdcn6XEXXNhoDnq+uK8Aw5ns z+pul8D3YLfXa9Im+vJt5jGAXwudAwPs8GYBhjaecpP35GMZpk0t403L703OEit4ZLgiY3pSNVA VSQhPvvsPj+h7fkozpU+sAhnXJdTARptu41f89L+TftZGpDA5NSjMgRgFi6Q75hnG7MP5+33y3b Q7NLv/qufusW683qSEb8fEqUBzN1a6RPUzoazfyGzM+B/GCLXbROpqNqWyF7aHSQ6R3TGV/HBi3 kGzUhsQLPxH8XTzOxhUihpDaQ= X-Google-Smtp-Source: AGHT+IGAn8aA94Gjc6rB7edvmJijL47gT2Orqoh2HUXTMCcoqOAgk+7GqJFDZKDrLhN1hgbOGdfvkQ== X-Received: by 2002:a05:6402:3506:b0:640:9b62:a8bb with SMTP id 4fb4d7f45d1cf-64350e89ad2mr17322636a12.22.1763550273047; Wed, 19 Nov 2025 03:04:33 -0800 (PST) Received: from foxbook (bhd138.neoplus.adsl.tpnet.pl. [83.28.93.138]) by smtp.gmail.com with ESMTPSA id 4fb4d7f45d1cf-644f13ff4d4sm5588048a12.12.2025.11.19.03.04.32 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Wed, 19 Nov 2025 03:04:32 -0800 (PST) Date: Wed, 19 Nov 2025 12:04:30 +0100 From: Michal Pecio To: Mathias Nyman , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 3/5] usb: xhci: Refactor and generalize trb_in_td() Message-ID: <20251119120430.5e41bf74.michal.pecio@gmail.com> In-Reply-To: <20251119120208.6a025eb0.michal.pecio@gmail.com> References: <20251119120208.6a025eb0.michal.pecio@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Replace the internal logic of trb_in_td() with a new one which works on (virtual) TRB pointers instead of DMA addresses. Since driver data structures use virtual pointers, the function and some of its callers will be simplified. Optimize for common case of trb =3D=3D end_trb. Extract the implementation into a lower level trb_in_range() function which works with arbitrary TRB ranges. Create a new trb_in_td() as the obvious wrapper around trb_in_range(). Create a new dma_in_td() using xhci_dma_to_trb() and trb_in_td(). Update former trb_in_td() callers to use new functions appropriately. Signed-off-by: Michal Pecio --- drivers/usb/host/xhci-ring.c | 101 +++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 46 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 531e2f207b17..a2257e1dc396 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -113,6 +113,11 @@ static bool trb_is_link(union xhci_trb *trb) return TRB_TYPE_LINK_LE32(trb->link.control); } =20 +static bool trb_on_seg(struct xhci_segment *seg, union xhci_trb *trb) +{ + return in_range((uintptr_t) trb, (uintptr_t) seg->trbs, TRB_SEGMENT_SIZE); +} + static bool last_trb_on_seg(struct xhci_segment *seg, union xhci_trb *trb) { return trb =3D=3D &seg->trbs[TRBS_PER_SEGMENT - 1]; @@ -304,54 +309,58 @@ static void inc_enq(struct xhci_hcd *xhci, struct xhc= i_ring *ring, } =20 /* - * If the suspect DMA address is a TRB in this TD, this function returns t= hat - * TRB's segment. Otherwise it returns 0. + * Check if seg:trb is a TRB in the range between start_trb and end_trb, i= nclusive. + * If start_trb =3D=3D end_trb, the range is one TRB, not the full ring. T= he range may + * cycle back and end in an earlier segment or on earlier TRB in start_trb= segment. */ -static struct xhci_segment *trb_in_td(struct xhci_td *td, dma_addr_t suspe= ct_dma) +static bool trb_in_range(struct xhci_hcd *xhci, union xhci_trb *start_trb,= union xhci_trb *end_trb, + struct xhci_segment *seg, union xhci_trb *trb) { - dma_addr_t start_dma; - dma_addr_t end_seg_dma; - dma_addr_t end_trb_dma; - struct xhci_segment *cur_seg; + struct xhci_segment *seg_other; =20 - start_dma =3D xhci_trb_virt_to_dma(td->start_seg, td->start_trb); - cur_seg =3D td->start_seg; + if (!trb || !seg) + return false; =20 - do { - if (start_dma =3D=3D 0) - return NULL; - /* We may get an event for a Link TRB in the middle of a TD */ - end_seg_dma =3D xhci_trb_virt_to_dma(cur_seg, - &cur_seg->trbs[TRBS_PER_SEGMENT - 1]); - /* If the end TRB isn't in this segment, this is set to 0 */ - end_trb_dma =3D xhci_trb_virt_to_dma(cur_seg, td->end_trb); - - if (end_trb_dma > 0) { - /* The end TRB is in this segment, so suspect should be here */ - if (start_dma <=3D end_trb_dma) { - if (suspect_dma >=3D start_dma && suspect_dma <=3D end_trb_dma) - return cur_seg; - } else { - /* Case for one segment with - * a TD wrapped around to the top - */ - if ((suspect_dma >=3D start_dma && - suspect_dma <=3D end_seg_dma) || - (suspect_dma >=3D cur_seg->dma && - suspect_dma <=3D end_trb_dma)) - return cur_seg; - } - return NULL; - } - /* Might still be somewhere in this segment */ - if (suspect_dma >=3D start_dma && suspect_dma <=3D end_seg_dma) - return cur_seg; + /* Typically end_trb is near trb in the same segment */ + if (trb_on_seg(seg, end_trb)) + return trb <=3D end_trb + /* must not start between trb and end_trb */ + ? !(trb < start_trb && start_trb <=3D end_trb) + /* must start between end_trb and trb */ + : end_trb < start_trb && start_trb <=3D trb; =20 - cur_seg =3D cur_seg->next; - start_dma =3D xhci_trb_virt_to_dma(cur_seg, &cur_seg->trbs[0]); - } while (cur_seg !=3D td->start_seg); + /* The range ends in other segment, easy if it starts here */ + if (trb_on_seg(seg, start_trb)) + return start_trb <=3D trb; =20 - return NULL; + /* Walk segments and see if end_trb is reached before start_trb */ + for (seg_other =3D seg->next; seg_other !=3D seg; seg_other =3D seg_other= ->next) { + bool found_start =3D trb_on_seg(seg_other, start_trb); + bool found_end =3D trb_on_seg(seg_other, end_trb); + + if (found_end) + return found_start ? end_trb < start_trb : true; + + if (found_start) + return false; + } + + xhci_err(xhci, "TRB range boundaries not on ring\n"); + return false; +} + +static bool trb_in_td(struct xhci_hcd *xhci, struct xhci_td *td, + struct xhci_segment *seg, union xhci_trb *trb) +{ + return trb_in_range(xhci, td->start_trb, td->end_trb, seg, trb); +} + +static bool dma_in_td(struct xhci_hcd *xhci, struct xhci_td *td, dma_addr_= t dma) +{ + struct xhci_segment *seg; + union xhci_trb *trb =3D xhci_dma_to_trb(td->start_seg, dma, &seg); + + return trb_in_td(xhci, td, seg, trb); } =20 /* @@ -1094,7 +1103,7 @@ static int xhci_invalidate_cancelled_tds(struct xhci_= virt_ep *ep) td->urb->stream_id); hw_deq &=3D TR_DEQ_PTR_MASK; =20 - if (td->cancel_status =3D=3D TD_HALTED || trb_in_td(td, hw_deq)) { + if (td->cancel_status =3D=3D TD_HALTED || dma_in_td(xhci, td, hw_deq)) { switch (td->cancel_status) { case TD_CLEARED: /* TD is already no-op */ case TD_CLEARING_CACHE: /* set TR deq command already queued */ @@ -1184,7 +1193,7 @@ static struct xhci_td *find_halted_td(struct xhci_vir= t_ep *ep) hw_deq =3D xhci_get_hw_deq(ep->xhci, ep->vdev, ep->ep_index, 0); hw_deq &=3D TR_DEQ_PTR_MASK; td =3D list_first_entry(&ep->ring->td_list, struct xhci_td, td_list); - if (trb_in_td(td, hw_deq)) + if (dma_in_td(ep->xhci, td, hw_deq)) return td; } return NULL; @@ -2843,7 +2852,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, */ td =3D list_first_entry_or_null(&ep_ring->td_list, struct xhci_td, td_lis= t); =20 - if (td && td->error_mid_td && !trb_in_td(td, ep_trb_dma)) { + if (td && td->error_mid_td && !trb_in_td(xhci, td, ep_seg, ep_trb)) { xhci_dbg(xhci, "Missing TD completion event after mid TD error\n"); xhci_dequeue_td(xhci, td, ep_ring, td->status); } @@ -2876,7 +2885,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, td_list); =20 /* Is this a TRB in the currently executing TD? */ - if (!trb_in_td(td, ep_trb_dma)) { + if (!trb_in_td(xhci, td, ep_seg, ep_trb)) { =20 if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { /* this event is unlikely to match any TD, don't skip them all */ --=20 2.48.1 From nobody Tue Dec 2 02:20:30 2025 Received: from mail-ej1-f46.google.com (mail-ej1-f46.google.com [209.85.218.46]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 75248350A2C for ; Wed, 19 Nov 2025 11:05:27 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.46 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763550331; cv=none; b=hyppB2yTaROsr73qs/pb7jVaUVkpXgFnp4ybpDowgUzvWSHlO3YScZMmQ2s4K4PutZE9mjAjUCZWAnyNOO6YVFIIXm+Wf7ciUPXvHn45CgkX0teO2AJQK1AZjMuTxGvoC4hlRA9ILfluQ6q9DkSJbyGKb3MfA5ZTs+Qkcb9WKAc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763550331; c=relaxed/simple; bh=ZZlMYEL4UcoCzWKCh7+jEM7X2xcMYZBm/2G+suCYUWA=; h=Date:From:To:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=cnYifAMCzRU+jc5ydxkI/MtqOPBV3fuIChiUvFi2u8IL4dTi62TJFKJIQj5h4T+A5wm0R9A//jFws78+vs2T7/bwrzJwxEQmZtxex4gWY4X7DkIwgVsdqBOsWTsqVUen4qtkyIELagPjsimBgxUWSnmgIdQgBgmTtlktntMhES4= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=lv+oDTbg; arc=none smtp.client-ip=209.85.218.46 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="lv+oDTbg" Received: by mail-ej1-f46.google.com with SMTP id a640c23a62f3a-b73161849e1so1212232666b.2 for ; Wed, 19 Nov 2025 03:05:27 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763550324; x=1764155124; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=a2r3ybB/cy39+Bu51zs1AfwKD0YYeIolsm0blPv0hSA=; b=lv+oDTbgFdwuK37WwCmQ8t+i87fMV97ZseEEsSWR6d3i+BKB6cK1WtUoh8n7g9YZXc ko0YSNYxVUg/DCwRIOonVMPwPJv3nbqJqT0W2DicQGMtpyWBg2JTtcdXMXenptC7v8MR cBf/5JSRUEMDcBensGmeP78l77TFRDC6lJX2553rcZFID5lgSu5kNVki6AIkoo7Yyerp Mhk11na11IubK1a1XQQbN82CxFZpD/iSG8NnBxbiayDZUgapd5LXDzsh/bCmok7GLDA3 oI3my9TQ8zptlU04+OOwwSVEJybp1LkRLEe8UhV1v5aCbbJBquP0fuViSXmnYetiq3L/ UkYw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763550324; x=1764155124; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=a2r3ybB/cy39+Bu51zs1AfwKD0YYeIolsm0blPv0hSA=; b=fBfpQ0SqAwnv+QgAfkcDSxbiDsIdTCt5F33/siJdlDS09uZUOIU4oWzDV8xF7sGJ2E fuC2s1XKjryM/NibHXoa3kXsaeszix+ZjpvpRL+PlPlZwuFkOOLseIHsn1fvKcUw9uTE wjgexFtTMGOtgohkfkFv0FaMHgA6/rHYxAZl/5VDDipgSxSJu2CI8ohiMe0PFbTSUHBZ tXFykOWZ+nIi8ZuVI7695EDPGsrvf3EIJXwS+iQ0QtxPVldU7noZaxnEiuu0bfOi5yY+ 6v3fmJ1de7Yq0Pqw1nh7t5qgoyYf66+xQfgz37NW3TO20XXASjcM9WbQ8JfsKg2exKLZ snwg== X-Forwarded-Encrypted: i=1; AJvYcCWuejn7wshJzzVu+enu/LHulF8YyblCWvJjtf8B/g4MOZanXjTchamraTXXsay7yU1aG2bNCAczoVL9fiE=@vger.kernel.org X-Gm-Message-State: AOJu0YzTPfe8HFx/SAhPfrP2VyGSHZJO/6I5n58ok4Axf7R9wtIbHV/u cp9KzNEDMifBBSDI31AEm0j+WiFuAyvJT1n8mcEi9aYYdQNlMuJoqOS2 X-Gm-Gg: ASbGncudyHQFrb7OPo5yToK3lohfzNC/HUptpjfDFN1nb4ZvqogLuJMkm8qIos9fACh 989CwrMB9oaQE7sxbEYPcYHVcJe4kWZW9ahTodT3yhNrIM/JIGQSQu4i0D6ut8Uj8GFZXp953r7 Yv8tiv2ECy1IyyJ1754hqncLr2SL5m/0gNLPGRb7C6I2o2dlpz/ipTic66seuMbaEmMipnOwE57 aF+a4UPw1hHXloyfnfxHO//UDG6ePRgdxdzwlZ1LC5lxo6qnjQuGxHgKbd4kQF/0kGxS5Wktel+ 83HvNKAzwg/z0vSquoGdNbVQLZUtvRQRVTMlfxmPUN+WIVH/RWYrE+xk/YEgahRBBgTL5sxU/5y EgloXGd9Um0IMwPsIbmFmHI1qNEdXqf9qGB/CvwyMhPCzxxfNoZkoPwqZKqVUU//KYD0VOXc4Vb uAqMj28ifbIOj97npnX3XCUfY= X-Google-Smtp-Source: AGHT+IEw5t/eIx5bVX1xsnAa+zaDwgtTnfVs0G0iRtj0eD3+nJI5zrqG9M+9zwSEwsT23oCUk2bCQw== X-Received: by 2002:a17:907:7fa5:b0:b73:75ea:febf with SMTP id a640c23a62f3a-b7637a128afmr200845966b.55.1763550323812; Wed, 19 Nov 2025 03:05:23 -0800 (PST) Received: from foxbook (bhd138.neoplus.adsl.tpnet.pl. [83.28.93.138]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b734fad45afsm1599924766b.24.2025.11.19.03.05.23 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Wed, 19 Nov 2025 03:05:23 -0800 (PST) Date: Wed, 19 Nov 2025 12:05:20 +0100 From: Michal Pecio To: Mathias Nyman , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 4/5] usb: xhci: Reduce error mid TD latency with a new "done TD" mechanism Message-ID: <20251119120520.7827bfa7.michal.pecio@gmail.com> In-Reply-To: <20251119120208.6a025eb0.michal.pecio@gmail.com> References: <20251119120208.6a025eb0.michal.pecio@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" No data transfer will occur after an error mid TD, so the TD can be given back to class driver without waiting for the final completion event. This reduces latency, particularly on buggy HCs from vendors like NEC or ASMedia which ignore the IOC flag after errors, making the TD wait until next ESIT to be given back. Since we can no longer expect the 'xhci_td' to stay around after it is given back, copy minimum metadata to 'ep_ring' which will enable recognition of subsequent events for the done TD. Existing solution with trb_in_td() proved very effective, so use a similar mechanism. Also take over handling out-of-spec Etron events, because we can do it more accurately than the previous implementation. We no longer need to handle Missed Service Errors with NULL 'ep_trb' for the sake of TDs stuck in error mid TD state, so drop that. Signed-off-by: Michal Pecio --- drivers/usb/host/xhci-ring.c | 116 +++++++++++++++++++---------------- drivers/usb/host/xhci.h | 2 +- 2 files changed, 64 insertions(+), 54 deletions(-) diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index a2257e1dc396..2b889caff0f5 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1542,6 +1542,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *= xhci, int slot_id, */ ep_ring->deq_seg =3D ep->queued_deq_seg; ep_ring->dequeue =3D ep->queued_deq_ptr; + /* If we were waiting for a done TD, we aren't anymore */ + ep_ring->done_end_trb =3D NULL; } else { xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xH= CI internal state.\n"); xhci_warn(xhci, "ep deq seg =3D %p, deq ptr =3D %p\n", @@ -2260,7 +2262,7 @@ int xhci_is_vendor_info_code(struct xhci_hcd *xhci, u= nsigned int trb_comp_code) =20 static void finish_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep, struct xhci_ring *ep_ring, struct xhci_td *td, u32 trb_comp_code, - struct xhci_segment *ep_seg, union xhci_trb *ep_trb) + struct xhci_segment *ep_seg, union xhci_trb *ep_trb, bool more_eve= nts) { struct xhci_ep_ctx *ep_ctx; =20 @@ -2300,6 +2302,9 @@ static void finish_td(struct xhci_hcd *xhci, struct x= hci_virt_ep *ep, /* update ring dequeue state */ ep_ring->deq_seg =3D ep_seg; ep_ring->dequeue =3D ep_trb; + /* td is done and going away, but it may still get more events */ + if (more_events) + ep_ring->done_end_trb =3D td->end_trb; =20 xhci_td_cleanup(xhci, td, ep_ring, td->status); } @@ -2408,7 +2413,7 @@ static void process_ctrl_td(struct xhci_hcd *xhci, st= ruct xhci_virt_ep *ep, td->urb->actual_length =3D requested; =20 finish_td: - finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb); + finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb, false); } =20 /* @@ -2426,6 +2431,11 @@ static void process_isoc_td(struct xhci_hcd *xhci, s= truct xhci_virt_ep *ep, bool sum_trbs_for_length =3D false; u32 remaining, requested, ep_trb_len; int short_framestatus; + /* Expect more events for this TD after certain events before the last TR= B */ + bool more_events =3D false; + /* On Etron SuperSpeed endpoints some errors on any TRB trigger more even= ts */ + bool etron_ss_bug =3D xhci->quirks & XHCI_ETRON_HOST && + td->urb->dev->speed =3D=3D USB_SPEED_SUPER; =20 trb_comp_code =3D GET_COMP_CODE(le32_to_cpu(event->transfer_len)); urb_priv =3D td->urb->hcpriv; @@ -2440,9 +2450,6 @@ static void process_isoc_td(struct xhci_hcd *xhci, st= ruct xhci_virt_ep *ep, /* handle completion code */ switch (trb_comp_code) { case COMP_SUCCESS: - /* Don't overwrite status if TD had an error, see xHCI 4.9.1 */ - if (td->error_mid_td) - break; if (remaining) { frame->status =3D short_framestatus; sum_trbs_for_length =3D true; @@ -2462,14 +2469,12 @@ static void process_isoc_td(struct xhci_hcd *xhci, = struct xhci_virt_ep *ep, fallthrough; case COMP_ISOCH_BUFFER_OVERRUN: frame->status =3D -EOVERFLOW; - if (ep_trb !=3D td->end_trb) - td->error_mid_td =3D true; + more_events =3D ep_trb !=3D td->end_trb || etron_ss_bug; /* xHCI 4.9.1 */ break; case COMP_MISSED_SERVICE_ERROR: frame->status =3D -EXDEV; sum_trbs_for_length =3D true; - if (ep_trb !=3D td->end_trb) - td->error_mid_td =3D true; + more_events =3D ep_trb !=3D td->end_trb; /* xHCI 4.11.2.5.2, no Etron bu= g */ break; case COMP_INCOMPATIBLE_DEVICE_ERROR: case COMP_STALL_ERROR: @@ -2478,8 +2483,7 @@ static void process_isoc_td(struct xhci_hcd *xhci, st= ruct xhci_virt_ep *ep, case COMP_USB_TRANSACTION_ERROR: frame->status =3D -EPROTO; sum_trbs_for_length =3D true; - if (ep_trb !=3D td->end_trb) - td->error_mid_td =3D true; + more_events =3D ep_trb !=3D td->end_trb || etron_ss_bug; /* xHCI 4.9.1 */ break; case COMP_STOPPED: sum_trbs_for_length =3D true; @@ -2501,9 +2505,6 @@ static void process_isoc_td(struct xhci_hcd *xhci, st= ruct xhci_virt_ep *ep, break; } =20 - if (td->urb_length_set) - goto finish_td; - if (sum_trbs_for_length) frame->actual_length =3D sum_trb_lengths(td, ep_trb) + ep_trb_len - remaining; @@ -2512,14 +2513,7 @@ static void process_isoc_td(struct xhci_hcd *xhci, s= truct xhci_virt_ep *ep, =20 td->urb->actual_length +=3D frame->actual_length; =20 -finish_td: - /* Don't give back TD yet if we encountered an error mid TD */ - if (td->error_mid_td && ep_trb !=3D td->end_trb) { - xhci_dbg(xhci, "Error mid isoc TD, wait for final completion event\n"); - td->urb_length_set =3D true; - return; - } - finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb); + finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb, more_even= ts); } =20 static void skip_isoc_td(struct xhci_hcd *xhci, struct xhci_td *td, @@ -2610,7 +2604,34 @@ static void process_bulk_intr_td(struct xhci_hcd *xh= ci, struct xhci_virt_ep *ep, td->urb->actual_length =3D 0; } =20 - finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb); + finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb, false); +} + +/* + * Process events for an already finished TD. See xHCI 4.9.1. + */ +static void process_done_td(struct xhci_hcd *xhci, struct xhci_ring *ep_ri= ng, + struct xhci_segment *ep_seg, union xhci_trb *ep_trb, + u32 trb_comp_code) +{ + switch (trb_comp_code) { + case COMP_STOPPED: + case COMP_STOPPED_LENGTH_INVALID: + case COMP_STOPPED_SHORT_PACKET: + /* stopped events don't terminate the TD */ + break; + default: + /* clear done TD if this is the final event */ + if (ep_trb =3D=3D ep_ring->done_end_trb) + ep_ring->done_end_trb =3D NULL; + else /* unexpected */ + xhci_dbg(xhci, "Done TD '%s' before end_trb\n", + xhci_trb_comp_code_string(trb_comp_code)); + } + + /* release TRBs completed by the xHC */ + ep_ring->deq_seg =3D ep_seg; + ep_ring->dequeue =3D ep_trb; } =20 /* Transfer events which don't point to a transfer TRB, see xhci 4.17.4 */ @@ -2646,11 +2667,6 @@ static bool xhci_spurious_success_tx_event(struct xh= ci_hcd *xhci, switch (ring->old_trb_comp_code) { case COMP_SHORT_PACKET: return xhci->quirks & XHCI_SPURIOUS_SUCCESS; - case COMP_USB_TRANSACTION_ERROR: - case COMP_BABBLE_DETECTED_ERROR: - case COMP_ISOCH_BUFFER_OVERRUN: - return xhci->quirks & XHCI_ETRON_HOST && - ring->type =3D=3D TYPE_ISOC; default: return false; } @@ -2800,15 +2816,17 @@ static int handle_tx_event(struct xhci_hcd *xhci, break; case COMP_MISSED_SERVICE_ERROR: /* - * When encounter missed service error, one or more isoc tds - * may be missed by xHC. - * Set skip flag of the ep_ring; Complete the missed tds as - * short transfer when process the ep_ring next time. + * One or more isoc TDs were missed by the xHC and ep_trb points to the + * last missed TD, or it may be NULL. Flag the endpoint and run skipping + * now if we know the missed TDs or leave them to be skipped later. + * See xHCI 4.10.3.2, note differences between spec rev 1.0 and later. */ ep->skip =3D true; xhci_dbg(xhci, "Miss service interval error for slot %u ep %u, set skip flag%s\n", - slot_id, ep_index, ep_trb_dma ? ", skip now" : ""); + slot_id, ep_index, ep_trb ? ", skip now" : ""); + if (!ep_trb) + return 0; break; case COMP_NO_PING_RESPONSE_ERROR: ep->skip =3D true; @@ -2838,29 +2856,21 @@ static int handle_tx_event(struct xhci_hcd *xhci, } =20 /* - * xhci 4.10.2 states isoc endpoints should continue - * processing the next TD if there was an error mid TD. - * So host like NEC don't generate an event for the last - * isoc TRB even if the IOC flag is set. - * xhci 4.9.1 states that if there are errors in mult-TRB - * TDs xHC should generate an error for that TRB, and if xHC - * proceeds to the next TD it should genete an event for - * any TRB with IOC flag on the way. Other host follow this. - * - * We wait for the final IOC event, but if we get an event - * anywhere outside this TD, just give it back already. + * Check if we are expecting more events for a "done" TD, which has been = given back before + * the xHC finished traversing all its TRBs, because it completed with an= error. */ - td =3D list_first_entry_or_null(&ep_ring->td_list, struct xhci_td, td_lis= t); - - if (td && td->error_mid_td && !trb_in_td(xhci, td, ep_seg, ep_trb)) { - xhci_dbg(xhci, "Missing TD completion event after mid TD error\n"); - xhci_dequeue_td(xhci, td, ep_ring, td->status); + if (ep_ring->done_end_trb) { + if (trb_in_range(xhci, ep_ring->dequeue, ep_ring->done_end_trb, ep_seg, = ep_trb)) { + process_done_td(xhci, ep_ring, ep_seg, ep_trb, trb_comp_code); + return 0; + } + /* + * Some HCs don't generate these events and silently move forward. We ge= t an event + * for the next TD, or maybe Missed Service or Ring Underrun. Handle it = normally. + */ + ep_ring->done_end_trb =3D NULL; } =20 - /* If the TRB pointer is NULL, missed TDs will be skipped on the next eve= nt */ - if (trb_comp_code =3D=3D COMP_MISSED_SERVICE_ERROR && !ep_trb_dma) - return 0; - if (list_empty(&ep_ring->td_list)) { /* * Don't print wanings if ring is empty due to a stopped endpoint genera= ting an diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 485ea7fc0433..a1dd9ce5f8aa 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1314,7 +1314,6 @@ struct xhci_td { struct xhci_segment *bounce_seg; /* actual_length of the URB has already been set */ bool urb_length_set; - bool error_mid_td; }; =20 /* @@ -1380,6 +1379,7 @@ struct xhci_ring { unsigned int num_trbs_free; /* used only by xhci DbC */ unsigned int bounce_buf_len; enum xhci_ring_type type; + union xhci_trb *done_end_trb; u32 old_trb_comp_code; struct radix_tree_root *trb_address_map; }; --=20 2.48.1 From nobody Tue Dec 2 02:20:30 2025 Received: from mail-ed1-f45.google.com (mail-ed1-f45.google.com [209.85.208.45]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7F6E62FE07E for ; Wed, 19 Nov 2025 11:06:06 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.208.45 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763550369; cv=none; b=UZCzqedf8BkcZTuBscK9m9QSIAtRab75HBQprzlva3DfTKzj0MN6BbCD4McSloN36bJVFXxJxuIjwtj3YOdANrbH9wTD8ttDbxGzoMUnWtKzNNTAEVcJIS4FCOkc/O0qW2Uv+9KfKv4kDajAeEmkKC4Kwy0XVpzvKVlPGtF3naM= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1763550369; c=relaxed/simple; bh=+2d51D44sRDt8eBybkvWwVZvcXIsPb6EJ/QSrTukauM=; h=Date:From:To:Cc:Subject:Message-ID:In-Reply-To:References: MIME-Version:Content-Type; b=C5Aa4jTn5dFxVtrzh/7YPe9V/ACKPAhlztBk9z70EdJqi7KrQIlJSZc4/NM3Wh32GVjn43lMvrKJEzb1Lzv7Qml/jkYgv2AjQNdhk1Ay7xzMdSr/gh7EjDz57g2Vjm4EQ9SLFZ5e9TE69K9zVD2F9wxbchq6uc1rtTnXCfpgAnQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=JwTfBbWa; arc=none smtp.client-ip=209.85.208.45 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="JwTfBbWa" Received: by mail-ed1-f45.google.com with SMTP id 4fb4d7f45d1cf-64312565c10so9825770a12.2 for ; Wed, 19 Nov 2025 03:06:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1763550364; x=1764155164; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:from:to:cc:subject:date :message-id:reply-to; bh=JjhqYSqklyXctCvXY19E4xXbS28k7b49erlVOkL0uaU=; b=JwTfBbWaJw1pvaovYWi9cZnu+Kb7Ed6nng7fOE1u66mDATB0H9+GwCQgeyC+TguWNd 6gRzqcPrPt5VJDspuuV3gZiirXAMRiKsK6aNZNEY52A0gDGyP7z4l/1pA3xzbD3AAQ/B z8jn0lVdXQbsgXnmAQPpztd1aGhM4XGChS8aa88ta0XITn09kdLGi//2S99FAvWUOxbK 8H5LhwpasiYit6IALb/qGrRwmgSV+mwryCXgZEIJEjoP9XnBjzBa1Z8uBiduwaJ9dKCw LRATbvO2LsihU2E4VgUXHi447MCfYdvf7OdtGiMm6CiQr2HHiDgxdUG/c7U4qBeaeK1C m/Xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1763550364; x=1764155164; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:subject:cc:to:from:date:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=JjhqYSqklyXctCvXY19E4xXbS28k7b49erlVOkL0uaU=; b=hZo+481ELfg9/yxumy/YsbE4guEcZi7Fn2NrMd4LOGEdttZx9aWnJkQVIr78M7jbSO QfxySIkEfMAntih6HHHdlXOR9Q+oFffp4pvMKBOaQbE2blYN2GnbtsgYMh5EDWkABtSG U43nLdyS0GxaIkZKun435BTIHSCSMUGGJMtCiGiz6im2p89F+uWY6hKo+8BDTPzh/tVP TtWEtq0QkKw1Iuk0CMNHZrSqAUfUvVTFzfdQOlM5YjlY5bmCI/EclD1walIRohJ2vIUF 8Y45WDj5dUa/TVlQfVHjqEvFaXCeHNu2OBVxm/xjFgq9IVL9+Q7mf/xEbLhvhpGPl+im mWBA== X-Forwarded-Encrypted: i=1; AJvYcCUdl0wrlGKzT8uRzwPdDX4LIJxp8mxG/nLcrA3ZCUbuswYeB4oXgvQvB9bQSOZGbk8SlCbPP3q6mpCAv4E=@vger.kernel.org X-Gm-Message-State: AOJu0YxJUYoj8UJzn7HFQNjTlmfIhdGYWTmjpei347OJ8hiOkNZgFqyV KWGhI2Kg+Lxt/g+hl1LukfZtgy6axoDgcuW0F6uR8F1yLHc61vcz8CrJ X-Gm-Gg: ASbGncuT/eWYEBYIoLot49scAX/+JyiPFNJK6MmqdQ3P+uG3vU5HxO6hShIVmQMo9+x xqWE/TGqQz9Qqt2M1PoVHMDFgTBKnJ0gKmU9jCr/MnZgpuUuC/kM+ROErEFBisX7e2gzoOe3Jgh MVc030wqsqpP1r2taaPGZrKaA0Vjizve0un0C8JimZ4lUREaX5lQCIcgGrDFihMC1XoZh8FtX7c MreC/KMd2/eoRLFZKO0y3GmXgn8b0FOvYnOuOgqh5qzXg72Fli0I5jJnstvOT8Q6Bj8YP5PUn+t O8qcpqMfs+uS/gYSqUp4QY6rEZg6MnmF2AGMkSuilW8TUsoKpka1DKknYuqvDuiabj2yR+nQFd+ eom6G29BfO6sbqYQx0N1tnjtrUqGQg5ZNxzFvcR2R8zNcMU8bIimGhEzkIp0mJs+MKGGDm806BQ V4lkeYUC5TsgeHB0XwZ+kyC9o= X-Google-Smtp-Source: AGHT+IHTBHx15yNRGnMidAAcOO2NJhpnoMhv5pnLm2GpLTZ+cNsXO29k/8OCuAvMMM9Y/toDz1Chwg== X-Received: by 2002:a17:907:fdcb:b0:b73:8757:68ad with SMTP id a640c23a62f3a-b7387577ab2mr1700356166b.47.1763550364225; Wed, 19 Nov 2025 03:06:04 -0800 (PST) Received: from foxbook (bhd138.neoplus.adsl.tpnet.pl. [83.28.93.138]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b73783ef841sm1215393966b.46.2025.11.19.03.06.03 (version=TLS1_2 cipher=AES128-SHA bits=128/128); Wed, 19 Nov 2025 03:06:03 -0800 (PST) Date: Wed, 19 Nov 2025 12:06:01 +0100 From: Michal Pecio To: Mathias Nyman , Greg Kroah-Hartman Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 5/5] usb: xhci: Handle short transfers as "done TDs" Message-ID: <20251119120601.77c46152.michal.pecio@gmail.com> In-Reply-To: <20251119120208.6a025eb0.michal.pecio@gmail.com> References: <20251119120208.6a025eb0.michal.pecio@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Short transfers (on any endpoint type) are similar to isochronous errors mid TD - the TD can be given back, but we need to keep track of xHC's progress through its TRBs and handle some more events. And here too there are buggy HCs which don't generate expected events. Reuse the "done TD" mechanism to also handle short transfers on all endpoints other than control, which have some special requirements. Done TD is more accurate than the previous code, because it matches events with TDs by TRB pointers, not by completion codes alone. Remove the SPURIOUS_SUCCESS quirk, as we can now detect missing end TRB events automatically and reliably. Ensure that no xhci_dbg() shows on every short transfer, which can be 8000 times per second. The patch was tested on isochronous and bulk endpoints at FS/HS/SS with several host controllers from various vendors. Behavior after Short Packet mid TD varies: 1. NEC uPD720200 and Etron EJ168 ignore IOC on the last TRB. 2. Renesas uPD720202 and AMD Carrizo APU generate Success with residual equal to last TRB size. 3. Fresco Logic FL1100 generates Short Packet, residual as above. 4. Various ASMedia HCs and AMD Promontory generate Short Packet again with repeated residual, even if the last TRB is shorter. 5. VIA VL805 generates Success with zero residual. No case of a Success with non-zero residual was observed, but commit 1530bbc6272d ("xhci: Add new short TX quirk for Fresco Logic host.") says that some Fresco Logic hardware (FL1000?) does that. Signed-off-by: Michal Pecio --- drivers/usb/host/xhci-mtk.c | 5 ---- drivers/usb/host/xhci-pci.c | 5 ---- drivers/usb/host/xhci-ring.c | 51 +++++++++--------------------------- drivers/usb/host/xhci.c | 7 ----- drivers/usb/host/xhci.h | 3 +-- 5 files changed, 13 insertions(+), 58 deletions(-) diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index 208558cf822d..d85f84e39291 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -454,11 +454,6 @@ static void xhci_mtk_quirks(struct device *dev, struct= xhci_hcd *xhci) struct xhci_hcd_mtk *mtk =3D hcd_to_mtk(hcd); =20 xhci->quirks |=3D XHCI_MTK_HOST; - /* - * MTK host controller gives a spurious successful event after a - * short transfer. Ignore it. - */ - xhci->quirks |=3D XHCI_SPURIOUS_SUCCESS; if (mtk->lpm_support) xhci->quirks |=3D XHCI_LPM_SUPPORT; if (mtk->u2_lpm_disable) diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c index d292adc65e5a..bcfe45d23f17 100644 --- a/drivers/usb/host/xhci-pci.c +++ b/drivers/usb/host/xhci-pci.c @@ -454,11 +454,6 @@ static void xhci_pci_quirks(struct device *dev, struct= xhci_hcd *xhci) =20 if (pdev->vendor =3D=3D PCI_VENDOR_ID_ASMEDIA && pdev->device =3D=3D PCI_DEVICE_ID_ASMEDIA_1042_XHCI) { - /* - * try to tame the ASMedia 1042 controller which reports 0.96 - * but appears to behave more like 1.0 - */ - xhci->quirks |=3D XHCI_SPURIOUS_SUCCESS; xhci->quirks |=3D XHCI_BROKEN_STREAMS; } if (pdev->vendor =3D=3D PCI_VENDOR_ID_ASMEDIA && diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 2b889caff0f5..a21d403305f1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -2450,16 +2450,16 @@ static void process_isoc_td(struct xhci_hcd *xhci, = struct xhci_virt_ep *ep, /* handle completion code */ switch (trb_comp_code) { case COMP_SUCCESS: - if (remaining) { - frame->status =3D short_framestatus; - sum_trbs_for_length =3D true; + /* check transfer completeness, some HCs may lie */ + if (ep_trb =3D=3D td->end_trb && !remaining) { + frame->status =3D 0; break; } - frame->status =3D 0; - break; + fallthrough; case COMP_SHORT_PACKET: frame->status =3D short_framestatus; sum_trbs_for_length =3D true; + more_events =3D ep_trb !=3D td->end_trb; /* xHCI 4.10.1.1.2 */ break; case COMP_BANDWIDTH_OVERRUN_ERROR: frame->status =3D -ECOMM; @@ -2547,6 +2547,8 @@ static void process_bulk_intr_td(struct xhci_hcd *xhc= i, struct xhci_virt_ep *ep, struct xhci_slot_ctx *slot_ctx; u32 trb_comp_code; u32 remaining, requested, ep_trb_len; + /* Expect more events for this TD after short transfers before the last T= RB */ + bool more_events =3D false; =20 slot_ctx =3D xhci_get_slot_ctx(xhci, ep->vdev->out_ctx); trb_comp_code =3D GET_COMP_CODE(le32_to_cpu(event->transfer_len)); @@ -2568,6 +2570,7 @@ static void process_bulk_intr_td(struct xhci_hcd *xhc= i, struct xhci_virt_ep *ep, break; case COMP_SHORT_PACKET: td->status =3D 0; + more_events =3D ep_trb !=3D td->end_trb; /* xHCI 4.10.1.1.2 */ break; case COMP_STOPPED_SHORT_PACKET: td->urb->actual_length =3D remaining; @@ -2604,11 +2607,11 @@ static void process_bulk_intr_td(struct xhci_hcd *x= hci, struct xhci_virt_ep *ep, td->urb->actual_length =3D 0; } =20 - finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb, false); + finish_td(xhci, ep, ep_ring, td, trb_comp_code, ep_seg, ep_trb, more_even= ts); } =20 /* - * Process events for an already finished TD. See xHCI 4.9.1. + * Process events for an already finished TD. See xHCI 4.9.1 and 4.10.1.1.= 2. */ static void process_done_td(struct xhci_hcd *xhci, struct xhci_ring *ep_ri= ng, struct xhci_segment *ep_seg, union xhci_trb *ep_trb, @@ -2661,17 +2664,6 @@ static int handle_transferless_tx_event(struct xhci_= hcd *xhci, struct xhci_virt_ return 0; } =20 -static bool xhci_spurious_success_tx_event(struct xhci_hcd *xhci, - struct xhci_ring *ring) -{ - switch (ring->old_trb_comp_code) { - case COMP_SHORT_PACKET: - return xhci->quirks & XHCI_SPURIOUS_SUCCESS; - default: - return false; - } -} - /* * If this function returns an error condition, it means it got a Transfer * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address. @@ -2733,11 +2725,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, * transfer type */ case COMP_SUCCESS: - if (EVENT_TRB_LEN(le32_to_cpu(event->transfer_len)) !=3D 0) { - trb_comp_code =3D COMP_SHORT_PACKET; - xhci_dbg(xhci, "Successful completion on short TX for slot %u ep %u wit= h last td comp code %d\n", - slot_id, ep_index, ep_ring->old_trb_comp_code); - } break; case COMP_SHORT_PACKET: break; @@ -2857,7 +2844,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, =20 /* * Check if we are expecting more events for a "done" TD, which has been = given back before - * the xHC finished traversing all its TRBs, because it completed with an= error. + * the xHC finished traversing all its TRBs, because it completed short o= r with an error. */ if (ep_ring->done_end_trb) { if (trb_in_range(xhci, ep_ring->dequeue, ep_ring->done_end_trb, ep_seg, = ep_trb)) { @@ -2880,8 +2867,7 @@ static int handle_tx_event(struct xhci_hcd *xhci, */ if (trb_comp_code !=3D COMP_STOPPED && trb_comp_code !=3D COMP_STOPPED_LENGTH_INVALID && - !ring_xrun_event && - !xhci_spurious_success_tx_event(xhci, ep_ring)) { + !ring_xrun_event) { xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n", slot_id, ep_index); } @@ -2942,17 +2928,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, return 0; } =20 - /* - * Some hosts give a spurious success event after a short - * transfer or error on last TRB. Ignore it. - */ - if (xhci_spurious_success_tx_event(xhci, ep_ring)) { - xhci_dbg(xhci, "Spurious event dma %pad, comp_code %u after %u\n", - &ep_trb_dma, trb_comp_code, ep_ring->old_trb_comp_code); - ep_ring->old_trb_comp_code =3D 0; - return 0; - } - /* HC is busted, give up! */ goto debug_finding_td; } @@ -2972,8 +2947,6 @@ static int handle_tx_event(struct xhci_hcd *xhci, */ } while (ep->skip); =20 - ep_ring->old_trb_comp_code =3D trb_comp_code; - /* Get out if a TD was queued at enqueue after the xrun occurred */ if (ring_xrun_event) return 0; diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 6da583c7778b..4e32cbe1c6b8 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -5473,13 +5473,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_qui= rks_t get_quirks) if (get_quirks) get_quirks(dev, xhci); =20 - /* In xhci controllers which follow xhci 1.0 spec gives a spurious - * success event after a short transfer. This quirk will ignore such - * spurious event. - */ - if (xhci->hci_version > 0x96) - xhci->quirks |=3D XHCI_SPURIOUS_SUCCESS; - if (xhci->hci_version =3D=3D 0x95 && link_quirk) { xhci_dbg(xhci, "QUIRK: Not clearing Link TRB chain bits"); xhci->quirks |=3D XHCI_LINK_TRB_QUIRK; diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index a1dd9ce5f8aa..e72eda6cee62 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -1380,7 +1380,6 @@ struct xhci_ring { unsigned int bounce_buf_len; enum xhci_ring_type type; union xhci_trb *done_end_trb; - u32 old_trb_comp_code; struct radix_tree_root *trb_address_map; }; =20 @@ -1587,7 +1586,7 @@ struct xhci_hcd { #define XHCI_RESET_EP_QUIRK BIT_ULL(1) /* Deprecated */ #define XHCI_NEC_HOST BIT_ULL(2) #define XHCI_AMD_PLL_FIX BIT_ULL(3) -#define XHCI_SPURIOUS_SUCCESS BIT_ULL(4) +#define XHCI_SPURIOUS_SUCCESS BIT_ULL(4) /* Deprecated */ /* * Certain Intel host controllers have a limit to the number of endpoint * contexts they can handle. Ideally, they would signal that they can't h= andle --=20 2.48.1