Commit 0c947b89 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag '5.17-rc-part1-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6

Pull cifs updates from Steve French:

 - multichannel patches mostly related to improving reconnect behavior

 - minor cleanup patches

* tag '5.17-rc-part1-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6:
  cifs: fix FILE_BOTH_DIRECTORY_INFO definition
  cifs: move superblock magic defitions to magic.h
  cifs: Fix smb311_update_preauth_hash() kernel-doc comment
  cifs: avoid race during socket reconnect between send and recv
  cifs: maintain a state machine for tcp/smb/tcon sessions
  cifs: fix hang on cifs_get_next_mid()
  cifs: take cifs_tcp_ses_lock for status checks
  cifs: reconnect only the connection and not smb session where possible
  cifs: add WARN_ON for when chan_count goes below minimum
  cifs: adjust DebugData to use chans_need_reconnect for conn status
  cifs: use the chans_need_reconnect bitmap for reconnect status
  cifs: track individual channel status using chans_need_reconnect
  cifs: remove redundant assignment to pointer p
parents a6097180 9bbf8662
...@@ -416,11 +416,17 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) ...@@ -416,11 +416,17 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
from_kuid(&init_user_ns, ses->cred_uid)); from_kuid(&init_user_ns, ses->cred_uid));
spin_lock(&ses->chan_lock); spin_lock(&ses->chan_lock);
if (CIFS_CHAN_NEEDS_RECONNECT(ses, 0))
seq_puts(m, "\tPrimary channel: DISCONNECTED ");
if (ses->chan_count > 1) { if (ses->chan_count > 1) {
seq_printf(m, "\n\n\tExtra Channels: %zu ", seq_printf(m, "\n\n\tExtra Channels: %zu ",
ses->chan_count-1); ses->chan_count-1);
for (j = 1; j < ses->chan_count; j++) for (j = 1; j < ses->chan_count; j++) {
cifs_dump_channel(m, j, &ses->chans[j]); cifs_dump_channel(m, j, &ses->chans[j]);
if (CIFS_CHAN_NEEDS_RECONNECT(ses, j))
seq_puts(m, "\tDISCONNECTED ");
}
} }
spin_unlock(&ses->chan_lock); spin_unlock(&ses->chan_lock);
......
...@@ -84,9 +84,9 @@ struct key_type cifs_spnego_key_type = { ...@@ -84,9 +84,9 @@ struct key_type cifs_spnego_key_type = {
/* get a key struct with a SPNEGO security blob, suitable for session setup */ /* get a key struct with a SPNEGO security blob, suitable for session setup */
struct key * 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_in *sa = (struct sockaddr_in *) &server->dstaddr;
struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr; struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
char *description, *dp; char *description, *dp;
......
...@@ -29,7 +29,8 @@ struct cifs_spnego_msg { ...@@ -29,7 +29,8 @@ struct cifs_spnego_msg {
#ifdef __KERNEL__ #ifdef __KERNEL__
extern struct key_type cifs_spnego_key_type; 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 /* KERNEL */
#endif /* _CIFS_SPNEGO_H */ #endif /* _CIFS_SPNEGO_H */
...@@ -498,10 +498,10 @@ static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *a ...@@ -498,10 +498,10 @@ static int cifs_swn_reconnect(struct cifs_tcon *tcon, struct sockaddr_storage *a
goto unlock; goto unlock;
} }
spin_lock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock);
if (tcon->ses->server->tcpStatus != CifsExiting) if (tcon->ses->server->tcpStatus != CifsExiting)
tcon->ses->server->tcpStatus = CifsNeedReconnect; tcon->ses->server->tcpStatus = CifsNeedReconnect;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
unlock: unlock:
mutex_unlock(&tcon->ses->server->srv_mutex); mutex_unlock(&tcon->ses->server->srv_mutex);
......
...@@ -141,9 +141,13 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server, ...@@ -141,9 +141,13 @@ int cifs_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server,
if ((cifs_pdu == NULL) || (server == NULL)) if ((cifs_pdu == NULL) || (server == NULL))
return -EINVAL; return -EINVAL;
spin_lock(&cifs_tcp_ses_lock);
if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) || if (!(cifs_pdu->Flags2 & SMBFLG2_SECURITY_SIGNATURE) ||
server->tcpStatus == CifsNeedNegotiate) server->tcpStatus == CifsNeedNegotiate) {
spin_unlock(&cifs_tcp_ses_lock);
return rc; return rc;
}
spin_unlock(&cifs_tcp_ses_lock);
if (!server->session_estab) { if (!server->session_estab) {
memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8); memcpy(cifs_pdu->Signature.SecuritySignature, "BSRSPYL", 8);
......
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <linux/random.h> #include <linux/random.h>
#include <linux/uuid.h> #include <linux/uuid.h>
#include <linux/xattr.h> #include <linux/xattr.h>
#include <uapi/linux/magic.h>
#include <net/ipv6.h> #include <net/ipv6.h>
#include "cifsfs.h" #include "cifsfs.h"
#include "cifspdu.h" #include "cifspdu.h"
...@@ -202,7 +203,7 @@ cifs_read_super(struct super_block *sb) ...@@ -202,7 +203,7 @@ cifs_read_super(struct super_block *sb)
sb->s_time_max = ts.tv_sec; sb->s_time_max = ts.tv_sec;
} }
sb->s_magic = CIFS_MAGIC_NUMBER; sb->s_magic = CIFS_SUPER_MAGIC;
sb->s_op = &cifs_super_ops; sb->s_op = &cifs_super_ops;
sb->s_xattr = cifs_xattr_handlers; sb->s_xattr = cifs_xattr_handlers;
rc = super_setup_bdi(sb); rc = super_setup_bdi(sb);
...@@ -773,7 +774,7 @@ cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb) ...@@ -773,7 +774,7 @@ cifs_get_root(struct smb3_fs_context *ctx, struct super_block *sb)
sep = CIFS_DIR_SEP(cifs_sb); sep = CIFS_DIR_SEP(cifs_sb);
dentry = dget(sb->s_root); dentry = dget(sb->s_root);
p = s = full_path; s = full_path;
do { do {
struct inode *dir = d_inode(dentry); struct inode *dir = d_inode(dentry);
......
...@@ -24,8 +24,6 @@ ...@@ -24,8 +24,6 @@
#include "../smbfs_common/smb2pdu.h" #include "../smbfs_common/smb2pdu.h"
#include "smb2pdu.h" #include "smb2pdu.h"
#define CIFS_MAGIC_NUMBER 0xFF534D42 /* the first four bytes of SMB PDUs */
#define SMB_PATH_MAX 260 #define SMB_PATH_MAX 260
#define CIFS_PORT 445 #define CIFS_PORT 445
#define RFC1001_PORT 139 #define RFC1001_PORT 139
...@@ -113,7 +111,13 @@ enum statusEnum { ...@@ -113,7 +111,13 @@ enum statusEnum {
CifsGood, CifsGood,
CifsExiting, CifsExiting,
CifsNeedReconnect, CifsNeedReconnect,
CifsNeedNegotiate CifsNeedNegotiate,
CifsInNegotiate,
CifsNeedSessSetup,
CifsInSessSetup,
CifsNeedTcon,
CifsInTcon,
CifsInFilesInvalidate
}; };
enum securityEnum { enum securityEnum {
...@@ -263,13 +267,16 @@ struct smb_version_operations { ...@@ -263,13 +267,16 @@ struct smb_version_operations {
/* check if we need to negotiate */ /* check if we need to negotiate */
bool (*need_neg)(struct TCP_Server_Info *); bool (*need_neg)(struct TCP_Server_Info *);
/* negotiate to the server */ /* 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 */ /* set negotiated write size */
unsigned int (*negotiate_wsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx); unsigned int (*negotiate_wsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx);
/* set negotiated read size */ /* set negotiated read size */
unsigned int (*negotiate_rsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx); unsigned int (*negotiate_rsize)(struct cifs_tcon *tcon, struct smb3_fs_context *ctx);
/* setup smb sessionn */ /* setup smb sessionn */
int (*sess_setup)(const unsigned int, struct cifs_ses *, int (*sess_setup)(const unsigned int, struct cifs_ses *,
struct TCP_Server_Info *server,
const struct nls_table *); const struct nls_table *);
/* close smb session */ /* close smb session */
int (*logoff)(const unsigned int, struct cifs_ses *); int (*logoff)(const unsigned int, struct cifs_ses *);
...@@ -414,7 +421,8 @@ struct smb_version_operations { ...@@ -414,7 +421,8 @@ struct smb_version_operations {
void (*set_lease_key)(struct inode *, struct cifs_fid *); void (*set_lease_key)(struct inode *, struct cifs_fid *);
/* generate new lease key */ /* generate new lease key */
void (*new_lease_key)(struct cifs_fid *); 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 *, int (*calc_signature)(struct smb_rqst *, struct TCP_Server_Info *,
bool allocate_crypto); bool allocate_crypto);
int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon, int (*set_integrity)(const unsigned int, struct cifs_tcon *tcon,
...@@ -582,7 +590,7 @@ struct TCP_Server_Info { ...@@ -582,7 +590,7 @@ struct TCP_Server_Info {
char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL]; char server_RFC1001_name[RFC1001_NAME_LEN_WITH_NULL];
struct smb_version_operations *ops; struct smb_version_operations *ops;
struct smb_version_values *vals; struct smb_version_values *vals;
/* updates to tcpStatus protected by GlobalMid_Lock */ /* updates to tcpStatus protected by cifs_tcp_ses_lock */
enum statusEnum tcpStatus; /* what we think the status is */ enum statusEnum tcpStatus; /* what we think the status is */
char *hostname; /* hostname portion of UNC string */ char *hostname; /* hostname portion of UNC string */
struct socket *ssocket; struct socket *ssocket;
...@@ -920,7 +928,7 @@ struct cifs_ses { ...@@ -920,7 +928,7 @@ struct cifs_ses {
struct mutex session_mutex; struct mutex session_mutex;
struct TCP_Server_Info *server; /* pointer to server info */ struct TCP_Server_Info *server; /* pointer to server info */
int ses_count; /* reference counter */ int ses_count; /* reference counter */
enum statusEnum status; /* updates protected by GlobalMid_Lock */ enum statusEnum status; /* updates protected by cifs_tcp_ses_lock */
unsigned overrideSecFlg; /* if non-zero override global sec flags */ unsigned overrideSecFlg; /* if non-zero override global sec flags */
char *serverOS; /* name of operating system underlying server */ char *serverOS; /* name of operating system underlying server */
char *serverNOS; /* name of network operating system of server */ char *serverNOS; /* name of network operating system of server */
...@@ -939,17 +947,13 @@ struct cifs_ses { ...@@ -939,17 +947,13 @@ struct cifs_ses {
struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */
enum securityEnum sectype; /* what security flavor was specified? */ enum securityEnum sectype; /* what security flavor was specified? */
bool sign; /* is signing required? */ bool sign; /* is signing required? */
bool need_reconnect:1; /* connection reset, uid now invalid */
bool domainAuto:1; bool domainAuto:1;
bool binding:1; /* are we binding the session? */
__u16 session_flags; __u16 session_flags;
__u8 smb3signingkey[SMB3_SIGN_KEY_SIZE]; __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
__u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE]; __u8 smb3encryptionkey[SMB3_ENC_DEC_KEY_SIZE];
__u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE]; __u8 smb3decryptionkey[SMB3_ENC_DEC_KEY_SIZE];
__u8 preauth_sha_hash[SMB2_PREAUTH_HASH_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 * Network interfaces available on the server this session is
* connected to. * connected to.
...@@ -969,45 +973,34 @@ struct cifs_ses { ...@@ -969,45 +973,34 @@ struct cifs_ses {
spinlock_t chan_lock; spinlock_t chan_lock;
/* ========= begin: protected by chan_lock ======== */ /* ========= begin: protected by chan_lock ======== */
#define CIFS_MAX_CHANNELS 16 #define CIFS_MAX_CHANNELS 16
#define CIFS_ALL_CHANNELS_SET(ses) \
((1UL << (ses)->chan_count) - 1)
#define CIFS_ALL_CHANS_NEED_RECONNECT(ses) \
((ses)->chans_need_reconnect == CIFS_ALL_CHANNELS_SET(ses))
#define CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses) \
((ses)->chans_need_reconnect = CIFS_ALL_CHANNELS_SET(ses))
#define CIFS_CHAN_NEEDS_RECONNECT(ses, index) \
test_bit((index), &(ses)->chans_need_reconnect)
struct cifs_chan chans[CIFS_MAX_CHANNELS]; struct cifs_chan chans[CIFS_MAX_CHANNELS];
struct cifs_chan *binding_chan;
size_t chan_count; size_t chan_count;
size_t chan_max; size_t chan_max;
atomic_t chan_seq; /* round robin state */ atomic_t chan_seq; /* round robin state */
/*
* 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 ======== */ /* ========= 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 static inline bool
cap_unix(struct cifs_ses *ses) cap_unix(struct cifs_ses *ses)
{ {
......
...@@ -2560,7 +2560,7 @@ typedef struct { ...@@ -2560,7 +2560,7 @@ typedef struct {
__le32 EaSize; /* length of the xattrs */ __le32 EaSize; /* length of the xattrs */
__u8 ShortNameLength; __u8 ShortNameLength;
__u8 Reserved; __u8 Reserved;
__u8 ShortName[12]; __u8 ShortName[24];
char FileName[1]; char FileName[1];
} __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */ } __attribute__((packed)) FILE_BOTH_DIRECTORY_INFO; /* level 0x104 FFrsp data */
......
...@@ -131,7 +131,8 @@ extern int SendReceiveBlockingLock(const unsigned int xid, ...@@ -131,7 +131,8 @@ extern int SendReceiveBlockingLock(const unsigned int xid,
struct smb_hdr *in_buf , struct smb_hdr *in_buf ,
struct smb_hdr *out_buf, struct smb_hdr *out_buf,
int *bytes_returned); int *bytes_returned);
extern int cifs_reconnect(struct TCP_Server_Info *server); extern int cifs_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session);
extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr); extern int checkSMB(char *buf, unsigned int len, struct TCP_Server_Info *srvr);
extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *); extern bool is_valid_oplock_break(char *, struct TCP_Server_Info *);
extern bool backup_cred(struct cifs_sb_info *); extern bool backup_cred(struct cifs_sb_info *);
...@@ -164,6 +165,7 @@ extern int small_smb_init_no_tc(const int smb_cmd, const int wct, ...@@ -164,6 +165,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, extern enum securityEnum select_sectype(struct TCP_Server_Info *server,
enum securityEnum requested); enum securityEnum requested);
extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, extern int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp); const struct nls_table *nls_cp);
extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601); extern struct timespec64 cifs_NTtimeToUnix(__le64 utc_nanoseconds_since_1601);
extern u64 cifs_UnixTimeToNT(struct timespec64); extern u64 cifs_UnixTimeToNT(struct timespec64);
...@@ -293,11 +295,15 @@ extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -293,11 +295,15 @@ extern int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon,
const struct nls_table *nlsc); const struct nls_table *nlsc);
extern int cifs_negotiate_protocol(const unsigned int xid, 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, extern int cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
struct nls_table *nls_info); struct nls_table *nls_info);
extern int cifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required); 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, extern int CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
const char *tree, struct cifs_tcon *tcon, const char *tree, struct cifs_tcon *tcon,
...@@ -504,8 +510,10 @@ extern int cifs_verify_signature(struct smb_rqst *rqst, ...@@ -504,8 +510,10 @@ extern int cifs_verify_signature(struct smb_rqst *rqst,
extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *); extern int setup_ntlmv2_rsp(struct cifs_ses *, const struct nls_table *);
extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server); extern void cifs_crypto_secmech_release(struct TCP_Server_Info *server);
extern int calc_seckey(struct cifs_ses *); extern int calc_seckey(struct cifs_ses *);
extern int generate_smb30signingkey(struct cifs_ses *); extern int generate_smb30signingkey(struct cifs_ses *ses,
extern int generate_smb311signingkey(struct cifs_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, extern int CIFSSMBCopy(unsigned int xid,
struct cifs_tcon *source_tcon, struct cifs_tcon *source_tcon,
...@@ -601,6 +609,19 @@ bool is_server_using_iface(struct TCP_Server_Info *server, ...@@ -601,6 +609,19 @@ bool is_server_using_iface(struct TCP_Server_Info *server,
bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface); bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
void cifs_ses_mark_for_reconnect(struct cifs_ses *ses); void cifs_ses_mark_for_reconnect(struct cifs_ses *ses);
unsigned int
cifs_ses_get_chan_index(struct cifs_ses *ses,
struct TCP_Server_Info *server);
void
cifs_chan_set_need_reconnect(struct cifs_ses *ses,
struct TCP_Server_Info *server);
void
cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
struct TCP_Server_Info *server);
bool
cifs_chan_needs_reconnect(struct cifs_ses *ses,
struct TCP_Server_Info *server);
void extract_unc_hostname(const char *unc, const char **h, size_t *len); void extract_unc_hostname(const char *unc, const char **h, size_t *len);
int copy_path_name(char *dst, const char *src); int copy_path_name(char *dst, const char *src);
int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
......
...@@ -73,6 +73,16 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) ...@@ -73,6 +73,16 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
struct list_head *tmp; struct list_head *tmp;
struct list_head *tmp1; struct list_head *tmp1;
/* only send once per connect */
spin_lock(&cifs_tcp_ses_lock);
if (tcon->ses->status != CifsGood ||
tcon->tidStatus != CifsNeedReconnect) {
spin_unlock(&cifs_tcp_ses_lock);
return;
}
tcon->tidStatus = CifsInFilesInvalidate;
spin_unlock(&cifs_tcp_ses_lock);
/* list all files open on tree connection and mark them invalid */ /* list all files open on tree connection and mark them invalid */
spin_lock(&tcon->open_file_lock); spin_lock(&tcon->open_file_lock);
list_for_each_safe(tmp, tmp1, &tcon->openFileList) { list_for_each_safe(tmp, tmp1, &tcon->openFileList) {
...@@ -89,6 +99,11 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon) ...@@ -89,6 +99,11 @@ cifs_mark_open_files_invalid(struct cifs_tcon *tcon)
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);
spin_lock(&cifs_tcp_ses_lock);
if (tcon->tidStatus == CifsInFilesInvalidate)
tcon->tidStatus = CifsNeedTcon;
spin_unlock(&cifs_tcp_ses_lock);
/* /*
* BB Add call to invalidate_inodes(sb) for all superblocks mounted * BB Add call to invalidate_inodes(sb) for all superblocks mounted
* to this tcon. * to this tcon.
...@@ -120,15 +135,18 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) ...@@ -120,15 +135,18 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
* only tree disconnect, open, and write, (and ulogoff which does not * only tree disconnect, open, and write, (and ulogoff which does not
* have tcon) are allowed as we start force umount * have tcon) are allowed as we start force umount
*/ */
spin_lock(&cifs_tcp_ses_lock);
if (tcon->tidStatus == CifsExiting) { if (tcon->tidStatus == CifsExiting) {
if (smb_command != SMB_COM_WRITE_ANDX && if (smb_command != SMB_COM_WRITE_ANDX &&
smb_command != SMB_COM_OPEN_ANDX && smb_command != SMB_COM_OPEN_ANDX &&
smb_command != SMB_COM_TREE_DISCONNECT) { smb_command != SMB_COM_TREE_DISCONNECT) {
spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "can not send cmd %d while umounting\n", cifs_dbg(FYI, "can not send cmd %d while umounting\n",
smb_command); smb_command);
return -ENODEV; return -ENODEV;
} }
} }
spin_unlock(&cifs_tcp_ses_lock);
retries = server->nr_targets; retries = server->nr_targets;
...@@ -148,8 +166,12 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) ...@@ -148,8 +166,12 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
} }
/* are we still trying to reconnect? */ /* are we still trying to reconnect? */
if (server->tcpStatus != CifsNeedReconnect) spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus != CifsNeedReconnect) {
spin_unlock(&cifs_tcp_ses_lock);
break; break;
}
spin_unlock(&cifs_tcp_ses_lock);
if (retries && --retries) if (retries && --retries)
continue; continue;
...@@ -166,31 +188,49 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) ...@@ -166,31 +188,49 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
retries = server->nr_targets; retries = server->nr_targets;
} }
if (!ses->need_reconnect && !tcon->need_reconnect) spin_lock(&ses->chan_lock);
if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
spin_unlock(&ses->chan_lock);
return 0; return 0;
}
spin_unlock(&ses->chan_lock);
nls_codepage = load_nls_default(); nls_codepage = load_nls_default();
/*
* need to prevent multiple threads trying to simultaneously
* reconnect the same SMB session
*/
mutex_lock(&ses->session_mutex);
/* /*
* Recheck after acquire mutex. If another thread is negotiating * Recheck after acquire mutex. If another thread is negotiating
* and the server never sends an answer the socket will be closed * and the server never sends an answer the socket will be closed
* and tcpStatus set to reconnect. * and tcpStatus set to reconnect.
*/ */
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsNeedReconnect) { if (server->tcpStatus == CifsNeedReconnect) {
spin_unlock(&cifs_tcp_ses_lock);
rc = -EHOSTDOWN; rc = -EHOSTDOWN;
mutex_unlock(&ses->session_mutex);
goto out; goto out;
} }
spin_unlock(&cifs_tcp_ses_lock);
rc = cifs_negotiate_protocol(0, ses); /*
if (rc == 0 && ses->need_reconnect) * need to prevent multiple threads trying to simultaneously
rc = cifs_setup_session(0, ses, nls_codepage); * reconnect the same SMB session
*/
spin_lock(&ses->chan_lock);
if (!cifs_chan_needs_reconnect(ses, server)) {
spin_unlock(&ses->chan_lock);
/* this means that we only need to tree connect */
if (tcon->need_reconnect)
goto skip_sess_setup;
rc = -EHOSTDOWN;
goto out;
}
spin_unlock(&ses->chan_lock);
mutex_lock(&ses->session_mutex);
rc = cifs_negotiate_protocol(0, ses, server);
if (!rc)
rc = cifs_setup_session(0, ses, server, nls_codepage);
/* do we need to reconnect tcon? */ /* do we need to reconnect tcon? */
if (rc || !tcon->need_reconnect) { if (rc || !tcon->need_reconnect) {
...@@ -198,6 +238,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) ...@@ -198,6 +238,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command)
goto out; goto out;
} }
skip_sess_setup:
cifs_mark_open_files_invalid(tcon); cifs_mark_open_files_invalid(tcon);
rc = cifs_tree_connect(0, tcon, nls_codepage); rc = cifs_tree_connect(0, tcon, nls_codepage);
mutex_unlock(&ses->session_mutex); mutex_unlock(&ses->session_mutex);
...@@ -337,8 +378,13 @@ static int ...@@ -337,8 +378,13 @@ static int
smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon, smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon,
void **request_buf, void **response_buf) void **request_buf, void **response_buf)
{ {
if (tcon->ses->need_reconnect || tcon->need_reconnect) spin_lock(&tcon->ses->chan_lock);
if (cifs_chan_needs_reconnect(tcon->ses, tcon->ses->server) ||
tcon->need_reconnect) {
spin_unlock(&tcon->ses->chan_lock);
return -EHOSTDOWN; return -EHOSTDOWN;
}
spin_unlock(&tcon->ses->chan_lock);
return __smb_init(smb_command, wct, tcon, request_buf, response_buf); return __smb_init(smb_command, wct, tcon, request_buf, response_buf);
} }
...@@ -476,14 +522,15 @@ should_set_ext_sec_flag(enum securityEnum sectype) ...@@ -476,14 +522,15 @@ should_set_ext_sec_flag(enum securityEnum sectype)
} }
int 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_REQ *pSMB;
NEGOTIATE_RSP *pSMBr; NEGOTIATE_RSP *pSMBr;
int rc = 0; int rc = 0;
int bytes_returned; int bytes_returned;
int i; int i;
struct TCP_Server_Info *server = ses->server;
u16 count; u16 count;
if (!server) { if (!server) {
...@@ -600,8 +647,12 @@ CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -600,8 +647,12 @@ CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon)
* the tcon is no longer on the list, so no need to take lock before * the tcon is no longer on the list, so no need to take lock before
* checking this. * checking this.
*/ */
if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) spin_lock(&tcon->ses->chan_lock);
return 0; if ((tcon->need_reconnect) || CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses)) {
spin_unlock(&tcon->ses->chan_lock);
return -EIO;
}
spin_unlock(&tcon->ses->chan_lock);
rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
(void **)&smb_buffer); (void **)&smb_buffer);
...@@ -696,9 +747,14 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses) ...@@ -696,9 +747,14 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses)
return -EIO; return -EIO;
mutex_lock(&ses->session_mutex); mutex_lock(&ses->session_mutex);
if (ses->need_reconnect) spin_lock(&ses->chan_lock);
if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
spin_unlock(&ses->chan_lock);
goto session_already_dead; /* no need to send SMBlogoff if uid goto session_already_dead; /* no need to send SMBlogoff if uid
already closed due to reconnect */ already closed due to reconnect */
}
spin_unlock(&ses->chan_lock);
rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
if (rc) { if (rc) {
mutex_unlock(&ses->session_mutex); mutex_unlock(&ses->session_mutex);
...@@ -1401,7 +1457,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -1401,7 +1457,7 @@ cifs_readv_receive(struct TCP_Server_Info *server, struct mid_q_entry *mid)
if (server->ops->is_session_expired && if (server->ops->is_session_expired &&
server->ops->is_session_expired(buf)) { server->ops->is_session_expired(buf)) {
cifs_reconnect(server); cifs_reconnect(server, true);
return -1; return -1;
} }
......
...@@ -166,14 +166,17 @@ static void cifs_resolve_server(struct work_struct *work) ...@@ -166,14 +166,17 @@ static void cifs_resolve_server(struct work_struct *work)
* Mark all sessions and tcons for reconnect. * Mark all sessions and tcons for reconnect.
* *
* @server needs to be previously set to CifsNeedReconnect. * @server needs to be previously set to CifsNeedReconnect.
*
*/ */
static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server) static void
cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session)
{ {
struct TCP_Server_Info *pserver;
struct cifs_ses *ses; struct cifs_ses *ses;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
struct mid_q_entry *mid, *nmid; struct mid_q_entry *mid, *nmid;
struct list_head retry_list; struct list_head retry_list;
struct TCP_Server_Info *pserver;
server->maxBuf = 0; server->maxBuf = 0;
server->max_read = 0; server->max_read = 0;
...@@ -191,16 +194,37 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server ...@@ -191,16 +194,37 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) { list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
ses->need_reconnect = true; spin_lock(&ses->chan_lock);
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server))
goto next_session;
cifs_chan_set_need_reconnect(ses, server);
/* If all channels need reconnect, then tcon needs reconnect */
if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses))
goto next_session;
ses->status = CifsNeedReconnect;
list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
tcon->need_reconnect = true; tcon->need_reconnect = true;
tcon->tidStatus = CifsNeedReconnect;
}
if (ses->tcon_ipc) if (ses->tcon_ipc)
ses->tcon_ipc->need_reconnect = true; ses->tcon_ipc->need_reconnect = true;
next_session:
spin_unlock(&ses->chan_lock);
} }
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
/*
* 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 and tearing down socket\n",
__func__);
/* do not want to be sending data on a socket we are freeing */ /* 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); mutex_lock(&server->srv_mutex);
if (server->ssocket) { if (server->ssocket) {
cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state, cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state,
...@@ -248,16 +272,16 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server ...@@ -248,16 +272,16 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets) static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets)
{ {
spin_lock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock);
server->nr_targets = num_targets; server->nr_targets = num_targets;
if (server->tcpStatus == CifsExiting) { if (server->tcpStatus == CifsExiting) {
/* the demux thread will exit normally next time through the loop */ /* the demux thread will exit normally next time through the loop */
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
wake_up(&server->response_q); wake_up(&server->response_q);
return false; return false;
} }
server->tcpStatus = CifsNeedReconnect; server->tcpStatus = CifsNeedReconnect;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
return true; return true;
} }
...@@ -268,15 +292,21 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num ...@@ -268,15 +292,21 @@ static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num
* mark all smb sessions as reconnecting for tcp session * mark all smb sessions as reconnecting for tcp session
* reconnect tcp session * reconnect tcp session
* wake up waiters on reconnection? - (not needed currently) * wake up waiters on reconnection? - (not needed currently)
*
* if mark_smb_session is passed as true, unconditionally mark
* the smb session (and tcon) for reconnect as well. This value
* doesn't really matter for non-multichannel scenario.
*
*/ */
static int __cifs_reconnect(struct TCP_Server_Info *server) static int __cifs_reconnect(struct TCP_Server_Info *server,
bool mark_smb_session)
{ {
int rc = 0; int rc = 0;
if (!cifs_tcp_ses_needs_reconnect(server, 1)) if (!cifs_tcp_ses_needs_reconnect(server, 1))
return 0; return 0;
cifs_mark_tcp_ses_conns_for_reconnect(server); cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session);
do { do {
try_to_freeze(); try_to_freeze();
...@@ -299,10 +329,10 @@ static int __cifs_reconnect(struct TCP_Server_Info *server) ...@@ -299,10 +329,10 @@ static int __cifs_reconnect(struct TCP_Server_Info *server)
} else { } else {
atomic_inc(&tcpSesReconnectCount); atomic_inc(&tcpSesReconnectCount);
set_credits(server, 1); set_credits(server, 1);
spin_lock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus != CifsExiting) if (server->tcpStatus != CifsExiting)
server->tcpStatus = CifsNeedNegotiate; server->tcpStatus = CifsNeedNegotiate;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
cifs_swn_reset_server_dstaddr(server); cifs_swn_reset_server_dstaddr(server);
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
} }
...@@ -371,7 +401,9 @@ static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_ ...@@ -371,7 +401,9 @@ static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_
return rc; return rc;
} }
static int reconnect_dfs_server(struct TCP_Server_Info *server) static int
reconnect_dfs_server(struct TCP_Server_Info *server,
bool mark_smb_session)
{ {
int rc = 0; int rc = 0;
const char *refpath = server->current_fullpath + 1; const char *refpath = server->current_fullpath + 1;
...@@ -395,7 +427,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server) ...@@ -395,7 +427,7 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
if (!cifs_tcp_ses_needs_reconnect(server, num_targets)) if (!cifs_tcp_ses_needs_reconnect(server, num_targets))
return 0; return 0;
cifs_mark_tcp_ses_conns_for_reconnect(server); cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session);
do { do {
try_to_freeze(); try_to_freeze();
...@@ -416,10 +448,10 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server) ...@@ -416,10 +448,10 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
*/ */
atomic_inc(&tcpSesReconnectCount); atomic_inc(&tcpSesReconnectCount);
set_credits(server, 1); set_credits(server, 1);
spin_lock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus != CifsExiting) if (server->tcpStatus != CifsExiting)
server->tcpStatus = CifsNeedNegotiate; server->tcpStatus = CifsNeedNegotiate;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
cifs_swn_reset_server_dstaddr(server); cifs_swn_reset_server_dstaddr(server);
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
} while (server->tcpStatus == CifsNeedReconnect); } while (server->tcpStatus == CifsNeedReconnect);
...@@ -430,29 +462,32 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server) ...@@ -430,29 +462,32 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server)
dfs_cache_free_tgts(&tl); dfs_cache_free_tgts(&tl);
/* Need to set up echo worker again once connection has been established */ /* Need to set up echo worker again once connection has been established */
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsNeedNegotiate) if (server->tcpStatus == CifsNeedNegotiate)
mod_delayed_work(cifsiod_wq, &server->echo, 0); mod_delayed_work(cifsiod_wq, &server->echo, 0);
spin_unlock(&cifs_tcp_ses_lock);
wake_up(&server->response_q); wake_up(&server->response_q);
return rc; return rc;
} }
int cifs_reconnect(struct TCP_Server_Info *server) int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
{ {
/* If tcp session is not an dfs connection, then reconnect to last target server */ /* If tcp session is not an dfs connection, then reconnect to last target server */
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
if (!server->is_dfs_conn || !server->origin_fullpath || !server->leaf_fullpath) { if (!server->is_dfs_conn || !server->origin_fullpath || !server->leaf_fullpath) {
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return __cifs_reconnect(server); return __cifs_reconnect(server, mark_smb_session);
} }
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return reconnect_dfs_server(server); return reconnect_dfs_server(server, mark_smb_session);
} }
#else #else
int cifs_reconnect(struct TCP_Server_Info *server) int cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
{ {
return __cifs_reconnect(server); return __cifs_reconnect(server, mark_smb_session);
} }
#endif #endif
...@@ -534,15 +569,18 @@ server_unresponsive(struct TCP_Server_Info *server) ...@@ -534,15 +569,18 @@ server_unresponsive(struct TCP_Server_Info *server)
* 65s kernel_recvmsg times out, and we see that we haven't gotten * 65s kernel_recvmsg times out, and we see that we haven't gotten
* a response in >60s. * a response in >60s.
*/ */
spin_lock(&cifs_tcp_ses_lock);
if ((server->tcpStatus == CifsGood || if ((server->tcpStatus == CifsGood ||
server->tcpStatus == CifsNeedNegotiate) && server->tcpStatus == CifsNeedNegotiate) &&
(!server->ops->can_echo || server->ops->can_echo(server)) && (!server->ops->can_echo || server->ops->can_echo(server)) &&
time_after(jiffies, server->lstrp + 3 * server->echo_interval)) { time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
spin_unlock(&cifs_tcp_ses_lock);
cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n", cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
(3 * server->echo_interval) / HZ); (3 * server->echo_interval) / HZ);
cifs_reconnect(server); cifs_reconnect(server, false);
return true; return true;
} }
spin_unlock(&cifs_tcp_ses_lock);
return false; return false;
} }
...@@ -576,7 +614,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg) ...@@ -576,7 +614,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
/* reconnect if no credits and no requests in flight */ /* reconnect if no credits and no requests in flight */
if (zero_credits(server)) { if (zero_credits(server)) {
cifs_reconnect(server); cifs_reconnect(server, false);
return -ECONNABORTED; return -ECONNABORTED;
} }
...@@ -587,13 +625,18 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg) ...@@ -587,13 +625,18 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
else else
length = sock_recvmsg(server->ssocket, smb_msg, 0); length = sock_recvmsg(server->ssocket, smb_msg, 0);
if (server->tcpStatus == CifsExiting) spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&cifs_tcp_ses_lock);
return -ESHUTDOWN; return -ESHUTDOWN;
}
if (server->tcpStatus == CifsNeedReconnect) { if (server->tcpStatus == CifsNeedReconnect) {
cifs_reconnect(server); spin_unlock(&cifs_tcp_ses_lock);
cifs_reconnect(server, false);
return -ECONNABORTED; return -ECONNABORTED;
} }
spin_unlock(&cifs_tcp_ses_lock);
if (length == -ERESTARTSYS || if (length == -ERESTARTSYS ||
length == -EAGAIN || length == -EAGAIN ||
...@@ -610,7 +653,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg) ...@@ -610,7 +653,7 @@ cifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
if (length <= 0) { if (length <= 0) {
cifs_dbg(FYI, "Received no data or error: %d\n", length); cifs_dbg(FYI, "Received no data or error: %d\n", length);
cifs_reconnect(server); cifs_reconnect(server, false);
return -ECONNABORTED; return -ECONNABORTED;
} }
} }
...@@ -689,11 +732,11 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type) ...@@ -689,11 +732,11 @@ is_smb_response(struct TCP_Server_Info *server, unsigned char type)
* initialize frame). * initialize frame).
*/ */
cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT); cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
cifs_reconnect(server); cifs_reconnect(server, true);
break; break;
default: default:
cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type); cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type);
cifs_reconnect(server); cifs_reconnect(server, true);
} }
return false; return false;
...@@ -771,9 +814,9 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) ...@@ -771,9 +814,9 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
cancel_delayed_work_sync(&server->echo); cancel_delayed_work_sync(&server->echo);
cancel_delayed_work_sync(&server->resolve); cancel_delayed_work_sync(&server->resolve);
spin_lock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock);
server->tcpStatus = CifsExiting; server->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
wake_up_all(&server->response_q); wake_up_all(&server->response_q);
/* check if we have blocked requests that need to free */ /* check if we have blocked requests that need to free */
...@@ -866,7 +909,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -866,7 +909,7 @@ standard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) - if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
server->vals->header_preamble_size) { server->vals->header_preamble_size) {
cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length); cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
cifs_reconnect(server); cifs_reconnect(server, true);
return -ECONNABORTED; return -ECONNABORTED;
} }
...@@ -913,7 +956,7 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid) ...@@ -913,7 +956,7 @@ cifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
if (server->ops->is_session_expired && if (server->ops->is_session_expired &&
server->ops->is_session_expired(buf)) { server->ops->is_session_expired(buf)) {
cifs_reconnect(server); cifs_reconnect(server, true);
return -1; return -1;
} }
...@@ -1017,7 +1060,7 @@ cifs_demultiplex_thread(void *p) ...@@ -1017,7 +1060,7 @@ cifs_demultiplex_thread(void *p)
server->vals->header_preamble_size) { server->vals->header_preamble_size) {
cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n", cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n",
server->pdu_size); server->pdu_size);
cifs_reconnect(server); cifs_reconnect(server, true);
continue; continue;
} }
...@@ -1069,7 +1112,7 @@ cifs_demultiplex_thread(void *p) ...@@ -1069,7 +1112,7 @@ cifs_demultiplex_thread(void *p)
server->ops->is_status_io_timeout(buf)) { server->ops->is_status_io_timeout(buf)) {
num_io_timeout++; num_io_timeout++;
if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) { if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) {
cifs_reconnect(server); cifs_reconnect(server, false);
num_io_timeout = 0; num_io_timeout = 0;
continue; continue;
} }
...@@ -1390,9 +1433,9 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) ...@@ -1390,9 +1433,9 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
else else
cancel_delayed_work_sync(&server->reconnect); cancel_delayed_work_sync(&server->reconnect);
spin_lock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock);
server->tcpStatus = CifsExiting; server->tcpStatus = CifsExiting;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
cifs_crypto_secmech_release(server); cifs_crypto_secmech_release(server);
...@@ -1545,7 +1588,9 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx, ...@@ -1545,7 +1588,9 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
* to the struct since the kernel thread not created yet * to the struct since the kernel thread not created yet
* no need to spinlock this update of tcpStatus * no need to spinlock this update of tcpStatus
*/ */
spin_lock(&cifs_tcp_ses_lock);
tcp_ses->tcpStatus = CifsNeedNegotiate; tcp_ses->tcpStatus = CifsNeedNegotiate;
spin_unlock(&cifs_tcp_ses_lock);
if ((ctx->max_credits < 20) || (ctx->max_credits > 60000)) if ((ctx->max_credits < 20) || (ctx->max_credits > 60000))
tcp_ses->max_credits = SMB2_MAX_CREDITS_AVAILABLE; tcp_ses->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
...@@ -1762,15 +1807,13 @@ void cifs_put_smb_ses(struct cifs_ses *ses) ...@@ -1762,15 +1807,13 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
return; return;
} }
spin_unlock(&cifs_tcp_ses_lock);
/* ses_count can never go negative */ /* ses_count can never go negative */
WARN_ON(ses->ses_count < 0); WARN_ON(ses->ses_count < 0);
spin_lock(&GlobalMid_Lock);
if (ses->status == CifsGood) if (ses->status == CifsGood)
ses->status = CifsExiting; ses->status = CifsExiting;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
cifs_free_ipc(ses); cifs_free_ipc(ses);
...@@ -1987,11 +2030,13 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ...@@ -1987,11 +2030,13 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
cifs_dbg(FYI, "Existing smb sess found (status=%d)\n", cifs_dbg(FYI, "Existing smb sess found (status=%d)\n",
ses->status); ses->status);
mutex_lock(&ses->session_mutex); spin_lock(&ses->chan_lock);
if (ses->need_reconnect) { if (cifs_chan_needs_reconnect(ses, server)) {
spin_unlock(&ses->chan_lock);
cifs_dbg(FYI, "Session needs reconnect\n"); cifs_dbg(FYI, "Session needs reconnect\n");
rc = cifs_negotiate_protocol(xid, ses); mutex_lock(&ses->session_mutex);
rc = cifs_negotiate_protocol(xid, ses, server);
if (rc) { if (rc) {
mutex_unlock(&ses->session_mutex); mutex_unlock(&ses->session_mutex);
/* problem -- put our ses reference */ /* problem -- put our ses reference */
...@@ -2000,7 +2045,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ...@@ -2000,7 +2045,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
return ERR_PTR(rc); return ERR_PTR(rc);
} }
rc = cifs_setup_session(xid, ses, rc = cifs_setup_session(xid, ses, server,
ctx->local_nls); ctx->local_nls);
if (rc) { if (rc) {
mutex_unlock(&ses->session_mutex); mutex_unlock(&ses->session_mutex);
...@@ -2009,8 +2054,11 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ...@@ -2009,8 +2054,11 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
free_xid(xid); free_xid(xid);
return ERR_PTR(rc); return ERR_PTR(rc);
} }
mutex_unlock(&ses->session_mutex);
spin_lock(&ses->chan_lock);
} }
mutex_unlock(&ses->session_mutex); spin_unlock(&ses->chan_lock);
/* existing SMB ses has a server reference already */ /* existing SMB ses has a server reference already */
cifs_put_tcp_session(server, 0); cifs_put_tcp_session(server, 0);
...@@ -2060,28 +2108,33 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ...@@ -2060,28 +2108,33 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
ses->sectype = ctx->sectype; ses->sectype = ctx->sectype;
ses->sign = ctx->sign; ses->sign = ctx->sign;
mutex_lock(&ses->session_mutex);
/* add server as first channel */ /* add server as first channel */
spin_lock(&ses->chan_lock); spin_lock(&ses->chan_lock);
ses->chans[0].server = server; ses->chans[0].server = server;
ses->chan_count = 1; ses->chan_count = 1;
ses->chan_max = ctx->multichannel ? ctx->max_channels:1; ses->chan_max = ctx->multichannel ? ctx->max_channels:1;
ses->chans_need_reconnect = 1;
spin_unlock(&ses->chan_lock); spin_unlock(&ses->chan_lock);
rc = cifs_negotiate_protocol(xid, ses); mutex_lock(&ses->session_mutex);
rc = cifs_negotiate_protocol(xid, ses, server);
if (!rc) if (!rc)
rc = cifs_setup_session(xid, ses, ctx->local_nls); rc = cifs_setup_session(xid, ses, server, ctx->local_nls);
mutex_unlock(&ses->session_mutex);
/* each channel uses a different signing key */ /* each channel uses a different signing key */
memcpy(ses->chans[0].signkey, ses->smb3signingkey, memcpy(ses->chans[0].signkey, ses->smb3signingkey,
sizeof(ses->smb3signingkey)); sizeof(ses->smb3signingkey));
mutex_unlock(&ses->session_mutex);
if (rc) if (rc)
goto get_ses_fail; goto get_ses_fail;
/* success, put it on the list and add it as first channel */ /*
* success, put it on the list and add it as first channel
* note: the session becomes active soon after this. So you'll
* need to lock before changing something in the session.
*/
spin_lock(&cifs_tcp_ses_lock); spin_lock(&cifs_tcp_ses_lock);
list_add(&ses->smb_ses_list, &server->smb_ses_list); list_add(&ses->smb_ses_list, &server->smb_ses_list);
spin_unlock(&cifs_tcp_ses_lock); spin_unlock(&cifs_tcp_ses_lock);
...@@ -2161,6 +2214,9 @@ cifs_put_tcon(struct cifs_tcon *tcon) ...@@ -2161,6 +2214,9 @@ cifs_put_tcon(struct cifs_tcon *tcon)
/* tc_count can never go negative */ /* tc_count can never go negative */
WARN_ON(tcon->tc_count < 0); WARN_ON(tcon->tc_count < 0);
list_del_init(&tcon->tcon_list);
spin_unlock(&cifs_tcp_ses_lock);
if (tcon->use_witness) { if (tcon->use_witness) {
int rc; int rc;
...@@ -2171,9 +2227,6 @@ cifs_put_tcon(struct cifs_tcon *tcon) ...@@ -2171,9 +2227,6 @@ cifs_put_tcon(struct cifs_tcon *tcon)
} }
} }
list_del_init(&tcon->tcon_list);
spin_unlock(&cifs_tcp_ses_lock);
xid = get_xid(); xid = get_xid();
if (ses->server->ops->tree_disconnect) if (ses->server->ops->tree_disconnect)
ses->server->ops->tree_disconnect(xid, tcon); ses->server->ops->tree_disconnect(xid, tcon);
...@@ -2290,10 +2343,6 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx) ...@@ -2290,10 +2343,6 @@ cifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
} }
} }
/*
* BB Do we need to wrap session_mutex around this TCon call and Unix
* SetFS as we do on SessSetup and reconnect?
*/
xid = get_xid(); xid = get_xid();
rc = ses->server->ops->tree_connect(xid, ses, ctx->UNC, tcon, rc = ses->server->ops->tree_connect(xid, ses, ctx->UNC, tcon,
ctx->local_nls); ctx->local_nls);
...@@ -3029,12 +3078,15 @@ static int mount_get_conns(struct mount_ctx *mnt_ctx) ...@@ -3029,12 +3078,15 @@ static int mount_get_conns(struct mount_ctx *mnt_ctx)
* for just this mount. * for just this mount.
*/ */
reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx); reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx);
spin_lock(&cifs_tcp_ses_lock);
if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) &&
(le64_to_cpu(tcon->fsUnixInfo.Capability) & (le64_to_cpu(tcon->fsUnixInfo.Capability) &
CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) {
spin_unlock(&cifs_tcp_ses_lock);
rc = -EACCES; rc = -EACCES;
goto out; goto out;
} }
spin_unlock(&cifs_tcp_ses_lock);
} else } else
tcon->unix_ext = 0; /* server does not support them */ tcon->unix_ext = 0; /* server does not support them */
...@@ -3709,7 +3761,9 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses, ...@@ -3709,7 +3761,9 @@ CIFSTCon(const unsigned int xid, struct cifs_ses *ses,
if (rc == 0) { if (rc == 0) {
bool is_unicode; bool is_unicode;
spin_lock(&cifs_tcp_ses_lock);
tcon->tidStatus = CifsGood; tcon->tidStatus = CifsGood;
spin_unlock(&cifs_tcp_ses_lock);
tcon->need_reconnect = false; tcon->need_reconnect = false;
tcon->tid = smb_buffer_response->Tid; tcon->tid = smb_buffer_response->Tid;
bcc_ptr = pByteArea(smb_buffer_response); bcc_ptr = pByteArea(smb_buffer_response);
...@@ -3799,26 +3853,32 @@ cifs_umount(struct cifs_sb_info *cifs_sb) ...@@ -3799,26 +3853,32 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
} }
int 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; int rc = 0;
struct TCP_Server_Info *server = cifs_ses_server(ses);
if (!server->ops->need_neg || !server->ops->negotiate) if (!server->ops->need_neg || !server->ops->negotiate)
return -ENOSYS; return -ENOSYS;
/* only send once per connect */ /* only send once per connect */
if (!server->ops->need_neg(server)) spin_lock(&cifs_tcp_ses_lock);
if (!server->ops->need_neg(server) ||
server->tcpStatus != CifsNeedNegotiate) {
spin_unlock(&cifs_tcp_ses_lock);
return 0; return 0;
}
server->tcpStatus = CifsInNegotiate;
spin_unlock(&cifs_tcp_ses_lock);
rc = server->ops->negotiate(xid, ses); rc = server->ops->negotiate(xid, ses, server);
if (rc == 0) { if (rc == 0) {
spin_lock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsNeedNegotiate) if (server->tcpStatus == CifsInNegotiate)
server->tcpStatus = CifsGood; server->tcpStatus = CifsNeedSessSetup;
else else
rc = -EHOSTDOWN; rc = -EHOSTDOWN;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
} }
return rc; return rc;
...@@ -3826,12 +3886,26 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses) ...@@ -3826,12 +3886,26 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
int int
cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
struct nls_table *nls_info) struct nls_table *nls_info)
{ {
int rc = -ENOSYS; int rc = -ENOSYS;
struct TCP_Server_Info *server = cifs_ses_server(ses); bool is_binding = false;
if (!ses->binding) { /* only send once per connect */
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus != CifsNeedSessSetup) {
spin_unlock(&cifs_tcp_ses_lock);
return 0;
}
ses->status = CifsInSessSetup;
spin_unlock(&cifs_tcp_ses_lock);
spin_lock(&ses->chan_lock);
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
spin_unlock(&ses->chan_lock);
if (!is_binding) {
ses->capabilities = server->capabilities; ses->capabilities = server->capabilities;
if (!linuxExtEnabled) if (!linuxExtEnabled)
ses->capabilities &= (~server->vals->cap_unix); ses->capabilities &= (~server->vals->cap_unix);
...@@ -3849,7 +3923,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses, ...@@ -3849,7 +3923,7 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
server->sec_mode, server->capabilities, server->timeAdj); server->sec_mode, server->capabilities, server->timeAdj);
if (server->ops->sess_setup) 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) if (rc)
cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc); cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc);
...@@ -4197,6 +4271,17 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t ...@@ -4197,6 +4271,17 @@ static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *t
struct dfs_cache_tgt_iterator *tit; struct dfs_cache_tgt_iterator *tit;
bool target_match; bool target_match;
/* only send once per connect */
spin_lock(&cifs_tcp_ses_lock);
if (tcon->ses->status != CifsGood ||
(tcon->tidStatus != CifsNew &&
tcon->tidStatus != CifsNeedTcon)) {
spin_unlock(&cifs_tcp_ses_lock);
return 0;
}
tcon->tidStatus = CifsInTcon;
spin_unlock(&cifs_tcp_ses_lock);
extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
tit = dfs_cache_get_tgt_iterator(tl); tit = dfs_cache_get_tgt_iterator(tl);
...@@ -4355,6 +4440,17 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru ...@@ -4355,6 +4440,17 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
{ {
const struct smb_version_operations *ops = tcon->ses->server->ops; const struct smb_version_operations *ops = tcon->ses->server->ops;
/* only send once per connect */
spin_lock(&cifs_tcp_ses_lock);
if (tcon->ses->status != CifsGood ||
(tcon->tidStatus != CifsNew &&
tcon->tidStatus != CifsNeedTcon)) {
spin_unlock(&cifs_tcp_ses_lock);
return 0;
}
tcon->tidStatus = CifsInTcon;
spin_unlock(&cifs_tcp_ses_lock);
return ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc); return ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
} }
#endif #endif
...@@ -896,10 +896,10 @@ map_and_check_smb_error(struct mid_q_entry *mid, bool logErr) ...@@ -896,10 +896,10 @@ map_and_check_smb_error(struct mid_q_entry *mid, bool logErr)
if (class == ERRSRV && code == ERRbaduid) { if (class == ERRSRV && code == ERRbaduid) {
cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n", cifs_dbg(FYI, "Server returned 0x%x, reconnecting session...\n",
code); code);
spin_lock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock);
if (mid->server->tcpStatus != CifsExiting) if (mid->server->tcpStatus != CifsExiting)
mid->server->tcpStatus = CifsNeedReconnect; mid->server->tcpStatus = CifsNeedReconnect;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
} }
} }
......
...@@ -121,7 +121,9 @@ typedef struct _AUTHENTICATE_MESSAGE { ...@@ -121,7 +121,9 @@ typedef struct _AUTHENTICATE_MESSAGE {
int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses); int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len, struct cifs_ses *ses);
int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen, int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses, struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp); const struct nls_table *nls_cp);
int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen, int build_ntlmssp_auth_blob(unsigned char **pbuffer, u16 *buflen,
struct cifs_ses *ses, struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp); const struct nls_table *nls_cp);
...@@ -65,6 +65,53 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface) ...@@ -65,6 +65,53 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
return false; return false;
} }
unsigned int
cifs_ses_get_chan_index(struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
unsigned int i;
for (i = 0; i < ses->chan_count; i++) {
if (ses->chans[i].server == server)
return i;
}
/* If we didn't find the channel, it is likely a bug */
WARN_ON(1);
return 0;
}
void
cifs_chan_set_need_reconnect(struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
set_bit(chan_index, &ses->chans_need_reconnect);
cifs_dbg(FYI, "Set reconnect bitmask for chan %u; now 0x%lx\n",
chan_index, ses->chans_need_reconnect);
}
void
cifs_chan_clear_need_reconnect(struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
clear_bit(chan_index, &ses->chans_need_reconnect);
cifs_dbg(FYI, "Cleared reconnect bitmask for chan %u; now 0x%lx\n",
chan_index, ses->chans_need_reconnect);
}
bool
cifs_chan_needs_reconnect(struct cifs_ses *ses,
struct TCP_Server_Info *server)
{
unsigned int chan_index = cifs_ses_get_chan_index(ses, server);
return CIFS_CHAN_NEEDS_RECONNECT(ses, chan_index);
}
/* returns number of channels added */ /* returns number of channels added */
int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
{ {
...@@ -261,9 +308,8 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, ...@@ -261,9 +308,8 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
chan_server = cifs_get_tcp_session(&ctx, ses->server); chan_server = cifs_get_tcp_session(&ctx, ses->server);
mutex_lock(&ses->session_mutex);
spin_lock(&ses->chan_lock); spin_lock(&ses->chan_lock);
chan = ses->binding_chan = &ses->chans[ses->chan_count]; chan = &ses->chans[ses->chan_count];
chan->server = chan_server; chan->server = chan_server;
if (IS_ERR(chan->server)) { if (IS_ERR(chan->server)) {
rc = PTR_ERR(chan->server); rc = PTR_ERR(chan->server);
...@@ -271,8 +317,15 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, ...@@ -271,8 +317,15 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
spin_unlock(&ses->chan_lock); spin_unlock(&ses->chan_lock);
goto out; goto out;
} }
ses->chan_count++;
atomic_set(&ses->chan_seq, 0);
/* Mark this channel as needing connect/setup */
cifs_chan_set_need_reconnect(ses, chan->server);
spin_unlock(&ses->chan_lock); spin_unlock(&ses->chan_lock);
mutex_lock(&ses->session_mutex);
/* /*
* We need to allocate the server crypto now as we will need * We need to allocate the server crypto now as we will need
* to sign packets before we generate the channel signing key * to sign packets before we generate the channel signing key
...@@ -281,37 +334,29 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, ...@@ -281,37 +334,29 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
rc = smb311_crypto_shash_allocate(chan->server); rc = smb311_crypto_shash_allocate(chan->server);
if (rc) { if (rc) {
cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__); cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
mutex_unlock(&ses->session_mutex);
goto out; goto out;
} }
ses->binding = true; rc = cifs_negotiate_protocol(xid, ses, chan->server);
rc = cifs_negotiate_protocol(xid, ses); if (!rc)
if (rc) rc = cifs_setup_session(xid, ses, chan->server, cifs_sb->local_nls);
goto out;
rc = cifs_setup_session(xid, ses, cifs_sb->local_nls);
if (rc)
goto out;
/* success, put it on the list
* XXX: sharing ses between 2 tcp servers is not possible, the
* way "internal" linked lists works in linux makes element
* only able to belong to one list
*
* the binding session is already established so the rest of
* the code should be able to look it up, no need to add the
* ses to the new server.
*/
spin_lock(&ses->chan_lock); mutex_unlock(&ses->session_mutex);
ses->chan_count++;
atomic_set(&ses->chan_seq, 0);
spin_unlock(&ses->chan_lock);
out: out:
ses->binding = false; if (rc && chan->server) {
ses->binding_chan = NULL; spin_lock(&ses->chan_lock);
mutex_unlock(&ses->session_mutex); /* we rely on all bits beyond chan_count to be clear */
cifs_chan_clear_need_reconnect(ses, chan->server);
ses->chan_count--;
/*
* chan_count should never reach 0 as at least the primary
* channel is always allocated
*/
WARN_ON(ses->chan_count < 1);
spin_unlock(&ses->chan_lock);
}
if (rc && chan->server) if (rc && chan->server)
cifs_put_tcp_session(chan->server, 0); cifs_put_tcp_session(chan->server, 0);
...@@ -325,14 +370,16 @@ void cifs_ses_mark_for_reconnect(struct cifs_ses *ses) ...@@ -325,14 +370,16 @@ void cifs_ses_mark_for_reconnect(struct cifs_ses *ses)
int i; int i;
for (i = 0; i < ses->chan_count; i++) { for (i = 0; i < ses->chan_count; i++) {
spin_lock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock);
if (ses->chans[i].server->tcpStatus != CifsExiting) if (ses->chans[i].server->tcpStatus != CifsExiting)
ses->chans[i].server->tcpStatus = CifsNeedReconnect; ses->chans[i].server->tcpStatus = CifsNeedReconnect;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
} }
} }
static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB) static __u32 cifs_ssetup_hdr(struct cifs_ses *ses,
struct TCP_Server_Info *server,
SESSION_SETUP_ANDX *pSMB)
{ {
__u32 capabilities = 0; __u32 capabilities = 0;
...@@ -345,7 +392,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB) ...@@ -345,7 +392,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32, pSMB->req.MaxBufferSize = cpu_to_le16(min_t(u32,
CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4, CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4,
USHRT_MAX)); USHRT_MAX));
pSMB->req.MaxMpxCount = cpu_to_le16(ses->server->maxReq); pSMB->req.MaxMpxCount = cpu_to_le16(server->maxReq);
pSMB->req.VcNumber = cpu_to_le16(1); pSMB->req.VcNumber = cpu_to_le16(1);
/* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */ /* Now no need to set SMBFLG_CASELESS or obsolete CANONICAL PATH */
...@@ -356,7 +403,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB) ...@@ -356,7 +403,7 @@ static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS | capabilities = CAP_LARGE_FILES | CAP_NT_SMBS | CAP_LEVEL_II_OPLOCKS |
CAP_LARGE_WRITE_X | CAP_LARGE_READ_X; CAP_LARGE_WRITE_X | CAP_LARGE_READ_X;
if (ses->server->sign) if (server->sign)
pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE; pSMB->req.hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
if (ses->capabilities & CAP_UNICODE) { if (ses->capabilities & CAP_UNICODE) {
...@@ -719,10 +766,10 @@ static inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf, ...@@ -719,10 +766,10 @@ static inline void cifs_security_buffer_from_str(SECURITY_BUFFER *pbuf,
int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, int build_ntlmssp_negotiate_blob(unsigned char **pbuffer,
u16 *buflen, u16 *buflen,
struct cifs_ses *ses, struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp) const struct nls_table *nls_cp)
{ {
int rc = 0; int rc = 0;
struct TCP_Server_Info *server = cifs_ses_server(ses);
NEGOTIATE_MESSAGE *sec_blob; NEGOTIATE_MESSAGE *sec_blob;
__u32 flags; __u32 flags;
unsigned char *tmp; unsigned char *tmp;
...@@ -776,6 +823,7 @@ int build_ntlmssp_negotiate_blob(unsigned char **pbuffer, ...@@ -776,6 +823,7 @@ int build_ntlmssp_negotiate_blob(unsigned char **pbuffer,
int build_ntlmssp_auth_blob(unsigned char **pbuffer, int build_ntlmssp_auth_blob(unsigned char **pbuffer,
u16 *buflen, u16 *buflen,
struct cifs_ses *ses, struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp) const struct nls_table *nls_cp)
{ {
int rc; int rc;
...@@ -912,6 +960,7 @@ cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) ...@@ -912,6 +960,7 @@ cifs_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
struct sess_data { struct sess_data {
unsigned int xid; unsigned int xid;
struct cifs_ses *ses; struct cifs_ses *ses;
struct TCP_Server_Info *server;
struct nls_table *nls_cp; struct nls_table *nls_cp;
void (*func)(struct sess_data *); void (*func)(struct sess_data *);
int result; int result;
...@@ -978,30 +1027,36 @@ static int ...@@ -978,30 +1027,36 @@ static int
sess_establish_session(struct sess_data *sess_data) sess_establish_session(struct sess_data *sess_data)
{ {
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
mutex_lock(&ses->server->srv_mutex); mutex_lock(&server->srv_mutex);
if (!ses->server->session_estab) { if (!server->session_estab) {
if (ses->server->sign) { if (server->sign) {
ses->server->session_key.response = server->session_key.response =
kmemdup(ses->auth_key.response, kmemdup(ses->auth_key.response,
ses->auth_key.len, GFP_KERNEL); ses->auth_key.len, GFP_KERNEL);
if (!ses->server->session_key.response) { if (!server->session_key.response) {
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&server->srv_mutex);
return -ENOMEM; return -ENOMEM;
} }
ses->server->session_key.len = server->session_key.len =
ses->auth_key.len; ses->auth_key.len;
} }
ses->server->sequence_number = 0x2; server->sequence_number = 0x2;
ses->server->session_estab = true; server->session_estab = true;
} }
mutex_unlock(&ses->server->srv_mutex); mutex_unlock(&server->srv_mutex);
cifs_dbg(FYI, "CIFS session established successfully\n"); cifs_dbg(FYI, "CIFS session established successfully\n");
spin_lock(&GlobalMid_Lock); spin_lock(&ses->chan_lock);
cifs_chan_clear_need_reconnect(ses, server);
spin_unlock(&ses->chan_lock);
/* Even if one channel is active, session is in good state */
spin_lock(&cifs_tcp_ses_lock);
server->tcpStatus = CifsGood;
ses->status = CifsGood; ses->status = CifsGood;
ses->need_reconnect = false; spin_unlock(&cifs_tcp_ses_lock);
spin_unlock(&GlobalMid_Lock);
return 0; return 0;
} }
...@@ -1036,6 +1091,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data) ...@@ -1036,6 +1091,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data)
SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMB;
char *bcc_ptr; char *bcc_ptr;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
__u32 capabilities; __u32 capabilities;
__u16 bytes_remaining; __u16 bytes_remaining;
...@@ -1047,7 +1103,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data) ...@@ -1047,7 +1103,7 @@ sess_auth_ntlmv2(struct sess_data *sess_data)
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
bcc_ptr = sess_data->iov[2].iov_base; bcc_ptr = sess_data->iov[2].iov_base;
capabilities = cifs_ssetup_hdr(ses, pSMB); capabilities = cifs_ssetup_hdr(ses, server, pSMB);
pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities); pSMB->req_no_secext.Capabilities = cpu_to_le32(capabilities);
...@@ -1145,6 +1201,7 @@ sess_auth_kerberos(struct sess_data *sess_data) ...@@ -1145,6 +1201,7 @@ sess_auth_kerberos(struct sess_data *sess_data)
SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMB;
char *bcc_ptr; char *bcc_ptr;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
__u32 capabilities; __u32 capabilities;
__u16 bytes_remaining; __u16 bytes_remaining;
struct key *spnego_key = NULL; struct key *spnego_key = NULL;
...@@ -1159,9 +1216,9 @@ sess_auth_kerberos(struct sess_data *sess_data) ...@@ -1159,9 +1216,9 @@ sess_auth_kerberos(struct sess_data *sess_data)
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
bcc_ptr = sess_data->iov[2].iov_base; bcc_ptr = sess_data->iov[2].iov_base;
capabilities = cifs_ssetup_hdr(ses, pSMB); capabilities = cifs_ssetup_hdr(ses, server, pSMB);
spnego_key = cifs_get_spnego_key(ses); spnego_key = cifs_get_spnego_key(ses, server);
if (IS_ERR(spnego_key)) { if (IS_ERR(spnego_key)) {
rc = PTR_ERR(spnego_key); rc = PTR_ERR(spnego_key);
spnego_key = NULL; spnego_key = NULL;
...@@ -1285,12 +1342,13 @@ _sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data) ...@@ -1285,12 +1342,13 @@ _sess_auth_rawntlmssp_assemble_req(struct sess_data *sess_data)
{ {
SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMB;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
__u32 capabilities; __u32 capabilities;
char *bcc_ptr; char *bcc_ptr;
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
capabilities = cifs_ssetup_hdr(ses, pSMB); capabilities = cifs_ssetup_hdr(ses, server, pSMB);
if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) { if ((pSMB->req.hdr.Flags2 & SMBFLG2_UNICODE) == 0) {
cifs_dbg(VFS, "NTLMSSP requires Unicode support\n"); cifs_dbg(VFS, "NTLMSSP requires Unicode support\n");
return -ENOSYS; return -ENOSYS;
...@@ -1324,6 +1382,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data) ...@@ -1324,6 +1382,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
struct smb_hdr *smb_buf; struct smb_hdr *smb_buf;
SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMB;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
__u16 bytes_remaining; __u16 bytes_remaining;
char *bcc_ptr; char *bcc_ptr;
unsigned char *ntlmsspblob = NULL; unsigned char *ntlmsspblob = NULL;
...@@ -1351,7 +1410,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data) ...@@ -1351,7 +1410,7 @@ sess_auth_rawntlmssp_negotiate(struct sess_data *sess_data)
/* Build security blob before we assemble the request */ /* Build security blob before we assemble the request */
rc = build_ntlmssp_negotiate_blob(&ntlmsspblob, rc = build_ntlmssp_negotiate_blob(&ntlmsspblob,
&blob_len, ses, &blob_len, ses, server,
sess_data->nls_cp); sess_data->nls_cp);
if (rc) if (rc)
goto out; goto out;
...@@ -1426,6 +1485,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) ...@@ -1426,6 +1485,7 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
struct smb_hdr *smb_buf; struct smb_hdr *smb_buf;
SESSION_SETUP_ANDX *pSMB; SESSION_SETUP_ANDX *pSMB;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
__u16 bytes_remaining; __u16 bytes_remaining;
char *bcc_ptr; char *bcc_ptr;
unsigned char *ntlmsspblob = NULL; unsigned char *ntlmsspblob = NULL;
...@@ -1442,7 +1502,8 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) ...@@ -1442,7 +1502,8 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base; pSMB = (SESSION_SETUP_ANDX *)sess_data->iov[0].iov_base;
smb_buf = (struct smb_hdr *)pSMB; smb_buf = (struct smb_hdr *)pSMB;
rc = build_ntlmssp_auth_blob(&ntlmsspblob, rc = build_ntlmssp_auth_blob(&ntlmsspblob,
&blob_len, ses, sess_data->nls_cp); &blob_len, ses, server,
sess_data->nls_cp);
if (rc) if (rc)
goto out_free_ntlmsspblob; goto out_free_ntlmsspblob;
sess_data->iov[1].iov_len = blob_len; sess_data->iov[1].iov_len = blob_len;
...@@ -1526,11 +1587,13 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data) ...@@ -1526,11 +1587,13 @@ sess_auth_rawntlmssp_authenticate(struct sess_data *sess_data)
sess_data->result = rc; sess_data->result = rc;
} }
static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data) static int select_sec(struct sess_data *sess_data)
{ {
int type; int type;
struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
type = cifs_select_sectype(ses->server, ses->sectype); type = cifs_select_sectype(server, ses->sectype);
cifs_dbg(FYI, "sess setup type %d\n", type); cifs_dbg(FYI, "sess setup type %d\n", type);
if (type == Unspecified) { if (type == Unspecified) {
cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); cifs_dbg(VFS, "Unable to select appropriate authentication method!\n");
...@@ -1561,7 +1624,8 @@ static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data) ...@@ -1561,7 +1624,8 @@ static int select_sec(struct cifs_ses *ses, struct sess_data *sess_data)
} }
int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
const struct nls_table *nls_cp) struct TCP_Server_Info *server,
const struct nls_table *nls_cp)
{ {
int rc = 0; int rc = 0;
struct sess_data *sess_data; struct sess_data *sess_data;
...@@ -1575,15 +1639,16 @@ int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses, ...@@ -1575,15 +1639,16 @@ int CIFS_SessSetup(const unsigned int xid, struct cifs_ses *ses,
if (!sess_data) if (!sess_data)
return -ENOMEM; return -ENOMEM;
rc = select_sec(ses, sess_data);
if (rc)
goto out;
sess_data->xid = xid; sess_data->xid = xid;
sess_data->ses = ses; sess_data->ses = ses;
sess_data->server = server;
sess_data->buf0_type = CIFS_NO_BUFFER; sess_data->buf0_type = CIFS_NO_BUFFER;
sess_data->nls_cp = (struct nls_table *) nls_cp; sess_data->nls_cp = (struct nls_table *) nls_cp;
rc = select_sec(sess_data);
if (rc)
goto out;
while (sess_data->func) while (sess_data->func)
sess_data->func(sess_data); sess_data->func(sess_data);
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/pagemap.h> #include <linux/pagemap.h>
#include <linux/vfs.h> #include <linux/vfs.h>
#include <uapi/linux/magic.h>
#include "cifsglob.h" #include "cifsglob.h"
#include "cifsproto.h" #include "cifsproto.h"
#include "cifs_debug.h" #include "cifs_debug.h"
...@@ -163,7 +164,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server) ...@@ -163,7 +164,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
{ {
__u64 mid = 0; __u64 mid = 0;
__u16 last_mid, cur_mid; __u16 last_mid, cur_mid;
bool collision; bool collision, reconnect = false;
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
...@@ -215,7 +216,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server) ...@@ -215,7 +216,7 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
* an eventual reconnect to clean out the pending_mid_q. * an eventual reconnect to clean out the pending_mid_q.
*/ */
if (num_mids > 32768) if (num_mids > 32768)
server->tcpStatus = CifsNeedReconnect; reconnect = true;
if (!collision) { if (!collision) {
mid = (__u64)cur_mid; mid = (__u64)cur_mid;
...@@ -225,6 +226,13 @@ cifs_get_next_mid(struct TCP_Server_Info *server) ...@@ -225,6 +226,13 @@ cifs_get_next_mid(struct TCP_Server_Info *server)
cur_mid++; cur_mid++;
} }
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
if (reconnect) {
spin_lock(&cifs_tcp_ses_lock);
server->tcpStatus = CifsNeedReconnect;
spin_unlock(&cifs_tcp_ses_lock);
}
return mid; return mid;
} }
...@@ -414,14 +422,16 @@ cifs_need_neg(struct TCP_Server_Info *server) ...@@ -414,14 +422,16 @@ cifs_need_neg(struct TCP_Server_Info *server)
} }
static int static int
cifs_negotiate(const unsigned int xid, struct cifs_ses *ses) cifs_negotiate(const unsigned int xid,
struct cifs_ses *ses,
struct TCP_Server_Info *server)
{ {
int rc; int rc;
rc = CIFSSMBNegotiate(xid, ses); rc = CIFSSMBNegotiate(xid, ses, server);
if (rc == -EAGAIN) { if (rc == -EAGAIN) {
/* retry only once on 1st time connection */ /* retry only once on 1st time connection */
set_credits(ses->server, 1); set_credits(server, 1);
rc = CIFSSMBNegotiate(xid, ses); rc = CIFSSMBNegotiate(xid, ses, server);
if (rc == -EAGAIN) if (rc == -EAGAIN)
rc = -EHOSTDOWN; rc = -EHOSTDOWN;
} }
...@@ -878,7 +888,7 @@ cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -878,7 +888,7 @@ cifs_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
{ {
int rc = -EOPNOTSUPP; int rc = -EOPNOTSUPP;
buf->f_type = CIFS_MAGIC_NUMBER; buf->f_type = CIFS_SUPER_MAGIC;
/* /*
* We could add a second check for a QFS Unix capability bit * We could add a second check for a QFS Unix capability bit
......
...@@ -13,8 +13,6 @@ ...@@ -13,8 +13,6 @@
#ifndef _SMB2_GLOB_H #ifndef _SMB2_GLOB_H
#define _SMB2_GLOB_H #define _SMB2_GLOB_H
#define SMB2_MAGIC_NUMBER 0xFE534D42
/* /*
***************************************************************** *****************************************************************
* Constants go here * Constants go here
......
...@@ -847,16 +847,17 @@ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *serve ...@@ -847,16 +847,17 @@ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *serve
* SMB2 header. * SMB2 header.
* *
* @ses: server session structure * @ses: server session structure
* @server: pointer to server info
* @iov: array containing the SMB request we will send to the server * @iov: array containing the SMB request we will send to the server
* @nvec: number of array entries for the iov * @nvec: number of array entries for the iov
*/ */
int int
smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec) smb311_update_preauth_hash(struct cifs_ses *ses, struct TCP_Server_Info *server,
struct kvec *iov, int nvec)
{ {
int i, rc; int i, rc;
struct sdesc *d; struct sdesc *d;
struct smb2_hdr *hdr; struct smb2_hdr *hdr;
struct TCP_Server_Info *server = cifs_ses_server(ses);
hdr = (struct smb2_hdr *)iov[0].iov_base; hdr = (struct smb2_hdr *)iov[0].iov_base;
/* neg prot are always taken */ /* neg prot are always taken */
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#include <linux/sort.h> #include <linux/sort.h>
#include <crypto/aead.h> #include <crypto/aead.h>
#include <linux/fiemap.h> #include <linux/fiemap.h>
#include <uapi/linux/magic.h>
#include "cifsfs.h" #include "cifsfs.h"
#include "cifsglob.h" #include "cifsglob.h"
#include "smb2pdu.h" #include "smb2pdu.h"
...@@ -121,9 +122,13 @@ smb2_add_credits(struct TCP_Server_Info *server, ...@@ -121,9 +122,13 @@ smb2_add_credits(struct TCP_Server_Info *server,
optype, scredits, add); optype, scredits, add);
} }
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsNeedReconnect if (server->tcpStatus == CifsNeedReconnect
|| server->tcpStatus == CifsExiting) || server->tcpStatus == CifsExiting) {
spin_unlock(&cifs_tcp_ses_lock);
return; return;
}
spin_unlock(&cifs_tcp_ses_lock);
switch (rc) { switch (rc) {
case -1: case -1:
...@@ -208,11 +213,15 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, ...@@ -208,11 +213,15 @@ smb2_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
return rc; return rc;
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
} else { } else {
spin_unlock(&server->req_lock);
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsExiting) { if (server->tcpStatus == CifsExiting) {
spin_unlock(&server->req_lock); spin_unlock(&cifs_tcp_ses_lock);
return -ENOENT; return -ENOENT;
} }
spin_unlock(&cifs_tcp_ses_lock);
spin_lock(&server->req_lock);
scredits = server->credits; scredits = server->credits;
/* can deadlock with reopen */ /* can deadlock with reopen */
if (scredits <= 8) { if (scredits <= 8) {
...@@ -384,14 +393,16 @@ smb2_need_neg(struct TCP_Server_Info *server) ...@@ -384,14 +393,16 @@ smb2_need_neg(struct TCP_Server_Info *server)
} }
static int static int
smb2_negotiate(const unsigned int xid, struct cifs_ses *ses) smb2_negotiate(const unsigned int xid,
struct cifs_ses *ses,
struct TCP_Server_Info *server)
{ {
int rc; int rc;
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
cifs_ses_server(ses)->CurrentMid = 0; server->CurrentMid = 0;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
rc = SMB2_negotiate(xid, ses); rc = SMB2_negotiate(xid, ses, server);
/* BB we probably don't need to retry with modern servers */ /* BB we probably don't need to retry with modern servers */
if (rc == -EAGAIN) if (rc == -EAGAIN)
rc = -EHOSTDOWN; rc = -EHOSTDOWN;
...@@ -2747,7 +2758,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2747,7 +2758,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
goto qfs_exit; goto qfs_exit;
rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base; rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
buf->f_type = SMB2_MAGIC_NUMBER; buf->f_type = SMB2_SUPER_MAGIC;
info = (struct smb2_fs_full_size_info *)( info = (struct smb2_fs_full_size_info *)(
le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp); le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset), rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
...@@ -2789,7 +2800,7 @@ smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -2789,7 +2800,7 @@ smb311_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid, rc = SMB311_posix_qfs_info(xid, tcon, fid.persistent_fid,
fid.volatile_fid, buf); fid.volatile_fid, buf);
buf->f_type = SMB2_MAGIC_NUMBER; buf->f_type = SMB2_SUPER_MAGIC;
SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid); SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
return rc; return rc;
} }
...@@ -4808,7 +4819,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, ...@@ -4808,7 +4819,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid,
if (server->ops->is_session_expired && if (server->ops->is_session_expired &&
server->ops->is_session_expired(buf)) { server->ops->is_session_expired(buf)) {
if (!is_offloaded) if (!is_offloaded)
cifs_reconnect(server); cifs_reconnect(server, true);
return -1; return -1;
} }
...@@ -4981,10 +4992,12 @@ static void smb2_decrypt_offload(struct work_struct *work) ...@@ -4981,10 +4992,12 @@ static void smb2_decrypt_offload(struct work_struct *work)
mid->callback(mid); mid->callback(mid);
} else { } else {
spin_lock(&cifs_tcp_ses_lock);
spin_lock(&GlobalMid_Lock); spin_lock(&GlobalMid_Lock);
if (dw->server->tcpStatus == CifsNeedReconnect) { if (dw->server->tcpStatus == CifsNeedReconnect) {
mid->mid_state = MID_RETRY_NEEDED; mid->mid_state = MID_RETRY_NEEDED;
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
spin_unlock(&cifs_tcp_ses_lock);
mid->callback(mid); mid->callback(mid);
} else { } else {
mid->mid_state = MID_REQUEST_SUBMITTED; mid->mid_state = MID_REQUEST_SUBMITTED;
...@@ -4992,6 +5005,7 @@ static void smb2_decrypt_offload(struct work_struct *work) ...@@ -4992,6 +5005,7 @@ static void smb2_decrypt_offload(struct work_struct *work)
list_add_tail(&mid->qhead, list_add_tail(&mid->qhead,
&dw->server->pending_mid_q); &dw->server->pending_mid_q);
spin_unlock(&GlobalMid_Lock); spin_unlock(&GlobalMid_Lock);
spin_unlock(&cifs_tcp_ses_lock);
} }
} }
cifs_mid_q_entry_release(mid); cifs_mid_q_entry_release(mid);
...@@ -5221,13 +5235,13 @@ smb3_receive_transform(struct TCP_Server_Info *server, ...@@ -5221,13 +5235,13 @@ smb3_receive_transform(struct TCP_Server_Info *server,
sizeof(struct smb2_hdr)) { sizeof(struct smb2_hdr)) {
cifs_server_dbg(VFS, "Transform message is too small (%u)\n", cifs_server_dbg(VFS, "Transform message is too small (%u)\n",
pdu_length); pdu_length);
cifs_reconnect(server); cifs_reconnect(server, true);
return -ECONNABORTED; return -ECONNABORTED;
} }
if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) { if (pdu_length < orig_len + sizeof(struct smb2_transform_hdr)) {
cifs_server_dbg(VFS, "Transform message is broken\n"); cifs_server_dbg(VFS, "Transform message is broken\n");
cifs_reconnect(server); cifs_reconnect(server, true);
return -ECONNABORTED; return -ECONNABORTED;
} }
......
...@@ -162,6 +162,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -162,6 +162,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL)
return 0; return 0;
spin_lock(&cifs_tcp_ses_lock);
if (tcon->tidStatus == CifsExiting) { if (tcon->tidStatus == CifsExiting) {
/* /*
* only tree disconnect, open, and write, * only tree disconnect, open, and write,
...@@ -171,11 +172,13 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -171,11 +172,13 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
if ((smb2_command != SMB2_WRITE) && if ((smb2_command != SMB2_WRITE) &&
(smb2_command != SMB2_CREATE) && (smb2_command != SMB2_CREATE) &&
(smb2_command != SMB2_TREE_DISCONNECT)) { (smb2_command != SMB2_TREE_DISCONNECT)) {
spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "can not send cmd %d while umounting\n", cifs_dbg(FYI, "can not send cmd %d while umounting\n",
smb2_command); smb2_command);
return -ENODEV; return -ENODEV;
} }
} }
spin_unlock(&cifs_tcp_ses_lock);
if ((!tcon->ses) || (tcon->ses->status == CifsExiting) || if ((!tcon->ses) || (tcon->ses->status == CifsExiting) ||
(!tcon->ses->server) || !server) (!tcon->ses->server) || !server)
return -EIO; return -EIO;
...@@ -214,8 +217,12 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -214,8 +217,12 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
} }
/* are we still trying to reconnect? */ /* are we still trying to reconnect? */
if (server->tcpStatus != CifsNeedReconnect) spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus != CifsNeedReconnect) {
spin_unlock(&cifs_tcp_ses_lock);
break; break;
}
spin_unlock(&cifs_tcp_ses_lock);
if (retries && --retries) if (retries && --retries)
continue; continue;
...@@ -232,64 +239,70 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, ...@@ -232,64 +239,70 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
retries = server->nr_targets; retries = server->nr_targets;
} }
if (!tcon->ses->need_reconnect && !tcon->need_reconnect) spin_lock(&ses->chan_lock);
if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
spin_unlock(&ses->chan_lock);
return 0; return 0;
}
cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d",
tcon->ses->chans_need_reconnect,
tcon->need_reconnect);
spin_unlock(&ses->chan_lock);
nls_codepage = load_nls_default(); nls_codepage = load_nls_default();
/*
* need to prevent multiple threads trying to simultaneously reconnect
* the same SMB session
*/
mutex_lock(&tcon->ses->session_mutex);
/* /*
* Recheck after acquire mutex. If another thread is negotiating * Recheck after acquire mutex. If another thread is negotiating
* and the server never sends an answer the socket will be closed * and the server never sends an answer the socket will be closed
* and tcpStatus set to reconnect. * and tcpStatus set to reconnect.
*/ */
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsNeedReconnect) { if (server->tcpStatus == CifsNeedReconnect) {
spin_unlock(&cifs_tcp_ses_lock);
rc = -EHOSTDOWN; rc = -EHOSTDOWN;
mutex_unlock(&tcon->ses->session_mutex);
goto out; goto out;
} }
spin_unlock(&cifs_tcp_ses_lock);
/* /*
* If we are reconnecting an extra channel, bind * need to prevent multiple threads trying to simultaneously
* reconnect the same SMB session
*/ */
if (CIFS_SERVER_IS_CHAN(server)) { spin_lock(&ses->chan_lock);
ses->binding = true; if (!cifs_chan_needs_reconnect(ses, server)) {
ses->binding_chan = cifs_ses_find_chan(ses, server); spin_unlock(&ses->chan_lock);
/* this means that we only need to tree connect */
if (tcon->need_reconnect)
goto skip_sess_setup;
goto out;
} }
spin_unlock(&ses->chan_lock);
rc = cifs_negotiate_protocol(0, tcon->ses); mutex_lock(&ses->session_mutex);
if (!rc && tcon->ses->need_reconnect) { rc = cifs_negotiate_protocol(0, ses, server);
rc = cifs_setup_session(0, tcon->ses, nls_codepage); if (!rc) {
rc = cifs_setup_session(0, ses, server, nls_codepage);
if ((rc == -EACCES) && !tcon->retry) { if ((rc == -EACCES) && !tcon->retry) {
mutex_unlock(&ses->session_mutex);
rc = -EHOSTDOWN; rc = -EHOSTDOWN;
ses->binding = false;
ses->binding_chan = NULL;
mutex_unlock(&tcon->ses->session_mutex);
goto failed; goto failed;
} }
} }
/*
* End of channel binding
*/
ses->binding = false;
ses->binding_chan = NULL;
if (rc || !tcon->need_reconnect) { if (rc || !tcon->need_reconnect) {
mutex_unlock(&tcon->ses->session_mutex); mutex_unlock(&ses->session_mutex);
goto out; goto out;
} }
skip_sess_setup:
cifs_mark_open_files_invalid(tcon); cifs_mark_open_files_invalid(tcon);
if (tcon->use_persistent) if (tcon->use_persistent)
tcon->need_reopen_files = true; tcon->need_reopen_files = true;
rc = cifs_tree_connect(0, tcon, nls_codepage); rc = cifs_tree_connect(0, tcon, nls_codepage);
mutex_unlock(&tcon->ses->session_mutex); mutex_unlock(&ses->session_mutex);
cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc); cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
if (rc) { if (rc) {
...@@ -833,7 +846,9 @@ add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode) ...@@ -833,7 +846,9 @@ add_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode)
*/ */
int int
SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) SMB2_negotiate(const unsigned int xid,
struct cifs_ses *ses,
struct TCP_Server_Info *server)
{ {
struct smb_rqst rqst; struct smb_rqst rqst;
struct smb2_negotiate_req *req; struct smb2_negotiate_req *req;
...@@ -842,7 +857,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses) ...@@ -842,7 +857,6 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
struct kvec rsp_iov; struct kvec rsp_iov;
int rc = 0; int rc = 0;
int resp_buftype; int resp_buftype;
struct TCP_Server_Info *server = cifs_ses_server(ses);
int blob_offset, blob_length; int blob_offset, blob_length;
char *security_blob; char *security_blob;
int flags = CIFS_NEG_OP; int flags = CIFS_NEG_OP;
...@@ -1221,6 +1235,7 @@ smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested) ...@@ -1221,6 +1235,7 @@ smb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
struct SMB2_sess_data { struct SMB2_sess_data {
unsigned int xid; unsigned int xid;
struct cifs_ses *ses; struct cifs_ses *ses;
struct TCP_Server_Info *server;
struct nls_table *nls_cp; struct nls_table *nls_cp;
void (*func)(struct SMB2_sess_data *); void (*func)(struct SMB2_sess_data *);
int result; int result;
...@@ -1242,9 +1257,10 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) ...@@ -1242,9 +1257,10 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
{ {
int rc; int rc;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
struct smb2_sess_setup_req *req; struct smb2_sess_setup_req *req;
struct TCP_Server_Info *server = cifs_ses_server(ses);
unsigned int total_len; unsigned int total_len;
bool is_binding = false;
rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server, rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server,
(void **) &req, (void **) &req,
...@@ -1252,11 +1268,16 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) ...@@ -1252,11 +1268,16 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
if (rc) if (rc)
return rc; return rc;
if (sess_data->ses->binding) { spin_lock(&ses->chan_lock);
req->hdr.SessionId = cpu_to_le64(sess_data->ses->Suid); is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
spin_unlock(&ses->chan_lock);
if (is_binding) {
req->hdr.SessionId = cpu_to_le64(ses->Suid);
req->hdr.Flags |= SMB2_FLAGS_SIGNED; req->hdr.Flags |= SMB2_FLAGS_SIGNED;
req->PreviousSessionId = 0; req->PreviousSessionId = 0;
req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; req->Flags = SMB2_SESSION_REQ_FLAG_BINDING;
cifs_dbg(FYI, "Binding to sess id: %llx\n", ses->Suid);
} else { } else {
/* First session, not a reauthenticate */ /* First session, not a reauthenticate */
req->hdr.SessionId = 0; req->hdr.SessionId = 0;
...@@ -1266,6 +1287,8 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) ...@@ -1266,6 +1287,8 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
*/ */
req->PreviousSessionId = cpu_to_le64(sess_data->previous_session); req->PreviousSessionId = cpu_to_le64(sess_data->previous_session);
req->Flags = 0; /* MBZ */ req->Flags = 0; /* MBZ */
cifs_dbg(FYI, "Fresh session. Previous: %llx\n",
sess_data->previous_session);
} }
/* enough to enable echos and oplocks and one max size write */ /* enough to enable echos and oplocks and one max size write */
...@@ -1325,7 +1348,7 @@ SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data) ...@@ -1325,7 +1348,7 @@ SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
/* BB add code to build os and lm fields */ /* BB add code to build os and lm fields */
rc = cifs_send_recv(sess_data->xid, sess_data->ses, rc = cifs_send_recv(sess_data->xid, sess_data->ses,
cifs_ses_server(sess_data->ses), sess_data->server,
&rqst, &rqst,
&sess_data->buf0_type, &sess_data->buf0_type,
CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov); CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov);
...@@ -1340,11 +1363,11 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) ...@@ -1340,11 +1363,11 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
{ {
int rc = 0; int rc = 0;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = cifs_ses_server(ses); struct TCP_Server_Info *server = sess_data->server;
mutex_lock(&server->srv_mutex); mutex_lock(&server->srv_mutex);
if (server->ops->generate_signingkey) { if (server->ops->generate_signingkey) {
rc = server->ops->generate_signingkey(ses); rc = server->ops->generate_signingkey(ses, server);
if (rc) { if (rc) {
cifs_dbg(FYI, cifs_dbg(FYI,
"SMB3 session key generation failed\n"); "SMB3 session key generation failed\n");
...@@ -1359,13 +1382,16 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) ...@@ -1359,13 +1382,16 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
cifs_dbg(FYI, "SMB2/3 session established successfully\n"); cifs_dbg(FYI, "SMB2/3 session established successfully\n");
/* keep existing ses state if binding */
if (!ses->binding) { spin_lock(&ses->chan_lock);
spin_lock(&GlobalMid_Lock); cifs_chan_clear_need_reconnect(ses, server);
ses->status = CifsGood; spin_unlock(&ses->chan_lock);
ses->need_reconnect = false;
spin_unlock(&GlobalMid_Lock); /* Even if one channel is active, session is in good state */
} spin_lock(&cifs_tcp_ses_lock);
server->tcpStatus = CifsGood;
ses->status = CifsGood;
spin_unlock(&cifs_tcp_ses_lock);
return rc; return rc;
} }
...@@ -1376,15 +1402,17 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) ...@@ -1376,15 +1402,17 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
{ {
int rc; int rc;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
struct cifs_spnego_msg *msg; struct cifs_spnego_msg *msg;
struct key *spnego_key = NULL; struct key *spnego_key = NULL;
struct smb2_sess_setup_rsp *rsp = NULL; struct smb2_sess_setup_rsp *rsp = NULL;
bool is_binding = false;
rc = SMB2_sess_alloc_buffer(sess_data); rc = SMB2_sess_alloc_buffer(sess_data);
if (rc) if (rc)
goto out; goto out;
spnego_key = cifs_get_spnego_key(ses); spnego_key = cifs_get_spnego_key(ses, server);
if (IS_ERR(spnego_key)) { if (IS_ERR(spnego_key)) {
rc = PTR_ERR(spnego_key); rc = PTR_ERR(spnego_key);
if (rc == -ENOKEY) if (rc == -ENOKEY)
...@@ -1405,8 +1433,12 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) ...@@ -1405,8 +1433,12 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
goto out_put_spnego_key; goto out_put_spnego_key;
} }
spin_lock(&ses->chan_lock);
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
spin_unlock(&ses->chan_lock);
/* keep session key if binding */ /* keep session key if binding */
if (!ses->binding) { if (!is_binding) {
ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len, ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
GFP_KERNEL); GFP_KERNEL);
if (!ses->auth_key.response) { if (!ses->auth_key.response) {
...@@ -1427,7 +1459,7 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data) ...@@ -1427,7 +1459,7 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
/* keep session id and flags if binding */ /* keep session id and flags if binding */
if (!ses->binding) { if (!is_binding) {
ses->Suid = le64_to_cpu(rsp->hdr.SessionId); ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
ses->session_flags = le16_to_cpu(rsp->SessionFlags); ses->session_flags = le16_to_cpu(rsp->SessionFlags);
} }
...@@ -1459,10 +1491,12 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) ...@@ -1459,10 +1491,12 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
{ {
int rc; int rc;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
struct smb2_sess_setup_rsp *rsp = NULL; struct smb2_sess_setup_rsp *rsp = NULL;
unsigned char *ntlmssp_blob = NULL; unsigned char *ntlmssp_blob = NULL;
bool use_spnego = false; /* else use raw ntlmssp */ bool use_spnego = false; /* else use raw ntlmssp */
u16 blob_length = 0; u16 blob_length = 0;
bool is_binding = false;
/* /*
* If memory allocation is successful, caller of this function * If memory allocation is successful, caller of this function
...@@ -1480,7 +1514,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) ...@@ -1480,7 +1514,7 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
goto out_err; goto out_err;
rc = build_ntlmssp_negotiate_blob(&ntlmssp_blob, rc = build_ntlmssp_negotiate_blob(&ntlmssp_blob,
&blob_length, ses, &blob_length, ses, server,
sess_data->nls_cp); sess_data->nls_cp);
if (rc) if (rc)
goto out_err; goto out_err;
...@@ -1519,8 +1553,12 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data) ...@@ -1519,8 +1553,12 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n"); cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
spin_lock(&ses->chan_lock);
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
spin_unlock(&ses->chan_lock);
/* keep existing ses id and flags if binding */ /* keep existing ses id and flags if binding */
if (!ses->binding) { if (!is_binding) {
ses->Suid = le64_to_cpu(rsp->hdr.SessionId); ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
ses->session_flags = le16_to_cpu(rsp->SessionFlags); ses->session_flags = le16_to_cpu(rsp->SessionFlags);
} }
...@@ -1545,11 +1583,13 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) ...@@ -1545,11 +1583,13 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
{ {
int rc; int rc;
struct cifs_ses *ses = sess_data->ses; struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
struct smb2_sess_setup_req *req; struct smb2_sess_setup_req *req;
struct smb2_sess_setup_rsp *rsp = NULL; struct smb2_sess_setup_rsp *rsp = NULL;
unsigned char *ntlmssp_blob = NULL; unsigned char *ntlmssp_blob = NULL;
bool use_spnego = false; /* else use raw ntlmssp */ bool use_spnego = false; /* else use raw ntlmssp */
u16 blob_length = 0; u16 blob_length = 0;
bool is_binding = false;
rc = SMB2_sess_alloc_buffer(sess_data); rc = SMB2_sess_alloc_buffer(sess_data);
if (rc) if (rc)
...@@ -1558,8 +1598,9 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) ...@@ -1558,8 +1598,9 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base; req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base;
req->hdr.SessionId = cpu_to_le64(ses->Suid); req->hdr.SessionId = cpu_to_le64(ses->Suid);
rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length, ses, rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length,
sess_data->nls_cp); ses, server,
sess_data->nls_cp);
if (rc) { if (rc) {
cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc); cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc);
goto out; goto out;
...@@ -1580,8 +1621,12 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) ...@@ -1580,8 +1621,12 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base; rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
spin_lock(&ses->chan_lock);
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
spin_unlock(&ses->chan_lock);
/* keep existing ses id and flags if binding */ /* keep existing ses id and flags if binding */
if (!ses->binding) { if (!is_binding) {
ses->Suid = le64_to_cpu(rsp->hdr.SessionId); ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
ses->session_flags = le16_to_cpu(rsp->SessionFlags); ses->session_flags = le16_to_cpu(rsp->SessionFlags);
} }
...@@ -1612,11 +1657,13 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data) ...@@ -1612,11 +1657,13 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
} }
static int static int
SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data) SMB2_select_sec(struct SMB2_sess_data *sess_data)
{ {
int type; int type;
struct cifs_ses *ses = sess_data->ses;
struct TCP_Server_Info *server = sess_data->server;
type = smb2_select_sectype(cifs_ses_server(ses), ses->sectype); type = smb2_select_sectype(server, ses->sectype);
cifs_dbg(FYI, "sess setup type %d\n", type); cifs_dbg(FYI, "sess setup type %d\n", type);
if (type == Unspecified) { if (type == Unspecified) {
cifs_dbg(VFS, "Unable to select appropriate authentication method!\n"); cifs_dbg(VFS, "Unable to select appropriate authentication method!\n");
...@@ -1640,10 +1687,10 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data) ...@@ -1640,10 +1687,10 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
int int
SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp) const struct nls_table *nls_cp)
{ {
int rc = 0; int rc = 0;
struct TCP_Server_Info *server = cifs_ses_server(ses);
struct SMB2_sess_data *sess_data; struct SMB2_sess_data *sess_data;
cifs_dbg(FYI, "Session Setup\n"); cifs_dbg(FYI, "Session Setup\n");
...@@ -1657,15 +1704,17 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, ...@@ -1657,15 +1704,17 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
if (!sess_data) if (!sess_data)
return -ENOMEM; return -ENOMEM;
rc = SMB2_select_sec(ses, sess_data);
if (rc)
goto out;
sess_data->xid = xid; sess_data->xid = xid;
sess_data->ses = ses; sess_data->ses = ses;
sess_data->server = server;
sess_data->buf0_type = CIFS_NO_BUFFER; sess_data->buf0_type = CIFS_NO_BUFFER;
sess_data->nls_cp = (struct nls_table *) nls_cp; sess_data->nls_cp = (struct nls_table *) nls_cp;
sess_data->previous_session = ses->Suid; sess_data->previous_session = ses->Suid;
rc = SMB2_select_sec(sess_data);
if (rc)
goto out;
/* /*
* Initialize the session hash with the server one. * Initialize the session hash with the server one.
*/ */
...@@ -1704,8 +1753,12 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) ...@@ -1704,8 +1753,12 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
return -EIO; return -EIO;
/* no need to send SMB logoff if uid already closed due to reconnect */ /* no need to send SMB logoff if uid already closed due to reconnect */
if (ses->need_reconnect) spin_lock(&ses->chan_lock);
if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
spin_unlock(&ses->chan_lock);
goto smb2_session_already_dead; goto smb2_session_already_dead;
}
spin_unlock(&ses->chan_lock);
rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server, rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server,
(void **) &req, &total_len); (void **) &req, &total_len);
...@@ -1867,7 +1920,9 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree, ...@@ -1867,7 +1920,9 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
tcon->share_flags = le32_to_cpu(rsp->ShareFlags); tcon->share_flags = le32_to_cpu(rsp->ShareFlags);
tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */ tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */
tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess); tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess);
spin_lock(&cifs_tcp_ses_lock);
tcon->tidStatus = CifsGood; tcon->tidStatus = CifsGood;
spin_unlock(&cifs_tcp_ses_lock);
tcon->need_reconnect = false; tcon->need_reconnect = false;
tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId); tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId);
strlcpy(tcon->treeName, tree, sizeof(tcon->treeName)); strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
...@@ -1913,8 +1968,13 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) ...@@ -1913,8 +1968,13 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
if (!ses || !(ses->server)) if (!ses || !(ses->server))
return -EIO; return -EIO;
if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) spin_lock(&ses->chan_lock);
if ((tcon->need_reconnect) ||
(CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses))) {
spin_unlock(&ses->chan_lock);
return 0; return 0;
}
spin_unlock(&ses->chan_lock);
close_cached_dir_lease(&tcon->crfid); close_cached_dir_lease(&tcon->crfid);
...@@ -3797,13 +3857,16 @@ SMB2_echo(struct TCP_Server_Info *server) ...@@ -3797,13 +3857,16 @@ SMB2_echo(struct TCP_Server_Info *server)
.rq_nvec = 1 }; .rq_nvec = 1 };
unsigned int total_len; unsigned int total_len;
cifs_dbg(FYI, "In echo request\n"); cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id);
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsNeedNegotiate) { if (server->tcpStatus == CifsNeedNegotiate) {
spin_unlock(&cifs_tcp_ses_lock);
/* No need to send echo on newly established connections */ /* No need to send echo on newly established connections */
mod_delayed_work(cifsiod_wq, &server->reconnect, 0); mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
return rc; return rc;
} }
spin_unlock(&cifs_tcp_ses_lock);
rc = smb2_plain_req_init(SMB2_ECHO, NULL, server, rc = smb2_plain_req_init(SMB2_ECHO, NULL, server,
(void **)&req, &total_len); (void **)&req, &total_len);
......
...@@ -123,8 +123,11 @@ extern void smb2_set_related(struct smb_rqst *rqst); ...@@ -123,8 +123,11 @@ extern void smb2_set_related(struct smb_rqst *rqst);
* SMB2 Worker functions - most of protocol specific implementation details * SMB2 Worker functions - most of protocol specific implementation details
* are contained within these calls. * are contained within these calls.
*/ */
extern int SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses); extern int SMB2_negotiate(const unsigned int xid,
struct cifs_ses *ses,
struct TCP_Server_Info *server);
extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses, extern int SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct nls_table *nls_cp); const struct nls_table *nls_cp);
extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses); extern int SMB2_logoff(const unsigned int xid, struct cifs_ses *ses);
extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, extern int SMB2_tcon(const unsigned int xid, struct cifs_ses *ses,
...@@ -276,6 +279,7 @@ extern void smb2_copy_fs_info_to_kstatfs( ...@@ -276,6 +279,7 @@ extern void smb2_copy_fs_info_to_kstatfs(
struct kstatfs *kst); struct kstatfs *kst);
extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server); extern int smb311_crypto_shash_allocate(struct TCP_Server_Info *server);
extern int smb311_update_preauth_hash(struct cifs_ses *ses, extern int smb311_update_preauth_hash(struct cifs_ses *ses,
struct TCP_Server_Info *server,
struct kvec *iov, int nvec); struct kvec *iov, int nvec);
extern int smb2_query_info_compound(const unsigned int xid, extern int smb2_query_info_compound(const unsigned int xid,
struct cifs_tcon *tcon, struct cifs_tcon *tcon,
......
...@@ -100,7 +100,8 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key) ...@@ -100,7 +100,8 @@ int smb2_get_sign_key(__u64 ses_id, struct TCP_Server_Info *server, u8 *key)
goto out; goto out;
found: found:
if (ses->binding) { if (cifs_chan_needs_reconnect(ses, server) &&
!CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
/* /*
* If we are in the process of binding a new channel * If we are in the process of binding a new channel
* to an existing session, use the master connection * to an existing session, use the master connection
...@@ -390,12 +391,18 @@ struct derivation_triplet { ...@@ -390,12 +391,18 @@ struct derivation_triplet {
static int static int
generate_smb3signingkey(struct cifs_ses *ses, generate_smb3signingkey(struct cifs_ses *ses,
struct TCP_Server_Info *server,
const struct derivation_triplet *ptriplet) const struct derivation_triplet *ptriplet)
{ {
int rc; int rc;
#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS bool is_binding = false;
struct TCP_Server_Info *server = ses->server; int chan_index = 0;
#endif
spin_lock(&ses->chan_lock);
is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
chan_index = cifs_ses_get_chan_index(ses, server);
/* TODO: introduce ref counting for channels when the can be freed */
spin_unlock(&ses->chan_lock);
/* /*
* All channels use the same encryption/decryption keys but * All channels use the same encryption/decryption keys but
...@@ -407,10 +414,10 @@ generate_smb3signingkey(struct cifs_ses *ses, ...@@ -407,10 +414,10 @@ generate_smb3signingkey(struct cifs_ses *ses,
* master connection signing key stored in the session * master connection signing key stored in the session
*/ */
if (ses->binding) { if (is_binding) {
rc = generate_key(ses, ptriplet->signing.label, rc = generate_key(ses, ptriplet->signing.label,
ptriplet->signing.context, ptriplet->signing.context,
cifs_ses_binding_channel(ses)->signkey, ses->chans[chan_index].signkey,
SMB3_SIGN_KEY_SIZE); SMB3_SIGN_KEY_SIZE);
if (rc) if (rc)
return rc; return rc;
...@@ -422,6 +429,7 @@ generate_smb3signingkey(struct cifs_ses *ses, ...@@ -422,6 +429,7 @@ generate_smb3signingkey(struct cifs_ses *ses,
if (rc) if (rc)
return rc; return rc;
/* safe to access primary channel, since it will never go away */
memcpy(ses->chans[0].signkey, ses->smb3signingkey, memcpy(ses->chans[0].signkey, ses->smb3signingkey,
SMB3_SIGN_KEY_SIZE); SMB3_SIGN_KEY_SIZE);
...@@ -470,7 +478,8 @@ generate_smb3signingkey(struct cifs_ses *ses, ...@@ -470,7 +478,8 @@ generate_smb3signingkey(struct cifs_ses *ses,
} }
int int
generate_smb30signingkey(struct cifs_ses *ses) generate_smb30signingkey(struct cifs_ses *ses,
struct TCP_Server_Info *server)
{ {
struct derivation_triplet triplet; struct derivation_triplet triplet;
...@@ -494,11 +503,12 @@ generate_smb30signingkey(struct cifs_ses *ses) ...@@ -494,11 +503,12 @@ generate_smb30signingkey(struct cifs_ses *ses)
d->context.iov_base = "ServerOut"; d->context.iov_base = "ServerOut";
d->context.iov_len = 10; d->context.iov_len = 10;
return generate_smb3signingkey(ses, &triplet); return generate_smb3signingkey(ses, server, &triplet);
} }
int int
generate_smb311signingkey(struct cifs_ses *ses) generate_smb311signingkey(struct cifs_ses *ses,
struct TCP_Server_Info *server)
{ {
struct derivation_triplet triplet; struct derivation_triplet triplet;
...@@ -522,7 +532,7 @@ generate_smb311signingkey(struct cifs_ses *ses) ...@@ -522,7 +532,7 @@ generate_smb311signingkey(struct cifs_ses *ses)
d->context.iov_base = ses->preauth_sha_hash; d->context.iov_base = ses->preauth_sha_hash;
d->context.iov_len = 64; d->context.iov_len = 64;
return generate_smb3signingkey(ses, &triplet); return generate_smb3signingkey(ses, server, &triplet);
} }
int int
...@@ -624,8 +634,12 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server) ...@@ -624,8 +634,12 @@ smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
if (!is_signed) if (!is_signed)
return 0; return 0;
if (server->tcpStatus == CifsNeedNegotiate) spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsNeedNegotiate) {
spin_unlock(&cifs_tcp_ses_lock);
return 0; return 0;
}
spin_unlock(&cifs_tcp_ses_lock);
if (!is_binding && !server->session_estab) { if (!is_binding && !server->session_estab) {
strncpy(shdr->Signature, "BSRSPYL", 8); strncpy(shdr->Signature, "BSRSPYL", 8);
return 0; return 0;
...@@ -741,30 +755,41 @@ static int ...@@ -741,30 +755,41 @@ static int
smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server, smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server,
struct smb2_hdr *shdr, struct mid_q_entry **mid) struct smb2_hdr *shdr, struct mid_q_entry **mid)
{ {
if (server->tcpStatus == CifsExiting) spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&cifs_tcp_ses_lock);
return -ENOENT; return -ENOENT;
}
if (server->tcpStatus == CifsNeedReconnect) { if (server->tcpStatus == CifsNeedReconnect) {
spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "tcp session dead - return to caller to retry\n"); cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
return -EAGAIN; return -EAGAIN;
} }
if (server->tcpStatus == CifsNeedNegotiate && if (server->tcpStatus == CifsNeedNegotiate &&
shdr->Command != SMB2_NEGOTIATE) shdr->Command != SMB2_NEGOTIATE) {
spin_unlock(&cifs_tcp_ses_lock);
return -EAGAIN; return -EAGAIN;
}
if (ses->status == CifsNew) { if (ses->status == CifsNew) {
if ((shdr->Command != SMB2_SESSION_SETUP) && if ((shdr->Command != SMB2_SESSION_SETUP) &&
(shdr->Command != SMB2_NEGOTIATE)) (shdr->Command != SMB2_NEGOTIATE)) {
spin_unlock(&cifs_tcp_ses_lock);
return -EAGAIN; return -EAGAIN;
}
/* else ok - we are setting up session */ /* else ok - we are setting up session */
} }
if (ses->status == CifsExiting) { if (ses->status == CifsExiting) {
if (shdr->Command != SMB2_LOGOFF) if (shdr->Command != SMB2_LOGOFF) {
spin_unlock(&cifs_tcp_ses_lock);
return -EAGAIN; return -EAGAIN;
}
/* else ok - we are shutting down the session */ /* else ok - we are shutting down the session */
} }
spin_unlock(&cifs_tcp_ses_lock);
*mid = smb2_mid_entry_alloc(shdr, server); *mid = smb2_mid_entry_alloc(shdr, server);
if (*mid == NULL) if (*mid == NULL)
...@@ -837,9 +862,13 @@ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst) ...@@ -837,9 +862,13 @@ smb2_setup_async_request(struct TCP_Server_Info *server, struct smb_rqst *rqst)
(struct smb2_hdr *)rqst->rq_iov[0].iov_base; (struct smb2_hdr *)rqst->rq_iov[0].iov_base;
struct mid_q_entry *mid; struct mid_q_entry *mid;
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsNeedNegotiate && if (server->tcpStatus == CifsNeedNegotiate &&
shdr->Command != SMB2_NEGOTIATE) shdr->Command != SMB2_NEGOTIATE) {
spin_unlock(&cifs_tcp_ses_lock);
return ERR_PTR(-EAGAIN); return ERR_PTR(-EAGAIN);
}
spin_unlock(&cifs_tcp_ses_lock);
smb2_seq_num_into_buf(server, shdr); smb2_seq_num_into_buf(server, shdr);
......
...@@ -430,9 +430,9 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst, ...@@ -430,9 +430,9 @@ __smb_send_rqst(struct TCP_Server_Info *server, int num_rqst,
* be taken as the remainder of this one. We need to kill the * be taken as the remainder of this one. We need to kill the
* socket so the server throws away the partial SMB * socket so the server throws away the partial SMB
*/ */
spin_lock(&GlobalMid_Lock); spin_lock(&cifs_tcp_ses_lock);
server->tcpStatus = CifsNeedReconnect; server->tcpStatus = CifsNeedReconnect;
spin_unlock(&GlobalMid_Lock); spin_unlock(&cifs_tcp_ses_lock);
trace_smb3_partial_send_reconnect(server->CurrentMid, trace_smb3_partial_send_reconnect(server->CurrentMid,
server->conn_id, server->hostname); server->conn_id, server->hostname);
} }
...@@ -578,10 +578,14 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, ...@@ -578,10 +578,14 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
return -ERESTARTSYS; return -ERESTARTSYS;
spin_lock(&server->req_lock); spin_lock(&server->req_lock);
} else { } else {
spin_unlock(&server->req_lock);
spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsExiting) { if (server->tcpStatus == CifsExiting) {
spin_unlock(&server->req_lock); spin_unlock(&cifs_tcp_ses_lock);
return -ENOENT; return -ENOENT;
} }
spin_unlock(&cifs_tcp_ses_lock);
/* /*
* For normal commands, reserve the last MAX_COMPOUND * For normal commands, reserve the last MAX_COMPOUND
...@@ -596,6 +600,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits, ...@@ -596,6 +600,7 @@ wait_for_free_credits(struct TCP_Server_Info *server, const int num_credits,
* for servers that are slow to hand out credits on * for servers that are slow to hand out credits on
* new sessions. * new sessions.
*/ */
spin_lock(&server->req_lock);
if (!optype && num_credits == 1 && if (!optype && num_credits == 1 &&
server->in_flight > 2 * MAX_COMPOUND && server->in_flight > 2 * MAX_COMPOUND &&
*credits <= MAX_COMPOUND) { *credits <= MAX_COMPOUND) {
...@@ -723,28 +728,36 @@ cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size, ...@@ -723,28 +728,36 @@ cifs_wait_mtu_credits(struct TCP_Server_Info *server, unsigned int size,
static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf, static int allocate_mid(struct cifs_ses *ses, struct smb_hdr *in_buf,
struct mid_q_entry **ppmidQ) struct mid_q_entry **ppmidQ)
{ {
spin_lock(&cifs_tcp_ses_lock);
if (ses->server->tcpStatus == CifsExiting) { if (ses->server->tcpStatus == CifsExiting) {
spin_unlock(&cifs_tcp_ses_lock);
return -ENOENT; return -ENOENT;
} }
if (ses->server->tcpStatus == CifsNeedReconnect) { if (ses->server->tcpStatus == CifsNeedReconnect) {
spin_unlock(&cifs_tcp_ses_lock);
cifs_dbg(FYI, "tcp session dead - return to caller to retry\n"); cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
return -EAGAIN; return -EAGAIN;
} }
if (ses->status == CifsNew) { if (ses->status == CifsNew) {
if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
(in_buf->Command != SMB_COM_NEGOTIATE)) (in_buf->Command != SMB_COM_NEGOTIATE)) {
spin_unlock(&cifs_tcp_ses_lock);
return -EAGAIN; return -EAGAIN;
}
/* else ok - we are setting up session */ /* else ok - we are setting up session */
} }
if (ses->status == CifsExiting) { if (ses->status == CifsExiting) {
/* check if SMB session is bad because we are setting it up */ /* check if SMB session is bad because we are setting it up */
if (in_buf->Command != SMB_COM_LOGOFF_ANDX) if (in_buf->Command != SMB_COM_LOGOFF_ANDX) {
spin_unlock(&cifs_tcp_ses_lock);
return -EAGAIN; return -EAGAIN;
}
/* else ok - we are shutting down session */ /* else ok - we are shutting down session */
} }
spin_unlock(&cifs_tcp_ses_lock);
*ppmidQ = AllocMidQEntry(in_buf, ses->server); *ppmidQ = AllocMidQEntry(in_buf, ses->server);
if (*ppmidQ == NULL) if (*ppmidQ == NULL)
...@@ -1044,19 +1057,11 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses) ...@@ -1044,19 +1057,11 @@ struct TCP_Server_Info *cifs_pick_channel(struct cifs_ses *ses)
if (!ses) if (!ses)
return NULL; return NULL;
spin_lock(&ses->chan_lock); /* round robin */
if (!ses->binding) { index = (uint)atomic_inc_return(&ses->chan_seq);
/* round robin */ index %= ses->chan_count;
if (ses->chan_count > 1) {
index = (uint)atomic_inc_return(&ses->chan_seq); return ses->chans[index].server;
index %= ses->chan_count;
}
spin_unlock(&ses->chan_lock);
return ses->chans[index].server;
} else {
spin_unlock(&ses->chan_lock);
return cifs_ses_server(ses);
}
} }
int int
...@@ -1084,8 +1089,12 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -1084,8 +1089,12 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
return -EIO; return -EIO;
} }
if (server->tcpStatus == CifsExiting) spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&cifs_tcp_ses_lock);
return -ENOENT; return -ENOENT;
}
spin_unlock(&cifs_tcp_ses_lock);
/* /*
* Wait for all the requests to become available. * Wait for all the requests to become available.
...@@ -1188,12 +1197,17 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -1188,12 +1197,17 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
/* /*
* Compounding is never used during session establish. * Compounding is never used during session establish.
*/ */
spin_lock(&cifs_tcp_ses_lock);
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) {
spin_unlock(&cifs_tcp_ses_lock);
mutex_lock(&server->srv_mutex); mutex_lock(&server->srv_mutex);
smb311_update_preauth_hash(ses, rqst[0].rq_iov, smb311_update_preauth_hash(ses, server, rqst[0].rq_iov, rqst[0].rq_nvec);
rqst[0].rq_nvec);
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
spin_lock(&cifs_tcp_ses_lock);
} }
spin_unlock(&cifs_tcp_ses_lock);
for (i = 0; i < num_rqst; i++) { for (i = 0; i < num_rqst; i++) {
rc = wait_for_response(server, midQ[i]); rc = wait_for_response(server, midQ[i]);
...@@ -1256,15 +1270,19 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses, ...@@ -1256,15 +1270,19 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
/* /*
* Compounding is never used during session establish. * Compounding is never used during session establish.
*/ */
spin_lock(&cifs_tcp_ses_lock);
if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) { if ((ses->status == CifsNew) || (optype & CIFS_NEG_OP) || (optype & CIFS_SESS_OP)) {
struct kvec iov = { struct kvec iov = {
.iov_base = resp_iov[0].iov_base, .iov_base = resp_iov[0].iov_base,
.iov_len = resp_iov[0].iov_len .iov_len = resp_iov[0].iov_len
}; };
spin_unlock(&cifs_tcp_ses_lock);
mutex_lock(&server->srv_mutex); mutex_lock(&server->srv_mutex);
smb311_update_preauth_hash(ses, &iov, 1); smb311_update_preauth_hash(ses, server, &iov, 1);
mutex_unlock(&server->srv_mutex); mutex_unlock(&server->srv_mutex);
spin_lock(&cifs_tcp_ses_lock);
} }
spin_unlock(&cifs_tcp_ses_lock);
out: out:
/* /*
...@@ -1353,8 +1371,12 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses, ...@@ -1353,8 +1371,12 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
return -EIO; return -EIO;
} }
if (server->tcpStatus == CifsExiting) spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&cifs_tcp_ses_lock);
return -ENOENT; return -ENOENT;
}
spin_unlock(&cifs_tcp_ses_lock);
/* Ensure that we do not send more than 50 overlapping requests /* Ensure that we do not send more than 50 overlapping requests
to the same server. We may make this configurable later or to the same server. We may make this configurable later or
...@@ -1494,8 +1516,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1494,8 +1516,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
return -EIO; return -EIO;
} }
if (server->tcpStatus == CifsExiting) spin_lock(&cifs_tcp_ses_lock);
if (server->tcpStatus == CifsExiting) {
spin_unlock(&cifs_tcp_ses_lock);
return -ENOENT; return -ENOENT;
}
spin_unlock(&cifs_tcp_ses_lock);
/* Ensure that we do not send more than 50 overlapping requests /* Ensure that we do not send more than 50 overlapping requests
to the same server. We may make this configurable later or to the same server. We may make this configurable later or
...@@ -1553,10 +1579,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1553,10 +1579,12 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
(server->tcpStatus != CifsNew))); (server->tcpStatus != CifsNew)));
/* Were we interrupted by a signal ? */ /* Were we interrupted by a signal ? */
spin_lock(&cifs_tcp_ses_lock);
if ((rc == -ERESTARTSYS) && if ((rc == -ERESTARTSYS) &&
(midQ->mid_state == MID_REQUEST_SUBMITTED) && (midQ->mid_state == MID_REQUEST_SUBMITTED) &&
((server->tcpStatus == CifsGood) || ((server->tcpStatus == CifsGood) ||
(server->tcpStatus == CifsNew))) { (server->tcpStatus == CifsNew))) {
spin_unlock(&cifs_tcp_ses_lock);
if (in_buf->Command == SMB_COM_TRANSACTION2) { if (in_buf->Command == SMB_COM_TRANSACTION2) {
/* POSIX lock. We send a NT_CANCEL SMB to cause the /* POSIX lock. We send a NT_CANCEL SMB to cause the
...@@ -1595,7 +1623,9 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon, ...@@ -1595,7 +1623,9 @@ SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
/* We got the response - restart system call. */ /* We got the response - restart system call. */
rstart = 1; rstart = 1;
spin_lock(&cifs_tcp_ses_lock);
} }
spin_unlock(&cifs_tcp_ses_lock);
rc = cifs_sync_mid_result(midQ, server); rc = cifs_sync_mid_result(midQ, server);
if (rc != 0) if (rc != 0)
......
...@@ -52,6 +52,7 @@ ...@@ -52,6 +52,7 @@
#define QNX6_SUPER_MAGIC 0x68191122 /* qnx6 fs detection */ #define QNX6_SUPER_MAGIC 0x68191122 /* qnx6 fs detection */
#define AFS_FS_MAGIC 0x6B414653 #define AFS_FS_MAGIC 0x6B414653
#define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */ #define REISERFS_SUPER_MAGIC 0x52654973 /* used by gcc */
/* used by file system utilities that /* used by file system utilities that
look at the superblock, etc. */ look at the superblock, etc. */
...@@ -60,6 +61,9 @@ ...@@ -60,6 +61,9 @@
#define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs" #define REISER2FS_JR_SUPER_MAGIC_STRING "ReIsEr3Fs"
#define SMB_SUPER_MAGIC 0x517B #define SMB_SUPER_MAGIC 0x517B
#define CIFS_SUPER_MAGIC 0xFF534D42 /* the first four bytes of SMB PDUs */
#define SMB2_SUPER_MAGIC 0xFE534D42
#define CGROUP_SUPER_MAGIC 0x27e0eb #define CGROUP_SUPER_MAGIC 0x27e0eb
#define CGROUP2_SUPER_MAGIC 0x63677270 #define CGROUP2_SUPER_MAGIC 0x63677270
......
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