Skip to content

Commit 6551d5c

Browse files
committed
pipe: make sure to wake up everybody when the last reader/writer closes
Andrei Vagin reported that commit 0ddad21 ("pipe: use exclusive waits when reading or writing") broke one of the CRIU tests. He even has a trivial reproducer: #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main() { int p[2]; pid_t p1, p2; int status; if (pipe(p) == -1) return 1; p1 = fork(); if (p1 == 0) { close(p[1]); read(p[0], &status, sizeof(status)); return 0; } p2 = fork(); if (p2 == 0) { close(p[1]); read(p[0], &status, sizeof(status)); return 0; } sleep(1); close(p[1]); wait(&status); wait(&status); return 0; } and the problem - once he points it out - is obvious. We use these nice exclusive waits, but when the last writer goes away, it then needs to wake up _every_ reader (and conversely, the last reader disappearing needs to wake every writer, of course). In fact, when going through this, we had several small oddities around how to wake things. We did in fact wake every reader when we changed the size of the pipe buffers. But that's entirely pointless, since that just acts as a possible source of new space - no new data to read. And when we change the size of the buffer, we don't need to wake all writers even when we add space - that case acts just as if somebody made space by reading, and any writer that finds itself not filling it up entirely will wake the next one. On the other hand, on the exit path, we tried to limit the wakeups with the proper poll keys etc, which is entirely pointless, because at that point we obviously need to wake up everybody. So don't do that: just wake up everybody - but only do that if the counts changed to zero. So fix those non-IO wakeups to be more proper: space change doesn't add any new data, but it might make room for writers, so it wakes up a writer. And the actual changes to reader/writer counts should wake up everybody, since everybody is affected (ie readers will all see EOF if the writers have gone away, and writers will all get EPIPE if all readers have gone away). Fixes: 0ddad21 ("pipe: use exclusive waits when reading or writing") Reported-and-tested-by: Andrei Vagin <[email protected]> Cc: Josh Triplett <[email protected]> Cc: Matthew Wilcox <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent b1da3ac commit 6551d5c

File tree

1 file changed

+10
-8
lines changed

1 file changed

+10
-8
lines changed

fs/pipe.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -722,9 +722,10 @@ pipe_release(struct inode *inode, struct file *file)
722722
if (file->f_mode & FMODE_WRITE)
723723
pipe->writers--;
724724

725-
if (pipe->readers || pipe->writers) {
726-
wake_up_interruptible_sync_poll(&pipe->rd_wait, EPOLLIN | EPOLLRDNORM | EPOLLERR | EPOLLHUP);
727-
wake_up_interruptible_sync_poll(&pipe->wr_wait, EPOLLOUT | EPOLLWRNORM | EPOLLERR | EPOLLHUP);
725+
/* Was that the last reader or writer, but not the other side? */
726+
if (!pipe->readers != !pipe->writers) {
727+
wake_up_interruptible_all(&pipe->rd_wait);
728+
wake_up_interruptible_all(&pipe->wr_wait);
728729
kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
729730
kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
730731
}
@@ -1026,8 +1027,8 @@ static int wait_for_partner(struct pipe_inode_info *pipe, unsigned int *cnt)
10261027

10271028
static void wake_up_partner(struct pipe_inode_info *pipe)
10281029
{
1029-
wake_up_interruptible(&pipe->rd_wait);
1030-
wake_up_interruptible(&pipe->wr_wait);
1030+
wake_up_interruptible_all(&pipe->rd_wait);
1031+
wake_up_interruptible_all(&pipe->wr_wait);
10311032
}
10321033

10331034
static int fifo_open(struct inode *inode, struct file *filp)
@@ -1144,7 +1145,7 @@ static int fifo_open(struct inode *inode, struct file *filp)
11441145

11451146
err_wr:
11461147
if (!--pipe->writers)
1147-
wake_up_interruptible(&pipe->rd_wait);
1148+
wake_up_interruptible_all(&pipe->rd_wait);
11481149
ret = -ERESTARTSYS;
11491150
goto err;
11501151

@@ -1271,8 +1272,9 @@ static long pipe_set_size(struct pipe_inode_info *pipe, unsigned long arg)
12711272
pipe->max_usage = nr_slots;
12721273
pipe->tail = tail;
12731274
pipe->head = head;
1274-
wake_up_interruptible_all(&pipe->rd_wait);
1275-
wake_up_interruptible_all(&pipe->wr_wait);
1275+
1276+
/* This might have made more room for writers */
1277+
wake_up_interruptible(&pipe->wr_wait);
12761278
return pipe->max_usage * PAGE_SIZE;
12771279

12781280
out_revert_acct:

0 commit comments

Comments
 (0)