]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
cifs: Fix negotiate retry functionality
authorPali Rohár <pali@kernel.org>
Sat, 2 Nov 2024 19:06:50 +0000 (20:06 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Jun 2025 12:40:02 +0000 (14:40 +0200)
[ Upstream commit e94e882a6d69525c07589222cf3a6ff57ad12b5b ]

SMB negotiate retry functionality in cifs_negotiate() is currently broken
and does not work when doing socket reconnect. Caller of this function,
which is cifs_negotiate_protocol() requires that tcpStatus after successful
execution of negotiate callback stay in CifsInNegotiate. But if the
CIFSSMBNegotiate() called from cifs_negotiate() fails due to connection
issues then tcpStatus is changed as so repeated CIFSSMBNegotiate() call
does not help.

Fix this problem by moving retrying code from negotiate callback (which is
either cifs_negotiate() or smb2_negotiate()) to cifs_negotiate_protocol()
which is caller of those callbacks. This allows to properly handle and
implement correct transistions between tcpStatus states as function
cifs_negotiate_protocol() already handles it.

With this change, cifs_negotiate_protocol() now handles also -EAGAIN error
set by the RFC1002_NEGATIVE_SESSION_RESPONSE processing after reconnecting
with NetBIOS session.

Signed-off-by: Pali Rohár <pali@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
fs/smb/client/connect.c
fs/smb/client/smb1ops.c
fs/smb/client/smb2ops.c

index 6aeb25006db82cc8d7b531106df9e9db6b934b2e..0baa64c48c3d14b1acd686f28d9323c15038dd1d 100644 (file)
@@ -4178,11 +4178,13 @@ int
 cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
                        struct TCP_Server_Info *server)
 {
+       bool in_retry = false;
        int rc = 0;
 
        if (!server->ops->need_neg || !server->ops->negotiate)
                return -ENOSYS;
 
+retry:
        /* only send once per connect */
        spin_lock(&server->srv_lock);
        if (server->tcpStatus != CifsGood &&
@@ -4202,6 +4204,14 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
        spin_unlock(&server->srv_lock);
 
        rc = server->ops->negotiate(xid, ses, server);
+       if (rc == -EAGAIN) {
+               /* Allow one retry attempt */
+               if (!in_retry) {
+                       in_retry = true;
+                       goto retry;
+               }
+               rc = -EHOSTDOWN;
+       }
        if (rc == 0) {
                spin_lock(&server->srv_lock);
                if (server->tcpStatus == CifsInNegotiate)
index 225cc7e0304c29e35ef54e22f6b5bafa42afc191..1489b9d21b6096c1507facc40457784535f3c3c6 100644 (file)
@@ -426,13 +426,6 @@ cifs_negotiate(const unsigned int xid,
 {
        int rc;
        rc = CIFSSMBNegotiate(xid, ses, server);
-       if (rc == -EAGAIN) {
-               /* retry only once on 1st time connection */
-               set_credits(server, 1);
-               rc = CIFSSMBNegotiate(xid, ses, server);
-               if (rc == -EAGAIN)
-                       rc = -EHOSTDOWN;
-       }
        return rc;
 }
 
index a62f3e5a7689c5b8e47f2d6910b2e50312c5ab9e..c9b9892b510eacb719ec823c642392c757744e57 100644 (file)
@@ -422,9 +422,6 @@ smb2_negotiate(const unsigned int xid,
        server->CurrentMid = 0;
        spin_unlock(&server->mid_lock);
        rc = SMB2_negotiate(xid, ses, server);
-       /* BB we probably don't need to retry with modern servers */
-       if (rc == -EAGAIN)
-               rc = -EHOSTDOWN;
        return rc;
 }