Skip to content

Commit 0f99fc5

Browse files
committed
splice: clear FMODE_NOWAIT on file if splice/vmsplice is used
In preparation for pipes setting FMODE_NOWAIT on pipes to indicate that RWF_NOWAIT/IOCB_NOWAIT is fully supported, have splice and vmsplice clear that file flag. Splice holds the pipe lock around IO and cannot easily be refactored to avoid that, as splice and pipes are inherently tied together. By clearing FMODE_NOWAIT if splice is being used on a pipe, other users of the pipe will know that the pipe is no longer safe for RWF_NOWAIT and friends. Suggested-by: Linus Torvalds <torvalds@linux-foundation.org> Signed-off-by: Jens Axboe <axboe@kernel.dk>
1 parent 457391b commit 0f99fc5

File tree

1 file changed

+30
-4
lines changed

1 file changed

+30
-4
lines changed

fs/splice.c

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,22 @@
3737

3838
#include "internal.h"
3939

40+
/*
41+
* Splice doesn't support FMODE_NOWAIT. Since pipes may set this flag to
42+
* indicate they support non-blocking reads or writes, we must clear it
43+
* here if set to avoid blocking other users of this pipe if splice is
44+
* being done on it.
45+
*/
46+
static noinline void noinline pipe_clear_nowait(struct file *file)
47+
{
48+
fmode_t fmode = READ_ONCE(file->f_mode);
49+
50+
do {
51+
if (!(fmode & FMODE_NOWAIT))
52+
break;
53+
} while (!try_cmpxchg(&file->f_mode, &fmode, fmode & ~FMODE_NOWAIT));
54+
}
55+
4056
/*
4157
* Attempt to steal a page from a pipe buffer. This should perhaps go into
4258
* a vm helper function, it's already simplified quite a bit by the
@@ -1211,10 +1227,16 @@ static long __do_splice(struct file *in, loff_t __user *off_in,
12111227
ipipe = get_pipe_info(in, true);
12121228
opipe = get_pipe_info(out, true);
12131229

1214-
if (ipipe && off_in)
1215-
return -ESPIPE;
1216-
if (opipe && off_out)
1217-
return -ESPIPE;
1230+
if (ipipe) {
1231+
if (off_in)
1232+
return -ESPIPE;
1233+
pipe_clear_nowait(in);
1234+
}
1235+
if (opipe) {
1236+
if (off_out)
1237+
return -ESPIPE;
1238+
pipe_clear_nowait(out);
1239+
}
12181240

12191241
if (off_out) {
12201242
if (copy_from_user(&offset, off_out, sizeof(loff_t)))
@@ -1311,6 +1333,8 @@ static long vmsplice_to_user(struct file *file, struct iov_iter *iter,
13111333
if (!pipe)
13121334
return -EBADF;
13131335

1336+
pipe_clear_nowait(file);
1337+
13141338
if (sd.total_len) {
13151339
pipe_lock(pipe);
13161340
ret = __splice_from_pipe(pipe, &sd, pipe_to_user);
@@ -1339,6 +1363,8 @@ static long vmsplice_to_pipe(struct file *file, struct iov_iter *iter,
13391363
if (!pipe)
13401364
return -EBADF;
13411365

1366+
pipe_clear_nowait(file);
1367+
13421368
pipe_lock(pipe);
13431369
ret = wait_for_space(pipe, flags);
13441370
if (!ret)

0 commit comments

Comments
 (0)