From f574751cdfac6645a050e64ea3107a57ae13b816 Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 21 Dec 2023 18:31:48 +0100 Subject: leds: trigger: netdev: Skip setting baseline state in activate if hw-controlled The current codes uses the sw_control path in set_baseline_state() when called from netdev_trig_activate() even if we're hw-controlled. This may result in errors when led_set_brightness() is called because we may not have set_brightness led ops (if hw doesn't support setting a "LED" to ON). In addition this path may schedule trigger_data->work which doesn't make sense when being hw-controlled. Therefore set trigger_data->hw_control = true before calling set_device_name() from netdev_trig_activate(). In this call chain we have to prevent set_baseline_state() from being called, because this would call hw_control_set(). Use led_cdev->trigger_data == NULL as indicator for being called from netdev_trig_activate(). Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/d3f2859c-2673-401c-a4f7-fcaef2167991@gmail.com Signed-off-by: Lee Jones --- drivers/leds/trigger/ledtrig-netdev.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/leds/trigger/ledtrig-netdev.c') diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index 8e5475819590..1a0cfbba5976 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -277,7 +277,10 @@ static int set_device_name(struct led_netdev_data *trigger_data, trigger_data->last_activity = 0; - set_baseline_state(trigger_data); + /* Skip if we're called from netdev_trig_activate() and hw_control is true */ + if (!trigger_data->hw_control || led_get_trigger_data(trigger_data->led_cdev)) + set_baseline_state(trigger_data); + mutex_unlock(&trigger_data->lock); rtnl_unlock(); @@ -617,8 +620,8 @@ static int netdev_trig_activate(struct led_classdev *led_cdev) if (dev) { const char *name = dev_name(dev); - set_device_name(trigger_data, name, strlen(name)); trigger_data->hw_control = true; + set_device_name(trigger_data, name, strlen(name)); rc = led_cdev->hw_control_get(led_cdev, &mode); if (!rc) -- cgit v1.2.3 From fd14a87230ed4d47541f87a1e810ea123614907a Mon Sep 17 00:00:00 2001 From: Heiner Kallweit Date: Thu, 21 Dec 2023 23:20:26 +0100 Subject: leds: trigger: netdev: Add module alias ledtrig:netdev Add module alias ledtrig:netdev to enable auto-loading of the module. Signed-off-by: Heiner Kallweit Link: https://lore.kernel.org/r/84a1bbd3-1ac7-4f37-849a-7f4d31698f76@gmail.com Signed-off-by: Lee Jones --- drivers/leds/trigger/ledtrig-netdev.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/leds/trigger/ledtrig-netdev.c') diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index 1a0cfbba5976..e22a7b08279b 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -666,3 +666,4 @@ MODULE_AUTHOR("Ben Whitten "); MODULE_AUTHOR("Oliver Jowett "); MODULE_DESCRIPTION("Netdev LED trigger"); MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("ledtrig:netdev"); -- cgit v1.2.3 From 06cdca014eca3449d8c34bf7c2fcb796a114a0b8 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Thu, 11 Jan 2024 17:04:54 +0100 Subject: leds: trigger: netdev: Display only supported link speed attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the addition of more link speed mode to the netdev trigger, it was pointed out that there may be a problem with bloating the attribute list with modes that won't ever be supported by the trigger as the attached device name doesn't support them. To clear and address this problem, change the logic where these additional trigger modes are listed. Since the netdev trigger REQUIRE a device name to be set, attach to the device name change function additional logic to parse the supported link speed modes using ethtool APIs and show only the supported link speed modes attribute. Link speed attribute are refreshed on device_name set and on NETDEV_CHANGE events. This only apply to the link speed modes and every other mode is still provided by default. Signed-off-by: Christian Marangi Reviewed-by: Marek BehĂșn Link: https://lore.kernel.org/r/20240111160501.1774-1-ansuelsmth@gmail.com Signed-off-by: Lee Jones --- drivers/leds/trigger/ledtrig-netdev.c | 90 ++++++++++++++++++++++++++++++++--- 1 file changed, 84 insertions(+), 6 deletions(-) (limited to 'drivers/leds/trigger/ledtrig-netdev.c') diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index e22a7b08279b..f0eb5820c48c 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -18,10 +18,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include "../leds.h" @@ -65,12 +67,15 @@ struct led_netdev_data { unsigned long mode; int link_speed; + __ETHTOOL_DECLARE_LINK_MODE_MASK(supported_link_modes); u8 duplex; bool carrier_link_up; bool hw_control; }; +static const struct attribute_group netdev_trig_link_speed_attrs_group; + static void set_baseline_state(struct led_netdev_data *trigger_data) { int current_brightness; @@ -218,13 +223,20 @@ static void get_device_state(struct led_netdev_data *trigger_data) struct ethtool_link_ksettings cmd; trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev); - if (!trigger_data->carrier_link_up) + + if (__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd)) return; - if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd)) { + if (trigger_data->carrier_link_up) { trigger_data->link_speed = cmd.base.speed; trigger_data->duplex = cmd.base.duplex; } + + /* + * Have a local copy of the link speed supported to avoid rtnl lock every time + * modes are refreshed on any change event + */ + linkmode_copy(trigger_data->supported_link_modes, cmd.link_modes.supported); } static ssize_t device_name_show(struct device *dev, @@ -298,6 +310,10 @@ 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); + return size; } @@ -461,15 +477,63 @@ static ssize_t offloaded_show(struct device *dev, static DEVICE_ATTR_RO(offloaded); -static struct attribute *netdev_trig_attrs[] = { - &dev_attr_device_name.attr, - &dev_attr_link.attr, +#define CHECK_LINK_MODE_ATTR(link_speed) \ + do { \ + if (attr == &dev_attr_link_##link_speed.attr && \ + link_ksettings.base.speed == SPEED_##link_speed) \ + return attr->mode; \ + } while (0) + +static umode_t netdev_trig_link_speed_visible(struct kobject *kobj, + struct attribute *attr, int n) +{ + struct device *dev = kobj_to_dev(kobj); + struct led_netdev_data *trigger_data; + unsigned long *supported_link_modes; + u32 mode; + + trigger_data = led_trigger_get_drvdata(dev); + supported_link_modes = trigger_data->supported_link_modes; + + /* + * Search in the supported link mode mask a matching supported mode. + * 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); + } + + return 0; +} + +static struct attribute *netdev_trig_link_speed_attrs[] = { &dev_attr_link_10.attr, &dev_attr_link_100.attr, &dev_attr_link_1000.attr, &dev_attr_link_2500.attr, &dev_attr_link_5000.attr, &dev_attr_link_10000.attr, + NULL +}; + +static const struct attribute_group netdev_trig_link_speed_attrs_group = { + .attrs = netdev_trig_link_speed_attrs, + .is_visible = netdev_trig_link_speed_visible, +}; + +static struct attribute *netdev_trig_attrs[] = { + &dev_attr_device_name.attr, + &dev_attr_link.attr, &dev_attr_full_duplex.attr, &dev_attr_half_duplex.attr, &dev_attr_rx.attr, @@ -478,7 +542,16 @@ static struct attribute *netdev_trig_attrs[] = { &dev_attr_offloaded.attr, NULL }; -ATTRIBUTE_GROUPS(netdev_trig); + +static const struct attribute_group netdev_trig_attrs_group = { + .attrs = netdev_trig_attrs, +}; + +static const struct attribute_group *netdev_trig_groups[] = { + &netdev_trig_attrs_group, + &netdev_trig_link_speed_attrs_group, + NULL, +}; static int netdev_trig_notify(struct notifier_block *nb, unsigned long evt, void *dv) @@ -487,6 +560,7 @@ static int netdev_trig_notify(struct notifier_block *nb, netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv); struct led_netdev_data *trigger_data = container_of(nb, struct led_netdev_data, notifier); + struct led_classdev *led_cdev = trigger_data->led_cdev; if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER @@ -521,6 +595,10 @@ static int netdev_trig_notify(struct notifier_block *nb, case NETDEV_UP: 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; } -- cgit v1.2.3 From 415798bc07dd1c1ae3a656aa026580816e0b9fe8 Mon Sep 17 00:00:00 2001 From: Christian Marangi Date: Sun, 4 Feb 2024 00:54:01 +0100 Subject: leds: trigger: netdev: Fix kernel panic on interface rename trig notify Commit d5e01266e7f5 ("leds: trigger: netdev: add additional specific link speed mode") in the various changes, reworked the way to set the LINKUP mode in commit cee4bd16c319 ("leds: trigger: netdev: Recheck NETDEV_LED_MODE_LINKUP on dev rename") and moved it to a generic function. This changed the logic where, in the previous implementation the dev from the trigger event was used to check if the carrier was ok, but in the new implementation with the generic function, the dev in trigger_data is used instead. This is problematic and cause a possible kernel panic due to the fact that the dev in the trigger_data still reference the old one as the new one (passed from the trigger event) still has to be hold and saved in the trigger_data struct (done in the NETDEV_REGISTER case). On calling of get_device_state(), an invalid net_dev is used and this cause a kernel panic. To handle this correctly, move the call to get_device_state() after the new net_dev is correctly set in trigger_data (in the NETDEV_REGISTER case) and correctly parse the new dev. Fixes: d5e01266e7f5 ("leds: trigger: netdev: add additional specific link speed mode") Cc: stable@vger.kernel.org Signed-off-by: Christian Marangi Reviewed-by: Andrew Lunn Link: https://lore.kernel.org/r/20240203235413.1146-1-ansuelsmth@gmail.com Signed-off-by: Lee Jones --- drivers/leds/trigger/ledtrig-netdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/leds/trigger/ledtrig-netdev.c') diff --git a/drivers/leds/trigger/ledtrig-netdev.c b/drivers/leds/trigger/ledtrig-netdev.c index f0eb5820c48c..ea00f6c70882 100644 --- a/drivers/leds/trigger/ledtrig-netdev.c +++ b/drivers/leds/trigger/ledtrig-netdev.c @@ -581,12 +581,12 @@ static int netdev_trig_notify(struct notifier_block *nb, trigger_data->duplex = DUPLEX_UNKNOWN; switch (evt) { case NETDEV_CHANGENAME: - get_device_state(trigger_data); - fallthrough; case NETDEV_REGISTER: dev_put(trigger_data->net_dev); dev_hold(dev); trigger_data->net_dev = dev; + if (evt == NETDEV_CHANGENAME) + get_device_state(trigger_data); break; case NETDEV_UNREGISTER: dev_put(trigger_data->net_dev); -- cgit v1.2.3