usb: hub: Fix flushing of delayed work used for post resume purposes
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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
71f5c98d29
commit
668c7b47a5
@@ -1044,12 +1044,11 @@ int usb_remove_device(struct usb_device *udev)
|
|||||||
|
|
||||||
enum hub_activation_type {
|
enum hub_activation_type {
|
||||||
HUB_INIT, HUB_INIT2, HUB_INIT3, /* INITs must come first */
|
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_func2(struct work_struct *ws);
|
||||||
static void hub_init_func3(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)
|
static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
||||||
{
|
{
|
||||||
@@ -1073,12 +1072,6 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||||||
goto init3;
|
goto init3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == HUB_POST_RESUME) {
|
|
||||||
usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
|
|
||||||
hub_put(hub);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
hub_get(hub);
|
hub_get(hub);
|
||||||
|
|
||||||
/* The superspeed hub except for root hub has to use Hub Depth
|
/* The superspeed hub except for root hub has to use Hub Depth
|
||||||
@@ -1332,8 +1325,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
|||||||
usb_autopm_get_interface_no_resume(
|
usb_autopm_get_interface_no_resume(
|
||||||
to_usb_interface(hub->intfdev));
|
to_usb_interface(hub->intfdev));
|
||||||
|
|
||||||
INIT_DELAYED_WORK(&hub->init_work, hub_post_resume);
|
queue_delayed_work(system_power_efficient_wq,
|
||||||
queue_delayed_work(system_power_efficient_wq, &hub->init_work,
|
&hub->post_resume_work,
|
||||||
msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
|
msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1358,9 +1351,10 @@ static void hub_init_func3(struct work_struct *ws)
|
|||||||
|
|
||||||
static void hub_post_resume(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));
|
||||||
|
hub_put(hub);
|
||||||
}
|
}
|
||||||
|
|
||||||
enum hub_quiescing_type {
|
enum hub_quiescing_type {
|
||||||
@@ -1388,7 +1382,7 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
|
|||||||
|
|
||||||
/* Stop hub_wq and related activity */
|
/* Stop hub_wq and related activity */
|
||||||
del_timer_sync(&hub->irq_urb_retry);
|
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);
|
usb_kill_urb(hub->urb);
|
||||||
if (hub->has_indicators)
|
if (hub->has_indicators)
|
||||||
cancel_delayed_work_sync(&hub->leds);
|
cancel_delayed_work_sync(&hub->leds);
|
||||||
@@ -1947,6 +1941,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
|
|||||||
hub->hdev = hdev;
|
hub->hdev = hdev;
|
||||||
INIT_DELAYED_WORK(&hub->leds, led_work);
|
INIT_DELAYED_WORK(&hub->leds, led_work);
|
||||||
INIT_DELAYED_WORK(&hub->init_work, NULL);
|
INIT_DELAYED_WORK(&hub->init_work, NULL);
|
||||||
|
INIT_DELAYED_WORK(&hub->post_resume_work, hub_post_resume);
|
||||||
INIT_WORK(&hub->events, hub_event);
|
INIT_WORK(&hub->events, hub_event);
|
||||||
INIT_LIST_HEAD(&hub->onboard_hub_devs);
|
INIT_LIST_HEAD(&hub->onboard_hub_devs);
|
||||||
spin_lock_init(&hub->irq_urb_lock);
|
spin_lock_init(&hub->irq_urb_lock);
|
||||||
|
@@ -69,6 +69,7 @@ struct usb_hub {
|
|||||||
u8 indicator[USB_MAXCHILDREN];
|
u8 indicator[USB_MAXCHILDREN];
|
||||||
struct delayed_work leds;
|
struct delayed_work leds;
|
||||||
struct delayed_work init_work;
|
struct delayed_work init_work;
|
||||||
|
struct delayed_work post_resume_work;
|
||||||
struct work_struct events;
|
struct work_struct events;
|
||||||
spinlock_t irq_urb_lock;
|
spinlock_t irq_urb_lock;
|
||||||
struct timer_list irq_urb_retry;
|
struct timer_list irq_urb_retry;
|
||||||
|
Reference in New Issue
Block a user