Re: [PATCH v2 3/4] Bluetooth: hci_h5: use device_schedule_reprobe()

Hans de Goede <[email protected]>
Newsgroups org.kernel.vger.linux-wireless,dev.linux.lists.driver-core,org.kernel.vger.linux-bluetooth,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Hi,

On 20-Aug-26 02:32, Daniel Golle wrote:
> h5_btrtl_resume() open-codes a deferred re-probe for RTL devices that
> lose their firmware state over suspend: it takes a module reference,
> allocates a work item, and the work function calls device_reprobe()
> and then ends with put_device(); kfree(); module_put(THIS_MODULE); in
> module text. That final module_put() is racy: once the reference
> count is decremented a concurrent rmmod can free the module text
> before the work function's epilogue has finished executing. The work
> also does not synchronize against shutdown or unbind, so a stale
> re-probe could undo an administrative unbind or detach a device whose
> ->shutdown() callback has already run.
> 
> Convert to the new device_schedule_reprobe() helper, whose work
> function is builtin text and which skips the re-probe when the device
> was removed, shutdown reached it, or it is no longer bound to the
> driver that scheduled the re-probe.
> 
> The old worker suppressed its error message for -EPROBE_DEFER; the
> helper needs no equivalent because its attach half is
> device_attach(), which folds probe deferral into the deferred-probe
> machinery silently.
> 
> Behavioral changes:
> 
> - A pending re-probe no longer pins the module: rmmod with a re-probe
>   pending now succeeds immediately and the re-probe becomes a no-op,
>   instead of rmmod failing with EBUSY.
> 
> - A re-probe scheduled before a system shutdown or before an
>   administrative unbind no longer detaches and rebinds the device
>   afterwards.
> 
> - A re-probe racing the next suspend now detaches immediately while
>   the probe is deferred until the following resume by the
>   defer_all_probes machinery, instead of probing mid-suspend.
> 
> Signed-off-by: Daniel Golle <[email protected]>

Thanks, this looks good to me and works as advertised on
a device where this code path gets hit:

Tested-by: Hans de Goede <[email protected]>
Reviewed-by: Hans de Goede <[email protected]>

Regards,

Hans



> ---
>  drivers/bluetooth/hci_h5.c | 43 ++++++--------------------------------
>  1 file changed, 6 insertions(+), 37 deletions(-)
> 
> diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
> index 93cdde981840..68eaa03a2005 100644
> --- a/drivers/bluetooth/hci_h5.c
> +++ b/drivers/bluetooth/hci_h5.c
> @@ -990,7 +990,7 @@ static int h5_btrtl_setup(struct h5 *h5)
>  static void h5_btrtl_open(struct h5 *h5)
>  {
>  	/*
> -	 * Since h5_btrtl_resume() does a device_reprobe() the suspend handling
> +	 * Since h5_btrtl_resume() schedules a device re-probe the suspend handling
>  	 * done by the hci_suspend_notifier is not necessary; it actually causes
>  	 * delays and a bunch of errors to get logged, so disable it.
>  	 */
> @@ -1047,46 +1047,15 @@ static int h5_btrtl_suspend(struct h5 *h5)
>  	return 0;
>  }
>  
> -struct h5_btrtl_reprobe {
> -	struct device *dev;
> -	struct work_struct work;
> -};
> -
> -static void h5_btrtl_reprobe_worker(struct work_struct *work)
> -{
> -	struct h5_btrtl_reprobe *reprobe =
> -		container_of(work, struct h5_btrtl_reprobe, work);
> -	int ret;
> -
> -	ret = device_reprobe(reprobe->dev);
> -	if (ret && ret != -EPROBE_DEFER)
> -		dev_err(reprobe->dev, "Reprobe error %d\n", ret);
> -
> -	put_device(reprobe->dev);
> -	kfree(reprobe);
> -	module_put(THIS_MODULE);
> -}
> -
>  static int h5_btrtl_resume(struct h5 *h5)
>  {
> -	if (test_bit(H5_WAKEUP_DISABLE, &h5->flags)) {
> -		struct h5_btrtl_reprobe *reprobe;
> -
> -		reprobe = kzalloc_obj(*reprobe);
> -		if (!reprobe)
> -			return -ENOMEM;
> -
> -		__module_get(THIS_MODULE);
> +	if (test_bit(H5_WAKEUP_DISABLE, &h5->flags))
> +		return device_schedule_reprobe(&h5->hu->serdev->dev, 0);
>  
> -		INIT_WORK(&reprobe->work, h5_btrtl_reprobe_worker);
> -		reprobe->dev = get_device(&h5->hu->serdev->dev);
> -		queue_work(system_long_wq, &reprobe->work);
> -	} else {
> -		gpiod_set_value_cansleep(h5->device_wake_gpio, 1);
> +	gpiod_set_value_cansleep(h5->device_wake_gpio, 1);
>  
> -		if (test_bit(H5_HW_FLOW_CONTROL, &h5->flags))
> -			serdev_device_set_flow_control(h5->hu->serdev, true);
> -	}
> +	if (test_bit(H5_HW_FLOW_CONTROL, &h5->flags))
> +		serdev_device_set_flow_control(h5->hu->serdev, true);
>  
>  	return 0;
>  }
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.