diff options
author | Jeff Layton <jlayton@redhat.com> | 2008-11-15 11:12:47 -0500 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2008-11-16 22:14:12 -0500 |
commit | f1987b44f642e96176adc88b7ce23a1d74806f89 (patch) | |
tree | fceaebf6b6d7eb1d1150120c44a842cbce8347f6 | |
parent | d82c2df54e2f7e447476350848d8eccc8d2fe46a (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.c | 236 | ||||
-rw-r--r-- | fs/cifs/cifsfs.c | 8 | ||||
-rw-r--r-- | fs/cifs/cifsglob.h | 13 | ||||
-rw-r--r-- | fs/cifs/cifssmb.c | 43 | ||||
-rw-r--r-- | fs/cifs/connect.c | 93 | ||||
-rw-r--r-- | fs/cifs/misc.c | 74 |
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 |
108 | static int cifs_debug_data_proc_show(struct seq_file *m, void *v) | 108 | static 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, | |||
278 | static int cifs_stats_proc_show(struct seq_file *m, void *v) | 285 | static 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 | */ |
235 | struct cifsTconInfo { | 235 | struct 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 | */ |
601 | GLOBAL_EXTERN struct list_head cifs_tcp_ses_list; | 600 | GLOBAL_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 | */ | ||
604 | GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; | 608 | GLOBAL_EXTERN rwlock_t cifs_tcp_ses_lock; |
605 | GLOBAL_EXTERN struct list_head GlobalTreeConnectionList; /* BB to be removed */ | ||
606 | GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ | 609 | GLOBAL_EXTERN rwlock_t GlobalSMBSeslock; /* protects list inserts on 3 above */ |
607 | 610 | ||
608 | GLOBAL_EXTERN struct list_head GlobalOplock_Q; | 611 | GLOBAL_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 | |||
124 | cifs_reconnect(struct TCP_Server_Info *server) | 124 | cifs_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 | ||
1464 | static struct cifsTconInfo * | ||
1465 | cifs_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 | |||
1486 | static void | ||
1487 | cifs_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 | |||
1465 | int | 1510 | int |
1466 | get_dfs_path(int xid, struct cifsSesInfo *pSesInfo, const char *old_path, | 1511 | get_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 | |||
3565 | cifs_umount(struct super_block *sb, struct cifs_sb_info *cifs_sb) | 3610 | cifs_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 | |||
493 | is_valid_oplock_break(struct smb_hdr *buf, struct TCP_Server_Info *srv) | 488 | is_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 | } |