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

Commit 63b7d3a4 authored by Pavel Shilovsky's avatar Pavel Shilovsky Committed by Steve French
Browse files

CIFS: Don't let read only caching for mandatory byte-range locked files



If we have mandatory byte-range locks on a file we can't cache reads
because pagereading may have conflicts with these locks on the server.
That's why we should allow level2 oplocks for files without mandatory
locks only.

Signed-off-by: default avatarPavel Shilovsky <piastry@etersoft.ru>
Acked-by: default avatarJeff Layton <jlayton@redhat.com>
Signed-off-by: default avatarSteve French <smfrench@gmail.com>
parent 88cf75aa
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -386,6 +386,7 @@ struct smb_version_values {
	unsigned int	cap_unix;
	unsigned int	cap_nt_find;
	unsigned int	cap_large_files;
	unsigned int	oplock_read;
};

#define HEADER_SIZE(server) (server->vals->header_size)
+53 −2
Original line number Diff line number Diff line
@@ -238,6 +238,23 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
	return rc;
}

static bool
cifs_has_mand_locks(struct cifsInodeInfo *cinode)
{
	struct cifs_fid_locks *cur;
	bool has_locks = false;

	down_read(&cinode->lock_sem);
	list_for_each_entry(cur, &cinode->llist, llist) {
		if (!list_empty(&cur->locks)) {
			has_locks = true;
			break;
		}
	}
	up_read(&cinode->lock_sem);
	return has_locks;
}

struct cifsFileInfo *
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
		  struct tcon_link *tlink, __u32 oplock)
@@ -248,6 +265,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
	struct cifsFileInfo *cfile;
	struct cifs_fid_locks *fdlocks;
	struct cifs_tcon *tcon = tlink_tcon(tlink);
	struct TCP_Server_Info *server = tcon->ses->server;

	cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
	if (cfile == NULL)
@@ -276,12 +294,22 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
	INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
	mutex_init(&cfile->fh_mutex);

	/*
	 * If the server returned a read oplock and we have mandatory brlocks,
	 * set oplock level to None.
	 */
	if (oplock == server->vals->oplock_read &&
						cifs_has_mand_locks(cinode)) {
		cFYI(1, "Reset oplock val from read to None due to mand locks");
		oplock = 0;
	}

	spin_lock(&cifs_file_list_lock);
	if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE)
	if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)
		oplock = fid->pending_open->oplock;
	list_del(&fid->pending_open->olist);

	tlink_tcon(tlink)->ses->server->ops->set_fid(cfile, fid, oplock);
	server->ops->set_fid(cfile, fid, oplock);

	list_add(&cfile->tlist, &tcon->openFileList);
	/* if readable file instance put first in list*/
@@ -1422,6 +1450,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
	struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
	struct TCP_Server_Info *server = tcon->ses->server;
	struct inode *inode = cfile->dentry->d_inode;

	if (posix_lck) {
		int posix_lock_type;
@@ -1459,6 +1488,21 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
		if (!rc)
			goto out;

		/*
		 * Windows 7 server can delay breaking lease from read to None
		 * if we set a byte-range lock on a file - break it explicitly
		 * before sending the lock to the server to be sure the next
		 * read won't conflict with non-overlapted locks due to
		 * pagereading.
		 */
		if (!CIFS_I(inode)->clientCanCacheAll &&
					CIFS_I(inode)->clientCanCacheRead) {
			cifs_invalidate_mapping(inode);
			cFYI(1, "Set no oplock for inode=%p due to mand locks",
			     inode);
			CIFS_I(inode)->clientCanCacheRead = false;
		}

		rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
					    type, 1, 0, wait_flag);
		if (rc) {
@@ -3537,6 +3581,13 @@ void cifs_oplock_break(struct work_struct *work)
	struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
	int rc = 0;

	if (!cinode->clientCanCacheAll && cinode->clientCanCacheRead &&
						cifs_has_mand_locks(cinode)) {
		cFYI(1, "Reset oplock to None for inode=%p due to mand locks",
		     inode);
		cinode->clientCanCacheRead = false;
	}

	if (inode && S_ISREG(inode->i_mode)) {
		if (cinode->clientCanCacheRead)
			break_lease(inode, O_RDONLY);
+1 −0
Original line number Diff line number Diff line
@@ -959,4 +959,5 @@ struct smb_version_values smb1_values = {
	.cap_unix = CAP_UNIX,
	.cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND,
	.cap_large_files = CAP_LARGE_FILES,
	.oplock_read = OPLOCK_READ,
};
+2 −0
Original line number Diff line number Diff line
@@ -708,6 +708,7 @@ struct smb_version_values smb20_values = {
	.cap_unix = 0,
	.cap_nt_find = SMB2_NT_FIND,
	.cap_large_files = SMB2_LARGE_FILES,
	.oplock_read = SMB2_OPLOCK_LEVEL_II,
};

struct smb_version_values smb21_values = {
@@ -725,6 +726,7 @@ struct smb_version_values smb21_values = {
	.cap_unix = 0,
	.cap_nt_find = SMB2_NT_FIND,
	.cap_large_files = SMB2_LARGE_FILES,
	.oplock_read = SMB2_OPLOCK_LEVEL_II,
};

struct smb_version_values smb30_values = {