Merge tag 'omap-for-v5.13/fixes-sata' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / fs / cifs / file.c
index 6caad10..379a427 100644 (file)
@@ -323,8 +323,7 @@ cifs_new_fileinfo(struct cifs_fid *fid, struct file *file,
        cfile->dentry = dget(dentry);
        cfile->f_flags = file->f_flags;
        cfile->invalidHandle = false;
-       cfile->oplock_break_received = false;
-       cfile->deferred_scheduled = false;
+       cfile->deferred_close_scheduled = false;
        cfile->tlink = cifs_get_tlink(tlink);
        INIT_WORK(&cfile->oplock_break, cifs_oplock_break);
        INIT_WORK(&cfile->put, cifsFileInfo_put_work);
@@ -574,21 +573,18 @@ int cifs_open(struct inode *inode, struct file *file)
                        file->f_op = &cifs_file_direct_ops;
        }
 
-       spin_lock(&CIFS_I(inode)->deferred_lock);
        /* Get the cached handle as SMB2 close is deferred */
        rc = cifs_get_readable_path(tcon, full_path, &cfile);
        if (rc == 0) {
                if (file->f_flags == cfile->f_flags) {
                        file->private_data = cfile;
+                       spin_lock(&CIFS_I(inode)->deferred_lock);
                        cifs_del_deferred_close(cfile);
                        spin_unlock(&CIFS_I(inode)->deferred_lock);
                        goto out;
                } else {
-                       spin_unlock(&CIFS_I(inode)->deferred_lock);
                        _cifsFileInfo_put(cfile, true, false);
                }
-       } else {
-               spin_unlock(&CIFS_I(inode)->deferred_lock);
        }
 
        if (server->oplocks)
@@ -878,12 +874,8 @@ 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;
+       cfile->deferred_close_scheduled = false;
        spin_unlock(&CIFS_I(d_inode(cfile->dentry))->deferred_lock);
        _cifsFileInfo_put(cfile, true, false);
 }
@@ -900,19 +892,26 @@ int cifs_close(struct inode *inode, struct file *file)
                file->private_data = NULL;
                dclose = kmalloc(sizeof(struct cifs_deferred_close), GFP_KERNEL);
                if ((cinode->oplock == CIFS_CACHE_RHW_FLG) &&
+                   cinode->lease_granted &&
                    dclose) {
                        if (test_bit(CIFS_INO_MODIFIED_ATTR, &cinode->flags))
                                inode->i_ctime = inode->i_mtime = current_time(inode);
                        spin_lock(&cinode->deferred_lock);
                        cifs_add_deferred_close(cfile, dclose);
-                       if (cfile->deferred_scheduled) {
-                               mod_delayed_work(deferredclose_wq,
-                                               &cfile->deferred, cifs_sb->ctx->acregmax);
+                       if (cfile->deferred_close_scheduled &&
+                           delayed_work_pending(&cfile->deferred)) {
+                               /*
+                                * If there is no pending work, mod_delayed_work queues new work.
+                                * So, Increase the ref count to avoid use-after-free.
+                                */
+                               if (!mod_delayed_work(deferredclose_wq,
+                                               &cfile->deferred, cifs_sb->ctx->acregmax))
+                                       cifsFileInfo_get(cfile);
                        } else {
                                /* Deferred close for files */
                                queue_delayed_work(deferredclose_wq,
                                                &cfile->deferred, cifs_sb->ctx->acregmax);
-                               cfile->deferred_scheduled = true;
+                               cfile->deferred_close_scheduled = true;
                                spin_unlock(&cinode->deferred_lock);
                                return 0;
                        }
@@ -2020,8 +2019,7 @@ struct cifsFileInfo *find_readable_file(struct cifsInodeInfo *cifs_inode,
                if (fsuid_only && !uid_eq(open_file->uid, current_fsuid()))
                        continue;
                if (OPEN_FMODE(open_file->f_flags) & FMODE_READ) {
-                       if ((!open_file->invalidHandle) &&
-                               (!open_file->oplock_break_received)) {
+                       if ((!open_file->invalidHandle)) {
                                /* found a good file */
                                /* lock it so it will not be closed on us */
                                cifsFileInfo_get(open_file);
@@ -4874,14 +4872,20 @@ oplock_break_ack:
        }
        /*
         * When oplock break is received and there are no active
-        * file handles but cached, then set the flag oplock_break_received.
+        * file handles but cached, then schedule deferred close immediately.
         * So, new open will not use cached handle.
         */
        spin_lock(&CIFS_I(inode)->deferred_lock);
        is_deferred = cifs_is_deferred_close(cfile, &dclose);
-       if (is_deferred && cfile->deferred_scheduled) {
-               cfile->oplock_break_received = true;
-               mod_delayed_work(deferredclose_wq, &cfile->deferred, 0);
+       if (is_deferred &&
+           cfile->deferred_close_scheduled &&
+           delayed_work_pending(&cfile->deferred)) {
+               /*
+                * If there is no pending work, mod_delayed_work queues new work.
+                * So, Increase the ref count to avoid use-after-free.
+                */
+               if (!mod_delayed_work(deferredclose_wq, &cfile->deferred, 0))
+                       cifsFileInfo_get(cfile);
        }
        spin_unlock(&CIFS_I(inode)->deferred_lock);
        _cifsFileInfo_put(cfile, false /* do not wait for ourself */, false);