From 15ef07feb96a23d8adb9cb157cd294878e47d745 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 3 Dec 2008 09:51:11 -0800 Subject: [PATCH] cifs patches for 2.6.27 --- ...fs-clean-up-server-protocol-handling.patch | 205 +++++ ...nd-tcon-and-add-new-tcp-sharing-code.patch | 444 ++++++++++ queue-2.6.27/cifs-fix-build-break.patch | 34 + ...-fix-check-for-dead-tcon-in-smb_init.patch | 36 + ...s-on-failed-mount-from-earlier-patch.patch | 58 ++ .../cifs-fix-cifs-reconnection-flags.patch | 520 ++++++++++++ .../cifs-minor-cleanup-to-cifs_mount.patch | 127 +++ ...epages-from-skipping-unwritten-pages.patch | 57 ++ ...e-sharing-of-smb-sessions-sans-races.patch | 657 +++++++++++++++ ...einstate-sharing-of-tree-connections.patch | 765 ++++++++++++++++++ ...list-to-prepare-for-mount-umount-fix.patch | 99 +++ queue-2.6.27/series | 11 + 12 files changed, 3013 insertions(+) create mode 100644 queue-2.6.27/cifs-clean-up-server-protocol-handling.patch create mode 100644 queue-2.6.27/cifs-disable-sharing-session-and-tcon-and-add-new-tcp-sharing-code.patch create mode 100644 queue-2.6.27/cifs-fix-build-break.patch create mode 100644 queue-2.6.27/cifs-fix-check-for-dead-tcon-in-smb_init.patch create mode 100644 queue-2.6.27/cifs-fix-check-for-tcon-seal-setting-and-fix-oops-on-failed-mount-from-earlier-patch.patch create mode 100644 queue-2.6.27/cifs-fix-cifs-reconnection-flags.patch create mode 100644 queue-2.6.27/cifs-minor-cleanup-to-cifs_mount.patch create mode 100644 queue-2.6.27/cifs-prevent-cifs_writepages-from-skipping-unwritten-pages.patch create mode 100644 queue-2.6.27/cifs-reinstate-sharing-of-smb-sessions-sans-races.patch create mode 100644 queue-2.6.27/cifs-reinstate-sharing-of-tree-connections.patch create mode 100644 queue-2.6.27/cifs-remove-unused-list-add-new-cifs-sock-list-to-prepare-for-mount-umount-fix.patch diff --git a/queue-2.6.27/cifs-clean-up-server-protocol-handling.patch b/queue-2.6.27/cifs-clean-up-server-protocol-handling.patch new file mode 100644 index 00000000000..7b996e3e32a --- /dev/null +++ b/queue-2.6.27/cifs-clean-up-server-protocol-handling.patch @@ -0,0 +1,205 @@ +From sjayaraman@suse.de Wed Dec 3 09:30:05 2008 +From: Steve French +Date: Fri, 21 Nov 2008 14:23:07 +0530 +Subject: cifs: clean up server protocol handling +To: stable@kernel.org +Cc: Steve French , Jeff Layton +Message-ID: <492676F3.7080004@suse.de> + +From: Steve French + +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 +Signed-off-by: Steve French +Cc: Suresh Jayaraman +Signed-off-by: Greg Kroah-Hartman + + +--- + 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")); diff --git a/queue-2.6.27/cifs-disable-sharing-session-and-tcon-and-add-new-tcp-sharing-code.patch b/queue-2.6.27/cifs-disable-sharing-session-and-tcon-and-add-new-tcp-sharing-code.patch new file mode 100644 index 00000000000..5a63b6b18a4 --- /dev/null +++ b/queue-2.6.27/cifs-disable-sharing-session-and-tcon-and-add-new-tcp-sharing-code.patch @@ -0,0 +1,444 @@ +From sjayaraman@suse.de Wed Dec 3 09:30:44 2008 +From: Jeff Layton +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 , Jeff Layton +Message-ID: <492676FD.7060706@suse.de> + +From: Jeff Layton + +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 +Signed-off-by: Steve French +Cc: Suresh Jayaraman +Signed-off-by: Greg Kroah-Hartman + +--- + 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")); + } diff --git a/queue-2.6.27/cifs-fix-build-break.patch b/queue-2.6.27/cifs-fix-build-break.patch new file mode 100644 index 00000000000..5474f784c39 --- /dev/null +++ b/queue-2.6.27/cifs-fix-build-break.patch @@ -0,0 +1,34 @@ +From sjayaraman@suse.de Wed Dec 3 09:41:59 2008 +From: Steve French +Date: Fri, 21 Nov 2008 14:24:05 +0530 +Subject: cifs: Fix build break +To: stable@kernel.org +Cc: Steve French +Message-ID: <4926772D.60304@suse.de> + +From: Steve French + +commit c2b3382cd4d6c6adef1347e81f20e16c93a39feb upstream + +Signed-off-by: Steve French +Cc: Suresh Jayaraman +Signed-off-by: Greg Kroah-Hartman + +--- + 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, diff --git a/queue-2.6.27/cifs-fix-check-for-dead-tcon-in-smb_init.patch b/queue-2.6.27/cifs-fix-check-for-dead-tcon-in-smb_init.patch new file mode 100644 index 00000000000..9f638c7e885 --- /dev/null +++ b/queue-2.6.27/cifs-fix-check-for-dead-tcon-in-smb_init.patch @@ -0,0 +1,36 @@ +From sjayaraman@suse.de Wed Dec 3 09:44:31 2008 +From: Steve French +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 , Jeff Layton +Message-ID: <492A3511.2040109@suse.de> + +From: Steve French + +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 +Signed-off-by: Steve French +Cc: Suresh Jayaraman +Signed-off-by: Greg Kroah-Hartman + + +--- + 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 */ diff --git a/queue-2.6.27/cifs-fix-check-for-tcon-seal-setting-and-fix-oops-on-failed-mount-from-earlier-patch.patch b/queue-2.6.27/cifs-fix-check-for-tcon-seal-setting-and-fix-oops-on-failed-mount-from-earlier-patch.patch new file mode 100644 index 00000000000..78abfe467a9 --- /dev/null +++ b/queue-2.6.27/cifs-fix-check-for-tcon-seal-setting-and-fix-oops-on-failed-mount-from-earlier-patch.patch @@ -0,0 +1,58 @@ +From sjayaraman@suse.de Wed Dec 3 09:42:51 2008 +From: Steve French +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 , Jeff Layton +Message-ID: <49267734.7050508@suse.de> + +From: Steve French + +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 +Signed-off-by: Steve French +Cc: Suresh Jayaraman +Signed-off-by: Greg Kroah-Hartman + + +--- + 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); diff --git a/queue-2.6.27/cifs-fix-cifs-reconnection-flags.patch b/queue-2.6.27/cifs-fix-cifs-reconnection-flags.patch new file mode 100644 index 00000000000..9a67c0a7076 --- /dev/null +++ b/queue-2.6.27/cifs-fix-cifs-reconnection-flags.patch @@ -0,0 +1,520 @@ +From sjayaraman@suse.de Wed Dec 3 09:28:09 2008 +From: Steve French +Date: Fri, 21 Nov 2008 14:22:45 +0530 +Subject: cifs: Fix cifs reconnection flags +To: stable@kernel.org +Cc: Steve French , Shirish S Pargaonkar , Jeff Layton +Message-ID: <492676DD.5070504@suse.de> + +From: Steve French + +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 +CC: Shirish Pargaonkar +Signed-off-by: Steve French +Signed-off-by: Greg Kroah-Hartman + +--- + 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)) { diff --git a/queue-2.6.27/cifs-minor-cleanup-to-cifs_mount.patch b/queue-2.6.27/cifs-minor-cleanup-to-cifs_mount.patch new file mode 100644 index 00000000000..f6193d7aeab --- /dev/null +++ b/queue-2.6.27/cifs-minor-cleanup-to-cifs_mount.patch @@ -0,0 +1,127 @@ +From sjayaraman@suse.de Wed Dec 3 09:40:26 2008 +From: Steve French +Date: Fri, 21 Nov 2008 14:23:44 +0530 +Subject: cifs: minor cleanup to cifs_mount +To: stable@kernel.org +Cc: Steve French +Message-ID: <49267718.9010206@suse.de> + +From: Steve French + +commit d82c2df54e2f7e447476350848d8eccc8d2fe46a upstream + +Signed-off-by: Steve French +Cc: Suresh Jayaraman +Signed-off-by: Greg Kroah-Hartman + + +--- + 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 diff --git a/queue-2.6.27/cifs-prevent-cifs_writepages-from-skipping-unwritten-pages.patch b/queue-2.6.27/cifs-prevent-cifs_writepages-from-skipping-unwritten-pages.patch new file mode 100644 index 00000000000..943c8a84e21 --- /dev/null +++ b/queue-2.6.27/cifs-prevent-cifs_writepages-from-skipping-unwritten-pages.patch @@ -0,0 +1,57 @@ +From sjayaraman@suse.de Wed Dec 3 09:43:30 2008 +From: Dave Kleikamp +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 , Dave Kleikamp , Shirish S Pargaonkar , Jeff Layton +Message-ID: <4926773C.6010409@suse.de> + +From: Dave Kleikamp + +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 +Acked-by: Jeff Layton +Cc: Shirish S Pargaonkar +Signed-off-by: Steve French +Cc: Suresh Jayaraman +Signed-off-by: Greg Kroah-Hartman + + +--- + 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) { diff --git a/queue-2.6.27/cifs-reinstate-sharing-of-smb-sessions-sans-races.patch b/queue-2.6.27/cifs-reinstate-sharing-of-smb-sessions-sans-races.patch new file mode 100644 index 00000000000..15379d109ed --- /dev/null +++ b/queue-2.6.27/cifs-reinstate-sharing-of-smb-sessions-sans-races.patch @@ -0,0 +1,657 @@ +From sjayaraman@suse.de Wed Dec 3 09:31:32 2008 +From: Jeff Layton +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 , Jeff Layton +Message-ID: <4926770A.7090107@suse.de> + +From: Jeff Layton + +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 +Signed-off-by: Steve French +Cc: Suresh Jayaraman +Signed-off-by: Greg Kroah-Hartman + +--- + 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); + } + } + } diff --git a/queue-2.6.27/cifs-reinstate-sharing-of-tree-connections.patch b/queue-2.6.27/cifs-reinstate-sharing-of-tree-connections.patch new file mode 100644 index 00000000000..4f8ddd17bd1 --- /dev/null +++ b/queue-2.6.27/cifs-reinstate-sharing-of-tree-connections.patch @@ -0,0 +1,765 @@ +From sjayaraman@suse.de Wed Dec 3 09:41:15 2008 +From: Jeff Layton +Date: Fri, 21 Nov 2008 14:23:55 +0530 +Subject: cifs: reinstate sharing of tree connections +To: stable@kernel.org +Cc: Steve French , Jeff Layton +Message-ID: <49267723.7060701@suse.de> + +From: Jeff Layton + +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 +Signed-off-by: Steve French +Cc: Suresh Jayaraman +Signed-off-by: Greg Kroah-Hartman + +--- + 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; + } diff --git a/queue-2.6.27/cifs-remove-unused-list-add-new-cifs-sock-list-to-prepare-for-mount-umount-fix.patch b/queue-2.6.27/cifs-remove-unused-list-add-new-cifs-sock-list-to-prepare-for-mount-umount-fix.patch new file mode 100644 index 00000000000..57e4edf5c7c --- /dev/null +++ b/queue-2.6.27/cifs-remove-unused-list-add-new-cifs-sock-list-to-prepare-for-mount-umount-fix.patch @@ -0,0 +1,99 @@ +From sjayaraman@suse.de Wed Dec 3 09:29:08 2008 +From: Steve French +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 , Jeff Layton , Shirish S Pargaonkar +Message-ID: <492676E9.2080408@suse.de> + +From: Steve French + +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 +CC: Shirish Pargaonkar +Signed-off-by: Steve French +Cc: Suresh Jayaraman +Signed-off-by: Greg Kroah-Hartman + +--- + 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; diff --git a/queue-2.6.27/series b/queue-2.6.27/series index cc856442fa2..82b514b199a 100644 --- a/queue-2.6.27/series +++ b/queue-2.6.27/series @@ -65,3 +65,14 @@ firewire-fw-sbp2-another-ipod-mini-quirk-entry.patch 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 -- 2.47.3