0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/init.h>
0010 #include <linux/kernel.h>
0011 #include <linux/kernel_stat.h>
0012 #include <linux/leds.h>
0013 #include <linux/module.h>
0014 #include <linux/panic_notifier.h>
0015 #include <linux/reboot.h>
0016 #include <linux/sched.h>
0017 #include <linux/slab.h>
0018 #include <linux/timer.h>
0019 #include "../leds.h"
0020
0021 static int panic_detected;
0022
0023 struct activity_data {
0024 struct timer_list timer;
0025 struct led_classdev *led_cdev;
0026 u64 last_used;
0027 u64 last_boot;
0028 int time_left;
0029 int state;
0030 int invert;
0031 };
0032
0033 static void led_activity_function(struct timer_list *t)
0034 {
0035 struct activity_data *activity_data = from_timer(activity_data, t,
0036 timer);
0037 struct led_classdev *led_cdev = activity_data->led_cdev;
0038 unsigned int target;
0039 unsigned int usage;
0040 int delay;
0041 u64 curr_used;
0042 u64 curr_boot;
0043 s32 diff_used;
0044 s32 diff_boot;
0045 int cpus;
0046 int i;
0047
0048 if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
0049 led_cdev->blink_brightness = led_cdev->new_blink_brightness;
0050
0051 if (unlikely(panic_detected)) {
0052
0053 led_set_brightness_nosleep(led_cdev, led_cdev->blink_brightness);
0054 return;
0055 }
0056
0057 cpus = 0;
0058 curr_used = 0;
0059
0060 for_each_possible_cpu(i) {
0061 struct kernel_cpustat kcpustat;
0062
0063 kcpustat_cpu_fetch(&kcpustat, i);
0064
0065 curr_used += kcpustat.cpustat[CPUTIME_USER]
0066 + kcpustat.cpustat[CPUTIME_NICE]
0067 + kcpustat.cpustat[CPUTIME_SYSTEM]
0068 + kcpustat.cpustat[CPUTIME_SOFTIRQ]
0069 + kcpustat.cpustat[CPUTIME_IRQ];
0070 cpus++;
0071 }
0072
0073
0074
0075
0076
0077
0078 curr_boot = ktime_get_boottime_ns() * cpus;
0079 diff_boot = (curr_boot - activity_data->last_boot) >> 16;
0080 diff_used = (curr_used - activity_data->last_used) >> 16;
0081 activity_data->last_boot = curr_boot;
0082 activity_data->last_used = curr_used;
0083
0084 if (diff_boot <= 0 || diff_used < 0)
0085 usage = 0;
0086 else if (diff_used >= diff_boot)
0087 usage = 100;
0088 else
0089 usage = 100 * diff_used / diff_boot;
0090
0091
0092
0093
0094
0095
0096
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124
0125
0126 activity_data->time_left -= 100;
0127 if (activity_data->time_left <= 0) {
0128 activity_data->time_left = 0;
0129 activity_data->state = !activity_data->state;
0130 led_set_brightness_nosleep(led_cdev,
0131 (activity_data->state ^ activity_data->invert) ?
0132 led_cdev->blink_brightness : LED_OFF);
0133 }
0134
0135 target = (cpus > 1) ? (100 / cpus) : 50;
0136
0137 if (usage < target)
0138 delay = activity_data->state ?
0139 10 :
0140 990 - 900 * usage / target;
0141 else
0142 delay = activity_data->state ?
0143 10 + 80 * (usage - target) / (100 - target) :
0144 90 - 80 * (usage - target) / (100 - target);
0145
0146
0147 if (!activity_data->time_left || delay <= activity_data->time_left)
0148 activity_data->time_left = delay;
0149
0150 delay = min_t(int, activity_data->time_left, 100);
0151 mod_timer(&activity_data->timer, jiffies + msecs_to_jiffies(delay));
0152 }
0153
0154 static ssize_t led_invert_show(struct device *dev,
0155 struct device_attribute *attr, char *buf)
0156 {
0157 struct activity_data *activity_data = led_trigger_get_drvdata(dev);
0158
0159 return sprintf(buf, "%u\n", activity_data->invert);
0160 }
0161
0162 static ssize_t led_invert_store(struct device *dev,
0163 struct device_attribute *attr,
0164 const char *buf, size_t size)
0165 {
0166 struct activity_data *activity_data = led_trigger_get_drvdata(dev);
0167 unsigned long state;
0168 int ret;
0169
0170 ret = kstrtoul(buf, 0, &state);
0171 if (ret)
0172 return ret;
0173
0174 activity_data->invert = !!state;
0175
0176 return size;
0177 }
0178
0179 static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
0180
0181 static struct attribute *activity_led_attrs[] = {
0182 &dev_attr_invert.attr,
0183 NULL
0184 };
0185 ATTRIBUTE_GROUPS(activity_led);
0186
0187 static int activity_activate(struct led_classdev *led_cdev)
0188 {
0189 struct activity_data *activity_data;
0190
0191 activity_data = kzalloc(sizeof(*activity_data), GFP_KERNEL);
0192 if (!activity_data)
0193 return -ENOMEM;
0194
0195 led_set_trigger_data(led_cdev, activity_data);
0196
0197 activity_data->led_cdev = led_cdev;
0198 timer_setup(&activity_data->timer, led_activity_function, 0);
0199 if (!led_cdev->blink_brightness)
0200 led_cdev->blink_brightness = led_cdev->max_brightness;
0201 led_activity_function(&activity_data->timer);
0202 set_bit(LED_BLINK_SW, &led_cdev->work_flags);
0203
0204 return 0;
0205 }
0206
0207 static void activity_deactivate(struct led_classdev *led_cdev)
0208 {
0209 struct activity_data *activity_data = led_get_trigger_data(led_cdev);
0210
0211 del_timer_sync(&activity_data->timer);
0212 kfree(activity_data);
0213 clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
0214 }
0215
0216 static struct led_trigger activity_led_trigger = {
0217 .name = "activity",
0218 .activate = activity_activate,
0219 .deactivate = activity_deactivate,
0220 .groups = activity_led_groups,
0221 };
0222
0223 static int activity_reboot_notifier(struct notifier_block *nb,
0224 unsigned long code, void *unused)
0225 {
0226 led_trigger_unregister(&activity_led_trigger);
0227 return NOTIFY_DONE;
0228 }
0229
0230 static int activity_panic_notifier(struct notifier_block *nb,
0231 unsigned long code, void *unused)
0232 {
0233 panic_detected = 1;
0234 return NOTIFY_DONE;
0235 }
0236
0237 static struct notifier_block activity_reboot_nb = {
0238 .notifier_call = activity_reboot_notifier,
0239 };
0240
0241 static struct notifier_block activity_panic_nb = {
0242 .notifier_call = activity_panic_notifier,
0243 };
0244
0245 static int __init activity_init(void)
0246 {
0247 int rc = led_trigger_register(&activity_led_trigger);
0248
0249 if (!rc) {
0250 atomic_notifier_chain_register(&panic_notifier_list,
0251 &activity_panic_nb);
0252 register_reboot_notifier(&activity_reboot_nb);
0253 }
0254 return rc;
0255 }
0256
0257 static void __exit activity_exit(void)
0258 {
0259 unregister_reboot_notifier(&activity_reboot_nb);
0260 atomic_notifier_chain_unregister(&panic_notifier_list,
0261 &activity_panic_nb);
0262 led_trigger_unregister(&activity_led_trigger);
0263 }
0264
0265 module_init(activity_init);
0266 module_exit(activity_exit);
0267
0268 MODULE_AUTHOR("Willy Tarreau <w@1wt.eu>");
0269 MODULE_DESCRIPTION("Activity LED trigger");
0270 MODULE_LICENSE("GPL v2");