diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-05-06 17:15:20 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-05-06 17:15:20 +0200 |
commit | 7644c8231987288e7aae378d2ff3c56a980d1988 (patch) | |
tree | 7ac262934dd9e596b80f5f0d85282937692192b4 /fs/splice.c | |
parent | Merge tag 'sound-fix-6.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/gi... (diff) | |
parent | pipe: set FMODE_NOWAIT on pipes (diff) | |
download | linux-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.c | 34 |
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) |