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

leds: trigger: Introduce a NETDEV trigger

This commit introduces a NETDEV trigger for named device
activity. Available triggers are link, rx, and tx.

Signed-off-by: Ben Whitten <ben.whitten@gmail.com>
Acked-by: Pavel Machek <pavel@ucw.cz>
Signed-off-by: Jacek Anaszewski <jacek.anaszewski@gmail.com>

authored by

Ben Whitten and committed by
Jacek Anaszewski
06f502f5 e0d42298

+549
+45
Documentation/ABI/testing/sysfs-class-led-trigger-netdev
··· 1 + What: /sys/class/leds/<led>/device_name 2 + Date: Dec 2017 3 + KernelVersion: 4.16 4 + Contact: linux-leds@vger.kernel.org 5 + Description: 6 + Specifies the network device name to monitor. 7 + 8 + What: /sys/class/leds/<led>/interval 9 + Date: Dec 2017 10 + KernelVersion: 4.16 11 + Contact: linux-leds@vger.kernel.org 12 + Description: 13 + Specifies the duration of the LED blink in milliseconds. 14 + Defaults to 50 ms. 15 + 16 + What: /sys/class/leds/<led>/link 17 + Date: Dec 2017 18 + KernelVersion: 4.16 19 + Contact: linux-leds@vger.kernel.org 20 + Description: 21 + Signal the link state of the named network device. 22 + If set to 0 (default), the LED's normal state is off. 23 + If set to 1, the LED's normal state reflects the link state 24 + of the named network device. 25 + Setting this value also immediately changes the LED state. 26 + 27 + What: /sys/class/leds/<led>/tx 28 + Date: Dec 2017 29 + KernelVersion: 4.16 30 + Contact: linux-leds@vger.kernel.org 31 + Description: 32 + Signal transmission of data on the named network device. 33 + If set to 0 (default), the LED will not blink on transmission. 34 + If set to 1, the LED will blink for the milliseconds specified 35 + in interval to signal transmission. 36 + 37 + What: /sys/class/leds/<led>/rx 38 + Date: Dec 2017 39 + KernelVersion: 4.16 40 + Contact: linux-leds@vger.kernel.org 41 + Description: 42 + Signal reception of data on the named network device. 43 + If set to 0 (default), the LED will not blink on reception. 44 + If set to 1, the LED will blink for the milliseconds specified 45 + in interval to signal reception.
+7
drivers/leds/trigger/Kconfig
··· 135 135 a different trigger. 136 136 If unsure, say Y. 137 137 138 + config LEDS_TRIGGER_NETDEV 139 + tristate "LED Netdev Trigger" 140 + depends on NET && LEDS_TRIGGERS 141 + help 142 + This allows LEDs to be controlled by network device activity. 143 + If unsure, say Y. 144 + 138 145 endif # LEDS_TRIGGERS
+1
drivers/leds/trigger/Makefile
··· 12 12 obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT) += ledtrig-transient.o 13 13 obj-$(CONFIG_LEDS_TRIGGER_CAMERA) += ledtrig-camera.o 14 14 obj-$(CONFIG_LEDS_TRIGGER_PANIC) += ledtrig-panic.o 15 + obj-$(CONFIG_LEDS_TRIGGER_NETDEV) += ledtrig-netdev.o
+496
drivers/leds/trigger/ledtrig-netdev.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // Copyright 2017 Ben Whitten <ben.whitten@gmail.com> 3 + // Copyright 2007 Oliver Jowett <oliver@opencloud.com> 4 + // 5 + // LED Kernel Netdev Trigger 6 + // 7 + // Toggles the LED to reflect the link and traffic state of a named net device 8 + // 9 + // Derived from ledtrig-timer.c which is: 10 + // Copyright 2005-2006 Openedhand Ltd. 11 + // Author: Richard Purdie <rpurdie@openedhand.com> 12 + 13 + #include <linux/atomic.h> 14 + #include <linux/ctype.h> 15 + #include <linux/device.h> 16 + #include <linux/init.h> 17 + #include <linux/jiffies.h> 18 + #include <linux/kernel.h> 19 + #include <linux/leds.h> 20 + #include <linux/list.h> 21 + #include <linux/module.h> 22 + #include <linux/netdevice.h> 23 + #include <linux/spinlock.h> 24 + #include <linux/timer.h> 25 + #include "../leds.h" 26 + 27 + /* 28 + * Configurable sysfs attributes: 29 + * 30 + * device_name - network device name to monitor 31 + * interval - duration of LED blink, in milliseconds 32 + * link - LED's normal state reflects whether the link is up 33 + * (has carrier) or not 34 + * tx - LED blinks on transmitted data 35 + * rx - LED blinks on receive data 36 + * 37 + */ 38 + 39 + struct led_netdev_data { 40 + spinlock_t lock; 41 + 42 + struct delayed_work work; 43 + struct notifier_block notifier; 44 + 45 + struct led_classdev *led_cdev; 46 + struct net_device *net_dev; 47 + 48 + char device_name[IFNAMSIZ]; 49 + atomic_t interval; 50 + unsigned int last_activity; 51 + 52 + unsigned long mode; 53 + #define NETDEV_LED_LINK 0 54 + #define NETDEV_LED_TX 1 55 + #define NETDEV_LED_RX 2 56 + #define NETDEV_LED_MODE_LINKUP 3 57 + }; 58 + 59 + enum netdev_led_attr { 60 + NETDEV_ATTR_LINK, 61 + NETDEV_ATTR_TX, 62 + NETDEV_ATTR_RX 63 + }; 64 + 65 + static void set_baseline_state(struct led_netdev_data *trigger_data) 66 + { 67 + int current_brightness; 68 + struct led_classdev *led_cdev = trigger_data->led_cdev; 69 + 70 + current_brightness = led_cdev->brightness; 71 + if (current_brightness) 72 + led_cdev->blink_brightness = current_brightness; 73 + if (!led_cdev->blink_brightness) 74 + led_cdev->blink_brightness = led_cdev->max_brightness; 75 + 76 + if (!test_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode)) 77 + led_set_brightness(led_cdev, LED_OFF); 78 + else { 79 + if (test_bit(NETDEV_LED_LINK, &trigger_data->mode)) 80 + led_set_brightness(led_cdev, 81 + led_cdev->blink_brightness); 82 + else 83 + led_set_brightness(led_cdev, LED_OFF); 84 + 85 + /* If we are looking for RX/TX start periodically 86 + * checking stats 87 + */ 88 + if (test_bit(NETDEV_LED_TX, &trigger_data->mode) || 89 + test_bit(NETDEV_LED_RX, &trigger_data->mode)) 90 + schedule_delayed_work(&trigger_data->work, 0); 91 + } 92 + } 93 + 94 + static ssize_t device_name_show(struct device *dev, 95 + struct device_attribute *attr, char *buf) 96 + { 97 + struct led_classdev *led_cdev = dev_get_drvdata(dev); 98 + struct led_netdev_data *trigger_data = led_cdev->trigger_data; 99 + ssize_t len; 100 + 101 + spin_lock_bh(&trigger_data->lock); 102 + len = sprintf(buf, "%s\n", trigger_data->device_name); 103 + spin_unlock_bh(&trigger_data->lock); 104 + 105 + return len; 106 + } 107 + 108 + static ssize_t device_name_store(struct device *dev, 109 + struct device_attribute *attr, const char *buf, 110 + size_t size) 111 + { 112 + struct led_classdev *led_cdev = dev_get_drvdata(dev); 113 + struct led_netdev_data *trigger_data = led_cdev->trigger_data; 114 + 115 + if (size >= IFNAMSIZ) 116 + return -EINVAL; 117 + 118 + cancel_delayed_work_sync(&trigger_data->work); 119 + 120 + spin_lock_bh(&trigger_data->lock); 121 + 122 + if (trigger_data->net_dev) { 123 + dev_put(trigger_data->net_dev); 124 + trigger_data->net_dev = NULL; 125 + } 126 + 127 + strncpy(trigger_data->device_name, buf, size); 128 + if (size > 0 && trigger_data->device_name[size - 1] == '\n') 129 + trigger_data->device_name[size - 1] = 0; 130 + 131 + if (trigger_data->device_name[0] != 0) 132 + trigger_data->net_dev = 133 + dev_get_by_name(&init_net, trigger_data->device_name); 134 + 135 + clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); 136 + if (trigger_data->net_dev != NULL) 137 + if (netif_carrier_ok(trigger_data->net_dev)) 138 + set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); 139 + 140 + trigger_data->last_activity = 0; 141 + 142 + set_baseline_state(trigger_data); 143 + spin_unlock_bh(&trigger_data->lock); 144 + 145 + return size; 146 + } 147 + 148 + static DEVICE_ATTR_RW(device_name); 149 + 150 + static ssize_t netdev_led_attr_show(struct device *dev, char *buf, 151 + enum netdev_led_attr attr) 152 + { 153 + struct led_classdev *led_cdev = dev_get_drvdata(dev); 154 + struct led_netdev_data *trigger_data = led_cdev->trigger_data; 155 + int bit; 156 + 157 + switch (attr) { 158 + case NETDEV_ATTR_LINK: 159 + bit = NETDEV_LED_LINK; 160 + break; 161 + case NETDEV_ATTR_TX: 162 + bit = NETDEV_LED_TX; 163 + break; 164 + case NETDEV_ATTR_RX: 165 + bit = NETDEV_LED_RX; 166 + break; 167 + default: 168 + return -EINVAL; 169 + } 170 + 171 + return sprintf(buf, "%u\n", test_bit(bit, &trigger_data->mode)); 172 + } 173 + 174 + static ssize_t netdev_led_attr_store(struct device *dev, const char *buf, 175 + size_t size, enum netdev_led_attr attr) 176 + { 177 + struct led_classdev *led_cdev = dev_get_drvdata(dev); 178 + struct led_netdev_data *trigger_data = led_cdev->trigger_data; 179 + unsigned long state; 180 + int ret; 181 + int bit; 182 + 183 + ret = kstrtoul(buf, 0, &state); 184 + if (ret) 185 + return ret; 186 + 187 + switch (attr) { 188 + case NETDEV_ATTR_LINK: 189 + bit = NETDEV_LED_LINK; 190 + break; 191 + case NETDEV_ATTR_TX: 192 + bit = NETDEV_LED_TX; 193 + break; 194 + case NETDEV_ATTR_RX: 195 + bit = NETDEV_LED_RX; 196 + break; 197 + default: 198 + return -EINVAL; 199 + } 200 + 201 + cancel_delayed_work_sync(&trigger_data->work); 202 + 203 + if (state) 204 + set_bit(bit, &trigger_data->mode); 205 + else 206 + clear_bit(bit, &trigger_data->mode); 207 + 208 + set_baseline_state(trigger_data); 209 + 210 + return size; 211 + } 212 + 213 + static ssize_t link_show(struct device *dev, 214 + struct device_attribute *attr, char *buf) 215 + { 216 + return netdev_led_attr_show(dev, buf, NETDEV_ATTR_LINK); 217 + } 218 + 219 + static ssize_t link_store(struct device *dev, 220 + struct device_attribute *attr, const char *buf, size_t size) 221 + { 222 + return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_LINK); 223 + } 224 + 225 + static DEVICE_ATTR_RW(link); 226 + 227 + static ssize_t tx_show(struct device *dev, 228 + struct device_attribute *attr, char *buf) 229 + { 230 + return netdev_led_attr_show(dev, buf, NETDEV_ATTR_TX); 231 + } 232 + 233 + static ssize_t tx_store(struct device *dev, 234 + struct device_attribute *attr, const char *buf, size_t size) 235 + { 236 + return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_TX); 237 + } 238 + 239 + static DEVICE_ATTR_RW(tx); 240 + 241 + static ssize_t rx_show(struct device *dev, 242 + struct device_attribute *attr, char *buf) 243 + { 244 + return netdev_led_attr_show(dev, buf, NETDEV_ATTR_RX); 245 + } 246 + 247 + static ssize_t rx_store(struct device *dev, 248 + struct device_attribute *attr, const char *buf, size_t size) 249 + { 250 + return netdev_led_attr_store(dev, buf, size, NETDEV_ATTR_RX); 251 + } 252 + 253 + static DEVICE_ATTR_RW(rx); 254 + 255 + static ssize_t interval_show(struct device *dev, 256 + struct device_attribute *attr, char *buf) 257 + { 258 + struct led_classdev *led_cdev = dev_get_drvdata(dev); 259 + struct led_netdev_data *trigger_data = led_cdev->trigger_data; 260 + 261 + return sprintf(buf, "%u\n", 262 + jiffies_to_msecs(atomic_read(&trigger_data->interval))); 263 + } 264 + 265 + static ssize_t interval_store(struct device *dev, 266 + struct device_attribute *attr, const char *buf, 267 + size_t size) 268 + { 269 + struct led_classdev *led_cdev = dev_get_drvdata(dev); 270 + struct led_netdev_data *trigger_data = led_cdev->trigger_data; 271 + unsigned long value; 272 + int ret; 273 + 274 + ret = kstrtoul(buf, 0, &value); 275 + if (ret) 276 + return ret; 277 + 278 + /* impose some basic bounds on the timer interval */ 279 + if (value >= 5 && value <= 10000) { 280 + cancel_delayed_work_sync(&trigger_data->work); 281 + 282 + atomic_set(&trigger_data->interval, msecs_to_jiffies(value)); 283 + set_baseline_state(trigger_data); /* resets timer */ 284 + } 285 + 286 + return size; 287 + } 288 + 289 + static DEVICE_ATTR_RW(interval); 290 + 291 + static int netdev_trig_notify(struct notifier_block *nb, 292 + unsigned long evt, void *dv) 293 + { 294 + struct net_device *dev = 295 + netdev_notifier_info_to_dev((struct netdev_notifier_info *)dv); 296 + struct led_netdev_data *trigger_data = container_of(nb, 297 + struct 298 + led_netdev_data, 299 + notifier); 300 + 301 + if (evt != NETDEV_UP && evt != NETDEV_DOWN && evt != NETDEV_CHANGE 302 + && evt != NETDEV_REGISTER && evt != NETDEV_UNREGISTER 303 + && evt != NETDEV_CHANGENAME) 304 + return NOTIFY_DONE; 305 + 306 + if (strcmp(dev->name, trigger_data->device_name)) 307 + return NOTIFY_DONE; 308 + 309 + cancel_delayed_work_sync(&trigger_data->work); 310 + 311 + spin_lock_bh(&trigger_data->lock); 312 + 313 + clear_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); 314 + switch (evt) { 315 + case NETDEV_REGISTER: 316 + if (trigger_data->net_dev) 317 + dev_put(trigger_data->net_dev); 318 + dev_hold(dev); 319 + trigger_data->net_dev = dev; 320 + break; 321 + case NETDEV_CHANGENAME: 322 + case NETDEV_UNREGISTER: 323 + if (trigger_data->net_dev) { 324 + dev_put(trigger_data->net_dev); 325 + trigger_data->net_dev = NULL; 326 + } 327 + break; 328 + case NETDEV_UP: 329 + case NETDEV_CHANGE: 330 + if (netif_carrier_ok(dev)) 331 + set_bit(NETDEV_LED_MODE_LINKUP, &trigger_data->mode); 332 + break; 333 + } 334 + 335 + set_baseline_state(trigger_data); 336 + 337 + spin_unlock_bh(&trigger_data->lock); 338 + 339 + return NOTIFY_DONE; 340 + } 341 + 342 + /* here's the real work! */ 343 + static void netdev_trig_work(struct work_struct *work) 344 + { 345 + struct led_netdev_data *trigger_data = container_of(work, 346 + struct 347 + led_netdev_data, 348 + work.work); 349 + struct rtnl_link_stats64 *dev_stats; 350 + unsigned int new_activity; 351 + struct rtnl_link_stats64 temp; 352 + unsigned long interval; 353 + int invert; 354 + 355 + /* If we dont have a device, insure we are off */ 356 + if (!trigger_data->net_dev) { 357 + led_set_brightness(trigger_data->led_cdev, LED_OFF); 358 + return; 359 + } 360 + 361 + /* If we are not looking for RX/TX then return */ 362 + if (!test_bit(NETDEV_LED_TX, &trigger_data->mode) && 363 + !test_bit(NETDEV_LED_RX, &trigger_data->mode)) 364 + return; 365 + 366 + dev_stats = dev_get_stats(trigger_data->net_dev, &temp); 367 + new_activity = 368 + (test_bit(NETDEV_LED_TX, &trigger_data->mode) ? 369 + dev_stats->tx_packets : 0) + 370 + (test_bit(NETDEV_LED_RX, &trigger_data->mode) ? 371 + dev_stats->rx_packets : 0); 372 + 373 + if (trigger_data->last_activity != new_activity) { 374 + led_stop_software_blink(trigger_data->led_cdev); 375 + 376 + invert = test_bit(NETDEV_LED_LINK, &trigger_data->mode); 377 + interval = jiffies_to_msecs( 378 + atomic_read(&trigger_data->interval)); 379 + /* base state is ON (link present) */ 380 + led_blink_set_oneshot(trigger_data->led_cdev, 381 + &interval, 382 + &interval, 383 + invert); 384 + trigger_data->last_activity = new_activity; 385 + } 386 + 387 + schedule_delayed_work(&trigger_data->work, 388 + (atomic_read(&trigger_data->interval)*2)); 389 + } 390 + 391 + static void netdev_trig_activate(struct led_classdev *led_cdev) 392 + { 393 + struct led_netdev_data *trigger_data; 394 + int rc; 395 + 396 + trigger_data = kzalloc(sizeof(struct led_netdev_data), GFP_KERNEL); 397 + if (!trigger_data) 398 + return; 399 + 400 + spin_lock_init(&trigger_data->lock); 401 + 402 + trigger_data->notifier.notifier_call = netdev_trig_notify; 403 + trigger_data->notifier.priority = 10; 404 + 405 + INIT_DELAYED_WORK(&trigger_data->work, netdev_trig_work); 406 + 407 + trigger_data->led_cdev = led_cdev; 408 + trigger_data->net_dev = NULL; 409 + trigger_data->device_name[0] = 0; 410 + 411 + trigger_data->mode = 0; 412 + atomic_set(&trigger_data->interval, msecs_to_jiffies(50)); 413 + trigger_data->last_activity = 0; 414 + 415 + led_cdev->trigger_data = trigger_data; 416 + 417 + rc = device_create_file(led_cdev->dev, &dev_attr_device_name); 418 + if (rc) 419 + goto err_out; 420 + rc = device_create_file(led_cdev->dev, &dev_attr_link); 421 + if (rc) 422 + goto err_out_device_name; 423 + rc = device_create_file(led_cdev->dev, &dev_attr_rx); 424 + if (rc) 425 + goto err_out_link; 426 + rc = device_create_file(led_cdev->dev, &dev_attr_tx); 427 + if (rc) 428 + goto err_out_rx; 429 + rc = device_create_file(led_cdev->dev, &dev_attr_interval); 430 + if (rc) 431 + goto err_out_tx; 432 + rc = register_netdevice_notifier(&trigger_data->notifier); 433 + if (rc) 434 + goto err_out_interval; 435 + return; 436 + 437 + err_out_interval: 438 + device_remove_file(led_cdev->dev, &dev_attr_interval); 439 + err_out_tx: 440 + device_remove_file(led_cdev->dev, &dev_attr_tx); 441 + err_out_rx: 442 + device_remove_file(led_cdev->dev, &dev_attr_rx); 443 + err_out_link: 444 + device_remove_file(led_cdev->dev, &dev_attr_link); 445 + err_out_device_name: 446 + device_remove_file(led_cdev->dev, &dev_attr_device_name); 447 + err_out: 448 + led_cdev->trigger_data = NULL; 449 + kfree(trigger_data); 450 + } 451 + 452 + static void netdev_trig_deactivate(struct led_classdev *led_cdev) 453 + { 454 + struct led_netdev_data *trigger_data = led_cdev->trigger_data; 455 + 456 + if (trigger_data) { 457 + unregister_netdevice_notifier(&trigger_data->notifier); 458 + 459 + device_remove_file(led_cdev->dev, &dev_attr_device_name); 460 + device_remove_file(led_cdev->dev, &dev_attr_link); 461 + device_remove_file(led_cdev->dev, &dev_attr_rx); 462 + device_remove_file(led_cdev->dev, &dev_attr_tx); 463 + device_remove_file(led_cdev->dev, &dev_attr_interval); 464 + 465 + cancel_delayed_work_sync(&trigger_data->work); 466 + 467 + if (trigger_data->net_dev) 468 + dev_put(trigger_data->net_dev); 469 + 470 + kfree(trigger_data); 471 + } 472 + } 473 + 474 + static struct led_trigger netdev_led_trigger = { 475 + .name = "netdev", 476 + .activate = netdev_trig_activate, 477 + .deactivate = netdev_trig_deactivate, 478 + }; 479 + 480 + static int __init netdev_trig_init(void) 481 + { 482 + return led_trigger_register(&netdev_led_trigger); 483 + } 484 + 485 + static void __exit netdev_trig_exit(void) 486 + { 487 + led_trigger_unregister(&netdev_led_trigger); 488 + } 489 + 490 + module_init(netdev_trig_init); 491 + module_exit(netdev_trig_exit); 492 + 493 + MODULE_AUTHOR("Ben Whitten <ben.whitten@gmail.com>"); 494 + MODULE_AUTHOR("Oliver Jowett <oliver@opencloud.com>"); 495 + MODULE_DESCRIPTION("Netdev LED trigger"); 496 + MODULE_LICENSE("GPL v2");