From 1cd53df733c21ae0d344a2dec941a3e2a06fefd9 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 4 Jul 2025 14:58:54 +0200 Subject: [PATCH] gpio: sysfs: don't look up exported lines as class devices 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 Link: https://lore.kernel.org/r/20250704-gpio-sysfs-chip-export-v4-7-9289d8758243@linaro.org Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 60 ++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index e10d720ee0adb..ccc293a4cc5d5 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -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); -- 2.47.2