from_kuid(&init_user_ns, ses->linux_uid),
                                   from_kuid(&init_user_ns, ses->cred_uid));
 
+                       spin_lock(&ses->chan_lock);
                        if (ses->chan_count > 1) {
                                seq_printf(m, "\n\n\tExtra Channels: %zu ",
                                           ses->chan_count-1);
                                for (j = 1; j < ses->chan_count; j++)
                                        cifs_dump_channel(m, j, &ses->chans[j]);
                        }
+                       spin_unlock(&ses->chan_lock);
 
                        seq_puts(m, "\n\n\tShares: ");
                        j = 0;
 
         * iface_lock should be taken when accessing any of these fields
         */
        spinlock_t iface_lock;
+       /* ========= begin: protected by iface_lock ======== */
        struct cifs_server_iface *iface_list;
        size_t iface_count;
        unsigned long iface_last_update; /* jiffies */
+       /* ========= end: protected by iface_lock ======== */
 
+       spinlock_t chan_lock;
+       /* ========= begin: protected by chan_lock ======== */
 #define CIFS_MAX_CHANNELS 16
        struct cifs_chan chans[CIFS_MAX_CHANNELS];
        struct cifs_chan *binding_chan;
        size_t chan_count;
        size_t chan_max;
        atomic_t chan_seq; /* round robin state */
+       /* ========= end: protected by chan_lock ======== */
 };
 
 /*
 
         * If an existing session is limited to less channels than
         * requested, it should not be reused
         */
-       if (ses->chan_max < ctx->max_channels)
+       spin_lock(&ses->chan_lock);
+       if (ses->chan_max < ctx->max_channels) {
+               spin_unlock(&ses->chan_lock);
                return 0;
+       }
+       spin_unlock(&ses->chan_lock);
 
        switch (ses->sectype) {
        case Kerberos:
 void cifs_put_smb_ses(struct cifs_ses *ses)
 {
        unsigned int rc, xid;
+       unsigned int chan_count;
        struct TCP_Server_Info *server = ses->server;
        cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
 
        list_del_init(&ses->smb_ses_list);
        spin_unlock(&cifs_tcp_ses_lock);
 
+       spin_lock(&ses->chan_lock);
+       chan_count = ses->chan_count;
+       spin_unlock(&ses->chan_lock);
+
        /* close any extra channels */
-       if (ses->chan_count > 1) {
+       if (chan_count > 1) {
                int i;
 
-               for (i = 1; i < ses->chan_count; i++)
+               for (i = 1; i < chan_count; i++) {
+                       /*
+                        * note: for now, we're okay accessing ses->chans
+                        * without chan_lock. But when chans can go away, we'll
+                        * need to introduce ref counting to make sure that chan
+                        * is not freed from under us.
+                        */
                        cifs_put_tcp_session(ses->chans[i].server, 0);
+                       ses->chans[i].server = NULL;
+               }
        }
 
        sesInfoFree(ses);
        mutex_lock(&ses->session_mutex);
 
        /* add server as first channel */
+       spin_lock(&ses->chan_lock);
        ses->chans[0].server = server;
        ses->chan_count = 1;
        ses->chan_max = ctx->multichannel ? ctx->max_channels:1;
+       spin_unlock(&ses->chan_lock);
 
        rc = cifs_negotiate_protocol(xid, ses);
        if (!rc)
 
                INIT_LIST_HEAD(&ret_buf->tcon_list);
                mutex_init(&ret_buf->session_mutex);
                spin_lock_init(&ret_buf->iface_lock);
+               spin_lock_init(&ret_buf->chan_lock);
        }
        return ret_buf;
 }
 
 {
        int i;
 
+       spin_lock(&ses->chan_lock);
        for (i = 0; i < ses->chan_count; i++) {
-               if (is_server_using_iface(ses->chans[i].server, iface))
+               if (is_server_using_iface(ses->chans[i].server, iface)) {
+                       spin_unlock(&ses->chan_lock);
                        return true;
+               }
        }
+       spin_unlock(&ses->chan_lock);
        return false;
 }
 
 /* returns number of channels added */
 int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses)
 {
-       int old_chan_count = ses->chan_count;
-       int left = ses->chan_max - ses->chan_count;
+       int old_chan_count, new_chan_count;
+       int left;
        int i = 0;
        int rc = 0;
        int tries = 0;
        struct cifs_server_iface *ifaces = NULL;
        size_t iface_count;
 
+       if (ses->server->dialect < SMB30_PROT_ID) {
+               cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
+               return 0;
+       }
+
+       spin_lock(&ses->chan_lock);
+
+       new_chan_count = old_chan_count = ses->chan_count;
+       left = ses->chan_max - ses->chan_count;
+
        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 < SMB30_PROT_ID) {
-               cifs_dbg(VFS, "multichannel is not supported on this protocol version, use 3.0 or above\n");
+               spin_unlock(&ses->chan_lock);
                return 0;
        }
 
        if (!(ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
                cifs_dbg(VFS, "server %s does not support multichannel\n", ses->server->hostname);
                ses->chan_max = 1;
+               spin_unlock(&ses->chan_lock);
                return 0;
        }
+       spin_unlock(&ses->chan_lock);
 
        /*
         * Make a copy of the iface list at the time and use that
                cifs_dbg(FYI, "successfully opened new channel on iface#%d\n",
                         i);
                left--;
+               new_chan_count++;
        }
 
        kfree(ifaces);
-       return ses->chan_count - old_chan_count;
+       return new_chan_count - old_chan_count;
 }
 
 /*
 {
        int i;
 
+       spin_lock(&ses->chan_lock);
        for (i = 0; i < ses->chan_count; i++) {
-               if (ses->chans[i].server == server)
+               if (ses->chans[i].server == server) {
+                       spin_unlock(&ses->chan_lock);
                        return &ses->chans[i];
+               }
        }
+       spin_unlock(&ses->chan_lock);
        return NULL;
 }
 
 cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
                     struct cifs_server_iface *iface)
 {
+       struct TCP_Server_Info *chan_server;
        struct cifs_chan *chan;
        struct smb3_fs_context ctx = {NULL};
        static const char unc_fmt[] = "\\%s\\foo";
               SMB2_CLIENT_GUID_SIZE);
        ctx.use_client_guid = true;
 
-       mutex_lock(&ses->session_mutex);
+       chan_server = cifs_get_tcp_session(&ctx);
 
+       mutex_lock(&ses->session_mutex);
+       spin_lock(&ses->chan_lock);
        chan = ses->binding_chan = &ses->chans[ses->chan_count];
-       chan->server = cifs_get_tcp_session(&ctx);
+       chan->server = chan_server;
        if (IS_ERR(chan->server)) {
                rc = PTR_ERR(chan->server);
                chan->server = NULL;
+               spin_unlock(&ses->chan_lock);
                goto out;
        }
+       spin_unlock(&ses->chan_lock);
+
        spin_lock(&cifs_tcp_ses_lock);
        chan->server->is_channel = true;
        spin_unlock(&cifs_tcp_ses_lock);
         * ses to the new server.
         */
 
+       spin_lock(&ses->chan_lock);
        ses->chan_count++;
        atomic_set(&ses->chan_seq, 0);
+       spin_unlock(&ses->chan_lock);
+
 out:
        ses->binding = false;
        ses->binding_chan = NULL;
 
        if (!ses)
                return NULL;
 
+       spin_lock(&ses->chan_lock);
        if (!ses->binding) {
                /* round robin */
                if (ses->chan_count > 1) {
                        index = (uint)atomic_inc_return(&ses->chan_seq);
                        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);
        }
 }