Commit 7644c823 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

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
parents 0021b532 afed6271
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -976,6 +976,9 @@ static int __do_pipe_flags(int *fd, struct file **files, int flags)
	audit_fd_pair(fdr, fdw);
	fd[0] = fdr;
	fd[1] = fdw;
	/* pipe groks IOCB_NOWAIT */
	files[0]->f_mode |= FMODE_NOWAIT;
	files[1]->f_mode |= FMODE_NOWAIT;
	return 0;

 err_fdr:
+30 −4
Original line number Diff line number Diff line
@@ -38,6 +38,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
@@ -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)
	if (ipipe) {
		if (off_in)
			return -ESPIPE;
	if (opipe && off_out)
		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)