aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShyam Prasad N <sprasad@microsoft.com>2023-12-06 16:37:38 +0000
committerSteve French <stfrench@microsoft.com>2023-12-06 11:04:23 -0600
commit04909192ada3285070f8ced0af7f07735478b364 (patch)
tree51a35d576634ce1f7867ae76a1288fb606c6db73
parent823342524868168bf681f135d01b4ae10f5863ec (diff)
downloadstaging-04909192ada3285070f8ced0af7f07735478b364.tar.gz
cifs: reconnect worker should take reference on server struct unconditionally
Reconnect worker currently assumes that the server struct is alive and only takes reference on the server if it needs to call smb2_reconnect. With the new ability to disable channels based on whether the server has multichannel disabled, this becomes a problem when we need to disable established channels. While disabling the channels and deallocating the server, there could be reconnect work that could not be cancelled (because it started). This change forces the reconnect worker to unconditionally take a reference on the server when it runs. Also, this change now allows smb2_reconnect to know if it was called by the reconnect worker. Based on this, the cifs_put_tcp_session can decide whether it can cancel the reconnect work synchronously or not. Signed-off-by: Shyam Prasad N <sprasad@microsoft.com> Signed-off-by: Steve French <stfrench@microsoft.com>
-rw-r--r--fs/smb/client/connect.c8
-rw-r--r--fs/smb/client/smb2pdu.c29
2 files changed, 19 insertions, 18 deletions
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
index e07975173cf42..9dc6dc2754c2e 100644
--- a/fs/smb/client/connect.c
+++ b/fs/smb/client/connect.c
@@ -1608,10 +1608,6 @@ 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 (SERVER_IS_CHAN(server))
- cifs_put_tcp_session(server->primary_server, from_reconnect);
-
cancel_delayed_work_sync(&server->echo);
if (from_reconnect)
@@ -1625,6 +1621,10 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
else
cancel_delayed_work_sync(&server->reconnect);
+ /* For secondary channels, we pick up ref-count on the primary server */
+ if (SERVER_IS_CHAN(server))
+ cifs_put_tcp_session(server->primary_server, from_reconnect);
+
spin_lock(&server->srv_lock);
server->tcpStatus = CifsExiting;
spin_unlock(&server->srv_lock);
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index 9d7289f8d87b6..20634fc6d4f09 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -158,7 +158,7 @@ out:
static int
smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
- struct TCP_Server_Info *server)
+ struct TCP_Server_Info *server, bool from_reconnect)
{
int rc = 0;
struct nls_table *nls_codepage = NULL;
@@ -331,7 +331,7 @@ again:
* as cifs_put_tcp_session takes a higher lock
* i.e. cifs_tcp_ses_lock
*/
- cifs_put_tcp_session(server, 1);
+ cifs_put_tcp_session(server, from_reconnect);
server->terminate = true;
cifs_signal_cifsd_for_reconnect(server, false);
@@ -499,7 +499,7 @@ static int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
{
int rc;
- rc = smb2_reconnect(smb2_command, tcon, server);
+ rc = smb2_reconnect(smb2_command, tcon, server, false);
if (rc)
return rc;
@@ -3895,6 +3895,15 @@ void smb2_reconnect_server(struct work_struct *work)
int rc;
bool resched = false;
+ /* first check if ref count has reached 0, if not inc ref count */
+ spin_lock(&cifs_tcp_ses_lock);
+ if (!server->srv_count) {
+ spin_unlock(&cifs_tcp_ses_lock);
+ return;
+ }
+ server->srv_count++;
+ spin_unlock(&cifs_tcp_ses_lock);
+
/* If server is a channel, select the primary channel */
pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
@@ -3952,17 +3961,10 @@ void smb2_reconnect_server(struct work_struct *work)
}
spin_unlock(&ses->chan_lock);
}
- /*
- * Get the reference to server struct to be sure that the last call of
- * cifs_put_tcon() in the loop below won't release the server pointer.
- */
- if (tcon_exist || ses_exist)
- server->srv_count++;
-
spin_unlock(&cifs_tcp_ses_lock);
list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
- rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server);
+ rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true);
if (!rc)
cifs_reopen_persistent_handles(tcon);
else
@@ -3995,7 +3997,7 @@ void smb2_reconnect_server(struct work_struct *work)
/* now reconnect sessions for necessary channels */
list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) {
tcon->ses = ses;
- rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server);
+ rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true);
if (rc)
resched = true;
list_del_init(&ses->rlist);
@@ -4010,8 +4012,7 @@ done:
mutex_unlock(&pserver->reconnect_mutex);
/* now we can safely release srv struct */
- if (tcon_exist || ses_exist)
- cifs_put_tcp_session(server, 1);
+ cifs_put_tcp_session(server, true);
}
int