2019-12-22 12:38:36

by Jan Stancek

[permalink] [raw]
Subject: [PATCH] pipe: fix empty pipe check in pipe_write()

LTP pipeio_1 test is hanging with v5.5-rc2-385-gb8e382a185eb,
with read side observing empty pipe and sleeping and write
side running out of space and then sleeping as well. In this
scenario there are 5 writers and 1 reader.

Problem is that after pipe_write() reacquires pipe lock, it
re-checks for empty pipe with potentially stale 'head' and
doesn't wake up read side anymore. pipe->tail can advance
beyond 'head', because there are multiple writers.

Use pipe->head for empty pipe check after reacquiring lock
to observe current state.

Testing: With patch, LTP pipeio_1 ran successfully in loop for 1 hour.
Without patch it hanged within a minute.

Fixes: 1b6b26ae7053 ("pipe: fix and clarify pipe write wakeup logic")
Reported-by: Rachel Sibley <[email protected]>
Signed-off-by: Jan Stancek <[email protected]>
---
fs/pipe.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/fs/pipe.c b/fs/pipe.c
index 04d004ee2e8c..57502c3c0fba 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -581,7 +581,7 @@ static inline bool pipe_writable(const struct pipe_inode_info *pipe)
}
wait_event_interruptible(pipe->wait, pipe_writable(pipe));
__pipe_lock(pipe);
- was_empty = pipe_empty(head, pipe->tail);
+ was_empty = pipe_empty(pipe->head, pipe->tail);
}
out:
__pipe_unlock(pipe);
--
1.8.3.1


2019-12-22 17:52:16

by Linus Torvalds

[permalink] [raw]
Subject: Re: [PATCH] pipe: fix empty pipe check in pipe_write()

On Sun, Dec 22, 2019 at 4:35 AM Jan Stancek <[email protected]> wrote:
> Problem is that after pipe_write() reacquires pipe lock, it
> re-checks for empty pipe with potentially stale 'head' and
> doesn't wake up read side anymore. pipe->tail can advance
> beyond 'head', because there are multiple writers.

Thank you. Patch is obviously correct, applied.

I wonder how much that whole "cache head/tail/mask" really helps, and
if we should strive to get rid of it entirely (and just make
"pipe_emptuy()" and friends take a 'const struct pipe_inode_info *"
argument).

Oh well. I've apple your one-liner, but next time I might decide the
cleverness and slight code generation advantage might not be worth it.

Hopefully there won't _be_ a next time, of course ;)

Linus