Bug 209251

Summary: Blocking splice() does not allow to read all the available data from pipe
Product: File System Reporter: Dmitry Izbitsky (izbitsky)
Component: OtherAssignee: fs_other
Status: NEW ---    
Severity: normal CC: izbitsky, regressions, sam
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: 5.9.0-rc4 Subsystem:
Regression: No Bisected commit-id:
Attachments: Reproducer for the bug

Description Dmitry Izbitsky 2020-09-13 07:20:42 UTC
Created attachment 292493 [details]
Reproducer for the bug

If I do the following (I checked mainline kernel 5.9.0-rc4, 4.19.0-10-amd64 on Debian, 4.15.0-106-generic on Ubuntu):

1. Overfill pipe_write_end with data;
2. Call splice(socket, 0, pipe_write_end, 0, 100, 0) from another thread which will block because there is nothing to read from the socket;
3. Try to read from pipe_read_fd all the data written in (1);

then read() will block after reading only part of the data – even though poll() reports POLLIN for pipe_read_fd. read() will block even if O_NONBLOCK is set for pipe_read_fd.

The reproducer program test_splice.c is in attachment. It can be used like

gcc test_splice.c -o test_splice -lpthread
./test_splice 127.0.0.1 11111

Output:

61441 bytes were written to pipe
Connection was accepted
Calling splice() in a thread to move data from the socket to the pipe which should block
Trying to read all the data written to pipe before

read() on pipe blocks for 4998 ms by now, after reading 4096 bytes
Sending data from peer to unblock splice()
splice() returned 10
61451 bytes was read from pipe
Joining a thread which calls splice()...
Joining a thread which unblocks splice()...


If I run this program on 3.10.0-1062.1.2.el7.x86_64 (CentOS Linux release 7.7.1908), it works fine:

./test_splice 127.0.0.1 11111
61441 bytes were written to pipe
Connection was accepted
Calling splice() in a thread to move data from the socket to the pipe which should block
Trying to read all the data written to pipe before
61441 bytes was read from pipe
Joining a thread which calls splice()...
Sending data from peer to unblock splice()
splice() returned 10
Joining a thread which unblocks splice()...