summaryrefslogtreecommitdiffstats
path: root/fs/splice.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-05-06 17:15:20 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2023-05-06 17:15:20 +0200
commit7644c8231987288e7aae378d2ff3c56a980d1988 (patch)
tree7ac262934dd9e596b80f5f0d85282937692192b4 /fs/splice.c
parentMerge tag 'sound-fix-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/gi... (diff)
parentpipe: set FMODE_NOWAIT on pipes (diff)
downloadlinux-7644c8231987288e7aae378d2ff3c56a980d1988.tar.xz
linux-7644c8231987288e7aae378d2ff3c56a980d1988.zip
Merge tag 'pipe-nonblock-2023-05-06' of git://git.kernel.dk/linux
Pull nonblocking pipe io_uring support from Jens Axboe: "Here's the revised edition of the FMODE_NOWAIT support for pipes, in which we just flag it as such supporting FMODE_NOWAIT unconditionally, but clear it if we ever end up using splice/vmsplice on the pipe. The pipe read/write side is perfectly fine for nonblocking IO, however splice and vmsplice can potentially wait for IO with the pipe lock held" * tag 'pipe-nonblock-2023-05-06' of git://git.kernel.dk/linux: pipe: set FMODE_NOWAIT on pipes splice: clear FMODE_NOWAIT on file if splice/vmsplice is used
Diffstat (limited to 'fs/splice.c')
-rw-r--r--fs/splice.c34
1 files changed, 30 insertions, 4 deletions
diff --git a/fs/splice.c b/fs/splice.c
index 0af8d150394f..3e06611d19ae 100644
--- a/fs/splice.c
+++ b/fs/splice.c
@@ -39,6 +39,22 @@
#include "internal.h"
/*
+ * Splice doesn't support FMODE_NOWAIT. Since pipes may set this flag to
+ * indicate they support non-blocking reads or writes, we must clear it
+ * here if set to avoid blocking other users of this pipe if splice is
+ * being done on it.
+ */
+static noinline void noinline pipe_clear_nowait(struct file *file)
+{
+ fmode_t fmode = READ_ONCE(file->f_mode);
+
+ do {
+ if (!(fmode & FMODE_NOWAIT))
+ break;
+ } while (!try_cmpxchg(&file->f_mode, &fmode, fmode & ~FMODE_NOWAIT));
+}
+
+/*
* Attempt to steal a page from a pipe buffer. This should perhaps go into
* a vm helper function, it's already simplified quite a bit by the
* addition of remove_mapping(). If success is returned, the caller may
@@ -1219,10 +1235,16 @@ static long __do_splice(struct file *in, loff_t __user *off_in,
ipipe = get_pipe_info(in, true);
opipe = get_pipe_info(out, true);
- if (ipipe && off_in)
- return -ESPIPE;
- if (opipe && off_out)
- return -ESPIPE;
+ if (ipipe) {
+ if (off_in)
+ return -ESPIPE;
+ pipe_clear_nowait(in);
+ }
+ if (opipe) {
+ if (off_out)
+ return -ESPIPE;
+ pipe_clear_nowait(out);
+ }
if (off_out) {
if (copy_from_user(&offset, off_out, sizeof(loff_t)))
@@ -1319,6 +1341,8 @@ static long vmsplice_to_user(struct file *file, struct iov_iter *iter,
if (!pipe)
return -EBADF;
+ pipe_clear_nowait(file);
+
if (sd.total_len) {
pipe_lock(pipe);
ret = __splice_from_pipe(pipe, &sd, pipe_to_user);
@@ -1347,6 +1371,8 @@ static long vmsplice_to_pipe(struct file *file, struct iov_iter *iter,
if (!pipe)
return -EBADF;
+ pipe_clear_nowait(file);
+
pipe_lock(pipe);
ret = wait_for_space(pipe, flags);
if (!ret)