From: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
To: Bartosz Golaszewski <brgl@kernel.org>,
Kent Gibson <warthog618@gmail.com>,
Linus Walleij <linusw@kernel.org>,
Andy Shevchenko <andriy.shevchenko@linux.intel.com>,
"Paul E. McKenney" <paulmck@kernel.org>
Cc: linux-gpio@vger.kernel.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org,
Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Subject: [PATCH] gpio: cdev: fix waitqueue deadlock on chip deregistration
Date: Thu, 05 Nov 2026 15:47:59 +0100 [thread overview]
Message-ID: <20261105-gpio-cdev-srcu-deadlock-v1-1-6047117030ec@oss.qualcomm.com> (raw)
The three calls to wait_event_interruptible_locked() sleep while holding
gdev->srcu. In gpiochip_remove(), synchronize_srcu() runs before
gcdev_unregister(). The latter fires the waitqueue wakeup events from
the notifier callbacks, so a reader blocked on an empty FIFO never leaves
its SRCU section and the removal will block forever in the former.
Since rcu_access_pointer() is a plain READ_ONCE(), we don't really need
to take the SRCU read lock. We don't access the chip structure after
checking it. Remove the lock and instead make the waitqueue aware of the
possible chip deregistration. Keep the initial gdev->chip checks as
removing them may change the error codes visible to user-space, for
instance in cases of a non-blocking read on an empty fifo.
Ideally, we'd factor out the common code into a dedicated helper but
there doesn't seem to exist a clean way of passing a kfifo as argument
so let's open-code the three call sites.
Cc: stable@vger.kernel.org
Fixes: d83cee3d2bb1 ("gpio: protect the pointer to gpio_chip in gpio_device with SRCU")
Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
---
I've had it on my TODO list for a while. I vaguely remember someone
reporting it to me but can't find the reference anymore. Please feel
free to add a Reported-by: and Closes:.
Paul: I've Cc'ed you as you may want to take a look at my SRCU logic
here.
---
drivers/gpio/gpiolib-cdev.c | 27 ++++++++++++++++++---------
1 file changed, 18 insertions(+), 9 deletions(-)
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index f68e33cbd7d4cd5153e82ad77768cf52a90601d6..62215d43b8ac757bbd5344bbcf8631feb2443a6b 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -1489,8 +1489,6 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
ssize_t bytes_read = 0;
int ret;
- guard(srcu)(&lr->gdev->srcu);
-
if (!rcu_access_pointer(lr->gdev->chip))
return -ENODEV;
@@ -1507,9 +1505,14 @@ static ssize_t linereq_read(struct file *file, char __user *buf,
return -EAGAIN;
ret = wait_event_interruptible_locked(lr->wait,
- !kfifo_is_empty(&lr->events));
+ !kfifo_is_empty(&lr->events) ||
+ !rcu_access_pointer(lr->gdev->chip));
if (ret)
return ret;
+
+ /* Woken up by chip removal. */
+ if (kfifo_is_empty(&lr->events))
+ return -ENODEV;
}
if (kfifo_out(&lr->events, &le, 1) != 1) {
@@ -1801,8 +1804,6 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
ssize_t ge_size;
int ret;
- guard(srcu)(&le->gdev->srcu);
-
if (!rcu_access_pointer(le->gdev->chip))
return -ENODEV;
@@ -1832,9 +1833,14 @@ static ssize_t lineevent_read(struct file *file, char __user *buf,
return -EAGAIN;
ret = wait_event_interruptible_locked(le->wait,
- !kfifo_is_empty(&le->events));
+ !kfifo_is_empty(&le->events) ||
+ !rcu_access_pointer(le->gdev->chip));
if (ret)
return ret;
+
+ /* Woken up by chip removal. */
+ if (kfifo_is_empty(&le->events))
+ return -ENODEV;
}
if (kfifo_out(&le->events, &ge, 1) != 1) {
@@ -2579,8 +2585,6 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
int ret;
size_t event_size;
- guard(srcu)(&cdev->gdev->srcu);
-
if (!rcu_access_pointer(cdev->gdev->chip))
return -ENODEV;
@@ -2600,9 +2604,14 @@ static ssize_t lineinfo_watch_read(struct file *file, char __user *buf,
return -EAGAIN;
ret = wait_event_interruptible_locked(cdev->wait,
- !kfifo_is_empty(&cdev->events));
+ !kfifo_is_empty(&cdev->events) ||
+ !rcu_access_pointer(cdev->gdev->chip));
if (ret)
return ret;
+
+ /* Woken up by chip removal. */
+ if (kfifo_is_empty(&cdev->events))
+ return -ENODEV;
}
#ifdef CONFIG_GPIO_CDEV_V1
/* must be after kfifo check so watch_abi_version is set */
---
base-commit: 8b7179210b22f8e48a82c5ceacb9d56a9c4cfec4
change-id: 20261105-gpio-cdev-srcu-deadlock-1377b86567a3
Best regards,
--
Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
next reply other threads:[~2026-11-05 14:48 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-05 14:47 Bartosz Golaszewski [this message]
2026-11-05 14:57 ` Kent Gibson
2026-11-05 15:02 ` Bartosz Golaszewski
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261105-gpio-cdev-srcu-deadlock-v1-1-6047117030ec@oss.qualcomm.com \
--to=bartosz.golaszewski@oss.qualcomm.com \
--cc=andriy.shevchenko@linux.intel.com \
--cc=brgl@kernel.org \
--cc=linusw@kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=paulmck@kernel.org \
--cc=stable@vger.kernel.org \
--cc=warthog618@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®