]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
cifs: disable sharing session and tcon and add new TCP sharing code
authorJeff Layton <jlayton@redhat.com>
Fri, 21 Nov 2008 08:53:17 +0000 (14:23 +0530)
committerGreg Kroah-Hartman <gregkh@suse.de>
Fri, 5 Dec 2008 18:55:26 +0000 (10:55 -0800)
commit e7ddee9037e7dd43de1ad08b51727e552aedd836 upstream.

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>
Cc: Suresh Jayaraman <sjayaraman@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
fs/cifs/cifs_debug.c
fs/cifs/cifsfs.c
fs/cifs/cifsglob.h
fs/cifs/cifsproto.h
fs/cifs/cifssmb.c
fs/cifs/connect.c

index ba8723d9599679ff86517d1d133856f9a734901d..40b5108fb4f9d463144ff167c59cd5cbef58839c 100644 (file)
@@ -144,7 +144,7 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
                        seq_printf(m, "TCP status: %d\n\tLocal Users To "
                                    "Server: %d SecMode: 0x%x Req On Wire: %d",
                                ses->server->tcpStatus,
-                               atomic_read(&ses->server->socketUseCount),
+                               ses->server->srv_count,
                                ses->server->secMode,
                                atomic_read(&ses->server->inFlight));
 
index f7f28cc986ea03b5450a90f4ecec9d028204c521..48c484660712b6a4e9041a02857d3ee8fad3a1c8 100644 (file)
@@ -1013,7 +1013,7 @@ init_cifs(void)
 {
        int rc = 0;
        cifs_proc_init();
-       INIT_LIST_HEAD(&global_cifs_sock_list);
+       INIT_LIST_HEAD(&cifs_tcp_ses_list);
        INIT_LIST_HEAD(&GlobalSMBSessionList); /* BB to be removed by jl */
        INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */
        INIT_LIST_HEAD(&GlobalOplock_Q);
@@ -1043,6 +1043,7 @@ init_cifs(void)
        GlobalMaxActiveXid = 0;
        memset(Local_System_Name, 0, 15);
        rwlock_init(&GlobalSMBSeslock);
+       rwlock_init(&cifs_tcp_ses_lock);
        spin_lock_init(&GlobalMid_Lock);
 
        if (cifs_max_pending < 2) {
index 781e4d66f9146dc1cda4491ae61c26b993a45282..fa1fb787c2091536c0eb0482887a7f831e6cb107 100644 (file)
@@ -123,6 +123,7 @@ struct cifs_cred {
 struct TCP_Server_Info {
        struct list_head tcp_ses_list;
        struct list_head smb_ses_list;
+       int srv_count; /* reference counter */
        /* 15 character server name + 0x20 16th byte indicating type = srv */
        char server_RFC1001_name[SERVER_NAME_LEN_WITH_NULL];
        char unicode_server_Name[SERVER_NAME_LEN_WITH_NULL * 2];
@@ -144,7 +145,6 @@ struct TCP_Server_Info {
        bool svlocal:1;                 /* local server or remote */
        bool noblocksnd;                /* use blocking sendmsg */
        bool noautotune;                /* do not autotune send buf sizes */
-       atomic_t socketUseCount; /* number of open cifs sessions on socket */
        atomic_t inFlight;  /* number of requests on the wire to server */
 #ifdef CONFIG_CIFS_STATS2
        atomic_t inSend; /* requests trying to send */
@@ -589,13 +589,18 @@ require use of the stronger protocol */
 #define GLOBAL_EXTERN extern
 #endif
 
-
-/* the list of TCP_Server_Info structures, ie each of the sockets
+/*
+ * the list of TCP_Server_Info structures, ie each of the sockets
  * connecting our client to a distinct server (ip address), is
- * chained together by global_cifs_sock_list. The list of all our SMB
+ * chained together by cifs_tcp_ses_list. The list of all our SMB
  * sessions (and from that the tree connections) can be found
- * by iterating over global_cifs_sock_list */
-GLOBAL_EXTERN struct list_head global_cifs_sock_list;
+ * by iterating over cifs_tcp_ses_list
+ */
+GLOBAL_EXTERN struct list_head         cifs_tcp_ses_list;
+
+/* protects cifs_tcp_ses_list and srv_count for each tcp session */
+GLOBAL_EXTERN rwlock_t         cifs_tcp_ses_lock;
+
 GLOBAL_EXTERN struct list_head GlobalSMBSessionList; /* BB to be removed by jl*/
 GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */
 GLOBAL_EXTERN rwlock_t GlobalSMBSeslock;  /* protects list inserts on 3 above */
index ca91f162364d0f0ce3ec4e5df489d4b4c37c3e10..b50f4e5a52f4f46f24da60fe5fd27ed2070cd2a3 100644 (file)
@@ -102,6 +102,7 @@ extern void acl_to_uid_mode(struct inode *inode, const char *path,
                            const __u16 *pfid);
 extern int mode_to_acl(struct inode *inode, const char *path, __u64);
 
+extern void cifs_put_tcp_session(struct TCP_Server_Info *server);
 extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
                        const char *);
 extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
index 47c59145cf30b5889fa64634a995876932b8dc0b..38d42fb458f694491f26bc7c2748a0c636cd09af 100644 (file)
@@ -664,8 +664,9 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                        rc = -EIO;
                        goto neg_err_exit;
                }
-
-               if (server->socketUseCount.counter > 1) {
+               read_lock(&cifs_tcp_ses_lock);
+               if (server->srv_count > 1) {
+                       read_unlock(&cifs_tcp_ses_lock);
                        if (memcmp(server->server_GUID,
                                   pSMBr->u.extended_response.
                                   GUID, 16) != 0) {
@@ -674,9 +675,11 @@ CIFSSMBNegotiate(unsigned int xid, struct cifsSesInfo *ses)
                                        pSMBr->u.extended_response.GUID,
                                        16);
                        }
-               } else
+               } else {
+                       read_unlock(&cifs_tcp_ses_lock);
                        memcpy(server->server_GUID,
                               pSMBr->u.extended_response.GUID, 16);
+               }
 
                if (count == 16) {
                        server->secType = RawNTLMSSP;
@@ -830,12 +833,9 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
        pSMB->AndXCommand = 0xFF;
        rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
 session_already_dead:
-       atomic_dec(&ses->server->socketUseCount);
-       if (atomic_read(&ses->server->socketUseCount) == 0) {
-               spin_lock(&GlobalMid_Lock);
-               ses->server->tcpStatus = CifsExiting;
-               spin_unlock(&GlobalMid_Lock);
-               rc = -ESHUTDOWN;
+       if (ses->server) {
+               cifs_put_tcp_session(ses->server);
+               rc = 0;
        }
        up(&ses->sesSem);
 
index 05efa2725f2146cab3e90ffb7482830f4253c630..c77cded579614289f8afd91c6be59263be5c822d 100644 (file)
@@ -657,6 +657,11 @@ multi_t2_fnd:
                }
        } /* end while !EXITING */
 
+       /* take it off the list, if it's not already */
+       write_lock(&cifs_tcp_ses_lock);
+       list_del_init(&server->tcp_ses_list);
+       write_unlock(&cifs_tcp_ses_lock);
+
        spin_lock(&GlobalMid_Lock);
        server->tcpStatus = CifsExiting;
        spin_unlock(&GlobalMid_Lock);
@@ -1346,92 +1351,66 @@ cifs_parse_mount_options(char *options, const char *devname,
        return 0;
 }
 
-static struct cifsSesInfo *
-cifs_find_tcp_session(struct in_addr *target_ip_addr,
-                     struct in6_addr *target_ip6_addr,
-                     char *userName, struct TCP_Server_Info **psrvTcp)
+static struct TCP_Server_Info *
+cifs_find_tcp_session(struct sockaddr *addr)
 {
        struct list_head *tmp;
-       struct cifsSesInfo *ses;
-
-       *psrvTcp = NULL;
-
-       read_lock(&GlobalSMBSeslock);
-       list_for_each(tmp, &GlobalSMBSessionList) {
-               ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
-               if (!ses->server)
+       struct TCP_Server_Info *server;
+       struct sockaddr_in *addr4 = (struct sockaddr_in *) addr;
+       struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) addr;
+
+       write_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp, &cifs_tcp_ses_list) {
+               server = list_entry(tmp, struct TCP_Server_Info,
+                                   tcp_ses_list);
+
+               /*
+                * the demux thread can exit on its own while still in CifsNew
+                * so don't accept any sockets in that state. Since the
+                * tcpStatus never changes back to CifsNew it's safe to check
+                * for this without a lock.
+                */
+               if (server->tcpStatus == CifsNew)
                        continue;
 
-               if (target_ip_addr &&
-                   ses->server->addr.sockAddr.sin_addr.s_addr != target_ip_addr->s_addr)
-                               continue;
-               else if (target_ip6_addr &&
-                        memcmp(&ses->server->addr.sockAddr6.sin6_addr,
-                               target_ip6_addr, sizeof(*target_ip6_addr)))
-                               continue;
-               /* BB lock server and tcp session; increment use count here?? */
-
-               /* found a match on the TCP session */
-               *psrvTcp = ses->server;
+               if (addr->sa_family == AF_INET &&
+                   (addr4->sin_addr.s_addr !=
+                    server->addr.sockAddr.sin_addr.s_addr))
+                       continue;
+               else if (addr->sa_family == AF_INET6 &&
+                        memcmp(&server->addr.sockAddr6.sin6_addr,
+                               &addr6->sin6_addr, sizeof(addr6->sin6_addr)))
+                       continue;
 
-               /* BB check if reconnection needed */
-               if (strncmp(ses->userName, userName, MAX_USERNAME_SIZE) == 0) {
-                       read_unlock(&GlobalSMBSeslock);
-                       /* Found exact match on both TCP and
-                          SMB sessions */
-                       return ses;
-               }
-               /* else tcp and smb sessions need reconnection */
+               ++server->srv_count;
+               write_unlock(&cifs_tcp_ses_lock);
+               return server;
        }
-       read_unlock(&GlobalSMBSeslock);
-
+       write_unlock(&cifs_tcp_ses_lock);
        return NULL;
 }
 
-static struct cifsTconInfo *
-find_unc(__be32 new_target_ip_addr, char *uncName, char *userName)
+void
+cifs_put_tcp_session(struct TCP_Server_Info *server)
 {
-       struct list_head *tmp;
-       struct cifsTconInfo *tcon;
-       __be32 old_ip;
-
-       read_lock(&GlobalSMBSeslock);
-
-       list_for_each(tmp, &GlobalTreeConnectionList) {
-               cFYI(1, ("Next tcon"));
-               tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
-               if (!tcon->ses || !tcon->ses->server)
-                       continue;
-
-               old_ip = tcon->ses->server->addr.sockAddr.sin_addr.s_addr;
-               cFYI(1, ("old ip addr: %x == new ip %x ?",
-                       old_ip, new_target_ip_addr));
-
-               if (old_ip != new_target_ip_addr)
-                       continue;
-
-               /* BB lock tcon, server, tcp session and increment use count? */
-               /* found a match on the TCP session */
-               /* BB check if reconnection needed */
-               cFYI(1, ("IP match, old UNC: %s new: %s",
-                       tcon->treeName, uncName));
+       struct task_struct *task;
 
-               if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE))
-                       continue;
+       write_lock(&cifs_tcp_ses_lock);
+       if (--server->srv_count > 0) {
+               write_unlock(&cifs_tcp_ses_lock);
+               return;
+       }
 
-               cFYI(1, ("and old usr: %s new: %s",
-                       tcon->treeName, uncName));
+       list_del_init(&server->tcp_ses_list);
+       write_unlock(&cifs_tcp_ses_lock);
 
-               if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE))
-                       continue;
-
-               /* matched smb session (user name) */
-               read_unlock(&GlobalSMBSeslock);
-               return tcon;
-       }
+       spin_lock(&GlobalMid_Lock);
+       server->tcpStatus = CifsExiting;
+       spin_unlock(&GlobalMid_Lock);
 
-       read_unlock(&GlobalSMBSeslock);
-       return NULL;
+       task = xchg(&server->tsk, NULL);
+       if (task)
+               force_sig(SIGKILL, task);
 }
 
 int
@@ -2046,21 +2025,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                }
        }
 
-       if (addr.sa_family == AF_INET)
-               existingCifsSes = cifs_find_tcp_session(&sin_server->sin_addr,
-                       NULL /* no ipv6 addr */,
-                       volume_info.username, &srvTcp);
-       else if (addr.sa_family == AF_INET6) {
-               cFYI(1, ("looking for ipv6 address"));
-               existingCifsSes = cifs_find_tcp_session(NULL /* no ipv4 addr */,
-                       &sin_server6->sin6_addr,
-                       volume_info.username, &srvTcp);
-       } else {
-               rc = -EINVAL;
-               goto out;
-       }
-
-       if (!srvTcp) {
+       srvTcp = cifs_find_tcp_session(&addr);
+       if (srvTcp) {
+               cFYI(1, ("Existing tcp session with server found"));
+       } else {        /* create socket */
                if (addr.sa_family == AF_INET6) {
                        cFYI(1, ("attempting ipv6 connect"));
                        /* BB should we allow ipv6 on port 139? */
@@ -2130,6 +2098,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        memcpy(srvTcp->server_RFC1001_name,
                                volume_info.target_rfc1001_name, 16);
                        srvTcp->sequence_number = 0;
+                       INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
+                       ++srvTcp->srv_count;
+                       write_lock(&cifs_tcp_ses_lock);
+                       list_add(&srvTcp->tcp_ses_list,
+                                &cifs_tcp_ses_list);
+                       write_unlock(&cifs_tcp_ses_lock);
                }
        }
 
@@ -2181,17 +2155,12 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
                        rc = cifs_setup_session(xid, pSesInfo,
                                                cifs_sb->local_nls);
                        up(&pSesInfo->sesSem);
-                       if (!rc)
-                               atomic_inc(&srvTcp->socketUseCount);
                }
        }
 
        /* search for existing tcon to this server share */
        if (!rc) {
                setup_cifs_sb(&volume_info, cifs_sb);
-               tcon =
-                   find_unc(sin_server->sin_addr.s_addr, volume_info.UNC,
-                            volume_info.username);
                if (tcon) {
                        cFYI(1, ("Found match on UNC path"));
                        if (tcon->seal != volume_info.seal)
@@ -2248,47 +2217,22 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
 /* on error free sesinfo and tcon struct if needed */
 mount_fail_check:
        if (rc) {
-               /* if session setup failed, use count is zero but
-               we still need to free cifsd thread */
-               if (atomic_read(&srvTcp->socketUseCount) == 0) {
-                       spin_lock(&GlobalMid_Lock);
-                       srvTcp->tcpStatus = CifsExiting;
-                       spin_unlock(&GlobalMid_Lock);
-                       if (srvTcp->tsk) {
-                               /* If we could verify that kthread_stop would
-                                  always wake up processes blocked in
-                                  tcp in recv_mesg then we could remove the
-                                  send_sig call */
-                               force_sig(SIGKILL, srvTcp->tsk);
-                               kthread_stop(srvTcp->tsk);
-                       }
-               }
                 /* If find_unc succeeded then rc == 0 so we can not end */
-               if (tcon)  /* up accidently freeing someone elses tcon struct */
+               /* up accidently freeing someone elses tcon struct */
+               if (tcon)
                        tconInfoFree(tcon);
+
                if (existingCifsSes == NULL) {
                        if (pSesInfo) {
                                if ((pSesInfo->server) &&
-                                   (pSesInfo->status == CifsGood)) {
-                                       int temp_rc;
-                                       temp_rc = CIFSSMBLogoff(xid, pSesInfo);
-                                       /* if the socketUseCount is now zero */
-                                       if ((temp_rc == -ESHUTDOWN) &&
-                                           (pSesInfo->server) &&
-                                           (pSesInfo->server->tsk)) {
-                                               force_sig(SIGKILL,
-                                                       pSesInfo->server->tsk);
-                                               kthread_stop(pSesInfo->server->tsk);
-                                       }
-                               } else {
+                                   (pSesInfo->status == CifsGood))
+                                       CIFSSMBLogoff(xid, pSesInfo);
+                               else {
                                        cFYI(1, ("No session or bad tcon"));
-                                       if ((pSesInfo->server) &&
-                                           (pSesInfo->server->tsk)) {
-                                               force_sig(SIGKILL,
-                                                       pSesInfo->server->tsk);
-                                               kthread_stop(pSesInfo->server->tsk);
-                                       }
                                }
+                                       if (pSesInfo->server)
+                                               cifs_put_tcp_session(
+                                                       pSesInfo->server);
                                sesInfoFree(pSesInfo);
                                /* pSesInfo = NULL; */
                        }
@@ -3596,15 +3540,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
                        if (rc == -EBUSY) {
                                FreeXid(xid);
                                return 0;
-                       } else if (rc == -ESHUTDOWN) {
-                               cFYI(1, ("Waking up socket by sending signal"));
-                               if (cifsd_task) {
-                                       force_sig(SIGKILL, cifsd_task);
-                                       kthread_stop(cifsd_task);
-                               }
-                               rc = 0;
-                       } /* else - we have an smb session
-                               left on this socket do not kill cifsd */
+                       }
                } else
                        cFYI(1, ("No session or bad tcon"));
        }