Skip to contentSkip to conversation

gpiolib: use a read-write semaphore to protect the GPIO device list

PATCH In review Bartosz Golaszewski 3 participants 10 messages Jan 2, 2024 – Jan 8, 2024 GPIO

lore
Share & tools
lore
#0 lore raw

v2 cover gpiolib: use a read-write semaphore to protect the GPIO device list

From: Bartosz Golaszewski <[email protected]>

I'm still figuring out how to keep GPIO descriptors coherent while (mostly) lockless. In the meantime, I found a potential race-condition during GPIO descriptor lookup and also figured that the correct way to protect the GPIO device list is actually a read-write semaphore as we're not modifying the list very often and readers should be able to iterate over it concurrently.

The first patch in this series is new in v2. I realized that we must not wait until .release() to remove the GPIO device from the list as this is why pinning down the GPIO device list during lookup would never work - we always could end up re-taking a reference to an object that was being released if it got looked up between when the last reference is dropped and the object is finally removed from the device list.

v1 -> v2:
- add patch 1/3 to fix a release timing issue

Bartosz Golaszewski (3):
gpiolib: remove the GPIO device from the list when it's unregistered
gpiolib: replace the GPIO device mutex with a read-write semaphore
gpiolib: pin GPIO devices in place during descriptor lookup

 drivers/gpio/gpiolib-sysfs.c |  2 +-
 drivers/gpio/gpiolib.c       | 62 ++++++++++++++++++++----------------
 drivers/gpio/gpiolib.h       |  2 +-
 3 files changed, 36 insertions(+), 30 deletions(-)
Signature · 1 line
2.40.1
#1 lore raw

PATCH v2 1/3 gpiolib: remove the GPIO device from the list when it's unregistered

From: Bartosz Golaszewski <[email protected]>

If we wait until the GPIO device's .release() callback gets invoked before we remove it from the global device list, then we risk that someone will look it up using gpio_device_find() between where we dropped the last reference and before .release() is done taking a reference again to an object that's being released.

The device must be removed when it's being unregistered - just like how we remove it from the GPIO bus.

Fixes ff2b13592299 ("gpio: make the gpiochip a real device")
---
 drivers/gpio/gpiolib.c | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)
drivers/gpio/gpiolib.c +3 -3
index e21497b989a1..e019c4243809 100644
@@ -651,9 +651,6 @@ static void gpiodev_release(struct device *dev)
 {
 	struct gpio_device *gdev = to_gpio_device(dev);
 
-	scoped_guard(mutex, &gpio_devices_lock)
-		list_del(&gdev->list);
-
 	ida_free(&gpio_ida, gdev->id);
 	kfree_const(gdev->label);
 	kfree(gdev->descs);
@@ -1068,6 +1065,9 @@ void gpiochip_remove(struct gpio_chip *gc)
 		dev_crit(&gdev->dev,
 			 "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
 
+	scoped_guard(mutex, &gpio_devices_lock)
+		list_del(&gdev->list);
+
 	/*
 	 * The gpiochip side puts its use of the device to rest here:
 	 * if there are no userspace clients, the chardev and device will
Signature · 1 line
2.40.1
Reviewed-by on v2 1/3
#2 lore raw

Re: [PATCH v2 1/3] gpiolib: remove the GPIO device from the list when it's unregistered

Quoted 8 lines · Bartosz Golaszewski

From: Bartosz Golaszewski <[email protected]>

If we wait until the GPIO device's .release() callback gets invoked before we remove it from the global device list, then we risk that someone will look it up using gpio_device_find() between where we dropped the last reference and before .release() is done taking a reference again to an object that's being released.

Quoted 5 lines

The device must be removed when it's being unregistered - just like how we remove it from the GPIO bus.

Fixes ff2b13592299 ("gpio: make the gpiochip a real device")

Makes sense!

Yours,
Linus Walleij

#3 lore raw

PATCH v2 2/3 gpiolib: replace the GPIO device mutex with a read-write semaphore

In reply to Bartosz Golaszewski

From: Bartosz Golaszewski <[email protected]>

There are only two spots where we modify (add to or remove objects from) the GPIO device list. Readers should be able to access it concurrently. Replace the mutex with a read-write semaphore and adjust the locking operations accordingly.

---
 drivers/gpio/gpiolib-sysfs.c |  2 +-
 drivers/gpio/gpiolib.c       | 18 +++++++++---------
 drivers/gpio/gpiolib.h       |  2 +-
 3 files changed, 11 insertions(+), 11 deletions(-)
drivers/gpio/gpiolib-sysfs.c +1 -1
index 2d29ae37d953..4dbf298bb5dd 100644
@@ -773,7 +773,7 @@ int gpiochip_sysfs_register_all(void)
 	struct gpio_device *gdev;
 	int ret;
 
-	guard(mutex)(&gpio_devices_lock);
+	guard(rwsem_read)(&gpio_devices_sem);
 
 	list_for_each_entry(gdev, &gpio_devices, list) {
 		if (gdev->mockdev)
drivers/gpio/gpiolib.c +9 -9
index e019c4243809..4c93cf73a826 100644
@@ -85,7 +85,7 @@ static DEFINE_MUTEX(gpio_lookup_lock);
 static LIST_HEAD(gpio_lookup_list);
 
 LIST_HEAD(gpio_devices);
-DEFINE_MUTEX(gpio_devices_lock);
+DECLARE_RWSEM(gpio_devices_sem);
 
 static DEFINE_MUTEX(gpio_machine_hogs_mutex);
 static LIST_HEAD(gpio_machine_hogs);
@@ -118,7 +118,7 @@ struct gpio_desc *gpio_to_desc(unsigned gpio)
 {
 	struct gpio_device *gdev;
 
-	scoped_guard(mutex, &gpio_devices_lock) {
+	scoped_guard(rwsem_read, &gpio_devices_sem) {
 		list_for_each_entry(gdev, &gpio_devices, list) {
 			if (gdev->base <= gpio &&
 			    gdev->base + gdev->ngpio > gpio)
@@ -402,7 +402,7 @@ static struct gpio_desc *gpio_name_to_desc(const char * const name)
 	if (!name)
 		return NULL;
 
-	guard(mutex)(&gpio_devices_lock);
+	guard(rwsem_read)(&gpio_devices_sem);
 
 	list_for_each_entry(gdev, &gpio_devices, list) {
 		struct gpio_desc *desc;
@@ -871,7 +871,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
 
 	gdev->ngpio = gc->ngpio;
 
-	scoped_guard(mutex, &gpio_devices_lock) {
+	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
@@ -1001,7 +1001,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
 		goto err_print_message;
 	}
 err_remove_from_list:
-	scoped_guard(mutex, &gpio_devices_lock)
+	scoped_guard(rwsem_write, &gpio_devices_sem)
 		list_del(&gdev->list);
 err_free_label:
 	kfree_const(gdev->label);
@@ -1065,7 +1065,7 @@ void gpiochip_remove(struct gpio_chip *gc)
 		dev_crit(&gdev->dev,
 			 "REMOVING GPIOCHIP WITH GPIOS STILL REQUESTED\n");
 
-	scoped_guard(mutex, &gpio_devices_lock)
+	scoped_guard(rwsem_write, &gpio_devices_sem)
 		list_del(&gdev->list);
 
 	/*
@@ -1114,7 +1114,7 @@ struct gpio_device *gpio_device_find(void *data,
 	 */
 	might_sleep();
 
-	guard(mutex)(&gpio_devices_lock);
+	guard(rwsem_read)(&gpio_devices_sem);
 
 	list_for_each_entry(gdev, &gpio_devices, list) {
 		if (gdev->chip && match(gdev->chip, data))
@@ -4730,7 +4730,7 @@ static void *gpiolib_seq_start(struct seq_file *s, loff_t *pos)
 
 	s->private = "";
 
-	guard(mutex)(&gpio_devices_lock);
+	guard(rwsem_read)(&gpio_devices_sem);
 
 	list_for_each_entry(gdev, &gpio_devices, list) {
 		if (index-- == 0)
@@ -4745,7 +4745,7 @@ static void *gpiolib_seq_next(struct seq_file *s, void *v, loff_t *pos)
 	struct gpio_device *gdev = v;
 	void *ret = NULL;
 
-	scoped_guard(mutex, &gpio_devices_lock) {
+	scoped_guard(rwsem_read, &gpio_devices_sem) {
 		if (list_is_last(&gdev->list, &gpio_devices))
 			ret = NULL;
 		else
drivers/gpio/gpiolib.h +1 -1
index 0ce7451a6b24..97df54abf57a 100644
@@ -137,7 +137,7 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory);
 
 extern spinlock_t gpio_lock;
 extern struct list_head gpio_devices;
-extern struct mutex gpio_devices_lock;
+extern struct rw_semaphore gpio_devices_sem;
 
 void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action);
 
Signature · 1 line
2.40.1
Reviewed-by on v2 2/3
#4 lore raw

Re: [PATCH v2 2/3] gpiolib: replace the GPIO device mutex with a read-write semaphore

Quoted 8 lines · Bartosz Golaszewski

From: Bartosz Golaszewski <[email protected]>

There are only two spots where we modify (add to or remove objects from) the GPIO device list. Readers should be able to access it concurrently. Replace the mutex with a read-write semaphore and adjust the locking operations accordingly.

Looks correct to me.

Yours,
Linus Walleij

#5 lore raw

PATCH v2 3/3 gpiolib: pin GPIO devices in place during descriptor lookup

In reply to Bartosz Golaszewski

From: Bartosz Golaszewski <[email protected]>

There's time between when we locate the relevant descriptor during lookup and when we actually take the reference to its parent GPIO device where - if the GPIO device in question is removed - we'll end up with a dangling pointer to freed memory. Make sure devices cannot be removed until we hold a new reference to the device.

---
 drivers/gpio/gpiolib.c | 40 +++++++++++++++++++++++-----------------
 1 file changed, 23 insertions(+), 17 deletions(-)
drivers/gpio/gpiolib.c +23 -17
index 4c93cf73a826..be57f8d6aeae 100644
@@ -4134,27 +4134,33 @@ static struct gpio_desc *gpiod_find_and_request(struct device *consumer,
 	struct gpio_desc *desc;
 	int ret;
 
-	desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx, &flags, &lookupflags);
-	if (gpiod_not_found(desc) && platform_lookup_allowed) {
+	scoped_guard(rwsem_read, &gpio_devices_sem) {
+		desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx,
+					    &flags, &lookupflags);
+		if (gpiod_not_found(desc) && platform_lookup_allowed) {
+			/*
+			 * Either we are not using DT or ACPI, or their lookup
+			 * did not return a result. In that case, use platform
+			 * lookup as a fallback.
+			 */
+			dev_dbg(consumer,
+				"using lookup tables for GPIO lookup\n");
+			desc = gpiod_find(consumer, con_id, idx, &lookupflags);
+		}
+
+		if (IS_ERR(desc)) {
+			dev_dbg(consumer, "No GPIO consumer %s found\n",
+				con_id);
+			return desc;
+		}
+
 		/*
-		 * Either we are not using DT or ACPI, or their lookup did not
-		 * return a result. In that case, use platform lookup as a
-		 * fallback.
+		 * If a connection label was passed use that, else attempt to
+		 * use the device name as label
 		 */
-		dev_dbg(consumer, "using lookup tables for GPIO lookup\n");
-		desc = gpiod_find(consumer, con_id, idx, &lookupflags);
+		ret = gpiod_request(desc, label);
 	}
 
-	if (IS_ERR(desc)) {
-		dev_dbg(consumer, "No GPIO consumer %s found\n", con_id);
-		return desc;
-	}
-
-	/*
-	 * If a connection label was passed use that, else attempt to use
-	 * the device name as label
-	 */
-	ret = gpiod_request(desc, label);
 	if (ret) {
 		if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
 			return ERR_PTR(ret);
Signature · 1 line
2.40.1
Reviewed-by on v2 3/3
#6 lore raw

Re: [PATCH v2 3/3] gpiolib: pin GPIO devices in place during descriptor lookup

Quoted 9 lines · Bartosz Golaszewski

From: Bartosz Golaszewski <[email protected]>

There's time between when we locate the relevant descriptor during lookup and when we actually take the reference to its parent GPIO device where - if the GPIO device in question is removed - we'll end up with a dangling pointer to freed memory. Make sure devices cannot be removed until we hold a new reference to the device.

Also looks right to me!

Yours,
Linus Walleij

#7 lore raw

Re: [PATCH v2 3/3] gpiolib: pin GPIO devices in place during descriptor lookup

In reply to Bartosz Golaszewski
Quoted 9 lines · Bartosz Golaszewski

From: Bartosz Golaszewski <[email protected]>

There's time between when we locate the relevant descriptor during lookup and when we actually take the reference to its parent GPIO device where - if the GPIO device in question is removed - we'll end up with a dangling pointer to freed memory. Make sure devices cannot be removed until we hold a new reference to the device.

This patch landed in linux-next as commit db660b9a9f86 ("gpiolib: pin GPIO devices in place during descriptor lookup"). Unfortunately it introduces a following lock-dep warning:

============================================
WARNING: possible recursive locking detected 6.7.0-rc7-00062-gdb660b9a9f86 #7819 Not tainted
--------------------------------------------
kworker/u4:2/27 is trying to acquire lock:
c13f4e1c (gpio_devices_sem){++++}-{3:3}, at: gpio_device_find+0x30/0x94

but task is already holding lock:
c13f4e1c (gpio_devices_sem){++++}-{3:3}, at:
gpiod_find_and_request+0x44/0x594

other info that might help us debug this:

  Possible unsafe locking scenario:

       CPU0
       ----
  lock(gpio_devices_sem);
  lock(gpio_devices_sem);

 *** DEADLOCK ***

 May be due to missing lock nesting notation

4 locks held by kworker/u4:2/27:
 #0: c1c06ca8 ((wq_completion)events_unbound){+.+.}-{0:0}, at:

process_one_work+0x148/0x608
  #1: e093df20 ((work_completion)(&entry->work)){+.+.}-{0:0}, at: 
process_one_work+0x148/0x608
  #2: c1f3048c (&dev->mutex){....}-{3:3}, at: 
__driver_attach_async_helper+0x38/0xec
  #3: c13f4e1c (gpio_devices_sem){++++}-{3:3}, at: 
gpiod_find_and_request+0x44/0x594

stack backtrace:
CPU: 0 PID: 27 Comm: kworker/u4:2 Not tainted 
6.7.0-rc7-00062-gdb660b9a9f86 #7819
Hardware name: Samsung Exynos (Flattened Device Tree)
Workqueue: events_unbound async_run_entry_fn
  unwind_backtrace from show_stack+0x10/0x14
  show_stack from dump_stack_lvl+0x58/0x70
  dump_stack_lvl from __lock_acquire+0x1300/0x2984
  __lock_acquire from lock_acquire+0x130/0x37c
  lock_acquire from down_read+0x44/0x224
  down_read from gpio_device_find+0x30/0x94
  gpio_device_find from of_get_named_gpiod_flags+0xa4/0x3a8
  of_get_named_gpiod_flags from of_find_gpio+0x80/0x168
  of_find_gpio from gpiod_find_and_request+0x120/0x594
  gpiod_find_and_request from gpiod_get_optional+0x54/0x90
  gpiod_get_optional from reg_fixed_voltage_probe+0x200/0x400
  reg_fixed_voltage_probe from platform_probe+0x5c/0xb8
  platform_probe from really_probe+0xe0/0x400
  really_probe from __driver_probe_device+0x9c/0x1f0
  __driver_probe_device from driver_probe_device+0x30/0xc0
  driver_probe_device from __driver_attach_async_helper+0x54/0xec
  __driver_attach_async_helper from async_run_entry_fn+0x40/0x154
  async_run_entry_fn from process_one_work+0x204/0x608
  process_one_work from worker_thread+0x1e0/0x498
  worker_thread from kthread+0x104/0x138
  kthread from ret_from_fork+0x14/0x28
Exception stack(0xe093dfb0 to 0xe093dff8)

...

Taking gpio_devices_sem more than once for reading is safe, but it looks that it needs some lock-dep annotations to to make it happy and avoid the above warning.

Quoted 49 lines
---
  drivers/gpio/gpiolib.c | 40 +++++++++++++++++++++++-----------------
  1 file changed, 23 insertions(+), 17 deletions(-)
drivers/gpio/gpiolib.c +23 -10
index 4c93cf73a826..be57f8d6aeae 100644
@@ -4134,27 +4134,33 @@ static struct gpio_desc *gpiod_find_and_request(struct device *consumer,
  	struct gpio_desc *desc;
  	int ret;
 
-	desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx, &flags, &lookupflags);
-	if (gpiod_not_found(desc) && platform_lookup_allowed) {
+	scoped_guard(rwsem_read, &gpio_devices_sem) {
+		desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx,
+					    &flags, &lookupflags);
+		if (gpiod_not_found(desc) && platform_lookup_allowed) {
+			/*
+			 * Either we are not using DT or ACPI, or their lookup
+			 * did not return a result. In that case, use platform
+			 * lookup as a fallback.
+			 */
+			dev_dbg(consumer,
+				"using lookup tables for GPIO lookup\n");
+			desc = gpiod_find(consumer, con_id, idx, &lookupflags);
+		}
+
+		if (IS_ERR(desc)) {
+			dev_dbg(consumer, "No GPIO consumer %s found\n",
+				con_id);
+			return desc;
+		}
+
  		/*
-		 * Either we are not using DT or ACPI, or their lookup did not
-		 * return a result. In that case, use platform lookup as a
-		 * fallback.
+		 * If a connection label was passed use that, else attempt to
+		 * use the device name as label
  		 */
-		dev_dbg(consumer, "using lookup tables for GPIO lookup\n");
-		desc = gpiod_find(consumer, con_id, idx, &lookupflags);
+		ret = gpiod_request(desc, label);
  	}
 
-	if (IS_ERR(desc)) {
-		dev_dbg(consumer, "No GPIO consumer %s found\n", con_id);
-		return desc;

drivers/gpio/gpiolib.c static struct gpio_desc *gpiod_find_and_request(struct device *consumer, · line 4166

Quoted 10 lines
drivers/gpio/gpiolib.c +0 -7
-	}
-
-	/*
-	 * If a connection label was passed use that, else attempt to use
-	 * the device name as label
-	 */
-	ret = gpiod_request(desc, label);
  	if (ret) {
  		if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
  			return ERR_PTR(ret);

Best regards

Signature · 2 lines
Marek Szyprowski, PhD
Samsung R&D Institute Poland
#8 lore raw

Re: [PATCH v2 3/3] gpiolib: pin GPIO devices in place during descriptor lookup

Quoted 140 lines · Marek Szyprowski
Quoted 9 lines · Bartosz Golaszewski

From: Bartosz Golaszewski <[email protected]>

There's time between when we locate the relevant descriptor during lookup and when we actually take the reference to its parent GPIO device where - if the GPIO device in question is removed - we'll end up with a dangling pointer to freed memory. Make sure devices cannot be removed until we hold a new reference to the device.

This patch landed in linux-next as commit db660b9a9f86 ("gpiolib: pin GPIO devices in place during descriptor lookup"). Unfortunately it introduces a following lock-dep warning:

============================================
WARNING: possible recursive locking detected 6.7.0-rc7-00062-gdb660b9a9f86 #7819 Not tainted
--------------------------------------------
kworker/u4:2/27 is trying to acquire lock:
c13f4e1c (gpio_devices_sem){++++}-{3:3}, at: gpio_device_find+0x30/0x94

but task is already holding lock:
c13f4e1c (gpio_devices_sem){++++}-{3:3}, at:
gpiod_find_and_request+0x44/0x594

other info that might help us debug this:

  Possible unsafe locking scenario:
        CPU0
        ----
   lock(gpio_devices_sem);
   lock(gpio_devices_sem);

*** DEADLOCK ***

May be due to missing lock nesting notation

4 locks held by kworker/u4:2/27:
#0: c1c06ca8 ((wq_completion)events_unbound){+.+.}-{0:0}, at:

process_one_work+0x148/0x608
  #1: e093df20 ((work_completion)(&entry->work)){+.+.}-{0:0}, at:
process_one_work+0x148/0x608
  #2: c1f3048c (&dev->mutex){....}-{3:3}, at:
__driver_attach_async_helper+0x38/0xec
  #3: c13f4e1c (gpio_devices_sem){++++}-{3:3}, at:
gpiod_find_and_request+0x44/0x594

stack backtrace:
CPU: 0 PID: 27 Comm: kworker/u4:2 Not tainted
6.7.0-rc7-00062-gdb660b9a9f86 #7819
Hardware name: Samsung Exynos (Flattened Device Tree)
Workqueue: events_unbound async_run_entry_fn
  unwind_backtrace from show_stack+0x10/0x14
  show_stack from dump_stack_lvl+0x58/0x70
  dump_stack_lvl from __lock_acquire+0x1300/0x2984
  __lock_acquire from lock_acquire+0x130/0x37c
  lock_acquire from down_read+0x44/0x224
  down_read from gpio_device_find+0x30/0x94
  gpio_device_find from of_get_named_gpiod_flags+0xa4/0x3a8
  of_get_named_gpiod_flags from of_find_gpio+0x80/0x168
  of_find_gpio from gpiod_find_and_request+0x120/0x594
  gpiod_find_and_request from gpiod_get_optional+0x54/0x90
  gpiod_get_optional from reg_fixed_voltage_probe+0x200/0x400
  reg_fixed_voltage_probe from platform_probe+0x5c/0xb8
  platform_probe from really_probe+0xe0/0x400
  really_probe from __driver_probe_device+0x9c/0x1f0
  __driver_probe_device from driver_probe_device+0x30/0xc0
  driver_probe_device from __driver_attach_async_helper+0x54/0xec
  __driver_attach_async_helper from async_run_entry_fn+0x40/0x154
  async_run_entry_fn from process_one_work+0x204/0x608
  process_one_work from worker_thread+0x1e0/0x498
  worker_thread from kthread+0x104/0x138
  kthread from ret_from_fork+0x14/0x28
Exception stack(0xe093dfb0 to 0xe093dff8)

...

Taking gpio_devices_sem more than once for reading is safe, but it looks that it needs some lock-dep annotations to to make it happy and avoid the above warning.

Quoted 56 lines
---
  drivers/gpio/gpiolib.c | 40 +++++++++++++++++++++++-----------------
  1 file changed, 23 insertions(+), 17 deletions(-)
drivers/gpio/gpiolib.c +23 -17
index 4c93cf73a826..be57f8d6aeae 100644
@@ -4134,27 +4134,33 @@ static struct gpio_desc *gpiod_find_and_request(struct device *consumer,
      struct gpio_desc *desc;
      int ret;
 
-     desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx, &flags, &lookupflags);
-     if (gpiod_not_found(desc) && platform_lookup_allowed) {
+     scoped_guard(rwsem_read, &gpio_devices_sem) {
+             desc = gpiod_find_by_fwnode(fwnode, consumer, con_id, idx,
+                                         &flags, &lookupflags);
+             if (gpiod_not_found(desc) && platform_lookup_allowed) {
+                     /*
+                      * Either we are not using DT or ACPI, or their lookup
+                      * did not return a result. In that case, use platform
+                      * lookup as a fallback.
+                      */
+                     dev_dbg(consumer,
+                             "using lookup tables for GPIO lookup\n");
+                     desc = gpiod_find(consumer, con_id, idx, &lookupflags);
+             }
+
+             if (IS_ERR(desc)) {
+                     dev_dbg(consumer, "No GPIO consumer %s found\n",
+                             con_id);
+                     return desc;
+             }
+
              /*
-              * Either we are not using DT or ACPI, or their lookup did not
-              * return a result. In that case, use platform lookup as a
-              * fallback.
+              * If a connection label was passed use that, else attempt to
+              * use the device name as label
               */
-             dev_dbg(consumer, "using lookup tables for GPIO lookup\n");
-             desc = gpiod_find(consumer, con_id, idx, &lookupflags);
+             ret = gpiod_request(desc, label);
      }
 
-     if (IS_ERR(desc)) {
-             dev_dbg(consumer, "No GPIO consumer %s found\n", con_id);
-             return desc;
-     }
-
-     /*
-      * If a connection label was passed use that, else attempt to use
-      * the device name as label
-      */
-     ret = gpiod_request(desc, label);
Quoted 9 lines

drivers/gpio/gpiolib.c static struct gpio_desc *gpiod_find_and_request(struct device *consumer, · line 4166

Quoted 3 lines
drivers/gpio/gpiolib.c +0 -0
      if (ret) {
              if (!(ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
                      return ERR_PTR(ret);

Best regards
--
Marek Szyprowski, PhD
Samsung R&D Institute Poland

Thanks for the report. I think it may have come too late in the release cycle as it has the potential to break a lot of things. I will back it out of my for-next branch. I'll resend it for v6.9.

Bart

#9 lore raw
In reply to Bartosz Golaszewski
Quoted 25 lines · Bartosz Golaszewski

From: Bartosz Golaszewski <[email protected]>

I'm still figuring out how to keep GPIO descriptors coherent while (mostly) lockless. In the meantime, I found a potential race-condition during GPIO descriptor lookup and also figured that the correct way to protect the GPIO device list is actually a read-write semaphore as we're not modifying the list very often and readers should be able to iterate over it concurrently.

The first patch in this series is new in v2. I realized that we must not wait until .release() to remove the GPIO device from the list as this is why pinning down the GPIO device list during lookup would never work - we always could end up re-taking a reference to an object that was being released if it got looked up between when the last reference is dropped and the object is finally removed from the device list.

v1 -> v2:
- add patch 1/3 to fix a release timing issue

Bartosz Golaszewski (3):
gpiolib: remove the GPIO device from the list when it's unregistered
gpiolib: replace the GPIO device mutex with a read-write semaphore
gpiolib: pin GPIO devices in place during descriptor lookup

Quoted 8 lines
 drivers/gpio/gpiolib-sysfs.c |  2 +-
 drivers/gpio/gpiolib.c       | 62 ++++++++++++++++++++----------------
 drivers/gpio/gpiolib.h       |  2 +-
 3 files changed, 36 insertions(+), 30 deletions(-)

--
2.40.1

Series applied.

Bart