Skip to content

Commit 266b5d0

Browse files
Wang Zhaolongsmfrench
authored andcommitted
smb: client: fix race condition in negotiate timeout by using more precise timing
When the SMB server reboots and the client immediately accesses the mount point, a race condition can occur that causes operations to fail with "Host is down" error. Reproduction steps: # Mount SMB share mount -t cifs //192.168.245.109/TEST /mnt/ -o xxxx ls /mnt # Reboot server ssh [email protected] reboot ssh [email protected] /path/to/cifs_server_setup.sh ssh [email protected] systemctl stop firewalld # Immediate access fails ls /mnt ls: cannot access '/mnt': Host is down # But works if there is a delay The issue is caused by a race condition between negotiate and reconnect. The 20-second negotiate timeout mechanism can interfere with the normal recovery process when both are triggered simultaneously. ls cifsd --------------------------------------------------- cifs_getattr cifs_revalidate_dentry cifs_get_inode_info cifs_get_fattr smb2_query_path_info smb2_compound_op SMB2_open_init smb2_reconnect cifs_negotiate_protocol smb2_negotiate cifs_send_recv smb_send_rqst wait_for_response cifs_demultiplex_thread cifs_read_from_socket cifs_readv_from_socket server_unresponsive cifs_reconnect __cifs_reconnect cifs_abort_connection mid->mid_state = MID_RETRY_NEEDED cifs_wake_up_task cifs_sync_mid_result // case MID_RETRY_NEEDED rc = -EAGAIN; // In smb2_negotiate() rc = -EHOSTDOWN; The server_unresponsive() timeout triggers cifs_reconnect(), which aborts ongoing mid requests and causes the ls command to receive -EAGAIN, leading to -EHOSTDOWN. Fix this by introducing a dedicated `neg_start` field to precisely tracks when the negotiate process begins. The timeout check now uses this accurate timestamp instead of `lstrp`, ensuring that: 1. Timeout is only triggered after negotiate has actually run for 20s 2. The mechanism doesn't interfere with concurrent recovery processes 3. Uninitialized timestamps (value 0) don't trigger false timeouts Fixes: 7ccc146 ("smb: client: fix hang in wait_for_response() for negproto") Signed-off-by: Wang Zhaolong <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 74ebd02 commit 266b5d0

File tree

2 files changed

+5
-3
lines changed

2 files changed

+5
-3
lines changed

fs/smb/client/cifsglob.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,7 @@ struct TCP_Server_Info {
777777
__le32 session_key_id; /* retrieved from negotiate response and send in session setup request */
778778
struct session_key session_key;
779779
unsigned long lstrp; /* when we got last response from this server */
780+
unsigned long neg_start; /* when negotiate started (jiffies) */
780781
struct cifs_secmech secmech; /* crypto sec mech functs, descriptors */
781782
#define CIFS_NEGFLAVOR_UNENCAP 1 /* wct == 17, but no ext_sec */
782783
#define CIFS_NEGFLAVOR_EXTENDED 2 /* wct == 17, ext_sec bit set */

fs/smb/client/connect.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -679,12 +679,12 @@ server_unresponsive(struct TCP_Server_Info *server)
679679
/*
680680
* If we're in the process of mounting a share or reconnecting a session
681681
* and the server abruptly shut down (e.g. socket wasn't closed, packet
682-
* had been ACK'ed but no SMB response), don't wait longer than 20s to
683-
* negotiate protocol.
682+
* had been ACK'ed but no SMB response), don't wait longer than 20s from
683+
* when negotiate actually started.
684684
*/
685685
spin_lock(&server->srv_lock);
686686
if (server->tcpStatus == CifsInNegotiate &&
687-
time_after(jiffies, server->lstrp + 20 * HZ)) {
687+
time_after(jiffies, server->neg_start + 20 * HZ)) {
688688
spin_unlock(&server->srv_lock);
689689
cifs_reconnect(server, false);
690690
return true;
@@ -4209,6 +4209,7 @@ cifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
42094209

42104210
server->lstrp = jiffies;
42114211
server->tcpStatus = CifsInNegotiate;
4212+
server->neg_start = jiffies;
42124213
spin_unlock(&server->srv_lock);
42134214

42144215
rc = server->ops->negotiate(xid, ses, server);

0 commit comments

Comments
 (0)