summaryrefslogtreecommitdiffstats
path: root/drivers/leds/trigger/ledtrig-gpio.c
diff options
context:
space:
mode:
authorSteven Rostedt <rostedt@goodmis.org>2026-09-02 09:55:01 -0400
committerSteven Rostedt <rostedt@goodmis.org>2026-09-02 11:08:21 -0400
commit4617721c502b2ddaa4e324e86da4997edf738fa5 (patch)
tree350aa83b47da0b574a814795392b20b0bed83cbc /drivers/leds/trigger/ledtrig-gpio.c
downloadlinux-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.c142
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");