Commit 9bbb6717 authored by Bernd Schubert's avatar Bernd Schubert Committed by Miklos Szeredi
Browse files

fuse: add fuse_dio_lock/unlock helper functions



So far this is just a helper to remove complex locking logic out of
fuse_direct_write_iter.  Especially needed by the next patch in the series
to that adds the fuse inode cache IO mode and adds in even more locking
complexity.

Signed-off-by: default avatarBernd Schubert <bschubert@ddn.com>
Signed-off-by: default avatarMiklos Szeredi <mszeredi@redhat.com>
parent 699cf824
Loading
Loading
Loading
Loading
+34 −27
Original line number Diff line number Diff line
@@ -1340,6 +1340,37 @@ static bool fuse_dio_wr_exclusive_lock(struct kiocb *iocb, struct iov_iter *from
	return false;
}

static void fuse_dio_lock(struct kiocb *iocb, struct iov_iter *from,
			  bool *exclusive)
{
	struct inode *inode = file_inode(iocb->ki_filp);

	*exclusive = fuse_dio_wr_exclusive_lock(iocb, from);
	if (*exclusive) {
		inode_lock(inode);
	} else {
		inode_lock_shared(inode);
		/*
		 * Previous check was without inode lock and might have raced,
		 * check again.
		 */
		if (fuse_io_past_eof(iocb, from)) {
			inode_unlock_shared(inode);
			inode_lock(inode);
			*exclusive = true;
		}
	}
}

static void fuse_dio_unlock(struct inode *inode, bool exclusive)
{
	if (exclusive) {
		inode_unlock(inode);
	} else {
		inode_unlock_shared(inode);
	}
}

static ssize_t fuse_cache_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
	struct file *file = iocb->ki_filp;
@@ -1604,30 +1635,9 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
	struct inode *inode = file_inode(iocb->ki_filp);
	struct fuse_io_priv io = FUSE_IO_PRIV_SYNC(iocb);
	ssize_t res;
	bool exclusive_lock = fuse_dio_wr_exclusive_lock(iocb, from);

	/*
	 * Take exclusive lock if
	 * - Parallel direct writes are disabled - a user space decision
	 * - Parallel direct writes are enabled and i_size is being extended.
	 * - Shared mmap on direct_io file is supported (FUSE_DIRECT_IO_ALLOW_MMAP).
	 *   This might not be needed at all, but needs further investigation.
	 */
	if (exclusive_lock)
		inode_lock(inode);
	else {
		inode_lock_shared(inode);

		/*
		 * Previous check was without any lock and might have raced.
		 */
		if (fuse_io_past_eof(iocb, from)) {
			inode_unlock_shared(inode);
			inode_lock(inode);
			exclusive_lock = true;
		}
	}
	bool exclusive;

	fuse_dio_lock(iocb, from, &exclusive);
	res = generic_write_checks(iocb, from);
	if (res > 0) {
		if (!is_sync_kiocb(iocb) && iocb->ki_flags & IOCB_DIRECT) {
@@ -1638,10 +1648,7 @@ static ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from)
			fuse_write_update_attr(inode, iocb->ki_pos, res);
		}
	}
	if (exclusive_lock)
		inode_unlock(inode);
	else
		inode_unlock_shared(inode);
	fuse_dio_unlock(inode, exclusive);

	return res;
}