Commit 14b1cd25 authored by Steve French's avatar Steve French
Browse files

cifs: Fix locking in cifs_strict_readv()



Fix to take the i_rwsem (through the netfs locking wrappers) before taking
cinode->lock_sem.

Fixes: 3ee1a1fc ("cifs: Cut over to using netfslib")
Reported-by: default avatarEnzo Matsumiya <ematsumiya@suse.de>
Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 29b4c7bb
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@
 *
 * The caller must hold any appropriate locks.
 */
static ssize_t netfs_unbuffered_read_iter_locked(struct kiocb *iocb, struct iov_iter *iter)
ssize_t netfs_unbuffered_read_iter_locked(struct kiocb *iocb, struct iov_iter *iter)
{
	struct netfs_io_request *rreq;
	ssize_t ret;
@@ -98,6 +98,7 @@ static ssize_t netfs_unbuffered_read_iter_locked(struct kiocb *iocb, struct iov_
		iov_iter_revert(iter, orig_count - iov_iter_count(iter));
	return ret;
}
EXPORT_SYMBOL(netfs_unbuffered_read_iter_locked);

/**
 * netfs_unbuffered_read_iter - Perform an unbuffered or direct I/O read
+1 −0
Original line number Diff line number Diff line
@@ -1995,6 +1995,7 @@ require use of the stronger protocol */
 *				->chans_need_reconnect
 *				->chans_in_reconnect
 * cifs_tcon->tc_lock		(anything that is not protected by another lock and can change)
 * inode->i_rwsem, taken by fs/netfs/locking.c e.g. should be taken before cifsInodeInfo locks
 * cifsInodeInfo->open_file_lock	cifsInodeInfo->openFileList	cifs_alloc_inode
 * cifsInodeInfo->writers_lock	cifsInodeInfo->writers		cifsInodeInfo_alloc
 * cifsInodeInfo->lock_sem	cifsInodeInfo->llist		cifs_init_once
+25 −9
Original line number Diff line number Diff line
@@ -2916,16 +2916,32 @@ cifs_strict_readv(struct kiocb *iocb, struct iov_iter *to)
	 * We need to hold the sem to be sure nobody modifies lock list
	 * with a brlock that prevents reading.
	 */
	if (iocb->ki_flags & IOCB_DIRECT) {
		rc = netfs_start_io_direct(inode);
		if (rc < 0)
			goto out;
		down_read(&cinode->lock_sem);
	if (!cifs_find_lock_conflict(cfile, iocb->ki_pos, iov_iter_count(to),
		if (!cifs_find_lock_conflict(
			    cfile, iocb->ki_pos, iov_iter_count(to),
			    tcon->ses->server->vals->shared_lock_type,
				     0, NULL, CIFS_READ_OP)) {
		if (iocb->ki_flags & IOCB_DIRECT)
			rc = netfs_unbuffered_read_iter(iocb, to);
		else
			rc = netfs_buffered_read_iter(iocb, to);
	}
			    0, NULL, CIFS_READ_OP))
			rc = netfs_unbuffered_read_iter_locked(iocb, to);
		up_read(&cinode->lock_sem);
		netfs_end_io_direct(inode);
	} else {
		rc = netfs_start_io_read(inode);
		if (rc < 0)
			goto out;
		down_read(&cinode->lock_sem);
		if (!cifs_find_lock_conflict(
			    cfile, iocb->ki_pos, iov_iter_count(to),
			    tcon->ses->server->vals->shared_lock_type,
			    0, NULL, CIFS_READ_OP))
			rc = filemap_read(iocb, to, 0);
		up_read(&cinode->lock_sem);
		netfs_end_io_read(inode);
	}
out:
	return rc;
}

+1 −0
Original line number Diff line number Diff line
@@ -389,6 +389,7 @@ struct netfs_cache_ops {
};

/* High-level read API. */
ssize_t netfs_unbuffered_read_iter_locked(struct kiocb *iocb, struct iov_iter *iter);
ssize_t netfs_unbuffered_read_iter(struct kiocb *iocb, struct iov_iter *iter);
ssize_t netfs_buffered_read_iter(struct kiocb *iocb, struct iov_iter *iter);
ssize_t netfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter);