blob: 17ba329e2b3de5cbf02b973cf0b3a04515736b43 [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 -0500209static int
Steve French3e844692005-10-03 13:37:24 -0700210smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec,
211 struct sockaddr *sin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212{
213 int rc = 0;
214 int i = 0;
215 struct msghdr smb_msg;
Steve French3e844692005-10-03 13:37:24 -0700216 struct smb_hdr *smb_buffer = iov[0].iov_base;
217 unsigned int len = iov[0].iov_len;
218 unsigned int total_len;
219 int first_vec = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500220
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 if(ssocket == NULL)
222 return -ENOTSOCK; /* BB eventually add reconnect code here */
Steve French3e844692005-10-03 13:37:24 -0700223
Linus Torvalds1da177e2005-04-16 15:20:36 -0700224 smb_msg.msg_name = sin;
225 smb_msg.msg_namelen = sizeof (struct sockaddr);
226 smb_msg.msg_control = NULL;
227 smb_msg.msg_controllen = 0;
228 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
229
230 /* smb header is converted in header_assemble. bcc and rest of SMB word
231 area, and byte area if necessary, is converted to littleendian in
232 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
233 Flags2 is converted in SendReceive */
234
Steve French3e844692005-10-03 13:37:24 -0700235
236 total_len = 0;
237 for (i = 0; i < n_vec; i++)
238 total_len += iov[i].iov_len;
239
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
Steve French3e844692005-10-03 13:37:24 -0700241 cFYI(1, ("Sending smb: total_len %d", total_len));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 dump_smb(smb_buffer, len);
243
Steve French3e844692005-10-03 13:37:24 -0700244 while (total_len) {
245 rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec],
246 n_vec - first_vec, total_len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
248 i++;
Steve French68058e72005-10-10 10:34:22 -0700249 if(i >= 14) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 cERROR(1,
Steve French68058e72005-10-10 10:34:22 -0700251 ("sends on sock %p stuck for 15 seconds",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 ssocket));
253 rc = -EAGAIN;
254 break;
255 }
Steve French68058e72005-10-10 10:34:22 -0700256 msleep(1 << i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 continue;
258 }
259 if (rc < 0)
260 break;
Steve French3e844692005-10-03 13:37:24 -0700261
262 if (rc >= total_len) {
263 WARN_ON(rc > total_len);
264 break;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500265 }
Steve French3e844692005-10-03 13:37:24 -0700266 if(rc == 0) {
267 /* should never happen, letting socket clear before
268 retrying is our only obvious option here */
Steve French04c08812005-10-03 19:33:15 -0700269 cERROR(1,("tcp sent no data"));
Steve French3e844692005-10-03 13:37:24 -0700270 msleep(500);
271 continue;
272 }
273 total_len -= rc;
Steve French68058e72005-10-10 10:34:22 -0700274 /* the line below resets i */
Steve French3e844692005-10-03 13:37:24 -0700275 for (i = first_vec; i < n_vec; i++) {
276 if (iov[i].iov_len) {
277 if (rc > iov[i].iov_len) {
278 rc -= iov[i].iov_len;
279 iov[i].iov_len = 0;
280 } else {
281 iov[i].iov_base += rc;
282 iov[i].iov_len -= rc;
283 first_vec = i;
284 break;
285 }
286 }
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500287 }
Steve French5e1253b2005-10-10 14:06:37 -0700288 i = 0; /* in case we get ENOSPC on the next send */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289 }
290
291 if (rc < 0) {
Steve French3e844692005-10-03 13:37:24 -0700292 cERROR(1,("Error %d sending data on socket to server", rc));
293 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700294 rc = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 return rc;
297}
298
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299int
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500300SendReceive2(const unsigned int xid, struct cifsSesInfo *ses,
Steve Frenchec637e32005-12-12 20:53:18 -0800301 struct kvec *iov, int n_vec, int * pRespBufType /* ret */,
Steve French3e844692005-10-03 13:37:24 -0700302 const int long_op)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303{
304 int rc = 0;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500305 unsigned int receive_len;
306 unsigned long timeout;
307 struct mid_q_entry *midQ;
Steve French3e844692005-10-03 13:37:24 -0700308 struct smb_hdr *in_buf = iov[0].iov_base;
Steve Frenchec637e32005-12-12 20:53:18 -0800309
310 *pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311
Steve French4b8f9302006-02-26 16:41:18 +0000312 if ((ses == NULL) || (ses->server == NULL)) {
313 cifs_small_buf_release(in_buf);
314 cERROR(1,("Null session"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315 return -EIO;
316 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
Steve French4b8f9302006-02-26 16:41:18 +0000318 if(ses->server->tcpStatus == CifsExiting) {
319 cifs_small_buf_release(in_buf);
Steve French31ca3bc2005-04-28 22:41:11 -0700320 return -ENOENT;
Steve French4b8f9302006-02-26 16:41:18 +0000321 }
Steve French31ca3bc2005-04-28 22:41:11 -0700322
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 French131afd0b2005-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 French131afd0b2005-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);
Steve French4b8f9302006-02-26 16:41:18 +0000348 cifs_small_buf_release(in_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 return -ENOENT;
350 }
351
352 /* can not count locking commands against total since
353 they are allowed to block on server */
354
355 if(long_op < 3) {
356 /* update # of requests on the wire to server */
357 atomic_inc(&ses->server->inFlight);
358 }
359 spin_unlock(&GlobalMid_Lock);
360 break;
361 }
362 }
363 }
364 /* make sure that we sign in the same order that we send on this socket
365 and avoid races inside tcp sendmsg code that could cause corruption
366 of smb data */
367
368 down(&ses->server->tcpSem);
369
370 if (ses->server->tcpStatus == CifsExiting) {
371 rc = -ENOENT;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500372 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
374 cFYI(1,("tcp session dead - return to caller to retry"));
375 rc = -EAGAIN;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500376 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 } else if (ses->status != CifsGood) {
378 /* check if SMB session is bad because we are setting it up */
379 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
380 (in_buf->Command != SMB_COM_NEGOTIATE)) {
381 rc = -EAGAIN;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500382 goto out_unlock2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 } /* else ok - we are setting up session */
384 }
385 midQ = AllocMidQEntry(in_buf, ses);
386 if (midQ == NULL) {
387 up(&ses->server->tcpSem);
Steve French4b8f9302006-02-26 16:41:18 +0000388 cifs_small_buf_release(in_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389 /* 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 -ENOMEM;
395 }
396
Steve French84afc292005-12-02 13:32:45 -0800397 rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398
399 midQ->midState = MID_REQUEST_SUBMITTED;
Steve French131afd0b2005-10-07 09:51:05 -0700400#ifdef CONFIG_CIFS_STATS2
401 atomic_inc(&ses->server->inSend);
402#endif
Steve French3e844692005-10-03 13:37:24 -0700403 rc = smb_send2(ses->server->ssocket, iov, n_vec,
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500404 (struct sockaddr *) &(ses->server->addr.sockAddr));
Steve French131afd0b2005-10-07 09:51:05 -0700405#ifdef CONFIG_CIFS_STATS2
406 atomic_dec(&ses->server->inSend);
Steve French1047abc2005-10-11 19:58:06 -0700407 midQ->when_sent = jiffies;
Steve French131afd0b2005-10-07 09:51:05 -0700408#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 if(rc < 0) {
410 DeleteMidQEntry(midQ);
411 up(&ses->server->tcpSem);
Steve French4b8f9302006-02-26 16:41:18 +0000412 cifs_small_buf_release(in_buf);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 /* If not lock req, update # of requests on wire to server */
414 if(long_op < 3) {
415 atomic_dec(&ses->server->inFlight);
416 wake_up(&ses->server->request_q);
417 }
418 return rc;
Steve French4b8f9302006-02-26 16:41:18 +0000419 } else {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420 up(&ses->server->tcpSem);
Steve French4b8f9302006-02-26 16:41:18 +0000421 cifs_small_buf_release(in_buf);
422 }
423
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500424 if (long_op == -1)
425 goto cifs_no_response_exit2;
426 else if (long_op == 2) /* writes past end of file can take loong time */
Steve French37c0eb42005-10-05 14:50:29 -0700427 timeout = 180 * HZ;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500428 else if (long_op == 1)
429 timeout = 45 * HZ; /* should be greater than
430 servers oplock break timeout (about 43 seconds) */
431 else if (long_op > 2) {
432 timeout = MAX_SCHEDULE_TIMEOUT;
433 } else
434 timeout = 15 * HZ;
435 /* wait for 15 seconds or until woken up due to response arriving or
436 due to last connection to this server being unmounted */
437 if (signal_pending(current)) {
438 /* if signal pending do not hold up user for full smb timeout
439 but we still give response a change to complete */
440 timeout = 2 * HZ;
441 }
442
443 /* No user interrupts in wait - wreaks havoc with performance */
444 if(timeout != MAX_SCHEDULE_TIMEOUT) {
445 timeout += jiffies;
446 wait_event(ses->server->response_q,
447 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
448 time_after(jiffies, timeout) ||
449 ((ses->server->tcpStatus != CifsGood) &&
450 (ses->server->tcpStatus != CifsNew)));
451 } else {
452 wait_event(ses->server->response_q,
453 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
454 ((ses->server->tcpStatus != CifsGood) &&
455 (ses->server->tcpStatus != CifsNew)));
456 }
457
458 spin_lock(&GlobalMid_Lock);
459 if (midQ->resp_buf) {
460 spin_unlock(&GlobalMid_Lock);
Steve French70ca7342005-09-22 16:32:06 -0700461 receive_len = midQ->resp_buf->smb_buf_length;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500462 } else {
Steve French37c0eb42005-10-05 14:50:29 -0700463 cERROR(1,("No response to cmd %d mid %d",
464 midQ->command, midQ->mid));
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500465 if(midQ->midState == MID_REQUEST_SUBMITTED) {
466 if(ses->server->tcpStatus == CifsExiting)
467 rc = -EHOSTDOWN;
468 else {
469 ses->server->tcpStatus = CifsNeedReconnect;
470 midQ->midState = MID_RETRY_NEEDED;
471 }
472 }
473
474 if (rc != -EHOSTDOWN) {
475 if(midQ->midState == MID_RETRY_NEEDED) {
476 rc = -EAGAIN;
477 cFYI(1,("marking request for retry"));
478 } else {
479 rc = -EIO;
480 }
481 }
482 spin_unlock(&GlobalMid_Lock);
483 DeleteMidQEntry(midQ);
484 /* If not lock req, update # of requests on wire to server */
485 if(long_op < 3) {
486 atomic_dec(&ses->server->inFlight);
487 wake_up(&ses->server->request_q);
488 }
489 return rc;
490 }
491
492 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
493 cERROR(1, ("Frame too large received. Length: %d Xid: %d",
494 receive_len, xid));
495 rc = -EIO;
496 } else { /* rcvd frame is ok */
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500497 if (midQ->resp_buf &&
498 (midQ->midState == MID_RESPONSE_RECEIVED)) {
Steve French84afc292005-12-02 13:32:45 -0800499
Steve Frenchec637e32005-12-12 20:53:18 -0800500 iov[0].iov_base = (char *)midQ->resp_buf;
501 if(midQ->largeBuf)
502 *pRespBufType = CIFS_LARGE_BUFFER;
503 else
504 *pRespBufType = CIFS_SMALL_BUFFER;
505 iov[0].iov_len = receive_len + 4;
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500506
Steve Frenchec637e32005-12-12 20:53:18 -0800507 dump_smb(midQ->resp_buf, 80);
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500508 /* convert the length into a more usable form */
509 if((receive_len > 24) &&
510 (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
511 SECMODE_SIGN_ENABLED))) {
Steve Frenchec637e32005-12-12 20:53:18 -0800512 rc = cifs_verify_signature(midQ->resp_buf,
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500513 ses->server->mac_signing_key,
514 midQ->sequence_number+1);
515 if(rc) {
516 cERROR(1,("Unexpected SMB signature"));
517 /* BB FIXME add code to kill session */
518 }
519 }
520
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500521 /* BB special case reconnect tid and uid here? */
Steve French6ab16d22005-11-29 20:55:11 -0800522 /* BB special case Errbadpassword and pwdexpired here */
Steve Frenchec637e32005-12-12 20:53:18 -0800523 rc = map_smb_to_linux_error(midQ->resp_buf);
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500524
525 /* convert ByteCount if necessary */
526 if (receive_len >=
527 sizeof (struct smb_hdr) -
528 4 /* do not count RFC1001 header */ +
Steve Frenchec637e32005-12-12 20:53:18 -0800529 (2 * midQ->resp_buf->WordCount) + 2 /* bcc */ )
530 BCC(midQ->resp_buf) =
531 le16_to_cpu(BCC_LE(midQ->resp_buf));
532 midQ->resp_buf = NULL; /* mark it so will not be freed
533 by DeleteMidQEntry */
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500534 } else {
535 rc = -EIO;
Steve Frenchab2f2182005-09-15 20:44:50 -0700536 cFYI(1,("Bad MID state?"));
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500537 }
538 }
539cifs_no_response_exit2:
540 DeleteMidQEntry(midQ);
541
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542 if(long_op < 3) {
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500543 atomic_dec(&ses->server->inFlight);
544 wake_up(&ses->server->request_q);
545 }
546
547 return rc;
548
549out_unlock2:
550 up(&ses->server->tcpSem);
Steve French4b8f9302006-02-26 16:41:18 +0000551 cifs_small_buf_release(in_buf);
Steve Frenchd6e04ae2005-06-13 13:24:43 -0500552 /* If not lock req, update # of requests on wire to server */
553 if(long_op < 3) {
554 atomic_dec(&ses->server->inFlight);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700555 wake_up(&ses->server->request_q);
556 }
557
558 return rc;
559}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560
561int
562SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
563 struct smb_hdr *in_buf, struct smb_hdr *out_buf,
564 int *pbytes_returned, const int long_op)
565{
566 int rc = 0;
567 unsigned int receive_len;
568 unsigned long timeout;
569 struct mid_q_entry *midQ;
570
571 if (ses == NULL) {
572 cERROR(1,("Null smb session"));
573 return -EIO;
574 }
575 if(ses->server == NULL) {
576 cERROR(1,("Null tcp session"));
577 return -EIO;
578 }
579
Steve French31ca3bc2005-04-28 22:41:11 -0700580 if(ses->server->tcpStatus == CifsExiting)
581 return -ENOENT;
582
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 /* Ensure that we do not send more than 50 overlapping requests
584 to the same server. We may make this configurable later or
585 use ses->maxReq */
586 if(long_op == -1) {
587 /* oplock breaks must not be held up */
588 atomic_inc(&ses->server->inFlight);
589 } else {
590 spin_lock(&GlobalMid_Lock);
591 while(1) {
Steve French275cde12005-04-28 22:41:10 -0700592 if(atomic_read(&ses->server->inFlight) >=
593 cifs_max_pending){
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 spin_unlock(&GlobalMid_Lock);
Steve French131afd0b2005-10-07 09:51:05 -0700595#ifdef CONFIG_CIFS_STATS2
596 atomic_inc(&ses->server->num_waiters);
597#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700598 wait_event(ses->server->request_q,
599 atomic_read(&ses->server->inFlight)
600 < cifs_max_pending);
Steve French131afd0b2005-10-07 09:51:05 -0700601#ifdef CONFIG_CIFS_STATS2
602 atomic_dec(&ses->server->num_waiters);
603#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 spin_lock(&GlobalMid_Lock);
605 } else {
606 if(ses->server->tcpStatus == CifsExiting) {
607 spin_unlock(&GlobalMid_Lock);
608 return -ENOENT;
609 }
610
611 /* can not count locking commands against total since
612 they are allowed to block on server */
613
614 if(long_op < 3) {
615 /* update # of requests on the wire to server */
616 atomic_inc(&ses->server->inFlight);
617 }
618 spin_unlock(&GlobalMid_Lock);
619 break;
620 }
621 }
622 }
623 /* make sure that we sign in the same order that we send on this socket
624 and avoid races inside tcp sendmsg code that could cause corruption
625 of smb data */
626
627 down(&ses->server->tcpSem);
628
629 if (ses->server->tcpStatus == CifsExiting) {
630 rc = -ENOENT;
631 goto out_unlock;
632 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
633 cFYI(1,("tcp session dead - return to caller to retry"));
634 rc = -EAGAIN;
635 goto out_unlock;
636 } else if (ses->status != CifsGood) {
637 /* check if SMB session is bad because we are setting it up */
638 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
639 (in_buf->Command != SMB_COM_NEGOTIATE)) {
640 rc = -EAGAIN;
641 goto out_unlock;
642 } /* else ok - we are setting up session */
643 }
644 midQ = AllocMidQEntry(in_buf, ses);
645 if (midQ == NULL) {
646 up(&ses->server->tcpSem);
647 /* If not lock req, update # of requests on wire to server */
648 if(long_op < 3) {
649 atomic_dec(&ses->server->inFlight);
650 wake_up(&ses->server->request_q);
651 }
652 return -ENOMEM;
653 }
654
655 if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
656 up(&ses->server->tcpSem);
Steve French26a21b92006-05-31 18:05:34 +0000657 cERROR(1, ("Illegal length, greater than maximum frame, %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 in_buf->smb_buf_length));
659 DeleteMidQEntry(midQ);
660 /* If not lock req, update # of requests on wire to server */
661 if(long_op < 3) {
662 atomic_dec(&ses->server->inFlight);
663 wake_up(&ses->server->request_q);
664 }
665 return -EIO;
666 }
667
Steve Frenchad009ac2005-04-28 22:41:05 -0700668 rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669
670 midQ->midState = MID_REQUEST_SUBMITTED;
Steve French131afd0b2005-10-07 09:51:05 -0700671#ifdef CONFIG_CIFS_STATS2
672 atomic_inc(&ses->server->inSend);
673#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
675 (struct sockaddr *) &(ses->server->addr.sockAddr));
Steve French131afd0b2005-10-07 09:51:05 -0700676#ifdef CONFIG_CIFS_STATS2
677 atomic_dec(&ses->server->inSend);
Steve French1047abc2005-10-11 19:58:06 -0700678 midQ->when_sent = jiffies;
Steve French131afd0b2005-10-07 09:51:05 -0700679#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 if(rc < 0) {
681 DeleteMidQEntry(midQ);
682 up(&ses->server->tcpSem);
683 /* If not lock req, update # of requests on wire to server */
684 if(long_op < 3) {
685 atomic_dec(&ses->server->inFlight);
686 wake_up(&ses->server->request_q);
687 }
688 return rc;
689 } else
690 up(&ses->server->tcpSem);
691 if (long_op == -1)
692 goto cifs_no_response_exit;
Steve French275cde12005-04-28 22:41:10 -0700693 else if (long_op == 2) /* writes past end of file can take loong time */
Steve French37c0eb42005-10-05 14:50:29 -0700694 timeout = 180 * HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 else if (long_op == 1)
696 timeout = 45 * HZ; /* should be greater than
697 servers oplock break timeout (about 43 seconds) */
698 else if (long_op > 2) {
699 timeout = MAX_SCHEDULE_TIMEOUT;
700 } else
701 timeout = 15 * HZ;
702 /* wait for 15 seconds or until woken up due to response arriving or
703 due to last connection to this server being unmounted */
704 if (signal_pending(current)) {
705 /* if signal pending do not hold up user for full smb timeout
706 but we still give response a change to complete */
707 timeout = 2 * HZ;
708 }
709
710 /* No user interrupts in wait - wreaks havoc with performance */
711 if(timeout != MAX_SCHEDULE_TIMEOUT) {
712 timeout += jiffies;
713 wait_event(ses->server->response_q,
714 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
715 time_after(jiffies, timeout) ||
716 ((ses->server->tcpStatus != CifsGood) &&
717 (ses->server->tcpStatus != CifsNew)));
718 } else {
719 wait_event(ses->server->response_q,
720 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
721 ((ses->server->tcpStatus != CifsGood) &&
722 (ses->server->tcpStatus != CifsNew)));
723 }
724
725 spin_lock(&GlobalMid_Lock);
726 if (midQ->resp_buf) {
727 spin_unlock(&GlobalMid_Lock);
Steve French70ca7342005-09-22 16:32:06 -0700728 receive_len = midQ->resp_buf->smb_buf_length;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 } else {
Steve French37c0eb42005-10-05 14:50:29 -0700730 cERROR(1,("No response for cmd %d mid %d",
731 midQ->command, midQ->mid));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732 if(midQ->midState == MID_REQUEST_SUBMITTED) {
733 if(ses->server->tcpStatus == CifsExiting)
734 rc = -EHOSTDOWN;
735 else {
736 ses->server->tcpStatus = CifsNeedReconnect;
737 midQ->midState = MID_RETRY_NEEDED;
738 }
739 }
740
741 if (rc != -EHOSTDOWN) {
742 if(midQ->midState == MID_RETRY_NEEDED) {
743 rc = -EAGAIN;
744 cFYI(1,("marking request for retry"));
745 } else {
746 rc = -EIO;
747 }
748 }
749 spin_unlock(&GlobalMid_Lock);
750 DeleteMidQEntry(midQ);
751 /* If not lock req, update # of requests on wire to server */
752 if(long_op < 3) {
753 atomic_dec(&ses->server->inFlight);
754 wake_up(&ses->server->request_q);
755 }
756 return rc;
757 }
758
759 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
Steve Frenchad009ac2005-04-28 22:41:05 -0700760 cERROR(1, ("Frame too large received. Length: %d Xid: %d",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 receive_len, xid));
762 rc = -EIO;
763 } else { /* rcvd frame is ok */
764
765 if (midQ->resp_buf && out_buf
766 && (midQ->midState == MID_RESPONSE_RECEIVED)) {
767 out_buf->smb_buf_length = receive_len;
768 memcpy((char *)out_buf + 4,
769 (char *)midQ->resp_buf + 4,
770 receive_len);
771
772 dump_smb(out_buf, 92);
773 /* convert the length into a more usable form */
774 if((receive_len > 24) &&
Steve Frenchad009ac2005-04-28 22:41:05 -0700775 (ses->server->secMode & (SECMODE_SIGN_REQUIRED |
776 SECMODE_SIGN_ENABLED))) {
777 rc = cifs_verify_signature(out_buf,
778 ses->server->mac_signing_key,
779 midQ->sequence_number+1);
780 if(rc) {
Steve French275cde12005-04-28 22:41:10 -0700781 cERROR(1,("Unexpected SMB signature"));
782 /* BB FIXME add code to kill session */
Steve Frenchad009ac2005-04-28 22:41:05 -0700783 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784 }
785
786 *pbytes_returned = out_buf->smb_buf_length;
787
Steve Frenchad009ac2005-04-28 22:41:05 -0700788 /* BB special case reconnect tid and uid here? */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 rc = map_smb_to_linux_error(out_buf);
790
791 /* convert ByteCount if necessary */
792 if (receive_len >=
793 sizeof (struct smb_hdr) -
794 4 /* do not count RFC1001 header */ +
795 (2 * out_buf->WordCount) + 2 /* bcc */ )
Steve French0f2b27c2005-11-16 14:25:50 -0800796 BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797 } else {
798 rc = -EIO;
Steve Frenchec637e32005-12-12 20:53:18 -0800799 cERROR(1,("Bad MID state?"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800 }
801 }
802cifs_no_response_exit:
803 DeleteMidQEntry(midQ);
804
805 if(long_op < 3) {
806 atomic_dec(&ses->server->inFlight);
807 wake_up(&ses->server->request_q);
808 }
809
810 return rc;
811
812out_unlock:
813 up(&ses->server->tcpSem);
814 /* If not lock req, update # of requests on wire to server */
815 if(long_op < 3) {
816 atomic_dec(&ses->server->inFlight);
817 wake_up(&ses->server->request_q);
818 }
819
820 return rc;
821}