Skip to content

Commit 63b7d3a

Browse files
piastrysmfrench
authored andcommitted
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: Pavel Shilovsky <piastry@etersoft.ru> Acked-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <smfrench@gmail.com>
1 parent 88cf75a commit 63b7d3a

File tree

4 files changed

+57
-2
lines changed

4 files changed

+57
-2
lines changed

fs/cifs/cifsglob.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ struct smb_version_values {
386386
unsigned int cap_unix;
387387
unsigned int cap_nt_find;
388388
unsigned int cap_large_files;
389+
unsigned int oplock_read;
389390
};
390391

391392
#define HEADER_SIZE(server) (server->vals->header_size)

fs/cifs/file.c

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,23 @@ cifs_nt_open(char *full_path, struct inode *inode, struct cifs_sb_info *cifs_sb,
238238
return rc;
239239
}
240240

241+
static bool
242+
cifs_has_mand_locks(struct cifsInodeInfo *cinode)
243+
{
244+
struct cifs_fid_locks *cur;
245+
bool has_locks = false;
246+
247+
down_read(&cinode->lock_sem);
248+
list_for_each_entry(cur, &cinode->llist, llist) {
249+
if (!list_empty(&cur->locks)) {
250+
has_locks = true;
251+
break;
252+
}
253+
}
254+
up_read(&cinode->lock_sem);
255+
return has_locks;
256+
}
257+
241258
struct cifsFileInfo *
242259
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
243260
struct tcon_link *tlink, __u32 oplock)
@@ -248,6 +265,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
248265
struct cifsFileInfo *cfile;
249266
struct cifs_fid_locks *fdlocks;
250267
struct cifs_tcon *tcon = tlink_tcon(tlink);
268+
struct TCP_Server_Info *server = tcon->ses->server;
251269

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

297+
/*
298+
* If the server returned a read oplock and we have mandatory brlocks,
299+
* set oplock level to None.
300+
*/
301+
if (oplock == server->vals->oplock_read &&
302+
cifs_has_mand_locks(cinode)) {
303+
cFYI(1, "Reset oplock val from read to None due to mand locks");
304+
oplock = 0;
305+
}
306+
279307
spin_lock(&cifs_file_list_lock);
280-
if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE)
308+
if (fid->pending_open->oplock != CIFS_OPLOCK_NO_CHANGE && oplock)
281309
oplock = fid->pending_open->oplock;
282310
list_del(&fid->pending_open->olist);
283311

284-
tlink_tcon(tlink)->ses->server->ops->set_fid(cfile, fid, oplock);
312+
server->ops->set_fid(cfile, fid, oplock);
285313

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

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

1491+
/*
1492+
* Windows 7 server can delay breaking lease from read to None
1493+
* if we set a byte-range lock on a file - break it explicitly
1494+
* before sending the lock to the server to be sure the next
1495+
* read won't conflict with non-overlapted locks due to
1496+
* pagereading.
1497+
*/
1498+
if (!CIFS_I(inode)->clientCanCacheAll &&
1499+
CIFS_I(inode)->clientCanCacheRead) {
1500+
cifs_invalidate_mapping(inode);
1501+
cFYI(1, "Set no oplock for inode=%p due to mand locks",
1502+
inode);
1503+
CIFS_I(inode)->clientCanCacheRead = false;
1504+
}
1505+
14621506
rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
14631507
type, 1, 0, wait_flag);
14641508
if (rc) {
@@ -3537,6 +3581,13 @@ void cifs_oplock_break(struct work_struct *work)
35373581
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
35383582
int rc = 0;
35393583

3584+
if (!cinode->clientCanCacheAll && cinode->clientCanCacheRead &&
3585+
cifs_has_mand_locks(cinode)) {
3586+
cFYI(1, "Reset oplock to None for inode=%p due to mand locks",
3587+
inode);
3588+
cinode->clientCanCacheRead = false;
3589+
}
3590+
35403591
if (inode && S_ISREG(inode->i_mode)) {
35413592
if (cinode->clientCanCacheRead)
35423593
break_lease(inode, O_RDONLY);

fs/cifs/smb1ops.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -959,4 +959,5 @@ struct smb_version_values smb1_values = {
959959
.cap_unix = CAP_UNIX,
960960
.cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND,
961961
.cap_large_files = CAP_LARGE_FILES,
962+
.oplock_read = OPLOCK_READ,
962963
};

fs/cifs/smb2ops.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,7 @@ struct smb_version_values smb20_values = {
708708
.cap_unix = 0,
709709
.cap_nt_find = SMB2_NT_FIND,
710710
.cap_large_files = SMB2_LARGE_FILES,
711+
.oplock_read = SMB2_OPLOCK_LEVEL_II,
711712
};
712713

713714
struct smb_version_values smb21_values = {
@@ -725,6 +726,7 @@ struct smb_version_values smb21_values = {
725726
.cap_unix = 0,
726727
.cap_nt_find = SMB2_NT_FIND,
727728
.cap_large_files = SMB2_LARGE_FILES,
729+
.oplock_read = SMB2_OPLOCK_LEVEL_II,
728730
};
729731

730732
struct smb_version_values smb30_values = {

0 commit comments

Comments
 (0)