cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ledtrig-heartbeat.c (5566B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * LED Heartbeat Trigger
      4 *
      5 * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
      6 *
      7 * Based on Richard Purdie's ledtrig-timer.c and some arch's
      8 * CONFIG_HEARTBEAT code.
      9 */
     10
     11#include <linux/module.h>
     12#include <linux/kernel.h>
     13#include <linux/init.h>
     14#include <linux/panic_notifier.h>
     15#include <linux/slab.h>
     16#include <linux/timer.h>
     17#include <linux/sched.h>
     18#include <linux/sched/loadavg.h>
     19#include <linux/leds.h>
     20#include <linux/reboot.h>
     21#include "../leds.h"
     22
     23static int panic_heartbeats;
     24
     25struct heartbeat_trig_data {
     26	struct led_classdev *led_cdev;
     27	unsigned int phase;
     28	unsigned int period;
     29	struct timer_list timer;
     30	unsigned int invert;
     31};
     32
     33static void led_heartbeat_function(struct timer_list *t)
     34{
     35	struct heartbeat_trig_data *heartbeat_data =
     36		from_timer(heartbeat_data, t, timer);
     37	struct led_classdev *led_cdev;
     38	unsigned long brightness = LED_OFF;
     39	unsigned long delay = 0;
     40
     41	led_cdev = heartbeat_data->led_cdev;
     42
     43	if (unlikely(panic_heartbeats)) {
     44		led_set_brightness_nosleep(led_cdev, LED_OFF);
     45		return;
     46	}
     47
     48	if (test_and_clear_bit(LED_BLINK_BRIGHTNESS_CHANGE, &led_cdev->work_flags))
     49		led_cdev->blink_brightness = led_cdev->new_blink_brightness;
     50
     51	/* acts like an actual heart beat -- ie thump-thump-pause... */
     52	switch (heartbeat_data->phase) {
     53	case 0:
     54		/*
     55		 * The hyperbolic function below modifies the
     56		 * heartbeat period length in dependency of the
     57		 * current (1min) load. It goes through the points
     58		 * f(0)=1260, f(1)=860, f(5)=510, f(inf)->300.
     59		 */
     60		heartbeat_data->period = 300 +
     61			(6720 << FSHIFT) / (5 * avenrun[0] + (7 << FSHIFT));
     62		heartbeat_data->period =
     63			msecs_to_jiffies(heartbeat_data->period);
     64		delay = msecs_to_jiffies(70);
     65		heartbeat_data->phase++;
     66		if (!heartbeat_data->invert)
     67			brightness = led_cdev->blink_brightness;
     68		break;
     69	case 1:
     70		delay = heartbeat_data->period / 4 - msecs_to_jiffies(70);
     71		heartbeat_data->phase++;
     72		if (heartbeat_data->invert)
     73			brightness = led_cdev->blink_brightness;
     74		break;
     75	case 2:
     76		delay = msecs_to_jiffies(70);
     77		heartbeat_data->phase++;
     78		if (!heartbeat_data->invert)
     79			brightness = led_cdev->blink_brightness;
     80		break;
     81	default:
     82		delay = heartbeat_data->period - heartbeat_data->period / 4 -
     83			msecs_to_jiffies(70);
     84		heartbeat_data->phase = 0;
     85		if (heartbeat_data->invert)
     86			brightness = led_cdev->blink_brightness;
     87		break;
     88	}
     89
     90	led_set_brightness_nosleep(led_cdev, brightness);
     91	mod_timer(&heartbeat_data->timer, jiffies + delay);
     92}
     93
     94static ssize_t led_invert_show(struct device *dev,
     95		struct device_attribute *attr, char *buf)
     96{
     97	struct heartbeat_trig_data *heartbeat_data =
     98		led_trigger_get_drvdata(dev);
     99
    100	return sprintf(buf, "%u\n", heartbeat_data->invert);
    101}
    102
    103static ssize_t led_invert_store(struct device *dev,
    104		struct device_attribute *attr, const char *buf, size_t size)
    105{
    106	struct heartbeat_trig_data *heartbeat_data =
    107		led_trigger_get_drvdata(dev);
    108	unsigned long state;
    109	int ret;
    110
    111	ret = kstrtoul(buf, 0, &state);
    112	if (ret)
    113		return ret;
    114
    115	heartbeat_data->invert = !!state;
    116
    117	return size;
    118}
    119
    120static DEVICE_ATTR(invert, 0644, led_invert_show, led_invert_store);
    121
    122static struct attribute *heartbeat_trig_attrs[] = {
    123	&dev_attr_invert.attr,
    124	NULL
    125};
    126ATTRIBUTE_GROUPS(heartbeat_trig);
    127
    128static int heartbeat_trig_activate(struct led_classdev *led_cdev)
    129{
    130	struct heartbeat_trig_data *heartbeat_data;
    131
    132	heartbeat_data = kzalloc(sizeof(*heartbeat_data), GFP_KERNEL);
    133	if (!heartbeat_data)
    134		return -ENOMEM;
    135
    136	led_set_trigger_data(led_cdev, heartbeat_data);
    137	heartbeat_data->led_cdev = led_cdev;
    138
    139	timer_setup(&heartbeat_data->timer, led_heartbeat_function, 0);
    140	heartbeat_data->phase = 0;
    141	if (!led_cdev->blink_brightness)
    142		led_cdev->blink_brightness = led_cdev->max_brightness;
    143	led_heartbeat_function(&heartbeat_data->timer);
    144	set_bit(LED_BLINK_SW, &led_cdev->work_flags);
    145
    146	return 0;
    147}
    148
    149static void heartbeat_trig_deactivate(struct led_classdev *led_cdev)
    150{
    151	struct heartbeat_trig_data *heartbeat_data =
    152		led_get_trigger_data(led_cdev);
    153
    154	del_timer_sync(&heartbeat_data->timer);
    155	kfree(heartbeat_data);
    156	clear_bit(LED_BLINK_SW, &led_cdev->work_flags);
    157}
    158
    159static struct led_trigger heartbeat_led_trigger = {
    160	.name     = "heartbeat",
    161	.activate = heartbeat_trig_activate,
    162	.deactivate = heartbeat_trig_deactivate,
    163	.groups = heartbeat_trig_groups,
    164};
    165
    166static int heartbeat_reboot_notifier(struct notifier_block *nb,
    167				     unsigned long code, void *unused)
    168{
    169	led_trigger_unregister(&heartbeat_led_trigger);
    170	return NOTIFY_DONE;
    171}
    172
    173static int heartbeat_panic_notifier(struct notifier_block *nb,
    174				     unsigned long code, void *unused)
    175{
    176	panic_heartbeats = 1;
    177	return NOTIFY_DONE;
    178}
    179
    180static struct notifier_block heartbeat_reboot_nb = {
    181	.notifier_call = heartbeat_reboot_notifier,
    182};
    183
    184static struct notifier_block heartbeat_panic_nb = {
    185	.notifier_call = heartbeat_panic_notifier,
    186};
    187
    188static int __init heartbeat_trig_init(void)
    189{
    190	int rc = led_trigger_register(&heartbeat_led_trigger);
    191
    192	if (!rc) {
    193		atomic_notifier_chain_register(&panic_notifier_list,
    194					       &heartbeat_panic_nb);
    195		register_reboot_notifier(&heartbeat_reboot_nb);
    196	}
    197	return rc;
    198}
    199
    200static void __exit heartbeat_trig_exit(void)
    201{
    202	unregister_reboot_notifier(&heartbeat_reboot_nb);
    203	atomic_notifier_chain_unregister(&panic_notifier_list,
    204					 &heartbeat_panic_nb);
    205	led_trigger_unregister(&heartbeat_led_trigger);
    206}
    207
    208module_init(heartbeat_trig_init);
    209module_exit(heartbeat_trig_exit);
    210
    211MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
    212MODULE_DESCRIPTION("Heartbeat LED trigger");
    213MODULE_LICENSE("GPL v2");