* [PATCH v6 1/5] leds: trigger: netdev: Put netdev on activate error path
2026-10-27 11:33 [PATCH v6 0/5] leds: trigger: netdev: fix sysfs_update_group() races A. Sverdlin
@ 2026-10-27 11:33 ` A. Sverdlin
2026-10-27 11:40 ` sashiko-bot
2026-10-27 11:33 ` [PATCH v6 2/5] leds: trigger: netdev: Access net_dev under trigger_data->lock in the worker A. Sverdlin
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: A. Sverdlin @ 2026-10-27 11:33 UTC (permalink / raw)
To: linux-leds
Cc: Alexander Sverdlin, Lee Jones, Pavel Machek, Andrew Lunn,
Mike Marciniszyn (Meta), Jakub Kicinski, Kees Cook,
Marek Behún, Christian Marangi, linux-kernel, stable
From: Alexander Sverdlin <alexander.sverdlin@siemens.com>
When hw control is active, set_device_name() takes a reference on the
netdev via dev_get_by_name(). The register_netdevice_notifier() failure
path frees trigger_data without dropping it, leaking the netdev.
Cc: stable@vger.kernel.org
Closes: https://lore.kernel.org/all/20260914140045.B8F4C1F000FF@smtp.kernel.org/
Fixes: 0316cc5629d1 ("leds: trigger: netdev: init mode if hw control already active")
Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
---
Changelog:
v5:
- removed unset_device_name()
v3:
- introduced unset_device_name() as a counterpart of set_device_name()
drivers/leds/trigger/ledtrig-netdev.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index 5b0132484594c..cfaf130157b3d 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -776,8 +776,10 @@ static int netdev_trig_activate(struct led_classdev *led_cdev)
led_set_trigger_data(led_cdev, trigger_data);
rc = register_netdevice_notifier(&trigger_data->notifier);
- if (rc)
+ if (rc) {
+ dev_put(trigger_data->net_dev);
kfree(trigger_data);
+ }
return rc;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v6 2/5] leds: trigger: netdev: Access net_dev under trigger_data->lock in the worker
2026-10-27 11:33 [PATCH v6 0/5] leds: trigger: netdev: fix sysfs_update_group() races A. Sverdlin
2026-10-27 11:33 ` [PATCH v6 1/5] leds: trigger: netdev: Put netdev on activate error path A. Sverdlin
@ 2026-10-27 11:33 ` A. Sverdlin
2026-10-27 11:50 ` sashiko-bot
2026-10-27 11:33 ` [PATCH v6 3/5] leds: trigger: netdev: Fix sysfs_update_group() races A. Sverdlin
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: A. Sverdlin @ 2026-10-27 11:33 UTC (permalink / raw)
To: linux-leds
Cc: Alexander Sverdlin, Lee Jones, Pavel Machek, Andrew Lunn,
Mike Marciniszyn (Meta), Jakub Kicinski, Kees Cook,
Marek Behún, Christian Marangi, linux-kernel, stable
From: Alexander Sverdlin <alexander.sverdlin@siemens.com>
netdev_trig_work() dereferences trigger_data->net_dev without the lock,
while set_device_name() and netdev_trig_notify() dev_put() and replace it
under trigger_data->lock. On NETDEV_UNREGISTER the worker can run
dev_get_stats() on a netdev being freed (UAF).
Take trigger_data->lock in the worker. cancel_delayed_work_sync() must then
never run under that lock.
Cc: stable@vger.kernel.org
Closes: https://lore.kernel.org/all/20260914142116.6DAC51F000FF@smtp.kernel.org/
Fixes: 06f502f57d0d ("leds: trigger: Introduce a NETDEV trigger")
Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
---
Changelog:
v3:
- explicit mutex_lock()/mutex_unlock() instead of guard()
drivers/leds/trigger/ledtrig-netdev.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index cfaf130157b3d..e72e850e0070f 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -676,9 +676,12 @@ static void netdev_trig_work(struct work_struct *work)
unsigned long interval;
int invert;
+ mutex_lock(&trigger_data->lock);
+
/* If we dont have a device, insure we are off */
if (!trigger_data->net_dev) {
led_set_brightness(trigger_data->led_cdev, LED_OFF);
+ mutex_unlock(&trigger_data->lock);
return;
}
@@ -686,8 +689,10 @@ static void netdev_trig_work(struct work_struct *work)
if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) &&
!test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) &&
!test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) &&
- !test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode))
+ !test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode)) {
+ mutex_unlock(&trigger_data->lock);
return;
+ }
dev_stats = dev_get_stats(trigger_data->net_dev, &temp);
new_activity =
@@ -728,6 +733,8 @@ static void netdev_trig_work(struct work_struct *work)
schedule_delayed_work(&trigger_data->work,
(atomic_read(&trigger_data->interval)*2));
+
+ mutex_unlock(&trigger_data->lock);
}
static int netdev_trig_activate(struct led_classdev *led_cdev)
--
2.56.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v6 3/5] leds: trigger: netdev: Fix sysfs_update_group() races
2026-10-27 11:33 [PATCH v6 0/5] leds: trigger: netdev: fix sysfs_update_group() races A. Sverdlin
2026-10-27 11:33 ` [PATCH v6 1/5] leds: trigger: netdev: Put netdev on activate error path A. Sverdlin
2026-10-27 11:33 ` [PATCH v6 2/5] leds: trigger: netdev: Access net_dev under trigger_data->lock in the worker A. Sverdlin
@ 2026-10-27 11:33 ` A. Sverdlin
2026-10-27 12:03 ` sashiko-bot
2026-10-27 11:33 ` [PATCH v6 4/5] leds: trigger: netdev: Report invalid interval to user space A. Sverdlin
2026-10-27 11:33 ` [PATCH v6 5/5] leds: trigger: netdev: Serialize mode/interval stores with trigger lock A. Sverdlin
4 siblings, 1 reply; 11+ messages in thread
From: A. Sverdlin @ 2026-10-27 11:33 UTC (permalink / raw)
To: linux-leds
Cc: Alexander Sverdlin, Lee Jones, Pavel Machek, Andrew Lunn,
Mike Marciniszyn (Meta), Jakub Kicinski, Kees Cook,
Marek Behún, Christian Marangi, linux-kernel, stable
From: Alexander Sverdlin <alexander.sverdlin@siemens.com>
The link_speed attribute group was listed in netdev_led_trigger.groups,
so the LED core creates and destroys it via device_add_groups() /
device_remove_groups() in led_trigger_set() (under trigger_lock), while
the trigger also refreshes it with sysfs_update_group() from
netdev_trig_notify() and device_name writes. With no shared lock this was
observed as a sysfs splat when the trigger is re-armed during PHY link-up:
sysfs: cannot create duplicate filename '...green:lan/link_10'
CPU 0 (led_trigger_set) CPU 1 (linkwatch workqueue)
----------------------- ---------------------------
activate():
register_netdevice_notifier()
. netdev_trig_notify(NETDEV_CHANGE):
. sysfs_update_group() creates "link_10"
device_add_groups()
creates "link_10" <- EEXIST!
The mirror case (device_remove_groups() racing the notifier) leaves an
orphaned sysfs file pointing at freed trigger_data, i.e. a UAF.
Manage the group in the trigger: drop it from netdev_led_trigger.groups
and create/destroy it in activate()/deactivate(), so the LED core never
touches it. The two remaining refreshers, netdev_trig_notify() and
device_name_store(), are serialized against each other by a dedicated
attr_lock, and the is_visible callback takes trigger_data->lock for its
supported_link_modes read. sysfs_update_group() is never called under
trigger_data->lock, so it cannot deadlock against a concurrent link_*
store that takes that lock.
Cc: stable@vger.kernel.org
Fixes: 06cdca014eca ("leds: trigger: netdev: Display only supported link speed attribute")
Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
---
Changelog:
v3:
- changed guard() to scoped_guard() and dev_put() to unset_device_name()
v2:
- this patch is a combined rework of patches 1&2 from v1
drivers/leds/trigger/ledtrig-netdev.c | 74 +++++++++++++++++----------
1 file changed, 47 insertions(+), 27 deletions(-)
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index e72e850e0070f..bf9a5ec245a43 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -57,6 +57,8 @@
struct led_netdev_data {
struct mutex lock;
+ /* Serializes link_speed group refreshes; never taken by attr stores */
+ struct mutex attr_lock;
struct delayed_work work;
struct notifier_block notifier;
@@ -336,8 +338,9 @@ static ssize_t device_name_store(struct device *dev,
if (ret < 0)
return ret;
- /* Refresh link_speed visibility */
- sysfs_update_group(&dev->kobj, &netdev_trig_link_speed_attrs_group);
+ /* Serialize the link_speed visibility refresh against netdev_trig_notify() */
+ scoped_guard(mutex, &trigger_data->attr_lock)
+ sysfs_update_group(&dev->kobj, &netdev_trig_link_speed_attrs_group);
return size;
}
@@ -544,22 +547,23 @@ static umode_t netdev_trig_link_speed_visible(struct kobject *kobj,
* Stop at the first matching entry as we care only to check if a particular
* speed is supported and not the kind.
*/
- for_each_set_bit(mode, supported_link_modes, __ETHTOOL_LINK_MODE_MASK_NBITS) {
- struct ethtool_link_ksettings link_ksettings;
-
- ethtool_params_from_link_mode(&link_ksettings, mode);
-
- CHECK_LINK_MODE_ATTR(10);
- CHECK_LINK_MODE_ATTR(100);
- CHECK_LINK_MODE_ATTR(1000);
- CHECK_LINK_MODE_ATTR(2500);
- CHECK_LINK_MODE_ATTR(5000);
- CHECK_LINK_MODE_ATTR(10000);
- CHECK_LINK_MODE_ATTR(25000);
- CHECK_LINK_MODE_ATTR(40000);
- CHECK_LINK_MODE_ATTR(50000);
- CHECK_LINK_MODE_ATTR(100000);
- }
+ scoped_guard(mutex, &trigger_data->lock)
+ for_each_set_bit(mode, supported_link_modes, __ETHTOOL_LINK_MODE_MASK_NBITS) {
+ struct ethtool_link_ksettings link_ksettings;
+
+ ethtool_params_from_link_mode(&link_ksettings, mode);
+
+ CHECK_LINK_MODE_ATTR(10);
+ CHECK_LINK_MODE_ATTR(100);
+ CHECK_LINK_MODE_ATTR(1000);
+ CHECK_LINK_MODE_ATTR(2500);
+ CHECK_LINK_MODE_ATTR(5000);
+ CHECK_LINK_MODE_ATTR(10000);
+ CHECK_LINK_MODE_ATTR(25000);
+ CHECK_LINK_MODE_ATTR(40000);
+ CHECK_LINK_MODE_ATTR(50000);
+ CHECK_LINK_MODE_ATTR(100000);
+ }
return 0;
}
@@ -603,7 +607,6 @@ static const struct attribute_group netdev_trig_attrs_group = {
static const struct attribute_group *netdev_trig_groups[] = {
&netdev_trig_attrs_group,
- &netdev_trig_link_speed_attrs_group,
NULL,
};
@@ -651,10 +654,6 @@ static int netdev_trig_notify(struct notifier_block *nb,
fallthrough;
case NETDEV_CHANGE:
get_device_state(trigger_data);
- /* Refresh link_speed visibility */
- if (evt == NETDEV_CHANGE)
- sysfs_update_group(&led_cdev->dev->kobj,
- &netdev_trig_link_speed_attrs_group);
break;
}
@@ -662,6 +661,12 @@ static int netdev_trig_notify(struct notifier_block *nb,
mutex_unlock(&trigger_data->lock);
+ if (evt == NETDEV_CHANGE) {
+ scoped_guard(mutex, &trigger_data->attr_lock)
+ sysfs_update_group(&led_cdev->dev->kobj,
+ &netdev_trig_link_speed_attrs_group);
+ }
+
return NOTIFY_DONE;
}
@@ -749,6 +754,7 @@ static int netdev_trig_activate(struct led_classdev *led_cdev)
return -ENOMEM;
mutex_init(&trigger_data->lock);
+ mutex_init(&trigger_data->attr_lock);
trigger_data->notifier.notifier_call = netdev_trig_notify;
trigger_data->notifier.priority = 10;
@@ -782,12 +788,23 @@ static int netdev_trig_activate(struct led_classdev *led_cdev)
led_set_trigger_data(led_cdev, trigger_data);
+ rc = sysfs_create_group(&led_cdev->dev->kobj,
+ &netdev_trig_link_speed_attrs_group);
+ if (rc)
+ goto err_free;
+
rc = register_netdevice_notifier(&trigger_data->notifier);
- if (rc) {
- dev_put(trigger_data->net_dev);
- kfree(trigger_data);
- }
+ if (rc)
+ goto err_remove_group;
+
+ return 0;
+err_remove_group:
+ sysfs_remove_group(&led_cdev->dev->kobj,
+ &netdev_trig_link_speed_attrs_group);
+err_free:
+ dev_put(trigger_data->net_dev);
+ kfree(trigger_data);
return rc;
}
@@ -797,6 +814,9 @@ static void netdev_trig_deactivate(struct led_classdev *led_cdev)
unregister_netdevice_notifier(&trigger_data->notifier);
+ sysfs_remove_group(&led_cdev->dev->kobj,
+ &netdev_trig_link_speed_attrs_group);
+
cancel_delayed_work_sync(&trigger_data->work);
dev_put(trigger_data->net_dev);
--
2.56.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v6 3/5] leds: trigger: netdev: Fix sysfs_update_group() races
2026-10-27 11:33 ` [PATCH v6 3/5] leds: trigger: netdev: Fix sysfs_update_group() races A. Sverdlin
@ 2026-10-27 12:03 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-10-27 12:03 UTC (permalink / raw)
To: A. Sverdlin, linux-leds; +Cc: lee
> From: Alexander Sverdlin <alexander.sverdlin@siemens.com>
>
> The link_speed attribute group was listed in netdev_led_trigger.groups,
> so the LED core creates and destroys it via device_add_groups() /
> device_remove_groups() in led_trigger_set() (under trigger_lock), while
> the trigger also refreshes it with sysfs_update_group() from
> netdev_trig_notify() and device_name writes. With no shared lock this was
> observed as a sysfs splat when the trigger is re-armed during PHY link-up:
>
> sysfs: cannot create duplicate filename '...green:lan/link_10'
>
> CPU 0 (led_trigger_set) CPU 1 (linkwatch workqueue)
> ----------------------- ---------------------------
> activate():
> register_netdevice_notifier()
> [ ... ]
> store that takes that lock.
>
> Cc: stable@vger.kernel.org
> Fixes: 06cdca014eca ("leds: trigger: netdev: Display only supported link speed attribute")
> Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
Sashiko has reviewed this patch and found no issues.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261027113349.383147-1-alexander.sverdlin@siemens.com?part=3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v6 4/5] leds: trigger: netdev: Report invalid interval to user space
2026-10-27 11:33 [PATCH v6 0/5] leds: trigger: netdev: fix sysfs_update_group() races A. Sverdlin
` (2 preceding siblings ...)
2026-10-27 11:33 ` [PATCH v6 3/5] leds: trigger: netdev: Fix sysfs_update_group() races A. Sverdlin
@ 2026-10-27 11:33 ` A. Sverdlin
2026-10-27 12:06 ` sashiko-bot
2026-10-27 11:33 ` [PATCH v6 5/5] leds: trigger: netdev: Serialize mode/interval stores with trigger lock A. Sverdlin
4 siblings, 1 reply; 11+ messages in thread
From: A. Sverdlin @ 2026-10-27 11:33 UTC (permalink / raw)
To: linux-leds
Cc: Alexander Sverdlin, Lee Jones, Pavel Machek, Andrew Lunn,
Mike Marciniszyn (Meta), Jakub Kicinski, Kees Cook,
Marek Behún, Christian Marangi, linux-kernel, stable
From: Alexander Sverdlin <alexander.sverdlin@siemens.com>
interval_store() silently accepts out-of-range values: a write outside
the [5, 10000] ms bounds returns the full count as if it succeeded, yet
the interval is left unchanged.
Reject out-of-range values with -EINVAL instead.
Cc: stable@vger.kernel.org
Fixes: 06f502f57d0d ("leds: trigger: Introduce a NETDEV trigger")
Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
---
Changelog:
v6:
- patch introduced
drivers/leds/trigger/ledtrig-netdev.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index bf9a5ec245a43..b1f84014acab9 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -502,12 +502,13 @@ static ssize_t interval_store(struct device *dev,
return ret;
/* impose some basic bounds on the timer interval */
- if (value >= 5 && value <= 10000) {
- cancel_delayed_work_sync(&trigger_data->work);
+ if (value < 5 || value > 10000)
+ return -EINVAL;
- atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
- set_baseline_state(trigger_data); /* resets timer */
- }
+ cancel_delayed_work_sync(&trigger_data->work);
+
+ atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
+ set_baseline_state(trigger_data); /* resets timer */
return size;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* [PATCH v6 5/5] leds: trigger: netdev: Serialize mode/interval stores with trigger lock
2026-10-27 11:33 [PATCH v6 0/5] leds: trigger: netdev: fix sysfs_update_group() races A. Sverdlin
` (3 preceding siblings ...)
2026-10-27 11:33 ` [PATCH v6 4/5] leds: trigger: netdev: Report invalid interval to user space A. Sverdlin
@ 2026-10-27 11:33 ` A. Sverdlin
2026-10-27 12:17 ` sashiko-bot
4 siblings, 1 reply; 11+ messages in thread
From: A. Sverdlin @ 2026-10-27 11:33 UTC (permalink / raw)
To: linux-leds
Cc: Alexander Sverdlin, Lee Jones, Pavel Machek, Andrew Lunn,
Mike Marciniszyn (Meta), Jakub Kicinski, Kees Cook,
Marek Behún, Christian Marangi, linux-kernel, stable
From: Alexander Sverdlin <alexander.sverdlin@siemens.com>
netdev_led_attr_store() and interval_store() update ->mode and run
set_baseline_state() without trigger_data->lock. kernfs only serializes
writes to the same file, so two attribute writes race on the non-atomic
read-modify-write of ->mode:
CPU0 (echo 1 > link_10) CPU1 (echo 1 > link_100)
----------------------- ------------------------
mode = trigger_data->mode;
mode = trigger_data->mode;
set_bit(LINK_10, &mode);
set_bit(LINK_100, &mode);
trigger_data->mode = mode;
trigger_data->mode = mode; // LINK_10 lost
They also race the notifier's link-state and ->hw_control updates.
Take trigger_data->lock in both stores. netdev_trig_work() also holds it,
so use the async cancel_delayed_work(). interval_store() now tests
->hw_control under the lock too, so it can no longer sample a stale value
and change the interval while a concurrent store enables offload.
The async cancel cannot stop a worker already blocked on the lock: it may
resume after a store enabled hw_control and keep software-blinking and
rescheduling itself. Gate the worker and its arming in set_baseline_state()
on a shared netdev_trig_sw_blink() helper, so it stops once offload is
active or RX/TX monitoring is off.
set_baseline_state() can also re-arm the worker from a store that acquires
trigger_data->lock in the window between a notifier's
cancel_delayed_work_sync() and the notifier taking the same lock. The
notifier then sets carrier_link_up = false but does not cancel the re-armed
work. Gate netdev_trig_sw_blink() on carrier_link_up as well, so a worker
left armed across a link-down stops instead of blinking.
Cc: stable@vger.kernel.org
Closes: https://lore.kernel.org/all/sashiko-outbox-178980@kernel.org/
Fixes: d5e01266e7f5 ("leds: trigger: netdev: add additional specific link speed mode")
Signed-off-by: Alexander Sverdlin <alexander.sverdlin@siemens.com>
---
Changelog:
v6:
- fixed unlocked hw_control check in interval_store()
- added missing carrier_link_up check in netdev_trig_sw_blink()
v4:
- introduced netdev_trig_sw_blink() and used it in netdev_trig_work()
v3:
- explicit mutex_lock()/mutex_unlock() instead of guard()
v2:
- reduced verbosity both in the comments and in commit message
- dropped sync from cancel_delayed_work() (worker now takes the lock)
drivers/leds/trigger/ledtrig-netdev.c | 56 +++++++++++++++++++--------
1 file changed, 39 insertions(+), 17 deletions(-)
diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c
index b1f84014acab9..a503a4d64cefd 100644
--- a/drivers/leds/trigger/ledtrig-netdev.c
+++ b/drivers/leds/trigger/ledtrig-netdev.c
@@ -81,6 +81,17 @@ struct led_netdev_data {
static const struct attribute_group netdev_trig_link_speed_attrs_group;
+/* True while the software activity worker should keep polling and blinking. */
+static bool netdev_trig_sw_blink(struct led_netdev_data *trigger_data)
+{
+ return trigger_data->net_dev && !trigger_data->hw_control &&
+ trigger_data->carrier_link_up &&
+ (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ||
+ test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ||
+ test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ||
+ test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode));
+}
+
static void set_baseline_state(struct led_netdev_data *trigger_data)
{
int current_brightness;
@@ -164,10 +175,7 @@ static void set_baseline_state(struct led_netdev_data *trigger_data)
/* If we are looking for RX/TX start periodically
* checking stats
*/
- if (test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) ||
- test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) ||
- test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) ||
- test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode))
+ if (netdev_trig_sw_blink(trigger_data))
schedule_delayed_work(&trigger_data->work, 0);
}
}
@@ -385,7 +393,7 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
{
struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev);
struct led_classdev *led_cdev = trigger_data->led_cdev;
- unsigned long state, mode = trigger_data->mode;
+ unsigned long state, mode;
int ret;
int bit;
@@ -417,6 +425,10 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
return -EINVAL;
}
+ /* async cancel: the worker takes this lock */
+ mutex_lock(&trigger_data->lock);
+
+ mode = trigger_data->mode;
assign_bit(bit, &mode, state);
if (test_bit(TRIGGER_NETDEV_LINK, &mode) &&
@@ -429,20 +441,26 @@ static ssize_t netdev_led_attr_store(struct device *dev, const char *buf,
test_bit(TRIGGER_NETDEV_LINK_25000, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_40000, &mode) ||
test_bit(TRIGGER_NETDEV_LINK_50000, &mode) ||
- test_bit(TRIGGER_NETDEV_LINK_100000, &mode)))
+ test_bit(TRIGGER_NETDEV_LINK_100000, &mode))) {
+ mutex_unlock(&trigger_data->lock);
return -EINVAL;
+ }
- cancel_delayed_work_sync(&trigger_data->work);
+ cancel_delayed_work(&trigger_data->work);
trigger_data->mode = mode;
trigger_data->hw_control = can_hw_control(trigger_data);
if (!led_cdev->brightness_set && !led_cdev->brightness_set_blocking &&
- !trigger_data->hw_control)
+ !trigger_data->hw_control) {
+ mutex_unlock(&trigger_data->lock);
return -EOPNOTSUPP;
+ }
set_baseline_state(trigger_data);
+ mutex_unlock(&trigger_data->lock);
+
return size;
}
@@ -494,9 +512,6 @@ static ssize_t interval_store(struct device *dev,
unsigned long value;
int ret;
- if (trigger_data->hw_control)
- return -EINVAL;
-
ret = kstrtoul(buf, 0, &value);
if (ret)
return ret;
@@ -505,11 +520,21 @@ static ssize_t interval_store(struct device *dev,
if (value < 5 || value > 10000)
return -EINVAL;
- cancel_delayed_work_sync(&trigger_data->work);
+ /* async cancel: the worker takes this lock */
+ mutex_lock(&trigger_data->lock);
+
+ if (trigger_data->hw_control) {
+ mutex_unlock(&trigger_data->lock);
+ return -EINVAL;
+ }
+
+ cancel_delayed_work(&trigger_data->work);
atomic_set(&trigger_data->interval, msecs_to_jiffies(value));
set_baseline_state(trigger_data); /* resets timer */
+ mutex_unlock(&trigger_data->lock);
+
return size;
}
@@ -691,11 +716,8 @@ static void netdev_trig_work(struct work_struct *work)
return;
}
- /* If we are not looking for RX/TX then return */
- if (!test_bit(TRIGGER_NETDEV_TX, &trigger_data->mode) &&
- !test_bit(TRIGGER_NETDEV_RX, &trigger_data->mode) &&
- !test_bit(TRIGGER_NETDEV_TX_ERR, &trigger_data->mode) &&
- !test_bit(TRIGGER_NETDEV_RX_ERR, &trigger_data->mode)) {
+ /* Nothing to do under hw offload or without RX/TX monitoring */
+ if (!netdev_trig_sw_blink(trigger_data)) {
mutex_unlock(&trigger_data->lock);
return;
}
--
2.56.0
^ permalink raw reply related [flat|nested] 11+ messages in thread