Commit 39236901 authored by Russell King's avatar Russell King Committed by Bryan Wu

leds: leds-pwm: properly clean up after probe failure

When probing with DT, we add each LED one at a time.  If we find a LED
without a PWM device (because it is not available yet) we fail the
initialisation, unregister previous LEDs, and then by way of managed
resources, we free the structure.

The problem with this is we may have a scheduled and active work_struct
in this structure, and this results in a nasty kernel oops.

We need to cancel this work_struct properly upon cleanup - and the
cleanup we require is the same cleanup as we do when the LED platform
device is removed.  Rather than writing this same code three times,
move it into a separate function and use it in all three places.

Fixes: c971ff18 ("leds: leds-pwm: Defer led_pwm_set() if PWM can sleep")
Signed-off-by: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: default avatarBryan Wu <cooloney@gmail.com>
parent 0016db26
...@@ -83,6 +83,15 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds) ...@@ -83,6 +83,15 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds)
(sizeof(struct led_pwm_data) * num_leds); (sizeof(struct led_pwm_data) * num_leds);
} }
static void led_pwm_cleanup(struct led_pwm_priv *priv)
{
while (priv->num_leds--) {
led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
if (priv->leds[priv->num_leds].can_sleep)
cancel_work_sync(&priv->leds[priv->num_leds].work);
}
}
static int led_pwm_create_of(struct platform_device *pdev, static int led_pwm_create_of(struct platform_device *pdev,
struct led_pwm_priv *priv) struct led_pwm_priv *priv)
{ {
...@@ -130,8 +139,7 @@ static int led_pwm_create_of(struct platform_device *pdev, ...@@ -130,8 +139,7 @@ static int led_pwm_create_of(struct platform_device *pdev,
return 0; return 0;
err: err:
while (priv->num_leds--) led_pwm_cleanup(priv);
led_classdev_unregister(&priv->leds[priv->num_leds].cdev);
return ret; return ret;
} }
...@@ -199,8 +207,8 @@ static int led_pwm_probe(struct platform_device *pdev) ...@@ -199,8 +207,8 @@ static int led_pwm_probe(struct platform_device *pdev)
return 0; return 0;
err: err:
while (i--) priv->num_leds = i;
led_classdev_unregister(&priv->leds[i].cdev); led_pwm_cleanup(priv);
return ret; return ret;
} }
...@@ -208,13 +216,8 @@ static int led_pwm_probe(struct platform_device *pdev) ...@@ -208,13 +216,8 @@ static int led_pwm_probe(struct platform_device *pdev)
static int led_pwm_remove(struct platform_device *pdev) static int led_pwm_remove(struct platform_device *pdev)
{ {
struct led_pwm_priv *priv = platform_get_drvdata(pdev); struct led_pwm_priv *priv = platform_get_drvdata(pdev);
int i;
for (i = 0; i < priv->num_leds; i++) { led_pwm_cleanup(priv);
led_classdev_unregister(&priv->leds[i].cdev);
if (priv->leds[i].can_sleep)
cancel_work_sync(&priv->leds[i].work);
}
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment