summaryrefslogtreecommitdiff
path: root/fs/cifs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/cifs')
-rw-r--r--fs/cifs/cifs_debug.c3
-rw-r--r--fs/cifs/cifsglob.h10
-rw-r--r--fs/cifs/cifsproto.h5
-rw-r--r--fs/cifs/connect.c24
-rw-r--r--fs/cifs/sess.c6
-rw-r--r--fs/cifs/smb2pdu.c2
6 files changed, 36 insertions, 14 deletions
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 248a8f973cf9..d282caf9f037 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -271,7 +271,8 @@ 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)
+ /* channel info will be printed as a part of sessions below */
+ if (CIFS_SERVER_IS_CHAN(server))
continue;
c++;
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 3c18c56a119b..be74606724c7 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -690,7 +690,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;
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index b2697356b5e7..f3073a62ce57 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -269,8 +269,9 @@ extern void cifs_close_all_deferred_files(struct cifs_tcon *cifs_tcon);
extern void cifs_close_deferred_file_under_dentry(struct cifs_tcon *cifs_tcon,
const char *path);
-
-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);
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index 09a532ed8fe4..f80b73f2d0a0 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -173,6 +173,7 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
struct cifs_tcon *tcon;
struct mid_q_entry *mid, *nmid;
struct list_head retry_list;
+ struct TCP_Server_Info *pserver;
server->maxBuf = 0;
server->max_read = 0;
@@ -184,8 +185,12 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
* are not used until reconnected.
*/
cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n", __func__);
+
+ /* If server is a channel, select the primary channel */
+ pserver = CIFS_SERVER_IS_CHAN(server) ? server->primary_server : server;
+
spin_lock(&cifs_tcp_ses_lock);
- list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+ list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
ses->need_reconnect = true;
list_for_each_entry(tcon, &ses->tcon_list, tcon_list)
tcon->need_reconnect = true;
@@ -1338,7 +1343,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;
@@ -1369,6 +1374,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);
cancel_delayed_work_sync(&server->resolve);
@@ -1401,7 +1410,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;
@@ -1438,6 +1448,10 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
tcp_ses->in_flight = 0;
tcp_ses->max_in_flight = 0;
tcp_ses->credits = 1;
+ if (primary_server) {
+ ++primary_server->srv_count;
+ tcp_ses->primary_server = primary_server;
+ }
init_waitqueue_head(&tcp_ses->response_q);
init_waitqueue_head(&tcp_ses->request_q);
INIT_LIST_HEAD(&tcp_ses->pending_mid_q);
@@ -1559,6 +1573,8 @@ out_err_crypto_release:
out_err:
if (tcp_ses) {
+ if (CIFS_SERVER_IS_CHAN(tcp_ses))
+ cifs_put_tcp_session(tcp_ses->primary_server, false);
kfree(tcp_ses->hostname);
if (tcp_ses->ssocket)
sock_release(tcp_ses->ssocket);
@@ -2960,7 +2976,7 @@ static int mount_get_conns(struct mount_ctx *mnt_ctx)
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);
server = NULL;
diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c
index 27660bffb918..2c10b186ed6e 100644
--- a/fs/cifs/sess.c
+++ b/fs/cifs/sess.c
@@ -258,7 +258,7 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
SMB2_CLIENT_GUID_SIZE);
ctx.use_client_guid = true;
- chan_server = cifs_get_tcp_session(&ctx);
+ chan_server = cifs_get_tcp_session(&ctx, ses->server);
mutex_lock(&ses->session_mutex);
spin_lock(&ses->chan_lock);
@@ -272,10 +272,6 @@ cifs_ses_add_channel(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
}
spin_unlock(&ses->chan_lock);
- spin_lock(&cifs_tcp_ses_lock);
- chan->server->is_channel = true;
- spin_unlock(&cifs_tcp_ses_lock);
-
/*
* We need to allocate the server crypto now as we will need
* to sign packets before we generate the channel signing key
diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
index 5e032b2b2adb..2f5f2c4c6183 100644
--- a/fs/cifs/smb2pdu.c
+++ b/fs/cifs/smb2pdu.c
@@ -257,7 +257,7 @@ 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);
}