ksmbd: lazy v2 lease break on smb2_write()
authorNamjae Jeon <linkinjeon@kernel.org>
Sun, 31 Dec 2023 07:19:18 +0000 (16:19 +0900)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 5 Jan 2024 14:19:40 +0000 (15:19 +0100)
[ Upstream commit c2a721eead71202a0d8ddd9b56ec8dce652c71d1 ]

Don't immediately send directory lease break notification on smb2_write().
Instead, It postpones it until smb2_close().

Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/smb/server/oplock.c
fs/smb/server/oplock.h
fs/smb/server/vfs.c
fs/smb/server/vfs_cache.h

index 147d984..562b180 100644 (file)
@@ -396,8 +396,8 @@ void close_id_del_oplock(struct ksmbd_file *fp)
 {
        struct oplock_info *opinfo;
 
-       if (S_ISDIR(file_inode(fp->filp)->i_mode))
-               return;
+       if (fp->reserve_lease_break)
+               smb_lazy_parent_lease_break_close(fp);
 
        opinfo = opinfo_get(fp);
        if (!opinfo)
@@ -1127,6 +1127,47 @@ void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
        ksmbd_inode_put(p_ci);
 }
 
+void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp)
+{
+       struct oplock_info *opinfo;
+       struct ksmbd_inode *p_ci = NULL;
+
+       rcu_read_lock();
+       opinfo = rcu_dereference(fp->f_opinfo);
+       rcu_read_unlock();
+
+       if (!opinfo->is_lease || opinfo->o_lease->version != 2)
+               return;
+
+       p_ci = ksmbd_inode_lookup_lock(fp->filp->f_path.dentry->d_parent);
+       if (!p_ci)
+               return;
+
+       read_lock(&p_ci->m_lock);
+       list_for_each_entry(opinfo, &p_ci->m_op_list, op_entry) {
+               if (!opinfo->is_lease)
+                       continue;
+
+               if (opinfo->o_lease->state != SMB2_OPLOCK_LEVEL_NONE) {
+                       if (!atomic_inc_not_zero(&opinfo->refcount))
+                               continue;
+
+                       atomic_inc(&opinfo->conn->r_count);
+                       if (ksmbd_conn_releasing(opinfo->conn)) {
+                               atomic_dec(&opinfo->conn->r_count);
+                               continue;
+                       }
+                       read_unlock(&p_ci->m_lock);
+                       oplock_break(opinfo, SMB2_OPLOCK_LEVEL_NONE);
+                       opinfo_conn_put(opinfo);
+                       read_lock(&p_ci->m_lock);
+               }
+       }
+       read_unlock(&p_ci->m_lock);
+
+       ksmbd_inode_put(p_ci);
+}
+
 /**
  * smb_grant_oplock() - handle oplock/lease request on file open
  * @work:              smb work
index b64d153..5b93ea9 100644 (file)
@@ -129,4 +129,5 @@ int find_same_lease_key(struct ksmbd_session *sess, struct ksmbd_inode *ci,
 void destroy_lease_table(struct ksmbd_conn *conn);
 void smb_send_parent_lease_break_noti(struct ksmbd_file *fp,
                                      struct lease_ctx_info *lctx);
+void smb_lazy_parent_lease_break_close(struct ksmbd_file *fp);
 #endif /* __KSMBD_OPLOCK_H */
index 9091dcd..4277750 100644 (file)
@@ -517,6 +517,9 @@ int ksmbd_vfs_write(struct ksmbd_work *work, struct ksmbd_file *fp,
                }
        }
 
+       /* Reserve lease break for parent dir at closing time */
+       fp->reserve_lease_break = true;
+
        /* Do we need to break any of a levelII oplock? */
        smb_break_all_levII_oplock(work, fp, 1);
 
index 4d4938d..a528f0c 100644 (file)
@@ -105,6 +105,7 @@ struct ksmbd_file {
        struct ksmbd_readdir_data       readdir_data;
        int                             dot_dotdot[2];
        unsigned int                    f_state;
+       bool                            reserve_lease_break;
 };
 
 static inline void set_ctx_actor(struct dir_context *ctx,