[1/2] iio: adc: sun4i-gpadc-iio: drop underflowing pm_runtime_put() calls

Message ID 20260906-gpadc-v1-1-92d3dc8ef355@gmail.com (mailing list archive)
State New
Headers
Series iio: adc: sun4i-gpadc-iio: two fixes |

Commit Message

Felix Gu Sept. 6, 2026, 3:58 p.m. UTC
Neither the error path in sun4i_gpadc_probe() nor sun4i_gpadc_remove()
ever holds a runtime PM usage count. So the pm_runtime_put() in both
places always triggers the "Runtime PM usage count underflow!" warning
on every failed probe and every unbind.

Drop both calls.

Fixes: d1caa9905538 ("iio: adc: add support for Allwinner SoCs ADC")
Signed-off-by: Felix Gu <ustc.gu@gmail.com>
---
 drivers/iio/adc/sun4i-gpadc-iio.c | 2 --
 1 file changed, 2 deletions(-)
  

Comments

sashiko-bot@kernel.org Sept. 6, 2026, 4:08 p.m. UTC | #1
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:

Pre-existing issues:
- [High] Unbalanced IRQ Disable on error path permanently breaks device interrupts.
- [High] Ignored pm_runtime_get_sync() return value leads to unsafe hardware access.
- [High] Race condition / use-after-PM-disable due to mixing manual PM disable with devres registration.
--

commit 800a3d2cff62e2654821d4421a37d86abb9c6de0
Author: Felix Gu <ustc.gu@gmail.com>

    iio: adc: sun4i-gpadc-iio: drop underflowing pm_runtime_put() calls
    
    Neither the error path in sun4i_gpadc_probe() nor sun4i_gpadc_remove()
    ever holds a runtime PM usage count. So the pm_runtime_put() in both
    places always triggers the "Runtime PM usage count underflow!" warning
    on every failed probe and every unbind.
    
    Drop both calls.

> diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
> index 203459ca99079..85382e435f2a7 100644
> --- a/drivers/iio/adc/sun4i-gpadc-iio.c
> +++ b/drivers/iio/adc/sun4i-gpadc-iio.c
[ ... ]
> @@ -668,7 +667,6 @@ static void sun4i_gpadc_remove(struct platform_device *pdev)
>  	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
>  	struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
>  
> -	pm_runtime_put(&pdev->dev);
>  	pm_runtime_disable(&pdev->dev);

[Severity: High]
This isn't a bug introduced by this patch, but does calling
pm_runtime_disable() here create a race condition?

The IIO device and thermal zone are registered using devres functions
(devm_iio_device_register / devm_thermal_of_zone_register) in
sun4i_gpadc_probe(), which means they are unregistered after
sun4i_gpadc_remove() completes.

Since pm_runtime_disable() is called before the devres-managed IIO device and
thermal zone are unregistered, the sysfs device and thermal zone remain fully
accessible while PM transitions are disabled.

Could a concurrent background poll from the thermal core or a userspace IIO
read during driver unbind trigger a pm_runtime_get_sync() that fails with
-EACCES, leading to an unclocked hardware crash?

This is another pre-existing issue, but does sun4i_gpadc_read() have an
unbalanced IRQ disable on its error path?

drivers/iio/adc/sun4i-gpadc-iio.c:sun4i_gpadc_read() {
    ...
	ret = sun4i_prepare_for_irq(indio_dev, channel, irq);
	if (ret)
		goto err;

	enable_irq(irq);
    ...
err:
	pm_runtime_put_autosuspend(indio_dev->dev.parent);
	disable_irq(irq);
	mutex_unlock(&info->mutex);

	return ret;
}

[Severity: High]
If sun4i_prepare_for_irq() returns an error, execution skips enable_irq()
but goes to the err: label, which unconditionally executes disable_irq().
Since the IRQ was requested with IRQF_NO_AUTOEN (disabled, depth=1), the
unmatched disable increments the depth to 2.

A subsequent successful read will call enable_irq(), only decrementing the
depth to 1, leaving the IRQ permanently disabled. Does a single transient
error during a userspace IIO read permanently break the hardware interface?

This is also a pre-existing issue, but should the return value of
pm_runtime_get_sync() be checked before accessing hardware registers?

drivers/iio/adc/sun4i-gpadc-iio.c:sun4i_prepare_for_irq() {
    ...
	pm_runtime_get_sync(indio_dev->dev.parent);

	reinit_completion(&info->completion);

	ret = regmap_write(info->regmap, SUN4I_GPADC_INT_FIFOC,
    ...
}

And in drivers/iio/adc/sun4i-gpadc-iio.c:sun4i_gpadc_temp_read():

	if (info->no_irq) {
		pm_runtime_get_sync(indio_dev->dev.parent);

		regmap_read(info->regmap, SUN4I_GPADC_TEMP_DATA, val);
    ...
}

[Severity: High]
pm_runtime_get_sync() can return a negative error code if the device fails to
resume or if runtime PM has been disabled. The return value is ignored before
issuing regmap_read() or regmap_write() calls.

If PM transitions fail or runtime PM is disabled, could this lead to accessing
unclocked or powered-off hardware, potentially resulting in system hangs or
bus faults?
  

Patch

diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index 203459ca9907..85382e435f2a 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -657,7 +657,6 @@  static int sun4i_gpadc_probe(struct platform_device *pdev)
 	if (!info->no_irq && IS_ENABLED(CONFIG_THERMAL_OF))
 		iio_map_array_unregister(indio_dev);
 
-	pm_runtime_put(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 
 	return ret;
@@ -668,7 +667,6 @@  static void sun4i_gpadc_remove(struct platform_device *pdev)
 	struct iio_dev *indio_dev = platform_get_drvdata(pdev);
 	struct sun4i_gpadc_iio *info = iio_priv(indio_dev);
 
-	pm_runtime_put(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 
 	if (!IS_ENABLED(CONFIG_THERMAL_OF))