--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:30:05 2008
+From: Steve French <sfrench@us.ibm.com>
+Date: Fri, 21 Nov 2008 14:23:07 +0530
+Subject: cifs: clean up server protocol handling
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>, Jeff Layton <jlayton@redhat.com>
+Message-ID: <492676F3.7080004@suse.de>
+
+From: Steve French <sfrench@us.ibm.com>
+
+commit 3ec332ef7a38c2327e18d087d4120a8e3bd3dc6e upstream.
+
+We're currently declaring both a sockaddr_in and sockaddr6_in on the
+stack, but we really only need storage for one of them. Declare a
+sockaddr struct and cast it to the proper type. Also, eliminate the
+protocolType field in the TCP_Server_Info struct. It's redundant since
+we have a sa_family field in the sockaddr anyway.
+
+We may need to revisit this if SCTP is ever implemented, but for now
+this will simplify the code.
+
+CIFS over IPv6 also has a number of problems currently. This fixes all
+of them that I found. Eventually, it would be nice to move more of the
+code to be protocol independent, but this is a start.
+
+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_spnego.c | 3 +-
+ fs/cifs/cifsglob.h | 3 --
+ fs/cifs/connect.c | 57 ++++++++++++++++++++++++++------------------------
+ 3 files changed, 33 insertions(+), 30 deletions(-)
+
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -85,8 +85,7 @@ enum securityEnum {
+ };
+
+ enum protocolEnum {
+- IPV4 = 0,
+- IPV6,
++ TCP = 0,
+ SCTP
+ /* Netbios frames protocol not supported at this time */
+ };
+--- a/fs/cifs/cifs_spnego.c
++++ b/fs/cifs/cifs_spnego.c
+@@ -70,7 +70,8 @@ struct key_type cifs_spnego_key_type = {
+ strlen("ver=0xFF") */
+ #define MAX_MECH_STR_LEN 13 /* length of longest security mechanism name, eg
+ in future could have strlen(";sec=ntlmsspi") */
+-#define MAX_IPV6_ADDR_LEN 42 /* eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/60 */
++/* max possible addr len eg FEDC:BA98:7654:3210:FEDC:BA98:7654:3210/128 */
++#define MAX_IPV6_ADDR_LEN 43
+ /* get a key struct with a SPNEGO security blob, suitable for session setup */
+ struct key *
+ cifs_get_spnego_key(struct cifsSesInfo *sesInfo)
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -190,7 +190,7 @@ cifs_reconnect(struct TCP_Server_Info *s
+
+ while ((!kthread_should_stop()) && (server->tcpStatus != CifsGood)) {
+ try_to_freeze();
+- if (server->protocolType == IPV6) {
++ if (server->addr.sockAddr6.sin6_family == AF_INET6) {
+ rc = ipv6_connect(&server->addr.sockAddr6,
+ &server->ssocket, server->noautotune);
+ } else {
+@@ -1960,10 +1960,10 @@ cifs_mount(struct super_block *sb, struc
+ {
+ int rc = 0;
+ int xid;
+- int address_type = AF_INET;
+ struct socket *csocket = NULL;
+- struct sockaddr_in sin_server;
+- struct sockaddr_in6 sin_server6;
++ struct sockaddr addr;
++ struct sockaddr_in *sin_server = (struct sockaddr_in *) &addr;
++ struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
+ struct smb_vol volume_info;
+ struct cifsSesInfo *pSesInfo = NULL;
+ struct cifsSesInfo *existingCifsSes = NULL;
+@@ -1974,6 +1974,7 @@ cifs_mount(struct super_block *sb, struc
+
+ /* cFYI(1, ("Entering cifs_mount. Xid: %d with: %s", xid, mount_data)); */
+
++ memset(&addr, 0, sizeof(struct sockaddr));
+ memset(&volume_info, 0, sizeof(struct smb_vol));
+ if (cifs_parse_mount_options(mount_data, devname, &volume_info)) {
+ rc = -EINVAL;
+@@ -1996,16 +1997,16 @@ cifs_mount(struct super_block *sb, struc
+
+ if (volume_info.UNCip && volume_info.UNC) {
+ rc = cifs_inet_pton(AF_INET, volume_info.UNCip,
+- &sin_server.sin_addr.s_addr);
++ &sin_server->sin_addr.s_addr);
+
+ if (rc <= 0) {
+ /* not ipv4 address, try ipv6 */
+ rc = cifs_inet_pton(AF_INET6, volume_info.UNCip,
+- &sin_server6.sin6_addr.in6_u);
++ &sin_server6->sin6_addr.in6_u);
+ if (rc > 0)
+- address_type = AF_INET6;
++ addr.sa_family = AF_INET6;
+ } else {
+- address_type = AF_INET;
++ addr.sa_family = AF_INET;
+ }
+
+ if (rc <= 0) {
+@@ -2045,39 +2046,38 @@ cifs_mount(struct super_block *sb, struc
+ }
+ }
+
+- if (address_type == AF_INET)
+- existingCifsSes = cifs_find_tcp_session(&sin_server.sin_addr,
++ 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 (address_type == AF_INET6) {
++ 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,
++ &sin_server6->sin6_addr,
+ volume_info.username, &srvTcp);
+ } else {
+ rc = -EINVAL;
+ goto out;
+ }
+
+- if (!srvTcp) { /* create socket */
+- if (volume_info.port)
+- sin_server.sin_port = htons(volume_info.port);
+- else
+- sin_server.sin_port = 0;
+- if (address_type == AF_INET6) {
++ if (!srvTcp) {
++ if (addr.sa_family == AF_INET6) {
+ cFYI(1, ("attempting ipv6 connect"));
+ /* BB should we allow ipv6 on port 139? */
+ /* other OS never observed in Wild doing 139 with v6 */
+- rc = ipv6_connect(&sin_server6, &csocket,
++ sin_server6->sin6_port = htons(volume_info.port);
++ rc = ipv6_connect(sin_server6, &csocket,
+ volume_info.noblocksnd);
+- } else
+- rc = ipv4_connect(&sin_server, &csocket,
++ } else {
++ sin_server->sin_port = htons(volume_info.port);
++ rc = ipv4_connect(sin_server, &csocket,
+ volume_info.source_rfc1001_name,
+ volume_info.target_rfc1001_name,
+ volume_info.noblocksnd,
+ volume_info.noautotune);
++ }
+ if (rc < 0) {
+- cERROR(1, ("Error connecting to IPv4 socket. "
++ cERROR(1, ("Error connecting to socket. "
+ "Aborting operation"));
+ if (csocket != NULL)
+ sock_release(csocket);
+@@ -2092,12 +2092,15 @@ cifs_mount(struct super_block *sb, struc
+ } else {
+ srvTcp->noblocksnd = volume_info.noblocksnd;
+ srvTcp->noautotune = volume_info.noautotune;
+- memcpy(&srvTcp->addr.sockAddr, &sin_server,
+- sizeof(struct sockaddr_in));
++ if (addr.sa_family == AF_INET6)
++ memcpy(&srvTcp->addr.sockAddr6, sin_server6,
++ sizeof(struct sockaddr_in6));
++ else
++ memcpy(&srvTcp->addr.sockAddr, sin_server,
++ sizeof(struct sockaddr_in));
+ atomic_set(&srvTcp->inFlight, 0);
+ /* BB Add code for ipv6 case too */
+ srvTcp->ssocket = csocket;
+- srvTcp->protocolType = IPV4;
+ srvTcp->hostname = extract_hostname(volume_info.UNC);
+ if (IS_ERR(srvTcp->hostname)) {
+ rc = PTR_ERR(srvTcp->hostname);
+@@ -2149,7 +2152,7 @@ cifs_mount(struct super_block *sb, struc
+ else {
+ pSesInfo->server = srvTcp;
+ sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
+- NIPQUAD(sin_server.sin_addr.s_addr));
++ NIPQUAD(sin_server->sin_addr.s_addr));
+ }
+
+ if (!rc) {
+@@ -2187,7 +2190,7 @@ cifs_mount(struct super_block *sb, struc
+ if (!rc) {
+ setup_cifs_sb(&volume_info, cifs_sb);
+ tcon =
+- find_unc(sin_server.sin_addr.s_addr, volume_info.UNC,
++ find_unc(sin_server->sin_addr.s_addr, volume_info.UNC,
+ volume_info.username);
+ if (tcon) {
+ cFYI(1, ("Found match on UNC path"));
--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:30:44 2008
+From: Jeff Layton <jlayton@redhat.com>
+Date: Fri, 21 Nov 2008 14:23:17 +0530
+Subject: cifs: disable sharing session and tcon and add new TCP sharing code
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>, Jeff Layton <jlayton@redhat.com>
+Message-ID: <492676FD.7060706@suse.de>
+
+From: Jeff Layton <jlayton@redhat.com>
+
+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 | 2
+ fs/cifs/cifsfs.c | 3
+ fs/cifs/cifsglob.h | 17 ++--
+ fs/cifs/cifsproto.h | 1
+ fs/cifs/cifssmb.c | 18 ++--
+ fs/cifs/connect.c | 206 +++++++++++++++++----------------------------------
+ 6 files changed, 95 insertions(+), 152 deletions(-)
+
+--- a/fs/cifs/cifs_debug.c
++++ b/fs/cifs/cifs_debug.c
+@@ -144,7 +144,7 @@ static int cifs_debug_data_proc_show(str
+ 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));
+
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -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) {
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -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 */
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -102,6 +102,7 @@ extern void acl_to_uid_mode(struct inode
+ 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 *);
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -664,8 +664,9 @@ CIFSSMBNegotiate(unsigned int xid, struc
+ 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, struc
+ 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 cifs
+ 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);
+
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -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,
+ 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));
++ struct task_struct *task;
+
+- 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));
+-
+- if (strncmp(tcon->treeName, uncName, MAX_TREE_SIZE))
+- continue;
+-
+- cFYI(1, ("and old usr: %s new: %s",
+- tcon->treeName, uncName));
++ write_lock(&cifs_tcp_ses_lock);
++ if (--server->srv_count > 0) {
++ write_unlock(&cifs_tcp_ses_lock);
++ return;
++ }
+
+- if (strncmp(tcon->ses->userName, userName, MAX_USERNAME_SIZE))
+- continue;
++ list_del_init(&server->tcp_ses_list);
++ write_unlock(&cifs_tcp_ses_lock);
+
+- /* 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, struc
+ }
+ }
+
+- 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, struc
+ 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, struc
+ 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, struc
+ /* 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, stru
+ 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"));
+ }
--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:41:59 2008
+From: Steve French <sfrench@us.ibm.com>
+Date: Fri, 21 Nov 2008 14:24:05 +0530
+Subject: cifs: Fix build break
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>
+Message-ID: <4926772D.60304@suse.de>
+
+From: Steve French <sfrench@us.ibm.com>
+
+commit c2b3382cd4d6c6adef1347e81f20e16c93a39feb upstream
+
+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 | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/fs/cifs/cifs_debug.c
++++ b/fs/cifs/cifs_debug.c
+@@ -249,9 +249,9 @@ static ssize_t cifs_stats_proc_write(str
+ list_for_each(tmp1, &cifs_tcp_ses_list) {
+ server = list_entry(tmp1, struct TCP_Server_Info,
+ tcp_ses_list);
+- list_for_each(tmp2, &server->smb_session_list) {
++ list_for_each(tmp2, &server->smb_ses_list) {
+ ses = list_entry(tmp2, struct cifsSesInfo,
+- smb_session_list);
++ smb_ses_list);
+ list_for_each(tmp3, &ses->tcon_list) {
+ tcon = list_entry(tmp3,
+ struct cifsTconInfo,
--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:44:31 2008
+From: Steve French <sfrench@us.ibm.com>
+Date: Mon, 24 Nov 2008 10:31:05 +0530
+Subject: cifs: fix check for dead tcon in smb_init
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>, Jeff Layton <jlayton@redhat.com>
+Message-ID: <492A3511.2040109@suse.de>
+
+From: Steve French <sfrench@us.ibm.com>
+
+commit bfb59820ee46616a7bdb4af6b8f7e109646de6ec upstream
+
+This was recently changed to check for need_reconnect, but should
+actually be a check for a tidStatus of CifsExiting.
+
+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/cifssmb.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -295,7 +295,7 @@ smb_init(int smb_command, int wct, struc
+ check for tcp and smb session status done differently
+ for those three - in the calling routine */
+ if (tcon) {
+- if (tcon->need_reconnect) {
++ if (tcon->tidStatus == CifsExiting) {
+ /* only tree disconnect, open, and write,
+ (and ulogoff which does not have tcon)
+ are allowed as we start force umount */
--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:42:51 2008
+From: Steve French <sfrench@us.ibm.com>
+Date: Fri, 21 Nov 2008 14:24:12 +0530
+Subject: cifs: Fix check for tcon seal setting and fix oops on failed mount from earlier patch
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>, Jeff Layton <jlayton@redhat.com>
+Message-ID: <49267734.7050508@suse.de>
+
+From: Steve French <sfrench@us.ibm.com>
+
+commit ab3f992983062440b4f37c666dac66d987902d91 upstream
+
+set tcon->ses earlier
+
+If the inital tree connect fails, we'll end up calling cifs_put_smb_ses
+with a NULL pointer. Fix it by setting the tcon->ses earlier.
+
+Acked-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/connect.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -2256,16 +2256,18 @@ cifs_mount(struct super_block *sb, struc
+ cFYI(1, ("Found match on UNC path"));
+ /* existing tcon already has a reference */
+ cifs_put_smb_ses(pSesInfo);
++ if (tcon->seal != volume_info.seal)
++ cERROR(1, ("transport encryption setting "
++ "conflicts with existing tid"));
+ } else {
+ tcon = tconInfoAlloc();
+ if (tcon == NULL) {
+ rc = -ENOMEM;
+ goto mount_fail_check;
+ }
++ tcon->ses = pSesInfo;
+
+ /* check for null share name ie connect to dfs root */
+-
+- /* BB check if works for exactly length 3 strings */
+ if ((strchr(volume_info.UNC + 3, '\\') == NULL)
+ && (strchr(volume_info.UNC + 3, '/') == NULL)) {
+ /* rc = connect_to_dfs_path(...) */
+@@ -2283,7 +2285,6 @@ cifs_mount(struct super_block *sb, struc
+ if (rc)
+ goto mount_fail_check;
+ tcon->seal = volume_info.seal;
+- tcon->ses = pSesInfo;
+ write_lock(&cifs_tcp_ses_lock);
+ list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
+ write_unlock(&cifs_tcp_ses_lock);
--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:28:09 2008
+From: Steve French <sfrench@us.ibm.com>
+Date: Fri, 21 Nov 2008 14:22:45 +0530
+Subject: cifs: Fix cifs reconnection flags
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>, Shirish S Pargaonkar <shirishp@us.ibm.com>, Jeff Layton <jlayton@redhat.com>
+Message-ID: <492676DD.5070504@suse.de>
+
+From: Steve French <sfrench@us.ibm.com>
+
+commit 3b7952109361c684caf0c50474da8662ecc81019 upstream
+
+[CIFS] Fix cifs reconnection flags
+
+In preparation for Jeff's big umount/mount fixes to remove the possibility of
+various races in cifs mount and linked list handling of sessions, sockets and
+tree connections, this patch cleans up some repetitive code in cifs_mount,
+and addresses a problem with ses->status and tcon->tidStatus in which we
+were overloading the "need_reconnect" state with other status in that
+field. So the "need_reconnect" flag has been broken out from those
+two state fields (need reconnect was not mutually exclusive from some of the
+other possible tid and ses states). In addition, a few exit cases in
+cifs_mount were cleaned up, and a problem with a tcon flag (for lease support)
+was not being set consistently for the 2nd mount of the same share
+
+CC: Jeff Layton <jlayton@redhat.com>
+CC: Shirish Pargaonkar <shirishp@us.ibm.com>
+Signed-off-by: Steve French <sfrench@us.ibm.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/cifs/cifsfs.c | 2
+ fs/cifs/cifsglob.h | 5 +
+ fs/cifs/cifssmb.c | 40 ++++----
+ fs/cifs/connect.c | 252 ++++++++++++++++++++++++++---------------------------
+ fs/cifs/file.c | 2
+ 5 files changed, 155 insertions(+), 146 deletions(-)
+
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -967,7 +967,7 @@ static int cifs_oplock_thread(void *dumm
+ not bother sending an oplock release if session
+ to server still is disconnected since oplock
+ already released by the server in that case */
+- if (pTcon->tidStatus != CifsNeedReconnect) {
++ if (!pTcon->need_reconnect) {
+ rc = CIFSSMBLock(0, pTcon, netfid,
+ 0 /* len */ , 0 /* offset */, 0,
+ 0, LOCKING_ANDX_OPLOCK_RELEASE,
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -122,6 +122,8 @@ struct cifs_cred {
+ */
+
+ struct TCP_Server_Info {
++ struct list_head tcp_ses_list;
++ struct list_head smb_ses_list;
+ /* 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];
+@@ -195,6 +197,7 @@ struct cifsUidInfo {
+ */
+ struct cifsSesInfo {
+ struct list_head cifsSessionList;
++ struct list_head tcon_list;
+ struct semaphore sesSem;
+ #if 0
+ struct cifsUidInfo *uidInfo; /* pointer to user info */
+@@ -216,6 +219,7 @@ struct cifsSesInfo {
+ char userName[MAX_USERNAME_SIZE + 1];
+ char *domainName;
+ char *password;
++ bool need_reconnect:1; /* connection reset, uid now invalid */
+ };
+ /* no more than one of the following three session flags may be set */
+ #define CIFS_SES_NT4 1
+@@ -287,6 +291,7 @@ struct cifsTconInfo {
+ bool seal:1; /* transport encryption for this mounted share */
+ bool unix_ext:1; /* if false disable Linux extensions to CIFS protocol
+ for this mount even if server would support */
++ bool need_reconnect:1; /* connection reset, tid now invalid */
+ /* BB add field for back pointer to sb struct(s)? */
+ };
+
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -190,10 +190,10 @@ small_smb_init(int smb_command, int wct,
+ /* need to prevent multiple threads trying to
+ simultaneously reconnect the same SMB session */
+ down(&tcon->ses->sesSem);
+- if (tcon->ses->status == CifsNeedReconnect)
++ if (tcon->ses->need_reconnect)
+ rc = cifs_setup_session(0, tcon->ses,
+ nls_codepage);
+- if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
++ if (!rc && (tcon->need_reconnect)) {
+ mark_open_files_invalid(tcon);
+ rc = CIFSTCon(0, tcon->ses, tcon->treeName,
+ tcon, nls_codepage);
+@@ -295,7 +295,7 @@ smb_init(int smb_command, int wct, struc
+ check for tcp and smb session status done differently
+ for those three - in the calling routine */
+ if (tcon) {
+- if (tcon->tidStatus == CifsExiting) {
++ if (tcon->need_reconnect) {
+ /* only tree disconnect, open, and write,
+ (and ulogoff which does not have tcon)
+ are allowed as we start force umount */
+@@ -337,10 +337,10 @@ smb_init(int smb_command, int wct, struc
+ /* need to prevent multiple threads trying to
+ simultaneously reconnect the same SMB session */
+ down(&tcon->ses->sesSem);
+- if (tcon->ses->status == CifsNeedReconnect)
++ if (tcon->ses->need_reconnect)
+ rc = cifs_setup_session(0, tcon->ses,
+ nls_codepage);
+- if (!rc && (tcon->tidStatus == CifsNeedReconnect)) {
++ if (!rc && (tcon->need_reconnect)) {
+ mark_open_files_invalid(tcon);
+ rc = CIFSTCon(0, tcon->ses, tcon->treeName,
+ tcon, nls_codepage);
+@@ -759,7 +759,7 @@ CIFSSMBTDis(const int xid, struct cifsTc
+
+ /* No need to return error on this operation if tid invalidated and
+ closed on server already e.g. due to tcp session crashing */
+- if (tcon->tidStatus == CifsNeedReconnect) {
++ if (tcon->need_reconnect) {
+ up(&tcon->tconSem);
+ return 0;
+ }
+@@ -806,32 +806,36 @@ CIFSSMBLogoff(const int xid, struct cifs
+ up(&ses->sesSem);
+ return -EBUSY;
+ }
++
++ if (ses->server == NULL)
++ return -EIO;
++
++ if (ses->need_reconnect)
++ goto session_already_dead; /* no need to send SMBlogoff if uid
++ already closed due to reconnect */
+ rc = small_smb_init(SMB_COM_LOGOFF_ANDX, 2, NULL, (void **)&pSMB);
+ if (rc) {
+ up(&ses->sesSem);
+ return rc;
+ }
+
+- if (ses->server) {
+- pSMB->hdr.Mid = GetNextMid(ses->server);
++ pSMB->hdr.Mid = GetNextMid(ses->server);
+
+- if (ses->server->secMode &
++ if (ses->server->secMode &
+ (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))
+ pSMB->hdr.Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
+- }
+
+ pSMB->hdr.Uid = ses->Suid;
+
+ pSMB->AndXCommand = 0xFF;
+ rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
+- if (ses->server) {
+- 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;
+- }
++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;
+ }
+ up(&ses->sesSem);
+
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -147,7 +147,7 @@ cifs_reconnect(struct TCP_Server_Info *s
+ ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
+ if (ses->server) {
+ if (ses->server == server) {
+- ses->status = CifsNeedReconnect;
++ ses->need_reconnect = true;
+ ses->ipc_tid = 0;
+ }
+ }
+@@ -156,7 +156,7 @@ cifs_reconnect(struct TCP_Server_Info *s
+ list_for_each(tmp, &GlobalTreeConnectionList) {
+ tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
+ if ((tcon->ses) && (tcon->ses->server == server))
+- tcon->tidStatus = CifsNeedReconnect;
++ tcon->need_reconnect = true;
+ }
+ read_unlock(&GlobalSMBSeslock);
+ /* do not want to be sending data on a socket we are freeing */
+@@ -1868,6 +1868,92 @@ convert_delimiter(char *path, char delim
+ }
+ }
+
++static void setup_cifs_sb(struct smb_vol *pvolume_info,
++ struct cifs_sb_info *cifs_sb)
++{
++ if (pvolume_info->rsize > CIFSMaxBufSize) {
++ cERROR(1, ("rsize %d too large, using MaxBufSize",
++ pvolume_info->rsize));
++ cifs_sb->rsize = CIFSMaxBufSize;
++ } else if ((pvolume_info->rsize) &&
++ (pvolume_info->rsize <= CIFSMaxBufSize))
++ cifs_sb->rsize = pvolume_info->rsize;
++ else /* default */
++ cifs_sb->rsize = CIFSMaxBufSize;
++
++ if (pvolume_info->wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
++ cERROR(1, ("wsize %d too large, using 4096 instead",
++ pvolume_info->wsize));
++ cifs_sb->wsize = 4096;
++ } else if (pvolume_info->wsize)
++ cifs_sb->wsize = pvolume_info->wsize;
++ else
++ cifs_sb->wsize = min_t(const int,
++ PAGEVEC_SIZE * PAGE_CACHE_SIZE,
++ 127*1024);
++ /* old default of CIFSMaxBufSize was too small now
++ that SMB Write2 can send multiple pages in kvec.
++ RFC1001 does not describe what happens when frame
++ bigger than 128K is sent so use that as max in
++ conjunction with 52K kvec constraint on arch with 4K
++ page size */
++
++ if (cifs_sb->rsize < 2048) {
++ cifs_sb->rsize = 2048;
++ /* Windows ME may prefer this */
++ cFYI(1, ("readsize set to minimum: 2048"));
++ }
++ /* calculate prepath */
++ cifs_sb->prepath = pvolume_info->prepath;
++ if (cifs_sb->prepath) {
++ cifs_sb->prepathlen = strlen(cifs_sb->prepath);
++ /* we can not convert the / to \ in the path
++ separators in the prefixpath yet because we do not
++ know (until reset_cifs_unix_caps is called later)
++ whether POSIX PATH CAP is available. We normalize
++ the / to \ after reset_cifs_unix_caps is called */
++ pvolume_info->prepath = NULL;
++ } else
++ cifs_sb->prepathlen = 0;
++ cifs_sb->mnt_uid = pvolume_info->linux_uid;
++ cifs_sb->mnt_gid = pvolume_info->linux_gid;
++ cifs_sb->mnt_file_mode = pvolume_info->file_mode;
++ cifs_sb->mnt_dir_mode = pvolume_info->dir_mode;
++ cFYI(1, ("file mode: 0x%x dir mode: 0x%x",
++ cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode));
++
++ if (pvolume_info->noperm)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
++ if (pvolume_info->setuids)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
++ if (pvolume_info->server_ino)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
++ if (pvolume_info->remap)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
++ if (pvolume_info->no_xattr)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
++ if (pvolume_info->sfu_emul)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
++ if (pvolume_info->nobrl)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
++ if (pvolume_info->cifs_acl)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
++ if (pvolume_info->override_uid)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
++ if (pvolume_info->override_gid)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
++ if (pvolume_info->dynperm)
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
++ if (pvolume_info->direct_io) {
++ cFYI(1, ("mounting share using direct i/o"));
++ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
++ }
++
++ if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm))
++ cERROR(1, ("mount option dynperm ignored if cifsacl "
++ "mount option supported"));
++}
++
+ int
+ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
+ char *mount_data, const char *devname)
+@@ -1973,9 +2059,7 @@ cifs_mount(struct super_block *sb, struc
+ goto out;
+ }
+
+- if (srvTcp) {
+- cFYI(1, ("Existing tcp session with server found"));
+- } else { /* create socket */
++ if (!srvTcp) { /* create socket */
+ if (volume_info.port)
+ sin_server.sin_port = htons(volume_info.port);
+ else
+@@ -2051,7 +2135,7 @@ cifs_mount(struct super_block *sb, struc
+ cFYI(1, ("Existing smb sess found (status=%d)",
+ pSesInfo->status));
+ down(&pSesInfo->sesSem);
+- if (pSesInfo->status == CifsNeedReconnect) {
++ if (pSesInfo->need_reconnect) {
+ cFYI(1, ("Session needs reconnect"));
+ rc = cifs_setup_session(xid, pSesInfo,
+ cifs_sb->local_nls);
+@@ -2101,139 +2185,52 @@ cifs_mount(struct super_block *sb, struc
+
+ /* search for existing tcon to this server share */
+ if (!rc) {
+- if (volume_info.rsize > CIFSMaxBufSize) {
+- cERROR(1, ("rsize %d too large, using MaxBufSize",
+- volume_info.rsize));
+- cifs_sb->rsize = CIFSMaxBufSize;
+- } else if ((volume_info.rsize) &&
+- (volume_info.rsize <= CIFSMaxBufSize))
+- cifs_sb->rsize = volume_info.rsize;
+- else /* default */
+- cifs_sb->rsize = CIFSMaxBufSize;
+-
+- if (volume_info.wsize > PAGEVEC_SIZE * PAGE_CACHE_SIZE) {
+- cERROR(1, ("wsize %d too large, using 4096 instead",
+- volume_info.wsize));
+- cifs_sb->wsize = 4096;
+- } else if (volume_info.wsize)
+- cifs_sb->wsize = volume_info.wsize;
+- else
+- cifs_sb->wsize =
+- min_t(const int, PAGEVEC_SIZE * PAGE_CACHE_SIZE,
+- 127*1024);
+- /* old default of CIFSMaxBufSize was too small now
+- that SMB Write2 can send multiple pages in kvec.
+- RFC1001 does not describe what happens when frame
+- bigger than 128K is sent so use that as max in
+- conjunction with 52K kvec constraint on arch with 4K
+- page size */
+-
+- if (cifs_sb->rsize < 2048) {
+- cifs_sb->rsize = 2048;
+- /* Windows ME may prefer this */
+- cFYI(1, ("readsize set to minimum: 2048"));
+- }
+- /* calculate prepath */
+- cifs_sb->prepath = volume_info.prepath;
+- if (cifs_sb->prepath) {
+- cifs_sb->prepathlen = strlen(cifs_sb->prepath);
+- /* we can not convert the / to \ in the path
+- separators in the prefixpath yet because we do not
+- know (until reset_cifs_unix_caps is called later)
+- whether POSIX PATH CAP is available. We normalize
+- the / to \ after reset_cifs_unix_caps is called */
+- volume_info.prepath = NULL;
+- } else
+- cifs_sb->prepathlen = 0;
+- cifs_sb->mnt_uid = volume_info.linux_uid;
+- cifs_sb->mnt_gid = volume_info.linux_gid;
+- cifs_sb->mnt_file_mode = volume_info.file_mode;
+- cifs_sb->mnt_dir_mode = volume_info.dir_mode;
+- cFYI(1, ("file mode: 0x%x dir mode: 0x%x",
+- cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode));
+-
+- if (volume_info.noperm)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
+- if (volume_info.setuids)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
+- if (volume_info.server_ino)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
+- if (volume_info.remap)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
+- if (volume_info.no_xattr)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
+- if (volume_info.sfu_emul)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
+- if (volume_info.nobrl)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
+- if (volume_info.cifs_acl)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
+- if (volume_info.override_uid)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
+- if (volume_info.override_gid)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
+- if (volume_info.dynperm)
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
+- if (volume_info.direct_io) {
+- cFYI(1, ("mounting share using direct i/o"));
+- cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
+- }
+-
+- if ((volume_info.cifs_acl) && (volume_info.dynperm))
+- cERROR(1, ("mount option dynperm ignored if cifsacl "
+- "mount option supported"));
+-
++ 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"));
+- /* we can have only one retry value for a connection
+- to a share so for resources mounted more than once
+- to the same server share the last value passed in
+- for the retry flag is used */
+- tcon->retry = volume_info.retry;
+- tcon->nocase = volume_info.nocase;
+ if (tcon->seal != volume_info.seal)
+ cERROR(1, ("transport encryption setting "
+ "conflicts with existing tid"));
+ } else {
+ tcon = tconInfoAlloc();
+- if (tcon == NULL)
++ if (tcon == NULL) {
+ rc = -ENOMEM;
+- else {
+- /* check for null share name ie connecting to
+- * dfs root */
+-
+- /* BB check if this works for exactly length
+- * three strings */
+- if ((strchr(volume_info.UNC + 3, '\\') == NULL)
+- && (strchr(volume_info.UNC + 3, '/') ==
+- NULL)) {
+-/* rc = connect_to_dfs_path(xid, pSesInfo,
+- "", cifs_sb->local_nls,
+- cifs_sb->mnt_cifs_flags &
+- CIFS_MOUNT_MAP_SPECIAL_CHR);*/
+- cFYI(1, ("DFS root not supported"));
+- rc = -ENODEV;
+- goto out;
+- } else {
+- /* BB Do we need to wrap sesSem around
+- * this TCon call and Unix SetFS as
+- * we do on SessSetup and reconnect? */
+- rc = CIFSTCon(xid, pSesInfo,
+- volume_info.UNC,
+- tcon, cifs_sb->local_nls);
+- cFYI(1, ("CIFS Tcon rc = %d", rc));
+- }
+- if (!rc) {
+- atomic_inc(&pSesInfo->inUse);
+- tcon->retry = volume_info.retry;
+- tcon->nocase = volume_info.nocase;
+- tcon->seal = volume_info.seal;
+- }
++ goto mount_fail_check;
+ }
++
++ /* check for null share name ie connect to dfs root */
++
++ /* BB check if works for exactly length 3 strings */
++ if ((strchr(volume_info.UNC + 3, '\\') == NULL)
++ && (strchr(volume_info.UNC + 3, '/') == NULL)) {
++ /* rc = connect_to_dfs_path(...) */
++ cFYI(1, ("DFS root not supported"));
++ rc = -ENODEV;
++ goto mount_fail_check;
++ } else {
++ /* BB Do we need to wrap sesSem around
++ * this TCon call and Unix SetFS as
++ * we do on SessSetup and reconnect? */
++ rc = CIFSTCon(xid, pSesInfo, volume_info.UNC,
++ tcon, cifs_sb->local_nls);
++ cFYI(1, ("CIFS Tcon rc = %d", rc));
++ }
++ if (!rc) {
++ atomic_inc(&pSesInfo->inUse);
++ tcon->seal = volume_info.seal;
++ } else
++ goto mount_fail_check;
+ }
++
++ /* we can have only one retry value for a connection
++ to a share so for resources mounted more than once
++ to the same server share the last value passed in
++ for the retry flag is used */
++ tcon->retry = volume_info.retry;
++ tcon->nocase = volume_info.nocase;
+ }
+ if (pSesInfo) {
+ if (pSesInfo->capabilities & CAP_LARGE_FILES) {
+@@ -2246,6 +2243,7 @@ cifs_mount(struct super_block *sb, struc
+ sb->s_time_gran = 100;
+
+ /* 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 */
+@@ -3499,6 +3497,7 @@ CIFSTCon(unsigned int xid, struct cifsSe
+ /* above now done in SendReceive */
+ if ((rc == 0) && (tcon != NULL)) {
+ tcon->tidStatus = CifsGood;
++ tcon->need_reconnect = false;
+ tcon->tid = smb_buffer_response->Tid;
+ bcc_ptr = pByteArea(smb_buffer_response);
+ length = strnlen(bcc_ptr, BCC(smb_buffer_response) - 2);
+@@ -3730,6 +3729,7 @@ int cifs_setup_session(unsigned int xid,
+ } else {
+ cFYI(1, ("CIFS Session Established successfully"));
+ pSesInfo->status = CifsGood;
++ pSesInfo->need_reconnect = false;
+ }
+
+ ss_err_exit:
+--- a/fs/cifs/file.c
++++ b/fs/cifs/file.c
+@@ -493,7 +493,7 @@ int cifs_close(struct inode *inode, stru
+ if (pTcon) {
+ /* no sense reconnecting to close a file that is
+ already closed */
+- if (pTcon->tidStatus != CifsNeedReconnect) {
++ if (!pTcon->need_reconnect) {
+ timeout = 2;
+ while ((atomic_read(&pSMBFile->wrtPending) != 0)
+ && (timeout <= 2048)) {
--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:40:26 2008
+From: Steve French <sfrench@us.ibm.com>
+Date: Fri, 21 Nov 2008 14:23:44 +0530
+Subject: cifs: minor cleanup to cifs_mount
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>
+Message-ID: <49267718.9010206@suse.de>
+
+From: Steve French <sfrench@us.ibm.com>
+
+commit d82c2df54e2f7e447476350848d8eccc8d2fe46a upstream
+
+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/connect.c | 74 ++++++++++++++++++++++++------------------------------
+ 1 file changed, 34 insertions(+), 40 deletions(-)
+
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -1357,7 +1357,6 @@ cifs_find_tcp_session(struct sockaddr *a
+ 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
+@@ -1378,6 +1377,7 @@ cifs_find_tcp_session(struct sockaddr *a
+
+ ++server->srv_count;
+ write_unlock(&cifs_tcp_ses_lock);
++ cFYI(1, ("Existing tcp session with server found"));
+ return server;
+ }
+ write_unlock(&cifs_tcp_ses_lock);
+@@ -2063,9 +2063,7 @@ cifs_mount(struct super_block *sb, struc
+ }
+
+ srvTcp = cifs_find_tcp_session(&addr);
+- if (srvTcp) {
+- cFYI(1, ("Existing tcp session with server found"));
+- } else { /* create socket */
++ if (!srvTcp) { /* create socket */
+ if (addr.sa_family == AF_INET6) {
+ cFYI(1, ("attempting ipv6 connect"));
+ /* BB should we allow ipv6 on port 139? */
+@@ -2272,44 +2270,40 @@ mount_fail_check:
+ cifs_put_smb_ses(pSesInfo);
+ else
+ cifs_put_tcp_session(srvTcp);
+- } else {
+- atomic_inc(&tcon->useCount);
+- cifs_sb->tcon = tcon;
+- tcon->ses = pSesInfo;
+-
+- /* do not care if following two calls succeed - informational */
+- if (!tcon->ipc) {
+- CIFSSMBQFSDeviceInfo(xid, tcon);
+- CIFSSMBQFSAttributeInfo(xid, tcon);
+- }
++ goto out;
++ }
++ atomic_inc(&tcon->useCount);
++ cifs_sb->tcon = tcon;
++ tcon->ses = pSesInfo;
++
++ /* do not care if following two calls succeed - informational */
++ if (!tcon->ipc) {
++ CIFSSMBQFSDeviceInfo(xid, tcon);
++ CIFSSMBQFSAttributeInfo(xid, tcon);
++ }
+
+- /* tell server which Unix caps we support */
+- if (tcon->ses->capabilities & CAP_UNIX)
+- /* reset of caps checks mount to see if unix extensions
+- disabled for just this mount */
+- reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
+- else
+- tcon->unix_ext = 0; /* server does not support them */
++ /* tell server which Unix caps we support */
++ if (tcon->ses->capabilities & CAP_UNIX)
++ /* reset of caps checks mount to see if unix extensions
++ disabled for just this mount */
++ reset_cifs_unix_caps(xid, tcon, sb, &volume_info);
++ else
++ tcon->unix_ext = 0; /* server does not support them */
+
+- /* convert forward to back slashes in prepath here if needed */
+- if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
+- convert_delimiter(cifs_sb->prepath,
+- CIFS_DIR_SEP(cifs_sb));
+-
+- if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
+- cifs_sb->rsize = 1024 * 127;
+- cFYI(DBG2,
+- ("no very large read support, rsize now 127K"));
+- }
+- if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
+- cifs_sb->wsize = min(cifs_sb->wsize,
+- (tcon->ses->server->maxBuf -
+- MAX_CIFS_HDR_SIZE));
+- if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
+- cifs_sb->rsize = min(cifs_sb->rsize,
+- (tcon->ses->server->maxBuf -
+- MAX_CIFS_HDR_SIZE));
+- }
++ /* convert forward to back slashes in prepath here if needed */
++ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_POSIX_PATHS) == 0)
++ convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
++
++ if ((tcon->unix_ext == 0) && (cifs_sb->rsize > (1024 * 127))) {
++ cifs_sb->rsize = 1024 * 127;
++ cFYI(DBG2, ("no very large read support, rsize now 127K"));
++ }
++ if (!(tcon->ses->capabilities & CAP_LARGE_WRITE_X))
++ cifs_sb->wsize = min(cifs_sb->wsize,
++ (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
++ if (!(tcon->ses->capabilities & CAP_LARGE_READ_X))
++ cifs_sb->rsize = min(cifs_sb->rsize,
++ (tcon->ses->server->maxBuf - MAX_CIFS_HDR_SIZE));
+
+ /* volume_info.password is freed above when existing session found
+ (in which case it is not needed anymore) but when new sesion is created
--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:43:30 2008
+From: Dave Kleikamp <shaggy@linux.vnet.ibm.com>
+Date: Fri, 21 Nov 2008 14:24:20 +0530
+Subject: cifs: prevent cifs_writepages() from skipping unwritten pages
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>, Dave Kleikamp <shaggy@linux.vnet.ibm.com>, Shirish S Pargaonkar <shirishp@us.ibm.com>, Jeff Layton <jlayton@redhat.com>
+Message-ID: <4926773C.6010409@suse.de>
+
+From: Dave Kleikamp <shaggy@linux.vnet.ibm.com>
+
+commit b066a48c9532243894f93a06ca5a0ee2cc21a8dc upstream
+
+prevent cifs_writepages() from skipping unwritten pages
+
+Fixes a data corruption under heavy stress in which pages could be left
+dirty after all open instances of a inode have been closed.
+
+In order to write contiguous pages whenever possible, cifs_writepages()
+asks pagevec_lookup_tag() for more pages than it may write at one time.
+Normally, it then resets index just past the last page written before calling
+pagevec_lookup_tag() again.
+
+If cifs_writepages() can't write the first page returned, it wasn't resetting
+index, and the next call to pagevec_lookup_tag() resulted in skipping all of
+the pages it previously returned, even though cifs_writepages() did nothing
+with them. This can result in data loss when the file descriptor is about
+to be closed.
+
+This patch ensures that index gets set back to the next returned page so
+that none get skipped.
+
+Signed-off-by: Dave Kleikamp <shaggy@linux.vnet.ibm.com>
+Acked-by: Jeff Layton <jlayton@redhat.com>
+Cc: Shirish S Pargaonkar <shirishp@us.ibm.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/file.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/fs/cifs/file.c
++++ b/fs/cifs/file.c
+@@ -1396,7 +1396,10 @@ retry:
+ if ((wbc->nr_to_write -= n_iov) <= 0)
+ done = 1;
+ index = next;
+- }
++ } else
++ /* Need to re-find the pages we skipped */
++ index = pvec.pages[0]->index + 1;
++
+ pagevec_release(&pvec);
+ }
+ if (!scanned && !done) {
--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:31:32 2008
+From: Jeff Layton <jlayton@redhat.com>
+Date: Fri, 21 Nov 2008 14:23:30 +0530
+Subject: cifs: reinstate sharing of SMB sessions sans races
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>, Jeff Layton <jlayton@redhat.com>
+Message-ID: <4926770A.7090107@suse.de>
+
+From: Jeff Layton <jlayton@redhat.com>
+
+commit 14fbf50d695207754daeb96270b3027a3821121f upstream
+
+We do this by abandoning the global list of SMB sessions and instead
+moving to a per-server list. This entails adding a new list head to the
+TCP_Server_Info struct. The refcounting for the cifsSesInfo is moved to
+a non-atomic variable. We have to protect it by a lock anyway, so there's
+no benefit to making it an atomic. The list and refcount are protected
+by the global cifs_tcp_ses_lock.
+
+The patch also adds a new routines to find and put SMB sessions and
+that properly take and put references under the lock.
+
+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 | 53 ++++++------
+ fs/cifs/cifsfs.c | 17 +--
+ fs/cifs/cifsglob.h | 6 -
+ fs/cifs/cifsproto.h | 1
+ fs/cifs/cifssmb.c | 22 +---
+ fs/cifs/connect.c | 225 ++++++++++++++++++++++++++++-----------------------
+ fs/cifs/misc.c | 16 +--
+ 7 files changed, 174 insertions(+), 166 deletions(-)
+
+--- a/fs/cifs/cifs_debug.c
++++ b/fs/cifs/cifs_debug.c
+@@ -107,9 +107,9 @@ void cifs_dump_mids(struct TCP_Server_In
+ #ifdef CONFIG_PROC_FS
+ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
+ {
+- struct list_head *tmp;
+- struct list_head *tmp1;
++ struct list_head *tmp, *tmp2, *tmp3;
+ struct mid_q_entry *mid_entry;
++ struct TCP_Server_Info *server;
+ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+ int i;
+@@ -122,43 +122,45 @@ static int cifs_debug_data_proc_show(str
+ seq_printf(m, "Servers:");
+
+ i = 0;
+- read_lock(&GlobalSMBSeslock);
+- list_for_each(tmp, &GlobalSMBSessionList) {
++ read_lock(&cifs_tcp_ses_lock);
++ list_for_each(tmp, &cifs_tcp_ses_list) {
++ server = list_entry(tmp, struct TCP_Server_Info,
++ tcp_ses_list);
+ i++;
+- ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
+- if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) ||
+- (ses->serverNOS == NULL)) {
+- seq_printf(m, "\nentry for %s not fully "
+- "displayed\n\t", ses->serverName);
+- } else {
+- seq_printf(m,
++ list_for_each(tmp2, &server->smb_ses_list) {
++ ses = list_entry(tmp2, struct cifsSesInfo,
++ smb_ses_list);
++ if ((ses->serverDomain == NULL) ||
++ (ses->serverOS == NULL) ||
++ (ses->serverNOS == NULL)) {
++ seq_printf(m, "\nentry for %s not fully "
++ "displayed\n\t", ses->serverName);
++ } else {
++ seq_printf(m,
+ "\n%d) Name: %s Domain: %s Mounts: %d OS:"
+ " %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB"
+ " session status: %d\t",
+ i, ses->serverName, ses->serverDomain,
+- atomic_read(&ses->inUse),
+- ses->serverOS, ses->serverNOS,
++ ses->ses_count, ses->serverOS, ses->serverNOS,
+ ses->capabilities, ses->status);
+- }
+- if (ses->server) {
++ }
+ seq_printf(m, "TCP status: %d\n\tLocal Users To "
+- "Server: %d SecMode: 0x%x Req On Wire: %d",
+- ses->server->tcpStatus,
+- ses->server->srv_count,
+- ses->server->secMode,
+- atomic_read(&ses->server->inFlight));
++ "Server: %d SecMode: 0x%x Req On Wire: %d",
++ server->tcpStatus, server->srv_count,
++ server->secMode,
++ atomic_read(&server->inFlight));
+
+ #ifdef CONFIG_CIFS_STATS2
+ seq_printf(m, " In Send: %d In MaxReq Wait: %d",
+- atomic_read(&ses->server->inSend),
+- atomic_read(&ses->server->num_waiters));
++ atomic_read(&server->inSend),
++ atomic_read(&server->num_waiters));
+ #endif
+
+ seq_puts(m, "\nMIDs:\n");
+
+ spin_lock(&GlobalMid_Lock);
+- list_for_each(tmp1, &ses->server->pending_mid_q) {
+- mid_entry = list_entry(tmp1, struct
++ list_for_each(tmp3, &server->pending_mid_q) {
++ mid_entry = list_entry(tmp3, struct
+ mid_q_entry,
+ qhead);
+ seq_printf(m, "State: %d com: %d pid:"
+@@ -171,9 +173,8 @@ static int cifs_debug_data_proc_show(str
+ }
+ spin_unlock(&GlobalMid_Lock);
+ }
+-
+ }
+- read_unlock(&GlobalSMBSeslock);
++ read_unlock(&cifs_tcp_ses_lock);
+ seq_putc(m, '\n');
+
+ seq_puts(m, "Shares:");
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -985,24 +985,24 @@ static int cifs_oplock_thread(void *dumm
+ static int cifs_dnotify_thread(void *dummyarg)
+ {
+ struct list_head *tmp;
+- struct cifsSesInfo *ses;
++ struct TCP_Server_Info *server;
+
+ do {
+ if (try_to_freeze())
+ continue;
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(15*HZ);
+- read_lock(&GlobalSMBSeslock);
+ /* check if any stuck requests that need
+ to be woken up and wakeq so the
+ thread can wake up and error out */
+- list_for_each(tmp, &GlobalSMBSessionList) {
+- ses = list_entry(tmp, struct cifsSesInfo,
+- cifsSessionList);
+- if (ses->server && atomic_read(&ses->server->inFlight))
+- wake_up_all(&ses->server->response_q);
++ read_lock(&cifs_tcp_ses_lock);
++ list_for_each(tmp, &cifs_tcp_ses_list) {
++ server = list_entry(tmp, struct TCP_Server_Info,
++ tcp_ses_list);
++ if (atomic_read(&server->inFlight))
++ wake_up_all(&server->response_q);
+ }
+- read_unlock(&GlobalSMBSeslock);
++ read_unlock(&cifs_tcp_ses_lock);
+ } while (!kthread_should_stop());
+
+ return 0;
+@@ -1014,7 +1014,6 @@ init_cifs(void)
+ int rc = 0;
+ cifs_proc_init();
+ 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);
+ #ifdef CONFIG_CIFS_EXPERIMENTAL
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -195,14 +195,14 @@ struct cifsUidInfo {
+ * Session structure. One of these for each uid session with a particular host
+ */
+ struct cifsSesInfo {
+- struct list_head cifsSessionList;
++ struct list_head smb_ses_list;
+ struct list_head tcon_list;
+ struct semaphore sesSem;
+ #if 0
+ struct cifsUidInfo *uidInfo; /* pointer to user info */
+ #endif
+ struct TCP_Server_Info *server; /* pointer to server info */
+- atomic_t inUse; /* # of mounts (tree connections) on this ses */
++ int ses_count; /* reference counter */
+ enum statusEnum status;
+ unsigned overrideSecFlg; /* if non-zero override global sec flags */
+ __u16 ipc_tid; /* special tid for connection to IPC share */
+@@ -600,8 +600,6 @@ GLOBAL_EXTERN struct list_head cifs_tcp
+
+ /* 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 */
+
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -102,7 +102,6 @@ extern void acl_to_uid_mode(struct inode
+ 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 *);
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -799,20 +799,16 @@ CIFSSMBLogoff(const int xid, struct cifs
+ int rc = 0;
+
+ cFYI(1, ("In SMBLogoff for session disconnect"));
+- if (ses)
+- down(&ses->sesSem);
+- else
+- return -EIO;
+-
+- atomic_dec(&ses->inUse);
+- if (atomic_read(&ses->inUse) > 0) {
+- up(&ses->sesSem);
+- return -EBUSY;
+- }
+
+- if (ses->server == NULL)
++ /*
++ * BB: do we need to check validity of ses and server? They should
++ * always be valid since we have an active reference. If not, that
++ * should probably be a BUG()
++ */
++ if (!ses || !ses->server)
+ return -EIO;
+
++ down(&ses->sesSem);
+ if (ses->need_reconnect)
+ goto session_already_dead; /* no need to send SMBlogoff if uid
+ already closed due to reconnect */
+@@ -833,10 +829,6 @@ CIFSSMBLogoff(const int xid, struct cifs
+ pSMB->AndXCommand = 0xFF;
+ rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
+ session_already_dead:
+- if (ses->server) {
+- cifs_put_tcp_session(ses->server);
+- rc = 0;
+- }
+ up(&ses->sesSem);
+
+ /* if session dead then we do not need to do ulogoff,
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -142,23 +142,18 @@ cifs_reconnect(struct TCP_Server_Info *s
+
+ /* before reconnecting the tcp session, mark the smb session (uid)
+ and the tid bad so they are not used until reconnected */
+- read_lock(&GlobalSMBSeslock);
+- list_for_each(tmp, &GlobalSMBSessionList) {
+- ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList);
+- if (ses->server) {
+- if (ses->server == server) {
+- ses->need_reconnect = true;
+- ses->ipc_tid = 0;
+- }
+- }
+- /* else tcp and smb sessions need reconnection */
++ read_lock(&cifs_tcp_ses_lock);
++ list_for_each(tmp, &server->smb_ses_list) {
++ ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
++ ses->need_reconnect = true;
++ ses->ipc_tid = 0;
+ }
++ read_unlock(&cifs_tcp_ses_lock);
+ list_for_each(tmp, &GlobalTreeConnectionList) {
+ tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
+ if ((tcon->ses) && (tcon->ses->server == server))
+ tcon->need_reconnect = true;
+ }
+- read_unlock(&GlobalSMBSeslock);
+ /* do not want to be sending data on a socket we are freeing */
+ down(&server->tcpSem);
+ if (server->ssocket) {
+@@ -702,29 +697,29 @@ multi_t2_fnd:
+ if (smallbuf) /* no sense logging a debug message if NULL */
+ cifs_small_buf_release(smallbuf);
+
+- read_lock(&GlobalSMBSeslock);
++ /*
++ * BB: we shouldn't have to do any of this. It shouldn't be
++ * possible to exit from the thread with active SMB sessions
++ */
++ read_lock(&cifs_tcp_ses_lock);
+ if (list_empty(&server->pending_mid_q)) {
+ /* loop through server session structures attached to this and
+ mark them dead */
+- list_for_each(tmp, &GlobalSMBSessionList) {
+- ses =
+- list_entry(tmp, struct cifsSesInfo,
+- cifsSessionList);
+- if (ses->server == server) {
+- ses->status = CifsExiting;
+- ses->server = NULL;
+- }
++ list_for_each(tmp, &server->smb_ses_list) {
++ ses = list_entry(tmp, struct cifsSesInfo,
++ smb_ses_list);
++ ses->status = CifsExiting;
++ ses->server = NULL;
+ }
+- read_unlock(&GlobalSMBSeslock);
++ read_unlock(&cifs_tcp_ses_lock);
+ } else {
+ /* although we can not zero the server struct pointer yet,
+ since there are active requests which may depnd on them,
+ mark the corresponding SMB sessions as exiting too */
+- list_for_each(tmp, &GlobalSMBSessionList) {
++ list_for_each(tmp, &server->smb_ses_list) {
+ ses = list_entry(tmp, struct cifsSesInfo,
+- cifsSessionList);
+- if (ses->server == server)
+- ses->status = CifsExiting;
++ smb_ses_list);
++ ses->status = CifsExiting;
+ }
+
+ spin_lock(&GlobalMid_Lock);
+@@ -739,7 +734,7 @@ multi_t2_fnd:
+ }
+ }
+ spin_unlock(&GlobalMid_Lock);
+- read_unlock(&GlobalSMBSeslock);
++ read_unlock(&cifs_tcp_ses_lock);
+ /* 1/8th of sec is more than enough time for them to exit */
+ msleep(125);
+ }
+@@ -761,14 +756,13 @@ multi_t2_fnd:
+ if there are any pointing to this (e.g
+ if a crazy root user tried to kill cifsd
+ kernel thread explicitly this might happen) */
+- write_lock(&GlobalSMBSeslock);
+- list_for_each(tmp, &GlobalSMBSessionList) {
+- ses = list_entry(tmp, struct cifsSesInfo,
+- cifsSessionList);
+- if (ses->server == server)
+- ses->server = NULL;
++ /* BB: This shouldn't be necessary, see above */
++ read_lock(&cifs_tcp_ses_lock);
++ list_for_each(tmp, &server->smb_ses_list) {
++ ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
++ ses->server = NULL;
+ }
+- write_unlock(&GlobalSMBSeslock);
++ read_unlock(&cifs_tcp_ses_lock);
+
+ kfree(server->hostname);
+ kfree(server);
+@@ -1390,7 +1384,7 @@ cifs_find_tcp_session(struct sockaddr *a
+ return NULL;
+ }
+
+-void
++static void
+ cifs_put_tcp_session(struct TCP_Server_Info *server)
+ {
+ struct task_struct *task;
+@@ -1413,6 +1407,50 @@ cifs_put_tcp_session(struct TCP_Server_I
+ force_sig(SIGKILL, task);
+ }
+
++static struct cifsSesInfo *
++cifs_find_smb_ses(struct TCP_Server_Info *server, char *username)
++{
++ struct list_head *tmp;
++ struct cifsSesInfo *ses;
++
++ write_lock(&cifs_tcp_ses_lock);
++ list_for_each(tmp, &server->smb_ses_list) {
++ ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
++ if (strncmp(ses->userName, username, MAX_USERNAME_SIZE))
++ continue;
++
++ ++ses->ses_count;
++ write_unlock(&cifs_tcp_ses_lock);
++ return ses;
++ }
++ write_unlock(&cifs_tcp_ses_lock);
++ return NULL;
++}
++
++static void
++cifs_put_smb_ses(struct cifsSesInfo *ses)
++{
++ int xid;
++ struct TCP_Server_Info *server = ses->server;
++
++ write_lock(&cifs_tcp_ses_lock);
++ if (--ses->ses_count > 0) {
++ write_unlock(&cifs_tcp_ses_lock);
++ return;
++ }
++
++ list_del_init(&ses->smb_ses_list);
++ write_unlock(&cifs_tcp_ses_lock);
++
++ if (ses->status == CifsGood) {
++ xid = GetXid();
++ CIFSSMBLogoff(xid, ses);
++ _FreeXid(xid);
++ }
++ sesInfoFree(ses);
++ cifs_put_tcp_session(server);
++}
++
+ int
+ get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
+ const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
+@@ -1945,7 +1983,6 @@ cifs_mount(struct super_block *sb, struc
+ struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
+ struct smb_vol volume_info;
+ struct cifsSesInfo *pSesInfo = NULL;
+- struct cifsSesInfo *existingCifsSes = NULL;
+ struct cifsTconInfo *tcon = NULL;
+ struct TCP_Server_Info *srvTcp = NULL;
+
+@@ -2099,6 +2136,7 @@ cifs_mount(struct super_block *sb, struc
+ volume_info.target_rfc1001_name, 16);
+ srvTcp->sequence_number = 0;
+ INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
++ INIT_LIST_HEAD(&srvTcp->smb_ses_list);
+ ++srvTcp->srv_count;
+ write_lock(&cifs_tcp_ses_lock);
+ list_add(&srvTcp->tcp_ses_list,
+@@ -2107,10 +2145,16 @@ cifs_mount(struct super_block *sb, struc
+ }
+ }
+
+- if (existingCifsSes) {
+- pSesInfo = existingCifsSes;
++ pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username);
++ if (pSesInfo) {
+ cFYI(1, ("Existing smb sess found (status=%d)",
+ pSesInfo->status));
++ /*
++ * The existing SMB session already has a reference to srvTcp,
++ * so we can put back the extra one we got before
++ */
++ cifs_put_tcp_session(srvTcp);
++
+ down(&pSesInfo->sesSem);
+ if (pSesInfo->need_reconnect) {
+ cFYI(1, ("Session needs reconnect"));
+@@ -2121,41 +2165,44 @@ cifs_mount(struct super_block *sb, struc
+ } else if (!rc) {
+ cFYI(1, ("Existing smb sess not found"));
+ pSesInfo = sesInfoAlloc();
+- if (pSesInfo == NULL)
++ if (pSesInfo == NULL) {
+ rc = -ENOMEM;
+- else {
+- pSesInfo->server = srvTcp;
+- sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
+- NIPQUAD(sin_server->sin_addr.s_addr));
++ goto mount_fail_check;
+ }
+
+- if (!rc) {
+- /* volume_info.password freed at unmount */
+- if (volume_info.password) {
+- pSesInfo->password = volume_info.password;
+- /* set to NULL to prevent freeing on exit */
+- volume_info.password = NULL;
+- }
+- if (volume_info.username)
+- strncpy(pSesInfo->userName,
+- volume_info.username,
+- MAX_USERNAME_SIZE);
+- if (volume_info.domainname) {
+- int len = strlen(volume_info.domainname);
+- pSesInfo->domainName =
+- kmalloc(len + 1, GFP_KERNEL);
+- if (pSesInfo->domainName)
+- strcpy(pSesInfo->domainName,
+- volume_info.domainname);
+- }
+- pSesInfo->linux_uid = volume_info.linux_uid;
+- pSesInfo->overrideSecFlg = volume_info.secFlg;
+- down(&pSesInfo->sesSem);
+- /* BB FIXME need to pass vol->secFlgs BB */
+- rc = cifs_setup_session(xid, pSesInfo,
+- cifs_sb->local_nls);
+- up(&pSesInfo->sesSem);
++ /* new SMB session uses our srvTcp ref */
++ pSesInfo->server = srvTcp;
++ sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
++ NIPQUAD(sin_server->sin_addr.s_addr));
++
++ write_lock(&cifs_tcp_ses_lock);
++ list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list);
++ write_unlock(&cifs_tcp_ses_lock);
++
++ /* volume_info.password freed at unmount */
++ if (volume_info.password) {
++ pSesInfo->password = volume_info.password;
++ /* set to NULL to prevent freeing on exit */
++ volume_info.password = NULL;
++ }
++ if (volume_info.username)
++ strncpy(pSesInfo->userName, volume_info.username,
++ MAX_USERNAME_SIZE);
++ if (volume_info.domainname) {
++ int len = strlen(volume_info.domainname);
++ pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL);
++ if (pSesInfo->domainName)
++ strcpy(pSesInfo->domainName,
++ volume_info.domainname);
+ }
++ pSesInfo->linux_uid = volume_info.linux_uid;
++ pSesInfo->overrideSecFlg = volume_info.secFlg;
++ down(&pSesInfo->sesSem);
++
++ /* BB FIXME need to pass vol->secFlgs BB */
++ rc = cifs_setup_session(xid, pSesInfo,
++ cifs_sb->local_nls);
++ up(&pSesInfo->sesSem);
+ }
+
+ /* search for existing tcon to this server share */
+@@ -2190,11 +2237,9 @@ cifs_mount(struct super_block *sb, struc
+ tcon, cifs_sb->local_nls);
+ cFYI(1, ("CIFS Tcon rc = %d", rc));
+ }
+- if (!rc) {
+- atomic_inc(&pSesInfo->inUse);
+- tcon->seal = volume_info.seal;
+- } else
++ if (rc)
+ goto mount_fail_check;
++ tcon->seal = volume_info.seal;
+ }
+
+ /* we can have only one retry value for a connection
+@@ -2214,7 +2259,7 @@ cifs_mount(struct super_block *sb, struc
+ /* BB FIXME fix time_gran to be larger for LANMAN sessions */
+ sb->s_time_gran = 100;
+
+-/* on error free sesinfo and tcon struct if needed */
++ /* on error free sesinfo and tcon struct if needed */
+ mount_fail_check:
+ if (rc) {
+ /* If find_unc succeeded then rc == 0 so we can not end */
+@@ -2222,21 +2267,11 @@ mount_fail_check:
+ if (tcon)
+ tconInfoFree(tcon);
+
+- if (existingCifsSes == NULL) {
+- if (pSesInfo) {
+- if ((pSesInfo->server) &&
+- (pSesInfo->status == CifsGood))
+- CIFSSMBLogoff(xid, pSesInfo);
+- else {
+- cFYI(1, ("No session or bad tcon"));
+- }
+- if (pSesInfo->server)
+- cifs_put_tcp_session(
+- pSesInfo->server);
+- sesInfoFree(pSesInfo);
+- /* pSesInfo = NULL; */
+- }
+- }
++ /* should also end up putting our tcp session ref if needed */
++ if (pSesInfo)
++ cifs_put_smb_ses(pSesInfo);
++ else
++ cifs_put_tcp_session(srvTcp);
+ } else {
+ atomic_inc(&tcon->useCount);
+ cifs_sb->tcon = tcon;
+@@ -3532,17 +3567,7 @@ cifs_umount(struct super_block *sb, stru
+ }
+ DeleteTconOplockQEntries(cifs_sb->tcon);
+ tconInfoFree(cifs_sb->tcon);
+- if ((ses) && (ses->server)) {
+- /* save off task so we do not refer to ses later */
+- cifsd_task = ses->server->tsk;
+- cFYI(1, ("About to do SMBLogoff "));
+- rc = CIFSSMBLogoff(xid, ses);
+- if (rc == -EBUSY) {
+- FreeXid(xid);
+- return 0;
+- }
+- } else
+- cFYI(1, ("No session or bad tcon"));
++ cifs_put_smb_ses(ses);
+ }
+
+ cifs_sb->tcon = NULL;
+@@ -3550,8 +3575,6 @@ cifs_umount(struct super_block *sb, stru
+ cifs_sb->prepathlen = 0;
+ cifs_sb->prepath = NULL;
+ kfree(tmp);
+- if (ses)
+- sesInfoFree(ses);
+
+ FreeXid(xid);
+ return rc;
+--- a/fs/cifs/misc.c
++++ b/fs/cifs/misc.c
+@@ -75,12 +75,11 @@ sesInfoAlloc(void)
+
+ ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL);
+ if (ret_buf) {
+- write_lock(&GlobalSMBSeslock);
+ atomic_inc(&sesInfoAllocCount);
+ ret_buf->status = CifsNew;
+- list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList);
++ ++ret_buf->ses_count;
++ INIT_LIST_HEAD(&ret_buf->smb_ses_list);
+ init_MUTEX(&ret_buf->sesSem);
+- write_unlock(&GlobalSMBSeslock);
+ }
+ return ret_buf;
+ }
+@@ -93,10 +92,7 @@ sesInfoFree(struct cifsSesInfo *buf_to_f
+ return;
+ }
+
+- write_lock(&GlobalSMBSeslock);
+ atomic_dec(&sesInfoAllocCount);
+- list_del(&buf_to_free->cifsSessionList);
+- write_unlock(&GlobalSMBSeslock);
+ kfree(buf_to_free->serverOS);
+ kfree(buf_to_free->serverDomain);
+ kfree(buf_to_free->serverNOS);
+@@ -354,9 +350,9 @@ header_assemble(struct smb_hdr *buffer,
+ if (current->fsuid != treeCon->ses->linux_uid) {
+ cFYI(1, ("Multiuser mode and UID "
+ "did not match tcon uid"));
+- read_lock(&GlobalSMBSeslock);
+- list_for_each(temp_item, &GlobalSMBSessionList) {
+- ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList);
++ read_lock(&cifs_tcp_ses_lock);
++ list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
++ ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
+ if (ses->linux_uid == current->fsuid) {
+ if (ses->server == treeCon->ses->server) {
+ cFYI(1, ("found matching uid substitute right smb_uid"));
+@@ -368,7 +364,7 @@ header_assemble(struct smb_hdr *buffer,
+ }
+ }
+ }
+- read_unlock(&GlobalSMBSeslock);
++ read_unlock(&cifs_tcp_ses_lock);
+ }
+ }
+ }
--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:41:15 2008
+From: Jeff Layton <jlayton@redhat.com>
+Date: Fri, 21 Nov 2008 14:23:55 +0530
+Subject: cifs: reinstate sharing of tree connections
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>, Jeff Layton <jlayton@redhat.com>
+Message-ID: <49267723.7060701@suse.de>
+
+From: Jeff Layton <jlayton@redhat.com>
+
+commit f1987b44f642e96176adc88b7ce23a1d74806f89 upstream
+
+Use a similar approach to the SMB session sharing. Add a list of tcons
+attached to each SMB session. Move the refcount to non-atomic. Protect
+all of the above with the cifs_tcp_ses_lock. Add functions to
+properly find and put references to the tcons.
+
+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 | 236 +++++++++++++++++++++++++++------------------------
+ fs/cifs/cifsfs.c | 8 -
+ fs/cifs/cifsglob.h | 13 +-
+ fs/cifs/cifssmb.c | 43 ++-------
+ fs/cifs/connect.c | 94 +++++++++++++-------
+ fs/cifs/misc.c | 74 +++++++--------
+ 6 files changed, 249 insertions(+), 219 deletions(-)
+
+--- a/fs/cifs/cifs_debug.c
++++ b/fs/cifs/cifs_debug.c
+@@ -107,12 +107,13 @@ void cifs_dump_mids(struct TCP_Server_In
+ #ifdef CONFIG_PROC_FS
+ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
+ {
+- struct list_head *tmp, *tmp2, *tmp3;
++ struct list_head *tmp1, *tmp2, *tmp3;
+ struct mid_q_entry *mid_entry;
+ struct TCP_Server_Info *server;
+ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+- int i;
++ int i, j;
++ __u32 dev_type;
+
+ seq_puts(m,
+ "Display Internal CIFS Data Structures for Debugging\n"
+@@ -123,8 +124,8 @@ static int cifs_debug_data_proc_show(str
+
+ i = 0;
+ read_lock(&cifs_tcp_ses_lock);
+- list_for_each(tmp, &cifs_tcp_ses_list) {
+- server = list_entry(tmp, struct TCP_Server_Info,
++ list_for_each(tmp1, &cifs_tcp_ses_list) {
++ server = list_entry(tmp1, struct TCP_Server_Info,
+ tcp_ses_list);
+ i++;
+ list_for_each(tmp2, &server->smb_ses_list) {
+@@ -133,12 +134,12 @@ static int cifs_debug_data_proc_show(str
+ if ((ses->serverDomain == NULL) ||
+ (ses->serverOS == NULL) ||
+ (ses->serverNOS == NULL)) {
+- seq_printf(m, "\nentry for %s not fully "
+- "displayed\n\t", ses->serverName);
++ seq_printf(m, "\n%d) entry for %s not fully "
++ "displayed\n\t", i, ses->serverName);
+ } else {
+ seq_printf(m,
+- "\n%d) Name: %s Domain: %s Mounts: %d OS:"
+- " %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB"
++ "\n%d) Name: %s Domain: %s Uses: %d OS:"
++ " %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB"
+ " session status: %d\t",
+ i, ses->serverName, ses->serverDomain,
+ ses->ses_count, ses->serverOS, ses->serverNOS,
+@@ -156,14 +157,44 @@ static int cifs_debug_data_proc_show(str
+ atomic_read(&server->num_waiters));
+ #endif
+
+- seq_puts(m, "\nMIDs:\n");
++ seq_puts(m, "\n\tShares:");
++ j = 0;
++ list_for_each(tmp3, &ses->tcon_list) {
++ tcon = list_entry(tmp3, struct cifsTconInfo,
++ tcon_list);
++ ++j;
++ dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
++ seq_printf(m, "\n\t%d) %s Mounts: %d ", j,
++ tcon->treeName, tcon->tc_count);
++ if (tcon->nativeFileSystem) {
++ seq_printf(m, "Type: %s ",
++ tcon->nativeFileSystem);
++ }
++ seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
++ "\nPathComponentMax: %d Status: 0x%d",
++ le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
++ le32_to_cpu(tcon->fsAttrInfo.Attributes),
++ le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
++ tcon->tidStatus);
++ if (dev_type == FILE_DEVICE_DISK)
++ seq_puts(m, " type: DISK ");
++ else if (dev_type == FILE_DEVICE_CD_ROM)
++ seq_puts(m, " type: CDROM ");
++ else
++ seq_printf(m, " type: %d ", dev_type);
++
++ if (tcon->need_reconnect)
++ seq_puts(m, "\tDISCONNECTED ");
++ seq_putc(m, '\n');
++ }
++
++ seq_puts(m, "\n\tMIDs:\n");
+
+ spin_lock(&GlobalMid_Lock);
+ list_for_each(tmp3, &server->pending_mid_q) {
+- mid_entry = list_entry(tmp3, struct
+- mid_q_entry,
++ mid_entry = list_entry(tmp3, struct mid_q_entry,
+ qhead);
+- seq_printf(m, "State: %d com: %d pid:"
++ seq_printf(m, "\tState: %d com: %d pid:"
+ " %d tsk: %p mid %d\n",
+ mid_entry->midState,
+ (int)mid_entry->command,
+@@ -177,41 +208,6 @@ static int cifs_debug_data_proc_show(str
+ read_unlock(&cifs_tcp_ses_lock);
+ seq_putc(m, '\n');
+
+- seq_puts(m, "Shares:");
+-
+- i = 0;
+- read_lock(&GlobalSMBSeslock);
+- list_for_each(tmp, &GlobalTreeConnectionList) {
+- __u32 dev_type;
+- i++;
+- tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
+- dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
+- seq_printf(m, "\n%d) %s Uses: %d ", i,
+- tcon->treeName, atomic_read(&tcon->useCount));
+- if (tcon->nativeFileSystem) {
+- seq_printf(m, "Type: %s ",
+- tcon->nativeFileSystem);
+- }
+- seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
+- "\nPathComponentMax: %d Status: %d",
+- le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
+- le32_to_cpu(tcon->fsAttrInfo.Attributes),
+- le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
+- tcon->tidStatus);
+- if (dev_type == FILE_DEVICE_DISK)
+- seq_puts(m, " type: DISK ");
+- else if (dev_type == FILE_DEVICE_CD_ROM)
+- seq_puts(m, " type: CDROM ");
+- else
+- seq_printf(m, " type: %d ", dev_type);
+-
+- if (tcon->need_reconnect)
+- seq_puts(m, "\tDISCONNECTED ");
+- }
+- read_unlock(&GlobalSMBSeslock);
+-
+- seq_putc(m, '\n');
+-
+ /* BB add code to dump additional info such as TCP session info now */
+ return 0;
+ }
+@@ -235,7 +231,9 @@ static ssize_t cifs_stats_proc_write(str
+ {
+ char c;
+ int rc;
+- struct list_head *tmp;
++ struct list_head *tmp1, *tmp2, *tmp3;
++ struct TCP_Server_Info *server;
++ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+
+ rc = get_user(c, buffer);
+@@ -243,33 +241,42 @@ static ssize_t cifs_stats_proc_write(str
+ return rc;
+
+ if (c == '1' || c == 'y' || c == 'Y' || c == '0') {
+- read_lock(&GlobalSMBSeslock);
+ #ifdef CONFIG_CIFS_STATS2
+ atomic_set(&totBufAllocCount, 0);
+ atomic_set(&totSmBufAllocCount, 0);
+ #endif /* CONFIG_CIFS_STATS2 */
+- list_for_each(tmp, &GlobalTreeConnectionList) {
+- tcon = list_entry(tmp, struct cifsTconInfo,
+- cifsConnectionList);
+- atomic_set(&tcon->num_smbs_sent, 0);
+- atomic_set(&tcon->num_writes, 0);
+- atomic_set(&tcon->num_reads, 0);
+- atomic_set(&tcon->num_oplock_brks, 0);
+- atomic_set(&tcon->num_opens, 0);
+- atomic_set(&tcon->num_closes, 0);
+- atomic_set(&tcon->num_deletes, 0);
+- atomic_set(&tcon->num_mkdirs, 0);
+- atomic_set(&tcon->num_rmdirs, 0);
+- atomic_set(&tcon->num_renames, 0);
+- atomic_set(&tcon->num_t2renames, 0);
+- atomic_set(&tcon->num_ffirst, 0);
+- atomic_set(&tcon->num_fnext, 0);
+- atomic_set(&tcon->num_fclose, 0);
+- atomic_set(&tcon->num_hardlinks, 0);
+- atomic_set(&tcon->num_symlinks, 0);
+- atomic_set(&tcon->num_locks, 0);
++ read_lock(&cifs_tcp_ses_lock);
++ list_for_each(tmp1, &cifs_tcp_ses_list) {
++ server = list_entry(tmp1, struct TCP_Server_Info,
++ tcp_ses_list);
++ list_for_each(tmp2, &server->smb_session_list) {
++ ses = list_entry(tmp2, struct cifsSesInfo,
++ smb_session_list);
++ list_for_each(tmp3, &ses->tcon_list) {
++ tcon = list_entry(tmp3,
++ struct cifsTconInfo,
++ tcon_list);
++ atomic_set(&tcon->num_smbs_sent, 0);
++ atomic_set(&tcon->num_writes, 0);
++ atomic_set(&tcon->num_reads, 0);
++ atomic_set(&tcon->num_oplock_brks, 0);
++ atomic_set(&tcon->num_opens, 0);
++ atomic_set(&tcon->num_closes, 0);
++ atomic_set(&tcon->num_deletes, 0);
++ atomic_set(&tcon->num_mkdirs, 0);
++ atomic_set(&tcon->num_rmdirs, 0);
++ atomic_set(&tcon->num_renames, 0);
++ atomic_set(&tcon->num_t2renames, 0);
++ atomic_set(&tcon->num_ffirst, 0);
++ atomic_set(&tcon->num_fnext, 0);
++ atomic_set(&tcon->num_fclose, 0);
++ atomic_set(&tcon->num_hardlinks, 0);
++ atomic_set(&tcon->num_symlinks, 0);
++ atomic_set(&tcon->num_locks, 0);
++ }
++ }
+ }
+- read_unlock(&GlobalSMBSeslock);
++ read_unlock(&cifs_tcp_ses_lock);
+ }
+
+ return count;
+@@ -278,7 +285,9 @@ static ssize_t cifs_stats_proc_write(str
+ static int cifs_stats_proc_show(struct seq_file *m, void *v)
+ {
+ int i;
+- struct list_head *tmp;
++ struct list_head *tmp1, *tmp2, *tmp3;
++ struct TCP_Server_Info *server;
++ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+
+ seq_printf(m,
+@@ -307,44 +316,55 @@ static int cifs_stats_proc_show(struct s
+ GlobalCurrentXid, GlobalMaxActiveXid);
+
+ i = 0;
+- read_lock(&GlobalSMBSeslock);
+- list_for_each(tmp, &GlobalTreeConnectionList) {
+- i++;
+- tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
+- seq_printf(m, "\n%d) %s", i, tcon->treeName);
+- if (tcon->need_reconnect)
+- seq_puts(m, "\tDISCONNECTED ");
+- seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
+- atomic_read(&tcon->num_smbs_sent),
+- atomic_read(&tcon->num_oplock_brks));
+- seq_printf(m, "\nReads: %d Bytes: %lld",
+- atomic_read(&tcon->num_reads),
+- (long long)(tcon->bytes_read));
+- seq_printf(m, "\nWrites: %d Bytes: %lld",
+- atomic_read(&tcon->num_writes),
+- (long long)(tcon->bytes_written));
+- seq_printf(m,
+- "\nLocks: %d HardLinks: %d Symlinks: %d",
+- atomic_read(&tcon->num_locks),
+- atomic_read(&tcon->num_hardlinks),
+- atomic_read(&tcon->num_symlinks));
+-
+- seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d",
+- atomic_read(&tcon->num_opens),
+- atomic_read(&tcon->num_closes),
+- atomic_read(&tcon->num_deletes));
+- seq_printf(m, "\nMkdirs: %d Rmdirs: %d",
+- atomic_read(&tcon->num_mkdirs),
+- atomic_read(&tcon->num_rmdirs));
+- seq_printf(m, "\nRenames: %d T2 Renames %d",
+- atomic_read(&tcon->num_renames),
+- atomic_read(&tcon->num_t2renames));
+- seq_printf(m, "\nFindFirst: %d FNext %d FClose %d",
+- atomic_read(&tcon->num_ffirst),
+- atomic_read(&tcon->num_fnext),
+- atomic_read(&tcon->num_fclose));
++ read_lock(&cifs_tcp_ses_lock);
++ list_for_each(tmp1, &cifs_tcp_ses_list) {
++ server = list_entry(tmp1, struct TCP_Server_Info,
++ tcp_ses_list);
++ list_for_each(tmp2, &server->smb_ses_list) {
++ ses = list_entry(tmp2, struct cifsSesInfo,
++ smb_ses_list);
++ list_for_each(tmp3, &ses->tcon_list) {
++ tcon = list_entry(tmp3,
++ struct cifsTconInfo,
++ tcon_list);
++ i++;
++ seq_printf(m, "\n%d) %s", i, tcon->treeName);
++ if (tcon->need_reconnect)
++ seq_puts(m, "\tDISCONNECTED ");
++ seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
++ atomic_read(&tcon->num_smbs_sent),
++ atomic_read(&tcon->num_oplock_brks));
++ seq_printf(m, "\nReads: %d Bytes: %lld",
++ atomic_read(&tcon->num_reads),
++ (long long)(tcon->bytes_read));
++ seq_printf(m, "\nWrites: %d Bytes: %lld",
++ atomic_read(&tcon->num_writes),
++ (long long)(tcon->bytes_written));
++ seq_printf(m, "\nLocks: %d HardLinks: %d "
++ "Symlinks: %d",
++ atomic_read(&tcon->num_locks),
++ atomic_read(&tcon->num_hardlinks),
++ atomic_read(&tcon->num_symlinks));
++ seq_printf(m, "\nOpens: %d Closes: %d"
++ "Deletes: %d",
++ atomic_read(&tcon->num_opens),
++ atomic_read(&tcon->num_closes),
++ atomic_read(&tcon->num_deletes));
++ seq_printf(m, "\nMkdirs: %d Rmdirs: %d",
++ atomic_read(&tcon->num_mkdirs),
++ atomic_read(&tcon->num_rmdirs));
++ seq_printf(m, "\nRenames: %d T2 Renames %d",
++ atomic_read(&tcon->num_renames),
++ atomic_read(&tcon->num_t2renames));
++ seq_printf(m, "\nFindFirst: %d FNext %d "
++ "FClose %d",
++ atomic_read(&tcon->num_ffirst),
++ atomic_read(&tcon->num_fnext),
++ atomic_read(&tcon->num_fclose));
++ }
++ }
+ }
+- read_unlock(&GlobalSMBSeslock);
++ read_unlock(&cifs_tcp_ses_lock);
+
+ seq_putc(m, '\n');
+ return 0;
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -510,10 +510,11 @@ static void cifs_umount_begin(struct sup
+ tcon = cifs_sb->tcon;
+ if (tcon == NULL)
+ return;
+- down(&tcon->tconSem);
+- if (atomic_read(&tcon->useCount) == 1)
++
++ read_lock(&cifs_tcp_ses_lock);
++ if (tcon->tc_count == 1)
+ tcon->tidStatus = CifsExiting;
+- up(&tcon->tconSem);
++ read_unlock(&cifs_tcp_ses_lock);
+
+ /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
+ /* cancel_notify_requests(tcon); */
+@@ -1014,7 +1015,6 @@ init_cifs(void)
+ int rc = 0;
+ cifs_proc_init();
+ INIT_LIST_HEAD(&cifs_tcp_ses_list);
+- INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */
+ INIT_LIST_HEAD(&GlobalOplock_Q);
+ #ifdef CONFIG_CIFS_EXPERIMENTAL
+ INIT_LIST_HEAD(&GlobalDnotifyReqList);
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -233,16 +233,15 @@ struct cifsSesInfo {
+ * session
+ */
+ struct cifsTconInfo {
+- struct list_head cifsConnectionList;
++ struct list_head tcon_list;
++ int tc_count;
+ struct list_head openFileList;
+- struct semaphore tconSem;
+ struct cifsSesInfo *ses; /* pointer to session associated with */
+ char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
+ char *nativeFileSystem;
+ __u16 tid; /* The 2 byte tree id */
+ __u16 Flags; /* optional support bits */
+ enum statusEnum tidStatus;
+- atomic_t useCount; /* how many explicit/implicit mounts to share */
+ #ifdef CONFIG_CIFS_STATS
+ atomic_t num_smbs_sent;
+ atomic_t num_writes;
+@@ -598,9 +597,13 @@ require use of the stronger protocol */
+ */
+ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
+
+-/* protects cifs_tcp_ses_list and srv_count for each tcp session */
++/*
++ * This lock protects the cifs_tcp_ses_list, the list of smb sessions per
++ * tcp session, and the list of tcon's per smb session. It also protects
++ * the reference counters for the server, smb session, and tcon. Finally,
++ * changes to the tcon->tidStatus should be done while holding this lock.
++ */
+ GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
+-GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */
+ GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */
+
+ GLOBAL_EXTERN struct list_head GlobalOplock_Q;
+--- a/fs/cifs/cifssmb.c
++++ b/fs/cifs/cifssmb.c
+@@ -742,50 +742,31 @@ CIFSSMBTDis(const int xid, struct cifsTc
+ int rc = 0;
+
+ cFYI(1, ("In tree disconnect"));
+- /*
+- * If last user of the connection and
+- * connection alive - disconnect it
+- * If this is the last connection on the server session disconnect it
+- * (and inside session disconnect we should check if tcp socket needs
+- * to be freed and kernel thread woken up).
+- */
+- if (tcon)
+- down(&tcon->tconSem);
+- else
+- return -EIO;
+
+- atomic_dec(&tcon->useCount);
+- if (atomic_read(&tcon->useCount) > 0) {
+- up(&tcon->tconSem);
+- return -EBUSY;
+- }
++ /* BB: do we need to check this? These should never be NULL. */
++ if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
++ return -EIO;
+
+- /* No need to return error on this operation if tid invalidated and
+- closed on server already e.g. due to tcp session crashing */
+- if (tcon->need_reconnect) {
+- up(&tcon->tconSem);
++ /*
++ * No need to return error on this operation if tid invalidated and
++ * closed on server already e.g. due to tcp session crashing. Also,
++ * the tcon is no longer on the list, so no need to take lock before
++ * checking this.
++ */
++ if (tcon->need_reconnect)
+ return 0;
+- }
+
+- if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) {
+- up(&tcon->tconSem);
+- return -EIO;
+- }
+ rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
+ (void **)&smb_buffer);
+- if (rc) {
+- up(&tcon->tconSem);
++ if (rc)
+ return rc;
+- }
+
+ rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
+ if (rc)
+ cFYI(1, ("Tree disconnect failed %d", rc));
+
+- up(&tcon->tconSem);
+-
+ /* No need to return error on this operation if tid invalidated and
+- closed on server already e.g. due to tcp session crashing */
++ closed on server already e.g. due to tcp session crashing */
+ if (rc == -EAGAIN)
+ rc = 0;
+
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -122,7 +122,7 @@ static int
+ cifs_reconnect(struct TCP_Server_Info *server)
+ {
+ int rc = 0;
+- struct list_head *tmp;
++ struct list_head *tmp, *tmp2;
+ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
+ struct mid_q_entry *mid_entry;
+@@ -147,13 +147,12 @@ cifs_reconnect(struct TCP_Server_Info *s
+ ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
+ ses->need_reconnect = true;
+ ses->ipc_tid = 0;
+- }
+- read_unlock(&cifs_tcp_ses_lock);
+- list_for_each(tmp, &GlobalTreeConnectionList) {
+- tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
+- if ((tcon->ses) && (tcon->ses->server == server))
++ list_for_each(tmp2, &ses->tcon_list) {
++ tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list);
+ tcon->need_reconnect = true;
++ }
+ }
++ read_unlock(&cifs_tcp_ses_lock);
+ /* do not want to be sending data on a socket we are freeing */
+ down(&server->tcpSem);
+ if (server->ssocket) {
+@@ -1451,6 +1450,52 @@ cifs_put_smb_ses(struct cifsSesInfo *ses
+ cifs_put_tcp_session(server);
+ }
+
++static struct cifsTconInfo *
++cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
++{
++ struct list_head *tmp;
++ struct cifsTconInfo *tcon;
++
++ write_lock(&cifs_tcp_ses_lock);
++ list_for_each(tmp, &ses->tcon_list) {
++ tcon = list_entry(tmp, struct cifsTconInfo, tcon_list);
++ if (tcon->tidStatus == CifsExiting)
++ continue;
++ if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE))
++ continue;
++
++ ++tcon->tc_count;
++ write_unlock(&cifs_tcp_ses_lock);
++ return tcon;
++ }
++ write_unlock(&cifs_tcp_ses_lock);
++ return NULL;
++}
++
++static void
++cifs_put_tcon(struct cifsTconInfo *tcon)
++{
++ int xid;
++ struct cifsSesInfo *ses = tcon->ses;
++
++ write_lock(&cifs_tcp_ses_lock);
++ if (--tcon->tc_count > 0) {
++ write_unlock(&cifs_tcp_ses_lock);
++ return;
++ }
++
++ list_del_init(&tcon->tcon_list);
++ write_unlock(&cifs_tcp_ses_lock);
++
++ xid = GetXid();
++ CIFSSMBTDis(xid, tcon);
++ _FreeXid(xid);
++
++ DeleteTconOplockQEntries(tcon);
++ tconInfoFree(tcon);
++ cifs_put_smb_ses(ses);
++}
++
+ int
+ get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
+ const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
+@@ -2206,11 +2251,11 @@ cifs_mount(struct super_block *sb, struc
+ /* search for existing tcon to this server share */
+ if (!rc) {
+ setup_cifs_sb(&volume_info, cifs_sb);
++ tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
+ if (tcon) {
+ cFYI(1, ("Found match on UNC path"));
+- if (tcon->seal != volume_info.seal)
+- cERROR(1, ("transport encryption setting "
+- "conflicts with existing tid"));
++ /* existing tcon already has a reference */
++ cifs_put_smb_ses(pSesInfo);
+ } else {
+ tcon = tconInfoAlloc();
+ if (tcon == NULL) {
+@@ -2238,6 +2283,10 @@ cifs_mount(struct super_block *sb, struc
+ if (rc)
+ goto mount_fail_check;
+ tcon->seal = volume_info.seal;
++ tcon->ses = pSesInfo;
++ write_lock(&cifs_tcp_ses_lock);
++ list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
++ write_unlock(&cifs_tcp_ses_lock);
+ }
+
+ /* we can have only one retry value for a connection
+@@ -2263,18 +2312,14 @@ mount_fail_check:
+ /* If find_unc succeeded then rc == 0 so we can not end */
+ /* up accidently freeing someone elses tcon struct */
+ if (tcon)
+- tconInfoFree(tcon);
+-
+- /* should also end up putting our tcp session ref if needed */
+- if (pSesInfo)
++ cifs_put_tcon(tcon);
++ else if (pSesInfo)
+ cifs_put_smb_ses(pSesInfo);
+ else
+ cifs_put_tcp_session(srvTcp);
+ goto out;
+ }
+- atomic_inc(&tcon->useCount);
+ cifs_sb->tcon = tcon;
+- tcon->ses = pSesInfo;
+
+ /* do not care if following two calls succeed - informational */
+ if (!tcon->ipc) {
+@@ -3545,24 +3590,10 @@ int
+ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
+ {
+ int rc = 0;
+- int xid;
+- struct cifsSesInfo *ses = NULL;
+- struct task_struct *cifsd_task;
+ char *tmp;
+
+- xid = GetXid();
+-
+- if (cifs_sb->tcon) {
+- ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/
+- rc = CIFSSMBTDis(xid, cifs_sb->tcon);
+- if (rc == -EBUSY) {
+- FreeXid(xid);
+- return 0;
+- }
+- DeleteTconOplockQEntries(cifs_sb->tcon);
+- tconInfoFree(cifs_sb->tcon);
+- cifs_put_smb_ses(ses);
+- }
++ if (cifs_sb->tcon)
++ cifs_put_tcon(cifs_sb->tcon);
+
+ cifs_sb->tcon = NULL;
+ tmp = cifs_sb->prepath;
+@@ -3570,7 +3601,6 @@ cifs_umount(struct super_block *sb, stru
+ cifs_sb->prepath = NULL;
+ kfree(tmp);
+
+- FreeXid(xid);
+ return rc;
+ }
+
+--- a/fs/cifs/misc.c
++++ b/fs/cifs/misc.c
+@@ -79,6 +79,7 @@ sesInfoAlloc(void)
+ ret_buf->status = CifsNew;
+ ++ret_buf->ses_count;
+ INIT_LIST_HEAD(&ret_buf->smb_ses_list);
++ INIT_LIST_HEAD(&ret_buf->tcon_list);
+ init_MUTEX(&ret_buf->sesSem);
+ }
+ return ret_buf;
+@@ -107,17 +108,14 @@ tconInfoAlloc(void)
+ struct cifsTconInfo *ret_buf;
+ ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL);
+ if (ret_buf) {
+- write_lock(&GlobalSMBSeslock);
+ atomic_inc(&tconInfoAllocCount);
+- list_add(&ret_buf->cifsConnectionList,
+- &GlobalTreeConnectionList);
+ ret_buf->tidStatus = CifsNew;
++ ++ret_buf->tc_count;
+ INIT_LIST_HEAD(&ret_buf->openFileList);
+- init_MUTEX(&ret_buf->tconSem);
++ INIT_LIST_HEAD(&ret_buf->tcon_list);
+ #ifdef CONFIG_CIFS_STATS
+ spin_lock_init(&ret_buf->stat_lock);
+ #endif
+- write_unlock(&GlobalSMBSeslock);
+ }
+ return ret_buf;
+ }
+@@ -129,10 +127,7 @@ tconInfoFree(struct cifsTconInfo *buf_to
+ cFYI(1, ("Null buffer passed to tconInfoFree"));
+ return;
+ }
+- write_lock(&GlobalSMBSeslock);
+ atomic_dec(&tconInfoAllocCount);
+- list_del(&buf_to_free->cifsConnectionList);
+- write_unlock(&GlobalSMBSeslock);
+ kfree(buf_to_free->nativeFileSystem);
+ kfree(buf_to_free);
+ }
+@@ -497,9 +492,10 @@ bool
+ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
+ {
+ struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
+- struct list_head *tmp;
+- struct list_head *tmp1;
++ struct list_head *tmp, *tmp1, *tmp2;
++ struct cifsSesInfo *ses;
+ struct cifsTconInfo *tcon;
++ struct cifsInodeInfo *pCifsInode;
+ struct cifsFileInfo *netfile;
+
+ cFYI(1, ("Checking for oplock break or dnotify response"));
+@@ -554,42 +550,42 @@ is_valid_oplock_break(struct smb_hdr *bu
+ return false;
+
+ /* look up tcon based on tid & uid */
+- read_lock(&GlobalSMBSeslock);
+- list_for_each(tmp, &GlobalTreeConnectionList) {
+- tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
+- if ((tcon->tid == buf->Tid) && (srv == tcon->ses->server)) {
++ read_lock(&cifs_tcp_ses_lock);
++ list_for_each(tmp, &srv->smb_ses_list) {
++ ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
++ list_for_each(tmp1, &ses->tcon_list) {
++ tcon = list_entry(tmp1, struct cifsTconInfo, tcon_list);
++ if (tcon->tid != buf->Tid)
++ continue;
++
+ cifs_stats_inc(&tcon->num_oplock_brks);
+- list_for_each(tmp1, &tcon->openFileList) {
+- netfile = list_entry(tmp1, struct cifsFileInfo,
++ list_for_each(tmp2, &tcon->openFileList) {
++ netfile = list_entry(tmp2, struct cifsFileInfo,
+ tlist);
+- if (pSMB->Fid == netfile->netfid) {
+- struct cifsInodeInfo *pCifsInode;
+- read_unlock(&GlobalSMBSeslock);
+- cFYI(1,
+- ("file id match, oplock break"));
+- pCifsInode =
+- CIFS_I(netfile->pInode);
+- pCifsInode->clientCanCacheAll = false;
+- if (pSMB->OplockLevel == 0)
+- pCifsInode->clientCanCacheRead
+- = false;
+- pCifsInode->oplockPending = true;
+- AllocOplockQEntry(netfile->pInode,
+- netfile->netfid,
+- tcon);
+- cFYI(1,
+- ("about to wake up oplock thread"));
+- if (oplockThread)
+- wake_up_process(oplockThread);
+- return true;
+- }
++ if (pSMB->Fid != netfile->netfid)
++ continue;
++
++ read_unlock(&cifs_tcp_ses_lock);
++ cFYI(1, ("file id match, oplock break"));
++ pCifsInode = CIFS_I(netfile->pInode);
++ pCifsInode->clientCanCacheAll = false;
++ if (pSMB->OplockLevel == 0)
++ pCifsInode->clientCanCacheRead = false;
++ pCifsInode->oplockPending = true;
++ AllocOplockQEntry(netfile->pInode,
++ netfile->netfid, tcon);
++ cFYI(1, ("about to wake up oplock thread"));
++ if (oplockThread)
++ wake_up_process(oplockThread);
++
++ return true;
+ }
+- read_unlock(&GlobalSMBSeslock);
++ read_unlock(&cifs_tcp_ses_lock);
+ cFYI(1, ("No matching file for oplock break"));
+ return true;
+ }
+ }
+- read_unlock(&GlobalSMBSeslock);
++ read_unlock(&cifs_tcp_ses_lock);
+ cFYI(1, ("Can not process oplock break for non-existent connection"));
+ return true;
+ }
--- /dev/null
+From sjayaraman@suse.de Wed Dec 3 09:29:08 2008
+From: Steve French <sfrench@us.ibm.com>
+Date: Fri, 21 Nov 2008 14:22:57 +0530
+Subject: cifs: remove unused list, add new cifs sock list to prepare for mount/umount fix
+To: stable@kernel.org
+Cc: Steve French <smfrench@gmail.com>, Jeff Layton <jlayton@redhat.com>, Shirish S Pargaonkar <shirishp@us.ibm.com>
+Message-ID: <492676E9.2080408@suse.de>
+
+From: Steve French <sfrench@us.ibm.com>
+
+commit fb396016647ae9de5b3bd8c4ee4f7b9cc7148bd5 upstream.
+
+Also adds two lines missing from the previous patch (for the need reconnect flag in the
+/proc/fs/cifs/DebugData handling)
+
+The new global_cifs_sock_list is added, and initialized in init_cifs but not used yet.
+Jeff Layton will be adding code in to use that and to remove the GlobalTcon and GlobalSMBSession
+lists.
+
+CC: Jeff Layton <jlayton@redhat.com>
+CC: Shirish Pargaonkar <shirishp@us.ibm.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 | 4 ++--
+ fs/cifs/cifsfs.c | 6 +++---
+ fs/cifs/cifsglob.h | 23 ++++++++---------------
+ 3 files changed, 13 insertions(+), 20 deletions(-)
+
+--- a/fs/cifs/cifs_debug.c
++++ b/fs/cifs/cifs_debug.c
+@@ -204,7 +204,7 @@ static int cifs_debug_data_proc_show(str
+ else
+ seq_printf(m, " type: %d ", dev_type);
+
+- if (tcon->tidStatus == CifsNeedReconnect)
++ if (tcon->need_reconnect)
+ seq_puts(m, "\tDISCONNECTED ");
+ }
+ read_unlock(&GlobalSMBSeslock);
+@@ -311,7 +311,7 @@ static int cifs_stats_proc_show(struct s
+ i++;
+ tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
+ seq_printf(m, "\n%d) %s", i, tcon->treeName);
+- if (tcon->tidStatus == CifsNeedReconnect)
++ if (tcon->need_reconnect)
+ seq_puts(m, "\tDISCONNECTED ");
+ seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
+ atomic_read(&tcon->num_smbs_sent),
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -1013,9 +1013,9 @@ init_cifs(void)
+ {
+ int rc = 0;
+ cifs_proc_init();
+-/* INIT_LIST_HEAD(&GlobalServerList);*/ /* BB not implemented yet */
+- INIT_LIST_HEAD(&GlobalSMBSessionList);
+- INIT_LIST_HEAD(&GlobalTreeConnectionList);
++ INIT_LIST_HEAD(&global_cifs_sock_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);
+ #ifdef CONFIG_CIFS_EXPERIMENTAL
+ INIT_LIST_HEAD(&GlobalDnotifyReqList);
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -590,22 +590,15 @@ require use of the stronger protocol */
+ #define GLOBAL_EXTERN extern
+ #endif
+
+-/*
+- * The list of servers that did not respond with NT LM 0.12.
+- * This list helps improve performance and eliminate the messages indicating
+- * that we had a communications error talking to the server in this list.
+- */
+-/* Feature not supported */
+-/* GLOBAL_EXTERN struct servers_not_supported *NotSuppList; */
+-
+-/*
+- * The following is a hash table of all the users we know about.
+- */
+-GLOBAL_EXTERN struct smbUidInfo *GlobalUidList[UID_HASH];
+
+-/* GLOBAL_EXTERN struct list_head GlobalServerList; BB not implemented yet */
+-GLOBAL_EXTERN struct list_head GlobalSMBSessionList;
+-GLOBAL_EXTERN struct list_head GlobalTreeConnectionList;
++/* 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
++ * 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;
++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 */
+
+ GLOBAL_EXTERN struct list_head GlobalOplock_Q;
ib-mlx4-fix-mtt-leakage-in-resize-cq.patch
net-fix-soft-lockups-oom-issues-w-unix-garbage-collector.patch
libata-improve-phantom-device-detection.patch
+cifs-fix-cifs-reconnection-flags.patch
+cifs-remove-unused-list-add-new-cifs-sock-list-to-prepare-for-mount-umount-fix.patch
+cifs-clean-up-server-protocol-handling.patch
+cifs-disable-sharing-session-and-tcon-and-add-new-tcp-sharing-code.patch
+cifs-reinstate-sharing-of-smb-sessions-sans-races.patch
+cifs-minor-cleanup-to-cifs_mount.patch
+cifs-reinstate-sharing-of-tree-connections.patch
+cifs-fix-build-break.patch
+cifs-fix-check-for-tcon-seal-setting-and-fix-oops-on-failed-mount-from-earlier-patch.patch
+cifs-prevent-cifs_writepages-from-skipping-unwritten-pages.patch
+cifs-fix-check-for-dead-tcon-in-smb_init.patch