aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2008-11-14 13:53:46 -0500
committerSteve French <sfrench@us.ibm.com>2008-11-14 18:56:55 -0500
commit14fbf50d695207754daeb96270b3027a3821121f (patch)
tree05e80aa7e5e6a6bc07a9354f744ba9c599699569
parente7ddee9037e7dd43de1ad08b51727e552aedd836 (diff)
cifs: reinstate sharing of SMB sessions sans races
We do this by abandoning the global list of SMB sessions and instead moving to a per-server list. This entails adding a new list head to the TCP_Server_Info struct. The refcounting for the cifsSesInfo is moved to a non-atomic variable. We have to protect it by a lock anyway, so there's no benefit to making it an atomic. The list and refcount are protected by the global cifs_tcp_ses_lock. The patch also adds a new routines to find and put SMB sessions and that properly take and put references under the lock. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
-rw-r--r--fs/cifs/cifs_debug.c53
-rw-r--r--fs/cifs/cifsfs.c17
-rw-r--r--fs/cifs/cifsglob.h6
-rw-r--r--fs/cifs/cifsproto.h1
-rw-r--r--fs/cifs/cifssmb.c22
-rw-r--r--fs/cifs/connect.c226
-rw-r--r--fs/cifs/misc.c16
7 files changed, 175 insertions, 166 deletions
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 40b5108fb4f9..59841a68b0b6 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -107,9 +107,9 @@ void cifs_dump_mids(struct TCP_Server_Info *server)
107#ifdef CONFIG_PROC_FS 107#ifdef CONFIG_PROC_FS
108static int cifs_debug_data_proc_show(struct seq_file *m, void *v) 108static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
109{ 109{
110 struct list_head *tmp; 110 struct list_head *tmp, *tmp2, *tmp3;
111 struct list_head *tmp1;
112 struct mid_q_entry *mid_entry; 111 struct mid_q_entry *mid_entry;
112 struct TCP_Server_Info *server;
113 struct cifsSesInfo *ses; 113 struct cifsSesInfo *ses;
114 struct cifsTconInfo *tcon; 114 struct cifsTconInfo *tcon;
115 int i; 115 int i;
@@ -122,43 +122,45 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
122 seq_printf(m, "Servers:"); 122 seq_printf(m, "Servers:");
123 123
124 i = 0; 124 i = 0;
125 read_lock(&GlobalSMBSeslock); 125 read_lock(&cifs_tcp_ses_lock);
126 list_for_each(tmp, &GlobalSMBSessionList) { 126 list_for_each(tmp, &cifs_tcp_ses_list) {
127 server = list_entry(tmp, struct TCP_Server_Info,
128 tcp_ses_list);
127 i++; 129 i++;
128 ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); 130 list_for_each(tmp2, &server->smb_ses_list) {
129 if ((ses->serverDomain == NULL) || (ses->serverOS == NULL) || 131 ses = list_entry(tmp2, struct cifsSesInfo,
130 (ses->serverNOS == NULL)) { 132 smb_ses_list);
131 seq_printf(m, "\nentry for %s not fully " 133 if ((ses->serverDomain == NULL) ||
132 "displayed\n\t", ses->serverName); 134 (ses->serverOS == NULL) ||
133 } else { 135 (ses->serverNOS == NULL)) {
134 seq_printf(m, 136 seq_printf(m, "\nentry for %s not fully "
137 "displayed\n\t", ses->serverName);
138 } else {
139 seq_printf(m,
135 "\n%d) Name: %s Domain: %s Mounts: %d OS:" 140 "\n%d) Name: %s Domain: %s Mounts: %d OS:"
136 " %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB" 141 " %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB"
137 " session status: %d\t", 142 " session status: %d\t",
138 i, ses->serverName, ses->serverDomain, 143 i, ses->serverName, ses->serverDomain,
139 atomic_read(&ses->inUse), 144 ses->ses_count, ses->serverOS, ses->serverNOS,
140 ses->serverOS, ses->serverNOS,
141 ses->capabilities, ses->status); 145 ses->capabilities, ses->status);
142 } 146 }
143 if (ses->server) {
144 seq_printf(m, "TCP status: %d\n\tLocal Users To " 147 seq_printf(m, "TCP status: %d\n\tLocal Users To "
145 "Server: %d SecMode: 0x%x Req On Wire: %d", 148 "Server: %d SecMode: 0x%x Req On Wire: %d",
146 ses->server->tcpStatus, 149 server->tcpStatus, server->srv_count,
147 ses->server->srv_count, 150 server->secMode,
148 ses->server->secMode, 151 atomic_read(&server->inFlight));
149 atomic_read(&ses->server->inFlight));
150 152
151#ifdef CONFIG_CIFS_STATS2 153#ifdef CONFIG_CIFS_STATS2
152 seq_printf(m, " In Send: %d In MaxReq Wait: %d", 154 seq_printf(m, " In Send: %d In MaxReq Wait: %d",
153 atomic_read(&ses->server->inSend), 155 atomic_read(&server->inSend),
154 atomic_read(&ses->server->num_waiters)); 156 atomic_read(&server->num_waiters));
155#endif 157#endif
156 158
157 seq_puts(m, "\nMIDs:\n"); 159 seq_puts(m, "\nMIDs:\n");
158 160
159 spin_lock(&GlobalMid_Lock); 161 spin_lock(&GlobalMid_Lock);
160 list_for_each(tmp1, &ses->server->pending_mid_q) { 162 list_for_each(tmp3, &server->pending_mid_q) {
161 mid_entry = list_entry(tmp1, struct 163 mid_entry = list_entry(tmp3, struct
162 mid_q_entry, 164 mid_q_entry,
163 qhead); 165 qhead);
164 seq_printf(m, "State: %d com: %d pid:" 166 seq_printf(m, "State: %d com: %d pid:"
@@ -171,9 +173,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
171 } 173 }
172 spin_unlock(&GlobalMid_Lock); 174 spin_unlock(&GlobalMid_Lock);
173 } 175 }
174
175 } 176 }
176 read_unlock(&GlobalSMBSeslock); 177 read_unlock(&cifs_tcp_ses_lock);
177 seq_putc(m, '\n'); 178 seq_putc(m, '\n');
178 179
179 seq_puts(m, "Shares:"); 180 seq_puts(m, "Shares:");
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index 2946dab0718f..a1e96620b097 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -1031,24 +1031,24 @@ static int cifs_oplock_thread(void *dummyarg)
1031static int cifs_dnotify_thread(void *dummyarg) 1031static int cifs_dnotify_thread(void *dummyarg)
1032{ 1032{
1033 struct list_head *tmp; 1033 struct list_head *tmp;
1034 struct cifsSesInfo *ses; 1034 struct TCP_Server_Info *server;
1035 1035
1036 do { 1036 do {
1037 if (try_to_freeze()) 1037 if (try_to_freeze())
1038 continue; 1038 continue;
1039 set_current_state(TASK_INTERRUPTIBLE); 1039 set_current_state(TASK_INTERRUPTIBLE);
1040 schedule_timeout(15*HZ); 1040 schedule_timeout(15*HZ);
1041 read_lock(&GlobalSMBSeslock);
1042 /* check if any stuck requests that need 1041 /* check if any stuck requests that need
1043 to be woken up and wakeq so the 1042 to be woken up and wakeq so the
1044 thread can wake up and error out */ 1043 thread can wake up and error out */
1045 list_for_each(tmp, &GlobalSMBSessionList) { 1044 read_lock(&cifs_tcp_ses_lock);
1046 ses = list_entry(tmp, struct cifsSesInfo, 1045 list_for_each(tmp, &cifs_tcp_ses_list) {
1047 cifsSessionList); 1046 server = list_entry(tmp, struct TCP_Server_Info,
1048 if (ses->server && atomic_read(&ses->server->inFlight)) 1047 tcp_ses_list);
1049 wake_up_all(&ses->server->response_q); 1048 if (atomic_read(&server->inFlight))
1049 wake_up_all(&server->response_q);
1050 } 1050 }
1051 read_unlock(&GlobalSMBSeslock); 1051 read_unlock(&cifs_tcp_ses_lock);
1052 } while (!kthread_should_stop()); 1052 } while (!kthread_should_stop());
1053 1053
1054 return 0; 1054 return 0;
@@ -1060,7 +1060,6 @@ init_cifs(void)
1060 int rc = 0; 1060 int rc = 0;
1061 cifs_proc_init(); 1061 cifs_proc_init();
1062 INIT_LIST_HEAD(&cifs_tcp_ses_list); 1062 INIT_LIST_HEAD(&cifs_tcp_ses_list);
1063 INIT_LIST_HEAD(&GlobalSMBSessionList); /* BB to be removed by jl */
1064 INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */ 1063 INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */
1065 INIT_LIST_HEAD(&GlobalOplock_Q); 1064 INIT_LIST_HEAD(&GlobalOplock_Q);
1066#ifdef CONFIG_CIFS_EXPERIMENTAL 1065#ifdef CONFIG_CIFS_EXPERIMENTAL
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 313f7bfedec7..631a99f72f22 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -195,14 +195,14 @@ struct cifsUidInfo {
195 * Session structure. One of these for each uid session with a particular host 195 * Session structure. One of these for each uid session with a particular host
196 */ 196 */
197struct cifsSesInfo { 197struct cifsSesInfo {
198 struct list_head cifsSessionList; 198 struct list_head smb_ses_list;
199 struct list_head tcon_list; 199 struct list_head tcon_list;
200 struct semaphore sesSem; 200 struct semaphore sesSem;
201#if 0 201#if 0
202 struct cifsUidInfo *uidInfo; /* pointer to user info */ 202 struct cifsUidInfo *uidInfo; /* pointer to user info */
203#endif 203#endif
204 struct TCP_Server_Info *server; /* pointer to server info */ 204 struct TCP_Server_Info *server; /* pointer to server info */
205 atomic_t inUse; /* # of mounts (tree connections) on this ses */ 205 int ses_count; /* reference counter */
206 enum statusEnum status; 206 enum statusEnum status;
207 unsigned overrideSecFlg; /* if non-zero override global sec flags */ 207 unsigned overrideSecFlg; /* if non-zero override global sec flags */
208 __u16 ipc_tid; /* special tid for connection to IPC share */ 208 __u16 ipc_tid; /* special tid for connection to IPC share */
@@ -602,8 +602,6 @@ GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
602 602
603/* protects cifs_tcp_ses_list and srv_count for each tcp session */ 603/* protects cifs_tcp_ses_list and srv_count for each tcp session */
604GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; 604GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
605
606GLOBAL_EXTERN struct list_head GlobalSMBSessionList; /* BB to be removed by jl*/
607GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */ 605GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */
608GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ 606GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */
609 607
diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
index 0250a994c6e6..6f21ecb85ce5 100644
--- a/fs/cifs/cifsproto.h
+++ b/fs/cifs/cifsproto.h
@@ -102,7 +102,6 @@ extern void acl_to_uid_mode(struct inode *inode, const char *path,
102 const __u16 *pfid); 102 const __u16 *pfid);
103extern int mode_to_acl(struct inode *inode, const char *path, __u64); 103extern int mode_to_acl(struct inode *inode, const char *path, __u64);
104 104
105extern void cifs_put_tcp_session(struct TCP_Server_Info *server);
106extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *, 105extern int cifs_mount(struct super_block *, struct cifs_sb_info *, char *,
107 const char *); 106 const char *);
108extern int cifs_umount(struct super_block *, struct cifs_sb_info *); 107extern int cifs_umount(struct super_block *, struct cifs_sb_info *);
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index cd9e9a145e4d..9c95617baa4d 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -799,20 +799,16 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
799 int rc = 0; 799 int rc = 0;
800 800
801 cFYI(1, ("In SMBLogoff for session disconnect")); 801 cFYI(1, ("In SMBLogoff for session disconnect"));
802 if (ses)
803 down(&ses->sesSem);
804 else
805 return -EIO;
806
807 atomic_dec(&ses->inUse);
808 if (atomic_read(&ses->inUse) > 0) {
809 up(&ses->sesSem);
810 return -EBUSY;
811 }
812 802
813 if (ses->server == NULL) 803 /*
804 * BB: do we need to check validity of ses and server? They should
805 * always be valid since we have an active reference. If not, that
806 * should probably be a BUG()
807 */
808 if (!ses || !ses->server)
814 return -EIO; 809 return -EIO;
815 810
811 down(&ses->sesSem);
816 if (ses->need_reconnect) 812 if (ses->need_reconnect)
817 goto session_already_dead; /* no need to send SMBlogoff if uid 813 goto session_already_dead; /* no need to send SMBlogoff if uid
818 already closed due to reconnect */ 814 already closed due to reconnect */
@@ -833,10 +829,6 @@ CIFSSMBLogoff(const int xid, struct cifsSesInfo *ses)
833 pSMB->AndXCommand = 0xFF; 829 pSMB->AndXCommand = 0xFF;
834 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0); 830 rc = SendReceiveNoRsp(xid, ses, (struct smb_hdr *) pSMB, 0);
835session_already_dead: 831session_already_dead:
836 if (ses->server) {
837 cifs_put_tcp_session(ses->server);
838 rc = 0;
839 }
840 up(&ses->sesSem); 832 up(&ses->sesSem);
841 833
842 /* if session dead then we do not need to do ulogoff, 834 /* if session dead then we do not need to do ulogoff,
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index a0314259f94d..44130e052e0b 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -144,23 +144,18 @@ cifs_reconnect(struct TCP_Server_Info *server)
144 144
145 /* before reconnecting the tcp session, mark the smb session (uid) 145 /* before reconnecting the tcp session, mark the smb session (uid)
146 and the tid bad so they are not used until reconnected */ 146 and the tid bad so they are not used until reconnected */
147 read_lock(&GlobalSMBSeslock); 147 read_lock(&cifs_tcp_ses_lock);
148 list_for_each(tmp, &GlobalSMBSessionList) { 148 list_for_each(tmp, &server->smb_ses_list) {
149 ses = list_entry(tmp, struct cifsSesInfo, cifsSessionList); 149 ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
150 if (ses->server) { 150 ses->need_reconnect = true;
151 if (ses->server == server) { 151 ses->ipc_tid = 0;
152 ses->need_reconnect = true;
153 ses->ipc_tid = 0;
154 }
155 }
156 /* else tcp and smb sessions need reconnection */
157 } 152 }
153 read_unlock(&cifs_tcp_ses_lock);
158 list_for_each(tmp, &GlobalTreeConnectionList) { 154 list_for_each(tmp, &GlobalTreeConnectionList) {
159 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); 155 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
160 if ((tcon->ses) && (tcon->ses->server == server)) 156 if ((tcon->ses) && (tcon->ses->server == server))
161 tcon->need_reconnect = true; 157 tcon->need_reconnect = true;
162 } 158 }
163 read_unlock(&GlobalSMBSeslock);
164 /* do not want to be sending data on a socket we are freeing */ 159 /* do not want to be sending data on a socket we are freeing */
165 down(&server->tcpSem); 160 down(&server->tcpSem);
166 if (server->ssocket) { 161 if (server->ssocket) {
@@ -696,29 +691,29 @@ multi_t2_fnd:
696 if (smallbuf) /* no sense logging a debug message if NULL */ 691 if (smallbuf) /* no sense logging a debug message if NULL */
697 cifs_small_buf_release(smallbuf); 692 cifs_small_buf_release(smallbuf);
698 693
699 read_lock(&GlobalSMBSeslock); 694 /*
695 * BB: we shouldn't have to do any of this. It shouldn't be
696 * possible to exit from the thread with active SMB sessions
697 */
698 read_lock(&cifs_tcp_ses_lock);
700 if (list_empty(&server->pending_mid_q)) { 699 if (list_empty(&server->pending_mid_q)) {
701 /* loop through server session structures attached to this and 700 /* loop through server session structures attached to this and
702 mark them dead */ 701 mark them dead */
703 list_for_each(tmp, &GlobalSMBSessionList) { 702 list_for_each(tmp, &server->smb_ses_list) {
704 ses = 703 ses = list_entry(tmp, struct cifsSesInfo,
705 list_entry(tmp, struct cifsSesInfo, 704 smb_ses_list);
706 cifsSessionList); 705 ses->status = CifsExiting;
707 if (ses->server == server) { 706 ses->server = NULL;
708 ses->status = CifsExiting;
709 ses->server = NULL;
710 }
711 } 707 }
712 read_unlock(&GlobalSMBSeslock); 708 read_unlock(&cifs_tcp_ses_lock);
713 } else { 709 } else {
714 /* although we can not zero the server struct pointer yet, 710 /* although we can not zero the server struct pointer yet,
715 since there are active requests which may depnd on them, 711 since there are active requests which may depnd on them,
716 mark the corresponding SMB sessions as exiting too */ 712 mark the corresponding SMB sessions as exiting too */
717 list_for_each(tmp, &GlobalSMBSessionList) { 713 list_for_each(tmp, &server->smb_ses_list) {
718 ses = list_entry(tmp, struct cifsSesInfo, 714 ses = list_entry(tmp, struct cifsSesInfo,
719 cifsSessionList); 715 smb_ses_list);
720 if (ses->server == server) 716 ses->status = CifsExiting;
721 ses->status = CifsExiting;
722 } 717 }
723 718
724 spin_lock(&GlobalMid_Lock); 719 spin_lock(&GlobalMid_Lock);
@@ -733,7 +728,7 @@ multi_t2_fnd:
733 } 728 }
734 } 729 }
735 spin_unlock(&GlobalMid_Lock); 730 spin_unlock(&GlobalMid_Lock);
736 read_unlock(&GlobalSMBSeslock); 731 read_unlock(&cifs_tcp_ses_lock);
737 /* 1/8th of sec is more than enough time for them to exit */ 732 /* 1/8th of sec is more than enough time for them to exit */
738 msleep(125); 733 msleep(125);
739 } 734 }
@@ -755,14 +750,13 @@ multi_t2_fnd:
755 if there are any pointing to this (e.g 750 if there are any pointing to this (e.g
756 if a crazy root user tried to kill cifsd 751 if a crazy root user tried to kill cifsd
757 kernel thread explicitly this might happen) */ 752 kernel thread explicitly this might happen) */
758 write_lock(&GlobalSMBSeslock); 753 /* BB: This shouldn't be necessary, see above */
759 list_for_each(tmp, &GlobalSMBSessionList) { 754 read_lock(&cifs_tcp_ses_lock);
760 ses = list_entry(tmp, struct cifsSesInfo, 755 list_for_each(tmp, &server->smb_ses_list) {
761 cifsSessionList); 756 ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
762 if (ses->server == server) 757 ses->server = NULL;
763 ses->server = NULL;
764 } 758 }
765 write_unlock(&GlobalSMBSeslock); 759 read_unlock(&cifs_tcp_ses_lock);
766 760
767 kfree(server->hostname); 761 kfree(server->hostname);
768 task_to_wake = xchg(&server->tsk, NULL); 762 task_to_wake = xchg(&server->tsk, NULL);
@@ -1401,7 +1395,7 @@ cifs_find_tcp_session(struct sockaddr *addr)
1401 return NULL; 1395 return NULL;
1402} 1396}
1403 1397
1404void 1398static void
1405cifs_put_tcp_session(struct TCP_Server_Info *server) 1399cifs_put_tcp_session(struct TCP_Server_Info *server)
1406{ 1400{
1407 struct task_struct *task; 1401 struct task_struct *task;
@@ -1424,6 +1418,50 @@ cifs_put_tcp_session(struct TCP_Server_Info *server)
1424 force_sig(SIGKILL, task); 1418 force_sig(SIGKILL, task);
1425} 1419}
1426 1420
1421static struct cifsSesInfo *
1422cifs_find_smb_ses(struct TCP_Server_Info *server, char *username)
1423{
1424 struct list_head *tmp;
1425 struct cifsSesInfo *ses;
1426
1427 write_lock(&cifs_tcp_ses_lock);
1428 list_for_each(tmp, &server->smb_ses_list) {
1429 ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
1430 if (strncmp(ses->userName, username, MAX_USERNAME_SIZE))
1431 continue;
1432
1433 ++ses->ses_count;
1434 write_unlock(&cifs_tcp_ses_lock);
1435 return ses;
1436 }
1437 write_unlock(&cifs_tcp_ses_lock);
1438 return NULL;
1439}
1440
1441static void
1442cifs_put_smb_ses(struct cifsSesInfo *ses)
1443{
1444 int xid;
1445 struct TCP_Server_Info *server = ses->server;
1446
1447 write_lock(&cifs_tcp_ses_lock);
1448 if (--ses->ses_count > 0) {
1449 write_unlock(&cifs_tcp_ses_lock);
1450 return;
1451 }
1452
1453 list_del_init(&ses->smb_ses_list);
1454 write_unlock(&cifs_tcp_ses_lock);
1455
1456 if (ses->status == CifsGood) {
1457 xid = GetXid();
1458 CIFSSMBLogoff(xid, ses);
1459 _FreeXid(xid);
1460 }
1461 sesInfoFree(ses);
1462 cifs_put_tcp_session(server);
1463}
1464
1427int 1465int
1428get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, 1466get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
1429 const struct nls_table *nls_codepage, unsigned int *pnum_referrals, 1467 const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
@@ -1958,7 +1996,6 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
1958 struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr; 1996 struct sockaddr_in6 *sin_server6 = (struct sockaddr_in6 *) &addr;
1959 struct smb_vol volume_info; 1997 struct smb_vol volume_info;
1960 struct cifsSesInfo *pSesInfo = NULL; 1998 struct cifsSesInfo *pSesInfo = NULL;
1961 struct cifsSesInfo *existingCifsSes = NULL;
1962 struct cifsTconInfo *tcon = NULL; 1999 struct cifsTconInfo *tcon = NULL;
1963 struct TCP_Server_Info *srvTcp = NULL; 2000 struct TCP_Server_Info *srvTcp = NULL;
1964 2001
@@ -2112,6 +2149,7 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
2112 volume_info.target_rfc1001_name, 16); 2149 volume_info.target_rfc1001_name, 16);
2113 srvTcp->sequence_number = 0; 2150 srvTcp->sequence_number = 0;
2114 INIT_LIST_HEAD(&srvTcp->tcp_ses_list); 2151 INIT_LIST_HEAD(&srvTcp->tcp_ses_list);
2152 INIT_LIST_HEAD(&srvTcp->smb_ses_list);
2115 ++srvTcp->srv_count; 2153 ++srvTcp->srv_count;
2116 write_lock(&cifs_tcp_ses_lock); 2154 write_lock(&cifs_tcp_ses_lock);
2117 list_add(&srvTcp->tcp_ses_list, 2155 list_add(&srvTcp->tcp_ses_list,
@@ -2120,10 +2158,16 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
2120 } 2158 }
2121 } 2159 }
2122 2160
2123 if (existingCifsSes) { 2161 pSesInfo = cifs_find_smb_ses(srvTcp, volume_info.username);
2124 pSesInfo = existingCifsSes; 2162 if (pSesInfo) {
2125 cFYI(1, ("Existing smb sess found (status=%d)", 2163 cFYI(1, ("Existing smb sess found (status=%d)",
2126 pSesInfo->status)); 2164 pSesInfo->status));
2165 /*
2166 * The existing SMB session already has a reference to srvTcp,
2167 * so we can put back the extra one we got before
2168 */
2169 cifs_put_tcp_session(srvTcp);
2170
2127 down(&pSesInfo->sesSem); 2171 down(&pSesInfo->sesSem);
2128 if (pSesInfo->need_reconnect) { 2172 if (pSesInfo->need_reconnect) {
2129 cFYI(1, ("Session needs reconnect")); 2173 cFYI(1, ("Session needs reconnect"));
@@ -2134,41 +2178,44 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
2134 } else if (!rc) { 2178 } else if (!rc) {
2135 cFYI(1, ("Existing smb sess not found")); 2179 cFYI(1, ("Existing smb sess not found"));
2136 pSesInfo = sesInfoAlloc(); 2180 pSesInfo = sesInfoAlloc();
2137 if (pSesInfo == NULL) 2181 if (pSesInfo == NULL) {
2138 rc = -ENOMEM; 2182 rc = -ENOMEM;
2139 else { 2183 goto mount_fail_check;
2140 pSesInfo->server = srvTcp;
2141 sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
2142 NIPQUAD(sin_server->sin_addr.s_addr));
2143 } 2184 }
2144 2185
2145 if (!rc) { 2186 /* new SMB session uses our srvTcp ref */
2146 /* volume_info.password freed at unmount */ 2187 pSesInfo->server = srvTcp;
2147 if (volume_info.password) { 2188 sprintf(pSesInfo->serverName, "%u.%u.%u.%u",
2148 pSesInfo->password = volume_info.password; 2189 NIPQUAD(sin_server->sin_addr.s_addr));
2149 /* set to NULL to prevent freeing on exit */ 2190
2150 volume_info.password = NULL; 2191 write_lock(&cifs_tcp_ses_lock);
2151 } 2192 list_add(&pSesInfo->smb_ses_list, &srvTcp->smb_ses_list);
2152 if (volume_info.username) 2193 write_unlock(&cifs_tcp_ses_lock);
2153 strncpy(pSesInfo->userName, 2194
2154 volume_info.username, 2195 /* volume_info.password freed at unmount */
2155 MAX_USERNAME_SIZE); 2196 if (volume_info.password) {
2156 if (volume_info.domainname) { 2197 pSesInfo->password = volume_info.password;
2157 int len = strlen(volume_info.domainname); 2198 /* set to NULL to prevent freeing on exit */
2158 pSesInfo->domainName = 2199 volume_info.password = NULL;
2159 kmalloc(len + 1, GFP_KERNEL); 2200 }
2160 if (pSesInfo->domainName) 2201 if (volume_info.username)
2161 strcpy(pSesInfo->domainName, 2202 strncpy(pSesInfo->userName, volume_info.username,
2162 volume_info.domainname); 2203 MAX_USERNAME_SIZE);
2163 } 2204 if (volume_info.domainname) {
2164 pSesInfo->linux_uid = volume_info.linux_uid; 2205 int len = strlen(volume_info.domainname);
2165 pSesInfo->overrideSecFlg = volume_info.secFlg; 2206 pSesInfo->domainName = kmalloc(len + 1, GFP_KERNEL);
2166 down(&pSesInfo->sesSem); 2207 if (pSesInfo->domainName)
2167 /* BB FIXME need to pass vol->secFlgs BB */ 2208 strcpy(pSesInfo->domainName,
2168 rc = cifs_setup_session(xid, pSesInfo, 2209 volume_info.domainname);
2169 cifs_sb->local_nls); 2210 }
2170 up(&pSesInfo->sesSem); 2211 pSesInfo->linux_uid = volume_info.linux_uid;
2171 } 2212 pSesInfo->overrideSecFlg = volume_info.secFlg;
2213 down(&pSesInfo->sesSem);
2214
2215 /* BB FIXME need to pass vol->secFlgs BB */
2216 rc = cifs_setup_session(xid, pSesInfo,
2217 cifs_sb->local_nls);
2218 up(&pSesInfo->sesSem);
2172 } 2219 }
2173 2220
2174 /* search for existing tcon to this server share */ 2221 /* search for existing tcon to this server share */
@@ -2209,11 +2256,9 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
2209 tcon->Flags)); 2256 tcon->Flags));
2210 } 2257 }
2211 } 2258 }
2212 if (!rc) { 2259 if (rc)
2213 atomic_inc(&pSesInfo->inUse);
2214 tcon->seal = volume_info.seal;
2215 } else
2216 goto mount_fail_check; 2260 goto mount_fail_check;
2261 tcon->seal = volume_info.seal;
2217 } 2262 }
2218 2263
2219 /* we can have only one retry value for a connection 2264 /* we can have only one retry value for a connection
@@ -2234,29 +2279,19 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
2234 /* BB FIXME fix time_gran to be larger for LANMAN sessions */ 2279 /* BB FIXME fix time_gran to be larger for LANMAN sessions */
2235 sb->s_time_gran = 100; 2280 sb->s_time_gran = 100;
2236 2281
2237/* on error free sesinfo and tcon struct if needed */
2238mount_fail_check: 2282mount_fail_check:
2283 /* on error free sesinfo and tcon struct if needed */
2239 if (rc) { 2284 if (rc) {
2240 /* If find_unc succeeded then rc == 0 so we can not end */ 2285 /* If find_unc succeeded then rc == 0 so we can not end */
2241 /* up accidently freeing someone elses tcon struct */ 2286 /* up accidently freeing someone elses tcon struct */
2242 if (tcon) 2287 if (tcon)
2243 tconInfoFree(tcon); 2288 tconInfoFree(tcon);
2244 2289
2245 if (existingCifsSes == NULL) { 2290 /* should also end up putting our tcp session ref if needed */
2246 if (pSesInfo) { 2291 if (pSesInfo)
2247 if ((pSesInfo->server) && 2292 cifs_put_smb_ses(pSesInfo);
2248 (pSesInfo->status == CifsGood)) 2293 else
2249 CIFSSMBLogoff(xid, pSesInfo); 2294 cifs_put_tcp_session(srvTcp);
2250 else {
2251 cFYI(1, ("No session or bad tcon"));
2252 if (pSesInfo->server)
2253 cifs_put_tcp_session(
2254 pSesInfo->server);
2255 }
2256 sesInfoFree(pSesInfo);
2257 /* pSesInfo = NULL; */
2258 }
2259 }
2260 } else { 2295 } else {
2261 atomic_inc(&tcon->useCount); 2296 atomic_inc(&tcon->useCount);
2262 cifs_sb->tcon = tcon; 2297 cifs_sb->tcon = tcon;
@@ -3551,16 +3586,7 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
3551 } 3586 }
3552 DeleteTconOplockQEntries(cifs_sb->tcon); 3587 DeleteTconOplockQEntries(cifs_sb->tcon);
3553 tconInfoFree(cifs_sb->tcon); 3588 tconInfoFree(cifs_sb->tcon);
3554 if ((ses) && (ses->server)) { 3589 cifs_put_smb_ses(ses);
3555 /* save off task so we do not refer to ses later */
3556 cFYI(1, ("About to do SMBLogoff "));
3557 rc = CIFSSMBLogoff(xid, ses);
3558 if (rc == -EBUSY) {
3559 FreeXid(xid);
3560 return 0;
3561 }
3562 } else
3563 cFYI(1, ("No session or bad tcon"));
3564 } 3590 }
3565 3591
3566 cifs_sb->tcon = NULL; 3592 cifs_sb->tcon = NULL;
@@ -3568,8 +3594,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
3568 cifs_sb->prepathlen = 0; 3594 cifs_sb->prepathlen = 0;
3569 cifs_sb->prepath = NULL; 3595 cifs_sb->prepath = NULL;
3570 kfree(tmp); 3596 kfree(tmp);
3571 if (ses)
3572 sesInfoFree(ses);
3573 3597
3574 FreeXid(xid); 3598 FreeXid(xid);
3575 return rc; 3599 return rc;
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 88786ba02d27..46c8c7baccba 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -75,12 +75,11 @@ sesInfoAlloc(void)
75 75
76 ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL); 76 ret_buf = kzalloc(sizeof(struct cifsSesInfo), GFP_KERNEL);
77 if (ret_buf) { 77 if (ret_buf) {
78 write_lock(&GlobalSMBSeslock);
79 atomic_inc(&sesInfoAllocCount); 78 atomic_inc(&sesInfoAllocCount);
80 ret_buf->status = CifsNew; 79 ret_buf->status = CifsNew;
81 list_add(&ret_buf->cifsSessionList, &GlobalSMBSessionList); 80 ++ret_buf->ses_count;
81 INIT_LIST_HEAD(&ret_buf->smb_ses_list);
82 init_MUTEX(&ret_buf->sesSem); 82 init_MUTEX(&ret_buf->sesSem);
83 write_unlock(&GlobalSMBSeslock);
84 } 83 }
85 return ret_buf; 84 return ret_buf;
86} 85}
@@ -93,10 +92,7 @@ sesInfoFree(struct cifsSesInfo *buf_to_free)
93 return; 92 return;
94 } 93 }
95 94
96 write_lock(&GlobalSMBSeslock);
97 atomic_dec(&sesInfoAllocCount); 95 atomic_dec(&sesInfoAllocCount);
98 list_del(&buf_to_free->cifsSessionList);
99 write_unlock(&GlobalSMBSeslock);
100 kfree(buf_to_free->serverOS); 96 kfree(buf_to_free->serverOS);
101 kfree(buf_to_free->serverDomain); 97 kfree(buf_to_free->serverDomain);
102 kfree(buf_to_free->serverNOS); 98 kfree(buf_to_free->serverNOS);
@@ -350,9 +346,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
350 if (current->fsuid != treeCon->ses->linux_uid) { 346 if (current->fsuid != treeCon->ses->linux_uid) {
351 cFYI(1, ("Multiuser mode and UID " 347 cFYI(1, ("Multiuser mode and UID "
352 "did not match tcon uid")); 348 "did not match tcon uid"));
353 read_lock(&GlobalSMBSeslock); 349 read_lock(&cifs_tcp_ses_lock);
354 list_for_each(temp_item, &GlobalSMBSessionList) { 350 list_for_each(temp_item, &treeCon->ses->server->smb_ses_list) {
355 ses = list_entry(temp_item, struct cifsSesInfo, cifsSessionList); 351 ses = list_entry(temp_item, struct cifsSesInfo, smb_ses_list);
356 if (ses->linux_uid == current->fsuid) { 352 if (ses->linux_uid == current->fsuid) {
357 if (ses->server == treeCon->ses->server) { 353 if (ses->server == treeCon->ses->server) {
358 cFYI(1, ("found matching uid substitute right smb_uid")); 354 cFYI(1, ("found matching uid substitute right smb_uid"));
@@ -364,7 +360,7 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ ,
364 } 360 }
365 } 361 }
366 } 362 }
367 read_unlock(&GlobalSMBSeslock); 363 read_unlock(&cifs_tcp_ses_lock);
368 } 364 }
369 } 365 }
370 } 366 }