public inbox for stable@vger.kernel.org
 help / color / mirror / Atom feed
From: Mathias Nyman <mathias.nyman@linux.intel.com>
To: Guan-Yu Lin <guanyulin@google.com>,
	gregkh@linuxfoundation.org, mathias.nyman@intel.com,
	perex@perex.cz, tiwai@suse.com, quic_wcheng@quicinc.com,
	broonie@kernel.org, arnd@arndb.de, xiaopei01@kylinos.cn,
	wesley.cheng@oss.qualcomm.com, hannelotta@gmail.com,
	sakari.ailus@linux.intel.com, eadavis@qq.com,
	stern@rowland.harvard.edu, amardeep.rai@intel.com,
	xu.yang_2@nxp.com, andriy.shevchenko@linux.intel.com,
	nkapron@google.com
Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-sound@vger.kernel.org, stable@vger.kernel.org
Subject: Re: [PATCH v3 1/2] usb: core: use dedicated spinlock for offload state
Date: Fri, 27 Mar 2026 01:58:34 +0200	[thread overview]
Message-ID: <bed5a8c0-2611-464a-bfd3-b00a8648727b@linux.intel.com> (raw)
In-Reply-To: <20260324203851.4091193-2-guanyulin@google.com>

Hi

On 3/24/26 22:38, Guan-Yu Lin wrote:
> Replace the coarse USB device lock with a dedicated offload_lock
> spinlock to reduce contention during offload operations. Use
> offload_pm_locked to synchronize with PM transitions and replace
> the legacy offload_at_suspend flag.
> 
> Optimize usb_offload_get/put by switching from auto-resume/suspend
> to pm_runtime_get_if_active(). This ensures offload state is only
> modified when the device is already active, avoiding unnecessary
> power transitions.
> 
> Cc: stable@vger.kernel.org
> Fixes: ef82a4803aab ("xhci: sideband: add api to trace sideband usage")
> Signed-off-by: Guan-Yu Lin <guanyulin@google.com>
> ---

> diff --git a/drivers/usb/core/offload.c b/drivers/usb/core/offload.c
> index 7c699f1b8d2b..c24945294d7e 100644
> --- a/drivers/usb/core/offload.c
> +++ b/drivers/usb/core/offload.c
> @@ -25,33 +25,30 @@
>    */
>   int usb_offload_get(struct usb_device *udev)
>   {
> -	int ret;
> +	int ret = 0;
>   
> -	usb_lock_device(udev);
> -	if (udev->state == USB_STATE_NOTATTACHED) {
> -		usb_unlock_device(udev);
> +	if (!usb_get_dev(udev))
>   		return -ENODEV;
> -	}
>   
> -	if (udev->state == USB_STATE_SUSPENDED ||
> -		   udev->offload_at_suspend) {
> -		usb_unlock_device(udev);
> -		return -EBUSY;
> +	if (pm_runtime_get_if_active(&udev->dev) != 1) {
> +		ret = -EBUSY;
> +		goto err_rpm;
>   	}
>   
> -	/*
> -	 * offload_usage could only be modified when the device is active, since
> -	 * it will alter the suspend flow of the device.
> -	 */
> -	ret = usb_autoresume_device(udev);
> -	if (ret < 0) {
> -		usb_unlock_device(udev);
> -		return ret;
> +	spin_lock(&udev->offload_lock);
> +
> +	if (udev->offload_pm_locked) {

Could we get rid of 'udev->offload_pm_locked' and 'usb_offload_set_pm_locked()'
by calling a synchronous pm_runtime_get_sync() or pm_runtime_resume_and_get()?

This way we can ensure udev->offload_usage isn't modified mid runtime suspend or
resume as resume is guaranteed to have finished and suspend won't be called,
at least not for the runtime case.

> +		ret = -EAGAIN;
> +		goto err;
>   	}
>   
>   	udev->offload_usage++;
> -	usb_autosuspend_device(udev);
> -	usb_unlock_device(udev);
> +
> +err:
> +	spin_unlock(&udev->offload_lock);
> +	pm_runtime_put_autosuspend(&udev->dev);
> +err_rpm:
> +	usb_put_dev(udev);
>   
>   	return ret;
>   }
> @@ -69,35 +66,32 @@ EXPORT_SYMBOL_GPL(usb_offload_get);
>    */
>   int usb_offload_put(struct usb_device *udev)
>   {
> -	int ret;
> +	int ret = 0;
>   
> -	usb_lock_device(udev);
> -	if (udev->state == USB_STATE_NOTATTACHED) {
> -		usb_unlock_device(udev);
> +	if (!usb_get_dev(udev))
>   		return -ENODEV;
> -	}
>   
> -	if (udev->state == USB_STATE_SUSPENDED ||
> -		   udev->offload_at_suspend) {
> -		usb_unlock_device(udev);
> -		return -EBUSY;
> +	if (pm_runtime_get_if_active(&udev->dev) != 1) {
> +		ret = -EBUSY;
> +		goto err_rpm;
>   	}
>   
> -	/*
> -	 * offload_usage could only be modified when the device is active, since
> -	 * it will alter the suspend flow of the device.
> -	 */
> -	ret = usb_autoresume_device(udev);
> -	if (ret < 0) {
> -		usb_unlock_device(udev);
> -		return ret;
> +	spin_lock(&udev->offload_lock);
> +
> +	if (udev->offload_pm_locked) {
> +		ret = -EAGAIN;
> +		goto err;


Ending up here is about unlucky timing, i.e. usb_offload_put() is called while
device is pretending to suspend/resume. Result here is that udev->offload_usage is
not decremented, and usb device won't properly suspend anymore even if device is
no longer offloaded.


>   	}
>   
>   	/* Drop the count when it wasn't 0, ignore the operation otherwise. */
>   	if (udev->offload_usage)
>   		udev->offload_usage--;
> -	usb_autosuspend_device(udev);
> -	usb_unlock_device(udev);
> +
> +err:
> +	spin_unlock(&udev->offload_lock);
> +	pm_runtime_put_autosuspend(&udev->dev);
> +err_rpm:
> +	usb_put_dev(udev);
>   
>   	return ret;
>   }
> @@ -112,25 +106,52 @@ EXPORT_SYMBOL_GPL(usb_offload_put);
>    * management.
>    *
>    * The caller must hold @udev's device lock. In addition, the caller should
> - * ensure downstream usb devices are all either suspended or marked as
> - * "offload_at_suspend" to ensure the correctness of the return value.
> + * ensure downstream usb devices are all marked as "offload_pm_locked" to
> + * ensure the correctness of the return value.
>    *
>    * Returns true on any offload activity, false otherwise.
>    */
>   bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex)
>   {
>   	struct usb_device *child;
> -	bool active;
> +	bool active = false;
>   	int port1;
>   
> +	spin_lock(&udev->offload_lock);
> +	if (udev->offload_usage)
> +		active = true;
> +	spin_unlock(&udev->offload_lock);
> +> +	if (active)
> +		return true;

Not sure what the purpose of the spinlock is above

> +
>   	usb_hub_for_each_child(udev, port1, child) {
>   		usb_lock_device(child);
>   		active = usb_offload_check(child);
>   		usb_unlock_device(child);
> +
>   		if (active)
> -			return true;
> +			break;
>   	}
>   
> -	return !!udev->offload_usage;
> +	return active;
>   }
>   EXPORT_SYMBOL_GPL(usb_offload_check);
> +
> +/**
> + * usb_offload_set_pm_locked - set the PM lock state of a USB device
> + * @udev: the USB device to modify
> + * @locked: the new lock state
> + *
> + * Setting @locked to true prevents offload_usage from being modified. This
> + * ensures that offload activities cannot be started or stopped during critical
> + * power management transitions, maintaining a stable state for the duration
> + * of the transition.
> + */
> +void usb_offload_set_pm_locked(struct usb_device *udev, bool locked)
> +{
> +	spin_lock(&udev->offload_lock);
> +	udev->offload_pm_locked = locked;
> +	spin_unlock(&udev->offload_lock);
> 

spinlock usage unclear here as well

Thanks
Mathias


  reply	other threads:[~2026-03-26 23:58 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20260324203851.4091193-1-guanyulin@google.com>
2026-03-24 20:38 ` [PATCH v3 1/2] usb: core: use dedicated spinlock for offload state Guan-Yu Lin
2026-03-26 23:58   ` Mathias Nyman [this message]
2026-03-24 20:38 ` [PATCH v3 2/2] usb: host: xhci-sideband: delegate offload_usage tracking to class drivers Guan-Yu Lin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=bed5a8c0-2611-464a-bfd3-b00a8648727b@linux.intel.com \
    --to=mathias.nyman@linux.intel.com \
    --cc=amardeep.rai@intel.com \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=arnd@arndb.de \
    --cc=broonie@kernel.org \
    --cc=eadavis@qq.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=guanyulin@google.com \
    --cc=hannelotta@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-sound@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mathias.nyman@intel.com \
    --cc=nkapron@google.com \
    --cc=perex@perex.cz \
    --cc=quic_wcheng@quicinc.com \
    --cc=sakari.ailus@linux.intel.com \
    --cc=stable@vger.kernel.org \
    --cc=stern@rowland.harvard.edu \
    --cc=tiwai@suse.com \
    --cc=wesley.cheng@oss.qualcomm.com \
    --cc=xiaopei01@kylinos.cn \
    --cc=xu.yang_2@nxp.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox