From c8fb5a52b977ef91dc9342e556c63b9602065c8a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 12 Jan 2024 09:55:25 +0300 Subject: [PATCH 1/6] gpio: rtd: Fix signedness bug in probe The "data->irqs[]" array holds unsigned int so this error handling will not work correctly. Fixes: eee636bff0dc ("gpio: rtd: Add support for Realtek DHC(Digital Home Center) RTD SoCs") Signed-off-by: Dan Carpenter Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rtd.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-rtd.c b/drivers/gpio/gpio-rtd.c index a7939bd0aa56..bf7f008f58d7 100644 --- a/drivers/gpio/gpio-rtd.c +++ b/drivers/gpio/gpio-rtd.c @@ -525,18 +525,21 @@ static int rtd_gpio_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct gpio_irq_chip *irq_chip; struct rtd_gpio *data; + int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; - data->irqs[0] = platform_get_irq(pdev, 0); - if (data->irqs[0] < 0) - return data->irqs[0]; + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return ret; + data->irqs[0] = ret; - data->irqs[1] = platform_get_irq(pdev, 1); - if (data->irqs[1] < 0) - return data->irqs[1]; + ret = platform_get_irq(pdev, 1); + if (ret < 0) + return ret; + data->irqs[1] = ret; data->info = device_get_match_data(dev); if (!data->info) From 314c020c4ed3de72b15603eb6892250bc4b51702 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Fri, 12 Jan 2024 12:32:58 +0100 Subject: [PATCH 2/6] dt-bindings: gpio: xilinx: Fix node address in gpio Node address doesn't match reg property which is not correct. Fixes: ba96b2e7974b ("dt-bindings: gpio: gpio-xilinx: Convert Xilinx axi gpio binding to YAML") Signed-off-by: Michal Simek Reviewed-by: Krzysztof Kozlowski Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml b/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml index c1060e5fcef3..d3d8a2e143ed 100644 --- a/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml +++ b/Documentation/devicetree/bindings/gpio/xlnx,gpio-xilinx.yaml @@ -126,7 +126,7 @@ examples: - | #include - gpio@e000a000 { + gpio@a0020000 { compatible = "xlnx,xps-gpio-1.00.a"; reg = <0xa0020000 0x10000>; #gpio-cells = <2>; From d460e9c2075164e9b1fa9c4c95f8c05517bd8752 Mon Sep 17 00:00:00 2001 From: Su Hui Date: Fri, 12 Jan 2024 12:24:04 +0800 Subject: [PATCH 3/6] gpio: mlxbf3: add an error code check in mlxbf3_gpio_probe Clang static checker warning: Value stored to 'ret' is never read. bgpio_init() returns error code if failed, it's better to add this check. Fixes: cd33f216d241 ("gpio: mlxbf3: Add gpio driver support") Signed-off-by: Su Hui [Bartosz: add the Fixes: tag] Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mlxbf3.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c index 7a3e1760fc5b..d5906d419b0a 100644 --- a/drivers/gpio/gpio-mlxbf3.c +++ b/drivers/gpio/gpio-mlxbf3.c @@ -215,6 +215,8 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev) gs->gpio_clr_io + MLXBF_GPIO_FW_DATA_OUT_CLEAR, gs->gpio_set_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_SET, gs->gpio_clr_io + MLXBF_GPIO_FW_OUTPUT_ENABLE_CLEAR, 0); + if (ret) + return dev_err_probe(dev, ret, "%s: bgpio_init() failed", __func__); gc->request = gpiochip_generic_request; gc->free = gpiochip_generic_free; From 832b371097eb928d077c827b8f117bf5b99d35c0 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Mon, 15 Jan 2024 16:05:26 +0100 Subject: [PATCH 4/6] gpiolib: Fix scope-based gpio_device refcounting Commit 9e4555d1e54a ("gpiolib: add support for scope-based management to gpio_device") sought to add scope-based gpio_device refcounting, but erroneously forgot a negation of IS_ERR_OR_NULL(). As a result, gpio_device_put() is not called if the gpio_device pointer is valid (meaning the ref is leaked), but only called if the pointer is NULL or an ERR_PTR(). While at it drop a superfluous trailing semicolon. Fixes: 9e4555d1e54a ("gpiolib: add support for scope-based management to gpio_device") Signed-off-by: Lukas Wunner Signed-off-by: Bartosz Golaszewski --- include/linux/gpio/driver.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index e846bd4e7559..9a5c6c76e653 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -635,7 +635,7 @@ struct gpio_device *gpio_device_get(struct gpio_device *gdev); void gpio_device_put(struct gpio_device *gdev); DEFINE_FREE(gpio_device_put, struct gpio_device *, - if (IS_ERR_OR_NULL(_T)) gpio_device_put(_T)); + if (!IS_ERR_OR_NULL(_T)) gpio_device_put(_T)) struct device *gpio_device_to_device(struct gpio_device *gdev); From 62b38f30a00ff47142e58d0a6d60dd296e0e8108 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 15 Jan 2024 10:56:47 -0800 Subject: [PATCH 5/6] gpio: EN7523: fix kernel-doc warnings Add "struct" keyword and explain the @dir array differently to prevent kernel-doc warnings: gpio-en7523.c:22: warning: cannot understand function prototype: 'struct airoha_gpio_ctrl ' gpio-en7523.c:27: warning: Function parameter or struct member 'dir' not described in 'airoha_gpio_ctrl' gpio-en7523.c:27: warning: Excess struct member 'dir0' description in 'airoha_gpio_ctrl' gpio-en7523.c:27: warning: Excess struct member 'dir1' description in 'airoha_gpio_ctrl' Fixes: 0868ad385aff ("gpio: Add support for Airoha EN7523 GPIO controller") Signed-off-by: Randy Dunlap Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-en7523.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-en7523.c b/drivers/gpio/gpio-en7523.c index f836a8db4c1d..69834db2c1cf 100644 --- a/drivers/gpio/gpio-en7523.c +++ b/drivers/gpio/gpio-en7523.c @@ -12,11 +12,11 @@ #define AIROHA_GPIO_MAX 32 /** - * airoha_gpio_ctrl - Airoha GPIO driver data + * struct airoha_gpio_ctrl - Airoha GPIO driver data * @gc: Associated gpio_chip instance. * @data: The data register. - * @dir0: The direction register for the lower 16 pins. - * @dir1: The direction register for the higher 16 pins. + * @dir: [0] The direction register for the lower 16 pins. + * [1]: The direction register for the higher 16 pins. * @output: The output enable register. */ struct airoha_gpio_ctrl { From efb8235bfdbe661c460f803150b50840a73b5f03 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 15 Jan 2024 12:17:43 +0100 Subject: [PATCH 6/6] gpiolib: revert the attempt to protect the GPIO device list with an rwsem This reverts commits 1979a2807547 ("gpiolib: replace the GPIO device mutex with a read-write semaphore") and 65a828bab158 ("gpiolib: use a mutex to protect the list of GPIO devices"). Unfortunately the legacy GPIO API that's still used in older code has to translate numbers from the global GPIO numberspace to descriptors. This results in a GPIO device lookup in every call to legacy functions. Some of those functions - like gpio_set/get_value() - can be called from atomic context so taking a sleeping lock that is an RW semaphore results in an error. We'll probably have to protect this list with SRCU. Reported-by: Dan Carpenter Closes: https://lore.kernel.org/linux-wireless/f7b5ff1e-8f34-4d98-a7be-b826cb897dc8@moroto.mountain/ Fixes: 1979a2807547 ("gpiolib: replace the GPIO device mutex with a read-write semaphore") Fixes: 65a828bab158 ("gpiolib: use a mutex to protect the list of GPIO devices") Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 45 ++++++------ drivers/gpio/gpiolib-sysfs.h | 6 -- drivers/gpio/gpiolib.c | 135 +++++++++++++++++++---------------- drivers/gpio/gpiolib.h | 2 - 4 files changed, 98 insertions(+), 90 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 4dbf298bb5dd..6bf5332136e5 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -768,25 +768,6 @@ int gpiochip_sysfs_register(struct gpio_device *gdev) return 0; } -int gpiochip_sysfs_register_all(void) -{ - struct gpio_device *gdev; - int ret; - - guard(rwsem_read)(&gpio_devices_sem); - - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->mockdev) - continue; - - ret = gpiochip_sysfs_register(gdev); - if (ret) - return ret; - } - - return 0; -} - void gpiochip_sysfs_unregister(struct gpio_device *gdev) { struct gpio_desc *desc; @@ -811,7 +792,9 @@ void gpiochip_sysfs_unregister(struct gpio_device *gdev) static int __init gpiolib_sysfs_init(void) { - int status; + int status; + unsigned long flags; + struct gpio_device *gdev; status = class_register(&gpio_class); if (status < 0) @@ -823,6 +806,26 @@ 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(). */ - return gpiochip_sysfs_register_all(); + 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); + + return status; } postcore_initcall(gpiolib_sysfs_init); diff --git a/drivers/gpio/gpiolib-sysfs.h b/drivers/gpio/gpiolib-sysfs.h index ab157cec0b4b..b794b396d6a5 100644 --- a/drivers/gpio/gpiolib-sysfs.h +++ b/drivers/gpio/gpiolib-sysfs.h @@ -8,7 +8,6 @@ struct gpio_device; #ifdef CONFIG_GPIO_SYSFS int gpiochip_sysfs_register(struct gpio_device *gdev); -int gpiochip_sysfs_register_all(void); void gpiochip_sysfs_unregister(struct gpio_device *gdev); #else @@ -18,11 +17,6 @@ static inline int gpiochip_sysfs_register(struct gpio_device *gdev) return 0; } -static inline int gpiochip_sysfs_register_all(void) -{ - return 0; -} - static inline void gpiochip_sysfs_unregister(struct gpio_device *gdev) { } diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 4c93cf73a826..44c8f5743a24 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2,7 +2,6 @@ #include #include -#include #include #include #include @@ -16,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -83,9 +81,7 @@ DEFINE_SPINLOCK(gpio_lock); static DEFINE_MUTEX(gpio_lookup_lock); static LIST_HEAD(gpio_lookup_list); - LIST_HEAD(gpio_devices); -DECLARE_RWSEM(gpio_devices_sem); static DEFINE_MUTEX(gpio_machine_hogs_mutex); static LIST_HEAD(gpio_machine_hogs); @@ -117,15 +113,20 @@ 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; - scoped_guard(rwsem_read, &gpio_devices_sem) { - list_for_each_entry(gdev, &gpio_devices, list) { - if (gdev->base <= gpio && - gdev->base + gdev->ngpio > gpio) - return &gdev->descs[gpio - gdev->base]; + 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]; } } + spin_unlock_irqrestore(&gpio_lock, flags); + if (!gpio_is_valid(gpio)) pr_warn("invalid GPIO %d\n", gpio); @@ -398,21 +399,26 @@ 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; if (!name) return NULL; - guard(rwsem_read)(&gpio_devices_sem); + spin_lock_irqsave(&gpio_lock, flags); list_for_each_entry(gdev, &gpio_devices, list) { struct gpio_desc *desc; for_each_gpio_desc(gdev->chip, desc) { - if (desc->name && !strcmp(desc->name, name)) + if (desc->name && !strcmp(desc->name, name)) { + spin_unlock_irqrestore(&gpio_lock, flags); return desc; + } } } + spin_unlock_irqrestore(&gpio_lock, flags); + return NULL; } @@ -807,6 +813,7 @@ 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; @@ -871,45 +878,48 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, gdev->ngpio = gc->ngpio; - scoped_guard(rwsem_write, &gpio_devices_sem) { - /* - * 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; + 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) { - base = gpiochip_find_base_unlocked(gc->ngpio); - if (base < 0) { - ret = base; - base = 0; - 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"); + spin_unlock_irqrestore(&gpio_lock, flags); + ret = base; + base = 0; goto err_free_label; } - - for (i = 0; i < gc->ngpio; i++) - gdev->descs[i].gdev = gdev; + /* + * 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) { + spin_unlock_irqrestore(&gpio_lock, flags); + 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); @@ -1001,8 +1011,9 @@ err_free_gpiochip_mask: goto err_print_message; } err_remove_from_list: - scoped_guard(rwsem_write, &gpio_devices_sem) - list_del(&gdev->list); + spin_lock_irqsave(&gpio_lock, flags); + list_del(&gdev->list); + spin_unlock_irqrestore(&gpio_lock, flags); err_free_label: kfree_const(gdev->label); err_free_descs: @@ -1065,7 +1076,7 @@ void gpiochip_remove(struct gpio_chip *gc) dev_crit(&gdev->dev, "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n"); - scoped_guard(rwsem_write, &gpio_devices_sem) + scoped_guard(spinlock_irqsave, &gpio_lock) list_del(&gdev->list); /* @@ -1114,7 +1125,7 @@ struct gpio_device *gpio_device_find(void *data, */ might_sleep(); - guard(rwsem_read)(&gpio_devices_sem); + guard(spinlock_irqsave)(&gpio_lock); list_for_each_entry(gdev, &gpio_devices, list) { if (gdev->chip && match(gdev->chip, data)) @@ -4725,33 +4736,35 @@ static void gpiolib_dbg_show(struct seq_file *s, struct gpio_device *gdev) static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos) { + unsigned long flags; struct gpio_device *gdev = NULL; loff_t index = *pos; s->private = ""; - guard(rwsem_read)(&gpio_devices_sem); - - list_for_each_entry(gdev, &gpio_devices, list) { - if (index-- == 0) + spin_lock_irqsave(&gpio_lock, flags); + list_for_each_entry(gdev, &gpio_devices, list) + if (index-- == 0) { + spin_unlock_irqrestore(&gpio_lock, flags); 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; - scoped_guard(rwsem_read, &gpio_devices_sem) { - if (list_is_last(&gdev->list, &gpio_devices)) - ret = NULL; - else - ret = list_first_entry(&gdev->list, struct gpio_device, - list); - } + 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"; ++*pos; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 97df54abf57a..a4a2520b5f31 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -15,7 +15,6 @@ #include /* for enum gpiod_flags */ #include #include -#include #include #include @@ -137,7 +136,6 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); extern spinlock_t gpio_lock; extern struct list_head gpio_devices; -extern struct rw_semaphore gpio_devices_sem; void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);