Merge to Fedora kernel-2.6.7-1.492
[linux-2.6.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 <asm/uaccess.h>
27 #include <asm/processor.h>
28 #include <linux/mempool.h>
29 #include "cifspdu.h"
30 #include "cifsglob.h"
31 #include "cifsproto.h"
32 #include "cifs_debug.h"
33   
34 extern mempool_t *cifs_mid_poolp;
35 extern kmem_cache_t *cifs_oplock_cachep;
36
37 struct mid_q_entry *
38 AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses)
39 {
40         struct mid_q_entry *temp;
41
42         if (ses == NULL) {
43                 cERROR(1, ("Null session passed in to AllocMidQEntry "));
44                 return NULL;
45         }
46         if (ses->server == NULL) {
47                 cERROR(1, ("Null TCP session in AllocMidQEntry"));
48                 return NULL;
49         }
50         
51         temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,SLAB_KERNEL | SLAB_NOFS);
52         if (temp == NULL)
53                 return temp;
54         else {
55                 memset(temp, 0, sizeof (struct mid_q_entry));
56                 temp->mid = smb_buffer->Mid;    /* always LE */
57                 temp->pid = current->pid;
58                 temp->command = smb_buffer->Command;
59                 cFYI(1, ("For smb_command %d", temp->command));
60                 do_gettimeofday(&temp->when_sent);
61                 temp->ses = ses;
62                 temp->tsk = current;
63         }
64
65         spin_lock(&GlobalMid_Lock);
66         list_add_tail(&temp->qhead, &ses->server->pending_mid_q);
67         atomic_inc(&midCount);
68         temp->midState = MID_REQUEST_ALLOCATED;
69         spin_unlock(&GlobalMid_Lock);
70         return temp;
71 }
72
73 void
74 DeleteMidQEntry(struct mid_q_entry *midEntry)
75 {
76         spin_lock(&GlobalMid_Lock);
77         midEntry->midState = MID_FREE;
78         list_del(&midEntry->qhead);
79         atomic_dec(&midCount);
80         spin_unlock(&GlobalMid_Lock);
81         cifs_buf_release(midEntry->resp_buf);
82         mempool_free(midEntry, cifs_mid_poolp);
83 }
84
85 struct oplock_q_entry *
86 AllocOplockQEntry(struct inode * pinode, __u16 fid, struct cifsTconInfo * tcon)
87 {
88         struct oplock_q_entry *temp;
89         if ((pinode== NULL) || (tcon == NULL)) {
90                 cERROR(1, ("Null parms passed to AllocOplockQEntry"));
91                 return NULL;
92         }
93         temp = (struct oplock_q_entry *) kmem_cache_alloc(cifs_oplock_cachep,
94                                                        SLAB_KERNEL);
95         if (temp == NULL)
96                 return temp;
97         else {
98                 temp->pinode = pinode;
99                 temp->tcon = tcon;
100                 temp->netfid = fid;
101                 spin_lock(&GlobalMid_Lock);
102                 list_add_tail(&temp->qhead, &GlobalOplock_Q);
103                 spin_unlock(&GlobalMid_Lock);
104         }
105         return temp;
106
107 }
108
109 void DeleteOplockQEntry(struct oplock_q_entry * oplockEntry)
110 {
111         spin_lock(&GlobalMid_Lock); 
112     /* should we check if list empty first? */
113         list_del(&oplockEntry->qhead);
114         spin_unlock(&GlobalMid_Lock);
115         kmem_cache_free(cifs_oplock_cachep, oplockEntry);
116 }
117
118 int
119 smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer,
120          unsigned int smb_buf_length, struct sockaddr *sin)
121 {
122         int rc = 0;
123         int i = 0;
124         struct msghdr smb_msg;
125         struct iovec iov;
126         mm_segment_t temp_fs;
127
128         if(ssocket == NULL)
129                 return -ENOTSOCK; /* BB eventually add reconnect code here */
130         iov.iov_base = smb_buffer;
131         iov.iov_len = smb_buf_length + 4;
132
133         smb_msg.msg_name = sin;
134         smb_msg.msg_namelen = sizeof (struct sockaddr);
135         smb_msg.msg_iov = &iov;
136         smb_msg.msg_iovlen = 1;
137         smb_msg.msg_control = NULL;
138         smb_msg.msg_controllen = 0;
139         smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
140
141         /* smb header is converted in header_assemble. bcc and rest of SMB word
142            area, and byte area if necessary, is converted to littleendian in 
143            cifssmb.c and RFC1001 len is converted to bigendian in smb_send 
144            Flags2 is converted in SendReceive */
145
146         smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
147         cFYI(1, ("Sending smb of length %d ", smb_buf_length));
148         dump_smb(smb_buffer, smb_buf_length + 4);
149
150         temp_fs = get_fs();     /* we must turn off socket api parm checking */
151         set_fs(get_ds());
152         while(iov.iov_len > 0) {
153                 rc = sock_sendmsg(ssocket, &smb_msg, smb_buf_length + 4);
154                 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
155                         i++;
156                         if(i > 60) {
157                                 cERROR(1,
158                                    ("sends on sock %p stuck for 30 seconds",
159                                     ssocket));
160                                 rc = -EAGAIN;
161                                 break;
162                         }
163                         set_current_state(TASK_INTERRUPTIBLE);
164                         schedule_timeout(HZ/2);
165                         continue;
166                 }
167                 if (rc < 0) 
168                         break;
169                 iov.iov_base += rc;
170                 iov.iov_len -= rc;
171         }
172         set_fs(temp_fs);
173
174         if (rc < 0) {
175                 cERROR(1,("Error %d sending data on socket to server.", rc));
176         } else {
177                 rc = 0;
178         }
179
180         return rc;
181 }
182
183 int
184 SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
185             struct smb_hdr *in_buf, struct smb_hdr *out_buf,
186             int *pbytes_returned, const int long_op)
187 {
188         int rc = 0;
189         unsigned int receive_len;
190         long timeout;
191         struct mid_q_entry *midQ;
192
193         if (ses == NULL) {
194                 cERROR(1,("Null smb session"));
195                 return -EIO;
196         }
197         if(ses->server == NULL) {
198                 cERROR(1,("Null tcp session"));
199                 return -EIO;
200         }
201
202         /* Ensure that we do not send more than 50 overlapping requests 
203            to the same server. We may make this configurable later or
204            use ses->maxReq */
205         if(long_op == -1) {
206                 /* oplock breaks must not be held up */
207                 atomic_inc(&ses->server->inFlight);
208         } else {
209                 spin_lock(&GlobalMid_Lock); 
210                 while(1) {        
211                         if(atomic_read(&ses->server->inFlight) >= CIFS_MAX_REQ){
212                                 spin_unlock(&GlobalMid_Lock);
213                                 wait_event(ses->server->request_q,
214                                         atomic_read(&ses->server->inFlight)
215                                          < CIFS_MAX_REQ);
216                                 spin_lock(&GlobalMid_Lock);
217                         } else {
218                                 if(ses->server->tcpStatus == CifsExiting) {
219                                         spin_unlock(&GlobalMid_Lock);
220                                         return -ENOENT;
221                                 }
222
223                         /* can not count locking commands against total since
224                            they are allowed to block on server */
225                                         
226                                 if(long_op < 3) {
227                                 /* update # of requests on the wire to server */
228                                         atomic_inc(&ses->server->inFlight);
229                                 }
230                                 spin_unlock(&GlobalMid_Lock);
231                                 break;
232                         }
233                 }
234         }
235         /* make sure that we sign in the same order that we send on this socket 
236            and avoid races inside tcp sendmsg code that could cause corruption
237            of smb data */
238
239         down(&ses->server->tcpSem); 
240
241         if (ses->server->tcpStatus == CifsExiting) {
242                 rc = -ENOENT;
243                 goto out_unlock;
244         } else if (ses->server->tcpStatus == CifsNeedReconnect) {
245                 cFYI(1,("tcp session dead - return to caller to retry"));
246                 rc = -EAGAIN;
247                 goto out_unlock;
248         } else if (ses->status != CifsGood) {
249                 /* check if SMB session is bad because we are setting it up */
250                 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && 
251                         (in_buf->Command != SMB_COM_NEGOTIATE)) {
252                         rc = -EAGAIN;
253                         goto out_unlock;
254                 } /* else ok - we are setting up session */
255         }
256         midQ = AllocMidQEntry(in_buf, ses);
257         if (midQ == NULL) {
258                 up(&ses->server->tcpSem);
259                 /* If not lock req, update # of requests on wire to server */
260                 if(long_op < 3) {
261                         atomic_dec(&ses->server->inFlight); 
262                         wake_up(&ses->server->request_q);
263                 }
264                 return -ENOMEM;
265         }
266
267         if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) {
268                 up(&ses->server->tcpSem);
269                 cERROR(1,
270                        ("Illegal length, greater than maximum frame, %d ",
271                         in_buf->smb_buf_length));
272                 DeleteMidQEntry(midQ);
273                 /* If not lock req, update # of requests on wire to server */
274                 if(long_op < 3) {
275                         atomic_dec(&ses->server->inFlight); 
276                         wake_up(&ses->server->request_q);
277                 }
278                 return -EIO;
279         }
280
281         if (in_buf->smb_buf_length > 12)
282                 in_buf->Flags2 = cpu_to_le16(in_buf->Flags2);
283         
284         rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
285
286         midQ->midState = MID_REQUEST_SUBMITTED;
287         rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
288                       (struct sockaddr *) &(ses->server->addr.sockAddr));
289         if(rc < 0) {
290                 DeleteMidQEntry(midQ);
291                 up(&ses->server->tcpSem);
292                 /* If not lock req, update # of requests on wire to server */
293                 if(long_op < 3) {
294                         atomic_dec(&ses->server->inFlight); 
295                         wake_up(&ses->server->request_q);
296                 }
297                 return rc;
298         } else
299                 up(&ses->server->tcpSem);
300         if (long_op == -1)
301                 goto cifs_no_response_exit;
302         else if (long_op == 2) /* writes past end of file can take looooong time */
303                 timeout = 300 * HZ;
304         else if (long_op == 1)
305                 timeout = 45 * HZ; /* should be greater than 
306                         servers oplock break timeout (about 43 seconds) */
307         else if (long_op > 2) {
308                 timeout = MAX_SCHEDULE_TIMEOUT;
309         } else
310                 timeout = 15 * HZ;
311         /* wait for 15 seconds or until woken up due to response arriving or 
312            due to last connection to this server being unmounted */
313         if (signal_pending(current)) {
314                 /* if signal pending do not hold up user for full smb timeout
315                 but we still give response a change to complete */
316                 if(midQ->midState & MID_REQUEST_SUBMITTED) {
317                         set_current_state(TASK_UNINTERRUPTIBLE);
318                         timeout = sleep_on_timeout(&ses->server->response_q,2 * HZ);
319                 }
320         } else { /* using normal timeout */
321                 /* timeout = wait_event_interruptible_timeout(ses->server->response_q,
322                         (midQ->midState & MID_RESPONSE_RECEIVED) || 
323                         ((ses->server->tcpStatus != CifsGood) &&
324                          (ses->server->tcpStatus != CifsNew)),
325                         timeout); */ 
326                 /* Can not allow user interrupts- wreaks havoc with performance */
327                 if(midQ->midState & MID_REQUEST_SUBMITTED) {
328                         set_current_state(TASK_UNINTERRUPTIBLE);
329                         timeout = sleep_on_timeout(&ses->server->response_q,timeout);
330                 }
331         }
332     
333         spin_lock(&GlobalMid_Lock);
334         if (midQ->resp_buf) {
335                 spin_unlock(&GlobalMid_Lock);
336                 receive_len = be32_to_cpu(midQ->resp_buf->smb_buf_length);
337         } else {
338                 cERROR(1,("No response buffer"));
339                 if(midQ->midState == MID_REQUEST_SUBMITTED) {
340                         if(ses->server->tcpStatus == CifsExiting)
341                                 rc = -EHOSTDOWN;
342                         else {
343                                 ses->server->tcpStatus = CifsNeedReconnect;
344                                 midQ->midState = MID_RETRY_NEEDED;
345                         }
346                 }
347
348                 if (rc != -EHOSTDOWN) {
349                         if(midQ->midState == MID_RETRY_NEEDED) {
350                                 rc = -EAGAIN;
351                                 cFYI(1,("marking request for retry"));
352                         } else {
353                                 rc = -EIO;
354                         }
355                 }
356                 spin_unlock(&GlobalMid_Lock);
357                 DeleteMidQEntry(midQ);
358                 /* If not lock req, update # of requests on wire to server */
359                 if(long_op < 3) {
360                         atomic_dec(&ses->server->inFlight); 
361                         wake_up(&ses->server->request_q);
362                 }
363                 return rc;
364         }
365   
366         if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) {
367                 cERROR(1,
368                        ("Frame too large received.  Length: %d  Xid: %d",
369                         receive_len, xid));
370                 rc = -EIO;
371         } else {                /* rcvd frame is ok */
372
373                 if (midQ->resp_buf && out_buf
374                     && (midQ->midState == MID_RESPONSE_RECEIVED)) {
375                         memcpy(out_buf, midQ->resp_buf,
376                                receive_len +
377                                4 /* include 4 byte RFC1001 header */ );
378
379                         dump_smb(out_buf, 92);
380                         /* convert the length into a more usable form */
381                         out_buf->smb_buf_length =
382                             be32_to_cpu(out_buf->smb_buf_length);
383                         if((out_buf->smb_buf_length > 24) &&
384                            (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) {
385                                 rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */
386                                 if(rc)
387                                         cFYI(1,("Unexpected signature received from server"));
388                         }
389
390                         if (out_buf->smb_buf_length > 12)
391                                 out_buf->Flags2 = le16_to_cpu(out_buf->Flags2);
392                         if (out_buf->smb_buf_length > 28)
393                                 out_buf->Pid = le16_to_cpu(out_buf->Pid);
394                         if (out_buf->smb_buf_length > 28)
395                                 out_buf->PidHigh =
396                                     le16_to_cpu(out_buf->PidHigh);
397
398                         *pbytes_returned = out_buf->smb_buf_length;
399
400                         /* BB special case reconnect tid and reconnect uid here? */
401                         rc = map_smb_to_linux_error(out_buf);
402
403                         /* convert ByteCount if necessary */
404                         if (receive_len >=
405                             sizeof (struct smb_hdr) -
406                             4 /* do not count RFC1001 header */  +
407                             (2 * out_buf->WordCount) + 2 /* bcc */ )
408                                 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
409                 } else {
410                         rc = -EIO;
411                         cFYI(1,("Bad MID state? "));
412                 }
413         }
414 cifs_no_response_exit:
415         DeleteMidQEntry(midQ);
416
417         if(long_op < 3) {
418                 atomic_dec(&ses->server->inFlight); 
419                 wake_up(&ses->server->request_q);
420         }
421
422         return rc;
423
424 out_unlock:
425         up(&ses->server->tcpSem);
426         /* If not lock req, update # of requests on wire to server */
427         if(long_op < 3) {
428                 atomic_dec(&ses->server->inFlight); 
429                 wake_up(&ses->server->request_q);
430         }
431
432         return rc;
433 }