From: Lee Jones <lee@kernel.org>
To: sashiko-reviews@lists.linux.dev
Cc: Paul Sajna <sajattack@postmarketos.org>,
linux-leds@vger.kernel.org, devicetree@vger.kernel.org,
robh@kernel.org, conor+dt@kernel.org
Subject: Re: [PATCH v3 5/6] leds: aw2013: Prevent writes to unpowered chip
Date: Tue, 20 Oct 2026 11:16:54 +0100 [thread overview]
Message-ID: <20261020101654.GF1206986@google.com> (raw)
In-Reply-To: <sashiko-outbox-169017@kernel.org>
On Tue, 13 Oct 2026, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
> - [High] leds: aw2013: Unconditional chip poweroff on I2C error breaks active LEDs
> - [Medium] leds: aw2013: lockless early return skips PWM clearing on shared regulators
> - [Medium] leds: aw2013: state desync if regulator_bulk_disable() fails
> - [Low] leds: aw2013: writes to unpowered chip in aw2013_blink_set()
> --
No more reviews until Sashko is clean or everything has been reasoned about.
[...]
> commit 6dcd66138416065d07df9a5558a059f701861e00
> Author: Paul Sajna <sajattack@postmarketos.org>
>
> leds: aw2013: Prevent writes to unpowered chip
>
> This patch replaces the chip_in_use check with a chip->enabled boolean in
> aw2013_brightness_set to prevent writing to the chip when it is unpowered.
> It also adds aw2013_chip_poweroff() to avoid sending I2C commands when
> powering on the chip fails.
>
> > Because aw2013_chip_enable() is skipped, the driver then unconditionally
> > calls regmap_write() and regmap_update_bits(), which perform I2C
> > transactions to the unpowered chip.
>
> [Severity: Low]
> Does this same issue still occur in aw2013_blink_set()?
>
> If a prior aw2013_brightness_set() call fails (e.g., regulator enable fails),
> cdev->brightness can remain > 0 while led->chip->enabled is false.
>
> A subsequent sysfs write to blink delays invokes aw2013_blink_set(), which
> sees brightness > 0, bypasses aw2013_brightness_set(), and unconditionally
> writes to the unpowered chip's registers:
>
> /* Never off - brightness is already set, disable blinking */
> if (!*delay_off) {
> ret = regmap_update_bits(led->chip->regmap, AW2013_LCFG(num),
> AW2013_LCFG_MD, 0);
> goto out;
> }
>
> [ ... ]
>
> > diff --git a/drivers/leds/leds-aw2013.c b/drivers/leds/leds-aw2013.c
> > index 3b2be960c04d5..6e661afd77755 100644
> > --- a/drivers/leds/leds-aw2013.c
> > +++ b/drivers/leds/leds-aw2013.c
> > @@ -193,15 +193,13 @@ static int aw2013_chip_init(struct aw2013 *chip)
> > return ret;
> > }
> >
> > -static void aw2013_chip_disable(struct aw2013 *chip)
> > +static void aw2013_chip_poweroff(struct aw2013 *chip)
> > {
> > int ret;
> >
> > if (!chip->enabled)
> > return;
> >
> > - regmap_write(chip->regmap, AW2013_GCR, 0);
> > -
> > ret = regulator_bulk_disable(ARRAY_SIZE(chip->regulators),
> > chip->regulators);
> > if (ret) {
> > dev_err(&chip->client->dev,
> > "Failed to disable regulators: %d\n", ret);
> > return;
> > }
> >
> > chip->enabled = false;
> > }
>
> [Severity: Medium]
> Could this early return cause a hardware state desynchronization if
> regulator_bulk_disable() fails?
>
> During aw2013_chip_disable(), the driver first writes 0 to AW2013_GCR to
> enter software standby, then calls aw2013_chip_poweroff(). If the regulator
> disable fails here, it returns early leaving chip->enabled = true.
>
> On subsequent brightness changes, aw2013_chip_enable() will see
> chip->enabled == true and skip aw2013_chip_init(). This skips restoring
> AW2013_GCR to 1, leaving the chip stuck in standby but completely
> unresponsive to brightness changes.
>
> [ ... ]
>
> > @@ -253,9 +261,12 @@ static int aw2013_brightness_set(struct led_classdev *cdev,
> > struct aw2013_led *led = container_of(cdev, struct aw2013_led, cdev);
> > int ret, num;
> >
> > + if (!led->chip->enabled && brightness == 0)
> > + return 0;
> > +
>
> [Severity: Medium]
> Can this lockless check introduce a TOCTOU race that skips clearing the
> LED's hardware registers?
>
> If two LEDs are on and both are set to 0 concurrently, the LED core updates
> cdev->brightness = 0 for both before scheduling their asynchronous works.
>
> The first worker executes aw2013_brightness_set(), acquires the mutex,
> clears its own PWM, and evaluates aw2013_chip_in_use(). Because both
> cdev->brightness values are now 0, aw2013_chip_in_use() returns false,
> causing the first worker to disable the chip (chip->enabled = false) and
> unlock the mutex.
>
> The second worker then evaluates this new lockless check, which is now
> true, and returns early without ever taking the lock or clearing its PWM
> register over I2C.
>
> When the regulators are shared with other devices, power is not physically
> cut, and the chip retains its register states in software standby. The next
> time the chip is enabled, will the second LED erroneously turn on because
> its registers were never cleared?
>
> > mutex_lock(&led->chip->mutex);
> >
> > - if (aw2013_chip_in_use(led->chip)) {
> > + if (!led->chip->enabled) {
> > ret = aw2013_chip_enable(led->chip);
> > if (ret)
> > goto error;
>
> [ ... ]
>
> > @@ -284,7 +295,12 @@ static int aw2013_brightness_set(struct led_classdev *cdev,
> > if (!aw2013_chip_in_use(led->chip))
> > aw2013_chip_disable(led->chip);
> >
> > + mutex_unlock(&led->chip->mutex);
> > +
> > + return ret;
> > +
> > error:
> > + aw2013_chip_poweroff(led->chip);
> > mutex_unlock(&led->chip->mutex);
> >
> > return ret;
>
> [Severity: High]
> Will this unconditional poweroff disrupt the hardware state of all other
> active LEDs on the same chip?
>
> If multiple LEDs are ON (so chip->enabled = true) and a transient I2C error
> occurs while changing the brightness of one LED, the code jumps to this
> error label.
>
> The new aw2013_chip_poweroff() call unconditionally drops power to the
> entire chip. This shuts down all other physically active LEDs without
> updating their cdev->brightness in the LED core.
>
> Should this error path only roll back aw2013_chip_enable() if it was the
> call that successfully enabled the chip on this specific invocation?
>
> --
> Sashiko AI review · https://sashiko.dev/#/patchset/20261012-aw2013-aw20xx-rename-v3-0-cab1fdd050b6@postmarketos.org?part=5
--
Lee Jones
next prev parent reply other threads:[~2026-10-20 10:16 UTC|newest]
Thread overview: 16+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-13 5:39 [PATCH v3 0/6] leds: aw2013: Add AW2027 support Paul Sajna
2026-10-13 5:39 ` [PATCH v3 1/6] dt-bindings: leds: aw2013: Add awinic,aw2027 compatible Paul Sajna
2026-10-13 5:43 ` sashiko-bot
2026-10-13 6:31 ` Krzysztof Kozlowski
2026-10-13 5:39 ` [PATCH v3 2/6] leds: aw2013: Add AW2027 support Paul Sajna
2026-10-13 5:53 ` sashiko-bot
2026-10-20 10:16 ` Lee Jones
2026-10-13 5:39 ` [PATCH v3 3/6] leds: aw2013: Use dev_err_probe Paul Sajna
2026-10-13 5:52 ` sashiko-bot
2026-10-13 5:39 ` [PATCH v3 4/6] leds: aw2013: Move assignment of chip->num_leds Paul Sajna
2026-10-13 5:57 ` sashiko-bot
2026-10-13 5:39 ` [PATCH v3 5/6] leds: aw2013: Prevent writes to unpowered chip Paul Sajna
2026-10-13 5:53 ` sashiko-bot
2026-10-20 10:16 ` Lee Jones [this message]
2026-10-13 5:40 ` [PATCH v3 6/6] leds: aw2013: Move reset register write Paul Sajna
2026-10-13 5:46 ` sashiko-bot
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=20261020101654.GF1206986@google.com \
--to=lee@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=linux-leds@vger.kernel.org \
--cc=robh@kernel.org \
--cc=sajattack@postmarketos.org \
--cc=sashiko-reviews@lists.linux.dev \
/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