blob: d8865fbd876ab785c3c22d0032f93ea9b36ce137 [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 */
Steve French68058e72005-10-10 10:34:22 -0700160 if(i > 12) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700161 cERROR(1,
Steve French68058e72005-10-10 10:34:22 -0700162 ("sends on sock %p stuck for 7 seconds",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700163 ssocket));
164 rc = -EAGAIN;
165 break;
166 }
Steve French68058e72005-10-10 10:34:22 -0700167 msleep(1 << i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168 continue;
169 }
170 if (rc < 0)
171 break;
Steve French5e1253b2005-10-10 14:06:37 -0700172 else
173 i = 0; /* reset i after each successful send */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 iov.iov_base += rc;
175 iov.iov_len -= rc;
176 len -= rc;
177 }
178
179 if (rc < 0) {
Steve French3e844692005-10-03 13:37:24 -0700180 cERROR(1,("Error %d sending data on socket to server", rc));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 } else {
182 rc = 0;
183 }
184
185 return rc;
186}
187
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500188#ifdef CONFIG_CIFS_EXPERIMENTAL
189static int
Steve French3e844692005-10-03 13:37:24 -0700190smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
191 struct sockaddr *sin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700192{
193 int rc = 0;
194 int i = 0;
195 struct msghdr smb_msg;
Steve French3e844692005-10-03 13:37:24 -0700196 struct smb_hdr *smb_buffer = iov[0].iov_base;
197 unsigned int len = iov[0].iov_len;
198 unsigned int total_len;
199 int first_vec = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500200
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 if(ssocket == NULL)
202 return -ENOTSOCK; /* BB eventually add reconnect code here */
Steve French3e844692005-10-03 13:37:24 -0700203
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 smb_msg.msg_name = sin;
205 smb_msg.msg_namelen = sizeof (struct sockaddr);
206 smb_msg.msg_control = NULL;
207 smb_msg.msg_controllen = 0;
208 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
209
210 /* smb header is converted in header_assemble. bcc and rest of SMB word
211 area, and byte area if necessary, is converted to littleendian in
212 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
213 Flags2 is converted in SendReceive */
214
Steve French3e844692005-10-03 13:37:24 -0700215
216 total_len = 0;
217 for (i = 0; i < n_vec; i++)
218 total_len += iov[i].iov_len;
219
Linus Torvalds1da177e2005-04-16 15:20:36 -0700220 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
Steve French3e844692005-10-03 13:37:24 -0700221 cFYI(1, ("Sending smb: total_len %d", total_len));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700222 dump_smb(smb_buffer, len);
223
Steve French3e844692005-10-03 13:37:24 -0700224 while (total_len) {
225 rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
226 n_vec - first_vec, total_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
228 i++;
Steve French68058e72005-10-10 10:34:22 -0700229 if(i >= 14) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 cERROR(1,
Steve French68058e72005-10-10 10:34:22 -0700231 ("sends on sock %p stuck for 15 seconds",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 ssocket));
233 rc = -EAGAIN;
234 break;
235 }
Steve French68058e72005-10-10 10:34:22 -0700236 msleep(1 << i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 continue;
238 }
239 if (rc < 0)
240 break;
Steve French3e844692005-10-03 13:37:24 -0700241
242 if (rc >= total_len) {
243 WARN_ON(rc > total_len);
244 break;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500245 }
Steve French3e844692005-10-03 13:37:24 -0700246 if(rc == 0) {
247 /* should never happen, letting socket clear before
248 retrying is our only obvious option here */
Steve French04c08812005-10-03 19:33:15 -0700249 cERROR(1,("tcp sent no data"));
Steve French3e844692005-10-03 13:37:24 -0700250 msleep(500);
251 continue;
252 }
253 total_len -= rc;
Steve French68058e72005-10-10 10:34:22 -0700254 /* the line below resets i */
Steve French3e844692005-10-03 13:37:24 -0700255 for (i = first_vec; i < n_vec; i++) {
256 if (iov[i].iov_len) {
257 if (rc > iov[i].iov_len) {
258 rc -= iov[i].iov_len;
259 iov[i].iov_len = 0;
260 } else {
261 iov[i].iov_base += rc;
262 iov[i].iov_len -= rc;
263 first_vec = i;
264 break;
265 }
266 }
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500267 }
Steve French5e1253b2005-10-10 14:06:37 -0700268 i = 0; /* in case we get ENOSPC on the next send */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 }
270
271 if (rc < 0) {
Steve French3e844692005-10-03 13:37:24 -0700272 cERROR(1,("Error %d sending data on socket to server", rc));
273 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275
276 return rc;
277}
278
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279int
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500280SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
Steve French3e844692005-10-03 13:37:24 -0700281 struct kvec *iov, int n_vec, int *pbytes_returned,
282 const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
284 int rc = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500285 unsigned int receive_len;
286 unsigned long timeout;
287 struct mid_q_entry *midQ;
Steve French3e844692005-10-03 13:37:24 -0700288 struct smb_hdr *in_buf = iov[0].iov_base;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290 if (ses == NULL) {
291 cERROR(1,("Null smb session"));
292 return -EIO;
293 }
294 if(ses->server == NULL) {
295 cERROR(1,("Null tcp session"));
296 return -EIO;
297 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500299 if(ses->server->tcpStatus == CifsExiting)
Steve French31ca3bc2005-04-28 22:41:11 -0700300 return -ENOENT;
301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 /* Ensure that we do not send more than 50 overlapping requests
303 to the same server. We may make this configurable later or
304 use ses->maxReq */
305 if(long_op == -1) {
306 /* oplock breaks must not be held up */
307 atomic_inc(&ses->server->inFlight);
308 } else {
309 spin_lock(&GlobalMid_Lock);
310 while(1) {
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500311 if(atomic_read(&ses->server->inFlight) >=
312 cifs_max_pending){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 spin_unlock(&GlobalMid_Lock);
Steve French131afd0b2005-10-07 09:51:05 -0700314#ifdef CONFIG_CIFS_STATS2
315 atomic_inc(&ses->server->num_waiters);
316#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317 wait_event(ses->server->request_q,
318 atomic_read(&ses->server->inFlight)
319 < cifs_max_pending);
Steve French131afd0b2005-10-07 09:51:05 -0700320#ifdef CONFIG_CIFS_STATS2
321 atomic_dec(&ses->server->num_waiters);
322#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 spin_lock(&GlobalMid_Lock);
324 } else {
325 if(ses->server->tcpStatus == CifsExiting) {
326 spin_unlock(&GlobalMid_Lock);
327 return -ENOENT;
328 }
329
330 /* can not count locking commands against total since
331 they are allowed to block on server */
332
333 if(long_op < 3) {
334 /* update # of requests on the wire to server */
335 atomic_inc(&ses->server->inFlight);
336 }
337 spin_unlock(&GlobalMid_Lock);
338 break;
339 }
340 }
341 }
342 /* make sure that we sign in the same order that we send on this socket
343 and avoid races inside tcp sendmsg code that could cause corruption
344 of smb data */
345
346 down(&ses->server->tcpSem);
347
348 if (ses->server->tcpStatus == CifsExiting) {
349 rc = -ENOENT;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500350 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
352 cFYI(1,("tcp session dead - return to caller to retry"));
353 rc = -EAGAIN;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500354 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 } else if (ses->status != CifsGood) {
356 /* check if SMB session is bad because we are setting it up */
357 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
358 (in_buf->Command != SMB_COM_NEGOTIATE)) {
359 rc = -EAGAIN;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500360 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700361 } /* else ok - we are setting up session */
362 }
363 midQ = AllocMidQEntry(in_buf, ses);
364 if (midQ == NULL) {
365 up(&ses->server->tcpSem);
366 /* If not lock req, update # of requests on wire to server */
367 if(long_op < 3) {
368 atomic_dec(&ses->server->inFlight);
369 wake_up(&ses->server->request_q);
370 }
371 return -ENOMEM;
372 }
373
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500374/* BB FIXME */
Steve French4a771182005-10-05 15:14:33 -0700375/* rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376
377 midQ->midState = MID_REQUEST_SUBMITTED;
Steve French131afd0b2005-10-07 09:51:05 -0700378#ifdef CONFIG_CIFS_STATS2
379 atomic_inc(&ses->server->inSend);
380#endif
Steve French3e844692005-10-03 13:37:24 -0700381 rc = smb_send2(ses->server->ssocket, iov, n_vec,
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500382 (struct sockaddr *) &(ses->server->addr.sockAddr));
Steve French131afd0b2005-10-07 09:51:05 -0700383#ifdef CONFIG_CIFS_STATS2
384 atomic_dec(&ses->server->inSend);
385#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386 if(rc < 0) {
387 DeleteMidQEntry(midQ);
388 up(&ses->server->tcpSem);
389 /* If not lock req, update # of requests on wire to server */
390 if(long_op < 3) {
391 atomic_dec(&ses->server->inFlight);
392 wake_up(&ses->server->request_q);
393 }
394 return rc;
395 } else
396 up(&ses->server->tcpSem);
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500397 if (long_op == -1)
398 goto cifs_no_response_exit2;
399 else if (long_op == 2) /* writes past end of file can take loong time */
Steve French37c0eb42005-10-05 14:50:29 -0700400 timeout = 180 * HZ;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500401 else if (long_op == 1)
402 timeout = 45 * HZ; /* should be greater than
403 servers oplock break timeout (about 43 seconds) */
404 else if (long_op > 2) {
405 timeout = MAX_SCHEDULE_TIMEOUT;
406 } else
407 timeout = 15 * HZ;
408 /* wait for 15 seconds or until woken up due to response arriving or
409 due to last connection to this server being unmounted */
410 if (signal_pending(current)) {
411 /* if signal pending do not hold up user for full smb timeout
412 but we still give response a change to complete */
413 timeout = 2 * HZ;
414 }
415
416 /* No user interrupts in wait - wreaks havoc with performance */
417 if(timeout != MAX_SCHEDULE_TIMEOUT) {
418 timeout += jiffies;
419 wait_event(ses->server->response_q,
420 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
421 time_after(jiffies, timeout) ||
422 ((ses->server->tcpStatus != CifsGood) &&
423 (ses->server->tcpStatus != CifsNew)));
424 } else {
425 wait_event(ses->server->response_q,
426 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
427 ((ses->server->tcpStatus != CifsGood) &&
428 (ses->server->tcpStatus != CifsNew)));
429 }
430
431 spin_lock(&GlobalMid_Lock);
432 if (midQ->resp_buf) {
433 spin_unlock(&GlobalMid_Lock);
Steve French70ca7342005-09-22 16:32:06 -0700434 receive_len = midQ->resp_buf->smb_buf_length;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500435 } else {
Steve French37c0eb42005-10-05 14:50:29 -0700436 cERROR(1,("No response to cmd %d mid %d",
437 midQ->command, midQ->mid));
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500438 if(midQ->midState == MID_REQUEST_SUBMITTED) {
439 if(ses->server->tcpStatus == CifsExiting)
440 rc = -EHOSTDOWN;
441 else {
442 ses->server->tcpStatus = CifsNeedReconnect;
443 midQ->midState = MID_RETRY_NEEDED;
444 }
445 }
446
447 if (rc != -EHOSTDOWN) {
448 if(midQ->midState == MID_RETRY_NEEDED) {
449 rc = -EAGAIN;
450 cFYI(1,("marking request for retry"));
451 } else {
452 rc = -EIO;
453 }
454 }
455 spin_unlock(&GlobalMid_Lock);
456 DeleteMidQEntry(midQ);
457 /* If not lock req, update # of requests on wire to server */
458 if(long_op < 3) {
459 atomic_dec(&ses->server->inFlight);
460 wake_up(&ses->server->request_q);
461 }
462 return rc;
463 }
464
465 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
466 cERROR(1, ("Frame too large received. Length: %d Xid: %d",
467 receive_len, xid));
468 rc = -EIO;
469 } else { /* rcvd frame is ok */
470
471 if (midQ->resp_buf &&
472 (midQ->midState == MID_RESPONSE_RECEIVED)) {
473 in_buf->smb_buf_length = receive_len;
474 /* BB verify that length would not overrun small buf */
475 memcpy((char *)in_buf + 4,
476 (char *)midQ->resp_buf + 4,
477 receive_len);
478
479 dump_smb(in_buf, 80);
480 /* convert the length into a more usable form */
481 if((receive_len > 24) &&
482 (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
483 SECMODE_SIGN_ENABLED))) {
484 rc = cifs_verify_signature(in_buf,
485 ses->server->mac_signing_key,
486 midQ->sequence_number+1);
487 if(rc) {
488 cERROR(1,("Unexpected SMB signature"));
489 /* BB FIXME add code to kill session */
490 }
491 }
492
493 *pbytes_returned = in_buf->smb_buf_length;
494
495 /* BB special case reconnect tid and uid here? */
496 rc = map_smb_to_linux_error(in_buf);
497
498 /* convert ByteCount if necessary */
499 if (receive_len >=
500 sizeof (struct smb_hdr) -
501 4 /* do not count RFC1001 header */ +
502 (2 * in_buf->WordCount) + 2 /* bcc */ )
503 BCC(in_buf) = le16_to_cpu(BCC(in_buf));
504 } else {
505 rc = -EIO;
Steve Frenchab2f2182005-09-15 20:44:50 -0700506 cFYI(1,("Bad MID state?"));
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500507 }
508 }
509cifs_no_response_exit2:
510 DeleteMidQEntry(midQ);
511
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512 if(long_op < 3) {
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500513 atomic_dec(&ses->server->inFlight);
514 wake_up(&ses->server->request_q);
515 }
516
517 return rc;
518
519out_unlock2:
520 up(&ses->server->tcpSem);
521 /* If not lock req, update # of requests on wire to server */
522 if(long_op < 3) {
523 atomic_dec(&ses->server->inFlight);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 wake_up(&ses->server->request_q);
525 }
526
527 return rc;
528}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529#endif /* CIFS_EXPERIMENTAL */
530
531int
532SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
533 struct smb_hdr *in_buf, struct smb_hdr *out_buf,
534 int *pbytes_returned, const int long_op)
535{
536 int rc = 0;
537 unsigned int receive_len;
538 unsigned long timeout;
539 struct mid_q_entry *midQ;
540
541 if (ses == NULL) {
542 cERROR(1,("Null smb session"));
543 return -EIO;
544 }
545 if(ses->server == NULL) {
546 cERROR(1,("Null tcp session"));
547 return -EIO;
548 }
549
Steve French31ca3bc2005-04-28 22:41:11 -0700550 if(ses->server->tcpStatus == CifsExiting)
551 return -ENOENT;
552
Linus Torvalds1da177e2005-04-16 15:20:36 -0700553 /* Ensure that we do not send more than 50 overlapping requests
554 to the same server. We may make this configurable later or
555 use ses->maxReq */
556 if(long_op == -1) {
557 /* oplock breaks must not be held up */
558 atomic_inc(&ses->server->inFlight);
559 } else {
560 spin_lock(&GlobalMid_Lock);
561 while(1) {
Steve French275cde12005-04-28 22:41:10 -0700562 if(atomic_read(&ses->server->inFlight) >=
563 cifs_max_pending){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700564 spin_unlock(&GlobalMid_Lock);
Steve French131afd0b2005-10-07 09:51:05 -0700565#ifdef CONFIG_CIFS_STATS2
566 atomic_inc(&ses->server->num_waiters);
567#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 wait_event(ses->server->request_q,
569 atomic_read(&ses->server->inFlight)
570 < cifs_max_pending);
Steve French131afd0b2005-10-07 09:51:05 -0700571#ifdef CONFIG_CIFS_STATS2
572 atomic_dec(&ses->server->num_waiters);
573#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574 spin_lock(&GlobalMid_Lock);
575 } else {
576 if(ses->server->tcpStatus == CifsExiting) {
577 spin_unlock(&GlobalMid_Lock);
578 return -ENOENT;
579 }
580
581 /* can not count locking commands against total since
582 they are allowed to block on server */
583
584 if(long_op < 3) {
585 /* update # of requests on the wire to server */
586 atomic_inc(&ses->server->inFlight);
587 }
588 spin_unlock(&GlobalMid_Lock);
589 break;
590 }
591 }
592 }
593 /* make sure that we sign in the same order that we send on this socket
594 and avoid races inside tcp sendmsg code that could cause corruption
595 of smb data */
596
597 down(&ses->server->tcpSem);
598
599 if (ses->server->tcpStatus == CifsExiting) {
600 rc = -ENOENT;
601 goto out_unlock;
602 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
603 cFYI(1,("tcp session dead - return to caller to retry"));
604 rc = -EAGAIN;
605 goto out_unlock;
606 } else if (ses->status != CifsGood) {
607 /* check if SMB session is bad because we are setting it up */
608 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
609 (in_buf->Command != SMB_COM_NEGOTIATE)) {
610 rc = -EAGAIN;
611 goto out_unlock;
612 } /* else ok - we are setting up session */
613 }
614 midQ = AllocMidQEntry(in_buf, ses);
615 if (midQ == NULL) {
616 up(&ses->server->tcpSem);
617 /* If not lock req, update # of requests on wire to server */
618 if(long_op < 3) {
619 atomic_dec(&ses->server->inFlight);
620 wake_up(&ses->server->request_q);
621 }
622 return -ENOMEM;
623 }
624
625 if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
626 up(&ses->server->tcpSem);
627 cERROR(1,
628 ("Illegal length, greater than maximum frame, %d ",
629 in_buf->smb_buf_length));
630 DeleteMidQEntry(midQ);
631 /* If not lock req, update # of requests on wire to server */
632 if(long_op < 3) {
633 atomic_dec(&ses->server->inFlight);
634 wake_up(&ses->server->request_q);
635 }
636 return -EIO;
637 }
638
Steve Frenchad009ac2005-04-28 22:41:05 -0700639 rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700640
641 midQ->midState = MID_REQUEST_SUBMITTED;
Steve French131afd0b2005-10-07 09:51:05 -0700642#ifdef CONFIG_CIFS_STATS2
643 atomic_inc(&ses->server->inSend);
644#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
646 (struct sockaddr *) &(ses->server->addr.sockAddr));
Steve French131afd0b2005-10-07 09:51:05 -0700647#ifdef CONFIG_CIFS_STATS2
648 atomic_dec(&ses->server->inSend);
649#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 if(rc < 0) {
651 DeleteMidQEntry(midQ);
652 up(&ses->server->tcpSem);
653 /* If not lock req, update # of requests on wire to server */
654 if(long_op < 3) {
655 atomic_dec(&ses->server->inFlight);
656 wake_up(&ses->server->request_q);
657 }
658 return rc;
659 } else
660 up(&ses->server->tcpSem);
661 if (long_op == -1)
662 goto cifs_no_response_exit;
Steve French275cde12005-04-28 22:41:10 -0700663 else if (long_op == 2) /* writes past end of file can take loong time */
Steve French37c0eb42005-10-05 14:50:29 -0700664 timeout = 180 * HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665 else if (long_op == 1)
666 timeout = 45 * HZ; /* should be greater than
667 servers oplock break timeout (about 43 seconds) */
668 else if (long_op > 2) {
669 timeout = MAX_SCHEDULE_TIMEOUT;
670 } else
671 timeout = 15 * HZ;
672 /* wait for 15 seconds or until woken up due to response arriving or
673 due to last connection to this server being unmounted */
674 if (signal_pending(current)) {
675 /* if signal pending do not hold up user for full smb timeout
676 but we still give response a change to complete */
677 timeout = 2 * HZ;
678 }
679
680 /* No user interrupts in wait - wreaks havoc with performance */
681 if(timeout != MAX_SCHEDULE_TIMEOUT) {
682 timeout += jiffies;
683 wait_event(ses->server->response_q,
684 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
685 time_after(jiffies, timeout) ||
686 ((ses->server->tcpStatus != CifsGood) &&
687 (ses->server->tcpStatus != CifsNew)));
688 } else {
689 wait_event(ses->server->response_q,
690 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
691 ((ses->server->tcpStatus != CifsGood) &&
692 (ses->server->tcpStatus != CifsNew)));
693 }
694
695 spin_lock(&GlobalMid_Lock);
696 if (midQ->resp_buf) {
697 spin_unlock(&GlobalMid_Lock);
Steve French70ca7342005-09-22 16:32:06 -0700698 receive_len = midQ->resp_buf->smb_buf_length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 } else {
Steve French37c0eb42005-10-05 14:50:29 -0700700 cERROR(1,("No response for cmd %d mid %d",
701 midQ->command, midQ->mid));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 if(midQ->midState == MID_REQUEST_SUBMITTED) {
703 if(ses->server->tcpStatus == CifsExiting)
704 rc = -EHOSTDOWN;
705 else {
706 ses->server->tcpStatus = CifsNeedReconnect;
707 midQ->midState = MID_RETRY_NEEDED;
708 }
709 }
710
711 if (rc != -EHOSTDOWN) {
712 if(midQ->midState == MID_RETRY_NEEDED) {
713 rc = -EAGAIN;
714 cFYI(1,("marking request for retry"));
715 } else {
716 rc = -EIO;
717 }
718 }
719 spin_unlock(&GlobalMid_Lock);
720 DeleteMidQEntry(midQ);
721 /* If not lock req, update # of requests on wire to server */
722 if(long_op < 3) {
723 atomic_dec(&ses->server->inFlight);
724 wake_up(&ses->server->request_q);
725 }
726 return rc;
727 }
728
729 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
Steve Frenchad009ac2005-04-28 22:41:05 -0700730 cERROR(1, ("Frame too large received. Length: %d Xid: %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700731 receive_len, xid));
732 rc = -EIO;
733 } else { /* rcvd frame is ok */
734
735 if (midQ->resp_buf && out_buf
736 && (midQ->midState == MID_RESPONSE_RECEIVED)) {
737 out_buf->smb_buf_length = receive_len;
738 memcpy((char *)out_buf + 4,
739 (char *)midQ->resp_buf + 4,
740 receive_len);
741
742 dump_smb(out_buf, 92);
743 /* convert the length into a more usable form */
744 if((receive_len > 24) &&
Steve Frenchad009ac2005-04-28 22:41:05 -0700745 (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
746 SECMODE_SIGN_ENABLED))) {
747 rc = cifs_verify_signature(out_buf,
748 ses->server->mac_signing_key,
749 midQ->sequence_number+1);
750 if(rc) {
Steve French275cde12005-04-28 22:41:10 -0700751 cERROR(1,("Unexpected SMB signature"));
752 /* BB FIXME add code to kill session */
Steve Frenchad009ac2005-04-28 22:41:05 -0700753 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 }
755
756 *pbytes_returned = out_buf->smb_buf_length;
757
Steve Frenchad009ac2005-04-28 22:41:05 -0700758 /* BB special case reconnect tid and uid here? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700759 rc = map_smb_to_linux_error(out_buf);
760
761 /* convert ByteCount if necessary */
762 if (receive_len >=
763 sizeof (struct smb_hdr) -
764 4 /* do not count RFC1001 header */ +
765 (2 * out_buf->WordCount) + 2 /* bcc */ )
766 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
767 } else {
768 rc = -EIO;
Steve Frencha5a2b482005-08-20 21:42:53 -0700769 cERROR(1,("Bad MID state? "));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770 }
771 }
772cifs_no_response_exit:
773 DeleteMidQEntry(midQ);
774
775 if(long_op < 3) {
776 atomic_dec(&ses->server->inFlight);
777 wake_up(&ses->server->request_q);
778 }
779
780 return rc;
781
782out_unlock:
783 up(&ses->server->tcpSem);
784 /* If not lock req, update # of requests on wire to server */
785 if(long_op < 3) {
786 atomic_dec(&ses->server->inFlight);
787 wake_up(&ses->server->request_q);
788 }
789
790 return rc;
791}