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 #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 int
183 CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses,
184 struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op)
185 {
186 int rc = 0;
187 unsigned long timeout = 15 * HZ;
188 struct mid_q_entry *midQ = NULL;
189
190 if (ses == NULL) {
191 cERROR(1,("Null smb session"));
192 return -EIO;
193 }
194 if(ses->server == NULL) {
195 cERROR(1,("Null tcp session"));
196 return -EIO;
197 }
198 if(pbytes_returned == NULL)
199 return -EIO;
200 else
201 *pbytes_returned = 0;
202
203
204
205 /* Ensure that we do not send more than 50 overlapping requests
206 to the same server. We may make this configurable later or
207 use ses->maxReq */
208 if(long_op == -1) {
209 /* oplock breaks must not be held up */
210 atomic_inc(&ses->server->inFlight);
211 } else {
212 spin_lock(&GlobalMid_Lock);
213 while(1) {
214 if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){
215 spin_unlock(&GlobalMid_Lock);
216 wait_event(ses->server->request_q,
217 atomic_read(&ses->server->inFlight)
218 < cifs_max_pending);
219 spin_lock(&GlobalMid_Lock);
220 } else {
221 if(ses->server->tcpStatus == CifsExiting) {
222 spin_unlock(&GlobalMid_Lock);
223 return -ENOENT;
224 }
225
226 /* can not count locking commands against total since
227 they are allowed to block on server */
228
229 if(long_op < 3) {
230 /* update # of requests on the wire to server */
231 atomic_inc(&ses->server->inFlight);
232 }
233 spin_unlock(&GlobalMid_Lock);
234 break;
235 }
236 }
237 }
238 /* make sure that we sign in the same order that we send on this socket
239 and avoid races inside tcp sendmsg code that could cause corruption
240 of smb data */
241
242 down(&ses->server->tcpSem);
243
244 if (ses->server->tcpStatus == CifsExiting) {
245 rc = -ENOENT;
246 goto cifs_out_label;
247 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
248 cFYI(1,("tcp session dead - return to caller to retry"));
249 rc = -EAGAIN;
250 goto cifs_out_label;
251 } else if (ses->status != CifsGood) {
252 /* check if SMB session is bad because we are setting it up */
253 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
254 (in_buf->Command != SMB_COM_NEGOTIATE)) {
255 rc = -EAGAIN;
256 goto cifs_out_label;
257 } /* else ok - we are setting up session */
258 }
259 midQ = AllocMidQEntry(in_buf, ses);
260 if (midQ == NULL) {
261 up(&ses->server->tcpSem);
262 /* If not lock req, update # of requests on wire to server */
263 if(long_op < 3) {
264 atomic_dec(&ses->server->inFlight);
265 wake_up(&ses->server->request_q);
266 }
267 return -ENOMEM;
268 }
269
270 if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
271 up(&ses->server->tcpSem);
272 cERROR(1,
273 ("Illegal length, greater than maximum frame, %d ",
274 in_buf->smb_buf_length));
275 DeleteMidQEntry(midQ);
276 /* If not lock req, update # of requests on wire to server */
277 if(long_op < 3) {
278 atomic_dec(&ses->server->inFlight);
279 wake_up(&ses->server->request_q);
280 }
281 return -EIO;
282 }
283
284 /* BB can we sign efficiently in this path? */
285 rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
286
287 midQ->midState = MID_REQUEST_SUBMITTED;
288 /* rc = smb_send2(ses->server->ssocket, in_buf, in_buf->smb_buf_length, piovec,
289 (struct sockaddr *) &(ses->server->addr.sockAddr));*/
290 if(rc < 0) {
291 DeleteMidQEntry(midQ);
292 up(&ses->server->tcpSem);
293 /* If not lock req, update # of requests on wire to server */
294 if(long_op < 3) {
295 atomic_dec(&ses->server->inFlight);
296 wake_up(&ses->server->request_q);
297 }
298 return rc;
299 } else
300 up(&ses->server->tcpSem);
301 cifs_out_label:
302 if(midQ)
303 DeleteMidQEntry(midQ);
304
305 if(long_op < 3) {
306 atomic_dec(&ses->server->inFlight);
307 wake_up(&ses->server->request_q);
308 }
309
310 return rc;
311 }
312
313
314 #endif /* CIFS_EXPERIMENTAL */
315
316 int
317 SendReceive(const unsigned int xid, struct cifsSesInfo *ses,
318 struct smb_hdr *in_buf, struct smb_hdr *out_buf,
319 int *pbytes_returned, const int long_op)
320 {
321 int rc = 0;
322 unsigned int receive_len;
323 unsigned long timeout;
324 struct mid_q_entry *midQ;
325
326 if (ses == NULL) {
327 cERROR(1,("Null smb session"));
328 return -EIO;
329 }
330 if(ses->server == NULL) {
331 cERROR(1,("Null tcp session"));
332 return -EIO;
333 }
334
335 /* Ensure that we do not send more than 50 overlapping requests
336 to the same server. We may make this configurable later or
337 use ses->maxReq */
338 if(long_op == -1) {
339 /* oplock breaks must not be held up */
340 atomic_inc(&ses->server->inFlight);
341 } else {
342 spin_lock(&GlobalMid_Lock);
343 while(1) {
344 if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){
345 spin_unlock(&GlobalMid_Lock);
346 wait_event(ses->server->request_q,
347 atomic_read(&ses->server->inFlight)
348 < cifs_max_pending);
349 spin_lock(&GlobalMid_Lock);
350 } else {
351 if(ses->server->tcpStatus == CifsExiting) {
352 spin_unlock(&GlobalMid_Lock);
353 return -ENOENT;
354 }
355
356 /* can not count locking commands against total since
357 they are allowed to block on server */
358
359 if(long_op < 3) {
360 /* update # of requests on the wire to server */
361 atomic_inc(&ses->server->inFlight);
362 }
363 spin_unlock(&GlobalMid_Lock);
364 break;
365 }
366 }
367 }
368 /* make sure that we sign in the same order that we send on this socket
369 and avoid races inside tcp sendmsg code that could cause corruption
370 of smb data */
371
372 down(&ses->server->tcpSem);
373
374 if (ses->server->tcpStatus == CifsExiting) {
375 rc = -ENOENT;
376 goto out_unlock;
377 } else if (ses->server->tcpStatus == CifsNeedReconnect) {
378 cFYI(1,("tcp session dead - return to caller to retry"));
379 rc = -EAGAIN;
380 goto out_unlock;
381 } else if (ses->status != CifsGood) {
382 /* check if SMB session is bad because we are setting it up */
383 if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) &&
384 (in_buf->Command != SMB_COM_NEGOTIATE)) {
385 rc = -EAGAIN;
386 goto out_unlock;
387 } /* else ok - we are setting up session */
388 }
389 midQ = AllocMidQEntry(in_buf, ses);
390 if (midQ == NULL) {
391 up(&ses->server->tcpSem);
392 /* If not lock req, update # of requests on wire to server */
393 if(long_op < 3) {
394 atomic_dec(&ses->server->inFlight);
395 wake_up(&ses->server->request_q);
396 }
397 return -ENOMEM;
398 }
399
400 if (in_buf->smb_buf_length > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE - 4) {
401 up(&ses->server->tcpSem);
402 cERROR(1,
403 ("Illegal length, greater than maximum frame, %d ",
404 in_buf->smb_buf_length));
405 DeleteMidQEntry(midQ);
406 /* If not lock req, update # of requests on wire to server */
407 if(long_op < 3) {
408 atomic_dec(&ses->server->inFlight);
409 wake_up(&ses->server->request_q);
410 }
411 return -EIO;
412 }
413
414 rc = cifs_sign_smb(in_buf, ses, &midQ->sequence_number);
415
416 midQ->midState = MID_REQUEST_SUBMITTED;
417 rc = smb_send(ses->server->ssocket, in_buf, in_buf->smb_buf_length,
418 (struct sockaddr *) &(ses->server->addr.sockAddr));
419 if(rc < 0) {
420 DeleteMidQEntry(midQ);
421 up(&ses->server->tcpSem);
422 /* If not lock req, update # of requests on wire to server */
423 if(long_op < 3) {
424 atomic_dec(&ses->server->inFlight);
425 wake_up(&ses->server->request_q);
426 }
427 return rc;
428 } else
429 up(&ses->server->tcpSem);
430 if (long_op == -1)
431 goto cifs_no_response_exit;
432 else if (long_op == 2) /* writes past end of file can take looooong time */
433 timeout = 300 * HZ;
434 else if (long_op == 1)
435 timeout = 45 * HZ; /* should be greater than
436 servers oplock break timeout (about 43 seconds) */
437 else if (long_op > 2) {
438 timeout = MAX_SCHEDULE_TIMEOUT;
439 } else
440 timeout = 15 * HZ;
441 /* wait for 15 seconds or until woken up due to response arriving or
442 due to last connection to this server being unmounted */
443 if (signal_pending(current)) {
444 /* if signal pending do not hold up user for full smb timeout
445 but we still give response a change to complete */
446 timeout = 2 * HZ;
447 }
448
449 /* No user interrupts in wait - wreaks havoc with performance */
450 if(timeout != MAX_SCHEDULE_TIMEOUT) {
451 timeout += jiffies;
452 wait_event(ses->server->response_q,
453 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
454 time_after(jiffies, timeout) ||
455 ((ses->server->tcpStatus != CifsGood) &&
456 (ses->server->tcpStatus != CifsNew)));
457 } else {
458 wait_event(ses->server->response_q,
459 (!(midQ->midState & MID_REQUEST_SUBMITTED)) ||
460 ((ses->server->tcpStatus != CifsGood) &&
461 (ses->server->tcpStatus != CifsNew)));
462 }
463
464 spin_lock(&GlobalMid_Lock);
465 if (midQ->resp_buf) {
466 spin_unlock(&GlobalMid_Lock);
467 receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf);
468 } else {
469 cERROR(1,("No response buffer"));
470 if(midQ->midState == MID_REQUEST_SUBMITTED) {
471 if(ses->server->tcpStatus == CifsExiting)
472 rc = -EHOSTDOWN;
473 else {
474 ses->server->tcpStatus = CifsNeedReconnect;
475 midQ->midState = MID_RETRY_NEEDED;
476 }
477 }
478
479 if (rc != -EHOSTDOWN) {
480 if(midQ->midState == MID_RETRY_NEEDED) {
481 rc = -EAGAIN;
482 cFYI(1,("marking request for retry"));
483 } else {
484 rc = -EIO;
485 }
486 }
487 spin_unlock(&GlobalMid_Lock);
488 DeleteMidQEntry(midQ);
489 /* If not lock req, update # of requests on wire to server */
490 if(long_op < 3) {
491 atomic_dec(&ses->server->inFlight);
492 wake_up(&ses->server->request_q);
493 }
494 return rc;
495 }
496
497 if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
498 cERROR(1,
499 ("Frame too large received. Length: %d Xid: %d",
500 receive_len, xid));
501 rc = -EIO;
502 } else { /* rcvd frame is ok */
503
504 if (midQ->resp_buf && out_buf
505 && (midQ->midState == MID_RESPONSE_RECEIVED)) {
506 out_buf->smb_buf_length = receive_len;
507 memcpy((char *)out_buf + 4,
508 (char *)midQ->resp_buf + 4,
509 receive_len);
510
511 dump_smb(out_buf, 92);
512 /* convert the length into a more usable form */
513 if((receive_len > 24) &&
514 (ses->server->secMode & (SECMODE_SIGN_REQUIRED | SECMODE_SIGN_ENABLED))) {
515 rc = cifs_verify_signature(out_buf, ses->mac_signing_key,midQ->sequence_number); /* BB fix BB */
516 if(rc)
517 cFYI(1,("Unexpected signature received from server"));
518 }
519
520 *pbytes_returned = out_buf->smb_buf_length;
521
522 /* BB special case reconnect tid and reconnect uid here? */
523 rc = map_smb_to_linux_error(out_buf);
524
525 /* convert ByteCount if necessary */
526 if (receive_len >=
527 sizeof (struct smb_hdr) -
528 4 /* do not count RFC1001 header */ +
529 (2 * out_buf->WordCount) + 2 /* bcc */ )
530 BCC(out_buf) = le16_to_cpu(BCC(out_buf));
531 } else {
532 rc = -EIO;
533 cFYI(1,("Bad MID state? "));
534 }
535 }
536 cifs_no_response_exit:
537 DeleteMidQEntry(midQ);
538
539 if(long_op < 3) {
540 atomic_dec(&ses->server->inFlight);
541 wake_up(&ses->server->request_q);
542 }
543
544 return rc;
545
546 out_unlock:
547 up(&ses->server->tcpSem);
548 /* If not lock req, update # of requests on wire to server */
549 if(long_op < 3) {
550 atomic_dec(&ses->server->inFlight);
551 wake_up(&ses->server->request_q);
552 }
553
554 return rc;
555 }
556
|
This page was automatically generated by the
LXR engine.
|