[PATCH v10 3/5] gpio: shared: unwind partial failures in gpiochip_setup_shared()

From: Bartosz Golaszewski

Date: Tue Nov 03 2026 - 08:16:35 EST


If gpio_shared_make_adev() fails for one of the references of a shared
GPIO entry, the auxiliary devices already registered for the preceding
references are never removed. Similarly, when setting up an entry fails,
the entries that were successfully set up before it keep their requested
descriptors and proxy devices, as the caller does not call
gpio_device_teardown_shared() when gpiochip_setup_shared() fails.

Make gpiochip_setup_shared() all-or-nothing. Have
gpio_shared_setup_entry() undo its own partial work: free the descriptor
and remove only the auxiliary devices of the references that precede the
failed one, so that no device is removed that was never registered. On
failure of an entry, tear down the previously completed entries in
reverse order using the same helper as the regular removal path.

Fixes: a060b8c511ab ("gpiolib: implement low-level, shared GPIO support")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@xxxxxxxxxxxxxxxx>
---
drivers/gpio/gpiolib-shared.c | 23 ++++++++++++++++++-----
1 file changed, 18 insertions(+), 5 deletions(-)

diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index c9500c2b904ca53dd1e3cfe1bdb921d767d18b3d..6b183864933870e54780715c4dd9179faf800d09 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -591,6 +591,9 @@ static int gpio_shared_setup_entry(struct gpio_chip *gc,
ret = gpio_shared_make_adev(gdev, entry, ref);
if (ret) {
gpiod_free_commit(desc);
+ list_for_each_entry_continue_reverse(ref, &entry->refs, list)
+ gpio_shared_remove_adev(&ref->adev);
+
return ret;
}
}
@@ -598,6 +601,13 @@ static int gpio_shared_setup_entry(struct gpio_chip *gc,
return 0;
}

+static bool gpio_shared_entry_needs_setup(struct gpio_device *gdev,
+ struct gpio_shared_entry *entry)
+{
+ return device_match_fwnode(&gdev->dev, entry->fwnode) &&
+ list_count_nodes(&entry->refs) > 1;
+}
+
int gpiochip_setup_shared(struct gpio_chip *gc)
{
struct gpio_device *gdev = gc->gpiodev;
@@ -624,15 +634,18 @@ int gpiochip_setup_shared(struct gpio_chip *gc)
* exposing shared pins. Find them and create the proxy devices.
*/
list_for_each_entry(entry, &gpio_shared_list, list) {
- if (!device_match_fwnode(&gdev->dev, entry->fwnode))
- continue;
-
- if (list_count_nodes(&entry->refs) <= 1)
+ if (!gpio_shared_entry_needs_setup(gdev, entry))
continue;

ret = gpio_shared_setup_entry(gc, entry);
- if (ret)
+ if (ret) {
+ list_for_each_entry_continue_reverse(entry, &gpio_shared_list, list) {
+ if (gpio_shared_entry_needs_setup(gdev, entry))
+ gpio_shared_teardown_entry(gdev, entry);
+ }
+
return ret;
+ }
}

return 0;

--
2.47.3