From f4028860a998f8a64c4e8c162a8091f7214502cf Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 4 Jan 2024 19:20:34 +0100 Subject: gpio: legacy: mark old interfaces as deprecated in kernel docs We've recently had someone try to use of_get_named_gpio() in new code. Mark legacy interfaces as deprecated in kernel docs to avoid any confusion. Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-legacy.c | 12 ++++++++++++ drivers/gpio/gpiolib-of.c | 2 ++ 2 files changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-legacy.c b/drivers/gpio/gpiolib-legacy.c index 97f4b498e343..b138682fec3d 100644 --- a/drivers/gpio/gpiolib-legacy.c +++ b/drivers/gpio/gpiolib-legacy.c @@ -6,6 +6,9 @@ #include "gpiolib.h" +/* + * **DEPRECATED** This function is deprecated and must not be used in new code. + */ void gpio_free(unsigned gpio) { gpiod_free(gpio_to_desc(gpio)); @@ -17,6 +20,8 @@ EXPORT_SYMBOL_GPL(gpio_free); * @gpio: the GPIO number * @flags: GPIO configuration as specified by GPIOF_* * @label: a literal description string of this GPIO + * + * **DEPRECATED** This function is deprecated and must not be used in new code. */ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) { @@ -53,6 +58,9 @@ int gpio_request_one(unsigned gpio, unsigned long flags, const char *label) } EXPORT_SYMBOL_GPL(gpio_request_one); +/* + * **DEPRECATED** This function is deprecated and must not be used in new code. + */ int gpio_request(unsigned gpio, const char *label) { struct gpio_desc *desc = gpio_to_desc(gpio); @@ -69,6 +77,8 @@ EXPORT_SYMBOL_GPL(gpio_request); * gpio_request_array - request multiple GPIOs in a single call * @array: array of the 'struct gpio' * @num: how many GPIOs in the array + * + * **DEPRECATED** This function is deprecated and must not be used in new code. */ int gpio_request_array(const struct gpio *array, size_t num) { @@ -92,6 +102,8 @@ EXPORT_SYMBOL_GPL(gpio_request_array); * gpio_free_array - release multiple GPIOs in a single call * @array: array of the 'struct gpio' * @num: how many GPIOs in the array + * + * **DEPRECATED** This function is deprecated and must not be used in new code. */ void gpio_free_array(const struct gpio *array, size_t num) { diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index e7770eedd146..77509aa19900 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -414,6 +414,8 @@ out: * @propname: Name of property containing gpio specifier(s) * @index: index of the GPIO * + * **DEPRECATED** This function is deprecated and must not be used in new code. + * * Returns GPIO number to use with Linux generic GPIO API, or one of the errno * value on the error condition. */ -- cgit v1.2.3 From 44a0d880b91dcaf3bb62a1ffa7253fe5373cd296 Mon Sep 17 00:00:00 2001 From: Wenhua Lin Date: Tue, 9 Jan 2024 15:38:49 +0800 Subject: gpio: eic-sprd: Optimize the calculation method of eic number The num_eics is a default value, but some SoCs support more than 8. In order to adapt to all projects, the total number of eics is automatically calculated through dts. Signed-off-by: Wenhua Lin Acked-by: Chunyan Zhang Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-eic-sprd.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index be7f2fa5aa7b..6aed3e2732f7 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -108,7 +108,6 @@ static struct sprd_eic *to_sprd_eic(struct notifier_block *nb) struct sprd_eic_variant_data { enum sprd_eic_type type; - u32 num_eics; }; static const char *sprd_eic_label_name[SPRD_EIC_MAX] = { @@ -118,22 +117,18 @@ static const char *sprd_eic_label_name[SPRD_EIC_MAX] = { static const struct sprd_eic_variant_data sc9860_eic_dbnc_data = { .type = SPRD_EIC_DEBOUNCE, - .num_eics = 8, }; static const struct sprd_eic_variant_data sc9860_eic_latch_data = { .type = SPRD_EIC_LATCH, - .num_eics = 8, }; static const struct sprd_eic_variant_data sc9860_eic_async_data = { .type = SPRD_EIC_ASYNC, - .num_eics = 8, }; static const struct sprd_eic_variant_data sc9860_eic_sync_data = { .type = SPRD_EIC_SYNC, - .num_eics = 8, }; static inline void __iomem *sprd_eic_offset_base(struct sprd_eic *sprd_eic, @@ -595,6 +590,7 @@ static int sprd_eic_probe(struct platform_device *pdev) struct gpio_irq_chip *irq; struct sprd_eic *sprd_eic; struct resource *res; + u16 num_banks = 0; int ret, i; pdata = of_device_get_match_data(dev); @@ -628,10 +624,12 @@ static int sprd_eic_probe(struct platform_device *pdev) sprd_eic->base[i] = devm_ioremap_resource(dev, res); if (IS_ERR(sprd_eic->base[i])) return PTR_ERR(sprd_eic->base[i]); + + num_banks++; } sprd_eic->chip.label = sprd_eic_label_name[sprd_eic->type]; - sprd_eic->chip.ngpio = pdata->num_eics; + sprd_eic->chip.ngpio = num_banks * SPRD_EIC_PER_BANK_NR; sprd_eic->chip.base = -1; sprd_eic->chip.parent = dev; sprd_eic->chip.direction_input = sprd_eic_direction_input; -- cgit v1.2.3 From 83a517c77715c4c268e893424ecfd4a407056af6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2024 17:17:25 +0100 Subject: gpio: cdev: remove leftover function pointer typedefs The locking wrappers were replaces with lock guards. These typedefs are no longer needed. Signed-off-by: Bartosz Golaszewski Reviewed-by: Kent Gibson --- drivers/gpio/gpiolib-cdev.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 2a88736629ef..34d6712fa07c 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -61,11 +61,6 @@ static_assert(IS_ALIGNED(sizeof(struct gpio_v2_line_values), 8)); * interface to gpiolib GPIOs via ioctl()s. */ -typedef __poll_t (*poll_fn)(struct file *, struct poll_table_struct *); -typedef long (*ioctl_fn)(struct file *, unsigned int, unsigned long); -typedef ssize_t (*read_fn)(struct file *, char __user *, - size_t count, loff_t *); - /* * GPIO line handle management */ -- cgit v1.2.3 From 88b7049635dc5d0e2a7dfaaf89e70a9654ed6561 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2024 11:06:06 +0100 Subject: gpio: unexport GPIO irq domain functions only used internally There are no external users for the irq domain helpers so unexport them and remove the prototypes from the driver header. Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 93 ++++++++++++++++++++------------------------- include/linux/gpio/driver.h | 12 ------ 2 files changed, 42 insertions(+), 63 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 44c8f5743a24..d50a786f8176 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1254,8 +1254,8 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) gpiochip_free_mask(&gc->irq.valid_mask); } -bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, - unsigned int offset) +static bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, + unsigned int offset) { if (!gpiochip_line_is_valid(gc, offset)) return false; @@ -1264,7 +1264,6 @@ bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, return true; return test_bit(offset, gc->irq.valid_mask); } -EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid); #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY @@ -1439,6 +1438,43 @@ static unsigned int gpiochip_child_offset_to_irq_noop(struct gpio_chip *gc, return offset; } +/** + * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * @reserve: If set, only reserve an interrupt vector instead of assigning one + * + * This function is a wrapper that calls gpiochip_lock_as_irq() and is to be + * used as the activate function for the &struct irq_domain_ops. The host_data + * for the IRQ domain must be the &struct gpio_chip. + */ +static int gpiochip_irq_domain_activate(struct irq_domain *domain, + struct irq_data *data, bool reserve) +{ + struct gpio_chip *gc = domain->host_data; + unsigned int hwirq = irqd_to_hwirq(data); + + return gpiochip_lock_as_irq(gc, hwirq); +} + +/** + * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ + * @domain: The IRQ domain used by this IRQ chip + * @data: Outermost irq_data associated with the IRQ + * + * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to + * be used as the deactivate function for the &struct irq_domain_ops. The + * host_data for the IRQ domain must be the &struct gpio_chip. + */ +static void gpiochip_irq_domain_deactivate(struct irq_domain *domain, + struct irq_data *data) +{ + struct gpio_chip *gc = domain->host_data; + unsigned int hwirq = irqd_to_hwirq(data); + + return gpiochip_unlock_as_irq(gc, hwirq); +} + static void gpiochip_hierarchy_setup_domain_ops(struct irq_domain_ops *ops) { ops->activate = gpiochip_irq_domain_activate; @@ -1556,7 +1592,8 @@ static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc) * gpiochip by assigning the gpiochip as chip data, and using the irqchip * stored inside the gpiochip. */ -int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwirq) +static int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) { struct gpio_chip *gc = d->host_data; int ret = 0; @@ -1593,9 +1630,8 @@ int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hwi return 0; } -EXPORT_SYMBOL_GPL(gpiochip_irq_map); -void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) +static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) { struct gpio_chip *gc = d->host_data; @@ -1604,7 +1640,6 @@ void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) irq_set_chip_and_handler(irq, NULL, NULL); irq_set_chip_data(irq, NULL); } -EXPORT_SYMBOL_GPL(gpiochip_irq_unmap); static const struct irq_domain_ops gpiochip_domain_ops = { .map = gpiochip_irq_map, @@ -1626,50 +1661,6 @@ static struct irq_domain *gpiochip_simple_create_domain(struct gpio_chip *gc) return domain; } -/* - * TODO: move these activate/deactivate in under the hierarchicial - * irqchip implementation as static once SPMI and SSBI (all external - * users) are phased over. - */ -/** - * gpiochip_irq_domain_activate() - Lock a GPIO to be used as an IRQ - * @domain: The IRQ domain used by this IRQ chip - * @data: Outermost irq_data associated with the IRQ - * @reserve: If set, only reserve an interrupt vector instead of assigning one - * - * This function is a wrapper that calls gpiochip_lock_as_irq() and is to be - * used as the activate function for the &struct irq_domain_ops. The host_data - * for the IRQ domain must be the &struct gpio_chip. - */ -int gpiochip_irq_domain_activate(struct irq_domain *domain, - struct irq_data *data, bool reserve) -{ - struct gpio_chip *gc = domain->host_data; - unsigned int hwirq = irqd_to_hwirq(data); - - return gpiochip_lock_as_irq(gc, hwirq); -} -EXPORT_SYMBOL_GPL(gpiochip_irq_domain_activate); - -/** - * gpiochip_irq_domain_deactivate() - Unlock a GPIO used as an IRQ - * @domain: The IRQ domain used by this IRQ chip - * @data: Outermost irq_data associated with the IRQ - * - * This function is a wrapper that will call gpiochip_unlock_as_irq() and is to - * be used as the deactivate function for the &struct irq_domain_ops. The - * host_data for the IRQ domain must be the &struct gpio_chip. - */ -void gpiochip_irq_domain_deactivate(struct irq_domain *domain, - struct irq_data *data) -{ - struct gpio_chip *gc = domain->host_data; - unsigned int hwirq = irqd_to_hwirq(data); - - return gpiochip_unlock_as_irq(gc, hwirq); -} -EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate); - static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset) { struct irq_domain *domain = gc->irq.domain; diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 9a5c6c76e653..363d06c7b637 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -704,18 +704,6 @@ int bgpio_init(struct gpio_chip *gc, struct device *dev, #define BGPIOF_NO_OUTPUT BIT(5) /* only input */ #define BGPIOF_NO_SET_ON_INPUT BIT(6) -int gpiochip_irq_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq); -void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq); - -int gpiochip_irq_domain_activate(struct irq_domain *domain, - struct irq_data *data, bool reserve); -void gpiochip_irq_domain_deactivate(struct irq_domain *domain, - struct irq_data *data); - -bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, - unsigned int offset); - #ifdef CONFIG_GPIOLIB_IRQCHIP int gpiochip_irqchip_add_domain(struct gpio_chip *gc, struct irq_domain *domain); -- cgit v1.2.3 From f57595788244a838deec2d3be375291327cbc035 Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Wed, 24 Jan 2024 21:58:57 +0100 Subject: gpio: vf610: allow disabling the vf610 driver The vf610 gpio driver is enabled by default for all i.MX machines, without any option to disable it in a board-specific config file. Most i.MX chipsets have no hardware for this driver. Change the default to enable GPIO_VF610 for SOC_VF610 and disable it otherwise. Add a text description after the bool type, this makes the driver selectable by make config etc. Fixes: 30a35c07d9e9 ("gpio: vf610: drop the SOC_VF610 dependency for GPIO_VF610") Signed-off-by: Martin Kaiser Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 1301cec94f12..353af1a4d0ac 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -711,7 +711,8 @@ config GPIO_UNIPHIER Say yes here to support UniPhier GPIOs. config GPIO_VF610 - def_bool y + bool "VF610 GPIO support" + default y if SOC_VF610 depends on ARCH_MXC select GPIOLIB_IRQCHIP help -- cgit v1.2.3 From 3eac8bbed22e940ac1645a884f221bef408f675c Mon Sep 17 00:00:00 2001 From: Martin Kaiser Date: Wed, 24 Jan 2024 21:58:58 +0100 Subject: gpio: vf610: enable COMPILE_TEST Enable COMPILE_TEST for the vf610 gpio driver to support test builds on systems without this hardware. Signed-off-by: Martin Kaiser Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 353af1a4d0ac..3081406ff57a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -713,7 +713,7 @@ config GPIO_UNIPHIER config GPIO_VF610 bool "VF610 GPIO support" default y if SOC_VF610 - depends on ARCH_MXC + depends on ARCH_MXC || COMPILE_TEST select GPIOLIB_IRQCHIP help Say yes here to support i.MX or Vybrid vf610 GPIOs. -- cgit v1.2.3 From a875746f603b86134bd1924b7289fc3542fd45e7 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sun, 4 Feb 2024 13:29:42 -0300 Subject: gpio: gpiolib: make gpio_bus_type const Now that the driver core can properly handle constant struct bus_type, move the gpio_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d50a786f8176..24d046268a01 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -63,7 +63,7 @@ static int gpio_bus_match(struct device *dev, struct device_driver *drv) return 1; } -static struct bus_type gpio_bus_type = { +static const struct bus_type gpio_bus_type = { .name = "gpio", .match = gpio_bus_match, }; -- cgit v1.2.3 From aab5c6f200238ac45001bec3d5494fff8438a8dc Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 5 Feb 2024 11:08:11 +0100 Subject: gpio: set device type for GPIO chips It's useful to have the device type information for those sub-devices that are actually GPIO chips registered with GPIOLIB. While at it: use the device type struct to setup the release callback which is the preferred way to use the device API. Signed-off-by: Bartosz Golaszewski Reviewed-by: Greg Kroah-Hartman --- drivers/gpio/gpiolib.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 24d046268a01..3cad49363a72 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -663,6 +663,11 @@ static void gpiodev_release(struct device *dev) kfree(gdev); } +static const struct device_type gpio_dev_type = { + .name = "gpio_chip", + .release = gpiodev_release, +}; + #ifdef CONFIG_GPIO_CDEV #define gcdev_register(gdev, devt) gpiolib_cdev_register((gdev), (devt)) #define gcdev_unregister(gdev) gpiolib_cdev_unregister((gdev)) @@ -680,6 +685,8 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev); int ret; + device_initialize(&gdev->dev); + /* * If fwnode doesn't belong to another device, it's safe to clear its * initialized flag. @@ -691,9 +698,6 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) if (ret) return ret; - /* From this point, the .release() function cleans up gpio_device */ - gdev->dev.release = gpiodev_release; - ret = gpiochip_sysfs_register(gdev); if (ret) goto err_remove_device; @@ -825,6 +829,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); if (!gdev) return -ENOMEM; + + gdev->dev.type = &gpio_dev_type; gdev->dev.bus = &gpio_bus_type; gdev->dev.parent = gc->parent; gdev->chip = gc; @@ -851,7 +857,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_free_ida; - device_initialize(&gdev->dev); if (gc->parent && gc->parent->driver) gdev->owner = gc->parent->driver->owner; else if (gc->owner) -- cgit v1.2.3 From faf6efd2e5e23d3319501132d9671c8606ef21bd Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 8 Feb 2024 21:27:04 +0100 Subject: gpio: constify opaque pointer in gpio_device_find() match function The match function used in gpio_device_find() should not modify the contents of passed opaque pointer, because such modification would not be necessary for actual matching and it could lead to quite unreadable, spaghetti code. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Mika Westerberg Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko [Bartosz: fix coding style in header] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi.c | 2 +- drivers/gpio/gpiolib-of.c | 7 ++++--- drivers/gpio/gpiolib.c | 6 +++--- include/linux/gpio/driver.h | 3 ++- 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index cd3e9657cc36..899cd505073e 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -126,7 +126,7 @@ static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock); static LIST_HEAD(acpi_gpio_deferred_req_irqs_list); static bool acpi_gpio_deferred_req_irqs_done; -static int acpi_gpiochip_find(struct gpio_chip *gc, void *data) +static int acpi_gpiochip_find(struct gpio_chip *gc, const void *data) { return device_match_acpi_handle(&gc->gpiodev->dev, data); } diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 77509aa19900..35d717fd393f 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -118,9 +118,10 @@ int of_gpio_get_count(struct device *dev, const char *con_id) return ret ? ret : -ENOENT; } -static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data) +static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, + const void *data) { - struct of_phandle_args *gpiospec = data; + const struct of_phandle_args *gpiospec = data; return device_match_of_node(&chip->gpiodev->dev, gpiospec->np) && chip->of_xlate && @@ -852,7 +853,7 @@ static void of_gpiochip_remove_hog(struct gpio_chip *chip, gpiochip_free_own_desc(desc); } -static int of_gpiochip_match_node(struct gpio_chip *chip, void *data) +static int of_gpiochip_match_node(struct gpio_chip *chip, const void *data) { return device_match_of_node(&chip->gpiodev->dev, data); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index d068788adb10..288aed5a9f6b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1119,7 +1119,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove); */ struct gpio_device *gpio_device_find(void *data, int (*match)(struct gpio_chip *gc, - void *data)) + const void *data)) { struct gpio_device *gdev; @@ -1141,7 +1141,7 @@ struct gpio_device *gpio_device_find(void *data, } EXPORT_SYMBOL_GPL(gpio_device_find); -static int gpio_chip_match_by_label(struct gpio_chip *gc, void *label) +static int gpio_chip_match_by_label(struct gpio_chip *gc, const void *label) { return gc->label && !strcmp(gc->label, label); } @@ -1161,7 +1161,7 @@ struct gpio_device *gpio_device_find_by_label(const char *label) } EXPORT_SYMBOL_GPL(gpio_device_find_by_label); -static int gpio_chip_match_by_fwnode(struct gpio_chip *gc, void *fwnode) +static int gpio_chip_match_by_fwnode(struct gpio_chip *gc, const void *fwnode) { return device_match_fwnode(&gc->gpiodev->dev, fwnode); } diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 3a37d058cfcf..9d0023f83a57 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -629,7 +629,8 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, struct lock_class_key *request_key); struct gpio_device *gpio_device_find(void *data, - int (*match)(struct gpio_chip *gc, void *data)); + int (*match)(struct gpio_chip *gc, + const void *data)); struct gpio_device *gpio_device_find_by_label(const char *label); struct gpio_device *gpio_device_find_by_fwnode(const struct fwnode_handle *fwnode); -- cgit v1.2.3 From e348544f7994d252427ed3ae637c7081cbb90f66 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 19 Jan 2024 16:43:13 +0100 Subject: gpio: protect the list of GPIO devices with SRCU We're working towards removing the "multi-function" GPIO spinlock that's implemented terribly wrong. We tried using an RW-semaphore to protect the list of GPIO devices but it turned out that we still have old code using legacy GPIO calls that need to translate the global GPIO number to the address of the associated descriptor and - to that end - traverse the list while holding the lock. If we change the spinlock to a sleeping lock then we'll end up with "scheduling while atomic" bugs. Let's allow lockless traversal of the list using SRCU and only use the mutex when modyfing the list. While at it: let's protect the period between when we start the lookup and when we finally request the descriptor (increasing the reference count of the GPIO device) with the SRCU read lock. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 247 +++++++++++++++++++++++++++---------------------- 1 file changed, 135 insertions(+), 112 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 288aed5a9f6b..a5598c3299ff 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -14,12 +15,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -81,7 +84,12 @@ DEFINE_SPINLOCK(gpio_lock); static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); + LIST_HEAD(gpio_devices); +/* Protects the GPIO device list against concurrent modifications. */ +static DEFINE_MUTEX(gpio_devices_lock); +/* Ensures coherence during read-only accesses to the list of GPIO devices. */ +DEFINE_STATIC_SRCU(gpio_devices_srcu); static DEFINE_MUTEX(gpio_machine_hogs_mutex); static LIST_HEAD(gpio_machine_hogs); @@ -113,20 +121,16 @@ static inline void desc_set_label(struct gpio_desc *d, const char *label) struct gpio_desc *gpio_to_desc(unsigned gpio) { struct gpio_device *gdev; - unsigned long flags; - - spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->base <= gpio && - gdev->base + gdev->ngpio > gpio) { - spin_unlock_irqrestore(&gpio_lock, flags); - return &gdev->descs[gpio - gdev->base]; + scoped_guard(srcu, &gpio_devices_srcu) { + list_for_each_entry_srcu(gdev, &gpio_devices, list, + srcu_read_lock_held(&gpio_devices_srcu)) { + if (gdev->base <= gpio && + gdev->base + gdev->ngpio > gpio) + return &gdev->descs[gpio - gdev->base]; } } - spin_unlock_irqrestore(&gpio_lock, flags); - if (!gpio_is_valid(gpio)) pr_warn("invalid GPIO %d\n", gpio); @@ -282,7 +286,8 @@ static int gpiochip_find_base_unlocked(int ngpio) struct gpio_device *gdev; int base = GPIO_DYNAMIC_BASE; - list_for_each_entry(gdev, &gpio_devices, list) { + list_for_each_entry_srcu(gdev, &gpio_devices, list, + lockdep_is_held(&gpio_devices_lock)) { /* found a free space? */ if (gdev->base >= base + ngpio) break; @@ -354,23 +359,25 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev) { struct gpio_device *prev, *next; + lockdep_assert_held(&gpio_devices_lock); + if (list_empty(&gpio_devices)) { /* initial entry in list */ - list_add_tail(&gdev->list, &gpio_devices); + list_add_tail_rcu(&gdev->list, &gpio_devices); return 0; } next = list_first_entry(&gpio_devices, struct gpio_device, list); if (gdev->base + gdev->ngpio <= next->base) { /* add before first entry */ - list_add(&gdev->list, &gpio_devices); + list_add_rcu(&gdev->list, &gpio_devices); return 0; } prev = list_last_entry(&gpio_devices, struct gpio_device, list); if (prev->base + prev->ngpio <= gdev->base) { /* add behind last entry */ - list_add_tail(&gdev->list, &gpio_devices); + list_add_tail_rcu(&gdev->list, &gpio_devices); return 0; } @@ -382,11 +389,13 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev) /* add between prev and next */ if (prev->base + prev->ngpio <= gdev->base && gdev->base + gdev->ngpio <= next->base) { - list_add(&gdev->list, &prev->list); + list_add_rcu(&gdev->list, &prev->list); return 0; } } + synchronize_srcu(&gpio_devices_srcu); + return -EBUSY; } @@ -399,26 +408,21 @@ static int gpiodev_add_to_list_unlocked(struct gpio_device *gdev) static struct gpio_desc *gpio_name_to_desc(const char * const name) { struct gpio_device *gdev; - unsigned long flags; + struct gpio_desc *desc; if (!name) return NULL; - spin_lock_irqsave(&gpio_lock, flags); - - list_for_each_entry(gdev, &gpio_devices, list) { - struct gpio_desc *desc; + guard(srcu)(&gpio_devices_srcu); + list_for_each_entry_srcu(gdev, &gpio_devices, list, + srcu_read_lock_held(&gpio_devices_srcu)) { for_each_gpio_desc(gdev->chip, desc) { - if (desc->name && !strcmp(desc->name, name)) { - spin_unlock_irqrestore(&gpio_lock, flags); + if (desc->name && !strcmp(desc->name, name)) return desc; - } } } - spin_unlock_irqrestore(&gpio_lock, flags); - return NULL; } @@ -752,7 +756,10 @@ static void gpiochip_setup_devs(void) struct gpio_device *gdev; int ret; - list_for_each_entry(gdev, &gpio_devices, list) { + guard(srcu)(&gpio_devices_srcu); + + list_for_each_entry_srcu(gdev, &gpio_devices, list, + srcu_read_lock_held(&gpio_devices_srcu)) { ret = gpiochip_setup_dev(gdev); if (ret) dev_err(&gdev->dev, @@ -817,7 +824,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *request_key) { struct gpio_device *gdev; - unsigned long flags; unsigned int i; int base = 0; int ret = 0; @@ -883,49 +889,47 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev->ngpio = gc->ngpio; - spin_lock_irqsave(&gpio_lock, flags); - - /* - * TODO: this allocates a Linux GPIO number base in the global - * GPIO numberspace for this chip. In the long run we want to - * get *rid* of this numberspace and use only descriptors, but - * it may be a pipe dream. It will not happen before we get rid - * of the sysfs interface anyways. - */ - base = gc->base; - if (base < 0) { - base = gpiochip_find_base_unlocked(gc->ngpio); - if (base < 0) { - spin_unlock_irqrestore(&gpio_lock, flags); - ret = base; - base = 0; - goto err_free_label; - } + scoped_guard(mutex, &gpio_devices_lock) { /* - * TODO: it should not be necessary to reflect the assigned - * base outside of the GPIO subsystem. Go over drivers and - * see if anyone makes use of this, else drop this and assign - * a poison instead. + * TODO: this allocates a Linux GPIO number base in the global + * GPIO numberspace for this chip. In the long run we want to + * get *rid* of this numberspace and use only descriptors, but + * it may be a pipe dream. It will not happen before we get rid + * of the sysfs interface anyways. */ - gc->base = base; - } else { - dev_warn(&gdev->dev, - "Static allocation of GPIO base is deprecated, use dynamic allocation.\n"); - } - gdev->base = base; + base = gc->base; + if (base < 0) { + base = gpiochip_find_base_unlocked(gc->ngpio); + if (base < 0) { + ret = base; + base = 0; + goto err_free_label; + } - ret = gpiodev_add_to_list_unlocked(gdev); - if (ret) { - spin_unlock_irqrestore(&gpio_lock, flags); - chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); - goto err_free_label; + /* + * TODO: it should not be necessary to reflect the + * assigned base outside of the GPIO subsystem. Go over + * drivers and see if anyone makes use of this, else + * drop this and assign a poison instead. + */ + gc->base = base; + } else { + dev_warn(&gdev->dev, + "Static allocation of GPIO base is deprecated, use dynamic allocation.\n"); + } + + gdev->base = base; + + ret = gpiodev_add_to_list_unlocked(gdev); + if (ret) { + chip_err(gc, "GPIO integer space overlap, cannot add chip\n"); + goto err_free_label; + } } for (i = 0; i < gc->ngpio; i++) gdev->descs[i].gdev = gdev; - spin_unlock_irqrestore(&gpio_lock, flags); - BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); init_rwsem(&gdev->sem); @@ -1011,9 +1015,9 @@ err_free_gpiochip_mask: gpiochip_remove_pin_ranges(gc); gpiochip_free_valid_mask(gc); err_remove_from_list: - spin_lock_irqsave(&gpio_lock, flags); - list_del(&gdev->list); - spin_unlock_irqrestore(&gpio_lock, flags); + scoped_guard(mutex, &gpio_devices_lock) + list_del_rcu(&gdev->list); + synchronize_srcu(&gpio_devices_srcu); if (gdev->dev.release) { /* release() has been registered by gpiochip_setup_dev() */ gpio_device_put(gdev); @@ -1057,6 +1061,11 @@ void gpiochip_remove(struct gpio_chip *gc) /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ gpiochip_sysfs_unregister(gdev); gpiochip_free_hogs(gc); + + scoped_guard(mutex, &gpio_devices_lock) + list_del_rcu(&gdev->list); + synchronize_srcu(&gpio_devices_srcu); + /* Numb the device, cancelling all outstanding operations */ gdev->chip = NULL; gpiochip_irqchip_remove(gc); @@ -1081,9 +1090,6 @@ void gpiochip_remove(struct gpio_chip *gc) dev_crit(&gdev->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); - scoped_guard(spinlock_irqsave, &gpio_lock) - list_del(&gdev->list); - /* * The gpiochip side puts its use of the device to rest here: * if there are no userspace clients, the chardev and device will @@ -1130,7 +1136,7 @@ struct gpio_device *gpio_device_find(void *data, */ might_sleep(); - guard(spinlock_irqsave)(&gpio_lock); + guard(srcu)(&gpio_devices_srcu); list_for_each_entry(gdev, &gpio_devices, list) { if (gdev->chip && match(gdev->chip, data)) @@ -4138,30 +4144,39 @@ static struct gpio_desc *gpiod_find_and_request(struct device *consumer, bool platform_lookup_allowed) { unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT; - struct gpio_desc *desc; - int ret; + /* + * scoped_guard() is implemented as a for loop, meaning static + * analyzers will complain about these two not being initialized. + */ + struct gpio_desc *desc = NULL; + int ret = 0; + + scoped_guard(srcu, &gpio_devices_srcu) { + desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx, + &flags, &lookupflags); + if (gpiod_not_found(desc) && platform_lookup_allowed) { + /* + * Either we are not using DT or ACPI, or their lookup + * did not return a result. In that case, use platform + * lookup as a fallback. + */ + dev_dbg(consumer, + "using lookup tables for GPIO lookup\n"); + desc = gpiod_find(consumer, con_id, idx, &lookupflags); + } + + if (IS_ERR(desc)) { + dev_dbg(consumer, "No GPIO consumer %s found\n", + con_id); + return desc; + } - desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx, &flags, &lookupflags); - if (gpiod_not_found(desc) && platform_lookup_allowed) { /* - * Either we are not using DT or ACPI, or their lookup did not - * return a result. In that case, use platform lookup as a - * fallback. + * If a connection label was passed use that, else attempt to use + * the device name as label */ - dev_dbg(consumer, "using lookup tables for GPIO lookup\n"); - desc = gpiod_find(consumer, con_id, idx, &lookupflags); - } - - if (IS_ERR(desc)) { - dev_dbg(consumer, "No GPIO consumer %s found\n", con_id); - return desc; + ret = gpiod_request(desc, label); } - - /* - * If a connection label was passed use that, else attempt to use - * the device name as label - */ - ret = gpiod_request(desc, label); if (ret) { if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE)) return ERR_PTR(ret); @@ -4730,61 +4745,69 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) } } +struct gpiolib_seq_priv { + bool newline; + int idx; +}; + static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) { - unsigned long flags; - struct gpio_device *gdev = NULL; + struct gpiolib_seq_priv *priv; + struct gpio_device *gdev; loff_t index = *pos; - s->private = ""; + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return NULL; + + s->private = priv; + priv->idx = srcu_read_lock(&gpio_devices_srcu); - spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(gdev, &gpio_devices, list) - if (index-- == 0) { - spin_unlock_irqrestore(&gpio_lock, flags); + list_for_each_entry_srcu(gdev, &gpio_devices, list, + srcu_read_lock_held(&gpio_devices_srcu)) { + if (index-- == 0) return gdev; - } - spin_unlock_irqrestore(&gpio_lock, flags); + } return NULL; } static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos) { - unsigned long flags; - struct gpio_device *gdev = v; - void *ret = NULL; + struct gpiolib_seq_priv *priv = s->private; + struct gpio_device *gdev = v, *next; - spin_lock_irqsave(&gpio_lock, flags); - if (list_is_last(&gdev->list, &gpio_devices)) - ret = NULL; - else - ret = list_first_entry(&gdev->list, struct gpio_device, list); - spin_unlock_irqrestore(&gpio_lock, flags); - - s->private = "\n"; + next = list_entry_rcu(gdev->list.next, struct gpio_device, list); + gdev = &next->list == &gpio_devices ? NULL : next; + priv->newline = true; ++*pos; - return ret; + return gdev; } static void gpiolib_seq_stop(struct seq_file *s, void *v) { + struct gpiolib_seq_priv *priv = s->private; + + srcu_read_unlock(&gpio_devices_srcu, priv->idx); + kfree(priv); } static int gpiolib_seq_show(struct seq_file *s, void *v) { + struct gpiolib_seq_priv *priv = s->private; struct gpio_device *gdev = v; struct gpio_chip *gc = gdev->chip; struct device *parent; if (!gc) { - seq_printf(s, "%s%s: (dangling chip)", (char *)s->private, + seq_printf(s, "%s%s: (dangling chip)", + priv->newline ? "\n" : "", dev_name(&gdev->dev)); return 0; } - seq_printf(s, "%s%s: GPIOs %d-%d", (char *)s->private, + seq_printf(s, "%s%s: GPIOs %d-%d", priv->newline ? "\n" : "", dev_name(&gdev->dev), gdev->base, gdev->base + gdev->ngpio - 1); parent = gc->parent; -- cgit v1.2.3 From 8ce6fd81a452d554889874e50cd34e92cf4547d6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 5 Jan 2024 13:12:45 +0100 Subject: gpio: of: assign and read the hog pointer atomically The device nodes representing GPIO hogs cannot be deleted without unregistering the GPIO chip so there's no need to serialize their access. However we must ensure that users can get the right address so write and read it atomically. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-of.c | 4 ++-- drivers/gpio/gpiolib.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 35d717fd393f..523b047a2803 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -801,7 +801,7 @@ static int of_gpiochip_add_hog(struct gpio_chip *chip, struct device_node *hog) return ret; #ifdef CONFIG_OF_DYNAMIC - desc->hog = hog; + WRITE_ONCE(desc->hog, hog); #endif } @@ -849,7 +849,7 @@ static void of_gpiochip_remove_hog(struct gpio_chip *chip, struct gpio_desc *desc; for_each_gpio_desc_with_flag(chip, desc, FLAG_IS_HOGGED) - if (desc->hog == hog) + if (READ_ONCE(desc->hog) == hog) gpiochip_free_own_desc(desc); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a5598c3299ff..03d7c3f92f5e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2330,7 +2330,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc) clear_bit(FLAG_EDGE_FALLING, &desc->flags); clear_bit(FLAG_IS_HOGGED, &desc->flags); #ifdef CONFIG_OF_DYNAMIC - desc->hog = NULL; + WRITE_ONCE(desc->hog, NULL); #endif ret = true; } -- cgit v1.2.3 From 0857c39bfd09183792c680858b4416498ee1d1ca Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 5 Jan 2024 19:51:19 +0100 Subject: gpio: remove unused logging helpers The general rule of the kernel is to not provide symbols that have no users upstream. Let's remove logging helpers that are not used anywhere. This will save us work later when we'll be modifying them to use the upcoming SRCU infrastructure. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.h | 13 ------------- 1 file changed, 13 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index a4a2520b5f31..c3ae5bfa3f2e 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -219,31 +219,18 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc) /* With descriptor prefix */ -#define gpiod_emerg(desc, fmt, ...) \ - pr_emerg("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\ - ##__VA_ARGS__) -#define gpiod_crit(desc, fmt, ...) \ - pr_crit("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \ - ##__VA_ARGS__) #define gpiod_err(desc, fmt, ...) \ pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \ ##__VA_ARGS__) #define gpiod_warn(desc, fmt, ...) \ pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \ ##__VA_ARGS__) -#define gpiod_info(desc, fmt, ...) \ - pr_info("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \ - ##__VA_ARGS__) #define gpiod_dbg(desc, fmt, ...) \ pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\ ##__VA_ARGS__) /* With chip prefix */ -#define chip_emerg(gc, fmt, ...) \ - dev_emerg(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) -#define chip_crit(gc, fmt, ...) \ - dev_crit(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) #define chip_err(gc, fmt, ...) \ dev_err(&gc->gpiodev->dev, "(%s): " fmt, gc->label, ##__VA_ARGS__) #define chip_warn(gc, fmt, ...) \ -- cgit v1.2.3 From d23dc4a9a88f16d0327e7b4bb03e6f43c1a1b166 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 29 Jan 2024 10:11:41 +0100 Subject: gpio: provide and use gpiod_get_label() We will soon serialize access to the descriptor label using SRCU. The write-side of the protection will require calling synchronize_srcu() which must not be called from atomic context. We have two irq helpers: gpiochip_lock_as_irq() and gpiochip_unlock_as_irq() that set the label if the GPIO is not requested but is being used as interrupt. They are called with a spinlock held from the interrupt subsystem. They must not do it if we are to use SRCU so instead let's move the special corner case to a dedicated getter. First: let's implement and use the getter where it's applicable. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-cdev.c | 4 ++-- drivers/gpio/gpiolib.c | 9 +++++++-- drivers/gpio/gpiolib.h | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 34d6712fa07c..2c0a0700762d 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2305,8 +2305,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, if (desc->name) strscpy(info->name, desc->name, sizeof(info->name)); - if (desc->label) - strscpy(info->consumer, desc->label, + if (gpiod_get_label(desc)) + strscpy(info->consumer, gpiod_get_label(desc), sizeof(info->consumer)); dflags = READ_ONCE(desc->flags); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 03d7c3f92f5e..c014404c3bed 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -105,6 +105,11 @@ static void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc); static bool gpiolib_initialized; +const char *gpiod_get_label(struct gpio_desc *desc) +{ + return desc->label; +} + static inline void desc_set_label(struct gpio_desc *d, const char *label) { d->label = label; @@ -2391,7 +2396,7 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) * * Until this happens, this allocation needs to be atomic. */ - label = kstrdup(desc->label, GFP_ATOMIC); + label = kstrdup(gpiod_get_label(desc), GFP_ATOMIC); if (!label) return ERR_PTR(-ENOMEM); @@ -4732,7 +4737,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) is_irq = test_bit(FLAG_USED_AS_IRQ, &desc->flags); active_low = test_bit(FLAG_ACTIVE_LOW, &desc->flags); seq_printf(s, " gpio-%-3d (%-20.20s|%-20.20s) %s %s %s%s\n", - gpio, desc->name ?: "", desc->label, + gpio, desc->name ?: "", gpiod_get_label(desc), is_out ? "out" : "in ", value >= 0 ? (value ? "hi" : "lo") : "? ", is_irq ? "IRQ " : "", diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index c3ae5bfa3f2e..1058f326fe2b 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -208,6 +208,7 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce); int gpiod_hog(struct gpio_desc *desc, const char *name, unsigned long lflags, enum gpiod_flags dflags); int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev); +const char *gpiod_get_label(struct gpio_desc *desc); /* * Return the GPIO number of the passed descriptor relative to its chip -- cgit v1.2.3 From ccfb6ff4f6c0574e01fb16934fb60a46285c5f3f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 29 Jan 2024 10:23:27 +0100 Subject: gpio: don't set label from irq helpers We will soon serialize access to the descriptor label using SRCU. The write-side of the protection will require calling synchronize_srcu() which must not be called from atomic context. We have two irq helpers: gpiochip_lock_as_irq() and gpiochip_unlock_as_irq() that set the label if the GPIO is not requested but is being used as interrupt. They are called with a spinlock held from the interrupt subsystem. They must not do it if we are to use SRCU so instead let's move the special corner case to a dedicated getter. Don't actually set the label to "interrupt" in the above case but rather use the newly added gpiod_get_label() helper to hide the logic that atomically checks the descriptor flags and returns the address of a static "interrupt" string. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c014404c3bed..79717776be84 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -107,7 +107,14 @@ static bool gpiolib_initialized; const char *gpiod_get_label(struct gpio_desc *desc) { - return desc->label; + unsigned long flags; + + flags = READ_ONCE(desc->flags); + if (test_bit(FLAG_USED_AS_IRQ, &flags) && + !test_bit(FLAG_REQUESTED, &flags)) + return "interrupt"; + + return test_bit(FLAG_REQUESTED, &flags) ? desc->label : NULL; } static inline void desc_set_label(struct gpio_desc *d, const char *label) @@ -3599,14 +3606,6 @@ int gpiochip_lock_as_irq(struct gpio_chip *gc, unsigned int offset) set_bit(FLAG_USED_AS_IRQ, &desc->flags); set_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); - /* - * If the consumer has not set up a label (such as when the - * IRQ is referenced from .to_irq()) we set up a label here - * so it is clear this is used as an interrupt. - */ - if (!desc->label) - desc_set_label(desc, "interrupt"); - return 0; } EXPORT_SYMBOL_GPL(gpiochip_lock_as_irq); @@ -3629,10 +3628,6 @@ void gpiochip_unlock_as_irq(struct gpio_chip *gc, unsigned int offset) clear_bit(FLAG_USED_AS_IRQ, &desc->flags); clear_bit(FLAG_IRQ_IS_ENABLED, &desc->flags); - - /* If we only had this marking, erase it */ - if (desc->label && !strcmp(desc->label, "interrupt")) - desc_set_label(desc, NULL); } EXPORT_SYMBOL_GPL(gpiochip_unlock_as_irq); -- cgit v1.2.3 From be711caa87c5c81d5dc00b244cac3a0b775adb18 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 5 Jan 2024 15:46:27 +0100 Subject: gpio: add SRCU infrastructure to struct gpio_desc Extend the GPIO descriptor with an SRCU structure in order to serialize the access to the label. Initialize and clean it up where applicable. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 18 ++++++++++++++++-- drivers/gpio/gpiolib.h | 3 +++ 2 files changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 79717776be84..78b9e9dc268a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -672,6 +672,10 @@ EXPORT_SYMBOL_GPL(gpiochip_line_is_valid); static void gpiodev_release(struct device *dev) { struct gpio_device *gdev = to_gpio_device(dev); + unsigned int i; + + for (i = 0; i < gdev->ngpio; i++) + cleanup_srcu_struct(&gdev->descs[i].srcu); ida_free(&gpio_ida, gdev->id); kfree_const(gdev->label); @@ -836,7 +840,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *request_key) { struct gpio_device *gdev; - unsigned int i; + unsigned int i, j; int base = 0; int ret = 0; @@ -970,6 +974,13 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, for (i = 0; i < gc->ngpio; i++) { struct gpio_desc *desc = &gdev->descs[i]; + ret = init_srcu_struct(&desc->srcu); + if (ret) { + for (j = 0; j < i; j++) + cleanup_srcu_struct(&gdev->descs[j].srcu); + goto err_remove_of_chip; + } + if (gc->get_direction && gpiochip_line_is_valid(gc, i)) { assign_bit(FLAG_IS_OUT, &desc->flags, !gc->get_direction(gc, i)); @@ -981,7 +992,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, ret = gpiochip_add_pin_ranges(gc); if (ret) - goto err_remove_of_chip; + goto err_cleanup_desc_srcu; acpi_gpiochip_add(gc); @@ -1020,6 +1031,9 @@ err_remove_irqchip_mask: gpiochip_irqchip_free_valid_mask(gc); err_remove_acpi_chip: acpi_gpiochip_remove(gc); +err_cleanup_desc_srcu: + for (i = 0; i < gdev->ngpio; i++) + cleanup_srcu_struct(&gdev->descs[i].srcu); err_remove_of_chip: gpiochip_free_hogs(gc); of_gpiochip_remove(gc); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 1058f326fe2b..6e14b629c48b 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -17,6 +17,7 @@ #include #include #include +#include #define GPIOCHIP_NAME "gpiochip" @@ -147,6 +148,7 @@ void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); * @label: Name of the consumer * @name: Line name * @hog: Pointer to the device node that hogs this line (if any) + * @srcu: SRCU struct protecting the label pointer. * * These are obtained using gpiod_get() and are preferable to the old * integer-based handles. @@ -184,6 +186,7 @@ struct gpio_desc { #ifdef CONFIG_OF_DYNAMIC struct device_node *hog; #endif + struct srcu_struct srcu; }; #define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) -- cgit v1.2.3 From 1f2bcb8c8ccdf9dc2e46f7986e1e22408506a6d6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 5 Jan 2024 17:08:36 +0100 Subject: gpio: protect the descriptor label with SRCU In order to ensure that the label is not freed while it's being accessed, let's protect it with SRCU and synchronize it everytime it's changed. Let's modify desc_set_label() to manage the memory used for the label as it can only be freed once synchronize_srcu() returns. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-cdev.c | 10 +++++++--- drivers/gpio/gpiolib.c | 47 +++++++++++++++++++++++++++++---------------- drivers/gpio/gpiolib.h | 34 ++++++++++++++++++++++---------- 3 files changed, 61 insertions(+), 30 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 2c0a0700762d..75f4912339a6 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2297,6 +2297,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, { struct gpio_chip *gc = desc->gdev->chip; unsigned long dflags; + const char *label; memset(info, 0, sizeof(*info)); info->offset = gpio_chip_hwgpio(desc); @@ -2305,9 +2306,12 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, if (desc->name) strscpy(info->name, desc->name, sizeof(info->name)); - if (gpiod_get_label(desc)) - strscpy(info->consumer, gpiod_get_label(desc), - sizeof(info->consumer)); + scoped_guard(srcu, &desc->srcu) { + label = gpiod_get_label(desc); + if (label) + strscpy(info->consumer, label, + sizeof(info->consumer)); + } dflags = READ_ONCE(desc->flags); } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 78b9e9dc268a..c72f3ffbe6b4 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -114,12 +114,26 @@ const char *gpiod_get_label(struct gpio_desc *desc) !test_bit(FLAG_REQUESTED, &flags)) return "interrupt"; - return test_bit(FLAG_REQUESTED, &flags) ? desc->label : NULL; + return test_bit(FLAG_REQUESTED, &flags) ? + rcu_dereference(desc->label) : NULL; } -static inline void desc_set_label(struct gpio_desc *d, const char *label) +static int desc_set_label(struct gpio_desc *desc, const char *label) { - d->label = label; + const char *new = NULL, *old; + + if (label) { + /* FIXME: make this GFP_KERNEL once the spinlock is out. */ + new = kstrdup_const(label, GFP_ATOMIC); + if (!new) + return -ENOMEM; + } + + old = rcu_replace_pointer(desc->label, new, 1); + synchronize_srcu(&desc->srcu); + kfree_const(old); + + return 0; } /** @@ -2229,9 +2243,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) * before IRQs are enabled, for non-sleeping (SOC) GPIOs. */ - if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { - desc_set_label(desc, label ? : "?"); - } else { + if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)) { ret = -EBUSY; goto out_free_unlock; } @@ -2259,6 +2271,13 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) spin_lock_irqsave(&gpio_lock, flags); } spin_unlock_irqrestore(&gpio_lock, flags); + + ret = desc_set_label(desc, label ? : "?"); + if (ret) { + clear_bit(FLAG_REQUESTED, &desc->flags); + return ret; + } + return 0; out_free_unlock: @@ -2343,8 +2362,6 @@ static bool gpiod_free_commit(struct gpio_desc *desc) gc->free(gc, gpio_chip_hwgpio(desc)); spin_lock_irqsave(&gpio_lock, flags); } - kfree_const(desc->label); - desc_set_label(desc, NULL); clear_bit(FLAG_ACTIVE_LOW, &desc->flags); clear_bit(FLAG_REQUESTED, &desc->flags); clear_bit(FLAG_OPEN_DRAIN, &desc->flags); @@ -2362,6 +2379,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc) } spin_unlock_irqrestore(&gpio_lock, flags); + desc_set_label(desc, NULL); gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED); return ret; @@ -2409,6 +2427,8 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) if (!test_bit(FLAG_REQUESTED, &desc->flags)) return NULL; + guard(srcu)(&desc->srcu); + /* * FIXME: Once we mark gpiod_direction_input/output() and * gpiod_get_direction() with might_sleep(), we'll be able to protect @@ -3520,16 +3540,8 @@ EXPORT_SYMBOL_GPL(gpiod_cansleep); int gpiod_set_consumer_name(struct gpio_desc *desc, const char *name) { VALIDATE_DESC(desc); - if (name) { - name = kstrdup_const(name, GFP_KERNEL); - if (!name) - return -ENOMEM; - } - kfree_const(desc->label); - desc_set_label(desc, name); - - return 0; + return desc_set_label(desc, name); } EXPORT_SYMBOL_GPL(gpiod_set_consumer_name); @@ -4739,6 +4751,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) int value; for_each_gpio_desc(gc, desc) { + guard(srcu)(&desc->srcu); if (test_bit(FLAG_REQUESTED, &desc->flags)) { gpiod_get_direction(desc); is_out = test_bit(FLAG_IS_OUT, &desc->flags); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 6e14b629c48b..d2e73eea9e92 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -180,7 +180,7 @@ struct gpio_desc { #define FLAG_EVENT_CLOCK_HTE 19 /* GPIO CDEV reports hardware timestamps in events */ /* Connection label */ - const char *label; + const char __rcu *label; /* Name of the GPIO */ const char *name; #ifdef CONFIG_OF_DYNAMIC @@ -223,15 +223,29 @@ static inline int gpio_chip_hwgpio(const struct gpio_desc *desc) /* With descriptor prefix */ -#define gpiod_err(desc, fmt, ...) \ - pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \ - ##__VA_ARGS__) -#define gpiod_warn(desc, fmt, ...) \ - pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?", \ - ##__VA_ARGS__) -#define gpiod_dbg(desc, fmt, ...) \ - pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), desc->label ? : "?",\ - ##__VA_ARGS__) +#define gpiod_err(desc, fmt, ...) \ +do { \ + scoped_guard(srcu, &desc->srcu) { \ + pr_err("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ + gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ + } \ +} while (0) + +#define gpiod_warn(desc, fmt, ...) \ +do { \ + scoped_guard(srcu, &desc->srcu) { \ + pr_warn("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ + gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ + } \ +} while (0) + +#define gpiod_dbg(desc, fmt, ...) \ +do { \ + scoped_guard(srcu, &desc->srcu) { \ + pr_debug("gpio-%d (%s): " fmt, desc_to_gpio(desc), \ + gpiod_get_label(desc) ? : "?", ##__VA_ARGS__); \ + } \ +} while (0) /* With chip prefix */ -- cgit v1.2.3 From 2a9101e875bc3aa6423b559e0ea43b2077f3be87 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2024 16:29:39 +0100 Subject: gpio: sysfs: use gpio_device_find() to iterate over existing devices With the list of GPIO devices now protected with SRCU we can use gpio_device_find() to traverse it from sysfs. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-sysfs.c | 46 ++++++++++++++++++++++---------------------- drivers/gpio/gpiolib.c | 2 +- drivers/gpio/gpiolib.h | 1 - 3 files changed, 24 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 6bf5332136e5..54b77ae1d176 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -790,11 +790,29 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) } } +/* + * We're not really looking for a device - we just want to iterate over the + * list and call this callback for each GPIO device. This is why this function + * always returns 0. + */ +static int gpiofind_sysfs_register(struct gpio_chip *gc, const void *data) +{ + struct gpio_device *gdev = gc->gpiodev; + int ret; + + if (gdev->mockdev) + return 0; + + ret = gpiochip_sysfs_register(gdev); + if (ret) + chip_err(gc, "failed to register the sysfs entry: %d\n", ret); + + return 0; +} + static int __init gpiolib_sysfs_init(void) { - int status; - unsigned long flags; - struct gpio_device *gdev; + int status; status = class_register(&gpio_class); if (status < 0) @@ -806,26 +824,8 @@ static int __init gpiolib_sysfs_init(void) * We run before arch_initcall() so chip->dev nodes can have * registered, and so arch_initcall() can always gpiod_export(). */ - spin_lock_irqsave(&gpio_lock, flags); - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->mockdev) - continue; - - /* - * TODO we yield gpio_lock here because - * gpiochip_sysfs_register() acquires a mutex. This is unsafe - * and needs to be fixed. - * - * Also it would be nice to use gpio_device_find() here so we - * can keep gpio_chips local to gpiolib.c, but the yield of - * gpio_lock prevents us from doing this. - */ - spin_unlock_irqrestore(&gpio_lock, flags); - status = gpiochip_sysfs_register(gdev); - spin_lock_irqsave(&gpio_lock, flags); - } - spin_unlock_irqrestore(&gpio_lock, flags); + (void)gpio_device_find(NULL, gpiofind_sysfs_register); - return status; + return 0; } postcore_initcall(gpiolib_sysfs_init); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c72f3ffbe6b4..f0cbbab17dc3 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -85,7 +85,7 @@ DEFINE_SPINLOCK(gpio_lock); static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); -LIST_HEAD(gpio_devices); +static LIST_HEAD(gpio_devices); /* Protects the GPIO device list against concurrent modifications. */ static DEFINE_MUTEX(gpio_devices_lock); /* Ensures coherence during read-only accesses to the list of GPIO devices. */ diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index d2e73eea9e92..2bf3f9e13ae4 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -136,7 +136,6 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); extern spinlock_t gpio_lock; -extern struct list_head gpio_devices; void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); -- cgit v1.2.3 From 35b545332b809a439aa1bf3fde5305e6eb243cf0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 12 Jan 2024 14:49:04 +0100 Subject: gpio: remove gpio_lock The "multi-function" gpio_lock is pretty much useless with how it's used in GPIOLIB currently. Because many GPIO API calls can be called from all contexts but may also call into sleeping driver callbacks, there are many places with utterly broken workarounds like yielding the lock to call a possibly sleeping function and then re-acquiring it again without taking into account that the protected state may have changed. It was also used to protect several unrelated things: like individual descriptors AND the GPIO device list. We now serialize access to these two with SRCU and so can finally remove the spinlock. There is of course the question of consistency of lockless access to GPIO descriptors. Because we only support exclusive access to GPIOs (officially anyway, I'm looking at you broken GPIOD_FLAGS_BIT_NONEXCLUSIVE bit...) and the API contract with providers does not guarantee serialization, it's enough to ensure we cannot accidentally dereference an invalid pointer and that the state we present to both users and providers remains consistent. To achieve that: read the flags field atomically except for a few special cases. Read their current value before executing callback code and use this value for any subsequent logic. Modifying the flags depends on the particular use-case and can differ. For instance: when requesting a GPIO, we need to set the REQUESTED bit immediately so that the next user trying to request the same line sees -EBUSY. While at it: the allocations that used GFP_ATOMIC until this point can now switch to GFP_KERNEL. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-cdev.c | 20 ++++---- drivers/gpio/gpiolib-sysfs.c | 17 +++---- drivers/gpio/gpiolib.c | 106 +++++++++++++------------------------------ drivers/gpio/gpiolib.h | 2 - 4 files changed, 47 insertions(+), 98 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 75f4912339a6..3588aaf90e45 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2302,18 +2302,16 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, memset(info, 0, sizeof(*info)); info->offset = gpio_chip_hwgpio(desc); - scoped_guard(spinlock_irqsave, &gpio_lock) { - if (desc->name) - strscpy(info->name, desc->name, sizeof(info->name)); - - scoped_guard(srcu, &desc->srcu) { - label = gpiod_get_label(desc); - if (label) - strscpy(info->consumer, label, - sizeof(info->consumer)); - } + if (desc->name) + strscpy(info->name, desc->name, sizeof(info->name)); + + dflags = READ_ONCE(desc->flags); - dflags = READ_ONCE(desc->flags); + scoped_guard(srcu, &desc->srcu) { + label = gpiod_get_label(desc); + if (label && test_bit(FLAG_REQUESTED, &dflags)) + strscpy(info->consumer, label, + sizeof(info->consumer)); } /* diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 54b77ae1d176..3ae7704b2996 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -563,7 +563,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) struct gpio_device *gdev; struct gpiod_data *data; struct gpio_chip *chip; - unsigned long flags; struct device *dev; int status, offset; @@ -578,6 +577,9 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) return -EINVAL; } + if (!test_and_set_bit(FLAG_EXPORT, &desc->flags)) + return -EPERM; + gdev = desc->gdev; chip = gdev->chip; @@ -589,18 +591,11 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) goto err_unlock; } - spin_lock_irqsave(&gpio_lock, flags); - if (!test_bit(FLAG_REQUESTED, &desc->flags) || - test_bit(FLAG_EXPORT, &desc->flags)) { - spin_unlock_irqrestore(&gpio_lock, flags); - gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n", - __func__, - test_bit(FLAG_REQUESTED, &desc->flags), - test_bit(FLAG_EXPORT, &desc->flags)); + if (!test_bit(FLAG_REQUESTED, &desc->flags)) { + gpiod_dbg(desc, "%s: unavailable (not requested)\n", __func__); status = -EPERM; goto err_unlock; } - spin_unlock_irqrestore(&gpio_lock, flags); data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) { @@ -628,7 +623,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) goto err_free_data; } - set_bit(FLAG_EXPORT, &desc->flags); mutex_unlock(&sysfs_lock); return 0; @@ -636,6 +630,7 @@ err_free_data: kfree(data); err_unlock: mutex_unlock(&sysfs_lock); + clear_bit(FLAG_EXPORT, &desc->flags); gpiod_dbg(desc, "%s: status %d\n", __func__, status); return status; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f0cbbab17dc3..04b4636baff1 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -76,12 +76,6 @@ static const struct bus_type gpio_bus_type = { */ #define FASTPATH_NGPIO CONFIG_GPIOLIB_FASTPATH_LIMIT -/* gpio_lock prevents conflicts during gpio_desc[] table updates. - * While any GPIO is requested, its gpio_chip is not removable; - * each GPIO's "requested" flag serves as a lock and refcount. - */ -DEFINE_SPINLOCK(gpio_lock); - static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); @@ -123,8 +117,7 @@ static int desc_set_label(struct gpio_desc *desc, const char *label) const char *new = NULL, *old; if (label) { - /* FIXME: make this GFP_KERNEL once the spinlock is out. */ - new = kstrdup_const(label, GFP_ATOMIC); + new = kstrdup_const(label, GFP_KERNEL); if (!new) return -ENOMEM; } @@ -1093,7 +1086,6 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); void gpiochip_remove(struct gpio_chip *gc) { struct gpio_device *gdev = gc->gpiodev; - unsigned long flags; unsigned int i; down_write(&gdev->sem); @@ -1119,12 +1111,10 @@ void gpiochip_remove(struct gpio_chip *gc) */ gpiochip_set_data(gc, NULL); - spin_lock_irqsave(&gpio_lock, flags); for (i = 0; i < gdev->ngpio; i++) { if (test_bit(FLAG_REQUESTED, &gdev->descs[i].flags)) break; } - spin_unlock_irqrestore(&gpio_lock, flags); if (i != gdev->ngpio) dev_crit(&gdev->dev, @@ -2227,62 +2217,43 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); static int gpiod_request_commit(struct gpio_desc *desc, const char *label) { struct gpio_chip *gc = desc->gdev->chip; - unsigned long flags; unsigned int offset; int ret; + if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)) + return -EBUSY; + if (label) { label = kstrdup_const(label, GFP_KERNEL); if (!label) return -ENOMEM; } - spin_lock_irqsave(&gpio_lock, flags); - /* NOTE: gpio_request() can be called in early boot, * before IRQs are enabled, for non-sleeping (SOC) GPIOs. */ - if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)) { - ret = -EBUSY; - goto out_free_unlock; - } - if (gc->request) { - /* gc->request may sleep */ - spin_unlock_irqrestore(&gpio_lock, flags); offset = gpio_chip_hwgpio(desc); if (gpiochip_line_is_valid(gc, offset)) ret = gc->request(gc, offset); else ret = -EINVAL; - spin_lock_irqsave(&gpio_lock, flags); - - if (ret) { - desc_set_label(desc, NULL); - clear_bit(FLAG_REQUESTED, &desc->flags); - goto out_free_unlock; - } + if (ret) + goto out_clear_bit; } - if (gc->get_direction) { - /* gc->get_direction may sleep */ - spin_unlock_irqrestore(&gpio_lock, flags); + + if (gc->get_direction) gpiod_get_direction(desc); - spin_lock_irqsave(&gpio_lock, flags); - } - spin_unlock_irqrestore(&gpio_lock, flags); ret = desc_set_label(desc, label ? : "?"); - if (ret) { - clear_bit(FLAG_REQUESTED, &desc->flags); - return ret; - } + if (ret) + goto out_clear_bit; return 0; -out_free_unlock: - spin_unlock_irqrestore(&gpio_lock, flags); - kfree_const(label); +out_clear_bit: + clear_bit(FLAG_REQUESTED, &desc->flags); return ret; } @@ -2352,35 +2323,32 @@ static bool gpiod_free_commit(struct gpio_desc *desc) might_sleep(); - spin_lock_irqsave(&gpio_lock, flags); - gc = desc->gdev->chip; - if (gc && test_bit(FLAG_REQUESTED, &desc->flags)) { - if (gc->free) { - spin_unlock_irqrestore(&gpio_lock, flags); - might_sleep_if(gc->can_sleep); + flags = READ_ONCE(desc->flags); + + if (gc && test_bit(FLAG_REQUESTED, &flags)) { + if (gc->free) gc->free(gc, gpio_chip_hwgpio(desc)); - spin_lock_irqsave(&gpio_lock, flags); - } - clear_bit(FLAG_ACTIVE_LOW, &desc->flags); - clear_bit(FLAG_REQUESTED, &desc->flags); - clear_bit(FLAG_OPEN_DRAIN, &desc->flags); - clear_bit(FLAG_OPEN_SOURCE, &desc->flags); - clear_bit(FLAG_PULL_UP, &desc->flags); - clear_bit(FLAG_PULL_DOWN, &desc->flags); - clear_bit(FLAG_BIAS_DISABLE, &desc->flags); - clear_bit(FLAG_EDGE_RISING, &desc->flags); - clear_bit(FLAG_EDGE_FALLING, &desc->flags); - clear_bit(FLAG_IS_HOGGED, &desc->flags); + + clear_bit(FLAG_ACTIVE_LOW, &flags); + clear_bit(FLAG_REQUESTED, &flags); + clear_bit(FLAG_OPEN_DRAIN, &flags); + clear_bit(FLAG_OPEN_SOURCE, &flags); + clear_bit(FLAG_PULL_UP, &flags); + clear_bit(FLAG_PULL_DOWN, &flags); + clear_bit(FLAG_BIAS_DISABLE, &flags); + clear_bit(FLAG_EDGE_RISING, &flags); + clear_bit(FLAG_EDGE_FALLING, &flags); + clear_bit(FLAG_IS_HOGGED, &flags); #ifdef CONFIG_OF_DYNAMIC WRITE_ONCE(desc->hog, NULL); #endif ret = true; - } + desc_set_label(desc, NULL); + WRITE_ONCE(desc->flags, flags); - spin_unlock_irqrestore(&gpio_lock, flags); - desc_set_label(desc, NULL); - gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED); + gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED); + } return ret; } @@ -2422,22 +2390,12 @@ char *gpiochip_dup_line_label(struct gpio_chip *gc, unsigned int offset) if (IS_ERR(desc)) return NULL; - guard(spinlock_irqsave)(&gpio_lock); - if (!test_bit(FLAG_REQUESTED, &desc->flags)) return NULL; guard(srcu)(&desc->srcu); - /* - * FIXME: Once we mark gpiod_direction_input/output() and - * gpiod_get_direction() with might_sleep(), we'll be able to protect - * the GPIO descriptors with mutex (while value setting operations will - * become lockless). - * - * Until this happens, this allocation needs to be atomic. - */ - label = kstrdup(gpiod_get_label(desc), GFP_ATOMIC); + label = kstrdup(gpiod_get_label(desc), GFP_KERNEL); if (!label) return ERR_PTR(-ENOMEM); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 2bf3f9e13ae4..9b7afe87f1bd 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -135,8 +135,6 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); -extern spinlock_t gpio_lock; - void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action); /** -- cgit v1.2.3 From 2559f2e09211dd4a5fde5bc5749ef4714fe38f7f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 23 Jan 2024 14:26:09 +0100 Subject: gpio: reinforce desc->flags handling We now removed the gpio_lock spinlock and modified the places previously protected by it to handle desc->flags access in a consistent way. Let's improve other places that were previously unprotected by reading the flags field of gpio_desc once and using the stored value for logic consistency. If we need to modify the field, let's also write it back once with a consistent value resulting from the function's logic. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 42 +++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 04b4636baff1..cfbaf47b47d2 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -336,18 +336,20 @@ static int gpiochip_find_base_unlocked(int ngpio) int gpiod_get_direction(struct gpio_desc *desc) { struct gpio_chip *gc; + unsigned long flags; unsigned int offset; int ret; gc = gpiod_to_chip(desc); offset = gpio_chip_hwgpio(desc); + flags = READ_ONCE(desc->flags); /* * Open drain emulation using input mode may incorrectly report * input here, fix that up. */ - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags) && - test_bit(FLAG_IS_OUT, &desc->flags)) + if (test_bit(FLAG_OPEN_DRAIN, &flags) && + test_bit(FLAG_IS_OUT, &flags)) return 0; if (!gc->get_direction) @@ -361,7 +363,8 @@ int gpiod_get_direction(struct gpio_desc *desc) if (ret > 0) ret = 1; - assign_bit(FLAG_IS_OUT, &desc->flags, !ret); + assign_bit(FLAG_IS_OUT, &flags, !ret); + WRITE_ONCE(desc->flags, flags); return ret; } @@ -751,9 +754,6 @@ static void gpiochip_machine_hog(struct gpio_chip *gc, struct gpiod_hog *hog) return; } - if (test_bit(FLAG_IS_HOGGED, &desc->flags)) - return; - rv = gpiod_hog(desc, hog->line_name, hog->lflags, hog->dflags); if (rv) gpiod_err(desc, "%s: unable to hog GPIO line (%s:%u): %d\n", @@ -2528,13 +2528,16 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) static int gpio_set_bias(struct gpio_desc *desc) { enum pin_config_param bias; + unsigned long flags; unsigned int arg; - if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) + flags = READ_ONCE(desc->flags); + + if (test_bit(FLAG_BIAS_DISABLE, &flags)) bias = PIN_CONFIG_BIAS_DISABLE; - else if (test_bit(FLAG_PULL_UP, &desc->flags)) + else if (test_bit(FLAG_PULL_UP, &flags)) bias = PIN_CONFIG_BIAS_PULL_UP; - else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) + else if (test_bit(FLAG_PULL_DOWN, &flags)) bias = PIN_CONFIG_BIAS_PULL_DOWN; else return 0; @@ -2700,24 +2703,28 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output_raw); */ int gpiod_direction_output(struct gpio_desc *desc, int value) { + unsigned long flags; int ret; VALIDATE_DESC(desc); - if (test_bit(FLAG_ACTIVE_LOW, &desc->flags)) + + flags = READ_ONCE(desc->flags); + + if (test_bit(FLAG_ACTIVE_LOW, &flags)) value = !value; else value = !!value; /* GPIOs used for enabled IRQs shall not be set as output */ - if (test_bit(FLAG_USED_AS_IRQ, &desc->flags) && - test_bit(FLAG_IRQ_IS_ENABLED, &desc->flags)) { + if (test_bit(FLAG_USED_AS_IRQ, &flags) && + test_bit(FLAG_IRQ_IS_ENABLED, &flags)) { gpiod_err(desc, "%s: tried to set a GPIO tied to an IRQ as output\n", __func__); return -EIO; } - if (test_bit(FLAG_OPEN_DRAIN, &desc->flags)) { + if (test_bit(FLAG_OPEN_DRAIN, &flags)) { /* First see if we can enable open drain in hardware */ ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_DRAIN); if (!ret) @@ -2727,7 +2734,7 @@ int gpiod_direction_output(struct gpio_desc *desc, int value) ret = gpiod_direction_input(desc); goto set_output_flag; } - } else if (test_bit(FLAG_OPEN_SOURCE, &desc->flags)) { + } else if (test_bit(FLAG_OPEN_SOURCE, &flags)) { ret = gpio_set_config(desc, PIN_CONFIG_DRIVE_OPEN_SOURCE); if (!ret) goto set_output_value; @@ -4424,21 +4431,22 @@ int gpiod_hog(struct gpio_desc *desc, const char *name, int hwnum; int ret; + if (test_and_set_bit(FLAG_IS_HOGGED, &desc->flags)) + return 0; + gc = gpiod_to_chip(desc); hwnum = gpio_chip_hwgpio(desc); local_desc = gpiochip_request_own_desc(gc, hwnum, name, lflags, dflags); if (IS_ERR(local_desc)) { + clear_bit(FLAG_IS_HOGGED, &desc->flags); ret = PTR_ERR(local_desc); pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n", name, gc->label, hwnum, ret); return ret; } - /* Mark GPIO as hogged so it can be identified and removed later */ - set_bit(FLAG_IS_HOGGED, &desc->flags); - gpiod_dbg(desc, "hogged as %s%s\n", (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? "output" : "input", (dflags & GPIOD_FLAGS_BIT_DIR_OUT) ? -- cgit v1.2.3 From b6f87adbacfab9001d08e56ac869e1c75734633d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 23 Jan 2024 17:52:36 +0100 Subject: gpio: remove unneeded code from gpio_device_get_desc() The GPIO chip pointer is unused. Let's remove it. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index cfbaf47b47d2..c6966c55816a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -184,16 +184,6 @@ EXPORT_SYMBOL_GPL(gpiochip_get_desc); struct gpio_desc * gpio_device_get_desc(struct gpio_device *gdev, unsigned int hwnum) { - struct gpio_chip *gc; - - /* - * FIXME: This will be locked once we protect gdev->chip everywhere - * with SRCU. - */ - gc = gdev->chip; - if (!gc) - return ERR_PTR(-ENODEV); - if (hwnum >= gdev->ngpio) return ERR_PTR(-EINVAL); -- cgit v1.2.3 From 59cba4a0e6ca1058fbf88fec22530a4e2841802a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2024 14:08:45 +0100 Subject: gpio: sysfs: extend the critical section for unregistering sysfs devices Checking the gdev->mockdev pointer for NULL must be part of the critical section. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-sysfs.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 3ae7704b2996..12ecb49fe75d 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 #include +#include #include #include #include @@ -768,15 +769,15 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) struct gpio_desc *desc; struct gpio_chip *chip = gdev->chip; - if (!gdev->mockdev) - return; + scoped_guard(mutex, &sysfs_lock) { + if (!gdev->mockdev) + return; - device_unregister(gdev->mockdev); + device_unregister(gdev->mockdev); - /* prevent further gpiod exports */ - mutex_lock(&sysfs_lock); - gdev->mockdev = NULL; - mutex_unlock(&sysfs_lock); + /* prevent further gpiod exports */ + gdev->mockdev = NULL; + } /* unregister gpiod class devices owned by sysfs */ for_each_gpio_desc_with_flag(chip, desc, FLAG_SYSFS) { -- cgit v1.2.3 From b93bca4bd6d221d5f34a20d69ec0f1ec636ae7df Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2024 15:11:38 +0100 Subject: gpio: sysfs: pass the GPIO device - not chip - to sysfs callbacks We're working towards protecting the chip pointer in struct gpio_device with SRCU. In order to use it in sysfs callbacks we must pass the pointer to the GPIO device that wraps the chip instead of the address of the chip itself as the user data. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-sysfs.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 12ecb49fe75d..84b8a7555791 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -401,27 +401,27 @@ static const struct attribute_group *gpio_groups[] = { static ssize_t base_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_chip *chip = dev_get_drvdata(dev); + const struct gpio_device *gdev = dev_get_drvdata(dev); - return sysfs_emit(buf, "%d\n", chip->base); + return sysfs_emit(buf, "%d\n", gdev->chip->base); } static DEVICE_ATTR_RO(base); static ssize_t label_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_chip *chip = dev_get_drvdata(dev); + const struct gpio_device *gdev = dev_get_drvdata(dev); - return sysfs_emit(buf, "%s\n", chip->label ?: ""); + return sysfs_emit(buf, "%s\n", gdev->chip->label ?: ""); } static DEVICE_ATTR_RO(label); static ssize_t ngpio_show(struct device *dev, struct device_attribute *attr, char *buf) { - const struct gpio_chip *chip = dev_get_drvdata(dev); + const struct gpio_device *gdev = dev_get_drvdata(dev); - return sysfs_emit(buf, "%u\n", chip->ngpio); + return sysfs_emit(buf, "%u\n", gdev->chip->ngpio); } static DEVICE_ATTR_RO(ngpio); @@ -751,7 +751,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) parent = &gdev->dev; /* use chip->base for the ID; it's already known to be unique */ - dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), chip, + dev = device_create_with_groups(&gpio_class, parent, MKDEV(0, 0), gdev, gpiochip_groups, GPIOCHIP_NAME "%d", chip->base); if (IS_ERR(dev)) -- cgit v1.2.3 From f4e14d45d7fe95f6c1c136baf9e232234669dfed Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2024 17:13:51 +0100 Subject: gpio: cdev: replace gpiochip_get_desc() with gpio_device_get_desc() gpio_device_get_desc() is the safer alternative to gpiochip_get_desc(). As we don't really need to dereference the chip pointer to retrieve the descriptors in character device code, let's use it. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-cdev.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 3588aaf90e45..8e37e3befa08 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -332,7 +332,7 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) /* Request each GPIO */ for (i = 0; i < handlereq.lines; i++) { u32 offset = handlereq.lineoffsets[i]; - struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); + struct gpio_desc *desc = gpio_device_get_desc(gdev, offset); if (IS_ERR(desc)) { ret = PTR_ERR(desc); @@ -1739,7 +1739,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) /* Request each GPIO */ for (i = 0; i < ulr.num_lines; i++) { u32 offset = ulr.offsets[i]; - struct gpio_desc *desc = gpiochip_get_desc(gdev->chip, offset); + struct gpio_desc *desc = gpio_device_get_desc(gdev, offset); if (IS_ERR(desc)) { ret = PTR_ERR(desc); @@ -2123,7 +2123,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) lflags = eventreq.handleflags; eflags = eventreq.eventflags; - desc = gpiochip_get_desc(gdev->chip, offset); + desc = gpio_device_get_desc(gdev, offset); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -2419,7 +2419,7 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip, return -EFAULT; /* this doubles as a range check on line_offset */ - desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.line_offset); + desc = gpio_device_get_desc(cdev->gdev, lineinfo.line_offset); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -2456,7 +2456,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip, if (memchr_inv(lineinfo.padding, 0, sizeof(lineinfo.padding))) return -EINVAL; - desc = gpiochip_get_desc(cdev->gdev->chip, lineinfo.offset); + desc = gpio_device_get_desc(cdev->gdev, lineinfo.offset); if (IS_ERR(desc)) return PTR_ERR(desc); -- cgit v1.2.3 From 3c7a47f6c5f0d3e3c6149b7eb920f420a741e969 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2024 17:21:16 +0100 Subject: gpio: cdev: don't access gdev->chip if it's not needed The variable holding the number of GPIO lines is duplicated in GPIO device so read it instead of unnecessarily dereferencing the chip pointer. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-cdev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 8e37e3befa08..e993c6a7215a 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2701,7 +2701,7 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) if (!cdev) return -ENODEV; - cdev->watched_lines = bitmap_zalloc(gdev->chip->ngpio, GFP_KERNEL); + cdev->watched_lines = bitmap_zalloc(gdev->ngpio, GFP_KERNEL); if (!cdev->watched_lines) goto out_free_cdev; -- cgit v1.2.3 From 5694f274a060ebd0e7128c11501281197fb7a5ce Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 5 Feb 2024 20:28:48 +0100 Subject: gpio: sysfs: don't access gdev->chip if it's not needed Don't dereference gdev->chip if the same information can be obtained from struct gpio_device. Suggested-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-sysfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 84b8a7555791..c1f96a14fe7d 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -403,7 +403,7 @@ static ssize_t base_show(struct device *dev, { const struct gpio_device *gdev = dev_get_drvdata(dev); - return sysfs_emit(buf, "%d\n", gdev->chip->base); + return sysfs_emit(buf, "%d\n", gdev->base); } static DEVICE_ATTR_RO(base); @@ -412,7 +412,7 @@ static ssize_t label_show(struct device *dev, { const struct gpio_device *gdev = dev_get_drvdata(dev); - return sysfs_emit(buf, "%s\n", gdev->chip->label ?: ""); + return sysfs_emit(buf, "%s\n", gdev->label); } static DEVICE_ATTR_RO(label); @@ -421,7 +421,7 @@ static ssize_t ngpio_show(struct device *dev, { const struct gpio_device *gdev = dev_get_drvdata(dev); - return sysfs_emit(buf, "%u\n", gdev->chip->ngpio); + return sysfs_emit(buf, "%u\n", gdev->ngpio); } static DEVICE_ATTR_RO(ngpio); -- cgit v1.2.3 From 7fe595b3c3cf3f9b8f21fce72f1f48a2cb41522e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 31 Jan 2024 11:40:57 +0100 Subject: gpio: don't dereference gdev->chip in gpiochip_setup_dev() We don't need to dereference gdev->chip in gpiochip_setup_dev() as at the time it's called, the label in the associated struct gpio_device is already set. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c6966c55816a..9fc9cfac7081 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -723,7 +723,7 @@ static int gpiochip_setup_dev(struct gpio_device *gdev) goto err_remove_device; dev_dbg(&gdev->dev, "registered GPIOs %d to %d on %s\n", gdev->base, - gdev->base + gdev->ngpio - 1, gdev->chip->label ? : "generic"); + gdev->base + gdev->ngpio - 1, gdev->label); return 0; -- cgit v1.2.3 From 6c82e737ab21c82982eaf5591b07268927a085d1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2024 21:22:19 +0100 Subject: gpio: reduce the functionality of validate_desc() Checking desc->gdev->chip for NULL without holding it in place with some serializing mechanism is pointless. Remove this check. Also don't check desc->gdev for NULL as it can never happen. We'll be protecting gdev->chip with SRCU soon but we will provide a dedicated, automatic class for that. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 9fc9cfac7081..5db3ed29dae6 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2257,19 +2257,12 @@ static int validate_desc(const struct gpio_desc *desc, const char *func) { if (!desc) return 0; + if (IS_ERR(desc)) { pr_warn("%s: invalid GPIO (errorpointer)\n", func); return PTR_ERR(desc); } - if (!desc->gdev) { - pr_warn("%s: invalid GPIO (no device)\n", func); - return -EINVAL; - } - if (!desc->gdev->chip) { - dev_warn(&desc->gdev->dev, - "%s: backing chip is gone\n", func); - return 0; - } + return 1; } @@ -2345,12 +2338,7 @@ static bool gpiod_free_commit(struct gpio_desc *desc) void gpiod_free(struct gpio_desc *desc) { - /* - * We must not use VALIDATE_DESC_VOID() as the underlying gdev->chip - * may already be NULL but we still want to put the references. - */ - if (!desc) - return; + VALIDATE_DESC_VOID(desc); if (!gpiod_free_commit(desc)) WARN_ON(1); -- cgit v1.2.3 From c5cf334dcc78185da08b1f5df2d754cc7bf02669 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 25 Jan 2024 15:05:19 +0100 Subject: gpio: remove unnecessary checks from gpiod_to_chip() We don't need to check the gdev pointer in struct gpio_desc - it's always assigned and never cleared. It's also pointless to check gdev->chip before we actually serialize access to it. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 5db3ed29dae6..76c27ba9ee64 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -214,7 +214,7 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); */ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { - if (!desc || !desc->gdev) + if (!desc) return NULL; return desc->gdev->chip; } @@ -3505,7 +3505,7 @@ int gpiod_to_irq(const struct gpio_desc *desc) * requires this function to not return zero on an invalid descriptor * but rather a negative error number. */ - if (!desc || IS_ERR(desc) || !desc->gdev || !desc->gdev->chip) + if (!desc || IS_ERR(desc)) return -EINVAL; gc = desc->gdev->chip; -- cgit v1.2.3 From 8a5b477bb3e9c891eb064accabb3162ccf2c590e Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sat, 27 Jan 2024 22:14:15 +0100 Subject: gpio: add the can_sleep flag to struct gpio_device Duplicating the can_sleep value in GPIO device will allow us to not needlessly dereference the chip pointer in several places and reduce the number of SRCU read-only critical sections. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 11 ++++++----- drivers/gpio/gpiolib.h | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 76c27ba9ee64..40052de2361c 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -901,6 +901,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, } gdev->ngpio = gc->ngpio; + gdev->can_sleep = gc->can_sleep; scoped_guard(mutex, &gpio_devices_lock) { /* @@ -3072,7 +3073,7 @@ int gpiod_get_raw_value(const struct gpio_desc *desc) { VALIDATE_DESC(desc); /* Should be using gpiod_get_raw_value_cansleep() */ - WARN_ON(desc->gdev->chip->can_sleep); + WARN_ON(desc->gdev->can_sleep); return gpiod_get_raw_value_commit(desc); } EXPORT_SYMBOL_GPL(gpiod_get_raw_value); @@ -3093,7 +3094,7 @@ int gpiod_get_value(const struct gpio_desc *desc) VALIDATE_DESC(desc); /* Should be using gpiod_get_value_cansleep() */ - WARN_ON(desc->gdev->chip->can_sleep); + WARN_ON(desc->gdev->can_sleep); value = gpiod_get_raw_value_commit(desc); if (value < 0) @@ -3366,7 +3367,7 @@ void gpiod_set_raw_value(struct gpio_desc *desc, int value) { VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_raw_value_cansleep() */ - WARN_ON(desc->gdev->chip->can_sleep); + WARN_ON(desc->gdev->can_sleep); gpiod_set_raw_value_commit(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_raw_value); @@ -3407,7 +3408,7 @@ void gpiod_set_value(struct gpio_desc *desc, int value) { VALIDATE_DESC_VOID(desc); /* Should be using gpiod_set_value_cansleep() */ - WARN_ON(desc->gdev->chip->can_sleep); + WARN_ON(desc->gdev->can_sleep); gpiod_set_value_nocheck(desc, value); } EXPORT_SYMBOL_GPL(gpiod_set_value); @@ -3471,7 +3472,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_array_value); int gpiod_cansleep(const struct gpio_desc *desc) { VALIDATE_DESC(desc); - return desc->gdev->chip->can_sleep; + return desc->gdev->can_sleep; } EXPORT_SYMBOL_GPL(gpiod_cansleep); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 9b7afe87f1bd..43ff4931e2c3 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -34,6 +34,8 @@ * @descs: array of ngpio descriptors. * @ngpio: the number of GPIO lines on this GPIO device, equal to the size * of the @descs array. + * @can_sleep: indicate whether the GPIO chip driver's callbacks can sleep + * implying that they cannot be used from atomic context * @base: GPIO base in the DEPRECATED global Linux GPIO numberspace, assigned * at device creation time. * @label: a descriptive name for the GPIO device, such as the part number @@ -64,6 +66,7 @@ struct gpio_device { struct gpio_desc *descs; int base; u16 ngpio; + bool can_sleep; const char *label; void *data; struct list_head list; -- cgit v1.2.3 From 47d8b4c1d868148c8fb51b785a89e58ca2d02c4d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 5 Jan 2024 20:42:44 +0100 Subject: gpio: add SRCU infrastructure to struct gpio_device Add the SRCU struct to GPIO device. It will be used to serialize access to the GPIO chip pointer. Initialize and clean it up where applicable. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 13 ++++++++++--- drivers/gpio/gpiolib.h | 2 ++ 2 files changed, 12 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 40052de2361c..4e0c6df4a880 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -680,6 +680,7 @@ static void gpiodev_release(struct device *dev) ida_free(&gpio_ida, gdev->id); kfree_const(gdev->label); kfree(gdev->descs); + cleanup_srcu_struct(&gdev->srcu); kfree(gdev); } @@ -948,6 +949,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); init_rwsem(&gdev->sem); + ret = init_srcu_struct(&gdev->srcu); + if (ret) + goto err_remove_from_list; + #ifdef CONFIG_PINCTRL INIT_LIST_HEAD(&gdev->pin_ranges); #endif @@ -955,15 +960,15 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (gc->names) { ret = gpiochip_set_desc_names(gc); if (ret) - goto err_remove_from_list; + goto err_cleanup_gdev_srcu; } ret = gpiochip_set_names(gc); if (ret) - goto err_remove_from_list; + goto err_cleanup_gdev_srcu; ret = gpiochip_init_valid_mask(gc); if (ret) - goto err_remove_from_list; + goto err_cleanup_gdev_srcu; ret = of_gpiochip_add(gc); if (ret) @@ -1038,6 +1043,8 @@ err_remove_of_chip: err_free_gpiochip_mask: gpiochip_remove_pin_ranges(gc); gpiochip_free_valid_mask(gc); +err_cleanup_gdev_srcu: + cleanup_srcu_struct(&gdev->srcu); err_remove_from_list: scoped_guard(mutex, &gpio_devices_lock) list_del_rcu(&gdev->list); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 43ff4931e2c3..35d71e30c546 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -49,6 +49,7 @@ * @sem: protects the structure from a NULL-pointer dereference of @chip by * user-space operations when the device gets unregistered during * a hot-unplug event + * @srcu: protects the pointer to the underlying GPIO chip * @pin_ranges: range of pins served by the GPIO driver * * This state container holds most of the runtime variable data @@ -73,6 +74,7 @@ struct gpio_device { struct blocking_notifier_head line_state_notifier; struct blocking_notifier_head device_notifier; struct rw_semaphore sem; + struct srcu_struct srcu; #ifdef CONFIG_PINCTRL /* -- cgit v1.2.3 From d83cee3d2bb131c7fca7e19f1e33dc7530fd7083 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 23 Jan 2024 12:01:10 +0100 Subject: gpio: protect the pointer to gpio_chip in gpio_device with SRCU Ensure we cannot crash if the GPIO device gets unregistered (and the chip pointer set to NULL) during any of the API calls. To that end: wait for all users of gdev->chip to exit their read-only SRCU critical sections in gpiochip_remove(). For brevity: add a guard class which can be instantiated at the top of every function requiring read-only access to the chip pointer and use it in all API calls taking a GPIO descriptor as argument. In places where we only deal with the GPIO device - use regular guard() helpers and rcu_dereference() for chip access. Do the same in API calls taking a const pointer to gpio_desc. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib-cdev.c | 64 ++++++----- drivers/gpio/gpiolib-sysfs.c | 57 +++++++--- drivers/gpio/gpiolib.c | 257 +++++++++++++++++++++++++++++-------------- drivers/gpio/gpiolib.h | 22 +++- 4 files changed, 271 insertions(+), 129 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index e993c6a7215a..9323b357df43 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -24,7 +24,6 @@ #include #include #include -#include #include #include #include @@ -205,9 +204,9 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd, unsigned int i; int ret; - guard(rwsem_read)(&lh->gdev->sem); + guard(srcu)(&lh->gdev->srcu); - if (!lh->gdev->chip) + if (!rcu_dereference(lh->gdev->chip)) return -ENODEV; switch (cmd) { @@ -1520,9 +1519,9 @@ static long linereq_ioctl(struct file *file, unsigned int cmd, struct linereq *lr = file->private_data; void __user *ip = (void __user *)arg; - guard(rwsem_read)(&lr->gdev->sem); + guard(srcu)(&lr->gdev->srcu); - if (!lr->gdev->chip) + if (!rcu_dereference(lr->gdev->chip)) return -ENODEV; switch (cmd) { @@ -1551,9 +1550,9 @@ static __poll_t linereq_poll(struct file *file, struct linereq *lr = file->private_data; __poll_t events = 0; - guard(rwsem_read)(&lr->gdev->sem); + guard(srcu)(&lr->gdev->srcu); - if (!lr->gdev->chip) + if (!rcu_dereference(lr->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &lr->wait, wait); @@ -1573,9 +1572,9 @@ static ssize_t linereq_read(struct file *file, char __user *buf, ssize_t bytes_read = 0; int ret; - guard(rwsem_read)(&lr->gdev->sem); + guard(srcu)(&lr->gdev->srcu); - if (!lr->gdev->chip) + if (!rcu_dereference(lr->gdev->chip)) return -ENODEV; if (count < sizeof(le)) @@ -1874,9 +1873,9 @@ static __poll_t lineevent_poll(struct file *file, struct lineevent_state *le = file->private_data; __poll_t events = 0; - guard(rwsem_read)(&le->gdev->sem); + guard(srcu)(&le->gdev->srcu); - if (!le->gdev->chip) + if (!rcu_dereference(le->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &le->wait, wait); @@ -1912,9 +1911,9 @@ static ssize_t lineevent_read(struct file *file, char __user *buf, ssize_t ge_size; int ret; - guard(rwsem_read)(&le->gdev->sem); + guard(srcu)(&le->gdev->srcu); - if (!le->gdev->chip) + if (!rcu_dereference(le->gdev->chip)) return -ENODEV; /* @@ -1995,9 +1994,9 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd, void __user *ip = (void __user *)arg; struct gpiohandle_data ghd; - guard(rwsem_read)(&le->gdev->sem); + guard(srcu)(&le->gdev->srcu); - if (!le->gdev->chip) + if (!rcu_dereference(le->gdev->chip)) return -ENODEV; /* @@ -2295,10 +2294,13 @@ static void gpio_v2_line_info_changed_to_v1( static void gpio_desc_to_lineinfo(struct gpio_desc *desc, struct gpio_v2_line_info *info) { - struct gpio_chip *gc = desc->gdev->chip; unsigned long dflags; const char *label; + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; + memset(info, 0, sizeof(*info)); info->offset = gpio_chip_hwgpio(desc); @@ -2331,8 +2333,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, test_bit(FLAG_USED_AS_IRQ, &dflags) || test_bit(FLAG_EXPORT, &dflags) || test_bit(FLAG_SYSFS, &dflags) || - !gpiochip_line_is_valid(gc, info->offset) || - !pinctrl_gpio_can_use_line(gc, info->offset)) + !gpiochip_line_is_valid(guard.gc, info->offset) || + !pinctrl_gpio_can_use_line(guard.gc, info->offset)) info->flags |= GPIO_V2_LINE_FLAG_USED; if (test_bit(FLAG_IS_OUT, &dflags)) @@ -2505,10 +2507,10 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) struct gpio_device *gdev = cdev->gdev; void __user *ip = (void __user *)arg; - guard(rwsem_read)(&gdev->sem); + guard(srcu)(&gdev->srcu); /* We fail any subsequent ioctl():s when the chip is gone */ - if (!gdev->chip) + if (!rcu_dereference(gdev->chip)) return -ENODEV; /* Fill in the struct and pass to userspace */ @@ -2591,9 +2593,9 @@ static __poll_t lineinfo_watch_poll(struct file *file, struct gpio_chardev_data *cdev = file->private_data; __poll_t events = 0; - guard(rwsem_read)(&cdev->gdev->sem); + guard(srcu)(&cdev->gdev->srcu); - if (!cdev->gdev->chip) + if (!rcu_dereference(cdev->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &cdev->wait, pollt); @@ -2614,9 +2616,9 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, int ret; size_t event_size; - guard(rwsem_read)(&cdev->gdev->sem); + guard(srcu)(&cdev->gdev->srcu); - if (!cdev->gdev->chip) + if (!rcu_dereference(cdev->gdev->chip)) return -ENODEV; #ifndef CONFIG_GPIO_CDEV_V1 @@ -2691,10 +2693,10 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) struct gpio_chardev_data *cdev; int ret = -ENOMEM; - guard(rwsem_read)(&gdev->sem); + guard(srcu)(&gdev->srcu); /* Fail on open if the backing gpiochip is gone */ - if (!gdev->chip) + if (!rcu_dereference(gdev->chip)) return -ENODEV; cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); @@ -2781,6 +2783,7 @@ static const struct file_operations gpio_fileops = { int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) { + struct gpio_chip *gc; int ret; cdev_init(&gdev->chrdev, &gpio_fileops); @@ -2791,8 +2794,13 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) if (ret) return ret; - chip_dbg(gdev->chip, "added GPIO chardev (%d:%d)\n", - MAJOR(devt), gdev->id); + guard(srcu)(&gdev->srcu); + + gc = rcu_dereference(gdev->chip); + if (!gc) + return -ENODEV; + + chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id); return 0; } diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index c1f96a14fe7d..6285fa5afbb1 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -171,6 +171,10 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) unsigned long irq_flags; int ret; + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + data->irq = gpiod_to_irq(desc); if (data->irq < 0) return -EIO; @@ -195,7 +199,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) * Remove this redundant call (along with the corresponding * unlock) when those drivers have been fixed. */ - ret = gpiochip_lock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); + ret = gpiochip_lock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); if (ret < 0) goto err_put_kn; @@ -209,7 +213,7 @@ static int gpio_sysfs_request_irq(struct device *dev, unsigned char flags) return 0; err_unlock: - gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); + gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); err_put_kn: sysfs_put(data->value_kn); @@ -225,9 +229,13 @@ static void gpio_sysfs_free_irq(struct device *dev) struct gpiod_data *data = dev_get_drvdata(dev); struct gpio_desc *desc = data->desc; + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; + data->irq_flags = 0; free_irq(data->irq, data); - gpiochip_unlock_as_irq(desc->gdev->chip, gpio_chip_hwgpio(desc)); + gpiochip_unlock_as_irq(guard.gc, gpio_chip_hwgpio(desc)); sysfs_put(data->value_kn); } @@ -444,13 +452,12 @@ static ssize_t export_store(const struct class *class, const char *buf, size_t len) { struct gpio_desc *desc; - struct gpio_chip *gc; int status, offset; long gpio; status = kstrtol(buf, 0, &gpio); - if (status < 0) - goto done; + if (status) + return status; desc = gpio_to_desc(gpio); /* reject invalid GPIOs */ @@ -458,9 +465,13 @@ static ssize_t export_store(const struct class *class, pr_warn("%s: invalid GPIO %ld\n", __func__, gpio); return -EINVAL; } - gc = desc->gdev->chip; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + offset = gpio_chip_hwgpio(desc); - if (!gpiochip_line_is_valid(gc, offset)) { + if (!gpiochip_line_is_valid(guard.gc, offset)) { pr_warn("%s: GPIO %ld masked\n", __func__, gpio); return -EINVAL; } @@ -563,7 +574,6 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) const char *ioname = NULL; struct gpio_device *gdev; struct gpiod_data *data; - struct gpio_chip *chip; struct device *dev; int status, offset; @@ -578,16 +588,19 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) return -EINVAL; } + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + if (!test_and_set_bit(FLAG_EXPORT, &desc->flags)) return -EPERM; gdev = desc->gdev; - chip = gdev->chip; mutex_lock(&sysfs_lock); /* check if chip is being removed */ - if (!chip || !gdev->mockdev) { + if (!gdev->mockdev) { status = -ENODEV; goto err_unlock; } @@ -606,14 +619,14 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) data->desc = desc; mutex_init(&data->mutex); - if (chip->direction_input && chip->direction_output) + if (guard.gc->direction_input && guard.gc->direction_output) data->direction_can_change = direction_may_change; else data->direction_can_change = false; offset = gpio_chip_hwgpio(desc); - if (chip->names && chip->names[offset]) - ioname = chip->names[offset]; + if (guard.gc->names && guard.gc->names[offset]) + ioname = guard.gc->names[offset]; dev = device_create_with_groups(&gpio_class, &gdev->dev, MKDEV(0, 0), data, gpio_groups, @@ -728,7 +741,7 @@ EXPORT_SYMBOL_GPL(gpiod_unexport); int gpiochip_sysfs_register(struct gpio_device *gdev) { - struct gpio_chip *chip = gdev->chip; + struct gpio_chip *chip; struct device *parent; struct device *dev; @@ -741,6 +754,12 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) if (!class_is_registered(&gpio_class)) return 0; + guard(srcu)(&gdev->srcu); + + chip = rcu_dereference(gdev->chip); + if (!chip) + return -ENODEV; + /* * For sysfs backward compatibility we need to preserve this * preferred parenting to the gpio_chip parent field, if set. @@ -767,7 +786,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) void gpiochip_sysfs_unregister(struct gpio_device *gdev) { struct gpio_desc *desc; - struct gpio_chip *chip = gdev->chip; + struct gpio_chip *chip; scoped_guard(mutex, &sysfs_lock) { if (!gdev->mockdev) @@ -779,6 +798,12 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) gdev->mockdev = NULL; } + guard(srcu)(&gdev->srcu); + + chip = rcu_dereference(gdev->chip); + if (chip) + return; + /* unregister gpiod class devices owned by sysfs */ for_each_gpio_desc_with_flag(chip, desc, FLAG_SYSFS) { gpiod_unexport(desc); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4e0c6df4a880..1a9d045b5a6b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -216,7 +216,7 @@ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { if (!desc) return NULL; - return desc->gdev->chip; + return rcu_dereference(desc->gdev->chip); } EXPORT_SYMBOL_GPL(gpiod_to_chip); @@ -285,7 +285,7 @@ EXPORT_SYMBOL(gpio_device_get_label); */ struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev) { - return gdev->chip; + return rcu_dereference(gdev->chip); } EXPORT_SYMBOL_GPL(gpio_device_get_chip); @@ -325,12 +325,21 @@ static int gpiochip_find_base_unlocked(int ngpio) */ int gpiod_get_direction(struct gpio_desc *desc) { - struct gpio_chip *gc; unsigned long flags; unsigned int offset; int ret; - gc = gpiod_to_chip(desc); + /* + * We cannot use VALIDATE_DESC() as we must not return 0 for a NULL + * descriptor like we usually do. + */ + if (!desc || IS_ERR(desc)) + return -EINVAL; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + offset = gpio_chip_hwgpio(desc); flags = READ_ONCE(desc->flags); @@ -342,10 +351,10 @@ int gpiod_get_direction(struct gpio_desc *desc) test_bit(FLAG_IS_OUT, &flags)) return 0; - if (!gc->get_direction) + if (!guard.gc->get_direction) return -ENOTSUPP; - ret = gc->get_direction(gc, offset); + ret = guard.gc->get_direction(guard.gc, offset); if (ret < 0) return ret; @@ -421,6 +430,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name) { struct gpio_device *gdev; struct gpio_desc *desc; + struct gpio_chip *gc; if (!name) return NULL; @@ -429,7 +439,13 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name) list_for_each_entry_srcu(gdev, &gpio_devices, list, srcu_read_lock_held(&gpio_devices_srcu)) { - for_each_gpio_desc(gdev->chip, desc) { + guard(srcu)(&gdev->srcu); + + gc = rcu_dereference(gdev->chip); + if (!gc) + continue; + + for_each_gpio_desc(gc, desc) { if (desc->name && !strcmp(desc->name, name)) return desc; } @@ -853,7 +869,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev->dev.type = &gpio_dev_type; gdev->dev.bus = &gpio_bus_type; gdev->dev.parent = gc->parent; - gdev->chip = gc; + rcu_assign_pointer(gdev->chip, gc); gc->gpiodev = gdev; gpiochip_set_data(gc, data); @@ -1097,7 +1113,8 @@ void gpiochip_remove(struct gpio_chip *gc) synchronize_srcu(&gpio_devices_srcu); /* Numb the device, cancelling all outstanding operations */ - gdev->chip = NULL; + rcu_assign_pointer(gdev->chip, NULL); + synchronize_srcu(&gdev->srcu); gpiochip_irqchip_remove(gc); acpi_gpiochip_remove(gc); of_gpiochip_remove(gc); @@ -1156,6 +1173,7 @@ struct gpio_device *gpio_device_find(void *data, const void *data)) { struct gpio_device *gdev; + struct gpio_chip *gc; /* * Not yet but in the future the spinlock below will become a mutex. @@ -1166,8 +1184,13 @@ struct gpio_device *gpio_device_find(void *data, guard(srcu)(&gpio_devices_srcu); - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->chip && match(gdev->chip, data)) + list_for_each_entry_srcu(gdev, &gpio_devices, list, + srcu_read_lock_held(&gpio_devices_srcu)) { + guard(srcu)(&gdev->srcu); + + gc = rcu_dereference(gdev->chip); + + if (gc && match(gc, data)) return gpio_device_get(gdev); } @@ -2214,10 +2237,13 @@ EXPORT_SYMBOL_GPL(gpiochip_remove_pin_ranges); */ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) { - struct gpio_chip *gc = desc->gdev->chip; unsigned int offset; int ret; + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)) return -EBUSY; @@ -2231,17 +2257,17 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) * before IRQs are enabled, for non-sleeping (SOC) GPIOs. */ - if (gc->request) { + if (guard.gc->request) { offset = gpio_chip_hwgpio(desc); - if (gpiochip_line_is_valid(gc, offset)) - ret = gc->request(gc, offset); + if (gpiochip_line_is_valid(guard.gc, offset)) + ret = guard.gc->request(guard.gc, offset); else ret = -EINVAL; if (ret) goto out_clear_bit; } - if (gc->get_direction) + if (guard.gc->get_direction) gpiod_get_direction(desc); ret = desc_set_label(desc, label ? : "?"); @@ -2308,18 +2334,18 @@ int gpiod_request(struct gpio_desc *desc, const char *label) static bool gpiod_free_commit(struct gpio_desc *desc) { - struct gpio_chip *gc; unsigned long flags; bool ret = false; might_sleep(); - gc = desc->gdev->chip; + CLASS(gpio_chip_guard, guard)(desc); + flags = READ_ONCE(desc->flags); - if (gc && test_bit(FLAG_REQUESTED, &flags)) { - if (gc->free) - gc->free(gc, gpio_chip_hwgpio(desc)); + if (guard.gc && test_bit(FLAG_REQUESTED, &flags)) { + if (guard.gc->free) + guard.gc->free(guard.gc, gpio_chip_hwgpio(desc)); clear_bit(FLAG_ACTIVE_LOW, &flags); clear_bit(FLAG_REQUESTED, &flags); @@ -2476,11 +2502,14 @@ static int gpio_set_config_with_argument(struct gpio_desc *desc, enum pin_config_param mode, u32 argument) { - struct gpio_chip *gc = desc->gdev->chip; unsigned long config; + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + config = pinconf_to_config_packed(mode, argument); - return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config); + return gpio_do_set_config(guard.gc, gpio_chip_hwgpio(desc), config); } static int gpio_set_config_with_argument_optional(struct gpio_desc *desc, @@ -2570,18 +2599,20 @@ int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce) */ int gpiod_direction_input(struct gpio_desc *desc) { - struct gpio_chip *gc; int ret = 0; VALIDATE_DESC(desc); - gc = desc->gdev->chip; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; /* * It is legal to have no .get() and .direction_input() specified if * the chip is output-only, but you can't specify .direction_input() * and not support the .get() operation, that doesn't make sense. */ - if (!gc->get && gc->direction_input) { + if (!guard.gc->get && guard.gc->direction_input) { gpiod_warn(desc, "%s: missing get() but have direction_input()\n", __func__); @@ -2594,10 +2625,12 @@ int gpiod_direction_input(struct gpio_desc *desc) * direction (if .get_direction() is supported) else we silently * assume we are in input mode after this. */ - if (gc->direction_input) { - ret = gc->direction_input(gc, gpio_chip_hwgpio(desc)); - } else if (gc->get_direction && - (gc->get_direction(gc, gpio_chip_hwgpio(desc)) != 1)) { + if (guard.gc->direction_input) { + ret = guard.gc->direction_input(guard.gc, + gpio_chip_hwgpio(desc)); + } else if (guard.gc->get_direction && + (guard.gc->get_direction(guard.gc, + gpio_chip_hwgpio(desc)) != 1)) { gpiod_warn(desc, "%s: missing direction_input() operation and line is output\n", __func__); @@ -2616,28 +2649,31 @@ EXPORT_SYMBOL_GPL(gpiod_direction_input); static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) { - struct gpio_chip *gc = desc->gdev->chip; - int val = !!value; - int ret = 0; + int val = !!value, ret = 0; + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; /* * It's OK not to specify .direction_output() if the gpiochip is * output-only, but if there is then not even a .set() operation it * is pretty tricky to drive the output line. */ - if (!gc->set && !gc->direction_output) { + if (!guard.gc->set && !guard.gc->direction_output) { gpiod_warn(desc, "%s: missing set() and direction_output() operations\n", __func__); return -EIO; } - if (gc->direction_output) { - ret = gc->direction_output(gc, gpio_chip_hwgpio(desc), val); + if (guard.gc->direction_output) { + ret = guard.gc->direction_output(guard.gc, + gpio_chip_hwgpio(desc), val); } else { /* Check that we are in output mode if we can */ - if (gc->get_direction && - gc->get_direction(gc, gpio_chip_hwgpio(desc))) { + if (guard.gc->get_direction && + guard.gc->get_direction(guard.gc, gpio_chip_hwgpio(desc))) { gpiod_warn(desc, "%s: missing direction_output() operation\n", __func__); @@ -2647,7 +2683,7 @@ static int gpiod_direction_output_raw_commit(struct gpio_desc *desc, int value) * If we can't actively set the direction, we are some * output-only chip, so just drive the output as desired. */ - gc->set(gc, gpio_chip_hwgpio(desc), val); + guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), val); } if (!ret) @@ -2763,17 +2799,20 @@ EXPORT_SYMBOL_GPL(gpiod_direction_output); int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) { int ret = 0; - struct gpio_chip *gc; VALIDATE_DESC(desc); - gc = desc->gdev->chip; - if (!gc->en_hw_timestamp) { + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + + if (!guard.gc->en_hw_timestamp) { gpiod_warn(desc, "%s: hw ts not supported\n", __func__); return -ENOTSUPP; } - ret = gc->en_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags); + ret = guard.gc->en_hw_timestamp(guard.gc, + gpio_chip_hwgpio(desc), flags); if (ret) gpiod_warn(desc, "%s: hw ts request failed\n", __func__); @@ -2792,17 +2831,20 @@ EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns); int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) { int ret = 0; - struct gpio_chip *gc; VALIDATE_DESC(desc); - gc = desc->gdev->chip; - if (!gc->dis_hw_timestamp) { + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + + if (!guard.gc->dis_hw_timestamp) { gpiod_warn(desc, "%s: hw ts not supported\n", __func__); return -ENOTSUPP; } - ret = gc->dis_hw_timestamp(gc, gpio_chip_hwgpio(desc), flags); + ret = guard.gc->dis_hw_timestamp(guard.gc, gpio_chip_hwgpio(desc), + flags); if (ret) gpiod_warn(desc, "%s: hw ts release failed\n", __func__); @@ -2821,12 +2863,13 @@ EXPORT_SYMBOL_GPL(gpiod_disable_hw_timestamp_ns); */ int gpiod_set_config(struct gpio_desc *desc, unsigned long config) { - struct gpio_chip *gc; - VALIDATE_DESC(desc); - gc = desc->gdev->chip; - return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config); + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + + return gpio_do_set_config(guard.gc, gpio_chip_hwgpio(desc), config); } EXPORT_SYMBOL_GPL(gpiod_set_config); @@ -2924,10 +2967,19 @@ static int gpio_chip_get_value(struct gpio_chip *gc, const struct gpio_desc *des static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) { + struct gpio_device *gdev; struct gpio_chip *gc; int value; - gc = desc->gdev->chip; + /* FIXME Unable to use gpio_chip_guard due to const desc. */ + gdev = desc->gdev; + + guard(srcu)(&gdev->srcu); + + gc = rcu_dereference(gdev->chip); + if (!gc) + return -ENODEV; + value = gpio_chip_get_value(gc, desc); value = value < 0 ? value : !!value; trace_gpio_value(desc_to_gpio(desc), 1, value); @@ -2953,6 +3005,14 @@ static int gpio_chip_get_multiple(struct gpio_chip *gc, return -EIO; } +/* The 'other' chip must be protected with its GPIO device's SRCU. */ +static bool gpio_device_chip_cmp(struct gpio_device *gdev, struct gpio_chip *gc) +{ + guard(srcu)(&gdev->srcu); + + return gc == rcu_dereference(gdev->chip); +} + int gpiod_get_array_value_complex(bool raw, bool can_sleep, unsigned int array_size, struct gpio_desc **desc_array, @@ -2990,33 +3050,36 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, } while (i < array_size) { - struct gpio_chip *gc = desc_array[i]->gdev->chip; DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO); DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO); unsigned long *mask, *bits; int first, j; - if (likely(gc->ngpio <= FASTPATH_NGPIO)) { + CLASS(gpio_chip_guard, guard)(desc_array[i]); + if (!guard.gc) + return -ENODEV; + + if (likely(guard.gc->ngpio <= FASTPATH_NGPIO)) { mask = fastpath_mask; bits = fastpath_bits; } else { gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC; - mask = bitmap_alloc(gc->ngpio, flags); + mask = bitmap_alloc(guard.gc->ngpio, flags); if (!mask) return -ENOMEM; - bits = bitmap_alloc(gc->ngpio, flags); + bits = bitmap_alloc(guard.gc->ngpio, flags); if (!bits) { bitmap_free(mask); return -ENOMEM; } } - bitmap_zero(mask, gc->ngpio); + bitmap_zero(mask, guard.gc->ngpio); if (!can_sleep) - WARN_ON(gc->can_sleep); + WARN_ON(guard.gc->can_sleep); /* collect all inputs belonging to the same chip */ first = i; @@ -3031,9 +3094,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep, i = find_next_zero_bit(array_info->get_mask, array_size, i); } while ((i < array_size) && - (desc_array[i]->gdev->chip == gc)); + gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc)); - ret = gpio_chip_get_multiple(gc, mask, bits); + ret = gpio_chip_get_multiple(guard.gc, mask, bits); if (ret) { if (mask != fastpath_mask) bitmap_free(mask); @@ -3174,14 +3237,16 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_value); */ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) { - int ret = 0; - struct gpio_chip *gc = desc->gdev->chip; - int offset = gpio_chip_hwgpio(desc); + int ret = 0, offset = gpio_chip_hwgpio(desc); + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; if (value) { - ret = gc->direction_input(gc, offset); + ret = guard.gc->direction_input(guard.gc, offset); } else { - ret = gc->direction_output(gc, offset, 0); + ret = guard.gc->direction_output(guard.gc, offset, 0); if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } @@ -3199,16 +3264,18 @@ static void gpio_set_open_drain_value_commit(struct gpio_desc *desc, bool value) */ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) { - int ret = 0; - struct gpio_chip *gc = desc->gdev->chip; - int offset = gpio_chip_hwgpio(desc); + int ret = 0, offset = gpio_chip_hwgpio(desc); + + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; if (value) { - ret = gc->direction_output(gc, offset, 1); + ret = guard.gc->direction_output(guard.gc, offset, 1); if (!ret) set_bit(FLAG_IS_OUT, &desc->flags); } else { - ret = gc->direction_input(gc, offset); + ret = guard.gc->direction_input(guard.gc, offset); } trace_gpio_direction(desc_to_gpio(desc), !value, ret); if (ret < 0) @@ -3219,11 +3286,12 @@ static void gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value static void gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { - struct gpio_chip *gc; + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return; - gc = desc->gdev->chip; trace_gpio_value(desc_to_gpio(desc), 0, value); - gc->set(gc, gpio_chip_hwgpio(desc), value); + guard.gc->set(guard.gc, gpio_chip_hwgpio(desc), value); } /* @@ -3284,33 +3352,36 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, } while (i < array_size) { - struct gpio_chip *gc = desc_array[i]->gdev->chip; DECLARE_BITMAP(fastpath_mask, FASTPATH_NGPIO); DECLARE_BITMAP(fastpath_bits, FASTPATH_NGPIO); unsigned long *mask, *bits; int count = 0; - if (likely(gc->ngpio <= FASTPATH_NGPIO)) { + CLASS(gpio_chip_guard, guard)(desc_array[i]); + if (!guard.gc) + return -ENODEV; + + if (likely(guard.gc->ngpio <= FASTPATH_NGPIO)) { mask = fastpath_mask; bits = fastpath_bits; } else { gfp_t flags = can_sleep ? GFP_KERNEL : GFP_ATOMIC; - mask = bitmap_alloc(gc->ngpio, flags); + mask = bitmap_alloc(guard.gc->ngpio, flags); if (!mask) return -ENOMEM; - bits = bitmap_alloc(gc->ngpio, flags); + bits = bitmap_alloc(guard.gc->ngpio, flags); if (!bits) { bitmap_free(mask); return -ENOMEM; } } - bitmap_zero(mask, gc->ngpio); + bitmap_zero(mask, guard.gc->ngpio); if (!can_sleep) - WARN_ON(gc->can_sleep); + WARN_ON(guard.gc->can_sleep); do { struct gpio_desc *desc = desc_array[i]; @@ -3346,10 +3417,10 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, i = find_next_zero_bit(array_info->set_mask, array_size, i); } while ((i < array_size) && - (desc_array[i]->gdev->chip == gc)); + gpio_device_chip_cmp(desc_array[i]->gdev, guard.gc)); /* push collected bits to outputs */ if (count != 0) - gpio_chip_set_multiple(gc, mask, bits); + gpio_chip_set_multiple(guard.gc, mask, bits); if (mask != fastpath_mask) bitmap_free(mask); @@ -3505,6 +3576,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_consumer_name); */ int gpiod_to_irq(const struct gpio_desc *desc) { + struct gpio_device *gdev; struct gpio_chip *gc; int offset; @@ -3516,7 +3588,13 @@ int gpiod_to_irq(const struct gpio_desc *desc) if (!desc || IS_ERR(desc)) return -EINVAL; - gc = desc->gdev->chip; + gdev = desc->gdev; + /* FIXME Cannot use gpio_chip_guard due to const desc. */ + guard(srcu)(&gdev->srcu); + gc = rcu_dereference(gdev->chip); + if (!gc) + return -ENODEV; + offset = gpio_chip_hwgpio(desc); if (gc->to_irq) { int retirq = gc->to_irq(gc, offset); @@ -4696,12 +4774,20 @@ core_initcall(gpiolib_dev_init); static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) { - struct gpio_chip *gc = gdev->chip; bool active_low, is_irq, is_out; unsigned int gpio = gdev->base; struct gpio_desc *desc; + struct gpio_chip *gc; int value; + guard(srcu)(&gdev->srcu); + + gc = rcu_dereference(gdev->chip); + if (!gc) { + seq_puts(s, "Underlying GPIO chip is gone\n"); + return; + } + for_each_gpio_desc(gc, desc) { guard(srcu)(&desc->srcu); if (test_bit(FLAG_REQUESTED, &desc->flags)) { @@ -4776,9 +4862,12 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) { struct gpiolib_seq_priv *priv = s->private; struct gpio_device *gdev = v; - struct gpio_chip *gc = gdev->chip; + struct gpio_chip *gc; struct device *parent; + guard(srcu)(&gdev->srcu); + + gc = rcu_dereference(gdev->chip); if (!gc) { seq_printf(s, "%s%s: (dangling chip)", priv->newline ? "\n" : "", diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 35d71e30c546..b3810f7d286a 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -63,7 +63,7 @@ struct gpio_device { int id; struct device *mockdev; struct module *owner; - struct gpio_chip *chip; + struct gpio_chip __rcu *chip; struct gpio_desc *descs; int base; u16 ngpio; @@ -193,6 +193,26 @@ struct gpio_desc { #define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) +struct gpio_chip_guard { + struct gpio_device *gdev; + struct gpio_chip *gc; + int idx; +}; + +DEFINE_CLASS(gpio_chip_guard, + struct gpio_chip_guard, + srcu_read_unlock(&_T.gdev->srcu, _T.idx), + ({ + struct gpio_chip_guard _guard; + + _guard.gdev = desc->gdev; + _guard.idx = srcu_read_lock(&_guard.gdev->srcu); + _guard.gc = rcu_dereference(_guard.gdev->chip); + + _guard; + }), + struct gpio_desc *desc) + int gpiod_request(struct gpio_desc *desc, const char *label); void gpiod_free(struct gpio_desc *desc); -- cgit v1.2.3 From f067372c6a3c3b0c5cc775157408970b8fa6c010 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2024 17:02:03 +0100 Subject: gpio: remove the RW semaphore from the GPIO device With all accesses to gdev->chip being protected with SRCU, we can now remove the RW-semaphore specific to the character device which fulfilled the same role up to this point. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 4 ---- drivers/gpio/gpiolib.h | 5 ----- 2 files changed, 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 1a9d045b5a6b..875b40376c0b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -963,7 +963,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); - init_rwsem(&gdev->sem); ret = init_srcu_struct(&gdev->srcu); if (ret) @@ -1102,8 +1101,6 @@ void gpiochip_remove(struct gpio_chip *gc) struct gpio_device *gdev = gc->gpiodev; unsigned int i; - down_write(&gdev->sem); - /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ gpiochip_sysfs_unregister(gdev); gpiochip_free_hogs(gc); @@ -1142,7 +1139,6 @@ void gpiochip_remove(struct gpio_chip *gc) * gone. */ gcdev_unregister(gdev); - up_write(&gdev->sem); gpio_device_put(gdev); } EXPORT_SYMBOL_GPL(gpiochip_remove); diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index b3810f7d286a..07443d26cbca 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -16,7 +16,6 @@ #include #include #include -#include #include #define GPIOCHIP_NAME "gpiochip" @@ -46,9 +45,6 @@ * requested, released or reconfigured * @device_notifier: used to notify character device wait queues about the GPIO * device being unregistered - * @sem: protects the structure from a NULL-pointer dereference of @chip by - * user-space operations when the device gets unregistered during - * a hot-unplug event * @srcu: protects the pointer to the underlying GPIO chip * @pin_ranges: range of pins served by the GPIO driver * @@ -73,7 +69,6 @@ struct gpio_device { struct list_head list; struct blocking_notifier_head line_state_notifier; struct blocking_notifier_head device_notifier; - struct rw_semaphore sem; struct srcu_struct srcu; #ifdef CONFIG_PINCTRL -- cgit v1.2.3 From 5e6284444024cf7e7d0ae538df8037a3a5efbec8 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 24 Jan 2024 19:27:39 +0100 Subject: gpio: mark unsafe gpio_chip manipulators as deprecated We still have some functions that return the address of the GPIO chip associated with the GPIO device. This is dangerous and the users should find a better solution. Let's add appropriate comments to the kernel docs. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Andy Shevchenko --- drivers/gpio/gpiolib.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 875b40376c0b..82811d9a4477 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -211,6 +211,11 @@ EXPORT_SYMBOL_GPL(desc_to_gpio); /** * gpiod_to_chip - Return the GPIO chip to which a GPIO descriptor belongs * @desc: descriptor to return the chip of + * + * *DEPRECATED* + * This function is unsafe and should not be used. Using the chip address + * without taking the SRCU read lock may result in dereferencing a dangling + * pointer. */ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { @@ -275,6 +280,7 @@ EXPORT_SYMBOL(gpio_device_get_label); * Returns: * Address of the GPIO chip backing this device. * + * *DEPRECATED* * Until we can get rid of all non-driver users of struct gpio_chip, we must * provide a way of retrieving the pointer to it from struct gpio_device. This * is *NOT* safe as the GPIO API is considered to be hot-unpluggable and the -- cgit v1.2.3 From ba5c5effe02c4ae06291c7c5e67de8fedad3cf9a Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 12 Feb 2024 22:39:20 +0100 Subject: gpio: initialize descriptor SRCU structure before adding OF-based chips In certain situations we may end up taking the GPIO descriptor SRCU read lock in of_gpiochip_add() before the SRCU struct is initialized. Move the initialization before the call to of_gpiochip_add(). Fixes: be711caa87c5 ("gpio: add SRCU infrastructure to struct gpio_desc") Fixes: 1f2bcb8c8ccd ("gpio: protect the descriptor label with SRCU") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202402122228.e607a080-lkp@intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 82811d9a4477..f5434e559382 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -991,10 +991,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_cleanup_gdev_srcu; - ret = of_gpiochip_add(gc); - if (ret) - goto err_free_gpiochip_mask; - for (i = 0; i < gc->ngpio; i++) { struct gpio_desc *desc = &gdev->descs[i]; @@ -1002,7 +998,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) { for (j = 0; j < i; j++) cleanup_srcu_struct(&gdev->descs[j].srcu); - goto err_remove_of_chip; + goto err_free_gpiochip_mask; } if (gc->get_direction && gpiochip_line_is_valid(gc, i)) { @@ -1014,10 +1010,14 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, } } - ret = gpiochip_add_pin_ranges(gc); + ret = of_gpiochip_add(gc); if (ret) goto err_cleanup_desc_srcu; + ret = gpiochip_add_pin_ranges(gc); + if (ret) + goto err_remove_of_chip; + acpi_gpiochip_add(gc); machine_gpiochip_add(gc); @@ -1055,12 +1055,12 @@ err_remove_irqchip_mask: gpiochip_irqchip_free_valid_mask(gc); err_remove_acpi_chip: acpi_gpiochip_remove(gc); -err_cleanup_desc_srcu: - for (i = 0; i < gdev->ngpio; i++) - cleanup_srcu_struct(&gdev->descs[i].srcu); err_remove_of_chip: gpiochip_free_hogs(gc); of_gpiochip_remove(gc); +err_cleanup_desc_srcu: + for (i = 0; i < gdev->ngpio; i++) + cleanup_srcu_struct(&gdev->descs[i].srcu); err_free_gpiochip_mask: gpiochip_remove_pin_ranges(gc); gpiochip_free_valid_mask(gc); -- cgit v1.2.3 From 815a1b5a6da4bedb29a1e15a94a042e525b0ba96 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 14 Feb 2024 09:44:16 +0100 Subject: gpio: take the SRCU read lock in gpiod_hog() gpiod_hog() may be called without the gpio_device SRCU read lock taken so we need to do it here as well. It's alright if someone else is already holding the lock as SRCU read critical sections can be nested. Fixes: d83cee3d2bb1 ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202402122234.d85cca9b-lkp@intel.com Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Paul E. McKenney --- drivers/gpio/gpiolib.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index f5434e559382..439d32d5aa38 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4492,24 +4492,27 @@ EXPORT_SYMBOL_GPL(gpiod_get_index_optional); int gpiod_hog(struct gpio_desc *desc, const char *name, unsigned long lflags, enum gpiod_flags dflags) { - struct gpio_chip *gc; + struct gpio_device *gdev = desc->gdev; struct gpio_desc *local_desc; int hwnum; int ret; + CLASS(gpio_chip_guard, guard)(desc); + if (!guard.gc) + return -ENODEV; + if (test_and_set_bit(FLAG_IS_HOGGED, &desc->flags)) return 0; - gc = gpiod_to_chip(desc); hwnum = gpio_chip_hwgpio(desc); - local_desc = gpiochip_request_own_desc(gc, hwnum, name, + local_desc = gpiochip_request_own_desc(guard.gc, hwnum, name, lflags, dflags); if (IS_ERR(local_desc)) { clear_bit(FLAG_IS_HOGGED, &desc->flags); ret = PTR_ERR(local_desc); pr_err("requesting hog GPIO %s (chip %s, offset %d) failed, %d\n", - name, gc->label, hwnum, ret); + name, gdev->label, hwnum, ret); return ret; } -- cgit v1.2.3 From 8574b5b47610df22048adcdabf318ca983024f28 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 14 Feb 2024 09:44:17 +0100 Subject: gpio: cdev: use correct pointer accessors with SRCU We never dereference the chip pointer in character device code so we can use the lighter rcu_access_pointer() helper. This also makes lockep happier as it no longer complains about suspicious rcu_dereference() usage. Fixes: d83cee3d2bb1 ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202402122234.d85cca9b-lkp@intel.com Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij Acked-by: Paul E. McKenney --- drivers/gpio/gpiolib-cdev.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 9323b357df43..85037fa4925e 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -206,7 +206,7 @@ static long linehandle_ioctl(struct file *file, unsigned int cmd, guard(srcu)(&lh->gdev->srcu); - if (!rcu_dereference(lh->gdev->chip)) + if (!rcu_access_pointer(lh->gdev->chip)) return -ENODEV; switch (cmd) { @@ -1521,7 +1521,7 @@ static long linereq_ioctl(struct file *file, unsigned int cmd, guard(srcu)(&lr->gdev->srcu); - if (!rcu_dereference(lr->gdev->chip)) + if (!rcu_access_pointer(lr->gdev->chip)) return -ENODEV; switch (cmd) { @@ -1552,7 +1552,7 @@ static __poll_t linereq_poll(struct file *file, guard(srcu)(&lr->gdev->srcu); - if (!rcu_dereference(lr->gdev->chip)) + if (!rcu_access_pointer(lr->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &lr->wait, wait); @@ -1574,7 +1574,7 @@ static ssize_t linereq_read(struct file *file, char __user *buf, guard(srcu)(&lr->gdev->srcu); - if (!rcu_dereference(lr->gdev->chip)) + if (!rcu_access_pointer(lr->gdev->chip)) return -ENODEV; if (count < sizeof(le)) @@ -1875,7 +1875,7 @@ static __poll_t lineevent_poll(struct file *file, guard(srcu)(&le->gdev->srcu); - if (!rcu_dereference(le->gdev->chip)) + if (!rcu_access_pointer(le->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &le->wait, wait); @@ -1913,7 +1913,7 @@ static ssize_t lineevent_read(struct file *file, char __user *buf, guard(srcu)(&le->gdev->srcu); - if (!rcu_dereference(le->gdev->chip)) + if (!rcu_access_pointer(le->gdev->chip)) return -ENODEV; /* @@ -1996,7 +1996,7 @@ static long lineevent_ioctl(struct file *file, unsigned int cmd, guard(srcu)(&le->gdev->srcu); - if (!rcu_dereference(le->gdev->chip)) + if (!rcu_access_pointer(le->gdev->chip)) return -ENODEV; /* @@ -2510,7 +2510,7 @@ static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) guard(srcu)(&gdev->srcu); /* We fail any subsequent ioctl():s when the chip is gone */ - if (!rcu_dereference(gdev->chip)) + if (!rcu_access_pointer(gdev->chip)) return -ENODEV; /* Fill in the struct and pass to userspace */ @@ -2595,7 +2595,7 @@ static __poll_t lineinfo_watch_poll(struct file *file, guard(srcu)(&cdev->gdev->srcu); - if (!rcu_dereference(cdev->gdev->chip)) + if (!rcu_access_pointer(cdev->gdev->chip)) return EPOLLHUP | EPOLLERR; poll_wait(file, &cdev->wait, pollt); @@ -2618,7 +2618,7 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf, guard(srcu)(&cdev->gdev->srcu); - if (!rcu_dereference(cdev->gdev->chip)) + if (!rcu_access_pointer(cdev->gdev->chip)) return -ENODEV; #ifndef CONFIG_GPIO_CDEV_V1 @@ -2696,7 +2696,7 @@ static int gpio_chrdev_open(struct inode *inode, struct file *file) guard(srcu)(&gdev->srcu); /* Fail on open if the backing gpiochip is gone */ - if (!rcu_dereference(gdev->chip)) + if (!rcu_access_pointer(gdev->chip)) return -ENODEV; cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); @@ -2796,8 +2796,7 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) guard(srcu)(&gdev->srcu); - gc = rcu_dereference(gdev->chip); - if (!gc) + if (!rcu_access_pointer(gdev->chip)) return -ENODEV; chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id); -- cgit v1.2.3 From d82b9e0887e69d9060c854b079a3a5024788f7cb Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 14 Feb 2024 09:44:18 +0100 Subject: gpio: use srcu_dereference() with SRCU-protected pointers Lockdep with CONFIG_PROVE_RCU enabled reports false positives about suspicious rcu_dereference() usage. Let's silence it by using srcu_dereference() which is the correct helper with SRCU. Fixes: d83cee3d2bb1 ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202402122234.d85cca9b-lkp@intel.com Signed-off-by: Bartosz Golaszewski Acked-by: Paul E. McKenney --- drivers/gpio/gpiolib-sysfs.c | 5 +++-- drivers/gpio/gpiolib.c | 16 ++++++++-------- drivers/gpio/gpiolib.h | 3 ++- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 6285fa5afbb1..71ba2a774197 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -756,7 +757,7 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) guard(srcu)(&gdev->srcu); - chip = rcu_dereference(gdev->chip); + chip = srcu_dereference(gdev->chip, &gdev->srcu); if (!chip) return -ENODEV; @@ -800,7 +801,7 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) guard(srcu)(&gdev->srcu); - chip = rcu_dereference(gdev->chip); + chip = srcu_dereference(gdev->chip, &gdev->srcu); if (chip) return; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 439d32d5aa38..b095f475805f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -109,7 +109,7 @@ const char *gpiod_get_label(struct gpio_desc *desc) return "interrupt"; return test_bit(FLAG_REQUESTED, &flags) ? - rcu_dereference(desc->label) : NULL; + srcu_dereference(desc->label, &desc->srcu) : NULL; } static int desc_set_label(struct gpio_desc *desc, const char *label) @@ -447,7 +447,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name) srcu_read_lock_held(&gpio_devices_srcu)) { guard(srcu)(&gdev->srcu); - gc = rcu_dereference(gdev->chip); + gc = srcu_dereference(gdev->chip, &gdev->srcu); if (!gc) continue; @@ -1190,7 +1190,7 @@ struct gpio_device *gpio_device_find(void *data, srcu_read_lock_held(&gpio_devices_srcu)) { guard(srcu)(&gdev->srcu); - gc = rcu_dereference(gdev->chip); + gc = srcu_dereference(gdev->chip, &gdev->srcu); if (gc && match(gc, data)) return gpio_device_get(gdev); @@ -2978,7 +2978,7 @@ static int gpiod_get_raw_value_commit(const struct gpio_desc *desc) guard(srcu)(&gdev->srcu); - gc = rcu_dereference(gdev->chip); + gc = srcu_dereference(gdev->chip, &gdev->srcu); if (!gc) return -ENODEV; @@ -3012,7 +3012,7 @@ static bool gpio_device_chip_cmp(struct gpio_device *gdev, struct gpio_chip *gc) { guard(srcu)(&gdev->srcu); - return gc == rcu_dereference(gdev->chip); + return gc == srcu_dereference(gdev->chip, &gdev->srcu); } int gpiod_get_array_value_complex(bool raw, bool can_sleep, @@ -3593,7 +3593,7 @@ int gpiod_to_irq(const struct gpio_desc *desc) gdev = desc->gdev; /* FIXME Cannot use gpio_chip_guard due to const desc. */ guard(srcu)(&gdev->srcu); - gc = rcu_dereference(gdev->chip); + gc = srcu_dereference(gdev->chip, &gdev->srcu); if (!gc) return -ENODEV; @@ -4787,7 +4787,7 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) guard(srcu)(&gdev->srcu); - gc = rcu_dereference(gdev->chip); + gc = srcu_dereference(gdev->chip, &gdev->srcu); if (!gc) { seq_puts(s, "Underlying GPIO chip is gone\n"); return; @@ -4872,7 +4872,7 @@ static int gpiolib_seq_show(struct seq_file *s, void *v) guard(srcu)(&gdev->srcu); - gc = rcu_dereference(gdev->chip); + gc = srcu_dereference(gdev->chip, &gdev->srcu); if (!gc) { seq_printf(s, "%s%s: (dangling chip)", priv->newline ? "\n" : "", diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 07443d26cbca..ada36aa0f81a 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -202,7 +202,8 @@ DEFINE_CLASS(gpio_chip_guard, _guard.gdev = desc->gdev; _guard.idx = srcu_read_lock(&_guard.gdev->srcu); - _guard.gc = rcu_dereference(_guard.gdev->chip); + _guard.gc = srcu_dereference(_guard.gdev->chip, + &_guard.gdev->srcu); _guard; }), -- cgit v1.2.3 From 0d7fa0eda4e3ffe1b7ebc73b6ef81298bddda649 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 14 Feb 2024 09:44:19 +0100 Subject: gpio: don't let lockdep complain about inherently dangerous RCU usage There are two legacy, deprecated functions - gpiod_to_chip() and gpio_device_get_chip() - that still have users in tree. They return the address of the SRCU-protected chip outside of the read-only critical sections. They are inherently dangerous and the users should convert to safer alternatives. Let's explicitly silence lockdep warnings by using rcu_dereference_check(ptr, 1). While at it: reuse gpio_device_get_chip() in gpiod_to_chip(). Fixes: d83cee3d2bb1 ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-lkp/202402122234.d85cca9b-lkp@intel.com Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index b095f475805f..02be0ba1a402 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -221,7 +221,8 @@ struct gpio_chip *gpiod_to_chip(const struct gpio_desc *desc) { if (!desc) return NULL; - return rcu_dereference(desc->gdev->chip); + + return gpio_device_get_chip(desc->gdev); } EXPORT_SYMBOL_GPL(gpiod_to_chip); @@ -291,7 +292,7 @@ EXPORT_SYMBOL(gpio_device_get_label); */ struct gpio_chip *gpio_device_get_chip(struct gpio_device *gdev) { - return rcu_dereference(gdev->chip); + return rcu_dereference_check(gdev->chip, 1); } EXPORT_SYMBOL_GPL(gpio_device_get_chip); -- cgit v1.2.3 From be91c19e47d1b9bf1ebd7ec4a859a50a53e54882 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 14 Feb 2024 09:52:48 +0100 Subject: gpio: sysfs: fix inverted pointer logic The logic is inverted, we want to return if the chip *IS* NULL. Fixes: d83cee3d2bb1 ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU") Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-gpio/15671341-0b29-40e0-b487-0a4cdc414d8e@moroto.mountain/ Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 71ba2a774197..67fc09a57f26 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -802,7 +802,7 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) guard(srcu)(&gdev->srcu); chip = srcu_dereference(gdev->chip, &gdev->srcu); - if (chip) + if (!chip) return; /* unregister gpiod class devices owned by sysfs */ -- cgit v1.2.3 From 91510d5959ad9eac451685e3bfc8385b89c23908 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 16 Feb 2024 11:59:30 +0100 Subject: gpio: cdev: fix a NULL-pointer dereference with DEBUG enabled We are actually passing the gc pointer to chip_dbg() so we have to srcu_dereference() it. Fixes: 8574b5b47610 ("gpio: cdev: use correct pointer accessors with SRCU") Reported-by: Marek Szyprowski Closes: https://lore.kernel.org/lkml/179caa10-5f86-4707-8bb0-fe1b316326d6@samsung.com/ Signed-off-by: Bartosz Golaszewski Tested-by: Marek Szyprowski --- drivers/gpio/gpiolib-cdev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 85037fa4925e..f384fa278764 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -2795,8 +2795,8 @@ int gpiolib_cdev_register(struct gpio_device *gdev, dev_t devt) return ret; guard(srcu)(&gdev->srcu); - - if (!rcu_access_pointer(gdev->chip)) + gc = srcu_dereference(gdev->chip, &gdev->srcu); + if (!gc) return -ENODEV; chip_dbg(gc, "added GPIO chardev (%d:%d)\n", MAJOR(devt), gdev->id); -- cgit v1.2.3 From 4a92857d6e8383eca6d661538bb25dc7004fd391 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 16 Feb 2024 14:52:17 +0100 Subject: gpio: constify opaque pointer "data" in gpio_device_find() The opaque pointer "data" in each match function used by gpio_device_find() is a pointer to const, thus the same argument passed to gpio_device_find() can adjusted similarly. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 2 +- include/linux/gpio/driver.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 02be0ba1a402..e4dd13d81b4d 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1171,7 +1171,7 @@ EXPORT_SYMBOL_GPL(gpiochip_remove); * If the function returns non-NULL, the returned reference must be freed by * the caller using gpio_device_put(). */ -struct gpio_device *gpio_device_find(void *data, +struct gpio_device *gpio_device_find(const void *data, int (*match)(struct gpio_chip *gc, const void *data)) { diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 9d0023f83a57..9c1fbfaebaa8 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -628,7 +628,7 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, void *data, struct lock_class_key *lock_key, struct lock_class_key *request_key); -struct gpio_device *gpio_device_find(void *data, +struct gpio_device *gpio_device_find(const void *data, int (*match)(struct gpio_chip *gc, const void *data)); struct gpio_device *gpio_device_find_by_label(const char *label); -- cgit v1.2.3 From 24ba441d2b06614871b2787d5c2e16e7f1a15462 Mon Sep 17 00:00:00 2001 From: Xiaolei Wang Date: Sat, 17 Feb 2024 21:52:55 +0800 Subject: gpio: fix memory leak in gpiod_request_commit() Since commit 1f2bcb8c8ccd ("gpio: protect the descriptor label with SRCU"), desc_set_label() already allocates memory for the label, so there is no need to allocate it again. If we do, we leak it. unreferenced object 0xffff0000c3e4d0c0 (size 32): comm "kworker/u16:4", pid 60, jiffies 4294894555 hex dump (first 32 bytes): 72 65 67 75 6c 61 74 6f 72 2d 63 61 6e 32 2d 73 regulator-can2-s 74 62 79 00 00 00 ff ff ff ff ff ff eb db ff ff tby............. backtrace (crc 2c3a0350): [<00000000e93c5cf4>] kmemleak_alloc+0x34/0x40 [<0000000097a2657f>] __kmalloc_node_track_caller+0x2c4/0x524 [<000000000dd1c057>] kstrdup+0x4c/0x98 [<00000000b513a96a>] kstrdup_const+0x34/0x40 [<000000008a7f0feb>] gpiod_request_commit+0xdc/0x358 [<00000000fc71ad64>] gpiod_request+0xd8/0x204 [<00000000fa24b091>] gpiod_find_and_request+0x170/0x780 [<0000000086ecf92d>] gpiod_get_index+0x70/0xe0 [<000000004aef97f9>] gpiod_get_optional+0x18/0x30 [<00000000312f1b25>] reg_fixed_voltage_probe+0x58c/0xad8 [<00000000e6f47635>] platform_probe+0xc4/0x198 [<00000000cf78fbdb>] really_probe+0x204/0x5a8 [<00000000e28d05ec>] __driver_probe_device+0x158/0x2c4 [<00000000e4fe452b>] driver_probe_device+0x60/0x18c [<00000000479fcf5d>] __device_attach_driver+0x168/0x208 [<000000007d389f38>] bus_for_each_drv+0x104/0x190 Fixes: 1f2bcb8c8ccd ("gpio: protect the descriptor label with SRCU") Signed-off-by: Xiaolei Wang [Bartosz: tweaked the commit message] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e4dd13d81b4d..3c22920bd201 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2250,12 +2250,6 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)) return -EBUSY; - if (label) { - label = kstrdup_const(label, GFP_KERNEL); - if (!label) - return -ENOMEM; - } - /* NOTE: gpio_request() can be called in early boot, * before IRQs are enabled, for non-sleeping (SOC) GPIOs. */ -- cgit v1.2.3 From ee9d5895672fe723a1626a91e4957fd3cd5cfdda Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Tue, 20 Feb 2024 08:26:02 +0100 Subject: gpio: constify of_phandle_args in of_find_gpio_device_by_xlate() Pointer to the struct of_phandle_args can be made const after gpio_device_find() arguments got constified. This should be part of commit 4a92857d6e83 ("gpio: constify opaque pointer "data" in gpio_device_find()"). Signed-off-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 523b047a2803..e35a9c7da4ee 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -129,7 +129,7 @@ static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, } static struct gpio_device * -of_find_gpio_device_by_xlate(struct of_phandle_args *gpiospec) +of_find_gpio_device_by_xlate(const struct of_phandle_args *gpiospec) { return gpio_device_find(gpiospec, of_gpiochip_match_node_and_xlate); } -- cgit v1.2.3 From f837fe1bffe6976ed5e7198b1892ea248b75358b Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 19 Feb 2024 20:52:27 -0800 Subject: gpio: Add ChromeOS EC GPIO driver The ChromeOS embedded controller (EC) supports setting the state of GPIOs when the system is unlocked, and getting the state of GPIOs in all cases. The GPIOs are on the EC itself, so the EC acts similar to a GPIO expander. Add a driver to get and set the GPIOs on the EC through the host command interface. Signed-off-by: Stephen Boyd Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 10 +++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-cros-ec.c | 209 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+) create mode 100644 drivers/gpio/gpio-cros-ec.c (limited to 'drivers') diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3081406ff57a..3fbb0bdb15c1 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1241,6 +1241,16 @@ config GPIO_BD9571MWV This driver can also be built as a module. If so, the module will be called gpio-bd9571mwv. +config GPIO_CROS_EC + tristate "ChromeOS EC GPIO support" + depends on CROS_EC + help + GPIO driver for the ChromeOS Embedded Controller (EC). GPIOs + cannot be set unless the system is unlocked. + + This driver can also be built as a module. If so, the module + will be called gpio-cros-ec. + config GPIO_CRYSTAL_COVE tristate "GPIO support for Crystal Cove PMIC" depends on (X86 || COMPILE_TEST) && INTEL_SOC_PMIC diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9e40af196aae..7ae4d81de1df 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o obj-$(CONFIG_GPIO_SNPS_CREG) += gpio-creg-snps.o +obj-$(CONFIG_GPIO_CROS_EC) += gpio-cros-ec.o obj-$(CONFIG_GPIO_CRYSTAL_COVE) += gpio-crystalcove.o obj-$(CONFIG_GPIO_CS5535) += gpio-cs5535.o obj-$(CONFIG_GPIO_DA9052) += gpio-da9052.o diff --git a/drivers/gpio/gpio-cros-ec.c b/drivers/gpio/gpio-cros-ec.c new file mode 100644 index 000000000000..842e1c060414 --- /dev/null +++ b/drivers/gpio/gpio-cros-ec.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2024 Google LLC + * + * This driver provides the ability to control GPIOs on the Chrome OS EC. + * There isn't any direction control, and setting values on GPIOs is only + * possible when the system is unlocked. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Prefix all names to avoid collisions with EC <-> AP nets */ +static const char cros_ec_gpio_prefix[] = "EC:"; + +/* Setting gpios is only supported when the system is unlocked */ +static void cros_ec_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); + struct cros_ec_device *cros_ec = gpiochip_get_data(gc); + struct ec_params_gpio_set params = { + .val = val, + }; + int ret; + ssize_t copied; + + copied = strscpy(params.name, name, sizeof(params.name)); + if (copied < 0) + return; + + ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_SET, ¶ms, + sizeof(params), NULL, 0); + if (ret < 0) + dev_err(gc->parent, "error setting gpio%d (%s) on EC: %d\n", gpio, name, ret); +} + +static int cros_ec_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); + struct cros_ec_device *cros_ec = gpiochip_get_data(gc); + struct ec_params_gpio_get params; + struct ec_response_gpio_get response; + int ret; + ssize_t copied; + + copied = strscpy(params.name, name, sizeof(params.name)); + if (copied < 0) + return -EINVAL; + + ret = cros_ec_cmd(cros_ec, 0, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) { + dev_err(gc->parent, "error getting gpio%d (%s) on EC: %d\n", gpio, name, ret); + return ret; + } + + return response.val; +} + +#define CROS_EC_GPIO_INPUT BIT(8) +#define CROS_EC_GPIO_OUTPUT BIT(9) + +static int cros_ec_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) +{ + const char *name = gc->names[gpio] + strlen(cros_ec_gpio_prefix); + struct cros_ec_device *cros_ec = gpiochip_get_data(gc); + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_INFO, + .get_info.index = gpio, + }; + struct ec_response_gpio_get_v1 response; + int ret; + + ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) { + dev_err(gc->parent, "error getting direction of gpio%d (%s) on EC: %d\n", gpio, name, ret); + return ret; + } + + if (response.get_info.flags & CROS_EC_GPIO_INPUT) + return GPIO_LINE_DIRECTION_IN; + + if (response.get_info.flags & CROS_EC_GPIO_OUTPUT) + return GPIO_LINE_DIRECTION_OUT; + + return -EINVAL; +} + +/* Query EC for all gpio line names */ +static int cros_ec_gpio_init_names(struct cros_ec_device *cros_ec, struct gpio_chip *gc) +{ + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_INFO, + }; + struct ec_response_gpio_get_v1 response; + int ret, i; + /* EC may not NUL terminate */ + size_t name_len = strlen(cros_ec_gpio_prefix) + sizeof(response.get_info.name) + 1; + ssize_t copied; + const char **names; + char *str; + + names = devm_kcalloc(gc->parent, gc->ngpio, sizeof(*names), GFP_KERNEL); + if (!names) + return -ENOMEM; + gc->names = names; + + str = devm_kcalloc(gc->parent, gc->ngpio, name_len, GFP_KERNEL); + if (!str) + return -ENOMEM; + + /* Get gpio line names one at a time */ + for (i = 0; i < gc->ngpio; i++) { + params.get_info.index = i; + ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) { + dev_err_probe(gc->parent, ret, "error getting gpio%d info\n", i); + return ret; + } + + names[i] = str; + copied = scnprintf(str, name_len, "%s%s", cros_ec_gpio_prefix, + response.get_info.name); + if (copied < 0) + return copied; + + str += copied + 1; + } + + return 0; +} + +/* Query EC for number of gpios */ +static int cros_ec_gpio_ngpios(struct cros_ec_device *cros_ec) +{ + struct ec_params_gpio_get_v1 params = { + .subcmd = EC_GPIO_GET_COUNT, + }; + struct ec_response_gpio_get_v1 response; + int ret; + + ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GPIO_GET, ¶ms, + sizeof(params), &response, sizeof(response)); + if (ret < 0) + return ret; + + return response.get_count.val; +} + +static int cros_ec_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *parent = dev->parent; + struct cros_ec_dev *ec_dev = dev_get_drvdata(parent); + struct cros_ec_device *cros_ec = ec_dev->ec_dev; + struct gpio_chip *gc; + int ngpios; + int ret; + + /* Use the fwnode from the protocol device, e.g. cros-ec-spi */ + device_set_node(dev, dev_fwnode(cros_ec->dev)); + + ngpios = cros_ec_gpio_ngpios(cros_ec); + if (ngpios < 0) { + dev_err_probe(dev, ngpios, "error getting gpio count\n"); + return ngpios; + } + + gc = devm_kzalloc(dev, sizeof(*gc), GFP_KERNEL); + if (!gc) + return -ENOMEM; + + gc->ngpio = ngpios; + gc->parent = dev; + ret = cros_ec_gpio_init_names(cros_ec, gc); + if (ret) + return ret; + + gc->can_sleep = true; + gc->label = dev_name(dev); + gc->base = -1; + gc->set = cros_ec_gpio_set; + gc->get = cros_ec_gpio_get; + gc->get_direction = cros_ec_gpio_get_direction; + + return devm_gpiochip_add_data(dev, gc, cros_ec); +} + +static struct platform_driver cros_ec_gpio_driver = { + .probe = cros_ec_gpio_probe, + .driver = { + .name = "cros-ec-gpio", + }, +}; +module_platform_driver(cros_ec_gpio_driver); + +MODULE_DESCRIPTION("ChromeOS EC GPIO Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 11498d99008fbe6a23e827773d9ee47728f23aa6 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 14 Feb 2024 11:45:06 +0100 Subject: gpio: sim: add lockdep asserts We have three functions in gpio-sim that are called with the device lock already held. We use the "_unlocked" suffix in their names to indicate that. This has proven to be confusing though as the naming convention in the kernel varies between using "_locked" or "_unlocked" for this purpose. Naming convention also doesn't enforce anything. Let's remove the suffix and add lockdep annotation at the top of these functions. This makes it clear the function requires a lock to be held (and which one specifically!) as well as results in a warning if it's not the case. The only place where the information is lost is the place where the function is called but the caller doesn't care about that information anyway. Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sim.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index c4106e37e6db..db42dc5616e4 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -697,8 +698,10 @@ static struct gpio_sim_device *gpio_sim_hog_get_device(struct gpio_sim_hog *hog) return gpio_sim_line_get_device(line); } -static bool gpio_sim_device_is_live_unlocked(struct gpio_sim_device *dev) +static bool gpio_sim_device_is_live(struct gpio_sim_device *dev) { + lockdep_assert_held(&dev->lock); + return !!dev->pdev; } @@ -737,7 +740,7 @@ gpio_sim_device_config_live_show(struct config_item *item, char *page) bool live; scoped_guard(mutex, &dev->lock) - live = gpio_sim_device_is_live_unlocked(dev); + live = gpio_sim_device_is_live(dev); return sprintf(page, "%c\n", live ? '1' : '0'); } @@ -926,7 +929,7 @@ static bool gpio_sim_bank_labels_non_unique(struct gpio_sim_device *dev) return false; } -static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev) +static int gpio_sim_device_activate(struct gpio_sim_device *dev) { struct platform_device_info pdevinfo; struct fwnode_handle *swnode; @@ -934,6 +937,8 @@ static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev) struct gpio_sim_bank *bank; int ret; + lockdep_assert_held(&dev->lock); + if (list_empty(&dev->bank_list)) return -ENODATA; @@ -998,10 +1003,12 @@ static int gpio_sim_device_activate_unlocked(struct gpio_sim_device *dev) return 0; } -static void gpio_sim_device_deactivate_unlocked(struct gpio_sim_device *dev) +static void gpio_sim_device_deactivate(struct gpio_sim_device *dev) { struct fwnode_handle *swnode; + lockdep_assert_held(&dev->lock); + swnode = dev_fwnode(&dev->pdev->dev); platform_device_unregister(dev->pdev); gpio_sim_remove_hogs(dev); @@ -1023,12 +1030,12 @@ gpio_sim_device_config_live_store(struct config_item *item, guard(mutex)(&dev->lock); - if (live == gpio_sim_device_is_live_unlocked(dev)) + if (live == gpio_sim_device_is_live(dev)) ret = -EPERM; else if (live) - ret = gpio_sim_device_activate_unlocked(dev); + ret = gpio_sim_device_activate(dev); else - gpio_sim_device_deactivate_unlocked(dev); + gpio_sim_device_deactivate(dev); return ret ?: count; } @@ -1069,7 +1076,7 @@ static ssize_t gpio_sim_bank_config_chip_name_show(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return device_for_each_child(&dev->pdev->dev, &ctx, gpio_sim_emit_chip_name); @@ -1098,7 +1105,7 @@ static ssize_t gpio_sim_bank_config_label_store(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return -EBUSY; trimmed = gpio_sim_strdup_trimmed(page, count); @@ -1142,7 +1149,7 @@ gpio_sim_bank_config_num_lines_store(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return -EBUSY; bank->num_lines = num_lines; @@ -1179,7 +1186,7 @@ static ssize_t gpio_sim_line_config_name_store(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return -EBUSY; trimmed = gpio_sim_strdup_trimmed(page, count); @@ -1219,7 +1226,7 @@ static ssize_t gpio_sim_hog_config_name_store(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return -EBUSY; trimmed = gpio_sim_strdup_trimmed(page, count); @@ -1274,7 +1281,7 @@ gpio_sim_hog_config_direction_store(struct config_item *item, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return -EBUSY; if (sysfs_streq(page, "input")) @@ -1392,7 +1399,7 @@ gpio_sim_bank_config_make_line_group(struct config_group *group, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return ERR_PTR(-EBUSY); line = kzalloc(sizeof(*line), GFP_KERNEL); @@ -1445,7 +1452,7 @@ gpio_sim_device_config_make_bank_group(struct config_group *group, guard(mutex)(&dev->lock); - if (gpio_sim_device_is_live_unlocked(dev)) + if (gpio_sim_device_is_live(dev)) return ERR_PTR(-EBUSY); bank = kzalloc(sizeof(*bank), GFP_KERNEL); @@ -1467,8 +1474,8 @@ static void gpio_sim_device_config_group_release(struct config_item *item) struct gpio_sim_device *dev = to_gpio_sim_device(item); scoped_guard(mutex, &dev->lock) { - if (gpio_sim_device_is_live_unlocked(dev)) - gpio_sim_device_deactivate_unlocked(dev); + if (gpio_sim_device_is_live(dev)) + gpio_sim_device_deactivate(dev); } mutex_destroy(&dev->lock); -- cgit v1.2.3 From 840a97e2fbaf5315f9705b97e99aa97e5e5fd6cb Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 20 Feb 2024 14:54:31 +0100 Subject: gpio: sim: delimit the fwnode name with a ":" when generating labels Typically, whenever a human-readable name is created for objects using a software node, its name is delimited with ":" as dashes are often used in other parts of the name. Make gpio-sim use the same pattern. This results in better looking default names: gpio-sim.0:node0 gpio-sim.0:node1 gpio-sim.1:node0 Signed-off-by: Bartosz Golaszewski Acked-by: Linus Walleij --- drivers/gpio/gpio-sim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index db42dc5616e4..a2706fe1340a 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -421,7 +421,7 @@ static int gpio_sim_add_bank(struct fwnode_handle *swnode, struct device *dev) ret = fwnode_property_read_string(swnode, "gpio-sim,label", &label); if (ret) { - label = devm_kasprintf(dev, GFP_KERNEL, "%s-%pfwP", + label = devm_kasprintf(dev, GFP_KERNEL, "%s:%pfwP", dev_name(dev), swnode); if (!label) return -ENOMEM; @@ -836,7 +836,7 @@ static int gpio_sim_add_hogs(struct gpio_sim_device *dev) GFP_KERNEL); else hog->chip_label = kasprintf(GFP_KERNEL, - "gpio-sim.%u-%pfwP", + "gpio-sim.%u:%pfwP", dev->id, bank->swnode); if (!hog->chip_label) { -- cgit v1.2.3 From 5d60c1e61fda628619605f5917aba62f071b7106 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 21 Feb 2024 11:21:03 +0100 Subject: gpio: don't warn about removing GPIO chips with active users anymore With SRCU we can now correctly handle the situation when a GPIO provider is removed while having users still holding references to GPIO descriptors. Remove all warnings emitted in this situation. Suggested-by: Kent Gibson Signed-off-by: Bartosz Golaszewski Reviewed-by: Herve Codina --- drivers/gpio/gpiolib.c | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3c22920bd201..63e793a410e3 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1106,7 +1106,6 @@ EXPORT_SYMBOL_GPL(gpiochip_add_data_with_key); void gpiochip_remove(struct gpio_chip *gc) { struct gpio_device *gdev = gc->gpiodev; - unsigned int i; /* FIXME: should the legacy sysfs handling be moved to gpio_device? */ gpiochip_sysfs_unregister(gdev); @@ -1130,15 +1129,6 @@ void gpiochip_remove(struct gpio_chip *gc) */ gpiochip_set_data(gc, NULL); - for (i = 0; i < gdev->ngpio; i++) { - if (test_bit(FLAG_REQUESTED, &gdev->descs[i].flags)) - break; - } - - if (i != gdev->ngpio) - dev_crit(&gdev->dev, - "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); - /* * The gpiochip side puts its use of the device to rest here: * if there are no userspace clients, the chardev and device will @@ -2329,10 +2319,9 @@ int gpiod_request(struct gpio_desc *desc, const char *label) return ret; } -static bool gpiod_free_commit(struct gpio_desc *desc) +static void gpiod_free_commit(struct gpio_desc *desc) { unsigned long flags; - bool ret = false; might_sleep(); @@ -2357,23 +2346,18 @@ static bool gpiod_free_commit(struct gpio_desc *desc) #ifdef CONFIG_OF_DYNAMIC WRITE_ONCE(desc->hog, NULL); #endif - ret = true; desc_set_label(desc, NULL); WRITE_ONCE(desc->flags, flags); gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED); } - - return ret; } void gpiod_free(struct gpio_desc *desc) { VALIDATE_DESC_VOID(desc); - if (!gpiod_free_commit(desc)) - WARN_ON(1); - + gpiod_free_commit(desc); module_put(desc->gdev->owner); gpio_device_put(desc->gdev); } -- cgit v1.2.3 From ebb03f692f5192ca2da554e97c8461ec7498d3bf Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 22 Feb 2024 11:25:13 +0100 Subject: gpio: sim: use for_each_hwgpio() Display debugfs information about all simulated GPIOs, not only the requested ones. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij --- drivers/gpio/gpio-sim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpio-sim.c b/drivers/gpio/gpio-sim.c index a2706fe1340a..2ed5cbe7c8a8 100644 --- a/drivers/gpio/gpio-sim.c +++ b/drivers/gpio/gpio-sim.c @@ -235,10 +235,10 @@ static void gpio_sim_dbg_show(struct seq_file *seq, struct gpio_chip *gc) guard(mutex)(&chip->lock); - for_each_requested_gpio(gc, i, label) + for_each_hwgpio(gc, i, label) seq_printf(seq, " gpio-%-3d (%s) %s,%s\n", gc->base + i, - label, + label ?: "", test_bit(i, chip->direction_map) ? "input" : test_bit(i, chip->value_map) ? "output-high" : "output-low", -- cgit v1.2.3 From 0d776cfd5e5b559fdf2e38285c2aea4b7048acbd Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 22 Feb 2024 22:52:53 -0800 Subject: gpiolib: Pass consumer device through to core in devm_fwnode_gpiod_get_index() This devm API takes a consumer device as an argument to setup the devm action, but throws it away when calling further into gpiolib. This leads to odd debug messages like this: (NULL device *): using DT '/gpio-keys/switch-pen-insert' for '(null)' GPIO lookup Let's pass the consumer device down, by directly calling what fwnode_gpiod_get_index() calls but pass the device used for devm. This changes the message to look like this instead: gpio-keys gpio-keys: using DT '/gpio-keys/switch-pen-insert' for '(null)' GPIO lookup Note that callers of fwnode_gpiod_get_index() will still see the NULL device pointer debug message, but there's not much we can do about that because the API doesn't take a struct device. Cc: Dmitry Torokhov Fixes: 8eb1f71e7acc ("gpiolib: consolidate GPIO lookups") Signed-off-by: Stephen Boyd Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-devres.c | 2 +- drivers/gpio/gpiolib.c | 14 +++++++------- drivers/gpio/gpiolib.h | 8 ++++++++ 3 files changed, 16 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index fe9ce6b19f15..4987e62dcb3d 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -158,7 +158,7 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, if (!dr) return ERR_PTR(-ENOMEM); - desc = fwnode_gpiod_get_index(fwnode, con_id, index, flags, label); + desc = gpiod_find_and_request(dev, fwnode, con_id, index, flags, label, false); if (IS_ERR(desc)) { devres_free(dr); return desc; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 63e793a410e3..3e2cd85673b6 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4171,13 +4171,13 @@ static struct gpio_desc *gpiod_find_by_fwnode(struct fwnode_handle *fwnode, return desc; } -static struct gpio_desc *gpiod_find_and_request(struct device *consumer, - struct fwnode_handle *fwnode, - const char *con_id, - unsigned int idx, - enum gpiod_flags flags, - const char *label, - bool platform_lookup_allowed) +struct gpio_desc *gpiod_find_and_request(struct device *consumer, + struct fwnode_handle *fwnode, + const char *con_id, + unsigned int idx, + enum gpiod_flags flags, + const char *label, + bool platform_lookup_allowed) { unsigned long lookupflags = GPIO_LOOKUP_FLAGS_DEFAULT; /* diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index ada36aa0f81a..f67d5991ab1c 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -223,6 +223,14 @@ static inline int gpiod_request_user(struct gpio_desc *desc, const char *label) return ret; } +struct gpio_desc *gpiod_find_and_request(struct device *consumer, + struct fwnode_handle *fwnode, + const char *con_id, + unsigned int idx, + enum gpiod_flags flags, + const char *label, + bool platform_lookup_allowed); + int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, unsigned long lflags, enum gpiod_flags dflags); int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce); -- cgit v1.2.3 From adcad5364a692dac85e6e741bdce2a7609449ab8 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 29 Feb 2024 16:51:38 +0200 Subject: gpio: of: Make of_gpio_get_count() take firmware node as a parameter Make of_gpio_get_count() take firmware node as a parameter in order to be aligned with other functions and decouple from unused device pointer. The latter helps to create a common fwnode_gpio_count() in the future. While at it, rename to be of_gpio_count() to be aligned with the others. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Reviewed-by: Mika Westerberg Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 14 +++++++------- drivers/gpio/gpiolib-of.h | 6 ++++-- drivers/gpio/gpiolib.c | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index e35a9c7da4ee..cb0cefaec37e 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -68,7 +68,7 @@ static int of_gpio_named_count(const struct device_node *np, /** * of_gpio_spi_cs_get_count() - special GPIO counting for SPI - * @dev: Consuming device + * @np: Consuming device node * @con_id: Function within the GPIO consumer * * Some elder GPIO controllers need special quirks. Currently we handle @@ -78,10 +78,9 @@ static int of_gpio_named_count(const struct device_node *np, * the counting of "cs-gpios" to count "gpios" transparent to the * driver. */ -static int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id) +static int of_gpio_spi_cs_get_count(const struct device_node *np, + const char *con_id) { - struct device_node *np = dev->of_node; - if (!IS_ENABLED(CONFIG_SPI_MASTER)) return 0; if (!con_id || strcmp(con_id, "cs")) @@ -93,13 +92,14 @@ static int of_gpio_spi_cs_get_count(struct device *dev, const char *con_id) return of_gpio_named_count(np, "gpios"); } -int of_gpio_get_count(struct device *dev, const char *con_id) +int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id) { + const struct device_node *np = to_of_node(fwnode); int ret; char propname[32]; unsigned int i; - ret = of_gpio_spi_cs_get_count(dev, con_id); + ret = of_gpio_spi_cs_get_count(np, con_id); if (ret > 0) return ret; @@ -111,7 +111,7 @@ int of_gpio_get_count(struct device *dev, const char *con_id) snprintf(propname, sizeof(propname), "%s", gpio_suffixes[i]); - ret = of_gpio_named_count(dev->of_node, propname); + ret = of_gpio_named_count(np, propname); if (ret > 0) break; } diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h index 6b3a5347c5d9..16d6ac8cb156 100644 --- a/drivers/gpio/gpiolib-of.h +++ b/drivers/gpio/gpiolib-of.h @@ -9,6 +9,7 @@ #include struct device; +struct fwnode_handle; struct gpio_chip; struct gpio_desc; @@ -21,7 +22,7 @@ struct gpio_desc *of_find_gpio(struct device_node *np, unsigned long *lookupflags); int of_gpiochip_add(struct gpio_chip *gc); void of_gpiochip_remove(struct gpio_chip *gc); -int of_gpio_get_count(struct device *dev, const char *con_id); +int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id); #else static inline struct gpio_desc *of_find_gpio(struct device_node *np, const char *con_id, @@ -32,7 +33,8 @@ static inline struct gpio_desc *of_find_gpio(struct device_node *np, } static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; } static inline void of_gpiochip_remove(struct gpio_chip *gc) { } -static inline int of_gpio_get_count(struct device *dev, const char *con_id) +static inline int of_gpio_count(const struct fwnode_handle *fwnode, + const char *con_id) { return 0; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3e2cd85673b6..01016b4c4674 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4285,7 +4285,7 @@ int gpiod_count(struct device *dev, const char *con_id) int count = -ENOENT; if (is_of_node(fwnode)) - count = of_gpio_get_count(dev, con_id); + count = of_gpio_count(fwnode, con_id); else if (is_acpi_node(fwnode)) count = acpi_gpio_count(dev, con_id); else if (is_software_node(fwnode)) -- cgit v1.2.3 From 8122c7c625fca4da427e85eac5f3261b6f6daeaa Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 29 Feb 2024 16:51:39 +0200 Subject: gpio: acpi: Make acpi_gpio_count() take firmware node as a parameter Make acpi_gpio_count() take firmware node as a parameter in order to be aligned with other functions and decouple from unused device pointer. The latter helps to create a common fwnode_gpio_count() in the future. Signed-off-by: Andy Shevchenko Reviewed-by: Linus Walleij Reviewed-by: Mika Westerberg Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi.c | 13 ++++++------- drivers/gpio/gpiolib-acpi.h | 5 +++-- drivers/gpio/gpiolib.c | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 899cd505073e..7f140df40f35 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1402,17 +1402,17 @@ static int acpi_find_gpio_count(struct acpi_resource *ares, void *data) } /** - * acpi_gpio_count - count the GPIOs associated with a device / function - * @dev: GPIO consumer, can be %NULL for system-global GPIOs + * acpi_gpio_count - count the GPIOs associated with a firmware node / function + * @fwnode: firmware node of the GPIO consumer * @con_id: function within the GPIO consumer * * Return: - * The number of GPIOs associated with a device / function or %-ENOENT, + * The number of GPIOs associated with a firmware node / function or %-ENOENT, * if no GPIO has been assigned to the requested function. */ -int acpi_gpio_count(struct device *dev, const char *con_id) +int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id) { - struct acpi_device *adev = ACPI_COMPANION(dev); + struct acpi_device *adev = to_acpi_device_node(fwnode); const union acpi_object *obj; const struct acpi_gpio_mapping *gm; int count = -ENOENT; @@ -1429,8 +1429,7 @@ int acpi_gpio_count(struct device *dev, const char *con_id) snprintf(propname, sizeof(propname), "%s", gpio_suffixes[i]); - ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, - &obj); + ret = acpi_dev_get_property(adev, propname, ACPI_TYPE_ANY, &obj); if (ret == 0) { if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) count = 1; diff --git a/drivers/gpio/gpiolib-acpi.h b/drivers/gpio/gpiolib-acpi.h index 0fcd7e14d7f9..7e1c51d04040 100644 --- a/drivers/gpio/gpiolib-acpi.h +++ b/drivers/gpio/gpiolib-acpi.h @@ -33,7 +33,7 @@ struct gpio_desc *acpi_find_gpio(struct fwnode_handle *fwnode, enum gpiod_flags *dflags, unsigned long *lookupflags); -int acpi_gpio_count(struct device *dev, const char *con_id); +int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id); #else static inline void acpi_gpiochip_add(struct gpio_chip *chip) { } static inline void acpi_gpiochip_remove(struct gpio_chip *chip) { } @@ -51,7 +51,8 @@ acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, { return ERR_PTR(-ENOENT); } -static inline int acpi_gpio_count(struct device *dev, const char *con_id) +static inline int acpi_gpio_count(const struct fwnode_handle *fwnode, + const char *con_id) { return -ENODEV; } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 01016b4c4674..6ea71b72c19a 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -4287,7 +4287,7 @@ int gpiod_count(struct device *dev, const char *con_id) if (is_of_node(fwnode)) count = of_gpio_count(fwnode, con_id); else if (is_acpi_node(fwnode)) - count = acpi_gpio_count(dev, con_id); + count = acpi_gpio_count(fwnode, con_id); else if (is_software_node(fwnode)) count = swnode_gpio_count(fwnode, con_id); -- cgit v1.2.3 From 8ae438f5ff168cd0e3ba6e3cef7240fa5d110528 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 4 Mar 2024 19:35:33 +0200 Subject: gpiolib: Deduplicate cleanup for-loop in gpiochip_add_data_with_key() There is no need to repeat for-loop twice in the error path in gpiochip_add_data_with_key(). Deduplicate it. While at it, rename loop variable to be more specific and avoid ambguity. It also properly unwinds the SRCU, i.e. in reversed order of allocating. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e2e583b40207..ce94e37bcbee 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -861,7 +861,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct lock_class_key *request_key) { struct gpio_device *gdev; - unsigned int i, j; + unsigned int desc_index; int base = 0; int ret = 0; @@ -965,8 +965,8 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, } } - for (i = 0; i < gc->ngpio; i++) - gdev->descs[i].gdev = gdev; + for (desc_index = 0; desc_index < gc->ngpio; desc_index++) + gdev->descs[desc_index].gdev = gdev; BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier); BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier); @@ -992,19 +992,16 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, if (ret) goto err_cleanup_gdev_srcu; - for (i = 0; i < gc->ngpio; i++) { - struct gpio_desc *desc = &gdev->descs[i]; + for (desc_index = 0; desc_index < gc->ngpio; desc_index++) { + struct gpio_desc *desc = &gdev->descs[desc_index]; ret = init_srcu_struct(&desc->srcu); - if (ret) { - for (j = 0; j < i; j++) - cleanup_srcu_struct(&gdev->descs[j].srcu); - goto err_free_gpiochip_mask; - } + if (ret) + goto err_cleanup_desc_srcu; - if (gc->get_direction && gpiochip_line_is_valid(gc, i)) { + if (gc->get_direction && gpiochip_line_is_valid(gc, desc_index)) { assign_bit(FLAG_IS_OUT, - &desc->flags, !gc->get_direction(gc, i)); + &desc->flags, !gc->get_direction(gc, desc_index)); } else { assign_bit(FLAG_IS_OUT, &desc->flags, !gc->direction_input); @@ -1061,9 +1058,8 @@ err_free_hogs: err_remove_of_chip: of_gpiochip_remove(gc); err_cleanup_desc_srcu: - for (i = 0; i < gdev->ngpio; i++) - cleanup_srcu_struct(&gdev->descs[i].srcu); -err_free_gpiochip_mask: + while (desc_index--) + cleanup_srcu_struct(&gdev->descs[desc_index].srcu); gpiochip_free_valid_mask(gc); err_cleanup_gdev_srcu: cleanup_srcu_struct(&gdev->srcu); -- cgit v1.2.3 From 8636f19c2d1f8199b27b4559d9caa115b3011f06 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Thu, 7 Mar 2024 22:43:16 +0100 Subject: gpio: sysfs: repair export returning -EPERM on 1st attempt It would make sense to return -EPERM if the bit was already set (already used), not if it was cleared. Before this fix pins can only be exported on the 2nd attempt: $ echo 522 > /sys/class/gpio/export sh: write error: Operation not permitted $ echo 522 > /sys/class/gpio/export Fixes: 35b545332b80 ("gpio: remove gpio_lock") Signed-off-by: Alexander Sverdlin Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 67fc09a57f26..6853ecd98bcb 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -593,7 +593,7 @@ int gpiod_export(struct gpio_desc *desc, bool direction_may_change) if (!guard.gc) return -ENODEV; - if (!test_and_set_bit(FLAG_EXPORT, &desc->flags)) + if (test_and_set_bit(FLAG_EXPORT, &desc->flags)) return -EPERM; gdev = desc->gdev; -- cgit v1.2.3