This commit was manufactured by cvs2svn to create tag
[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         struct msghdr smb_msg;
124         struct iovec iov;
125         mm_segment_t temp_fs;
126
127         if(ssocket == NULL)
128                 return -ENOTSOCK; /* BB eventually add reconnect code here */
129 /*  ssocket->sk->allocation = GFP_BUFFER; *//* BB is this spurious? */
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                         set_current_state(TASK_INTERRUPTIBLE);
156                         schedule_timeout(HZ/2);
157                         continue;
158                 }
159                 if (rc < 0) 
160                         break;
161                 iov.iov_base += rc;
162                 iov.iov_len -= rc;
163         }
164         set_fs(temp_fs);
165
166         if (rc < 0) {
167                 cERROR(1,("Error %d sending data on socket to server.", rc));
168         } else {
169                 rc = 0;
170         }
171
172         return rc;
173 }
174
175 int
176 SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
177             struct smb_hdr *in_buf, struct smb_hdr *out_buf,
178             int *pbytes_returned, const int long_op)
179 {
180         int rc = 0;
181         unsigned int receive_len;
182         long timeout;
183         struct mid_q_entry *midQ;
184
185         if (ses == NULL) {
186                 cERROR(1,("Null smb session"));
187                 return -EIO;
188         }
189         if(ses->server == NULL) {
190                 cERROR(1,("Null tcp session"));
191                 return -EIO;
192         }
193
194         /* Ensure that we do not send more than 50 overlapping requests 
195                 to the same server. We may make this configurable later or
196                 use ses->maxReq */
197
198         /* can not count locking commands against the total since
199                 they are allowed to block on server */
200         if(long_op < 3) {
201                 /* update # of requests on the wire to this server */
202                 atomic_inc(&ses->server->inFlight); 
203         }
204  
205         if(atomic_read(&ses->server->inFlight) > CIFS_MAX_REQ) {
206                 wait_event(ses->server->request_q,atomic_read(&ses->server->inFlight) <= CIFS_MAX_REQ);
207         }
208
209         /* make sure that we sign in the same order that we send on this socket 
210                 and avoid races inside tcp sendmsg code that could cause corruption
211                 of smb data */
212
213         down(&ses->server->tcpSem); 
214
215         if (ses->server->tcpStatus == CifsExiting) {
216                 rc = -ENOENT;
217                 goto out_unlock;
218         } else if (ses->server->tcpStatus == CifsNeedReconnect) {
219                 cFYI(1,("tcp session dead - return to caller to retry"));
220                 rc = -EAGAIN;
221                 goto out_unlock;
222         } else if (ses->status != CifsGood) {
223                 /* check if SMB session is bad because we are setting it up */
224                 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && 
225                         (in_buf->Command != SMB_COM_NEGOTIATE)) {
226                         rc = -EAGAIN;
227                         goto out_unlock;
228                 } /* else ok - we are setting up session */
229         }
230         midQ = AllocMidQEntry(in_buf, ses);
231         if (midQ == NULL) {
232                 up(&ses->server->tcpSem);
233                 /* If not lock req, update # of requests on wire to server */
234                 if(long_op < 3) {
235                         atomic_dec(&ses->server->inFlight); 
236                         wake_up(&ses->server->request_q);
237                 }
238                 return -ENOMEM;
239         }
240
241         if (in_buf->smb_buf_length > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE - 4) {
242                 up(&ses->server->tcpSem);
243                 cERROR(1,
244                        ("Illegal length, greater than maximum frame, %d ",
245                         in_buf->smb_buf_length));
246                 DeleteMidQEntry(midQ);
247                 /* If not lock req, update # of requests on wire to server */
248                 if(long_op < 3) {
249                         atomic_dec(&ses->server->inFlight); 
250                         wake_up(&ses->server->request_q);
251                 }
252                 return -EIO;
253         }
254
255         if (in_buf->smb_buf_length > 12)
256                 in_buf->Flags2 = cpu_to_le16(in_buf->Flags2);
257         
258         rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
259
260         midQ->midState = MID_REQUEST_SUBMITTED;
261         rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
262                       (struct sockaddr *) &(ses->server->addr.sockAddr));
263         up(&ses->server->tcpSem);
264         if (long_op == -1)
265                 goto cifs_no_response_exit;
266         else if (long_op == 2) /* writes past end of file can take looooong time */
267                 timeout = 300 * HZ;
268         else if (long_op == 1)
269                 timeout = 45 * HZ; /* should be greater than 
270                         servers oplock break timeout (about 43 seconds) */
271         else if (long_op > 2) {
272                 timeout = MAX_SCHEDULE_TIMEOUT;
273         } else
274                 timeout = 15 * HZ;
275         /* wait for 15 seconds or until woken up due to response arriving or 
276            due to last connection to this server being unmounted */
277         if (signal_pending(current)) {
278                 /* if signal pending do not hold up user for full smb timeout
279                 but we still give response a change to complete */
280                 if(midQ->midState & MID_REQUEST_SUBMITTED) {
281                         set_current_state(TASK_UNINTERRUPTIBLE);
282                         timeout = sleep_on_timeout(&ses->server->response_q,2 * HZ);
283                 }
284         } else { /* using normal timeout */
285                 /* timeout = wait_event_interruptible_timeout(ses->server->response_q,
286                         (midQ->midState & MID_RESPONSE_RECEIVED) || 
287                         ((ses->server->tcpStatus != CifsGood) &&
288                          (ses->server->tcpStatus != CifsNew)),
289                         timeout); */ 
290                 /* Can not allow user interrupts- wreaks havoc with performance */
291                 if(midQ->midState & MID_REQUEST_SUBMITTED) {
292                         set_current_state(TASK_UNINTERRUPTIBLE);
293                         timeout = sleep_on_timeout(&ses->server->response_q,timeout);
294                 }
295         }
296     
297         spin_lock(&GlobalMid_Lock);
298         if (midQ->resp_buf) {
299                 spin_unlock(&GlobalMid_Lock);
300                 receive_len = be32_to_cpu(midQ->resp_buf->smb_buf_length);
301         } else {
302                 cERROR(1,("No response buffer"));
303                 if(midQ->midState == MID_REQUEST_SUBMITTED) {
304                         if(ses->server->tcpStatus == CifsExiting)
305                                 rc = -EHOSTDOWN;
306                         else {
307                                 ses->server->tcpStatus = CifsNeedReconnect;
308                                 midQ->midState = MID_RETRY_NEEDED;
309                         }
310                 }
311
312                 if (rc != -EHOSTDOWN) {
313                         if(midQ->midState == MID_RETRY_NEEDED) {
314                                 rc = -EAGAIN;
315                                 cFYI(1,("marking request for retry"));
316                         } else {
317                                 rc = -EIO;
318                         }
319                 }
320                 spin_unlock(&GlobalMid_Lock);
321                 DeleteMidQEntry(midQ);
322                 /* If not lock req, update # of requests on wire to server */
323                 if(long_op < 3) {
324                         atomic_dec(&ses->server->inFlight); 
325                         wake_up(&ses->server->request_q);
326                 }
327                 return rc;
328         }
329   
330         if (receive_len > CIFS_MAX_MSGSIZE + MAX_CIFS_HDR_SIZE) {
331                 cERROR(1,
332                        ("Frame too large received.  Length: %d  Xid: %d",
333                         receive_len, xid));
334                 rc = -EIO;
335         } else {                /* rcvd frame is ok */
336
337                 if (midQ->resp_buf && out_buf
338                     && (midQ->midState == MID_RESPONSE_RECEIVED)) {
339                         memcpy(out_buf, midQ->resp_buf,
340                                receive_len +
341                                4 /* include 4 byte RFC1001 header */ );
342
343                         dump_smb(out_buf, 92);
344                         /* convert the length into a more usable form */
345                         out_buf->smb_buf_length =
346                             be32_to_cpu(out_buf->smb_buf_length);
347                         if((out_buf->smb_buf_length > 24) &&
348                            (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) {
349                                 rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */
350                                 if(rc)
351                                         cFYI(1,("Unexpected signature received from server"));
352                         }
353
354                         if (out_buf->smb_buf_length > 12)
355                                 out_buf->Flags2 = le16_to_cpu(out_buf->Flags2);
356                         if (out_buf->smb_buf_length > 28)
357                                 out_buf->Pid = le16_to_cpu(out_buf->Pid);
358                         if (out_buf->smb_buf_length > 28)
359                                 out_buf->PidHigh =
360                                     le16_to_cpu(out_buf->PidHigh);
361
362                         *pbytes_returned = out_buf->smb_buf_length;
363
364                         /* BB special case reconnect tid and reconnect uid here? */
365                         rc = map_smb_to_linux_error(out_buf);
366
367                         /* convert ByteCount if necessary */
368                         if (receive_len >=
369                             sizeof (struct smb_hdr) -
370                             4 /* do not count RFC1001 header */  +
371                             (2 * out_buf->WordCount) + 2 /* bcc */ )
372                                 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
373                 } else {
374                         rc = -EIO;
375                         cFYI(1,("Bad MID state? "));
376                 }
377         }
378 cifs_no_response_exit:
379         DeleteMidQEntry(midQ);
380
381         if(long_op < 3) {
382                 atomic_dec(&ses->server->inFlight); 
383                 wake_up(&ses->server->request_q);
384         }
385
386         return rc;
387
388 out_unlock:
389         up(&ses->server->tcpSem);
390         /* If not lock req, update # of requests on wire to server */
391         if(long_op < 3) {
392                 atomic_dec(&ses->server->inFlight); 
393                 wake_up(&ses->server->request_q);
394         }
395
396         return rc;
397 }