ocfs2: fix i_mutex locking in ocfs2_splice_to_file()
Rearrange locking of i_mutex on destination and call to ocfs2_rw_lock() so locks are 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>
This commit is contained in:
parent
eb443e5a25
commit
328eaaba4e
3 changed files with 81 additions and 24 deletions
|
@ -1912,6 +1912,22 @@ out_sems:
|
||||||
return written ? written : ret;
|
return written ? written : ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int ocfs2_splice_to_file(struct pipe_inode_info *pipe,
|
||||||
|
struct file *out,
|
||||||
|
struct splice_desc *sd)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, &sd->pos,
|
||||||
|
sd->total_len, 0, NULL);
|
||||||
|
if (ret < 0) {
|
||||||
|
mlog_errno(ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return splice_from_pipe_feed(pipe, sd, pipe_to_file);
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe,
|
static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe,
|
||||||
struct file *out,
|
struct file *out,
|
||||||
loff_t *ppos,
|
loff_t *ppos,
|
||||||
|
@ -1919,38 +1935,76 @@ static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe,
|
||||||
unsigned int flags)
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
struct inode *inode = out->f_path.dentry->d_inode;
|
struct address_space *mapping = out->f_mapping;
|
||||||
|
struct inode *inode = mapping->host;
|
||||||
|
struct splice_desc sd = {
|
||||||
|
.total_len = len,
|
||||||
|
.flags = flags,
|
||||||
|
.pos = *ppos,
|
||||||
|
.u.file = out,
|
||||||
|
};
|
||||||
|
|
||||||
mlog_entry("(0x%p, 0x%p, %u, '%.*s')\n", out, pipe,
|
mlog_entry("(0x%p, 0x%p, %u, '%.*s')\n", out, pipe,
|
||||||
(unsigned int)len,
|
(unsigned int)len,
|
||||||
out->f_path.dentry->d_name.len,
|
out->f_path.dentry->d_name.len,
|
||||||
out->f_path.dentry->d_name.name);
|
out->f_path.dentry->d_name.name);
|
||||||
|
|
||||||
mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
|
|
||||||
|
|
||||||
ret = ocfs2_rw_lock(inode, 1);
|
|
||||||
if (ret < 0) {
|
|
||||||
mlog_errno(ret);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, ppos, len, 0,
|
|
||||||
NULL);
|
|
||||||
if (ret < 0) {
|
|
||||||
mlog_errno(ret);
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pipe->inode)
|
if (pipe->inode)
|
||||||
mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_CHILD);
|
mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_PARENT);
|
||||||
ret = generic_file_splice_write_nolock(pipe, out, ppos, len, flags);
|
|
||||||
|
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 = ocfs2_rw_lock(inode, 1);
|
||||||
|
if (ret < 0)
|
||||||
|
mlog_errno(ret);
|
||||||
|
else {
|
||||||
|
ret = ocfs2_splice_to_file(pipe, out, &sd);
|
||||||
|
ocfs2_rw_unlock(inode, 1);
|
||||||
|
}
|
||||||
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
} while (ret > 0);
|
||||||
|
splice_from_pipe_end(pipe, &sd);
|
||||||
|
|
||||||
if (pipe->inode)
|
if (pipe->inode)
|
||||||
mutex_unlock(&pipe->inode->i_mutex);
|
mutex_unlock(&pipe->inode->i_mutex);
|
||||||
|
|
||||||
out_unlock:
|
if (sd.num_spliced)
|
||||||
ocfs2_rw_unlock(inode, 1);
|
ret = sd.num_spliced;
|
||||||
out:
|
|
||||||
mutex_unlock(&inode->i_mutex);
|
if (ret > 0) {
|
||||||
|
unsigned long nr_pages;
|
||||||
|
|
||||||
|
*ppos += ret;
|
||||||
|
nr_pages = (ret + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If file or inode is SYNC and we actually wrote some data,
|
||||||
|
* sync it.
|
||||||
|
*/
|
||||||
|
if (unlikely((out->f_flags & O_SYNC) || IS_SYNC(inode))) {
|
||||||
|
int err;
|
||||||
|
|
||||||
|
mutex_lock(&inode->i_mutex);
|
||||||
|
err = ocfs2_rw_lock(inode, 1);
|
||||||
|
if (err < 0) {
|
||||||
|
mlog_errno(err);
|
||||||
|
} else {
|
||||||
|
err = generic_osync_inode(inode, mapping,
|
||||||
|
OSYNC_METADATA|OSYNC_DATA);
|
||||||
|
ocfs2_rw_unlock(inode, 1);
|
||||||
|
}
|
||||||
|
mutex_unlock(&inode->i_mutex);
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
ret = err;
|
||||||
|
}
|
||||||
|
balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
|
||||||
|
}
|
||||||
|
|
||||||
mlog_exit(ret);
|
mlog_exit(ret);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -555,8 +555,8 @@ static int pipe_to_sendpage(struct pipe_inode_info *pipe,
|
||||||
* SPLICE_F_MOVE isn't set, or we cannot move the page, we simply create
|
* SPLICE_F_MOVE isn't set, or we cannot move the page, we simply create
|
||||||
* a new page in the output file page cache and fill/dirty that.
|
* a new page in the output file page cache and fill/dirty that.
|
||||||
*/
|
*/
|
||||||
static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
||||||
struct splice_desc *sd)
|
struct splice_desc *sd)
|
||||||
{
|
{
|
||||||
struct file *file = sd->u.file;
|
struct file *file = sd->u.file;
|
||||||
struct address_space *mapping = file->f_mapping;
|
struct address_space *mapping = file->f_mapping;
|
||||||
|
@ -600,6 +600,7 @@ static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
|
||||||
out:
|
out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
EXPORT_SYMBOL(pipe_to_file);
|
||||||
|
|
||||||
static void wakeup_pipe_writers(struct pipe_inode_info *pipe)
|
static void wakeup_pipe_writers(struct pipe_inode_info *pipe)
|
||||||
{
|
{
|
||||||
|
|
|
@ -75,6 +75,8 @@ extern int splice_from_pipe_next(struct pipe_inode_info *,
|
||||||
extern void splice_from_pipe_begin(struct splice_desc *);
|
extern void splice_from_pipe_begin(struct splice_desc *);
|
||||||
extern void splice_from_pipe_end(struct pipe_inode_info *,
|
extern void splice_from_pipe_end(struct pipe_inode_info *,
|
||||||
struct splice_desc *);
|
struct splice_desc *);
|
||||||
|
extern int pipe_to_file(struct pipe_inode_info *, struct pipe_buffer *,
|
||||||
|
struct splice_desc *);
|
||||||
|
|
||||||
extern ssize_t splice_to_pipe(struct pipe_inode_info *,
|
extern ssize_t splice_to_pipe(struct pipe_inode_info *,
|
||||||
struct splice_pipe_desc *);
|
struct splice_pipe_desc *);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue