linux-cifs.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Steve French <smfrench@gmail.com>
To: Aurelien Aptel <aaptel@suse.com>
Cc: CIFS <linux-cifs@vger.kernel.org>
Subject: Re: [WIP v3] multichannel
Date: Tue, 17 Sep 2019 21:44:49 -0500	[thread overview]
Message-ID: <CAH2r5msndZMzJyOSDD16+=Y_b9MC-dJAuKGNWk5wzajxUcDS8A@mail.gmail.com> (raw)
In-Reply-To: <20190917235114.26247-1-aaptel@suse.com>

Checkpatch had a few minor indentation complaints and then probably a
valid one about not using %px and these two as well:

WARNING: Prefer 'unsigned int' to bare use of 'unsigned'
#1245: FILE: fs/cifs/transport.c:999:
+        uint index = ((unsigned)get_random_int()) % ses->chan_count;

WARNING: Missing a blank line after declarations
#1246: FILE: fs/cifs/transport.c:1000:
+        uint index = ((unsigned)get_random_int()) % ses->chan_count;
+        cifs_dbg(VFS, "XXX: send/recv: using random channel %d", index);

On Tue, Sep 17, 2019 at 6:51 PM Aurelien Aptel <aaptel@suse.com> wrote:
>
> WIP WIP WIP WIP WIP WIP
>
> to test, mount server with multiple interface with
>
>     -o vers=3.11,multichannel,max_channels=3
>
> WIP WIP WIP WIP WIP WIP
>
> changes since last version:
> - reuse client guid from master tcp connection
> - now works against samba & windows server
>
> Signed-off-by: Aurelien Aptel <aaptel@suse.com>
> ---
>  fs/cifs/cifs_debug.c    |   6 +-
>  fs/cifs/cifs_spnego.c   |   2 +-
>  fs/cifs/cifsglob.h      |  30 ++++++-
>  fs/cifs/cifsproto.h     |   8 ++
>  fs/cifs/connect.c       |  84 +++++++++++++++----
>  fs/cifs/sess.c          | 216 +++++++++++++++++++++++++++++++++++++++++++++++-
>  fs/cifs/smb2misc.c      |  37 ++++++---
>  fs/cifs/smb2ops.c       |  13 ++-
>  fs/cifs/smb2pdu.c       | 106 ++++++++++++++----------
>  fs/cifs/smb2proto.h     |   3 +-
>  fs/cifs/smb2transport.c | 162 +++++++++++++++++++++++++++---------
>  fs/cifs/transport.c     |  14 +++-
>  12 files changed, 562 insertions(+), 119 deletions(-)
>
> diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
> index 0b4eee3bed66..ef454170f37d 100644
> --- a/fs/cifs/cifs_debug.c
> +++ b/fs/cifs/cifs_debug.c
> @@ -410,8 +410,12 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
>                                 seq_printf(m, "\n\tServer interfaces: %zu\n",
>                                            ses->iface_count);
>                         for (j = 0; j < ses->iface_count; j++) {
> +                               struct cifs_server_iface *iface;
> +                               iface = &ses->iface_list[j];
>                                 seq_printf(m, "\t%d)", j);
> -                               cifs_dump_iface(m, &ses->iface_list[j]);
> +                               cifs_dump_iface(m, iface);
> +                               if (is_ses_using_iface(ses, iface))
> +                                       seq_puts(m, "\t\t[CONNECTED]\n");
>                         }
>                         spin_unlock(&ses->iface_lock);
>                 }
> diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c
> index 7f01c6e60791..7b9b876b513b 100644
> --- a/fs/cifs/cifs_spnego.c
> +++ b/fs/cifs/cifs_spnego.c
> @@ -98,7 +98,7 @@ struct key_type cifs_spnego_key_type = {
>  struct key *
>  cifs_get_spnego_key(struct cifs_ses *sesInfo)
>  {
> -       struct TCP_Server_Info *server = sesInfo->server;
> +       struct TCP_Server_Info *server = cifs_ses_server(sesInfo);
>         struct sockaddr_in *sa = (struct sockaddr_in *) &server->dstaddr;
>         struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *) &server->dstaddr;
>         char *description, *dp;
> diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
> index 54e204589cb9..441970293697 100644
> --- a/fs/cifs/cifsglob.h
> +++ b/fs/cifs/cifsglob.h
> @@ -230,7 +230,8 @@ struct smb_version_operations {
>         bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
>         /* setup request: allocate mid, sign message */
>         struct mid_q_entry *(*setup_request)(struct cifs_ses *,
> -                                               struct smb_rqst *);
> +                                            struct TCP_Server_Info *,
> +                                            struct smb_rqst *);
>         /* setup async request: allocate mid, sign message */
>         struct mid_q_entry *(*setup_async_request)(struct TCP_Server_Info *,
>                                                 struct smb_rqst *);
> @@ -590,6 +591,9 @@ struct smb_vol {
>         bool resilient:1; /* noresilient not required since not fored for CA */
>         bool domainauto:1;
>         bool rdma:1;
> +       bool multichannel:1;
> +       bool use_client_guid:1;
> +       u8 client_guid[SMB2_CLIENT_GUID_SIZE]; /* reuse existing guid from master channel */
>         unsigned int bsize;
>         unsigned int rsize;
>         unsigned int wsize;
> @@ -606,6 +610,7 @@ struct smb_vol {
>         __u64 snapshot_time; /* needed for timewarp tokens */
>         __u32 handle_timeout; /* persistent and durable handle timeout in ms */
>         unsigned int max_credits; /* smb3 max_credits 10 < credits < 60000 */
> +       unsigned int max_channels;
>         __u16 compression; /* compression algorithm 0xFFFF default 0=disabled */
>         bool rootfs:1; /* if it's a SMB root file system */
>  };
> @@ -952,11 +957,17 @@ struct cifs_server_iface {
>         struct sockaddr_storage sockaddr;
>  };
>
> +struct cifs_chan {
> +       struct TCP_Server_Info *server;
> +       __u8 signkey[SMB3_SIGN_KEY_SIZE];
> +};
> +
>  /*
>   * Session structure.  One of these for each uid session with a particular host
>   */
>  struct cifs_ses {
>         struct list_head smb_ses_list;
> +       struct list_head chan_ses_list;
>         struct list_head tcon_list;
>         struct cifs_tcon *tcon_ipc;
>         struct mutex session_mutex;
> @@ -982,12 +993,15 @@ struct cifs_ses {
>         bool sign;              /* is signing required? */
>         bool need_reconnect:1; /* connection reset, uid now invalid */
>         bool domainAuto:1;
> +       bool binding:1; /* are we binding the session? */
>         __u16 session_flags;
>         __u8 smb3signingkey[SMB3_SIGN_KEY_SIZE];
>         __u8 smb3encryptionkey[SMB3_SIGN_KEY_SIZE];
>         __u8 smb3decryptionkey[SMB3_SIGN_KEY_SIZE];
>         __u8 preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
>
> +       __u8 binding_preauth_sha_hash[SMB2_PREAUTH_HASH_SIZE];
> +
>         /*
>          * Network interfaces available on the server this session is
>          * connected to.
> @@ -1001,8 +1015,22 @@ struct cifs_ses {
>         struct cifs_server_iface *iface_list;
>         size_t iface_count;
>         unsigned long iface_last_update; /* jiffies */
> +
> +#define CIFS_MAX_CHANNELS 16
> +       struct cifs_chan chans[CIFS_MAX_CHANNELS];
> +       size_t chan_count;
> +       size_t chan_max;
>  };
>
> +static inline
> +struct TCP_Server_Info *cifs_ses_server(struct cifs_ses *ses)
> +{
> +       if (ses->binding)
> +               return ses->chans[ses->chan_count].server;
> +       else
> +               return ses->server;
> +}
> +
>  static inline bool
>  cap_unix(struct cifs_ses *ses)
>  {
> diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
> index 99b1b1ef558c..4cda8bba308b 100644
> --- a/fs/cifs/cifsproto.h
> +++ b/fs/cifs/cifsproto.h
> @@ -109,6 +109,7 @@ extern int SendReceive(const unsigned int /* xid */ , struct cifs_ses *,
>  extern int SendReceiveNoRsp(const unsigned int xid, struct cifs_ses *ses,
>                             char *in_buf, int flags);
>  extern struct mid_q_entry *cifs_setup_request(struct cifs_ses *,
> +                               struct TCP_Server_Info *,
>                                 struct smb_rqst *);
>  extern struct mid_q_entry *cifs_setup_async_request(struct TCP_Server_Info *,
>                                                 struct smb_rqst *);
> @@ -241,6 +242,7 @@ extern void cifs_add_pending_open_locked(struct cifs_fid *fid,
>                                          struct tcon_link *tlink,
>                                          struct cifs_pending_open *open);
>  extern void cifs_del_pending_open(struct cifs_pending_open *open);
> +extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb_vol *vol);
>  extern void cifs_put_tcp_session(struct TCP_Server_Info *server,
>                                  int from_reconnect);
>  extern void cifs_put_tcon(struct cifs_tcon *tcon);
> @@ -582,6 +584,12 @@ void cifs_free_hash(struct crypto_shash **shash, struct sdesc **sdesc);
>
>  extern void rqst_page_get_length(struct smb_rqst *rqst, unsigned int page,
>                                 unsigned int *len, unsigned int *offset);
> +int cifs_try_adding_channels(struct cifs_ses *ses);
> +int cifs_ses_add_channel(struct cifs_ses *ses,
> +                               struct cifs_server_iface *iface);
> +bool is_server_using_iface(struct TCP_Server_Info *server,
> +                          struct cifs_server_iface *iface);
> +bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface);
>
>  void extract_unc_hostname(const char *unc, const char **h, size_t *len);
>  int copy_path_name(char *dst, const char *src);
> diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
> index 2850c3ce4391..efd9ed46d5d6 100644
> --- a/fs/cifs/connect.c
> +++ b/fs/cifs/connect.c
> @@ -97,6 +97,7 @@ enum {
>         Opt_persistent, Opt_nopersistent,
>         Opt_resilient, Opt_noresilient,
>         Opt_domainauto, Opt_rdma, Opt_modesid, Opt_rootfs,
> +       Opt_multichannel, Opt_nomultichannel,
>         Opt_compress,
>
>         /* Mount options which take numeric value */
> @@ -106,7 +107,7 @@ enum {
>         Opt_min_enc_offload,
>         Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo,
>         Opt_echo_interval, Opt_max_credits, Opt_handletimeout,
> -       Opt_snapshot,
> +       Opt_snapshot, Opt_max_channels,
>
>         /* Mount options which take string value */
>         Opt_user, Opt_pass, Opt_ip,
> @@ -199,6 +200,8 @@ static const match_table_t cifs_mount_option_tokens = {
>         { Opt_noresilient, "noresilienthandles"},
>         { Opt_domainauto, "domainauto"},
>         { Opt_rdma, "rdma"},
> +       { Opt_multichannel, "multichannel" },
> +       { Opt_nomultichannel, "nomultichannel" },
>
>         { Opt_backupuid, "backupuid=%s" },
>         { Opt_backupgid, "backupgid=%s" },
> @@ -218,6 +221,7 @@ static const match_table_t cifs_mount_option_tokens = {
>         { Opt_echo_interval, "echo_interval=%s" },
>         { Opt_max_credits, "max_credits=%s" },
>         { Opt_snapshot, "snapshot=%s" },
> +       { Opt_max_channels, "max_channels=%s" },
>         { Opt_compress, "compress=%s" },
>
>         { Opt_blank_user, "user=" },
> @@ -1664,6 +1668,10 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
>
>         vol->echo_interval = SMB_ECHO_INTERVAL_DEFAULT;
>
> +       /* default to no multichannel (single server connection) */
> +       vol->multichannel = false;
> +       vol->max_channels = 1;
> +
>         if (!mountdata)
>                 goto cifs_parse_mount_err;
>
> @@ -1957,6 +1965,12 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
>                 case Opt_rdma:
>                         vol->rdma = true;
>                         break;
> +               case Opt_multichannel:
> +                       vol->multichannel = true;
> +                       break;
> +               case Opt_nomultichannel:
> +                       vol->multichannel = false;
> +                       break;
>                 case Opt_compress:
>                         vol->compression = UNKNOWN_TYPE;
>                         cifs_dbg(VFS,
> @@ -2120,6 +2134,15 @@ cifs_parse_mount_options(const char *mountdata, const char *devname,
>                         }
>                         vol->max_credits = option;
>                         break;
> +               case Opt_max_channels:
> +                       if (get_option_ul(args, &option) || option < 1 ||
> +                               option > CIFS_MAX_CHANNELS) {
> +                               cifs_dbg(VFS, "%s: Invalid max_channels value, needs to be 1-%d\n",
> +                                        __func__, CIFS_MAX_CHANNELS);
> +                               goto cifs_parse_mount_err;
> +                       }
> +                       vol->max_channels = option;
> +                       break;
>
>                 /* String Arguments */
>
> @@ -2665,6 +2688,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
>  {
>         struct task_struct *task;
>
> +       cifs_dbg(VFS, "XXX server %px", server);
>         spin_lock(&cifs_tcp_ses_lock);
>         if (--server->srv_count > 0) {
>                 spin_unlock(&cifs_tcp_ses_lock);
> @@ -2705,7 +2729,7 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
>                 send_sig(SIGKILL, task, 1);
>  }
>
> -static struct TCP_Server_Info *
> +struct TCP_Server_Info *
>  cifs_get_tcp_session(struct smb_vol *volume_info)
>  {
>         struct TCP_Server_Info *tcp_ses = NULL;
> @@ -2764,7 +2788,10 @@ cifs_get_tcp_session(struct smb_vol *volume_info)
>                sizeof(tcp_ses->srcaddr));
>         memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
>                 sizeof(tcp_ses->dstaddr));
> -       generate_random_uuid(tcp_ses->client_guid);
> +       if (volume_info->use_client_guid)
> +               memcpy(tcp_ses->client_guid, volume_info->client_guid, SMB2_CLIENT_GUID_SIZE);
> +       else
> +               generate_random_uuid(tcp_ses->client_guid);
>         /*
>          * at this point we are the only ones with the pointer
>          * to the struct since the kernel thread not created yet
> @@ -2853,6 +2880,13 @@ static int match_session(struct cifs_ses *ses, struct smb_vol *vol)
>             vol->sectype != ses->sectype)
>                 return 0;
>
> +       /*
> +        * If an existing session is limited to less channels than
> +        * requested, it should not be reused
> +        */
> +       if (ses->chan_max < vol->max_channels)
> +               return 0;
> +
>         switch (ses->sectype) {
>         case Kerberos:
>                 if (!uid_eq(vol->cred_uid, ses->cred_uid))
> @@ -3269,14 +3303,25 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
>         ses->sectype = volume_info->sectype;
>         ses->sign = volume_info->sign;
>         mutex_lock(&ses->session_mutex);
> +
> +       /* add server as first channel */
> +       ses->chans[0].server = server;
> +       ses->chan_count = 1;
> +       ses->chan_max = volume_info->multichannel ? volume_info->max_channels:1;
> +
>         rc = cifs_negotiate_protocol(xid, ses);
>         if (!rc)
>                 rc = cifs_setup_session(xid, ses, volume_info->local_nls);
> +
> +       /* each channel uses a different signing key */
> +       memcpy(ses->chans[0].signkey, ses->smb3signingkey,
> +              sizeof(ses->smb3signingkey));
> +
>         mutex_unlock(&ses->session_mutex);
>         if (rc)
>                 goto get_ses_fail;
>
> -       /* success, put it on the list */
> +       /* success, put it on the list and add it as first channel */
>         spin_lock(&cifs_tcp_ses_lock);
>         list_add(&ses->smb_ses_list, &server->smb_ses_list);
>         spin_unlock(&cifs_tcp_ses_lock);
> @@ -4885,6 +4930,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
>         cifs_autodisable_serverino(cifs_sb);
>  out:
>         free_xid(xid);
> +       cifs_try_adding_channels(ses);
>         return mount_setup_tlink(cifs_sb, ses, tcon);
>
>  error:
> @@ -5130,7 +5176,7 @@ int
>  cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
>  {
>         int rc = 0;
> -       struct TCP_Server_Info *server = ses->server;
> +       struct TCP_Server_Info *server = cifs_ses_server(ses);
>
>         if (!server->ops->need_neg || !server->ops->negotiate)
>                 return -ENOSYS;
> @@ -5157,23 +5203,25 @@ cifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
>                    struct nls_table *nls_info)
>  {
>         int rc = -ENOSYS;
> -       struct TCP_Server_Info *server = ses->server;
> -
> -       ses->capabilities = server->capabilities;
> -       if (linuxExtEnabled == 0)
> -               ses->capabilities &= (~server->vals->cap_unix);
> +       struct TCP_Server_Info *server = cifs_ses_server(ses);
> +
> +       if (!ses->binding) {
> +               ses->capabilities = server->capabilities;
> +               if (linuxExtEnabled == 0)
> +                       ses->capabilities &= (~server->vals->cap_unix);
> +
> +               if (ses->auth_key.response) {
> +                       cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
> +                                ses->auth_key.response);
> +                       kfree(ses->auth_key.response);
> +                       ses->auth_key.response = NULL;
> +                       ses->auth_key.len = 0;
> +               }
> +       }
>
>         cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
>                  server->sec_mode, server->capabilities, server->timeAdj);
>
> -       if (ses->auth_key.response) {
> -               cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
> -                        ses->auth_key.response);
> -               kfree(ses->auth_key.response);
> -               ses->auth_key.response = NULL;
> -               ses->auth_key.len = 0;
> -       }
> -
>         if (server->ops->sess_setup)
>                 rc = server->ops->sess_setup(xid, ses, nls_info);
>
> diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
> index 4c764ff7edd2..20c73a42755d 100644
> --- a/fs/cifs/sess.c
> +++ b/fs/cifs/sess.c
> @@ -31,6 +31,217 @@
>  #include <linux/utsname.h>
>  #include <linux/slab.h>
>  #include "cifs_spnego.h"
> +#include "smb2proto.h"
> +
> +bool
> +is_server_using_iface(struct TCP_Server_Info *server,
> +                     struct cifs_server_iface *iface)
> +{
> +       struct sockaddr_in *i4 = (struct sockaddr_in *)&iface->sockaddr;
> +       struct sockaddr_in6 *i6 = (struct sockaddr_in6 *)&iface->sockaddr;
> +       struct sockaddr_in *s4 = (struct sockaddr_in *)&server->dstaddr;
> +       struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&server->dstaddr;
> +
> +       if (server->dstaddr.ss_family != iface->sockaddr.ss_family)
> +               return false;
> +       if (server->dstaddr.ss_family == AF_INET) {
> +               if (s4->sin_addr.s_addr != i4->sin_addr.s_addr)
> +                       return false;
> +       } else if (server->dstaddr.ss_family == AF_INET6) {
> +               if (memcmp(&s6->sin6_addr, &i6->sin6_addr,
> +                          sizeof(i6->sin6_addr)) != 0)
> +                       return false;
> +       } else {
> +               /* unknown family.. */
> +               return false;
> +       }
> +       return true;
> +}
> +
> +bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface)
> +{
> +       int i;
> +       for (i = 0; i < ses->chan_count; i++) {
> +               if (is_server_using_iface(ses->chans[i].server, iface))
> +                       return true;
> +       }
> +       return false;
> +}
> +
> +/* returns number of channels added */
> +int cifs_try_adding_channels(struct cifs_ses *ses)
> +{
> +       int old_chan_count = ses->chan_count;
> +       int left = ses->chan_max - ses->chan_count;
> +       int i = 0;
> +       int rc = 0;
> +
> +       if (left <= 0) {
> +               cifs_dbg(FYI,
> +                        "ses already at max_channels (%zu), nothing to open\n",
> +                        ses->chan_max);
> +               return 0;
> +       }
> +
> +       if (ses->server->dialect != SMB311_PROT_ID) {
> +               cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.1.1\n");
> +               return 0;
> +       }
> +
> +       /* ifaces are sorted by speed, try them in order */
> +       for (i = 0; left > 0 && i < ses->iface_count; i++) {
> +               struct cifs_server_iface *iface;
> +
> +               iface = &ses->iface_list[i];
> +               if (is_ses_using_iface(ses, iface) && !iface->rss_capable)
> +                       continue;
> +
> +               rc = cifs_ses_add_channel(ses, iface);
> +               if (rc) {
> +                       cifs_dbg(FYI, "failed to open extra channel\n");
> +                       continue;
> +               }
> +
> +               cifs_dbg(FYI, "sucessfully opened new channel\n");
> +               left--;
> +       }
> +
> +       /*
> +        * TODO: if we still have channels left to open try to connect
> +        * to same RSS-capable iface multiple times
> +        */
> +
> +       return ses->chan_count - old_chan_count;
> +}
> +
> +int
> +cifs_ses_add_channel(struct cifs_ses *ses, struct cifs_server_iface *iface)
> +{
> +       struct cifs_chan *chan;
> +       struct smb_vol vol = {0};
> +       const char unc_fmt[] = "\\%s\\foo";
> +       char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0};
> +       struct sockaddr_in *ipv4 = (struct sockaddr_in *)&iface->sockaddr;
> +       struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&iface->sockaddr;
> +       int rc;
> +       unsigned int xid = get_xid();
> +
> +       cifs_dbg(FYI, "adding channel to ses %p (speed:%zu bps rdma:%s ",
> +                ses, iface->speed, iface->rdma_capable ? "yes" : "no");
> +       if (iface->sockaddr.ss_family == AF_INET)
> +               cifs_dbg(FYI, "ip:%pI4)\n", &ipv4->sin_addr);
> +       else
> +               cifs_dbg(FYI, "ip:%pI6)\n", &ipv6->sin6_addr);
> +
> +       /*
> +        * Setup a smb_vol with mostly the same info as the existing
> +        * session and overwrite it with the requested iface data.
> +        *
> +        * We need to setup at least the fields used for negprot and
> +        * sesssetup.
> +        *
> +        * We only need the volume here, so we can reuse memory from
> +        * the session and server without caring about memory
> +        * management.
> +        */
> +
> +       /* Always make new connection for now (TODO?) */
> +       vol.nosharesock = true;
> +
> +       /* Auth */
> +       vol.domainauto = ses->domainAuto;
> +       vol.domainname = ses->domainName;
> +       vol.username = ses->user_name;
> +       vol.password = ses->password;
> +       vol.sectype = ses->sectype;
> +       vol.sign = ses->sign;
> +
> +       /* UNC and paths */
> +       /* XXX: Use ses->server->hostname? */
> +       sprintf(unc, unc_fmt, ses->serverName);
> +       vol.UNC = unc;
> +       vol.prepath = "";
> +
> +       /* Require SMB3.1.1 */
> +       vol.vals = &smb311_values;
> +       vol.ops = &smb311_operations;
> +
> +       vol.noblocksnd = ses->server->noblocksnd;
> +       vol.noautotune = ses->server->noautotune;
> +       vol.sockopt_tcp_nodelay = ses->server->tcp_nodelay;
> +       vol.echo_interval = ses->server->echo_interval / HZ;
> +
> +       /*
> +        * This will be used for encoding/decoding user/domain/pw
> +        * during sess setup auth.
> +        *
> +        * XXX: We use the default for simplicity but the proper way
> +        * would be to use the one that ses used, which is not
> +        * stored. This might break when dealing with non-ascii
> +        * strings.
> +        */
> +       vol.local_nls = load_nls_default();
> +
> +       /* Use RDMA if possible */
> +       vol.rdma = iface->rdma_capable;
> +       memcpy(&vol.dstaddr, &iface->sockaddr, sizeof(struct sockaddr_storage));
> +
> +       /* reuse master con client guid */
> +       memcpy(&vol.client_guid, ses->server->client_guid, SMB2_CLIENT_GUID_SIZE);
> +       vol.use_client_guid = true;
> +
> +       mutex_lock(&ses->session_mutex);
> +
> +       chan = &ses->chans[ses->chan_count];
> +       chan->server = cifs_get_tcp_session(&vol);
> +       if (IS_ERR(chan->server)) {
> +               rc = PTR_ERR(chan->server);
> +               chan->server = NULL;
> +               goto out;
> +       }
> +
> +       /*
> +        * We need to allocate the server crypto now as we will need
> +        * to sign packets before we generate the channel signing key
> +        * (we sign with the session key)
> +        */
> +       rc = smb311_crypto_shash_allocate(chan->server);
> +       if (rc) {
> +               cifs_dbg(VFS, "%s: crypto alloc failed\n", __func__);
> +               goto out;
> +       }
> +
> +       ses->binding = true;
> +       rc = cifs_negotiate_protocol(xid, ses);
> +       if (rc)
> +               goto out;
> +
> +       rc = cifs_setup_session(xid, ses, vol.local_nls);
> +       if (rc)
> +               goto out;
> +
> +       /* success, put it on the list
> +        * XXX: sharing ses between 2 tcp server 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.
> +        */
> +
> +       ses->chan_count++;
> +
> +out:
> +       ses->binding = false;
> +       mutex_unlock(&ses->session_mutex);
> +
> +       if (rc && chan->server)
> +               cifs_put_tcp_session(chan->server, 0);
> +       unload_nls(vol.local_nls);
> +
> +       return rc;
> +}
>
>  static __u32 cifs_ssetup_hdr(struct cifs_ses *ses, SESSION_SETUP_ANDX *pSMB)
>  {
> @@ -342,6 +553,7 @@ int decode_ntlmssp_challenge(char *bcc_ptr, int blob_len,
>  void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
>                                          struct cifs_ses *ses)
>  {
> +       struct TCP_Server_Info *server = cifs_ses_server(ses);
>         NEGOTIATE_MESSAGE *sec_blob = (NEGOTIATE_MESSAGE *)pbuffer;
>         __u32 flags;
>
> @@ -354,9 +566,9 @@ void build_ntlmssp_negotiate_blob(unsigned char *pbuffer,
>                 NTLMSSP_NEGOTIATE_128 | NTLMSSP_NEGOTIATE_UNICODE |
>                 NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_NEGOTIATE_EXTENDED_SEC |
>                 NTLMSSP_NEGOTIATE_SEAL;
> -       if (ses->server->sign)
> +       if (server->sign)
>                 flags |= NTLMSSP_NEGOTIATE_SIGN;
> -       if (!ses->server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
> +       if (!server->session_estab || ses->ntlmssp->sesskey_per_smbsess)
>                 flags |= NTLMSSP_NEGOTIATE_KEY_XCH;
>
>         sec_blob->NegotiateFlags = cpu_to_le32(flags);
> diff --git a/fs/cifs/smb2misc.c b/fs/cifs/smb2misc.c
> index e311f58dc1c8..a95ed951c67f 100644
> --- a/fs/cifs/smb2misc.c
> +++ b/fs/cifs/smb2misc.c
> @@ -29,6 +29,7 @@
>  #include "cifs_unicode.h"
>  #include "smb2status.h"
>  #include "smb2glob.h"
> +#include "nterr.h"
>
>  static int
>  check_smb2_hdr(struct smb2_sync_hdr *shdr, __u64 mid)
> @@ -788,23 +789,37 @@ smb311_update_preauth_hash(struct cifs_ses *ses, struct kvec *iov, int nvec)
>         int i, rc;
>         struct sdesc *d;
>         struct smb2_sync_hdr *hdr;
> +       struct TCP_Server_Info *server = cifs_ses_server(ses);
>
> -       if (ses->server->tcpStatus == CifsGood) {
> -               /* skip non smb311 connections */
> -               if (ses->server->dialect != SMB311_PROT_ID)
> -                       return 0;
> +       hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
> +       /* neg prot are always taken */
> +       if (hdr->Command == SMB2_NEGOTIATE)
> +               goto ok;
>
> -               /* skip last sess setup response */
> -               hdr = (struct smb2_sync_hdr *)iov[0].iov_base;
> -               if (hdr->Flags & SMB2_FLAGS_SIGNED)
> -                       return 0;
> -       }
> +       /*
> +        * If we process a command which wasn't a negprot it means the
> +        * neg prot was already done, so the server dialect was set
> +        * and we can test it. Preauth requires 3.1.1 for now.
> +        */
> +       if (server->dialect != SMB311_PROT_ID)
> +               return 0;
> +
> +       if (hdr->Command != SMB2_SESSION_SETUP)
> +               return 0;
> +
> +       /* skip last sess setup response */
> +       if ((hdr->Flags & SMB2_FLAGS_SERVER_TO_REDIR)
> +           && (hdr->Status == NT_STATUS_OK
> +               || (hdr->Status !=
> +                   cpu_to_le32(NT_STATUS_MORE_PROCESSING_REQUIRED))))
> +               return 0;
>
> -       rc = smb311_crypto_shash_allocate(ses->server);
> +ok:
> +       rc = smb311_crypto_shash_allocate(server);
>         if (rc)
>                 return rc;
>
> -       d = ses->server->secmech.sdescsha512;
> +       d = server->secmech.sdescsha512;
>         rc = crypto_shash_init(&d->shash);
>         if (rc) {
>                 cifs_dbg(VFS, "%s: could not init sha512 shash\n", __func__);
> diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
> index eaed18061314..0e66dc1aa1c9 100644
> --- a/fs/cifs/smb2ops.c
> +++ b/fs/cifs/smb2ops.c
> @@ -10,6 +10,7 @@
>  #include <linux/falloc.h>
>  #include <linux/scatterlist.h>
>  #include <linux/uuid.h>
> +#include <linux/sort.h>
>  #include <crypto/aead.h>
>  #include "cifsglob.h"
>  #include "smb2pdu.h"
> @@ -315,7 +316,7 @@ smb2_negotiate(const unsigned int xid, struct cifs_ses *ses)
>  {
>         int rc;
>
> -       ses->server->CurrentMid = 0;
> +       cifs_ses_server(ses)->CurrentMid = 0;
>         rc = SMB2_negotiate(xid, ses);
>         /* BB we probably don't need to retry with modern servers */
>         if (rc == -EAGAIN)
> @@ -558,6 +559,13 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
>         return rc;
>  }
>
> +static int compare_iface(const void *ia, const void *ib)
> +{
> +       const struct cifs_server_iface *a = (struct cifs_server_iface *)ia;
> +       const struct cifs_server_iface *b = (struct cifs_server_iface *)ib;
> +
> +       return a->speed == b->speed ? 0 : (a->speed > b->speed ? -1 : 1);
> +}
>
>  static int
>  SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
> @@ -587,6 +595,9 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon)
>         if (rc)
>                 goto out;
>
> +       /* sort interfaces from fastest to slowest */
> +       sort(iface_list, iface_count, sizeof(*iface_list), compare_iface, NULL);
> +
>         spin_lock(&ses->iface_lock);
>         kfree(ses->iface_list);
>         ses->iface_list = iface_list;
> diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
> index 87066f1af12c..8bcb278fdb0a 100644
> --- a/fs/cifs/smb2pdu.c
> +++ b/fs/cifs/smb2pdu.c
> @@ -789,7 +789,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
>         struct kvec rsp_iov;
>         int rc = 0;
>         int resp_buftype;
> -       struct TCP_Server_Info *server = ses->server;
> +       struct TCP_Server_Info *server = cifs_ses_server(ses);
>         int blob_offset, blob_length;
>         char *security_blob;
>         int flags = CIFS_NEG_OP;
> @@ -811,7 +811,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
>         memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
>         memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
>
> -       if (strcmp(ses->server->vals->version_string,
> +       if (strcmp(server->vals->version_string,
>                    SMB3ANY_VERSION_STRING) == 0) {
>                 req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
>                 req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
> @@ -827,7 +827,7 @@ SMB2_negotiate(const unsigned int xid, struct cifs_ses *ses)
>                 total_len += 8;
>         } else {
>                 /* otherwise send specific dialect */
> -               req->Dialects[0] = cpu_to_le16(ses->server->vals->protocol_id);
> +               req->Dialects[0] = cpu_to_le16(server->vals->protocol_id);
>                 req->DialectCount = cpu_to_le16(1);
>                 total_len += 2;
>         }
> @@ -1169,7 +1169,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
>         int rc;
>         struct cifs_ses *ses = sess_data->ses;
>         struct smb2_sess_setup_req *req;
> -       struct TCP_Server_Info *server = ses->server;
> +       struct TCP_Server_Info *server = cifs_ses_server(ses);
>         unsigned int total_len;
>
>         rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, (void **) &req,
> @@ -1177,13 +1177,18 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
>         if (rc)
>                 return rc;
>
> -       /* First session, not a reauthenticate */
> -       req->sync_hdr.SessionId = 0;
> -
> -       /* if reconnect, we need to send previous sess id, otherwise it is 0 */
> -       req->PreviousSessionId = sess_data->previous_session;
> -
> -       req->Flags = 0; /* MBZ */
> +       if (sess_data->ses->binding) {
> +               req->sync_hdr.SessionId = sess_data->ses->Suid;
> +               req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED;
> +               req->PreviousSessionId = 0;
> +               req->Flags = SMB2_SESSION_REQ_FLAG_BINDING;
> +       } else {
> +               /* First session, not a reauthenticate */
> +               req->sync_hdr.SessionId = 0;
> +               /* if reconnect, we need to send previous sess id, otherwise it is 0 */
> +               req->PreviousSessionId = sess_data->previous_session;
> +               req->Flags = 0; /* MBZ */
> +       }
>
>         /* enough to enable echos and oplocks and one max size write */
>         req->sync_hdr.CreditRequest = cpu_to_le16(130);
> @@ -1256,28 +1261,33 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
>  {
>         int rc = 0;
>         struct cifs_ses *ses = sess_data->ses;
> +       struct TCP_Server_Info *server = cifs_ses_server(ses);
>
> -       mutex_lock(&ses->server->srv_mutex);
> -       if (ses->server->ops->generate_signingkey) {
> -               rc = ses->server->ops->generate_signingkey(ses);
> +       mutex_lock(&server->srv_mutex);
> +       if (server->ops->generate_signingkey) {
> +               rc = server->ops->generate_signingkey(ses);
>                 if (rc) {
>                         cifs_dbg(FYI,
>                                 "SMB3 session key generation failed\n");
> -                       mutex_unlock(&ses->server->srv_mutex);
> +                       mutex_unlock(&server->srv_mutex);
>                         return rc;
>                 }
>         }
> -       if (!ses->server->session_estab) {
> -               ses->server->sequence_number = 0x2;
> -               ses->server->session_estab = true;
> +       if (!server->session_estab) {
> +               server->sequence_number = 0x2;
> +               server->session_estab = true;
>         }
> -       mutex_unlock(&ses->server->srv_mutex);
> +       mutex_unlock(&server->srv_mutex);
>
>         cifs_dbg(FYI, "SMB2/3 session established successfully\n");
> -       spin_lock(&GlobalMid_Lock);
> -       ses->status = CifsGood;
> -       ses->need_reconnect = false;
> -       spin_unlock(&GlobalMid_Lock);
> +       /* keep exising ses state if binding */
> +       if (!ses->binding) {
> +               spin_lock(&GlobalMid_Lock);
> +               ses->status = CifsGood;
> +               ses->need_reconnect = false;
> +               spin_unlock(&GlobalMid_Lock);
> +       }
> +
>         return rc;
>  }
>
> @@ -1315,16 +1325,19 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
>                 goto out_put_spnego_key;
>         }
>
> -       ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
> -                                        GFP_KERNEL);
> -       if (!ses->auth_key.response) {
> -               cifs_dbg(VFS,
> -                       "Kerberos can't allocate (%u bytes) memory",
> -                       msg->sesskey_len);
> -               rc = -ENOMEM;
> -               goto out_put_spnego_key;
> +       /* keep session key if binding */
> +       if (!ses->binding) {
> +               ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
> +                                                GFP_KERNEL);
> +               if (!ses->auth_key.response) {
> +                       cifs_dbg(VFS,
> +                                "Kerberos can't allocate (%u bytes) memory",
> +                                msg->sesskey_len);
> +                       rc = -ENOMEM;
> +                       goto out_put_spnego_key;
> +               }
> +               ses->auth_key.len = msg->sesskey_len;
>         }
> -       ses->auth_key.len = msg->sesskey_len;
>
>         sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
>         sess_data->iov[1].iov_len = msg->secblob_len;
> @@ -1334,9 +1347,11 @@ SMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
>                 goto out_put_spnego_key;
>
>         rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
> -       ses->Suid = rsp->sync_hdr.SessionId;
> -
> -       ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       /* keep session id and flags if binding */
> +       if (!ses->binding) {
> +               ses->Suid = rsp->sync_hdr.SessionId;
> +               ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       }
>
>         rc = SMB2_sess_establish_session(sess_data);
>  out_put_spnego_key:
> @@ -1430,9 +1445,11 @@ SMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
>
>         cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
>
> -
> -       ses->Suid = rsp->sync_hdr.SessionId;
> -       ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       /* keep existing ses id and flags if binding */
> +       if (!ses->binding) {
> +               ses->Suid = rsp->sync_hdr.SessionId;
> +               ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       }
>
>  out:
>         kfree(ntlmssp_blob);
> @@ -1489,8 +1506,11 @@ SMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
>
>         rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
>
> -       ses->Suid = rsp->sync_hdr.SessionId;
> -       ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       /* keep existing ses id and flags if binding */
> +       if (!ses->binding) {
> +               ses->Suid = rsp->sync_hdr.SessionId;
> +               ses->session_flags = le16_to_cpu(rsp->SessionFlags);
> +       }
>
>         rc = SMB2_sess_establish_session(sess_data);
>  out:
> @@ -1507,7 +1527,7 @@ SMB2_select_sec(struct cifs_ses *ses, struct SMB2_sess_data *sess_data)
>  {
>         int type;
>
> -       type = smb2_select_sectype(ses->server, ses->sectype);
> +       type = smb2_select_sectype(cifs_ses_server(ses), ses->sectype);
>         cifs_dbg(FYI, "sess setup type %d\n", type);
>         if (type == Unspecified) {
>                 cifs_dbg(VFS,
> @@ -1535,7 +1555,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
>                 const struct nls_table *nls_cp)
>  {
>         int rc = 0;
> -       struct TCP_Server_Info *server = ses->server;
> +       struct TCP_Server_Info *server = cifs_ses_server(ses);
>         struct SMB2_sess_data *sess_data;
>
>         cifs_dbg(FYI, "Session Setup\n");
> @@ -1561,7 +1581,7 @@ SMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
>         /*
>          * Initialize the session hash with the server one.
>          */
> -       memcpy(ses->preauth_sha_hash, ses->server->preauth_sha_hash,
> +       memcpy(ses->preauth_sha_hash, server->preauth_sha_hash,
>                SMB2_PREAUTH_HASH_SIZE);
>
>         while (sess_data->func)
> diff --git a/fs/cifs/smb2proto.h b/fs/cifs/smb2proto.h
> index 67a91b11fd59..804b6dc5546b 100644
> --- a/fs/cifs/smb2proto.h
> +++ b/fs/cifs/smb2proto.h
> @@ -46,7 +46,8 @@ extern int smb2_verify_signature(struct smb_rqst *, struct TCP_Server_Info *);
>  extern int smb2_check_receive(struct mid_q_entry *mid,
>                               struct TCP_Server_Info *server, bool log_error);
>  extern struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
> -                             struct smb_rqst *rqst);
> +                                             struct TCP_Server_Info *,
> +                                             struct smb_rqst *rqst);
>  extern struct mid_q_entry *smb2_setup_async_request(
>                         struct TCP_Server_Info *server, struct smb_rqst *rqst);
>  extern struct cifs_ses *smb2_find_smb_ses(struct TCP_Server_Info *server,
> diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c
> index 148d7942c796..539cf2fc1f3b 100644
> --- a/fs/cifs/smb2transport.c
> +++ b/fs/cifs/smb2transport.c
> @@ -48,7 +48,7 @@ smb2_crypto_shash_allocate(struct TCP_Server_Info *server)
>                                &server->secmech.sdeschmacsha256);
>  }
>
> -static int
> +int
>  smb3_crypto_shash_allocate(struct TCP_Server_Info *server)
>  {
>         struct cifs_secmech *p = &server->secmech;
> @@ -98,6 +98,47 @@ smb311_crypto_shash_allocate(struct TCP_Server_Info *server)
>         return rc;
>  }
>
> +u8 *smb2_find_chan_signkey(struct cifs_ses *ses, struct TCP_Server_Info *server)
> +{
> +       int i;
> +       struct cifs_chan *chan;
> +       int count;
> +       spin_lock(&cifs_tcp_ses_lock);
> +       count = ses->chan_count;
> +       if (ses->binding)
> +               count++;
> +       for (i = 0; i < count; i++) {
> +               chan = ses->chans + i;
> +               if (chan->server == server) {
> +                       spin_unlock(&cifs_tcp_ses_lock);
> +                       return chan->signkey;
> +               }
> +       }
> +       spin_unlock(&cifs_tcp_ses_lock);
> +       return NULL;
> +}
> +
> +struct cifs_ses *
> +smb2_find_global_smb_ses(__u64 ses_id)
> +{
> +       struct TCP_Server_Info *server;
> +       struct cifs_ses *ses;
> +       cifs_dbg(VFS, "XXX: searching for sesid %llu", ses_id);
> +       spin_lock(&cifs_tcp_ses_lock);
> +       list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
> +               cifs_dbg(VFS, "XXX: server %px ", server);
> +               list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
> +                       cifs_dbg(VFS, "XXX: ses %px ", ses);
> +                       if (ses->Suid == ses_id) {
> +                               spin_unlock(&cifs_tcp_ses_lock);
> +                               return ses;
> +                       }
> +               }
> +       }
> +       spin_unlock(&cifs_tcp_ses_lock);
> +       return NULL;
> +}
> +
>  static struct cifs_ses *
>  smb2_find_smb_ses_unlocked(struct TCP_Server_Info *server, __u64 ses_id)
>  {
> @@ -328,21 +369,34 @@ generate_smb3signingkey(struct cifs_ses *ses,
>  {
>         int rc;
>
> -       rc = generate_key(ses, ptriplet->signing.label,
> -                         ptriplet->signing.context, ses->smb3signingkey,
> -                         SMB3_SIGN_KEY_SIZE);
> -       if (rc)
> -               return rc;
> -
> -       rc = generate_key(ses, ptriplet->encryption.label,
> -                         ptriplet->encryption.context, ses->smb3encryptionkey,
> -                         SMB3_SIGN_KEY_SIZE);
> -       if (rc)
> -               return rc;
> -
> -       rc = generate_key(ses, ptriplet->decryption.label,
> -                         ptriplet->decryption.context,
> -                         ses->smb3decryptionkey, SMB3_SIGN_KEY_SIZE);
> +       if (ses->binding) {
> +               struct TCP_Server_Info *server;
> +               cifs_dbg(VFS, "XXX: BINDING! gen signkey");
> +               server = cifs_ses_server(ses);
> +               rc = generate_key(ses, ptriplet->signing.label,
> +                                 ptriplet->signing.context,
> +                                 smb2_find_chan_signkey(ses, server),
> +                                 SMB3_SIGN_KEY_SIZE);
> +               if (rc)
> +                       return rc;
> +       } else {
> +               rc = generate_key(ses, ptriplet->signing.label,
> +                                 ptriplet->signing.context,
> +                                 ses->smb3signingkey,
> +                                 SMB3_SIGN_KEY_SIZE);
> +               if (rc)
> +                       return rc;
> +               rc = generate_key(ses, ptriplet->encryption.label,
> +                                 ptriplet->encryption.context,
> +                                 ses->smb3encryptionkey,
> +                                 SMB3_SIGN_KEY_SIZE);
> +               rc = generate_key(ses, ptriplet->decryption.label,
> +                                 ptriplet->decryption.context,
> +                                 ses->smb3decryptionkey,
> +                                 SMB3_SIGN_KEY_SIZE);
> +               if (rc)
> +                       return rc;
> +       }
>
>         if (rc)
>                 return rc;
> @@ -434,18 +488,35 @@ smb3_calc_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
>         struct cifs_ses *ses;
>         struct shash_desc *shash = &server->secmech.sdesccmacaes->shash;
>         struct smb_rqst drqst;
> +       u8 *key;
>
> -       ses = smb2_find_smb_ses(server, shdr->SessionId);
> +       ses = smb2_find_global_smb_ses(shdr->SessionId);
>         if (!ses) {
>                 cifs_server_dbg(VFS, "%s: Could not find session\n", __func__);
>                 return 0;
>         }
>
> +       /*
> +        * If we are binding an existing session use the session key,
> +        * otherwise use the channel key
> +        */
> +       if (ses->binding) {
> +               key = ses->smb3signingkey;
> +               cifs_dbg(VFS, "XXX: using bind ses key %px", key);
> +       } else {
> +               key = smb2_find_chan_signkey(ses, server);
> +               cifs_dbg(VFS, "XXX: using chan ses key %px", key);
> +               if (!key) {
> +                       cifs_dbg(VFS, "XXX: could not find channel key");
> +                       return 0;
> +               }
> +        }
> +
>         memset(smb3_signature, 0x0, SMB2_CMACAES_SIZE);
>         memset(shdr->Signature, 0x0, SMB2_SIGNATURE_SIZE);
>
>         rc = crypto_shash_setkey(server->secmech.cmacaes,
> -                                ses->smb3signingkey, SMB2_CMACAES_SIZE);
> +                                key, SMB2_CMACAES_SIZE);
>         if (rc) {
>                 cifs_server_dbg(VFS, "%s: Could not set key for cmac aes\n", __func__);
>                 return rc;
> @@ -494,18 +565,35 @@ static int
>  smb2_sign_rqst(struct smb_rqst *rqst, struct TCP_Server_Info *server)
>  {
>         int rc = 0;
> -       struct smb2_sync_hdr *shdr =
> -                       (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
> +       struct smb2_sync_hdr *shdr;
> +       struct smb2_sess_setup_req *ssr;
> +       bool is_binding;
> +       bool is_signed;
>
> -       if (!(shdr->Flags & SMB2_FLAGS_SIGNED) ||
> -           server->tcpStatus == CifsNeedNegotiate)
> -               return rc;
> +       shdr = (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
> +       ssr = (struct smb2_sess_setup_req *)shdr;
>
> -       if (!server->session_estab) {
> +       is_binding = shdr->Command == SMB2_SESSION_SETUP &&
> +               (ssr->Flags & SMB2_SESSION_REQ_FLAG_BINDING);
> +       is_signed = shdr->Flags & SMB2_FLAGS_SIGNED;
> +
> +       printk(KERN_WARNING "XXX: sign_rqst\n");
> +
> +       if (!is_signed) {
> +               printk(KERN_WARNING "XXX: no signed flag\n");
> +               return 0;
> +       }
> +       if (server->tcpStatus == CifsNeedNegotiate) {
> +               printk(KERN_WARNING "XXX: need nego\n");
> +               return 0;
> +       }
> +       if (!is_binding && !server->session_estab) {
>                 strncpy(shdr->Signature, "BSRSPYL", 8);
> -               return rc;
> +               printk(KERN_WARNING "XXX: !session_estab\n");
> +               return 0;
>         }
>
> +       printk(KERN_WARNING "XXX: actually signing!\n");
>         rc = server->ops->calc_signature(rqst, server);
>
>         return rc;
> @@ -610,18 +698,18 @@ smb2_mid_entry_alloc(const struct smb2_sync_hdr *shdr,
>  }
>
>  static int
> -smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
> +smb2_get_mid_entry(struct cifs_ses *ses, struct TCP_Server_Info *server, struct smb2_sync_hdr *shdr,
>                    struct mid_q_entry **mid)
>  {
> -       if (ses->server->tcpStatus == CifsExiting)
> +       if (server->tcpStatus == CifsExiting)
>                 return -ENOENT;
>
> -       if (ses->server->tcpStatus == CifsNeedReconnect) {
> +       if (server->tcpStatus == CifsNeedReconnect) {
>                 cifs_dbg(FYI, "tcp session dead - return to caller to retry\n");
>                 return -EAGAIN;
>         }
>
> -       if (ses->server->tcpStatus == CifsNeedNegotiate &&
> +       if (server->tcpStatus == CifsNeedNegotiate &&
>            shdr->Command != SMB2_NEGOTIATE)
>                 return -EAGAIN;
>
> @@ -638,11 +726,11 @@ smb2_get_mid_entry(struct cifs_ses *ses, struct smb2_sync_hdr *shdr,
>                 /* else ok - we are shutting down the session */
>         }
>
> -       *mid = smb2_mid_entry_alloc(shdr, ses->server);
> +       *mid = smb2_mid_entry_alloc(shdr, server);
>         if (*mid == NULL)
>                 return -ENOMEM;
>         spin_lock(&GlobalMid_Lock);
> -       list_add_tail(&(*mid)->qhead, &ses->server->pending_mid_q);
> +       list_add_tail(&(*mid)->qhead, &server->pending_mid_q);
>         spin_unlock(&GlobalMid_Lock);
>
>         return 0;
> @@ -675,24 +763,24 @@ smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
>  }
>
>  struct mid_q_entry *
> -smb2_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
> +smb2_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *server, struct smb_rqst *rqst)
>  {
>         int rc;
>         struct smb2_sync_hdr *shdr =
>                         (struct smb2_sync_hdr *)rqst->rq_iov[0].iov_base;
>         struct mid_q_entry *mid;
>
> -       smb2_seq_num_into_buf(ses->server, shdr);
> +       smb2_seq_num_into_buf(server, shdr);
>
> -       rc = smb2_get_mid_entry(ses, shdr, &mid);
> +       rc = smb2_get_mid_entry(ses, server, shdr, &mid);
>         if (rc) {
> -               revert_current_mid_from_hdr(ses->server, shdr);
> +               revert_current_mid_from_hdr(server, shdr);
>                 return ERR_PTR(rc);
>         }
>
> -       rc = smb2_sign_rqst(rqst, ses->server);
> +       rc = smb2_sign_rqst(rqst, server);
>         if (rc) {
> -               revert_current_mid_from_hdr(ses->server, shdr);
> +               revert_current_mid_from_hdr(server, shdr);
>                 cifs_delete_mid(mid);
>                 return ERR_PTR(rc);
>         }
> diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
> index 308ad0f495e1..a4c2d62ee437 100644
> --- a/fs/cifs/transport.c
> +++ b/fs/cifs/transport.c
> @@ -923,7 +923,7 @@ cifs_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
>  }
>
>  struct mid_q_entry *
> -cifs_setup_request(struct cifs_ses *ses, struct smb_rqst *rqst)
> +cifs_setup_request(struct cifs_ses *ses, struct TCP_Server_Info *ignored, struct smb_rqst *rqst)
>  {
>         int rc;
>         struct smb_hdr *hdr = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
> @@ -995,7 +995,15 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
>                 return -EIO;
>         }
>
> -       server = ses->server;
> +       if (!ses->binding) {
> +               uint index = ((unsigned)get_random_int()) % ses->chan_count;
> +               cifs_dbg(VFS, "XXX: send/recv: using random channel %d", index);
> +               server = ses->chans[index].server;
> +       } else {
> +               cifs_dbg(VFS, "XXX: send/recv: binding, using last serv");
> +               server = cifs_ses_server(ses);
> +       }
> +
>         if (server->tcpStatus == CifsExiting)
>                 return -ENOENT;
>
> @@ -1040,7 +1048,7 @@ compound_send_recv(const unsigned int xid, struct cifs_ses *ses,
>         }
>
>         for (i = 0; i < num_rqst; i++) {
> -               midQ[i] = server->ops->setup_request(ses, &rqst[i]);
> +               midQ[i] = server->ops->setup_request(ses, server, &rqst[i]);
>                 if (IS_ERR(midQ[i])) {
>                         revert_current_mid(server, i);
>                         for (j = 0; j < i; j++)
> --
> 2.16.4
>


-- 
Thanks,

Steve

      reply	other threads:[~2019-09-18  2:45 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-09-17 22:14 [WIP] multichannel Aurelien Aptel
2019-09-17 23:51 ` [WIP v3] multichannel Aurelien Aptel
2019-09-18  2:44   ` Steve French [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to='CAH2r5msndZMzJyOSDD16+=Y_b9MC-dJAuKGNWk5wzajxUcDS8A@mail.gmail.com' \
    --to=smfrench@gmail.com \
    --cc=aaptel@suse.com \
    --cc=linux-cifs@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).