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;
+       /* reuse existing guid for multichannel */
+       u8 client_guid[SMB2_CLIENT_GUID_SIZE];
        unsigned int bsize;
        unsigned int rsize;
        unsigned int wsize;
        __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 */
 };
        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_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;
+       atomic_t chan_seq; /* round robin state */
 };
 
 static inline bool
 
        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 */
        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,
        { Opt_noresilient, "noresilienthandles"},
        { Opt_domainauto, "domainauto"},
        { Opt_rdma, "rdma"},
+       { Opt_multichannel, "multichannel" },
+       { Opt_nomultichannel, "nomultichannel" },
 
        { Opt_backupuid, "backupuid=%s" },
        { Opt_backupgid, "backupgid=%s" },
        { 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=" },
 
        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;
 
                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,
                        }
                        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 */
 
               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
            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))