]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
gpio: shared: fix deadlock on shared proxy's parent removal
authorBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Fri, 22 May 2026 09:12:36 +0000 (11:12 +0200)
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Thu, 28 May 2026 13:23:39 +0000 (15:23 +0200)
Commit 710abda58055 ("gpio: shared: call gpio_chip::of_xlate() if set")
used the mutex embedded in struct gpio_shared_entry to protect the
offset field which now can be modified after assignment. The critical
section however is too wide and introduced a potential deadlock on the
removal of the shared GPIO proxy's parent.

Make the critical section shorter - only protect the offset when it's
being read.

While at it: mention the fact that the entry lock is now also used to
protect against concurrent access to the offset field in the structure's
documentation.

Cc: stable@vger.kernel.org
Fixes: 710abda58055 ("gpio: shared: call gpio_chip::of_xlate() if set")
Reviewed-by: Linus Walleij <linusw@kernel.org>
Link: https://patch.msgid.link/20260522-gpio-shared-deadlock-v1-1-76bca088f8c0@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
drivers/gpio/gpiolib-shared.c

index e02d6b93a4ab42b197f0fd64e4854a303f54f140..087b64c06c9f42b698abe5741e63102538beb488 100644 (file)
@@ -53,7 +53,7 @@ struct gpio_shared_entry {
        unsigned int offset;
        /* Index in the property value array. */
        size_t index;
-       /* Synchronizes the modification of shared_desc. */
+       /* Synchronizes the modification of shared_desc and offset. */
        struct mutex lock;
        struct gpio_shared_desc *shared_desc;
        struct kref ref;
@@ -598,12 +598,11 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
        struct gpio_shared_ref *ref;
 
        list_for_each_entry(entry, &gpio_shared_list, list) {
-               guard(mutex)(&entry->lock);
-
                if (!device_match_fwnode(&gdev->dev, entry->fwnode))
                        continue;
 
-               gpiod_free_commit(&gdev->descs[entry->offset]);
+               scoped_guard(mutex, &entry->lock)
+                       gpiod_free_commit(&gdev->descs[entry->offset]);
 
                list_for_each_entry(ref, &entry->refs, list) {
                        guard(mutex)(&ref->lock);