blob: 893a6fef98530ec206b1516bc89589f351dd66c4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * fs/cifs/transport.c
3 *
Steve Frenchb8643e12005-04-28 22:41:07 -07004 * Copyright (C) International Business Machines Corp., 2002,2005
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 * Author(s): Steve French (sfrench@us.ibm.com)
6 *
7 * This library is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published
9 * by the Free Software Foundation; either version 2.1 of the License, or
10 * (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
15 * the GNU Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21
22#include <linux/fs.h>
23#include <linux/list.h>
24#include <linux/wait.h>
25#include <linux/net.h>
26#include <linux/delay.h>
27#include <asm/uaccess.h>
28#include <asm/processor.h>
29#include <linux/mempool.h>
30#include "cifspdu.h"
31#include "cifsglob.h"
32#include "cifsproto.h"
33#include "cifs_debug.h"
34
35extern mempool_t *cifs_mid_poolp;
36extern kmem_cache_t *cifs_oplock_cachep;
37
38static struct mid_q_entry *
39AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
40{
41 struct mid_q_entry *temp;
42
43 if (ses == NULL) {
Steve French275cde12005-04-28 22:41:10 -070044 cERROR(1, ("Null session passed in to AllocMidQEntry"));
Linus Torvalds1da177e2005-04-16 15:20:36 -070045 return NULL;
46 }
47 if (ses->server == NULL) {
48 cERROR(1, ("Null TCP session in AllocMidQEntry"));
49 return NULL;
50 }
51
Steve Frenchd6e04ae2005-06-13 13:24:43 -050052 temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,
53 SLAB_KERNEL | SLAB_NOFS);
Linus Torvalds1da177e2005-04-16 15:20:36 -070054 if (temp == NULL)
55 return temp;
56 else {
57 memset(temp, 0, sizeof (struct mid_q_entry));
58 temp->mid = smb_buffer->Mid; /* always LE */
59 temp->pid = current->pid;
60 temp->command = smb_buffer->Command;
61 cFYI(1, ("For smb_command %d", temp->command));
62 do_gettimeofday(&temp->when_sent);
63 temp->ses = ses;
64 temp->tsk = current;
65 }
66
67 spin_lock(&GlobalMid_Lock);
68 list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
69 atomic_inc(&midCount);
70 temp->midState = MID_REQUEST_ALLOCATED;
71 spin_unlock(&GlobalMid_Lock);
72 return temp;
73}
74
75static void
76DeleteMidQEntry(struct mid_q_entry *midEntry)
77{
78 spin_lock(&GlobalMid_Lock);
79 midEntry->midState = MID_FREE;
80 list_del(&midEntry->qhead);
81 atomic_dec(&midCount);
82 spin_unlock(&GlobalMid_Lock);
Steve Frenchb8643e12005-04-28 22:41:07 -070083 if(midEntry->largeBuf)
84 cifs_buf_release(midEntry->resp_buf);
85 else
86 cifs_small_buf_release(midEntry->resp_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 mempool_free(midEntry, cifs_mid_poolp);
88}
89
90struct oplock_q_entry *
91AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon)
92{
93 struct oplock_q_entry *temp;
94 if ((pinode== NULL) || (tcon == NULL)) {
95 cERROR(1, ("Null parms passed to AllocOplockQEntry"));
96 return NULL;
97 }
98 temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
99 SLAB_KERNEL);
100 if (temp == NULL)
101 return temp;
102 else {
103 temp->pinode = pinode;
104 temp->tcon = tcon;
105 temp->netfid = fid;
106 spin_lock(&GlobalMid_Lock);
107 list_add_tail(&temp->qhead, &GlobalOplock_Q);
108 spin_unlock(&GlobalMid_Lock);
109 }
110 return temp;
111
112}
113
114void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
115{
116 spin_lock(&GlobalMid_Lock);
117 /* should we check if list empty first? */
118 list_del(&oplockEntry->qhead);
119 spin_unlock(&GlobalMid_Lock);
120 kmem_cache_free(cifs_oplock_cachep, oplockEntry);
121}
122
123int
124smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
125 unsigned int smb_buf_length, struct sockaddr *sin)
126{
127 int rc = 0;
128 int i = 0;
129 struct msghdr smb_msg;
130 struct kvec iov;
131 unsigned len = smb_buf_length + 4;
132
133 if(ssocket == NULL)
134 return -ENOTSOCK; /* BB eventually add reconnect code here */
135 iov.iov_base = smb_buffer;
136 iov.iov_len = len;
137
138 smb_msg.msg_name = sin;
139 smb_msg.msg_namelen = sizeof (struct sockaddr);
140 smb_msg.msg_control = NULL;
141 smb_msg.msg_controllen = 0;
142 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
143
144 /* smb header is converted in header_assemble. bcc and rest of SMB word
145 area, and byte area if necessary, is converted to littleendian in
146 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
147 Flags2 is converted in SendReceive */
148
149 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
Steve French3e844692005-10-03 13:37:24 -0700150 cFYI(1, ("Sending smb of length %d", smb_buf_length));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151 dump_smb(smb_buffer, len);
152
153 while (len > 0) {
154 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
155 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
156 i++;
Steve French3e844692005-10-03 13:37:24 -0700157 /* smaller timeout here than send2 since smaller size */
158 /* Although it may not be required, this also is smaller
159 oplock break time */
160 if(i > 30) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 cERROR(1,
Steve French3e844692005-10-03 13:37:24 -0700162 ("sends on sock %p stuck for 15 seconds",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 ssocket));
164 rc = -EAGAIN;
165 break;
166 }
167 msleep(500);
168 continue;
169 }
170 if (rc < 0)
171 break;
172 iov.iov_base += rc;
173 iov.iov_len -= rc;
174 len -= rc;
175 }
176
177 if (rc < 0) {
Steve French3e844692005-10-03 13:37:24 -0700178 cERROR(1,("Error %d sending data on socket to server", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179 } else {
180 rc = 0;
181 }
182
183 return rc;
184}
185
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500186#ifdef CONFIG_CIFS_EXPERIMENTAL
187static int
Steve French3e844692005-10-03 13:37:24 -0700188smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
189 struct sockaddr *sin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190{
191 int rc = 0;
192 int i = 0;
193 struct msghdr smb_msg;
Steve French3e844692005-10-03 13:37:24 -0700194 struct smb_hdr *smb_buffer = iov[0].iov_base;
195 unsigned int len = iov[0].iov_len;
196 unsigned int total_len;
197 int first_vec = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500198
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 if(ssocket == NULL)
200 return -ENOTSOCK; /* BB eventually add reconnect code here */
Steve French3e844692005-10-03 13:37:24 -0700201
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 smb_msg.msg_name = sin;
203 smb_msg.msg_namelen = sizeof (struct sockaddr);
204 smb_msg.msg_control = NULL;
205 smb_msg.msg_controllen = 0;
206 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
207
208 /* smb header is converted in header_assemble. bcc and rest of SMB word
209 area, and byte area if necessary, is converted to littleendian in
210 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
211 Flags2 is converted in SendReceive */
212
Steve French3e844692005-10-03 13:37:24 -0700213
214 total_len = 0;
215 for (i = 0; i < n_vec; i++)
216 total_len += iov[i].iov_len;
217
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
Steve French3e844692005-10-03 13:37:24 -0700219 cFYI(1, ("Sending smb: total_len %d", total_len));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 dump_smb(smb_buffer, len);
221
Steve French3e844692005-10-03 13:37:24 -0700222 while (total_len) {
223 rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
224 n_vec - first_vec, total_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
226 i++;
Steve French3e844692005-10-03 13:37:24 -0700227 if(i > 40) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228 cERROR(1,
Steve French3e844692005-10-03 13:37:24 -0700229 ("sends on sock %p stuck for 20 seconds",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 ssocket));
231 rc = -EAGAIN;
232 break;
233 }
234 msleep(500);
235 continue;
236 }
237 if (rc < 0)
238 break;
Steve French3e844692005-10-03 13:37:24 -0700239
240 if (rc >= total_len) {
241 WARN_ON(rc > total_len);
242 break;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500243 }
Steve French3e844692005-10-03 13:37:24 -0700244 if(rc == 0) {
245 /* should never happen, letting socket clear before
246 retrying is our only obvious option here */
Steve French04c08812005-10-03 19:33:15 -0700247 cERROR(1,("tcp sent no data"));
Steve French3e844692005-10-03 13:37:24 -0700248 msleep(500);
249 continue;
250 }
251 total_len -= rc;
252 for (i = first_vec; i < n_vec; i++) {
253 if (iov[i].iov_len) {
254 if (rc > iov[i].iov_len) {
255 rc -= iov[i].iov_len;
256 iov[i].iov_len = 0;
257 } else {
258 iov[i].iov_base += rc;
259 iov[i].iov_len -= rc;
260 first_vec = i;
261 break;
262 }
263 }
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500264 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265 }
266
267 if (rc < 0) {
Steve French3e844692005-10-03 13:37:24 -0700268 cERROR(1,("Error %d sending data on socket to server", rc));
269 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271
272 return rc;
273}
274
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275int
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500276SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
Steve French3e844692005-10-03 13:37:24 -0700277 struct kvec *iov, int n_vec, int *pbytes_returned,
278 const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279{
280 int rc = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500281 unsigned int receive_len;
282 unsigned long timeout;
283 struct mid_q_entry *midQ;
Steve French3e844692005-10-03 13:37:24 -0700284 struct smb_hdr *in_buf = iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285
286 if (ses == NULL) {
287 cERROR(1,("Null smb session"));
288 return -EIO;
289 }
290 if(ses->server == NULL) {
291 cERROR(1,("Null tcp session"));
292 return -EIO;
293 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500295 if(ses->server->tcpStatus == CifsExiting)
Steve French31ca3bc2005-04-28 22:41:11 -0700296 return -ENOENT;
297
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298 /* Ensure that we do not send more than 50 overlapping requests
299 to the same server. We may make this configurable later or
300 use ses->maxReq */
301 if(long_op == -1) {
302 /* oplock breaks must not be held up */
303 atomic_inc(&ses->server->inFlight);
304 } else {
305 spin_lock(&GlobalMid_Lock);
306 while(1) {
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500307 if(atomic_read(&ses->server->inFlight) >=
308 cifs_max_pending){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700309 spin_unlock(&GlobalMid_Lock);
Steve French131afd0b2005-10-07 09:51:05 -0700310#ifdef CONFIG_CIFS_STATS2
311 atomic_inc(&ses->server->num_waiters);
312#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 wait_event(ses->server->request_q,
314 atomic_read(&ses->server->inFlight)
315 < cifs_max_pending);
Steve French131afd0b2005-10-07 09:51:05 -0700316#ifdef CONFIG_CIFS_STATS2
317 atomic_dec(&ses->server->num_waiters);
318#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319 spin_lock(&GlobalMid_Lock);
320 } else {
321 if(ses->server->tcpStatus == CifsExiting) {
322 spin_unlock(&GlobalMid_Lock);
323 return -ENOENT;
324 }
325
326 /* can not count locking commands against total since
327 they are allowed to block on server */
328
329 if(long_op < 3) {
330 /* update # of requests on the wire to server */
331 atomic_inc(&ses->server->inFlight);
332 }
333 spin_unlock(&GlobalMid_Lock);
334 break;
335 }
336 }
337 }
338 /* make sure that we sign in the same order that we send on this socket
339 and avoid races inside tcp sendmsg code that could cause corruption
340 of smb data */
341
342 down(&ses->server->tcpSem);
343
344 if (ses->server->tcpStatus == CifsExiting) {
345 rc = -ENOENT;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500346 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
348 cFYI(1,("tcp session dead - return to caller to retry"));
349 rc = -EAGAIN;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500350 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 } else if (ses->status != CifsGood) {
352 /* check if SMB session is bad because we are setting it up */
353 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
354 (in_buf->Command != SMB_COM_NEGOTIATE)) {
355 rc = -EAGAIN;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500356 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 } /* else ok - we are setting up session */
358 }
359 midQ = AllocMidQEntry(in_buf, ses);
360 if (midQ == NULL) {
361 up(&ses->server->tcpSem);
362 /* If not lock req, update # of requests on wire to server */
363 if(long_op < 3) {
364 atomic_dec(&ses->server->inFlight);
365 wake_up(&ses->server->request_q);
366 }
367 return -ENOMEM;
368 }
369
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500370/* BB FIXME */
Steve French4a771182005-10-05 15:14:33 -0700371/* rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372
373 midQ->midState = MID_REQUEST_SUBMITTED;
Steve French131afd0b2005-10-07 09:51:05 -0700374#ifdef CONFIG_CIFS_STATS2
375 atomic_inc(&ses->server->inSend);
376#endif
Steve French3e844692005-10-03 13:37:24 -0700377 rc = smb_send2(ses->server->ssocket, iov, n_vec,
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500378 (struct sockaddr *) &(ses->server->addr.sockAddr));
Steve French131afd0b2005-10-07 09:51:05 -0700379#ifdef CONFIG_CIFS_STATS2
380 atomic_dec(&ses->server->inSend);
381#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 if(rc < 0) {
383 DeleteMidQEntry(midQ);
384 up(&ses->server->tcpSem);
385 /* If not lock req, update # of requests on wire to server */
386 if(long_op < 3) {
387 atomic_dec(&ses->server->inFlight);
388 wake_up(&ses->server->request_q);
389 }
390 return rc;
391 } else
392 up(&ses->server->tcpSem);
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500393 if (long_op == -1)
394 goto cifs_no_response_exit2;
395 else if (long_op == 2) /* writes past end of file can take loong time */
Steve French37c0eb42005-10-05 14:50:29 -0700396 timeout = 180 * HZ;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500397 else if (long_op == 1)
398 timeout = 45 * HZ; /* should be greater than
399 servers oplock break timeout (about 43 seconds) */
400 else if (long_op > 2) {
401 timeout = MAX_SCHEDULE_TIMEOUT;
402 } else
403 timeout = 15 * HZ;
404 /* wait for 15 seconds or until woken up due to response arriving or
405 due to last connection to this server being unmounted */
406 if (signal_pending(current)) {
407 /* if signal pending do not hold up user for full smb timeout
408 but we still give response a change to complete */
409 timeout = 2 * HZ;
410 }
411
412 /* No user interrupts in wait - wreaks havoc with performance */
413 if(timeout != MAX_SCHEDULE_TIMEOUT) {
414 timeout += jiffies;
415 wait_event(ses->server->response_q,
416 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
417 time_after(jiffies, timeout) ||
418 ((ses->server->tcpStatus != CifsGood) &&
419 (ses->server->tcpStatus != CifsNew)));
420 } else {
421 wait_event(ses->server->response_q,
422 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
423 ((ses->server->tcpStatus != CifsGood) &&
424 (ses->server->tcpStatus != CifsNew)));
425 }
426
427 spin_lock(&GlobalMid_Lock);
428 if (midQ->resp_buf) {
429 spin_unlock(&GlobalMid_Lock);
Steve French70ca7342005-09-22 16:32:06 -0700430 receive_len = midQ->resp_buf->smb_buf_length;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500431 } else {
Steve French37c0eb42005-10-05 14:50:29 -0700432 cERROR(1,("No response to cmd %d mid %d",
433 midQ->command, midQ->mid));
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500434 if(midQ->midState == MID_REQUEST_SUBMITTED) {
435 if(ses->server->tcpStatus == CifsExiting)
436 rc = -EHOSTDOWN;
437 else {
438 ses->server->tcpStatus = CifsNeedReconnect;
439 midQ->midState = MID_RETRY_NEEDED;
440 }
441 }
442
443 if (rc != -EHOSTDOWN) {
444 if(midQ->midState == MID_RETRY_NEEDED) {
445 rc = -EAGAIN;
446 cFYI(1,("marking request for retry"));
447 } else {
448 rc = -EIO;
449 }
450 }
451 spin_unlock(&GlobalMid_Lock);
452 DeleteMidQEntry(midQ);
453 /* If not lock req, update # of requests on wire to server */
454 if(long_op < 3) {
455 atomic_dec(&ses->server->inFlight);
456 wake_up(&ses->server->request_q);
457 }
458 return rc;
459 }
460
461 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
462 cERROR(1, ("Frame too large received. Length: %d Xid: %d",
463 receive_len, xid));
464 rc = -EIO;
465 } else { /* rcvd frame is ok */
466
467 if (midQ->resp_buf &&
468 (midQ->midState == MID_RESPONSE_RECEIVED)) {
469 in_buf->smb_buf_length = receive_len;
470 /* BB verify that length would not overrun small buf */
471 memcpy((char *)in_buf + 4,
472 (char *)midQ->resp_buf + 4,
473 receive_len);
474
475 dump_smb(in_buf, 80);
476 /* convert the length into a more usable form */
477 if((receive_len > 24) &&
478 (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
479 SECMODE_SIGN_ENABLED))) {
480 rc = cifs_verify_signature(in_buf,
481 ses->server->mac_signing_key,
482 midQ->sequence_number+1);
483 if(rc) {
484 cERROR(1,("Unexpected SMB signature"));
485 /* BB FIXME add code to kill session */
486 }
487 }
488
489 *pbytes_returned = in_buf->smb_buf_length;
490
491 /* BB special case reconnect tid and uid here? */
492 rc = map_smb_to_linux_error(in_buf);
493
494 /* convert ByteCount if necessary */
495 if (receive_len >=
496 sizeof (struct smb_hdr) -
497 4 /* do not count RFC1001 header */ +
498 (2 * in_buf->WordCount) + 2 /* bcc */ )
499 BCC(in_buf) = le16_to_cpu(BCC(in_buf));
500 } else {
501 rc = -EIO;
Steve Frenchab2f2182005-09-15 20:44:50 -0700502 cFYI(1,("Bad MID state?"));
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500503 }
504 }
505cifs_no_response_exit2:
506 DeleteMidQEntry(midQ);
507
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 if(long_op < 3) {
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500509 atomic_dec(&ses->server->inFlight);
510 wake_up(&ses->server->request_q);
511 }
512
513 return rc;
514
515out_unlock2:
516 up(&ses->server->tcpSem);
517 /* If not lock req, update # of requests on wire to server */
518 if(long_op < 3) {
519 atomic_dec(&ses->server->inFlight);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 wake_up(&ses->server->request_q);
521 }
522
523 return rc;
524}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525#endif /* CIFS_EXPERIMENTAL */
526
527int
528SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
529 struct smb_hdr *in_buf, struct smb_hdr *out_buf,
530 int *pbytes_returned, const int long_op)
531{
532 int rc = 0;
533 unsigned int receive_len;
534 unsigned long timeout;
535 struct mid_q_entry *midQ;
536
537 if (ses == NULL) {
538 cERROR(1,("Null smb session"));
539 return -EIO;
540 }
541 if(ses->server == NULL) {
542 cERROR(1,("Null tcp session"));
543 return -EIO;
544 }
545
Steve French31ca3bc2005-04-28 22:41:11 -0700546 if(ses->server->tcpStatus == CifsExiting)
547 return -ENOENT;
548
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549 /* Ensure that we do not send more than 50 overlapping requests
550 to the same server. We may make this configurable later or
551 use ses->maxReq */
552 if(long_op == -1) {
553 /* oplock breaks must not be held up */
554 atomic_inc(&ses->server->inFlight);
555 } else {
556 spin_lock(&GlobalMid_Lock);
557 while(1) {
Steve French275cde12005-04-28 22:41:10 -0700558 if(atomic_read(&ses->server->inFlight) >=
559 cifs_max_pending){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560 spin_unlock(&GlobalMid_Lock);
Steve French131afd0b2005-10-07 09:51:05 -0700561#ifdef CONFIG_CIFS_STATS2
562 atomic_inc(&ses->server->num_waiters);
563#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 wait_event(ses->server->request_q,
565 atomic_read(&ses->server->inFlight)
566 < cifs_max_pending);
Steve French131afd0b2005-10-07 09:51:05 -0700567#ifdef CONFIG_CIFS_STATS2
568 atomic_dec(&ses->server->num_waiters);
569#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570 spin_lock(&GlobalMid_Lock);
571 } else {
572 if(ses->server->tcpStatus == CifsExiting) {
573 spin_unlock(&GlobalMid_Lock);
574 return -ENOENT;
575 }
576
577 /* can not count locking commands against total since
578 they are allowed to block on server */
579
580 if(long_op < 3) {
581 /* update # of requests on the wire to server */
582 atomic_inc(&ses->server->inFlight);
583 }
584 spin_unlock(&GlobalMid_Lock);
585 break;
586 }
587 }
588 }
589 /* make sure that we sign in the same order that we send on this socket
590 and avoid races inside tcp sendmsg code that could cause corruption
591 of smb data */
592
593 down(&ses->server->tcpSem);
594
595 if (ses->server->tcpStatus == CifsExiting) {
596 rc = -ENOENT;
597 goto out_unlock;
598 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
599 cFYI(1,("tcp session dead - return to caller to retry"));
600 rc = -EAGAIN;
601 goto out_unlock;
602 } else if (ses->status != CifsGood) {
603 /* check if SMB session is bad because we are setting it up */
604 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
605 (in_buf->Command != SMB_COM_NEGOTIATE)) {
606 rc = -EAGAIN;
607 goto out_unlock;
608 } /* else ok - we are setting up session */
609 }
610 midQ = AllocMidQEntry(in_buf, ses);
611 if (midQ == NULL) {
612 up(&ses->server->tcpSem);
613 /* If not lock req, update # of requests on wire to server */
614 if(long_op < 3) {
615 atomic_dec(&ses->server->inFlight);
616 wake_up(&ses->server->request_q);
617 }
618 return -ENOMEM;
619 }
620
621 if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
622 up(&ses->server->tcpSem);
623 cERROR(1,
624 ("Illegal length, greater than maximum frame, %d ",
625 in_buf->smb_buf_length));
626 DeleteMidQEntry(midQ);
627 /* If not lock req, update # of requests on wire to server */
628 if(long_op < 3) {
629 atomic_dec(&ses->server->inFlight);
630 wake_up(&ses->server->request_q);
631 }
632 return -EIO;
633 }
634
Steve Frenchad009ac2005-04-28 22:41:05 -0700635 rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637 midQ->midState = MID_REQUEST_SUBMITTED;
Steve French131afd0b2005-10-07 09:51:05 -0700638#ifdef CONFIG_CIFS_STATS2
639 atomic_inc(&ses->server->inSend);
640#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700641 rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
642 (struct sockaddr *) &(ses->server->addr.sockAddr));
Steve French131afd0b2005-10-07 09:51:05 -0700643#ifdef CONFIG_CIFS_STATS2
644 atomic_dec(&ses->server->inSend);
645#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 if(rc < 0) {
647 DeleteMidQEntry(midQ);
648 up(&ses->server->tcpSem);
649 /* If not lock req, update # of requests on wire to server */
650 if(long_op < 3) {
651 atomic_dec(&ses->server->inFlight);
652 wake_up(&ses->server->request_q);
653 }
654 return rc;
655 } else
656 up(&ses->server->tcpSem);
657 if (long_op == -1)
658 goto cifs_no_response_exit;
Steve French275cde12005-04-28 22:41:10 -0700659 else if (long_op == 2) /* writes past end of file can take loong time */
Steve French37c0eb42005-10-05 14:50:29 -0700660 timeout = 180 * HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 else if (long_op == 1)
662 timeout = 45 * HZ; /* should be greater than
663 servers oplock break timeout (about 43 seconds) */
664 else if (long_op > 2) {
665 timeout = MAX_SCHEDULE_TIMEOUT;
666 } else
667 timeout = 15 * HZ;
668 /* wait for 15 seconds or until woken up due to response arriving or
669 due to last connection to this server being unmounted */
670 if (signal_pending(current)) {
671 /* if signal pending do not hold up user for full smb timeout
672 but we still give response a change to complete */
673 timeout = 2 * HZ;
674 }
675
676 /* No user interrupts in wait - wreaks havoc with performance */
677 if(timeout != MAX_SCHEDULE_TIMEOUT) {
678 timeout += jiffies;
679 wait_event(ses->server->response_q,
680 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
681 time_after(jiffies, timeout) ||
682 ((ses->server->tcpStatus != CifsGood) &&
683 (ses->server->tcpStatus != CifsNew)));
684 } else {
685 wait_event(ses->server->response_q,
686 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
687 ((ses->server->tcpStatus != CifsGood) &&
688 (ses->server->tcpStatus != CifsNew)));
689 }
690
691 spin_lock(&GlobalMid_Lock);
692 if (midQ->resp_buf) {
693 spin_unlock(&GlobalMid_Lock);
Steve French70ca7342005-09-22 16:32:06 -0700694 receive_len = midQ->resp_buf->smb_buf_length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 } else {
Steve French37c0eb42005-10-05 14:50:29 -0700696 cERROR(1,("No response for cmd %d mid %d",
697 midQ->command, midQ->mid));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 if(midQ->midState == MID_REQUEST_SUBMITTED) {
699 if(ses->server->tcpStatus == CifsExiting)
700 rc = -EHOSTDOWN;
701 else {
702 ses->server->tcpStatus = CifsNeedReconnect;
703 midQ->midState = MID_RETRY_NEEDED;
704 }
705 }
706
707 if (rc != -EHOSTDOWN) {
708 if(midQ->midState == MID_RETRY_NEEDED) {
709 rc = -EAGAIN;
710 cFYI(1,("marking request for retry"));
711 } else {
712 rc = -EIO;
713 }
714 }
715 spin_unlock(&GlobalMid_Lock);
716 DeleteMidQEntry(midQ);
717 /* If not lock req, update # of requests on wire to server */
718 if(long_op < 3) {
719 atomic_dec(&ses->server->inFlight);
720 wake_up(&ses->server->request_q);
721 }
722 return rc;
723 }
724
725 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
Steve Frenchad009ac2005-04-28 22:41:05 -0700726 cERROR(1, ("Frame too large received. Length: %d Xid: %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727 receive_len, xid));
728 rc = -EIO;
729 } else { /* rcvd frame is ok */
730
731 if (midQ->resp_buf && out_buf
732 && (midQ->midState == MID_RESPONSE_RECEIVED)) {
733 out_buf->smb_buf_length = receive_len;
734 memcpy((char *)out_buf + 4,
735 (char *)midQ->resp_buf + 4,
736 receive_len);
737
738 dump_smb(out_buf, 92);
739 /* convert the length into a more usable form */
740 if((receive_len > 24) &&
Steve Frenchad009ac2005-04-28 22:41:05 -0700741 (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
742 SECMODE_SIGN_ENABLED))) {
743 rc = cifs_verify_signature(out_buf,
744 ses->server->mac_signing_key,
745 midQ->sequence_number+1);
746 if(rc) {
Steve French275cde12005-04-28 22:41:10 -0700747 cERROR(1,("Unexpected SMB signature"));
748 /* BB FIXME add code to kill session */
Steve Frenchad009ac2005-04-28 22:41:05 -0700749 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 }
751
752 *pbytes_returned = out_buf->smb_buf_length;
753
Steve Frenchad009ac2005-04-28 22:41:05 -0700754 /* BB special case reconnect tid and uid here? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 rc = map_smb_to_linux_error(out_buf);
756
757 /* convert ByteCount if necessary */
758 if (receive_len >=
759 sizeof (struct smb_hdr) -
760 4 /* do not count RFC1001 header */ +
761 (2 * out_buf->WordCount) + 2 /* bcc */ )
762 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
763 } else {
764 rc = -EIO;
Steve Frencha5a2b482005-08-20 21:42:53 -0700765 cERROR(1,("Bad MID state? "));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 }
767 }
768cifs_no_response_exit:
769 DeleteMidQEntry(midQ);
770
771 if(long_op < 3) {
772 atomic_dec(&ses->server->inFlight);
773 wake_up(&ses->server->request_q);
774 }
775
776 return rc;
777
778out_unlock:
779 up(&ses->server->tcpSem);
780 /* If not lock req, update # of requests on wire to server */
781 if(long_op < 3) {
782 atomic_dec(&ses->server->inFlight);
783 wake_up(&ses->server->request_q);
784 }
785
786 return rc;
787}