drivers/usb/core/hub.c | 21 ++++++++------------- drivers/usb/core/hub.h | 1 + 2 files changed, 9 insertions(+), 13 deletions(-)
From: Mathias Nyman <mathias.nyman@linux.intel.com>
commit 9bd9c8026341f75f25c53104eb7e656e357ca1a2 upstream
Delayed work that prevents USB3 hubs from runtime-suspending too early
needed to be flushed in hub_quiesce() to resolve issues detected on
QC SC8280XP CRD board during suspend resume testing.
This flushing did however trigger new issues on Raspberry Pi 3B+, which
doesn't have USB3 ports, and doesn't queue any post resume delayed work.
The flushed 'hub->init_work' item is used for several purposes, and
is originally initialized with a 'NULL' work function. The work function
is also changed on the fly, which may contribute to the issue.
Solve this by creating a dedicated delayed work item for post resume work,
and flush that delayed work in hub_quiesce()
Cc: stable <stable@kernel.org>
Fixes: a49e1e2e785f ("usb: hub: Fix flushing and scheduling of delayed work that tunes runtime pm")
Reported-by: Mark Brown <broonie@kernel.org>
Closes: https://lore.kernel.org/linux-usb/aF5rNp1l0LWITnEB@finisterre.sirena.org.uk
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Tested-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com> # SC8280XP CRD
Tested-by: Mark Brown <broonie@kernel.org>
Link: https://lore.kernel.org/r/20250627164348.3982628-2-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[florian: adjust for lack of hub_{get,put} and timer_delete_sync]
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
---
drivers/usb/core/hub.c | 21 ++++++++-------------
drivers/usb/core/hub.h | 1 +
2 files changed, 9 insertions(+), 13 deletions(-)
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 1a2039d1b342..63bb62680362 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1030,12 +1030,11 @@ int usb_remove_device(struct usb_device *udev)
enum hub_activation_type {
HUB_INIT, HUB_INIT2, HUB_INIT3, /* INITs must come first */
- HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME, HUB_POST_RESUME,
+ HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME,
};
static void hub_init_func2(struct work_struct *ws);
static void hub_init_func3(struct work_struct *ws);
-static void hub_post_resume(struct work_struct *ws);
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
@@ -1059,12 +1058,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
goto init3;
}
- if (type == HUB_POST_RESUME) {
- usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
- kref_put(&hub->kref, hub_release);
- return;
- }
-
kref_get(&hub->kref);
/* The superspeed hub except for root hub has to use Hub Depth
@@ -1318,8 +1311,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
usb_autopm_get_interface_no_resume(
to_usb_interface(hub->intfdev));
- INIT_DELAYED_WORK(&hub->init_work, hub_post_resume);
- queue_delayed_work(system_power_efficient_wq, &hub->init_work,
+ queue_delayed_work(system_power_efficient_wq,
+ &hub->post_resume_work,
msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
return;
}
@@ -1344,9 +1337,10 @@ static void hub_init_func3(struct work_struct *ws)
static void hub_post_resume(struct work_struct *ws)
{
- struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work);
+ struct usb_hub *hub = container_of(ws, struct usb_hub, post_resume_work.work);
- hub_activate(hub, HUB_POST_RESUME);
+ usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
+ kref_put(&hub->kref, hub_release);
}
enum hub_quiescing_type {
@@ -1374,7 +1368,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
/* Stop hub_wq and related activity */
del_timer_sync(&hub->irq_urb_retry);
- flush_delayed_work(&hub->init_work);
+ flush_delayed_work(&hub->post_resume_work);
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work_sync(&hub->leds);
@@ -1921,6 +1915,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
hub->hdev = hdev;
INIT_DELAYED_WORK(&hub->leds, led_work);
INIT_DELAYED_WORK(&hub->init_work, NULL);
+ INIT_DELAYED_WORK(&hub->post_resume_work, hub_post_resume);
INIT_WORK(&hub->events, hub_event);
spin_lock_init(&hub->irq_urb_lock);
timer_setup(&hub->irq_urb_retry, hub_retry_irq_urb, 0);
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 1c455800f7d3..de29ce856953 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -69,6 +69,7 @@ struct usb_hub {
u8 indicator[USB_MAXCHILDREN];
struct delayed_work leds;
struct delayed_work init_work;
+ struct delayed_work post_resume_work;
struct work_struct events;
spinlock_t irq_urb_lock;
struct timer_list irq_urb_retry;
--
2.34.1
© 2016 - 2025 Red Hat, Inc.