diff options
| author | Pavel Shilovsky <piastry@etersoft.ru> | 2012-12-24 05:41:19 -0500 |
|---|---|---|
| committer | Steve French <smfrench@gmail.com> | 2013-01-02 00:04:30 -0500 |
| commit | 63b7d3a41ccadef971a4ffbe6662119d4275ebf9 (patch) | |
| tree | 02ccbd85b5628cc1cb4cb982c74514fe8d851fdb | |
| parent | 88cf75aaaf27a652b3e85960ac3060172dd3edac (diff) | |
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>
| -rw-r--r-- | fs/cifs/cifsglob.h | 1 | ||||
| -rw-r--r-- | fs/cifs/file.c | 55 | ||||
| -rw-r--r-- | fs/cifs/smb1ops.c | 1 | ||||
| -rw-r--r-- | fs/cifs/smb2ops.c | 2 |
4 files changed, 57 insertions, 2 deletions
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index dfab450a191e..e6899cea1c35 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h | |||
| @@ -386,6 +386,7 @@ struct smb_version_values { | |||
| 386 | unsigned int cap_unix; | 386 | unsigned int cap_unix; |
| 387 | unsigned int cap_nt_find; | 387 | unsigned int cap_nt_find; |
| 388 | unsigned int cap_large_files; | 388 | unsigned int cap_large_files; |
| 389 | unsigned int oplock_read; | ||
| 389 | }; | 390 | }; |
| 390 | 391 | ||
| 391 | #define HEADER_SIZE(server) (server->vals->header_size) | 392 | #define HEADER_SIZE(server) (server->vals->header_size) |
diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 22c37254b64e..8ea6ca50a665 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c | |||
| @@ -238,6 +238,23 @@ out: | |||
| 238 | return rc; | 238 | return rc; |
| 239 | } | 239 | } |
| 240 | 240 | ||
| 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 | |||
| 241 | struct cifsFileInfo * | 258 | struct cifsFileInfo * |
| 242 | cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | 259 | cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, |
| 243 | struct tcon_link *tlink, __u32 oplock) | 260 | struct tcon_link *tlink, __u32 oplock) |
| @@ -248,6 +265,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
| 248 | struct cifsFileInfo *cfile; | 265 | struct cifsFileInfo *cfile; |
| 249 | struct cifs_fid_locks *fdlocks; | 266 | struct cifs_fid_locks *fdlocks; |
| 250 | struct cifs_tcon *tcon = tlink_tcon(tlink); | 267 | struct cifs_tcon *tcon = tlink_tcon(tlink); |
| 268 | struct TCP_Server_Info *server = tcon->ses->server; | ||
| 251 | 269 | ||
| 252 | cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); | 270 | cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL); |
| 253 | if (cfile == NULL) | 271 | if (cfile == NULL) |
| @@ -276,12 +294,22 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file, | |||
| 276 | INIT_WORK(&cfile->oplock_break, cifs_oplock_break); | 294 | INIT_WORK(&cfile->oplock_break, cifs_oplock_break); |
| 277 | mutex_init(&cfile->fh_mutex); | 295 | mutex_init(&cfile->fh_mutex); |
| 278 | 296 | ||
| 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 | |||
| 279 | spin_lock(&cifs_file_list_lock); | 307 | 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) |
| 281 | oplock = fid->pending_open->oplock; | 309 | oplock = fid->pending_open->oplock; |
| 282 | list_del(&fid->pending_open->olist); | 310 | list_del(&fid->pending_open->olist); |
| 283 | 311 | ||
| 284 | tlink_tcon(tlink)->ses->server->ops->set_fid(cfile, fid, oplock); | 312 | server->ops->set_fid(cfile, fid, oplock); |
| 285 | 313 | ||
| 286 | list_add(&cfile->tlist, &tcon->openFileList); | 314 | list_add(&cfile->tlist, &tcon->openFileList); |
| 287 | /* if readable file instance put first in list*/ | 315 | /* if readable file instance put first in list*/ |
| @@ -1422,6 +1450,7 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, | |||
| 1422 | struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; | 1450 | struct cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data; |
| 1423 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); | 1451 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); |
| 1424 | struct TCP_Server_Info *server = tcon->ses->server; | 1452 | struct TCP_Server_Info *server = tcon->ses->server; |
| 1453 | struct inode *inode = cfile->dentry->d_inode; | ||
| 1425 | 1454 | ||
| 1426 | if (posix_lck) { | 1455 | if (posix_lck) { |
| 1427 | int posix_lock_type; | 1456 | int posix_lock_type; |
| @@ -1459,6 +1488,21 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type, | |||
| 1459 | if (!rc) | 1488 | if (!rc) |
| 1460 | goto out; | 1489 | goto out; |
| 1461 | 1490 | ||
| 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 | |||
| 1462 | rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, | 1506 | rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length, |
| 1463 | type, 1, 0, wait_flag); | 1507 | type, 1, 0, wait_flag); |
| 1464 | if (rc) { | 1508 | if (rc) { |
| @@ -3537,6 +3581,13 @@ void cifs_oplock_break(struct work_struct *work) | |||
| 3537 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); | 3581 | struct cifs_tcon *tcon = tlink_tcon(cfile->tlink); |
| 3538 | int rc = 0; | 3582 | int rc = 0; |
| 3539 | 3583 | ||
| 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 | |||
| 3540 | if (inode && S_ISREG(inode->i_mode)) { | 3591 | if (inode && S_ISREG(inode->i_mode)) { |
| 3541 | if (cinode->clientCanCacheRead) | 3592 | if (cinode->clientCanCacheRead) |
| 3542 | break_lease(inode, O_RDONLY); | 3593 | break_lease(inode, O_RDONLY); |
diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index dd79056c0581..47bc5a87f94e 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c | |||
| @@ -959,4 +959,5 @@ struct smb_version_values smb1_values = { | |||
| 959 | .cap_unix = CAP_UNIX, | 959 | .cap_unix = CAP_UNIX, |
| 960 | .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, | 960 | .cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND, |
| 961 | .cap_large_files = CAP_LARGE_FILES, | 961 | .cap_large_files = CAP_LARGE_FILES, |
| 962 | .oplock_read = OPLOCK_READ, | ||
| 962 | }; | 963 | }; |
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index d79de7bc4435..c9c7aa7ed966 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c | |||
| @@ -708,6 +708,7 @@ struct smb_version_values smb20_values = { | |||
| 708 | .cap_unix = 0, | 708 | .cap_unix = 0, |
| 709 | .cap_nt_find = SMB2_NT_FIND, | 709 | .cap_nt_find = SMB2_NT_FIND, |
| 710 | .cap_large_files = SMB2_LARGE_FILES, | 710 | .cap_large_files = SMB2_LARGE_FILES, |
| 711 | .oplock_read = SMB2_OPLOCK_LEVEL_II, | ||
| 711 | }; | 712 | }; |
| 712 | 713 | ||
| 713 | struct smb_version_values smb21_values = { | 714 | struct smb_version_values smb21_values = { |
| @@ -725,6 +726,7 @@ struct smb_version_values smb21_values = { | |||
| 725 | .cap_unix = 0, | 726 | .cap_unix = 0, |
| 726 | .cap_nt_find = SMB2_NT_FIND, | 727 | .cap_nt_find = SMB2_NT_FIND, |
| 727 | .cap_large_files = SMB2_LARGE_FILES, | 728 | .cap_large_files = SMB2_LARGE_FILES, |
| 729 | .oplock_read = SMB2_OPLOCK_LEVEL_II, | ||
| 728 | }; | 730 | }; |
| 729 | 731 | ||
| 730 | struct smb_version_values smb30_values = { | 732 | struct smb_version_values smb30_values = { |
