All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] leds/trigger/activity: add a system activity LED trigger
@ 2017-02-11 23:41 Willy Tarreau
  2017-02-15 10:24 ` Pavel Machek
  2017-03-09 20:48 ` Jacek Anaszewski
  0 siblings, 2 replies; 11+ messages in thread
From: Willy Tarreau @ 2017-02-11 23:41 UTC (permalink / raw)
  To: Richard Purdie, Jacek Anaszewski, Pavel Machek
  Cc: linux-kernel, linux-leds, Willy Tarreau

The "activity" trigger was inspired by the heartbeat one, but aims at
providing instant indication of the immediate CPU usage. Under idle
condition, it flashes 10ms every second. At 100% usage, it flashes
90ms every 100ms. The blinking frequency increases from 1 to 10 Hz
until either the load is high enough to saturate one CPU core or 50%
load is reached on a single-core system. Then past this point only the
duty cycle increases from 10 to 90%.

This results in a very visible activity reporting allowing one to
immediately tell whether a machine is under load or not, making it
quite suitable to be used in clusters.

Signed-off-by: Willy Tarreau <w@1wt.eu>
---
 drivers/leds/trigger/Kconfig            |   9 +
 drivers/leds/trigger/Makefile           |   1 +
 drivers/leds/trigger/ledtrig-activity.c | 290 ++++++++++++++++++++++++++++++++
 3 files changed, 300 insertions(+)
 create mode 100644 drivers/leds/trigger/ledtrig-activity.c

diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
index 3f9ddb9..8432d9e 100644
--- a/drivers/leds/trigger/Kconfig
+++ b/drivers/leds/trigger/Kconfig
@@ -77,6 +77,15 @@ config LEDS_TRIGGER_CPU
 
 	  If unsure, say N.
 
+config LEDS_TRIGGER_ACTIVITY
+	tristate "LED activity Trigger"
+	depends on LEDS_TRIGGERS
+	help
+	  This allows LEDs to be controlled by a immediate CPU usage.
+	  The flash frequency and duty cycle varies from faint flashes to
+	  intense brightness depending on the instant CPU load.
+	  If unsure, say Y.
+
 config LEDS_TRIGGER_GPIO
 	tristate "LED GPIO Trigger"
 	depends on LEDS_TRIGGERS
diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
index a72c43c..e572ce57 100644
--- a/drivers/leds/trigger/Makefile
+++ b/drivers/leds/trigger/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o
 obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)	+= ledtrig-backlight.o
 obj-$(CONFIG_LEDS_TRIGGER_GPIO)		+= ledtrig-gpio.o
 obj-$(CONFIG_LEDS_TRIGGER_CPU)		+= ledtrig-cpu.o
+obj-$(CONFIG_LEDS_TRIGGER_ACTIVITY)	+= ledtrig-activity.o
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
 obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
 obj-$(CONFIG_LEDS_TRIGGER_CAMERA)	+= ledtrig-camera.o
diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c
new file mode 100644
index 0000000..9acdc56
--- /dev/null
+++ b/drivers/leds/trigger/ledtrig-activity.c
@@ -0,0 +1,290 @@
+/*
+ * Activity LED trigger
+ *
+ * Copyright (C) 2017 Willy Tarreau <w@1wt.eu>
+ * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/leds.h>
+#include <linux/reboot.h>
+#include <linux/suspend.h>
+#include "../leds.h"
+
+static int panic_detected;
+
+struct activity_data {
+	struct timer_list timer;
+	u64 last_idle;
+	u64 last_boot;
+	int time_left;
+	int state;
+	int invert;
+};
+
+static void led_activity_function(unsigned long data)
+{
+	struct led_classdev *led_cdev = (struct led_classdev *)data;
+	struct activity_data *activity_data = led_cdev->trigger_data;
+	struct timespec boot_time;
+	unsigned int target;
+	unsigned int usage;
+	int delay;
+	u64 curr_idle;
+	u64 curr_boot;
+	u32 diff_idle;
+	u32 diff_boot;
+	int cpus;
+	int i;
+
+	if (unlikely(panic_detected)) {
+		/* full brightness in case of panic */
+		led_set_brightness_nosleep(led_cdev, led_cdev->max_brightness);
+		return;
+	}
+
+	get_monotonic_boottime(&boot_time);
+
+	cpus = 0;
+	curr_idle = 0;
+	for_each_possible_cpu(i) {
+		curr_idle += (__force u64)kcpustat_cpu(i).cpustat[CPUTIME_IDLE];
+		curr_idle += (__force u64)kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT];
+		cpus++;
+	}
+
+	curr_boot = div_s64(timespec_to_ns(&boot_time), NSEC_PER_SEC / HZ) * cpus;
+	curr_idle = cputime64_to_jiffies64(curr_idle);
+	diff_boot = curr_boot - activity_data->last_boot;
+	diff_idle = curr_idle - activity_data->last_idle;
+
+	activity_data->last_boot = curr_boot;
+	activity_data->last_idle = curr_idle;
+
+	if (diff_boot <= 0)
+		usage = 0;
+	else if (diff_idle <= diff_boot)
+		usage = 100 - 100 * diff_idle / diff_boot;
+	else
+		usage = 100;
+
+	/*
+	 * Now we know the total boot_time multiplied by the number of CPUs, and
+	 * the total idle+wait time for all CPUs. We'll compare how they evolved
+	 * since last call. The % of overall CPU usage is :
+	 *
+	 *      1 - delta_idle / delta_boot
+	 *
+	 * What we want is that when the CPU usage is zero, the LED must blink
+	 * slowly with very faint flashes that are detectable but not disturbing
+	 * (typically 10ms every second, or 10ms ON, 990ms OFF). Then we want
+	 * blinking frequency to increase up to the point where the load is
+	 * enough to saturate one core in multi-core systems or 50% in single
+	 * core systems. At this point it should reach 10 Hz with a 10/90 duty
+	 * cycle (10ms ON, 90ms OFF). After this point, the blinking frequency
+	 * remains stable (10 Hz) and only the duty cycle increases to report
+	 * the activity, up to the point where we have 90ms ON, 10ms OFF when
+	 * all cores are saturated. It's important that the LED never stays in
+	 * a steady state so that it's easy to distinguish an idle or saturated
+	 * machine from a hung one.
+	 *
+	 * This gives us :
+	 *   - a target CPU usage of min(50%, 100%/#CPU) for a 10% duty cycle
+	 *     (10ms ON, 90ms OFF)
+	 *   - below target :
+	 *      ON_ms  = 10
+	 *      OFF_ms = 90 + (1 - usage/target) * 900
+	 *   - above target :
+	 *      ON_ms  = 10 + (usage-target)/(100%-target) * 80
+	 *      OFF_ms = 90 - (usage-target)/(100%-target) * 80
+	 *
+	 * In order to keep a good responsiveness, we cap the sleep time to
+	 * 100 ms and keep track of the sleep time left. This allows us to
+	 * quickly change it if needed.
+	 */
+
+	activity_data->time_left -= 100;
+	if (activity_data->time_left <= 0) {
+		activity_data->time_left = 0;
+		activity_data->state = !activity_data->state;
+		led_set_brightness_nosleep(led_cdev,
+			(activity_data->state ^ activity_data->invert) ?
+			led_cdev->max_brightness : LED_OFF);
+	}
+
+	target = (cpus > 1) ? (100 / cpus) : 50;
+
+	if (usage < target)
+		delay = activity_data->state ?
+			10 :                        /* ON  */
+			990 - 900 * usage / target; /* OFF */
+	else
+		delay = activity_data->state ?
+			10 + 80 * (usage - target) / (100 - target) : /* ON  */
+			90 - 80 * (usage - target) / (100 - target);  /* OFF */
+
+
+	if (!activity_data->time_left || delay <= activity_data->time_left)
+		activity_data->time_left = delay;
+
+	delay = min_t(int, activity_data->time_left, 100);
+	mod_timer(&activity_data->timer, jiffies + msecs_to_jiffies(delay));
+}
+
+static ssize_t led_invert_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct activity_data *activity_data = led_cdev->trigger_data;
+
+	return sprintf(buf, "%u\n", activity_data->invert);
+}
+
+static ssize_t led_invert_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct activity_data *activity_data = led_cdev->trigger_data;
+	unsigned long state;
+	int ret;
+
+	ret = kstrtoul(buf, 0, &state);
+	if (ret)
+		return ret;
+
+	activity_data->invert = !!state;
+
+	return size;
+}
+
+static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
+
+static void activity_activate(struct led_classdev *led_cdev)
+{
+	struct activity_data *activity_data;
+	int rc;
+
+	activity_data = kzalloc(sizeof(*activity_data), GFP_KERNEL);
+	if (!activity_data)
+		return;
+
+	led_cdev->trigger_data = activity_data;
+	rc = device_create_file(led_cdev->dev, &dev_attr_invert);
+	if (rc) {
+		kfree(led_cdev->trigger_data);
+		return;
+	}
+
+	setup_timer(&activity_data->timer,
+		    led_activity_function, (unsigned long)led_cdev);
+	led_activity_function(activity_data->timer.data);
+	led_cdev->activated = true;
+}
+
+static void activity_deactivate(struct led_classdev *led_cdev)
+{
+	struct activity_data *activity_data = led_cdev->trigger_data;
+
+	if (led_cdev->activated) {
+		del_timer_sync(&activity_data->timer);
+		device_remove_file(led_cdev->dev, &dev_attr_invert);
+		kfree(activity_data);
+		led_cdev->activated = false;
+	}
+}
+
+static struct led_trigger activity_led_trigger = {
+	.name       = "activity",
+	.activate   = activity_activate,
+	.deactivate = activity_deactivate,
+};
+
+static int activity_pm_notifier(struct notifier_block *nb,
+                                unsigned long pm_event, void *unused)
+{
+	int rc;
+
+	switch (pm_event) {
+	case PM_SUSPEND_PREPARE:
+	case PM_HIBERNATION_PREPARE:
+	case PM_RESTORE_PREPARE:
+		led_trigger_unregister(&activity_led_trigger);
+		break;
+	case PM_POST_SUSPEND:
+	case PM_POST_HIBERNATION:
+	case PM_POST_RESTORE:
+		rc = led_trigger_register(&activity_led_trigger);
+		if (rc)
+			pr_err("could not re-register activity trigger\n");
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static int activity_reboot_notifier(struct notifier_block *nb,
+                                    unsigned long code, void *unused)
+{
+	led_trigger_unregister(&activity_led_trigger);
+	return NOTIFY_DONE;
+}
+
+static int activity_panic_notifier(struct notifier_block *nb,
+                                   unsigned long code, void *unused)
+{
+	panic_detected = 1;
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block activity_pm_nb = {
+	.notifier_call = activity_pm_notifier,
+};
+
+static struct notifier_block activity_reboot_nb = {
+	.notifier_call = activity_reboot_notifier,
+};
+
+static struct notifier_block activity_panic_nb = {
+	.notifier_call = activity_panic_notifier,
+};
+
+static int __init activity_init(void)
+{
+	int rc = led_trigger_register(&activity_led_trigger);
+
+	if (!rc) {
+		atomic_notifier_chain_register(&panic_notifier_list,
+					       &activity_panic_nb);
+		register_reboot_notifier(&activity_reboot_nb);
+		register_pm_notifier(&activity_pm_nb);
+	}
+	return rc;
+}
+
+static void __exit activity_exit(void)
+{
+	unregister_pm_notifier(&activity_pm_nb);
+	unregister_reboot_notifier(&activity_reboot_nb);
+	atomic_notifier_chain_unregister(&panic_notifier_list,
+					 &activity_panic_nb);
+	led_trigger_unregister(&activity_led_trigger);
+}
+
+module_init(activity_init);
+module_exit(activity_exit);
+
+MODULE_AUTHOR("Willy Tarreau <w@1wt.eu>");
+MODULE_DESCRIPTION("Activity LED trigger");
+MODULE_LICENSE("GPL");
-- 
2.8.0.rc2.1.gbe9624a

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH] leds/trigger/activity: add a system activity LED trigger
  2017-02-11 23:41 [PATCH] leds/trigger/activity: add a system activity LED trigger Willy Tarreau
@ 2017-02-15 10:24 ` Pavel Machek
  2017-02-15 10:42   ` Willy Tarreau
  2017-03-09 20:48 ` Jacek Anaszewski
  1 sibling, 1 reply; 11+ messages in thread
From: Pavel Machek @ 2017-02-15 10:24 UTC (permalink / raw)
  To: Willy Tarreau; +Cc: Richard Purdie, Jacek Anaszewski, linux-kernel, linux-leds

[-- Attachment #1: Type: text/plain, Size: 12315 bytes --]

On Sun 2017-02-12 00:41:54, Willy Tarreau wrote:
> The "activity" trigger was inspired by the heartbeat one, but aims at
> providing instant indication of the immediate CPU usage. Under idle
> condition, it flashes 10ms every second. At 100% usage, it flashes
> 90ms every 100ms. The blinking frequency increases from 1 to 10 Hz
> until either the load is high enough to saturate one CPU core or 50%
> load is reached on a single-core system. Then past this point only the
> duty cycle increases from 10 to 90%.
> 
> This results in a very visible activity reporting allowing one to
> immediately tell whether a machine is under load or not, making it
> quite suitable to be used in clusters.
> 
> Signed-off-by: Willy Tarreau <w@1wt.eu>

Hmm. Evil question. Why not use LEDS_TRIGGER_CPU instead?

Recently it gained support for "summarizing" all the cpus onto one
led.

								Pavel

> ---
>  drivers/leds/trigger/Kconfig            |   9 +
>  drivers/leds/trigger/Makefile           |   1 +
>  drivers/leds/trigger/ledtrig-activity.c | 290 ++++++++++++++++++++++++++++++++
>  3 files changed, 300 insertions(+)
>  create mode 100644 drivers/leds/trigger/ledtrig-activity.c
> 
> diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
> index 3f9ddb9..8432d9e 100644
> --- a/drivers/leds/trigger/Kconfig
> +++ b/drivers/leds/trigger/Kconfig
> @@ -77,6 +77,15 @@ config LEDS_TRIGGER_CPU
>  
>  	  If unsure, say N.
>  
> +config LEDS_TRIGGER_ACTIVITY
> +	tristate "LED activity Trigger"
> +	depends on LEDS_TRIGGERS
> +	help
> +	  This allows LEDs to be controlled by a immediate CPU usage.
> +	  The flash frequency and duty cycle varies from faint flashes to
> +	  intense brightness depending on the instant CPU load.
> +	  If unsure, say Y.
> +
>  config LEDS_TRIGGER_GPIO
>  	tristate "LED GPIO Trigger"
>  	depends on LEDS_TRIGGERS
> diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
> index a72c43c..e572ce57 100644
> --- a/drivers/leds/trigger/Makefile
> +++ b/drivers/leds/trigger/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o
>  obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)	+= ledtrig-backlight.o
>  obj-$(CONFIG_LEDS_TRIGGER_GPIO)		+= ledtrig-gpio.o
>  obj-$(CONFIG_LEDS_TRIGGER_CPU)		+= ledtrig-cpu.o
> +obj-$(CONFIG_LEDS_TRIGGER_ACTIVITY)	+= ledtrig-activity.o
>  obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
>  obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
>  obj-$(CONFIG_LEDS_TRIGGER_CAMERA)	+= ledtrig-camera.o
> diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c
> new file mode 100644
> index 0000000..9acdc56
> --- /dev/null
> +++ b/drivers/leds/trigger/ledtrig-activity.c
> @@ -0,0 +1,290 @@
> +/*
> + * Activity LED trigger
> + *
> + * Copyright (C) 2017 Willy Tarreau <w@1wt.eu>
> + * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/kernel_stat.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/timer.h>
> +#include <linux/sched.h>
> +#include <linux/leds.h>
> +#include <linux/reboot.h>
> +#include <linux/suspend.h>
> +#include "../leds.h"
> +
> +static int panic_detected;
> +
> +struct activity_data {
> +	struct timer_list timer;
> +	u64 last_idle;
> +	u64 last_boot;
> +	int time_left;
> +	int state;
> +	int invert;
> +};
> +
> +static void led_activity_function(unsigned long data)
> +{
> +	struct led_classdev *led_cdev = (struct led_classdev *)data;
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +	struct timespec boot_time;
> +	unsigned int target;
> +	unsigned int usage;
> +	int delay;
> +	u64 curr_idle;
> +	u64 curr_boot;
> +	u32 diff_idle;
> +	u32 diff_boot;
> +	int cpus;
> +	int i;
> +
> +	if (unlikely(panic_detected)) {
> +		/* full brightness in case of panic */
> +		led_set_brightness_nosleep(led_cdev, led_cdev->max_brightness);
> +		return;
> +	}
> +
> +	get_monotonic_boottime(&boot_time);
> +
> +	cpus = 0;
> +	curr_idle = 0;
> +	for_each_possible_cpu(i) {
> +		curr_idle += (__force u64)kcpustat_cpu(i).cpustat[CPUTIME_IDLE];
> +		curr_idle += (__force u64)kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT];
> +		cpus++;
> +	}
> +
> +	curr_boot = div_s64(timespec_to_ns(&boot_time), NSEC_PER_SEC / HZ) * cpus;
> +	curr_idle = cputime64_to_jiffies64(curr_idle);
> +	diff_boot = curr_boot - activity_data->last_boot;
> +	diff_idle = curr_idle - activity_data->last_idle;
> +
> +	activity_data->last_boot = curr_boot;
> +	activity_data->last_idle = curr_idle;
> +
> +	if (diff_boot <= 0)
> +		usage = 0;
> +	else if (diff_idle <= diff_boot)
> +		usage = 100 - 100 * diff_idle / diff_boot;
> +	else
> +		usage = 100;
> +
> +	/*
> +	 * Now we know the total boot_time multiplied by the number of CPUs, and
> +	 * the total idle+wait time for all CPUs. We'll compare how they evolved
> +	 * since last call. The % of overall CPU usage is :
> +	 *
> +	 *      1 - delta_idle / delta_boot
> +	 *
> +	 * What we want is that when the CPU usage is zero, the LED must blink
> +	 * slowly with very faint flashes that are detectable but not disturbing
> +	 * (typically 10ms every second, or 10ms ON, 990ms OFF). Then we want
> +	 * blinking frequency to increase up to the point where the load is
> +	 * enough to saturate one core in multi-core systems or 50% in single
> +	 * core systems. At this point it should reach 10 Hz with a 10/90 duty
> +	 * cycle (10ms ON, 90ms OFF). After this point, the blinking frequency
> +	 * remains stable (10 Hz) and only the duty cycle increases to report
> +	 * the activity, up to the point where we have 90ms ON, 10ms OFF when
> +	 * all cores are saturated. It's important that the LED never stays in
> +	 * a steady state so that it's easy to distinguish an idle or saturated
> +	 * machine from a hung one.
> +	 *
> +	 * This gives us :
> +	 *   - a target CPU usage of min(50%, 100%/#CPU) for a 10% duty cycle
> +	 *     (10ms ON, 90ms OFF)
> +	 *   - below target :
> +	 *      ON_ms  = 10
> +	 *      OFF_ms = 90 + (1 - usage/target) * 900
> +	 *   - above target :
> +	 *      ON_ms  = 10 + (usage-target)/(100%-target) * 80
> +	 *      OFF_ms = 90 - (usage-target)/(100%-target) * 80
> +	 *
> +	 * In order to keep a good responsiveness, we cap the sleep time to
> +	 * 100 ms and keep track of the sleep time left. This allows us to
> +	 * quickly change it if needed.
> +	 */
> +
> +	activity_data->time_left -= 100;
> +	if (activity_data->time_left <= 0) {
> +		activity_data->time_left = 0;
> +		activity_data->state = !activity_data->state;
> +		led_set_brightness_nosleep(led_cdev,
> +			(activity_data->state ^ activity_data->invert) ?
> +			led_cdev->max_brightness : LED_OFF);
> +	}
> +
> +	target = (cpus > 1) ? (100 / cpus) : 50;
> +
> +	if (usage < target)
> +		delay = activity_data->state ?
> +			10 :                        /* ON  */
> +			990 - 900 * usage / target; /* OFF */
> +	else
> +		delay = activity_data->state ?
> +			10 + 80 * (usage - target) / (100 - target) : /* ON  */
> +			90 - 80 * (usage - target) / (100 - target);  /* OFF */
> +
> +
> +	if (!activity_data->time_left || delay <= activity_data->time_left)
> +		activity_data->time_left = delay;
> +
> +	delay = min_t(int, activity_data->time_left, 100);
> +	mod_timer(&activity_data->timer, jiffies + msecs_to_jiffies(delay));
> +}
> +
> +static ssize_t led_invert_show(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +
> +	return sprintf(buf, "%u\n", activity_data->invert);
> +}
> +
> +static ssize_t led_invert_store(struct device *dev,
> +                                struct device_attribute *attr,
> +                                const char *buf, size_t size)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +	unsigned long state;
> +	int ret;
> +
> +	ret = kstrtoul(buf, 0, &state);
> +	if (ret)
> +		return ret;
> +
> +	activity_data->invert = !!state;
> +
> +	return size;
> +}
> +
> +static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
> +
> +static void activity_activate(struct led_classdev *led_cdev)
> +{
> +	struct activity_data *activity_data;
> +	int rc;
> +
> +	activity_data = kzalloc(sizeof(*activity_data), GFP_KERNEL);
> +	if (!activity_data)
> +		return;
> +
> +	led_cdev->trigger_data = activity_data;
> +	rc = device_create_file(led_cdev->dev, &dev_attr_invert);
> +	if (rc) {
> +		kfree(led_cdev->trigger_data);
> +		return;
> +	}
> +
> +	setup_timer(&activity_data->timer,
> +		    led_activity_function, (unsigned long)led_cdev);
> +	led_activity_function(activity_data->timer.data);
> +	led_cdev->activated = true;
> +}
> +
> +static void activity_deactivate(struct led_classdev *led_cdev)
> +{
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +
> +	if (led_cdev->activated) {
> +		del_timer_sync(&activity_data->timer);
> +		device_remove_file(led_cdev->dev, &dev_attr_invert);
> +		kfree(activity_data);
> +		led_cdev->activated = false;
> +	}
> +}
> +
> +static struct led_trigger activity_led_trigger = {
> +	.name       = "activity",
> +	.activate   = activity_activate,
> +	.deactivate = activity_deactivate,
> +};
> +
> +static int activity_pm_notifier(struct notifier_block *nb,
> +                                unsigned long pm_event, void *unused)
> +{
> +	int rc;
> +
> +	switch (pm_event) {
> +	case PM_SUSPEND_PREPARE:
> +	case PM_HIBERNATION_PREPARE:
> +	case PM_RESTORE_PREPARE:
> +		led_trigger_unregister(&activity_led_trigger);
> +		break;
> +	case PM_POST_SUSPEND:
> +	case PM_POST_HIBERNATION:
> +	case PM_POST_RESTORE:
> +		rc = led_trigger_register(&activity_led_trigger);
> +		if (rc)
> +			pr_err("could not re-register activity trigger\n");
> +		break;
> +	default:
> +		break;
> +	}
> +	return NOTIFY_DONE;
> +}
> +
> +static int activity_reboot_notifier(struct notifier_block *nb,
> +                                    unsigned long code, void *unused)
> +{
> +	led_trigger_unregister(&activity_led_trigger);
> +	return NOTIFY_DONE;
> +}
> +
> +static int activity_panic_notifier(struct notifier_block *nb,
> +                                   unsigned long code, void *unused)
> +{
> +	panic_detected = 1;
> +	return NOTIFY_DONE;
> +}
> +
> +static struct notifier_block activity_pm_nb = {
> +	.notifier_call = activity_pm_notifier,
> +};
> +
> +static struct notifier_block activity_reboot_nb = {
> +	.notifier_call = activity_reboot_notifier,
> +};
> +
> +static struct notifier_block activity_panic_nb = {
> +	.notifier_call = activity_panic_notifier,
> +};
> +
> +static int __init activity_init(void)
> +{
> +	int rc = led_trigger_register(&activity_led_trigger);
> +
> +	if (!rc) {
> +		atomic_notifier_chain_register(&panic_notifier_list,
> +					       &activity_panic_nb);
> +		register_reboot_notifier(&activity_reboot_nb);
> +		register_pm_notifier(&activity_pm_nb);
> +	}
> +	return rc;
> +}
> +
> +static void __exit activity_exit(void)
> +{
> +	unregister_pm_notifier(&activity_pm_nb);
> +	unregister_reboot_notifier(&activity_reboot_nb);
> +	atomic_notifier_chain_unregister(&panic_notifier_list,
> +					 &activity_panic_nb);
> +	led_trigger_unregister(&activity_led_trigger);
> +}
> +
> +module_init(activity_init);
> +module_exit(activity_exit);
> +
> +MODULE_AUTHOR("Willy Tarreau <w@1wt.eu>");
> +MODULE_DESCRIPTION("Activity LED trigger");
> +MODULE_LICENSE("GPL");

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] leds/trigger/activity: add a system activity LED trigger
  2017-02-15 10:24 ` Pavel Machek
@ 2017-02-15 10:42   ` Willy Tarreau
  2017-02-15 21:10     ` Pavel Machek
  0 siblings, 1 reply; 11+ messages in thread
From: Willy Tarreau @ 2017-02-15 10:42 UTC (permalink / raw)
  To: Pavel Machek; +Cc: Richard Purdie, Jacek Anaszewski, linux-kernel, linux-leds

Hi Pavel!

On Wed, Feb 15, 2017 at 11:24:26AM +0100, Pavel Machek wrote:
> On Sun 2017-02-12 00:41:54, Willy Tarreau wrote:
> > The "activity" trigger was inspired by the heartbeat one, but aims at
> > providing instant indication of the immediate CPU usage. Under idle
> > condition, it flashes 10ms every second. At 100% usage, it flashes
> > 90ms every 100ms. The blinking frequency increases from 1 to 10 Hz
> > until either the load is high enough to saturate one CPU core or 50%
> > load is reached on a single-core system. Then past this point only the
> > duty cycle increases from 10 to 90%.
> > 
> > This results in a very visible activity reporting allowing one to
> > immediately tell whether a machine is under load or not, making it
> > quite suitable to be used in clusters.
> > 
> > Signed-off-by: Willy Tarreau <w@1wt.eu>
> 
> Hmm. Evil question. Why not use LEDS_TRIGGER_CPU instead?
> 
> Recently it gained support for "summarizing" all the cpus onto one
> led.

That's not an evil question, it's perfectly correct as it's the first
one I've tried :-) But it's basically an all on or all off report, you
see if the CPU is instantly being used or not. Also, when the CPU is
idle you have no way to tell the machine is not dead, and when it's
saturated you have no way to check it's not stuck. In the end I found
the lack of progressivity in the visual report to be very problematic
for my typical use case where I want to be able to spot in one second
if a machine in my build farm is under-loaded.

I thought about modifying the cpu trigger to support a different mode
of reporting but I noticed that the two approaches are quite different
and very likely suit different purposes, even if there can be some
overlap for a number of use cases. I think that most users just want
to see if something is running or draining their battery and CPU is
better suited there. But to differenciate between 10, 50 and 100%
usage, it really is not (at least for me).

Cheers,
Willy

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] leds/trigger/activity: add a system activity LED trigger
  2017-02-15 10:42   ` Willy Tarreau
@ 2017-02-15 21:10     ` Pavel Machek
  0 siblings, 0 replies; 11+ messages in thread
From: Pavel Machek @ 2017-02-15 21:10 UTC (permalink / raw)
  To: Willy Tarreau; +Cc: Richard Purdie, Jacek Anaszewski, linux-kernel, linux-leds

[-- Attachment #1: Type: text/plain, Size: 2492 bytes --]

Hi!

> On Wed, Feb 15, 2017 at 11:24:26AM +0100, Pavel Machek wrote:
> > On Sun 2017-02-12 00:41:54, Willy Tarreau wrote:
> > > The "activity" trigger was inspired by the heartbeat one, but aims at
> > > providing instant indication of the immediate CPU usage. Under idle
> > > condition, it flashes 10ms every second. At 100% usage, it flashes
> > > 90ms every 100ms. The blinking frequency increases from 1 to 10 Hz
> > > until either the load is high enough to saturate one CPU core or 50%
> > > load is reached on a single-core system. Then past this point only the
> > > duty cycle increases from 10 to 90%.
> > > 
> > > This results in a very visible activity reporting allowing one to
> > > immediately tell whether a machine is under load or not, making it
> > > quite suitable to be used in clusters.
> > > 
> > > Signed-off-by: Willy Tarreau <w@1wt.eu>
> > 
> > Hmm. Evil question. Why not use LEDS_TRIGGER_CPU instead?
> > 
> > Recently it gained support for "summarizing" all the cpus onto one
> > led.
> 
> That's not an evil question, it's perfectly correct as it's the first
> one I've tried :-) But it's basically an all on or all off report, you
> see if the CPU is instantly being used or not. Also, when the CPU is
> idle you have no way to tell the machine is not dead, and when it's
> saturated you have no way to check it's not stuck. In the end I found
> the lack of progressivity in the visual report to be very problematic
> for my typical use case where I want to be able to spot in one second
> if a machine in my build farm is under-loaded.

Well, when I used this kind of LED, it usually did flicker even on
"idle" systems, because system is never idle. But you are right, it is
easy for 100% cpu to be used, and then you could not tell if it is stuck.

> I thought about modifying the cpu trigger to support a different mode
> of reporting but I noticed that the two approaches are quite different
> and very likely suit different purposes, even if there can be some
> overlap for a number of use cases. I think that most users just want
> to see if something is running or draining their battery and CPU is
> better suited there. But to differenciate between 10, 50 and 100%
> usage, it really is not (at least for me).

...so this makes sense.

Best regards,
								Pavel
-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 181 bytes --]

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] leds/trigger/activity: add a system activity LED trigger
  2017-02-11 23:41 [PATCH] leds/trigger/activity: add a system activity LED trigger Willy Tarreau
  2017-02-15 10:24 ` Pavel Machek
@ 2017-03-09 20:48 ` Jacek Anaszewski
  2017-03-10  6:44   ` Willy Tarreau
  2017-08-24 12:07   ` Willy Tarreau
  1 sibling, 2 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2017-03-09 20:48 UTC (permalink / raw)
  To: Willy Tarreau, Richard Purdie, Pavel Machek; +Cc: linux-kernel, linux-leds

Hi Willy,

Thanks for the patch.

Unfortunately I'm getting following build break, while trying to
test it on recent linux-next:

drivers/leds/trigger/ledtrig-activity.c: In function
'led_activity_function':
drivers/leds/trigger/ledtrig-activity.c:67:14: error: implicit
declaration of function 'cputime64_to_jiffies64'
[-Werror=implicit-function-declaration]
  curr_idle = cputime64_to_jiffies64(curr_idle);

Best regards,
Jacek Anaszewski


On 02/12/2017 12:41 AM, Willy Tarreau wrote:
> The "activity" trigger was inspired by the heartbeat one, but aims at
> providing instant indication of the immediate CPU usage. Under idle
> condition, it flashes 10ms every second. At 100% usage, it flashes
> 90ms every 100ms. The blinking frequency increases from 1 to 10 Hz
> until either the load is high enough to saturate one CPU core or 50%
> load is reached on a single-core system. Then past this point only the
> duty cycle increases from 10 to 90%.
> 
> This results in a very visible activity reporting allowing one to
> immediately tell whether a machine is under load or not, making it
> quite suitable to be used in clusters.
> 
> Signed-off-by: Willy Tarreau <w@1wt.eu>
> ---
>  drivers/leds/trigger/Kconfig            |   9 +
>  drivers/leds/trigger/Makefile           |   1 +
>  drivers/leds/trigger/ledtrig-activity.c | 290 ++++++++++++++++++++++++++++++++
>  3 files changed, 300 insertions(+)
>  create mode 100644 drivers/leds/trigger/ledtrig-activity.c
> 
> diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
> index 3f9ddb9..8432d9e 100644
> --- a/drivers/leds/trigger/Kconfig
> +++ b/drivers/leds/trigger/Kconfig
> @@ -77,6 +77,15 @@ config LEDS_TRIGGER_CPU
>  
>  	  If unsure, say N.
>  
> +config LEDS_TRIGGER_ACTIVITY
> +	tristate "LED activity Trigger"
> +	depends on LEDS_TRIGGERS
> +	help
> +	  This allows LEDs to be controlled by a immediate CPU usage.
> +	  The flash frequency and duty cycle varies from faint flashes to
> +	  intense brightness depending on the instant CPU load.
> +	  If unsure, say Y.
> +
>  config LEDS_TRIGGER_GPIO
>  	tristate "LED GPIO Trigger"
>  	depends on LEDS_TRIGGERS
> diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
> index a72c43c..e572ce57 100644
> --- a/drivers/leds/trigger/Makefile
> +++ b/drivers/leds/trigger/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o
>  obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)	+= ledtrig-backlight.o
>  obj-$(CONFIG_LEDS_TRIGGER_GPIO)		+= ledtrig-gpio.o
>  obj-$(CONFIG_LEDS_TRIGGER_CPU)		+= ledtrig-cpu.o
> +obj-$(CONFIG_LEDS_TRIGGER_ACTIVITY)	+= ledtrig-activity.o
>  obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
>  obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
>  obj-$(CONFIG_LEDS_TRIGGER_CAMERA)	+= ledtrig-camera.o
> diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c
> new file mode 100644
> index 0000000..9acdc56
> --- /dev/null
> +++ b/drivers/leds/trigger/ledtrig-activity.c
> @@ -0,0 +1,290 @@
> +/*
> + * Activity LED trigger
> + *
> + * Copyright (C) 2017 Willy Tarreau <w@1wt.eu>
> + * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/kernel_stat.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/timer.h>
> +#include <linux/sched.h>
> +#include <linux/leds.h>
> +#include <linux/reboot.h>
> +#include <linux/suspend.h>
> +#include "../leds.h"
> +
> +static int panic_detected;
> +
> +struct activity_data {
> +	struct timer_list timer;
> +	u64 last_idle;
> +	u64 last_boot;
> +	int time_left;
> +	int state;
> +	int invert;
> +};
> +
> +static void led_activity_function(unsigned long data)
> +{
> +	struct led_classdev *led_cdev = (struct led_classdev *)data;
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +	struct timespec boot_time;
> +	unsigned int target;
> +	unsigned int usage;
> +	int delay;
> +	u64 curr_idle;
> +	u64 curr_boot;
> +	u32 diff_idle;
> +	u32 diff_boot;
> +	int cpus;
> +	int i;
> +
> +	if (unlikely(panic_detected)) {
> +		/* full brightness in case of panic */
> +		led_set_brightness_nosleep(led_cdev, led_cdev->max_brightness);
> +		return;
> +	}
> +
> +	get_monotonic_boottime(&boot_time);
> +
> +	cpus = 0;
> +	curr_idle = 0;
> +	for_each_possible_cpu(i) {
> +		curr_idle += (__force u64)kcpustat_cpu(i).cpustat[CPUTIME_IDLE];
> +		curr_idle += (__force u64)kcpustat_cpu(i).cpustat[CPUTIME_IOWAIT];
> +		cpus++;
> +	}
> +
> +	curr_boot = div_s64(timespec_to_ns(&boot_time), NSEC_PER_SEC / HZ) * cpus;
> +	curr_idle = cputime64_to_jiffies64(curr_idle);
> +	diff_boot = curr_boot - activity_data->last_boot;
> +	diff_idle = curr_idle - activity_data->last_idle;
> +
> +	activity_data->last_boot = curr_boot;
> +	activity_data->last_idle = curr_idle;
> +
> +	if (diff_boot <= 0)
> +		usage = 0;
> +	else if (diff_idle <= diff_boot)
> +		usage = 100 - 100 * diff_idle / diff_boot;
> +	else
> +		usage = 100;
> +
> +	/*
> +	 * Now we know the total boot_time multiplied by the number of CPUs, and
> +	 * the total idle+wait time for all CPUs. We'll compare how they evolved
> +	 * since last call. The % of overall CPU usage is :
> +	 *
> +	 *      1 - delta_idle / delta_boot
> +	 *
> +	 * What we want is that when the CPU usage is zero, the LED must blink
> +	 * slowly with very faint flashes that are detectable but not disturbing
> +	 * (typically 10ms every second, or 10ms ON, 990ms OFF). Then we want
> +	 * blinking frequency to increase up to the point where the load is
> +	 * enough to saturate one core in multi-core systems or 50% in single
> +	 * core systems. At this point it should reach 10 Hz with a 10/90 duty
> +	 * cycle (10ms ON, 90ms OFF). After this point, the blinking frequency
> +	 * remains stable (10 Hz) and only the duty cycle increases to report
> +	 * the activity, up to the point where we have 90ms ON, 10ms OFF when
> +	 * all cores are saturated. It's important that the LED never stays in
> +	 * a steady state so that it's easy to distinguish an idle or saturated
> +	 * machine from a hung one.
> +	 *
> +	 * This gives us :
> +	 *   - a target CPU usage of min(50%, 100%/#CPU) for a 10% duty cycle
> +	 *     (10ms ON, 90ms OFF)
> +	 *   - below target :
> +	 *      ON_ms  = 10
> +	 *      OFF_ms = 90 + (1 - usage/target) * 900
> +	 *   - above target :
> +	 *      ON_ms  = 10 + (usage-target)/(100%-target) * 80
> +	 *      OFF_ms = 90 - (usage-target)/(100%-target) * 80
> +	 *
> +	 * In order to keep a good responsiveness, we cap the sleep time to
> +	 * 100 ms and keep track of the sleep time left. This allows us to
> +	 * quickly change it if needed.
> +	 */
> +
> +	activity_data->time_left -= 100;
> +	if (activity_data->time_left <= 0) {
> +		activity_data->time_left = 0;
> +		activity_data->state = !activity_data->state;
> +		led_set_brightness_nosleep(led_cdev,
> +			(activity_data->state ^ activity_data->invert) ?
> +			led_cdev->max_brightness : LED_OFF);
> +	}
> +
> +	target = (cpus > 1) ? (100 / cpus) : 50;
> +
> +	if (usage < target)
> +		delay = activity_data->state ?
> +			10 :                        /* ON  */
> +			990 - 900 * usage / target; /* OFF */
> +	else
> +		delay = activity_data->state ?
> +			10 + 80 * (usage - target) / (100 - target) : /* ON  */
> +			90 - 80 * (usage - target) / (100 - target);  /* OFF */
> +
> +
> +	if (!activity_data->time_left || delay <= activity_data->time_left)
> +		activity_data->time_left = delay;
> +
> +	delay = min_t(int, activity_data->time_left, 100);
> +	mod_timer(&activity_data->timer, jiffies + msecs_to_jiffies(delay));
> +}
> +
> +static ssize_t led_invert_show(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +
> +	return sprintf(buf, "%u\n", activity_data->invert);
> +}
> +
> +static ssize_t led_invert_store(struct device *dev,
> +                                struct device_attribute *attr,
> +                                const char *buf, size_t size)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +	unsigned long state;
> +	int ret;
> +
> +	ret = kstrtoul(buf, 0, &state);
> +	if (ret)
> +		return ret;
> +
> +	activity_data->invert = !!state;
> +
> +	return size;
> +}
> +
> +static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
> +
> +static void activity_activate(struct led_classdev *led_cdev)
> +{
> +	struct activity_data *activity_data;
> +	int rc;
> +
> +	activity_data = kzalloc(sizeof(*activity_data), GFP_KERNEL);
> +	if (!activity_data)
> +		return;
> +
> +	led_cdev->trigger_data = activity_data;
> +	rc = device_create_file(led_cdev->dev, &dev_attr_invert);
> +	if (rc) {
> +		kfree(led_cdev->trigger_data);
> +		return;
> +	}
> +
> +	setup_timer(&activity_data->timer,
> +		    led_activity_function, (unsigned long)led_cdev);
> +	led_activity_function(activity_data->timer.data);
> +	led_cdev->activated = true;
> +}
> +
> +static void activity_deactivate(struct led_classdev *led_cdev)
> +{
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +
> +	if (led_cdev->activated) {
> +		del_timer_sync(&activity_data->timer);
> +		device_remove_file(led_cdev->dev, &dev_attr_invert);
> +		kfree(activity_data);
> +		led_cdev->activated = false;
> +	}
> +}
> +
> +static struct led_trigger activity_led_trigger = {
> +	.name       = "activity",
> +	.activate   = activity_activate,
> +	.deactivate = activity_deactivate,
> +};
> +
> +static int activity_pm_notifier(struct notifier_block *nb,
> +                                unsigned long pm_event, void *unused)
> +{
> +	int rc;
> +
> +	switch (pm_event) {
> +	case PM_SUSPEND_PREPARE:
> +	case PM_HIBERNATION_PREPARE:
> +	case PM_RESTORE_PREPARE:
> +		led_trigger_unregister(&activity_led_trigger);
> +		break;
> +	case PM_POST_SUSPEND:
> +	case PM_POST_HIBERNATION:
> +	case PM_POST_RESTORE:
> +		rc = led_trigger_register(&activity_led_trigger);
> +		if (rc)
> +			pr_err("could not re-register activity trigger\n");
> +		break;
> +	default:
> +		break;
> +	}
> +	return NOTIFY_DONE;
> +}
> +
> +static int activity_reboot_notifier(struct notifier_block *nb,
> +                                    unsigned long code, void *unused)
> +{
> +	led_trigger_unregister(&activity_led_trigger);
> +	return NOTIFY_DONE;
> +}
> +
> +static int activity_panic_notifier(struct notifier_block *nb,
> +                                   unsigned long code, void *unused)
> +{
> +	panic_detected = 1;
> +	return NOTIFY_DONE;
> +}
> +
> +static struct notifier_block activity_pm_nb = {
> +	.notifier_call = activity_pm_notifier,
> +};
> +
> +static struct notifier_block activity_reboot_nb = {
> +	.notifier_call = activity_reboot_notifier,
> +};
> +
> +static struct notifier_block activity_panic_nb = {
> +	.notifier_call = activity_panic_notifier,
> +};
> +
> +static int __init activity_init(void)
> +{
> +	int rc = led_trigger_register(&activity_led_trigger);
> +
> +	if (!rc) {
> +		atomic_notifier_chain_register(&panic_notifier_list,
> +					       &activity_panic_nb);
> +		register_reboot_notifier(&activity_reboot_nb);
> +		register_pm_notifier(&activity_pm_nb);
> +	}
> +	return rc;
> +}
> +
> +static void __exit activity_exit(void)
> +{
> +	unregister_pm_notifier(&activity_pm_nb);
> +	unregister_reboot_notifier(&activity_reboot_nb);
> +	atomic_notifier_chain_unregister(&panic_notifier_list,
> +					 &activity_panic_nb);
> +	led_trigger_unregister(&activity_led_trigger);
> +}
> +
> +module_init(activity_init);
> +module_exit(activity_exit);
> +
> +MODULE_AUTHOR("Willy Tarreau <w@1wt.eu>");
> +MODULE_DESCRIPTION("Activity LED trigger");
> +MODULE_LICENSE("GPL");
> 

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] leds/trigger/activity: add a system activity LED trigger
  2017-03-09 20:48 ` Jacek Anaszewski
@ 2017-03-10  6:44   ` Willy Tarreau
  2017-08-24 12:07   ` Willy Tarreau
  1 sibling, 0 replies; 11+ messages in thread
From: Willy Tarreau @ 2017-03-10  6:44 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Richard Purdie, Pavel Machek, linux-kernel, linux-leds

Hi Jacek,

On Thu, Mar 09, 2017 at 09:48:39PM +0100, Jacek Anaszewski wrote:
> Hi Willy,
> 
> Thanks for the patch.
> 
> Unfortunately I'm getting following build break, while trying to
> test it on recent linux-next:
> 
> drivers/leds/trigger/ledtrig-activity.c: In function
> 'led_activity_function':
> drivers/leds/trigger/ledtrig-activity.c:67:14: error: implicit
> declaration of function 'cputime64_to_jiffies64'
> [-Werror=implicit-function-declaration]
>   curr_idle = cputime64_to_jiffies64(curr_idle);

Thanks for letting me know, I'll check this ASAP.

Cheers,
Willy

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] leds/trigger/activity: add a system activity LED trigger
  2017-03-09 20:48 ` Jacek Anaszewski
  2017-03-10  6:44   ` Willy Tarreau
@ 2017-08-24 12:07   ` Willy Tarreau
  2017-08-27 16:44     ` Jacek Anaszewski
  1 sibling, 1 reply; 11+ messages in thread
From: Willy Tarreau @ 2017-08-24 12:07 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Richard Purdie, Pavel Machek, linux-kernel, linux-leds

[-- Attachment #1: Type: text/plain, Size: 664 bytes --]

Hi Jacek,

On Thu, Mar 09, 2017 at 09:48:39PM +0100, Jacek Anaszewski wrote:
> Hi Willy,
> 
> Thanks for the patch.
> 
> Unfortunately I'm getting following build break, while trying to
> test it on recent linux-next:
> 
> drivers/leds/trigger/ledtrig-activity.c: In function
> 'led_activity_function':
> drivers/leds/trigger/ledtrig-activity.c:67:14: error: implicit
> declaration of function 'cputime64_to_jiffies64'
> [-Werror=implicit-function-declaration]
>   curr_idle = cputime64_to_jiffies64(curr_idle);

It took me longer than I hoped to get back to this, but here's a new
version which works on 4.13-rc6 by not relying on jiffies anymore.

Thanks!
Willy

[-- Attachment #2: 0001-leds-trigger-activity-add-a-system-activity-LED-trig.patch --]
[-- Type: text/plain, Size: 11644 bytes --]

>From 655256adb790590bbc0c35a9e34bf0a22ea95b5d Mon Sep 17 00:00:00 2001
From: Willy Tarreau <w@1wt.eu>
Date: Fri, 10 Feb 2017 19:45:07 +0100
Subject: leds/trigger/activity: add a system activity LED trigger

The "activity" trigger was inspired by the heartbeat one, but aims at
providing instant indication of the immediate CPU usage. Under idle
condition, it flashes 10ms every second. At 100% usage, it flashes
90ms every 100ms. The blinking frequency increases from 1 to 10 Hz
until either the load is high enough to saturate one CPU core or 50%
load is reached on a single-core system. Then past this point only the
duty cycle increases from 10 to 90%.

This results in a very visible activity reporting allowing one to
immediately tell whether a machine is under load or not, making it
quite suitable to be used in clusters.

Signed-off-by: Willy Tarreau <w@1wt.eu>

---
since v1:
  - update to 4.13-rc6 by getting rid of cputime64_to_jiffies64()
  - fix behaviour on NO_HZ where cumulated idle time could be wrong
---
 drivers/leds/trigger/Kconfig            |   9 +
 drivers/leds/trigger/Makefile           |   1 +
 drivers/leds/trigger/ledtrig-activity.c | 297 ++++++++++++++++++++++++++++++++
 3 files changed, 307 insertions(+)
 create mode 100644 drivers/leds/trigger/ledtrig-activity.c

diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
index 3f9ddb9..8432d9e 100644
--- a/drivers/leds/trigger/Kconfig
+++ b/drivers/leds/trigger/Kconfig
@@ -77,6 +77,15 @@ config LEDS_TRIGGER_CPU
 
 	  If unsure, say N.
 
+config LEDS_TRIGGER_ACTIVITY
+	tristate "LED activity Trigger"
+	depends on LEDS_TRIGGERS
+	help
+	  This allows LEDs to be controlled by a immediate CPU usage.
+	  The flash frequency and duty cycle varies from faint flashes to
+	  intense brightness depending on the instant CPU load.
+	  If unsure, say Y.
+
 config LEDS_TRIGGER_GPIO
 	tristate "LED GPIO Trigger"
 	depends on LEDS_TRIGGERS
diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
index a72c43c..e572ce57 100644
--- a/drivers/leds/trigger/Makefile
+++ b/drivers/leds/trigger/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o
 obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)	+= ledtrig-backlight.o
 obj-$(CONFIG_LEDS_TRIGGER_GPIO)		+= ledtrig-gpio.o
 obj-$(CONFIG_LEDS_TRIGGER_CPU)		+= ledtrig-cpu.o
+obj-$(CONFIG_LEDS_TRIGGER_ACTIVITY)	+= ledtrig-activity.o
 obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
 obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
 obj-$(CONFIG_LEDS_TRIGGER_CAMERA)	+= ledtrig-camera.o
diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c
new file mode 100644
index 0000000..6f00235
--- /dev/null
+++ b/drivers/leds/trigger/ledtrig-activity.c
@@ -0,0 +1,297 @@
+/*
+ * Activity LED trigger
+ *
+ * Copyright (C) 2017 Willy Tarreau <w@1wt.eu>
+ * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kernel_stat.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/sched.h>
+#include <linux/leds.h>
+#include <linux/reboot.h>
+#include <linux/suspend.h>
+#include "../leds.h"
+
+static int panic_detected;
+
+struct activity_data {
+	struct timer_list timer;
+	u64 last_used;
+	u64 last_boot;
+	int time_left;
+	int state;
+	int invert;
+};
+
+static void led_activity_function(unsigned long data)
+{
+	struct led_classdev *led_cdev = (struct led_classdev *)data;
+	struct activity_data *activity_data = led_cdev->trigger_data;
+	struct timespec boot_time;
+	unsigned int target;
+	unsigned int usage;
+	int delay;
+	u64 curr_used;
+	u64 curr_boot;
+	s32 diff_used;
+	s32 diff_boot;
+	int cpus;
+	int i;
+
+	if (unlikely(panic_detected)) {
+		/* full brightness in case of panic */
+		led_set_brightness_nosleep(led_cdev, led_cdev->max_brightness);
+		return;
+	}
+
+	get_monotonic_boottime(&boot_time);
+
+	cpus = 0;
+	curr_used = 0;
+
+	for_each_possible_cpu(i) {
+		curr_used += kcpustat_cpu(i).cpustat[CPUTIME_USER]
+			  +  kcpustat_cpu(i).cpustat[CPUTIME_NICE]
+			  +  kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]
+			  +  kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]
+			  +  kcpustat_cpu(i).cpustat[CPUTIME_IRQ];
+		cpus++;
+	}
+
+	/* We come here every 100ms in the worst case, so that's 100M ns of
+	 * cumulated time. By dividing by 2^16, we get the time resolution
+	 * down to 16us, ensuring we won't overflow 32-bit computations below
+	 * even up to 3k CPUs, while keeping divides cheap on smaller systems.
+	 */
+	curr_boot = timespec_to_ns(&boot_time) * cpus;
+	diff_boot = (curr_boot - activity_data->last_boot) >> 16;
+	diff_used = (curr_used - activity_data->last_used) >> 16;
+	activity_data->last_boot = curr_boot;
+	activity_data->last_used = curr_used;
+
+	if (diff_boot <= 0 || diff_used < 0)
+		usage = 0;
+	else if (diff_used >= diff_boot)
+		usage = 100;
+	else
+		usage = 100 * diff_used / diff_boot;
+
+	/*
+	 * Now we know the total boot_time multiplied by the number of CPUs, and
+	 * the total idle+wait time for all CPUs. We'll compare how they evolved
+	 * since last call. The % of overall CPU usage is :
+	 *
+	 *      1 - delta_idle / delta_boot
+	 *
+	 * What we want is that when the CPU usage is zero, the LED must blink
+	 * slowly with very faint flashes that are detectable but not disturbing
+	 * (typically 10ms every second, or 10ms ON, 990ms OFF). Then we want
+	 * blinking frequency to increase up to the point where the load is
+	 * enough to saturate one core in multi-core systems or 50% in single
+	 * core systems. At this point it should reach 10 Hz with a 10/90 duty
+	 * cycle (10ms ON, 90ms OFF). After this point, the blinking frequency
+	 * remains stable (10 Hz) and only the duty cycle increases to report
+	 * the activity, up to the point where we have 90ms ON, 10ms OFF when
+	 * all cores are saturated. It's important that the LED never stays in
+	 * a steady state so that it's easy to distinguish an idle or saturated
+	 * machine from a hung one.
+	 *
+	 * This gives us :
+	 *   - a target CPU usage of min(50%, 100%/#CPU) for a 10% duty cycle
+	 *     (10ms ON, 90ms OFF)
+	 *   - below target :
+	 *      ON_ms  = 10
+	 *      OFF_ms = 90 + (1 - usage/target) * 900
+	 *   - above target :
+	 *      ON_ms  = 10 + (usage-target)/(100%-target) * 80
+	 *      OFF_ms = 90 - (usage-target)/(100%-target) * 80
+	 *
+	 * In order to keep a good responsiveness, we cap the sleep time to
+	 * 100 ms and keep track of the sleep time left. This allows us to
+	 * quickly change it if needed.
+	 */
+
+	activity_data->time_left -= 100;
+	if (activity_data->time_left <= 0) {
+		activity_data->time_left = 0;
+		activity_data->state = !activity_data->state;
+		led_set_brightness_nosleep(led_cdev,
+			(activity_data->state ^ activity_data->invert) ?
+			led_cdev->max_brightness : LED_OFF);
+	}
+
+	target = (cpus > 1) ? (100 / cpus) : 50;
+
+	if (usage < target)
+		delay = activity_data->state ?
+			10 :                        /* ON  */
+			990 - 900 * usage / target; /* OFF */
+	else
+		delay = activity_data->state ?
+			10 + 80 * (usage - target) / (100 - target) : /* ON  */
+			90 - 80 * (usage - target) / (100 - target);  /* OFF */
+
+
+	if (!activity_data->time_left || delay <= activity_data->time_left)
+		activity_data->time_left = delay;
+
+	delay = min_t(int, activity_data->time_left, 100);
+	mod_timer(&activity_data->timer, jiffies + msecs_to_jiffies(delay));
+}
+
+static ssize_t led_invert_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct activity_data *activity_data = led_cdev->trigger_data;
+
+	return sprintf(buf, "%u\n", activity_data->invert);
+}
+
+static ssize_t led_invert_store(struct device *dev,
+                                struct device_attribute *attr,
+                                const char *buf, size_t size)
+{
+	struct led_classdev *led_cdev = dev_get_drvdata(dev);
+	struct activity_data *activity_data = led_cdev->trigger_data;
+	unsigned long state;
+	int ret;
+
+	ret = kstrtoul(buf, 0, &state);
+	if (ret)
+		return ret;
+
+	activity_data->invert = !!state;
+
+	return size;
+}
+
+static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
+
+static void activity_activate(struct led_classdev *led_cdev)
+{
+	struct activity_data *activity_data;
+	int rc;
+
+	activity_data = kzalloc(sizeof(*activity_data), GFP_KERNEL);
+	if (!activity_data)
+		return;
+
+	led_cdev->trigger_data = activity_data;
+	rc = device_create_file(led_cdev->dev, &dev_attr_invert);
+	if (rc) {
+		kfree(led_cdev->trigger_data);
+		return;
+	}
+
+	setup_timer(&activity_data->timer,
+		    led_activity_function, (unsigned long)led_cdev);
+	led_activity_function(activity_data->timer.data);
+	led_cdev->activated = true;
+}
+
+static void activity_deactivate(struct led_classdev *led_cdev)
+{
+	struct activity_data *activity_data = led_cdev->trigger_data;
+
+	if (led_cdev->activated) {
+		del_timer_sync(&activity_data->timer);
+		device_remove_file(led_cdev->dev, &dev_attr_invert);
+		kfree(activity_data);
+		led_cdev->activated = false;
+	}
+}
+
+static struct led_trigger activity_led_trigger = {
+	.name       = "activity",
+	.activate   = activity_activate,
+	.deactivate = activity_deactivate,
+};
+
+static int activity_pm_notifier(struct notifier_block *nb,
+                                unsigned long pm_event, void *unused)
+{
+	int rc;
+
+	switch (pm_event) {
+	case PM_SUSPEND_PREPARE:
+	case PM_HIBERNATION_PREPARE:
+	case PM_RESTORE_PREPARE:
+		led_trigger_unregister(&activity_led_trigger);
+		break;
+	case PM_POST_SUSPEND:
+	case PM_POST_HIBERNATION:
+	case PM_POST_RESTORE:
+		rc = led_trigger_register(&activity_led_trigger);
+		if (rc)
+			pr_err("could not re-register activity trigger\n");
+		break;
+	default:
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static int activity_reboot_notifier(struct notifier_block *nb,
+                                    unsigned long code, void *unused)
+{
+	led_trigger_unregister(&activity_led_trigger);
+	return NOTIFY_DONE;
+}
+
+static int activity_panic_notifier(struct notifier_block *nb,
+                                   unsigned long code, void *unused)
+{
+	panic_detected = 1;
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block activity_pm_nb = {
+	.notifier_call = activity_pm_notifier,
+};
+
+static struct notifier_block activity_reboot_nb = {
+	.notifier_call = activity_reboot_notifier,
+};
+
+static struct notifier_block activity_panic_nb = {
+	.notifier_call = activity_panic_notifier,
+};
+
+static int __init activity_init(void)
+{
+	int rc = led_trigger_register(&activity_led_trigger);
+
+	if (!rc) {
+		atomic_notifier_chain_register(&panic_notifier_list,
+					       &activity_panic_nb);
+		register_reboot_notifier(&activity_reboot_nb);
+		register_pm_notifier(&activity_pm_nb);
+	}
+	return rc;
+}
+
+static void __exit activity_exit(void)
+{
+	unregister_pm_notifier(&activity_pm_nb);
+	unregister_reboot_notifier(&activity_reboot_nb);
+	atomic_notifier_chain_unregister(&panic_notifier_list,
+					 &activity_panic_nb);
+	led_trigger_unregister(&activity_led_trigger);
+}
+
+module_init(activity_init);
+module_exit(activity_exit);
+
+MODULE_AUTHOR("Willy Tarreau <w@1wt.eu>");
+MODULE_DESCRIPTION("Activity LED trigger");
+MODULE_LICENSE("GPL");
-- 
1.7.12.1


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH] leds/trigger/activity: add a system activity LED trigger
  2017-08-24 12:07   ` Willy Tarreau
@ 2017-08-27 16:44     ` Jacek Anaszewski
  2017-08-28  6:57       ` Willy Tarreau
       [not found]       ` <1503945891-31722-1-git-send-email-w@1wt.eu>
  0 siblings, 2 replies; 11+ messages in thread
From: Jacek Anaszewski @ 2017-08-27 16:44 UTC (permalink / raw)
  To: Willy Tarreau; +Cc: Richard Purdie, Pavel Machek, linux-kernel, linux-leds

Hi Willy,

Thanks for the updated patch.

One formal note: please send the patches with git send-email instead
of attaching them to the message.

It simplifies reviewing and applying patches. For the purpose of this
review I just copied the contents of the attachment and pasted as
a quotation. Then I added my comments to the quoted code. Please
refer below.

On 08/24/2017 02:07 PM, Willy Tarreau wrote:
> Hi Jacek,
> 
> On Thu, Mar 09, 2017 at 09:48:39PM +0100, Jacek Anaszewski wrote:
>> Hi Willy,
>>
>> Thanks for the patch.
>>
>> Unfortunately I'm getting following build break, while trying to
>> test it on recent linux-next:
>>
>> drivers/leds/trigger/ledtrig-activity.c: In function
>> 'led_activity_function':
>> drivers/leds/trigger/ledtrig-activity.c:67:14: error: implicit
>> declaration of function 'cputime64_to_jiffies64'
>> [-Werror=implicit-function-declaration]
>>   curr_idle = cputime64_to_jiffies64(curr_idle);
> 
> It took me longer than I hoped to get back to this, but here's a new
> version which works on 4.13-rc6 by not relying on jiffies anymore.
> 
> Thanks!
> Willy
> 

---------- Beginning of the quoted patch ----------


>>From 655256adb790590bbc0c35a9e34bf0a22ea95b5d Mon Sep 17 00:00:00 2001
> From: Willy Tarreau <w@1wt.eu>
> Date: Fri, 10 Feb 2017 19:45:07 +0100
> Subject: leds/trigger/activity: add a system activity LED trigger
> 
> The "activity" trigger was inspired by the heartbeat one, but aims at
> providing instant indication of the immediate CPU usage. Under idle
> condition, it flashes 10ms every second. At 100% usage, it flashes
> 90ms every 100ms. The blinking frequency increases from 1 to 10 Hz
> until either the load is high enough to saturate one CPU core or 50%
> load is reached on a single-core system. Then past this point only the
> duty cycle increases from 10 to 90%.
> 
> This results in a very visible activity reporting allowing one to
> immediately tell whether a machine is under load or not, making it
> quite suitable to be used in clusters.
> 
> Signed-off-by: Willy Tarreau <w@1wt.eu>
> 
> ---
> since v1:
>   - update to 4.13-rc6 by getting rid of cputime64_to_jiffies64()
>   - fix behaviour on NO_HZ where cumulated idle time could be wrong
> ---
>  drivers/leds/trigger/Kconfig            |   9 +
>  drivers/leds/trigger/Makefile           |   1 +
>  drivers/leds/trigger/ledtrig-activity.c | 297 ++++++++++++++++++++++++++++++++
>  3 files changed, 307 insertions(+)
>  create mode 100644 drivers/leds/trigger/ledtrig-activity.c
> 
> diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
> index 3f9ddb9..8432d9e 100644
> --- a/drivers/leds/trigger/Kconfig
> +++ b/drivers/leds/trigger/Kconfig
> @@ -77,6 +77,15 @@ config LEDS_TRIGGER_CPU
>  
>  	  If unsure, say N.
>  
> +config LEDS_TRIGGER_ACTIVITY
> +	tristate "LED activity Trigger"
> +	depends on LEDS_TRIGGERS
> +	help
> +	  This allows LEDs to be controlled by a immediate CPU usage.
> +	  The flash frequency and duty cycle varies from faint flashes to
> +	  intense brightness depending on the instant CPU load.
> +	  If unsure, say Y.
> +
>  config LEDS_TRIGGER_GPIO
>  	tristate "LED GPIO Trigger"
>  	depends on LEDS_TRIGGERS
> diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
> index a72c43c..e572ce57 100644
> --- a/drivers/leds/trigger/Makefile
> +++ b/drivers/leds/trigger/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o
>  obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)	+= ledtrig-backlight.o
>  obj-$(CONFIG_LEDS_TRIGGER_GPIO)		+= ledtrig-gpio.o
>  obj-$(CONFIG_LEDS_TRIGGER_CPU)		+= ledtrig-cpu.o
> +obj-$(CONFIG_LEDS_TRIGGER_ACTIVITY)	+= ledtrig-activity.o
>  obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
>  obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
>  obj-$(CONFIG_LEDS_TRIGGER_CAMERA)	+= ledtrig-camera.o
> diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c
> new file mode 100644
> index 0000000..6f00235
> --- /dev/null
> +++ b/drivers/leds/trigger/ledtrig-activity.c
> @@ -0,0 +1,297 @@
> +/*
> + * Activity LED trigger
> + *
> + * Copyright (C) 2017 Willy Tarreau <w@1wt.eu>
> + * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/kernel_stat.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/timer.h>
> +#include <linux/sched.h>
> +#include <linux/leds.h>
> +#include <linux/reboot.h>
> +#include <linux/suspend.h>

Please sort the includes alphabetically.

> +#include "../leds.h"
> +
> +static int panic_detected;
> +
> +struct activity_data {
> +	struct timer_list timer;
> +	u64 last_used;
> +	u64 last_boot;
> +	int time_left;
> +	int state;
> +	int invert;
> +};
> +
> +static void led_activity_function(unsigned long data)
> +{
> +	struct led_classdev *led_cdev = (struct led_classdev *)data;
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +	struct timespec boot_time;
> +	unsigned int target;
> +	unsigned int usage;
> +	int delay;
> +	u64 curr_used;
> +	u64 curr_boot;
> +	s32 diff_used;
> +	s32 diff_boot;
> +	int cpus;
> +	int i;
> +
> +	if (unlikely(panic_detected)) {
> +		/* full brightness in case of panic */
> +		led_set_brightness_nosleep(led_cdev, led_cdev->max_brightness);
> +		return;
> +	}
> +
> +	get_monotonic_boottime(&boot_time);
> +
> +	cpus = 0;
> +	curr_used = 0;
> +
> +	for_each_possible_cpu(i) {
> +		curr_used += kcpustat_cpu(i).cpustat[CPUTIME_USER]
> +			  +  kcpustat_cpu(i).cpustat[CPUTIME_NICE]
> +			  +  kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]
> +			  +  kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]
> +			  +  kcpustat_cpu(i).cpustat[CPUTIME_IRQ];
> +		cpus++;
> +	}
> +
> +	/* We come here every 100ms in the worst case, so that's 100M ns of
> +	 * cumulated time. By dividing by 2^16, we get the time resolution
> +	 * down to 16us, ensuring we won't overflow 32-bit computations below
> +	 * even up to 3k CPUs, while keeping divides cheap on smaller systems.
> +	 */
> +	curr_boot = timespec_to_ns(&boot_time) * cpus;
> +	diff_boot = (curr_boot - activity_data->last_boot) >> 16;
> +	diff_used = (curr_used - activity_data->last_used) >> 16;
> +	activity_data->last_boot = curr_boot;
> +	activity_data->last_used = curr_used;
> +
> +	if (diff_boot <= 0 || diff_used < 0)
> +		usage = 0;
> +	else if (diff_used >= diff_boot)
> +		usage = 100;
> +	else
> +		usage = 100 * diff_used / diff_boot;
> +
> +	/*
> +	 * Now we know the total boot_time multiplied by the number of CPUs, and
> +	 * the total idle+wait time for all CPUs. We'll compare how they evolved
> +	 * since last call. The % of overall CPU usage is :
> +	 *
> +	 *      1 - delta_idle / delta_boot
> +	 *
> +	 * What we want is that when the CPU usage is zero, the LED must blink
> +	 * slowly with very faint flashes that are detectable but not disturbing
> +	 * (typically 10ms every second, or 10ms ON, 990ms OFF). Then we want
> +	 * blinking frequency to increase up to the point where the load is
> +	 * enough to saturate one core in multi-core systems or 50% in single
> +	 * core systems. At this point it should reach 10 Hz with a 10/90 duty
> +	 * cycle (10ms ON, 90ms OFF). After this point, the blinking frequency
> +	 * remains stable (10 Hz) and only the duty cycle increases to report
> +	 * the activity, up to the point where we have 90ms ON, 10ms OFF when
> +	 * all cores are saturated. It's important that the LED never stays in
> +	 * a steady state so that it's easy to distinguish an idle or saturated
> +	 * machine from a hung one.
> +	 *
> +	 * This gives us :
> +	 *   - a target CPU usage of min(50%, 100%/#CPU) for a 10% duty cycle
> +	 *     (10ms ON, 90ms OFF)
> +	 *   - below target :
> +	 *      ON_ms  = 10
> +	 *      OFF_ms = 90 + (1 - usage/target) * 900
> +	 *   - above target :
> +	 *      ON_ms  = 10 + (usage-target)/(100%-target) * 80
> +	 *      OFF_ms = 90 - (usage-target)/(100%-target) * 80
> +	 *
> +	 * In order to keep a good responsiveness, we cap the sleep time to
> +	 * 100 ms and keep track of the sleep time left. This allows us to
> +	 * quickly change it if needed.
> +	 */
> +
> +	activity_data->time_left -= 100;
> +	if (activity_data->time_left <= 0) {
> +		activity_data->time_left = 0;
> +		activity_data->state = !activity_data->state;
> +		led_set_brightness_nosleep(led_cdev,
> +			(activity_data->state ^ activity_data->invert) ?
> +			led_cdev->max_brightness : LED_OFF);

Have you considered making the top brightness adjustable? I'd make it
possible especially that we have a similar solution in the
ledtrig-heartbeat.c already - see the following patch in 4.12:

commit fb3d769173d26268d7bf068094a599bb28b2ac63
Author: Jacek Anaszewski <j.anaszewski@samsung.com>
Date:   Wed Nov 9 11:43:46 2016 +0100

    leds: ledtrig-heartbeat: Make top brightness adjustable

    LED class heartbeat trigger allowed only for blinking with
max_brightness
    value. This patch adds more flexibility by exploiting part of LED core
    software blink infrastructure.


> +	}
> +
> +	target = (cpus > 1) ? (100 / cpus) : 50;
> +
> +	if (usage < target)
> +		delay = activity_data->state ?
> +			10 :                        /* ON  */
> +			990 - 900 * usage / target; /* OFF */
> +	else
> +		delay = activity_data->state ?
> +			10 + 80 * (usage - target) / (100 - target) : /* ON  */
> +			90 - 80 * (usage - target) / (100 - target);  /* OFF */
> +
> +
> +	if (!activity_data->time_left || delay <= activity_data->time_left)
> +		activity_data->time_left = delay;
> +
> +	delay = min_t(int, activity_data->time_left, 100);
> +	mod_timer(&activity_data->timer, jiffies + msecs_to_jiffies(delay));
> +}
> +
> +static ssize_t led_invert_show(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +
> +	return sprintf(buf, "%u\n", activity_data->invert);
> +}
> +
> +static ssize_t led_invert_store(struct device *dev,
> +                                struct device_attribute *attr,
> +                                const char *buf, size_t size)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +	unsigned long state;
> +	int ret;
> +
> +	ret = kstrtoul(buf, 0, &state);
> +	if (ret)
> +		return ret;
> +
> +	activity_data->invert = !!state;
> +
> +	return size;
> +}
> +
> +static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
> +
> +static void activity_activate(struct led_classdev *led_cdev)
> +{
> +	struct activity_data *activity_data;
> +	int rc;
> +
> +	activity_data = kzalloc(sizeof(*activity_data), GFP_KERNEL);
> +	if (!activity_data)
> +		return;
> +
> +	led_cdev->trigger_data = activity_data;
> +	rc = device_create_file(led_cdev->dev, &dev_attr_invert);
> +	if (rc) {
> +		kfree(led_cdev->trigger_data);
> +		return;
> +	}
> +
> +	setup_timer(&activity_data->timer,
> +		    led_activity_function, (unsigned long)led_cdev);
> +	led_activity_function(activity_data->timer.data);
> +	led_cdev->activated = true;
> +}
> +
> +static void activity_deactivate(struct led_classdev *led_cdev)
> +{
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +
> +	if (led_cdev->activated) {
> +		del_timer_sync(&activity_data->timer);
> +		device_remove_file(led_cdev->dev, &dev_attr_invert);
> +		kfree(activity_data);
> +		led_cdev->activated = false;
> +	}
> +}
> +
> +static struct led_trigger activity_led_trigger = {
> +	.name       = "activity",
> +	.activate   = activity_activate,
> +	.deactivate = activity_deactivate,
> +};
> +
> +static int activity_pm_notifier(struct notifier_block *nb,
> +                                unsigned long pm_event, void *unused)
> +{
> +	int rc;
> +
> +	switch (pm_event) {
> +	case PM_SUSPEND_PREPARE:
> +	case PM_HIBERNATION_PREPARE:
> +	case PM_RESTORE_PREPARE:
> +		led_trigger_unregister(&activity_led_trigger);
> +		break;
> +	case PM_POST_SUSPEND:
> +	case PM_POST_HIBERNATION:
> +	case PM_POST_RESTORE:
> +		rc = led_trigger_register(&activity_led_trigger);
> +		if (rc)
> +			pr_err("could not re-register activity trigger\n");
> +		break;
> +	default:
> +		break;
> +	}
> +	return NOTIFY_DONE;
> +}

It turned out to cause problems in ledtrig-heartbeat.c and was reverted.
Please don't register pm notifier and remove related facilities from the
patch according to the following revert patch:

commit 436c4c45b5b9562b59cedbb51b7343ab4a6dd8cc
Author: Zhang Bo <bo.zhang@nxp.com>
Date:   Tue Jun 13 10:39:20 2017 +0800

    Revert "leds: handle suspend/resume in heartbeat trigger"

    This reverts commit 5ab92a7cb82c66bf30685583a38a18538e3807db.

    System cannot enter suspend mode because of heartbeat led trigger.
    In autosleep_wq, try_to_suspend function will try to enter suspend
    mode in specific period. it will get wakeup_count then call pm_notifier
    chain callback function and freeze processes.
    Heartbeat_pm_notifier is called and it call led_trigger_unregister to
    change the trigger of led device to none. It will send uevent message
    and the wakeup source count changed. As wakeup_count changed, suspend
    will abort.




> +static int activity_reboot_notifier(struct notifier_block *nb,
> +                                    unsigned long code, void *unused)
> +{
> +	led_trigger_unregister(&activity_led_trigger);
> +	return NOTIFY_DONE;
> +}
> +
> +static int activity_panic_notifier(struct notifier_block *nb,
> +                                   unsigned long code, void *unused)
> +{
> +	panic_detected = 1;
> +	return NOTIFY_DONE;
> +}
> +
> +static struct notifier_block activity_pm_nb = {
> +	.notifier_call = activity_pm_notifier,
> +};
> +
> +static struct notifier_block activity_reboot_nb = {
> +	.notifier_call = activity_reboot_notifier,
> +};
> +
> +static struct notifier_block activity_panic_nb = {
> +	.notifier_call = activity_panic_notifier,
> +};
> +
> +static int __init activity_init(void)
> +{
> +	int rc = led_trigger_register(&activity_led_trigger);
> +
> +	if (!rc) {
> +		atomic_notifier_chain_register(&panic_notifier_list,
> +					       &activity_panic_nb);
> +		register_reboot_notifier(&activity_reboot_nb);
> +		register_pm_notifier(&activity_pm_nb);
> +	}
> +	return rc;
> +}
> +
> +static void __exit activity_exit(void)
> +{
> +	unregister_pm_notifier(&activity_pm_nb);
> +	unregister_reboot_notifier(&activity_reboot_nb);
> +	atomic_notifier_chain_unregister(&panic_notifier_list,
> +					 &activity_panic_nb);
> +	led_trigger_unregister(&activity_led_trigger);
> +}
> +
> +module_init(activity_init);
> +module_exit(activity_exit);
> +
> +MODULE_AUTHOR("Willy Tarreau <w@1wt.eu>");
> +MODULE_DESCRIPTION("Activity LED trigger");
> +MODULE_LICENSE("GPL");

-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH] leds/trigger/activity: add a system activity LED trigger
  2017-08-27 16:44     ` Jacek Anaszewski
@ 2017-08-28  6:57       ` Willy Tarreau
       [not found]       ` <1503945891-31722-1-git-send-email-w@1wt.eu>
  1 sibling, 0 replies; 11+ messages in thread
From: Willy Tarreau @ 2017-08-28  6:57 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Richard Purdie, Pavel Machek, linux-kernel, linux-leds

Hi Jacek,

On Sun, Aug 27, 2017 at 06:44:05PM +0200, Jacek Anaszewski wrote:
> Hi Willy,
> 
> Thanks for the updated patch.
> 
> One formal note: please send the patches with git send-email instead
> of attaching them to the message.

Yep, I hesitated and wanted to reply. Will do it the other way next
time, sorry for the hassle.

> > diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c
> > new file mode 100644
> > index 0000000..6f00235
> > --- /dev/null
> > +++ b/drivers/leds/trigger/ledtrig-activity.c
> > @@ -0,0 +1,297 @@
> > +/*
> > + * Activity LED trigger
> > + *
> > + * Copyright (C) 2017 Willy Tarreau <w@1wt.eu>
> > + * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + *
> > + */
> > +#include <linux/module.h>
> > +#include <linux/kernel.h>
> > +#include <linux/kernel_stat.h>
> > +#include <linux/init.h>
> > +#include <linux/slab.h>
> > +#include <linux/timer.h>
> > +#include <linux/sched.h>
> > +#include <linux/leds.h>
> > +#include <linux/reboot.h>
> > +#include <linux/suspend.h>
> 
> Please sort the includes alphabetically.

I'm amazed I did this, I suspect I inherited it from the original file
because I'm also used to annoy people for the same thing! Shame on me!

> > +	activity_data->time_left -= 100;
> > +	if (activity_data->time_left <= 0) {
> > +		activity_data->time_left = 0;
> > +		activity_data->state = !activity_data->state;
> > +		led_set_brightness_nosleep(led_cdev,
> > +			(activity_data->state ^ activity_data->invert) ?
> > +			led_cdev->max_brightness : LED_OFF);
> 
> Have you considered making the top brightness adjustable? I'd make it
> possible especially that we have a similar solution in the
> ledtrig-heartbeat.c already - see the following patch in 4.12:
> 
> commit fb3d769173d26268d7bf068094a599bb28b2ac63
> Author: Jacek Anaszewski <j.anaszewski@samsung.com>
> Date:   Wed Nov 9 11:43:46 2016 +0100
(...)

I never thought about it and it makes a lot of sense actually. I'll check
this commit, thanks for the pointer.

> > +	switch (pm_event) {
> > +	case PM_SUSPEND_PREPARE:
> > +	case PM_HIBERNATION_PREPARE:
> > +	case PM_RESTORE_PREPARE:
> > +		led_trigger_unregister(&activity_led_trigger);
> > +		break;
> > +	case PM_POST_SUSPEND:
> > +	case PM_POST_HIBERNATION:
> > +	case PM_POST_RESTORE:
> > +		rc = led_trigger_register(&activity_led_trigger);
> > +		if (rc)
> > +			pr_err("could not re-register activity trigger\n");
> > +		break;
> > +	default:
> > +		break;
> > +	}
> > +	return NOTIFY_DONE;
> > +}
> 
> It turned out to cause problems in ledtrig-heartbeat.c and was reverted.
> Please don't register pm notifier and remove related facilities from the
> patch according to the following revert patch:
> 
> commit 436c4c45b5b9562b59cedbb51b7343ab4a6dd8cc
> Author: Zhang Bo <bo.zhang@nxp.com>
> Date:   Tue Jun 13 10:39:20 2017 +0800

OK fine for me. I thought it was mandatory to properly handle pm
eventhough I was not particularly interested in this for this
specific purpose.

I'll send you an updated patch ASAP.

Thanks very much for your review,
Willy

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v3] leds/trigger/activity: add a system activity LED trigger
       [not found]       ` <1503945891-31722-1-git-send-email-w@1wt.eu>
@ 2017-08-29 20:44         ` Jacek Anaszewski
  2017-08-30  2:38           ` Willy Tarreau
  0 siblings, 1 reply; 11+ messages in thread
From: Jacek Anaszewski @ 2017-08-29 20:44 UTC (permalink / raw)
  To: Willy Tarreau; +Cc: Richard Purdie, Pavel Machek, linux-kernel, linux-leds

Hi Willy,

Thanks for the update.

On 08/28/2017 08:44 PM, Willy Tarreau wrote:
> The "activity" trigger was inspired by the heartbeat one, but aims at
> providing instant indication of the immediate CPU usage. Under idle
> condition, it flashes 10ms every second. At 100% usage, it flashes
> 90ms every 100ms. The blinking frequency increases from 1 to 10 Hz
> until either the load is high enough to saturate one CPU core or 50%
> load is reached on a single-core system. Then past this point only the
> duty cycle increases from 10 to 90%.
> 
> This results in a very visible activity reporting allowing one to
> immediately tell whether a machine is under load or not, making it
> quite suitable to be used in clusters.
> 
> Signed-off-by: Willy Tarreau <w@1wt.eu>
> 
> ---
> 
> I've applied all the changes you recommended and tested them on an
> orangepi-pc2, everything still works fine.
> 
> since v2:
>   - sorted includes
>   - removed pm notifiers as in 436c4c45b5
>   - made max brightness adjustable as in fb3d7691
> since v1:
>   - update to 4.13-rc6 by getting rid of cputime64_to_jiffies64()
>   - fix behaviour on NO_HZ where cumulated idle time could be wrong
> ---
>  drivers/leds/trigger/Kconfig            |   9 ++
>  drivers/leds/trigger/Makefile           |   1 +
>  drivers/leds/trigger/ledtrig-activity.c | 273 ++++++++++++++++++++++++++++++++
>  3 files changed, 283 insertions(+)
>  create mode 100644 drivers/leds/trigger/ledtrig-activity.c
> 
> diff --git a/drivers/leds/trigger/Kconfig b/drivers/leds/trigger/Kconfig
> index 3f9ddb9..8432d9e 100644
> --- a/drivers/leds/trigger/Kconfig
> +++ b/drivers/leds/trigger/Kconfig
> @@ -77,6 +77,15 @@ config LEDS_TRIGGER_CPU
>  
>  	  If unsure, say N.
>  
> +config LEDS_TRIGGER_ACTIVITY
> +	tristate "LED activity Trigger"
> +	depends on LEDS_TRIGGERS
> +	help
> +	  This allows LEDs to be controlled by a immediate CPU usage.
> +	  The flash frequency and duty cycle varies from faint flashes to
> +	  intense brightness depending on the instant CPU load.
> +	  If unsure, say Y.
> +
>  config LEDS_TRIGGER_GPIO
>  	tristate "LED GPIO Trigger"
>  	depends on LEDS_TRIGGERS
> diff --git a/drivers/leds/trigger/Makefile b/drivers/leds/trigger/Makefile
> index a72c43c..e572ce57 100644
> --- a/drivers/leds/trigger/Makefile
> +++ b/drivers/leds/trigger/Makefile
> @@ -6,6 +6,7 @@ obj-$(CONFIG_LEDS_TRIGGER_HEARTBEAT)	+= ledtrig-heartbeat.o
>  obj-$(CONFIG_LEDS_TRIGGER_BACKLIGHT)	+= ledtrig-backlight.o
>  obj-$(CONFIG_LEDS_TRIGGER_GPIO)		+= ledtrig-gpio.o
>  obj-$(CONFIG_LEDS_TRIGGER_CPU)		+= ledtrig-cpu.o
> +obj-$(CONFIG_LEDS_TRIGGER_ACTIVITY)	+= ledtrig-activity.o
>  obj-$(CONFIG_LEDS_TRIGGER_DEFAULT_ON)	+= ledtrig-default-on.o
>  obj-$(CONFIG_LEDS_TRIGGER_TRANSIENT)	+= ledtrig-transient.o
>  obj-$(CONFIG_LEDS_TRIGGER_CAMERA)	+= ledtrig-camera.o
> diff --git a/drivers/leds/trigger/ledtrig-activity.c b/drivers/leds/trigger/ledtrig-activity.c
> new file mode 100644
> index 0000000..c6635c5
> --- /dev/null
> +++ b/drivers/leds/trigger/ledtrig-activity.c
> @@ -0,0 +1,273 @@
> +/*
> + * Activity LED trigger
> + *
> + * Copyright (C) 2017 Willy Tarreau <w@1wt.eu>
> + * Partially based on Atsushi Nemoto's ledtrig-heartbeat.c.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/kernel_stat.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +#include <linux/reboot.h>
> +#include <linux/sched.h>
> +#include <linux/slab.h>
> +#include <linux/timer.h>
> +#include "../leds.h"
> +
> +static int panic_detected;
> +
> +struct activity_data {
> +	struct timer_list timer;
> +	u64 last_used;
> +	u64 last_boot;
> +	int time_left;
> +	int state;
> +	int invert;
> +};
> +
> +static void led_activity_function(unsigned long data)
> +{
> +	struct led_classdev *led_cdev = (struct led_classdev *)data;
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +	struct timespec boot_time;
> +	unsigned int target;
> +	unsigned int usage;
> +	int delay;
> +	u64 curr_used;
> +	u64 curr_boot;
> +	s32 diff_used;
> +	s32 diff_boot;
> +	int cpus;
> +	int i;
> +
> +	if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
> +		led_cdev->blink_brightness = led_cdev->new_blink_brightness;
> +
> +	if (unlikely(panic_detected)) {
> +		/* full brightness in case of panic */
> +		led_set_brightness_nosleep(led_cdev, led_cdev->blink_brightness);
> +		return;
> +	}
> +
> +	get_monotonic_boottime(&boot_time);
> +
> +	cpus = 0;
> +	curr_used = 0;
> +
> +	for_each_possible_cpu(i) {
> +		curr_used += kcpustat_cpu(i).cpustat[CPUTIME_USER]
> +			  +  kcpustat_cpu(i).cpustat[CPUTIME_NICE]
> +			  +  kcpustat_cpu(i).cpustat[CPUTIME_SYSTEM]
> +			  +  kcpustat_cpu(i).cpustat[CPUTIME_SOFTIRQ]
> +			  +  kcpustat_cpu(i).cpustat[CPUTIME_IRQ];
> +		cpus++;
> +	}
> +
> +	/* We come here every 100ms in the worst case, so that's 100M ns of
> +	 * cumulated time. By dividing by 2^16, we get the time resolution
> +	 * down to 16us, ensuring we won't overflow 32-bit computations below
> +	 * even up to 3k CPUs, while keeping divides cheap on smaller systems.
> +	 */
> +	curr_boot = timespec_to_ns(&boot_time) * cpus;
> +	diff_boot = (curr_boot - activity_data->last_boot) >> 16;
> +	diff_used = (curr_used - activity_data->last_used) >> 16;
> +	activity_data->last_boot = curr_boot;
> +	activity_data->last_used = curr_used;
> +
> +	if (diff_boot <= 0 || diff_used < 0)
> +		usage = 0;
> +	else if (diff_used >= diff_boot)
> +		usage = 100;
> +	else
> +		usage = 100 * diff_used / diff_boot;
> +
> +	/*
> +	 * Now we know the total boot_time multiplied by the number of CPUs, and
> +	 * the total idle+wait time for all CPUs. We'll compare how they evolved
> +	 * since last call. The % of overall CPU usage is :
> +	 *
> +	 *      1 - delta_idle / delta_boot
> +	 *
> +	 * What we want is that when the CPU usage is zero, the LED must blink
> +	 * slowly with very faint flashes that are detectable but not disturbing
> +	 * (typically 10ms every second, or 10ms ON, 990ms OFF). Then we want
> +	 * blinking frequency to increase up to the point where the load is
> +	 * enough to saturate one core in multi-core systems or 50% in single
> +	 * core systems. At this point it should reach 10 Hz with a 10/90 duty
> +	 * cycle (10ms ON, 90ms OFF). After this point, the blinking frequency
> +	 * remains stable (10 Hz) and only the duty cycle increases to report
> +	 * the activity, up to the point where we have 90ms ON, 10ms OFF when
> +	 * all cores are saturated. It's important that the LED never stays in
> +	 * a steady state so that it's easy to distinguish an idle or saturated
> +	 * machine from a hung one.
> +	 *
> +	 * This gives us :
> +	 *   - a target CPU usage of min(50%, 100%/#CPU) for a 10% duty cycle
> +	 *     (10ms ON, 90ms OFF)
> +	 *   - below target :
> +	 *      ON_ms  = 10
> +	 *      OFF_ms = 90 + (1 - usage/target) * 900
> +	 *   - above target :
> +	 *      ON_ms  = 10 + (usage-target)/(100%-target) * 80
> +	 *      OFF_ms = 90 - (usage-target)/(100%-target) * 80
> +	 *
> +	 * In order to keep a good responsiveness, we cap the sleep time to
> +	 * 100 ms and keep track of the sleep time left. This allows us to
> +	 * quickly change it if needed.
> +	 */
> +
> +	activity_data->time_left -= 100;
> +	if (activity_data->time_left <= 0) {
> +		activity_data->time_left = 0;
> +		activity_data->state = !activity_data->state;
> +		led_set_brightness_nosleep(led_cdev,
> +			(activity_data->state ^ activity_data->invert) ?
> +			led_cdev->blink_brightness : LED_OFF);
> +	}
> +
> +	target = (cpus > 1) ? (100 / cpus) : 50;
> +
> +	if (usage < target)
> +		delay = activity_data->state ?
> +			10 :                        /* ON  */
> +			990 - 900 * usage / target; /* OFF */
> +	else
> +		delay = activity_data->state ?
> +			10 + 80 * (usage - target) / (100 - target) : /* ON  */
> +			90 - 80 * (usage - target) / (100 - target);  /* OFF */
> +
> +
> +	if (!activity_data->time_left || delay <= activity_data->time_left)
> +		activity_data->time_left = delay;
> +
> +	delay = min_t(int, activity_data->time_left, 100);
> +	mod_timer(&activity_data->timer, jiffies + msecs_to_jiffies(delay));
> +}
> +
> +static ssize_t led_invert_show(struct device *dev,
> +                               struct device_attribute *attr, char *buf)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +
> +	return sprintf(buf, "%u\n", activity_data->invert);
> +}
> +
> +static ssize_t led_invert_store(struct device *dev,
> +                                struct device_attribute *attr,
> +                                const char *buf, size_t size)
> +{
> +	struct led_classdev *led_cdev = dev_get_drvdata(dev);
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +	unsigned long state;
> +	int ret;
> +
> +	ret = kstrtoul(buf, 0, &state);
> +	if (ret)
> +		return ret;
> +
> +	activity_data->invert = !!state;
> +
> +	return size;
> +}
> +
> +static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
> +
> +static void activity_activate(struct led_classdev *led_cdev)
> +{
> +	struct activity_data *activity_data;
> +	int rc;
> +
> +	activity_data = kzalloc(sizeof(*activity_data), GFP_KERNEL);
> +	if (!activity_data)
> +		return;
> +
> +	led_cdev->trigger_data = activity_data;
> +	rc = device_create_file(led_cdev->dev, &dev_attr_invert);
> +	if (rc) {
> +		kfree(led_cdev->trigger_data);
> +		return;
> +	}
> +
> +	setup_timer(&activity_data->timer,
> +		    led_activity_function, (unsigned long)led_cdev);
> +	if (!led_cdev->blink_brightness)
> +		led_cdev->blink_brightness = led_cdev->max_brightness;
> +	led_activity_function(activity_data->timer.data);
> +	set_bit(LED_BLINK_SW, &led_cdev->work_flags);
> +	led_cdev->activated = true;
> +}
> +
> +static void activity_deactivate(struct led_classdev *led_cdev)
> +{
> +	struct activity_data *activity_data = led_cdev->trigger_data;
> +
> +	if (led_cdev->activated) {
> +		del_timer_sync(&activity_data->timer);
> +		device_remove_file(led_cdev->dev, &dev_attr_invert);
> +		kfree(activity_data);
> +		clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
> +		led_cdev->activated = false;
> +	}
> +}
> +
> +static struct led_trigger activity_led_trigger = {
> +	.name       = "activity",
> +	.activate   = activity_activate,
> +	.deactivate = activity_deactivate,
> +};
> +
> +static int activity_reboot_notifier(struct notifier_block *nb,
> +                                    unsigned long code, void *unused)
> +{
> +	led_trigger_unregister(&activity_led_trigger);
> +	return NOTIFY_DONE;
> +}
> +
> +static int activity_panic_notifier(struct notifier_block *nb,
> +                                   unsigned long code, void *unused)
> +{
> +	panic_detected = 1;
> +	return NOTIFY_DONE;
> +}
> +
> +static struct notifier_block activity_reboot_nb = {
> +	.notifier_call = activity_reboot_notifier,
> +};
> +
> +static struct notifier_block activity_panic_nb = {
> +	.notifier_call = activity_panic_notifier,
> +};
> +
> +static int __init activity_init(void)
> +{
> +	int rc = led_trigger_register(&activity_led_trigger);
> +
> +	if (!rc) {
> +		atomic_notifier_chain_register(&panic_notifier_list,
> +					       &activity_panic_nb);
> +		register_reboot_notifier(&activity_reboot_nb);
> +	}
> +	return rc;
> +}
> +
> +static void __exit activity_exit(void)
> +{
> +	unregister_reboot_notifier(&activity_reboot_nb);
> +	atomic_notifier_chain_unregister(&panic_notifier_list,
> +					 &activity_panic_nb);
> +	led_trigger_unregister(&activity_led_trigger);
> +}
> +
> +module_init(activity_init);
> +module_exit(activity_exit);
> +
> +MODULE_AUTHOR("Willy Tarreau <w@1wt.eu>");
> +MODULE_DESCRIPTION("Activity LED trigger");
> +MODULE_LICENSE("GPL");
> 

Applied to the for-4.15 branch of linux-leds.git.

-- 
Best regards,
Jacek Anaszewski

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v3] leds/trigger/activity: add a system activity LED trigger
  2017-08-29 20:44         ` [PATCH v3] " Jacek Anaszewski
@ 2017-08-30  2:38           ` Willy Tarreau
  0 siblings, 0 replies; 11+ messages in thread
From: Willy Tarreau @ 2017-08-30  2:38 UTC (permalink / raw)
  To: Jacek Anaszewski; +Cc: Richard Purdie, Pavel Machek, linux-kernel, linux-leds

Hi Jacek,

On Tue, Aug 29, 2017 at 10:44:03PM +0200, Jacek Anaszewski wrote:
> Applied to the for-4.15 branch of linux-leds.git.

Great, thanks!

Willy

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2017-08-30  2:38 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-11 23:41 [PATCH] leds/trigger/activity: add a system activity LED trigger Willy Tarreau
2017-02-15 10:24 ` Pavel Machek
2017-02-15 10:42   ` Willy Tarreau
2017-02-15 21:10     ` Pavel Machek
2017-03-09 20:48 ` Jacek Anaszewski
2017-03-10  6:44   ` Willy Tarreau
2017-08-24 12:07   ` Willy Tarreau
2017-08-27 16:44     ` Jacek Anaszewski
2017-08-28  6:57       ` Willy Tarreau
     [not found]       ` <1503945891-31722-1-git-send-email-w@1wt.eu>
2017-08-29 20:44         ` [PATCH v3] " Jacek Anaszewski
2017-08-30  2:38           ` Willy Tarreau

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.