#include <linux/export.h>
#include <linux/syscalls.h>
#include <linux/uio.h>
+#include <linux/fsnotify.h>
#include <linux/security.h>
#include <linux/gfp.h>
#include <linux/socket.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
* a vm helper function, it's already simplified quite a bit by the
* addition of remove_mapping(). If success is returned, the caller may
ret = do_splice_from(ipipe, out, &offset, len, flags);
file_end_write(out);
+ if (ret > 0)
+ fsnotify_modify(out);
+
if (!off_out)
out->f_pos = offset;
else
flags |= SPLICE_F_NONBLOCK;
ret = splice_file_to_pipe(in, opipe, &offset, len, flags);
+
+ if (ret > 0)
+ fsnotify_access(in);
+
if (!off_in)
in->f_pos = offset;
else
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)))
if (!pipe)
return -EBADF;
+ pipe_clear_nowait(file);
+
if (sd.total_len) {
pipe_lock(pipe);
ret = __splice_from_pipe(pipe, &sd, pipe_to_user);
if (!pipe)
return -EBADF;
+ pipe_clear_nowait(file);
+
pipe_lock(pipe);
ret = wait_for_space(pipe, flags);
if (!ret)