Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 147033
b: refs/heads/master
c: 7c77f0b
h: refs/heads/master
i:
  147031: 8be8498
v: v3
  • Loading branch information
Miklos Szeredi authored and Jens Axboe committed May 11, 2009
1 parent 12a0403 commit 93a733d
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 12 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: b1f744937f1be3e6d3009382a755679133cf782d
refs/heads/master: 7c77f0b3f9208c339a4b40737bb2cb0f0319bb8d
162 changes: 151 additions & 11 deletions trunk/fs/splice.c
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,9 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
return ret;
}

static int splice_pipe_to_pipe(struct pipe_inode_info *ipipe,
struct pipe_inode_info *opipe,
size_t len, unsigned int flags);
/*
* After the inode slimming patch, i_pipe/i_bdev/i_cdev share the same
* location, so checking ->i_pipe is not enough to verify that this is a
Expand All @@ -1132,12 +1135,32 @@ static long do_splice(struct file *in, loff_t __user *off_in,
struct file *out, loff_t __user *off_out,
size_t len, unsigned int flags)
{
struct pipe_inode_info *pipe;
struct pipe_inode_info *ipipe;
struct pipe_inode_info *opipe;
loff_t offset, *off;
long ret;

pipe = pipe_info(in->f_path.dentry->d_inode);
if (pipe) {
ipipe = pipe_info(in->f_path.dentry->d_inode);
opipe = pipe_info(out->f_path.dentry->d_inode);

if (ipipe && opipe) {
if (off_in || off_out)
return -ESPIPE;

if (!(in->f_mode & FMODE_READ))
return -EBADF;

if (!(out->f_mode & FMODE_WRITE))
return -EBADF;

/* Splicing to self would be fun, but... */
if (ipipe == opipe)
return -EINVAL;

return splice_pipe_to_pipe(ipipe, opipe, len, flags);
}

if (ipipe) {
if (off_in)
return -ESPIPE;
if (off_out) {
Expand All @@ -1149,16 +1172,15 @@ static long do_splice(struct file *in, loff_t __user *off_in,
} else
off = &out->f_pos;

ret = do_splice_from(pipe, out, off, len, flags);
ret = do_splice_from(ipipe, out, off, len, flags);

if (off_out && copy_to_user(off_out, off, sizeof(loff_t)))
ret = -EFAULT;

return ret;
}

pipe = pipe_info(out->f_path.dentry->d_inode);
if (pipe) {
if (opipe) {
if (off_out)
return -ESPIPE;
if (off_in) {
Expand All @@ -1170,7 +1192,7 @@ static long do_splice(struct file *in, loff_t __user *off_in,
} else
off = &in->f_pos;

ret = do_splice_to(in, off, pipe, len, flags);
ret = do_splice_to(in, off, opipe, len, flags);

if (off_in && copy_to_user(off_in, off, sizeof(loff_t)))
ret = -EFAULT;
Expand Down Expand Up @@ -1511,7 +1533,7 @@ SYSCALL_DEFINE6(splice, int, fd_in, loff_t __user *, off_in,
* Make sure there's data to read. Wait for input if we can, otherwise
* return an appropriate error.
*/
static int link_ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
static int ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
{
int ret;

Expand Down Expand Up @@ -1549,7 +1571,7 @@ static int link_ipipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
* Make sure there's writeable room. Wait for room if we can, otherwise
* return an appropriate error.
*/
static int link_opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
static int opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
{
int ret;

Expand Down Expand Up @@ -1586,6 +1608,124 @@ static int link_opipe_prep(struct pipe_inode_info *pipe, unsigned int flags)
return ret;
}

/*
* Splice contents of ipipe to opipe.
*/
static int splice_pipe_to_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, nbuf;
bool input_wakeup = false;


retry:
ret = ipipe_prep(ipipe, flags);
if (ret)
return ret;

ret = opipe_prep(opipe, flags);
if (ret)
return ret;

/*
* Potential ABBA deadlock, work around it by ordering lock
* grabbing by pipe info address. Otherwise two different processes
* could deadlock (one doing tee from A -> B, the other from B -> A).
*/
pipe_double_lock(ipipe, opipe);

do {
if (!opipe->readers) {
send_sig(SIGPIPE, current, 0);
if (!ret)
ret = -EPIPE;
break;
}

if (!ipipe->nrbufs && !ipipe->writers)
break;

/*
* Cannot make any progress, because either the input
* pipe is empty or the output pipe is full.
*/
if (!ipipe->nrbufs || opipe->nrbufs >= PIPE_BUFFERS) {
/* Already processed some buffers, break */
if (ret)
break;

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

/*
* We raced with another reader/writer and haven't
* managed to process any buffers. A zero return
* value means EOF, so retry instead.
*/
pipe_unlock(ipipe);
pipe_unlock(opipe);
goto retry;
}

ibuf = ipipe->bufs + ipipe->curbuf;
nbuf = (opipe->curbuf + opipe->nrbufs) % PIPE_BUFFERS;
obuf = opipe->bufs + nbuf;

if (len >= ibuf->len) {
/*
* Simply move the whole buffer from ipipe to opipe
*/
*obuf = *ibuf;
ibuf->ops = NULL;
opipe->nrbufs++;
ipipe->curbuf = (ipipe->curbuf + 1) % PIPE_BUFFERS;
ipipe->nrbufs--;
input_wakeup = true;
} else {
/*
* Get a reference to this pipe buffer,
* so we can copy the contents over.
*/
ibuf->ops->get(ipipe, ibuf);
*obuf = *ibuf;

/*
* Don't inherit the gift flag, we need to
* prevent multiple steals of this page.
*/
obuf->flags &= ~PIPE_BUF_FLAG_GIFT;

obuf->len = len;
opipe->nrbufs++;
ibuf->offset += obuf->len;
ibuf->len -= obuf->len;
}
ret += obuf->len;
len -= obuf->len;
} while (len);

pipe_unlock(ipipe);
pipe_unlock(opipe);

/*
* If we put data in the output pipe, wakeup any potential readers.
*/
if (ret > 0) {
smp_mb();
if (waitqueue_active(&opipe->wait))
wake_up_interruptible(&opipe->wait);
kill_fasync(&opipe->fasync_readers, SIGIO, POLL_IN);
}
if (input_wakeup)
wakeup_pipe_writers(ipipe);

return ret;
}

/*
* Link contents of ipipe to opipe.
*/
Expand Down Expand Up @@ -1690,9 +1830,9 @@ static long do_tee(struct file *in, struct file *out, size_t len,
* Keep going, unless we encounter an error. The ipipe/opipe
* ordering doesn't really matter.
*/
ret = link_ipipe_prep(ipipe, flags);
ret = ipipe_prep(ipipe, flags);
if (!ret) {
ret = link_opipe_prep(opipe, flags);
ret = opipe_prep(opipe, flags);
if (!ret)
ret = link_pipe(ipipe, opipe, len, flags);
}
Expand Down

0 comments on commit 93a733d

Please sign in to comment.