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

From: Bartosz Golaszewski

Date: Wed Oct 21 2026 - 10:30:23 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 | 35 ++++++++++++++++++++++++++---------
1 file changed, 26 insertions(+), 9 deletions(-)

diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index 47b434db72f1ebd35ec36ebdb35f30809f4522f2..043d5daba7a73062151a089b3ee8ccdac726f221 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -589,13 +589,25 @@ static int gpio_shared_setup_entry(struct gpio_chip *gc,
ref->con_id ?: "(none)");

ret = gpio_shared_make_adev(gdev, entry, ref);
- if (ret) {
- gpiod_free_commit(desc);
- return ret;
- }
+ if (ret)
+ goto err_out;
}

return 0;
+
+err_out:
+ gpiod_free_commit(desc);
+ list_for_each_entry_continue_reverse(ref, &entry->refs, list)
+ gpio_shared_remove_adev(&ref->adev);
+
+ return ret;
+}
+
+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)
@@ -624,18 +636,23 @@ 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)
- return ret;
+ goto err_unwind;
}

return 0;
+
+err_unwind:
+ 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;
}

void gpio_device_teardown_shared(struct gpio_device *gdev)

--
2.47.3