cifs: add multichannel mount options and data structs
authorAurelien Aptel <aaptel@suse.com>
Fri, 20 Sep 2019 02:32:20 +0000 (04:32 +0200)
committerSteve French <stfrench@microsoft.com>
Mon, 25 Nov 2019 07:16:30 +0000 (01:16 -0600)
adds:
- [no]multichannel to enable/disable multichannel
- max_channels=N to control how many channels to create

these options are then stored in the volume struct.

- store channels and max_channels in cifs_ses

Signed-off-by: Aurelien Aptel <aaptel@suse.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/connect.c

index 7bd7cd3..5664bb7 100644 (file)
@@ -613,6 +613,10 @@ cifs_show_options(struct seq_file *s, struct dentry *root)
        /* convert actimeo and display it in seconds */
        seq_printf(s, ",actimeo=%lu", cifs_sb->actimeo / HZ);
 
+       if (tcon->ses->chan_max > 1)
+               seq_printf(s, ",multichannel,max_channel=%zu",
+                          tcon->ses->chan_max);
+
        return 0;
 }
 
index 5745322..ac37104 100644 (file)
@@ -591,6 +591,10 @@ 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;
+       /* reuse existing guid for multichannel */
+       u8 client_guid[SMB2_CLIENT_GUID_SIZE];
        unsigned int bsize;
        unsigned int rsize;
        unsigned int wsize;
@@ -607,6 +611,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 */
 };
@@ -953,6 +958,11 @@ 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
  */
@@ -1002,6 +1012,12 @@ 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;
+       atomic_t chan_seq; /* round robin state */
 };
 
 static inline bool
index a7a0267..1a7d14a 100644 (file)
@@ -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=" },
@@ -1681,6 +1685,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;
 
@@ -1974,6 +1982,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,
@@ -2137,6 +2151,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 */
 
@@ -2781,7 +2804,11 @@ 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
@@ -2870,6 +2897,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))