diff options
| author | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 09:55:01 -0400 |
|---|---|---|
| committer | Steven Rostedt <rostedt@goodmis.org> | 2026-09-02 11:08:21 -0400 |
| commit | 4617721c502b2ddaa4e324e86da4997edf738fa5 (patch) | |
| tree | 350aa83b47da0b574a814795392b20b0bed83cbc /drivers/leds/trigger/ledtrig-gpio.c | |
| download | linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.tar.gz linux-stable-4617721c502b2ddaa4e324e86da4997edf738fa5.zip | |
ftrace: Synchronize the initialization of ftrace_opsgrafted
There's some internal state that ftrace_ops needs to have set, but since
it can be declared outside of the ftrace.c code, it calls
ftrace_ops_init() on the ops in every global function. The issue is that
if two tasks call it on the same ops at the same time it is possible to
have the initialization of one corrupt the initialization of the other
call.
Create a ops_mutex to use to synchronize every initialization of the
ftrace_ops. The mutex is taken within checking the ftrace_ops flag that
states it was initializied but the flag is checked again after the mutex
has been taken. Checking first outside the mutex allows it to shortcut
having to take the mutex. But then the check needs to be done again after
the mute is taken in case of races.
Cc: stable@vger.kernel.org
Link: https://patch.msgid.link/20260902095501.6b59af20@gandalf.local.home
Fixes: f04f24fb7e48d ("ftrace, kprobes: Fix a deadlock on ftrace_regex_lock")
Reported-by: sashiko-bot@kernel.org
Close: https://lore.kernel.org/all/20260829025528.49A831F000E9@smtp.kernel.org/
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
Diffstat (limited to 'drivers/leds/trigger/ledtrig-gpio.c')
| -rw-r--r-- | drivers/leds/trigger/ledtrig-gpio.c | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/drivers/leds/trigger/ledtrig-gpio.c b/drivers/leds/trigger/ledtrig-gpio.c new file mode 100644 index 000000000..3b3e6869e --- /dev/null +++ b/drivers/leds/trigger/ledtrig-gpio.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ledtrig-gio.c - LED Trigger Based on GPIO events + * + * Copyright 2009 Felipe Balbi <me@felipebalbi.com> + * Copyright 2023 Linus Walleij <linus.walleij@linaro.org> + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/gpio/consumer.h> +#include <linux/interrupt.h> +#include <linux/leds.h> +#include <linux/slab.h> +#include "../leds.h" + +struct gpio_trig_data { + struct led_classdev *led; + unsigned desired_brightness; /* desired brightness when led is on */ + struct gpio_desc *gpiod; /* gpio that triggers the led */ +}; + +static irqreturn_t gpio_trig_irq(int irq, void *_led) +{ + struct led_classdev *led = _led; + struct gpio_trig_data *gpio_data = led_get_trigger_data(led); + int tmp; + + tmp = gpiod_get_value_cansleep(gpio_data->gpiod); + if (tmp) { + if (gpio_data->desired_brightness) + led_set_brightness_nosleep(gpio_data->led, + gpio_data->desired_brightness); + else + led_set_brightness_nosleep(gpio_data->led, LED_FULL); + } else { + led_set_brightness_nosleep(gpio_data->led, LED_OFF); + } + + return IRQ_HANDLED; +} + +static ssize_t desired_brightness_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); + + return sysfs_emit(buf, "%u\n", gpio_data->desired_brightness); +} + +static ssize_t desired_brightness_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t n) +{ + struct gpio_trig_data *gpio_data = led_trigger_get_drvdata(dev); + u8 desired_brightness; + int ret; + + ret = kstrtou8(buf, 10, &desired_brightness); + if (ret) + return ret; + + gpio_data->desired_brightness = desired_brightness; + + return n; +} +static DEVICE_ATTR_RW(desired_brightness); + +static struct attribute *gpio_trig_attrs[] = { + &dev_attr_desired_brightness.attr, + NULL +}; +ATTRIBUTE_GROUPS(gpio_trig); + +static int gpio_trig_activate(struct led_classdev *led) +{ + struct gpio_trig_data *gpio_data; + struct device *dev = led->dev; + int ret; + + gpio_data = kzalloc_obj(*gpio_data); + if (!gpio_data) + return -ENOMEM; + + /* + * The generic property "trigger-sources" is followed, + * and we hope that this is a GPIO. + */ + gpio_data->gpiod = gpiod_get_optional(dev, "trigger-sources", + GPIOD_IN | GPIOD_FLAGS_BIT_NONEXCLUSIVE); + if (IS_ERR(gpio_data->gpiod)) { + ret = PTR_ERR(gpio_data->gpiod); + kfree(gpio_data); + return ret; + } + if (!gpio_data->gpiod) { + dev_err(dev, "no valid GPIO for the trigger\n"); + kfree(gpio_data); + return -EINVAL; + } + + gpiod_set_consumer_name(gpio_data->gpiod, "led-trigger"); + + gpio_data->led = led; + led_set_trigger_data(led, gpio_data); + + ret = request_threaded_irq(gpiod_to_irq(gpio_data->gpiod), NULL, gpio_trig_irq, + IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_RISING + | IRQF_TRIGGER_FALLING, "ledtrig-gpio", led); + if (ret) { + dev_err(dev, "request_irq failed with error %d\n", ret); + gpiod_put(gpio_data->gpiod); + kfree(gpio_data); + return ret; + } + + /* Finally update the LED to initial status */ + gpio_trig_irq(0, led); + + return 0; +} + +static void gpio_trig_deactivate(struct led_classdev *led) +{ + struct gpio_trig_data *gpio_data = led_get_trigger_data(led); + + free_irq(gpiod_to_irq(gpio_data->gpiod), led); + gpiod_put(gpio_data->gpiod); + kfree(gpio_data); +} + +static struct led_trigger gpio_led_trigger = { + .name = "gpio", + .activate = gpio_trig_activate, + .deactivate = gpio_trig_deactivate, + .groups = gpio_trig_groups, +}; +module_led_trigger(gpio_led_trigger); + +MODULE_AUTHOR("Felipe Balbi <me@felipebalbi.com>"); +MODULE_DESCRIPTION("GPIO LED trigger"); +MODULE_LICENSE("GPL v2"); |
