Skip to content

Commit ee5e001

Browse files
Slavomir Kaslevtorvalds
authored andcommitted
fs: Make splice() and tee() take into account O_NONBLOCK flag on pipes
The current implementation of splice() and tee() ignores O_NONBLOCK set on pipe file descriptors and checks only the SPLICE_F_NONBLOCK flag for blocking on pipe arguments. This is inconsistent since splice()-ing from/to non-pipe file descriptors does take O_NONBLOCK into consideration. Fix this by promoting O_NONBLOCK, when set on a pipe, to SPLICE_F_NONBLOCK. Some context for how the current implementation of splice() leads to inconsistent behavior. In the ongoing work[1] to add VM tracing capability to trace-cmd we stream tracing data over named FIFOs or vsockets from guests back to the host. When we receive SIGINT from user to stop tracing, we set O_NONBLOCK on the input file descriptor and set SPLICE_F_NONBLOCK for the next call to splice(). If splice() was blocked waiting on data from the input FIFO, after SIGINT splice() restarts with the same arguments (no SPLICE_F_NONBLOCK) and blocks again instead of returning -EAGAIN when no data is available. This differs from the splice() behavior when reading from a vsocket or when we're doing a traditional read()/write() loop (trace-cmd's --nosplice argument). With this patch applied we get the same behavior in all situations after setting O_NONBLOCK which also matches the behavior of doing a read()/write() loop instead of splice(). This change does have potential of breaking users who don't expect EAGAIN from splice() when SPLICE_F_NONBLOCK is not set. OTOH programs that set O_NONBLOCK and don't anticipate EAGAIN are arguably buggy[2]. [1] https://github.com/skaslev/trace-cmd/tree/vsock [2] https://github.com/torvalds/linux/blob/d47e3da1759230e394096fd742aad423c291ba48/fs/read_write.c#L1425 Signed-off-by: Slavomir Kaslev <kaslevs@vmware.com> Reviewed-by: Steven Rostedt (VMware) <rostedt@goodmis.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
1 parent 4f9020f commit ee5e001

1 file changed

Lines changed: 12 additions & 0 deletions

File tree

‎fs/splice.c‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,6 +1123,9 @@ static long do_splice(struct file *in, loff_t __user *off_in,
11231123
if (ipipe == opipe)
11241124
return -EINVAL;
11251125

1126+
if ((in->f_flags | out->f_flags) & O_NONBLOCK)
1127+
flags |= SPLICE_F_NONBLOCK;
1128+
11261129
return splice_pipe_to_pipe(ipipe, opipe, len, flags);
11271130
}
11281131

@@ -1148,6 +1151,9 @@ static long do_splice(struct file *in, loff_t __user *off_in,
11481151
if (unlikely(ret < 0))
11491152
return ret;
11501153

1154+
if (in->f_flags & O_NONBLOCK)
1155+
flags |= SPLICE_F_NONBLOCK;
1156+
11511157
file_start_write(out);
11521158
ret = do_splice_from(ipipe, out, &offset, len, flags);
11531159
file_end_write(out);
@@ -1172,6 +1178,9 @@ static long do_splice(struct file *in, loff_t __user *off_in,
11721178
offset = in->f_pos;
11731179
}
11741180

1181+
if (out->f_flags & O_NONBLOCK)
1182+
flags |= SPLICE_F_NONBLOCK;
1183+
11751184
pipe_lock(opipe);
11761185
ret = wait_for_space(opipe, flags);
11771186
if (!ret)
@@ -1717,6 +1726,9 @@ static long do_tee(struct file *in, struct file *out, size_t len,
17171726
* copying the data.
17181727
*/
17191728
if (ipipe && opipe && ipipe != opipe) {
1729+
if ((in->f_flags | out->f_flags) & O_NONBLOCK)
1730+
flags |= SPLICE_F_NONBLOCK;
1731+
17201732
/*
17211733
* Keep going, unless we encounter an error. The ipipe/opipe
17221734
* ordering doesn't really matter.

0 commit comments

Comments
 (0)