]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
gpio: sysfs: don't look up exported lines as class devices
authorBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Fri, 4 Jul 2025 12:58:54 +0000 (14:58 +0200)
committerBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Wed, 16 Jul 2025 08:27:08 +0000 (10:27 +0200)
In preparation for adding a parallel, per-chip attribute group for
exported GPIO lines, stop using class device APIs to refer to it in the
code. When unregistering the chip, don't call class_find_device() but
instead store exported lines in a linked list inside the GPIO chip data
object and look it up there.

Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-7-9289d8758243@linaro.org
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@linaro.org>
drivers/gpio/gpiolib-sysfs.c

index e10d720ee0adb3b0f6e91eccbf64c33e5700c616..ccc293a4cc5d51294703959317061af55fb0dab0 100644 (file)
@@ -42,7 +42,10 @@ enum {
 };
 
 struct gpiod_data {
+       struct list_head list;
+
        struct gpio_desc *desc;
+       struct device *dev;
 
        struct mutex mutex;
        struct kernfs_node *value_kn;
@@ -62,6 +65,7 @@ struct gpiod_data {
 };
 
 struct gpiodev_data {
+       struct list_head exported_lines;
        struct gpio_device *gdev;
        struct device *cdev_id; /* Class device by GPIO device ID */
        struct device *cdev_base; /* Class device by GPIO base */
@@ -687,10 +691,10 @@ static void gpiod_attr_init(struct device_attribute *dev_attr, const char *name,
  */
 int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
 {
+       struct gpiodev_data *gdev_data;
        struct gpiod_data *desc_data;
        struct gpio_device *gdev;
        struct attribute **attrs;
-       struct device *dev;
        int status;
 
        /* can't export until sysfs is available ... */
@@ -751,25 +755,40 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
        desc_data->class_attr_group.attrs = desc_data->class_attrs;
        desc_data->class_attr_groups[0] = &desc_data->class_attr_group;
 
-       dev = device_create_with_groups(&gpio_class, &gdev->dev,
-                                       MKDEV(0, 0), desc_data,
-                                       desc_data->class_attr_groups,
-                                       "gpio%u", desc_to_gpio(desc));
-       if (IS_ERR(dev)) {
-               status = PTR_ERR(dev);
+       /*
+        * Note: we need to continue passing desc_data here as there's still
+        * at least one known user of gpiod_export_link() in the tree. This
+        * function still uses class_find_device() internally.
+        */
+       desc_data->dev = device_create_with_groups(&gpio_class, &gdev->dev,
+                                                  MKDEV(0, 0), desc_data,
+                                                  desc_data->class_attr_groups,
+                                                  "gpio%u",
+                                                  desc_to_gpio(desc));
+       if (IS_ERR(desc_data->dev)) {
+               status = PTR_ERR(desc_data->dev);
                goto err_free_data;
        }
 
-       desc_data->value_kn = sysfs_get_dirent(dev->kobj.sd, "value");
+       desc_data->value_kn = sysfs_get_dirent(desc_data->dev->kobj.sd,
+                                                      "value");
        if (!desc_data->value_kn) {
                status = -ENODEV;
                goto err_unregister_device;
        }
 
+       gdev_data = gdev_get_data(gdev);
+       if (!gdev_data) {
+               status = -ENODEV;
+               goto err_unregister_device;
+       }
+
+       list_add(&desc_data->list, &gdev_data->exported_lines);
+
        return 0;
 
 err_unregister_device:
-       device_unregister(dev);
+       device_unregister(desc_data->dev);
 err_free_data:
        kfree(desc_data);
 err_clear_bit:
@@ -828,8 +847,9 @@ EXPORT_SYMBOL_GPL(gpiod_export_link);
  */
 void gpiod_unexport(struct gpio_desc *desc)
 {
-       struct gpiod_data *desc_data;
-       struct device *dev;
+       struct gpiod_data *desc_data = NULL;
+       struct gpiodev_data *gdev_data;
+       struct gpio_device *gdev;
 
        if (!desc) {
                pr_warn("%s: invalid GPIO\n", __func__);
@@ -840,14 +860,22 @@ void gpiod_unexport(struct gpio_desc *desc)
                if (!test_bit(FLAG_EXPORT, &desc->flags))
                        return;
 
-               dev = class_find_device(&gpio_class, NULL, desc, match_export);
-               if (!dev)
+               gdev = gpiod_to_gpio_device(desc);
+               gdev_data = gdev_get_data(gdev);
+               if (!gdev_data)
+                       return;
+
+               list_for_each_entry(desc_data, &gdev_data->exported_lines, list)
+                       if (gpiod_is_equal(desc, desc_data->desc))
+                               break;
+
+               if (!desc_data)
                        return;
 
-               desc_data = dev_get_drvdata(dev);
+               list_del(&desc_data->list);
                clear_bit(FLAG_EXPORT, &desc->flags);
                sysfs_put(desc_data->value_kn);
-               device_unregister(dev);
+               device_unregister(desc_data->dev);
 
                /*
                 * Release irq after deregistration to prevent race with
@@ -857,7 +885,6 @@ void gpiod_unexport(struct gpio_desc *desc)
                        gpio_sysfs_free_irq(desc_data);
        }
 
-       put_device(dev);
        mutex_destroy(&desc_data->mutex);
        kfree(desc_data);
 }
@@ -899,6 +926,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev)
                return -ENOMEM;
 
        data->gdev = gdev;
+       INIT_LIST_HEAD(&data->exported_lines);
 
        guard(mutex)(&sysfs_lock);