blob: f8871196098c9abe371a64a1df5d269cb407f503 [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));
Steve French1047abc2005-10-11 19:58:06 -070062 /* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */
63 /* when mid allocated can be before when sent */
64 temp->when_alloc = jiffies;
Linus Torvalds1da177e2005-04-16 15:20:36 -070065 temp->ses = ses;
66 temp->tsk = current;
67 }
68
69 spin_lock(&GlobalMid_Lock);
70 list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
71 atomic_inc(&midCount);
72 temp->midState = MID_REQUEST_ALLOCATED;
73 spin_unlock(&GlobalMid_Lock);
74 return temp;
75}
76
77static void
78DeleteMidQEntry(struct mid_q_entry *midEntry)
79{
Steve French1047abc2005-10-11 19:58:06 -070080#ifdef CONFIG_CIFS_STATS2
81 unsigned long now;
82#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 spin_lock(&GlobalMid_Lock);
84 midEntry->midState = MID_FREE;
85 list_del(&midEntry->qhead);
86 atomic_dec(&midCount);
87 spin_unlock(&GlobalMid_Lock);
Steve Frenchb8643e12005-04-28 22:41:07 -070088 if(midEntry->largeBuf)
89 cifs_buf_release(midEntry->resp_buf);
90 else
91 cifs_small_buf_release(midEntry->resp_buf);
Steve French1047abc2005-10-11 19:58:06 -070092#ifdef CONFIG_CIFS_STATS2
93 now = jiffies;
94 /* commands taking longer than one second are indications that
95 something is wrong, unless it is quite a slow link or server */
96 if((now - midEntry->when_alloc) > HZ) {
97 if((cifsFYI & CIFS_TIMER) &&
98 (midEntry->command != SMB_COM_LOCKING_ANDX)) {
99 printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %d",
100 midEntry->command, midEntry->mid);
101 printk(" A: 0x%lx S: 0x%lx R: 0x%lx\n",
102 now - midEntry->when_alloc,
103 now - midEntry->when_sent,
104 now - midEntry->when_received);
105 }
106 }
107#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 mempool_free(midEntry, cifs_mid_poolp);
109}
110
111struct oplock_q_entry *
112AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon)
113{
114 struct oplock_q_entry *temp;
115 if ((pinode== NULL) || (tcon == NULL)) {
116 cERROR(1, ("Null parms passed to AllocOplockQEntry"));
117 return NULL;
118 }
119 temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
120 SLAB_KERNEL);
121 if (temp == NULL)
122 return temp;
123 else {
124 temp->pinode = pinode;
125 temp->tcon = tcon;
126 temp->netfid = fid;
127 spin_lock(&GlobalMid_Lock);
128 list_add_tail(&temp->qhead, &GlobalOplock_Q);
129 spin_unlock(&GlobalMid_Lock);
130 }
131 return temp;
132
133}
134
135void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
136{
137 spin_lock(&GlobalMid_Lock);
138 /* should we check if list empty first? */
139 list_del(&oplockEntry->qhead);
140 spin_unlock(&GlobalMid_Lock);
141 kmem_cache_free(cifs_oplock_cachep, oplockEntry);
142}
143
144int
145smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
146 unsigned int smb_buf_length, struct sockaddr *sin)
147{
148 int rc = 0;
149 int i = 0;
150 struct msghdr smb_msg;
151 struct kvec iov;
152 unsigned len = smb_buf_length + 4;
153
154 if(ssocket == NULL)
155 return -ENOTSOCK; /* BB eventually add reconnect code here */
156 iov.iov_base = smb_buffer;
157 iov.iov_len = len;
158
159 smb_msg.msg_name = sin;
160 smb_msg.msg_namelen = sizeof (struct sockaddr);
161 smb_msg.msg_control = NULL;
162 smb_msg.msg_controllen = 0;
163 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
164
165 /* smb header is converted in header_assemble. bcc and rest of SMB word
166 area, and byte area if necessary, is converted to littleendian in
167 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
168 Flags2 is converted in SendReceive */
169
170 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
Steve French3e844692005-10-03 13:37:24 -0700171 cFYI(1, ("Sending smb of length %d", smb_buf_length));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700172 dump_smb(smb_buffer, len);
173
174 while (len > 0) {
175 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
176 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
177 i++;
Steve French3e844692005-10-03 13:37:24 -0700178 /* smaller timeout here than send2 since smaller size */
179 /* Although it may not be required, this also is smaller
180 oplock break time */
Steve French68058e72005-10-10 10:34:22 -0700181 if(i > 12) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 cERROR(1,
Steve French68058e72005-10-10 10:34:22 -0700183 ("sends on sock %p stuck for 7 seconds",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 ssocket));
185 rc = -EAGAIN;
186 break;
187 }
Steve French68058e72005-10-10 10:34:22 -0700188 msleep(1 << i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 continue;
190 }
191 if (rc < 0)
192 break;
Steve French5e1253b2005-10-10 14:06:37 -0700193 else
194 i = 0; /* reset i after each successful send */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 iov.iov_base += rc;
196 iov.iov_len -= rc;
197 len -= rc;
198 }
199
200 if (rc < 0) {
Steve French3e844692005-10-03 13:37:24 -0700201 cERROR(1,("Error %d sending data on socket to server", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 } else {
203 rc = 0;
204 }
205
206 return rc;
207}
208
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500209#ifdef CONFIG_CIFS_EXPERIMENTAL
210static int
Steve French3e844692005-10-03 13:37:24 -0700211smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
212 struct sockaddr *sin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213{
214 int rc = 0;
215 int i = 0;
216 struct msghdr smb_msg;
Steve French3e844692005-10-03 13:37:24 -0700217 struct smb_hdr *smb_buffer = iov[0].iov_base;
218 unsigned int len = iov[0].iov_len;
219 unsigned int total_len;
220 int first_vec = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500221
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 if(ssocket == NULL)
223 return -ENOTSOCK; /* BB eventually add reconnect code here */
Steve French3e844692005-10-03 13:37:24 -0700224
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225 smb_msg.msg_name = sin;
226 smb_msg.msg_namelen = sizeof (struct sockaddr);
227 smb_msg.msg_control = NULL;
228 smb_msg.msg_controllen = 0;
229 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
230
231 /* smb header is converted in header_assemble. bcc and rest of SMB word
232 area, and byte area if necessary, is converted to littleendian in
233 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
234 Flags2 is converted in SendReceive */
235
Steve French3e844692005-10-03 13:37:24 -0700236
237 total_len = 0;
238 for (i = 0; i < n_vec; i++)
239 total_len += iov[i].iov_len;
240
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
Steve French3e844692005-10-03 13:37:24 -0700242 cFYI(1, ("Sending smb: total_len %d", total_len));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243 dump_smb(smb_buffer, len);
244
Steve French3e844692005-10-03 13:37:24 -0700245 while (total_len) {
246 rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
247 n_vec - first_vec, total_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
249 i++;
Steve French68058e72005-10-10 10:34:22 -0700250 if(i >= 14) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251 cERROR(1,
Steve French68058e72005-10-10 10:34:22 -0700252 ("sends on sock %p stuck for 15 seconds",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253 ssocket));
254 rc = -EAGAIN;
255 break;
256 }
Steve French68058e72005-10-10 10:34:22 -0700257 msleep(1 << i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 continue;
259 }
260 if (rc < 0)
261 break;
Steve French3e844692005-10-03 13:37:24 -0700262
263 if (rc >= total_len) {
264 WARN_ON(rc > total_len);
265 break;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500266 }
Steve French3e844692005-10-03 13:37:24 -0700267 if(rc == 0) {
268 /* should never happen, letting socket clear before
269 retrying is our only obvious option here */
Steve French04c08812005-10-03 19:33:15 -0700270 cERROR(1,("tcp sent no data"));
Steve French3e844692005-10-03 13:37:24 -0700271 msleep(500);
272 continue;
273 }
274 total_len -= rc;
Steve French68058e72005-10-10 10:34:22 -0700275 /* the line below resets i */
Steve French3e844692005-10-03 13:37:24 -0700276 for (i = first_vec; i < n_vec; i++) {
277 if (iov[i].iov_len) {
278 if (rc > iov[i].iov_len) {
279 rc -= iov[i].iov_len;
280 iov[i].iov_len = 0;
281 } else {
282 iov[i].iov_base += rc;
283 iov[i].iov_len -= rc;
284 first_vec = i;
285 break;
286 }
287 }
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500288 }
Steve French5e1253b2005-10-10 14:06:37 -0700289 i = 0; /* in case we get ENOSPC on the next send */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 }
291
292 if (rc < 0) {
Steve French3e844692005-10-03 13:37:24 -0700293 cERROR(1,("Error %d sending data on socket to server", rc));
294 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
297 return rc;
298}
299
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300int
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500301SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
Steve French3e844692005-10-03 13:37:24 -0700302 struct kvec *iov, int n_vec, int *pbytes_returned,
303 const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304{
305 int rc = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500306 unsigned int receive_len;
307 unsigned long timeout;
308 struct mid_q_entry *midQ;
Steve French3e844692005-10-03 13:37:24 -0700309 struct smb_hdr *in_buf = iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
311 if (ses == NULL) {
312 cERROR(1,("Null smb session"));
313 return -EIO;
314 }
315 if(ses->server == NULL) {
316 cERROR(1,("Null tcp session"));
317 return -EIO;
318 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500320 if(ses->server->tcpStatus == CifsExiting)
Steve French31ca3bc2005-04-28 22:41:11 -0700321 return -ENOENT;
322
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 /* Ensure that we do not send more than 50 overlapping requests
324 to the same server. We may make this configurable later or
325 use ses->maxReq */
326 if(long_op == -1) {
327 /* oplock breaks must not be held up */
328 atomic_inc(&ses->server->inFlight);
329 } else {
330 spin_lock(&GlobalMid_Lock);
331 while(1) {
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500332 if(atomic_read(&ses->server->inFlight) >=
333 cifs_max_pending){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 spin_unlock(&GlobalMid_Lock);
Steve French131afd02005-10-07 09:51:05 -0700335#ifdef CONFIG_CIFS_STATS2
336 atomic_inc(&ses->server->num_waiters);
337#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 wait_event(ses->server->request_q,
339 atomic_read(&ses->server->inFlight)
340 < cifs_max_pending);
Steve French131afd02005-10-07 09:51:05 -0700341#ifdef CONFIG_CIFS_STATS2
342 atomic_dec(&ses->server->num_waiters);
343#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 spin_lock(&GlobalMid_Lock);
345 } else {
346 if(ses->server->tcpStatus == CifsExiting) {
347 spin_unlock(&GlobalMid_Lock);
348 return -ENOENT;
349 }
350
351 /* can not count locking commands against total since
352 they are allowed to block on server */
353
354 if(long_op < 3) {
355 /* update # of requests on the wire to server */
356 atomic_inc(&ses->server->inFlight);
357 }
358 spin_unlock(&GlobalMid_Lock);
359 break;
360 }
361 }
362 }
363 /* make sure that we sign in the same order that we send on this socket
364 and avoid races inside tcp sendmsg code that could cause corruption
365 of smb data */
366
367 down(&ses->server->tcpSem);
368
369 if (ses->server->tcpStatus == CifsExiting) {
370 rc = -ENOENT;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500371 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700372 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
373 cFYI(1,("tcp session dead - return to caller to retry"));
374 rc = -EAGAIN;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500375 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 } else if (ses->status != CifsGood) {
377 /* check if SMB session is bad because we are setting it up */
378 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
379 (in_buf->Command != SMB_COM_NEGOTIATE)) {
380 rc = -EAGAIN;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500381 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 } /* else ok - we are setting up session */
383 }
384 midQ = AllocMidQEntry(in_buf, ses);
385 if (midQ == NULL) {
386 up(&ses->server->tcpSem);
387 /* If not lock req, update # of requests on wire to server */
388 if(long_op < 3) {
389 atomic_dec(&ses->server->inFlight);
390 wake_up(&ses->server->request_q);
391 }
392 return -ENOMEM;
393 }
394
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500395/* BB FIXME */
Steve French4a771182005-10-05 15:14:33 -0700396/* rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397
398 midQ->midState = MID_REQUEST_SUBMITTED;
Steve French131afd02005-10-07 09:51:05 -0700399#ifdef CONFIG_CIFS_STATS2
400 atomic_inc(&ses->server->inSend);
401#endif
Steve French3e844692005-10-03 13:37:24 -0700402 rc = smb_send2(ses->server->ssocket, iov, n_vec,
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500403 (struct sockaddr *) &(ses->server->addr.sockAddr));
Steve French131afd02005-10-07 09:51:05 -0700404#ifdef CONFIG_CIFS_STATS2
405 atomic_dec(&ses->server->inSend);
Steve French1047abc2005-10-11 19:58:06 -0700406 midQ->when_sent = jiffies;
Steve French131afd02005-10-07 09:51:05 -0700407#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 if(rc < 0) {
409 DeleteMidQEntry(midQ);
410 up(&ses->server->tcpSem);
411 /* If not lock req, update # of requests on wire to server */
412 if(long_op < 3) {
413 atomic_dec(&ses->server->inFlight);
414 wake_up(&ses->server->request_q);
415 }
416 return rc;
417 } else
418 up(&ses->server->tcpSem);
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500419 if (long_op == -1)
420 goto cifs_no_response_exit2;
421 else if (long_op == 2) /* writes past end of file can take loong time */
Steve French37c0eb42005-10-05 14:50:29 -0700422 timeout = 180 * HZ;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500423 else if (long_op == 1)
424 timeout = 45 * HZ; /* should be greater than
425 servers oplock break timeout (about 43 seconds) */
426 else if (long_op > 2) {
427 timeout = MAX_SCHEDULE_TIMEOUT;
428 } else
429 timeout = 15 * HZ;
430 /* wait for 15 seconds or until woken up due to response arriving or
431 due to last connection to this server being unmounted */
432 if (signal_pending(current)) {
433 /* if signal pending do not hold up user for full smb timeout
434 but we still give response a change to complete */
435 timeout = 2 * HZ;
436 }
437
438 /* No user interrupts in wait - wreaks havoc with performance */
439 if(timeout != MAX_SCHEDULE_TIMEOUT) {
440 timeout += jiffies;
441 wait_event(ses->server->response_q,
442 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
443 time_after(jiffies, timeout) ||
444 ((ses->server->tcpStatus != CifsGood) &&
445 (ses->server->tcpStatus != CifsNew)));
446 } else {
447 wait_event(ses->server->response_q,
448 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
449 ((ses->server->tcpStatus != CifsGood) &&
450 (ses->server->tcpStatus != CifsNew)));
451 }
452
453 spin_lock(&GlobalMid_Lock);
454 if (midQ->resp_buf) {
455 spin_unlock(&GlobalMid_Lock);
Steve French70ca7342005-09-22 16:32:06 -0700456 receive_len = midQ->resp_buf->smb_buf_length;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500457 } else {
Steve French37c0eb42005-10-05 14:50:29 -0700458 cERROR(1,("No response to cmd %d mid %d",
459 midQ->command, midQ->mid));
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500460 if(midQ->midState == MID_REQUEST_SUBMITTED) {
461 if(ses->server->tcpStatus == CifsExiting)
462 rc = -EHOSTDOWN;
463 else {
464 ses->server->tcpStatus = CifsNeedReconnect;
465 midQ->midState = MID_RETRY_NEEDED;
466 }
467 }
468
469 if (rc != -EHOSTDOWN) {
470 if(midQ->midState == MID_RETRY_NEEDED) {
471 rc = -EAGAIN;
472 cFYI(1,("marking request for retry"));
473 } else {
474 rc = -EIO;
475 }
476 }
477 spin_unlock(&GlobalMid_Lock);
478 DeleteMidQEntry(midQ);
479 /* If not lock req, update # of requests on wire to server */
480 if(long_op < 3) {
481 atomic_dec(&ses->server->inFlight);
482 wake_up(&ses->server->request_q);
483 }
484 return rc;
485 }
486
487 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
488 cERROR(1, ("Frame too large received. Length: %d Xid: %d",
489 receive_len, xid));
490 rc = -EIO;
491 } else { /* rcvd frame is ok */
492
493 if (midQ->resp_buf &&
494 (midQ->midState == MID_RESPONSE_RECEIVED)) {
495 in_buf->smb_buf_length = receive_len;
496 /* BB verify that length would not overrun small buf */
497 memcpy((char *)in_buf + 4,
498 (char *)midQ->resp_buf + 4,
499 receive_len);
500
501 dump_smb(in_buf, 80);
502 /* convert the length into a more usable form */
503 if((receive_len > 24) &&
504 (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
505 SECMODE_SIGN_ENABLED))) {
506 rc = cifs_verify_signature(in_buf,
507 ses->server->mac_signing_key,
508 midQ->sequence_number+1);
509 if(rc) {
510 cERROR(1,("Unexpected SMB signature"));
511 /* BB FIXME add code to kill session */
512 }
513 }
514
515 *pbytes_returned = in_buf->smb_buf_length;
516
517 /* BB special case reconnect tid and uid here? */
Steve French6ab16d22005-11-29 20:55:11 -0800518 /* BB special case Errbadpassword and pwdexpired here */
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500519 rc = map_smb_to_linux_error(in_buf);
520
521 /* convert ByteCount if necessary */
522 if (receive_len >=
523 sizeof (struct smb_hdr) -
524 4 /* do not count RFC1001 header */ +
525 (2 * in_buf->WordCount) + 2 /* bcc */ )
Steve French0f2b27c2005-11-16 14:25:50 -0800526 BCC(in_buf) = le16_to_cpu(BCC_LE(in_buf));
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500527 } else {
528 rc = -EIO;
Steve Frenchab2f2182005-09-15 20:44:50 -0700529 cFYI(1,("Bad MID state?"));
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500530 }
531 }
532cifs_no_response_exit2:
533 DeleteMidQEntry(midQ);
534
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 if(long_op < 3) {
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500536 atomic_dec(&ses->server->inFlight);
537 wake_up(&ses->server->request_q);
538 }
539
540 return rc;
541
542out_unlock2:
543 up(&ses->server->tcpSem);
544 /* If not lock req, update # of requests on wire to server */
545 if(long_op < 3) {
546 atomic_dec(&ses->server->inFlight);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 wake_up(&ses->server->request_q);
548 }
549
550 return rc;
551}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700552#endif /* CIFS_EXPERIMENTAL */
553
554int
555SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
556 struct smb_hdr *in_buf, struct smb_hdr *out_buf,
557 int *pbytes_returned, const int long_op)
558{
559 int rc = 0;
560 unsigned int receive_len;
561 unsigned long timeout;
562 struct mid_q_entry *midQ;
563
564 if (ses == NULL) {
565 cERROR(1,("Null smb session"));
566 return -EIO;
567 }
568 if(ses->server == NULL) {
569 cERROR(1,("Null tcp session"));
570 return -EIO;
571 }
572
Steve French31ca3bc2005-04-28 22:41:11 -0700573 if(ses->server->tcpStatus == CifsExiting)
574 return -ENOENT;
575
Linus Torvalds1da177e2005-04-16 15:20:36 -0700576 /* Ensure that we do not send more than 50 overlapping requests
577 to the same server. We may make this configurable later or
578 use ses->maxReq */
579 if(long_op == -1) {
580 /* oplock breaks must not be held up */
581 atomic_inc(&ses->server->inFlight);
582 } else {
583 spin_lock(&GlobalMid_Lock);
584 while(1) {
Steve French275cde12005-04-28 22:41:10 -0700585 if(atomic_read(&ses->server->inFlight) >=
586 cifs_max_pending){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 spin_unlock(&GlobalMid_Lock);
Steve French131afd02005-10-07 09:51:05 -0700588#ifdef CONFIG_CIFS_STATS2
589 atomic_inc(&ses->server->num_waiters);
590#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 wait_event(ses->server->request_q,
592 atomic_read(&ses->server->inFlight)
593 < cifs_max_pending);
Steve French131afd02005-10-07 09:51:05 -0700594#ifdef CONFIG_CIFS_STATS2
595 atomic_dec(&ses->server->num_waiters);
596#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 spin_lock(&GlobalMid_Lock);
598 } else {
599 if(ses->server->tcpStatus == CifsExiting) {
600 spin_unlock(&GlobalMid_Lock);
601 return -ENOENT;
602 }
603
604 /* can not count locking commands against total since
605 they are allowed to block on server */
606
607 if(long_op < 3) {
608 /* update # of requests on the wire to server */
609 atomic_inc(&ses->server->inFlight);
610 }
611 spin_unlock(&GlobalMid_Lock);
612 break;
613 }
614 }
615 }
616 /* make sure that we sign in the same order that we send on this socket
617 and avoid races inside tcp sendmsg code that could cause corruption
618 of smb data */
619
620 down(&ses->server->tcpSem);
621
622 if (ses->server->tcpStatus == CifsExiting) {
623 rc = -ENOENT;
624 goto out_unlock;
625 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
626 cFYI(1,("tcp session dead - return to caller to retry"));
627 rc = -EAGAIN;
628 goto out_unlock;
629 } else if (ses->status != CifsGood) {
630 /* check if SMB session is bad because we are setting it up */
631 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
632 (in_buf->Command != SMB_COM_NEGOTIATE)) {
633 rc = -EAGAIN;
634 goto out_unlock;
635 } /* else ok - we are setting up session */
636 }
637 midQ = AllocMidQEntry(in_buf, ses);
638 if (midQ == NULL) {
639 up(&ses->server->tcpSem);
640 /* If not lock req, update # of requests on wire to server */
641 if(long_op < 3) {
642 atomic_dec(&ses->server->inFlight);
643 wake_up(&ses->server->request_q);
644 }
645 return -ENOMEM;
646 }
647
648 if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
649 up(&ses->server->tcpSem);
650 cERROR(1,
651 ("Illegal length, greater than maximum frame, %d ",
652 in_buf->smb_buf_length));
653 DeleteMidQEntry(midQ);
654 /* If not lock req, update # of requests on wire to server */
655 if(long_op < 3) {
656 atomic_dec(&ses->server->inFlight);
657 wake_up(&ses->server->request_q);
658 }
659 return -EIO;
660 }
661
Steve Frenchad009ac2005-04-28 22:41:05 -0700662 rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
664 midQ->midState = MID_REQUEST_SUBMITTED;
Steve French131afd02005-10-07 09:51:05 -0700665#ifdef CONFIG_CIFS_STATS2
666 atomic_inc(&ses->server->inSend);
667#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
669 (struct sockaddr *) &(ses->server->addr.sockAddr));
Steve French131afd02005-10-07 09:51:05 -0700670#ifdef CONFIG_CIFS_STATS2
671 atomic_dec(&ses->server->inSend);
Steve French1047abc2005-10-11 19:58:06 -0700672 midQ->when_sent = jiffies;
Steve French131afd02005-10-07 09:51:05 -0700673#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 if(rc < 0) {
675 DeleteMidQEntry(midQ);
676 up(&ses->server->tcpSem);
677 /* If not lock req, update # of requests on wire to server */
678 if(long_op < 3) {
679 atomic_dec(&ses->server->inFlight);
680 wake_up(&ses->server->request_q);
681 }
682 return rc;
683 } else
684 up(&ses->server->tcpSem);
685 if (long_op == -1)
686 goto cifs_no_response_exit;
Steve French275cde12005-04-28 22:41:10 -0700687 else if (long_op == 2) /* writes past end of file can take loong time */
Steve French37c0eb42005-10-05 14:50:29 -0700688 timeout = 180 * HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700689 else if (long_op == 1)
690 timeout = 45 * HZ; /* should be greater than
691 servers oplock break timeout (about 43 seconds) */
692 else if (long_op > 2) {
693 timeout = MAX_SCHEDULE_TIMEOUT;
694 } else
695 timeout = 15 * HZ;
696 /* wait for 15 seconds or until woken up due to response arriving or
697 due to last connection to this server being unmounted */
698 if (signal_pending(current)) {
699 /* if signal pending do not hold up user for full smb timeout
700 but we still give response a change to complete */
701 timeout = 2 * HZ;
702 }
703
704 /* No user interrupts in wait - wreaks havoc with performance */
705 if(timeout != MAX_SCHEDULE_TIMEOUT) {
706 timeout += jiffies;
707 wait_event(ses->server->response_q,
708 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
709 time_after(jiffies, timeout) ||
710 ((ses->server->tcpStatus != CifsGood) &&
711 (ses->server->tcpStatus != CifsNew)));
712 } else {
713 wait_event(ses->server->response_q,
714 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
715 ((ses->server->tcpStatus != CifsGood) &&
716 (ses->server->tcpStatus != CifsNew)));
717 }
718
719 spin_lock(&GlobalMid_Lock);
720 if (midQ->resp_buf) {
721 spin_unlock(&GlobalMid_Lock);
Steve French70ca7342005-09-22 16:32:06 -0700722 receive_len = midQ->resp_buf->smb_buf_length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 } else {
Steve French37c0eb42005-10-05 14:50:29 -0700724 cERROR(1,("No response for cmd %d mid %d",
725 midQ->command, midQ->mid));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726 if(midQ->midState == MID_REQUEST_SUBMITTED) {
727 if(ses->server->tcpStatus == CifsExiting)
728 rc = -EHOSTDOWN;
729 else {
730 ses->server->tcpStatus = CifsNeedReconnect;
731 midQ->midState = MID_RETRY_NEEDED;
732 }
733 }
734
735 if (rc != -EHOSTDOWN) {
736 if(midQ->midState == MID_RETRY_NEEDED) {
737 rc = -EAGAIN;
738 cFYI(1,("marking request for retry"));
739 } else {
740 rc = -EIO;
741 }
742 }
743 spin_unlock(&GlobalMid_Lock);
744 DeleteMidQEntry(midQ);
745 /* If not lock req, update # of requests on wire to server */
746 if(long_op < 3) {
747 atomic_dec(&ses->server->inFlight);
748 wake_up(&ses->server->request_q);
749 }
750 return rc;
751 }
752
753 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
Steve Frenchad009ac2005-04-28 22:41:05 -0700754 cERROR(1, ("Frame too large received. Length: %d Xid: %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 receive_len, xid));
756 rc = -EIO;
757 } else { /* rcvd frame is ok */
758
759 if (midQ->resp_buf && out_buf
760 && (midQ->midState == MID_RESPONSE_RECEIVED)) {
761 out_buf->smb_buf_length = receive_len;
762 memcpy((char *)out_buf + 4,
763 (char *)midQ->resp_buf + 4,
764 receive_len);
765
766 dump_smb(out_buf, 92);
767 /* convert the length into a more usable form */
768 if((receive_len > 24) &&
Steve Frenchad009ac2005-04-28 22:41:05 -0700769 (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
770 SECMODE_SIGN_ENABLED))) {
771 rc = cifs_verify_signature(out_buf,
772 ses->server->mac_signing_key,
773 midQ->sequence_number+1);
774 if(rc) {
Steve French275cde12005-04-28 22:41:10 -0700775 cERROR(1,("Unexpected SMB signature"));
776 /* BB FIXME add code to kill session */
Steve Frenchad009ac2005-04-28 22:41:05 -0700777 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 }
779
780 *pbytes_returned = out_buf->smb_buf_length;
781
Steve Frenchad009ac2005-04-28 22:41:05 -0700782 /* BB special case reconnect tid and uid here? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783 rc = map_smb_to_linux_error(out_buf);
784
785 /* convert ByteCount if necessary */
786 if (receive_len >=
787 sizeof (struct smb_hdr) -
788 4 /* do not count RFC1001 header */ +
789 (2 * out_buf->WordCount) + 2 /* bcc */ )
Steve French0f2b27c2005-11-16 14:25:50 -0800790 BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 } else {
792 rc = -EIO;
Steve Frencha5a2b482005-08-20 21:42:53 -0700793 cERROR(1,("Bad MID state? "));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 }
795 }
796cifs_no_response_exit:
797 DeleteMidQEntry(midQ);
798
799 if(long_op < 3) {
800 atomic_dec(&ses->server->inFlight);
801 wake_up(&ses->server->request_q);
802 }
803
804 return rc;
805
806out_unlock:
807 up(&ses->server->tcpSem);
808 /* If not lock req, update # of requests on wire to server */
809 if(long_op < 3) {
810 atomic_dec(&ses->server->inFlight);
811 wake_up(&ses->server->request_q);
812 }
813
814 return rc;
815}