vserver 1.9.3
[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 kvec iov;
126         unsigned len = smb_buf_length + 4;
127
128         if(ssocket == NULL)
129                 return -ENOTSOCK; /* BB eventually add reconnect code here */
130         iov.iov_base = smb_buffer;
131         iov.iov_len = len;
132
133         smb_msg.msg_name = sin;
134         smb_msg.msg_namelen = sizeof (struct sockaddr);
135         smb_msg.msg_control = NULL;
136         smb_msg.msg_controllen = 0;
137         smb_msg.msg_flags = MSG_DONTWAIT + MSG_NOSIGNAL; /* BB add more flags?*/
138
139         /* smb header is converted in header_assemble. bcc and rest of SMB word
140            area, and byte area if necessary, is converted to littleendian in 
141            cifssmb.c and RFC1001 len is converted to bigendian in smb_send 
142            Flags2 is converted in SendReceive */
143
144         smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length);
145         cFYI(1, ("Sending smb of length %d ", smb_buf_length));
146         dump_smb(smb_buffer, len);
147
148         while (len > 0) {
149                 rc = kernel_sendmsg(ssocket, &smb_msg, &iov, 1, len);
150                 if ((rc == -ENOSPC) || (rc == -EAGAIN)) {
151                         i++;
152                         if(i > 60) {
153                                 cERROR(1,
154                                    ("sends on sock %p stuck for 30 seconds",
155                                     ssocket));
156                                 rc = -EAGAIN;
157                                 break;
158                         }
159                         set_current_state(TASK_INTERRUPTIBLE);
160                         schedule_timeout(HZ/2);
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 int
180 SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
181             struct smb_hdr *in_buf, struct smb_hdr *out_buf,
182             int *pbytes_returned, const int long_op)
183 {
184         int rc = 0;
185         unsigned int receive_len;
186         unsigned long timeout;
187         struct mid_q_entry *midQ;
188
189         if (ses == NULL) {
190                 cERROR(1,("Null smb session"));
191                 return -EIO;
192         }
193         if(ses->server == NULL) {
194                 cERROR(1,("Null tcp session"));
195                 return -EIO;
196         }
197
198         /* Ensure that we do not send more than 50 overlapping requests 
199            to the same server. We may make this configurable later or
200            use ses->maxReq */
201         if(long_op == -1) {
202                 /* oplock breaks must not be held up */
203                 atomic_inc(&ses->server->inFlight);
204         } else {
205                 spin_lock(&GlobalMid_Lock); 
206                 while(1) {        
207                         if(atomic_read(&ses->server->inFlight) >= CIFS_MAX_REQ){
208                                 spin_unlock(&GlobalMid_Lock);
209                                 wait_event(ses->server->request_q,
210                                         atomic_read(&ses->server->inFlight)
211                                          < CIFS_MAX_REQ);
212                                 spin_lock(&GlobalMid_Lock);
213                         } else {
214                                 if(ses->server->tcpStatus == CifsExiting) {
215                                         spin_unlock(&GlobalMid_Lock);
216                                         return -ENOENT;
217                                 }
218
219                         /* can not count locking commands against total since
220                            they are allowed to block on server */
221                                         
222                                 if(long_op < 3) {
223                                 /* update # of requests on the wire to server */
224                                         atomic_inc(&ses->server->inFlight);
225                                 }
226                                 spin_unlock(&GlobalMid_Lock);
227                                 break;
228                         }
229                 }
230         }
231         /* make sure that we sign in the same order that we send on this socket 
232            and avoid races inside tcp sendmsg code that could cause corruption
233            of smb data */
234
235         down(&ses->server->tcpSem); 
236
237         if (ses->server->tcpStatus == CifsExiting) {
238                 rc = -ENOENT;
239                 goto out_unlock;
240         } else if (ses->server->tcpStatus == CifsNeedReconnect) {
241                 cFYI(1,("tcp session dead - return to caller to retry"));
242                 rc = -EAGAIN;
243                 goto out_unlock;
244         } else if (ses->status != CifsGood) {
245                 /* check if SMB session is bad because we are setting it up */
246                 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && 
247                         (in_buf->Command != SMB_COM_NEGOTIATE)) {
248                         rc = -EAGAIN;
249                         goto out_unlock;
250                 } /* else ok - we are setting up session */
251         }
252         midQ = AllocMidQEntry(in_buf, ses);
253         if (midQ == NULL) {
254                 up(&ses->server->tcpSem);
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 -ENOMEM;
261         }
262
263         if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) {
264                 up(&ses->server->tcpSem);
265                 cERROR(1,
266                        ("Illegal length, greater than maximum frame, %d ",
267                         in_buf->smb_buf_length));
268                 DeleteMidQEntry(midQ);
269                 /* If not lock req, update # of requests on wire to server */
270                 if(long_op < 3) {
271                         atomic_dec(&ses->server->inFlight); 
272                         wake_up(&ses->server->request_q);
273                 }
274                 return -EIO;
275         }
276
277         rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
278
279         midQ->midState = MID_REQUEST_SUBMITTED;
280         rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
281                       (struct sockaddr *) &(ses->server->addr.sockAddr));
282         if(rc < 0) {
283                 DeleteMidQEntry(midQ);
284                 up(&ses->server->tcpSem);
285                 /* If not lock req, update # of requests on wire to server */
286                 if(long_op < 3) {
287                         atomic_dec(&ses->server->inFlight); 
288                         wake_up(&ses->server->request_q);
289                 }
290                 return rc;
291         } else
292                 up(&ses->server->tcpSem);
293         if (long_op == -1)
294                 goto cifs_no_response_exit;
295         else if (long_op == 2) /* writes past end of file can take looooong time */
296                 timeout = 300 * HZ;
297         else if (long_op == 1)
298                 timeout = 45 * HZ; /* should be greater than 
299                         servers oplock break timeout (about 43 seconds) */
300         else if (long_op > 2) {
301                 timeout = MAX_SCHEDULE_TIMEOUT;
302         } else
303                 timeout = 15 * HZ;
304         /* wait for 15 seconds or until woken up due to response arriving or 
305            due to last connection to this server being unmounted */
306         if (signal_pending(current)) {
307                 /* if signal pending do not hold up user for full smb timeout
308                 but we still give response a change to complete */
309                 timeout = 2 * HZ;
310                 
311         }   
312
313         /* No user interrupts in wait - wreaks havoc with performance */
314         if(timeout != MAX_SCHEDULE_TIMEOUT) {
315                 timeout += jiffies;
316                 wait_event(ses->server->response_q,
317                         (midQ->midState & MID_RESPONSE_RECEIVED) || 
318                         time_after(jiffies, timeout) || 
319                         ((ses->server->tcpStatus != CifsGood) &&
320                          (ses->server->tcpStatus != CifsNew)));
321         } else {
322                 wait_event(ses->server->response_q,
323                         (midQ->midState & MID_RESPONSE_RECEIVED) || 
324                         ((ses->server->tcpStatus != CifsGood) &&
325                          (ses->server->tcpStatus != CifsNew)));
326         }
327
328         spin_lock(&GlobalMid_Lock);
329         if (midQ->resp_buf) {
330                 spin_unlock(&GlobalMid_Lock);
331                 receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf);
332         } else {
333                 cERROR(1,("No response buffer"));
334                 if(midQ->midState == MID_REQUEST_SUBMITTED) {
335                         if(ses->server->tcpStatus == CifsExiting)
336                                 rc = -EHOSTDOWN;
337                         else {
338                                 ses->server->tcpStatus = CifsNeedReconnect;
339                                 midQ->midState = MID_RETRY_NEEDED;
340                         }
341                 }
342
343                 if (rc != -EHOSTDOWN) {
344                         if(midQ->midState == MID_RETRY_NEEDED) {
345                                 rc = -EAGAIN;
346                                 cFYI(1,("marking request for retry"));
347                         } else {
348                                 rc = -EIO;
349                         }
350                 }
351                 spin_unlock(&GlobalMid_Lock);
352                 DeleteMidQEntry(midQ);
353                 /* If not lock req, update # of requests on wire to server */
354                 if(long_op < 3) {
355                         atomic_dec(&ses->server->inFlight); 
356                         wake_up(&ses->server->request_q);
357                 }
358                 return rc;
359         }
360   
361         if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) {
362                 cERROR(1,
363                        ("Frame too large received.  Length: %d  Xid: %d",
364                         receive_len, xid));
365                 rc = -EIO;
366         } else {                /* rcvd frame is ok */
367
368                 if (midQ->resp_buf && out_buf
369                     && (midQ->midState == MID_RESPONSE_RECEIVED)) {
370                         out_buf->smb_buf_length = receive_len;
371                         memcpy((char *)out_buf + 4,
372                                (char *)midQ->resp_buf + 4,
373                                receive_len);
374
375                         dump_smb(out_buf, 92);
376                         /* convert the length into a more usable form */
377                         if((receive_len > 24) &&
378                            (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) {
379                                 rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */
380                                 if(rc)
381                                         cFYI(1,("Unexpected signature received from server"));
382                         }
383
384                         *pbytes_returned = out_buf->smb_buf_length;
385
386                         /* BB special case reconnect tid and reconnect uid here? */
387                         rc = map_smb_to_linux_error(out_buf);
388
389                         /* convert ByteCount if necessary */
390                         if (receive_len >=
391                             sizeof (struct smb_hdr) -
392                             4 /* do not count RFC1001 header */  +
393                             (2 * out_buf->WordCount) + 2 /* bcc */ )
394                                 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
395                 } else {
396                         rc = -EIO;
397                         cFYI(1,("Bad MID state? "));
398                 }
399         }
400 cifs_no_response_exit:
401         DeleteMidQEntry(midQ);
402
403         if(long_op < 3) {
404                 atomic_dec(&ses->server->inFlight); 
405                 wake_up(&ses->server->request_q);
406         }
407
408         return rc;
409
410 out_unlock:
411         up(&ses->server->tcpSem);
412         /* If not lock req, update # of requests on wire to server */
413         if(long_op < 3) {
414                 atomic_dec(&ses->server->inFlight); 
415                 wake_up(&ses->server->request_q);
416         }
417
418         return rc;
419 }