]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
eventpoll: clarify POLLFREE handshake comments
authorChristian Brauner <brauner@kernel.org>
Fri, 24 Apr 2026 13:46:34 +0000 (15:46 +0200)
committerChristian Brauner <brauner@kernel.org>
Tue, 28 Apr 2026 15:27:27 +0000 (17:27 +0200)
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>
Link: https://patch.msgid.link/20260424-work-epoll-rework-v1-3-249ed00a20f3@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>
fs/eventpoll.c

index 477fcbc8e95e15fcfab145e6084ff502f1661cba..1d1fd6464c38702db2c2fcd2adfccc8f8850f412 100644 (file)
@@ -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 @@ out_unlock:
 
        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);
        }