aboutsummaryrefslogtreecommitdiffstats
path: root/fs/cifs/transport.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 18:20:36 -0400
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/cifs/transport.c
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
Diffstat (limited to 'fs/cifs/transport.c')
-rw-r--r--fs/cifs/transport.c619
1 files changed, 619 insertions, 0 deletions
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c
new file mode 100644
index 000000000000..af13e526b150
--- /dev/null
+++ b/fs/cifs/transport.c
@@ -0,0 +1,619 @@
1/*
2 * fs/cifs/transport.c
3 *
4 * Copyright (C) International Business Machines Corp., 2002,2004
5 * 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) {
44 cERROR(1, ("Null session passed in to AllocMidQEntry "));
45 return NULL;
46 }
47 if (ses->server == NULL) {
48 cERROR(1, ("Null TCP session in AllocMidQEntry"));
49 return NULL;
50 }
51
52 temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,SLAB_KERNEL | SLAB_NOFS);
53 if (temp == NULL)
54 return temp;
55 else {
56 memset(temp, 0, sizeof (struct mid_q_entry));
57 temp->mid = smb_buffer->Mid; /* always LE */
58 temp->pid = current->pid;
59 temp->command = smb_buffer->Command;
60 cFYI(1, ("For smb_command %d", temp->command));
61 do_gettimeofday(&temp->when_sent);
62 temp->ses = ses;
63 temp->tsk = current;
64 }
65
66 spin_lock(&GlobalMid_Lock);
67 list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
68 atomic_inc(&midCount);
69 temp->midState = MID_REQUEST_ALLOCATED;
70 spin_unlock(&GlobalMid_Lock);
71 return temp;
72}
73
74static void
75DeleteMidQEntry(struct mid_q_entry *midEntry)
76{
77 spin_lock(&GlobalMid_Lock);
78 midEntry->midState = MID_FREE;
79 list_del(&midEntry->qhead);
80 atomic_dec(&midCount);
81 spin_unlock(&GlobalMid_Lock);
82 cifs_buf_release(midEntry->resp_buf);
83 mempool_free(midEntry, cifs_mid_poolp);
84}
85
86struct oplock_q_entry *
87AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon)
88{
89 struct oplock_q_entry *temp;
90 if ((pinode== NULL) || (tcon == NULL)) {
91 cERROR(1, ("Null parms passed to AllocOplockQEntry"));
92 return NULL;
93 }
94 temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
95 SLAB_KERNEL);
96 if (temp == NULL)
97 return temp;
98 else {
99 temp->pinode = pinode;
100 temp->tcon = tcon;
101 temp->netfid = fid;
102 spin_lock(&GlobalMid_Lock);
103 list_add_tail(&temp->qhead, &GlobalOplock_Q);
104 spin_unlock(&GlobalMid_Lock);
105 }
106 return temp;
107
108}
109
110void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
111{
112 spin_lock(&GlobalMid_Lock);
113 /* should we check if list empty first? */
114 list_del(&oplockEntry->qhead);
115 spin_unlock(&GlobalMid_Lock);
116 kmem_cache_free(cifs_oplock_cachep, oplockEntry);
117}
118
119int
120smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
121 unsigned int smb_buf_length, struct sockaddr *sin)
122{
123 int rc = 0;
124 int i = 0;
125 struct msghdr smb_msg;
126 struct kvec iov;
127 unsigned len = smb_buf_length + 4;
128
129 if(ssocket == NULL)
130 return -ENOTSOCK; /* BB eventually add reconnect code here */
131 iov.iov_base = smb_buffer;
132 iov.iov_len = len;
133
134 smb_msg.msg_name = sin;
135 smb_msg.msg_namelen = sizeof (struct sockaddr);
136 smb_msg.msg_control = NULL;
137 smb_msg.msg_controllen = 0;
138 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
139
140 /* smb header is converted in header_assemble. bcc and rest of SMB word
141 area, and byte area if necessary, is converted to littleendian in
142 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
143 Flags2 is converted in SendReceive */
144
145 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
146 cFYI(1, ("Sending smb of length %d ", smb_buf_length));
147 dump_smb(smb_buffer, len);
148
149 while (len > 0) {
150 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
151 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
152 i++;
153 if(i > 60) {
154 cERROR(1,
155 ("sends on sock %p stuck for 30 seconds",
156 ssocket));
157 rc = -EAGAIN;
158 break;
159 }
160 msleep(500);
161 continue;
162 }
163 if (rc < 0)
164 break;
165 iov.iov_base += rc;
166 iov.iov_len -= rc;
167 len -= rc;
168 }
169
170 if (rc < 0) {
171 cERROR(1,("Error %d sending data on socket to server.", rc));
172 } else {
173 rc = 0;
174 }
175
176 return rc;
177}
178
179#ifdef CIFS_EXPERIMENTAL
180/* BB finish off this function, adding support for writing set of pages as iovec */
181/* and also adding support for operations that need to parse the response smb */
182
183int
184smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer,
185 unsigned int smb_buf_length, struct kvec * write_vector /* page list */, struct sockaddr *sin)
186{
187 int rc = 0;
188 int i = 0;
189 struct msghdr smb_msg;
190 number_of_pages += 1; /* account for SMB header */
191 struct kvec * piov = kmalloc(number_of_pages * sizeof(struct kvec));
192 if(i=0;i<num_pages-1;i++
193 unsigned len = smb_buf_length + 4;
194
195 if(ssocket == NULL)
196 return -ENOTSOCK; /* BB eventually add reconnect code here */
197 iov.iov_base = smb_buffer;
198 iov.iov_len = len;
199
200 smb_msg.msg_name = sin;
201 smb_msg.msg_namelen = sizeof (struct sockaddr);
202 smb_msg.msg_control = NULL;
203 smb_msg.msg_controllen = 0;
204 smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
205
206 /* smb header is converted in header_assemble. bcc and rest of SMB word
207 area, and byte area if necessary, is converted to littleendian in
208 cifssmb.c and RFC1001 len is converted to bigendian in smb_send
209 Flags2 is converted in SendReceive */
210
211 smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
212 cFYI(1, ("Sending smb of length %d ", smb_buf_length));
213 dump_smb(smb_buffer, len);
214
215 while (len > 0) {
216 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, number_of_pages, len?);
217 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
218 i++;
219 if(i > 60) {
220 cERROR(1,
221 ("sends on sock %p stuck for 30 seconds",
222 ssocket));
223 rc = -EAGAIN;
224 break;
225 }
226 msleep(500);
227 continue;
228 }
229 if (rc < 0)
230 break;
231 iov.iov_base += rc;
232 iov.iov_len -= rc;
233 len -= rc;
234 }
235
236 if (rc < 0) {
237 cERROR(1,("Error %d sending data on socket to server.", rc));
238 } else {
239 rc = 0;
240 }
241
242 return rc;
243}
244
245
246int
247CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses,
248 struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op)
249{
250 int rc = 0;
251 unsigned long timeout = 15 * HZ;
252 struct mid_q_entry *midQ = NULL;
253
254 if (ses == NULL) {
255 cERROR(1,("Null smb session"));
256 return -EIO;
257 }
258 if(ses->server == NULL) {
259 cERROR(1,("Null tcp session"));
260 return -EIO;
261 }
262 if(pbytes_returned == NULL)
263 return -EIO;
264 else
265 *pbytes_returned = 0;
266
267
268
269 /* Ensure that we do not send more than 50 overlapping requests
270 to the same server. We may make this configurable later or
271 use ses->maxReq */
272 if(long_op == -1) {
273 /* oplock breaks must not be held up */
274 atomic_inc(&ses->server->inFlight);
275 } else {
276 spin_lock(&GlobalMid_Lock);
277 while(1) {
278 if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){
279 spin_unlock(&GlobalMid_Lock);
280 wait_event(ses->server->request_q,
281 atomic_read(&ses->server->inFlight)
282 < cifs_max_pending);
283 spin_lock(&GlobalMid_Lock);
284 } else {
285 if(ses->server->tcpStatus == CifsExiting) {
286 spin_unlock(&GlobalMid_Lock);
287 return -ENOENT;
288 }
289
290 /* can not count locking commands against total since
291 they are allowed to block on server */
292
293 if(long_op < 3) {
294 /* update # of requests on the wire to server */
295 atomic_inc(&ses->server->inFlight);
296 }
297 spin_unlock(&GlobalMid_Lock);
298 break;
299 }
300 }
301 }
302 /* make sure that we sign in the same order that we send on this socket
303 and avoid races inside tcp sendmsg code that could cause corruption
304 of smb data */
305
306 down(&ses->server->tcpSem);
307
308 if (ses->server->tcpStatus == CifsExiting) {
309 rc = -ENOENT;
310 goto cifs_out_label;
311 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
312 cFYI(1,("tcp session dead - return to caller to retry"));
313 rc = -EAGAIN;
314 goto cifs_out_label;
315 } else if (ses->status != CifsGood) {
316 /* check if SMB session is bad because we are setting it up */
317 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
318 (in_buf->Command != SMB_COM_NEGOTIATE)) {
319 rc = -EAGAIN;
320 goto cifs_out_label;
321 } /* else ok - we are setting up session */
322 }
323 midQ = AllocMidQEntry(in_buf, ses);
324 if (midQ == NULL) {
325 up(&ses->server->tcpSem);
326 /* If not lock req, update # of requests on wire to server */
327 if(long_op < 3) {
328 atomic_dec(&ses->server->inFlight);
329 wake_up(&ses->server->request_q);
330 }
331 return -ENOMEM;
332 }
333
334 if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
335 up(&ses->server->tcpSem);
336 cERROR(1,
337 ("Illegal length, greater than maximum frame, %d ",
338 in_buf->smb_buf_length));
339 DeleteMidQEntry(midQ);
340 /* If not lock req, update # of requests on wire to server */
341 if(long_op < 3) {
342 atomic_dec(&ses->server->inFlight);
343 wake_up(&ses->server->request_q);
344 }
345 return -EIO;
346 }
347
348 /* BB can we sign efficiently in this path? */
349 rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
350
351 midQ->midState = MID_REQUEST_SUBMITTED;
352/* rc = smb_sendv(ses->server->ssocket, in_buf, in_buf->smb_buf_length, piovec,
353 (struct sockaddr *) &(ses->server->addr.sockAddr));*/
354 if(rc < 0) {
355 DeleteMidQEntry(midQ);
356 up(&ses->server->tcpSem);
357 /* If not lock req, update # of requests on wire to server */
358 if(long_op < 3) {
359 atomic_dec(&ses->server->inFlight);
360 wake_up(&ses->server->request_q);
361 }
362 return rc;
363 } else
364 up(&ses->server->tcpSem);
365cifs_out_label:
366 if(midQ)
367 DeleteMidQEntry(midQ);
368
369 if(long_op < 3) {
370 atomic_dec(&ses->server->inFlight);
371 wake_up(&ses->server->request_q);
372 }
373
374 return rc;
375}
376
377
378#endif /* CIFS_EXPERIMENTAL */
379
380int
381SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
382 struct smb_hdr *in_buf, struct smb_hdr *out_buf,
383 int *pbytes_returned, const int long_op)
384{
385 int rc = 0;
386 unsigned int receive_len;
387 unsigned long timeout;
388 struct mid_q_entry *midQ;
389
390 if (ses == NULL) {
391 cERROR(1,("Null smb session"));
392 return -EIO;
393 }
394 if(ses->server == NULL) {
395 cERROR(1,("Null tcp session"));
396 return -EIO;
397 }
398
399 /* Ensure that we do not send more than 50 overlapping requests
400 to the same server. We may make this configurable later or
401 use ses->maxReq */
402 if(long_op == -1) {
403 /* oplock breaks must not be held up */
404 atomic_inc(&ses->server->inFlight);
405 } else {
406 spin_lock(&GlobalMid_Lock);
407 while(1) {
408 if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){
409 spin_unlock(&GlobalMid_Lock);
410 wait_event(ses->server->request_q,
411 atomic_read(&ses->server->inFlight)
412 < cifs_max_pending);
413 spin_lock(&GlobalMid_Lock);
414 } else {
415 if(ses->server->tcpStatus == CifsExiting) {
416 spin_unlock(&GlobalMid_Lock);
417 return -ENOENT;
418 }
419
420 /* can not count locking commands against total since
421 they are allowed to block on server */
422
423 if(long_op < 3) {
424 /* update # of requests on the wire to server */
425 atomic_inc(&ses->server->inFlight);
426 }
427 spin_unlock(&GlobalMid_Lock);
428 break;
429 }
430 }
431 }
432 /* make sure that we sign in the same order that we send on this socket
433 and avoid races inside tcp sendmsg code that could cause corruption
434 of smb data */
435
436 down(&ses->server->tcpSem);
437
438 if (ses->server->tcpStatus == CifsExiting) {
439 rc = -ENOENT;
440 goto out_unlock;
441 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
442 cFYI(1,("tcp session dead - return to caller to retry"));
443 rc = -EAGAIN;
444 goto out_unlock;
445 } else if (ses->status != CifsGood) {
446 /* check if SMB session is bad because we are setting it up */
447 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
448 (in_buf->Command != SMB_COM_NEGOTIATE)) {
449 rc = -EAGAIN;
450 goto out_unlock;
451 } /* else ok - we are setting up session */
452 }
453 midQ = AllocMidQEntry(in_buf, ses);
454 if (midQ == NULL) {
455 up(&ses->server->tcpSem);
456 /* If not lock req, update # of requests on wire to server */
457 if(long_op < 3) {
458 atomic_dec(&ses->server->inFlight);
459 wake_up(&ses->server->request_q);
460 }
461 return -ENOMEM;
462 }
463
464 if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
465 up(&ses->server->tcpSem);
466 cERROR(1,
467 ("Illegal length, greater than maximum frame, %d ",
468 in_buf->smb_buf_length));
469 DeleteMidQEntry(midQ);
470 /* If not lock req, update # of requests on wire to server */
471 if(long_op < 3) {
472 atomic_dec(&ses->server->inFlight);
473 wake_up(&ses->server->request_q);
474 }
475 return -EIO;
476 }
477
478 rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
479
480 midQ->midState = MID_REQUEST_SUBMITTED;
481 rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
482 (struct sockaddr *) &(ses->server->addr.sockAddr));
483 if(rc < 0) {
484 DeleteMidQEntry(midQ);
485 up(&ses->server->tcpSem);
486 /* If not lock req, update # of requests on wire to server */
487 if(long_op < 3) {
488 atomic_dec(&ses->server->inFlight);
489 wake_up(&ses->server->request_q);
490 }
491 return rc;
492 } else
493 up(&ses->server->tcpSem);
494 if (long_op == -1)
495 goto cifs_no_response_exit;
496 else if (long_op == 2) /* writes past end of file can take looooong time */
497 timeout = 300 * HZ;
498 else if (long_op == 1)
499 timeout = 45 * HZ; /* should be greater than
500 servers oplock break timeout (about 43 seconds) */
501 else if (long_op > 2) {
502 timeout = MAX_SCHEDULE_TIMEOUT;
503 } else
504 timeout = 15 * HZ;
505 /* wait for 15 seconds or until woken up due to response arriving or
506 due to last connection to this server being unmounted */
507 if (signal_pending(current)) {
508 /* if signal pending do not hold up user for full smb timeout
509 but we still give response a change to complete */
510 timeout = 2 * HZ;
511 }
512
513 /* No user interrupts in wait - wreaks havoc with performance */
514 if(timeout != MAX_SCHEDULE_TIMEOUT) {
515 timeout += jiffies;
516 wait_event(ses->server->response_q,
517 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
518 time_after(jiffies, timeout) ||
519 ((ses->server->tcpStatus != CifsGood) &&
520 (ses->server->tcpStatus != CifsNew)));
521 } else {
522 wait_event(ses->server->response_q,
523 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
524 ((ses->server->tcpStatus != CifsGood) &&
525 (ses->server->tcpStatus != CifsNew)));
526 }
527
528 spin_lock(&GlobalMid_Lock);
529 if (midQ->resp_buf) {
530 spin_unlock(&GlobalMid_Lock);
531 receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf);
532 } else {
533 cERROR(1,("No response buffer"));
534 if(midQ->midState == MID_REQUEST_SUBMITTED) {
535 if(ses->server->tcpStatus == CifsExiting)
536 rc = -EHOSTDOWN;
537 else {
538 ses->server->tcpStatus = CifsNeedReconnect;
539 midQ->midState = MID_RETRY_NEEDED;
540 }
541 }
542
543 if (rc != -EHOSTDOWN) {
544 if(midQ->midState == MID_RETRY_NEEDED) {
545 rc = -EAGAIN;
546 cFYI(1,("marking request for retry"));
547 } else {
548 rc = -EIO;
549 }
550 }
551 spin_unlock(&GlobalMid_Lock);
552 DeleteMidQEntry(midQ);
553 /* If not lock req, update # of requests on wire to server */
554 if(long_op < 3) {
555 atomic_dec(&ses->server->inFlight);
556 wake_up(&ses->server->request_q);
557 }
558 return rc;
559 }
560
561 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
562 cERROR(1,
563 ("Frame too large received. Length: %d Xid: %d",
564 receive_len, xid));
565 rc = -EIO;
566 } else { /* rcvd frame is ok */
567
568 if (midQ->resp_buf && out_buf
569 && (midQ->midState == MID_RESPONSE_RECEIVED)) {
570 out_buf->smb_buf_length = receive_len;
571 memcpy((char *)out_buf + 4,
572 (char *)midQ->resp_buf + 4,
573 receive_len);
574
575 dump_smb(out_buf, 92);
576 /* convert the length into a more usable form */
577 if((receive_len > 24) &&
578 (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) {
579 rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */
580 if(rc)
581 cFYI(1,("Unexpected signature received from server"));
582 }
583
584 *pbytes_returned = out_buf->smb_buf_length;
585
586 /* BB special case reconnect tid and reconnect uid here? */
587 rc = map_smb_to_linux_error(out_buf);
588
589 /* convert ByteCount if necessary */
590 if (receive_len >=
591 sizeof (struct smb_hdr) -
592 4 /* do not count RFC1001 header */ +
593 (2 * out_buf->WordCount) + 2 /* bcc */ )
594 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
595 } else {
596 rc = -EIO;
597 cFYI(1,("Bad MID state? "));
598 }
599 }
600cifs_no_response_exit:
601 DeleteMidQEntry(midQ);
602
603 if(long_op < 3) {
604 atomic_dec(&ses->server->inFlight);
605 wake_up(&ses->server->request_q);
606 }
607
608 return rc;
609
610out_unlock:
611 up(&ses->server->tcpSem);
612 /* If not lock req, update # of requests on wire to server */
613 if(long_op < 3) {
614 atomic_dec(&ses->server->inFlight);
615 wake_up(&ses->server->request_q);
616 }
617
618 return rc;
619}