* [PATCH 0/2] leds: use brightness_set_blocking for sleepable callbacks @ 2026-06-15 14:57 Runyu Xiao 2026-06-15 14:57 ` [PATCH 1/2] leds: lm3530: use brightness_set_blocking for sleepable callback Runyu Xiao 2026-06-15 14:57 ` [PATCH 2/2] leds: menf21bmc: " Runyu Xiao 0 siblings, 2 replies; 5+ messages in thread From: Runyu Xiao @ 2026-06-15 14:57 UTC (permalink / raw) To: Lee Jones, Pavel Machek, linux-leds Cc: Andreas Werner, Andrew Morton, Shreshtha Kumar Sahu, Bryan Wu, linux-kernel, jianhao.xu, runyu.xiao This 2-patch series covers two LED callback contract mismatches that were found by our static analysis tool and then manually reviewed against the current tree. Both reports preserve the same grounded issue: the callback may sleep because it takes a mutex or performs I2C transfers, but the driver still exposes it as brightness_set. The corresponding PoCs keep the original callback names and transport paths, and Lockdep reproduces the same atomic-sleep class in both cases. Patch 1 updates lm3530. Patch 2 updates menf21bmc. Both patches are build-tested and checkpatch-clean. I do not have the LM3530 or MEN 14F021P00 BMC hardware for runtime testing. Runyu Xiao (2): leds: lm3530: use brightness_set_blocking for sleepable callback leds: menf21bmc: use brightness_set_blocking for sleepable callback drivers/leds/leds-lm3530.c | 12 ++++++++---- drivers/leds/leds-menf21bmc.c | 18 ++++++++++++------ 2 files changed, 20 insertions(+), 10 deletions(-) -- 2.34.1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] leds: lm3530: use brightness_set_blocking for sleepable callback 2026-06-15 14:57 [PATCH 0/2] leds: use brightness_set_blocking for sleepable callbacks Runyu Xiao @ 2026-06-15 14:57 ` Runyu Xiao 2026-07-02 10:20 ` Lee Jones 2026-06-15 14:57 ` [PATCH 2/2] leds: menf21bmc: " Runyu Xiao 1 sibling, 1 reply; 5+ messages in thread From: Runyu Xiao @ 2026-06-15 14:57 UTC (permalink / raw) To: Lee Jones, Pavel Machek, linux-leds Cc: Andreas Werner, Andrew Morton, Shreshtha Kumar Sahu, Bryan Wu, linux-kernel, jianhao.xu, runyu.xiao, stable lm3530_brightness_set() talks to the device over I2C and can sleep, but the driver registers it as brightness_set. That leaves the LED core free to invoke the callback from atomic contexts. This issue was found by our static analysis tool and then manually reviewed against the current tree. A minimal Lockdep reproducer that keeps the original registration and call chain is enough to trigger the warning: lm3530_probe() still publishes lm3530_brightness_set() as brightness_set, led_trigger_event_atomic() invokes it under spin_lock_irqsave(), and the callback reaches i2c_smbus_write_byte_data() as its first sleepable edge. Lockdep reports: BUG: sleeping function called from invalid context i2c_smbus_write_byte_data.constprop.0+0x14/0x30 [vuln_msv] lm3530_brightness_set+0x4e/0x66 [vuln_msv] led_trigger_event_atomic.constprop.0+0x2b/0x40 [vuln_msv] Convert the callback to brightness_set_blocking so the LED core only invokes it from a sleepable context. Fixes: b1e6b7068f02 ("leds: add driver for LM3530 ALS") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn> --- Notes: - Validated with a grounded Lockdep PoC that preserves the lm3530_probe() brightness_set registration and the led_trigger_event_atomic() -> lm3530_brightness_set() -> i2c_smbus_write_byte_data() path. - checkpatch.pl --strict: clean. - Not tested on LM3530 hardware. drivers/leds/leds-lm3530.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c index e44a3db106c3..ba744961ebcd 100644 --- a/drivers/leds/leds-lm3530.c +++ b/drivers/leds/leds-lm3530.c @@ -301,10 +301,11 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) return ret; } -static void lm3530_brightness_set(struct led_classdev *led_cdev, - enum led_brightness brt_val) +static int +lm3530_brightness_set_blocking(struct led_classdev *led_cdev, + enum led_brightness brt_val) { - int err; + int err = 0; struct lm3530_data *drvdata = container_of(led_cdev, struct lm3530_data, led_dev); struct lm3530_platform_data *pdata = drvdata->pdata; @@ -344,6 +345,8 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev, default: break; } + + return err; } static ssize_t mode_show(struct device *dev, @@ -438,7 +441,8 @@ static int lm3530_probe(struct i2c_client *client) drvdata->brightness = LED_OFF; drvdata->enable = false; drvdata->led_dev.name = LM3530_LED_DEV; - drvdata->led_dev.brightness_set = lm3530_brightness_set; + drvdata->led_dev.brightness_set_blocking = + lm3530_brightness_set_blocking; drvdata->led_dev.max_brightness = MAX_BRIGHTNESS; drvdata->led_dev.groups = lm3530_groups; -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 1/2] leds: lm3530: use brightness_set_blocking for sleepable callback 2026-06-15 14:57 ` [PATCH 1/2] leds: lm3530: use brightness_set_blocking for sleepable callback Runyu Xiao @ 2026-07-02 10:20 ` Lee Jones 0 siblings, 0 replies; 5+ messages in thread From: Lee Jones @ 2026-07-02 10:20 UTC (permalink / raw) To: Runyu Xiao Cc: Pavel Machek, linux-leds, Andreas Werner, Andrew Morton, Shreshtha Kumar Sahu, Bryan Wu, linux-kernel, jianhao.xu, stable On Mon, 15 Jun 2026, Runyu Xiao wrote: > lm3530_brightness_set() talks to the device over I2C and can sleep, but > the driver registers it as brightness_set. That leaves the LED core > free to invoke the callback from atomic contexts. > > This issue was found by our static analysis tool and then manually > reviewed against the current tree. > > A minimal Lockdep reproducer that keeps the original registration and > call chain is enough to trigger the warning: lm3530_probe() still > publishes lm3530_brightness_set() as brightness_set, > led_trigger_event_atomic() invokes it under spin_lock_irqsave(), and > the callback reaches i2c_smbus_write_byte_data() as its first > sleepable edge. > > Lockdep reports: > > BUG: sleeping function called from invalid context > i2c_smbus_write_byte_data.constprop.0+0x14/0x30 [vuln_msv] > lm3530_brightness_set+0x4e/0x66 [vuln_msv] > led_trigger_event_atomic.constprop.0+0x2b/0x40 [vuln_msv] > > Convert the callback to brightness_set_blocking so the LED core only > invokes it from a sleepable context. > > Fixes: b1e6b7068f02 ("leds: add driver for LM3530 ALS") > Cc: stable@vger.kernel.org > Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn> > --- > Notes: > - Validated with a grounded Lockdep PoC that preserves the > lm3530_probe() brightness_set registration and the > led_trigger_event_atomic() -> lm3530_brightness_set() -> > i2c_smbus_write_byte_data() path. > - checkpatch.pl --strict: clean. > - Not tested on LM3530 hardware. > > drivers/leds/leds-lm3530.c | 12 ++++++++---- > 1 file changed, 8 insertions(+), 4 deletions(-) > > diff --git a/drivers/leds/leds-lm3530.c b/drivers/leds/leds-lm3530.c > index e44a3db106c3..ba744961ebcd 100644 > --- a/drivers/leds/leds-lm3530.c > +++ b/drivers/leds/leds-lm3530.c > @@ -301,10 +301,11 @@ static int lm3530_init_registers(struct lm3530_data *drvdata) > return ret; > } > > -static void lm3530_brightness_set(struct led_classdev *led_cdev, > - enum led_brightness brt_val) > +static int > +lm3530_brightness_set_blocking(struct led_classdev *led_cdev, > + enum led_brightness brt_val) > { > - int err; > + int err = 0; > struct lm3530_data *drvdata = > container_of(led_cdev, struct lm3530_data, led_dev); > struct lm3530_platform_data *pdata = drvdata->pdata; > @@ -344,6 +345,8 @@ static void lm3530_brightness_set(struct led_classdev *led_cdev, > default: > break; > } > + > + return err; What makes you think that the original author didn't want these failures to be non-catastrophic? > } > > static ssize_t mode_show(struct device *dev, > @@ -438,7 +441,8 @@ static int lm3530_probe(struct i2c_client *client) > drvdata->brightness = LED_OFF; > drvdata->enable = false; > drvdata->led_dev.name = LM3530_LED_DEV; > - drvdata->led_dev.brightness_set = lm3530_brightness_set; > + drvdata->led_dev.brightness_set_blocking = > + lm3530_brightness_set_blocking; We've had no complaints about this in the 15-years it's been operational. I think it's safe to conclude that this isn't causes anyone issues. I'd be concerned that these changes, although seemingly correct on the surface, may cause problems for users, which would be unacceptable. > drvdata->led_dev.max_brightness = MAX_BRIGHTNESS; > drvdata->led_dev.groups = lm3530_groups; > > -- > 2.34.1 -- Lee Jones ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/2] leds: menf21bmc: use brightness_set_blocking for sleepable callback 2026-06-15 14:57 [PATCH 0/2] leds: use brightness_set_blocking for sleepable callbacks Runyu Xiao 2026-06-15 14:57 ` [PATCH 1/2] leds: lm3530: use brightness_set_blocking for sleepable callback Runyu Xiao @ 2026-06-15 14:57 ` Runyu Xiao 2026-07-02 10:45 ` Lee Jones 1 sibling, 1 reply; 5+ messages in thread From: Runyu Xiao @ 2026-06-15 14:57 UTC (permalink / raw) To: Lee Jones, Pavel Machek, linux-leds Cc: Andreas Werner, Andrew Morton, Shreshtha Kumar Sahu, Bryan Wu, linux-kernel, jianhao.xu, runyu.xiao, stable menf21bmc_led_set() serializes access with a mutex and performs I2C transfers, but the driver exposes it through brightness_set. That mismatches the LED core callback contract for atomic callers. This issue was found by our static analysis tool and then manually reviewed against the current tree. A minimal Lockdep reproducer that keeps the original registration and call chain is enough to trigger the warning: menf21bmc_led_probe() still publishes menf21bmc_led_set() as brightness_set, led_trigger_event_atomic() invokes it under spin_lock_irqsave(), and the callback immediately tries mutex_lock(&led_lock) before reaching its I2C accesses. Lockdep reports: BUG: sleeping function called from invalid context __mutex_lock+0x4f/0xd20 menf21bmc_led_set+0x15/0x49 [vuln_msv] [ BUG: Invalid wait context ] ... (led_lock#2) ... at: menf21bmc_led_set+0x15/0x49 [vuln_msv] Convert the driver to brightness_set_blocking and return the transport status to the LED core. Fixes: 38433639af91 ("leds: leds-menf21bmc: Introduce MEN 14F021P00 BMC LED driver") Cc: stable@vger.kernel.org Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn> --- Notes: - Validated with a grounded Lockdep PoC that preserves the menf21bmc_led_probe() brightness_set registration and the led_trigger_event_atomic() -> menf21bmc_led_set() -> mutex_lock(&led_lock) path. - checkpatch.pl --strict: clean. - Not tested on MEN 14F021P00 BMC hardware. drivers/leds/leds-menf21bmc.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/drivers/leds/leds-menf21bmc.c b/drivers/leds/leds-menf21bmc.c index 6b1b47160602..da476fe6ed2c 100644 --- a/drivers/leds/leds-menf21bmc.c +++ b/drivers/leds/leds-menf21bmc.c @@ -49,28 +49,33 @@ static struct menf21bmc_led leds[] = { static DEFINE_MUTEX(led_lock); -static void -menf21bmc_led_set(struct led_classdev *led_cdev, enum led_brightness value) +static int +menf21bmc_led_set_blocking(struct led_classdev *led_cdev, + enum led_brightness value) { int led_val; + int ret = 0; struct menf21bmc_led *led = container_of(led_cdev, struct menf21bmc_led, cdev); mutex_lock(&led_lock); led_val = i2c_smbus_read_byte_data(led->i2c_client, BMC_CMD_LED_GET_SET); - if (led_val < 0) + if (led_val < 0) { + ret = led_val; goto err_out; + } if (value == LED_OFF) led_val &= ~led->led_bit; else led_val |= led->led_bit; - i2c_smbus_write_byte_data(led->i2c_client, - BMC_CMD_LED_GET_SET, led_val); + ret = i2c_smbus_write_byte_data(led->i2c_client, + BMC_CMD_LED_GET_SET, led_val); err_out: mutex_unlock(&led_lock); + return ret; } static int menf21bmc_led_probe(struct platform_device *pdev) @@ -81,7 +86,8 @@ static int menf21bmc_led_probe(struct platform_device *pdev) for (i = 0; i < ARRAY_SIZE(leds); i++) { leds[i].cdev.name = leds[i].name; - leds[i].cdev.brightness_set = menf21bmc_led_set; + leds[i].cdev.brightness_set_blocking = + menf21bmc_led_set_blocking; leds[i].i2c_client = i2c_client; ret = devm_led_classdev_register(&pdev->dev, &leds[i].cdev); if (ret < 0) { -- 2.34.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH 2/2] leds: menf21bmc: use brightness_set_blocking for sleepable callback 2026-06-15 14:57 ` [PATCH 2/2] leds: menf21bmc: " Runyu Xiao @ 2026-07-02 10:45 ` Lee Jones 0 siblings, 0 replies; 5+ messages in thread From: Lee Jones @ 2026-07-02 10:45 UTC (permalink / raw) To: Runyu Xiao, Andreas Werner Cc: Pavel Machek, linux-leds, Andreas Werner, Andrew Morton, Shreshtha Kumar Sahu, Bryan Wu, linux-kernel, jianhao.xu, stable Andreas, any thoughts? On Mon, 15 Jun 2026, Runyu Xiao wrote: > menf21bmc_led_set() serializes access with a mutex and performs I2C > transfers, but the driver exposes it through brightness_set. That > mismatches the LED core callback contract for atomic callers. > > This issue was found by our static analysis tool and then manually > reviewed against the current tree. > > A minimal Lockdep reproducer that keeps the original registration and > call chain is enough to trigger the warning: menf21bmc_led_probe() > still publishes menf21bmc_led_set() as brightness_set, > led_trigger_event_atomic() invokes it under spin_lock_irqsave(), and > the callback immediately tries mutex_lock(&led_lock) before reaching > its I2C accesses. > > Lockdep reports: > > BUG: sleeping function called from invalid context > __mutex_lock+0x4f/0xd20 > menf21bmc_led_set+0x15/0x49 [vuln_msv] > [ BUG: Invalid wait context ] > ... (led_lock#2) ... at: menf21bmc_led_set+0x15/0x49 [vuln_msv] > > Convert the driver to brightness_set_blocking and return the transport > status to the LED core. > > Fixes: 38433639af91 ("leds: leds-menf21bmc: Introduce MEN 14F021P00 BMC LED driver") > Cc: stable@vger.kernel.org > Signed-off-by: Runyu Xiao <runyu.xiao@seu.edu.cn> > --- > Notes: > - Validated with a grounded Lockdep PoC that preserves the > menf21bmc_led_probe() brightness_set registration and the > led_trigger_event_atomic() -> menf21bmc_led_set() -> > mutex_lock(&led_lock) path. > - checkpatch.pl --strict: clean. > - Not tested on MEN 14F021P00 BMC hardware. > > drivers/leds/leds-menf21bmc.c | 18 ++++++++++++------ > 1 file changed, 12 insertions(+), 6 deletions(-) > > diff --git a/drivers/leds/leds-menf21bmc.c b/drivers/leds/leds-menf21bmc.c > index 6b1b47160602..da476fe6ed2c 100644 > --- a/drivers/leds/leds-menf21bmc.c > +++ b/drivers/leds/leds-menf21bmc.c > @@ -49,28 +49,33 @@ static struct menf21bmc_led leds[] = { > > static DEFINE_MUTEX(led_lock); > > -static void > -menf21bmc_led_set(struct led_classdev *led_cdev, enum led_brightness value) > +static int > +menf21bmc_led_set_blocking(struct led_classdev *led_cdev, > + enum led_brightness value) > { > int led_val; > + int ret = 0; > struct menf21bmc_led *led = container_of(led_cdev, > struct menf21bmc_led, cdev); > > mutex_lock(&led_lock); > led_val = i2c_smbus_read_byte_data(led->i2c_client, > BMC_CMD_LED_GET_SET); > - if (led_val < 0) > + if (led_val < 0) { > + ret = led_val; > goto err_out; > + } > > if (value == LED_OFF) > led_val &= ~led->led_bit; > else > led_val |= led->led_bit; > > - i2c_smbus_write_byte_data(led->i2c_client, > - BMC_CMD_LED_GET_SET, led_val); > + ret = i2c_smbus_write_byte_data(led->i2c_client, > + BMC_CMD_LED_GET_SET, led_val); > err_out: > mutex_unlock(&led_lock); > + return ret; > } > > static int menf21bmc_led_probe(struct platform_device *pdev) > @@ -81,7 +86,8 @@ static int menf21bmc_led_probe(struct platform_device *pdev) > > for (i = 0; i < ARRAY_SIZE(leds); i++) { > leds[i].cdev.name = leds[i].name; > - leds[i].cdev.brightness_set = menf21bmc_led_set; > + leds[i].cdev.brightness_set_blocking = > + menf21bmc_led_set_blocking; > leds[i].i2c_client = i2c_client; > ret = devm_led_classdev_register(&pdev->dev, &leds[i].cdev); > if (ret < 0) { > -- > 2.34.1 -- Lee Jones ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-02 10:45 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-15 14:57 [PATCH 0/2] leds: use brightness_set_blocking for sleepable callbacks Runyu Xiao 2026-06-15 14:57 ` [PATCH 1/2] leds: lm3530: use brightness_set_blocking for sleepable callback Runyu Xiao 2026-07-02 10:20 ` Lee Jones 2026-06-15 14:57 ` [PATCH 2/2] leds: menf21bmc: " Runyu Xiao 2026-07-02 10:45 ` Lee Jones
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox