Skip to content

Commit

Permalink
cifs: use the chans_need_reconnect bitmap for reconnect status
Browse files Browse the repository at this point in the history
We use the concept of "binding" when one of the secondary channel
is in the process of connecting/reconnecting to the server. Till this
binding process completes, and the channel is bound to an existing session,
we redirect traffic from other established channels on the binding channel,
effectively blocking all traffic till individual channels get reconnected.

With my last set of commits, we can get rid of this binding serialization.
We now have a bitmap of connection states for each channel. We will use
this bitmap instead for tracking channel status.

Having a bitmap also now enables us to keep the session alive, as long
as even a single channel underneath is alive.

Unfortunately, this also meant that we need to supply the tcp connection
info for the channel during all negotiate and session setup functions.
These changes have resulted in a slightly bigger code churn.
However, I expect perf and robustness improvements in the mchan scenario
after this change.

Signed-off-by: Shyam Prasad N <sprasad@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
  • Loading branch information
Shyam Prasad N authored and Steve French committed Jan 3, 2022
1 parent d1a931c commit f486ef8
Showing 15 changed files with 236 additions and 207 deletions.
4 changes: 2 additions & 2 deletions fs/cifs/cifs_spnego.c
Original file line number Diff line number Diff line change
@@ -84,9 +84,9 @@ struct key_type cifs_spnego_key_type = {

/* get a key struct with a SPNEGO security blob, suitable for session setup */
struct key *
cifs_get_spnego_key(struct cifs_ses *sesInfo)
cifs_get_spnego_key(struct cifs_ses *sesInfo,
struct TCP_Server_Info *server)
{
struct TCP_Server_Info *server = cifs_ses_server(sesInfo);
struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
char *description, *dp;
3 changes: 2 additions & 1 deletion fs/cifs/cifs_spnego.h
Original file line number Diff line number Diff line change
@@ -29,7 +29,8 @@ struct cifs_spnego_msg {

#ifdef __KERNEL__
extern struct key_type cifs_spnego_key_type;
extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo);
extern struct key *cifs_get_spnego_key(struct cifs_ses *sesInfo,
struct TCP_Server_Info *server);
#endif /* KERNEL */

#endif /* _CIFS_SPNEGO_H */
48 changes: 11 additions & 37 deletions fs/cifs/cifsglob.h
Original file line number Diff line number Diff line change
@@ -263,13 +263,16 @@ struct smb_version_operations {
/* check if we need to negotiate */
bool (*need_neg)(struct TCP_Server_Info *);
/* negotiate to the server */
int (*negotiate)(const unsigned int, struct cifs_ses *);
int (*negotiate)(const unsigned int xid,
struct cifs_ses *ses,
struct TCP_Server_Info *server);
/* set negotiated write size */
unsigned int (*negotiate_wsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx);
/* set negotiated read size */
unsigned int (*negotiate_rsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx);
/* setup smb sessionn */
int (*sess_setup)(const unsigned int, struct cifs_ses *,
struct TCP_Server_Info *server,
const struct nls_table *);
/* close smb session */
int (*logoff)(const unsigned int, struct cifs_ses *);
@@ -414,7 +417,8 @@ struct smb_version_operations {
void (*set_lease_key)(struct inode *, struct cifs_fid *);
/* generate new lease key */
void (*new_lease_key)(struct cifs_fid *);
int (*generate_signingkey)(struct cifs_ses *);
int (*generate_signingkey)(struct cifs_ses *ses,
struct TCP_Server_Info *server);
int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *,
bool allocate_crypto);
int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
@@ -940,15 +944,12 @@ struct cifs_ses {
enum securityEnum sectype; /* what security flavor was specified? */
bool sign; /* is signing required? */
bool domainAuto:1;
bool binding:1; /* are we binding the session? */
__u16 session_flags;
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE];
__u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE];
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];

__u8 binding_preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];

/*
* Network interfaces available on the server this session is
* connected to.
@@ -976,7 +977,6 @@ struct cifs_ses {
test_bit((index), &(ses)->chans_need_reconnect)

struct cifs_chan chans[CIFS_MAX_CHANNELS];
struct cifs_chan *binding_chan;
size_t chan_count;
size_t chan_max;
atomic_t chan_seq; /* round robin state */
@@ -985,42 +985,16 @@ struct cifs_ses {
* chans_need_reconnect is a bitmap indicating which of the channels
* under this smb session needs to be reconnected.
* If not multichannel session, only one bit will be used.
*
* We will ask for sess and tcon reconnection only if all the
* channels are marked for needing reconnection. This will
* enable the sessions on top to continue to live till any
* of the channels below are active.
*/
unsigned long chans_need_reconnect;
/* ========= end: protected by chan_lock ======== */
};

/*
* When binding a new channel, we need to access the channel which isn't fully
* established yet.
*/

static inline
struct cifs_chan *cifs_ses_binding_channel(struct cifs_ses *ses)
{
if (ses->binding)
return ses->binding_chan;
else
return NULL;
}

/*
* Returns the server pointer of the session. When binding a new
* channel this returns the last channel which isn't fully established
* yet.
*
* This function should be use for negprot/sess.setup codepaths. For
* the other requests see cifs_pick_channel().
*/
static inline
struct TCP_Server_Info *cifs_ses_server(struct cifs_ses *ses)
{
if (ses->binding)
return ses->binding_chan->server;
else
return ses->server;
}

static inline bool
cap_unix(struct cifs_ses *ses)
{
15 changes: 11 additions & 4 deletions fs/cifs/cifsproto.h
Original file line number Diff line number Diff line change
@@ -164,6 +164,7 @@ extern int small_smb_init_no_tc(const int smb_cmd, const int wct,
extern enum securityEnum select_sectype(struct TCP_Server_Info *server,
enum securityEnum requested);
extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp);
extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
extern u64 cifs_UnixTimeToNT(struct timespec64);
@@ -293,11 +294,15 @@ extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon,
const struct nls_table *nlsc);

extern int cifs_negotiate_protocol(const unsigned int xid,
struct cifs_ses *ses);
struct cifs_ses *ses,
struct TCP_Server_Info *server);
extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
struct nls_table *nls_info);
extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required);
extern int CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses);
extern int CIFSSMBNegotiate(const unsigned int xid,
struct cifs_ses *ses,
struct TCP_Server_Info *server);

extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
const char *tree, struct cifs_tcon *tcon,
@@ -504,8 +509,10 @@ extern int cifs_verify_signature(struct smb_rqst *rqst,
extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server);
extern int calc_seckey(struct cifs_ses *);
extern int generate_smb30signingkey(struct cifs_ses *);
extern int generate_smb311signingkey(struct cifs_ses *);
extern int generate_smb30signingkey(struct cifs_ses *ses,
struct TCP_Server_Info *server);
extern int generate_smb311signingkey(struct cifs_ses *ses,
struct TCP_Server_Info *server);

extern int CIFSSMBCopy(unsigned int xid,
struct cifs_tcon *source_tcon,
12 changes: 7 additions & 5 deletions fs/cifs/cifssmb.c
Original file line number Diff line number Diff line change
@@ -199,7 +199,8 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
spin_lock(&ses->chan_lock);
if (!cifs_chan_needs_reconnect(ses, server)) {
spin_unlock(&ses->chan_lock);
/* this just means that we only need to tcon */

/* this means that we only need to tree connect */
if (tcon->need_reconnect)
goto skip_sess_setup;

@@ -209,9 +210,9 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
}
spin_unlock(&ses->chan_lock);

rc = cifs_negotiate_protocol(0, ses);
rc = cifs_negotiate_protocol(0, ses, server);
if (!rc)
rc = cifs_setup_session(0, ses, nls_codepage);
rc = cifs_setup_session(0, ses, server, nls_codepage);

/* do we need to reconnect tcon? */
if (rc || !tcon->need_reconnect) {
@@ -503,14 +504,15 @@ should_set_ext_sec_flag(enum securityEnum sectype)
}

int
CIFSSMBNegotiate(const unsigned int xid, struct cifs_ses *ses)
CIFSSMBNegotiate(const unsigned int xid,
struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
NEGOTIATE_REQ *pSMB;
NEGOTIATE_RSP *pSMBr;
int rc = 0;
int bytes_returned;
int i;
struct TCP_Server_Info *server = ses->server;
u16 count;

if (!server) {
36 changes: 26 additions & 10 deletions fs/cifs/connect.c
Original file line number Diff line number Diff line change
@@ -169,6 +169,7 @@ static void cifs_resolve_server(struct work_struct *work)
*/
static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server)
{
unsigned int num_sessions = 0;
struct cifs_ses *ses;
struct cifs_tcon *tcon;
struct mid_q_entry *mid, *nmid;
@@ -201,6 +202,8 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
if (!CIFS_ALL_CHANS_NEED_RECONNECT(ses))
goto next_session;

num_sessions++;

list_for_each_entry(tcon, &ses->tcon_list, tcon_list)
tcon->need_reconnect = true;
if (ses->tcon_ipc)
@@ -211,6 +214,14 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
}
spin_unlock(&cifs_tcp_ses_lock);

if (num_sessions == 0)
return;
/*
* before reconnecting the tcp session, mark the smb session (uid)
* and the tid bad so they are not used until reconnected
*/
cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n",
__func__);
/* do not want to be sending data on a socket we are freeing */
cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
mutex_lock(&server->srv_mutex);
@@ -2005,7 +2016,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
spin_unlock(&ses->chan_lock);
cifs_dbg(FYI, "Session needs reconnect\n");

rc = cifs_negotiate_protocol(xid, ses);
rc = cifs_negotiate_protocol(xid, ses, server);
if (rc) {
mutex_unlock(&ses->session_mutex);
/* problem -- put our ses reference */
@@ -2014,7 +2025,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
return ERR_PTR(rc);
}

rc = cifs_setup_session(xid, ses,
rc = cifs_setup_session(xid, ses, server,
ctx->local_nls);
if (rc) {
mutex_unlock(&ses->session_mutex);
@@ -2086,9 +2097,9 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
ses->chans_need_reconnect = 1;
spin_unlock(&ses->chan_lock);

rc = cifs_negotiate_protocol(xid, ses);
rc = cifs_negotiate_protocol(xid, ses, server);
if (!rc)
rc = cifs_setup_session(xid, ses, ctx->local_nls);
rc = cifs_setup_session(xid, ses, server, ctx->local_nls);

/* each channel uses a different signing key */
memcpy(ses->chans[0].signkey, ses->smb3signingkey,
@@ -3820,10 +3831,10 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
}

int
cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
int rc = 0;
struct TCP_Server_Info *server = cifs_ses_server(ses);

if (!server->ops->need_neg || !server->ops->negotiate)
return -ENOSYS;
@@ -3832,7 +3843,7 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
if (!server->ops->need_neg(server))
return 0;

rc = server->ops->negotiate(xid, ses);
rc = server->ops->negotiate(xid, ses, server);
if (rc == 0) {
spin_lock(&GlobalMid_Lock);
if (server->tcpStatus == CifsNeedNegotiate)
@@ -3847,12 +3858,17 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)

int
cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
struct nls_table *nls_info)
{
int rc = -ENOSYS;
struct TCP_Server_Info *server = cifs_ses_server(ses);
bool is_binding = false;

spin_lock(&ses->chan_lock);
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
spin_unlock(&ses->chan_lock);

if (!ses->binding) {
if (!is_binding) {
ses->capabilities = server->capabilities;
if (!linuxExtEnabled)
ses->capabilities &= (~server->vals->cap_unix);
@@ -3870,7 +3886,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
server->sec_mode, server->capabilities, server->timeAdj);

if (server->ops->sess_setup)
rc = server->ops->sess_setup(xid, ses, nls_info);
rc = server->ops->sess_setup(xid, ses, server, nls_info);

if (rc)
cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc);
2 changes: 2 additions & 0 deletions fs/cifs/ntlmssp.h
Original file line number Diff line number Diff line change
@@ -121,7 +121,9 @@ typedef struct _AUTHENTICATE_MESSAGE {
int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp);
int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp);
Loading

0 comments on commit f486ef8

Please sign in to comment.