From: Christian Brauner <brauner@kernel.org>
To: linux-fsdevel@vger.kernel.org
Cc: Alexander Viro <viro@zeniv.linux.org.uk>, Jan Kara <jack@suse.cz>,
Linus Torvalds <torvalds@linux-foundation.org>,
Jens Axboe <axboe@kernel.dk>,
"Christian Brauner (Amutable)" <brauner@kernel.org>
Subject: [PATCH 03/17] eventpoll: clarify POLLFREE handshake comments
Date: Fri, 24 Apr 2026 15:46:34 +0200 [thread overview]
Message-ID: <20260424-work-epoll-rework-v1-3-249ed00a20f3@kernel.org> (raw)
In-Reply-To: <20260424-work-epoll-rework-v1-0-249ed00a20f3@kernel.org>
ep_remove_wait_queue() and the POLLFREE branch of ep_poll_callback()
are the two halves of a release/acquire handshake that lets a
subsystem (binder, signalfd, ...) tear down a wait-queue head from
under a registered epitem. The existing local comments documented the
race but did not name the protocol or refer readers from one side to
the other. After the previous commit added a "POLLFREE handshake"
section to the top-of-file banner, these sites can point at the
banner and at each other.
Rework the two comment blocks so that each side is labelled
"acquire side" or "release side", references the banner, and
explains its role in the protocol. On the release side fuse the two
former comments into one narrative: list_del_init() tolerates a
second delete from a racing ep_remove_wait_queue(), and the
smp_store_release() is what lets that racing remover discover the
teardown.
Comment-only; no functional change.
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
---
fs/eventpoll.c | 38 +++++++++++++++++++++++++-------------
1 file changed, 25 insertions(+), 13 deletions(-)
diff --git a/fs/eventpoll.c b/fs/eventpoll.c
index 477fcbc8e95e..1d1fd6464c38 100644
--- a/fs/eventpoll.c
+++ b/fs/eventpoll.c
@@ -828,10 +828,15 @@ static void ep_remove_wait_queue(struct eppoll_entry *pwq)
rcu_read_lock();
/*
- * If it is cleared by POLLFREE, it should be rcu-safe.
- * If we read NULL we need a barrier paired with
- * smp_store_release() in ep_poll_callback(), otherwise
- * we rely on whead->lock.
+ * POLLFREE handshake, acquire side; see "POLLFREE handshake"
+ * at the top of this file.
+ *
+ * A NULL load is paired with the smp_store_release(&whead, NULL)
+ * in ep_poll_callback()'s POLLFREE branch: the teardown is
+ * complete and we must not touch whead again. On a non-NULL load
+ * rcu_read_lock() keeps the waitqueue memory alive (POLLFREE
+ * firers RCU-defer the free) and whead->lock inside
+ * remove_wait_queue() serializes us against the store side.
*/
whead = smp_load_acquire(&pwq->whead);
if (whead)
@@ -1505,17 +1510,24 @@ static int ep_poll_callback(wait_queue_entry_t *wait, unsigned mode, int sync, v
if (pollflags & POLLFREE) {
/*
- * If we race with ep_remove_wait_queue() it can miss
- * ->whead = NULL and do another remove_wait_queue() after
- * us, so we can't use __remove_wait_queue().
+ * POLLFREE handshake, release side; see "POLLFREE handshake"
+ * at the top of this file.
+ *
+ * Unlink our wait entry with list_del_init rather than
+ * __remove_wait_queue: a concurrent ep_remove_wait_queue()
+ * that already loaded a non-NULL whead may still call
+ * remove_wait_queue() after us, and list_del_init() tolerates
+ * the second delete.
+ *
+ * smp_store_release(&whead, NULL) publishes the teardown to
+ * ep_remove_wait_queue()'s smp_load_acquire(). Before this
+ * store, a racing ep_clear_and_put() / ep_remove() reaches
+ * ep_remove_wait_queue() which sees whead != NULL and takes
+ * whead->lock -- the same lock held by our caller, so it
+ * serializes behind us. Once whead is zeroed, nothing else
+ * protects ep / epi / wait.
*/
list_del_init(&wait->entry);
- /*
- * ->whead != NULL protects us from the race with
- * ep_clear_and_put() or ep_remove(), ep_remove_wait_queue()
- * takes whead->lock held by the caller. Once we nullify it,
- * nothing protects ep/epi or even wait.
- */
smp_store_release(&ep_pwq_from_wait(wait)->whead, NULL);
}
--
2.47.3
next prev parent reply other threads:[~2026-04-24 13:46 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-04-24 13:46 [PATCH 00/17] eventpoll: clarity refactor Christian Brauner
2026-04-24 13:46 ` [PATCH 01/17] eventpoll: expand top-of-file overview / locking doc Christian Brauner
2026-04-24 13:46 ` [PATCH 02/17] eventpoll: document loop-check / path-check globals Christian Brauner
2026-04-24 13:46 ` Christian Brauner [this message]
2026-04-24 13:46 ` [PATCH 04/17] eventpoll: refresh epi_fget() / ep_remove_file() comments Christian Brauner
2026-04-24 13:46 ` [PATCH 05/17] eventpoll: document ep_clear_and_put() two-pass pattern Christian Brauner
2026-04-24 13:46 ` [PATCH 06/17] eventpoll: rename ep_refcount_dec_and_test() to ep_put() Christian Brauner
2026-04-24 13:46 ` [PATCH 07/17] eventpoll: drop unused depth argument from epoll_mutex_lock() Christian Brauner
2026-04-24 13:46 ` [PATCH 08/17] eventpoll: rename attach_epitem() to ep_attach_file() Christian Brauner
2026-04-24 13:46 ` [PATCH 09/17] eventpoll: relocate KCMP helpers near compat syscalls Christian Brauner
2026-04-24 13:46 ` [PATCH 10/17] eventpoll: split ep_insert() into alloc + register stages Christian Brauner
2026-04-24 13:46 ` [PATCH 11/17] eventpoll: split ep_clear_and_put() into drain helpers Christian Brauner
2026-04-24 13:46 ` [PATCH 12/17] eventpoll: extract ep_deliver_event() from ep_send_events() Christian Brauner
2026-04-24 13:46 ` [PATCH 13/17] eventpoll: extract lock dance from do_epoll_ctl() into ep_ctl_lock() Christian Brauner
2026-04-24 13:46 ` [PATCH 14/17] eventpoll: wrap EP_UNACTIVE_PTR in typed sentinel helpers Christian Brauner
2026-04-24 13:46 ` [PATCH 15/17] eventpoll: rename epi->next and txlist for clarity Christian Brauner
2026-04-24 16:06 ` Linus Torvalds
2026-04-24 13:46 ` [PATCH 16/17] eventpoll: use bool for predicate helpers Christian Brauner
2026-04-24 13:46 ` [PATCH 17/17] eventpoll: hoist CTL_ADD scratch state into struct ep_ctl_ctx Christian Brauner
2026-04-24 15:33 ` [PATCH 00/17] eventpoll: clarity refactor Linus Torvalds
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=20260424-work-epoll-rework-v1-3-249ed00a20f3@kernel.org \
--to=brauner@kernel.org \
--cc=axboe@kernel.dk \
--cc=jack@suse.cz \
--cc=linux-fsdevel@vger.kernel.org \
--cc=torvalds@linux-foundation.org \
--cc=viro@zeniv.linux.org.uk \
/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