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>
This commit is contained in:
Родитель
88cf75aaaf
Коммит
63b7d3a41c
|
@ -386,6 +386,7 @@ struct smb_version_values {
|
||||||
unsigned int cap_unix;
|
unsigned int cap_unix;
|
||||||
unsigned int cap_nt_find;
|
unsigned int cap_nt_find;
|
||||||
unsigned int cap_large_files;
|
unsigned int cap_large_files;
|
||||||
|
unsigned int oplock_read;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define HEADER_SIZE(server) (server->vals->header_size)
|
#define HEADER_SIZE(server) (server->vals->header_size)
|
||||||
|
|
|
@ -238,6 +238,23 @@ out:
|
||||||
return rc;
|
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 *
|
struct cifsFileInfo *
|
||||||
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||||
struct tcon_link *tlink, __u32 oplock)
|
struct tcon_link *tlink, __u32 oplock)
|
||||||
|
@ -248,6 +265,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
|
||||||
struct cifsFileInfo *cfile;
|
struct cifsFileInfo *cfile;
|
||||||
struct cifs_fid_locks *fdlocks;
|
struct cifs_fid_locks *fdlocks;
|
||||||
struct cifs_tcon *tcon = tlink_tcon(tlink);
|
struct cifs_tcon *tcon = tlink_tcon(tlink);
|
||||||
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
|
||||||
cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
|
cfile = kzalloc(sizeof(struct cifsFileInfo), GFP_KERNEL);
|
||||||
if (cfile == NULL)
|
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);
|
INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
|
||||||
mutex_init(&cfile->fh_mutex);
|
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);
|
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;
|
oplock = fid->pending_open->oplock;
|
||||||
list_del(&fid->pending_open->olist);
|
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);
|
list_add(&cfile->tlist, &tcon->openFileList);
|
||||||
/* if readable file instance put first in list*/
|
/* 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 cifsFileInfo *cfile = (struct cifsFileInfo *)file->private_data;
|
||||||
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
||||||
struct TCP_Server_Info *server = tcon->ses->server;
|
struct TCP_Server_Info *server = tcon->ses->server;
|
||||||
|
struct inode *inode = cfile->dentry->d_inode;
|
||||||
|
|
||||||
if (posix_lck) {
|
if (posix_lck) {
|
||||||
int posix_lock_type;
|
int posix_lock_type;
|
||||||
|
@ -1459,6 +1488,21 @@ cifs_setlk(struct file *file, struct file_lock *flock, __u32 type,
|
||||||
if (!rc)
|
if (!rc)
|
||||||
goto out;
|
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,
|
rc = server->ops->mand_lock(xid, cfile, flock->fl_start, length,
|
||||||
type, 1, 0, wait_flag);
|
type, 1, 0, wait_flag);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
@ -3537,6 +3581,13 @@ void cifs_oplock_break(struct work_struct *work)
|
||||||
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
struct cifs_tcon *tcon = tlink_tcon(cfile->tlink);
|
||||||
int rc = 0;
|
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 (inode && S_ISREG(inode->i_mode)) {
|
||||||
if (cinode->clientCanCacheRead)
|
if (cinode->clientCanCacheRead)
|
||||||
break_lease(inode, O_RDONLY);
|
break_lease(inode, O_RDONLY);
|
||||||
|
|
|
@ -959,4 +959,5 @@ struct smb_version_values smb1_values = {
|
||||||
.cap_unix = CAP_UNIX,
|
.cap_unix = CAP_UNIX,
|
||||||
.cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND,
|
.cap_nt_find = CAP_NT_SMBS | CAP_NT_FIND,
|
||||||
.cap_large_files = CAP_LARGE_FILES,
|
.cap_large_files = CAP_LARGE_FILES,
|
||||||
|
.oplock_read = OPLOCK_READ,
|
||||||
};
|
};
|
||||||
|
|
|
@ -708,6 +708,7 @@ struct smb_version_values smb20_values = {
|
||||||
.cap_unix = 0,
|
.cap_unix = 0,
|
||||||
.cap_nt_find = SMB2_NT_FIND,
|
.cap_nt_find = SMB2_NT_FIND,
|
||||||
.cap_large_files = SMB2_LARGE_FILES,
|
.cap_large_files = SMB2_LARGE_FILES,
|
||||||
|
.oplock_read = SMB2_OPLOCK_LEVEL_II,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values smb21_values = {
|
struct smb_version_values smb21_values = {
|
||||||
|
@ -725,6 +726,7 @@ struct smb_version_values smb21_values = {
|
||||||
.cap_unix = 0,
|
.cap_unix = 0,
|
||||||
.cap_nt_find = SMB2_NT_FIND,
|
.cap_nt_find = SMB2_NT_FIND,
|
||||||
.cap_large_files = SMB2_LARGE_FILES,
|
.cap_large_files = SMB2_LARGE_FILES,
|
||||||
|
.oplock_read = SMB2_OPLOCK_LEVEL_II,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct smb_version_values smb30_values = {
|
struct smb_version_values smb30_values = {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче