Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

cifs: prevent data race in cifs_reconnect_tcon()

Make sure to get an up-to-date TCP_Server_Info::nr_targets value prior
to waiting the server to be reconnected in cifs_reconnect_tcon(). It
is set in cifs_tcp_ses_needs_reconnect() and protected by
TCP_Server_Info::srv_lock.

Create a new cifs_wait_for_server_reconnect() helper that can be used
by both SMB2+ and CIFS reconnect code.

Signed-off-by: Paulo Alcantara (SUSE) <pc@manguebit.com>
Signed-off-by: Steve French <stfrench@microsoft.com>

authored by

Paulo Alcantara and committed by
Steve French
1bcd548d b9ee2e30

+69 -101
+1
fs/cifs/cifsproto.h
··· 694 694 695 695 struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); 696 696 void cifs_put_tcon_super(struct super_block *sb); 697 + int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry); 697 698 698 699 #endif /* _CIFSPROTO_H */
+3 -40
fs/cifs/cifssmb.c
··· 72 72 struct cifs_ses *ses; 73 73 struct TCP_Server_Info *server; 74 74 struct nls_table *nls_codepage; 75 - int retries; 76 75 77 76 /* 78 77 * SMBs NegProt, SessSetup, uLogoff do not have tcon yet so check for ··· 101 102 } 102 103 spin_unlock(&tcon->tc_lock); 103 104 104 - retries = server->nr_targets; 105 - 106 - /* 107 - * Give demultiplex thread up to 10 seconds to each target available for 108 - * reconnect -- should be greater than cifs socket timeout which is 7 109 - * seconds. 110 - */ 111 - while (server->tcpStatus == CifsNeedReconnect) { 112 - rc = wait_event_interruptible_timeout(server->response_q, 113 - (server->tcpStatus != CifsNeedReconnect), 114 - 10 * HZ); 115 - if (rc < 0) { 116 - cifs_dbg(FYI, "%s: aborting reconnect due to a received signal by the process\n", 117 - __func__); 118 - return -ERESTARTSYS; 119 - } 120 - 121 - /* are we still trying to reconnect? */ 122 - spin_lock(&server->srv_lock); 123 - if (server->tcpStatus != CifsNeedReconnect) { 124 - spin_unlock(&server->srv_lock); 125 - break; 126 - } 127 - spin_unlock(&server->srv_lock); 128 - 129 - if (retries && --retries) 130 - continue; 131 - 132 - /* 133 - * on "soft" mounts we wait once. Hard mounts keep 134 - * retrying until process is killed or server comes 135 - * back on-line 136 - */ 137 - if (!tcon->retry) { 138 - cifs_dbg(FYI, "gave up waiting on reconnect in smb_init\n"); 139 - return -EHOSTDOWN; 140 - } 141 - retries = server->nr_targets; 142 - } 105 + rc = cifs_wait_for_server_reconnect(server, tcon->retry); 106 + if (rc) 107 + return rc; 143 108 144 109 spin_lock(&ses->chan_lock); 145 110 if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
+44
fs/cifs/misc.c
··· 1266 1266 return 0; 1267 1267 } 1268 1268 #endif 1269 + 1270 + int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry) 1271 + { 1272 + int timeout = 10; 1273 + int rc; 1274 + 1275 + spin_lock(&server->srv_lock); 1276 + if (server->tcpStatus != CifsNeedReconnect) { 1277 + spin_unlock(&server->srv_lock); 1278 + return 0; 1279 + } 1280 + timeout *= server->nr_targets; 1281 + spin_unlock(&server->srv_lock); 1282 + 1283 + /* 1284 + * Give demultiplex thread up to 10 seconds to each target available for 1285 + * reconnect -- should be greater than cifs socket timeout which is 7 1286 + * seconds. 1287 + * 1288 + * On "soft" mounts we wait once. Hard mounts keep retrying until 1289 + * process is killed or server comes back on-line. 1290 + */ 1291 + do { 1292 + rc = wait_event_interruptible_timeout(server->response_q, 1293 + (server->tcpStatus != CifsNeedReconnect), 1294 + timeout * HZ); 1295 + if (rc < 0) { 1296 + cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n", 1297 + __func__); 1298 + return -ERESTARTSYS; 1299 + } 1300 + 1301 + /* are we still trying to reconnect? */ 1302 + spin_lock(&server->srv_lock); 1303 + if (server->tcpStatus != CifsNeedReconnect) { 1304 + spin_unlock(&server->srv_lock); 1305 + return 0; 1306 + } 1307 + spin_unlock(&server->srv_lock); 1308 + } while (retry); 1309 + 1310 + cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__); 1311 + return -EHOSTDOWN; 1312 + }
+21 -61
fs/cifs/smb2pdu.c
··· 139 139 return; 140 140 } 141 141 142 - static int wait_for_server_reconnect(struct TCP_Server_Info *server, 143 - __le16 smb2_command, bool retry) 144 - { 145 - int timeout = 10; 146 - int rc; 147 - 148 - spin_lock(&server->srv_lock); 149 - if (server->tcpStatus != CifsNeedReconnect) { 150 - spin_unlock(&server->srv_lock); 151 - return 0; 152 - } 153 - timeout *= server->nr_targets; 154 - spin_unlock(&server->srv_lock); 155 - 156 - /* 157 - * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE 158 - * here since they are implicitly done when session drops. 159 - */ 160 - switch (smb2_command) { 161 - /* 162 - * BB Should we keep oplock break and add flush to exceptions? 163 - */ 164 - case SMB2_TREE_DISCONNECT: 165 - case SMB2_CANCEL: 166 - case SMB2_CLOSE: 167 - case SMB2_OPLOCK_BREAK: 168 - return -EAGAIN; 169 - } 170 - 171 - /* 172 - * Give demultiplex thread up to 10 seconds to each target available for 173 - * reconnect -- should be greater than cifs socket timeout which is 7 174 - * seconds. 175 - * 176 - * On "soft" mounts we wait once. Hard mounts keep retrying until 177 - * process is killed or server comes back on-line. 178 - */ 179 - do { 180 - rc = wait_event_interruptible_timeout(server->response_q, 181 - (server->tcpStatus != CifsNeedReconnect), 182 - timeout * HZ); 183 - if (rc < 0) { 184 - cifs_dbg(FYI, "%s: aborting reconnect due to received signal\n", 185 - __func__); 186 - return -ERESTARTSYS; 187 - } 188 - 189 - /* are we still trying to reconnect? */ 190 - spin_lock(&server->srv_lock); 191 - if (server->tcpStatus != CifsNeedReconnect) { 192 - spin_unlock(&server->srv_lock); 193 - return 0; 194 - } 195 - spin_unlock(&server->srv_lock); 196 - } while (retry); 197 - 198 - cifs_dbg(FYI, "%s: gave up waiting on reconnect\n", __func__); 199 - return -EHOSTDOWN; 200 - } 201 - 202 142 static int 203 143 smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, 204 144 struct TCP_Server_Info *server) ··· 183 243 (!tcon->ses->server) || !server) 184 244 return -EIO; 185 245 186 - rc = wait_for_server_reconnect(server, smb2_command, tcon->retry); 246 + spin_lock(&server->srv_lock); 247 + if (server->tcpStatus == CifsNeedReconnect) { 248 + /* 249 + * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE 250 + * here since they are implicitly done when session drops. 251 + */ 252 + switch (smb2_command) { 253 + /* 254 + * BB Should we keep oplock break and add flush to exceptions? 255 + */ 256 + case SMB2_TREE_DISCONNECT: 257 + case SMB2_CANCEL: 258 + case SMB2_CLOSE: 259 + case SMB2_OPLOCK_BREAK: 260 + spin_unlock(&server->srv_lock); 261 + return -EAGAIN; 262 + } 263 + } 264 + spin_unlock(&server->srv_lock); 265 + 266 + rc = cifs_wait_for_server_reconnect(server, tcon->retry); 187 267 if (rc) 188 268 return rc; 189 269