diff options
Diffstat (limited to 'fs/cifs/misc.c')
-rw-r--r-- | fs/cifs/misc.c | 84 |
1 files changed, 77 insertions, 7 deletions
diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c index 20ae4153f791..beeff8284169 100644 --- a/fs/cifs/misc.c +++ b/fs/cifs/misc.c | |||
@@ -34,8 +34,6 @@ extern mempool_t *cifs_sm_req_poolp; | |||
34 | extern mempool_t *cifs_req_poolp; | 34 | extern mempool_t *cifs_req_poolp; |
35 | extern struct task_struct * oplockThread; | 35 | extern struct task_struct * oplockThread; |
36 | 36 | ||
37 | static __u16 GlobalMid; /* multiplex id - rotating counter */ | ||
38 | |||
39 | /* The xid serves as a useful identifier for each incoming vfs request, | 37 | /* The xid serves as a useful identifier for each incoming vfs request, |
40 | in a similar way to the mid which is useful to track each sent smb, | 38 | in a similar way to the mid which is useful to track each sent smb, |
41 | and CurrentXid can also provide a running counter (although it | 39 | and CurrentXid can also provide a running counter (although it |
@@ -51,6 +49,8 @@ _GetXid(void) | |||
51 | GlobalTotalActiveXid++; | 49 | GlobalTotalActiveXid++; |
52 | if (GlobalTotalActiveXid > GlobalMaxActiveXid) | 50 | if (GlobalTotalActiveXid > GlobalMaxActiveXid) |
53 | GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */ | 51 | GlobalMaxActiveXid = GlobalTotalActiveXid; /* keep high water mark for number of simultaneous vfs ops in our filesystem */ |
52 | if(GlobalTotalActiveXid > 65000) | ||
53 | cFYI(1,("warning: more than 65000 requests active")); | ||
54 | xid = GlobalCurrentXid++; | 54 | xid = GlobalCurrentXid++; |
55 | spin_unlock(&GlobalMid_Lock); | 55 | spin_unlock(&GlobalMid_Lock); |
56 | return xid; | 56 | return xid; |
@@ -218,6 +218,76 @@ cifs_small_buf_release(void *buf_to_free) | |||
218 | return; | 218 | return; |
219 | } | 219 | } |
220 | 220 | ||
221 | /* | ||
222 | Find a free multiplex id (SMB mid). Otherwise there could be | ||
223 | mid collisions which might cause problems, demultiplexing the | ||
224 | wrong response to this request. Multiplex ids could collide if | ||
225 | one of a series requests takes much longer than the others, or | ||
226 | if a very large number of long lived requests (byte range | ||
227 | locks or FindNotify requests) are pending. No more than | ||
228 | 64K-1 requests can be outstanding at one time. If no | ||
229 | mids are available, return zero. A future optimization | ||
230 | could make the combination of mids and uid the key we use | ||
231 | to demultiplex on (rather than mid alone). | ||
232 | In addition to the above check, the cifs demultiplex | ||
233 | code already used the command code as a secondary | ||
234 | check of the frame and if signing is negotiated the | ||
235 | response would be discarded if the mid were the same | ||
236 | but the signature was wrong. Since the mid is not put in the | ||
237 | pending queue until later (when it is about to be dispatched) | ||
238 | we do have to limit the number of outstanding requests | ||
239 | to somewhat less than 64K-1 although it is hard to imagine | ||
240 | so many threads being in the vfs at one time. | ||
241 | */ | ||
242 | __u16 GetNextMid(struct TCP_Server_Info *server) | ||
243 | { | ||
244 | __u16 mid = 0; | ||
245 | __u16 last_mid; | ||
246 | int collision; | ||
247 | |||
248 | if(server == NULL) | ||
249 | return mid; | ||
250 | |||
251 | spin_lock(&GlobalMid_Lock); | ||
252 | last_mid = server->CurrentMid; /* we do not want to loop forever */ | ||
253 | server->CurrentMid++; | ||
254 | /* This nested loop looks more expensive than it is. | ||
255 | In practice the list of pending requests is short, | ||
256 | fewer than 50, and the mids are likely to be unique | ||
257 | on the first pass through the loop unless some request | ||
258 | takes longer than the 64 thousand requests before it | ||
259 | (and it would also have to have been a request that | ||
260 | did not time out) */ | ||
261 | while(server->CurrentMid != last_mid) { | ||
262 | struct list_head *tmp; | ||
263 | struct mid_q_entry *mid_entry; | ||
264 | |||
265 | collision = 0; | ||
266 | if(server->CurrentMid == 0) | ||
267 | server->CurrentMid++; | ||
268 | |||
269 | list_for_each(tmp, &server->pending_mid_q) { | ||
270 | mid_entry = list_entry(tmp, struct mid_q_entry, qhead); | ||
271 | |||
272 | if ((mid_entry->mid == server->CurrentMid) && | ||
273 | (mid_entry->midState == MID_REQUEST_SUBMITTED)) { | ||
274 | /* This mid is in use, try a different one */ | ||
275 | collision = 1; | ||
276 | break; | ||
277 | } | ||
278 | } | ||
279 | if(collision == 0) { | ||
280 | mid = server->CurrentMid; | ||
281 | break; | ||
282 | } | ||
283 | server->CurrentMid++; | ||
284 | } | ||
285 | spin_unlock(&GlobalMid_Lock); | ||
286 | return mid; | ||
287 | } | ||
288 | |||
289 | /* NB: MID can not be set if treeCon not passed in, in that | ||
290 | case it is responsbility of caller to set the mid */ | ||
221 | void | 291 | void |
222 | header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , | 292 | header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , |
223 | const struct cifsTconInfo *treeCon, int word_count | 293 | const struct cifsTconInfo *treeCon, int word_count |
@@ -233,7 +303,8 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , | |||
233 | (2 * word_count) + sizeof (struct smb_hdr) - | 303 | (2 * word_count) + sizeof (struct smb_hdr) - |
234 | 4 /* RFC 1001 length field does not count */ + | 304 | 4 /* RFC 1001 length field does not count */ + |
235 | 2 /* for bcc field itself */ ; | 305 | 2 /* for bcc field itself */ ; |
236 | /* Note that this is the only network field that has to be converted to big endian and it is done just before we send it */ | 306 | /* Note that this is the only network field that has to be converted |
307 | to big endian and it is done just before we send it */ | ||
237 | 308 | ||
238 | buffer->Protocol[0] = 0xFF; | 309 | buffer->Protocol[0] = 0xFF; |
239 | buffer->Protocol[1] = 'S'; | 310 | buffer->Protocol[1] = 'S'; |
@@ -245,8 +316,6 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , | |||
245 | buffer->Pid = cpu_to_le16((__u16)current->tgid); | 316 | buffer->Pid = cpu_to_le16((__u16)current->tgid); |
246 | buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); | 317 | buffer->PidHigh = cpu_to_le16((__u16)(current->tgid >> 16)); |
247 | spin_lock(&GlobalMid_Lock); | 318 | spin_lock(&GlobalMid_Lock); |
248 | GlobalMid++; | ||
249 | buffer->Mid = GlobalMid; | ||
250 | spin_unlock(&GlobalMid_Lock); | 319 | spin_unlock(&GlobalMid_Lock); |
251 | if (treeCon) { | 320 | if (treeCon) { |
252 | buffer->Tid = treeCon->tid; | 321 | buffer->Tid = treeCon->tid; |
@@ -256,8 +325,9 @@ header_assemble(struct smb_hdr *buffer, char smb_command /* command */ , | |||
256 | if (treeCon->ses->capabilities & CAP_STATUS32) { | 325 | if (treeCon->ses->capabilities & CAP_STATUS32) { |
257 | buffer->Flags2 |= SMBFLG2_ERR_STATUS; | 326 | buffer->Flags2 |= SMBFLG2_ERR_STATUS; |
258 | } | 327 | } |
259 | 328 | /* Uid is not converted */ | |
260 | buffer->Uid = treeCon->ses->Suid; /* always in LE format */ | 329 | buffer->Uid = treeCon->ses->Suid; |
330 | buffer->Mid = GetNextMid(treeCon->ses->server); | ||
261 | if(multiuser_mount != 0) { | 331 | if(multiuser_mount != 0) { |
262 | /* For the multiuser case, there are few obvious technically */ | 332 | /* For the multiuser case, there are few obvious technically */ |
263 | /* possible mechanisms to match the local linux user (uid) */ | 333 | /* possible mechanisms to match the local linux user (uid) */ |