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

Commit 9ca68662 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* 'tee' of git://brick.kernel.dk/data/git/linux-2.6-block:
  [PATCH] splice: add support for sys_tee()
  [PATCH] splice: pass offset around for ->splice_read() and ->splice_write()
parents e57a5059 70524490
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -314,3 +314,4 @@ ENTRY(sys_call_table)
	.long sys_get_robust_list
	.long sys_splice
	.long sys_sync_file_range
	.long sys_tee			/* 315 */
+1 −0
Original line number Diff line number Diff line
@@ -1609,5 +1609,6 @@ sys_call_table:
	data8 sys_set_robust_list
	data8 sys_get_robust_list
	data8 sys_sync_file_range		// 1300
	data8 sys_tee

	.org sys_call_table + 8*NR_syscalls	// guard against failures to increase NR_syscalls
+1 −0
Original line number Diff line number Diff line
@@ -323,3 +323,4 @@ COMPAT_SYS(pselect6)
COMPAT_SYS(ppoll)
SYSCALL(unshare)
SYSCALL(splice)
SYSCALL(tee)
+7 −0
Original line number Diff line number Diff line
@@ -131,12 +131,19 @@ static int anon_pipe_buf_steal(struct pipe_inode_info *pipe,
	return 0;
}

static void anon_pipe_buf_get(struct pipe_inode_info *info,
			      struct pipe_buffer *buf)
{
	page_cache_get(buf->page);
}

static struct pipe_buf_operations anon_pipe_buf_ops = {
	.can_merge = 1,
	.map = anon_pipe_buf_map,
	.unmap = anon_pipe_buf_unmap,
	.release = anon_pipe_buf_release,
	.steal = anon_pipe_buf_steal,
	.get = anon_pipe_buf_get,
};

static ssize_t
+230 −42
Original line number Diff line number Diff line
@@ -125,12 +125,19 @@ static void page_cache_pipe_buf_unmap(struct pipe_inode_info *info,
	kunmap(buf->page);
}

static void page_cache_pipe_buf_get(struct pipe_inode_info *info,
				    struct pipe_buffer *buf)
{
	page_cache_get(buf->page);
}

static struct pipe_buf_operations page_cache_pipe_buf_ops = {
	.can_merge = 0,
	.map = page_cache_pipe_buf_map,
	.unmap = page_cache_pipe_buf_unmap,
	.release = page_cache_pipe_buf_release,
	.steal = page_cache_pipe_buf_steal,
	.get = page_cache_pipe_buf_get,
};

/*
@@ -231,8 +238,9 @@ static ssize_t move_to_pipe(struct pipe_inode_info *pipe, struct page **pages,
}

static int
__generic_file_splice_read(struct file *in, struct pipe_inode_info *pipe,
			   size_t len, unsigned int flags)
__generic_file_splice_read(struct file *in, loff_t *ppos,
			   struct pipe_inode_info *pipe, size_t len,
			   unsigned int flags)
{
	struct address_space *mapping = in->f_mapping;
	unsigned int offset, nr_pages;
@@ -241,8 +249,8 @@ __generic_file_splice_read(struct file *in, struct pipe_inode_info *pipe,
	pgoff_t index;
	int i, error;

	index = in->f_pos >> PAGE_CACHE_SHIFT;
	offset = in->f_pos & ~PAGE_CACHE_MASK;
	index = *ppos >> PAGE_CACHE_SHIFT;
	offset = *ppos & ~PAGE_CACHE_MASK;
	nr_pages = (len + offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;

	if (nr_pages > PIPE_BUFFERS)
@@ -348,8 +356,9 @@ __generic_file_splice_read(struct file *in, struct pipe_inode_info *pipe,
 *
 * Will read pages from given file and fill them into a pipe.
 */
ssize_t generic_file_splice_read(struct file *in, struct pipe_inode_info *pipe,
				 size_t len, unsigned int flags)
ssize_t generic_file_splice_read(struct file *in, loff_t *ppos,
				 struct pipe_inode_info *pipe, size_t len,
				 unsigned int flags)
{
	ssize_t spliced;
	int ret;
@@ -358,12 +367,12 @@ ssize_t generic_file_splice_read(struct file *in, struct pipe_inode_info *pipe,
	spliced = 0;

	while (len) {
		ret = __generic_file_splice_read(in, pipe, len, flags);
		ret = __generic_file_splice_read(in, ppos, pipe, len, flags);

		if (ret <= 0)
			break;

		in->f_pos += ret;
		*ppos += ret;
		len -= ret;
		spliced += ret;

@@ -561,7 +570,7 @@ typedef int (splice_actor)(struct pipe_inode_info *, struct pipe_buffer *,
 * to the wanted destination. See pipe_to_file/pipe_to_sendpage above.
 */
static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out,
			      size_t len, unsigned int flags,
			      loff_t *ppos, size_t len, unsigned int flags,
			      splice_actor *actor)
{
	int ret, do_wakeup, err;
@@ -573,7 +582,7 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out,
	sd.total_len = len;
	sd.flags = flags;
	sd.file = out;
	sd.pos = out->f_pos;
	sd.pos = *ppos;

	if (pipe->inode)
		mutex_lock(&pipe->inode->i_mutex);
@@ -656,9 +665,7 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out,
		kill_fasync(&pipe->fasync_writers, SIGIO, POLL_OUT);
	}

	out->f_pos = sd.pos;
	return ret;

}

/**
@@ -674,12 +681,12 @@ static ssize_t move_from_pipe(struct pipe_inode_info *pipe, struct file *out,
 */
ssize_t
generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out,
			  size_t len, unsigned int flags)
			  loff_t *ppos, size_t len, unsigned int flags)
{
	struct address_space *mapping = out->f_mapping;
	ssize_t ret;

	ret = move_from_pipe(pipe, out, len, flags, pipe_to_file);
	ret = move_from_pipe(pipe, out, ppos, len, flags, pipe_to_file);

	/*
	 * If file or inode is SYNC and we actually wrote some data, sync it.
@@ -715,9 +722,9 @@ EXPORT_SYMBOL(generic_file_splice_write);
 *
 */
ssize_t generic_splice_sendpage(struct pipe_inode_info *pipe, struct file *out,
				size_t len, unsigned int flags)
				loff_t *ppos, size_t len, unsigned int flags)
{
	return move_from_pipe(pipe, out, len, flags, pipe_to_sendpage);
	return move_from_pipe(pipe, out, ppos, len, flags, pipe_to_sendpage);
}

EXPORT_SYMBOL(generic_splice_sendpage);
@@ -726,9 +733,8 @@ EXPORT_SYMBOL(generic_splice_sendpage);
 * Attempt to initiate a splice from pipe to file.
 */
static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
			   size_t len, unsigned int flags)
			   loff_t *ppos, size_t len, unsigned int flags)
{
	loff_t pos;
	int ret;

	if (unlikely(!out->f_op || !out->f_op->splice_write))
@@ -737,22 +743,21 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
	if (unlikely(!(out->f_mode & FMODE_WRITE)))
		return -EBADF;

	pos = out->f_pos;

	ret = rw_verify_area(WRITE, out, &pos, len);
	ret = rw_verify_area(WRITE, out, ppos, len);
	if (unlikely(ret < 0))
		return ret;

	return out->f_op->splice_write(pipe, out, len, flags);
	return out->f_op->splice_write(pipe, out, ppos, len, flags);
}

/*
 * Attempt to initiate a splice from a file to a pipe.
 */
static long do_splice_to(struct file *in, struct pipe_inode_info *pipe,
			 size_t len, unsigned int flags)
static long do_splice_to(struct file *in, loff_t *ppos,
			 struct pipe_inode_info *pipe, size_t len,
			 unsigned int flags)
{
	loff_t pos, isize, left;
	loff_t isize, left;
	int ret;

	if (unlikely(!in->f_op || !in->f_op->splice_read))
@@ -761,28 +766,27 @@ static long do_splice_to(struct file *in, struct pipe_inode_info *pipe,
	if (unlikely(!(in->f_mode & FMODE_READ)))
		return -EBADF;

	pos = in->f_pos;

	ret = rw_verify_area(READ, in, &pos, len);
	ret = rw_verify_area(READ, in, ppos, len);
	if (unlikely(ret < 0))
		return ret;

	isize = i_size_read(in->f_mapping->host);
	if (unlikely(in->f_pos >= isize))
	if (unlikely(*ppos >= isize))
		return 0;
	
	left = isize - in->f_pos;
	left = isize - *ppos;
	if (unlikely(left < len))
		len = left;

	return in->f_op->splice_read(in, pipe, len, flags);
	return in->f_op->splice_read(in, ppos, pipe, len, flags);
}

long do_splice_direct(struct file *in, struct file *out, size_t len,
		      unsigned int flags)
long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
		      size_t len, unsigned int flags)
{
	struct pipe_inode_info *pipe;
	long ret, bytes;
	loff_t out_off;
	umode_t i_mode;
	int i;

@@ -820,6 +824,7 @@ long do_splice_direct(struct file *in, struct file *out, size_t len,
	 */
	ret = 0;
	bytes = 0;
	out_off = 0;

	while (len) {
		size_t read_len, max_read_len;
@@ -829,7 +834,7 @@ long do_splice_direct(struct file *in, struct file *out, size_t len,
		 */
		max_read_len = min(len, (size_t)(PIPE_BUFFERS*PAGE_SIZE));

		ret = do_splice_to(in, pipe, max_read_len, flags);
		ret = do_splice_to(in, ppos, pipe, max_read_len, flags);
		if (unlikely(ret < 0))
			goto out_release;

@@ -840,7 +845,7 @@ long do_splice_direct(struct file *in, struct file *out, size_t len,
		 * must not do the output in nonblocking mode as then we
		 * could get stuck data in the internal pipe:
		 */
		ret = do_splice_from(pipe, out, read_len,
		ret = do_splice_from(pipe, out, &out_off, read_len,
				     flags & ~SPLICE_F_NONBLOCK);
		if (unlikely(ret < 0))
			goto out_release;
@@ -898,6 +903,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,
		      size_t len, unsigned int flags)
{
	struct pipe_inode_info *pipe;
	loff_t offset, *off;

	pipe = in->f_dentry->d_inode->i_pipe;
	if (pipe) {
@@ -906,12 +912,13 @@ static long do_splice(struct file *in, loff_t __user *off_in,
		if (off_out) {
			if (out->f_op->llseek == no_llseek)
				return -EINVAL;
			if (copy_from_user(&out->f_pos, off_out,
					   sizeof(loff_t)))
			if (copy_from_user(&offset, off_out, sizeof(loff_t)))
				return -EFAULT;
		}
			off = &offset;
		} else
			off = &out->f_pos;

		return do_splice_from(pipe, out, len, flags);
		return do_splice_from(pipe, out, off, len, flags);
	}

	pipe = out->f_dentry->d_inode->i_pipe;
@@ -921,11 +928,13 @@ static long do_splice(struct file *in, loff_t __user *off_in,
		if (off_in) {
			if (in->f_op->llseek == no_llseek)
				return -EINVAL;
			if (copy_from_user(&in->f_pos, off_in, sizeof(loff_t)))
			if (copy_from_user(&offset, off_in, sizeof(loff_t)))
				return -EFAULT;
		}
			off = &offset;
		} else
			off = &in->f_pos;

		return do_splice_to(in, pipe, len, flags);
		return do_splice_to(in, off, pipe, len, flags);
	}

	return -EINVAL;
@@ -961,3 +970,182 @@ asmlinkage long sys_splice(int fd_in, loff_t __user *off_in,

	return error;
}

/*
 * Link contents of ipipe to opipe.
 */
static int link_pipe(struct pipe_inode_info *ipipe,
		     struct pipe_inode_info *opipe,
		     size_t len, unsigned int flags)
{
	struct pipe_buffer *ibuf, *obuf;
	int ret = 0, do_wakeup = 0, i;

	/*
	 * Potential ABBA deadlock, work around it by ordering lock
	 * grabbing by inode address. Otherwise two different processes
	 * could deadlock (one doing tee from A -> B, the other from B -> A).
	 */
	if (ipipe->inode < opipe->inode) {
		mutex_lock(&ipipe->inode->i_mutex);
		mutex_lock(&opipe->inode->i_mutex);
	} else {
		mutex_lock(&opipe->inode->i_mutex);
		mutex_lock(&ipipe->inode->i_mutex);
	}

	for (i = 0;; i++) {
		if (!opipe->readers) {
			send_sig(SIGPIPE, current, 0);
			if (!ret)
				ret = -EPIPE;
			break;
		}
		if (ipipe->nrbufs - i) {
			ibuf = ipipe->bufs + ((ipipe->curbuf + i) & (PIPE_BUFFERS - 1));

			/*
			 * If we have room, fill this buffer
			 */
			if (opipe->nrbufs < PIPE_BUFFERS) {
				int nbuf = (opipe->curbuf + opipe->nrbufs) & (PIPE_BUFFERS - 1);

				/*
				 * Get a reference to this pipe buffer,
				 * so we can copy the contents over.
				 */
				ibuf->ops->get(ipipe, ibuf);

				obuf = opipe->bufs + nbuf;
				*obuf = *ibuf;

				if (obuf->len > len)
					obuf->len = len;

				opipe->nrbufs++;
				do_wakeup = 1;
				ret += obuf->len;
				len -= obuf->len;

				if (!len)
					break;
				if (opipe->nrbufs < PIPE_BUFFERS)
					continue;
			}

			/*
			 * We have input available, but no output room.
			 * If we already copied data, return that.
			 */
			if (flags & SPLICE_F_NONBLOCK) {
				if (!ret)
					ret = -EAGAIN;
				break;
			}
			if (signal_pending(current)) {
				if (!ret)
					ret = -ERESTARTSYS;
				break;
			}
			if (do_wakeup) {
				smp_mb();
				if (waitqueue_active(&opipe->wait))
					wake_up_interruptible(&opipe->wait);
				kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN);
				do_wakeup = 0;
			}

			opipe->waiting_writers++;
			pipe_wait(opipe);
			opipe->waiting_writers--;
			continue;
		}

		/*
		 * No input buffers, do the usual checks for available
		 * writers and blocking and wait if necessary
		 */
		if (!ipipe->writers)
			break;
		if (!ipipe->waiting_writers) {
			if (ret)
				break;
		}
		if (flags & SPLICE_F_NONBLOCK) {
			if (!ret)
				ret = -EAGAIN;
			break;
		}
		if (signal_pending(current)) {
			if (!ret)
				ret = -ERESTARTSYS;
			break;
		}

		if (waitqueue_active(&ipipe->wait))
			wake_up_interruptible_sync(&ipipe->wait);
		kill_fasync(&ipipe->fasync_writers, SIGIO, POLL_OUT);

		pipe_wait(ipipe);
	}

	mutex_unlock(&ipipe->inode->i_mutex);
	mutex_unlock(&opipe->inode->i_mutex);

	if (do_wakeup) {
		smp_mb();
		if (waitqueue_active(&opipe->wait))
			wake_up_interruptible(&opipe->wait);
		kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN);
	}

	return ret;
}

/*
 * This is a tee(1) implementation that works on pipes. It doesn't copy
 * any data, it simply references the 'in' pages on the 'out' pipe.
 * The 'flags' used are the SPLICE_F_* variants, currently the only
 * applicable one is SPLICE_F_NONBLOCK.
 */
static long do_tee(struct file *in, struct file *out, size_t len,
		   unsigned int flags)
{
	struct pipe_inode_info *ipipe = in->f_dentry->d_inode->i_pipe;
	struct pipe_inode_info *opipe = out->f_dentry->d_inode->i_pipe;

	/*
	 * Link ipipe to the two output pipes, consuming as we go along.
	 */
	if (ipipe && opipe)
		return link_pipe(ipipe, opipe, len, flags);

	return -EINVAL;
}

asmlinkage long sys_tee(int fdin, int fdout, size_t len, unsigned int flags)
{
	struct file *in;
	int error, fput_in;

	if (unlikely(!len))
		return 0;

	error = -EBADF;
	in = fget_light(fdin, &fput_in);
	if (in) {
		if (in->f_mode & FMODE_READ) {
			int fput_out;
			struct file *out = fget_light(fdout, &fput_out);

			if (out) {
				if (out->f_mode & FMODE_WRITE)
					error = do_tee(in, out, len, flags);
				fput_light(out, fput_out);
			}
		}
 		fput_light(in, fput_in);
 	}

	return error;
}
Loading