Linux-2.6.12-rc2
[linux-flexiantxendom0-natty.git] / fs / cifs / transport.c
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   
35 extern mempool_t *cifs_mid_poolp;
36 extern kmem_cache_t *cifs_oplock_cachep;
37
38 static struct mid_q_entry *
39 AllocMidQEntry(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
74 static void
75 DeleteMidQEntry(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
86 struct oplock_q_entry *
87 AllocOplockQEntry(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
110 void 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
119 int
120 smb_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
183 int
184 smb_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
246 int
247 CIFSSendRcv(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);
365 cifs_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
380 int
381 SendReceive(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         }
600 cifs_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
610 out_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 }