diff mbox series

[1/9] cifs: Move cached-dir functions into a separate file

Message ID 20220809021156.3086869-2-lsahlber@redhat.com
State New
Headers show
Series [1/9] cifs: Move cached-dir functions into a separate file | expand

Commit Message

Ronnie Sahlberg Aug. 9, 2022, 2:11 a.m. UTC
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

Comments

Steve French Aug. 11, 2022, 2:53 a.m. UTC | #1
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
>
Steve French Aug. 11, 2022, 3:08 a.m. UTC | #2
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
Paulo Alcantara Aug. 11, 2022, 1:03 p.m. UTC | #3
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>
diff mbox series

Patch

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,