All of lore.kernel.org
 help / color / mirror / Atom feed
* WIP: Expand directory handle cache to also cache non-root directories
@ 2022-08-09  2:11 Ronnie Sahlberg
  2022-08-09  2:11 ` [PATCH 1/9] cifs: Move cached-dir functions into a separate file Ronnie Sahlberg
                   ` (8 more replies)
  0 siblings, 9 replies; 27+ messages in thread
From: Ronnie Sahlberg @ 2022-08-09  2:11 UTC (permalink / raw)
  To: linux-cifs; +Cc: Steve French

Steve, List

Do not apply to the tree, but please review and comment.

Please see a Work-In-Progress patch series for expanding the handle cache
to start caching any directories, not just root.
Currently the limit is arbitrarily set to 16.
The first 8 patches are trivial and mostly just moving some functions
around without affecting behavior. They prepare the cifs module to 
mostly only access the details of struct cached_fid from the file
cached_fid.c

The 9th patch is where the actual meat is and where we now dynamically
create cached directory structures and keep them on a list hanging off
struct cached_fids.

This now means that we can safely, while holding a lease, do "ls /mnt/...
 >/dev/null" and only the first directory listing will result in network i/o.

What is still misisng and what we need to implement before we will get any
major boosts in performance is to also track the "dentry" for the cached dir.
I currently only cached hte dentry for the root, because it is easy to
get this from the superblock. But we will need to walk the dentries and ALSO
get() and use the dentry for all other directories that we cache.
This will be very important to do for performance as this will allow
us to serve attributes for the entries of the cached directory out of cache.
See inode.c:cifs_dentry_needs_reval()

I do not yet do that because "cifs: start caching all ..." is already big enough
as a patch as is, but I will implement walking the dentries and improve this
caching next.




^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 1/9] cifs: Move cached-dir functions into a separate file
  2022-08-09  2:11 WIP: Expand directory handle cache to also cache non-root directories Ronnie Sahlberg
@ 2022-08-09  2:11 ` Ronnie Sahlberg
  2022-08-11  2:53   ` Steve French
  2022-08-11 13:03   ` Paulo Alcantara
  2022-08-09  2:11 ` [PATCH 2/9] cifs: Do not use tcon->cfid directly, use the cfid we get from open_cached_dir Ronnie Sahlberg
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 27+ messages in thread
From: Ronnie Sahlberg @ 2022-08-09  2:11 UTC (permalink / raw)
  To: linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Also rename crfid to cfid to have consistent naming for this variable.

This commit does not change any logic.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/Makefile     |   2 +-
 fs/cifs/cached_dir.c | 363 +++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/cached_dir.h |  26 ++++
 fs/cifs/cifsfs.c     |  20 +--
 fs/cifs/cifsglob.h   |   2 +-
 fs/cifs/cifsproto.h  |   1 -
 fs/cifs/cifssmb.c    |   8 +-
 fs/cifs/inode.c      |   1 +
 fs/cifs/misc.c       |  12 +-
 fs/cifs/readdir.c    |   1 +
 fs/cifs/smb2inode.c  |   5 +-
 fs/cifs/smb2misc.c   |  13 +-
 fs/cifs/smb2ops.c    | 297 +----------------------------------
 fs/cifs/smb2pdu.c    |   3 +-
 fs/cifs/smb2proto.h  |  10 --
 15 files changed, 412 insertions(+), 352 deletions(-)
 create mode 100644 fs/cifs/cached_dir.c
 create mode 100644 fs/cifs/cached_dir.h

diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index 8c9f2c00be72..343a59e0d64d 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_CIFS) += cifs.o
 
 cifs-y := trace.o cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o \
 	  inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \
-	  cifs_unicode.o nterr.o cifsencrypt.o \
+	  cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \
 	  readdir.o ioctl.o sess.o export.o unc.o winucase.o \
 	  smb2ops.o smb2maperror.o smb2transport.o \
 	  smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \
diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
new file mode 100644
index 000000000000..f2e17c1d5196
--- /dev/null
+++ b/fs/cifs/cached_dir.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Functions to handle the cached directory entries
+ *
+ *  Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
+ */
+
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "smb2proto.h"
+#include "cached_dir.h"
+
+/*
+ * Open the and cache a directory handle.
+ * If error then *cfid is not initialized.
+ */
+int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
+		const char *path,
+		struct cifs_sb_info *cifs_sb,
+		struct cached_fid **cfid)
+{
+	struct cifs_ses *ses;
+	struct TCP_Server_Info *server;
+	struct cifs_open_parms oparms;
+	struct smb2_create_rsp *o_rsp = NULL;
+	struct smb2_query_info_rsp *qi_rsp = NULL;
+	int resp_buftype[2];
+	struct smb_rqst rqst[2];
+	struct kvec rsp_iov[2];
+	struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
+	struct kvec qi_iov[1];
+	int rc, flags = 0;
+	__le16 utf16_path = 0; /* Null - since an open of top of share */
+	u8 oplock = SMB2_OPLOCK_LEVEL_II;
+	struct cifs_fid *pfid;
+	struct dentry *dentry;
+
+	if (tcon == NULL || tcon->nohandlecache ||
+	    is_smb1_server(tcon->ses->server))
+		return -ENOTSUPP;
+
+	ses = tcon->ses;
+	server = ses->server;
+
+	if (cifs_sb->root == NULL)
+		return -ENOENT;
+
+	if (strlen(path))
+		return -ENOENT;
+
+	dentry = cifs_sb->root;
+
+	mutex_lock(&tcon->cfid.fid_mutex);
+	if (tcon->cfid.is_valid) {
+		cifs_dbg(FYI, "found a cached root file handle\n");
+		*cfid = &tcon->cfid;
+		kref_get(&tcon->cfid.refcount);
+		mutex_unlock(&tcon->cfid.fid_mutex);
+		return 0;
+	}
+
+	/*
+	 * We do not hold the lock for the open because in case
+	 * SMB2_open needs to reconnect, it will end up calling
+	 * cifs_mark_open_files_invalid() which takes the lock again
+	 * thus causing a deadlock
+	 */
+
+	mutex_unlock(&tcon->cfid.fid_mutex);
+
+	if (smb3_encryption_required(tcon))
+		flags |= CIFS_TRANSFORM_REQ;
+
+	if (!server->ops->new_lease_key)
+		return -EIO;
+
+	pfid = tcon->cfid.fid;
+	server->ops->new_lease_key(pfid);
+
+	memset(rqst, 0, sizeof(rqst));
+	resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
+	memset(rsp_iov, 0, sizeof(rsp_iov));
+
+	/* Open */
+	memset(&open_iov, 0, sizeof(open_iov));
+	rqst[0].rq_iov = open_iov;
+	rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
+
+	oparms.tcon = tcon;
+	oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
+	oparms.desired_access = FILE_READ_ATTRIBUTES;
+	oparms.disposition = FILE_OPEN;
+	oparms.fid = pfid;
+	oparms.reconnect = false;
+
+	rc = SMB2_open_init(tcon, server,
+			    &rqst[0], &oplock, &oparms, &utf16_path);
+	if (rc)
+		goto oshr_free;
+	smb2_set_next_command(tcon, &rqst[0]);
+
+	memset(&qi_iov, 0, sizeof(qi_iov));
+	rqst[1].rq_iov = qi_iov;
+	rqst[1].rq_nvec = 1;
+
+	rc = SMB2_query_info_init(tcon, server,
+				  &rqst[1], COMPOUND_FID,
+				  COMPOUND_FID, FILE_ALL_INFORMATION,
+				  SMB2_O_INFO_FILE, 0,
+				  sizeof(struct smb2_file_all_info) +
+				  PATH_MAX * 2, 0, NULL);
+	if (rc)
+		goto oshr_free;
+
+	smb2_set_related(&rqst[1]);
+
+	rc = compound_send_recv(xid, ses, server,
+				flags, 2, rqst,
+				resp_buftype, rsp_iov);
+	mutex_lock(&tcon->cfid.fid_mutex);
+
+	/*
+	 * Now we need to check again as the cached root might have
+	 * been successfully re-opened from a concurrent process
+	 */
+
+	if (tcon->cfid.is_valid) {
+		/* work was already done */
+
+		/* stash fids for close() later */
+		struct cifs_fid fid = {
+			.persistent_fid = pfid->persistent_fid,
+			.volatile_fid = pfid->volatile_fid,
+		};
+
+		/*
+		 * caller expects this func to set the fid in cfid to valid
+		 * cached root, so increment the refcount.
+		 */
+		kref_get(&tcon->cfid.refcount);
+
+		mutex_unlock(&tcon->cfid.fid_mutex);
+
+		if (rc == 0) {
+			/* close extra handle outside of crit sec */
+			SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+		}
+		rc = 0;
+		goto oshr_free;
+	}
+
+	/* Cached root is still invalid, continue normaly */
+
+	if (rc) {
+		if (rc == -EREMCHG) {
+			tcon->need_reconnect = true;
+			pr_warn_once("server share %s deleted\n",
+				     tcon->treeName);
+		}
+		goto oshr_exit;
+	}
+
+	atomic_inc(&tcon->num_remote_opens);
+
+	o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
+	oparms.fid->persistent_fid = o_rsp->PersistentFileId;
+	oparms.fid->volatile_fid = o_rsp->VolatileFileId;
+#ifdef CONFIG_CIFS_DEBUG2
+	oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
+#endif /* CIFS_DEBUG2 */
+
+	tcon->cfid.tcon = tcon;
+	tcon->cfid.is_valid = true;
+	tcon->cfid.dentry = dentry;
+	dget(dentry);
+	kref_init(&tcon->cfid.refcount);
+
+	/* BB TBD check to see if oplock level check can be removed below */
+	if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
+		/*
+		 * See commit 2f94a3125b87. Increment the refcount when we
+		 * get a lease for root, release it if lease break occurs
+		 */
+		kref_get(&tcon->cfid.refcount);
+		tcon->cfid.has_lease = true;
+		smb2_parse_contexts(server, o_rsp,
+				&oparms.fid->epoch,
+				    oparms.fid->lease_key, &oplock,
+				    NULL, NULL);
+	} else
+		goto oshr_exit;
+
+	qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
+	if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
+		goto oshr_exit;
+	if (!smb2_validate_and_copy_iov(
+				le16_to_cpu(qi_rsp->OutputBufferOffset),
+				sizeof(struct smb2_file_all_info),
+				&rsp_iov[1], sizeof(struct smb2_file_all_info),
+				(char *)&tcon->cfid.file_all_info))
+		tcon->cfid.file_all_info_is_valid = true;
+	tcon->cfid.time = jiffies;
+
+
+oshr_exit:
+	mutex_unlock(&tcon->cfid.fid_mutex);
+oshr_free:
+	SMB2_open_free(&rqst[0]);
+	SMB2_query_info_free(&rqst[1]);
+	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
+	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
+	if (rc == 0) {
+		*cfid = &tcon->cfid;	
+}
+	return rc;
+}
+
+int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
+			      struct dentry *dentry,
+			      struct cached_fid **cfid)
+{
+	mutex_lock(&tcon->cfid.fid_mutex);
+	if (tcon->cfid.dentry == dentry) {
+		cifs_dbg(FYI, "found a cached root file handle by dentry\n");
+		*cfid = &tcon->cfid;
+		kref_get(&tcon->cfid.refcount);
+		mutex_unlock(&tcon->cfid.fid_mutex);
+		return 0;
+	}
+	mutex_unlock(&tcon->cfid.fid_mutex);
+	return -ENOENT;
+}
+
+static void
+smb2_close_cached_fid(struct kref *ref)
+{
+	struct cached_fid *cfid = container_of(ref, struct cached_fid,
+					       refcount);
+	struct cached_dirent *dirent, *q;
+
+	if (cfid->is_valid) {
+		cifs_dbg(FYI, "clear cached root file handle\n");
+		SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
+			   cfid->fid->volatile_fid);
+	}
+
+	/*
+	 * We only check validity above to send SMB2_close,
+	 * but we still need to invalidate these entries
+	 * when this function is called
+	 */
+	cfid->is_valid = false;
+	cfid->file_all_info_is_valid = false;
+	cfid->has_lease = false;
+	if (cfid->dentry) {
+		dput(cfid->dentry);
+		cfid->dentry = NULL;
+	}
+	/*
+	 * Delete all cached dirent names
+	 */
+	mutex_lock(&cfid->dirents.de_mutex);
+	list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
+		list_del(&dirent->entry);
+		kfree(dirent->name);
+		kfree(dirent);
+	}
+	cfid->dirents.is_valid = 0;
+	cfid->dirents.is_failed = 0;
+	cfid->dirents.ctx = NULL;
+	cfid->dirents.pos = 0;
+	mutex_unlock(&cfid->dirents.de_mutex);
+
+}
+
+void close_cached_dir(struct cached_fid *cfid)
+{
+	mutex_lock(&cfid->fid_mutex);
+	kref_put(&cfid->refcount, smb2_close_cached_fid);
+	mutex_unlock(&cfid->fid_mutex);
+}
+
+void close_cached_dir_lease_locked(struct cached_fid *cfid)
+{
+	if (cfid->has_lease) {
+		cfid->has_lease = false;
+		kref_put(&cfid->refcount, smb2_close_cached_fid);
+	}
+}
+
+void close_cached_dir_lease(struct cached_fid *cfid)
+{
+	mutex_lock(&cfid->fid_mutex);
+	close_cached_dir_lease_locked(cfid);
+	mutex_unlock(&cfid->fid_mutex);
+}
+
+/*
+ * Called from cifs_kill_sb when we unmount a share
+ */
+void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
+{
+	struct rb_root *root = &cifs_sb->tlink_tree;
+	struct rb_node *node;
+	struct cached_fid *cfid;
+	struct cifs_tcon *tcon;
+	struct tcon_link *tlink;
+
+	for (node = rb_first(root); node; node = rb_next(node)) {
+		tlink = rb_entry(node, struct tcon_link, tl_rbnode);
+		tcon = tlink_tcon(tlink);
+		if (IS_ERR(tcon))
+			continue;
+		cfid = &tcon->cfid;
+		mutex_lock(&cfid->fid_mutex);
+		if (cfid->dentry) {
+			dput(cfid->dentry);
+			cfid->dentry = NULL;
+		}
+		mutex_unlock(&cfid->fid_mutex);
+	}
+}
+
+/*
+ * Invalidate and close all cached dirs when a TCON has been reset
+ * due to a session loss.
+ */
+void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
+{
+	mutex_lock(&tcon->cfid.fid_mutex);
+	tcon->cfid.is_valid = false;
+	/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
+	close_cached_dir_lease_locked(&tcon->cfid);
+	memset(tcon->cfid.fid, 0, sizeof(struct cifs_fid));
+	mutex_unlock(&tcon->cfid.fid_mutex);
+}
+
+static void
+smb2_cached_lease_break(struct work_struct *work)
+{
+	struct cached_fid *cfid = container_of(work,
+				struct cached_fid, lease_break);
+
+	close_cached_dir_lease(cfid);
+}
+
+int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
+{
+	if (tcon->cfid.is_valid &&
+	    !memcmp(lease_key,
+		    tcon->cfid.fid->lease_key,
+		    SMB2_LEASE_KEY_SIZE)) {
+		tcon->cfid.time = 0;
+		INIT_WORK(&tcon->cfid.lease_break,
+			  smb2_cached_lease_break);
+		queue_work(cifsiod_wq,
+			   &tcon->cfid.lease_break);
+		spin_unlock(&cifs_tcp_ses_lock);
+		return true;
+	}
+	return false;
+}
diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
new file mode 100644
index 000000000000..3731c755eea5
--- /dev/null
+++ b/fs/cifs/cached_dir.h
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Functions to handle the cached directory entries
+ *
+ *  Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
+ */
+
+#ifndef _CACHED_DIR_H
+#define _CACHED_DIR_H
+
+
+extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
+			   const char *path,
+			   struct cifs_sb_info *cifs_sb,
+			   struct cached_fid **cfid);
+extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
+				     struct dentry *dentry,
+				     struct cached_fid **cfid);
+extern void close_cached_dir(struct cached_fid *cfid);
+extern void close_cached_dir_lease(struct cached_fid *cfid);
+extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
+extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
+extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
+extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
+
+#endif			/* _CACHED_DIR_H */
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index f909d9e9faaa..615fbe2bff3c 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -46,6 +46,7 @@
 #include "netlink.h"
 #endif
 #include "fs_context.h"
+#include "cached_dir.h"
 
 /*
  * DOS dates from 1980/1/1 through 2107/12/31
@@ -264,30 +265,13 @@ cifs_read_super(struct super_block *sb)
 static void cifs_kill_sb(struct super_block *sb)
 {
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-	struct cifs_tcon *tcon;
-	struct cached_fid *cfid;
-	struct rb_root *root = &cifs_sb->tlink_tree;
-	struct rb_node *node;
-	struct tcon_link *tlink;
 
 	/*
 	 * We ned to release all dentries for the cached directories
 	 * before we kill the sb.
 	 */
 	if (cifs_sb->root) {
-		for (node = rb_first(root); node; node = rb_next(node)) {
-			tlink = rb_entry(node, struct tcon_link, tl_rbnode);
-			tcon = tlink_tcon(tlink);
-			if (IS_ERR(tcon))
-				continue;
-			cfid = &tcon->crfid;
-			mutex_lock(&cfid->fid_mutex);
-			if (cfid->dentry) {
-				dput(cfid->dentry);
-				cfid->dentry = NULL;
-			}
-			mutex_unlock(&cfid->fid_mutex);
-		}
+		close_all_cached_dirs(cifs_sb);
 
 		/* finally release root dentry */
 		dput(cifs_sb->root);
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 9b7f409bfc8c..657fabb9067b 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1253,7 +1253,7 @@ struct cifs_tcon {
 	struct fscache_volume *fscache;	/* cookie for share */
 #endif
 	struct list_head pending_opens;	/* list of incomplete opens */
-	struct cached_fid crfid; /* Cached root fid */
+	struct cached_fid cfid; /* Cached root fid */
 	/* BB add field for back pointer to sb struct(s)? */
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	struct list_head ulist; /* cache update list */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index d59aebefa71c..881bf112d6ae 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -599,7 +599,6 @@ enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
 struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
 void cifs_aio_ctx_release(struct kref *refcount);
 int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
-void smb2_cached_lease_break(struct work_struct *work);
 
 int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
 		    struct sdesc **sdesc);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 9ed21752f2df..78dfadd729fe 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -35,6 +35,7 @@
 #ifdef CONFIG_CIFS_DFS_UPCALL
 #include "dfs_cache.h"
 #endif
+#include "cached_dir.h"
 
 #ifdef CONFIG_CIFS_POSIX
 static struct {
@@ -91,12 +92,7 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
 	}
 	spin_unlock(&tcon->open_file_lock);
 
-	mutex_lock(&tcon->crfid.fid_mutex);
-	tcon->crfid.is_valid = false;
-	/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
-	close_cached_dir_lease_locked(&tcon->crfid);
-	memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
-	mutex_unlock(&tcon->crfid.fid_mutex);
+	invalidate_all_cached_dirs(tcon);
 
 	spin_lock(&cifs_tcp_ses_lock);
 	if (tcon->status == TID_IN_FILES_INVALIDATE)
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 3ad303dd5e5a..7714f47d199b 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -25,6 +25,7 @@
 #include "fscache.h"
 #include "fs_context.h"
 #include "cifs_ioctl.h"
+#include "cached_dir.h"
 
 static void cifs_set_ops(struct inode *inode)
 {
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 16168ebd1a62..fa1a03ddbbe2 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -115,13 +115,13 @@ tconInfoAlloc(void)
 	ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
 	if (!ret_buf)
 		return NULL;
-	ret_buf->crfid.fid = kzalloc(sizeof(*ret_buf->crfid.fid), GFP_KERNEL);
-	if (!ret_buf->crfid.fid) {
+	ret_buf->cfid.fid = kzalloc(sizeof(*ret_buf->cfid.fid), GFP_KERNEL);
+	if (!ret_buf->cfid.fid) {
 		kfree(ret_buf);
 		return NULL;
 	}
-	INIT_LIST_HEAD(&ret_buf->crfid.dirents.entries);
-	mutex_init(&ret_buf->crfid.dirents.de_mutex);
+	INIT_LIST_HEAD(&ret_buf->cfid.dirents.entries);
+	mutex_init(&ret_buf->cfid.dirents.de_mutex);
 
 	atomic_inc(&tconInfoAllocCount);
 	ret_buf->status = TID_NEW;
@@ -129,7 +129,7 @@ tconInfoAlloc(void)
 	INIT_LIST_HEAD(&ret_buf->openFileList);
 	INIT_LIST_HEAD(&ret_buf->tcon_list);
 	spin_lock_init(&ret_buf->open_file_lock);
-	mutex_init(&ret_buf->crfid.fid_mutex);
+	mutex_init(&ret_buf->cfid.fid_mutex);
 	spin_lock_init(&ret_buf->stat_lock);
 	atomic_set(&ret_buf->num_local_opens, 0);
 	atomic_set(&ret_buf->num_remote_opens, 0);
@@ -147,7 +147,7 @@ tconInfoFree(struct cifs_tcon *buf_to_free)
 	atomic_dec(&tconInfoAllocCount);
 	kfree(buf_to_free->nativeFileSystem);
 	kfree_sensitive(buf_to_free->password);
-	kfree(buf_to_free->crfid.fid);
+	kfree(buf_to_free->cfid.fid);
 	kfree(buf_to_free);
 }
 
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 384cabdf47ca..a06072ae6c7e 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -21,6 +21,7 @@
 #include "cifsfs.h"
 #include "smb2proto.h"
 #include "fs_context.h"
+#include "cached_dir.h"
 
 /*
  * To be safe - for UCS to UTF-8 with strings loaded with the rare long
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 8571a459c710..f6f9fc3f2e2d 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -23,6 +23,7 @@
 #include "smb2glob.h"
 #include "smb2pdu.h"
 #include "smb2proto.h"
+#include "cached_dir.h"
 
 static void
 free_set_inf_compound(struct smb_rqst *rqst)
@@ -518,9 +519,9 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
 	/* If it is a root and its handle is cached then use it */
 	if (!rc) {
-		if (tcon->crfid.file_all_info_is_valid) {
+		if (tcon->cfid.file_all_info_is_valid) {
 			move_smb2_info_to_cifs(data,
-					       &tcon->crfid.file_all_info);
+					       &tcon->cfid.file_all_info);
 		} else {
 			rc = SMB2_query_info(xid, tcon,
 					     cfid->fid->persistent_fid,
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index db0f27fd373b..d3d9174ddd7c 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -16,6 +16,7 @@
 #include "smb2status.h"
 #include "smb2glob.h"
 #include "nterr.h"
+#include "cached_dir.h"
 
 static int
 check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
@@ -639,18 +640,8 @@ smb2_is_valid_lease_break(char *buffer)
 				}
 				spin_unlock(&tcon->open_file_lock);
 
-				if (tcon->crfid.is_valid &&
-				    !memcmp(rsp->LeaseKey,
-					    tcon->crfid.fid->lease_key,
-					    SMB2_LEASE_KEY_SIZE)) {
-					tcon->crfid.time = 0;
-					INIT_WORK(&tcon->crfid.lease_break,
-						  smb2_cached_lease_break);
-					queue_work(cifsiod_wq,
-						   &tcon->crfid.lease_break);
-					spin_unlock(&cifs_tcp_ses_lock);
+				if (cached_dir_lease_break(tcon, rsp->LeaseKey))
 					return true;
-				}
 			}
 		}
 	}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index aa4c1d403708..01aafedc477e 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -27,6 +27,7 @@
 #include "smbdirect.h"
 #include "fscache.h"
 #include "fs_context.h"
+#include "cached_dir.h"
 
 /* Change credits for different ops and return the total number of credits */
 static int
@@ -701,300 +702,6 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
 	return rc;
 }
 
-static void
-smb2_close_cached_fid(struct kref *ref)
-{
-	struct cached_fid *cfid = container_of(ref, struct cached_fid,
-					       refcount);
-	struct cached_dirent *dirent, *q;
-
-	if (cfid->is_valid) {
-		cifs_dbg(FYI, "clear cached root file handle\n");
-		SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
-			   cfid->fid->volatile_fid);
-	}
-
-	/*
-	 * We only check validity above to send SMB2_close,
-	 * but we still need to invalidate these entries
-	 * when this function is called
-	 */
-	cfid->is_valid = false;
-	cfid->file_all_info_is_valid = false;
-	cfid->has_lease = false;
-	if (cfid->dentry) {
-		dput(cfid->dentry);
-		cfid->dentry = NULL;
-	}
-	/*
-	 * Delete all cached dirent names
-	 */
-	mutex_lock(&cfid->dirents.de_mutex);
-	list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
-		list_del(&dirent->entry);
-		kfree(dirent->name);
-		kfree(dirent);
-	}
-	cfid->dirents.is_valid = 0;
-	cfid->dirents.is_failed = 0;
-	cfid->dirents.ctx = NULL;
-	cfid->dirents.pos = 0;
-	mutex_unlock(&cfid->dirents.de_mutex);
-
-}
-
-void close_cached_dir(struct cached_fid *cfid)
-{
-	mutex_lock(&cfid->fid_mutex);
-	kref_put(&cfid->refcount, smb2_close_cached_fid);
-	mutex_unlock(&cfid->fid_mutex);
-}
-
-void close_cached_dir_lease_locked(struct cached_fid *cfid)
-{
-	if (cfid->has_lease) {
-		cfid->has_lease = false;
-		kref_put(&cfid->refcount, smb2_close_cached_fid);
-	}
-}
-
-void close_cached_dir_lease(struct cached_fid *cfid)
-{
-	mutex_lock(&cfid->fid_mutex);
-	close_cached_dir_lease_locked(cfid);
-	mutex_unlock(&cfid->fid_mutex);
-}
-
-void
-smb2_cached_lease_break(struct work_struct *work)
-{
-	struct cached_fid *cfid = container_of(work,
-				struct cached_fid, lease_break);
-
-	close_cached_dir_lease(cfid);
-}
-
-/*
- * Open the and cache a directory handle.
- * Only supported for the root handle.
- * If error then *cfid is not initialized.
- */
-int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
-		const char *path,
-		struct cifs_sb_info *cifs_sb,
-		struct cached_fid **cfid)
-{
-	struct cifs_ses *ses;
-	struct TCP_Server_Info *server;
-	struct cifs_open_parms oparms;
-	struct smb2_create_rsp *o_rsp = NULL;
-	struct smb2_query_info_rsp *qi_rsp = NULL;
-	int resp_buftype[2];
-	struct smb_rqst rqst[2];
-	struct kvec rsp_iov[2];
-	struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
-	struct kvec qi_iov[1];
-	int rc, flags = 0;
-	__le16 utf16_path = 0; /* Null - since an open of top of share */
-	u8 oplock = SMB2_OPLOCK_LEVEL_II;
-	struct cifs_fid *pfid;
-	struct dentry *dentry;
-
-	if (tcon == NULL || tcon->nohandlecache ||
-	    is_smb1_server(tcon->ses->server))
-		return -ENOTSUPP;
-
-	ses = tcon->ses;
-	server = ses->server;
-
-	if (cifs_sb->root == NULL)
-		return -ENOENT;
-
-	if (strlen(path))
-		return -ENOENT;
-
-	dentry = cifs_sb->root;
-
-	mutex_lock(&tcon->crfid.fid_mutex);
-	if (tcon->crfid.is_valid) {
-		cifs_dbg(FYI, "found a cached root file handle\n");
-		*cfid = &tcon->crfid;
-		kref_get(&tcon->crfid.refcount);
-		mutex_unlock(&tcon->crfid.fid_mutex);
-		return 0;
-	}
-
-	/*
-	 * We do not hold the lock for the open because in case
-	 * SMB2_open needs to reconnect, it will end up calling
-	 * cifs_mark_open_files_invalid() which takes the lock again
-	 * thus causing a deadlock
-	 */
-
-	mutex_unlock(&tcon->crfid.fid_mutex);
-
-	if (smb3_encryption_required(tcon))
-		flags |= CIFS_TRANSFORM_REQ;
-
-	if (!server->ops->new_lease_key)
-		return -EIO;
-
-	pfid = tcon->crfid.fid;
-	server->ops->new_lease_key(pfid);
-
-	memset(rqst, 0, sizeof(rqst));
-	resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
-	memset(rsp_iov, 0, sizeof(rsp_iov));
-
-	/* Open */
-	memset(&open_iov, 0, sizeof(open_iov));
-	rqst[0].rq_iov = open_iov;
-	rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
-
-	oparms.tcon = tcon;
-	oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
-	oparms.desired_access = FILE_READ_ATTRIBUTES;
-	oparms.disposition = FILE_OPEN;
-	oparms.fid = pfid;
-	oparms.reconnect = false;
-
-	rc = SMB2_open_init(tcon, server,
-			    &rqst[0], &oplock, &oparms, &utf16_path);
-	if (rc)
-		goto oshr_free;
-	smb2_set_next_command(tcon, &rqst[0]);
-
-	memset(&qi_iov, 0, sizeof(qi_iov));
-	rqst[1].rq_iov = qi_iov;
-	rqst[1].rq_nvec = 1;
-
-	rc = SMB2_query_info_init(tcon, server,
-				  &rqst[1], COMPOUND_FID,
-				  COMPOUND_FID, FILE_ALL_INFORMATION,
-				  SMB2_O_INFO_FILE, 0,
-				  sizeof(struct smb2_file_all_info) +
-				  PATH_MAX * 2, 0, NULL);
-	if (rc)
-		goto oshr_free;
-
-	smb2_set_related(&rqst[1]);
-
-	rc = compound_send_recv(xid, ses, server,
-				flags, 2, rqst,
-				resp_buftype, rsp_iov);
-	mutex_lock(&tcon->crfid.fid_mutex);
-
-	/*
-	 * Now we need to check again as the cached root might have
-	 * been successfully re-opened from a concurrent process
-	 */
-
-	if (tcon->crfid.is_valid) {
-		/* work was already done */
-
-		/* stash fids for close() later */
-		struct cifs_fid fid = {
-			.persistent_fid = pfid->persistent_fid,
-			.volatile_fid = pfid->volatile_fid,
-		};
-
-		/*
-		 * caller expects this func to set the fid in crfid to valid
-		 * cached root, so increment the refcount.
-		 */
-		kref_get(&tcon->crfid.refcount);
-
-		mutex_unlock(&tcon->crfid.fid_mutex);
-
-		if (rc == 0) {
-			/* close extra handle outside of crit sec */
-			SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
-		}
-		rc = 0;
-		goto oshr_free;
-	}
-
-	/* Cached root is still invalid, continue normaly */
-
-	if (rc) {
-		if (rc == -EREMCHG) {
-			tcon->need_reconnect = true;
-			pr_warn_once("server share %s deleted\n",
-				     tcon->treeName);
-		}
-		goto oshr_exit;
-	}
-
-	atomic_inc(&tcon->num_remote_opens);
-
-	o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
-	oparms.fid->persistent_fid = o_rsp->PersistentFileId;
-	oparms.fid->volatile_fid = o_rsp->VolatileFileId;
-#ifdef CONFIG_CIFS_DEBUG2
-	oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
-#endif /* CIFS_DEBUG2 */
-
-	tcon->crfid.tcon = tcon;
-	tcon->crfid.is_valid = true;
-	tcon->crfid.dentry = dentry;
-	dget(dentry);
-	kref_init(&tcon->crfid.refcount);
-
-	/* BB TBD check to see if oplock level check can be removed below */
-	if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
-		/*
-		 * See commit 2f94a3125b87. Increment the refcount when we
-		 * get a lease for root, release it if lease break occurs
-		 */
-		kref_get(&tcon->crfid.refcount);
-		tcon->crfid.has_lease = true;
-		smb2_parse_contexts(server, o_rsp,
-				&oparms.fid->epoch,
-				    oparms.fid->lease_key, &oplock,
-				    NULL, NULL);
-	} else
-		goto oshr_exit;
-
-	qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
-	if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
-		goto oshr_exit;
-	if (!smb2_validate_and_copy_iov(
-				le16_to_cpu(qi_rsp->OutputBufferOffset),
-				sizeof(struct smb2_file_all_info),
-				&rsp_iov[1], sizeof(struct smb2_file_all_info),
-				(char *)&tcon->crfid.file_all_info))
-		tcon->crfid.file_all_info_is_valid = true;
-	tcon->crfid.time = jiffies;
-
-
-oshr_exit:
-	mutex_unlock(&tcon->crfid.fid_mutex);
-oshr_free:
-	SMB2_open_free(&rqst[0]);
-	SMB2_query_info_free(&rqst[1]);
-	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
-	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
-	if (rc == 0)
-		*cfid = &tcon->crfid;
-	return rc;
-}
-
-int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
-			      struct dentry *dentry,
-			      struct cached_fid **cfid)
-{
-	mutex_lock(&tcon->crfid.fid_mutex);
-	if (tcon->crfid.dentry == dentry) {
-		cifs_dbg(FYI, "found a cached root file handle by dentry\n");
-		*cfid = &tcon->crfid;
-		kref_get(&tcon->crfid.refcount);
-		mutex_unlock(&tcon->crfid.fid_mutex);
-		return 0;
-	}
-	mutex_unlock(&tcon->crfid.fid_mutex);
-	return -ENOENT;
-}
-
 static void
 smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
 	      struct cifs_sb_info *cifs_sb)
@@ -1077,7 +784,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 	struct cifs_open_parms oparms;
 	struct cifs_fid fid;
 
-	if ((*full_path == 0) && tcon->crfid.is_valid)
+	if ((*full_path == 0) && tcon->cfid.is_valid)
 		return 0;
 
 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 295ee8b88055..9ee1b6225619 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -39,6 +39,7 @@
 #ifdef CONFIG_CIFS_DFS_UPCALL
 #include "dfs_cache.h"
 #endif
+#include "cached_dir.h"
 
 /*
  *  The following table defines the expected "StructureSize" of SMB2 requests
@@ -1978,7 +1979,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
 	}
 	spin_unlock(&ses->chan_lock);
 
-	close_cached_dir_lease(&tcon->crfid);
+	close_cached_dir_lease(&tcon->cfid);
 
 	rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server,
 				 (void **) &req,
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index a69f1eed1cfe..51c5bf4a338a 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -54,16 +54,6 @@ extern bool smb2_is_valid_oplock_break(char *buffer,
 extern int smb3_handle_read_data(struct TCP_Server_Info *server,
 				 struct mid_q_entry *mid);
 
-extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
-			   const char *path,
-			   struct cifs_sb_info *cifs_sb,
-			   struct cached_fid **cfid);
-extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
-				     struct dentry *dentry,
-				     struct cached_fid **cfid);
-extern void close_cached_dir(struct cached_fid *cfid);
-extern void close_cached_dir_lease(struct cached_fid *cfid);
-extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
 extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
 				   struct smb2_file_all_info *src);
 extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 2/9] cifs: Do not use tcon->cfid directly, use the cfid we get from open_cached_dir
  2022-08-09  2:11 WIP: Expand directory handle cache to also cache non-root directories Ronnie Sahlberg
  2022-08-09  2:11 ` [PATCH 1/9] cifs: Move cached-dir functions into a separate file Ronnie Sahlberg
@ 2022-08-09  2:11 ` Ronnie Sahlberg
  2022-08-09  4:22   ` Steve French
  2022-08-11 13:03   ` Paulo Alcantara
  2022-08-09  2:11 ` [PATCH 3/9] cifs: Add constructor/destructors for tcon->cfid Ronnie Sahlberg
                   ` (6 subsequent siblings)
  8 siblings, 2 replies; 27+ messages in thread
From: Ronnie Sahlberg @ 2022-08-09  2:11 UTC (permalink / raw)
  To: linux-cifs; +Cc: Steve French, Ronnie Sahlberg

They are the same right now but tcon-> will later point to a different
type of struct containing a list of cfids.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/smb2inode.c | 4 ++--
 fs/cifs/smb2pdu.c   | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index f6f9fc3f2e2d..09f01f70e020 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -519,9 +519,9 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
 	/* If it is a root and its handle is cached then use it */
 	if (!rc) {
-		if (tcon->cfid.file_all_info_is_valid) {
+		if (cfid->file_all_info_is_valid) {
 			move_smb2_info_to_cifs(data,
-					       &tcon->cfid.file_all_info);
+					       &cfid->file_all_info);
 		} else {
 			rc = SMB2_query_info(xid, tcon,
 					     cfid->fid->persistent_fid,
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 9ee1b6225619..5dbd2cac470c 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -1979,7 +1979,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
 	}
 	spin_unlock(&ses->chan_lock);
 
-	close_cached_dir_lease(&tcon->cfid);
+	invalidate_all_cached_dirs(tcon);
 
 	rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server,
 				 (void **) &req,
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 3/9] cifs: Add constructor/destructors for tcon->cfid
  2022-08-09  2:11 WIP: Expand directory handle cache to also cache non-root directories Ronnie Sahlberg
  2022-08-09  2:11 ` [PATCH 1/9] cifs: Move cached-dir functions into a separate file Ronnie Sahlberg
  2022-08-09  2:11 ` [PATCH 2/9] cifs: Do not use tcon->cfid directly, use the cfid we get from open_cached_dir Ronnie Sahlberg
@ 2022-08-09  2:11 ` Ronnie Sahlberg
  2022-08-11 13:15   ` Paulo Alcantara
  2022-08-09  2:11 ` [PATCH 4/9] cifs: Make tcon contain a wrapper structure cached_fids instead of cached_fid Ronnie Sahlberg
                   ` (5 subsequent siblings)
  8 siblings, 1 reply; 27+ messages in thread
From: Ronnie Sahlberg @ 2022-08-09  2:11 UTC (permalink / raw)
  To: linux-cifs; +Cc: Steve French, Ronnie Sahlberg

and move the structure definitions into cached_dir.h

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/cached_dir.c | 114 ++++++++++++++++++++++++++-----------------
 fs/cifs/cached_dir.h |  38 +++++++++++++++
 fs/cifs/cifsglob.h   |  38 +--------------
 fs/cifs/misc.c       |  20 ++++----
 fs/cifs/smb2inode.c  |   4 +-
 fs/cifs/smb2ops.c    |   8 +--
 6 files changed, 123 insertions(+), 99 deletions(-)

diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
index f2e17c1d5196..7ca085299890 100644
--- a/fs/cifs/cached_dir.c
+++ b/fs/cifs/cached_dir.c
@@ -18,7 +18,7 @@
 int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 		const char *path,
 		struct cifs_sb_info *cifs_sb,
-		struct cached_fid **cfid)
+		struct cached_fid **ret_cfid)
 {
 	struct cifs_ses *ses;
 	struct TCP_Server_Info *server;
@@ -35,7 +35,8 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	u8 oplock = SMB2_OPLOCK_LEVEL_II;
 	struct cifs_fid *pfid;
 	struct dentry *dentry;
-
+	struct cached_fid *cfid;
+	  
 	if (tcon == NULL || tcon->nohandlecache ||
 	    is_smb1_server(tcon->ses->server))
 		return -ENOTSUPP;
@@ -51,12 +52,13 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 
 	dentry = cifs_sb->root;
 
-	mutex_lock(&tcon->cfid.fid_mutex);
-	if (tcon->cfid.is_valid) {
+	cfid = tcon->cfid;
+	mutex_lock(&cfid->fid_mutex);
+	if (cfid->is_valid) {
 		cifs_dbg(FYI, "found a cached root file handle\n");
-		*cfid = &tcon->cfid;
-		kref_get(&tcon->cfid.refcount);
-		mutex_unlock(&tcon->cfid.fid_mutex);
+		*ret_cfid = cfid;
+		kref_get(&cfid->refcount);
+		mutex_unlock(&cfid->fid_mutex);
 		return 0;
 	}
 
@@ -67,7 +69,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	 * thus causing a deadlock
 	 */
 
-	mutex_unlock(&tcon->cfid.fid_mutex);
+	mutex_unlock(&cfid->fid_mutex);
 
 	if (smb3_encryption_required(tcon))
 		flags |= CIFS_TRANSFORM_REQ;
@@ -75,7 +77,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	if (!server->ops->new_lease_key)
 		return -EIO;
 
-	pfid = tcon->cfid.fid;
+	pfid = &cfid->fid;
 	server->ops->new_lease_key(pfid);
 
 	memset(rqst, 0, sizeof(rqst));
@@ -118,14 +120,14 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	rc = compound_send_recv(xid, ses, server,
 				flags, 2, rqst,
 				resp_buftype, rsp_iov);
-	mutex_lock(&tcon->cfid.fid_mutex);
+	mutex_lock(&cfid->fid_mutex);
 
 	/*
 	 * Now we need to check again as the cached root might have
 	 * been successfully re-opened from a concurrent process
 	 */
 
-	if (tcon->cfid.is_valid) {
+	if (cfid->is_valid) {
 		/* work was already done */
 
 		/* stash fids for close() later */
@@ -138,9 +140,9 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 		 * caller expects this func to set the fid in cfid to valid
 		 * cached root, so increment the refcount.
 		 */
-		kref_get(&tcon->cfid.refcount);
+		kref_get(&cfid->refcount);
 
-		mutex_unlock(&tcon->cfid.fid_mutex);
+		mutex_unlock(&cfid->fid_mutex);
 
 		if (rc == 0) {
 			/* close extra handle outside of crit sec */
@@ -170,11 +172,11 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
 #endif /* CIFS_DEBUG2 */
 
-	tcon->cfid.tcon = tcon;
-	tcon->cfid.is_valid = true;
-	tcon->cfid.dentry = dentry;
+	cfid->tcon = tcon;
+	cfid->is_valid = true;
+	cfid->dentry = dentry;
 	dget(dentry);
-	kref_init(&tcon->cfid.refcount);
+	kref_init(&cfid->refcount);
 
 	/* BB TBD check to see if oplock level check can be removed below */
 	if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
@@ -182,8 +184,8 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 		 * See commit 2f94a3125b87. Increment the refcount when we
 		 * get a lease for root, release it if lease break occurs
 		 */
-		kref_get(&tcon->cfid.refcount);
-		tcon->cfid.has_lease = true;
+		kref_get(&cfid->refcount);
+		cfid->has_lease = true;
 		smb2_parse_contexts(server, o_rsp,
 				&oparms.fid->epoch,
 				    oparms.fid->lease_key, &oplock,
@@ -198,37 +200,41 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 				le16_to_cpu(qi_rsp->OutputBufferOffset),
 				sizeof(struct smb2_file_all_info),
 				&rsp_iov[1], sizeof(struct smb2_file_all_info),
-				(char *)&tcon->cfid.file_all_info))
-		tcon->cfid.file_all_info_is_valid = true;
-	tcon->cfid.time = jiffies;
+				(char *)&cfid->file_all_info))
+		cfid->file_all_info_is_valid = true;
+	cfid->time = jiffies;
 
 
 oshr_exit:
-	mutex_unlock(&tcon->cfid.fid_mutex);
+	mutex_unlock(&cfid->fid_mutex);
 oshr_free:
 	SMB2_open_free(&rqst[0]);
 	SMB2_query_info_free(&rqst[1]);
 	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
 	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
 	if (rc == 0) {
-		*cfid = &tcon->cfid;	
-}
+		*ret_cfid = cfid;	
+	}
 	return rc;
 }
 
 int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
 			      struct dentry *dentry,
-			      struct cached_fid **cfid)
+			      struct cached_fid **ret_cfid)
 {
-	mutex_lock(&tcon->cfid.fid_mutex);
-	if (tcon->cfid.dentry == dentry) {
+	struct cached_fid *cfid;
+
+	cfid = tcon->cfid;
+
+	mutex_lock(&cfid->fid_mutex);
+	if (cfid->dentry == dentry) {
 		cifs_dbg(FYI, "found a cached root file handle by dentry\n");
-		*cfid = &tcon->cfid;
-		kref_get(&tcon->cfid.refcount);
-		mutex_unlock(&tcon->cfid.fid_mutex);
+		*ret_cfid = cfid;
+		kref_get(&cfid->refcount);
+		mutex_unlock(&cfid->fid_mutex);
 		return 0;
 	}
-	mutex_unlock(&tcon->cfid.fid_mutex);
+	mutex_unlock(&cfid->fid_mutex);
 	return -ENOENT;
 }
 
@@ -241,8 +247,8 @@ smb2_close_cached_fid(struct kref *ref)
 
 	if (cfid->is_valid) {
 		cifs_dbg(FYI, "clear cached root file handle\n");
-		SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
-			   cfid->fid->volatile_fid);
+		SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
+			   cfid->fid.volatile_fid);
 	}
 
 	/*
@@ -312,7 +318,7 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
 		tcon = tlink_tcon(tlink);
 		if (IS_ERR(tcon))
 			continue;
-		cfid = &tcon->cfid;
+		cfid = tcon->cfid;
 		mutex_lock(&cfid->fid_mutex);
 		if (cfid->dentry) {
 			dput(cfid->dentry);
@@ -328,12 +334,12 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
  */
 void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
 {
-	mutex_lock(&tcon->cfid.fid_mutex);
-	tcon->cfid.is_valid = false;
+	mutex_lock(&tcon->cfid->fid_mutex);
+	tcon->cfid->is_valid = false;
 	/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
-	close_cached_dir_lease_locked(&tcon->cfid);
-	memset(tcon->cfid.fid, 0, sizeof(struct cifs_fid));
-	mutex_unlock(&tcon->cfid.fid_mutex);
+	close_cached_dir_lease_locked(tcon->cfid);
+	memset(&tcon->cfid->fid, 0, sizeof(struct cifs_fid));
+	mutex_unlock(&tcon->cfid->fid_mutex);
 }
 
 static void
@@ -347,17 +353,35 @@ smb2_cached_lease_break(struct work_struct *work)
 
 int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
 {
-	if (tcon->cfid.is_valid &&
+	if (tcon->cfid->is_valid &&
 	    !memcmp(lease_key,
-		    tcon->cfid.fid->lease_key,
+		    tcon->cfid->fid.lease_key,
 		    SMB2_LEASE_KEY_SIZE)) {
-		tcon->cfid.time = 0;
-		INIT_WORK(&tcon->cfid.lease_break,
+		tcon->cfid->time = 0;
+		INIT_WORK(&tcon->cfid->lease_break,
 			  smb2_cached_lease_break);
 		queue_work(cifsiod_wq,
-			   &tcon->cfid.lease_break);
+			   &tcon->cfid->lease_break);
 		spin_unlock(&cifs_tcp_ses_lock);
 		return true;
 	}
 	return false;
 }
+
+struct cached_fid *init_cached_dir(void)
+{
+	struct cached_fid *cfid;
+
+	cfid = kzalloc(sizeof(*cfid), GFP_KERNEL);
+	if (!cfid)
+		return NULL;
+	INIT_LIST_HEAD(&cfid->dirents.entries);
+	mutex_init(&cfid->dirents.de_mutex);
+	mutex_init(&cfid->fid_mutex);
+	return cfid;
+}
+
+void free_cached_dir(struct cifs_tcon *tcon)
+{
+	kfree(tcon->cfid);
+}
diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
index 3731c755eea5..b44edfbd8e1a 100644
--- a/fs/cifs/cached_dir.h
+++ b/fs/cifs/cached_dir.h
@@ -9,6 +9,44 @@
 #define _CACHED_DIR_H
 
 
+struct cached_dirent {
+	struct list_head entry;
+	char *name;
+	int namelen;
+	loff_t pos;
+
+	struct cifs_fattr fattr;
+};
+
+struct cached_dirents {
+	bool is_valid:1;
+	bool is_failed:1;
+	struct dir_context *ctx; /*
+				  * Only used to make sure we only take entries
+				  * from a single context. Never dereferenced.
+				  */
+	struct mutex de_mutex;
+	int pos;		 /* Expected ctx->pos */
+	struct list_head entries;
+};
+
+struct cached_fid {
+	bool is_valid:1;	/* Do we have a useable root fid */
+	bool file_all_info_is_valid:1;
+	bool has_lease:1;
+	unsigned long time; /* jiffies of when lease was taken */
+	struct kref refcount;
+	struct cifs_fid fid;
+	struct mutex fid_mutex;
+	struct cifs_tcon *tcon;
+	struct dentry *dentry;
+	struct work_struct lease_break;
+	struct smb2_file_all_info file_all_info;
+	struct cached_dirents dirents;
+};
+
+extern struct cached_fid *init_cached_dir(void);
+extern void free_cached_dir(struct cifs_tcon *tcon);
 extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 			   const char *path,
 			   struct cifs_sb_info *cifs_sb,
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 657fabb9067b..d3b233a2737d 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1125,42 +1125,6 @@ struct cifs_fattr {
 	u32             cf_cifstag;
 };
 
-struct cached_dirent {
-	struct list_head entry;
-	char *name;
-	int namelen;
-	loff_t pos;
-
-	struct cifs_fattr fattr;
-};
-
-struct cached_dirents {
-	bool is_valid:1;
-	bool is_failed:1;
-	struct dir_context *ctx; /*
-				  * Only used to make sure we only take entries
-				  * from a single context. Never dereferenced.
-				  */
-	struct mutex de_mutex;
-	int pos;		 /* Expected ctx->pos */
-	struct list_head entries;
-};
-
-struct cached_fid {
-	bool is_valid:1;	/* Do we have a useable root fid */
-	bool file_all_info_is_valid:1;
-	bool has_lease:1;
-	unsigned long time; /* jiffies of when lease was taken */
-	struct kref refcount;
-	struct cifs_fid *fid;
-	struct mutex fid_mutex;
-	struct cifs_tcon *tcon;
-	struct dentry *dentry;
-	struct work_struct lease_break;
-	struct smb2_file_all_info file_all_info;
-	struct cached_dirents dirents;
-};
-
 /*
  * there is one of these for each connection to a resource on a particular
  * session
@@ -1253,7 +1217,7 @@ struct cifs_tcon {
 	struct fscache_volume *fscache;	/* cookie for share */
 #endif
 	struct list_head pending_opens;	/* list of incomplete opens */
-	struct cached_fid cfid; /* Cached root fid */
+	struct cached_fid *cfid; /* Cached root fid */
 	/* BB add field for back pointer to sb struct(s)? */
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	struct list_head ulist; /* cache update list */
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index fa1a03ddbbe2..4e2e3b557591 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -23,6 +23,7 @@
 #include "dns_resolve.h"
 #endif
 #include "fs_context.h"
+#include "cached_dir.h"
 
 extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
@@ -115,13 +116,11 @@ tconInfoAlloc(void)
 	ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
 	if (!ret_buf)
 		return NULL;
-	ret_buf->cfid.fid = kzalloc(sizeof(*ret_buf->cfid.fid), GFP_KERNEL);
-	if (!ret_buf->cfid.fid) {
+	ret_buf->cfid = init_cached_dir();
+	if (!ret_buf->cfid) {
 		kfree(ret_buf);
 		return NULL;
 	}
-	INIT_LIST_HEAD(&ret_buf->cfid.dirents.entries);
-	mutex_init(&ret_buf->cfid.dirents.de_mutex);
 
 	atomic_inc(&tconInfoAllocCount);
 	ret_buf->status = TID_NEW;
@@ -129,7 +128,6 @@ tconInfoAlloc(void)
 	INIT_LIST_HEAD(&ret_buf->openFileList);
 	INIT_LIST_HEAD(&ret_buf->tcon_list);
 	spin_lock_init(&ret_buf->open_file_lock);
-	mutex_init(&ret_buf->cfid.fid_mutex);
 	spin_lock_init(&ret_buf->stat_lock);
 	atomic_set(&ret_buf->num_local_opens, 0);
 	atomic_set(&ret_buf->num_remote_opens, 0);
@@ -138,17 +136,17 @@ tconInfoAlloc(void)
 }
 
 void
-tconInfoFree(struct cifs_tcon *buf_to_free)
+tconInfoFree(struct cifs_tcon *tcon)
 {
-	if (buf_to_free == NULL) {
+	if (tcon == NULL) {
 		cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n");
 		return;
 	}
+	free_cached_dir(tcon);
 	atomic_dec(&tconInfoAllocCount);
-	kfree(buf_to_free->nativeFileSystem);
-	kfree_sensitive(buf_to_free->password);
-	kfree(buf_to_free->cfid.fid);
-	kfree(buf_to_free);
+	kfree(tcon->nativeFileSystem);
+	kfree_sensitive(tcon->password);
+	kfree(tcon);
 }
 
 struct smb_hdr *
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 09f01f70e020..9696184a09e3 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -524,8 +524,8 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 					       &cfid->file_all_info);
 		} else {
 			rc = SMB2_query_info(xid, tcon,
-					     cfid->fid->persistent_fid,
-					     cfid->fid->volatile_fid, smb2_data);
+					     cfid->fid.persistent_fid,
+					     cfid->fid.volatile_fid, smb2_data);
 			if (!rc)
 				move_smb2_info_to_cifs(data, smb2_data);
 		}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 01aafedc477e..e50b6ef309e1 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -722,7 +722,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
 
 	rc = open_cached_dir(xid, tcon, "", cifs_sb, &cfid);
 	if (rc == 0)
-		memcpy(&fid, cfid->fid, sizeof(struct cifs_fid));
+		memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid));
 	else
 		rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL,
 			       NULL, NULL);
@@ -784,7 +784,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 	struct cifs_open_parms oparms;
 	struct cifs_fid fid;
 
-	if ((*full_path == 0) && tcon->cfid.is_valid)
+	if ((*full_path == 0) && tcon->cfid->is_valid)
 		return 0;
 
 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
@@ -2458,8 +2458,8 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 	if (cfid) {
 		rc = SMB2_query_info_init(tcon, server,
 					  &rqst[1],
-					  cfid->fid->persistent_fid,
-					  cfid->fid->volatile_fid,
+					  cfid->fid.persistent_fid,
+					  cfid->fid.volatile_fid,
 					  class, type, 0,
 					  output_len, 0,
 					  NULL);
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 4/9] cifs: Make tcon contain a wrapper structure cached_fids instead of cached_fid
  2022-08-09  2:11 WIP: Expand directory handle cache to also cache non-root directories Ronnie Sahlberg
                   ` (2 preceding siblings ...)
  2022-08-09  2:11 ` [PATCH 3/9] cifs: Add constructor/destructors for tcon->cfid Ronnie Sahlberg
@ 2022-08-09  2:11 ` Ronnie Sahlberg
  2022-08-11 13:16   ` Paulo Alcantara
  2022-08-09  2:11 ` [PATCH 5/9] cifs: Do not access tcon->cfids->cfid directly from is_path_accessible Ronnie Sahlberg
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 27+ messages in thread
From: Ronnie Sahlberg @ 2022-08-09  2:11 UTC (permalink / raw)
  To: linux-cifs; +Cc: Steve French, Ronnie Sahlberg

This wrapper structure will later be expanded to contain a list of
fids that are cached and not just the root fid.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/cached_dir.c | 50 ++++++++++++++++++++++++--------------------
 fs/cifs/cached_dir.h |  8 +++++--
 fs/cifs/cifsglob.h   |  2 +-
 fs/cifs/misc.c       |  6 +++---
 fs/cifs/smb2ops.c    |  2 +-
 5 files changed, 38 insertions(+), 30 deletions(-)

diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
index 7ca085299890..604ac444ad25 100644
--- a/fs/cifs/cached_dir.c
+++ b/fs/cifs/cached_dir.c
@@ -52,7 +52,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 
 	dentry = cifs_sb->root;
 
-	cfid = tcon->cfid;
+	cfid = &tcon->cfids->cfid;
 	mutex_lock(&cfid->fid_mutex);
 	if (cfid->is_valid) {
 		cifs_dbg(FYI, "found a cached root file handle\n");
@@ -224,7 +224,7 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
 {
 	struct cached_fid *cfid;
 
-	cfid = tcon->cfid;
+	cfid = &tcon->cfids->cfid;
 
 	mutex_lock(&cfid->fid_mutex);
 	if (cfid->dentry == dentry) {
@@ -318,7 +318,7 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
 		tcon = tlink_tcon(tlink);
 		if (IS_ERR(tcon))
 			continue;
-		cfid = tcon->cfid;
+		cfid = &tcon->cfids->cfid;
 		mutex_lock(&cfid->fid_mutex);
 		if (cfid->dentry) {
 			dput(cfid->dentry);
@@ -334,12 +334,14 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
  */
 void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
 {
-	mutex_lock(&tcon->cfid->fid_mutex);
-	tcon->cfid->is_valid = false;
+	struct cached_fid *cfid = &tcon->cfids->cfid;
+
+	mutex_lock(&cfid->fid_mutex);
+	cfid->is_valid = false;
 	/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
-	close_cached_dir_lease_locked(tcon->cfid);
-	memset(&tcon->cfid->fid, 0, sizeof(struct cifs_fid));
-	mutex_unlock(&tcon->cfid->fid_mutex);
+	close_cached_dir_lease_locked(cfid);
+	memset(&cfid->fid, 0, sizeof(struct cifs_fid));
+	mutex_unlock(&cfid->fid_mutex);
 }
 
 static void
@@ -353,35 +355,37 @@ smb2_cached_lease_break(struct work_struct *work)
 
 int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
 {
-	if (tcon->cfid->is_valid &&
+	struct cached_fid *cfid = &tcon->cfids->cfid;
+
+	if (cfid->is_valid &&
 	    !memcmp(lease_key,
-		    tcon->cfid->fid.lease_key,
+		    cfid->fid.lease_key,
 		    SMB2_LEASE_KEY_SIZE)) {
-		tcon->cfid->time = 0;
-		INIT_WORK(&tcon->cfid->lease_break,
+		cfid->time = 0;
+		INIT_WORK(&cfid->lease_break,
 			  smb2_cached_lease_break);
 		queue_work(cifsiod_wq,
-			   &tcon->cfid->lease_break);
+			   &cfid->lease_break);
 		spin_unlock(&cifs_tcp_ses_lock);
 		return true;
 	}
 	return false;
 }
 
-struct cached_fid *init_cached_dir(void)
+struct cached_fids *init_cached_dirs(void)
 {
-	struct cached_fid *cfid;
+	struct cached_fids *cfids;
 
-	cfid = kzalloc(sizeof(*cfid), GFP_KERNEL);
-	if (!cfid)
+	cfids = kzalloc(sizeof(*cfids), GFP_KERNEL);
+	if (!cfids)
 		return NULL;
-	INIT_LIST_HEAD(&cfid->dirents.entries);
-	mutex_init(&cfid->dirents.de_mutex);
-	mutex_init(&cfid->fid_mutex);
-	return cfid;
+	INIT_LIST_HEAD(&cfids->cfid.dirents.entries);
+	mutex_init(&cfids->cfid.dirents.de_mutex);
+	mutex_init(&cfids->cfid.fid_mutex);
+	return cfids;
 }
 
-void free_cached_dir(struct cifs_tcon *tcon)
+void free_cached_dirs(struct cached_fids *cfids)
 {
-	kfree(tcon->cfid);
+	kfree(cfids);
 }
diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
index b44edfbd8e1a..4ea30a5ba4e7 100644
--- a/fs/cifs/cached_dir.h
+++ b/fs/cifs/cached_dir.h
@@ -45,8 +45,12 @@ struct cached_fid {
 	struct cached_dirents dirents;
 };
 
-extern struct cached_fid *init_cached_dir(void);
-extern void free_cached_dir(struct cifs_tcon *tcon);
+struct cached_fids {
+	struct cached_fid cfid;
+};
+
+extern struct cached_fids *init_cached_dirs(void);
+extern void free_cached_dirs(struct cached_fids *cfids);
 extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 			   const char *path,
 			   struct cifs_sb_info *cifs_sb,
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index d3b233a2737d..edee34011ffc 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1217,7 +1217,7 @@ struct cifs_tcon {
 	struct fscache_volume *fscache;	/* cookie for share */
 #endif
 	struct list_head pending_opens;	/* list of incomplete opens */
-	struct cached_fid *cfid; /* Cached root fid */
+	struct cached_fids *cfids; /* Cached root fid */
 	/* BB add field for back pointer to sb struct(s)? */
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	struct list_head ulist; /* cache update list */
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 4e2e3b557591..d193c6242366 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -116,8 +116,8 @@ tconInfoAlloc(void)
 	ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
 	if (!ret_buf)
 		return NULL;
-	ret_buf->cfid = init_cached_dir();
-	if (!ret_buf->cfid) {
+	ret_buf->cfids = init_cached_dirs();
+	if (!ret_buf->cfids) {
 		kfree(ret_buf);
 		return NULL;
 	}
@@ -142,7 +142,7 @@ tconInfoFree(struct cifs_tcon *tcon)
 		cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n");
 		return;
 	}
-	free_cached_dir(tcon);
+	free_cached_dirs(tcon->cfids);
 	atomic_dec(&tconInfoAllocCount);
 	kfree(tcon->nativeFileSystem);
 	kfree_sensitive(tcon->password);
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index e50b6ef309e1..780ada4d8f6e 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -784,7 +784,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 	struct cifs_open_parms oparms;
 	struct cifs_fid fid;
 
-	if ((*full_path == 0) && tcon->cfid->is_valid)
+	if ((*full_path == 0) && tcon->cfids->cfid.is_valid)
 		return 0;
 
 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 5/9] cifs: Do not access tcon->cfids->cfid directly from is_path_accessible
  2022-08-09  2:11 WIP: Expand directory handle cache to also cache non-root directories Ronnie Sahlberg
                   ` (3 preceding siblings ...)
  2022-08-09  2:11 ` [PATCH 4/9] cifs: Make tcon contain a wrapper structure cached_fids instead of cached_fid Ronnie Sahlberg
@ 2022-08-09  2:11 ` Ronnie Sahlberg
  2022-08-11 13:20   ` Paulo Alcantara
  2022-08-09  2:11 ` [PATCH 6/9] cifs: cifs: handlecache, only track the dentry for the root handle Ronnie Sahlberg
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 27+ messages in thread
From: Ronnie Sahlberg @ 2022-08-09  2:11 UTC (permalink / raw)
  To: linux-cifs; +Cc: Steve French, Ronnie Sahlberg

cfids will soon keep a list of cached fids so we should not access this
directly from outside of cached_dir.c

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/cached_dir.c | 10 ++++++----
 fs/cifs/cached_dir.h |  2 +-
 fs/cifs/readdir.c    |  4 ++--
 fs/cifs/smb2inode.c  |  2 +-
 fs/cifs/smb2ops.c    | 18 ++++++++++++++----
 5 files changed, 24 insertions(+), 12 deletions(-)

diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
index 604ac444ad25..1fb80b23bbeb 100644
--- a/fs/cifs/cached_dir.c
+++ b/fs/cifs/cached_dir.c
@@ -16,9 +16,9 @@
  * If error then *cfid is not initialized.
  */
 int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
-		const char *path,
-		struct cifs_sb_info *cifs_sb,
-		struct cached_fid **ret_cfid)
+		    const char *path,
+		    struct cifs_sb_info *cifs_sb,
+		    bool lookup_only, struct cached_fid **ret_cfid)
 {
 	struct cifs_ses *ses;
 	struct TCP_Server_Info *server;
@@ -68,9 +68,11 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	 * cifs_mark_open_files_invalid() which takes the lock again
 	 * thus causing a deadlock
 	 */
-
 	mutex_unlock(&cfid->fid_mutex);
 
+	if (lookup_only)
+		return -ENOENT;
+
 	if (smb3_encryption_required(tcon))
 		flags |= CIFS_TRANSFORM_REQ;
 
diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
index 4ea30a5ba4e7..5a384fad2432 100644
--- a/fs/cifs/cached_dir.h
+++ b/fs/cifs/cached_dir.h
@@ -54,7 +54,7 @@ extern void free_cached_dirs(struct cached_fids *cfids);
 extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 			   const char *path,
 			   struct cifs_sb_info *cifs_sb,
-			   struct cached_fid **cfid);
+			   bool lookup_only, struct cached_fid **cfid);
 extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
 				     struct dentry *dentry,
 				     struct cached_fid **cfid);
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index a06072ae6c7e..2eece8a07c11 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -1072,7 +1072,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
 		tcon = tlink_tcon(cifsFile->tlink);
 	}
 
-	rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
+	rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
 	cifs_put_tlink(tlink);
 	if (rc)
 		goto cache_not_found;
@@ -1143,7 +1143,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
 	tcon = tlink_tcon(cifsFile->tlink);
 	rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path,
 			     &current_entry, &num_to_fill);
-	open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
+	open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
 	if (rc) {
 		cifs_dbg(FYI, "fce error %d\n", rc);
 		goto rddir2_exit;
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 9696184a09e3..b83f59051b26 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -516,7 +516,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 	if (strcmp(full_path, ""))
 		rc = -ENOENT;
 	else
-		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
+		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
 	/* If it is a root and its handle is cached then use it */
 	if (!rc) {
 		if (cfid->file_all_info_is_valid) {
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 780ada4d8f6e..4727ee537f11 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -720,7 +720,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
 	oparms.fid = &fid;
 	oparms.reconnect = false;
 
-	rc = open_cached_dir(xid, tcon, "", cifs_sb, &cfid);
+	rc = open_cached_dir(xid, tcon, "", cifs_sb, false, &cfid);
 	if (rc == 0)
 		memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid));
 	else
@@ -783,9 +783,16 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
 	struct cifs_open_parms oparms;
 	struct cifs_fid fid;
+	struct cached_fid *cfid;
 
-	if ((*full_path == 0) && tcon->cfids->cfid.is_valid)
-		return 0;
+	rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
+	if (!rc) {
+		if (cfid->is_valid) {
+			close_cached_dir(cfid);
+			return 0;
+		}
+		close_cached_dir(cfid);
+	}
 
 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
 	if (!utf16_path)
@@ -2431,8 +2438,11 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 	resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
 	memset(rsp_iov, 0, sizeof(rsp_iov));
 
+	/*
+	 * We can only call this for things we know are directories.
+	 */
 	if (!strcmp(path, ""))
-		open_cached_dir(xid, tcon, path, cifs_sb, &cfid); /* cfid null if open dir failed */
+		open_cached_dir(xid, tcon, path, cifs_sb, false, &cfid); /* cfid null if open dir failed */
 
 	memset(&open_iov, 0, sizeof(open_iov));
 	rqst[0].rq_iov = open_iov;
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 6/9] cifs: cifs: handlecache, only track the dentry for the root handle
  2022-08-09  2:11 WIP: Expand directory handle cache to also cache non-root directories Ronnie Sahlberg
                   ` (4 preceding siblings ...)
  2022-08-09  2:11 ` [PATCH 5/9] cifs: Do not access tcon->cfids->cfid directly from is_path_accessible Ronnie Sahlberg
@ 2022-08-09  2:11 ` Ronnie Sahlberg
  2022-08-11 13:34   ` Paulo Alcantara
  2022-08-09  2:11 ` [PATCH 7/9] cifs: store a pointer to a fid in the cfid structure instead of the struct Ronnie Sahlberg
                   ` (2 subsequent siblings)
  8 siblings, 1 reply; 27+ messages in thread
From: Ronnie Sahlberg @ 2022-08-09  2:11 UTC (permalink / raw)
  To: linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/cached_dir.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
index 1fb80b23bbeb..9423fee378f4 100644
--- a/fs/cifs/cached_dir.c
+++ b/fs/cifs/cached_dir.c
@@ -47,11 +47,12 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	if (cifs_sb->root == NULL)
 		return -ENOENT;
 
+	if (!strlen(path))
+		dentry = cifs_sb->root;
+
 	if (strlen(path))
 		return -ENOENT;
 
-	dentry = cifs_sb->root;
-
 	cfid = &tcon->cfids->cfid;
 	mutex_lock(&cfid->fid_mutex);
 	if (cfid->is_valid) {
@@ -177,7 +178,8 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	cfid->tcon = tcon;
 	cfid->is_valid = true;
 	cfid->dentry = dentry;
-	dget(dentry);
+	if (dentry)
+		dget(dentry);
 	kref_init(&cfid->refcount);
 
 	/* BB TBD check to see if oplock level check can be removed below */
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 7/9] cifs: store a pointer to a fid in the cfid structure instead of the struct
  2022-08-09  2:11 WIP: Expand directory handle cache to also cache non-root directories Ronnie Sahlberg
                   ` (5 preceding siblings ...)
  2022-08-09  2:11 ` [PATCH 6/9] cifs: cifs: handlecache, only track the dentry for the root handle Ronnie Sahlberg
@ 2022-08-09  2:11 ` Ronnie Sahlberg
  2022-08-11 13:37   ` Paulo Alcantara
  2022-08-09  2:11 ` [PATCH 8/9] cifs: don't unlock cifs_tcp_ses_lock in cached_dir_lease_break() Ronnie Sahlberg
  2022-08-09  2:11 ` [PATCH 9/9] cifs: start caching all directories we open and get a lease for Ronnie Sahlberg
  8 siblings, 1 reply; 27+ messages in thread
From: Ronnie Sahlberg @ 2022-08-09  2:11 UTC (permalink / raw)
  To: linux-cifs; +Cc: Steve French, Ronnie Sahlberg

also create a constructor that takes a path name and stores it in the fid.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/cached_dir.c | 63 ++++++++++++++++++++++++++++++++++++++------
 fs/cifs/cached_dir.h |  4 ++-
 2 files changed, 58 insertions(+), 9 deletions(-)

diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
index 9423fee378f4..5f2d01833f1e 100644
--- a/fs/cifs/cached_dir.c
+++ b/fs/cifs/cached_dir.c
@@ -11,6 +11,8 @@
 #include "smb2proto.h"
 #include "cached_dir.h"
 
+struct cached_fid *init_cached_dir(const char *path);
+
 /*
  * Open the and cache a directory handle.
  * If error then *cfid is not initialized.
@@ -53,7 +55,14 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	if (strlen(path))
 		return -ENOENT;
 
-	cfid = &tcon->cfids->cfid;
+	cfid = tcon->cfids->cfid;
+	if (cfid == NULL) {
+		cfid = init_cached_dir(path);
+		tcon->cfids->cfid = cfid;
+	}
+	if (cfid == NULL)
+		return -ENOMEM;
+
 	mutex_lock(&cfid->fid_mutex);
 	if (cfid->is_valid) {
 		cifs_dbg(FYI, "found a cached root file handle\n");
@@ -228,7 +237,9 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
 {
 	struct cached_fid *cfid;
 
-	cfid = &tcon->cfids->cfid;
+	cfid = tcon->cfids->cfid;
+	if (cfid == NULL)
+		return -ENOENT;
 
 	mutex_lock(&cfid->fid_mutex);
 	if (cfid->dentry == dentry) {
@@ -322,7 +333,9 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
 		tcon = tlink_tcon(tlink);
 		if (IS_ERR(tcon))
 			continue;
-		cfid = &tcon->cfids->cfid;
+		cfid = tcon->cfids->cfid;
+		if (cfid == NULL)
+			continue;
 		mutex_lock(&cfid->fid_mutex);
 		if (cfid->dentry) {
 			dput(cfid->dentry);
@@ -338,7 +351,10 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
  */
 void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
 {
-	struct cached_fid *cfid = &tcon->cfids->cfid;
+	struct cached_fid *cfid = tcon->cfids->cfid;
+
+	if (cfid == NULL)
+		return;
 
 	mutex_lock(&cfid->fid_mutex);
 	cfid->is_valid = false;
@@ -359,7 +375,10 @@ smb2_cached_lease_break(struct work_struct *work)
 
 int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
 {
-	struct cached_fid *cfid = &tcon->cfids->cfid;
+	struct cached_fid *cfid = tcon->cfids->cfid;
+
+	if (cfid == NULL)
+		return false;
 
 	if (cfid->is_valid &&
 	    !memcmp(lease_key,
@@ -376,6 +395,32 @@ int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
 	return false;
 }
 
+struct cached_fid *init_cached_dir(const char *path)
+{
+	struct cached_fid *cfid;
+
+	cfid = kzalloc(sizeof(*cfid), GFP_KERNEL);
+	if (!cfid)
+		return NULL;
+	cfid->path = kstrdup(path, GFP_KERNEL);
+	if (!cfid->path) {
+		kfree(cfid);
+		return NULL;
+	}
+
+	INIT_LIST_HEAD(&cfid->dirents.entries);
+	mutex_init(&cfid->dirents.de_mutex);
+	mutex_init(&cfid->fid_mutex);
+	return cfid;
+}
+
+void free_cached_dir(struct cached_fid *cfid)
+{
+	kfree(cfid->path);
+	cfid->path = NULL;
+	kfree(cfid);
+}
+
 struct cached_fids *init_cached_dirs(void)
 {
 	struct cached_fids *cfids;
@@ -383,13 +428,15 @@ struct cached_fids *init_cached_dirs(void)
 	cfids = kzalloc(sizeof(*cfids), GFP_KERNEL);
 	if (!cfids)
 		return NULL;
-	INIT_LIST_HEAD(&cfids->cfid.dirents.entries);
-	mutex_init(&cfids->cfid.dirents.de_mutex);
-	mutex_init(&cfids->cfid.fid_mutex);
+	mutex_init(&cfids->cfid_list_mutex);
 	return cfids;
 }
 
 void free_cached_dirs(struct cached_fids *cfids)
 {
+	if (cfids->cfid) {
+		free_cached_dir(cfids->cfid);
+		cfids->cfid = NULL;
+	}
 	kfree(cfids);
 }
diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
index 5a384fad2432..d34ff3e31488 100644
--- a/fs/cifs/cached_dir.h
+++ b/fs/cifs/cached_dir.h
@@ -31,6 +31,7 @@ struct cached_dirents {
 };
 
 struct cached_fid {
+	const char *path;
 	bool is_valid:1;	/* Do we have a useable root fid */
 	bool file_all_info_is_valid:1;
 	bool has_lease:1;
@@ -46,7 +47,8 @@ struct cached_fid {
 };
 
 struct cached_fids {
-	struct cached_fid cfid;
+	struct mutex cfid_list_mutex;
+	struct cached_fid *cfid;
 };
 
 extern struct cached_fids *init_cached_dirs(void);
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 8/9] cifs: don't unlock cifs_tcp_ses_lock in cached_dir_lease_break()
  2022-08-09  2:11 WIP: Expand directory handle cache to also cache non-root directories Ronnie Sahlberg
                   ` (6 preceding siblings ...)
  2022-08-09  2:11 ` [PATCH 7/9] cifs: store a pointer to a fid in the cfid structure instead of the struct Ronnie Sahlberg
@ 2022-08-09  2:11 ` Ronnie Sahlberg
  2022-08-11 13:38   ` Paulo Alcantara
  2022-08-09  2:11 ` [PATCH 9/9] cifs: start caching all directories we open and get a lease for Ronnie Sahlberg
  8 siblings, 1 reply; 27+ messages in thread
From: Ronnie Sahlberg @ 2022-08-09  2:11 UTC (permalink / raw)
  To: linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Unlock it from the caller, which is also where the lock is taken.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/cached_dir.c | 1 -
 fs/cifs/smb2misc.c   | 4 +++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
index 5f2d01833f1e..9d221f0bf976 100644
--- a/fs/cifs/cached_dir.c
+++ b/fs/cifs/cached_dir.c
@@ -389,7 +389,6 @@ int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
 			  smb2_cached_lease_break);
 		queue_work(cifsiod_wq,
 			   &cfid->lease_break);
-		spin_unlock(&cifs_tcp_ses_lock);
 		return true;
 	}
 	return false;
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index d3d9174ddd7c..2cc038aca5bc 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -640,8 +640,10 @@ smb2_is_valid_lease_break(char *buffer)
 				}
 				spin_unlock(&tcon->open_file_lock);
 
-				if (cached_dir_lease_break(tcon, rsp->LeaseKey))
+				if (cached_dir_lease_break(tcon, rsp->LeaseKey)) {
+					spin_unlock(&cifs_tcp_ses_lock);
 					return true;
+				}
 			}
 		}
 	}
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 9/9] cifs: start caching all directories we open and get a lease for
  2022-08-09  2:11 WIP: Expand directory handle cache to also cache non-root directories Ronnie Sahlberg
                   ` (7 preceding siblings ...)
  2022-08-09  2:11 ` [PATCH 8/9] cifs: don't unlock cifs_tcp_ses_lock in cached_dir_lease_break() Ronnie Sahlberg
@ 2022-08-09  2:11 ` Ronnie Sahlberg
  8 siblings, 0 replies; 27+ messages in thread
From: Ronnie Sahlberg @ 2022-08-09  2:11 UTC (permalink / raw)
  To: linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/cached_dir.c | 396 ++++++++++++++++++++++++-------------------
 fs/cifs/cached_dir.h |  18 +-
 fs/cifs/inode.c      |   7 +-
 fs/cifs/smb2ops.c    |   2 +-
 4 files changed, 240 insertions(+), 183 deletions(-)

diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
index 9d221f0bf976..4ae9c897eeae 100644
--- a/fs/cifs/cached_dir.c
+++ b/fs/cifs/cached_dir.c
@@ -11,7 +11,55 @@
 #include "smb2proto.h"
 #include "cached_dir.h"
 
-struct cached_fid *init_cached_dir(const char *path);
+static struct cached_fid *init_cached_dir(const char *path);
+static void smb2_close_cached_fid(struct kref *ref);
+static void smb2_drop_cached_fid_locked(struct kref *ref);
+static void free_cached_dir(struct cached_fid *cfid);
+
+static struct cached_fid *find_or_create_cached_dir(struct cached_fids *cfids,
+						    const char *path,
+						    bool lookup_only)
+{
+	struct cached_fid *cfid;
+
+	spin_lock(&cfids->cfid_list_lock);
+	list_for_each_entry(cfid, &cfids->entries, entry) {
+		if (!strcmp(cfid->path, path)) {
+			/*
+			 * If it doesn't have a lease it is either not yet
+			 * fully cached or it may be in the process of
+			 * being deleted due to a lease break.
+			 */
+			if (!cfid->has_lease) {
+				spin_unlock(&cfids->cfid_list_lock);
+				return NULL;
+			}
+			kref_get(&cfid->refcount);
+			spin_unlock(&cfids->cfid_list_lock);
+			return cfid;
+		}
+	}
+	if (lookup_only) {
+		spin_unlock(&cfids->cfid_list_lock);
+		return NULL;
+	}
+	if (cfids->num_entries >= MAX_CACHED_FIDS) {
+		spin_unlock(&cfids->cfid_list_lock);
+		return NULL;
+	}
+	cfid = init_cached_dir(path);
+	if (cfid == NULL) {
+		spin_unlock(&cfids->cfid_list_lock);
+		return NULL;
+	}
+	cfid->cfids = cfids;
+	cfids->num_entries++;
+	list_add(&cfid->entry, &cfids->entries);
+	/* should we do this get in open_cached_dir() instead ? */
+	kref_get(&cfid->refcount);
+	spin_unlock(&cfids->cfid_list_lock);
+	return cfid;
+}
 
 /*
  * Open the and cache a directory handle.
@@ -33,42 +81,45 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
 	struct kvec qi_iov[1];
 	int rc, flags = 0;
-	__le16 utf16_path = 0; /* Null - since an open of top of share */
+	__le16 *utf16_path = NULL;
 	u8 oplock = SMB2_OPLOCK_LEVEL_II;
 	struct cifs_fid *pfid;
-	struct dentry *dentry;
+	struct dentry *dentry = NULL;
 	struct cached_fid *cfid;
+	struct cached_fids *cfids;
 	  
-	if (tcon == NULL || tcon->nohandlecache ||
+
+	if (tcon == NULL || tcon->cfids == NULL || tcon->nohandlecache ||
 	    is_smb1_server(tcon->ses->server))
 		return -ENOTSUPP;
 
 	ses = tcon->ses;
 	server = ses->server;
+	cfids = tcon->cfids;
 
 	if (cifs_sb->root == NULL)
 		return -ENOENT;
 
+	/*
+	 * TODO: for better caching we need to find and use the dentry also
+	 * for non-root directories.
+	 */
 	if (!strlen(path))
 		dentry = cifs_sb->root;
 
-	if (strlen(path))
-		return -ENOENT;
+	utf16_path = cifs_convert_path_to_utf16(path, cifs_sb);
+	if (!utf16_path)
+		return -ENOMEM;
 
-	cfid = tcon->cfids->cfid;
+	cfid = find_or_create_cached_dir(cfids, path, lookup_only);
 	if (cfid == NULL) {
-		cfid = init_cached_dir(path);
-		tcon->cfids->cfid = cfid;
+		kfree(utf16_path);
+		return -ENOENT;
 	}
-	if (cfid == NULL)
-		return -ENOMEM;
-
-	mutex_lock(&cfid->fid_mutex);
-	if (cfid->is_valid) {
+	if (cfid->has_lease) {
 		cifs_dbg(FYI, "found a cached root file handle\n");
 		*ret_cfid = cfid;
-		kref_get(&cfid->refcount);
-		mutex_unlock(&cfid->fid_mutex);
+		kfree(utf16_path);
 		return 0;
 	}
 
@@ -78,17 +129,13 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	 * cifs_mark_open_files_invalid() which takes the lock again
 	 * thus causing a deadlock
 	 */
-	mutex_unlock(&cfid->fid_mutex);
-
-	if (lookup_only)
-		return -ENOENT;
-
 	if (smb3_encryption_required(tcon))
 		flags |= CIFS_TRANSFORM_REQ;
 
-	if (!server->ops->new_lease_key)
+	if (!server->ops->new_lease_key) {
+		kfree(utf16_path);
 		return -EIO;
-
+	}
 	pfid = &cfid->fid;
 	server->ops->new_lease_key(pfid);
 
@@ -109,7 +156,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	oparms.reconnect = false;
 
 	rc = SMB2_open_init(tcon, server,
-			    &rqst[0], &oplock, &oparms, &utf16_path);
+			    &rqst[0], &oplock, &oparms, utf16_path);
 	if (rc)
 		goto oshr_free;
 	smb2_set_next_command(tcon, &rqst[0]);
@@ -132,47 +179,13 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	rc = compound_send_recv(xid, ses, server,
 				flags, 2, rqst,
 				resp_buftype, rsp_iov);
-	mutex_lock(&cfid->fid_mutex);
-
-	/*
-	 * Now we need to check again as the cached root might have
-	 * been successfully re-opened from a concurrent process
-	 */
-
-	if (cfid->is_valid) {
-		/* work was already done */
-
-		/* stash fids for close() later */
-		struct cifs_fid fid = {
-			.persistent_fid = pfid->persistent_fid,
-			.volatile_fid = pfid->volatile_fid,
-		};
-
-		/*
-		 * caller expects this func to set the fid in cfid to valid
-		 * cached root, so increment the refcount.
-		 */
-		kref_get(&cfid->refcount);
-
-		mutex_unlock(&cfid->fid_mutex);
-
-		if (rc == 0) {
-			/* close extra handle outside of crit sec */
-			SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
-		}
-		rc = 0;
-		goto oshr_free;
-	}
-
-	/* Cached root is still invalid, continue normaly */
-
 	if (rc) {
 		if (rc == -EREMCHG) {
 			tcon->need_reconnect = true;
 			pr_warn_once("server share %s deleted\n",
 				     tcon->treeName);
 		}
-		goto oshr_exit;
+		goto oshr_free;
 	}
 
 	atomic_inc(&tcon->num_remote_opens);
@@ -185,30 +198,23 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 #endif /* CIFS_DEBUG2 */
 
 	cfid->tcon = tcon;
-	cfid->is_valid = true;
-	cfid->dentry = dentry;
-	if (dentry)
+	if (dentry) {
+		cfid->dentry = dentry;
 		dget(dentry);
-	kref_init(&cfid->refcount);
-
+	}
 	/* BB TBD check to see if oplock level check can be removed below */
-	if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
-		/*
-		 * See commit 2f94a3125b87. Increment the refcount when we
-		 * get a lease for root, release it if lease break occurs
-		 */
-		kref_get(&cfid->refcount);
-		cfid->has_lease = true;
-		smb2_parse_contexts(server, o_rsp,
-				&oparms.fid->epoch,
-				    oparms.fid->lease_key, &oplock,
-				    NULL, NULL);
-	} else
-		goto oshr_exit;
+	if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) {
+		goto oshr_free;
+	}
 
+	smb2_parse_contexts(server, o_rsp,
+			    &oparms.fid->epoch,
+			    oparms.fid->lease_key, &oplock,
+			    NULL, NULL);
+	
 	qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
 	if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
-		goto oshr_exit;
+		goto oshr_free;
 	if (!smb2_validate_and_copy_iov(
 				le16_to_cpu(qi_rsp->OutputBufferOffset),
 				sizeof(struct smb2_file_all_info),
@@ -216,15 +222,23 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 				(char *)&cfid->file_all_info))
 		cfid->file_all_info_is_valid = true;
 	cfid->time = jiffies;
+	cfid->has_lease = true;
 
 
-oshr_exit:
-	mutex_unlock(&cfid->fid_mutex);
 oshr_free:
+	kfree(utf16_path);
 	SMB2_open_free(&rqst[0]);
 	SMB2_query_info_free(&rqst[1]);
 	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
 	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
+	if (!cfid->has_lease) {
+		spin_lock(&cfids->cfid_list_lock);
+		cfids->num_entries--;
+		list_del_init(&cfid->entry);
+		spin_unlock(&cfids->cfid_list_lock);
+		free_cached_dir(cfid);
+		rc = -ENOENT;
+	}
 	if (rc == 0) {
 		*ret_cfid = cfid;	
 	}
@@ -236,85 +250,90 @@ int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
 			      struct cached_fid **ret_cfid)
 {
 	struct cached_fid *cfid;
+	struct cached_fids *cfids = tcon->cfids;
 
-	cfid = tcon->cfids->cfid;
-	if (cfid == NULL)
+	if (cfids == NULL)
 		return -ENOENT;
 
-	mutex_lock(&cfid->fid_mutex);
-	if (cfid->dentry == dentry) {
-		cifs_dbg(FYI, "found a cached root file handle by dentry\n");
-		*ret_cfid = cfid;
-		kref_get(&cfid->refcount);
-		mutex_unlock(&cfid->fid_mutex);
-		return 0;
+	spin_lock(&cfids->cfid_list_lock);
+	list_for_each_entry(cfid, &cfids->entries, entry) {
+		if (dentry && cfid->dentry == dentry) {
+			cifs_dbg(FYI, "found a cached root file handle by dentry\n");
+			kref_get(&cfid->refcount);
+			*ret_cfid = cfid;	
+			spin_unlock(&cfids->cfid_list_lock);
+			return 0;
+		}
 	}
-	mutex_unlock(&cfid->fid_mutex);
+	spin_unlock(&cfids->cfid_list_lock);
 	return -ENOENT;
 }
 
 static void
-smb2_close_cached_fid(struct kref *ref)
+smb2_close_cached_fid_internal(struct kref *ref, bool locked)
 {
 	struct cached_fid *cfid = container_of(ref, struct cached_fid,
 					       refcount);
-	struct cached_dirent *dirent, *q;
 
-	if (cfid->is_valid) {
+	if (!locked)
+		spin_lock(&cfid->cfids->cfid_list_lock);
+	cfid->cfids->num_entries--;
+	list_del_init(&cfid->entry);
+	if (cfid->dentry)
+		dput(cfid->dentry);
+	if (!locked)
+		spin_unlock(&cfid->cfids->cfid_list_lock);
+
+	if (cfid->has_lease) {
 		cifs_dbg(FYI, "clear cached root file handle\n");
 		SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
 			   cfid->fid.volatile_fid);
 	}
 
+	free_cached_dir(cfid);
+}
+
+static void
+smb2_close_cached_fid(struct kref *ref)
+{
+	smb2_close_cached_fid_internal(ref, false);
+}
+
+static void
+smb2_drop_cached_fid_locked(struct kref *ref)
+{
+	struct cached_fid *cfid = container_of(ref, struct cached_fid,
+					       refcount);
 	/*
-	 * We only check validity above to send SMB2_close,
-	 * but we still need to invalidate these entries
-	 * when this function is called
+	 * If the caller already holds cfid_list_lock we must force have_lease
+	 * to be false in order to avoid calling the blocking function
+	 * SMB2_close(). This could leave the file open on the server so we
+	 * must only call smb2_drop_cached_fid_locked IFF
+	 * - we know all handles are closed becase we are about to disconnect
+	 *   the share during umount.
+	 * - we have lost the session so all open files are implicitely closed
+	 *   anyway.
 	 */
-	cfid->is_valid = false;
-	cfid->file_all_info_is_valid = false;
 	cfid->has_lease = false;
-	if (cfid->dentry) {
-		dput(cfid->dentry);
-		cfid->dentry = NULL;
-	}
-	/*
-	 * Delete all cached dirent names
-	 */
-	mutex_lock(&cfid->dirents.de_mutex);
-	list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
-		list_del(&dirent->entry);
-		kfree(dirent->name);
-		kfree(dirent);
-	}
-	cfid->dirents.is_valid = 0;
-	cfid->dirents.is_failed = 0;
-	cfid->dirents.ctx = NULL;
-	cfid->dirents.pos = 0;
-	mutex_unlock(&cfid->dirents.de_mutex);
-
+	smb2_close_cached_fid_internal(ref, true);
 }
 
 void close_cached_dir(struct cached_fid *cfid)
 {
-	mutex_lock(&cfid->fid_mutex);
 	kref_put(&cfid->refcount, smb2_close_cached_fid);
-	mutex_unlock(&cfid->fid_mutex);
-}
-
-void close_cached_dir_lease_locked(struct cached_fid *cfid)
-{
-	if (cfid->has_lease) {
-		cfid->has_lease = false;
-		kref_put(&cfid->refcount, smb2_close_cached_fid);
-	}
 }
 
-void close_cached_dir_lease(struct cached_fid *cfid)
+static void close_cached_dir_lease(struct cached_fid *cfid)
 {
-	mutex_lock(&cfid->fid_mutex);
-	close_cached_dir_lease_locked(cfid);
-	mutex_unlock(&cfid->fid_mutex);
+	/*
+	 * TODO: If this is not the last reference then we
+	 * we must send a lease break ack and flag this dir as not
+	 * being cached.
+	 * This is to handle the situation where an app holds opendir()
+	 * open for a long time during which we get a lease break from the
+	 * server.
+	 */
+	kref_put(&cfid->refcount, smb2_close_cached_fid);
 }
 
 /*
@@ -327,41 +346,41 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
 	struct cached_fid *cfid;
 	struct cifs_tcon *tcon;
 	struct tcon_link *tlink;
+	struct cached_fids *cfids;
 
 	for (node = rb_first(root); node; node = rb_next(node)) {
 		tlink = rb_entry(node, struct tcon_link, tl_rbnode);
 		tcon = tlink_tcon(tlink);
 		if (IS_ERR(tcon))
 			continue;
-		cfid = tcon->cfids->cfid;
-		if (cfid == NULL)
+		cfids = tcon->cfids;
+		if (cfids == NULL)
 			continue;
-		mutex_lock(&cfid->fid_mutex);
-		if (cfid->dentry) {
-			dput(cfid->dentry);
-			cfid->dentry = NULL;
+		list_for_each_entry(cfid, &cfids->entries, entry) {
+			if (cfid->dentry) {
+				dput(cfid->dentry);
+				cfid->dentry = NULL;
+			}
 		}
-		mutex_unlock(&cfid->fid_mutex);
 	}
 }
 
 /*
- * Invalidate and close all cached dirs when a TCON has been reset
+ * Invalidate all cached dirs when a TCON has been reset
  * due to a session loss.
  */
 void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
 {
-	struct cached_fid *cfid = tcon->cfids->cfid;
-
-	if (cfid == NULL)
-		return;
-
-	mutex_lock(&cfid->fid_mutex);
-	cfid->is_valid = false;
-	/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
-	close_cached_dir_lease_locked(cfid);
-	memset(&cfid->fid, 0, sizeof(struct cifs_fid));
-	mutex_unlock(&cfid->fid_mutex);
+	struct cached_fids *cfids = tcon->cfids;
+	struct cached_fid *cfid, *q;
+
+	spin_lock(&cfids->cfid_list_lock);
+	list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
+		cfids->num_entries--;
+		list_del_init(&cfid->entry);
+		kref_put(&cfid->refcount, smb2_drop_cached_fid_locked);
+	}
+	spin_unlock(&cfids->cfid_list_lock);
 }
 
 static void
@@ -375,33 +394,39 @@ smb2_cached_lease_break(struct work_struct *work)
 
 int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
 {
-	struct cached_fid *cfid = tcon->cfids->cfid;
+	struct cached_fids *cfids = tcon->cfids;
+	struct cached_fid *cfid;
 
-	if (cfid == NULL)
+	if (cfids == NULL)
 		return false;
 
-	if (cfid->is_valid &&
-	    !memcmp(lease_key,
-		    cfid->fid.lease_key,
-		    SMB2_LEASE_KEY_SIZE)) {
-		cfid->time = 0;
-		INIT_WORK(&cfid->lease_break,
-			  smb2_cached_lease_break);
-		queue_work(cifsiod_wq,
-			   &cfid->lease_break);
-		return true;
+	spin_lock(&cfids->cfid_list_lock);
+	list_for_each_entry(cfid, &cfids->entries, entry) {
+		if (cfid->has_lease &&
+		    !memcmp(lease_key,
+			    cfid->fid.lease_key,
+			    SMB2_LEASE_KEY_SIZE)) {
+			cfid->time = 0;
+			INIT_WORK(&cfid->lease_break,
+				  smb2_cached_lease_break);
+			queue_work(cifsiod_wq,
+				   &cfid->lease_break);
+			spin_unlock(&cfids->cfid_list_lock);
+			return true;
+		}
 	}
+	spin_unlock(&cfids->cfid_list_lock);
 	return false;
 }
 
-struct cached_fid *init_cached_dir(const char *path)
+static struct cached_fid *init_cached_dir(const char *path)
 {
 	struct cached_fid *cfid;
 
-	cfid = kzalloc(sizeof(*cfid), GFP_KERNEL);
+	cfid = kzalloc(sizeof(*cfid), GFP_ATOMIC);
 	if (!cfid)
 		return NULL;
-	cfid->path = kstrdup(path, GFP_KERNEL);
+	cfid->path = kstrdup(path, GFP_ATOMIC);
 	if (!cfid->path) {
 		kfree(cfid);
 		return NULL;
@@ -409,12 +434,29 @@ struct cached_fid *init_cached_dir(const char *path)
 
 	INIT_LIST_HEAD(&cfid->dirents.entries);
 	mutex_init(&cfid->dirents.de_mutex);
-	mutex_init(&cfid->fid_mutex);
+	spin_lock_init(&cfid->fid_lock);
+	kref_init(&cfid->refcount);
 	return cfid;
 }
 
-void free_cached_dir(struct cached_fid *cfid)
+static void free_cached_dir(struct cached_fid *cfid)
 {
+	struct cached_dirent *dirent, *q;
+
+	if (cfid->dentry) {
+		dput(cfid->dentry);
+		cfid->dentry = NULL;
+	}
+
+	/*
+	 * Delete all cached dirent names
+	 */
+	list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
+		list_del(&dirent->entry);
+		kfree(dirent->name);
+		kfree(dirent);
+	}
+
 	kfree(cfid->path);
 	cfid->path = NULL;
 	kfree(cfid);
@@ -427,15 +469,25 @@ struct cached_fids *init_cached_dirs(void)
 	cfids = kzalloc(sizeof(*cfids), GFP_KERNEL);
 	if (!cfids)
 		return NULL;
-	mutex_init(&cfids->cfid_list_mutex);
+	spin_lock_init(&cfids->cfid_list_lock);
+	INIT_LIST_HEAD(&cfids->entries);
 	return cfids;
 }
 
+/*
+ * Called from tconInfoFree when we are tearing down the tcon.
+ * There are no active users or open files/directories at this point.
+ */
 void free_cached_dirs(struct cached_fids *cfids)
 {
-	if (cfids->cfid) {
-		free_cached_dir(cfids->cfid);
-		cfids->cfid = NULL;
+	struct cached_fid *cfid, *q;
+
+	spin_lock(&cfids->cfid_list_lock);
+	list_for_each_entry_safe(cfid, q, &cfids->entries, entry) {
+		cfids->num_entries--;
+		list_del_init(&cfid->entry);
+		free_cached_dir(cfid);
 	}
-	kfree(cfids);
+	spin_unlock(&cfids->cfid_list_lock);
+ 	kfree(cfids);
 }
diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
index d34ff3e31488..c6df8804afb4 100644
--- a/fs/cifs/cached_dir.h
+++ b/fs/cifs/cached_dir.h
@@ -31,14 +31,15 @@ struct cached_dirents {
 };
 
 struct cached_fid {
+	struct list_head entry;
+	struct cached_fids *cfids;
 	const char *path;
-	bool is_valid:1;	/* Do we have a useable root fid */
-	bool file_all_info_is_valid:1;
 	bool has_lease:1;
+	bool file_all_info_is_valid:1;
 	unsigned long time; /* jiffies of when lease was taken */
 	struct kref refcount;
 	struct cifs_fid fid;
-	struct mutex fid_mutex;
+	spinlock_t fid_lock;
 	struct cifs_tcon *tcon;
 	struct dentry *dentry;
 	struct work_struct lease_break;
@@ -46,9 +47,14 @@ struct cached_fid {
 	struct cached_dirents dirents;
 };
 
+#define MAX_CACHED_FIDS 16
 struct cached_fids {
-	struct mutex cfid_list_mutex;
-	struct cached_fid *cfid;
+	/* Must be held when:
+	 * - accessing the cfids->entries list
+	 */
+	spinlock_t cfid_list_lock;
+	int num_entries;
+	struct list_head entries;
 };
 
 extern struct cached_fids *init_cached_dirs(void);
@@ -61,8 +67,6 @@ extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
 				     struct dentry *dentry,
 				     struct cached_fid **cfid);
 extern void close_cached_dir(struct cached_fid *cfid);
-extern void close_cached_dir_lease(struct cached_fid *cfid);
-extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
 extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
 extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
 extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index 7714f47d199b..7d17d20502ca 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -2225,15 +2225,16 @@ cifs_dentry_needs_reval(struct dentry *dentry)
 		return true;
 
 	if (!open_cached_dir_by_dentry(tcon, dentry->d_parent, &cfid)) {
-		mutex_lock(&cfid->fid_mutex);
+		spin_lock(&cfid->fid_lock);
 		if (cfid->time && cifs_i->time > cfid->time) {
-			mutex_unlock(&cfid->fid_mutex);
+			spin_unlock(&cfid->fid_lock);
 			close_cached_dir(cfid);
 			return false;
 		}
-		mutex_unlock(&cfid->fid_mutex);
+		spin_unlock(&cfid->fid_lock);
 		close_cached_dir(cfid);
 	}
+
 	/*
 	 * depending on inode type, check if attribute caching disabled for
 	 * files or directories
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 4727ee537f11..2204a19517cd 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -787,7 +787,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 
 	rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
 	if (!rc) {
-		if (cfid->is_valid) {
+		if (cfid->has_lease) {
 			close_cached_dir(cfid);
 			return 0;
 		}
-- 
2.35.3


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/9] cifs: Do not use tcon->cfid directly, use the cfid we get from open_cached_dir
  2022-08-09  2:11 ` [PATCH 2/9] cifs: Do not use tcon->cfid directly, use the cfid we get from open_cached_dir Ronnie Sahlberg
@ 2022-08-09  4:22   ` Steve French
  2022-08-11 13:03   ` Paulo Alcantara
  1 sibling, 0 replies; 27+ messages in thread
From: Steve French @ 2022-08-09  4:22 UTC (permalink / raw)
  To: Ronnie Sahlberg; +Cc: linux-cifs

This looks harmless if it makes future patches easier

On Mon, Aug 8, 2022 at 9:12 PM Ronnie Sahlberg <lsahlber@redhat.com> wrote:
>
> They are the same right now but tcon-> will later point to a different
> type of struct containing a list of cfids.
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/smb2inode.c | 4 ++--
>  fs/cifs/smb2pdu.c   | 2 +-
>  2 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
> index f6f9fc3f2e2d..09f01f70e020 100644
> --- a/fs/cifs/smb2inode.c
> +++ b/fs/cifs/smb2inode.c
> @@ -519,9 +519,9 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
>                 rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
>         /* If it is a root and its handle is cached then use it */
>         if (!rc) {
> -               if (tcon->cfid.file_all_info_is_valid) {
> +               if (cfid->file_all_info_is_valid) {
>                         move_smb2_info_to_cifs(data,
> -                                              &tcon->cfid.file_all_info);
> +                                              &cfid->file_all_info);
>                 } else {
>                         rc = SMB2_query_info(xid, tcon,
>                                              cfid->fid->persistent_fid,
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index 9ee1b6225619..5dbd2cac470c 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -1979,7 +1979,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
>         }
>         spin_unlock(&ses->chan_lock);
>
> -       close_cached_dir_lease(&tcon->cfid);
> +       invalidate_all_cached_dirs(tcon);
>
>         rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server,
>                                  (void **) &req,
> --
> 2.35.3
>


-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/9] cifs: Move cached-dir functions into a separate file
  2022-08-09  2:11 ` [PATCH 1/9] cifs: Move cached-dir functions into a separate file Ronnie Sahlberg
@ 2022-08-11  2:53   ` Steve French
  2022-08-11  3:08     ` Steve French
  2022-08-11 13:03   ` Paulo Alcantara
  1 sibling, 1 reply; 27+ messages in thread
From: Steve French @ 2022-08-11  2:53 UTC (permalink / raw)
  To: Ronnie Sahlberg; +Cc: linux-cifs

[-- Attachment #1: Type: text/plain, Size: 38353 bytes --]

Lightly updated (rebase to merge with current for-next) and also
combined with patch 8 of the series to avoid a lock warning.

Tentatively merged this restructuring into cifs-2.6.git for-next
pending testing.

On Mon, Aug 8, 2022 at 9:12 PM Ronnie Sahlberg <lsahlber@redhat.com> wrote:
>
> Also rename crfid to cfid to have consistent naming for this variable.
>
> This commit does not change any logic.
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/Makefile     |   2 +-
>  fs/cifs/cached_dir.c | 363 +++++++++++++++++++++++++++++++++++++++++++
>  fs/cifs/cached_dir.h |  26 ++++
>  fs/cifs/cifsfs.c     |  20 +--
>  fs/cifs/cifsglob.h   |   2 +-
>  fs/cifs/cifsproto.h  |   1 -
>  fs/cifs/cifssmb.c    |   8 +-
>  fs/cifs/inode.c      |   1 +
>  fs/cifs/misc.c       |  12 +-
>  fs/cifs/readdir.c    |   1 +
>  fs/cifs/smb2inode.c  |   5 +-
>  fs/cifs/smb2misc.c   |  13 +-
>  fs/cifs/smb2ops.c    | 297 +----------------------------------
>  fs/cifs/smb2pdu.c    |   3 +-
>  fs/cifs/smb2proto.h  |  10 --
>  15 files changed, 412 insertions(+), 352 deletions(-)
>  create mode 100644 fs/cifs/cached_dir.c
>  create mode 100644 fs/cifs/cached_dir.h
>
> diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
> index 8c9f2c00be72..343a59e0d64d 100644
> --- a/fs/cifs/Makefile
> +++ b/fs/cifs/Makefile
> @@ -7,7 +7,7 @@ obj-$(CONFIG_CIFS) += cifs.o
>
>  cifs-y := trace.o cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o \
>           inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \
> -         cifs_unicode.o nterr.o cifsencrypt.o \
> +         cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \
>           readdir.o ioctl.o sess.o export.o unc.o winucase.o \
>           smb2ops.o smb2maperror.o smb2transport.o \
>           smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \
> diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
> new file mode 100644
> index 000000000000..f2e17c1d5196
> --- /dev/null
> +++ b/fs/cifs/cached_dir.c
> @@ -0,0 +1,363 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Functions to handle the cached directory entries
> + *
> + *  Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
> + */
> +
> +#include "cifsglob.h"
> +#include "cifsproto.h"
> +#include "cifs_debug.h"
> +#include "smb2proto.h"
> +#include "cached_dir.h"
> +
> +/*
> + * Open the and cache a directory handle.
> + * If error then *cfid is not initialized.
> + */
> +int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
> +               const char *path,
> +               struct cifs_sb_info *cifs_sb,
> +               struct cached_fid **cfid)
> +{
> +       struct cifs_ses *ses;
> +       struct TCP_Server_Info *server;
> +       struct cifs_open_parms oparms;
> +       struct smb2_create_rsp *o_rsp = NULL;
> +       struct smb2_query_info_rsp *qi_rsp = NULL;
> +       int resp_buftype[2];
> +       struct smb_rqst rqst[2];
> +       struct kvec rsp_iov[2];
> +       struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
> +       struct kvec qi_iov[1];
> +       int rc, flags = 0;
> +       __le16 utf16_path = 0; /* Null - since an open of top of share */
> +       u8 oplock = SMB2_OPLOCK_LEVEL_II;
> +       struct cifs_fid *pfid;
> +       struct dentry *dentry;
> +
> +       if (tcon == NULL || tcon->nohandlecache ||
> +           is_smb1_server(tcon->ses->server))
> +               return -ENOTSUPP;
> +
> +       ses = tcon->ses;
> +       server = ses->server;
> +
> +       if (cifs_sb->root == NULL)
> +               return -ENOENT;
> +
> +       if (strlen(path))
> +               return -ENOENT;
> +
> +       dentry = cifs_sb->root;
> +
> +       mutex_lock(&tcon->cfid.fid_mutex);
> +       if (tcon->cfid.is_valid) {
> +               cifs_dbg(FYI, "found a cached root file handle\n");
> +               *cfid = &tcon->cfid;
> +               kref_get(&tcon->cfid.refcount);
> +               mutex_unlock(&tcon->cfid.fid_mutex);
> +               return 0;
> +       }
> +
> +       /*
> +        * We do not hold the lock for the open because in case
> +        * SMB2_open needs to reconnect, it will end up calling
> +        * cifs_mark_open_files_invalid() which takes the lock again
> +        * thus causing a deadlock
> +        */
> +
> +       mutex_unlock(&tcon->cfid.fid_mutex);
> +
> +       if (smb3_encryption_required(tcon))
> +               flags |= CIFS_TRANSFORM_REQ;
> +
> +       if (!server->ops->new_lease_key)
> +               return -EIO;
> +
> +       pfid = tcon->cfid.fid;
> +       server->ops->new_lease_key(pfid);
> +
> +       memset(rqst, 0, sizeof(rqst));
> +       resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
> +       memset(rsp_iov, 0, sizeof(rsp_iov));
> +
> +       /* Open */
> +       memset(&open_iov, 0, sizeof(open_iov));
> +       rqst[0].rq_iov = open_iov;
> +       rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
> +
> +       oparms.tcon = tcon;
> +       oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
> +       oparms.desired_access = FILE_READ_ATTRIBUTES;
> +       oparms.disposition = FILE_OPEN;
> +       oparms.fid = pfid;
> +       oparms.reconnect = false;
> +
> +       rc = SMB2_open_init(tcon, server,
> +                           &rqst[0], &oplock, &oparms, &utf16_path);
> +       if (rc)
> +               goto oshr_free;
> +       smb2_set_next_command(tcon, &rqst[0]);
> +
> +       memset(&qi_iov, 0, sizeof(qi_iov));
> +       rqst[1].rq_iov = qi_iov;
> +       rqst[1].rq_nvec = 1;
> +
> +       rc = SMB2_query_info_init(tcon, server,
> +                                 &rqst[1], COMPOUND_FID,
> +                                 COMPOUND_FID, FILE_ALL_INFORMATION,
> +                                 SMB2_O_INFO_FILE, 0,
> +                                 sizeof(struct smb2_file_all_info) +
> +                                 PATH_MAX * 2, 0, NULL);
> +       if (rc)
> +               goto oshr_free;
> +
> +       smb2_set_related(&rqst[1]);
> +
> +       rc = compound_send_recv(xid, ses, server,
> +                               flags, 2, rqst,
> +                               resp_buftype, rsp_iov);
> +       mutex_lock(&tcon->cfid.fid_mutex);
> +
> +       /*
> +        * Now we need to check again as the cached root might have
> +        * been successfully re-opened from a concurrent process
> +        */
> +
> +       if (tcon->cfid.is_valid) {
> +               /* work was already done */
> +
> +               /* stash fids for close() later */
> +               struct cifs_fid fid = {
> +                       .persistent_fid = pfid->persistent_fid,
> +                       .volatile_fid = pfid->volatile_fid,
> +               };
> +
> +               /*
> +                * caller expects this func to set the fid in cfid to valid
> +                * cached root, so increment the refcount.
> +                */
> +               kref_get(&tcon->cfid.refcount);
> +
> +               mutex_unlock(&tcon->cfid.fid_mutex);
> +
> +               if (rc == 0) {
> +                       /* close extra handle outside of crit sec */
> +                       SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
> +               }
> +               rc = 0;
> +               goto oshr_free;
> +       }
> +
> +       /* Cached root is still invalid, continue normaly */
> +
> +       if (rc) {
> +               if (rc == -EREMCHG) {
> +                       tcon->need_reconnect = true;
> +                       pr_warn_once("server share %s deleted\n",
> +                                    tcon->treeName);
> +               }
> +               goto oshr_exit;
> +       }
> +
> +       atomic_inc(&tcon->num_remote_opens);
> +
> +       o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
> +       oparms.fid->persistent_fid = o_rsp->PersistentFileId;
> +       oparms.fid->volatile_fid = o_rsp->VolatileFileId;
> +#ifdef CONFIG_CIFS_DEBUG2
> +       oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
> +#endif /* CIFS_DEBUG2 */
> +
> +       tcon->cfid.tcon = tcon;
> +       tcon->cfid.is_valid = true;
> +       tcon->cfid.dentry = dentry;
> +       dget(dentry);
> +       kref_init(&tcon->cfid.refcount);
> +
> +       /* BB TBD check to see if oplock level check can be removed below */
> +       if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
> +               /*
> +                * See commit 2f94a3125b87. Increment the refcount when we
> +                * get a lease for root, release it if lease break occurs
> +                */
> +               kref_get(&tcon->cfid.refcount);
> +               tcon->cfid.has_lease = true;
> +               smb2_parse_contexts(server, o_rsp,
> +                               &oparms.fid->epoch,
> +                                   oparms.fid->lease_key, &oplock,
> +                                   NULL, NULL);
> +       } else
> +               goto oshr_exit;
> +
> +       qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
> +       if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
> +               goto oshr_exit;
> +       if (!smb2_validate_and_copy_iov(
> +                               le16_to_cpu(qi_rsp->OutputBufferOffset),
> +                               sizeof(struct smb2_file_all_info),
> +                               &rsp_iov[1], sizeof(struct smb2_file_all_info),
> +                               (char *)&tcon->cfid.file_all_info))
> +               tcon->cfid.file_all_info_is_valid = true;
> +       tcon->cfid.time = jiffies;
> +
> +
> +oshr_exit:
> +       mutex_unlock(&tcon->cfid.fid_mutex);
> +oshr_free:
> +       SMB2_open_free(&rqst[0]);
> +       SMB2_query_info_free(&rqst[1]);
> +       free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
> +       free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
> +       if (rc == 0) {
> +               *cfid = &tcon->cfid;
> +}
> +       return rc;
> +}
> +
> +int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
> +                             struct dentry *dentry,
> +                             struct cached_fid **cfid)
> +{
> +       mutex_lock(&tcon->cfid.fid_mutex);
> +       if (tcon->cfid.dentry == dentry) {
> +               cifs_dbg(FYI, "found a cached root file handle by dentry\n");
> +               *cfid = &tcon->cfid;
> +               kref_get(&tcon->cfid.refcount);
> +               mutex_unlock(&tcon->cfid.fid_mutex);
> +               return 0;
> +       }
> +       mutex_unlock(&tcon->cfid.fid_mutex);
> +       return -ENOENT;
> +}
> +
> +static void
> +smb2_close_cached_fid(struct kref *ref)
> +{
> +       struct cached_fid *cfid = container_of(ref, struct cached_fid,
> +                                              refcount);
> +       struct cached_dirent *dirent, *q;
> +
> +       if (cfid->is_valid) {
> +               cifs_dbg(FYI, "clear cached root file handle\n");
> +               SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
> +                          cfid->fid->volatile_fid);
> +       }
> +
> +       /*
> +        * We only check validity above to send SMB2_close,
> +        * but we still need to invalidate these entries
> +        * when this function is called
> +        */
> +       cfid->is_valid = false;
> +       cfid->file_all_info_is_valid = false;
> +       cfid->has_lease = false;
> +       if (cfid->dentry) {
> +               dput(cfid->dentry);
> +               cfid->dentry = NULL;
> +       }
> +       /*
> +        * Delete all cached dirent names
> +        */
> +       mutex_lock(&cfid->dirents.de_mutex);
> +       list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
> +               list_del(&dirent->entry);
> +               kfree(dirent->name);
> +               kfree(dirent);
> +       }
> +       cfid->dirents.is_valid = 0;
> +       cfid->dirents.is_failed = 0;
> +       cfid->dirents.ctx = NULL;
> +       cfid->dirents.pos = 0;
> +       mutex_unlock(&cfid->dirents.de_mutex);
> +
> +}
> +
> +void close_cached_dir(struct cached_fid *cfid)
> +{
> +       mutex_lock(&cfid->fid_mutex);
> +       kref_put(&cfid->refcount, smb2_close_cached_fid);
> +       mutex_unlock(&cfid->fid_mutex);
> +}
> +
> +void close_cached_dir_lease_locked(struct cached_fid *cfid)
> +{
> +       if (cfid->has_lease) {
> +               cfid->has_lease = false;
> +               kref_put(&cfid->refcount, smb2_close_cached_fid);
> +       }
> +}
> +
> +void close_cached_dir_lease(struct cached_fid *cfid)
> +{
> +       mutex_lock(&cfid->fid_mutex);
> +       close_cached_dir_lease_locked(cfid);
> +       mutex_unlock(&cfid->fid_mutex);
> +}
> +
> +/*
> + * Called from cifs_kill_sb when we unmount a share
> + */
> +void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
> +{
> +       struct rb_root *root = &cifs_sb->tlink_tree;
> +       struct rb_node *node;
> +       struct cached_fid *cfid;
> +       struct cifs_tcon *tcon;
> +       struct tcon_link *tlink;
> +
> +       for (node = rb_first(root); node; node = rb_next(node)) {
> +               tlink = rb_entry(node, struct tcon_link, tl_rbnode);
> +               tcon = tlink_tcon(tlink);
> +               if (IS_ERR(tcon))
> +                       continue;
> +               cfid = &tcon->cfid;
> +               mutex_lock(&cfid->fid_mutex);
> +               if (cfid->dentry) {
> +                       dput(cfid->dentry);
> +                       cfid->dentry = NULL;
> +               }
> +               mutex_unlock(&cfid->fid_mutex);
> +       }
> +}
> +
> +/*
> + * Invalidate and close all cached dirs when a TCON has been reset
> + * due to a session loss.
> + */
> +void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
> +{
> +       mutex_lock(&tcon->cfid.fid_mutex);
> +       tcon->cfid.is_valid = false;
> +       /* cached handle is not valid, so SMB2_CLOSE won't be sent below */
> +       close_cached_dir_lease_locked(&tcon->cfid);
> +       memset(tcon->cfid.fid, 0, sizeof(struct cifs_fid));
> +       mutex_unlock(&tcon->cfid.fid_mutex);
> +}
> +
> +static void
> +smb2_cached_lease_break(struct work_struct *work)
> +{
> +       struct cached_fid *cfid = container_of(work,
> +                               struct cached_fid, lease_break);
> +
> +       close_cached_dir_lease(cfid);
> +}
> +
> +int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
> +{
> +       if (tcon->cfid.is_valid &&
> +           !memcmp(lease_key,
> +                   tcon->cfid.fid->lease_key,
> +                   SMB2_LEASE_KEY_SIZE)) {
> +               tcon->cfid.time = 0;
> +               INIT_WORK(&tcon->cfid.lease_break,
> +                         smb2_cached_lease_break);
> +               queue_work(cifsiod_wq,
> +                          &tcon->cfid.lease_break);
> +               spin_unlock(&cifs_tcp_ses_lock);
> +               return true;
> +       }
> +       return false;
> +}
> diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
> new file mode 100644
> index 000000000000..3731c755eea5
> --- /dev/null
> +++ b/fs/cifs/cached_dir.h
> @@ -0,0 +1,26 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Functions to handle the cached directory entries
> + *
> + *  Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
> + */
> +
> +#ifndef _CACHED_DIR_H
> +#define _CACHED_DIR_H
> +
> +
> +extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
> +                          const char *path,
> +                          struct cifs_sb_info *cifs_sb,
> +                          struct cached_fid **cfid);
> +extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
> +                                    struct dentry *dentry,
> +                                    struct cached_fid **cfid);
> +extern void close_cached_dir(struct cached_fid *cfid);
> +extern void close_cached_dir_lease(struct cached_fid *cfid);
> +extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
> +extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
> +extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
> +extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
> +
> +#endif                 /* _CACHED_DIR_H */
> diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
> index f909d9e9faaa..615fbe2bff3c 100644
> --- a/fs/cifs/cifsfs.c
> +++ b/fs/cifs/cifsfs.c
> @@ -46,6 +46,7 @@
>  #include "netlink.h"
>  #endif
>  #include "fs_context.h"
> +#include "cached_dir.h"
>
>  /*
>   * DOS dates from 1980/1/1 through 2107/12/31
> @@ -264,30 +265,13 @@ cifs_read_super(struct super_block *sb)
>  static void cifs_kill_sb(struct super_block *sb)
>  {
>         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
> -       struct cifs_tcon *tcon;
> -       struct cached_fid *cfid;
> -       struct rb_root *root = &cifs_sb->tlink_tree;
> -       struct rb_node *node;
> -       struct tcon_link *tlink;
>
>         /*
>          * We ned to release all dentries for the cached directories
>          * before we kill the sb.
>          */
>         if (cifs_sb->root) {
> -               for (node = rb_first(root); node; node = rb_next(node)) {
> -                       tlink = rb_entry(node, struct tcon_link, tl_rbnode);
> -                       tcon = tlink_tcon(tlink);
> -                       if (IS_ERR(tcon))
> -                               continue;
> -                       cfid = &tcon->crfid;
> -                       mutex_lock(&cfid->fid_mutex);
> -                       if (cfid->dentry) {
> -                               dput(cfid->dentry);
> -                               cfid->dentry = NULL;
> -                       }
> -                       mutex_unlock(&cfid->fid_mutex);
> -               }
> +               close_all_cached_dirs(cifs_sb);
>
>                 /* finally release root dentry */
>                 dput(cifs_sb->root);
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 9b7f409bfc8c..657fabb9067b 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -1253,7 +1253,7 @@ struct cifs_tcon {
>         struct fscache_volume *fscache; /* cookie for share */
>  #endif
>         struct list_head pending_opens; /* list of incomplete opens */
> -       struct cached_fid crfid; /* Cached root fid */
> +       struct cached_fid cfid; /* Cached root fid */
>         /* BB add field for back pointer to sb struct(s)? */
>  #ifdef CONFIG_CIFS_DFS_UPCALL
>         struct list_head ulist; /* cache update list */
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index d59aebefa71c..881bf112d6ae 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -599,7 +599,6 @@ enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
>  struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
>  void cifs_aio_ctx_release(struct kref *refcount);
>  int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
> -void smb2_cached_lease_break(struct work_struct *work);
>
>  int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
>                     struct sdesc **sdesc);
> diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> index 9ed21752f2df..78dfadd729fe 100644
> --- a/fs/cifs/cifssmb.c
> +++ b/fs/cifs/cifssmb.c
> @@ -35,6 +35,7 @@
>  #ifdef CONFIG_CIFS_DFS_UPCALL
>  #include "dfs_cache.h"
>  #endif
> +#include "cached_dir.h"
>
>  #ifdef CONFIG_CIFS_POSIX
>  static struct {
> @@ -91,12 +92,7 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
>         }
>         spin_unlock(&tcon->open_file_lock);
>
> -       mutex_lock(&tcon->crfid.fid_mutex);
> -       tcon->crfid.is_valid = false;
> -       /* cached handle is not valid, so SMB2_CLOSE won't be sent below */
> -       close_cached_dir_lease_locked(&tcon->crfid);
> -       memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
> -       mutex_unlock(&tcon->crfid.fid_mutex);
> +       invalidate_all_cached_dirs(tcon);
>
>         spin_lock(&cifs_tcp_ses_lock);
>         if (tcon->status == TID_IN_FILES_INVALIDATE)
> diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> index 3ad303dd5e5a..7714f47d199b 100644
> --- a/fs/cifs/inode.c
> +++ b/fs/cifs/inode.c
> @@ -25,6 +25,7 @@
>  #include "fscache.h"
>  #include "fs_context.h"
>  #include "cifs_ioctl.h"
> +#include "cached_dir.h"
>
>  static void cifs_set_ops(struct inode *inode)
>  {
> diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> index 16168ebd1a62..fa1a03ddbbe2 100644
> --- a/fs/cifs/misc.c
> +++ b/fs/cifs/misc.c
> @@ -115,13 +115,13 @@ tconInfoAlloc(void)
>         ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
>         if (!ret_buf)
>                 return NULL;
> -       ret_buf->crfid.fid = kzalloc(sizeof(*ret_buf->crfid.fid), GFP_KERNEL);
> -       if (!ret_buf->crfid.fid) {
> +       ret_buf->cfid.fid = kzalloc(sizeof(*ret_buf->cfid.fid), GFP_KERNEL);
> +       if (!ret_buf->cfid.fid) {
>                 kfree(ret_buf);
>                 return NULL;
>         }
> -       INIT_LIST_HEAD(&ret_buf->crfid.dirents.entries);
> -       mutex_init(&ret_buf->crfid.dirents.de_mutex);
> +       INIT_LIST_HEAD(&ret_buf->cfid.dirents.entries);
> +       mutex_init(&ret_buf->cfid.dirents.de_mutex);
>
>         atomic_inc(&tconInfoAllocCount);
>         ret_buf->status = TID_NEW;
> @@ -129,7 +129,7 @@ tconInfoAlloc(void)
>         INIT_LIST_HEAD(&ret_buf->openFileList);
>         INIT_LIST_HEAD(&ret_buf->tcon_list);
>         spin_lock_init(&ret_buf->open_file_lock);
> -       mutex_init(&ret_buf->crfid.fid_mutex);
> +       mutex_init(&ret_buf->cfid.fid_mutex);
>         spin_lock_init(&ret_buf->stat_lock);
>         atomic_set(&ret_buf->num_local_opens, 0);
>         atomic_set(&ret_buf->num_remote_opens, 0);
> @@ -147,7 +147,7 @@ tconInfoFree(struct cifs_tcon *buf_to_free)
>         atomic_dec(&tconInfoAllocCount);
>         kfree(buf_to_free->nativeFileSystem);
>         kfree_sensitive(buf_to_free->password);
> -       kfree(buf_to_free->crfid.fid);
> +       kfree(buf_to_free->cfid.fid);
>         kfree(buf_to_free);
>  }
>
> diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
> index 384cabdf47ca..a06072ae6c7e 100644
> --- a/fs/cifs/readdir.c
> +++ b/fs/cifs/readdir.c
> @@ -21,6 +21,7 @@
>  #include "cifsfs.h"
>  #include "smb2proto.h"
>  #include "fs_context.h"
> +#include "cached_dir.h"
>
>  /*
>   * To be safe - for UCS to UTF-8 with strings loaded with the rare long
> diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
> index 8571a459c710..f6f9fc3f2e2d 100644
> --- a/fs/cifs/smb2inode.c
> +++ b/fs/cifs/smb2inode.c
> @@ -23,6 +23,7 @@
>  #include "smb2glob.h"
>  #include "smb2pdu.h"
>  #include "smb2proto.h"
> +#include "cached_dir.h"
>
>  static void
>  free_set_inf_compound(struct smb_rqst *rqst)
> @@ -518,9 +519,9 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
>                 rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
>         /* If it is a root and its handle is cached then use it */
>         if (!rc) {
> -               if (tcon->crfid.file_all_info_is_valid) {
> +               if (tcon->cfid.file_all_info_is_valid) {
>                         move_smb2_info_to_cifs(data,
> -                                              &tcon->crfid.file_all_info);
> +                                              &tcon->cfid.file_all_info);
>                 } else {
>                         rc = SMB2_query_info(xid, tcon,
>                                              cfid->fid->persistent_fid,
> diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
> index db0f27fd373b..d3d9174ddd7c 100644
> --- a/fs/cifs/smb2misc.c
> +++ b/fs/cifs/smb2misc.c
> @@ -16,6 +16,7 @@
>  #include "smb2status.h"
>  #include "smb2glob.h"
>  #include "nterr.h"
> +#include "cached_dir.h"
>
>  static int
>  check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
> @@ -639,18 +640,8 @@ smb2_is_valid_lease_break(char *buffer)
>                                 }
>                                 spin_unlock(&tcon->open_file_lock);
>
> -                               if (tcon->crfid.is_valid &&
> -                                   !memcmp(rsp->LeaseKey,
> -                                           tcon->crfid.fid->lease_key,
> -                                           SMB2_LEASE_KEY_SIZE)) {
> -                                       tcon->crfid.time = 0;
> -                                       INIT_WORK(&tcon->crfid.lease_break,
> -                                                 smb2_cached_lease_break);
> -                                       queue_work(cifsiod_wq,
> -                                                  &tcon->crfid.lease_break);
> -                                       spin_unlock(&cifs_tcp_ses_lock);
> +                               if (cached_dir_lease_break(tcon, rsp->LeaseKey))
>                                         return true;
> -                               }
>                         }
>                 }
>         }
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index aa4c1d403708..01aafedc477e 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -27,6 +27,7 @@
>  #include "smbdirect.h"
>  #include "fscache.h"
>  #include "fs_context.h"
> +#include "cached_dir.h"
>
>  /* Change credits for different ops and return the total number of credits */
>  static int
> @@ -701,300 +702,6 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
>         return rc;
>  }
>
> -static void
> -smb2_close_cached_fid(struct kref *ref)
> -{
> -       struct cached_fid *cfid = container_of(ref, struct cached_fid,
> -                                              refcount);
> -       struct cached_dirent *dirent, *q;
> -
> -       if (cfid->is_valid) {
> -               cifs_dbg(FYI, "clear cached root file handle\n");
> -               SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
> -                          cfid->fid->volatile_fid);
> -       }
> -
> -       /*
> -        * We only check validity above to send SMB2_close,
> -        * but we still need to invalidate these entries
> -        * when this function is called
> -        */
> -       cfid->is_valid = false;
> -       cfid->file_all_info_is_valid = false;
> -       cfid->has_lease = false;
> -       if (cfid->dentry) {
> -               dput(cfid->dentry);
> -               cfid->dentry = NULL;
> -       }
> -       /*
> -        * Delete all cached dirent names
> -        */
> -       mutex_lock(&cfid->dirents.de_mutex);
> -       list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
> -               list_del(&dirent->entry);
> -               kfree(dirent->name);
> -               kfree(dirent);
> -       }
> -       cfid->dirents.is_valid = 0;
> -       cfid->dirents.is_failed = 0;
> -       cfid->dirents.ctx = NULL;
> -       cfid->dirents.pos = 0;
> -       mutex_unlock(&cfid->dirents.de_mutex);
> -
> -}
> -
> -void close_cached_dir(struct cached_fid *cfid)
> -{
> -       mutex_lock(&cfid->fid_mutex);
> -       kref_put(&cfid->refcount, smb2_close_cached_fid);
> -       mutex_unlock(&cfid->fid_mutex);
> -}
> -
> -void close_cached_dir_lease_locked(struct cached_fid *cfid)
> -{
> -       if (cfid->has_lease) {
> -               cfid->has_lease = false;
> -               kref_put(&cfid->refcount, smb2_close_cached_fid);
> -       }
> -}
> -
> -void close_cached_dir_lease(struct cached_fid *cfid)
> -{
> -       mutex_lock(&cfid->fid_mutex);
> -       close_cached_dir_lease_locked(cfid);
> -       mutex_unlock(&cfid->fid_mutex);
> -}
> -
> -void
> -smb2_cached_lease_break(struct work_struct *work)
> -{
> -       struct cached_fid *cfid = container_of(work,
> -                               struct cached_fid, lease_break);
> -
> -       close_cached_dir_lease(cfid);
> -}
> -
> -/*
> - * Open the and cache a directory handle.
> - * Only supported for the root handle.
> - * If error then *cfid is not initialized.
> - */
> -int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
> -               const char *path,
> -               struct cifs_sb_info *cifs_sb,
> -               struct cached_fid **cfid)
> -{
> -       struct cifs_ses *ses;
> -       struct TCP_Server_Info *server;
> -       struct cifs_open_parms oparms;
> -       struct smb2_create_rsp *o_rsp = NULL;
> -       struct smb2_query_info_rsp *qi_rsp = NULL;
> -       int resp_buftype[2];
> -       struct smb_rqst rqst[2];
> -       struct kvec rsp_iov[2];
> -       struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
> -       struct kvec qi_iov[1];
> -       int rc, flags = 0;
> -       __le16 utf16_path = 0; /* Null - since an open of top of share */
> -       u8 oplock = SMB2_OPLOCK_LEVEL_II;
> -       struct cifs_fid *pfid;
> -       struct dentry *dentry;
> -
> -       if (tcon == NULL || tcon->nohandlecache ||
> -           is_smb1_server(tcon->ses->server))
> -               return -ENOTSUPP;
> -
> -       ses = tcon->ses;
> -       server = ses->server;
> -
> -       if (cifs_sb->root == NULL)
> -               return -ENOENT;
> -
> -       if (strlen(path))
> -               return -ENOENT;
> -
> -       dentry = cifs_sb->root;
> -
> -       mutex_lock(&tcon->crfid.fid_mutex);
> -       if (tcon->crfid.is_valid) {
> -               cifs_dbg(FYI, "found a cached root file handle\n");
> -               *cfid = &tcon->crfid;
> -               kref_get(&tcon->crfid.refcount);
> -               mutex_unlock(&tcon->crfid.fid_mutex);
> -               return 0;
> -       }
> -
> -       /*
> -        * We do not hold the lock for the open because in case
> -        * SMB2_open needs to reconnect, it will end up calling
> -        * cifs_mark_open_files_invalid() which takes the lock again
> -        * thus causing a deadlock
> -        */
> -
> -       mutex_unlock(&tcon->crfid.fid_mutex);
> -
> -       if (smb3_encryption_required(tcon))
> -               flags |= CIFS_TRANSFORM_REQ;
> -
> -       if (!server->ops->new_lease_key)
> -               return -EIO;
> -
> -       pfid = tcon->crfid.fid;
> -       server->ops->new_lease_key(pfid);
> -
> -       memset(rqst, 0, sizeof(rqst));
> -       resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
> -       memset(rsp_iov, 0, sizeof(rsp_iov));
> -
> -       /* Open */
> -       memset(&open_iov, 0, sizeof(open_iov));
> -       rqst[0].rq_iov = open_iov;
> -       rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
> -
> -       oparms.tcon = tcon;
> -       oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
> -       oparms.desired_access = FILE_READ_ATTRIBUTES;
> -       oparms.disposition = FILE_OPEN;
> -       oparms.fid = pfid;
> -       oparms.reconnect = false;
> -
> -       rc = SMB2_open_init(tcon, server,
> -                           &rqst[0], &oplock, &oparms, &utf16_path);
> -       if (rc)
> -               goto oshr_free;
> -       smb2_set_next_command(tcon, &rqst[0]);
> -
> -       memset(&qi_iov, 0, sizeof(qi_iov));
> -       rqst[1].rq_iov = qi_iov;
> -       rqst[1].rq_nvec = 1;
> -
> -       rc = SMB2_query_info_init(tcon, server,
> -                                 &rqst[1], COMPOUND_FID,
> -                                 COMPOUND_FID, FILE_ALL_INFORMATION,
> -                                 SMB2_O_INFO_FILE, 0,
> -                                 sizeof(struct smb2_file_all_info) +
> -                                 PATH_MAX * 2, 0, NULL);
> -       if (rc)
> -               goto oshr_free;
> -
> -       smb2_set_related(&rqst[1]);
> -
> -       rc = compound_send_recv(xid, ses, server,
> -                               flags, 2, rqst,
> -                               resp_buftype, rsp_iov);
> -       mutex_lock(&tcon->crfid.fid_mutex);
> -
> -       /*
> -        * Now we need to check again as the cached root might have
> -        * been successfully re-opened from a concurrent process
> -        */
> -
> -       if (tcon->crfid.is_valid) {
> -               /* work was already done */
> -
> -               /* stash fids for close() later */
> -               struct cifs_fid fid = {
> -                       .persistent_fid = pfid->persistent_fid,
> -                       .volatile_fid = pfid->volatile_fid,
> -               };
> -
> -               /*
> -                * caller expects this func to set the fid in crfid to valid
> -                * cached root, so increment the refcount.
> -                */
> -               kref_get(&tcon->crfid.refcount);
> -
> -               mutex_unlock(&tcon->crfid.fid_mutex);
> -
> -               if (rc == 0) {
> -                       /* close extra handle outside of crit sec */
> -                       SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
> -               }
> -               rc = 0;
> -               goto oshr_free;
> -       }
> -
> -       /* Cached root is still invalid, continue normaly */
> -
> -       if (rc) {
> -               if (rc == -EREMCHG) {
> -                       tcon->need_reconnect = true;
> -                       pr_warn_once("server share %s deleted\n",
> -                                    tcon->treeName);
> -               }
> -               goto oshr_exit;
> -       }
> -
> -       atomic_inc(&tcon->num_remote_opens);
> -
> -       o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
> -       oparms.fid->persistent_fid = o_rsp->PersistentFileId;
> -       oparms.fid->volatile_fid = o_rsp->VolatileFileId;
> -#ifdef CONFIG_CIFS_DEBUG2
> -       oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
> -#endif /* CIFS_DEBUG2 */
> -
> -       tcon->crfid.tcon = tcon;
> -       tcon->crfid.is_valid = true;
> -       tcon->crfid.dentry = dentry;
> -       dget(dentry);
> -       kref_init(&tcon->crfid.refcount);
> -
> -       /* BB TBD check to see if oplock level check can be removed below */
> -       if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
> -               /*
> -                * See commit 2f94a3125b87. Increment the refcount when we
> -                * get a lease for root, release it if lease break occurs
> -                */
> -               kref_get(&tcon->crfid.refcount);
> -               tcon->crfid.has_lease = true;
> -               smb2_parse_contexts(server, o_rsp,
> -                               &oparms.fid->epoch,
> -                                   oparms.fid->lease_key, &oplock,
> -                                   NULL, NULL);
> -       } else
> -               goto oshr_exit;
> -
> -       qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
> -       if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
> -               goto oshr_exit;
> -       if (!smb2_validate_and_copy_iov(
> -                               le16_to_cpu(qi_rsp->OutputBufferOffset),
> -                               sizeof(struct smb2_file_all_info),
> -                               &rsp_iov[1], sizeof(struct smb2_file_all_info),
> -                               (char *)&tcon->crfid.file_all_info))
> -               tcon->crfid.file_all_info_is_valid = true;
> -       tcon->crfid.time = jiffies;
> -
> -
> -oshr_exit:
> -       mutex_unlock(&tcon->crfid.fid_mutex);
> -oshr_free:
> -       SMB2_open_free(&rqst[0]);
> -       SMB2_query_info_free(&rqst[1]);
> -       free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
> -       free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
> -       if (rc == 0)
> -               *cfid = &tcon->crfid;
> -       return rc;
> -}
> -
> -int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
> -                             struct dentry *dentry,
> -                             struct cached_fid **cfid)
> -{
> -       mutex_lock(&tcon->crfid.fid_mutex);
> -       if (tcon->crfid.dentry == dentry) {
> -               cifs_dbg(FYI, "found a cached root file handle by dentry\n");
> -               *cfid = &tcon->crfid;
> -               kref_get(&tcon->crfid.refcount);
> -               mutex_unlock(&tcon->crfid.fid_mutex);
> -               return 0;
> -       }
> -       mutex_unlock(&tcon->crfid.fid_mutex);
> -       return -ENOENT;
> -}
> -
>  static void
>  smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
>               struct cifs_sb_info *cifs_sb)
> @@ -1077,7 +784,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
>         struct cifs_open_parms oparms;
>         struct cifs_fid fid;
>
> -       if ((*full_path == 0) && tcon->crfid.is_valid)
> +       if ((*full_path == 0) && tcon->cfid.is_valid)
>                 return 0;
>
>         utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index 295ee8b88055..9ee1b6225619 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -39,6 +39,7 @@
>  #ifdef CONFIG_CIFS_DFS_UPCALL
>  #include "dfs_cache.h"
>  #endif
> +#include "cached_dir.h"
>
>  /*
>   *  The following table defines the expected "StructureSize" of SMB2 requests
> @@ -1978,7 +1979,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
>         }
>         spin_unlock(&ses->chan_lock);
>
> -       close_cached_dir_lease(&tcon->crfid);
> +       close_cached_dir_lease(&tcon->cfid);
>
>         rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server,
>                                  (void **) &req,
> diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
> index a69f1eed1cfe..51c5bf4a338a 100644
> --- a/fs/cifs/smb2proto.h
> +++ b/fs/cifs/smb2proto.h
> @@ -54,16 +54,6 @@ extern bool smb2_is_valid_oplock_break(char *buffer,
>  extern int smb3_handle_read_data(struct TCP_Server_Info *server,
>                                  struct mid_q_entry *mid);
>
> -extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
> -                          const char *path,
> -                          struct cifs_sb_info *cifs_sb,
> -                          struct cached_fid **cfid);
> -extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
> -                                    struct dentry *dentry,
> -                                    struct cached_fid **cfid);
> -extern void close_cached_dir(struct cached_fid *cfid);
> -extern void close_cached_dir_lease(struct cached_fid *cfid);
> -extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
>  extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
>                                    struct smb2_file_all_info *src);
>  extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
> --
> 2.35.3
>


-- 
Thanks,

Steve

[-- Attachment #2: 0001-cifs-Move-cached-dir-functions-into-a-separate-file.patch --]
[-- Type: text/x-patch, Size: 18703 bytes --]

From 274c44196c0935f669c8827e1665eace7d7f4670 Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@microsoft.com>
Date: Wed, 10 Aug 2022 21:50:11 -0500
Subject: [PATCH] cifs: Move cached-dir functions into a separate file

Also rename crfid to cfid to have consistent naming for this variable.

This commit does not change any logic.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/Makefile    |   2 +-
 fs/cifs/cifsfs.c    |  20 +--
 fs/cifs/cifsglob.h  |   2 +-
 fs/cifs/cifsproto.h |   1 -
 fs/cifs/file.c      |   9 +-
 fs/cifs/inode.c     |   1 +
 fs/cifs/misc.c      |  12 +-
 fs/cifs/readdir.c   |   1 +
 fs/cifs/smb2inode.c |   5 +-
 fs/cifs/smb2misc.c  |  11 +-
 fs/cifs/smb2ops.c   | 297 +-------------------------------------------
 fs/cifs/smb2pdu.c   |   3 +-
 fs/cifs/smb2proto.h |  10 --
 13 files changed, 23 insertions(+), 351 deletions(-)

diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index e882e912a517..7c9785973f49 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_CIFS) += cifs.o
 
 cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \
 	  inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \
-	  cifs_unicode.o nterr.o cifsencrypt.o \
+	  cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \
 	  readdir.o ioctl.o sess.o export.o unc.o winucase.o \
 	  smb2ops.o smb2maperror.o smb2transport.o \
 	  smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 8849f0852110..945fb083cea7 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -46,6 +46,7 @@
 #include "netlink.h"
 #endif
 #include "fs_context.h"
+#include "cached_dir.h"
 
 /*
  * DOS dates from 1980/1/1 through 2107/12/31
@@ -283,30 +284,13 @@ cifs_read_super(struct super_block *sb)
 static void cifs_kill_sb(struct super_block *sb)
 {
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-	struct cifs_tcon *tcon;
-	struct cached_fid *cfid;
-	struct rb_root *root = &cifs_sb->tlink_tree;
-	struct rb_node *node;
-	struct tcon_link *tlink;
 
 	/*
 	 * We ned to release all dentries for the cached directories
 	 * before we kill the sb.
 	 */
 	if (cifs_sb->root) {
-		for (node = rb_first(root); node; node = rb_next(node)) {
-			tlink = rb_entry(node, struct tcon_link, tl_rbnode);
-			tcon = tlink_tcon(tlink);
-			if (IS_ERR(tcon))
-				continue;
-			cfid = &tcon->crfid;
-			mutex_lock(&cfid->fid_mutex);
-			if (cfid->dentry) {
-				dput(cfid->dentry);
-				cfid->dentry = NULL;
-			}
-			mutex_unlock(&cfid->fid_mutex);
-		}
+		close_all_cached_dirs(cifs_sb);
 
 		/* finally release root dentry */
 		dput(cifs_sb->root);
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index a93024eaf251..8b82f13d11e0 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1257,7 +1257,7 @@ struct cifs_tcon {
 	struct fscache_volume *fscache;	/* cookie for share */
 #endif
 	struct list_head pending_opens;	/* list of incomplete opens */
-	struct cached_fid crfid; /* Cached root fid */
+	struct cached_fid cfid; /* Cached root fid */
 	/* BB add field for back pointer to sb struct(s)? */
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	struct list_head ulist; /* cache update list */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index daaadffa2b88..87a77a684339 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -597,7 +597,6 @@ enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
 struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
 void cifs_aio_ctx_release(struct kref *refcount);
 int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
-void smb2_cached_lease_break(struct work_struct *work);
 
 int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
 		    struct sdesc **sdesc);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 09975bd7d860..42f2639a1a66 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -34,6 +34,7 @@
 #include "smbdirect.h"
 #include "fs_context.h"
 #include "cifs_ioctl.h"
+#include "cached_dir.h"
 
 /*
  * Mark as invalid, all open files on tree connections since they
@@ -64,13 +65,7 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
 	}
 	spin_unlock(&tcon->open_file_lock);
 
-	mutex_lock(&tcon->crfid.fid_mutex);
-	tcon->crfid.is_valid = false;
-	/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
-	close_cached_dir_lease_locked(&tcon->crfid);
-	memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
-	mutex_unlock(&tcon->crfid.fid_mutex);
-
+	invalidate_all_cached_dirs(tcon);
 	spin_lock(&tcon->tc_lock);
 	if (tcon->status == TID_IN_FILES_INVALIDATE)
 		tcon->status = TID_NEED_TCON;
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index eeeaba3dec05..bac08c20f559 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -25,6 +25,7 @@
 #include "fscache.h"
 #include "fs_context.h"
 #include "cifs_ioctl.h"
+#include "cached_dir.h"
 
 static void cifs_set_ops(struct inode *inode)
 {
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 7a906067db04..ea0405cfaee3 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -116,13 +116,13 @@ tconInfoAlloc(void)
 	ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
 	if (!ret_buf)
 		return NULL;
-	ret_buf->crfid.fid = kzalloc(sizeof(*ret_buf->crfid.fid), GFP_KERNEL);
-	if (!ret_buf->crfid.fid) {
+	ret_buf->cfid.fid = kzalloc(sizeof(*ret_buf->cfid.fid), GFP_KERNEL);
+	if (!ret_buf->cfid.fid) {
 		kfree(ret_buf);
 		return NULL;
 	}
-	INIT_LIST_HEAD(&ret_buf->crfid.dirents.entries);
-	mutex_init(&ret_buf->crfid.dirents.de_mutex);
+	INIT_LIST_HEAD(&ret_buf->cfid.dirents.entries);
+	mutex_init(&ret_buf->cfid.dirents.de_mutex);
 
 	atomic_inc(&tconInfoAllocCount);
 	ret_buf->status = TID_NEW;
@@ -131,7 +131,7 @@ tconInfoAlloc(void)
 	INIT_LIST_HEAD(&ret_buf->openFileList);
 	INIT_LIST_HEAD(&ret_buf->tcon_list);
 	spin_lock_init(&ret_buf->open_file_lock);
-	mutex_init(&ret_buf->crfid.fid_mutex);
+	mutex_init(&ret_buf->cfid.fid_mutex);
 	spin_lock_init(&ret_buf->stat_lock);
 	atomic_set(&ret_buf->num_local_opens, 0);
 	atomic_set(&ret_buf->num_remote_opens, 0);
@@ -149,7 +149,7 @@ tconInfoFree(struct cifs_tcon *buf_to_free)
 	atomic_dec(&tconInfoAllocCount);
 	kfree(buf_to_free->nativeFileSystem);
 	kfree_sensitive(buf_to_free->password);
-	kfree(buf_to_free->crfid.fid);
+	kfree(buf_to_free->cfid.fid);
 	kfree(buf_to_free);
 }
 
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 384cabdf47ca..a06072ae6c7e 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -21,6 +21,7 @@
 #include "cifsfs.h"
 #include "smb2proto.h"
 #include "fs_context.h"
+#include "cached_dir.h"
 
 /*
  * To be safe - for UCS to UTF-8 with strings loaded with the rare long
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 8571a459c710..f6f9fc3f2e2d 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -23,6 +23,7 @@
 #include "smb2glob.h"
 #include "smb2pdu.h"
 #include "smb2proto.h"
+#include "cached_dir.h"
 
 static void
 free_set_inf_compound(struct smb_rqst *rqst)
@@ -518,9 +519,9 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
 	/* If it is a root and its handle is cached then use it */
 	if (!rc) {
-		if (tcon->crfid.file_all_info_is_valid) {
+		if (tcon->cfid.file_all_info_is_valid) {
 			move_smb2_info_to_cifs(data,
-					       &tcon->crfid.file_all_info);
+					       &tcon->cfid.file_all_info);
 		} else {
 			rc = SMB2_query_info(xid, tcon,
 					     cfid->fid->persistent_fid,
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 818cc4dee0e2..6a6ec6efb45a 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -16,6 +16,7 @@
 #include "smb2status.h"
 #include "smb2glob.h"
 #include "nterr.h"
+#include "cached_dir.h"
 
 static int
 check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
@@ -648,15 +649,7 @@ smb2_is_valid_lease_break(char *buffer)
 				}
 				spin_unlock(&tcon->open_file_lock);
 
-				if (tcon->crfid.is_valid &&
-				    !memcmp(rsp->LeaseKey,
-					    tcon->crfid.fid->lease_key,
-					    SMB2_LEASE_KEY_SIZE)) {
-					tcon->crfid.time = 0;
-					INIT_WORK(&tcon->crfid.lease_break,
-						  smb2_cached_lease_break);
-					queue_work(cifsiod_wq,
-						   &tcon->crfid.lease_break);
+				if (cached_dir_lease_break(tcon, rsp->LeaseKey)) {
 					spin_unlock(&cifs_tcp_ses_lock);
 					return true;
 				}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index c0039dc0715a..8cb1eed1dd63 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -27,6 +27,7 @@
 #include "smbdirect.h"
 #include "fscache.h"
 #include "fs_context.h"
+#include "cached_dir.h"
 
 /* Change credits for different ops and return the total number of credits */
 static int
@@ -701,300 +702,6 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
 	return rc;
 }
 
-static void
-smb2_close_cached_fid(struct kref *ref)
-{
-	struct cached_fid *cfid = container_of(ref, struct cached_fid,
-					       refcount);
-	struct cached_dirent *dirent, *q;
-
-	if (cfid->is_valid) {
-		cifs_dbg(FYI, "clear cached root file handle\n");
-		SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
-			   cfid->fid->volatile_fid);
-	}
-
-	/*
-	 * We only check validity above to send SMB2_close,
-	 * but we still need to invalidate these entries
-	 * when this function is called
-	 */
-	cfid->is_valid = false;
-	cfid->file_all_info_is_valid = false;
-	cfid->has_lease = false;
-	if (cfid->dentry) {
-		dput(cfid->dentry);
-		cfid->dentry = NULL;
-	}
-	/*
-	 * Delete all cached dirent names
-	 */
-	mutex_lock(&cfid->dirents.de_mutex);
-	list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
-		list_del(&dirent->entry);
-		kfree(dirent->name);
-		kfree(dirent);
-	}
-	cfid->dirents.is_valid = 0;
-	cfid->dirents.is_failed = 0;
-	cfid->dirents.ctx = NULL;
-	cfid->dirents.pos = 0;
-	mutex_unlock(&cfid->dirents.de_mutex);
-
-}
-
-void close_cached_dir(struct cached_fid *cfid)
-{
-	mutex_lock(&cfid->fid_mutex);
-	kref_put(&cfid->refcount, smb2_close_cached_fid);
-	mutex_unlock(&cfid->fid_mutex);
-}
-
-void close_cached_dir_lease_locked(struct cached_fid *cfid)
-{
-	if (cfid->has_lease) {
-		cfid->has_lease = false;
-		kref_put(&cfid->refcount, smb2_close_cached_fid);
-	}
-}
-
-void close_cached_dir_lease(struct cached_fid *cfid)
-{
-	mutex_lock(&cfid->fid_mutex);
-	close_cached_dir_lease_locked(cfid);
-	mutex_unlock(&cfid->fid_mutex);
-}
-
-void
-smb2_cached_lease_break(struct work_struct *work)
-{
-	struct cached_fid *cfid = container_of(work,
-				struct cached_fid, lease_break);
-
-	close_cached_dir_lease(cfid);
-}
-
-/*
- * Open the and cache a directory handle.
- * Only supported for the root handle.
- * If error then *cfid is not initialized.
- */
-int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
-		const char *path,
-		struct cifs_sb_info *cifs_sb,
-		struct cached_fid **cfid)
-{
-	struct cifs_ses *ses;
-	struct TCP_Server_Info *server;
-	struct cifs_open_parms oparms;
-	struct smb2_create_rsp *o_rsp = NULL;
-	struct smb2_query_info_rsp *qi_rsp = NULL;
-	int resp_buftype[2];
-	struct smb_rqst rqst[2];
-	struct kvec rsp_iov[2];
-	struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
-	struct kvec qi_iov[1];
-	int rc, flags = 0;
-	__le16 utf16_path = 0; /* Null - since an open of top of share */
-	u8 oplock = SMB2_OPLOCK_LEVEL_II;
-	struct cifs_fid *pfid;
-	struct dentry *dentry;
-
-	if (tcon == NULL || tcon->nohandlecache ||
-	    is_smb1_server(tcon->ses->server))
-		return -ENOTSUPP;
-
-	ses = tcon->ses;
-	server = ses->server;
-
-	if (cifs_sb->root == NULL)
-		return -ENOENT;
-
-	if (strlen(path))
-		return -ENOENT;
-
-	dentry = cifs_sb->root;
-
-	mutex_lock(&tcon->crfid.fid_mutex);
-	if (tcon->crfid.is_valid) {
-		cifs_dbg(FYI, "found a cached root file handle\n");
-		*cfid = &tcon->crfid;
-		kref_get(&tcon->crfid.refcount);
-		mutex_unlock(&tcon->crfid.fid_mutex);
-		return 0;
-	}
-
-	/*
-	 * We do not hold the lock for the open because in case
-	 * SMB2_open needs to reconnect, it will end up calling
-	 * cifs_mark_open_files_invalid() which takes the lock again
-	 * thus causing a deadlock
-	 */
-
-	mutex_unlock(&tcon->crfid.fid_mutex);
-
-	if (smb3_encryption_required(tcon))
-		flags |= CIFS_TRANSFORM_REQ;
-
-	if (!server->ops->new_lease_key)
-		return -EIO;
-
-	pfid = tcon->crfid.fid;
-	server->ops->new_lease_key(pfid);
-
-	memset(rqst, 0, sizeof(rqst));
-	resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
-	memset(rsp_iov, 0, sizeof(rsp_iov));
-
-	/* Open */
-	memset(&open_iov, 0, sizeof(open_iov));
-	rqst[0].rq_iov = open_iov;
-	rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
-
-	oparms.tcon = tcon;
-	oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
-	oparms.desired_access = FILE_READ_ATTRIBUTES;
-	oparms.disposition = FILE_OPEN;
-	oparms.fid = pfid;
-	oparms.reconnect = false;
-
-	rc = SMB2_open_init(tcon, server,
-			    &rqst[0], &oplock, &oparms, &utf16_path);
-	if (rc)
-		goto oshr_free;
-	smb2_set_next_command(tcon, &rqst[0]);
-
-	memset(&qi_iov, 0, sizeof(qi_iov));
-	rqst[1].rq_iov = qi_iov;
-	rqst[1].rq_nvec = 1;
-
-	rc = SMB2_query_info_init(tcon, server,
-				  &rqst[1], COMPOUND_FID,
-				  COMPOUND_FID, FILE_ALL_INFORMATION,
-				  SMB2_O_INFO_FILE, 0,
-				  sizeof(struct smb2_file_all_info) +
-				  PATH_MAX * 2, 0, NULL);
-	if (rc)
-		goto oshr_free;
-
-	smb2_set_related(&rqst[1]);
-
-	rc = compound_send_recv(xid, ses, server,
-				flags, 2, rqst,
-				resp_buftype, rsp_iov);
-	mutex_lock(&tcon->crfid.fid_mutex);
-
-	/*
-	 * Now we need to check again as the cached root might have
-	 * been successfully re-opened from a concurrent process
-	 */
-
-	if (tcon->crfid.is_valid) {
-		/* work was already done */
-
-		/* stash fids for close() later */
-		struct cifs_fid fid = {
-			.persistent_fid = pfid->persistent_fid,
-			.volatile_fid = pfid->volatile_fid,
-		};
-
-		/*
-		 * caller expects this func to set the fid in crfid to valid
-		 * cached root, so increment the refcount.
-		 */
-		kref_get(&tcon->crfid.refcount);
-
-		mutex_unlock(&tcon->crfid.fid_mutex);
-
-		if (rc == 0) {
-			/* close extra handle outside of crit sec */
-			SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
-		}
-		rc = 0;
-		goto oshr_free;
-	}
-
-	/* Cached root is still invalid, continue normaly */
-
-	if (rc) {
-		if (rc == -EREMCHG) {
-			tcon->need_reconnect = true;
-			pr_warn_once("server share %s deleted\n",
-				     tcon->treeName);
-		}
-		goto oshr_exit;
-	}
-
-	atomic_inc(&tcon->num_remote_opens);
-
-	o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
-	oparms.fid->persistent_fid = o_rsp->PersistentFileId;
-	oparms.fid->volatile_fid = o_rsp->VolatileFileId;
-#ifdef CONFIG_CIFS_DEBUG2
-	oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
-#endif /* CIFS_DEBUG2 */
-
-	tcon->crfid.tcon = tcon;
-	tcon->crfid.is_valid = true;
-	tcon->crfid.dentry = dentry;
-	dget(dentry);
-	kref_init(&tcon->crfid.refcount);
-
-	/* BB TBD check to see if oplock level check can be removed below */
-	if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
-		/*
-		 * See commit 2f94a3125b87. Increment the refcount when we
-		 * get a lease for root, release it if lease break occurs
-		 */
-		kref_get(&tcon->crfid.refcount);
-		tcon->crfid.has_lease = true;
-		smb2_parse_contexts(server, o_rsp,
-				&oparms.fid->epoch,
-				    oparms.fid->lease_key, &oplock,
-				    NULL, NULL);
-	} else
-		goto oshr_exit;
-
-	qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
-	if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
-		goto oshr_exit;
-	if (!smb2_validate_and_copy_iov(
-				le16_to_cpu(qi_rsp->OutputBufferOffset),
-				sizeof(struct smb2_file_all_info),
-				&rsp_iov[1], sizeof(struct smb2_file_all_info),
-				(char *)&tcon->crfid.file_all_info))
-		tcon->crfid.file_all_info_is_valid = true;
-	tcon->crfid.time = jiffies;
-
-
-oshr_exit:
-	mutex_unlock(&tcon->crfid.fid_mutex);
-oshr_free:
-	SMB2_open_free(&rqst[0]);
-	SMB2_query_info_free(&rqst[1]);
-	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
-	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
-	if (rc == 0)
-		*cfid = &tcon->crfid;
-	return rc;
-}
-
-int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
-			      struct dentry *dentry,
-			      struct cached_fid **cfid)
-{
-	mutex_lock(&tcon->crfid.fid_mutex);
-	if (tcon->crfid.dentry == dentry) {
-		cifs_dbg(FYI, "found a cached root file handle by dentry\n");
-		*cfid = &tcon->crfid;
-		kref_get(&tcon->crfid.refcount);
-		mutex_unlock(&tcon->crfid.fid_mutex);
-		return 0;
-	}
-	mutex_unlock(&tcon->crfid.fid_mutex);
-	return -ENOENT;
-}
-
 static void
 smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
 	      struct cifs_sb_info *cifs_sb)
@@ -1077,7 +784,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 	struct cifs_open_parms oparms;
 	struct cifs_fid fid;
 
-	if ((*full_path == 0) && tcon->crfid.is_valid)
+	if ((*full_path == 0) && tcon->cfid.is_valid)
 		return 0;
 
 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 590a1d4ac140..7c200b938267 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -39,6 +39,7 @@
 #ifdef CONFIG_CIFS_DFS_UPCALL
 #include "dfs_cache.h"
 #endif
+#include "cached_dir.h"
 
 /*
  *  The following table defines the expected "StructureSize" of SMB2 requests
@@ -1978,7 +1979,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
 	}
 	spin_unlock(&ses->chan_lock);
 
-	close_cached_dir_lease(&tcon->crfid);
+	close_cached_dir_lease(&tcon->cfid);
 
 	rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server,
 				 (void **) &req,
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index a69f1eed1cfe..51c5bf4a338a 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -54,16 +54,6 @@ extern bool smb2_is_valid_oplock_break(char *buffer,
 extern int smb3_handle_read_data(struct TCP_Server_Info *server,
 				 struct mid_q_entry *mid);
 
-extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
-			   const char *path,
-			   struct cifs_sb_info *cifs_sb,
-			   struct cached_fid **cfid);
-extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
-				     struct dentry *dentry,
-				     struct cached_fid **cfid);
-extern void close_cached_dir(struct cached_fid *cfid);
-extern void close_cached_dir_lease(struct cached_fid *cfid);
-extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
 extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
 				   struct smb2_file_all_info *src);
 extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/9] cifs: Move cached-dir functions into a separate file
  2022-08-11  2:53   ` Steve French
@ 2022-08-11  3:08     ` Steve French
  0 siblings, 0 replies; 27+ messages in thread
From: Steve French @ 2022-08-11  3:08 UTC (permalink / raw)
  To: Ronnie Sahlberg; +Cc: linux-cifs

[-- Attachment #1: Type: text/plain, Size: 40623 bytes --]

Updated to fix various checkpatch warnings, and to include new file
accidentally left out in rebased patch.

On Wed, Aug 10, 2022 at 9:53 PM Steve French <smfrench@gmail.com> wrote:
>
> Lightly updated (rebase to merge with current for-next) and also
> combined with patch 8 of the series to avoid a lock warning.
>
> Tentatively merged this restructuring into cifs-2.6.git for-next
> pending testing.
>
> On Mon, Aug 8, 2022 at 9:12 PM Ronnie Sahlberg <lsahlber@redhat.com> wrote:
> >
> > Also rename crfid to cfid to have consistent naming for this variable.
> >
> > This commit does not change any logic.
> >
> > Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> > ---
> >  fs/cifs/Makefile     |   2 +-
> >  fs/cifs/cached_dir.c | 363 +++++++++++++++++++++++++++++++++++++++++++
> >  fs/cifs/cached_dir.h |  26 ++++
> >  fs/cifs/cifsfs.c     |  20 +--
> >  fs/cifs/cifsglob.h   |   2 +-
> >  fs/cifs/cifsproto.h  |   1 -
> >  fs/cifs/cifssmb.c    |   8 +-
> >  fs/cifs/inode.c      |   1 +
> >  fs/cifs/misc.c       |  12 +-
> >  fs/cifs/readdir.c    |   1 +
> >  fs/cifs/smb2inode.c  |   5 +-
> >  fs/cifs/smb2misc.c   |  13 +-
> >  fs/cifs/smb2ops.c    | 297 +----------------------------------
> >  fs/cifs/smb2pdu.c    |   3 +-
> >  fs/cifs/smb2proto.h  |  10 --
> >  15 files changed, 412 insertions(+), 352 deletions(-)
> >  create mode 100644 fs/cifs/cached_dir.c
> >  create mode 100644 fs/cifs/cached_dir.h
> >
> > diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
> > index 8c9f2c00be72..343a59e0d64d 100644
> > --- a/fs/cifs/Makefile
> > +++ b/fs/cifs/Makefile
> > @@ -7,7 +7,7 @@ obj-$(CONFIG_CIFS) += cifs.o
> >
> >  cifs-y := trace.o cifsfs.o cifssmb.o cifs_debug.o connect.o dir.o file.o \
> >           inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \
> > -         cifs_unicode.o nterr.o cifsencrypt.o \
> > +         cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \
> >           readdir.o ioctl.o sess.o export.o unc.o winucase.o \
> >           smb2ops.o smb2maperror.o smb2transport.o \
> >           smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \
> > diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
> > new file mode 100644
> > index 000000000000..f2e17c1d5196
> > --- /dev/null
> > +++ b/fs/cifs/cached_dir.c
> > @@ -0,0 +1,363 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + *  Functions to handle the cached directory entries
> > + *
> > + *  Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
> > + */
> > +
> > +#include "cifsglob.h"
> > +#include "cifsproto.h"
> > +#include "cifs_debug.h"
> > +#include "smb2proto.h"
> > +#include "cached_dir.h"
> > +
> > +/*
> > + * Open the and cache a directory handle.
> > + * If error then *cfid is not initialized.
> > + */
> > +int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
> > +               const char *path,
> > +               struct cifs_sb_info *cifs_sb,
> > +               struct cached_fid **cfid)
> > +{
> > +       struct cifs_ses *ses;
> > +       struct TCP_Server_Info *server;
> > +       struct cifs_open_parms oparms;
> > +       struct smb2_create_rsp *o_rsp = NULL;
> > +       struct smb2_query_info_rsp *qi_rsp = NULL;
> > +       int resp_buftype[2];
> > +       struct smb_rqst rqst[2];
> > +       struct kvec rsp_iov[2];
> > +       struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
> > +       struct kvec qi_iov[1];
> > +       int rc, flags = 0;
> > +       __le16 utf16_path = 0; /* Null - since an open of top of share */
> > +       u8 oplock = SMB2_OPLOCK_LEVEL_II;
> > +       struct cifs_fid *pfid;
> > +       struct dentry *dentry;
> > +
> > +       if (tcon == NULL || tcon->nohandlecache ||
> > +           is_smb1_server(tcon->ses->server))
> > +               return -ENOTSUPP;
> > +
> > +       ses = tcon->ses;
> > +       server = ses->server;
> > +
> > +       if (cifs_sb->root == NULL)
> > +               return -ENOENT;
> > +
> > +       if (strlen(path))
> > +               return -ENOENT;
> > +
> > +       dentry = cifs_sb->root;
> > +
> > +       mutex_lock(&tcon->cfid.fid_mutex);
> > +       if (tcon->cfid.is_valid) {
> > +               cifs_dbg(FYI, "found a cached root file handle\n");
> > +               *cfid = &tcon->cfid;
> > +               kref_get(&tcon->cfid.refcount);
> > +               mutex_unlock(&tcon->cfid.fid_mutex);
> > +               return 0;
> > +       }
> > +
> > +       /*
> > +        * We do not hold the lock for the open because in case
> > +        * SMB2_open needs to reconnect, it will end up calling
> > +        * cifs_mark_open_files_invalid() which takes the lock again
> > +        * thus causing a deadlock
> > +        */
> > +
> > +       mutex_unlock(&tcon->cfid.fid_mutex);
> > +
> > +       if (smb3_encryption_required(tcon))
> > +               flags |= CIFS_TRANSFORM_REQ;
> > +
> > +       if (!server->ops->new_lease_key)
> > +               return -EIO;
> > +
> > +       pfid = tcon->cfid.fid;
> > +       server->ops->new_lease_key(pfid);
> > +
> > +       memset(rqst, 0, sizeof(rqst));
> > +       resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
> > +       memset(rsp_iov, 0, sizeof(rsp_iov));
> > +
> > +       /* Open */
> > +       memset(&open_iov, 0, sizeof(open_iov));
> > +       rqst[0].rq_iov = open_iov;
> > +       rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
> > +
> > +       oparms.tcon = tcon;
> > +       oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
> > +       oparms.desired_access = FILE_READ_ATTRIBUTES;
> > +       oparms.disposition = FILE_OPEN;
> > +       oparms.fid = pfid;
> > +       oparms.reconnect = false;
> > +
> > +       rc = SMB2_open_init(tcon, server,
> > +                           &rqst[0], &oplock, &oparms, &utf16_path);
> > +       if (rc)
> > +               goto oshr_free;
> > +       smb2_set_next_command(tcon, &rqst[0]);
> > +
> > +       memset(&qi_iov, 0, sizeof(qi_iov));
> > +       rqst[1].rq_iov = qi_iov;
> > +       rqst[1].rq_nvec = 1;
> > +
> > +       rc = SMB2_query_info_init(tcon, server,
> > +                                 &rqst[1], COMPOUND_FID,
> > +                                 COMPOUND_FID, FILE_ALL_INFORMATION,
> > +                                 SMB2_O_INFO_FILE, 0,
> > +                                 sizeof(struct smb2_file_all_info) +
> > +                                 PATH_MAX * 2, 0, NULL);
> > +       if (rc)
> > +               goto oshr_free;
> > +
> > +       smb2_set_related(&rqst[1]);
> > +
> > +       rc = compound_send_recv(xid, ses, server,
> > +                               flags, 2, rqst,
> > +                               resp_buftype, rsp_iov);
> > +       mutex_lock(&tcon->cfid.fid_mutex);
> > +
> > +       /*
> > +        * Now we need to check again as the cached root might have
> > +        * been successfully re-opened from a concurrent process
> > +        */
> > +
> > +       if (tcon->cfid.is_valid) {
> > +               /* work was already done */
> > +
> > +               /* stash fids for close() later */
> > +               struct cifs_fid fid = {
> > +                       .persistent_fid = pfid->persistent_fid,
> > +                       .volatile_fid = pfid->volatile_fid,
> > +               };
> > +
> > +               /*
> > +                * caller expects this func to set the fid in cfid to valid
> > +                * cached root, so increment the refcount.
> > +                */
> > +               kref_get(&tcon->cfid.refcount);
> > +
> > +               mutex_unlock(&tcon->cfid.fid_mutex);
> > +
> > +               if (rc == 0) {
> > +                       /* close extra handle outside of crit sec */
> > +                       SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
> > +               }
> > +               rc = 0;
> > +               goto oshr_free;
> > +       }
> > +
> > +       /* Cached root is still invalid, continue normaly */
> > +
> > +       if (rc) {
> > +               if (rc == -EREMCHG) {
> > +                       tcon->need_reconnect = true;
> > +                       pr_warn_once("server share %s deleted\n",
> > +                                    tcon->treeName);
> > +               }
> > +               goto oshr_exit;
> > +       }
> > +
> > +       atomic_inc(&tcon->num_remote_opens);
> > +
> > +       o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
> > +       oparms.fid->persistent_fid = o_rsp->PersistentFileId;
> > +       oparms.fid->volatile_fid = o_rsp->VolatileFileId;
> > +#ifdef CONFIG_CIFS_DEBUG2
> > +       oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
> > +#endif /* CIFS_DEBUG2 */
> > +
> > +       tcon->cfid.tcon = tcon;
> > +       tcon->cfid.is_valid = true;
> > +       tcon->cfid.dentry = dentry;
> > +       dget(dentry);
> > +       kref_init(&tcon->cfid.refcount);
> > +
> > +       /* BB TBD check to see if oplock level check can be removed below */
> > +       if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
> > +               /*
> > +                * See commit 2f94a3125b87. Increment the refcount when we
> > +                * get a lease for root, release it if lease break occurs
> > +                */
> > +               kref_get(&tcon->cfid.refcount);
> > +               tcon->cfid.has_lease = true;
> > +               smb2_parse_contexts(server, o_rsp,
> > +                               &oparms.fid->epoch,
> > +                                   oparms.fid->lease_key, &oplock,
> > +                                   NULL, NULL);
> > +       } else
> > +               goto oshr_exit;
> > +
> > +       qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
> > +       if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
> > +               goto oshr_exit;
> > +       if (!smb2_validate_and_copy_iov(
> > +                               le16_to_cpu(qi_rsp->OutputBufferOffset),
> > +                               sizeof(struct smb2_file_all_info),
> > +                               &rsp_iov[1], sizeof(struct smb2_file_all_info),
> > +                               (char *)&tcon->cfid.file_all_info))
> > +               tcon->cfid.file_all_info_is_valid = true;
> > +       tcon->cfid.time = jiffies;
> > +
> > +
> > +oshr_exit:
> > +       mutex_unlock(&tcon->cfid.fid_mutex);
> > +oshr_free:
> > +       SMB2_open_free(&rqst[0]);
> > +       SMB2_query_info_free(&rqst[1]);
> > +       free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
> > +       free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
> > +       if (rc == 0) {
> > +               *cfid = &tcon->cfid;
> > +}
> > +       return rc;
> > +}
> > +
> > +int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
> > +                             struct dentry *dentry,
> > +                             struct cached_fid **cfid)
> > +{
> > +       mutex_lock(&tcon->cfid.fid_mutex);
> > +       if (tcon->cfid.dentry == dentry) {
> > +               cifs_dbg(FYI, "found a cached root file handle by dentry\n");
> > +               *cfid = &tcon->cfid;
> > +               kref_get(&tcon->cfid.refcount);
> > +               mutex_unlock(&tcon->cfid.fid_mutex);
> > +               return 0;
> > +       }
> > +       mutex_unlock(&tcon->cfid.fid_mutex);
> > +       return -ENOENT;
> > +}
> > +
> > +static void
> > +smb2_close_cached_fid(struct kref *ref)
> > +{
> > +       struct cached_fid *cfid = container_of(ref, struct cached_fid,
> > +                                              refcount);
> > +       struct cached_dirent *dirent, *q;
> > +
> > +       if (cfid->is_valid) {
> > +               cifs_dbg(FYI, "clear cached root file handle\n");
> > +               SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
> > +                          cfid->fid->volatile_fid);
> > +       }
> > +
> > +       /*
> > +        * We only check validity above to send SMB2_close,
> > +        * but we still need to invalidate these entries
> > +        * when this function is called
> > +        */
> > +       cfid->is_valid = false;
> > +       cfid->file_all_info_is_valid = false;
> > +       cfid->has_lease = false;
> > +       if (cfid->dentry) {
> > +               dput(cfid->dentry);
> > +               cfid->dentry = NULL;
> > +       }
> > +       /*
> > +        * Delete all cached dirent names
> > +        */
> > +       mutex_lock(&cfid->dirents.de_mutex);
> > +       list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
> > +               list_del(&dirent->entry);
> > +               kfree(dirent->name);
> > +               kfree(dirent);
> > +       }
> > +       cfid->dirents.is_valid = 0;
> > +       cfid->dirents.is_failed = 0;
> > +       cfid->dirents.ctx = NULL;
> > +       cfid->dirents.pos = 0;
> > +       mutex_unlock(&cfid->dirents.de_mutex);
> > +
> > +}
> > +
> > +void close_cached_dir(struct cached_fid *cfid)
> > +{
> > +       mutex_lock(&cfid->fid_mutex);
> > +       kref_put(&cfid->refcount, smb2_close_cached_fid);
> > +       mutex_unlock(&cfid->fid_mutex);
> > +}
> > +
> > +void close_cached_dir_lease_locked(struct cached_fid *cfid)
> > +{
> > +       if (cfid->has_lease) {
> > +               cfid->has_lease = false;
> > +               kref_put(&cfid->refcount, smb2_close_cached_fid);
> > +       }
> > +}
> > +
> > +void close_cached_dir_lease(struct cached_fid *cfid)
> > +{
> > +       mutex_lock(&cfid->fid_mutex);
> > +       close_cached_dir_lease_locked(cfid);
> > +       mutex_unlock(&cfid->fid_mutex);
> > +}
> > +
> > +/*
> > + * Called from cifs_kill_sb when we unmount a share
> > + */
> > +void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
> > +{
> > +       struct rb_root *root = &cifs_sb->tlink_tree;
> > +       struct rb_node *node;
> > +       struct cached_fid *cfid;
> > +       struct cifs_tcon *tcon;
> > +       struct tcon_link *tlink;
> > +
> > +       for (node = rb_first(root); node; node = rb_next(node)) {
> > +               tlink = rb_entry(node, struct tcon_link, tl_rbnode);
> > +               tcon = tlink_tcon(tlink);
> > +               if (IS_ERR(tcon))
> > +                       continue;
> > +               cfid = &tcon->cfid;
> > +               mutex_lock(&cfid->fid_mutex);
> > +               if (cfid->dentry) {
> > +                       dput(cfid->dentry);
> > +                       cfid->dentry = NULL;
> > +               }
> > +               mutex_unlock(&cfid->fid_mutex);
> > +       }
> > +}
> > +
> > +/*
> > + * Invalidate and close all cached dirs when a TCON has been reset
> > + * due to a session loss.
> > + */
> > +void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
> > +{
> > +       mutex_lock(&tcon->cfid.fid_mutex);
> > +       tcon->cfid.is_valid = false;
> > +       /* cached handle is not valid, so SMB2_CLOSE won't be sent below */
> > +       close_cached_dir_lease_locked(&tcon->cfid);
> > +       memset(tcon->cfid.fid, 0, sizeof(struct cifs_fid));
> > +       mutex_unlock(&tcon->cfid.fid_mutex);
> > +}
> > +
> > +static void
> > +smb2_cached_lease_break(struct work_struct *work)
> > +{
> > +       struct cached_fid *cfid = container_of(work,
> > +                               struct cached_fid, lease_break);
> > +
> > +       close_cached_dir_lease(cfid);
> > +}
> > +
> > +int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
> > +{
> > +       if (tcon->cfid.is_valid &&
> > +           !memcmp(lease_key,
> > +                   tcon->cfid.fid->lease_key,
> > +                   SMB2_LEASE_KEY_SIZE)) {
> > +               tcon->cfid.time = 0;
> > +               INIT_WORK(&tcon->cfid.lease_break,
> > +                         smb2_cached_lease_break);
> > +               queue_work(cifsiod_wq,
> > +                          &tcon->cfid.lease_break);
> > +               spin_unlock(&cifs_tcp_ses_lock);
> > +               return true;
> > +       }
> > +       return false;
> > +}
> > diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
> > new file mode 100644
> > index 000000000000..3731c755eea5
> > --- /dev/null
> > +++ b/fs/cifs/cached_dir.h
> > @@ -0,0 +1,26 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + *  Functions to handle the cached directory entries
> > + *
> > + *  Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
> > + */
> > +
> > +#ifndef _CACHED_DIR_H
> > +#define _CACHED_DIR_H
> > +
> > +
> > +extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
> > +                          const char *path,
> > +                          struct cifs_sb_info *cifs_sb,
> > +                          struct cached_fid **cfid);
> > +extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
> > +                                    struct dentry *dentry,
> > +                                    struct cached_fid **cfid);
> > +extern void close_cached_dir(struct cached_fid *cfid);
> > +extern void close_cached_dir_lease(struct cached_fid *cfid);
> > +extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
> > +extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
> > +extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
> > +extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
> > +
> > +#endif                 /* _CACHED_DIR_H */
> > diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
> > index f909d9e9faaa..615fbe2bff3c 100644
> > --- a/fs/cifs/cifsfs.c
> > +++ b/fs/cifs/cifsfs.c
> > @@ -46,6 +46,7 @@
> >  #include "netlink.h"
> >  #endif
> >  #include "fs_context.h"
> > +#include "cached_dir.h"
> >
> >  /*
> >   * DOS dates from 1980/1/1 through 2107/12/31
> > @@ -264,30 +265,13 @@ cifs_read_super(struct super_block *sb)
> >  static void cifs_kill_sb(struct super_block *sb)
> >  {
> >         struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
> > -       struct cifs_tcon *tcon;
> > -       struct cached_fid *cfid;
> > -       struct rb_root *root = &cifs_sb->tlink_tree;
> > -       struct rb_node *node;
> > -       struct tcon_link *tlink;
> >
> >         /*
> >          * We ned to release all dentries for the cached directories
> >          * before we kill the sb.
> >          */
> >         if (cifs_sb->root) {
> > -               for (node = rb_first(root); node; node = rb_next(node)) {
> > -                       tlink = rb_entry(node, struct tcon_link, tl_rbnode);
> > -                       tcon = tlink_tcon(tlink);
> > -                       if (IS_ERR(tcon))
> > -                               continue;
> > -                       cfid = &tcon->crfid;
> > -                       mutex_lock(&cfid->fid_mutex);
> > -                       if (cfid->dentry) {
> > -                               dput(cfid->dentry);
> > -                               cfid->dentry = NULL;
> > -                       }
> > -                       mutex_unlock(&cfid->fid_mutex);
> > -               }
> > +               close_all_cached_dirs(cifs_sb);
> >
> >                 /* finally release root dentry */
> >                 dput(cifs_sb->root);
> > diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> > index 9b7f409bfc8c..657fabb9067b 100644
> > --- a/fs/cifs/cifsglob.h
> > +++ b/fs/cifs/cifsglob.h
> > @@ -1253,7 +1253,7 @@ struct cifs_tcon {
> >         struct fscache_volume *fscache; /* cookie for share */
> >  #endif
> >         struct list_head pending_opens; /* list of incomplete opens */
> > -       struct cached_fid crfid; /* Cached root fid */
> > +       struct cached_fid cfid; /* Cached root fid */
> >         /* BB add field for back pointer to sb struct(s)? */
> >  #ifdef CONFIG_CIFS_DFS_UPCALL
> >         struct list_head ulist; /* cache update list */
> > diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> > index d59aebefa71c..881bf112d6ae 100644
> > --- a/fs/cifs/cifsproto.h
> > +++ b/fs/cifs/cifsproto.h
> > @@ -599,7 +599,6 @@ enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
> >  struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
> >  void cifs_aio_ctx_release(struct kref *refcount);
> >  int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
> > -void smb2_cached_lease_break(struct work_struct *work);
> >
> >  int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
> >                     struct sdesc **sdesc);
> > diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
> > index 9ed21752f2df..78dfadd729fe 100644
> > --- a/fs/cifs/cifssmb.c
> > +++ b/fs/cifs/cifssmb.c
> > @@ -35,6 +35,7 @@
> >  #ifdef CONFIG_CIFS_DFS_UPCALL
> >  #include "dfs_cache.h"
> >  #endif
> > +#include "cached_dir.h"
> >
> >  #ifdef CONFIG_CIFS_POSIX
> >  static struct {
> > @@ -91,12 +92,7 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
> >         }
> >         spin_unlock(&tcon->open_file_lock);
> >
> > -       mutex_lock(&tcon->crfid.fid_mutex);
> > -       tcon->crfid.is_valid = false;
> > -       /* cached handle is not valid, so SMB2_CLOSE won't be sent below */
> > -       close_cached_dir_lease_locked(&tcon->crfid);
> > -       memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
> > -       mutex_unlock(&tcon->crfid.fid_mutex);
> > +       invalidate_all_cached_dirs(tcon);
> >
> >         spin_lock(&cifs_tcp_ses_lock);
> >         if (tcon->status == TID_IN_FILES_INVALIDATE)
> > diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
> > index 3ad303dd5e5a..7714f47d199b 100644
> > --- a/fs/cifs/inode.c
> > +++ b/fs/cifs/inode.c
> > @@ -25,6 +25,7 @@
> >  #include "fscache.h"
> >  #include "fs_context.h"
> >  #include "cifs_ioctl.h"
> > +#include "cached_dir.h"
> >
> >  static void cifs_set_ops(struct inode *inode)
> >  {
> > diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
> > index 16168ebd1a62..fa1a03ddbbe2 100644
> > --- a/fs/cifs/misc.c
> > +++ b/fs/cifs/misc.c
> > @@ -115,13 +115,13 @@ tconInfoAlloc(void)
> >         ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
> >         if (!ret_buf)
> >                 return NULL;
> > -       ret_buf->crfid.fid = kzalloc(sizeof(*ret_buf->crfid.fid), GFP_KERNEL);
> > -       if (!ret_buf->crfid.fid) {
> > +       ret_buf->cfid.fid = kzalloc(sizeof(*ret_buf->cfid.fid), GFP_KERNEL);
> > +       if (!ret_buf->cfid.fid) {
> >                 kfree(ret_buf);
> >                 return NULL;
> >         }
> > -       INIT_LIST_HEAD(&ret_buf->crfid.dirents.entries);
> > -       mutex_init(&ret_buf->crfid.dirents.de_mutex);
> > +       INIT_LIST_HEAD(&ret_buf->cfid.dirents.entries);
> > +       mutex_init(&ret_buf->cfid.dirents.de_mutex);
> >
> >         atomic_inc(&tconInfoAllocCount);
> >         ret_buf->status = TID_NEW;
> > @@ -129,7 +129,7 @@ tconInfoAlloc(void)
> >         INIT_LIST_HEAD(&ret_buf->openFileList);
> >         INIT_LIST_HEAD(&ret_buf->tcon_list);
> >         spin_lock_init(&ret_buf->open_file_lock);
> > -       mutex_init(&ret_buf->crfid.fid_mutex);
> > +       mutex_init(&ret_buf->cfid.fid_mutex);
> >         spin_lock_init(&ret_buf->stat_lock);
> >         atomic_set(&ret_buf->num_local_opens, 0);
> >         atomic_set(&ret_buf->num_remote_opens, 0);
> > @@ -147,7 +147,7 @@ tconInfoFree(struct cifs_tcon *buf_to_free)
> >         atomic_dec(&tconInfoAllocCount);
> >         kfree(buf_to_free->nativeFileSystem);
> >         kfree_sensitive(buf_to_free->password);
> > -       kfree(buf_to_free->crfid.fid);
> > +       kfree(buf_to_free->cfid.fid);
> >         kfree(buf_to_free);
> >  }
> >
> > diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
> > index 384cabdf47ca..a06072ae6c7e 100644
> > --- a/fs/cifs/readdir.c
> > +++ b/fs/cifs/readdir.c
> > @@ -21,6 +21,7 @@
> >  #include "cifsfs.h"
> >  #include "smb2proto.h"
> >  #include "fs_context.h"
> > +#include "cached_dir.h"
> >
> >  /*
> >   * To be safe - for UCS to UTF-8 with strings loaded with the rare long
> > diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
> > index 8571a459c710..f6f9fc3f2e2d 100644
> > --- a/fs/cifs/smb2inode.c
> > +++ b/fs/cifs/smb2inode.c
> > @@ -23,6 +23,7 @@
> >  #include "smb2glob.h"
> >  #include "smb2pdu.h"
> >  #include "smb2proto.h"
> > +#include "cached_dir.h"
> >
> >  static void
> >  free_set_inf_compound(struct smb_rqst *rqst)
> > @@ -518,9 +519,9 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
> >                 rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
> >         /* If it is a root and its handle is cached then use it */
> >         if (!rc) {
> > -               if (tcon->crfid.file_all_info_is_valid) {
> > +               if (tcon->cfid.file_all_info_is_valid) {
> >                         move_smb2_info_to_cifs(data,
> > -                                              &tcon->crfid.file_all_info);
> > +                                              &tcon->cfid.file_all_info);
> >                 } else {
> >                         rc = SMB2_query_info(xid, tcon,
> >                                              cfid->fid->persistent_fid,
> > diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
> > index db0f27fd373b..d3d9174ddd7c 100644
> > --- a/fs/cifs/smb2misc.c
> > +++ b/fs/cifs/smb2misc.c
> > @@ -16,6 +16,7 @@
> >  #include "smb2status.h"
> >  #include "smb2glob.h"
> >  #include "nterr.h"
> > +#include "cached_dir.h"
> >
> >  static int
> >  check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
> > @@ -639,18 +640,8 @@ smb2_is_valid_lease_break(char *buffer)
> >                                 }
> >                                 spin_unlock(&tcon->open_file_lock);
> >
> > -                               if (tcon->crfid.is_valid &&
> > -                                   !memcmp(rsp->LeaseKey,
> > -                                           tcon->crfid.fid->lease_key,
> > -                                           SMB2_LEASE_KEY_SIZE)) {
> > -                                       tcon->crfid.time = 0;
> > -                                       INIT_WORK(&tcon->crfid.lease_break,
> > -                                                 smb2_cached_lease_break);
> > -                                       queue_work(cifsiod_wq,
> > -                                                  &tcon->crfid.lease_break);
> > -                                       spin_unlock(&cifs_tcp_ses_lock);
> > +                               if (cached_dir_lease_break(tcon, rsp->LeaseKey))
> >                                         return true;
> > -                               }
> >                         }
> >                 }
> >         }
> > diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> > index aa4c1d403708..01aafedc477e 100644
> > --- a/fs/cifs/smb2ops.c
> > +++ b/fs/cifs/smb2ops.c
> > @@ -27,6 +27,7 @@
> >  #include "smbdirect.h"
> >  #include "fscache.h"
> >  #include "fs_context.h"
> > +#include "cached_dir.h"
> >
> >  /* Change credits for different ops and return the total number of credits */
> >  static int
> > @@ -701,300 +702,6 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
> >         return rc;
> >  }
> >
> > -static void
> > -smb2_close_cached_fid(struct kref *ref)
> > -{
> > -       struct cached_fid *cfid = container_of(ref, struct cached_fid,
> > -                                              refcount);
> > -       struct cached_dirent *dirent, *q;
> > -
> > -       if (cfid->is_valid) {
> > -               cifs_dbg(FYI, "clear cached root file handle\n");
> > -               SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
> > -                          cfid->fid->volatile_fid);
> > -       }
> > -
> > -       /*
> > -        * We only check validity above to send SMB2_close,
> > -        * but we still need to invalidate these entries
> > -        * when this function is called
> > -        */
> > -       cfid->is_valid = false;
> > -       cfid->file_all_info_is_valid = false;
> > -       cfid->has_lease = false;
> > -       if (cfid->dentry) {
> > -               dput(cfid->dentry);
> > -               cfid->dentry = NULL;
> > -       }
> > -       /*
> > -        * Delete all cached dirent names
> > -        */
> > -       mutex_lock(&cfid->dirents.de_mutex);
> > -       list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
> > -               list_del(&dirent->entry);
> > -               kfree(dirent->name);
> > -               kfree(dirent);
> > -       }
> > -       cfid->dirents.is_valid = 0;
> > -       cfid->dirents.is_failed = 0;
> > -       cfid->dirents.ctx = NULL;
> > -       cfid->dirents.pos = 0;
> > -       mutex_unlock(&cfid->dirents.de_mutex);
> > -
> > -}
> > -
> > -void close_cached_dir(struct cached_fid *cfid)
> > -{
> > -       mutex_lock(&cfid->fid_mutex);
> > -       kref_put(&cfid->refcount, smb2_close_cached_fid);
> > -       mutex_unlock(&cfid->fid_mutex);
> > -}
> > -
> > -void close_cached_dir_lease_locked(struct cached_fid *cfid)
> > -{
> > -       if (cfid->has_lease) {
> > -               cfid->has_lease = false;
> > -               kref_put(&cfid->refcount, smb2_close_cached_fid);
> > -       }
> > -}
> > -
> > -void close_cached_dir_lease(struct cached_fid *cfid)
> > -{
> > -       mutex_lock(&cfid->fid_mutex);
> > -       close_cached_dir_lease_locked(cfid);
> > -       mutex_unlock(&cfid->fid_mutex);
> > -}
> > -
> > -void
> > -smb2_cached_lease_break(struct work_struct *work)
> > -{
> > -       struct cached_fid *cfid = container_of(work,
> > -                               struct cached_fid, lease_break);
> > -
> > -       close_cached_dir_lease(cfid);
> > -}
> > -
> > -/*
> > - * Open the and cache a directory handle.
> > - * Only supported for the root handle.
> > - * If error then *cfid is not initialized.
> > - */
> > -int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
> > -               const char *path,
> > -               struct cifs_sb_info *cifs_sb,
> > -               struct cached_fid **cfid)
> > -{
> > -       struct cifs_ses *ses;
> > -       struct TCP_Server_Info *server;
> > -       struct cifs_open_parms oparms;
> > -       struct smb2_create_rsp *o_rsp = NULL;
> > -       struct smb2_query_info_rsp *qi_rsp = NULL;
> > -       int resp_buftype[2];
> > -       struct smb_rqst rqst[2];
> > -       struct kvec rsp_iov[2];
> > -       struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
> > -       struct kvec qi_iov[1];
> > -       int rc, flags = 0;
> > -       __le16 utf16_path = 0; /* Null - since an open of top of share */
> > -       u8 oplock = SMB2_OPLOCK_LEVEL_II;
> > -       struct cifs_fid *pfid;
> > -       struct dentry *dentry;
> > -
> > -       if (tcon == NULL || tcon->nohandlecache ||
> > -           is_smb1_server(tcon->ses->server))
> > -               return -ENOTSUPP;
> > -
> > -       ses = tcon->ses;
> > -       server = ses->server;
> > -
> > -       if (cifs_sb->root == NULL)
> > -               return -ENOENT;
> > -
> > -       if (strlen(path))
> > -               return -ENOENT;
> > -
> > -       dentry = cifs_sb->root;
> > -
> > -       mutex_lock(&tcon->crfid.fid_mutex);
> > -       if (tcon->crfid.is_valid) {
> > -               cifs_dbg(FYI, "found a cached root file handle\n");
> > -               *cfid = &tcon->crfid;
> > -               kref_get(&tcon->crfid.refcount);
> > -               mutex_unlock(&tcon->crfid.fid_mutex);
> > -               return 0;
> > -       }
> > -
> > -       /*
> > -        * We do not hold the lock for the open because in case
> > -        * SMB2_open needs to reconnect, it will end up calling
> > -        * cifs_mark_open_files_invalid() which takes the lock again
> > -        * thus causing a deadlock
> > -        */
> > -
> > -       mutex_unlock(&tcon->crfid.fid_mutex);
> > -
> > -       if (smb3_encryption_required(tcon))
> > -               flags |= CIFS_TRANSFORM_REQ;
> > -
> > -       if (!server->ops->new_lease_key)
> > -               return -EIO;
> > -
> > -       pfid = tcon->crfid.fid;
> > -       server->ops->new_lease_key(pfid);
> > -
> > -       memset(rqst, 0, sizeof(rqst));
> > -       resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
> > -       memset(rsp_iov, 0, sizeof(rsp_iov));
> > -
> > -       /* Open */
> > -       memset(&open_iov, 0, sizeof(open_iov));
> > -       rqst[0].rq_iov = open_iov;
> > -       rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
> > -
> > -       oparms.tcon = tcon;
> > -       oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
> > -       oparms.desired_access = FILE_READ_ATTRIBUTES;
> > -       oparms.disposition = FILE_OPEN;
> > -       oparms.fid = pfid;
> > -       oparms.reconnect = false;
> > -
> > -       rc = SMB2_open_init(tcon, server,
> > -                           &rqst[0], &oplock, &oparms, &utf16_path);
> > -       if (rc)
> > -               goto oshr_free;
> > -       smb2_set_next_command(tcon, &rqst[0]);
> > -
> > -       memset(&qi_iov, 0, sizeof(qi_iov));
> > -       rqst[1].rq_iov = qi_iov;
> > -       rqst[1].rq_nvec = 1;
> > -
> > -       rc = SMB2_query_info_init(tcon, server,
> > -                                 &rqst[1], COMPOUND_FID,
> > -                                 COMPOUND_FID, FILE_ALL_INFORMATION,
> > -                                 SMB2_O_INFO_FILE, 0,
> > -                                 sizeof(struct smb2_file_all_info) +
> > -                                 PATH_MAX * 2, 0, NULL);
> > -       if (rc)
> > -               goto oshr_free;
> > -
> > -       smb2_set_related(&rqst[1]);
> > -
> > -       rc = compound_send_recv(xid, ses, server,
> > -                               flags, 2, rqst,
> > -                               resp_buftype, rsp_iov);
> > -       mutex_lock(&tcon->crfid.fid_mutex);
> > -
> > -       /*
> > -        * Now we need to check again as the cached root might have
> > -        * been successfully re-opened from a concurrent process
> > -        */
> > -
> > -       if (tcon->crfid.is_valid) {
> > -               /* work was already done */
> > -
> > -               /* stash fids for close() later */
> > -               struct cifs_fid fid = {
> > -                       .persistent_fid = pfid->persistent_fid,
> > -                       .volatile_fid = pfid->volatile_fid,
> > -               };
> > -
> > -               /*
> > -                * caller expects this func to set the fid in crfid to valid
> > -                * cached root, so increment the refcount.
> > -                */
> > -               kref_get(&tcon->crfid.refcount);
> > -
> > -               mutex_unlock(&tcon->crfid.fid_mutex);
> > -
> > -               if (rc == 0) {
> > -                       /* close extra handle outside of crit sec */
> > -                       SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
> > -               }
> > -               rc = 0;
> > -               goto oshr_free;
> > -       }
> > -
> > -       /* Cached root is still invalid, continue normaly */
> > -
> > -       if (rc) {
> > -               if (rc == -EREMCHG) {
> > -                       tcon->need_reconnect = true;
> > -                       pr_warn_once("server share %s deleted\n",
> > -                                    tcon->treeName);
> > -               }
> > -               goto oshr_exit;
> > -       }
> > -
> > -       atomic_inc(&tcon->num_remote_opens);
> > -
> > -       o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
> > -       oparms.fid->persistent_fid = o_rsp->PersistentFileId;
> > -       oparms.fid->volatile_fid = o_rsp->VolatileFileId;
> > -#ifdef CONFIG_CIFS_DEBUG2
> > -       oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
> > -#endif /* CIFS_DEBUG2 */
> > -
> > -       tcon->crfid.tcon = tcon;
> > -       tcon->crfid.is_valid = true;
> > -       tcon->crfid.dentry = dentry;
> > -       dget(dentry);
> > -       kref_init(&tcon->crfid.refcount);
> > -
> > -       /* BB TBD check to see if oplock level check can be removed below */
> > -       if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
> > -               /*
> > -                * See commit 2f94a3125b87. Increment the refcount when we
> > -                * get a lease for root, release it if lease break occurs
> > -                */
> > -               kref_get(&tcon->crfid.refcount);
> > -               tcon->crfid.has_lease = true;
> > -               smb2_parse_contexts(server, o_rsp,
> > -                               &oparms.fid->epoch,
> > -                                   oparms.fid->lease_key, &oplock,
> > -                                   NULL, NULL);
> > -       } else
> > -               goto oshr_exit;
> > -
> > -       qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
> > -       if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
> > -               goto oshr_exit;
> > -       if (!smb2_validate_and_copy_iov(
> > -                               le16_to_cpu(qi_rsp->OutputBufferOffset),
> > -                               sizeof(struct smb2_file_all_info),
> > -                               &rsp_iov[1], sizeof(struct smb2_file_all_info),
> > -                               (char *)&tcon->crfid.file_all_info))
> > -               tcon->crfid.file_all_info_is_valid = true;
> > -       tcon->crfid.time = jiffies;
> > -
> > -
> > -oshr_exit:
> > -       mutex_unlock(&tcon->crfid.fid_mutex);
> > -oshr_free:
> > -       SMB2_open_free(&rqst[0]);
> > -       SMB2_query_info_free(&rqst[1]);
> > -       free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
> > -       free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
> > -       if (rc == 0)
> > -               *cfid = &tcon->crfid;
> > -       return rc;
> > -}
> > -
> > -int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
> > -                             struct dentry *dentry,
> > -                             struct cached_fid **cfid)
> > -{
> > -       mutex_lock(&tcon->crfid.fid_mutex);
> > -       if (tcon->crfid.dentry == dentry) {
> > -               cifs_dbg(FYI, "found a cached root file handle by dentry\n");
> > -               *cfid = &tcon->crfid;
> > -               kref_get(&tcon->crfid.refcount);
> > -               mutex_unlock(&tcon->crfid.fid_mutex);
> > -               return 0;
> > -       }
> > -       mutex_unlock(&tcon->crfid.fid_mutex);
> > -       return -ENOENT;
> > -}
> > -
> >  static void
> >  smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
> >               struct cifs_sb_info *cifs_sb)
> > @@ -1077,7 +784,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
> >         struct cifs_open_parms oparms;
> >         struct cifs_fid fid;
> >
> > -       if ((*full_path == 0) && tcon->crfid.is_valid)
> > +       if ((*full_path == 0) && tcon->cfid.is_valid)
> >                 return 0;
> >
> >         utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
> > diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> > index 295ee8b88055..9ee1b6225619 100644
> > --- a/fs/cifs/smb2pdu.c
> > +++ b/fs/cifs/smb2pdu.c
> > @@ -39,6 +39,7 @@
> >  #ifdef CONFIG_CIFS_DFS_UPCALL
> >  #include "dfs_cache.h"
> >  #endif
> > +#include "cached_dir.h"
> >
> >  /*
> >   *  The following table defines the expected "StructureSize" of SMB2 requests
> > @@ -1978,7 +1979,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
> >         }
> >         spin_unlock(&ses->chan_lock);
> >
> > -       close_cached_dir_lease(&tcon->crfid);
> > +       close_cached_dir_lease(&tcon->cfid);
> >
> >         rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server,
> >                                  (void **) &req,
> > diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
> > index a69f1eed1cfe..51c5bf4a338a 100644
> > --- a/fs/cifs/smb2proto.h
> > +++ b/fs/cifs/smb2proto.h
> > @@ -54,16 +54,6 @@ extern bool smb2_is_valid_oplock_break(char *buffer,
> >  extern int smb3_handle_read_data(struct TCP_Server_Info *server,
> >                                  struct mid_q_entry *mid);
> >
> > -extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
> > -                          const char *path,
> > -                          struct cifs_sb_info *cifs_sb,
> > -                          struct cached_fid **cfid);
> > -extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
> > -                                    struct dentry *dentry,
> > -                                    struct cached_fid **cfid);
> > -extern void close_cached_dir(struct cached_fid *cfid);
> > -extern void close_cached_dir_lease(struct cached_fid *cfid);
> > -extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
> >  extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
> >                                    struct smb2_file_all_info *src);
> >  extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
> > --
> > 2.35.3
> >
>
>
> --
> Thanks,
>
> Steve



-- 
Thanks,

Steve

[-- Attachment #2: 0001-cifs-Move-cached-dir-functions-into-a-separate-file.patch --]
[-- Type: text/x-patch, Size: 29663 bytes --]

From 520d1609762e0383a25b77e414abc90adb5e5521 Mon Sep 17 00:00:00 2001
From: Ronnie Sahlberg <lsahlber@redhat.com>
Date: Wed, 10 Aug 2022 22:00:08 -0500
Subject: [PATCH] cifs: Move cached-dir functions into a separate file

Also rename crfid to cfid to have consistent naming for this variable.

This commit does not change any logic.

Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/Makefile     |   2 +-
 fs/cifs/cached_dir.c | 362 +++++++++++++++++++++++++++++++++++++++++++
 fs/cifs/cached_dir.h |  26 ++++
 fs/cifs/cifsfs.c     |  20 +--
 fs/cifs/cifsglob.h   |   2 +-
 fs/cifs/cifsproto.h  |   1 -
 fs/cifs/file.c       |   9 +-
 fs/cifs/inode.c      |   1 +
 fs/cifs/misc.c       |  12 +-
 fs/cifs/readdir.c    |   1 +
 fs/cifs/smb2inode.c  |   5 +-
 fs/cifs/smb2misc.c   |  11 +-
 fs/cifs/smb2ops.c    | 297 +----------------------------------
 fs/cifs/smb2pdu.c    |   3 +-
 fs/cifs/smb2proto.h  |  10 --
 15 files changed, 411 insertions(+), 351 deletions(-)
 create mode 100644 fs/cifs/cached_dir.c
 create mode 100644 fs/cifs/cached_dir.h

diff --git a/fs/cifs/Makefile b/fs/cifs/Makefile
index e882e912a517..7c9785973f49 100644
--- a/fs/cifs/Makefile
+++ b/fs/cifs/Makefile
@@ -7,7 +7,7 @@ obj-$(CONFIG_CIFS) += cifs.o
 
 cifs-y := trace.o cifsfs.o cifs_debug.o connect.o dir.o file.o \
 	  inode.o link.o misc.o netmisc.o smbencrypt.o transport.o \
-	  cifs_unicode.o nterr.o cifsencrypt.o \
+	  cached_dir.o cifs_unicode.o nterr.o cifsencrypt.o \
 	  readdir.o ioctl.o sess.o export.o unc.o winucase.o \
 	  smb2ops.o smb2maperror.o smb2transport.o \
 	  smb2misc.o smb2pdu.o smb2inode.o smb2file.o cifsacl.o fs_context.o \
diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
new file mode 100644
index 000000000000..753b850b6f87
--- /dev/null
+++ b/fs/cifs/cached_dir.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Functions to handle the cached directory entries
+ *
+ *  Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
+ */
+
+#include "cifsglob.h"
+#include "cifsproto.h"
+#include "cifs_debug.h"
+#include "smb2proto.h"
+#include "cached_dir.h"
+
+/*
+ * Open the and cache a directory handle.
+ * If error then *cfid is not initialized.
+ */
+int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
+		const char *path,
+		struct cifs_sb_info *cifs_sb,
+		struct cached_fid **cfid)
+{
+	struct cifs_ses *ses;
+	struct TCP_Server_Info *server;
+	struct cifs_open_parms oparms;
+	struct smb2_create_rsp *o_rsp = NULL;
+	struct smb2_query_info_rsp *qi_rsp = NULL;
+	int resp_buftype[2];
+	struct smb_rqst rqst[2];
+	struct kvec rsp_iov[2];
+	struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
+	struct kvec qi_iov[1];
+	int rc, flags = 0;
+	__le16 utf16_path = 0; /* Null - since an open of top of share */
+	u8 oplock = SMB2_OPLOCK_LEVEL_II;
+	struct cifs_fid *pfid;
+	struct dentry *dentry;
+
+	if (tcon == NULL || tcon->nohandlecache ||
+	    is_smb1_server(tcon->ses->server))
+		return -EOPNOTSUPP;
+
+	ses = tcon->ses;
+	server = ses->server;
+
+	if (cifs_sb->root == NULL)
+		return -ENOENT;
+
+	if (strlen(path))
+		return -ENOENT;
+
+	dentry = cifs_sb->root;
+
+	mutex_lock(&tcon->cfid.fid_mutex);
+	if (tcon->cfid.is_valid) {
+		cifs_dbg(FYI, "found a cached root file handle\n");
+		*cfid = &tcon->cfid;
+		kref_get(&tcon->cfid.refcount);
+		mutex_unlock(&tcon->cfid.fid_mutex);
+		return 0;
+	}
+
+	/*
+	 * We do not hold the lock for the open because in case
+	 * SMB2_open needs to reconnect, it will end up calling
+	 * cifs_mark_open_files_invalid() which takes the lock again
+	 * thus causing a deadlock
+	 */
+
+	mutex_unlock(&tcon->cfid.fid_mutex);
+
+	if (smb3_encryption_required(tcon))
+		flags |= CIFS_TRANSFORM_REQ;
+
+	if (!server->ops->new_lease_key)
+		return -EIO;
+
+	pfid = tcon->cfid.fid;
+	server->ops->new_lease_key(pfid);
+
+	memset(rqst, 0, sizeof(rqst));
+	resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
+	memset(rsp_iov, 0, sizeof(rsp_iov));
+
+	/* Open */
+	memset(&open_iov, 0, sizeof(open_iov));
+	rqst[0].rq_iov = open_iov;
+	rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
+
+	oparms.tcon = tcon;
+	oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
+	oparms.desired_access = FILE_READ_ATTRIBUTES;
+	oparms.disposition = FILE_OPEN;
+	oparms.fid = pfid;
+	oparms.reconnect = false;
+
+	rc = SMB2_open_init(tcon, server,
+			    &rqst[0], &oplock, &oparms, &utf16_path);
+	if (rc)
+		goto oshr_free;
+	smb2_set_next_command(tcon, &rqst[0]);
+
+	memset(&qi_iov, 0, sizeof(qi_iov));
+	rqst[1].rq_iov = qi_iov;
+	rqst[1].rq_nvec = 1;
+
+	rc = SMB2_query_info_init(tcon, server,
+				  &rqst[1], COMPOUND_FID,
+				  COMPOUND_FID, FILE_ALL_INFORMATION,
+				  SMB2_O_INFO_FILE, 0,
+				  sizeof(struct smb2_file_all_info) +
+				  PATH_MAX * 2, 0, NULL);
+	if (rc)
+		goto oshr_free;
+
+	smb2_set_related(&rqst[1]);
+
+	rc = compound_send_recv(xid, ses, server,
+				flags, 2, rqst,
+				resp_buftype, rsp_iov);
+	mutex_lock(&tcon->cfid.fid_mutex);
+
+	/*
+	 * Now we need to check again as the cached root might have
+	 * been successfully re-opened from a concurrent process
+	 */
+
+	if (tcon->cfid.is_valid) {
+		/* work was already done */
+
+		/* stash fids for close() later */
+		struct cifs_fid fid = {
+			.persistent_fid = pfid->persistent_fid,
+			.volatile_fid = pfid->volatile_fid,
+		};
+
+		/*
+		 * caller expects this func to set the fid in cfid to valid
+		 * cached root, so increment the refcount.
+		 */
+		kref_get(&tcon->cfid.refcount);
+
+		mutex_unlock(&tcon->cfid.fid_mutex);
+
+		if (rc == 0) {
+			/* close extra handle outside of crit sec */
+			SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
+		}
+		rc = 0;
+		goto oshr_free;
+	}
+
+	/* Cached root is still invalid, continue normaly */
+
+	if (rc) {
+		if (rc == -EREMCHG) {
+			tcon->need_reconnect = true;
+			pr_warn_once("server share %s deleted\n",
+				     tcon->treeName);
+		}
+		goto oshr_exit;
+	}
+
+	atomic_inc(&tcon->num_remote_opens);
+
+	o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
+	oparms.fid->persistent_fid = o_rsp->PersistentFileId;
+	oparms.fid->volatile_fid = o_rsp->VolatileFileId;
+#ifdef CONFIG_CIFS_DEBUG2
+	oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
+#endif /* CIFS_DEBUG2 */
+
+	tcon->cfid.tcon = tcon;
+	tcon->cfid.is_valid = true;
+	tcon->cfid.dentry = dentry;
+	dget(dentry);
+	kref_init(&tcon->cfid.refcount);
+
+	/* BB TBD check to see if oplock level check can be removed below */
+	if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
+		/*
+		 * See commit 2f94a3125b87. Increment the refcount when we
+		 * get a lease for root, release it if lease break occurs
+		 */
+		kref_get(&tcon->cfid.refcount);
+		tcon->cfid.has_lease = true;
+		smb2_parse_contexts(server, o_rsp,
+				&oparms.fid->epoch,
+				    oparms.fid->lease_key, &oplock,
+				    NULL, NULL);
+	} else
+		goto oshr_exit;
+
+	qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
+	if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
+		goto oshr_exit;
+	if (!smb2_validate_and_copy_iov(
+				le16_to_cpu(qi_rsp->OutputBufferOffset),
+				sizeof(struct smb2_file_all_info),
+				&rsp_iov[1], sizeof(struct smb2_file_all_info),
+				(char *)&tcon->cfid.file_all_info))
+		tcon->cfid.file_all_info_is_valid = true;
+	tcon->cfid.time = jiffies;
+
+
+oshr_exit:
+	mutex_unlock(&tcon->cfid.fid_mutex);
+oshr_free:
+	SMB2_open_free(&rqst[0]);
+	SMB2_query_info_free(&rqst[1]);
+	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
+	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
+	if (rc == 0)
+		*cfid = &tcon->cfid;
+
+	return rc;
+}
+
+int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
+			      struct dentry *dentry,
+			      struct cached_fid **cfid)
+{
+	mutex_lock(&tcon->cfid.fid_mutex);
+	if (tcon->cfid.dentry == dentry) {
+		cifs_dbg(FYI, "found a cached root file handle by dentry\n");
+		*cfid = &tcon->cfid;
+		kref_get(&tcon->cfid.refcount);
+		mutex_unlock(&tcon->cfid.fid_mutex);
+		return 0;
+	}
+	mutex_unlock(&tcon->cfid.fid_mutex);
+	return -ENOENT;
+}
+
+static void
+smb2_close_cached_fid(struct kref *ref)
+{
+	struct cached_fid *cfid = container_of(ref, struct cached_fid,
+					       refcount);
+	struct cached_dirent *dirent, *q;
+
+	if (cfid->is_valid) {
+		cifs_dbg(FYI, "clear cached root file handle\n");
+		SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
+			   cfid->fid->volatile_fid);
+	}
+
+	/*
+	 * We only check validity above to send SMB2_close,
+	 * but we still need to invalidate these entries
+	 * when this function is called
+	 */
+	cfid->is_valid = false;
+	cfid->file_all_info_is_valid = false;
+	cfid->has_lease = false;
+	if (cfid->dentry) {
+		dput(cfid->dentry);
+		cfid->dentry = NULL;
+	}
+	/*
+	 * Delete all cached dirent names
+	 */
+	mutex_lock(&cfid->dirents.de_mutex);
+	list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
+		list_del(&dirent->entry);
+		kfree(dirent->name);
+		kfree(dirent);
+	}
+	cfid->dirents.is_valid = 0;
+	cfid->dirents.is_failed = 0;
+	cfid->dirents.ctx = NULL;
+	cfid->dirents.pos = 0;
+	mutex_unlock(&cfid->dirents.de_mutex);
+
+}
+
+void close_cached_dir(struct cached_fid *cfid)
+{
+	mutex_lock(&cfid->fid_mutex);
+	kref_put(&cfid->refcount, smb2_close_cached_fid);
+	mutex_unlock(&cfid->fid_mutex);
+}
+
+void close_cached_dir_lease_locked(struct cached_fid *cfid)
+{
+	if (cfid->has_lease) {
+		cfid->has_lease = false;
+		kref_put(&cfid->refcount, smb2_close_cached_fid);
+	}
+}
+
+void close_cached_dir_lease(struct cached_fid *cfid)
+{
+	mutex_lock(&cfid->fid_mutex);
+	close_cached_dir_lease_locked(cfid);
+	mutex_unlock(&cfid->fid_mutex);
+}
+
+/*
+ * Called from cifs_kill_sb when we unmount a share
+ */
+void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
+{
+	struct rb_root *root = &cifs_sb->tlink_tree;
+	struct rb_node *node;
+	struct cached_fid *cfid;
+	struct cifs_tcon *tcon;
+	struct tcon_link *tlink;
+
+	for (node = rb_first(root); node; node = rb_next(node)) {
+		tlink = rb_entry(node, struct tcon_link, tl_rbnode);
+		tcon = tlink_tcon(tlink);
+		if (IS_ERR(tcon))
+			continue;
+		cfid = &tcon->cfid;
+		mutex_lock(&cfid->fid_mutex);
+		if (cfid->dentry) {
+			dput(cfid->dentry);
+			cfid->dentry = NULL;
+		}
+		mutex_unlock(&cfid->fid_mutex);
+	}
+}
+
+/*
+ * Invalidate and close all cached dirs when a TCON has been reset
+ * due to a session loss.
+ */
+void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
+{
+	mutex_lock(&tcon->cfid.fid_mutex);
+	tcon->cfid.is_valid = false;
+	/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
+	close_cached_dir_lease_locked(&tcon->cfid);
+	memset(tcon->cfid.fid, 0, sizeof(struct cifs_fid));
+	mutex_unlock(&tcon->cfid.fid_mutex);
+}
+
+static void
+smb2_cached_lease_break(struct work_struct *work)
+{
+	struct cached_fid *cfid = container_of(work,
+				struct cached_fid, lease_break);
+
+	close_cached_dir_lease(cfid);
+}
+
+int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
+{
+	if (tcon->cfid.is_valid &&
+	    !memcmp(lease_key,
+		    tcon->cfid.fid->lease_key,
+		    SMB2_LEASE_KEY_SIZE)) {
+		tcon->cfid.time = 0;
+		INIT_WORK(&tcon->cfid.lease_break,
+			  smb2_cached_lease_break);
+		queue_work(cifsiod_wq,
+			   &tcon->cfid.lease_break);
+		return true;
+	}
+	return false;
+}
diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
new file mode 100644
index 000000000000..51c6b968f8b6
--- /dev/null
+++ b/fs/cifs/cached_dir.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Functions to handle the cached directory entries
+ *
+ *  Copyright (c) 2022, Ronnie Sahlberg <lsahlber@redhat.com>
+ */
+
+#ifndef _CACHED_DIR_H
+#define _CACHED_DIR_H
+
+
+extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
+			   const char *path,
+			   struct cifs_sb_info *cifs_sb,
+			   struct cached_fid **cfid);
+extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
+				     struct dentry *dentry,
+				     struct cached_fid **cfid);
+extern void close_cached_dir(struct cached_fid *cfid);
+extern void close_cached_dir_lease(struct cached_fid *cfid);
+extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
+extern void close_all_cached_dirs(struct cifs_sb_info *cifs_sb);
+extern void invalidate_all_cached_dirs(struct cifs_tcon *tcon);
+extern int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16]);
+
+#endif			/* _CACHED_DIR_H */
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 8849f0852110..945fb083cea7 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -46,6 +46,7 @@
 #include "netlink.h"
 #endif
 #include "fs_context.h"
+#include "cached_dir.h"
 
 /*
  * DOS dates from 1980/1/1 through 2107/12/31
@@ -283,30 +284,13 @@ cifs_read_super(struct super_block *sb)
 static void cifs_kill_sb(struct super_block *sb)
 {
 	struct cifs_sb_info *cifs_sb = CIFS_SB(sb);
-	struct cifs_tcon *tcon;
-	struct cached_fid *cfid;
-	struct rb_root *root = &cifs_sb->tlink_tree;
-	struct rb_node *node;
-	struct tcon_link *tlink;
 
 	/*
 	 * We ned to release all dentries for the cached directories
 	 * before we kill the sb.
 	 */
 	if (cifs_sb->root) {
-		for (node = rb_first(root); node; node = rb_next(node)) {
-			tlink = rb_entry(node, struct tcon_link, tl_rbnode);
-			tcon = tlink_tcon(tlink);
-			if (IS_ERR(tcon))
-				continue;
-			cfid = &tcon->crfid;
-			mutex_lock(&cfid->fid_mutex);
-			if (cfid->dentry) {
-				dput(cfid->dentry);
-				cfid->dentry = NULL;
-			}
-			mutex_unlock(&cfid->fid_mutex);
-		}
+		close_all_cached_dirs(cifs_sb);
 
 		/* finally release root dentry */
 		dput(cifs_sb->root);
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index a93024eaf251..8b82f13d11e0 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1257,7 +1257,7 @@ struct cifs_tcon {
 	struct fscache_volume *fscache;	/* cookie for share */
 #endif
 	struct list_head pending_opens;	/* list of incomplete opens */
-	struct cached_fid crfid; /* Cached root fid */
+	struct cached_fid cfid; /* Cached root fid */
 	/* BB add field for back pointer to sb struct(s)? */
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	struct list_head ulist; /* cache update list */
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index daaadffa2b88..87a77a684339 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -597,7 +597,6 @@ enum securityEnum cifs_select_sectype(struct TCP_Server_Info *,
 struct cifs_aio_ctx *cifs_aio_ctx_alloc(void);
 void cifs_aio_ctx_release(struct kref *refcount);
 int setup_aio_ctx_iter(struct cifs_aio_ctx *ctx, struct iov_iter *iter, int rw);
-void smb2_cached_lease_break(struct work_struct *work);
 
 int cifs_alloc_hash(const char *name, struct crypto_shash **shash,
 		    struct sdesc **sdesc);
diff --git a/fs/cifs/file.c b/fs/cifs/file.c
index 09975bd7d860..42f2639a1a66 100644
--- a/fs/cifs/file.c
+++ b/fs/cifs/file.c
@@ -34,6 +34,7 @@
 #include "smbdirect.h"
 #include "fs_context.h"
 #include "cifs_ioctl.h"
+#include "cached_dir.h"
 
 /*
  * Mark as invalid, all open files on tree connections since they
@@ -64,13 +65,7 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
 	}
 	spin_unlock(&tcon->open_file_lock);
 
-	mutex_lock(&tcon->crfid.fid_mutex);
-	tcon->crfid.is_valid = false;
-	/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
-	close_cached_dir_lease_locked(&tcon->crfid);
-	memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
-	mutex_unlock(&tcon->crfid.fid_mutex);
-
+	invalidate_all_cached_dirs(tcon);
 	spin_lock(&tcon->tc_lock);
 	if (tcon->status == TID_IN_FILES_INVALIDATE)
 		tcon->status = TID_NEED_TCON;
diff --git a/fs/cifs/inode.c b/fs/cifs/inode.c
index eeeaba3dec05..bac08c20f559 100644
--- a/fs/cifs/inode.c
+++ b/fs/cifs/inode.c
@@ -25,6 +25,7 @@
 #include "fscache.h"
 #include "fs_context.h"
 #include "cifs_ioctl.h"
+#include "cached_dir.h"
 
 static void cifs_set_ops(struct inode *inode)
 {
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 7a906067db04..ea0405cfaee3 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -116,13 +116,13 @@ tconInfoAlloc(void)
 	ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
 	if (!ret_buf)
 		return NULL;
-	ret_buf->crfid.fid = kzalloc(sizeof(*ret_buf->crfid.fid), GFP_KERNEL);
-	if (!ret_buf->crfid.fid) {
+	ret_buf->cfid.fid = kzalloc(sizeof(*ret_buf->cfid.fid), GFP_KERNEL);
+	if (!ret_buf->cfid.fid) {
 		kfree(ret_buf);
 		return NULL;
 	}
-	INIT_LIST_HEAD(&ret_buf->crfid.dirents.entries);
-	mutex_init(&ret_buf->crfid.dirents.de_mutex);
+	INIT_LIST_HEAD(&ret_buf->cfid.dirents.entries);
+	mutex_init(&ret_buf->cfid.dirents.de_mutex);
 
 	atomic_inc(&tconInfoAllocCount);
 	ret_buf->status = TID_NEW;
@@ -131,7 +131,7 @@ tconInfoAlloc(void)
 	INIT_LIST_HEAD(&ret_buf->openFileList);
 	INIT_LIST_HEAD(&ret_buf->tcon_list);
 	spin_lock_init(&ret_buf->open_file_lock);
-	mutex_init(&ret_buf->crfid.fid_mutex);
+	mutex_init(&ret_buf->cfid.fid_mutex);
 	spin_lock_init(&ret_buf->stat_lock);
 	atomic_set(&ret_buf->num_local_opens, 0);
 	atomic_set(&ret_buf->num_remote_opens, 0);
@@ -149,7 +149,7 @@ tconInfoFree(struct cifs_tcon *buf_to_free)
 	atomic_dec(&tconInfoAllocCount);
 	kfree(buf_to_free->nativeFileSystem);
 	kfree_sensitive(buf_to_free->password);
-	kfree(buf_to_free->crfid.fid);
+	kfree(buf_to_free->cfid.fid);
 	kfree(buf_to_free);
 }
 
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index 384cabdf47ca..a06072ae6c7e 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -21,6 +21,7 @@
 #include "cifsfs.h"
 #include "smb2proto.h"
 #include "fs_context.h"
+#include "cached_dir.h"
 
 /*
  * To be safe - for UCS to UTF-8 with strings loaded with the rare long
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 8571a459c710..f6f9fc3f2e2d 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -23,6 +23,7 @@
 #include "smb2glob.h"
 #include "smb2pdu.h"
 #include "smb2proto.h"
+#include "cached_dir.h"
 
 static void
 free_set_inf_compound(struct smb_rqst *rqst)
@@ -518,9 +519,9 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
 	/* If it is a root and its handle is cached then use it */
 	if (!rc) {
-		if (tcon->crfid.file_all_info_is_valid) {
+		if (tcon->cfid.file_all_info_is_valid) {
 			move_smb2_info_to_cifs(data,
-					       &tcon->crfid.file_all_info);
+					       &tcon->cfid.file_all_info);
 		} else {
 			rc = SMB2_query_info(xid, tcon,
 					     cfid->fid->persistent_fid,
diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
index 818cc4dee0e2..6a6ec6efb45a 100644
--- a/fs/cifs/smb2misc.c
+++ b/fs/cifs/smb2misc.c
@@ -16,6 +16,7 @@
 #include "smb2status.h"
 #include "smb2glob.h"
 #include "nterr.h"
+#include "cached_dir.h"
 
 static int
 check_smb2_hdr(struct smb2_hdr *shdr, __u64 mid)
@@ -648,15 +649,7 @@ smb2_is_valid_lease_break(char *buffer)
 				}
 				spin_unlock(&tcon->open_file_lock);
 
-				if (tcon->crfid.is_valid &&
-				    !memcmp(rsp->LeaseKey,
-					    tcon->crfid.fid->lease_key,
-					    SMB2_LEASE_KEY_SIZE)) {
-					tcon->crfid.time = 0;
-					INIT_WORK(&tcon->crfid.lease_break,
-						  smb2_cached_lease_break);
-					queue_work(cifsiod_wq,
-						   &tcon->crfid.lease_break);
+				if (cached_dir_lease_break(tcon, rsp->LeaseKey)) {
 					spin_unlock(&cifs_tcp_ses_lock);
 					return true;
 				}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index c0039dc0715a..8cb1eed1dd63 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -27,6 +27,7 @@
 #include "smbdirect.h"
 #include "fscache.h"
 #include "fs_context.h"
+#include "cached_dir.h"
 
 /* Change credits for different ops and return the total number of credits */
 static int
@@ -701,300 +702,6 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
 	return rc;
 }
 
-static void
-smb2_close_cached_fid(struct kref *ref)
-{
-	struct cached_fid *cfid = container_of(ref, struct cached_fid,
-					       refcount);
-	struct cached_dirent *dirent, *q;
-
-	if (cfid->is_valid) {
-		cifs_dbg(FYI, "clear cached root file handle\n");
-		SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
-			   cfid->fid->volatile_fid);
-	}
-
-	/*
-	 * We only check validity above to send SMB2_close,
-	 * but we still need to invalidate these entries
-	 * when this function is called
-	 */
-	cfid->is_valid = false;
-	cfid->file_all_info_is_valid = false;
-	cfid->has_lease = false;
-	if (cfid->dentry) {
-		dput(cfid->dentry);
-		cfid->dentry = NULL;
-	}
-	/*
-	 * Delete all cached dirent names
-	 */
-	mutex_lock(&cfid->dirents.de_mutex);
-	list_for_each_entry_safe(dirent, q, &cfid->dirents.entries, entry) {
-		list_del(&dirent->entry);
-		kfree(dirent->name);
-		kfree(dirent);
-	}
-	cfid->dirents.is_valid = 0;
-	cfid->dirents.is_failed = 0;
-	cfid->dirents.ctx = NULL;
-	cfid->dirents.pos = 0;
-	mutex_unlock(&cfid->dirents.de_mutex);
-
-}
-
-void close_cached_dir(struct cached_fid *cfid)
-{
-	mutex_lock(&cfid->fid_mutex);
-	kref_put(&cfid->refcount, smb2_close_cached_fid);
-	mutex_unlock(&cfid->fid_mutex);
-}
-
-void close_cached_dir_lease_locked(struct cached_fid *cfid)
-{
-	if (cfid->has_lease) {
-		cfid->has_lease = false;
-		kref_put(&cfid->refcount, smb2_close_cached_fid);
-	}
-}
-
-void close_cached_dir_lease(struct cached_fid *cfid)
-{
-	mutex_lock(&cfid->fid_mutex);
-	close_cached_dir_lease_locked(cfid);
-	mutex_unlock(&cfid->fid_mutex);
-}
-
-void
-smb2_cached_lease_break(struct work_struct *work)
-{
-	struct cached_fid *cfid = container_of(work,
-				struct cached_fid, lease_break);
-
-	close_cached_dir_lease(cfid);
-}
-
-/*
- * Open the and cache a directory handle.
- * Only supported for the root handle.
- * If error then *cfid is not initialized.
- */
-int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
-		const char *path,
-		struct cifs_sb_info *cifs_sb,
-		struct cached_fid **cfid)
-{
-	struct cifs_ses *ses;
-	struct TCP_Server_Info *server;
-	struct cifs_open_parms oparms;
-	struct smb2_create_rsp *o_rsp = NULL;
-	struct smb2_query_info_rsp *qi_rsp = NULL;
-	int resp_buftype[2];
-	struct smb_rqst rqst[2];
-	struct kvec rsp_iov[2];
-	struct kvec open_iov[SMB2_CREATE_IOV_SIZE];
-	struct kvec qi_iov[1];
-	int rc, flags = 0;
-	__le16 utf16_path = 0; /* Null - since an open of top of share */
-	u8 oplock = SMB2_OPLOCK_LEVEL_II;
-	struct cifs_fid *pfid;
-	struct dentry *dentry;
-
-	if (tcon == NULL || tcon->nohandlecache ||
-	    is_smb1_server(tcon->ses->server))
-		return -ENOTSUPP;
-
-	ses = tcon->ses;
-	server = ses->server;
-
-	if (cifs_sb->root == NULL)
-		return -ENOENT;
-
-	if (strlen(path))
-		return -ENOENT;
-
-	dentry = cifs_sb->root;
-
-	mutex_lock(&tcon->crfid.fid_mutex);
-	if (tcon->crfid.is_valid) {
-		cifs_dbg(FYI, "found a cached root file handle\n");
-		*cfid = &tcon->crfid;
-		kref_get(&tcon->crfid.refcount);
-		mutex_unlock(&tcon->crfid.fid_mutex);
-		return 0;
-	}
-
-	/*
-	 * We do not hold the lock for the open because in case
-	 * SMB2_open needs to reconnect, it will end up calling
-	 * cifs_mark_open_files_invalid() which takes the lock again
-	 * thus causing a deadlock
-	 */
-
-	mutex_unlock(&tcon->crfid.fid_mutex);
-
-	if (smb3_encryption_required(tcon))
-		flags |= CIFS_TRANSFORM_REQ;
-
-	if (!server->ops->new_lease_key)
-		return -EIO;
-
-	pfid = tcon->crfid.fid;
-	server->ops->new_lease_key(pfid);
-
-	memset(rqst, 0, sizeof(rqst));
-	resp_buftype[0] = resp_buftype[1] = CIFS_NO_BUFFER;
-	memset(rsp_iov, 0, sizeof(rsp_iov));
-
-	/* Open */
-	memset(&open_iov, 0, sizeof(open_iov));
-	rqst[0].rq_iov = open_iov;
-	rqst[0].rq_nvec = SMB2_CREATE_IOV_SIZE;
-
-	oparms.tcon = tcon;
-	oparms.create_options = cifs_create_options(cifs_sb, CREATE_NOT_FILE);
-	oparms.desired_access = FILE_READ_ATTRIBUTES;
-	oparms.disposition = FILE_OPEN;
-	oparms.fid = pfid;
-	oparms.reconnect = false;
-
-	rc = SMB2_open_init(tcon, server,
-			    &rqst[0], &oplock, &oparms, &utf16_path);
-	if (rc)
-		goto oshr_free;
-	smb2_set_next_command(tcon, &rqst[0]);
-
-	memset(&qi_iov, 0, sizeof(qi_iov));
-	rqst[1].rq_iov = qi_iov;
-	rqst[1].rq_nvec = 1;
-
-	rc = SMB2_query_info_init(tcon, server,
-				  &rqst[1], COMPOUND_FID,
-				  COMPOUND_FID, FILE_ALL_INFORMATION,
-				  SMB2_O_INFO_FILE, 0,
-				  sizeof(struct smb2_file_all_info) +
-				  PATH_MAX * 2, 0, NULL);
-	if (rc)
-		goto oshr_free;
-
-	smb2_set_related(&rqst[1]);
-
-	rc = compound_send_recv(xid, ses, server,
-				flags, 2, rqst,
-				resp_buftype, rsp_iov);
-	mutex_lock(&tcon->crfid.fid_mutex);
-
-	/*
-	 * Now we need to check again as the cached root might have
-	 * been successfully re-opened from a concurrent process
-	 */
-
-	if (tcon->crfid.is_valid) {
-		/* work was already done */
-
-		/* stash fids for close() later */
-		struct cifs_fid fid = {
-			.persistent_fid = pfid->persistent_fid,
-			.volatile_fid = pfid->volatile_fid,
-		};
-
-		/*
-		 * caller expects this func to set the fid in crfid to valid
-		 * cached root, so increment the refcount.
-		 */
-		kref_get(&tcon->crfid.refcount);
-
-		mutex_unlock(&tcon->crfid.fid_mutex);
-
-		if (rc == 0) {
-			/* close extra handle outside of crit sec */
-			SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
-		}
-		rc = 0;
-		goto oshr_free;
-	}
-
-	/* Cached root is still invalid, continue normaly */
-
-	if (rc) {
-		if (rc == -EREMCHG) {
-			tcon->need_reconnect = true;
-			pr_warn_once("server share %s deleted\n",
-				     tcon->treeName);
-		}
-		goto oshr_exit;
-	}
-
-	atomic_inc(&tcon->num_remote_opens);
-
-	o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
-	oparms.fid->persistent_fid = o_rsp->PersistentFileId;
-	oparms.fid->volatile_fid = o_rsp->VolatileFileId;
-#ifdef CONFIG_CIFS_DEBUG2
-	oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
-#endif /* CIFS_DEBUG2 */
-
-	tcon->crfid.tcon = tcon;
-	tcon->crfid.is_valid = true;
-	tcon->crfid.dentry = dentry;
-	dget(dentry);
-	kref_init(&tcon->crfid.refcount);
-
-	/* BB TBD check to see if oplock level check can be removed below */
-	if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
-		/*
-		 * See commit 2f94a3125b87. Increment the refcount when we
-		 * get a lease for root, release it if lease break occurs
-		 */
-		kref_get(&tcon->crfid.refcount);
-		tcon->crfid.has_lease = true;
-		smb2_parse_contexts(server, o_rsp,
-				&oparms.fid->epoch,
-				    oparms.fid->lease_key, &oplock,
-				    NULL, NULL);
-	} else
-		goto oshr_exit;
-
-	qi_rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
-	if (le32_to_cpu(qi_rsp->OutputBufferLength) < sizeof(struct smb2_file_all_info))
-		goto oshr_exit;
-	if (!smb2_validate_and_copy_iov(
-				le16_to_cpu(qi_rsp->OutputBufferOffset),
-				sizeof(struct smb2_file_all_info),
-				&rsp_iov[1], sizeof(struct smb2_file_all_info),
-				(char *)&tcon->crfid.file_all_info))
-		tcon->crfid.file_all_info_is_valid = true;
-	tcon->crfid.time = jiffies;
-
-
-oshr_exit:
-	mutex_unlock(&tcon->crfid.fid_mutex);
-oshr_free:
-	SMB2_open_free(&rqst[0]);
-	SMB2_query_info_free(&rqst[1]);
-	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
-	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
-	if (rc == 0)
-		*cfid = &tcon->crfid;
-	return rc;
-}
-
-int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
-			      struct dentry *dentry,
-			      struct cached_fid **cfid)
-{
-	mutex_lock(&tcon->crfid.fid_mutex);
-	if (tcon->crfid.dentry == dentry) {
-		cifs_dbg(FYI, "found a cached root file handle by dentry\n");
-		*cfid = &tcon->crfid;
-		kref_get(&tcon->crfid.refcount);
-		mutex_unlock(&tcon->crfid.fid_mutex);
-		return 0;
-	}
-	mutex_unlock(&tcon->crfid.fid_mutex);
-	return -ENOENT;
-}
-
 static void
 smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
 	      struct cifs_sb_info *cifs_sb)
@@ -1077,7 +784,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 	struct cifs_open_parms oparms;
 	struct cifs_fid fid;
 
-	if ((*full_path == 0) && tcon->crfid.is_valid)
+	if ((*full_path == 0) && tcon->cfid.is_valid)
 		return 0;
 
 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 590a1d4ac140..7c200b938267 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -39,6 +39,7 @@
 #ifdef CONFIG_CIFS_DFS_UPCALL
 #include "dfs_cache.h"
 #endif
+#include "cached_dir.h"
 
 /*
  *  The following table defines the expected "StructureSize" of SMB2 requests
@@ -1978,7 +1979,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
 	}
 	spin_unlock(&ses->chan_lock);
 
-	close_cached_dir_lease(&tcon->crfid);
+	close_cached_dir_lease(&tcon->cfid);
 
 	rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server,
 				 (void **) &req,
diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
index a69f1eed1cfe..51c5bf4a338a 100644
--- a/fs/cifs/smb2proto.h
+++ b/fs/cifs/smb2proto.h
@@ -54,16 +54,6 @@ extern bool smb2_is_valid_oplock_break(char *buffer,
 extern int smb3_handle_read_data(struct TCP_Server_Info *server,
 				 struct mid_q_entry *mid);
 
-extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
-			   const char *path,
-			   struct cifs_sb_info *cifs_sb,
-			   struct cached_fid **cfid);
-extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
-				     struct dentry *dentry,
-				     struct cached_fid **cfid);
-extern void close_cached_dir(struct cached_fid *cfid);
-extern void close_cached_dir_lease(struct cached_fid *cfid);
-extern void close_cached_dir_lease_locked(struct cached_fid *cfid);
 extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
 				   struct smb2_file_all_info *src);
 extern int smb2_query_reparse_tag(const unsigned int xid, struct cifs_tcon *tcon,
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/9] cifs: Move cached-dir functions into a separate file
  2022-08-09  2:11 ` [PATCH 1/9] cifs: Move cached-dir functions into a separate file Ronnie Sahlberg
  2022-08-11  2:53   ` Steve French
@ 2022-08-11 13:03   ` Paulo Alcantara
  1 sibling, 0 replies; 27+ messages in thread
From: Paulo Alcantara @ 2022-08-11 13:03 UTC (permalink / raw)
  To: Ronnie Sahlberg, linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Ronnie Sahlberg <lsahlber@redhat.com> writes:

> Also rename crfid to cfid to have consistent naming for this variable.
>
> This commit does not change any logic.
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/Makefile     |   2 +-
>  fs/cifs/cached_dir.c | 363 +++++++++++++++++++++++++++++++++++++++++++
>  fs/cifs/cached_dir.h |  26 ++++
>  fs/cifs/cifsfs.c     |  20 +--
>  fs/cifs/cifsglob.h   |   2 +-
>  fs/cifs/cifsproto.h  |   1 -
>  fs/cifs/cifssmb.c    |   8 +-
>  fs/cifs/inode.c      |   1 +
>  fs/cifs/misc.c       |  12 +-
>  fs/cifs/readdir.c    |   1 +
>  fs/cifs/smb2inode.c  |   5 +-
>  fs/cifs/smb2misc.c   |  13 +-
>  fs/cifs/smb2ops.c    | 297 +----------------------------------
>  fs/cifs/smb2pdu.c    |   3 +-
>  fs/cifs/smb2proto.h  |  10 --
>  15 files changed, 412 insertions(+), 352 deletions(-)
>  create mode 100644 fs/cifs/cached_dir.c
>  create mode 100644 fs/cifs/cached_dir.h

Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/9] cifs: Do not use tcon->cfid directly, use the cfid we get from open_cached_dir
  2022-08-09  2:11 ` [PATCH 2/9] cifs: Do not use tcon->cfid directly, use the cfid we get from open_cached_dir Ronnie Sahlberg
  2022-08-09  4:22   ` Steve French
@ 2022-08-11 13:03   ` Paulo Alcantara
  1 sibling, 0 replies; 27+ messages in thread
From: Paulo Alcantara @ 2022-08-11 13:03 UTC (permalink / raw)
  To: Ronnie Sahlberg, linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Ronnie Sahlberg <lsahlber@redhat.com> writes:

> They are the same right now but tcon-> will later point to a different
> type of struct containing a list of cfids.
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/smb2inode.c | 4 ++--
>  fs/cifs/smb2pdu.c   | 2 +-
>  2 files changed, 3 insertions(+), 3 deletions(-)

Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/9] cifs: Add constructor/destructors for tcon->cfid
  2022-08-09  2:11 ` [PATCH 3/9] cifs: Add constructor/destructors for tcon->cfid Ronnie Sahlberg
@ 2022-08-11 13:15   ` Paulo Alcantara
  2022-08-12  0:14     ` Steve French
  0 siblings, 1 reply; 27+ messages in thread
From: Paulo Alcantara @ 2022-08-11 13:15 UTC (permalink / raw)
  To: Ronnie Sahlberg, linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Ronnie Sahlberg <lsahlber@redhat.com> writes:

> and move the structure definitions into cached_dir.h
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/cached_dir.c | 114 ++++++++++++++++++++++++++-----------------
>  fs/cifs/cached_dir.h |  38 +++++++++++++++
>  fs/cifs/cifsglob.h   |  38 +--------------
>  fs/cifs/misc.c       |  20 ++++----
>  fs/cifs/smb2inode.c  |   4 +-
>  fs/cifs/smb2ops.c    |   8 +--
>  6 files changed, 123 insertions(+), 99 deletions(-)

Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 4/9] cifs: Make tcon contain a wrapper structure cached_fids instead of cached_fid
  2022-08-09  2:11 ` [PATCH 4/9] cifs: Make tcon contain a wrapper structure cached_fids instead of cached_fid Ronnie Sahlberg
@ 2022-08-11 13:16   ` Paulo Alcantara
  0 siblings, 0 replies; 27+ messages in thread
From: Paulo Alcantara @ 2022-08-11 13:16 UTC (permalink / raw)
  To: Ronnie Sahlberg, linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Ronnie Sahlberg <lsahlber@redhat.com> writes:

> This wrapper structure will later be expanded to contain a list of
> fids that are cached and not just the root fid.
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/cached_dir.c | 50 ++++++++++++++++++++++++--------------------
>  fs/cifs/cached_dir.h |  8 +++++--
>  fs/cifs/cifsglob.h   |  2 +-
>  fs/cifs/misc.c       |  6 +++---
>  fs/cifs/smb2ops.c    |  2 +-
>  5 files changed, 38 insertions(+), 30 deletions(-)

Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 5/9] cifs: Do not access tcon->cfids->cfid directly from is_path_accessible
  2022-08-09  2:11 ` [PATCH 5/9] cifs: Do not access tcon->cfids->cfid directly from is_path_accessible Ronnie Sahlberg
@ 2022-08-11 13:20   ` Paulo Alcantara
  2022-08-12  0:56     ` Steve French
  0 siblings, 1 reply; 27+ messages in thread
From: Paulo Alcantara @ 2022-08-11 13:20 UTC (permalink / raw)
  To: Ronnie Sahlberg, linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Ronnie Sahlberg <lsahlber@redhat.com> writes:

> cfids will soon keep a list of cached fids so we should not access this
> directly from outside of cached_dir.c
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/cached_dir.c | 10 ++++++----
>  fs/cifs/cached_dir.h |  2 +-
>  fs/cifs/readdir.c    |  4 ++--
>  fs/cifs/smb2inode.c  |  2 +-
>  fs/cifs/smb2ops.c    | 18 ++++++++++++++----
>  5 files changed, 24 insertions(+), 12 deletions(-)

Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 6/9] cifs: cifs: handlecache, only track the dentry for the root handle
  2022-08-09  2:11 ` [PATCH 6/9] cifs: cifs: handlecache, only track the dentry for the root handle Ronnie Sahlberg
@ 2022-08-11 13:34   ` Paulo Alcantara
  0 siblings, 0 replies; 27+ messages in thread
From: Paulo Alcantara @ 2022-08-11 13:34 UTC (permalink / raw)
  To: Ronnie Sahlberg, linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Ronnie Sahlberg <lsahlber@redhat.com> writes:

> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/cached_dir.c | 8 +++++---
>  1 file changed, 5 insertions(+), 3 deletions(-)
>
> diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
> index 1fb80b23bbeb..9423fee378f4 100644
> --- a/fs/cifs/cached_dir.c
> +++ b/fs/cifs/cached_dir.c
> @@ -47,11 +47,12 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
>  	if (cifs_sb->root == NULL)
>  		return -ENOENT;
>  
> +	if (!strlen(path))
> +		dentry = cifs_sb->root;
> +

Why are you calling strlen() twice?  If below check is false, that means
path is empty and then you can set dentry to cifs_sb->root, as the
original code did.

>  	if (strlen(path))
>  		return -ENOENT;
>  
> -	dentry = cifs_sb->root;
> -

>  	cfid = &tcon->cfids->cfid;
>  	mutex_lock(&cfid->fid_mutex);
>  	if (cfid->is_valid) {
> @@ -177,7 +178,8 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
>  	cfid->tcon = tcon;
>  	cfid->is_valid = true;
>  	cfid->dentry = dentry;
> -	dget(dentry);
> +	if (dentry)

No need for NULL check.  dget() already does that.

> +		dget(dentry);
>  	kref_init(&cfid->refcount);
>  
>  	/* BB TBD check to see if oplock level check can be removed below */

Otherwise, look good to me.

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 7/9] cifs: store a pointer to a fid in the cfid structure instead of the struct
  2022-08-09  2:11 ` [PATCH 7/9] cifs: store a pointer to a fid in the cfid structure instead of the struct Ronnie Sahlberg
@ 2022-08-11 13:37   ` Paulo Alcantara
  0 siblings, 0 replies; 27+ messages in thread
From: Paulo Alcantara @ 2022-08-11 13:37 UTC (permalink / raw)
  To: Ronnie Sahlberg, linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Ronnie Sahlberg <lsahlber@redhat.com> writes:

> also create a constructor that takes a path name and stores it in the fid.
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/cached_dir.c | 63 ++++++++++++++++++++++++++++++++++++++------
>  fs/cifs/cached_dir.h |  4 ++-
>  2 files changed, 58 insertions(+), 9 deletions(-)

Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 8/9] cifs: don't unlock cifs_tcp_ses_lock in cached_dir_lease_break()
  2022-08-09  2:11 ` [PATCH 8/9] cifs: don't unlock cifs_tcp_ses_lock in cached_dir_lease_break() Ronnie Sahlberg
@ 2022-08-11 13:38   ` Paulo Alcantara
  2022-08-11 15:27     ` Steve French
  0 siblings, 1 reply; 27+ messages in thread
From: Paulo Alcantara @ 2022-08-11 13:38 UTC (permalink / raw)
  To: Ronnie Sahlberg, linux-cifs; +Cc: Steve French, Ronnie Sahlberg

Ronnie Sahlberg <lsahlber@redhat.com> writes:

> Unlock it from the caller, which is also where the lock is taken.
>
> Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> ---
>  fs/cifs/cached_dir.c | 1 -
>  fs/cifs/smb2misc.c   | 4 +++-
>  2 files changed, 3 insertions(+), 2 deletions(-)

Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 8/9] cifs: don't unlock cifs_tcp_ses_lock in cached_dir_lease_break()
  2022-08-11 13:38   ` Paulo Alcantara
@ 2022-08-11 15:27     ` Steve French
  0 siblings, 0 replies; 27+ messages in thread
From: Steve French @ 2022-08-11 15:27 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: Ronnie Sahlberg, linux-cifs

FYI - I have merged this patch into patch1 to make it clearer

On Thu, Aug 11, 2022 at 8:37 AM Paulo Alcantara <pc@cjr.nz> wrote:
>
> Ronnie Sahlberg <lsahlber@redhat.com> writes:
>
> > Unlock it from the caller, which is also where the lock is taken.
> >
> > Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> > ---
> >  fs/cifs/cached_dir.c | 1 -
> >  fs/cifs/smb2misc.c   | 4 +++-
> >  2 files changed, 3 insertions(+), 2 deletions(-)
>
> Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>



-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 3/9] cifs: Add constructor/destructors for tcon->cfid
  2022-08-11 13:15   ` Paulo Alcantara
@ 2022-08-12  0:14     ` Steve French
  0 siblings, 0 replies; 27+ messages in thread
From: Steve French @ 2022-08-12  0:14 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: Ronnie Sahlberg, linux-cifs

[-- Attachment #1: Type: text/plain, Size: 783 bytes --]

Rebased onto current for-next, and fixed a minor formatting issue, and
added the RB. Tentatively merged pending testing.


On Thu, Aug 11, 2022 at 8:14 AM Paulo Alcantara <pc@cjr.nz> wrote:
>
> Ronnie Sahlberg <lsahlber@redhat.com> writes:
>
> > and move the structure definitions into cached_dir.h
> >
> > Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> > ---
> >  fs/cifs/cached_dir.c | 114 ++++++++++++++++++++++++++-----------------
> >  fs/cifs/cached_dir.h |  38 +++++++++++++++
> >  fs/cifs/cifsglob.h   |  38 +--------------
> >  fs/cifs/misc.c       |  20 ++++----
> >  fs/cifs/smb2inode.c  |   4 +-
> >  fs/cifs/smb2ops.c    |   8 +--
> >  6 files changed, 123 insertions(+), 99 deletions(-)
>
> Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>



-- 
Thanks,

Steve

[-- Attachment #2: 0001-cifs-Add-constructor-destructors-for-tcon-cfid.patch --]
[-- Type: text/x-patch, Size: 14369 bytes --]

From f6b0817f60dd900f300e2a524c3f709f44ecfa47 Mon Sep 17 00:00:00 2001
From: Ronnie Sahlberg <lsahlber@redhat.com>
Date: Thu, 11 Aug 2022 19:04:29 -0500
Subject: [PATCH] cifs: Add constructor/destructors for tcon->cfid

and move the structure definitions into cached_dir.h

Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
---
 fs/cifs/cached_dir.c | 112 ++++++++++++++++++++++++++-----------------
 fs/cifs/cached_dir.h |  38 +++++++++++++++
 fs/cifs/cifsglob.h   |  38 +--------------
 fs/cifs/misc.c       |  20 ++++----
 fs/cifs/smb2inode.c  |   4 +-
 fs/cifs/smb2ops.c    |   8 ++--
 6 files changed, 122 insertions(+), 98 deletions(-)

diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
index 753b850b6f87..517e3816c4f3 100644
--- a/fs/cifs/cached_dir.c
+++ b/fs/cifs/cached_dir.c
@@ -18,7 +18,7 @@
 int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 		const char *path,
 		struct cifs_sb_info *cifs_sb,
-		struct cached_fid **cfid)
+		struct cached_fid **ret_cfid)
 {
 	struct cifs_ses *ses;
 	struct TCP_Server_Info *server;
@@ -35,7 +35,8 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	u8 oplock = SMB2_OPLOCK_LEVEL_II;
 	struct cifs_fid *pfid;
 	struct dentry *dentry;
-
+	struct cached_fid *cfid;
+
 	if (tcon == NULL || tcon->nohandlecache ||
 	    is_smb1_server(tcon->ses->server))
 		return -EOPNOTSUPP;
@@ -51,12 +52,13 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 
 	dentry = cifs_sb->root;
 
-	mutex_lock(&tcon->cfid.fid_mutex);
-	if (tcon->cfid.is_valid) {
+	cfid = tcon->cfid;
+	mutex_lock(&cfid->fid_mutex);
+	if (cfid->is_valid) {
 		cifs_dbg(FYI, "found a cached root file handle\n");
-		*cfid = &tcon->cfid;
-		kref_get(&tcon->cfid.refcount);
-		mutex_unlock(&tcon->cfid.fid_mutex);
+		*ret_cfid = cfid;
+		kref_get(&cfid->refcount);
+		mutex_unlock(&cfid->fid_mutex);
 		return 0;
 	}
 
@@ -67,7 +69,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	 * thus causing a deadlock
 	 */
 
-	mutex_unlock(&tcon->cfid.fid_mutex);
+	mutex_unlock(&cfid->fid_mutex);
 
 	if (smb3_encryption_required(tcon))
 		flags |= CIFS_TRANSFORM_REQ;
@@ -75,7 +77,7 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	if (!server->ops->new_lease_key)
 		return -EIO;
 
-	pfid = tcon->cfid.fid;
+	pfid = &cfid->fid;
 	server->ops->new_lease_key(pfid);
 
 	memset(rqst, 0, sizeof(rqst));
@@ -118,14 +120,14 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	rc = compound_send_recv(xid, ses, server,
 				flags, 2, rqst,
 				resp_buftype, rsp_iov);
-	mutex_lock(&tcon->cfid.fid_mutex);
+	mutex_lock(&cfid->fid_mutex);
 
 	/*
 	 * Now we need to check again as the cached root might have
 	 * been successfully re-opened from a concurrent process
 	 */
 
-	if (tcon->cfid.is_valid) {
+	if (cfid->is_valid) {
 		/* work was already done */
 
 		/* stash fids for close() later */
@@ -138,9 +140,9 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 		 * caller expects this func to set the fid in cfid to valid
 		 * cached root, so increment the refcount.
 		 */
-		kref_get(&tcon->cfid.refcount);
+		kref_get(&cfid->refcount);
 
-		mutex_unlock(&tcon->cfid.fid_mutex);
+		mutex_unlock(&cfid->fid_mutex);
 
 		if (rc == 0) {
 			/* close extra handle outside of crit sec */
@@ -170,11 +172,11 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
 #endif /* CIFS_DEBUG2 */
 
-	tcon->cfid.tcon = tcon;
-	tcon->cfid.is_valid = true;
-	tcon->cfid.dentry = dentry;
+	cfid->tcon = tcon;
+	cfid->is_valid = true;
+	cfid->dentry = dentry;
 	dget(dentry);
-	kref_init(&tcon->cfid.refcount);
+	kref_init(&cfid->refcount);
 
 	/* BB TBD check to see if oplock level check can be removed below */
 	if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
@@ -182,8 +184,8 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 		 * See commit 2f94a3125b87. Increment the refcount when we
 		 * get a lease for root, release it if lease break occurs
 		 */
-		kref_get(&tcon->cfid.refcount);
-		tcon->cfid.has_lease = true;
+		kref_get(&cfid->refcount);
+		cfid->has_lease = true;
 		smb2_parse_contexts(server, o_rsp,
 				&oparms.fid->epoch,
 				    oparms.fid->lease_key, &oplock,
@@ -198,37 +200,41 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 				le16_to_cpu(qi_rsp->OutputBufferOffset),
 				sizeof(struct smb2_file_all_info),
 				&rsp_iov[1], sizeof(struct smb2_file_all_info),
-				(char *)&tcon->cfid.file_all_info))
-		tcon->cfid.file_all_info_is_valid = true;
-	tcon->cfid.time = jiffies;
+				(char *)&cfid->file_all_info))
+		cfid->file_all_info_is_valid = true;
 
+	cfid->time = jiffies;
 
 oshr_exit:
-	mutex_unlock(&tcon->cfid.fid_mutex);
+	mutex_unlock(&cfid->fid_mutex);
 oshr_free:
 	SMB2_open_free(&rqst[0]);
 	SMB2_query_info_free(&rqst[1]);
 	free_rsp_buf(resp_buftype[0], rsp_iov[0].iov_base);
 	free_rsp_buf(resp_buftype[1], rsp_iov[1].iov_base);
 	if (rc == 0)
-		*cfid = &tcon->cfid;
+		*ret_cfid = cfid;
 
 	return rc;
 }
 
 int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
 			      struct dentry *dentry,
-			      struct cached_fid **cfid)
+			      struct cached_fid **ret_cfid)
 {
-	mutex_lock(&tcon->cfid.fid_mutex);
-	if (tcon->cfid.dentry == dentry) {
+	struct cached_fid *cfid;
+
+	cfid = tcon->cfid;
+
+	mutex_lock(&cfid->fid_mutex);
+	if (cfid->dentry == dentry) {
 		cifs_dbg(FYI, "found a cached root file handle by dentry\n");
-		*cfid = &tcon->cfid;
-		kref_get(&tcon->cfid.refcount);
-		mutex_unlock(&tcon->cfid.fid_mutex);
+		*ret_cfid = cfid;
+		kref_get(&cfid->refcount);
+		mutex_unlock(&cfid->fid_mutex);
 		return 0;
 	}
-	mutex_unlock(&tcon->cfid.fid_mutex);
+	mutex_unlock(&cfid->fid_mutex);
 	return -ENOENT;
 }
 
@@ -241,8 +247,8 @@ smb2_close_cached_fid(struct kref *ref)
 
 	if (cfid->is_valid) {
 		cifs_dbg(FYI, "clear cached root file handle\n");
-		SMB2_close(0, cfid->tcon, cfid->fid->persistent_fid,
-			   cfid->fid->volatile_fid);
+		SMB2_close(0, cfid->tcon, cfid->fid.persistent_fid,
+			   cfid->fid.volatile_fid);
 	}
 
 	/*
@@ -312,7 +318,7 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
 		tcon = tlink_tcon(tlink);
 		if (IS_ERR(tcon))
 			continue;
-		cfid = &tcon->cfid;
+		cfid = tcon->cfid;
 		mutex_lock(&cfid->fid_mutex);
 		if (cfid->dentry) {
 			dput(cfid->dentry);
@@ -328,12 +334,12 @@ void close_all_cached_dirs(struct cifs_sb_info *cifs_sb)
  */
 void invalidate_all_cached_dirs(struct cifs_tcon *tcon)
 {
-	mutex_lock(&tcon->cfid.fid_mutex);
-	tcon->cfid.is_valid = false;
+	mutex_lock(&tcon->cfid->fid_mutex);
+	tcon->cfid->is_valid = false;
 	/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
-	close_cached_dir_lease_locked(&tcon->cfid);
-	memset(tcon->cfid.fid, 0, sizeof(struct cifs_fid));
-	mutex_unlock(&tcon->cfid.fid_mutex);
+	close_cached_dir_lease_locked(tcon->cfid);
+	memset(&tcon->cfid->fid, 0, sizeof(struct cifs_fid));
+	mutex_unlock(&tcon->cfid->fid_mutex);
 }
 
 static void
@@ -347,16 +353,34 @@ smb2_cached_lease_break(struct work_struct *work)
 
 int cached_dir_lease_break(struct cifs_tcon *tcon, __u8 lease_key[16])
 {
-	if (tcon->cfid.is_valid &&
+	if (tcon->cfid->is_valid &&
 	    !memcmp(lease_key,
-		    tcon->cfid.fid->lease_key,
+		    tcon->cfid->fid.lease_key,
 		    SMB2_LEASE_KEY_SIZE)) {
-		tcon->cfid.time = 0;
-		INIT_WORK(&tcon->cfid.lease_break,
+		tcon->cfid->time = 0;
+		INIT_WORK(&tcon->cfid->lease_break,
 			  smb2_cached_lease_break);
 		queue_work(cifsiod_wq,
-			   &tcon->cfid.lease_break);
+			   &tcon->cfid->lease_break);
 		return true;
 	}
 	return false;
 }
+
+struct cached_fid *init_cached_dir(void)
+{
+	struct cached_fid *cfid;
+
+	cfid = kzalloc(sizeof(*cfid), GFP_KERNEL);
+	if (!cfid)
+		return NULL;
+	INIT_LIST_HEAD(&cfid->dirents.entries);
+	mutex_init(&cfid->dirents.de_mutex);
+	mutex_init(&cfid->fid_mutex);
+	return cfid;
+}
+
+void free_cached_dir(struct cifs_tcon *tcon)
+{
+	kfree(tcon->cfid);
+}
diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
index 51c6b968f8b6..89c0343d7e26 100644
--- a/fs/cifs/cached_dir.h
+++ b/fs/cifs/cached_dir.h
@@ -9,6 +9,44 @@
 #define _CACHED_DIR_H
 
 
+struct cached_dirent {
+	struct list_head entry;
+	char *name;
+	int namelen;
+	loff_t pos;
+
+	struct cifs_fattr fattr;
+};
+
+struct cached_dirents {
+	bool is_valid:1;
+	bool is_failed:1;
+	struct dir_context *ctx; /*
+				  * Only used to make sure we only take entries
+				  * from a single context. Never dereferenced.
+				  */
+	struct mutex de_mutex;
+	int pos;		 /* Expected ctx->pos */
+	struct list_head entries;
+};
+
+struct cached_fid {
+	bool is_valid:1;	/* Do we have a useable root fid */
+	bool file_all_info_is_valid:1;
+	bool has_lease:1;
+	unsigned long time; /* jiffies of when lease was taken */
+	struct kref refcount;
+	struct cifs_fid fid;
+	struct mutex fid_mutex;
+	struct cifs_tcon *tcon;
+	struct dentry *dentry;
+	struct work_struct lease_break;
+	struct smb2_file_all_info file_all_info;
+	struct cached_dirents dirents;
+};
+
+extern struct cached_fid *init_cached_dir(void);
+extern void free_cached_dir(struct cifs_tcon *tcon);
 extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 			   const char *path,
 			   struct cifs_sb_info *cifs_sb,
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 8b82f13d11e0..bc0ee2d4b47b 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -1128,42 +1128,6 @@ struct cifs_fattr {
 	u32             cf_cifstag;
 };
 
-struct cached_dirent {
-	struct list_head entry;
-	char *name;
-	int namelen;
-	loff_t pos;
-
-	struct cifs_fattr fattr;
-};
-
-struct cached_dirents {
-	bool is_valid:1;
-	bool is_failed:1;
-	struct dir_context *ctx; /*
-				  * Only used to make sure we only take entries
-				  * from a single context. Never dereferenced.
-				  */
-	struct mutex de_mutex;
-	int pos;		 /* Expected ctx->pos */
-	struct list_head entries;
-};
-
-struct cached_fid {
-	bool is_valid:1;	/* Do we have a useable root fid */
-	bool file_all_info_is_valid:1;
-	bool has_lease:1;
-	unsigned long time; /* jiffies of when lease was taken */
-	struct kref refcount;
-	struct cifs_fid *fid;
-	struct mutex fid_mutex;
-	struct cifs_tcon *tcon;
-	struct dentry *dentry;
-	struct work_struct lease_break;
-	struct smb2_file_all_info file_all_info;
-	struct cached_dirents dirents;
-};
-
 /*
  * there is one of these for each connection to a resource on a particular
  * session
@@ -1257,7 +1221,7 @@ struct cifs_tcon {
 	struct fscache_volume *fscache;	/* cookie for share */
 #endif
 	struct list_head pending_opens;	/* list of incomplete opens */
-	struct cached_fid cfid; /* Cached root fid */
+	struct cached_fid *cfid; /* Cached root fid */
 	/* BB add field for back pointer to sb struct(s)? */
 #ifdef CONFIG_CIFS_DFS_UPCALL
 	struct list_head ulist; /* cache update list */
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index ea0405cfaee3..3f4f3d1a378f 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -23,6 +23,7 @@
 #include "dns_resolve.h"
 #endif
 #include "fs_context.h"
+#include "cached_dir.h"
 
 extern mempool_t *cifs_sm_req_poolp;
 extern mempool_t *cifs_req_poolp;
@@ -116,13 +117,11 @@ tconInfoAlloc(void)
 	ret_buf = kzalloc(sizeof(*ret_buf), GFP_KERNEL);
 	if (!ret_buf)
 		return NULL;
-	ret_buf->cfid.fid = kzalloc(sizeof(*ret_buf->cfid.fid), GFP_KERNEL);
-	if (!ret_buf->cfid.fid) {
+	ret_buf->cfid = init_cached_dir();
+	if (!ret_buf->cfid) {
 		kfree(ret_buf);
 		return NULL;
 	}
-	INIT_LIST_HEAD(&ret_buf->cfid.dirents.entries);
-	mutex_init(&ret_buf->cfid.dirents.de_mutex);
 
 	atomic_inc(&tconInfoAllocCount);
 	ret_buf->status = TID_NEW;
@@ -131,7 +130,6 @@ tconInfoAlloc(void)
 	INIT_LIST_HEAD(&ret_buf->openFileList);
 	INIT_LIST_HEAD(&ret_buf->tcon_list);
 	spin_lock_init(&ret_buf->open_file_lock);
-	mutex_init(&ret_buf->cfid.fid_mutex);
 	spin_lock_init(&ret_buf->stat_lock);
 	atomic_set(&ret_buf->num_local_opens, 0);
 	atomic_set(&ret_buf->num_remote_opens, 0);
@@ -140,17 +138,17 @@ tconInfoAlloc(void)
 }
 
 void
-tconInfoFree(struct cifs_tcon *buf_to_free)
+tconInfoFree(struct cifs_tcon *tcon)
 {
-	if (buf_to_free == NULL) {
+	if (tcon == NULL) {
 		cifs_dbg(FYI, "Null buffer passed to tconInfoFree\n");
 		return;
 	}
+	free_cached_dir(tcon);
 	atomic_dec(&tconInfoAllocCount);
-	kfree(buf_to_free->nativeFileSystem);
-	kfree_sensitive(buf_to_free->password);
-	kfree(buf_to_free->cfid.fid);
-	kfree(buf_to_free);
+	kfree(tcon->nativeFileSystem);
+	kfree_sensitive(tcon->password);
+	kfree(tcon);
 }
 
 struct smb_hdr *
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 09f01f70e020..9696184a09e3 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -524,8 +524,8 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 					       &cfid->file_all_info);
 		} else {
 			rc = SMB2_query_info(xid, tcon,
-					     cfid->fid->persistent_fid,
-					     cfid->fid->volatile_fid, smb2_data);
+					     cfid->fid.persistent_fid,
+					     cfid->fid.volatile_fid, smb2_data);
 			if (!rc)
 				move_smb2_info_to_cifs(data, smb2_data);
 		}
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 8cb1eed1dd63..6507761a8040 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -722,7 +722,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
 
 	rc = open_cached_dir(xid, tcon, "", cifs_sb, &cfid);
 	if (rc == 0)
-		memcpy(&fid, cfid->fid, sizeof(struct cifs_fid));
+		memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid));
 	else
 		rc = SMB2_open(xid, &oparms, &srch_path, &oplock, NULL, NULL,
 			       NULL, NULL);
@@ -784,7 +784,7 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 	struct cifs_open_parms oparms;
 	struct cifs_fid fid;
 
-	if ((*full_path == 0) && tcon->cfid.is_valid)
+	if ((*full_path == 0) && tcon->cfid->is_valid)
 		return 0;
 
 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
@@ -2457,8 +2457,8 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 	if (cfid) {
 		rc = SMB2_query_info_init(tcon, server,
 					  &rqst[1],
-					  cfid->fid->persistent_fid,
-					  cfid->fid->volatile_fid,
+					  cfid->fid.persistent_fid,
+					  cfid->fid.volatile_fid,
 					  class, type, 0,
 					  output_len, 0,
 					  NULL);
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH 5/9] cifs: Do not access tcon->cfids->cfid directly from is_path_accessible
  2022-08-11 13:20   ` Paulo Alcantara
@ 2022-08-12  0:56     ` Steve French
  2022-08-12  0:57       ` Steve French
  0 siblings, 1 reply; 27+ messages in thread
From: Steve French @ 2022-08-12  0:56 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: Ronnie Sahlberg, linux-cifs

Tentatively merged this patch (after rebasing it and fixing a minor
checkpatch problem).  Added Paulo's RB

Skipped adding patch 4 though (cifs: Make tcon contain a wrapper
structure cached_fids instead of cached_fid) - let me know if problem
skipping that patch in the short term

On Thu, Aug 11, 2022 at 8:20 AM Paulo Alcantara <pc@cjr.nz> wrote:
>
> Ronnie Sahlberg <lsahlber@redhat.com> writes:
>
> > cfids will soon keep a list of cached fids so we should not access this
> > directly from outside of cached_dir.c
> >
> > Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> > ---
> >  fs/cifs/cached_dir.c | 10 ++++++----
> >  fs/cifs/cached_dir.h |  2 +-
> >  fs/cifs/readdir.c    |  4 ++--
> >  fs/cifs/smb2inode.c  |  2 +-
> >  fs/cifs/smb2ops.c    | 18 ++++++++++++++----
> >  5 files changed, 24 insertions(+), 12 deletions(-)
>
> Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>



-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 5/9] cifs: Do not access tcon->cfids->cfid directly from is_path_accessible
  2022-08-12  0:56     ` Steve French
@ 2022-08-12  0:57       ` Steve French
  2022-08-12  1:26         ` Steve French
  0 siblings, 1 reply; 27+ messages in thread
From: Steve French @ 2022-08-12  0:57 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: Ronnie Sahlberg, linux-cifs

[-- Attachment #1: Type: text/plain, Size: 1091 bytes --]

updated patch attached


On Thu, Aug 11, 2022 at 7:56 PM Steve French <smfrench@gmail.com> wrote:
>
> Tentatively merged this patch (after rebasing it and fixing a minor
> checkpatch problem).  Added Paulo's RB
>
> Skipped adding patch 4 though (cifs: Make tcon contain a wrapper
> structure cached_fids instead of cached_fid) - let me know if problem
> skipping that patch in the short term
>
> On Thu, Aug 11, 2022 at 8:20 AM Paulo Alcantara <pc@cjr.nz> wrote:
> >
> > Ronnie Sahlberg <lsahlber@redhat.com> writes:
> >
> > > cfids will soon keep a list of cached fids so we should not access this
> > > directly from outside of cached_dir.c
> > >
> > > Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> > > ---
> > >  fs/cifs/cached_dir.c | 10 ++++++----
> > >  fs/cifs/cached_dir.h |  2 +-
> > >  fs/cifs/readdir.c    |  4 ++--
> > >  fs/cifs/smb2inode.c  |  2 +-
> > >  fs/cifs/smb2ops.c    | 18 ++++++++++++++----
> > >  5 files changed, 24 insertions(+), 12 deletions(-)
> >
> > Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
>
>
>
> --
> Thanks,
>
> Steve



-- 
Thanks,

Steve

[-- Attachment #2: 0001-cifs-Do-not-access-tcon-cfids-cfid-directly-from-is_.patch --]
[-- Type: text/x-patch, Size: 5192 bytes --]

From 5696418f5e91404da134b2cc9b88d64f4f574651 Mon Sep 17 00:00:00 2001
From: Steve French <stfrench@microsoft.com>
Date: Thu, 11 Aug 2022 19:51:18 -0500
Subject: [PATCH] cifs: Do not access tcon->cfids->cfid directly from
 is_path_accessible

cfids will soon keep a list of cached fids so we should not access this
directly from outside of cached_dir.c

Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
---
 fs/cifs/cached_dir.c | 10 ++++++----
 fs/cifs/cached_dir.h |  2 +-
 fs/cifs/readdir.c    |  4 ++--
 fs/cifs/smb2inode.c  |  2 +-
 fs/cifs/smb2ops.c    | 19 +++++++++++++++----
 5 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/fs/cifs/cached_dir.c b/fs/cifs/cached_dir.c
index 78e8deb82a0a..b401339f6e73 100644
--- a/fs/cifs/cached_dir.c
+++ b/fs/cifs/cached_dir.c
@@ -16,9 +16,9 @@
  * If error then *cfid is not initialized.
  */
 int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
-		const char *path,
-		struct cifs_sb_info *cifs_sb,
-		struct cached_fid **ret_cfid)
+		    const char *path,
+		    struct cifs_sb_info *cifs_sb,
+		    bool lookup_only, struct cached_fid **ret_cfid)
 {
 	struct cifs_ses *ses;
 	struct TCP_Server_Info *server;
@@ -68,9 +68,11 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	 * cifs_mark_open_files_invalid() which takes the lock again
 	 * thus causing a deadlock
 	 */
-
 	mutex_unlock(&cfid->fid_mutex);
 
+	if (lookup_only)
+		return -ENOENT;
+
 	if (smb3_encryption_required(tcon))
 		flags |= CIFS_TRANSFORM_REQ;
 
diff --git a/fs/cifs/cached_dir.h b/fs/cifs/cached_dir.h
index 89c0343d7e26..bd262dc8b179 100644
--- a/fs/cifs/cached_dir.h
+++ b/fs/cifs/cached_dir.h
@@ -50,7 +50,7 @@ extern void free_cached_dir(struct cifs_tcon *tcon);
 extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 			   const char *path,
 			   struct cifs_sb_info *cifs_sb,
-			   struct cached_fid **cfid);
+			   bool lookup_only, struct cached_fid **cfid);
 extern int open_cached_dir_by_dentry(struct cifs_tcon *tcon,
 				     struct dentry *dentry,
 				     struct cached_fid **cfid);
diff --git a/fs/cifs/readdir.c b/fs/cifs/readdir.c
index a06072ae6c7e..2eece8a07c11 100644
--- a/fs/cifs/readdir.c
+++ b/fs/cifs/readdir.c
@@ -1072,7 +1072,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
 		tcon = tlink_tcon(cifsFile->tlink);
 	}
 
-	rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
+	rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
 	cifs_put_tlink(tlink);
 	if (rc)
 		goto cache_not_found;
@@ -1143,7 +1143,7 @@ int cifs_readdir(struct file *file, struct dir_context *ctx)
 	tcon = tlink_tcon(cifsFile->tlink);
 	rc = find_cifs_entry(xid, tcon, ctx->pos, file, full_path,
 			     &current_entry, &num_to_fill);
-	open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
+	open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
 	if (rc) {
 		cifs_dbg(FYI, "fce error %d\n", rc);
 		goto rddir2_exit;
diff --git a/fs/cifs/smb2inode.c b/fs/cifs/smb2inode.c
index 9696184a09e3..b83f59051b26 100644
--- a/fs/cifs/smb2inode.c
+++ b/fs/cifs/smb2inode.c
@@ -516,7 +516,7 @@ smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
 	if (strcmp(full_path, ""))
 		rc = -ENOENT;
 	else
-		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, &cfid);
+		rc = open_cached_dir(xid, tcon, full_path, cifs_sb, false, &cfid);
 	/* If it is a root and its handle is cached then use it */
 	if (!rc) {
 		if (cfid->file_all_info_is_valid) {
diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
index 6507761a8040..f406af596887 100644
--- a/fs/cifs/smb2ops.c
+++ b/fs/cifs/smb2ops.c
@@ -720,7 +720,7 @@ smb3_qfs_tcon(const unsigned int xid, struct cifs_tcon *tcon,
 	oparms.fid = &fid;
 	oparms.reconnect = false;
 
-	rc = open_cached_dir(xid, tcon, "", cifs_sb, &cfid);
+	rc = open_cached_dir(xid, tcon, "", cifs_sb, false, &cfid);
 	if (rc == 0)
 		memcpy(&fid, &cfid->fid, sizeof(struct cifs_fid));
 	else
@@ -783,9 +783,16 @@ smb2_is_path_accessible(const unsigned int xid, struct cifs_tcon *tcon,
 	__u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
 	struct cifs_open_parms oparms;
 	struct cifs_fid fid;
+	struct cached_fid *cfid;
 
-	if ((*full_path == 0) && tcon->cfid->is_valid)
-		return 0;
+	rc = open_cached_dir(xid, tcon, full_path, cifs_sb, true, &cfid);
+	if (!rc) {
+		if (cfid->is_valid) {
+			close_cached_dir(cfid);
+			return 0;
+		}
+		close_cached_dir(cfid);
+	}
 
 	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
 	if (!utf16_path)
@@ -2430,8 +2437,12 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 	resp_buftype[0] = resp_buftype[1] = resp_buftype[2] = CIFS_NO_BUFFER;
 	memset(rsp_iov, 0, sizeof(rsp_iov));
 
+	/*
+	 * We can only call this for things we know are directories.
+	 */
 	if (!strcmp(path, ""))
-		open_cached_dir(xid, tcon, path, cifs_sb, &cfid); /* cfid null if open dir failed */
+		open_cached_dir(xid, tcon, path, cifs_sb, false,
+				&cfid); /* cfid null if open dir failed */
 
 	memset(&open_iov, 0, sizeof(open_iov));
 	rqst[0].rq_iov = open_iov;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH 5/9] cifs: Do not access tcon->cfids->cfid directly from is_path_accessible
  2022-08-12  0:57       ` Steve French
@ 2022-08-12  1:26         ` Steve French
  2022-08-12  1:34           ` Steve French
  0 siblings, 1 reply; 27+ messages in thread
From: Steve French @ 2022-08-12  1:26 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: Ronnie Sahlberg, linux-cifs

forgot to mention the obvious - the way to tell that this would be
helpful is simply looking at "/proc/fs/cifs/Stats" to see number of
opens vs. other operations.   Can also see pretty easily from simple
trace commands ("trace-cmd record -e smb3_open* smb3_close*") etc

On Thu, Aug 11, 2022 at 7:57 PM Steve French <smfrench@gmail.com> wrote:
>
> updated patch attached
>
>
> On Thu, Aug 11, 2022 at 7:56 PM Steve French <smfrench@gmail.com> wrote:
> >
> > Tentatively merged this patch (after rebasing it and fixing a minor
> > checkpatch problem).  Added Paulo's RB
> >
> > Skipped adding patch 4 though (cifs: Make tcon contain a wrapper
> > structure cached_fids instead of cached_fid) - let me know if problem
> > skipping that patch in the short term
> >
> > On Thu, Aug 11, 2022 at 8:20 AM Paulo Alcantara <pc@cjr.nz> wrote:
> > >
> > > Ronnie Sahlberg <lsahlber@redhat.com> writes:
> > >
> > > > cfids will soon keep a list of cached fids so we should not access this
> > > > directly from outside of cached_dir.c
> > > >
> > > > Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> > > > ---
> > > >  fs/cifs/cached_dir.c | 10 ++++++----
> > > >  fs/cifs/cached_dir.h |  2 +-
> > > >  fs/cifs/readdir.c    |  4 ++--
> > > >  fs/cifs/smb2inode.c  |  2 +-
> > > >  fs/cifs/smb2ops.c    | 18 ++++++++++++++----
> > > >  5 files changed, 24 insertions(+), 12 deletions(-)
> > >
> > > Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
> >
> >
> >
> > --
> > Thanks,
> >
> > Steve
>
>
>
> --
> Thanks,
>
> Steve



-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 5/9] cifs: Do not access tcon->cfids->cfid directly from is_path_accessible
  2022-08-12  1:26         ` Steve French
@ 2022-08-12  1:34           ` Steve French
  0 siblings, 0 replies; 27+ messages in thread
From: Steve French @ 2022-08-12  1:34 UTC (permalink / raw)
  To: Paulo Alcantara; +Cc: Ronnie Sahlberg, linux-cifs

And one more obvious thing ... this also fixes the problem where safe
caching gets unintionally turned off (deferred close, handle leases)
by users who disable actimeo (which is unsafe metadata caching).  So
setting actimeo=0 which some workloads do to make sure no stale
mtime/filesize would end up unintentionally hurting safe handle
caching (deferred close).

This timer should be controlled separately than actimeo

On Thu, Aug 11, 2022 at 8:26 PM Steve French <smfrench@gmail.com> wrote:
>
> forgot to mention the obvious - the way to tell that this would be
> helpful is simply looking at "/proc/fs/cifs/Stats" to see number of
> opens vs. other operations.   Can also see pretty easily from simple
> trace commands ("trace-cmd record -e smb3_open* smb3_close*") etc
>
> On Thu, Aug 11, 2022 at 7:57 PM Steve French <smfrench@gmail.com> wrote:
> >
> > updated patch attached
> >
> >
> > On Thu, Aug 11, 2022 at 7:56 PM Steve French <smfrench@gmail.com> wrote:
> > >
> > > Tentatively merged this patch (after rebasing it and fixing a minor
> > > checkpatch problem).  Added Paulo's RB
> > >
> > > Skipped adding patch 4 though (cifs: Make tcon contain a wrapper
> > > structure cached_fids instead of cached_fid) - let me know if problem
> > > skipping that patch in the short term
> > >
> > > On Thu, Aug 11, 2022 at 8:20 AM Paulo Alcantara <pc@cjr.nz> wrote:
> > > >
> > > > Ronnie Sahlberg <lsahlber@redhat.com> writes:
> > > >
> > > > > cfids will soon keep a list of cached fids so we should not access this
> > > > > directly from outside of cached_dir.c
> > > > >
> > > > > Signed-off-by: Ronnie Sahlberg <lsahlber@redhat.com>
> > > > > ---
> > > > >  fs/cifs/cached_dir.c | 10 ++++++----
> > > > >  fs/cifs/cached_dir.h |  2 +-
> > > > >  fs/cifs/readdir.c    |  4 ++--
> > > > >  fs/cifs/smb2inode.c  |  2 +-
> > > > >  fs/cifs/smb2ops.c    | 18 ++++++++++++++----
> > > > >  5 files changed, 24 insertions(+), 12 deletions(-)
> > > >
> > > > Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
> > >
> > >
> > >
> > > --
> > > Thanks,
> > >
> > > Steve
> >
> >
> >
> > --
> > Thanks,
> >
> > Steve
>
>
>
> --
> Thanks,
>
> Steve



-- 
Thanks,

Steve

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2022-08-12  1:34 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-09  2:11 WIP: Expand directory handle cache to also cache non-root directories Ronnie Sahlberg
2022-08-09  2:11 ` [PATCH 1/9] cifs: Move cached-dir functions into a separate file Ronnie Sahlberg
2022-08-11  2:53   ` Steve French
2022-08-11  3:08     ` Steve French
2022-08-11 13:03   ` Paulo Alcantara
2022-08-09  2:11 ` [PATCH 2/9] cifs: Do not use tcon->cfid directly, use the cfid we get from open_cached_dir Ronnie Sahlberg
2022-08-09  4:22   ` Steve French
2022-08-11 13:03   ` Paulo Alcantara
2022-08-09  2:11 ` [PATCH 3/9] cifs: Add constructor/destructors for tcon->cfid Ronnie Sahlberg
2022-08-11 13:15   ` Paulo Alcantara
2022-08-12  0:14     ` Steve French
2022-08-09  2:11 ` [PATCH 4/9] cifs: Make tcon contain a wrapper structure cached_fids instead of cached_fid Ronnie Sahlberg
2022-08-11 13:16   ` Paulo Alcantara
2022-08-09  2:11 ` [PATCH 5/9] cifs: Do not access tcon->cfids->cfid directly from is_path_accessible Ronnie Sahlberg
2022-08-11 13:20   ` Paulo Alcantara
2022-08-12  0:56     ` Steve French
2022-08-12  0:57       ` Steve French
2022-08-12  1:26         ` Steve French
2022-08-12  1:34           ` Steve French
2022-08-09  2:11 ` [PATCH 6/9] cifs: cifs: handlecache, only track the dentry for the root handle Ronnie Sahlberg
2022-08-11 13:34   ` Paulo Alcantara
2022-08-09  2:11 ` [PATCH 7/9] cifs: store a pointer to a fid in the cfid structure instead of the struct Ronnie Sahlberg
2022-08-11 13:37   ` Paulo Alcantara
2022-08-09  2:11 ` [PATCH 8/9] cifs: don't unlock cifs_tcp_ses_lock in cached_dir_lease_break() Ronnie Sahlberg
2022-08-11 13:38   ` Paulo Alcantara
2022-08-11 15:27     ` Steve French
2022-08-09  2:11 ` [PATCH 9/9] cifs: start caching all directories we open and get a lease for Ronnie Sahlberg

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.