diff options
author | Jeff Layton <jlayton@redhat.com> | 2008-11-14 13:44:38 -0500 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2008-11-14 18:42:32 -0500 |
commit | e7ddee9037e7dd43de1ad08b51727e552aedd836 (patch) | |
tree | 1f4fa723aad80809c5980fcb197aba90a84c26ca /fs/cifs/connect.c | |
parent | 3ec332ef7a38c2327e18d087d4120a8e3bd3dc6e (diff) |
cifs: disable sharing session and tcon and add new TCP sharing code
The code that allows these structs to be shared is extremely racy.
Disable the sharing of SMB and tcon structs for now until we can
come up with a way to do this that's race free.
We want to continue to share TCP sessions, however since they are
required for multiuser mounts. For that, implement a new (hopefully
race-free) scheme. Add a new global list of TCP sessions, and take
care to get a reference to it whenever we're dealing with one.
Signed-off-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Steve French <sfrench@us.ibm.com>
Diffstat (limited to 'fs/cifs/connect.c')
-rw-r--r-- | fs/cifs/connect.c | 205 |
1 files changed, 72 insertions, 133 deletions
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 30ab8dc68e17..a0314259f94d 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c | |||
@@ -659,6 +659,11 @@ multi_t2_fnd: | |||
659 | } | 659 | } |
660 | } /* end while !EXITING */ | 660 | } /* end while !EXITING */ |
661 | 661 | ||
662 | /* take it off the list, if it's not already */ | ||
663 | write_lock(&cifs_tcp_ses_lock); | ||
664 | list_del_init(&server->tcp_ses_list); | ||
665 | write_unlock(&cifs_tcp_ses_lock); | ||
666 | |||
662 | spin_lock(&GlobalMid_Lock); | 667 | spin_lock(&GlobalMid_Lock); |
663 | server->tcpStatus = CifsExiting; | 668 | server->tcpStatus = CifsExiting; |
664 | spin_unlock(&GlobalMid_Lock); | 669 | spin_unlock(&GlobalMid_Lock); |
@@ -1357,92 +1362,66 @@ cifs_parse_mount_options(char *options, const char *devname, | |||
1357 | return 0; | 1362 | return 0; |
1358 | } | 1363 | } |
1359 | 1364 | ||
1360 | static struct cifsSesInfo * | 1365 | static struct TCP_Server_Info * |
1361 | cifs_find_tcp_session(struct in_addr *target_ip_addr, | 1366 | cifs_find_tcp_session(struct sockaddr *addr) |
1362 | struct in6_addr *target_ip6_addr, | ||
1363 | char *userName, struct TCP_Server_Info **psrvTcp) | ||
1364 | { | 1367 | { |
1365 | struct list_head *tmp; | 1368 | struct list_head *tmp; |
1366 | struct cifsSesInfo *ses; | 1369 | struct TCP_Server_Info *server; |
1367 | 1370 | struct sockaddr_in *addr4 = (struct sockaddr_in *) addr; | |
1368 | *psrvTcp = NULL; | 1371 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr; |
1369 | 1372 | ||
1370 | read_lock(&GlobalSMBSeslock); | 1373 | write_lock(&cifs_tcp_ses_lock); |
1371 | list_for_each(tmp, &GlobalSMBSessionList) { | 1374 | list_for_each(tmp, &cifs_tcp_ses_list) { |
1372 | ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); | 1375 | server = list_entry(tmp, struct TCP_Server_Info, |
1373 | if (!ses->server) | 1376 | tcp_ses_list); |
1377 | |||
1378 | /* | ||
1379 | * the demux thread can exit on its own while still in CifsNew | ||
1380 | * so don't accept any sockets in that state. Since the | ||
1381 | * tcpStatus never changes back to CifsNew it's safe to check | ||
1382 | * for this without a lock. | ||
1383 | */ | ||
1384 | if (server->tcpStatus == CifsNew) | ||
1374 | continue; | 1385 | continue; |
1375 | 1386 | ||
1376 | if (target_ip_addr && | 1387 | if (addr->sa_family == AF_INET && |
1377 | ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr) | 1388 | (addr4->sin_addr.s_addr != |
1378 | continue; | 1389 | server->addr.sockAddr.sin_addr.s_addr)) |
1379 | else if (target_ip6_addr && | 1390 | continue; |
1380 | memcmp(&ses->server->addr.sockAddr6.sin6_addr, | 1391 | else if (addr->sa_family == AF_INET6 && |
1381 | target_ip6_addr, sizeof(*target_ip6_addr))) | 1392 | memcmp(&server->addr.sockAddr6.sin6_addr, |
1382 | continue; | 1393 | &addr6->sin6_addr, sizeof(addr6->sin6_addr))) |
1383 | /* BB lock server and tcp session; increment use count here?? */ | 1394 | continue; |
1384 | |||
1385 | /* found a match on the TCP session */ | ||
1386 | *psrvTcp = ses->server; | ||
1387 | 1395 | ||
1388 | /* BB check if reconnection needed */ | 1396 | ++server->srv_count; |
1389 | if (strncmp(ses->userName, userName, MAX_USERNAME_SIZE) == 0) { | 1397 | write_unlock(&cifs_tcp_ses_lock); |
1390 | read_unlock(&GlobalSMBSeslock); | 1398 | return server; |
1391 | /* Found exact match on both TCP and | ||
1392 | SMB sessions */ | ||
1393 | return ses; | ||
1394 | } | ||
1395 | /* else tcp and smb sessions need reconnection */ | ||
1396 | } | 1399 | } |
1397 | read_unlock(&GlobalSMBSeslock); | 1400 | write_unlock(&cifs_tcp_ses_lock); |
1398 | |||
1399 | return NULL; | 1401 | return NULL; |
1400 | } | 1402 | } |
1401 | 1403 | ||
1402 | static struct cifsTconInfo * | 1404 | void |
1403 | find_unc(__be32 new_target_ip_addr, char *uncName, char *userName) | 1405 | cifs_put_tcp_session(struct TCP_Server_Info *server) |
1404 | { | 1406 | { |
1405 | struct list_head *tmp; | 1407 | struct task_struct *task; |
1406 | struct cifsTconInfo *tcon; | ||
1407 | __be32 old_ip; | ||
1408 | |||
1409 | read_lock(&GlobalSMBSeslock); | ||
1410 | |||
1411 | list_for_each(tmp, &GlobalTreeConnectionList) { | ||
1412 | cFYI(1, ("Next tcon")); | ||
1413 | tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); | ||
1414 | if (!tcon->ses || !tcon->ses->server) | ||
1415 | continue; | ||
1416 | |||
1417 | old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr; | ||
1418 | cFYI(1, ("old ip addr: %x == new ip %x ?", | ||
1419 | old_ip, new_target_ip_addr)); | ||
1420 | |||
1421 | if (old_ip != new_target_ip_addr) | ||
1422 | continue; | ||
1423 | |||
1424 | /* BB lock tcon, server, tcp session and increment use count? */ | ||
1425 | /* found a match on the TCP session */ | ||
1426 | /* BB check if reconnection needed */ | ||
1427 | cFYI(1, ("IP match, old UNC: %s new: %s", | ||
1428 | tcon->treeName, uncName)); | ||
1429 | 1408 | ||
1430 | if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE)) | 1409 | write_lock(&cifs_tcp_ses_lock); |
1431 | continue; | 1410 | if (--server->srv_count > 0) { |
1411 | write_unlock(&cifs_tcp_ses_lock); | ||
1412 | return; | ||
1413 | } | ||
1432 | 1414 | ||
1433 | cFYI(1, ("and old usr: %s new: %s", | 1415 | list_del_init(&server->tcp_ses_list); |
1434 | tcon->treeName, uncName)); | 1416 | write_unlock(&cifs_tcp_ses_lock); |
1435 | 1417 | ||
1436 | if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE)) | 1418 | spin_lock(&GlobalMid_Lock); |
1437 | continue; | 1419 | server->tcpStatus = CifsExiting; |
1438 | 1420 | spin_unlock(&GlobalMid_Lock); | |
1439 | /* matched smb session (user name) */ | ||
1440 | read_unlock(&GlobalSMBSeslock); | ||
1441 | return tcon; | ||
1442 | } | ||
1443 | 1421 | ||
1444 | read_unlock(&GlobalSMBSeslock); | 1422 | task = xchg(&server->tsk, NULL); |
1445 | return NULL; | 1423 | if (task) |
1424 | force_sig(SIGKILL, task); | ||
1446 | } | 1425 | } |
1447 | 1426 | ||
1448 | int | 1427 | int |
@@ -1881,16 +1860,6 @@ convert_delimiter(char *path, char delim) | |||
1881 | } | 1860 | } |
1882 | } | 1861 | } |
1883 | 1862 | ||
1884 | static void | ||
1885 | kill_cifsd(struct TCP_Server_Info *server) | ||
1886 | { | ||
1887 | struct task_struct *task; | ||
1888 | |||
1889 | task = xchg(&server->tsk, NULL); | ||
1890 | if (task) | ||
1891 | force_sig(SIGKILL, task); | ||
1892 | } | ||
1893 | |||
1894 | static void setup_cifs_sb(struct smb_vol *pvolume_info, | 1863 | static void setup_cifs_sb(struct smb_vol *pvolume_info, |
1895 | struct cifs_sb_info *cifs_sb) | 1864 | struct cifs_sb_info *cifs_sb) |
1896 | { | 1865 | { |
@@ -2069,21 +2038,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2069 | } | 2038 | } |
2070 | } | 2039 | } |
2071 | 2040 | ||
2072 | if (addr.sa_family == AF_INET) | 2041 | srvTcp = cifs_find_tcp_session(&addr); |
2073 | existingCifsSes = cifs_find_tcp_session(&sin_server->sin_addr, | 2042 | if (srvTcp) { |
2074 | NULL /* no ipv6 addr */, | 2043 | cFYI(1, ("Existing tcp session with server found")); |
2075 | volume_info.username, &srvTcp); | 2044 | } else { /* create socket */ |
2076 | else if (addr.sa_family == AF_INET6) { | ||
2077 | cFYI(1, ("looking for ipv6 address")); | ||
2078 | existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */, | ||
2079 | &sin_server6->sin6_addr, | ||
2080 | volume_info.username, &srvTcp); | ||
2081 | } else { | ||
2082 | rc = -EINVAL; | ||
2083 | goto out; | ||
2084 | } | ||
2085 | |||
2086 | if (!srvTcp) { | ||
2087 | if (addr.sa_family == AF_INET6) { | 2045 | if (addr.sa_family == AF_INET6) { |
2088 | cFYI(1, ("attempting ipv6 connect")); | 2046 | cFYI(1, ("attempting ipv6 connect")); |
2089 | /* BB should we allow ipv6 on port 139? */ | 2047 | /* BB should we allow ipv6 on port 139? */ |
@@ -2153,6 +2111,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2153 | memcpy(srvTcp->server_RFC1001_name, | 2111 | memcpy(srvTcp->server_RFC1001_name, |
2154 | volume_info.target_rfc1001_name, 16); | 2112 | volume_info.target_rfc1001_name, 16); |
2155 | srvTcp->sequence_number = 0; | 2113 | srvTcp->sequence_number = 0; |
2114 | INIT_LIST_HEAD(&srvTcp->tcp_ses_list); | ||
2115 | ++srvTcp->srv_count; | ||
2116 | write_lock(&cifs_tcp_ses_lock); | ||
2117 | list_add(&srvTcp->tcp_ses_list, | ||
2118 | &cifs_tcp_ses_list); | ||
2119 | write_unlock(&cifs_tcp_ses_lock); | ||
2156 | } | 2120 | } |
2157 | } | 2121 | } |
2158 | 2122 | ||
@@ -2204,8 +2168,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2204 | rc = cifs_setup_session(xid, pSesInfo, | 2168 | rc = cifs_setup_session(xid, pSesInfo, |
2205 | cifs_sb->local_nls); | 2169 | cifs_sb->local_nls); |
2206 | up(&pSesInfo->sesSem); | 2170 | up(&pSesInfo->sesSem); |
2207 | if (!rc) | ||
2208 | atomic_inc(&srvTcp->socketUseCount); | ||
2209 | } | 2171 | } |
2210 | } | 2172 | } |
2211 | 2173 | ||
@@ -2213,9 +2175,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2213 | if (!rc) { | 2175 | if (!rc) { |
2214 | setup_cifs_sb(&volume_info, cifs_sb); | 2176 | setup_cifs_sb(&volume_info, cifs_sb); |
2215 | 2177 | ||
2216 | tcon = | ||
2217 | find_unc(sin_server->sin_addr.s_addr, volume_info.UNC, | ||
2218 | volume_info.username); | ||
2219 | if (tcon) { | 2178 | if (tcon) { |
2220 | cFYI(1, ("Found match on UNC path")); | 2179 | cFYI(1, ("Found match on UNC path")); |
2221 | if (tcon->seal != volume_info.seal) | 2180 | if (tcon->seal != volume_info.seal) |
@@ -2278,35 +2237,21 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb, | |||
2278 | /* on error free sesinfo and tcon struct if needed */ | 2237 | /* on error free sesinfo and tcon struct if needed */ |
2279 | mount_fail_check: | 2238 | mount_fail_check: |
2280 | if (rc) { | 2239 | if (rc) { |
2281 | /* if session setup failed, use count is zero but | 2240 | /* If find_unc succeeded then rc == 0 so we can not end */ |
2282 | we still need to free cifsd thread */ | 2241 | /* up accidently freeing someone elses tcon struct */ |
2283 | if (atomic_read(&srvTcp->socketUseCount) == 0) { | 2242 | if (tcon) |
2284 | spin_lock(&GlobalMid_Lock); | ||
2285 | srvTcp->tcpStatus = CifsExiting; | ||
2286 | spin_unlock(&GlobalMid_Lock); | ||
2287 | kill_cifsd(srvTcp); | ||
2288 | } | ||
2289 | /* If find_unc succeeded then rc == 0 so we can not end */ | ||
2290 | if (tcon) /* up accidently freeing someone elses tcon struct */ | ||
2291 | tconInfoFree(tcon); | 2243 | tconInfoFree(tcon); |
2244 | |||
2292 | if (existingCifsSes == NULL) { | 2245 | if (existingCifsSes == NULL) { |
2293 | if (pSesInfo) { | 2246 | if (pSesInfo) { |
2294 | if ((pSesInfo->server) && | 2247 | if ((pSesInfo->server) && |
2295 | (pSesInfo->status == CifsGood)) { | 2248 | (pSesInfo->status == CifsGood)) |
2296 | int temp_rc; | 2249 | CIFSSMBLogoff(xid, pSesInfo); |
2297 | temp_rc = CIFSSMBLogoff(xid, pSesInfo); | 2250 | else { |
2298 | /* if the socketUseCount is now zero */ | ||
2299 | if ((temp_rc == -ESHUTDOWN) && | ||
2300 | (pSesInfo->server)) | ||
2301 | kill_cifsd(pSesInfo->server); | ||
2302 | } else { | ||
2303 | cFYI(1, ("No session or bad tcon")); | 2251 | cFYI(1, ("No session or bad tcon")); |
2304 | if (pSesInfo->server) { | 2252 | if (pSesInfo->server) |
2305 | spin_lock(&GlobalMid_Lock); | 2253 | cifs_put_tcp_session( |
2306 | srvTcp->tcpStatus = CifsExiting; | 2254 | pSesInfo->server); |
2307 | spin_unlock(&GlobalMid_Lock); | ||
2308 | kill_cifsd(pSesInfo->server); | ||
2309 | } | ||
2310 | } | 2255 | } |
2311 | sesInfoFree(pSesInfo); | 2256 | sesInfoFree(pSesInfo); |
2312 | /* pSesInfo = NULL; */ | 2257 | /* pSesInfo = NULL; */ |
@@ -3613,13 +3558,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | |||
3613 | if (rc == -EBUSY) { | 3558 | if (rc == -EBUSY) { |
3614 | FreeXid(xid); | 3559 | FreeXid(xid); |
3615 | return 0; | 3560 | return 0; |
3616 | } else if (rc == -ESHUTDOWN) { | 3561 | } |
3617 | cFYI(1, ("Waking up socket by sending signal")); | ||
3618 | if (ses->server) | ||
3619 | kill_cifsd(ses->server); | ||
3620 | rc = 0; | ||
3621 | } /* else - we have an smb session | ||
3622 | left on this socket do not kill cifsd */ | ||
3623 | } else | 3562 | } else |
3624 | cFYI(1, ("No session or bad tcon")); | 3563 | cFYI(1, ("No session or bad tcon")); |
3625 | } | 3564 | } |