]> git.proxmox.com Git - mirror_ubuntu-kernels.git/commitdiff
Cifs: Fix kernel oops caused by deferred close for files.
authorRohith Surabattula <rohiths@microsoft.com>
Mon, 19 Apr 2021 19:02:03 +0000 (19:02 +0000)
committerSteve French <stfrench@microsoft.com>
Tue, 4 May 2021 16:53:15 +0000 (11:53 -0500)
Fix regression issue caused by deferred close for files.

Signed-off-by: Rohith Surabattula <rohiths@microsoft.com>
Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifsproto.h
fs/cifs/file.c
fs/cifs/inode.c
fs/cifs/misc.c

index c6dacce87d3a24da13f6547d198c0b59fdd02b86..3c6b97ef39d3b7ead1b61f51a473f03db553bf99 100644 (file)
@@ -278,6 +278,8 @@ extern void cifs_del_deferred_close(struct cifsFileInfo *cfile);
 
 extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode);
 
+extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);
+
 extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx);
 extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
                                 int from_reconnect);
index c95893351b6c22a0c8225f554480f48e9a221f8d..919c82d4713dfa8835385cbfdfae4d493a597f24 100644 (file)
@@ -878,6 +878,10 @@ void smb2_deferred_work_close(struct work_struct *work)
                        struct cifsFileInfo, deferred.work);
 
        spin_lock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
+       if (!cfile->deferred_scheduled) {
+               spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
+               return;
+       }
        cifs_del_deferred_close(cfile);
        cfile->deferred_scheduled = false;
        spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
@@ -1987,8 +1991,10 @@ cifs_write(struct cifsFileInfo *open_file, __u32 pid, const char *write_data,
 
        if (total_written > 0) {
                spin_lock(&d_inode(dentry)->i_lock);
-               if (*offset > d_inode(dentry)->i_size)
+               if (*offset > d_inode(dentry)->i_size) {
                        i_size_write(d_inode(dentry), *offset);
+                       d_inode(dentry)->i_blocks = (512 - 1 + *offset) >> 9;
+               }
                spin_unlock(&d_inode(dentry)->i_lock);
        }
        mark_inode_dirty_sync(d_inode(dentry));
@@ -2647,8 +2653,10 @@ static int cifs_write_end(struct file *file, struct address_space *mapping,
 
        if (rc > 0) {
                spin_lock(&inode->i_lock);
-               if (pos > inode->i_size)
+               if (pos > inode->i_size) {
                        i_size_write(inode, pos);
+                       inode->i_blocks = (512 - 1 + pos) >> 9;
+               }
                spin_unlock(&inode->i_lock);
        }
 
@@ -4864,7 +4872,6 @@ oplock_break_ack:
                                                             cinode);
                cifs_dbg(FYI, "Oplock release rc = %d\n", rc);
        }
-       _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
        /*
         * When oplock break is received and there are no active
         * file handles but cached, then set the flag oplock_break_received.
@@ -4872,11 +4879,12 @@ oplock_break_ack:
         */
        spin_lock(&CIFS_I(inode)->deferred_lock);
        is_deferred = cifs_is_deferred_close(cfile, &dclose);
-       if (is_deferred) {
+       if (is_deferred && cfile->deferred_scheduled) {
                cfile->oplock_break_received = true;
                mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
        }
        spin_unlock(&CIFS_I(inode)->deferred_lock);
+       _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);
        cifs_done_oplock_break(cinode);
 }
 
index 728ff45b6667d3a1ac4aadf06f849a65876e0e0b..591f18e3e933bc3026edc6e27d0b3a2281b54a03 100644 (file)
@@ -1647,7 +1647,7 @@ int cifs_unlink(struct inode *dir, struct dentry *dentry)
                goto unlink_out;
        }
 
-       cifs_close_deferred_file(CIFS_I(inode));
+       cifs_close_all_deferred_files(tcon);
        if (cap_unix(tcon->ses) && (CIFS_UNIX_POSIX_PATH_OPS_CAP &
                                le64_to_cpu(tcon->fsUnixInfo.Capability))) {
                rc = CIFSPOSIXDelFile(xid, tcon, full_path,
@@ -2125,6 +2125,7 @@ cifs_rename2(struct user_namespace *mnt_userns, struct inode *source_dir,
                goto cifs_rename_exit;
        }
 
+       cifs_close_all_deferred_files(tcon);
        rc = cifs_do_rename(xid, source_dentry, from_name, target_dentry,
                            to_name);
 
index e63fbd4a6bfe762610448677800cff83112ea3cd..524dbdfb7184d964b6e6fe7d0462d3cef0f1056c 100644 (file)
@@ -734,6 +734,23 @@ cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode)
        }
 }
 
+void
+cifs_close_all_deferred_files(struct cifs_tcon *tcon)
+{
+       struct cifsFileInfo *cfile;
+       struct cifsInodeInfo *cinode;
+       struct list_head *tmp;
+
+       spin_lock(&tcon->open_file_lock);
+       list_for_each(tmp, &tcon->openFileList) {
+               cfile = list_entry(tmp, struct cifsFileInfo, tlist);
+               cinode = CIFS_I(d_inode(cfile->dentry));
+               if (delayed_work_pending(&cfile->deferred))
+                       mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
+       }
+       spin_unlock(&tcon->open_file_lock);
+}
+
 /* parses DFS refferal V3 structure
  * caller is responsible for freeing target_nodes
  * returns: