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

Commit 328eaaba authored by Miklos Szeredi's avatar Miklos Szeredi Committed by Jens Axboe
Browse files

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: default avatarMiklos Szeredi <mszeredi@suse.cz>
Signed-off-by: default avatarJens Axboe <jens.axboe@oracle.com>
parent eb443e5a
Loading
Loading
Loading
Loading
+74 −20
Original line number Diff line number Diff line
@@ -1912,6 +1912,22 @@ static ssize_t ocfs2_file_aio_write(struct kiocb *iocb,
	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,
				       struct file *out,
				       loff_t *ppos,
@@ -1919,39 +1935,77 @@ static ssize_t ocfs2_file_splice_write(struct pipe_inode_info *pipe,
				       unsigned int flags)
{
	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,
		   (unsigned int)len,
		   out->f_path.dentry->d_name.len,
		   out->f_path.dentry->d_name.name);

	mutex_lock_nested(&inode->i_mutex, I_MUTEX_PARENT);
	if (pipe->inode)
		mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_PARENT);

	ret = ocfs2_rw_lock(inode, 1);
	if (ret < 0) {
		mlog_errno(ret);
		goto out;
	}
	splice_from_pipe_begin(&sd);
	do {
		ret = splice_from_pipe_next(pipe, &sd);
		if (ret <= 0)
			break;

	ret = ocfs2_prepare_inode_for_write(out->f_path.dentry, ppos, len, 0,
					    NULL);
	if (ret < 0) {
		mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
		ret = ocfs2_rw_lock(inode, 1);
		if (ret < 0)
			mlog_errno(ret);
		goto out_unlock;
		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)
		mutex_lock_nested(&pipe->inode->i_mutex, I_MUTEX_CHILD);
	ret = generic_file_splice_write_nolock(pipe, out, ppos, len, flags);
	if (pipe->inode)
		mutex_unlock(&pipe->inode->i_mutex);

out_unlock:
	if (sd.num_spliced)
		ret = sd.num_spliced;

	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);
out:
			}
			mutex_unlock(&inode->i_mutex);

			if (err)
				ret = err;
		}
		balance_dirty_pages_ratelimited_nr(mapping, nr_pages);
	}

	mlog_exit(ret);
	return ret;
}
+3 −2
Original line number Diff line number Diff line
@@ -555,7 +555,7 @@ 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
 * 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 file *file = sd->u.file;
@@ -600,6 +600,7 @@ static int pipe_to_file(struct pipe_inode_info *pipe, struct pipe_buffer *buf,
out:
	return ret;
}
EXPORT_SYMBOL(pipe_to_file);

static void wakeup_pipe_writers(struct pipe_inode_info *pipe)
{
+2 −0
Original line number Diff line number Diff line
@@ -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_end(struct pipe_inode_info *,
				 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 *,
			      struct splice_pipe_desc *);