1

USB / Thunderbolt fixes for 6.10-rc4

Here are some small USB and Thunderbolt driver fixes for 6.10-rc4.
 Included in here are:
   - thunderbolt debugfs bugfix
   - USB typec bugfixes
   - kcov usb bugfix
   - xhci bugfixes
   - usb-storage bugfix
   - dt-bindings bugfix
   - cdc-wdm log message spam bugfix
 
 All of these, except for the last cdc-wdm log level change, have been in
 linux-next for a while with no reported problems.  The cdc-wdm bugfix
 has been tested by syzbot and proved to fix the reported cpu lockup
 issues when the log is constantly spammed by a broken device.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZm68wA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ylZmgCfeRfuQkFXgRs1LYZ8x2g2FfHmxFgAoJe+2RAW
 yR6Lp2bPhH+YQS3rhjNL
 =hNwg
 -----END PGP SIGNATURE-----

Merge tag 'usb-6.10-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB / Thunderbolt fixes from Greg KH:
 "Here are some small USB and Thunderbolt driver fixes for 6.10-rc4.
  Included in here are:

   - thunderbolt debugfs bugfix

   - USB typec bugfixes

   - kcov usb bugfix

   - xhci bugfixes

   - usb-storage bugfix

   - dt-bindings bugfix

   - cdc-wdm log message spam bugfix

  All of these, except for the last cdc-wdm log level change, have been
  in linux-next for a while with no reported problems. The cdc-wdm
  bugfix has been tested by syzbot and proved to fix the reported cpu
  lockup issues when the log is constantly spammed by a broken device"

* tag 'usb-6.10-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb:
  USB: class: cdc-wdm: Fix CPU lockup caused by excessive log messages
  xhci: Handle TD clearing for multiple streams case
  xhci: Apply broken streams quirk to Etron EJ188 xHCI host
  xhci: Apply reset resume quirk to Etron EJ188 xHCI host
  xhci: Set correct transferred length for cancelled bulk transfers
  usb-storage: alauda: Check whether the media is initialized
  usb: typec: ucsi: Ack also failed Get Error commands
  kcov, usb: disable interrupts in kcov_remote_start_usb_softirq
  dt-bindings: usb: realtek,rts5411: Add missing "additionalProperties" on child nodes
  usb: typec: tcpm: Ignore received Hard Reset in TOGGLING state
  usb: typec: tcpm: fix use-after-free case in tcpm_register_source_caps
  USB: xen-hcd: Traverse host/ when CONFIG_USB_XEN_HCD is selected
  usb: typec: ucsi: glink: increase max ports for x1e80100
  Revert "usb: chipidea: move ci_ulpi_init after the phy initialization"
  thunderbolt: debugfs: Fix margin debugfs node creation condition
This commit is contained in:
Linus Torvalds 2024-06-16 11:20:26 -07:00
commit b5beaa4474
15 changed files with 131 additions and 42 deletions

View File

@ -65,6 +65,7 @@ patternProperties:
description: The hard wired USB devices
type: object
$ref: /schemas/usb/usb-device.yaml
additionalProperties: true
required:
- peer-hub

View File

@ -943,8 +943,9 @@ static void margining_port_init(struct tb_port *port)
debugfs_create_file("run", 0600, dir, port, &margining_run_fops);
debugfs_create_file("results", 0600, dir, port, &margining_results_fops);
debugfs_create_file("test", 0600, dir, port, &margining_test_fops);
if (independent_voltage_margins(usb4) ||
(supports_time(usb4) && independent_time_margins(usb4)))
if (independent_voltage_margins(usb4) == USB4_MARGIN_CAP_0_VOLTAGE_HL ||
(supports_time(usb4) &&
independent_time_margins(usb4) == USB4_MARGIN_CAP_1_TIME_LR))
debugfs_create_file("margin", 0600, dir, port, &margining_margin_fops);
}

View File

@ -35,6 +35,7 @@ obj-$(CONFIG_USB_R8A66597_HCD) += host/
obj-$(CONFIG_USB_FSL_USB2) += host/
obj-$(CONFIG_USB_FOTG210_HCD) += host/
obj-$(CONFIG_USB_MAX3421_HCD) += host/
obj-$(CONFIG_USB_XEN_HCD) += host/
obj-$(CONFIG_USB_C67X00_HCD) += c67x00/

View File

@ -1084,6 +1084,10 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
ret = ci_ulpi_init(ci);
if (ret)
return ret;
if (ci->platdata->phy) {
ci->phy = ci->platdata->phy;
} else if (ci->platdata->usb_phy) {
@ -1138,10 +1142,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
goto ulpi_exit;
}
ret = ci_ulpi_init(ci);
if (ret)
return ret;
ci->hw_bank.phys = res->start;
ci->irq = platform_get_irq(pdev, 0);

View File

@ -68,6 +68,11 @@ int ci_ulpi_init(struct ci_hdrc *ci)
if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
return 0;
/*
* Set PORTSC correctly so we can read/write ULPI registers for
* identification purposes
*/
hw_phymode_configure(ci);
ci->ulpi_ops.read = ci_ulpi_read;
ci->ulpi_ops.write = ci_ulpi_write;

View File

@ -266,14 +266,14 @@ static void wdm_int_callback(struct urb *urb)
dev_err(&desc->intf->dev, "Stall on int endpoint\n");
goto sw; /* halt is cleared in work */
default:
dev_err(&desc->intf->dev,
dev_err_ratelimited(&desc->intf->dev,
"nonzero urb status received: %d\n", status);
break;
}
}
if (urb->actual_length < sizeof(struct usb_cdc_notification)) {
dev_err(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
dev_err_ratelimited(&desc->intf->dev, "wdm_int_callback - %d bytes\n",
urb->actual_length);
goto exit;
}

View File

@ -1623,6 +1623,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
struct usb_anchor *anchor = urb->anchor;
int status = urb->unlinked;
unsigned long flags;
urb->hcpriv = NULL;
if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
@ -1640,13 +1641,14 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
/* pass ownership to the completion handler */
urb->status = status;
/*
* This function can be called in task context inside another remote
* coverage collection section, but kcov doesn't support that kind of
* recursion yet. Only collect coverage in softirq context for now.
* Only collect coverage in the softirq context and disable interrupts
* to avoid scenarios with nested remote coverage collection sections
* that KCOV does not support.
* See the comment next to kcov_remote_start_usb_softirq() for details.
*/
kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
flags = kcov_remote_start_usb_softirq((u64)urb->dev->bus->busnum);
urb->complete(urb);
kcov_remote_stop_softirq();
kcov_remote_stop_softirq(flags);
usb_anchor_resume_wakeups(anchor);
atomic_dec(&urb->use_count);

View File

@ -36,6 +36,7 @@
#define PCI_VENDOR_ID_ETRON 0x1b6f
#define PCI_DEVICE_ID_EJ168 0x7023
#define PCI_DEVICE_ID_EJ188 0x7052
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_XHCI 0x8c31
#define PCI_DEVICE_ID_INTEL_LYNXPOINT_LP_XHCI 0x9c31
@ -395,6 +396,12 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci->quirks |= XHCI_BROKEN_STREAMS;
}
if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
pdev->device == PCI_DEVICE_ID_EJ188) {
xhci->quirks |= XHCI_RESET_ON_RESUME;
xhci->quirks |= XHCI_BROKEN_STREAMS;
}
if (pdev->vendor == PCI_VENDOR_ID_RENESAS &&
pdev->device == 0x0014) {
xhci->quirks |= XHCI_ZERO_64B_REGS;

View File

@ -1031,13 +1031,27 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
break;
case TD_DIRTY: /* TD is cached, clear it */
case TD_HALTED:
case TD_CLEARING_CACHE_DEFERRED:
if (cached_td) {
if (cached_td->urb->stream_id != td->urb->stream_id) {
/* Multiple streams case, defer move dq */
xhci_dbg(xhci,
"Move dq deferred: stream %u URB %p\n",
td->urb->stream_id, td->urb);
td->cancel_status = TD_CLEARING_CACHE_DEFERRED;
break;
}
/* Should never happen, but clear the TD if it does */
xhci_warn(xhci,
"Found multiple active URBs %p and %p in stream %u?\n",
td->urb, cached_td->urb,
td->urb->stream_id);
td_to_noop(xhci, ring, cached_td, false);
cached_td->cancel_status = TD_CLEARED;
}
td->cancel_status = TD_CLEARING_CACHE;
if (cached_td)
/* FIXME stream case, several stopped rings */
xhci_dbg(xhci,
"Move dq past stream %u URB %p instead of stream %u URB %p\n",
td->urb->stream_id, td->urb,
cached_td->urb->stream_id, cached_td->urb);
cached_td = td;
break;
}
@ -1057,10 +1071,16 @@ static int xhci_invalidate_cancelled_tds(struct xhci_virt_ep *ep)
if (err) {
/* Failed to move past cached td, just set cached TDs to no-op */
list_for_each_entry_safe(td, tmp_td, &ep->cancelled_td_list, cancelled_td_list) {
if (td->cancel_status != TD_CLEARING_CACHE)
/*
* Deferred TDs need to have the deq pointer set after the above command
* completes, so if that failed we just give up on all of them (and
* complain loudly since this could cause issues due to caching).
*/
if (td->cancel_status != TD_CLEARING_CACHE &&
td->cancel_status != TD_CLEARING_CACHE_DEFERRED)
continue;
xhci_dbg(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
td->urb);
xhci_warn(xhci, "Failed to clear cancelled cached URB %p, mark clear anyway\n",
td->urb);
td_to_noop(xhci, ring, td, false);
td->cancel_status = TD_CLEARED;
}
@ -1346,6 +1366,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
struct xhci_ep_ctx *ep_ctx;
struct xhci_slot_ctx *slot_ctx;
struct xhci_td *td, *tmp_td;
bool deferred = false;
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
@ -1432,6 +1453,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
xhci_dbg(ep->xhci, "%s: Giveback cancelled URB %p TD\n",
__func__, td->urb);
xhci_td_cleanup(ep->xhci, td, ep_ring, td->status);
} else if (td->cancel_status == TD_CLEARING_CACHE_DEFERRED) {
deferred = true;
} else {
xhci_dbg(ep->xhci, "%s: Keep cancelled URB %p TD as cancel_status is %d\n",
__func__, td->urb, td->cancel_status);
@ -1441,8 +1464,17 @@ cleanup:
ep->ep_state &= ~SET_DEQ_PENDING;
ep->queued_deq_seg = NULL;
ep->queued_deq_ptr = NULL;
/* Restart any rings with pending URBs */
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
if (deferred) {
/* We have more streams to clear */
xhci_dbg(ep->xhci, "%s: Pending TDs to clear, continuing with invalidation\n",
__func__);
xhci_invalidate_cancelled_tds(ep);
} else {
/* Restart any rings with pending URBs */
xhci_dbg(ep->xhci, "%s: All TDs cleared, ring doorbell\n", __func__);
ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
}
}
static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
@ -2524,9 +2556,8 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
goto finish_td;
case COMP_STOPPED_LENGTH_INVALID:
/* stopped on ep trb with invalid length, exclude it */
ep_trb_len = 0;
remaining = 0;
break;
td->urb->actual_length = sum_trb_lengths(xhci, ep_ring, ep_trb);
goto finish_td;
case COMP_USB_TRANSACTION_ERROR:
if (xhci->quirks & XHCI_NO_SOFT_RETRY ||
(ep->err_count++ > MAX_SOFT_RETRY) ||

View File

@ -1276,6 +1276,7 @@ enum xhci_cancelled_td_status {
TD_DIRTY = 0,
TD_HALTED,
TD_CLEARING_CACHE,
TD_CLEARING_CACHE_DEFERRED,
TD_CLEARED,
};

View File

@ -105,6 +105,8 @@ struct alauda_info {
unsigned char sense_key;
unsigned long sense_asc; /* additional sense code */
unsigned long sense_ascq; /* additional sense code qualifier */
bool media_initialized;
};
#define short_pack(lsb,msb) ( ((u16)(lsb)) | ( ((u16)(msb))<<8 ) )
@ -476,11 +478,12 @@ static int alauda_check_media(struct us_data *us)
}
/* Check for media change */
if (status[0] & 0x08) {
if (status[0] & 0x08 || !info->media_initialized) {
usb_stor_dbg(us, "Media change detected\n");
alauda_free_maps(&MEDIA_INFO(us));
alauda_init_media(us);
rc = alauda_init_media(us);
if (rc == USB_STOR_TRANSPORT_GOOD)
info->media_initialized = true;
info->sense_key = UNIT_ATTENTION;
info->sense_asc = 0x28;
info->sense_ascq = 0x00;

View File

@ -3014,8 +3014,10 @@ static int tcpm_register_source_caps(struct tcpm_port *port)
memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps);
caps.role = TYPEC_SOURCE;
if (cap)
if (cap) {
usb_power_delivery_unregister_capabilities(cap);
port->partner_source_caps = NULL;
}
cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps);
if (IS_ERR(cap))
@ -6172,6 +6174,7 @@ static void _tcpm_pd_hard_reset(struct tcpm_port *port)
port->tcpc->set_bist_data(port->tcpc, false);
switch (port->state) {
case TOGGLING:
case ERROR_RECOVERY:
case PORT_RESET:
case PORT_RESET_WAIT_OFF:

View File

@ -153,8 +153,13 @@ static int ucsi_exec_command(struct ucsi *ucsi, u64 cmd)
}
if (cci & UCSI_CCI_ERROR) {
if (cmd == UCSI_GET_ERROR_STATUS)
if (cmd == UCSI_GET_ERROR_STATUS) {
ret = ucsi_acknowledge(ucsi, false);
if (ret)
return ret;
return -EIO;
}
return ucsi_read_error(ucsi);
}

View File

@ -14,7 +14,7 @@
#include <linux/soc/qcom/pmic_glink.h>
#include "ucsi.h"
#define PMIC_GLINK_MAX_PORTS 2
#define PMIC_GLINK_MAX_PORTS 3
#define UCSI_BUF_SIZE 48

View File

@ -55,21 +55,47 @@ static inline void kcov_remote_start_usb(u64 id)
/*
* The softirq flavor of kcov_remote_*() functions is introduced as a temporary
* work around for kcov's lack of nested remote coverage sections support in
* task context. Adding support for nested sections is tracked in:
* https://bugzilla.kernel.org/show_bug.cgi?id=210337
* workaround for KCOV's lack of nested remote coverage sections support.
*
* Adding support is tracked in https://bugzilla.kernel.org/show_bug.cgi?id=210337.
*
* kcov_remote_start_usb_softirq():
*
* 1. Only collects coverage when called in the softirq context. This allows
* avoiding nested remote coverage collection sections in the task context.
* For example, USB/IP calls usb_hcd_giveback_urb() in the task context
* within an existing remote coverage collection section. Thus, KCOV should
* not attempt to start collecting coverage within the coverage collection
* section in __usb_hcd_giveback_urb() in this case.
*
* 2. Disables interrupts for the duration of the coverage collection section.
* This allows avoiding nested remote coverage collection sections in the
* softirq context (a softirq might occur during the execution of a work in
* the BH workqueue, which runs with in_serving_softirq() > 0).
* For example, usb_giveback_urb_bh() runs in the BH workqueue with
* interrupts enabled, so __usb_hcd_giveback_urb() might be interrupted in
* the middle of its remote coverage collection section, and the interrupt
* handler might invoke __usb_hcd_giveback_urb() again.
*/
static inline void kcov_remote_start_usb_softirq(u64 id)
static inline unsigned long kcov_remote_start_usb_softirq(u64 id)
{
if (in_serving_softirq())
unsigned long flags = 0;
if (in_serving_softirq()) {
local_irq_save(flags);
kcov_remote_start_usb(id);
}
return flags;
}
static inline void kcov_remote_stop_softirq(void)
static inline void kcov_remote_stop_softirq(unsigned long flags)
{
if (in_serving_softirq())
if (in_serving_softirq()) {
kcov_remote_stop();
local_irq_restore(flags);
}
}
#ifdef CONFIG_64BIT
@ -103,8 +129,11 @@ static inline u64 kcov_common_handle(void)
}
static inline void kcov_remote_start_common(u64 id) {}
static inline void kcov_remote_start_usb(u64 id) {}
static inline void kcov_remote_start_usb_softirq(u64 id) {}
static inline void kcov_remote_stop_softirq(void) {}
static inline unsigned long kcov_remote_start_usb_softirq(u64 id)
{
return 0;
}
static inline void kcov_remote_stop_softirq(unsigned long flags) {}
#endif /* CONFIG_KCOV */
#endif /* _LINUX_KCOV_H */