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