patch-2_6_7-vs1_9_1_12
[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
206         /* can not count locking commands against the total since
207                 they are allowed to block on server */
208         if(long_op < 3) {
209                 /* update # of requests on the wire to this server */
210                 atomic_inc(&ses->server->inFlight); 
211         }
212  
213         if(atomic_read(&ses->server->inFlight) > CIFS_MAX_REQ) {
214                 wait_event(ses->server->request_q,atomic_read(&ses->server->inFlight) <= CIFS_MAX_REQ);
215         }
216
217         /* make sure that we sign in the same order that we send on this socket 
218                 and avoid races inside tcp sendmsg code that could cause corruption
219                 of smb data */
220
221         down(&ses->server->tcpSem); 
222
223         if (ses->server->tcpStatus == CifsExiting) {
224                 rc = -ENOENT;
225                 goto out_unlock;
226         } else if (ses->server->tcpStatus == CifsNeedReconnect) {
227                 cFYI(1,("tcp session dead - return to caller to retry"));
228                 rc = -EAGAIN;
229                 goto out_unlock;
230         } else if (ses->status != CifsGood) {
231                 /* check if SMB session is bad because we are setting it up */
232                 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && 
233                         (in_buf->Command != SMB_COM_NEGOTIATE)) {
234                         rc = -EAGAIN;
235                         goto out_unlock;
236                 } /* else ok - we are setting up session */
237         }
238         midQ = AllocMidQEntry(in_buf, ses);
239         if (midQ == NULL) {
240                 up(&ses->server->tcpSem);
241                 /* If not lock req, update # of requests on wire to server */
242                 if(long_op < 3) {
243                         atomic_dec(&ses->server->inFlight); 
244                         wake_up(&ses->server->request_q);
245                 }
246                 return -ENOMEM;
247         }
248
249         if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) {
250                 up(&ses->server->tcpSem);
251                 cERROR(1,
252                        ("Illegal length, greater than maximum frame, %d ",
253                         in_buf->smb_buf_length));
254                 DeleteMidQEntry(midQ);
255                 /* If not lock req, update # of requests on wire to server */
256                 if(long_op < 3) {
257                         atomic_dec(&ses->server->inFlight); 
258                         wake_up(&ses->server->request_q);
259                 }
260                 return -EIO;
261         }
262
263         if (in_buf->smb_buf_length > 12)
264                 in_buf->Flags2 = cpu_to_le16(in_buf->Flags2);
265         
266         rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
267
268         midQ->midState = MID_REQUEST_SUBMITTED;
269         rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
270                       (struct sockaddr *) &(ses->server->addr.sockAddr));
271         if(rc < 0) {
272                 DeleteMidQEntry(midQ);
273                 up(&ses->server->tcpSem);
274                 /* If not lock req, update # of requests on wire to server */
275                 if(long_op < 3) {
276                         atomic_dec(&ses->server->inFlight); 
277                         wake_up(&ses->server->request_q);
278                 }
279                 return rc;
280         } else
281                 up(&ses->server->tcpSem);
282         if (long_op == -1)
283                 goto cifs_no_response_exit;
284         else if (long_op == 2) /* writes past end of file can take looooong time */
285                 timeout = 300 * HZ;
286         else if (long_op == 1)
287                 timeout = 45 * HZ; /* should be greater than 
288                         servers oplock break timeout (about 43 seconds) */
289         else if (long_op > 2) {
290                 timeout = MAX_SCHEDULE_TIMEOUT;
291         } else
292                 timeout = 15 * HZ;
293         /* wait for 15 seconds or until woken up due to response arriving or 
294            due to last connection to this server being unmounted */
295         if (signal_pending(current)) {
296                 /* if signal pending do not hold up user for full smb timeout
297                 but we still give response a change to complete */
298                 if(midQ->midState & MID_REQUEST_SUBMITTED) {
299                         set_current_state(TASK_UNINTERRUPTIBLE);
300                         timeout = sleep_on_timeout(&ses->server->response_q,2 * HZ);
301                 }
302         } else { /* using normal timeout */
303                 /* timeout = wait_event_interruptible_timeout(ses->server->response_q,
304                         (midQ->midState & MID_RESPONSE_RECEIVED) || 
305                         ((ses->server->tcpStatus != CifsGood) &&
306                          (ses->server->tcpStatus != CifsNew)),
307                         timeout); */ 
308                 /* Can not allow user interrupts- wreaks havoc with performance */
309                 if(midQ->midState & MID_REQUEST_SUBMITTED) {
310                         set_current_state(TASK_UNINTERRUPTIBLE);
311                         timeout = sleep_on_timeout(&ses->server->response_q,timeout);
312                 }
313         }
314     
315         spin_lock(&GlobalMid_Lock);
316         if (midQ->resp_buf) {
317                 spin_unlock(&GlobalMid_Lock);
318                 receive_len = be32_to_cpu(midQ->resp_buf->smb_buf_length);
319         } else {
320                 cERROR(1,("No response buffer"));
321                 if(midQ->midState == MID_REQUEST_SUBMITTED) {
322                         if(ses->server->tcpStatus == CifsExiting)
323                                 rc = -EHOSTDOWN;
324                         else {
325                                 ses->server->tcpStatus = CifsNeedReconnect;
326                                 midQ->midState = MID_RETRY_NEEDED;
327                         }
328                 }
329
330                 if (rc != -EHOSTDOWN) {
331                         if(midQ->midState == MID_RETRY_NEEDED) {
332                                 rc = -EAGAIN;
333                                 cFYI(1,("marking request for retry"));
334                         } else {
335                                 rc = -EIO;
336                         }
337                 }
338                 spin_unlock(&GlobalMid_Lock);
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 rc;
346         }
347   
348         if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) {
349                 cERROR(1,
350                        ("Frame too large received.  Length: %d  Xid: %d",
351                         receive_len, xid));
352                 rc = -EIO;
353         } else {                /* rcvd frame is ok */
354
355                 if (midQ->resp_buf && out_buf
356                     && (midQ->midState == MID_RESPONSE_RECEIVED)) {
357                         memcpy(out_buf, midQ->resp_buf,
358                                receive_len +
359                                4 /* include 4 byte RFC1001 header */ );
360
361                         dump_smb(out_buf, 92);
362                         /* convert the length into a more usable form */
363                         out_buf->smb_buf_length =
364                             be32_to_cpu(out_buf->smb_buf_length);
365                         if((out_buf->smb_buf_length > 24) &&
366                            (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) {
367                                 rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */
368                                 if(rc)
369                                         cFYI(1,("Unexpected signature received from server"));
370                         }
371
372                         if (out_buf->smb_buf_length > 12)
373                                 out_buf->Flags2 = le16_to_cpu(out_buf->Flags2);
374                         if (out_buf->smb_buf_length > 28)
375                                 out_buf->Pid = le16_to_cpu(out_buf->Pid);
376                         if (out_buf->smb_buf_length > 28)
377                                 out_buf->PidHigh =
378                                     le16_to_cpu(out_buf->PidHigh);
379
380                         *pbytes_returned = out_buf->smb_buf_length;
381
382                         /* BB special case reconnect tid and reconnect uid here? */
383                         rc = map_smb_to_linux_error(out_buf);
384
385                         /* convert ByteCount if necessary */
386                         if (receive_len >=
387                             sizeof (struct smb_hdr) -
388                             4 /* do not count RFC1001 header */  +
389                             (2 * out_buf->WordCount) + 2 /* bcc */ )
390                                 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
391                 } else {
392                         rc = -EIO;
393                         cFYI(1,("Bad MID state? "));
394                 }
395         }
396 cifs_no_response_exit:
397         DeleteMidQEntry(midQ);
398
399         if(long_op < 3) {
400                 atomic_dec(&ses->server->inFlight); 
401                 wake_up(&ses->server->request_q);
402         }
403
404         return rc;
405
406 out_unlock:
407         up(&ses->server->tcpSem);
408         /* If not lock req, update # of requests on wire to server */
409         if(long_op < 3) {
410                 atomic_dec(&ses->server->inFlight); 
411                 wake_up(&ses->server->request_q);
412         }
413
414         return rc;
415 }