From patchwork Fri Jun 18 06:29:42 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Steve French X-Patchwork-Id: 12330273 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-12.8 required=3.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_CR_TRAILER,INCLUDES_PATCH, MAILING_LIST_MULTI,SPF_HELO_NONE,SPF_PASS autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 12537C48BDF for ; Fri, 18 Jun 2021 06:29:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E0DC060C41 for ; Fri, 18 Jun 2021 06:29:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S231436AbhFRGcE (ORCPT ); Fri, 18 Jun 2021 02:32:04 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:50152 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231422AbhFRGcE (ORCPT ); Fri, 18 Jun 2021 02:32:04 -0400 Received: from mail-lf1-x12b.google.com (mail-lf1-x12b.google.com [IPv6:2a00:1450:4864:20::12b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6926FC061574 for ; Thu, 17 Jun 2021 23:29:55 -0700 (PDT) Received: by mail-lf1-x12b.google.com with SMTP id j2so14722745lfg.9 for ; Thu, 17 Jun 2021 23:29:55 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:from:date:message-id:subject:to; bh=0MbWd1AfCSK01HrWsd4kG/qhZFGBppSG/d3MO0ZX71E=; b=fu2MR3qy/3eaJKmo4fAVwM1SX20rLAv895bUJw4mvQH4D4v6RW++WmaUpcz4xDDbZK WZxphjXYOO9norcgDO4NuE8ldrhuMmsd09yzSbUIOtIu0S9qjWdgTUPgti/gXseGD/0i DyIzjwUe6jl3ZEiR/R232eSVAB3HUljRZp7rbG0ezXczn3GLiZV+48Zg7wq7VjKHRt+/ lzlHUHJmWPFR8mQPO5W2KPX/kpNm7bq6lAvT3qS+4dgL8G43qEaubeITKNNy6dMjZNh+ 9zhgJJLD+M3ai9vf+K5zck6amg5hm1HkbW5lGWeSHR1Azf10QtKk9bvSMJBRfrq+4iXf N45A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:from:date:message-id:subject:to; bh=0MbWd1AfCSK01HrWsd4kG/qhZFGBppSG/d3MO0ZX71E=; b=oSEleyBxL6Fw4pN2bif76pVTsVUs3uOQf2aoKvDS3KJfEf6xxxgMV8sdM6FX9awkWJ AMxTAl5XlGbkYbW+UX379FJ6O1QqVF6/p1CVgLmdV+4xOy9xJMxFhz3yBs/LM/leOXPv igWYvE0YYzGsRPEOyYzo/mOSnjJ74tfYKR6K7V9gJAq06W/ywUUnlHamuMJ3VXj9N/zQ 3SbZjJGqlvlEvMuQSWBHhEw5NGHm5tTJ0Cy/GNUiq3m5Bd9DLTf0a9GJ36zSwMIh5DgY kamEAeqh0nhImkdGxqvBrutStDJFr4PJTEJysCe79ZEJsW+SyU3NP2HUviqWwm49abOu GI9A== X-Gm-Message-State: AOAM531FokhMgmTSfFDA/rnQk3xz72fE1OHt9OMrmHbRDRD6e7ypAdA4 rxRLMLkyTUPCUfQONkt6ha9mKHVM9oeJZSQtd8PM1qBdMdM= X-Google-Smtp-Source: ABdhPJyzKEmP5y8jVEMCczgBWxKV3STH0epq8PUjFu7ITY3Ep8ZS/6H4rCkK5gl0+rhPNbT/KNqphxPLN7tNNSLtgH4= X-Received: by 2002:a05:6512:344f:: with SMTP id j15mr1892115lfr.175.1623997793593; Thu, 17 Jun 2021 23:29:53 -0700 (PDT) MIME-Version: 1.0 From: Steve French Date: Fri, 18 Jun 2021 01:29:42 -0500 Message-ID: Subject: [PATCH] [CIFS] changes to support multichannel during channel reconnect To: Shyam Prasad N , CIFS Precedence: bulk List-ID: X-Mailing-List: linux-cifs@vger.kernel.org Lightly updated version of Shyam's patch to merge into current for-next From e7605c8b168e48a31a8cfe2dcb5b2478fb26f678 Mon Sep 17 00:00:00 2001 From: Shyam Prasad N Date: Sat, 22 May 2021 15:44:44 +0000 Subject: [PATCH] cifs: changes to support multichannel during channel reconnect The reconnect scenario when using a multichannel mount had a few issues: 1. A tcp connection on binding channel did not have a way to get to the primary connection or smb channel easily. I replaced is_channel field with primary_server, which is a referenced pointer to the primary channel tcp connection. This also allows us to get to the smb session for the channel. 2. In case of cifs_reconnect, we needed a way to identify the channels under the smb session which are in reconnect, so that the traffic to other channels can continue. So I replaced the bool need_reconnect with a bitmask identifying all the channels that are currently needing reconnection and called it chans_need_reconnect. When a channel needs reconnection, the bit corresponding to the index of the server in ses->chans is used to set this bitmask. Checking if no channels or all the channels need reconnect then becomes very easy. 3. Mark tcon->need_reconnect only when all the channels underneath need to reconnect. Signed-off-by: Shyam Prasad N Signed-off-by: Steve French --- fs/cifs/cifs_debug.c | 2 +- fs/cifs/cifsglob.h | 24 +++++++++++-- fs/cifs/cifsproto.h | 17 ++++++++- fs/cifs/cifssmb.c | 27 +++++++++++--- fs/cifs/connect.c | 44 +++++++++++++++++++---- fs/cifs/sess.c | 80 ++++++++++++++++++++++++++++++++++++----- fs/cifs/smb2pdu.c | 44 +++++++++++++++++------ fs/cifs/smb2transport.c | 5 ++- 8 files changed, 207 insertions(+), 36 deletions(-) diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c index 8857ac7e7a14..ac66985728c0 100644 --- a/fs/cifs/cifs_debug.c +++ b/fs/cifs/cifs_debug.c @@ -275,7 +275,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v) c = 0; spin_lock(&cifs_tcp_ses_lock); list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { - if (server->is_channel) + if (CIFS_SERVER_IS_CHAN(server)) continue; c++; diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h index 988346904fd0..7849df3da81b 100644 --- a/fs/cifs/cifsglob.h +++ b/fs/cifs/cifsglob.h @@ -681,7 +681,15 @@ struct TCP_Server_Info { */ int nr_targets; bool noblockcnt; /* use non-blocking connect() */ - bool is_channel; /* if a session channel */ + + /* + * If this is a session channel, + * primary_server holds the ref-counted + * pointer to primary channel connection for the session. + */ +#define CIFS_SERVER_IS_CHAN(server) (!!(server)->primary_server) + struct TCP_Server_Info *primary_server; + #ifdef CONFIG_CIFS_SWN_UPCALL bool use_swn_dstaddr; struct sockaddr_storage swn_dstaddr; @@ -905,7 +913,6 @@ struct cifs_ses { struct ntlmssp_auth *ntlmssp; /* ciphertext, flags, server challenge */ enum securityEnum sectype; /* what security flavor was specified? */ 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; @@ -931,11 +938,24 @@ struct cifs_ses { unsigned long iface_last_update; /* jiffies */ #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_CHAN_NEEDS_RECONNECT(ses, index) \ + test_bit((index), &(ses)->chans_need_reconnect) + 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 */ + /* + * 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. + */ + unsigned long chans_need_reconnect; }; /* diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h index e0def0f0714b..28951cd11280 100644 --- a/fs/cifs/cifsproto.h +++ b/fs/cifs/cifsproto.h @@ -268,7 +268,9 @@ extern void cifs_close_deferred_file(struct cifsInodeInfo *cifs_inode); extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon); -extern struct TCP_Server_Info *cifs_get_tcp_session(struct smb3_fs_context *ctx); +extern struct TCP_Server_Info * +cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server); extern void cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect); extern void cifs_put_tcon(struct cifs_tcon *tcon); @@ -607,6 +609,19 @@ 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); +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); int copy_path_name(char *dst, const char *src); int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c index 58ebec4d4413..94cd154c4b9a 100644 --- a/fs/cifs/cifssmb.c +++ b/fs/cifs/cifssmb.c @@ -183,9 +183,13 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) retries = server->nr_targets; } - if (!ses->need_reconnect && !tcon->need_reconnect) + if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) return 0; + cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d", + tcon->ses->chans_need_reconnect, + tcon->need_reconnect); + nls_codepage = load_nls_default(); /* @@ -205,8 +209,19 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) goto out; } + /* Recheck after acquire mutex */ + if (!cifs_chan_needs_reconnect(ses, server)) { + /* this just means that we only need to tcon */ + if (tcon->need_reconnect) + goto skip_sess_setup; + + rc = -EHOSTDOWN; + mutex_unlock(&ses->session_mutex); + goto out; + } + rc = cifs_negotiate_protocol(0, ses); - if (rc == 0 && ses->need_reconnect) + if (!rc) rc = cifs_setup_session(0, ses, nls_codepage); /* do we need to reconnect tcon? */ @@ -215,6 +230,7 @@ cifs_reconnect_tcon(struct cifs_tcon *tcon, int smb_command) goto out; } +skip_sess_setup: cifs_mark_open_files_invalid(tcon); rc = cifs_tree_connect(0, tcon, nls_codepage); mutex_unlock(&ses->session_mutex); @@ -354,7 +370,8 @@ static int smb_init_no_reconnect(int smb_command, int wct, struct cifs_tcon *tcon, void **request_buf, void **response_buf) { - if (tcon->ses->need_reconnect || tcon->need_reconnect) + if (cifs_chan_needs_reconnect(tcon->ses, tcon->ses->server) || + tcon->need_reconnect) return -EHOSTDOWN; return __smb_init(smb_command, wct, tcon, request_buf, response_buf); @@ -705,7 +722,7 @@ CIFSSMBTDis(const unsigned int xid, struct cifs_tcon *tcon) * the tcon is no longer on the list, so no need to take lock before * checking this. */ - if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) + if ((tcon->need_reconnect) || CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses)) return 0; rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, @@ -801,7 +818,7 @@ CIFSSMBLogoff(const unsigned int xid, struct cifs_ses *ses) return -EIO; mutex_lock(&ses->session_mutex); - if (ses->need_reconnect) + if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) goto session_already_dead; /* no need to send SMBlogoff if uid already closed due to reconnect */ rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB); diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 8d95607a9312..092daf52eb17 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -164,7 +164,7 @@ static inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb, * cifs tcp session reconnection * * mark tcp session as reconnecting so temporarily locked - * mark all smb sessions as reconnecting for tcp session + * update all smb sessions about reconnect of this tcp session * reconnect tcp session * wake up waiters on reconnection? - (not needed currently) */ @@ -172,6 +172,7 @@ int cifs_reconnect(struct TCP_Server_Info *server) { int rc = 0; + struct TCP_Server_Info *pserver; struct list_head *tmp, *tmp2; struct cifs_ses *ses; struct cifs_tcon *tcon; @@ -184,6 +185,9 @@ cifs_reconnect(struct TCP_Server_Info *server) struct dfs_cache_tgt_iterator *tgt_it = NULL; #endif + /* If server is a channel, select the primary channel */ + pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server; + spin_lock(&GlobalMid_Lock); server->nr_targets = 1; #ifdef CONFIG_CIFS_DFS_UPCALL @@ -234,16 +238,30 @@ cifs_reconnect(struct TCP_Server_Info *server) and the tid bad so they are not used until reconnected */ cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n", __func__); + spin_lock(&cifs_tcp_ses_lock); - list_for_each(tmp, &server->smb_ses_list) { + list_for_each(tmp, &pserver->smb_ses_list) { ses = list_entry(tmp, struct cifs_ses, smb_ses_list); - ses->need_reconnect = true; + + /* + * we're having to pick this mutex within a spinlock + * Make sure that we don't sleep too much. + */ + mutex_lock(&ses->session_mutex); + cifs_chan_set_need_reconnect(ses, server); + + /* If all channels need reconnect, then tcon needs reconnect */ + if (!CIFS_ALL_CHANS_NEED_RECONNECT(ses)) + goto skip_tcon_reconnect; + list_for_each(tmp2, &ses->tcon_list) { tcon = list_entry(tmp2, struct cifs_tcon, tcon_list); tcon->need_reconnect = true; } if (ses->tcon_ipc) ses->tcon_ipc->need_reconnect = true; +skip_tcon_reconnect: + mutex_unlock(&ses->session_mutex); } spin_unlock(&cifs_tcp_ses_lock); @@ -1231,7 +1249,7 @@ cifs_find_tcp_session(struct smb3_fs_context *ctx) * Skip ses channels since they're only handled in lower layers * (e.g. cifs_send_recv). */ - if (server->is_channel || !match_server(server, ctx)) + if (CIFS_SERVER_IS_CHAN(server) || !match_server(server, ctx)) continue; ++server->srv_count; @@ -1259,6 +1277,10 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) list_del_init(&server->tcp_ses_list); spin_unlock(&cifs_tcp_ses_lock); + /* For secondary channels, we pick up ref-count on the primary server */ + if (CIFS_SERVER_IS_CHAN(server)) + cifs_put_tcp_session(server->primary_server, from_reconnect); + cancel_delayed_work_sync(&server->echo); if (from_reconnect) @@ -1289,7 +1311,8 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect) } struct TCP_Server_Info * -cifs_get_tcp_session(struct smb3_fs_context *ctx) +cifs_get_tcp_session(struct smb3_fs_context *ctx, + struct TCP_Server_Info *primary_server) { struct TCP_Server_Info *tcp_ses = NULL; int rc; @@ -1316,6 +1339,10 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx) goto out_err_crypto_release; } + if (primary_server) { + ++primary_server->srv_count; + tcp_ses->primary_server = primary_server; + } tcp_ses->conn_id = atomic_inc_return(&tcpSesNextId); tcp_ses->noblockcnt = ctx->rootfs; tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs; @@ -1431,6 +1458,8 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx) out_err: if (tcp_ses) { + if (CIFS_SERVER_IS_CHAN(tcp_ses)) + cifs_put_tcp_session(tcp_ses->primary_server, false); if (!IS_ERR(tcp_ses->hostname)) kfree(tcp_ses->hostname); if (tcp_ses->ssocket) @@ -1812,7 +1841,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) free_xid(xid); return ERR_PTR(rc); } - if (ses->need_reconnect) { + if (cifs_chan_needs_reconnect(ses, server)) { cifs_dbg(FYI, "Session needs reconnect\n"); rc = cifs_setup_session(xid, ses, ctx->local_nls); @@ -1874,6 +1903,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx) ses->chans[0].server = server; ses->chan_count = 1; ses->chan_max = ctx->multichannel ? ctx->max_channels:1; + ses->chans_need_reconnect = 1; rc = cifs_negotiate_protocol(xid, ses); if (!rc) @@ -2796,7 +2826,7 @@ static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cif *xid = get_xid(); /* get a reference to a tcp session */ - server = cifs_get_tcp_session(ctx); + server = cifs_get_tcp_session(ctx, NULL); if (IS_ERR(server)) { rc = PTR_ERR(server); return rc; diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index c5785fd3f52e..a005d13fb8f0 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -62,6 +62,53 @@ bool is_ses_using_iface(struct cifs_ses *ses, struct cifs_server_iface *iface) 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 */ int cifs_try_adding_channels(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses) { @@ -170,6 +217,7 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, struct cifs_server_iface *iface) { struct cifs_chan *chan; + struct TCP_Server_Info *chan_server; struct smb3_fs_context ctx = {NULL}; static const char unc_fmt[] = "\\%s\\foo"; char unc[sizeof(unc_fmt)+SERVER_NAME_LEN_WITH_NULL] = {0}; @@ -241,18 +289,23 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, SMB2_CLIENT_GUID_SIZE); ctx.use_client_guid = true; - mutex_lock(&ses->session_mutex); + /* Indicate ses->server as the primary server */ + chan_server = cifs_get_tcp_session(&ctx, ses->server); + mutex_lock(&ses->session_mutex); 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; goto out; } - spin_lock(&cifs_tcp_ses_lock); - chan->server->is_channel = true; - spin_unlock(&cifs_tcp_ses_lock); + + ses->chan_count++; + atomic_set(&ses->chan_seq, 0); + + /* Mark this channel as needing connect/setup */ + cifs_chan_set_need_reconnect(ses, chan->server); /* * We need to allocate the server crypto now as we will need @@ -284,11 +337,15 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, * ses to the new server. */ - ses->chan_count++; - atomic_set(&ses->chan_seq, 0); out: ses->binding = false; ses->binding_chan = NULL; + + if (rc && chan->server) { + cifs_chan_clear_need_reconnect(ses, chan->server); + ses->chan_count--; + } + mutex_unlock(&ses->session_mutex); if (rc && chan->server) @@ -917,9 +974,14 @@ sess_establish_session(struct sess_data *sess_data) mutex_unlock(&ses->server->srv_mutex); cifs_dbg(FYI, "CIFS session established successfully\n"); + if (ses->binding) + cifs_chan_clear_need_reconnect(ses, ses->binding_chan->server); + else + cifs_chan_clear_need_reconnect(ses, ses->server); + /* keep existing ses state if binding */ spin_lock(&GlobalMid_Lock); - ses->status = CifsGood; - ses->need_reconnect = false; + if (!ses->binding) + ses->status = CifsGood; spin_unlock(&GlobalMid_Lock); return 0; diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index 29b729de2b27..87f1795fffd4 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -230,9 +230,13 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, retries = server->nr_targets; } - if (!tcon->ses->need_reconnect && !tcon->need_reconnect) + if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) return 0; + cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d", + tcon->ses->chans_need_reconnect, + tcon->need_reconnect); + nls_codepage = load_nls_default(); /* @@ -255,13 +259,24 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, /* * If we are reconnecting an extra channel, bind */ - if (server->is_channel) { + if (CIFS_SERVER_IS_CHAN(server)) { ses->binding = true; ses->binding_chan = cifs_ses_find_chan(ses, server); } + /* Recheck after acquire mutex */ + if (!cifs_chan_needs_reconnect(ses, server)) { + /* this just means that we only need to tcon */ + if (tcon->need_reconnect) + goto skip_sess_setup; + + rc = -EHOSTDOWN; + mutex_unlock(&ses->session_mutex); + goto out; + } + rc = cifs_negotiate_protocol(0, tcon->ses); - if (!rc && tcon->ses->need_reconnect) { + if (!rc && tcon->ses->chans_need_reconnect) { rc = cifs_setup_session(0, tcon->ses, nls_codepage); if ((rc == -EACCES) && !tcon->retry) { rc = -EHOSTDOWN; @@ -282,6 +297,7 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, goto out; } +skip_sess_setup: cifs_mark_open_files_invalid(tcon); if (tcon->use_persistent) tcon->need_reopen_files = true; @@ -1190,6 +1206,7 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) req->sync_hdr.Flags |= SMB2_FLAGS_SIGNED; req->PreviousSessionId = 0; req->Flags = SMB2_SESSION_REQ_FLAG_BINDING; + cifs_dbg(FYI, "Binding to sess id: %llx\n", sess_data->ses->Suid); } else { /* First session, not a reauthenticate */ req->sync_hdr.SessionId = 0; @@ -1199,6 +1216,8 @@ SMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data) */ req->PreviousSessionId = sess_data->previous_session; 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 */ @@ -1292,13 +1311,17 @@ SMB2_sess_establish_session(struct SMB2_sess_data *sess_data) mutex_unlock(&server->srv_mutex); cifs_dbg(FYI, "SMB2/3 session established successfully\n"); + + if (ses->binding) + cifs_chan_clear_need_reconnect(ses, ses->binding_chan->server); + else + cifs_chan_clear_need_reconnect(ses, ses->server); + /* keep existing ses state if binding */ - if (!ses->binding) { - spin_lock(&GlobalMid_Lock); + spin_lock(&GlobalMid_Lock); + if (!ses->binding) ses->status = CifsGood; - ses->need_reconnect = false; - spin_unlock(&GlobalMid_Lock); - } + spin_unlock(&GlobalMid_Lock); return rc; } @@ -1642,7 +1665,7 @@ SMB2_logoff(const unsigned int xid, struct cifs_ses *ses) return -EIO; /* no need to send SMB logoff if uid already closed due to reconnect */ - if (ses->need_reconnect) + if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) goto smb2_session_already_dead; rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server, @@ -1854,7 +1877,8 @@ SMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon) if (!ses || !(ses->server)) return -EIO; - if ((tcon->need_reconnect) || (tcon->ses->need_reconnect)) + if ((tcon->need_reconnect) || + (CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses))) return 0; close_cached_dir_lease(&tcon->crfid); diff --git a/fs/cifs/smb2transport.c b/fs/cifs/smb2transport.c index 6f7952ea4941..d15f0aaa1338 100644 --- a/fs/cifs/smb2transport.c +++ b/fs/cifs/smb2transport.c @@ -195,8 +195,8 @@ smb2_find_smb_tcon(struct TCP_Server_Info *server, __u64 ses_id, __u32 tid) } tcon = smb2_find_smb_sess_tcon_unlocked(ses, tid); if (!tcon) { - cifs_put_smb_ses(ses); spin_unlock(&cifs_tcp_ses_lock); + cifs_put_smb_ses(ses); return NULL; } spin_unlock(&cifs_tcp_ses_lock); @@ -398,6 +398,7 @@ generate_smb3signingkey(struct cifs_ses *ses, #ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS struct TCP_Server_Info *server = ses->server; #endif + unsigned int chan_index; /* * All channels use the same encryption/decryption keys but @@ -414,6 +415,8 @@ generate_smb3signingkey(struct cifs_ses *ses, ptriplet->signing.context, cifs_ses_binding_channel(ses)->signkey, SMB3_SIGN_KEY_SIZE); + cifs_dbg(FYI, "%s: Generated key for chan %u\n", + __func__, chan_index); if (rc) return rc; } else { -- 2.30.2