]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
gpio: shared: shorten the critical section in gpiochip_setup_shared()
authorBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Wed, 25 Mar 2026 11:06:38 +0000 (12:06 +0100)
committerBartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Mon, 30 Mar 2026 07:51:15 +0000 (09:51 +0200)
Commit 710abda58055 ("gpio: shared: call gpio_chip::of_xlate() if set")
introduced a critical section around the adjustmenet of entry->offset.
However this may cause a deadlock if we create the auxiliary shared
proxy devices with this lock taken. We only need to protect
entry->offset while it's read/written so shorten the critical section
and release the lock before creating the proxy device as the field in
question is no longer accessed at this point.

Fixes: 710abda58055 ("gpio: shared: call gpio_chip::of_xlate() if set")
Reported-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Link: https://patch.msgid.link/20260325-gpio-shared-deadlock-v1-1-e4e7a5319e95@oss.qualcomm.com
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
drivers/gpio/gpiolib-shared.c

index e257212fa5e3df249de0d06eebdb2165ae734ebc..e02d6b93a4ab42b197f0fd64e4854a303f54f140 100644 (file)
@@ -533,48 +533,48 @@ 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) {
-               guard(mutex)(&entry->lock);
-
                if (!device_match_fwnode(&gdev->dev, entry->fwnode))
                        continue;
 
                if (list_count_nodes(&entry->refs) <= 1)
                        continue;
 
+               scoped_guard(mutex, &entry->lock) {
 #if IS_ENABLED(CONFIG_OF)
-               if (is_of_node(entry->fwnode) && gc->of_xlate) {
-                       /*
-                        * This is the earliest that we can tranlate the
-                        * devicetree offset to the chip offset.
-                        */
-                       struct of_phandle_args gpiospec = { };
+                       if (is_of_node(entry->fwnode) && gc->of_xlate) {
+                               /*
+                                * This is the earliest that we can tranlate the
+                                * devicetree offset to the chip offset.
+                                */
+                               struct of_phandle_args gpiospec = { };
 
-                       gpiospec.np = to_of_node(entry->fwnode);
-                       gpiospec.args_count = 2;
-                       gpiospec.args[0] = entry->offset;
+                               gpiospec.np = to_of_node(entry->fwnode);
+                               gpiospec.args_count = 2;
+                               gpiospec.args[0] = entry->offset;
 
-                       ret = gc->of_xlate(gc, &gpiospec, NULL);
-                       if (ret < 0)
-                               return ret;
+                               ret = gc->of_xlate(gc, &gpiospec, NULL);
+                               if (ret < 0)
+                                       return ret;
 
-                       entry->offset = ret;
-               }
+                               entry->offset = ret;
+                       }
 #endif /* CONFIG_OF */
 
-               desc = &gdev->descs[entry->offset];
+                       desc = &gdev->descs[entry->offset];
 
-               __set_bit(GPIOD_FLAG_SHARED, &desc->flags);
-               /*
-                * Shared GPIOs are not requested via the normal path. Make
-                * them inaccessible to anyone even before we register the
-                * chip.
-                */
-               ret = gpiod_request_commit(desc, "shared");
-               if (ret)
-                       return ret;
+                       __set_bit(GPIOD_FLAG_SHARED, &desc->flags);
+                       /*
+                        * Shared GPIOs are not requested via the normal path. Make
+                        * them inaccessible to anyone even before we register the
+                        * chip.
+                        */
+                       ret = gpiod_request_commit(desc, "shared");
+                       if (ret)
+                               return ret;
 
-               pr_debug("GPIO %u owned by %s is shared by multiple consumers\n",
-                        entry->offset, gpio_device_get_label(gdev));
+                       pr_debug("GPIO %u owned by %s is shared by multiple consumers\n",
+                                entry->offset, gpio_device_get_label(gdev));
+               }
 
                list_for_each_entry(ref, &entry->refs, list) {
                        pr_debug("Setting up a shared GPIO entry for %s (con_id: '%s')\n",