Merge tag 'pipe-nonblock-2023-05-06' of git://git.kernel.dk/linux
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 6 May 2023 15:15:20 +0000 (08:15 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 6 May 2023 15:15:20 +0000 (08:15 -0700)
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

1  2 
fs/splice.c

diff --combined fs/splice.c
@@@ -30,7 -30,6 +30,7 @@@
  #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
@@@ -1166,9 -1181,6 +1182,9 @@@ long do_splice(struct file *in, loff_t 
                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
@@@ -1219,10 -1227,16 +1235,16 @@@ static long __do_splice(struct file *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 -1333,8 +1341,8 @@@ static long vmsplice_to_user(struct fil
        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 -1363,8 +1371,8 @@@ static long vmsplice_to_pipe(struct fil
        if (!pipe)
                return -EBADF;
  
+       pipe_clear_nowait(file);
        pipe_lock(pipe);
        ret = wait_for_space(pipe, flags);
        if (!ret)