Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'leds-trigger-netdev-add-additional-modes'

Christian Marangi says:

====================
leds: trigger: netdev: add additional modes

This is a continue of [1]. It was decided to take a more gradual
approach to implement LEDs support for switch and phy starting with
basic support and then implementing the hw control part when we have all
the prereq done.

This should be the final part for the netdev trigger.
I added net-next tag and added netdev mailing list since I was informed
that this should be merged with netdev branch.

We collect some info around and we found a good set of modes that are
common in almost all the PHY and Switch.

These modes are:
- Modes for dedicated link speed(10, 100, 1000 mbps). Additional mode
can be added later following this example.
- Modes for half and full duplex.

The original idea was to add hw control only modes.
While the concept makes sense in practice it would results in lots of
additional code and extra check to make sure we are setting correct modes.

With the suggestion from Andrew it was pointed out that using the ethtool
APIs we can actually get the current link speed and duplex and this
effectively removed the problem of having hw control only modes since we
can fallback to software.

Since these modes are supported by software, we can skip providing an
user for this in the LED driver to support hw control for these new modes
(that will come right after this is merged) and prevent this to be another
multi subsystem series.

For link speed and duplex we use ethtool APIs.

To call ethtool APIs, rtnl lock is needed but this can be skipped on
handling netdev events as the lock is already held.

[1] https://lore.kernel.org/lkml/20230216013230.22978-1-ansuelsmth@gmail.com/
====================

Link: https://lore.kernel.org/r/20230619204700.6665-1-ansuelsmth@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+110 -11
+105 -11
drivers/leds/trigger/ledtrig-netdev.c
··· 13 13 #include <linux/atomic.h> 14 14 #include <linux/ctype.h> 15 15 #include <linux/device.h> 16 + #include <linux/ethtool.h> 16 17 #include <linux/init.h> 17 18 #include <linux/jiffies.h> 18 19 #include <linux/kernel.h> ··· 22 21 #include <linux/module.h> 23 22 #include <linux/netdevice.h> 24 23 #include <linux/mutex.h> 24 + #include <linux/rtnetlink.h> 25 25 #include <linux/timer.h> 26 26 #include "../leds.h" 27 27 ··· 54 52 unsigned int last_activity; 55 53 56 54 unsigned long mode; 55 + int link_speed; 56 + u8 duplex; 57 + 57 58 bool carrier_link_up; 58 59 bool hw_control; 59 60 }; ··· 82 77 if (!trigger_data->carrier_link_up) { 83 78 led_set_brightness(led_cdev, LED_OFF); 84 79 } else { 80 + bool blink_on = false; 81 + 85 82 if (test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode)) 83 + blink_on = true; 84 + 85 + if (test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) && 86 + trigger_data->link_speed == SPEED_10) 87 + blink_on = true; 88 + 89 + if (test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) && 90 + trigger_data->link_speed == SPEED_100) 91 + blink_on = true; 92 + 93 + if (test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) && 94 + trigger_data->link_speed == SPEED_1000) 95 + blink_on = true; 96 + 97 + if (test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) && 98 + trigger_data->duplex == DUPLEX_HALF) 99 + blink_on = true; 100 + 101 + if (test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode) && 102 + trigger_data->duplex == DUPLEX_FULL) 103 + blink_on = true; 104 + 105 + if (blink_on) 86 106 led_set_brightness(led_cdev, 87 107 led_cdev->blink_brightness); 88 108 else ··· 191 161 return true; 192 162 } 193 163 164 + static void get_device_state(struct led_netdev_data *trigger_data) 165 + { 166 + struct ethtool_link_ksettings cmd; 167 + 168 + trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev); 169 + if (!trigger_data->carrier_link_up) 170 + return; 171 + 172 + if (!__ethtool_get_link_ksettings(trigger_data->net_dev, &cmd)) { 173 + trigger_data->link_speed = cmd.base.speed; 174 + trigger_data->duplex = cmd.base.duplex; 175 + } 176 + } 177 + 194 178 static ssize_t device_name_show(struct device *dev, 195 179 struct device_attribute *attr, char *buf) 196 180 { ··· 240 196 dev_get_by_name(&init_net, trigger_data->device_name); 241 197 242 198 trigger_data->carrier_link_up = false; 243 - if (trigger_data->net_dev != NULL) 244 - trigger_data->carrier_link_up = netif_carrier_ok(trigger_data->net_dev); 199 + trigger_data->link_speed = SPEED_UNKNOWN; 200 + trigger_data->duplex = DUPLEX_UNKNOWN; 201 + if (trigger_data->net_dev != NULL) { 202 + rtnl_lock(); 203 + get_device_state(trigger_data); 204 + rtnl_unlock(); 205 + } 245 206 246 207 trigger_data->last_activity = 0; 247 208 ··· 283 234 284 235 switch (attr) { 285 236 case TRIGGER_NETDEV_LINK: 237 + case TRIGGER_NETDEV_LINK_10: 238 + case TRIGGER_NETDEV_LINK_100: 239 + case TRIGGER_NETDEV_LINK_1000: 240 + case TRIGGER_NETDEV_HALF_DUPLEX: 241 + case TRIGGER_NETDEV_FULL_DUPLEX: 286 242 case TRIGGER_NETDEV_TX: 287 243 case TRIGGER_NETDEV_RX: 288 244 bit = attr; ··· 303 249 size_t size, enum led_trigger_netdev_modes attr) 304 250 { 305 251 struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); 306 - unsigned long state; 252 + unsigned long state, mode = trigger_data->mode; 307 253 int ret; 308 254 int bit; 309 255 ··· 313 259 314 260 switch (attr) { 315 261 case TRIGGER_NETDEV_LINK: 262 + case TRIGGER_NETDEV_LINK_10: 263 + case TRIGGER_NETDEV_LINK_100: 264 + case TRIGGER_NETDEV_LINK_1000: 265 + case TRIGGER_NETDEV_HALF_DUPLEX: 266 + case TRIGGER_NETDEV_FULL_DUPLEX: 316 267 case TRIGGER_NETDEV_TX: 317 268 case TRIGGER_NETDEV_RX: 318 269 bit = attr; ··· 326 267 return -EINVAL; 327 268 } 328 269 270 + if (state) 271 + set_bit(bit, &mode); 272 + else 273 + clear_bit(bit, &mode); 274 + 275 + if (test_bit(TRIGGER_NETDEV_LINK, &mode) && 276 + (test_bit(TRIGGER_NETDEV_LINK_10, &mode) || 277 + test_bit(TRIGGER_NETDEV_LINK_100, &mode) || 278 + test_bit(TRIGGER_NETDEV_LINK_1000, &mode))) 279 + return -EINVAL; 280 + 329 281 cancel_delayed_work_sync(&trigger_data->work); 330 282 331 - if (state) 332 - set_bit(bit, &trigger_data->mode); 333 - else 334 - clear_bit(bit, &trigger_data->mode); 335 - 283 + trigger_data->mode = mode; 336 284 trigger_data->hw_control = can_hw_control(trigger_data); 337 285 338 286 set_baseline_state(trigger_data); ··· 361 295 static DEVICE_ATTR_RW(trigger_name) 362 296 363 297 DEFINE_NETDEV_TRIGGER(link, TRIGGER_NETDEV_LINK); 298 + DEFINE_NETDEV_TRIGGER(link_10, TRIGGER_NETDEV_LINK_10); 299 + DEFINE_NETDEV_TRIGGER(link_100, TRIGGER_NETDEV_LINK_100); 300 + DEFINE_NETDEV_TRIGGER(link_1000, TRIGGER_NETDEV_LINK_1000); 301 + DEFINE_NETDEV_TRIGGER(half_duplex, TRIGGER_NETDEV_HALF_DUPLEX); 302 + DEFINE_NETDEV_TRIGGER(full_duplex, TRIGGER_NETDEV_FULL_DUPLEX); 364 303 DEFINE_NETDEV_TRIGGER(tx, TRIGGER_NETDEV_TX); 365 304 DEFINE_NETDEV_TRIGGER(rx, TRIGGER_NETDEV_RX); 366 305 ··· 406 335 407 336 static DEVICE_ATTR_RW(interval); 408 337 338 + static ssize_t hw_control_show(struct device *dev, 339 + struct device_attribute *attr, char *buf) 340 + { 341 + struct led_netdev_data *trigger_data = led_trigger_get_drvdata(dev); 342 + 343 + return sprintf(buf, "%d\n", trigger_data->hw_control); 344 + } 345 + 346 + static DEVICE_ATTR_RO(hw_control); 347 + 409 348 static struct attribute *netdev_trig_attrs[] = { 410 349 &dev_attr_device_name.attr, 411 350 &dev_attr_link.attr, 351 + &dev_attr_link_10.attr, 352 + &dev_attr_link_100.attr, 353 + &dev_attr_link_1000.attr, 354 + &dev_attr_full_duplex.attr, 355 + &dev_attr_half_duplex.attr, 412 356 &dev_attr_rx.attr, 413 357 &dev_attr_tx.attr, 414 358 &dev_attr_interval.attr, 359 + &dev_attr_hw_control.attr, 415 360 NULL 416 361 }; 417 362 ATTRIBUTE_GROUPS(netdev_trig); ··· 455 368 mutex_lock(&trigger_data->lock); 456 369 457 370 trigger_data->carrier_link_up = false; 371 + trigger_data->link_speed = SPEED_UNKNOWN; 372 + trigger_data->duplex = DUPLEX_UNKNOWN; 458 373 switch (evt) { 459 374 case NETDEV_CHANGENAME: 460 - trigger_data->carrier_link_up = netif_carrier_ok(dev); 375 + get_device_state(trigger_data); 461 376 fallthrough; 462 377 case NETDEV_REGISTER: 463 378 if (trigger_data->net_dev) ··· 473 384 break; 474 385 case NETDEV_UP: 475 386 case NETDEV_CHANGE: 476 - trigger_data->carrier_link_up = netif_carrier_ok(dev); 387 + get_device_state(trigger_data); 477 388 break; 478 389 } 479 390 ··· 516 427 if (trigger_data->last_activity != new_activity) { 517 428 led_stop_software_blink(trigger_data->led_cdev); 518 429 519 - invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode); 430 + invert = test_bit(TRIGGER_NETDEV_LINK, &trigger_data->mode) || 431 + test_bit(TRIGGER_NETDEV_LINK_10, &trigger_data->mode) || 432 + test_bit(TRIGGER_NETDEV_LINK_100, &trigger_data->mode) || 433 + test_bit(TRIGGER_NETDEV_LINK_1000, &trigger_data->mode) || 434 + test_bit(TRIGGER_NETDEV_HALF_DUPLEX, &trigger_data->mode) || 435 + test_bit(TRIGGER_NETDEV_FULL_DUPLEX, &trigger_data->mode); 520 436 interval = jiffies_to_msecs( 521 437 atomic_read(&trigger_data->interval)); 522 438 /* base state is ON (link present) */
+5
include/linux/leds.h
··· 555 555 /* Trigger specific enum */ 556 556 enum led_trigger_netdev_modes { 557 557 TRIGGER_NETDEV_LINK = 0, 558 + TRIGGER_NETDEV_LINK_10, 559 + TRIGGER_NETDEV_LINK_100, 560 + TRIGGER_NETDEV_LINK_1000, 561 + TRIGGER_NETDEV_HALF_DUPLEX, 562 + TRIGGER_NETDEV_FULL_DUPLEX, 558 563 TRIGGER_NETDEV_TX, 559 564 TRIGGER_NETDEV_RX, 560 565