]> git.proxmox.com Git - mirror_ubuntu-zesty-kernel.git/blobdiff - fs/cifs/file.c
CIFS: Don't let read only caching for mandatory byte-range locked files
[mirror_ubuntu-zesty-kernel.git] / fs / cifs / file.c
index 22c37254b64ef3e7624ce0b7ab00d1523dd3df69..8ea6ca50a665cd4dcb016042b52d16ef5492bab4 100644 (file)
@@ -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);