Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8924feff authored by Al Viro's avatar Al Viro
Browse files

splice: lift pipe_lock out of splice_to_pipe()



* splice_to_pipe() stops at pipe overflow and does *not* take pipe_lock
* ->splice_read() instances do the same
* vmsplice_to_pipe() and do_splice() (ultimate callers of splice_to_pipe())
  arrange for waiting, looping, etc. themselves.

That should make pipe_lock the outermost one.

Unfortunately, existing rules for the amount passed by vmsplice_to_pipe()
and do_splice() are quite ugly _and_ userland code can be easily broken
by changing those.  It's not even "no more than the maximal capacity of
this pipe" - it's "once we'd fed pipe->nr_buffers pages into the pipe,
leave instead of waiting".

Considering how poorly these rules are documented, let's try "wait for some
space to appear, unless given SPLICE_F_NONBLOCK, then push into pipe
and if we run into overflow, we are done".

Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent db85a9eb
Loading
Loading
Loading
Loading
+0 −2
Original line number Original line Diff line number Diff line
@@ -1364,7 +1364,6 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
		goto out;
		goto out;


	ret = 0;
	ret = 0;
	pipe_lock(pipe);


	if (!pipe->readers) {
	if (!pipe->readers) {
		send_sig(SIGPIPE, current, 0);
		send_sig(SIGPIPE, current, 0);
@@ -1400,7 +1399,6 @@ static ssize_t fuse_dev_splice_read(struct file *in, loff_t *ppos,
	}
	}


out_unlock:
out_unlock:
	pipe_unlock(pipe);


	if (do_wakeup) {
	if (do_wakeup) {
		smp_mb();
		smp_mb();
+58 −71
Original line number Original line Diff line number Diff line
@@ -183,26 +183,18 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
		       struct splice_pipe_desc *spd)
		       struct splice_pipe_desc *spd)
{
{
	unsigned int spd_pages = spd->nr_pages;
	unsigned int spd_pages = spd->nr_pages;
	int ret, do_wakeup, page_nr;
	int ret = 0, page_nr = 0;


	if (!spd_pages)
	if (!spd_pages)
		return 0;
		return 0;


	ret = 0;
	if (unlikely(!pipe->readers)) {
	do_wakeup = 0;
	page_nr = 0;

	pipe_lock(pipe);

	for (;;) {
		if (!pipe->readers) {
		send_sig(SIGPIPE, current, 0);
		send_sig(SIGPIPE, current, 0);
			if (!ret)
		ret = -EPIPE;
		ret = -EPIPE;
			break;
		goto out;
	}
	}


		if (pipe->nrbufs < pipe->buffers) {
	while (pipe->nrbufs < pipe->buffers) {
		int newbuf = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
		int newbuf = (pipe->curbuf + pipe->nrbufs) & (pipe->buffers - 1);
		struct pipe_buffer *buf = pipe->bufs + newbuf;
		struct pipe_buffer *buf = pipe->bufs + newbuf;


@@ -218,44 +210,14 @@ ssize_t splice_to_pipe(struct pipe_inode_info *pipe,
		page_nr++;
		page_nr++;
		ret += buf->len;
		ret += buf->len;


			if (pipe->files)
				do_wakeup = 1;

		if (!--spd->nr_pages)
		if (!--spd->nr_pages)
				break;
			if (pipe->nrbufs < pipe->buffers)
				continue;

			break;
			break;
	}
	}


		if (spd->flags & SPLICE_F_NONBLOCK) {
	if (!ret)
	if (!ret)
		ret = -EAGAIN;
		ret = -EAGAIN;
			break;
		}

		if (signal_pending(current)) {
			if (!ret)
				ret = -ERESTARTSYS;
			break;
		}

		if (do_wakeup) {
			wakeup_pipe_readers(pipe);
			do_wakeup = 0;
		}

		pipe->waiting_writers++;
		pipe_wait(pipe);
		pipe->waiting_writers--;
	}

	pipe_unlock(pipe);

	if (do_wakeup)
		wakeup_pipe_readers(pipe);


out:
	while (page_nr < spd_pages)
	while (page_nr < spd_pages)
		spd->spd_release(spd, page_nr++);
		spd->spd_release(spd, page_nr++);


@@ -1339,6 +1301,20 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
}
}
EXPORT_SYMBOL(do_splice_direct);
EXPORT_SYMBOL(do_splice_direct);


static int wait_for_space(struct pipe_inode_info *pipe, unsigned flags)
{
	while (pipe->nrbufs == pipe->buffers) {
		if (flags & SPLICE_F_NONBLOCK)
			return -EAGAIN;
		if (signal_pending(current))
			return -ERESTARTSYS;
		pipe->waiting_writers++;
		pipe_wait(pipe);
		pipe->waiting_writers--;
	}
	return 0;
}

static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
			       struct pipe_inode_info *opipe,
			       struct pipe_inode_info *opipe,
			       size_t len, unsigned int flags);
			       size_t len, unsigned int flags);
@@ -1421,8 +1397,13 @@ static long do_splice(struct file *in, loff_t __user *off_in,
			offset = in->f_pos;
			offset = in->f_pos;
		}
		}


		pipe_lock(opipe);
		ret = wait_for_space(opipe, flags);
		if (!ret)
			ret = do_splice_to(in, &offset, opipe, len, flags);
			ret = do_splice_to(in, &offset, opipe, len, flags);

		pipe_unlock(opipe);
		if (ret > 0)
			wakeup_pipe_readers(opipe);
		if (!off_in)
		if (!off_in)
			in->f_pos = offset;
			in->f_pos = offset;
		else if (copy_to_user(off_in, &offset, sizeof(loff_t)))
		else if (copy_to_user(off_in, &offset, sizeof(loff_t)))
@@ -1546,6 +1527,9 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *uiov,
		return -ENOMEM;
		return -ENOMEM;
	}
	}


	pipe_lock(pipe);
	ret = wait_for_space(pipe, flags);
	if (!ret) {
		spd.nr_pages = get_iovec_page_array(&from, spd.pages,
		spd.nr_pages = get_iovec_page_array(&from, spd.pages,
						    spd.partial,
						    spd.partial,
						    spd.nr_pages_max);
						    spd.nr_pages_max);
@@ -1553,7 +1537,10 @@ static long vmsplice_to_pipe(struct file *file, const struct iovec __user *uiov,
			ret = spd.nr_pages;
			ret = spd.nr_pages;
		else
		else
			ret = splice_to_pipe(pipe, &spd);
			ret = splice_to_pipe(pipe, &spd);

	}
	pipe_unlock(pipe);
	if (ret > 0)
		wakeup_pipe_readers(pipe);
	splice_shrink_spd(&spd);
	splice_shrink_spd(&spd);
	kfree(iov);
	kfree(iov);
	return ret;
	return ret;