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

led-triggers.c (11394B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * LED Triggers Core
      4 *
      5 * Copyright 2005-2007 Openedhand Ltd.
      6 *
      7 * Author: Richard Purdie <rpurdie@openedhand.com>
      8 */
      9
     10#include <linux/export.h>
     11#include <linux/kernel.h>
     12#include <linux/list.h>
     13#include <linux/spinlock.h>
     14#include <linux/device.h>
     15#include <linux/timer.h>
     16#include <linux/rwsem.h>
     17#include <linux/leds.h>
     18#include <linux/slab.h>
     19#include <linux/mm.h>
     20#include "leds.h"
     21
     22/*
     23 * Nests outside led_cdev->trigger_lock
     24 */
     25static DECLARE_RWSEM(triggers_list_lock);
     26LIST_HEAD(trigger_list);
     27
     28 /* Used by LED Class */
     29
     30static inline bool
     31trigger_relevant(struct led_classdev *led_cdev, struct led_trigger *trig)
     32{
     33	return !trig->trigger_type || trig->trigger_type == led_cdev->trigger_type;
     34}
     35
     36ssize_t led_trigger_write(struct file *filp, struct kobject *kobj,
     37			  struct bin_attribute *bin_attr, char *buf,
     38			  loff_t pos, size_t count)
     39{
     40	struct device *dev = kobj_to_dev(kobj);
     41	struct led_classdev *led_cdev = dev_get_drvdata(dev);
     42	struct led_trigger *trig;
     43	int ret = count;
     44
     45	mutex_lock(&led_cdev->led_access);
     46
     47	if (led_sysfs_is_disabled(led_cdev)) {
     48		ret = -EBUSY;
     49		goto unlock;
     50	}
     51
     52	if (sysfs_streq(buf, "none")) {
     53		led_trigger_remove(led_cdev);
     54		goto unlock;
     55	}
     56
     57	down_read(&triggers_list_lock);
     58	list_for_each_entry(trig, &trigger_list, next_trig) {
     59		if (sysfs_streq(buf, trig->name) && trigger_relevant(led_cdev, trig)) {
     60			down_write(&led_cdev->trigger_lock);
     61			led_trigger_set(led_cdev, trig);
     62			up_write(&led_cdev->trigger_lock);
     63
     64			up_read(&triggers_list_lock);
     65			goto unlock;
     66		}
     67	}
     68	/* we come here only if buf matches no trigger */
     69	ret = -EINVAL;
     70	up_read(&triggers_list_lock);
     71
     72unlock:
     73	mutex_unlock(&led_cdev->led_access);
     74	return ret;
     75}
     76EXPORT_SYMBOL_GPL(led_trigger_write);
     77
     78__printf(3, 4)
     79static int led_trigger_snprintf(char *buf, ssize_t size, const char *fmt, ...)
     80{
     81	va_list args;
     82	int i;
     83
     84	va_start(args, fmt);
     85	if (size <= 0)
     86		i = vsnprintf(NULL, 0, fmt, args);
     87	else
     88		i = vscnprintf(buf, size, fmt, args);
     89	va_end(args);
     90
     91	return i;
     92}
     93
     94static int led_trigger_format(char *buf, size_t size,
     95			      struct led_classdev *led_cdev)
     96{
     97	struct led_trigger *trig;
     98	int len = led_trigger_snprintf(buf, size, "%s",
     99				       led_cdev->trigger ? "none" : "[none]");
    100
    101	list_for_each_entry(trig, &trigger_list, next_trig) {
    102		bool hit;
    103
    104		if (!trigger_relevant(led_cdev, trig))
    105			continue;
    106
    107		hit = led_cdev->trigger && !strcmp(led_cdev->trigger->name, trig->name);
    108
    109		len += led_trigger_snprintf(buf + len, size - len,
    110					    " %s%s%s", hit ? "[" : "",
    111					    trig->name, hit ? "]" : "");
    112	}
    113
    114	len += led_trigger_snprintf(buf + len, size - len, "\n");
    115
    116	return len;
    117}
    118
    119/*
    120 * It was stupid to create 10000 cpu triggers, but we are stuck with it now.
    121 * Don't make that mistake again. We work around it here by creating binary
    122 * attribute, which is not limited by length. This is _not_ good design, do not
    123 * copy it.
    124 */
    125ssize_t led_trigger_read(struct file *filp, struct kobject *kobj,
    126			struct bin_attribute *attr, char *buf,
    127			loff_t pos, size_t count)
    128{
    129	struct device *dev = kobj_to_dev(kobj);
    130	struct led_classdev *led_cdev = dev_get_drvdata(dev);
    131	void *data;
    132	int len;
    133
    134	down_read(&triggers_list_lock);
    135	down_read(&led_cdev->trigger_lock);
    136
    137	len = led_trigger_format(NULL, 0, led_cdev);
    138	data = kvmalloc(len + 1, GFP_KERNEL);
    139	if (!data) {
    140		up_read(&led_cdev->trigger_lock);
    141		up_read(&triggers_list_lock);
    142		return -ENOMEM;
    143	}
    144	len = led_trigger_format(data, len + 1, led_cdev);
    145
    146	up_read(&led_cdev->trigger_lock);
    147	up_read(&triggers_list_lock);
    148
    149	len = memory_read_from_buffer(buf, count, &pos, data, len);
    150
    151	kvfree(data);
    152
    153	return len;
    154}
    155EXPORT_SYMBOL_GPL(led_trigger_read);
    156
    157/* Caller must ensure led_cdev->trigger_lock held */
    158int led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trig)
    159{
    160	char *event = NULL;
    161	char *envp[2];
    162	const char *name;
    163	int ret;
    164
    165	if (!led_cdev->trigger && !trig)
    166		return 0;
    167
    168	name = trig ? trig->name : "none";
    169	event = kasprintf(GFP_KERNEL, "TRIGGER=%s", name);
    170
    171	/* Remove any existing trigger */
    172	if (led_cdev->trigger) {
    173		spin_lock(&led_cdev->trigger->leddev_list_lock);
    174		list_del_rcu(&led_cdev->trig_list);
    175		spin_unlock(&led_cdev->trigger->leddev_list_lock);
    176
    177		/* ensure it's no longer visible on the led_cdevs list */
    178		synchronize_rcu();
    179
    180		cancel_work_sync(&led_cdev->set_brightness_work);
    181		led_stop_software_blink(led_cdev);
    182		if (led_cdev->trigger->deactivate)
    183			led_cdev->trigger->deactivate(led_cdev);
    184		device_remove_groups(led_cdev->dev, led_cdev->trigger->groups);
    185		led_cdev->trigger = NULL;
    186		led_cdev->trigger_data = NULL;
    187		led_cdev->activated = false;
    188		led_set_brightness(led_cdev, LED_OFF);
    189	}
    190	if (trig) {
    191		spin_lock(&trig->leddev_list_lock);
    192		list_add_tail_rcu(&led_cdev->trig_list, &trig->led_cdevs);
    193		spin_unlock(&trig->leddev_list_lock);
    194		led_cdev->trigger = trig;
    195
    196		if (trig->activate)
    197			ret = trig->activate(led_cdev);
    198		else
    199			ret = 0;
    200
    201		if (ret)
    202			goto err_activate;
    203
    204		ret = device_add_groups(led_cdev->dev, trig->groups);
    205		if (ret) {
    206			dev_err(led_cdev->dev, "Failed to add trigger attributes\n");
    207			goto err_add_groups;
    208		}
    209	}
    210
    211	if (event) {
    212		envp[0] = event;
    213		envp[1] = NULL;
    214		if (kobject_uevent_env(&led_cdev->dev->kobj, KOBJ_CHANGE, envp))
    215			dev_err(led_cdev->dev,
    216				"%s: Error sending uevent\n", __func__);
    217		kfree(event);
    218	}
    219
    220	return 0;
    221
    222err_add_groups:
    223
    224	if (trig->deactivate)
    225		trig->deactivate(led_cdev);
    226err_activate:
    227
    228	spin_lock(&led_cdev->trigger->leddev_list_lock);
    229	list_del_rcu(&led_cdev->trig_list);
    230	spin_unlock(&led_cdev->trigger->leddev_list_lock);
    231	synchronize_rcu();
    232	led_cdev->trigger = NULL;
    233	led_cdev->trigger_data = NULL;
    234	led_set_brightness(led_cdev, LED_OFF);
    235	kfree(event);
    236
    237	return ret;
    238}
    239EXPORT_SYMBOL_GPL(led_trigger_set);
    240
    241void led_trigger_remove(struct led_classdev *led_cdev)
    242{
    243	down_write(&led_cdev->trigger_lock);
    244	led_trigger_set(led_cdev, NULL);
    245	up_write(&led_cdev->trigger_lock);
    246}
    247EXPORT_SYMBOL_GPL(led_trigger_remove);
    248
    249void led_trigger_set_default(struct led_classdev *led_cdev)
    250{
    251	struct led_trigger *trig;
    252
    253	if (!led_cdev->default_trigger)
    254		return;
    255
    256	down_read(&triggers_list_lock);
    257	down_write(&led_cdev->trigger_lock);
    258	list_for_each_entry(trig, &trigger_list, next_trig) {
    259		if (!strcmp(led_cdev->default_trigger, trig->name) &&
    260		    trigger_relevant(led_cdev, trig)) {
    261			led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
    262			led_trigger_set(led_cdev, trig);
    263			break;
    264		}
    265	}
    266	up_write(&led_cdev->trigger_lock);
    267	up_read(&triggers_list_lock);
    268}
    269EXPORT_SYMBOL_GPL(led_trigger_set_default);
    270
    271void led_trigger_rename_static(const char *name, struct led_trigger *trig)
    272{
    273	/* new name must be on a temporary string to prevent races */
    274	BUG_ON(name == trig->name);
    275
    276	down_write(&triggers_list_lock);
    277	/* this assumes that trig->name was originaly allocated to
    278	 * non constant storage */
    279	strcpy((char *)trig->name, name);
    280	up_write(&triggers_list_lock);
    281}
    282EXPORT_SYMBOL_GPL(led_trigger_rename_static);
    283
    284/* LED Trigger Interface */
    285
    286int led_trigger_register(struct led_trigger *trig)
    287{
    288	struct led_classdev *led_cdev;
    289	struct led_trigger *_trig;
    290
    291	spin_lock_init(&trig->leddev_list_lock);
    292	INIT_LIST_HEAD(&trig->led_cdevs);
    293
    294	down_write(&triggers_list_lock);
    295	/* Make sure the trigger's name isn't already in use */
    296	list_for_each_entry(_trig, &trigger_list, next_trig) {
    297		if (!strcmp(_trig->name, trig->name) &&
    298		    (trig->trigger_type == _trig->trigger_type ||
    299		     !trig->trigger_type || !_trig->trigger_type)) {
    300			up_write(&triggers_list_lock);
    301			return -EEXIST;
    302		}
    303	}
    304	/* Add to the list of led triggers */
    305	list_add_tail(&trig->next_trig, &trigger_list);
    306	up_write(&triggers_list_lock);
    307
    308	/* Register with any LEDs that have this as a default trigger */
    309	down_read(&leds_list_lock);
    310	list_for_each_entry(led_cdev, &leds_list, node) {
    311		down_write(&led_cdev->trigger_lock);
    312		if (!led_cdev->trigger && led_cdev->default_trigger &&
    313		    !strcmp(led_cdev->default_trigger, trig->name) &&
    314		    trigger_relevant(led_cdev, trig)) {
    315			led_cdev->flags |= LED_INIT_DEFAULT_TRIGGER;
    316			led_trigger_set(led_cdev, trig);
    317		}
    318		up_write(&led_cdev->trigger_lock);
    319	}
    320	up_read(&leds_list_lock);
    321
    322	return 0;
    323}
    324EXPORT_SYMBOL_GPL(led_trigger_register);
    325
    326void led_trigger_unregister(struct led_trigger *trig)
    327{
    328	struct led_classdev *led_cdev;
    329
    330	if (list_empty_careful(&trig->next_trig))
    331		return;
    332
    333	/* Remove from the list of led triggers */
    334	down_write(&triggers_list_lock);
    335	list_del_init(&trig->next_trig);
    336	up_write(&triggers_list_lock);
    337
    338	/* Remove anyone actively using this trigger */
    339	down_read(&leds_list_lock);
    340	list_for_each_entry(led_cdev, &leds_list, node) {
    341		down_write(&led_cdev->trigger_lock);
    342		if (led_cdev->trigger == trig)
    343			led_trigger_set(led_cdev, NULL);
    344		up_write(&led_cdev->trigger_lock);
    345	}
    346	up_read(&leds_list_lock);
    347}
    348EXPORT_SYMBOL_GPL(led_trigger_unregister);
    349
    350static void devm_led_trigger_release(struct device *dev, void *res)
    351{
    352	led_trigger_unregister(*(struct led_trigger **)res);
    353}
    354
    355int devm_led_trigger_register(struct device *dev,
    356			      struct led_trigger *trig)
    357{
    358	struct led_trigger **dr;
    359	int rc;
    360
    361	dr = devres_alloc(devm_led_trigger_release, sizeof(*dr),
    362			  GFP_KERNEL);
    363	if (!dr)
    364		return -ENOMEM;
    365
    366	*dr = trig;
    367
    368	rc = led_trigger_register(trig);
    369	if (rc)
    370		devres_free(dr);
    371	else
    372		devres_add(dev, dr);
    373
    374	return rc;
    375}
    376EXPORT_SYMBOL_GPL(devm_led_trigger_register);
    377
    378/* Simple LED Trigger Interface */
    379
    380void led_trigger_event(struct led_trigger *trig,
    381			enum led_brightness brightness)
    382{
    383	struct led_classdev *led_cdev;
    384
    385	if (!trig)
    386		return;
    387
    388	rcu_read_lock();
    389	list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list)
    390		led_set_brightness(led_cdev, brightness);
    391	rcu_read_unlock();
    392}
    393EXPORT_SYMBOL_GPL(led_trigger_event);
    394
    395static void led_trigger_blink_setup(struct led_trigger *trig,
    396			     unsigned long *delay_on,
    397			     unsigned long *delay_off,
    398			     int oneshot,
    399			     int invert)
    400{
    401	struct led_classdev *led_cdev;
    402
    403	if (!trig)
    404		return;
    405
    406	rcu_read_lock();
    407	list_for_each_entry_rcu(led_cdev, &trig->led_cdevs, trig_list) {
    408		if (oneshot)
    409			led_blink_set_oneshot(led_cdev, delay_on, delay_off,
    410					      invert);
    411		else
    412			led_blink_set(led_cdev, delay_on, delay_off);
    413	}
    414	rcu_read_unlock();
    415}
    416
    417void led_trigger_blink(struct led_trigger *trig,
    418		       unsigned long *delay_on,
    419		       unsigned long *delay_off)
    420{
    421	led_trigger_blink_setup(trig, delay_on, delay_off, 0, 0);
    422}
    423EXPORT_SYMBOL_GPL(led_trigger_blink);
    424
    425void led_trigger_blink_oneshot(struct led_trigger *trig,
    426			       unsigned long *delay_on,
    427			       unsigned long *delay_off,
    428			       int invert)
    429{
    430	led_trigger_blink_setup(trig, delay_on, delay_off, 1, invert);
    431}
    432EXPORT_SYMBOL_GPL(led_trigger_blink_oneshot);
    433
    434void led_trigger_register_simple(const char *name, struct led_trigger **tp)
    435{
    436	struct led_trigger *trig;
    437	int err;
    438
    439	trig = kzalloc(sizeof(struct led_trigger), GFP_KERNEL);
    440
    441	if (trig) {
    442		trig->name = name;
    443		err = led_trigger_register(trig);
    444		if (err < 0) {
    445			kfree(trig);
    446			trig = NULL;
    447			pr_warn("LED trigger %s failed to register (%d)\n",
    448				name, err);
    449		}
    450	} else {
    451		pr_warn("LED trigger %s failed to register (no memory)\n",
    452			name);
    453	}
    454	*tp = trig;
    455}
    456EXPORT_SYMBOL_GPL(led_trigger_register_simple);
    457
    458void led_trigger_unregister_simple(struct led_trigger *trig)
    459{
    460	if (trig)
    461		led_trigger_unregister(trig);
    462	kfree(trig);
    463}
    464EXPORT_SYMBOL_GPL(led_trigger_unregister_simple);