diff options
author | Miklos Szeredi <miklos@szeredi.hu> | 2009-04-14 19:48:38 +0200 |
---|---|---|
committer | Jens Axboe <jens.axboe@oracle.com> | 2009-04-15 12:10:11 +0200 |
commit | eb443e5a25d43996deb62b9bcee1a4ce5dea2ead (patch) | |
tree | 7f63bb98302137e249904d608d5bb4b919a987ed /fs/splice.c | |
parent | 2933970b960223076d6affcf7a77e2bc546b8102 (diff) | |
download | lwn-eb443e5a25d43996deb62b9bcee1a4ce5dea2ead.tar.gz lwn-eb443e5a25d43996deb62b9bcee1a4ce5dea2ead.zip |
splice: fix i_mutex locking in generic_splice_write()
Rearrange locking of i_mutex on destination so it's only held while
buffers are copied with the pipe_to_file() actor, and not while
waiting for more data on the pipe.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
Diffstat (limited to 'fs/splice.c')
-rw-r--r-- | fs/splice.c | 34 |
1 files changed, 23 insertions, 11 deletions
diff --git a/fs/splice.c b/fs/splice.c index 349576b2c75a..a1f595b9db40 100644 --- a/fs/splice.c +++ b/fs/splice.c @@ -895,17 +895,29 @@ generic_file_splice_write(struct pipe_inode_info *pipe, struct file *out, }; ssize_t ret; - WARN_ON(S_ISFIFO(inode->i_mode)); - mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT); - ret = file_remove_suid(out); - if (likely(!ret)) { - if (pipe->inode) - mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_CHILD); - ret = __splice_from_pipe(pipe, &sd, pipe_to_file); - if (pipe->inode) - mutex_unlock(&pipe->inode->i_mutex); - } - mutex_unlock(&inode->i_mutex); + if (pipe->inode) + mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_PARENT); + + splice_from_pipe_begin(&sd); + do { + ret = splice_from_pipe_next(pipe, &sd); + if (ret <= 0) + break; + + mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD); + ret = file_remove_suid(out); + if (!ret) + ret = splice_from_pipe_feed(pipe, &sd, pipe_to_file); + mutex_unlock(&inode->i_mutex); + } while (ret > 0); + splice_from_pipe_end(pipe, &sd); + + if (pipe->inode) + mutex_unlock(&pipe->inode->i_mutex); + + if (sd.num_spliced) + ret = sd.num_spliced; + if (ret > 0) { unsigned long nr_pages; |