All of lore.kernel.org
 help / color / mirror / Atom feed
From: Oleg Nesterov <oleg@redhat.com>
To: Breno Leitao <leitao@debian.org>,
	Christian Brauner <brauner@kernel.org>,
	Mateusz Guzik <mjguzik@gmail.com>, Jens Axboe <axboe@kernel.dk>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>, Jan Kara <jack@suse.cz>,
	linux-fsdevel@vger.kernel.org, linux-kernel@vger.kernel.org,
	io-uring@vger.kernel.org, Alexey Gladkov <legion@kernel.org>
Subject: Re: [PATCH 0/1] pipe: only enable the extra wake_up(rd_wait) when epoll is actually used
Date: Fri, 24 Jul 2026 15:58:17 +0200	[thread overview]
Message-ID: <amNvebqiG2fH0W_L@redhat.com> (raw)
In-Reply-To: <amIu5WWgTNkdvqIz@redhat.com>

On 07/23, Oleg Nesterov wrote:
>
> OK, sashiko has some concerns
>
> 	https://sashiko.dev/#/patchset/amIqmbbZx3NlzUsX%40redhat.com

Let me quote:

	Does skipping this wakeup for non-epoll consumers break io_uring?

	Applications polling pipes via io_uring do not attach an eventpoll context,
	so pipe->epoll_usage will be false. If a writer writes to an empty pipe,
	io_uring receives the wakeup.

	If the writer then writes a second chunk before the first is drained,
	anon_pipe_write() observes was_empty == false and
	pipe_get_epoll_usage() == false, skipping the waitqueue wakeup.

	Could this cause io_uring to miss events and hang permanently, waiting
	for a CQE that will never be emitted for the new data?

and I am starting to think sashiko is right (damn as always ;) and this
patch does affect/break io_uring.

Jens, could you confirm? If yes, we need to update the comments in pipe.c
(I've attached 1/1 at the end, so that you can see what this patch does)

I know nothing about io_uring and io_uring/poll.c is not trivial to say at
least ;) please check my understanding.

It seems that IORING_OP_POLL_ADD / IORING_POLL_ADD_MULTI is edge-triggered
by default! Like EPOLL_CTL_ADD / EPOLLET.

This means that io_uring depends on the "nasty semantics" too, io_poll_wake()
path should add the task work which calls io_req_post_cqe() every time the new
data arrives, even if the pipe was not empty.

Strange...

And. io_poll_parse_events() doesn't set EPOLLET if IORING_POLL_ADD_LEVEL, but
how is it possible to use IORING_POLL_ADD_LEVEL? io_poll_add_prep() only allows
IORING_POLL_ADD_MULTI in flags? OK, I don't understand this code anyway...

Oleg.
---

Subject: [PATCH] pipe: only enable the extra wake_up(rd_wait) when epoll is actually used

pipe_poll() unconditionally sets poll_usage on the first call, forcing
anon_pipe_write() to wake up readers on every write even if the pipe was
not empty. But this is only needed for epoll's "nasty semantics"; poll()
and select() users pay for it for no reason.

Rename it to epoll_usage, and only set it when the caller is actually
using epoll on the read side of the pipe.

Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
 fs/pipe.c                 | 23 +++++++++++++++++++----
 include/linux/pipe_fs_i.h |  6 ++++--
 2 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/fs/pipe.c b/fs/pipe.c
index 32140cb00d7e..b45b4b8d0b0b 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -357,6 +357,23 @@ static inline unsigned int pipe_update_tail(struct pipe_inode_info *pipe,
 	return tail;
 }
 
+static void pipe_set_epoll_usage(struct file *filp, struct pipe_inode_info *pipe)
+{
+#ifdef CONFIG_EPOLL
+	if ((filp->f_mode & FMODE_READ) && filp->f_ep &&
+	    unlikely(!READ_ONCE(pipe->epoll_usage)))
+		WRITE_ONCE(pipe->epoll_usage, true);
+#endif
+}
+
+static bool pipe_get_epoll_usage(struct pipe_inode_info *pipe)
+{
+#ifdef CONFIG_EPOLL
+	return pipe->epoll_usage;
+#endif
+	return false;
+}
+
 static ssize_t
 anon_pipe_read(struct kiocb *iocb, struct iov_iter *to)
 {
@@ -689,7 +706,7 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
 	 * Epoll nonsensically wants a wakeup whether the pipe
 	 * was already empty or not.
 	 */
-	if (was_empty || pipe->poll_usage)
+	if (was_empty || pipe_get_epoll_usage(pipe))
 		wake_up_interruptible_sync_poll(&pipe->rd_wait, EPOLLIN | EPOLLRDNORM);
 	kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
 	if (wake_next_writer)
@@ -761,9 +778,7 @@ pipe_poll(struct file *filp, poll_table *wait)
 	union pipe_index idx;
 
 	/* Epoll has some historical nasty semantics, this enables them */
-	if (unlikely(!READ_ONCE(pipe->poll_usage)))
-		WRITE_ONCE(pipe->poll_usage, true);
-
+	pipe_set_epoll_usage(filp, pipe);
 	/*
 	 * Reading pipe state only -- no need for acquiring the semaphore.
 	 *
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index 7f6a92ac9704..d06da9bad3a8 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -74,7 +74,7 @@ union pipe_index {
  *	@files: number of struct file referring this pipe (protected by ->i_lock)
  *	@r_counter: reader counter
  *	@w_counter: writer counter
- *	@poll_usage: is this pipe used for epoll, which has crazy wakeups?
+ *	@epoll_usage: is this pipe used for epoll, which has crazy wakeups?
  *	@fasync_readers: reader side fasync
  *	@fasync_writers: writer side fasync
  *	@bufs: the circular array of pipe buffers
@@ -95,7 +95,9 @@ struct pipe_inode_info {
 	unsigned int files;
 	unsigned int r_counter;
 	unsigned int w_counter;
-	bool poll_usage;
+#ifdef CONFIG_EPOLL
+	bool epoll_usage;
+#endif
 #ifdef CONFIG_WATCH_QUEUE
 	bool note_loss;
 #endif
-- 
2.52.0



  reply	other threads:[~2026-07-24 13:58 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-23 14:51 [PATCH 0/1] pipe: only enable the extra wake_up(rd_wait) when epoll is actually used Oleg Nesterov
2026-07-23 14:52 ` [PATCH 1/1] " Oleg Nesterov
2026-07-23 16:41   ` Mateusz Guzik
2026-07-23 15:10 ` [PATCH 0/1] " Oleg Nesterov
2026-07-24 13:58   ` Oleg Nesterov [this message]
2026-07-24 14:43     ` Mateusz Guzik
2026-07-24 14:54       ` Oleg Nesterov

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=amNvebqiG2fH0W_L@redhat.com \
    --to=oleg@redhat.com \
    --cc=axboe@kernel.dk \
    --cc=brauner@kernel.org \
    --cc=io-uring@vger.kernel.org \
    --cc=jack@suse.cz \
    --cc=legion@kernel.org \
    --cc=leitao@debian.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mjguzik@gmail.com \
    --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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.