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>
This commit is contained in:
Родитель
457391b038
Коммит
0f99fc513d
34
fs/splice.c
34
fs/splice.c
|
@ -37,6 +37,22 @@
|
||||||
|
|
||||||
#include "internal.h"
|
#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
|
* 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
|
* 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,
|
||||||
ipipe = get_pipe_info(in, true);
|
ipipe = get_pipe_info(in, true);
|
||||||
opipe = get_pipe_info(out, true);
|
opipe = get_pipe_info(out, true);
|
||||||
|
|
||||||
if (ipipe && off_in)
|
if (ipipe) {
|
||||||
return -ESPIPE;
|
if (off_in)
|
||||||
if (opipe && off_out)
|
return -ESPIPE;
|
||||||
return -ESPIPE;
|
pipe_clear_nowait(in);
|
||||||
|
}
|
||||||
|
if (opipe) {
|
||||||
|
if (off_out)
|
||||||
|
return -ESPIPE;
|
||||||
|
pipe_clear_nowait(out);
|
||||||
|
}
|
||||||
|
|
||||||
if (off_out) {
|
if (off_out) {
|
||||||
if (copy_from_user(&offset, off_out, sizeof(loff_t)))
|
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,
|
||||||
if (!pipe)
|
if (!pipe)
|
||||||
return -EBADF;
|
return -EBADF;
|
||||||
|
|
||||||
|
pipe_clear_nowait(file);
|
||||||
|
|
||||||
if (sd.total_len) {
|
if (sd.total_len) {
|
||||||
pipe_lock(pipe);
|
pipe_lock(pipe);
|
||||||
ret = __splice_from_pipe(pipe, &sd, pipe_to_user);
|
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,
|
||||||
if (!pipe)
|
if (!pipe)
|
||||||
return -EBADF;
|
return -EBADF;
|
||||||
|
|
||||||
|
pipe_clear_nowait(file);
|
||||||
|
|
||||||
pipe_lock(pipe);
|
pipe_lock(pipe);
|
||||||
ret = wait_for_space(pipe, flags);
|
ret = wait_for_space(pipe, flags);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
|
|
Загрузка…
Ссылка в новой задаче