diff mbox series

[2/4] xhci: turn off port power in shutdown

Message ID 20220623111945.1557702-3-mathias.nyman@linux.intel.com (mailing list archive)
State Superseded
Commit 83810f84ecf11dfc5a9414a8b762c3501b328185
Headers show
Series xhci fixes for usb-linus | expand

Commit Message

Mathias Nyman June 23, 2022, 11:19 a.m. UTC
If ports are not turned off in shutdown then runtime suspended
self-powered USB devices may survive in U3 link state over S5.

During subsequent boot, if firmware sends an IPC command to program
the port in DISCONNECT state, it will time out, causing significant
delay in the boot time.

Turning off roothub port power is also recommended in xhci
specification 4.19.4 "Port Power" in the additional note.

Cc: stable@vger.kernel.org
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
 drivers/usb/host/xhci-hub.c |  2 +-
 drivers/usb/host/xhci.c     | 15 +++++++++++++--
 drivers/usb/host/xhci.h     |  2 ++
 3 files changed, 16 insertions(+), 3 deletions(-)

Comments

Joey Corleone July 19, 2022, 1:42 p.m. UTC | #1
On 23.06.22 13:19, Mathias Nyman wrote:
> If ports are not turned off in shutdown then runtime suspended
> self-powered USB devices may survive in U3 link state over S5.
> 
> During subsequent boot, if firmware sends an IPC command to program
> the port in DISCONNECT state, it will time out, causing significant
> delay in the boot time.
> 
> Turning off roothub port power is also recommended in xhci
> specification 4.19.4 "Port Power" in the additional note.
> 
> Cc: stable@vger.kernel.org
> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
> ---
>   drivers/usb/host/xhci-hub.c |  2 +-
>   drivers/usb/host/xhci.c     | 15 +++++++++++++--
>   drivers/usb/host/xhci.h     |  2 ++
>   3 files changed, 16 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
> index c54f2bc23d3f..0fdc014c9401 100644
> --- a/drivers/usb/host/xhci-hub.c
> +++ b/drivers/usb/host/xhci-hub.c
> @@ -652,7 +652,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
>    * It will release and re-aquire the lock while calling ACPI
>    * method.
>    */
> -static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
> +void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
>   				u16 index, bool on, unsigned long *flags)
>   	__must_hold(&xhci->lock)
>   {
> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
> index cb99bed5f755..65858f607437 100644
> --- a/drivers/usb/host/xhci.c
> +++ b/drivers/usb/host/xhci.c
> @@ -791,6 +791,8 @@ static void xhci_stop(struct usb_hcd *hcd)
>   void xhci_shutdown(struct usb_hcd *hcd)
>   {
>   	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
> +	unsigned long flags;
> +	int i;
>   
>   	if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
>   		usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
> @@ -806,12 +808,21 @@ void xhci_shutdown(struct usb_hcd *hcd)
>   		del_timer_sync(&xhci->shared_hcd->rh_timer);
>   	}
>   
> -	spin_lock_irq(&xhci->lock);
> +	spin_lock_irqsave(&xhci->lock, flags);
>   	xhci_halt(xhci);
> +
> +	/* Power off USB2 ports*/
> +	for (i = 0; i < xhci->usb2_rhub.num_ports; i++)
> +		xhci_set_port_power(xhci, xhci->main_hcd, i, false, &flags);
> +
> +	/* Power off USB3 ports*/
> +	for (i = 0; i < xhci->usb3_rhub.num_ports; i++)
> +		xhci_set_port_power(xhci, xhci->shared_hcd, i, false, &flags);
> +
>   	/* Workaround for spurious wakeups at shutdown with HSW */
>   	if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
>   		xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
> -	spin_unlock_irq(&xhci->lock);
> +	spin_unlock_irqrestore(&xhci->lock, flags);
>   
>   	xhci_cleanup_msix(xhci);
>   
> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
> index 0bd76c94a4b1..28aaf031f9a8 100644
> --- a/drivers/usb/host/xhci.h
> +++ b/drivers/usb/host/xhci.h
> @@ -2196,6 +2196,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
>   int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
>   int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
>   struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
> +void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, u16 index,
> +			 bool on, unsigned long *flags);
>   
>   void xhci_hc_died(struct xhci_hcd *xhci);
>   

fyi: there is a report [1] about a regression introduced by this patch.

Joey

[1] https://bugzilla.kernel.org/show_bug.cgi?id=216243
Mathias Nyman July 19, 2022, 4:49 p.m. UTC | #2
On 19.7.2022 16.42, Joey Corleone wrote:
> On 23.06.22 13:19, Mathias Nyman wrote:
>> If ports are not turned off in shutdown then runtime suspended
>> self-powered USB devices may survive in U3 link state over S5.
>>
>> During subsequent boot, if firmware sends an IPC command to program
>> the port in DISCONNECT state, it will time out, causing significant
>> delay in the boot time.
>>
>> Turning off roothub port power is also recommended in xhci
>> specification 4.19.4 "Port Power" in the additional note.
>>
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
>> ---
>>   drivers/usb/host/xhci-hub.c |  2 +-
>>   drivers/usb/host/xhci.c     | 15 +++++++++++++--
>>   drivers/usb/host/xhci.h     |  2 ++
>>   3 files changed, 16 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
>> index c54f2bc23d3f..0fdc014c9401 100644
>> --- a/drivers/usb/host/xhci-hub.c
>> +++ b/drivers/usb/host/xhci-hub.c
>> @@ -652,7 +652,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
>>    * It will release and re-aquire the lock while calling ACPI
>>    * method.
>>    */
>> -static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
>> +void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
>>                   u16 index, bool on, unsigned long *flags)
>>       __must_hold(&xhci->lock)
>>   {
>> diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
>> index cb99bed5f755..65858f607437 100644
>> --- a/drivers/usb/host/xhci.c
>> +++ b/drivers/usb/host/xhci.c
>> @@ -791,6 +791,8 @@ static void xhci_stop(struct usb_hcd *hcd)
>>   void xhci_shutdown(struct usb_hcd *hcd)
>>   {
>>       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
>> +    unsigned long flags;
>> +    int i;
>>       if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
>>           usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
>> @@ -806,12 +808,21 @@ void xhci_shutdown(struct usb_hcd *hcd)
>>           del_timer_sync(&xhci->shared_hcd->rh_timer);
>>       }
>> -    spin_lock_irq(&xhci->lock);
>> +    spin_lock_irqsave(&xhci->lock, flags);
>>       xhci_halt(xhci);
>> +
>> +    /* Power off USB2 ports*/
>> +    for (i = 0; i < xhci->usb2_rhub.num_ports; i++)
>> +        xhci_set_port_power(xhci, xhci->main_hcd, i, false, &flags);
>> +
>> +    /* Power off USB3 ports*/
>> +    for (i = 0; i < xhci->usb3_rhub.num_ports; i++)
>> +        xhci_set_port_power(xhci, xhci->shared_hcd, i, false, &flags);
>> +
>>       /* Workaround for spurious wakeups at shutdown with HSW */
>>       if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
>>           xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
>> -    spin_unlock_irq(&xhci->lock);
>> +    spin_unlock_irqrestore(&xhci->lock, flags);
>>       xhci_cleanup_msix(xhci);
>> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
>> index 0bd76c94a4b1..28aaf031f9a8 100644
>> --- a/drivers/usb/host/xhci.h
>> +++ b/drivers/usb/host/xhci.h
>> @@ -2196,6 +2196,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
>>   int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
>>   int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
>>   struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
>> +void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, u16 index,
>> +             bool on, unsigned long *flags);
>>   void xhci_hc_died(struct xhci_hcd *xhci);
> 
> fyi: there is a report [1] about a regression introduced by this patch.
> 
> Joey
> 
> [1] https://bugzilla.kernel.org/show_bug.cgi?id=216243

Thanks for the notice, I'm away until August.
If it starts to cause more issues then this one incident then it should be reverted.

Will take a look at this when I return.
Meanwhile a log with xhci dynamic debug enabled could better show what is going on

mount -t debugfs none /sys/kernel/debug
echo 'module xhci_hcd =p' >/sys/kernel/debug/dynamic_debug/control

Thanks
Mathias
diff mbox series

Patch

diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index c54f2bc23d3f..0fdc014c9401 100644
--- a/drivers/usb/host/xhci-hub.c
+++ b/drivers/usb/host/xhci-hub.c
@@ -652,7 +652,7 @@  struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
  * It will release and re-aquire the lock while calling ACPI
  * method.
  */
-static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
+void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
 				u16 index, bool on, unsigned long *flags)
 	__must_hold(&xhci->lock)
 {
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index cb99bed5f755..65858f607437 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -791,6 +791,8 @@  static void xhci_stop(struct usb_hcd *hcd)
 void xhci_shutdown(struct usb_hcd *hcd)
 {
 	struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+	unsigned long flags;
+	int i;
 
 	if (xhci->quirks & XHCI_SPURIOUS_REBOOT)
 		usb_disable_xhci_ports(to_pci_dev(hcd->self.sysdev));
@@ -806,12 +808,21 @@  void xhci_shutdown(struct usb_hcd *hcd)
 		del_timer_sync(&xhci->shared_hcd->rh_timer);
 	}
 
-	spin_lock_irq(&xhci->lock);
+	spin_lock_irqsave(&xhci->lock, flags);
 	xhci_halt(xhci);
+
+	/* Power off USB2 ports*/
+	for (i = 0; i < xhci->usb2_rhub.num_ports; i++)
+		xhci_set_port_power(xhci, xhci->main_hcd, i, false, &flags);
+
+	/* Power off USB3 ports*/
+	for (i = 0; i < xhci->usb3_rhub.num_ports; i++)
+		xhci_set_port_power(xhci, xhci->shared_hcd, i, false, &flags);
+
 	/* Workaround for spurious wakeups at shutdown with HSW */
 	if (xhci->quirks & XHCI_SPURIOUS_WAKEUP)
 		xhci_reset(xhci, XHCI_RESET_SHORT_USEC);
-	spin_unlock_irq(&xhci->lock);
+	spin_unlock_irqrestore(&xhci->lock, flags);
 
 	xhci_cleanup_msix(xhci);
 
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 0bd76c94a4b1..28aaf031f9a8 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -2196,6 +2196,8 @@  int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex,
 int xhci_hub_status_data(struct usb_hcd *hcd, char *buf);
 int xhci_find_raw_port_number(struct usb_hcd *hcd, int port1);
 struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd);
+void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd, u16 index,
+			 bool on, unsigned long *flags);
 
 void xhci_hc_died(struct xhci_hcd *xhci);