aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeff Layton <jlayton@redhat.com>2008-11-15 11:12:47 -0500
committerSteve French <sfrench@us.ibm.com>2008-11-16 22:14:12 -0500
commitf1987b44f642e96176adc88b7ce23a1d74806f89 (patch)
treefceaebf6b6d7eb1d1150120c44a842cbce8347f6
parentd82c2df54e2f7e447476350848d8eccc8d2fe46a (diff)
cifs: reinstate sharing of tree connections
Use a similar approach to the SMB session sharing. Add a list of tcons attached to each SMB session. Move the refcount to non-atomic. Protect all of the above with the cifs_tcp_ses_lock. Add functions to properly find and put references to the tcons. Signed-off-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Steve French <sfrench@us.ibm.com>
-rw-r--r--fs/cifs/cifs_debug.c236
-rw-r--r--fs/cifs/cifsfs.c8
-rw-r--r--fs/cifs/cifsglob.h13
-rw-r--r--fs/cifs/cifssmb.c43
-rw-r--r--fs/cifs/connect.c93
-rw-r--r--fs/cifs/misc.c74
6 files changed, 249 insertions, 218 deletions
diff --git a/fs/cifs/cifs_debug.c b/fs/cifs/cifs_debug.c
index 59841a68b0b6..1d6dfa8923ca 100644
--- a/fs/cifs/cifs_debug.c
+++ b/fs/cifs/cifs_debug.c
@@ -107,12 +107,13 @@ 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, *tmp2, *tmp3; 110 struct list_head *tmp1, *tmp2, *tmp3;
111 struct mid_q_entry *mid_entry; 111 struct mid_q_entry *mid_entry;
112 struct TCP_Server_Info *server; 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, j;
116 __u32 dev_type;
116 117
117 seq_puts(m, 118 seq_puts(m,
118 "Display Internal CIFS Data Structures for Debugging\n" 119 "Display Internal CIFS Data Structures for Debugging\n"
@@ -123,8 +124,8 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
123 124
124 i = 0; 125 i = 0;
125 read_lock(&cifs_tcp_ses_lock); 126 read_lock(&cifs_tcp_ses_lock);
126 list_for_each(tmp, &cifs_tcp_ses_list) { 127 list_for_each(tmp1, &cifs_tcp_ses_list) {
127 server = list_entry(tmp, struct TCP_Server_Info, 128 server = list_entry(tmp1, struct TCP_Server_Info,
128 tcp_ses_list); 129 tcp_ses_list);
129 i++; 130 i++;
130 list_for_each(tmp2, &server->smb_ses_list) { 131 list_for_each(tmp2, &server->smb_ses_list) {
@@ -133,12 +134,12 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
133 if ((ses->serverDomain == NULL) || 134 if ((ses->serverDomain == NULL) ||
134 (ses->serverOS == NULL) || 135 (ses->serverOS == NULL) ||
135 (ses->serverNOS == NULL)) { 136 (ses->serverNOS == NULL)) {
136 seq_printf(m, "\nentry for %s not fully " 137 seq_printf(m, "\n%d) entry for %s not fully "
137 "displayed\n\t", ses->serverName); 138 "displayed\n\t", i, ses->serverName);
138 } else { 139 } else {
139 seq_printf(m, 140 seq_printf(m,
140 "\n%d) Name: %s Domain: %s Mounts: %d OS:" 141 "\n%d) Name: %s Domain: %s Uses: %d OS:"
141 " %s \n\tNOS: %s\tCapability: 0x%x\n\tSMB" 142 " %s\n\tNOS: %s\tCapability: 0x%x\n\tSMB"
142 " session status: %d\t", 143 " session status: %d\t",
143 i, ses->serverName, ses->serverDomain, 144 i, ses->serverName, ses->serverDomain,
144 ses->ses_count, ses->serverOS, ses->serverNOS, 145 ses->ses_count, ses->serverOS, ses->serverNOS,
@@ -156,14 +157,44 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
156 atomic_read(&server->num_waiters)); 157 atomic_read(&server->num_waiters));
157#endif 158#endif
158 159
159 seq_puts(m, "\nMIDs:\n"); 160 seq_puts(m, "\n\tShares:");
161 j = 0;
162 list_for_each(tmp3, &ses->tcon_list) {
163 tcon = list_entry(tmp3, struct cifsTconInfo,
164 tcon_list);
165 ++j;
166 dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
167 seq_printf(m, "\n\t%d) %s Mounts: %d ", j,
168 tcon->treeName, tcon->tc_count);
169 if (tcon->nativeFileSystem) {
170 seq_printf(m, "Type: %s ",
171 tcon->nativeFileSystem);
172 }
173 seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
174 "\nPathComponentMax: %d Status: 0x%d",
175 le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
176 le32_to_cpu(tcon->fsAttrInfo.Attributes),
177 le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
178 tcon->tidStatus);
179 if (dev_type == FILE_DEVICE_DISK)
180 seq_puts(m, " type: DISK ");
181 else if (dev_type == FILE_DEVICE_CD_ROM)
182 seq_puts(m, " type: CDROM ");
183 else
184 seq_printf(m, " type: %d ", dev_type);
185
186 if (tcon->need_reconnect)
187 seq_puts(m, "\tDISCONNECTED ");
188 seq_putc(m, '\n');
189 }
190
191 seq_puts(m, "\n\tMIDs:\n");
160 192
161 spin_lock(&GlobalMid_Lock); 193 spin_lock(&GlobalMid_Lock);
162 list_for_each(tmp3, &server->pending_mid_q) { 194 list_for_each(tmp3, &server->pending_mid_q) {
163 mid_entry = list_entry(tmp3, struct 195 mid_entry = list_entry(tmp3, struct mid_q_entry,
164 mid_q_entry,
165 qhead); 196 qhead);
166 seq_printf(m, "State: %d com: %d pid:" 197 seq_printf(m, "\tState: %d com: %d pid:"
167 " %d tsk: %p mid %d\n", 198 " %d tsk: %p mid %d\n",
168 mid_entry->midState, 199 mid_entry->midState,
169 (int)mid_entry->command, 200 (int)mid_entry->command,
@@ -177,41 +208,6 @@ static int cifs_debug_data_proc_show(struct seq_file *m, void *v)
177 read_unlock(&cifs_tcp_ses_lock); 208 read_unlock(&cifs_tcp_ses_lock);
178 seq_putc(m, '\n'); 209 seq_putc(m, '\n');
179 210
180 seq_puts(m, "Shares:");
181
182 i = 0;
183 read_lock(&GlobalSMBSeslock);
184 list_for_each(tmp, &GlobalTreeConnectionList) {
185 __u32 dev_type;
186 i++;
187 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
188 dev_type = le32_to_cpu(tcon->fsDevInfo.DeviceType);
189 seq_printf(m, "\n%d) %s Uses: %d ", i,
190 tcon->treeName, atomic_read(&tcon->useCount));
191 if (tcon->nativeFileSystem) {
192 seq_printf(m, "Type: %s ",
193 tcon->nativeFileSystem);
194 }
195 seq_printf(m, "DevInfo: 0x%x Attributes: 0x%x"
196 "\nPathComponentMax: %d Status: %d",
197 le32_to_cpu(tcon->fsDevInfo.DeviceCharacteristics),
198 le32_to_cpu(tcon->fsAttrInfo.Attributes),
199 le32_to_cpu(tcon->fsAttrInfo.MaxPathNameComponentLength),
200 tcon->tidStatus);
201 if (dev_type == FILE_DEVICE_DISK)
202 seq_puts(m, " type: DISK ");
203 else if (dev_type == FILE_DEVICE_CD_ROM)
204 seq_puts(m, " type: CDROM ");
205 else
206 seq_printf(m, " type: %d ", dev_type);
207
208 if (tcon->need_reconnect)
209 seq_puts(m, "\tDISCONNECTED ");
210 }
211 read_unlock(&GlobalSMBSeslock);
212
213 seq_putc(m, '\n');
214
215 /* BB add code to dump additional info such as TCP session info now */ 211 /* BB add code to dump additional info such as TCP session info now */
216 return 0; 212 return 0;
217} 213}
@@ -235,7 +231,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
235{ 231{
236 char c; 232 char c;
237 int rc; 233 int rc;
238 struct list_head *tmp; 234 struct list_head *tmp1, *tmp2, *tmp3;
235 struct TCP_Server_Info *server;
236 struct cifsSesInfo *ses;
239 struct cifsTconInfo *tcon; 237 struct cifsTconInfo *tcon;
240 238
241 rc = get_user(c, buffer); 239 rc = get_user(c, buffer);
@@ -243,33 +241,42 @@ static ssize_t cifs_stats_proc_write(struct file *file,
243 return rc; 241 return rc;
244 242
245 if (c == '1' || c == 'y' || c == 'Y' || c == '0') { 243 if (c == '1' || c == 'y' || c == 'Y' || c == '0') {
246 read_lock(&GlobalSMBSeslock);
247#ifdef CONFIG_CIFS_STATS2 244#ifdef CONFIG_CIFS_STATS2
248 atomic_set(&totBufAllocCount, 0); 245 atomic_set(&totBufAllocCount, 0);
249 atomic_set(&totSmBufAllocCount, 0); 246 atomic_set(&totSmBufAllocCount, 0);
250#endif /* CONFIG_CIFS_STATS2 */ 247#endif /* CONFIG_CIFS_STATS2 */
251 list_for_each(tmp, &GlobalTreeConnectionList) { 248 read_lock(&cifs_tcp_ses_lock);
252 tcon = list_entry(tmp, struct cifsTconInfo, 249 list_for_each(tmp1, &cifs_tcp_ses_list) {
253 cifsConnectionList); 250 server = list_entry(tmp1, struct TCP_Server_Info,
254 atomic_set(&tcon->num_smbs_sent, 0); 251 tcp_ses_list);
255 atomic_set(&tcon->num_writes, 0); 252 list_for_each(tmp2, &server->smb_session_list) {
256 atomic_set(&tcon->num_reads, 0); 253 ses = list_entry(tmp2, struct cifsSesInfo,
257 atomic_set(&tcon->num_oplock_brks, 0); 254 smb_session_list);
258 atomic_set(&tcon->num_opens, 0); 255 list_for_each(tmp3, &ses->tcon_list) {
259 atomic_set(&tcon->num_closes, 0); 256 tcon = list_entry(tmp3,
260 atomic_set(&tcon->num_deletes, 0); 257 struct cifsTconInfo,
261 atomic_set(&tcon->num_mkdirs, 0); 258 tcon_list);
262 atomic_set(&tcon->num_rmdirs, 0); 259 atomic_set(&tcon->num_smbs_sent, 0);
263 atomic_set(&tcon->num_renames, 0); 260 atomic_set(&tcon->num_writes, 0);
264 atomic_set(&tcon->num_t2renames, 0); 261 atomic_set(&tcon->num_reads, 0);
265 atomic_set(&tcon->num_ffirst, 0); 262 atomic_set(&tcon->num_oplock_brks, 0);
266 atomic_set(&tcon->num_fnext, 0); 263 atomic_set(&tcon->num_opens, 0);
267 atomic_set(&tcon->num_fclose, 0); 264 atomic_set(&tcon->num_closes, 0);
268 atomic_set(&tcon->num_hardlinks, 0); 265 atomic_set(&tcon->num_deletes, 0);
269 atomic_set(&tcon->num_symlinks, 0); 266 atomic_set(&tcon->num_mkdirs, 0);
270 atomic_set(&tcon->num_locks, 0); 267 atomic_set(&tcon->num_rmdirs, 0);
268 atomic_set(&tcon->num_renames, 0);
269 atomic_set(&tcon->num_t2renames, 0);
270 atomic_set(&tcon->num_ffirst, 0);
271 atomic_set(&tcon->num_fnext, 0);
272 atomic_set(&tcon->num_fclose, 0);
273 atomic_set(&tcon->num_hardlinks, 0);
274 atomic_set(&tcon->num_symlinks, 0);
275 atomic_set(&tcon->num_locks, 0);
276 }
277 }
271 } 278 }
272 read_unlock(&GlobalSMBSeslock); 279 read_unlock(&cifs_tcp_ses_lock);
273 } 280 }
274 281
275 return count; 282 return count;
@@ -278,7 +285,9 @@ static ssize_t cifs_stats_proc_write(struct file *file,
278static int cifs_stats_proc_show(struct seq_file *m, void *v) 285static int cifs_stats_proc_show(struct seq_file *m, void *v)
279{ 286{
280 int i; 287 int i;
281 struct list_head *tmp; 288 struct list_head *tmp1, *tmp2, *tmp3;
289 struct TCP_Server_Info *server;
290 struct cifsSesInfo *ses;
282 struct cifsTconInfo *tcon; 291 struct cifsTconInfo *tcon;
283 292
284 seq_printf(m, 293 seq_printf(m,
@@ -307,44 +316,55 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
307 GlobalCurrentXid, GlobalMaxActiveXid); 316 GlobalCurrentXid, GlobalMaxActiveXid);
308 317
309 i = 0; 318 i = 0;
310 read_lock(&GlobalSMBSeslock); 319 read_lock(&cifs_tcp_ses_lock);
311 list_for_each(tmp, &GlobalTreeConnectionList) { 320 list_for_each(tmp1, &cifs_tcp_ses_list) {
312 i++; 321 server = list_entry(tmp1, struct TCP_Server_Info,
313 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); 322 tcp_ses_list);
314 seq_printf(m, "\n%d) %s", i, tcon->treeName); 323 list_for_each(tmp2, &server->smb_ses_list) {
315 if (tcon->need_reconnect) 324 ses = list_entry(tmp2, struct cifsSesInfo,
316 seq_puts(m, "\tDISCONNECTED "); 325 smb_ses_list);
317 seq_printf(m, "\nSMBs: %d Oplock Breaks: %d", 326 list_for_each(tmp3, &ses->tcon_list) {
318 atomic_read(&tcon->num_smbs_sent), 327 tcon = list_entry(tmp3,
319 atomic_read(&tcon->num_oplock_brks)); 328 struct cifsTconInfo,
320 seq_printf(m, "\nReads: %d Bytes: %lld", 329 tcon_list);
321 atomic_read(&tcon->num_reads), 330 i++;
322 (long long)(tcon->bytes_read)); 331 seq_printf(m, "\n%d) %s", i, tcon->treeName);
323 seq_printf(m, "\nWrites: %d Bytes: %lld", 332 if (tcon->need_reconnect)
324 atomic_read(&tcon->num_writes), 333 seq_puts(m, "\tDISCONNECTED ");
325 (long long)(tcon->bytes_written)); 334 seq_printf(m, "\nSMBs: %d Oplock Breaks: %d",
326 seq_printf(m, 335 atomic_read(&tcon->num_smbs_sent),
327 "\nLocks: %d HardLinks: %d Symlinks: %d", 336 atomic_read(&tcon->num_oplock_brks));
328 atomic_read(&tcon->num_locks), 337 seq_printf(m, "\nReads: %d Bytes: %lld",
329 atomic_read(&tcon->num_hardlinks), 338 atomic_read(&tcon->num_reads),
330 atomic_read(&tcon->num_symlinks)); 339 (long long)(tcon->bytes_read));
331 340 seq_printf(m, "\nWrites: %d Bytes: %lld",
332 seq_printf(m, "\nOpens: %d Closes: %d Deletes: %d", 341 atomic_read(&tcon->num_writes),
333 atomic_read(&tcon->num_opens), 342 (long long)(tcon->bytes_written));
334 atomic_read(&tcon->num_closes), 343 seq_printf(m, "\nLocks: %d HardLinks: %d "
335 atomic_read(&tcon->num_deletes)); 344 "Symlinks: %d",
336 seq_printf(m, "\nMkdirs: %d Rmdirs: %d", 345 atomic_read(&tcon->num_locks),
337 atomic_read(&tcon->num_mkdirs), 346 atomic_read(&tcon->num_hardlinks),
338 atomic_read(&tcon->num_rmdirs)); 347 atomic_read(&tcon->num_symlinks));
339 seq_printf(m, "\nRenames: %d T2 Renames %d", 348 seq_printf(m, "\nOpens: %d Closes: %d"
340 atomic_read(&tcon->num_renames), 349 "Deletes: %d",
341 atomic_read(&tcon->num_t2renames)); 350 atomic_read(&tcon->num_opens),
342 seq_printf(m, "\nFindFirst: %d FNext %d FClose %d", 351 atomic_read(&tcon->num_closes),
343 atomic_read(&tcon->num_ffirst), 352 atomic_read(&tcon->num_deletes));
344 atomic_read(&tcon->num_fnext), 353 seq_printf(m, "\nMkdirs: %d Rmdirs: %d",
345 atomic_read(&tcon->num_fclose)); 354 atomic_read(&tcon->num_mkdirs),
355 atomic_read(&tcon->num_rmdirs));
356 seq_printf(m, "\nRenames: %d T2 Renames %d",
357 atomic_read(&tcon->num_renames),
358 atomic_read(&tcon->num_t2renames));
359 seq_printf(m, "\nFindFirst: %d FNext %d "
360 "FClose %d",
361 atomic_read(&tcon->num_ffirst),
362 atomic_read(&tcon->num_fnext),
363 atomic_read(&tcon->num_fclose));
364 }
365 }
346 } 366 }
347 read_unlock(&GlobalSMBSeslock); 367 read_unlock(&cifs_tcp_ses_lock);
348 368
349 seq_putc(m, '\n'); 369 seq_putc(m, '\n');
350 return 0; 370 return 0;
diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
index a1e96620b097..d9cf467309e8 100644
--- a/fs/cifs/cifsfs.c
+++ b/fs/cifs/cifsfs.c
@@ -514,10 +514,11 @@ static void cifs_umount_begin(struct super_block *sb)
514 tcon = cifs_sb->tcon; 514 tcon = cifs_sb->tcon;
515 if (tcon == NULL) 515 if (tcon == NULL)
516 return; 516 return;
517 down(&tcon->tconSem); 517
518 if (atomic_read(&tcon->useCount) == 1) 518 read_lock(&cifs_tcp_ses_lock);
519 if (tcon->tc_count == 1)
519 tcon->tidStatus = CifsExiting; 520 tcon->tidStatus = CifsExiting;
520 up(&tcon->tconSem); 521 read_unlock(&cifs_tcp_ses_lock);
521 522
522 /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */ 523 /* cancel_brl_requests(tcon); */ /* BB mark all brl mids as exiting */
523 /* cancel_notify_requests(tcon); */ 524 /* cancel_notify_requests(tcon); */
@@ -1060,7 +1061,6 @@ init_cifs(void)
1060 int rc = 0; 1061 int rc = 0;
1061 cifs_proc_init(); 1062 cifs_proc_init();
1062 INIT_LIST_HEAD(&cifs_tcp_ses_list); 1063 INIT_LIST_HEAD(&cifs_tcp_ses_list);
1063 INIT_LIST_HEAD(&GlobalTreeConnectionList); /* BB to be removed by jl */
1064 INIT_LIST_HEAD(&GlobalOplock_Q); 1064 INIT_LIST_HEAD(&GlobalOplock_Q);
1065#ifdef CONFIG_CIFS_EXPERIMENTAL 1065#ifdef CONFIG_CIFS_EXPERIMENTAL
1066 INIT_LIST_HEAD(&GlobalDnotifyReqList); 1066 INIT_LIST_HEAD(&GlobalDnotifyReqList);
diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
index 631a99f72f22..f1ae1f57c30d 100644
--- a/fs/cifs/cifsglob.h
+++ b/fs/cifs/cifsglob.h
@@ -233,16 +233,15 @@ struct cifsSesInfo {
233 * session 233 * session
234 */ 234 */
235struct cifsTconInfo { 235struct cifsTconInfo {
236 struct list_head cifsConnectionList; 236 struct list_head tcon_list;
237 int tc_count;
237 struct list_head openFileList; 238 struct list_head openFileList;
238 struct semaphore tconSem;
239 struct cifsSesInfo *ses; /* pointer to session associated with */ 239 struct cifsSesInfo *ses; /* pointer to session associated with */
240 char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */ 240 char treeName[MAX_TREE_SIZE + 1]; /* UNC name of resource in ASCII */
241 char *nativeFileSystem; 241 char *nativeFileSystem;
242 __u16 tid; /* The 2 byte tree id */ 242 __u16 tid; /* The 2 byte tree id */
243 __u16 Flags; /* optional support bits */ 243 __u16 Flags; /* optional support bits */
244 enum statusEnum tidStatus; 244 enum statusEnum tidStatus;
245 atomic_t useCount; /* how many explicit/implicit mounts to share */
246#ifdef CONFIG_CIFS_STATS 245#ifdef CONFIG_CIFS_STATS
247 atomic_t num_smbs_sent; 246 atomic_t num_smbs_sent;
248 atomic_t num_writes; 247 atomic_t num_writes;
@@ -600,9 +599,13 @@ require use of the stronger protocol */
600 */ 599 */
601GLOBAL_EXTERN struct list_head cifs_tcp_ses_list; 600GLOBAL_EXTERN struct list_head cifs_tcp_ses_list;
602 601
603/* protects cifs_tcp_ses_list and srv_count for each tcp session */ 602/*
603 * This lock protects the cifs_tcp_ses_list, the list of smb sessions per
604 * tcp session, and the list of tcon's per smb session. It also protects
605 * the reference counters for the server, smb session, and tcon. Finally,
606 * changes to the tcon->tidStatus should be done while holding this lock.
607 */
604GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; 608GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock;
605GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */
606GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ 609GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */
607 610
608GLOBAL_EXTERN struct list_head GlobalOplock_Q; 611GLOBAL_EXTERN struct list_head GlobalOplock_Q;
diff --git a/fs/cifs/cifssmb.c b/fs/cifs/cifssmb.c
index 9c95617baa4d..e6bb2d9d5b09 100644
--- a/fs/cifs/cifssmb.c
+++ b/fs/cifs/cifssmb.c
@@ -742,50 +742,31 @@ CIFSSMBTDis(const int xid, struct cifsTconInfo *tcon)
742 int rc = 0; 742 int rc = 0;
743 743
744 cFYI(1, ("In tree disconnect")); 744 cFYI(1, ("In tree disconnect"));
745 /*
746 * If last user of the connection and
747 * connection alive - disconnect it
748 * If this is the last connection on the server session disconnect it
749 * (and inside session disconnect we should check if tcp socket needs
750 * to be freed and kernel thread woken up).
751 */
752 if (tcon)
753 down(&tcon->tconSem);
754 else
755 return -EIO;
756 745
757 atomic_dec(&tcon->useCount); 746 /* BB: do we need to check this? These should never be NULL. */
758 if (atomic_read(&tcon->useCount) > 0) { 747 if ((tcon->ses == NULL) || (tcon->ses->server == NULL))
759 up(&tcon->tconSem); 748 return -EIO;
760 return -EBUSY;
761 }
762 749
763 /* No need to return error on this operation if tid invalidated and 750 /*
764 closed on server already e.g. due to tcp session crashing */ 751 * No need to return error on this operation if tid invalidated and
765 if (tcon->need_reconnect) { 752 * closed on server already e.g. due to tcp session crashing. Also,
766 up(&tcon->tconSem); 753 * the tcon is no longer on the list, so no need to take lock before
754 * checking this.
755 */
756 if (tcon->need_reconnect)
767 return 0; 757 return 0;
768 }
769 758
770 if ((tcon->ses == NULL) || (tcon->ses->server == NULL)) {
771 up(&tcon->tconSem);
772 return -EIO;
773 }
774 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon, 759 rc = small_smb_init(SMB_COM_TREE_DISCONNECT, 0, tcon,
775 (void **)&smb_buffer); 760 (void **)&smb_buffer);
776 if (rc) { 761 if (rc)
777 up(&tcon->tconSem);
778 return rc; 762 return rc;
779 }
780 763
781 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0); 764 rc = SendReceiveNoRsp(xid, tcon->ses, smb_buffer, 0);
782 if (rc) 765 if (rc)
783 cFYI(1, ("Tree disconnect failed %d", rc)); 766 cFYI(1, ("Tree disconnect failed %d", rc));
784 767
785 up(&tcon->tconSem);
786
787 /* No need to return error on this operation if tid invalidated and 768 /* No need to return error on this operation if tid invalidated and
788 closed on server already e.g. due to tcp session crashing */ 769 closed on server already e.g. due to tcp session crashing */
789 if (rc == -EAGAIN) 770 if (rc == -EAGAIN)
790 rc = 0; 771 rc = 0;
791 772
diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
index a3dc0d7cafc3..2f2be8faabb3 100644
--- a/fs/cifs/connect.c
+++ b/fs/cifs/connect.c
@@ -124,7 +124,7 @@ static int
124cifs_reconnect(struct TCP_Server_Info *server) 124cifs_reconnect(struct TCP_Server_Info *server)
125{ 125{
126 int rc = 0; 126 int rc = 0;
127 struct list_head *tmp; 127 struct list_head *tmp, *tmp2;
128 struct cifsSesInfo *ses; 128 struct cifsSesInfo *ses;
129 struct cifsTconInfo *tcon; 129 struct cifsTconInfo *tcon;
130 struct mid_q_entry *mid_entry; 130 struct mid_q_entry *mid_entry;
@@ -149,13 +149,12 @@ cifs_reconnect(struct TCP_Server_Info *server)
149 ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list); 149 ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
150 ses->need_reconnect = true; 150 ses->need_reconnect = true;
151 ses->ipc_tid = 0; 151 ses->ipc_tid = 0;
152 } 152 list_for_each(tmp2, &ses->tcon_list) {
153 read_unlock(&cifs_tcp_ses_lock); 153 tcon = list_entry(tmp2, struct cifsTconInfo, tcon_list);
154 list_for_each(tmp, &GlobalTreeConnectionList) {
155 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList);
156 if ((tcon->ses) && (tcon->ses->server == server))
157 tcon->need_reconnect = true; 154 tcon->need_reconnect = true;
155 }
158 } 156 }
157 read_unlock(&cifs_tcp_ses_lock);
159 /* do not want to be sending data on a socket we are freeing */ 158 /* do not want to be sending data on a socket we are freeing */
160 down(&server->tcpSem); 159 down(&server->tcpSem);
161 if (server->ssocket) { 160 if (server->ssocket) {
@@ -1462,6 +1461,52 @@ cifs_put_smb_ses(struct cifsSesInfo *ses)
1462 cifs_put_tcp_session(server); 1461 cifs_put_tcp_session(server);
1463} 1462}
1464 1463
1464static struct cifsTconInfo *
1465cifs_find_tcon(struct cifsSesInfo *ses, const char *unc)
1466{
1467 struct list_head *tmp;
1468 struct cifsTconInfo *tcon;
1469
1470 write_lock(&cifs_tcp_ses_lock);
1471 list_for_each(tmp, &ses->tcon_list) {
1472 tcon = list_entry(tmp, struct cifsTconInfo, tcon_list);
1473 if (tcon->tidStatus == CifsExiting)
1474 continue;
1475 if (strncmp(tcon->treeName, unc, MAX_TREE_SIZE))
1476 continue;
1477
1478 ++tcon->tc_count;
1479 write_unlock(&cifs_tcp_ses_lock);
1480 return tcon;
1481 }
1482 write_unlock(&cifs_tcp_ses_lock);
1483 return NULL;
1484}
1485
1486static void
1487cifs_put_tcon(struct cifsTconInfo *tcon)
1488{
1489 int xid;
1490 struct cifsSesInfo *ses = tcon->ses;
1491
1492 write_lock(&cifs_tcp_ses_lock);
1493 if (--tcon->tc_count > 0) {
1494 write_unlock(&cifs_tcp_ses_lock);
1495 return;
1496 }
1497
1498 list_del_init(&tcon->tcon_list);
1499 write_unlock(&cifs_tcp_ses_lock);
1500
1501 xid = GetXid();
1502 CIFSSMBTDis(xid, tcon);
1503 _FreeXid(xid);
1504
1505 DeleteTconOplockQEntries(tcon);
1506 tconInfoFree(tcon);
1507 cifs_put_smb_ses(ses);
1508}
1509
1465int 1510int
1466get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, 1511get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path,
1467 const struct nls_table *nls_codepage, unsigned int *pnum_referrals, 1512 const struct nls_table *nls_codepage, unsigned int *pnum_referrals,
@@ -2220,11 +2265,11 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
2220 if (!rc) { 2265 if (!rc) {
2221 setup_cifs_sb(&volume_info, cifs_sb); 2266 setup_cifs_sb(&volume_info, cifs_sb);
2222 2267
2268 tcon = cifs_find_tcon(pSesInfo, volume_info.UNC);
2223 if (tcon) { 2269 if (tcon) {
2224 cFYI(1, ("Found match on UNC path")); 2270 cFYI(1, ("Found match on UNC path"));
2225 if (tcon->seal != volume_info.seal) 2271 /* existing tcon already has a reference */
2226 cERROR(1, ("transport encryption setting " 2272 cifs_put_smb_ses(pSesInfo);
2227 "conflicts with existing tid"));
2228 } else { 2273 } else {
2229 tcon = tconInfoAlloc(); 2274 tcon = tconInfoAlloc();
2230 if (tcon == NULL) { 2275 if (tcon == NULL) {
@@ -2257,6 +2302,10 @@ cifs_mount(struct super_block *sb, struct cifs_sb_info *cifs_sb,
2257 if (rc) 2302 if (rc)
2258 goto mount_fail_check; 2303 goto mount_fail_check;
2259 tcon->seal = volume_info.seal; 2304 tcon->seal = volume_info.seal;
2305 tcon->ses = pSesInfo;
2306 write_lock(&cifs_tcp_ses_lock);
2307 list_add(&tcon->tcon_list, &pSesInfo->tcon_list);
2308 write_unlock(&cifs_tcp_ses_lock);
2260 } 2309 }
2261 2310
2262 /* we can have only one retry value for a connection 2311 /* we can have only one retry value for a connection
@@ -2283,18 +2332,14 @@ mount_fail_check:
2283 /* If find_unc succeeded then rc == 0 so we can not end */ 2332 /* If find_unc succeeded then rc == 0 so we can not end */
2284 /* up accidently freeing someone elses tcon struct */ 2333 /* up accidently freeing someone elses tcon struct */
2285 if (tcon) 2334 if (tcon)
2286 tconInfoFree(tcon); 2335 cifs_put_tcon(tcon);
2287 2336 else if (pSesInfo)
2288 /* should also end up putting our tcp session ref if needed */
2289 if (pSesInfo)
2290 cifs_put_smb_ses(pSesInfo); 2337 cifs_put_smb_ses(pSesInfo);
2291 else 2338 else
2292 cifs_put_tcp_session(srvTcp); 2339 cifs_put_tcp_session(srvTcp);
2293 goto out; 2340 goto out;
2294 } 2341 }
2295 atomic_inc(&tcon->useCount);
2296 cifs_sb->tcon = tcon; 2342 cifs_sb->tcon = tcon;
2297 tcon->ses = pSesInfo;
2298 2343
2299 /* do not care if following two calls succeed - informational */ 2344 /* do not care if following two calls succeed - informational */
2300 if (!tcon->ipc) { 2345 if (!tcon->ipc) {
@@ -3565,23 +3610,10 @@ int
3565cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) 3610cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
3566{ 3611{
3567 int rc = 0; 3612 int rc = 0;
3568 int xid;
3569 struct cifsSesInfo *ses = NULL;
3570 char *tmp; 3613 char *tmp;
3571 3614
3572 xid = GetXid(); 3615 if (cifs_sb->tcon)
3573 3616 cifs_put_tcon(cifs_sb->tcon);
3574 if (cifs_sb->tcon) {
3575 ses = cifs_sb->tcon->ses; /* save ptr to ses before delete tcon!*/
3576 rc = CIFSSMBTDis(xid, cifs_sb->tcon);
3577 if (rc == -EBUSY) {
3578 FreeXid(xid);
3579 return 0;
3580 }
3581 DeleteTconOplockQEntries(cifs_sb->tcon);
3582 tconInfoFree(cifs_sb->tcon);
3583 cifs_put_smb_ses(ses);
3584 }
3585 3617
3586 cifs_sb->tcon = NULL; 3618 cifs_sb->tcon = NULL;
3587 tmp = cifs_sb->prepath; 3619 tmp = cifs_sb->prepath;
@@ -3589,7 +3621,6 @@ cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb)
3589 cifs_sb->prepath = NULL; 3621 cifs_sb->prepath = NULL;
3590 kfree(tmp); 3622 kfree(tmp);
3591 3623
3592 FreeXid(xid);
3593 return rc; 3624 return rc;
3594} 3625}
3595 3626
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
index 46c8c7baccba..addd1dcc2d79 100644
--- a/fs/cifs/misc.c
+++ b/fs/cifs/misc.c
@@ -79,6 +79,7 @@ sesInfoAlloc(void)
79 ret_buf->status = CifsNew; 79 ret_buf->status = CifsNew;
80 ++ret_buf->ses_count; 80 ++ret_buf->ses_count;
81 INIT_LIST_HEAD(&ret_buf->smb_ses_list); 81 INIT_LIST_HEAD(&ret_buf->smb_ses_list);
82 INIT_LIST_HEAD(&ret_buf->tcon_list);
82 init_MUTEX(&ret_buf->sesSem); 83 init_MUTEX(&ret_buf->sesSem);
83 } 84 }
84 return ret_buf; 85 return ret_buf;
@@ -107,17 +108,14 @@ tconInfoAlloc(void)
107 struct cifsTconInfo *ret_buf; 108 struct cifsTconInfo *ret_buf;
108 ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL); 109 ret_buf = kzalloc(sizeof(struct cifsTconInfo), GFP_KERNEL);
109 if (ret_buf) { 110 if (ret_buf) {
110 write_lock(&GlobalSMBSeslock);
111 atomic_inc(&tconInfoAllocCount); 111 atomic_inc(&tconInfoAllocCount);
112 list_add(&ret_buf->cifsConnectionList,
113 &GlobalTreeConnectionList);
114 ret_buf->tidStatus = CifsNew; 112 ret_buf->tidStatus = CifsNew;
113 ++ret_buf->tc_count;
115 INIT_LIST_HEAD(&ret_buf->openFileList); 114 INIT_LIST_HEAD(&ret_buf->openFileList);
116 init_MUTEX(&ret_buf->tconSem); 115 INIT_LIST_HEAD(&ret_buf->tcon_list);
117#ifdef CONFIG_CIFS_STATS 116#ifdef CONFIG_CIFS_STATS
118 spin_lock_init(&ret_buf->stat_lock); 117 spin_lock_init(&ret_buf->stat_lock);
119#endif 118#endif
120 write_unlock(&GlobalSMBSeslock);
121 } 119 }
122 return ret_buf; 120 return ret_buf;
123} 121}
@@ -129,10 +127,7 @@ tconInfoFree(struct cifsTconInfo *buf_to_free)
129 cFYI(1, ("Null buffer passed to tconInfoFree")); 127 cFYI(1, ("Null buffer passed to tconInfoFree"));
130 return; 128 return;
131 } 129 }
132 write_lock(&GlobalSMBSeslock);
133 atomic_dec(&tconInfoAllocCount); 130 atomic_dec(&tconInfoAllocCount);
134 list_del(&buf_to_free->cifsConnectionList);
135 write_unlock(&GlobalSMBSeslock);
136 kfree(buf_to_free->nativeFileSystem); 131 kfree(buf_to_free->nativeFileSystem);
137 kfree(buf_to_free); 132 kfree(buf_to_free);
138} 133}
@@ -493,9 +488,10 @@ bool
493is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) 488is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
494{ 489{
495 struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf; 490 struct smb_com_lock_req *pSMB = (struct smb_com_lock_req *)buf;
496 struct list_head *tmp; 491 struct list_head *tmp, *tmp1, *tmp2;
497 struct list_head *tmp1; 492 struct cifsSesInfo *ses;
498 struct cifsTconInfo *tcon; 493 struct cifsTconInfo *tcon;
494 struct cifsInodeInfo *pCifsInode;
499 struct cifsFileInfo *netfile; 495 struct cifsFileInfo *netfile;
500 496
501 cFYI(1, ("Checking for oplock break or dnotify response")); 497 cFYI(1, ("Checking for oplock break or dnotify response"));
@@ -550,42 +546,42 @@ is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv)
550 return false; 546 return false;
551 547
552 /* look up tcon based on tid & uid */ 548 /* look up tcon based on tid & uid */
553 read_lock(&GlobalSMBSeslock); 549 read_lock(&cifs_tcp_ses_lock);
554 list_for_each(tmp, &GlobalTreeConnectionList) { 550 list_for_each(tmp, &srv->smb_ses_list) {
555 tcon = list_entry(tmp, struct cifsTconInfo, cifsConnectionList); 551 ses = list_entry(tmp, struct cifsSesInfo, smb_ses_list);
556 if ((tcon->tid == buf->Tid) && (srv == tcon->ses->server)) { 552 list_for_each(tmp1, &ses->tcon_list) {
553 tcon = list_entry(tmp1, struct cifsTconInfo, tcon_list);
554 if (tcon->tid != buf->Tid)
555 continue;
556
557 cifs_stats_inc(&tcon->num_oplock_brks); 557 cifs_stats_inc(&tcon->num_oplock_brks);
558 list_for_each(tmp1, &tcon->openFileList) { 558 list_for_each(tmp2, &tcon->openFileList) {
559 netfile = list_entry(tmp1, struct cifsFileInfo, 559 netfile = list_entry(tmp2, struct cifsFileInfo,
560 tlist); 560 tlist);
561 if (pSMB->Fid == netfile->netfid) { 561 if (pSMB->Fid != netfile->netfid)
562 struct cifsInodeInfo *pCifsInode; 562 continue;
563 read_unlock(&GlobalSMBSeslock); 563
564 cFYI(1, 564 read_unlock(&cifs_tcp_ses_lock);
565 ("file id match, oplock break")); 565 cFYI(1, ("file id match, oplock break"));
566 pCifsInode = 566 pCifsInode = CIFS_I(netfile->pInode);
567 CIFS_I(netfile->pInode); 567 pCifsInode->clientCanCacheAll = false;
568 pCifsInode->clientCanCacheAll = false; 568 if (pSMB->OplockLevel == 0)
569 if (pSMB->OplockLevel == 0) 569 pCifsInode->clientCanCacheRead = false;
570 pCifsInode->clientCanCacheRead 570 pCifsInode->oplockPending = true;
571 = false; 571 AllocOplockQEntry(netfile->pInode,
572 pCifsInode->oplockPending = true; 572 netfile->netfid, tcon);
573 AllocOplockQEntry(netfile->pInode, 573 cFYI(1, ("about to wake up oplock thread"));
574 netfile->netfid, 574 if (oplockThread)
575 tcon); 575 wake_up_process(oplockThread);
576 cFYI(1, 576
577 ("about to wake up oplock thread")); 577 return true;
578 if (oplockThread)
579 wake_up_process(oplockThread);
580 return true;
581 }
582 } 578 }
583 read_unlock(&GlobalSMBSeslock); 579 read_unlock(&cifs_tcp_ses_lock);
584 cFYI(1, ("No matching file for oplock break")); 580 cFYI(1, ("No matching file for oplock break"));
585 return true; 581 return true;
586 } 582 }
587 } 583 }
588 read_unlock(&GlobalSMBSeslock); 584 read_unlock(&cifs_tcp_ses_lock);
589 cFYI(1, ("Can not process oplock break for non-existent connection")); 585 cFYI(1, ("Can not process oplock break for non-existent connection"));
590 return true; 586 return true;
591} 587}