Commit d9191319 authored by Pavel Shilovsky's avatar Pavel Shilovsky Committed by Steve French

CIFS: Close cached root handle only if it has a lease

SMB2_tdis() checks if a root handle is valid in order to decide
whether it needs to close the handle or not. However if another
thread has reference for the handle, it may end up with putting
the reference twice. The extra reference that we want to put
during the tree disconnect is the reference that has a directory
lease. So, track the fact that we have a directory lease and
close the handle only in that case.
Signed-off-by: default avatarPavel Shilovsky <pshilov@microsoft.com>
Reviewed-by: default avatarRonnie Sahlberg <lsahlber@redhat.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent e0fc5b11
...@@ -1061,7 +1061,7 @@ cap_unix(struct cifs_ses *ses) ...@@ -1061,7 +1061,7 @@ cap_unix(struct cifs_ses *ses)
struct cached_fid { struct cached_fid {
bool is_valid:1; /* Do we have a useable root fid */ bool is_valid:1; /* Do we have a useable root fid */
bool file_all_info_is_valid:1; bool file_all_info_is_valid:1;
bool has_lease:1;
struct kref refcount; struct kref refcount;
struct cifs_fid *fid; struct cifs_fid *fid;
struct mutex fid_mutex; struct mutex fid_mutex;
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
#include "cifsproto.h" #include "cifsproto.h"
#include "cifs_unicode.h" #include "cifs_unicode.h"
#include "cifs_debug.h" #include "cifs_debug.h"
#include "smb2proto.h"
#include "fscache.h" #include "fscache.h"
#include "smbdirect.h" #include "smbdirect.h"
#ifdef CONFIG_CIFS_DFS_UPCALL #ifdef CONFIG_CIFS_DFS_UPCALL
...@@ -112,6 +113,8 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) ...@@ -112,6 +113,8 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
mutex_lock(&tcon->crfid.fid_mutex); mutex_lock(&tcon->crfid.fid_mutex);
tcon->crfid.is_valid = false; tcon->crfid.is_valid = false;
/* cached handle is not valid, so SMB2_CLOSE won't be sent below */
close_shroot_lease_locked(&tcon->crfid);
memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid)); memset(tcon->crfid.fid, 0, sizeof(struct cifs_fid));
mutex_unlock(&tcon->crfid.fid_mutex); mutex_unlock(&tcon->crfid.fid_mutex);
......
...@@ -616,6 +616,7 @@ smb2_close_cached_fid(struct kref *ref) ...@@ -616,6 +616,7 @@ smb2_close_cached_fid(struct kref *ref)
cfid->fid->volatile_fid); cfid->fid->volatile_fid);
cfid->is_valid = false; cfid->is_valid = false;
cfid->file_all_info_is_valid = false; cfid->file_all_info_is_valid = false;
cfid->has_lease = false;
} }
} }
...@@ -626,13 +627,28 @@ void close_shroot(struct cached_fid *cfid) ...@@ -626,13 +627,28 @@ void close_shroot(struct cached_fid *cfid)
mutex_unlock(&cfid->fid_mutex); mutex_unlock(&cfid->fid_mutex);
} }
void close_shroot_lease_locked(struct cached_fid *cfid)
{
if (cfid->has_lease) {
cfid->has_lease = false;
kref_put(&cfid->refcount, smb2_close_cached_fid);
}
}
void close_shroot_lease(struct cached_fid *cfid)
{
mutex_lock(&cfid->fid_mutex);
close_shroot_lease_locked(cfid);
mutex_unlock(&cfid->fid_mutex);
}
void void
smb2_cached_lease_break(struct work_struct *work) smb2_cached_lease_break(struct work_struct *work)
{ {
struct cached_fid *cfid = container_of(work, struct cached_fid *cfid = container_of(work,
struct cached_fid, lease_break); struct cached_fid, lease_break);
close_shroot(cfid); close_shroot_lease(cfid);
} }
/* /*
...@@ -773,6 +789,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid) ...@@ -773,6 +789,7 @@ int open_shroot(unsigned int xid, struct cifs_tcon *tcon, struct cifs_fid *pfid)
/* BB TBD check to see if oplock level check can be removed below */ /* BB TBD check to see if oplock level check can be removed below */
if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) { if (o_rsp->OplockLevel == SMB2_OPLOCK_LEVEL_LEASE) {
kref_get(&tcon->crfid.refcount); kref_get(&tcon->crfid.refcount);
tcon->crfid.has_lease = true;
smb2_parse_contexts(server, o_rsp, smb2_parse_contexts(server, o_rsp,
&oparms.fid->epoch, &oparms.fid->epoch,
oparms.fid->lease_key, &oplock, NULL); oparms.fid->lease_key, &oplock, NULL);
......
...@@ -1847,8 +1847,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -1847,8 +1847,7 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) if ((tcon->need_reconnect) || (tcon->ses->need_reconnect))
return 0; return 0;
if (tcon->crfid.is_valid) close_shroot_lease(&tcon->crfid);
close_shroot(&tcon->crfid);
rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req, rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, (void **) &req,
&total_len); &total_len);
......
...@@ -70,6 +70,8 @@ extern int smb3_handle_read_data(struct TCP_Server_Info *server, ...@@ -70,6 +70,8 @@ extern int smb3_handle_read_data(struct TCP_Server_Info *server,
extern int open_shroot(unsigned int xid, struct cifs_tcon *tcon, extern int open_shroot(unsigned int xid, struct cifs_tcon *tcon,
struct cifs_fid *pfid); struct cifs_fid *pfid);
extern void close_shroot(struct cached_fid *cfid); extern void close_shroot(struct cached_fid *cfid);
extern void close_shroot_lease(struct cached_fid *cfid);
extern void close_shroot_lease_locked(struct cached_fid *cfid);
extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst, extern void move_smb2_info_to_cifs(FILE_ALL_INFO *dst,
struct smb2_file_all_info *src); struct smb2_file_all_info *src);
extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon, extern int smb2_query_path_info(const unsigned int xid, struct cifs_tcon *tcon,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment