Message ID | 1356537234-11507-4-git-send-email-piastry@etersoft.ru (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Wed, 26 Dec 2012 19:53:54 +0400 Pavel Shilovsky <piastry@etersoft.ru> wrote: > 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> > --- > fs/cifs/cifsglob.h | 1 + > fs/cifs/file.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- > fs/cifs/smb1ops.c | 1 + > fs/cifs/smb2ops.c | 3 +++ > 4 files changed, 58 insertions(+), 2 deletions(-) > > diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h > index dfab450..e6899ce 100644 > --- a/fs/cifs/cifsglob.h > +++ b/fs/cifs/cifsglob.h > @@ -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) > diff --git a/fs/cifs/file.c b/fs/cifs/file.c > index 22c3725..8ea6ca5 100644 > --- a/fs/cifs/file.c > +++ b/fs/cifs/file.c > @@ -238,6 +238,23 @@ out: > 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); > diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c > index a5d234c..a1de86c 100644 > --- a/fs/cifs/smb1ops.c > +++ b/fs/cifs/smb1ops.c > @@ -952,4 +952,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, > }; > diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c > index d79de7b..bceffe7 100644 > --- a/fs/cifs/smb2ops.c > +++ b/fs/cifs/smb2ops.c > @@ -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 = { > @@ -742,4 +744,5 @@ struct smb_version_values smb30_values = { > .cap_unix = 0, > .cap_nt_find = SMB2_NT_FIND, > .cap_large_files = SMB2_LARGE_FILES, > + .oplock_read = SMB2_OPLOCK_LEVEL_II, > }; Acked-by: Jeff Layton <jlayton@redhat.com> -- To unsubscribe from this list: send the line "unsubscribe linux-cifs" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index dfab450..e6899ce 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -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) diff --git a/fs/cifs/file.c b/fs/cifs/file.c index 22c3725..8ea6ca5 100644 --- a/fs/cifs/file.c +++ b/fs/cifs/file.c @@ -238,6 +238,23 @@ out: 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); diff --git a/fs/cifs/smb1ops.c b/fs/cifs/smb1ops.c index a5d234c..a1de86c 100644 --- a/fs/cifs/smb1ops.c +++ b/fs/cifs/smb1ops.c @@ -952,4 +952,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, }; diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c index d79de7b..bceffe7 100644 --- a/fs/cifs/smb2ops.c +++ b/fs/cifs/smb2ops.c @@ -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 = { @@ -742,4 +744,5 @@ struct smb_version_values smb30_values = { .cap_unix = 0, .cap_nt_find = SMB2_NT_FIND, .cap_large_files = SMB2_LARGE_FILES, + .oplock_read = SMB2_OPLOCK_LEVEL_II, };
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> --- fs/cifs/cifsglob.h | 1 + fs/cifs/file.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- fs/cifs/smb1ops.c | 1 + fs/cifs/smb2ops.c | 3 +++ 4 files changed, 58 insertions(+), 2 deletions(-)