Re: [PATCH 18/21] leds: aw2013: Unlock chip mutex before registering LEDs
From: Nikita Travkin
Date: Tue Oct 27 2026 - 10:29:59 EST
Lee Jones писал(а) 21.10.2026 17:32:
> aw2013_probe() holds chip->mutex across aw2013_probe_dt(), which calls
> devm_led_classdev_register_ext(). When a default trigger such as
> "timer" is configured on an LED in the Device Tree, the LED core
> synchronously activates the trigger during registration and invokes
> aw2013_blink_set() on the same thread, which attempts to acquire
> chip->mutex and deadlocks.
>
> Reset the chip, disable the regulators, and release chip->mutex in
> aw2013_probe() before calling aw2013_probe_dt(), and update
> chip->num_leds before each LED is registered so trigger callbacks can
> enable the chip cleanly during registration.
>
> Fixes: 59ea3c9faf32 ("leds: add aw2013 driver")
> Closes: https://sashiko.dev/#/bug/linux-5e2c9083-b95b-4aa4-aa0a-b0be8865ff12
> Cc: stable@xxxxxxxxxxxxxxx
> Signed-off-by: Lee Jones <lee@xxxxxxxxxx>
Reviewed-by: Nikita Travkin <nikita@xxxxxxx>
Thanks for making those fixes!
Nikita
> ---
> drivers/leds/leds-aw2013.c | 54 ++++++++++++++++++++++----------------
> 1 file changed, 32 insertions(+), 22 deletions(-)
>
> diff --git a/drivers/leds/leds-aw2013.c b/drivers/leds/leds-aw2013.c
> index dfeac8b2993f..c947cd068963 100644
> --- a/drivers/leds/leds-aw2013.c
> +++ b/drivers/leds/leds-aw2013.c
> @@ -271,8 +271,6 @@ static int aw2013_probe_dt(struct aw2013 *chip)
> if (!count || count > AW2013_MAX_LEDS)
> return -EINVAL;
>
> - regmap_write(chip->regmap, AW2013_RSTR, AW2013_RSTR_RESET);
> -
> for_each_available_child_of_node_scoped(np, child) {
> struct led_init_data init_data = {};
> u32 source;
> @@ -305,19 +303,29 @@ static int aw2013_probe_dt(struct aw2013 *chip)
> led->cdev.brightness_set_blocking = aw2013_brightness_set;
> led->cdev.blink_set = aw2013_blink_set;
>
> + mutex_lock(&chip->mutex);
> + chip->num_leds = ++i;
> + if (chip->enabled) {
> + ret = regmap_update_bits(chip->regmap,
> + AW2013_LCFG(led->num),
> + AW2013_LCFG_IMAX_MASK,
> + led->imax);
> + if (ret) {
> + mutex_unlock(&chip->mutex);
> + return ret;
> + }
> + }
> + mutex_unlock(&chip->mutex);
> +
> ret = devm_led_classdev_register_ext(&chip->client->dev,
> &led->cdev, &init_data);
> if (ret < 0)
> return ret;
> -
> - i++;
> }
>
> if (!count)
> return -EINVAL;
>
> - chip->num_leds = i;
> -
> return 0;
> }
>
> @@ -393,25 +401,27 @@ static int aw2013_probe(struct i2c_client *client)
> goto error_reg;
> }
>
> + ret = regmap_write(chip->regmap, AW2013_RSTR, AW2013_RSTR_RESET);
> + if (ret) {
> + dev_err(&client->dev, "Failed to reset chip: %d\n", ret);
> + goto error_reg;
> + }
> +
> + ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
> + chip->regulators);
> + if (ret) {
> + dev_err(&client->dev,
> + "Failed to disable regulators: %d\n", ret);
> + goto error;
> + }
> +
> + mutex_unlock(&chip->mutex);
> +
> ret = devm_add_action(&client->dev, aw2013_chip_disable_action, chip);
> if (ret)
> - goto error_reg;
> + return ret;
>
> - ret = aw2013_probe_dt(chip);
> - if (ret < 0)
> - goto error_reg;
> -
> - ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
> - chip->regulators);
> - if (ret) {
> - dev_err(&client->dev,
> - "Failed to disable regulators: %d\n", ret);
> - goto error;
> - }
> -
> - mutex_unlock(&chip->mutex);
> -
> - return 0;
> + return aw2013_probe_dt(chip);
>
> error_reg:
> regulator_bulk_disable(ARRAY_SIZE(chip->regulators),