Blame view
fs/cifs/transport.c
26.9 KB
1da177e4c
|
1 2 3 |
/* * fs/cifs/transport.c * |
79a58d1f6
|
4 |
* Copyright (C) International Business Machines Corp., 2002,2007 |
1da177e4c
|
5 |
* Author(s): Steve French (sfrench@us.ibm.com) |
14a441a2b
|
6 |
* Jeremy Allison (jra@samba.org) 2006. |
79a58d1f6
|
7 |
* |
1da177e4c
|
8 9 10 11 12 13 14 15 16 17 18 19 |
* This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; if not, write to the Free Software |
79a58d1f6
|
20 |
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
1da177e4c
|
21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
*/ #include <linux/fs.h> #include <linux/list.h> #include <linux/wait.h> #include <linux/net.h> #include <linux/delay.h> #include <asm/uaccess.h> #include <asm/processor.h> #include <linux/mempool.h> #include "cifspdu.h" #include "cifsglob.h" #include "cifsproto.h" #include "cifs_debug.h" |
50c2f7538
|
35 |
|
1da177e4c
|
36 |
extern mempool_t *cifs_mid_poolp; |
e18b890bb
|
37 |
extern struct kmem_cache *cifs_oplock_cachep; |
1da177e4c
|
38 39 |
static struct mid_q_entry * |
7ee1af765
|
40 |
AllocMidQEntry(const struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) |
1da177e4c
|
41 42 43 44 |
{ struct mid_q_entry *temp; if (ses == NULL) { |
275cde1a1
|
45 |
cERROR(1, ("Null session passed in to AllocMidQEntry")); |
1da177e4c
|
46 47 48 49 50 51 |
return NULL; } if (ses->server == NULL) { cERROR(1, ("Null TCP session in AllocMidQEntry")); return NULL; } |
50c2f7538
|
52 |
|
d6e04ae64
|
53 |
temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp, |
e94b17660
|
54 |
GFP_KERNEL | GFP_NOFS); |
1da177e4c
|
55 56 57 58 59 60 61 62 |
if (temp == NULL) return temp; else { memset(temp, 0, sizeof (struct mid_q_entry)); temp->mid = smb_buffer->Mid; /* always LE */ temp->pid = current->pid; temp->command = smb_buffer->Command; cFYI(1, ("For smb_command %d", temp->command)); |
1047abc15
|
63 64 65 |
/* do_gettimeofday(&temp->when_sent);*/ /* easier to use jiffies */ /* when mid allocated can be before when sent */ temp->when_alloc = jiffies; |
1da177e4c
|
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
temp->ses = ses; temp->tsk = current; } spin_lock(&GlobalMid_Lock); list_add_tail(&temp->qhead, &ses->server->pending_mid_q); atomic_inc(&midCount); temp->midState = MID_REQUEST_ALLOCATED; spin_unlock(&GlobalMid_Lock); return temp; } static void DeleteMidQEntry(struct mid_q_entry *midEntry) { |
1047abc15
|
81 82 83 |
#ifdef CONFIG_CIFS_STATS2 unsigned long now; #endif |
1da177e4c
|
84 85 86 87 88 |
spin_lock(&GlobalMid_Lock); midEntry->midState = MID_FREE; list_del(&midEntry->qhead); atomic_dec(&midCount); spin_unlock(&GlobalMid_Lock); |
79a58d1f6
|
89 |
if (midEntry->largeBuf) |
b8643e1b5
|
90 91 92 |
cifs_buf_release(midEntry->resp_buf); else cifs_small_buf_release(midEntry->resp_buf); |
1047abc15
|
93 94 95 96 |
#ifdef CONFIG_CIFS_STATS2 now = jiffies; /* commands taking longer than one second are indications that something is wrong, unless it is quite a slow link or server */ |
79a58d1f6
|
97 98 |
if ((now - midEntry->when_alloc) > HZ) { if ((cifsFYI & CIFS_TIMER) && |
1047abc15
|
99 100 101 102 103 104 105 106 107 108 109 |
(midEntry->command != SMB_COM_LOCKING_ANDX)) { printk(KERN_DEBUG " CIFS slow rsp: cmd %d mid %d", midEntry->command, midEntry->mid); printk(" A: 0x%lx S: 0x%lx R: 0x%lx ", now - midEntry->when_alloc, now - midEntry->when_sent, now - midEntry->when_received); } } #endif |
1da177e4c
|
110 111 112 113 |
mempool_free(midEntry, cifs_mid_poolp); } struct oplock_q_entry * |
79a58d1f6
|
114 |
AllocOplockQEntry(struct inode *pinode, __u16 fid, struct cifsTconInfo *tcon) |
1da177e4c
|
115 116 |
{ struct oplock_q_entry *temp; |
79a58d1f6
|
117 |
if ((pinode == NULL) || (tcon == NULL)) { |
1da177e4c
|
118 119 120 121 |
cERROR(1, ("Null parms passed to AllocOplockQEntry")); return NULL; } temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep, |
e94b17660
|
122 |
GFP_KERNEL); |
1da177e4c
|
123 124 125 126 127 128 129 130 131 132 133 134 135 |
if (temp == NULL) return temp; else { temp->pinode = pinode; temp->tcon = tcon; temp->netfid = fid; spin_lock(&GlobalMid_Lock); list_add_tail(&temp->qhead, &GlobalOplock_Q); spin_unlock(&GlobalMid_Lock); } return temp; } |
79a58d1f6
|
136 |
void DeleteOplockQEntry(struct oplock_q_entry *oplockEntry) |
1da177e4c
|
137 |
{ |
79a58d1f6
|
138 |
spin_lock(&GlobalMid_Lock); |
1da177e4c
|
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
/* should we check if list empty first? */ list_del(&oplockEntry->qhead); spin_unlock(&GlobalMid_Lock); kmem_cache_free(cifs_oplock_cachep, oplockEntry); } int smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, unsigned int smb_buf_length, struct sockaddr *sin) { int rc = 0; int i = 0; struct msghdr smb_msg; struct kvec iov; unsigned len = smb_buf_length + 4; |
79a58d1f6
|
154 |
if (ssocket == NULL) |
1da177e4c
|
155 156 157 158 159 160 161 162 163 164 165 |
return -ENOTSOCK; /* BB eventually add reconnect code here */ iov.iov_base = smb_buffer; iov.iov_len = len; smb_msg.msg_name = sin; smb_msg.msg_namelen = sizeof (struct sockaddr); smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/ /* smb header is converted in header_assemble. bcc and rest of SMB word |
79a58d1f6
|
166 167 |
area, and byte area if necessary, is converted to littleendian in cifssmb.c and RFC1001 len is converted to bigendian in smb_send |
1da177e4c
|
168 169 170 |
Flags2 is converted in SendReceive */ smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); |
3e84469d0
|
171 |
cFYI(1, ("Sending smb of length %d", smb_buf_length)); |
1da177e4c
|
172 173 174 175 176 177 |
dump_smb(smb_buffer, len); while (len > 0) { rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len); if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; |
3e84469d0
|
178 |
/* smaller timeout here than send2 since smaller size */ |
79a58d1f6
|
179 180 181 |
/* Although it may not be required, this also is smaller oplock break time */ if (i > 12) { |
1da177e4c
|
182 |
cERROR(1, |
68058e757
|
183 |
("sends on sock %p stuck for 7 seconds", |
1da177e4c
|
184 185 186 187 |
ssocket)); rc = -EAGAIN; break; } |
68058e757
|
188 |
msleep(1 << i); |
1da177e4c
|
189 190 |
continue; } |
79a58d1f6
|
191 |
if (rc < 0) |
1da177e4c
|
192 |
break; |
5e1253b50
|
193 194 |
else i = 0; /* reset i after each successful send */ |
1da177e4c
|
195 196 197 198 199 200 |
iov.iov_base += rc; iov.iov_len -= rc; len -= rc; } if (rc < 0) { |
79a58d1f6
|
201 |
cERROR(1, ("Error %d sending data on socket to server", rc)); |
1da177e4c
|
202 203 204 |
} else { rc = 0; } |
7ee1af765
|
205 206 207 |
/* Don't want to modify the buffer as a side effect of this call. */ smb_buffer->smb_buf_length = smb_buf_length; |
1da177e4c
|
208 209 |
return rc; } |
d6e04ae64
|
210 |
static int |
3e84469d0
|
211 212 |
smb_send2(struct socket *ssocket, struct kvec *iov, int n_vec, struct sockaddr *sin) |
1da177e4c
|
213 214 215 216 |
{ int rc = 0; int i = 0; struct msghdr smb_msg; |
3e84469d0
|
217 218 219 220 |
struct smb_hdr *smb_buffer = iov[0].iov_base; unsigned int len = iov[0].iov_len; unsigned int total_len; int first_vec = 0; |
7ee1af765
|
221 |
unsigned int smb_buf_length = smb_buffer->smb_buf_length; |
50c2f7538
|
222 |
|
79a58d1f6
|
223 |
if (ssocket == NULL) |
1da177e4c
|
224 |
return -ENOTSOCK; /* BB eventually add reconnect code here */ |
3e84469d0
|
225 |
|
1da177e4c
|
226 227 228 229 230 231 232 |
smb_msg.msg_name = sin; smb_msg.msg_namelen = sizeof (struct sockaddr); smb_msg.msg_control = NULL; smb_msg.msg_controllen = 0; smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/ /* smb header is converted in header_assemble. bcc and rest of SMB word |
79a58d1f6
|
233 234 |
area, and byte area if necessary, is converted to littleendian in cifssmb.c and RFC1001 len is converted to bigendian in smb_send |
1da177e4c
|
235 |
Flags2 is converted in SendReceive */ |
3e84469d0
|
236 237 238 239 |
total_len = 0; for (i = 0; i < n_vec; i++) total_len += iov[i].iov_len; |
1da177e4c
|
240 |
smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); |
3e84469d0
|
241 |
cFYI(1, ("Sending smb: total_len %d", total_len)); |
1da177e4c
|
242 |
dump_smb(smb_buffer, len); |
3e84469d0
|
243 244 245 |
while (total_len) { rc = kernel_sendmsg(ssocket, &smb_msg, &iov[first_vec], n_vec - first_vec, total_len); |
1da177e4c
|
246 247 |
if ((rc == -ENOSPC) || (rc == -EAGAIN)) { i++; |
79a58d1f6
|
248 |
if (i >= 14) { |
1da177e4c
|
249 |
cERROR(1, |
68058e757
|
250 |
("sends on sock %p stuck for 15 seconds", |
1da177e4c
|
251 252 253 254 |
ssocket)); rc = -EAGAIN; break; } |
68058e757
|
255 |
msleep(1 << i); |
1da177e4c
|
256 257 |
continue; } |
79a58d1f6
|
258 |
if (rc < 0) |
1da177e4c
|
259 |
break; |
3e84469d0
|
260 261 262 263 264 |
if (rc >= total_len) { WARN_ON(rc > total_len); break; } |
79a58d1f6
|
265 |
if (rc == 0) { |
3e84469d0
|
266 267 |
/* should never happen, letting socket clear before retrying is our only obvious option here */ |
79a58d1f6
|
268 |
cERROR(1, ("tcp sent no data")); |
3e84469d0
|
269 270 |
msleep(500); continue; |
d6e04ae64
|
271 |
} |
3e84469d0
|
272 |
total_len -= rc; |
68058e757
|
273 |
/* the line below resets i */ |
3e84469d0
|
274 275 276 277 278 279 280 281 282 283 284 285 |
for (i = first_vec; i < n_vec; i++) { if (iov[i].iov_len) { if (rc > iov[i].iov_len) { rc -= iov[i].iov_len; iov[i].iov_len = 0; } else { iov[i].iov_base += rc; iov[i].iov_len -= rc; first_vec = i; break; } } |
d6e04ae64
|
286 |
} |
5e1253b50
|
287 |
i = 0; /* in case we get ENOSPC on the next send */ |
1da177e4c
|
288 289 290 |
} if (rc < 0) { |
79a58d1f6
|
291 |
cERROR(1, ("Error %d sending data on socket to server", rc)); |
3e84469d0
|
292 |
} else |
1da177e4c
|
293 |
rc = 0; |
1da177e4c
|
294 |
|
7ee1af765
|
295 296 297 |
/* Don't want to modify the buffer as a side effect of this call. */ smb_buffer->smb_buf_length = smb_buf_length; |
1da177e4c
|
298 299 |
return rc; } |
7ee1af765
|
300 |
static int wait_for_free_request(struct cifsSesInfo *ses, const int long_op) |
1da177e4c
|
301 |
{ |
79a58d1f6
|
302 |
if (long_op == -1) { |
1da177e4c
|
303 304 305 |
/* oplock breaks must not be held up */ atomic_inc(&ses->server->inFlight); } else { |
79a58d1f6
|
306 307 308 |
spin_lock(&GlobalMid_Lock); while (1) { if (atomic_read(&ses->server->inFlight) >= |
d6e04ae64
|
309 |
cifs_max_pending){ |
1da177e4c
|
310 |
spin_unlock(&GlobalMid_Lock); |
131afd0b7
|
311 312 313 |
#ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->num_waiters); #endif |
1da177e4c
|
314 315 316 |
wait_event(ses->server->request_q, atomic_read(&ses->server->inFlight) < cifs_max_pending); |
131afd0b7
|
317 318 319 |
#ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->num_waiters); #endif |
1da177e4c
|
320 321 |
spin_lock(&GlobalMid_Lock); } else { |
79a58d1f6
|
322 |
if (ses->server->tcpStatus == CifsExiting) { |
1da177e4c
|
323 324 325 |
spin_unlock(&GlobalMid_Lock); return -ENOENT; } |
79a58d1f6
|
326 327 |
/* can not count locking commands against total as they are allowed to block on server */ |
50c2f7538
|
328 |
|
1da177e4c
|
329 |
/* update # of requests on the wire to server */ |
7ee1af765
|
330 |
if (long_op < 3) |
1da177e4c
|
331 |
atomic_inc(&ses->server->inFlight); |
1da177e4c
|
332 333 334 335 336 |
spin_unlock(&GlobalMid_Lock); break; } } } |
7ee1af765
|
337 338 |
return 0; } |
1da177e4c
|
339 |
|
7ee1af765
|
340 341 342 |
static int allocate_mid(struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct mid_q_entry **ppmidQ) { |
1da177e4c
|
343 |
if (ses->server->tcpStatus == CifsExiting) { |
7ee1af765
|
344 |
return -ENOENT; |
1da177e4c
|
345 |
} else if (ses->server->tcpStatus == CifsNeedReconnect) { |
79a58d1f6
|
346 |
cFYI(1, ("tcp session dead - return to caller to retry")); |
7ee1af765
|
347 |
return -EAGAIN; |
1da177e4c
|
348 349 |
} else if (ses->status != CifsGood) { /* check if SMB session is bad because we are setting it up */ |
79a58d1f6
|
350 |
if ((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && |
1da177e4c
|
351 |
(in_buf->Command != SMB_COM_NEGOTIATE)) { |
7ee1af765
|
352 |
return -EAGAIN; |
1da177e4c
|
353 354 |
} /* else ok - we are setting up session */ } |
7ee1af765
|
355 356 357 358 359 360 |
*ppmidQ = AllocMidQEntry(in_buf, ses); if (*ppmidQ == NULL) { return -ENOMEM; } return 0; } |
79a58d1f6
|
361 |
static int wait_for_response(struct cifsSesInfo *ses, |
7ee1af765
|
362 363 364 365 366 367 368 369 370 |
struct mid_q_entry *midQ, unsigned long timeout, unsigned long time_to_wait) { unsigned long curr_timeout; for (;;) { curr_timeout = timeout + jiffies; wait_event(ses->server->response_q, |
79a58d1f6
|
371 372 |
(!(midQ->midState == MID_REQUEST_SUBMITTED)) || time_after(jiffies, curr_timeout) || |
7ee1af765
|
373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); if (time_after(jiffies, curr_timeout) && (midQ->midState == MID_REQUEST_SUBMITTED) && ((ses->server->tcpStatus == CifsGood) || (ses->server->tcpStatus == CifsNew))) { unsigned long lrt; /* We timed out. Is the server still sending replies ? */ spin_lock(&GlobalMid_Lock); lrt = ses->server->lstrp; spin_unlock(&GlobalMid_Lock); /* Calculate time_to_wait past last receive time. |
79a58d1f6
|
390 |
Although we prefer not to time out if the |
7ee1af765
|
391 |
server is still responding - we will time |
79a58d1f6
|
392 |
out if the server takes more than 15 (or 45 |
7ee1af765
|
393 |
or 180) seconds to respond to this request |
79a58d1f6
|
394 |
and has not responded to any request from |
7ee1af765
|
395 396 397 398 |
other threads on the client within 10 seconds */ lrt += time_to_wait; if (time_after(jiffies, lrt)) { /* No replies for time_to_wait. */ |
79a58d1f6
|
399 |
cERROR(1, ("server not responding")); |
7ee1af765
|
400 401 402 403 404 405 406 407 408 |
return -1; } } else { return 0; } } } int |
79a58d1f6
|
409 410 |
SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, struct kvec *iov, int n_vec, int *pRespBufType /* ret */, |
7ee1af765
|
411 412 413 414 415 416 417 |
const int long_op) { int rc = 0; unsigned int receive_len; unsigned long timeout; struct mid_q_entry *midQ; struct smb_hdr *in_buf = iov[0].iov_base; |
50c2f7538
|
418 |
|
7ee1af765
|
419 420 421 422 |
*pRespBufType = CIFS_NO_BUFFER; /* no response buf yet */ if ((ses == NULL) || (ses->server == NULL)) { cifs_small_buf_release(in_buf); |
79a58d1f6
|
423 |
cERROR(1, ("Null session")); |
7ee1af765
|
424 425 |
return -EIO; } |
79a58d1f6
|
426 |
if (ses->server->tcpStatus == CifsExiting) { |
7ee1af765
|
427 428 429 |
cifs_small_buf_release(in_buf); return -ENOENT; } |
79a58d1f6
|
430 |
/* Ensure that we do not send more than 50 overlapping requests |
7ee1af765
|
431 432 433 434 435 436 437 438 |
to the same server. We may make this configurable later or use ses->maxReq */ rc = wait_for_free_request(ses, long_op); if (rc) { cifs_small_buf_release(in_buf); return rc; } |
79a58d1f6
|
439 |
/* make sure that we sign in the same order that we send on this socket |
7ee1af765
|
440 441 |
and avoid races inside tcp sendmsg code that could cause corruption of smb data */ |
79a58d1f6
|
442 |
down(&ses->server->tcpSem); |
7ee1af765
|
443 444 445 |
rc = allocate_mid(ses, in_buf, &midQ); if (rc) { |
1da177e4c
|
446 |
up(&ses->server->tcpSem); |
4b8f930ff
|
447 |
cifs_small_buf_release(in_buf); |
7ee1af765
|
448 |
/* Update # of requests on wire to server */ |
79a58d1f6
|
449 |
atomic_dec(&ses->server->inFlight); |
7ee1af765
|
450 451 |
wake_up(&ses->server->request_q); return rc; |
1da177e4c
|
452 |
} |
79a58d1f6
|
453 |
rc = cifs_sign_smb2(iov, n_vec, ses->server, &midQ->sequence_number); |
1da177e4c
|
454 455 |
midQ->midState = MID_REQUEST_SUBMITTED; |
131afd0b7
|
456 457 458 |
#ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif |
3e84469d0
|
459 |
rc = smb_send2(ses->server->ssocket, iov, n_vec, |
d6e04ae64
|
460 |
(struct sockaddr *) &(ses->server->addr.sockAddr)); |
131afd0b7
|
461 462 |
#ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); |
1047abc15
|
463 |
midQ->when_sent = jiffies; |
131afd0b7
|
464 |
#endif |
7ee1af765
|
465 466 467 |
up(&ses->server->tcpSem); cifs_small_buf_release(in_buf); |
79a58d1f6
|
468 |
if (rc < 0) |
7ee1af765
|
469 |
goto out; |
4b8f930ff
|
470 |
|
d6e04ae64
|
471 |
if (long_op == -1) |
7ee1af765
|
472 |
goto out; |
d6e04ae64
|
473 |
else if (long_op == 2) /* writes past end of file can take loong time */ |
37c0eb467
|
474 |
timeout = 180 * HZ; |
d6e04ae64
|
475 |
else if (long_op == 1) |
79a58d1f6
|
476 |
timeout = 45 * HZ; /* should be greater than |
d6e04ae64
|
477 |
servers oplock break timeout (about 43 seconds) */ |
7ee1af765
|
478 |
else |
d6e04ae64
|
479 |
timeout = 15 * HZ; |
7ee1af765
|
480 |
|
79a58d1f6
|
481 |
/* wait for 15 seconds or until woken up due to response arriving or |
d6e04ae64
|
482 483 484 |
due to last connection to this server being unmounted */ if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout |
8a236264f
|
485 |
but we still give response a chance to complete */ |
d6e04ae64
|
486 |
timeout = 2 * HZ; |
79a58d1f6
|
487 |
} |
d6e04ae64
|
488 489 |
/* No user interrupts in wait - wreaks havoc with performance */ |
7ee1af765
|
490 |
wait_for_response(ses, midQ, timeout, 10 * HZ); |
d6e04ae64
|
491 492 493 494 |
spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); |
70ca734a1
|
495 |
receive_len = midQ->resp_buf->smb_buf_length; |
d6e04ae64
|
496 |
} else { |
79a58d1f6
|
497 |
cERROR(1, ("No response to cmd %d mid %d", |
37c0eb467
|
498 |
midQ->command, midQ->mid)); |
79a58d1f6
|
499 500 |
if (midQ->midState == MID_REQUEST_SUBMITTED) { if (ses->server->tcpStatus == CifsExiting) |
d6e04ae64
|
501 502 503 504 505 506 507 508 |
rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { |
79a58d1f6
|
509 |
if (midQ->midState == MID_RETRY_NEEDED) { |
d6e04ae64
|
510 |
rc = -EAGAIN; |
79a58d1f6
|
511 |
cFYI(1, ("marking request for retry")); |
d6e04ae64
|
512 513 514 515 516 517 |
} else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); |
7ee1af765
|
518 |
/* Update # of requests on wire to server */ |
79a58d1f6
|
519 |
atomic_dec(&ses->server->inFlight); |
7ee1af765
|
520 |
wake_up(&ses->server->request_q); |
d6e04ae64
|
521 522 |
return rc; } |
50c2f7538
|
523 |
|
d6e04ae64
|
524 525 526 527 528 |
if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; } else { /* rcvd frame is ok */ |
79a58d1f6
|
529 |
if (midQ->resp_buf && |
d6e04ae64
|
530 |
(midQ->midState == MID_RESPONSE_RECEIVED)) { |
84afc29b1
|
531 |
|
ec637e3ff
|
532 |
iov[0].iov_base = (char *)midQ->resp_buf; |
79a58d1f6
|
533 |
if (midQ->largeBuf) |
ec637e3ff
|
534 535 536 537 |
*pRespBufType = CIFS_LARGE_BUFFER; else *pRespBufType = CIFS_SMALL_BUFFER; iov[0].iov_len = receive_len + 4; |
d6e04ae64
|
538 |
|
ec637e3ff
|
539 |
dump_smb(midQ->resp_buf, 80); |
d6e04ae64
|
540 |
/* convert the length into a more usable form */ |
79a58d1f6
|
541 |
if ((receive_len > 24) && |
d6e04ae64
|
542 543 |
(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { |
ec637e3ff
|
544 |
rc = cifs_verify_signature(midQ->resp_buf, |
b609f06ac
|
545 |
&ses->server->mac_signing_key, |
d6e04ae64
|
546 |
midQ->sequence_number+1); |
79a58d1f6
|
547 548 |
if (rc) { cERROR(1, ("Unexpected SMB signature")); |
d6e04ae64
|
549 550 551 |
/* BB FIXME add code to kill session */ } } |
d6e04ae64
|
552 |
/* BB special case reconnect tid and uid here? */ |
6ab16d249
|
553 |
/* BB special case Errbadpassword and pwdexpired here */ |
ec637e3ff
|
554 |
rc = map_smb_to_linux_error(midQ->resp_buf); |
d6e04ae64
|
555 556 557 558 559 |
/* convert ByteCount if necessary */ if (receive_len >= sizeof (struct smb_hdr) - 4 /* do not count RFC1001 header */ + |
ec637e3ff
|
560 |
(2 * midQ->resp_buf->WordCount) + 2 /* bcc */ ) |
79a58d1f6
|
561 |
BCC(midQ->resp_buf) = |
ec637e3ff
|
562 563 564 |
le16_to_cpu(BCC_LE(midQ->resp_buf)); midQ->resp_buf = NULL; /* mark it so will not be freed by DeleteMidQEntry */ |
d6e04ae64
|
565 566 |
} else { rc = -EIO; |
79a58d1f6
|
567 |
cFYI(1, ("Bad MID state?")); |
d6e04ae64
|
568 569 |
} } |
1da177e4c
|
570 |
|
7ee1af765
|
571 |
out: |
7ee1af765
|
572 |
DeleteMidQEntry(midQ); |
79a58d1f6
|
573 |
atomic_dec(&ses->server->inFlight); |
7ee1af765
|
574 |
wake_up(&ses->server->request_q); |
1da177e4c
|
575 |
|
d6e04ae64
|
576 577 |
return rc; } |
1da177e4c
|
578 579 580 581 582 583 584 585 586 587 588 589 |
int SendReceive(const unsigned int xid, struct cifsSesInfo *ses, struct smb_hdr *in_buf, struct smb_hdr *out_buf, int *pbytes_returned, const int long_op) { int rc = 0; unsigned int receive_len; unsigned long timeout; struct mid_q_entry *midQ; if (ses == NULL) { |
79a58d1f6
|
590 |
cERROR(1, ("Null smb session")); |
1da177e4c
|
591 592 |
return -EIO; } |
79a58d1f6
|
593 594 |
if (ses->server == NULL) { cERROR(1, ("Null tcp session")); |
1da177e4c
|
595 596 |
return -EIO; } |
79a58d1f6
|
597 |
if (ses->server->tcpStatus == CifsExiting) |
31ca3bc3c
|
598 |
return -ENOENT; |
79a58d1f6
|
599 |
/* Ensure that we do not send more than 50 overlapping requests |
1da177e4c
|
600 601 |
to the same server. We may make this configurable later or use ses->maxReq */ |
1da177e4c
|
602 |
|
7ee1af765
|
603 604 605 |
rc = wait_for_free_request(ses, long_op); if (rc) return rc; |
79a58d1f6
|
606 |
/* make sure that we sign in the same order that we send on this socket |
1da177e4c
|
607 608 |
and avoid races inside tcp sendmsg code that could cause corruption of smb data */ |
79a58d1f6
|
609 |
down(&ses->server->tcpSem); |
1da177e4c
|
610 |
|
7ee1af765
|
611 612 |
rc = allocate_mid(ses, in_buf, &midQ); if (rc) { |
1da177e4c
|
613 |
up(&ses->server->tcpSem); |
7ee1af765
|
614 |
/* Update # of requests on wire to server */ |
79a58d1f6
|
615 |
atomic_dec(&ses->server->inFlight); |
7ee1af765
|
616 617 |
wake_up(&ses->server->request_q); return rc; |
1da177e4c
|
618 619 620 |
} if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { |
26a21b980
|
621 |
cERROR(1, ("Illegal length, greater than maximum frame, %d", |
1da177e4c
|
622 623 |
in_buf->smb_buf_length)); DeleteMidQEntry(midQ); |
7ee1af765
|
624 625 |
up(&ses->server->tcpSem); /* Update # of requests on wire to server */ |
79a58d1f6
|
626 |
atomic_dec(&ses->server->inFlight); |
7ee1af765
|
627 |
wake_up(&ses->server->request_q); |
1da177e4c
|
628 629 |
return -EIO; } |
ad009ac96
|
630 |
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); |
1da177e4c
|
631 632 |
midQ->midState = MID_REQUEST_SUBMITTED; |
131afd0b7
|
633 634 635 |
#ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif |
1da177e4c
|
636 637 |
rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr)); |
131afd0b7
|
638 639 |
#ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); |
1047abc15
|
640 |
midQ->when_sent = jiffies; |
131afd0b7
|
641 |
#endif |
7ee1af765
|
642 |
up(&ses->server->tcpSem); |
79a58d1f6
|
643 |
if (rc < 0) |
7ee1af765
|
644 |
goto out; |
1da177e4c
|
645 |
if (long_op == -1) |
7ee1af765
|
646 |
goto out; |
275cde1a1
|
647 |
else if (long_op == 2) /* writes past end of file can take loong time */ |
37c0eb467
|
648 |
timeout = 180 * HZ; |
1da177e4c
|
649 |
else if (long_op == 1) |
79a58d1f6
|
650 |
timeout = 45 * HZ; /* should be greater than |
1da177e4c
|
651 |
servers oplock break timeout (about 43 seconds) */ |
7ee1af765
|
652 |
else |
1da177e4c
|
653 |
timeout = 15 * HZ; |
79a58d1f6
|
654 |
/* wait for 15 seconds or until woken up due to response arriving or |
1da177e4c
|
655 656 657 |
due to last connection to this server being unmounted */ if (signal_pending(current)) { /* if signal pending do not hold up user for full smb timeout |
8a236264f
|
658 |
but we still give response a chance to complete */ |
1da177e4c
|
659 |
timeout = 2 * HZ; |
79a58d1f6
|
660 |
} |
1da177e4c
|
661 662 |
/* No user interrupts in wait - wreaks havoc with performance */ |
7ee1af765
|
663 |
wait_for_response(ses, midQ, timeout, 10 * HZ); |
1da177e4c
|
664 665 666 667 |
spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); |
70ca734a1
|
668 |
receive_len = midQ->resp_buf->smb_buf_length; |
1da177e4c
|
669 |
} else { |
79a58d1f6
|
670 |
cERROR(1, ("No response for cmd %d mid %d", |
37c0eb467
|
671 |
midQ->command, midQ->mid)); |
79a58d1f6
|
672 673 |
if (midQ->midState == MID_REQUEST_SUBMITTED) { if (ses->server->tcpStatus == CifsExiting) |
1da177e4c
|
674 675 676 677 678 679 680 681 |
rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { |
79a58d1f6
|
682 |
if (midQ->midState == MID_RETRY_NEEDED) { |
1da177e4c
|
683 |
rc = -EAGAIN; |
79a58d1f6
|
684 |
cFYI(1, ("marking request for retry")); |
1da177e4c
|
685 686 687 688 689 690 |
} else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); |
7ee1af765
|
691 |
/* Update # of requests on wire to server */ |
79a58d1f6
|
692 |
atomic_dec(&ses->server->inFlight); |
7ee1af765
|
693 |
wake_up(&ses->server->request_q); |
1da177e4c
|
694 695 |
return rc; } |
50c2f7538
|
696 |
|
1da177e4c
|
697 |
if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { |
ad009ac96
|
698 |
cERROR(1, ("Frame too large received. Length: %d Xid: %d", |
1da177e4c
|
699 700 701 702 703 704 705 706 707 708 709 710 711 |
receive_len, xid)); rc = -EIO; } else { /* rcvd frame is ok */ if (midQ->resp_buf && out_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { out_buf->smb_buf_length = receive_len; memcpy((char *)out_buf + 4, (char *)midQ->resp_buf + 4, receive_len); dump_smb(out_buf, 92); /* convert the length into a more usable form */ |
79a58d1f6
|
712 |
if ((receive_len > 24) && |
ad009ac96
|
713 714 715 |
(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, |
b609f06ac
|
716 |
&ses->server->mac_signing_key, |
ad009ac96
|
717 |
midQ->sequence_number+1); |
79a58d1f6
|
718 719 |
if (rc) { cERROR(1, ("Unexpected SMB signature")); |
275cde1a1
|
720 |
/* BB FIXME add code to kill session */ |
ad009ac96
|
721 |
} |
1da177e4c
|
722 723 724 |
} *pbytes_returned = out_buf->smb_buf_length; |
ad009ac96
|
725 |
/* BB special case reconnect tid and uid here? */ |
1da177e4c
|
726 727 728 729 730 731 732 |
rc = map_smb_to_linux_error(out_buf); /* convert ByteCount if necessary */ if (receive_len >= sizeof (struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) |
0f2b27c43
|
733 |
BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); |
1da177e4c
|
734 735 |
} else { rc = -EIO; |
79a58d1f6
|
736 |
cERROR(1, ("Bad MID state?")); |
1da177e4c
|
737 738 |
} } |
7ee1af765
|
739 740 |
out: |
1da177e4c
|
741 |
DeleteMidQEntry(midQ); |
79a58d1f6
|
742 |
atomic_dec(&ses->server->inFlight); |
7ee1af765
|
743 |
wake_up(&ses->server->request_q); |
1da177e4c
|
744 |
|
7ee1af765
|
745 746 |
return rc; } |
1da177e4c
|
747 |
|
7ee1af765
|
748 749 750 751 752 753 754 755 756 757 758 759 |
/* Send an NT_CANCEL SMB to cause the POSIX blocking lock to return. */ static int send_nt_cancel(struct cifsTconInfo *tcon, struct smb_hdr *in_buf, struct mid_q_entry *midQ) { int rc = 0; struct cifsSesInfo *ses = tcon->ses; __u16 mid = in_buf->Mid; header_assemble(in_buf, SMB_COM_NT_CANCEL, tcon, 0); in_buf->Mid = mid; |
79a58d1f6
|
760 |
down(&ses->server->tcpSem); |
7ee1af765
|
761 762 763 764 765 766 767 768 |
rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); if (rc) { up(&ses->server->tcpSem); return rc; } rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr)); up(&ses->server->tcpSem); |
1da177e4c
|
769 |
return rc; |
7ee1af765
|
770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 |
} /* We send a LOCKINGX_CANCEL_LOCK to cause the Windows blocking lock to return. */ static int send_lock_cancel(const unsigned int xid, struct cifsTconInfo *tcon, struct smb_hdr *in_buf, struct smb_hdr *out_buf) { int bytes_returned; struct cifsSesInfo *ses = tcon->ses; LOCK_REQ *pSMB = (LOCK_REQ *)in_buf; /* We just modify the current in_buf to change the type of lock from LOCKING_ANDX_SHARED_LOCK or LOCKING_ANDX_EXCLUSIVE_LOCK to LOCKING_ANDX_CANCEL_LOCK. */ pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES; pSMB->Timeout = 0; pSMB->hdr.Mid = GetNextMid(ses->server); return SendReceive(xid, ses, in_buf, out_buf, &bytes_returned, 0); } int SendReceiveBlockingLock(const unsigned int xid, struct cifsTconInfo *tcon, struct smb_hdr *in_buf, struct smb_hdr *out_buf, int *pbytes_returned) { int rc = 0; int rstart = 0; unsigned int receive_len; struct mid_q_entry *midQ; struct cifsSesInfo *ses; if (tcon == NULL || tcon->ses == NULL) { |
79a58d1f6
|
809 |
cERROR(1, ("Null smb session")); |
7ee1af765
|
810 811 812 |
return -EIO; } ses = tcon->ses; |
79a58d1f6
|
813 814 |
if (ses->server == NULL) { cERROR(1, ("Null tcp session")); |
7ee1af765
|
815 816 |
return -EIO; } |
79a58d1f6
|
817 |
if (ses->server->tcpStatus == CifsExiting) |
7ee1af765
|
818 |
return -ENOENT; |
79a58d1f6
|
819 |
/* Ensure that we do not send more than 50 overlapping requests |
7ee1af765
|
820 821 822 823 824 825 |
to the same server. We may make this configurable later or use ses->maxReq */ rc = wait_for_free_request(ses, 3); if (rc) return rc; |
79a58d1f6
|
826 |
/* make sure that we sign in the same order that we send on this socket |
7ee1af765
|
827 828 |
and avoid races inside tcp sendmsg code that could cause corruption of smb data */ |
79a58d1f6
|
829 |
down(&ses->server->tcpSem); |
7ee1af765
|
830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 |
rc = allocate_mid(ses, in_buf, &midQ); if (rc) { up(&ses->server->tcpSem); return rc; } if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) { up(&ses->server->tcpSem); cERROR(1, ("Illegal length, greater than maximum frame, %d", in_buf->smb_buf_length)); DeleteMidQEntry(midQ); return -EIO; } rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); |
1da177e4c
|
846 |
|
7ee1af765
|
847 848 849 850 851 852 853 854 855 856 |
midQ->midState = MID_REQUEST_SUBMITTED; #ifdef CONFIG_CIFS_STATS2 atomic_inc(&ses->server->inSend); #endif rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length, (struct sockaddr *) &(ses->server->addr.sockAddr)); #ifdef CONFIG_CIFS_STATS2 atomic_dec(&ses->server->inSend); midQ->when_sent = jiffies; #endif |
1da177e4c
|
857 |
up(&ses->server->tcpSem); |
7ee1af765
|
858 |
|
79a58d1f6
|
859 |
if (rc < 0) { |
7ee1af765
|
860 861 862 863 864 865 |
DeleteMidQEntry(midQ); return rc; } /* Wait for a reply - allow signals to interrupt. */ rc = wait_event_interruptible(ses->server->response_q, |
79a58d1f6
|
866 |
(!(midQ->midState == MID_REQUEST_SUBMITTED)) || |
7ee1af765
|
867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 |
((ses->server->tcpStatus != CifsGood) && (ses->server->tcpStatus != CifsNew))); /* Were we interrupted by a signal ? */ if ((rc == -ERESTARTSYS) && (midQ->midState == MID_REQUEST_SUBMITTED) && ((ses->server->tcpStatus == CifsGood) || (ses->server->tcpStatus == CifsNew))) { if (in_buf->Command == SMB_COM_TRANSACTION2) { /* POSIX lock. We send a NT_CANCEL SMB to cause the blocking lock to return. */ rc = send_nt_cancel(tcon, in_buf, midQ); if (rc) { DeleteMidQEntry(midQ); return rc; } } else { /* Windows lock. We send a LOCKINGX_CANCEL_LOCK to cause the blocking lock to return. */ rc = send_lock_cancel(xid, tcon, in_buf, out_buf); /* If we get -ENOLCK back the lock may have already been removed. Don't exit in this case. */ if (rc && rc != -ENOLCK) { DeleteMidQEntry(midQ); return rc; } } /* Wait 5 seconds for the response. */ |
79a58d1f6
|
900 |
if (wait_for_response(ses, midQ, 5 * HZ, 5 * HZ) == 0) { |
7ee1af765
|
901 902 903 904 905 906 907 908 909 910 |
/* We got the response - restart system call. */ rstart = 1; } } spin_lock(&GlobalMid_Lock); if (midQ->resp_buf) { spin_unlock(&GlobalMid_Lock); receive_len = midQ->resp_buf->smb_buf_length; } else { |
79a58d1f6
|
911 |
cERROR(1, ("No response for cmd %d mid %d", |
7ee1af765
|
912 |
midQ->command, midQ->mid)); |
79a58d1f6
|
913 914 |
if (midQ->midState == MID_REQUEST_SUBMITTED) { if (ses->server->tcpStatus == CifsExiting) |
7ee1af765
|
915 916 917 918 919 920 921 922 |
rc = -EHOSTDOWN; else { ses->server->tcpStatus = CifsNeedReconnect; midQ->midState = MID_RETRY_NEEDED; } } if (rc != -EHOSTDOWN) { |
79a58d1f6
|
923 |
if (midQ->midState == MID_RETRY_NEEDED) { |
7ee1af765
|
924 |
rc = -EAGAIN; |
79a58d1f6
|
925 |
cFYI(1, ("marking request for retry")); |
7ee1af765
|
926 927 928 929 930 931 932 |
} else { rc = -EIO; } } spin_unlock(&GlobalMid_Lock); DeleteMidQEntry(midQ); return rc; |
1da177e4c
|
933 |
} |
50c2f7538
|
934 |
|
7ee1af765
|
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 |
if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { cERROR(1, ("Frame too large received. Length: %d Xid: %d", receive_len, xid)); rc = -EIO; } else { /* rcvd frame is ok */ if (midQ->resp_buf && out_buf && (midQ->midState == MID_RESPONSE_RECEIVED)) { out_buf->smb_buf_length = receive_len; memcpy((char *)out_buf + 4, (char *)midQ->resp_buf + 4, receive_len); dump_smb(out_buf, 92); /* convert the length into a more usable form */ |
79a58d1f6
|
950 |
if ((receive_len > 24) && |
7ee1af765
|
951 952 953 |
(ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) { rc = cifs_verify_signature(out_buf, |
b609f06ac
|
954 |
&ses->server->mac_signing_key, |
7ee1af765
|
955 |
midQ->sequence_number+1); |
79a58d1f6
|
956 957 |
if (rc) { cERROR(1, ("Unexpected SMB signature")); |
7ee1af765
|
958 959 960 961 962 963 964 965 |
/* BB FIXME add code to kill session */ } } *pbytes_returned = out_buf->smb_buf_length; /* BB special case reconnect tid and uid here? */ rc = map_smb_to_linux_error(out_buf); |
1da177e4c
|
966 |
|
7ee1af765
|
967 968 969 970 971 972 973 974 |
/* convert ByteCount if necessary */ if (receive_len >= sizeof (struct smb_hdr) - 4 /* do not count RFC1001 header */ + (2 * out_buf->WordCount) + 2 /* bcc */ ) BCC(out_buf) = le16_to_cpu(BCC_LE(out_buf)); } else { rc = -EIO; |
79a58d1f6
|
975 |
cERROR(1, ("Bad MID state?")); |
7ee1af765
|
976 977 978 979 980 |
} } DeleteMidQEntry(midQ); if (rstart && rc == -EACCES) return -ERESTARTSYS; |
1da177e4c
|
981 982 |
return rc; } |