diff options
author | Steve French <sfrench@us.ibm.com> | 2005-06-13 14:24:43 -0400 |
---|---|---|
committer | Steve French <sfrench@us.ibm.com> | 2005-06-13 14:24:43 -0400 |
commit | d6e04ae64c6b06ef76a5d4fb49106b393b7fa50a (patch) | |
tree | 0ae0d4e7c94ccbba95e55d7512eb628d845eff20 /fs/cifs/transport.c | |
parent | 2830077f7ae93ef2f7a312e3e489110963612e77 (diff) |
[CIFS] CIFS writepage improvements - eliminate double copy
Signed-off-by: Steve French (sfrench@us.ibm.com)
Diffstat (limited to 'fs/cifs/transport.c')
-rw-r--r-- | fs/cifs/transport.c | 223 |
1 files changed, 173 insertions, 50 deletions
diff --git a/fs/cifs/transport.c b/fs/cifs/transport.c index 0046c219833d..04f4af07fdd4 100644 --- a/fs/cifs/transport.c +++ b/fs/cifs/transport.c | |||
@@ -49,7 +49,8 @@ AllocMidQEntry(struct smb_hdr *smb_buffer, struct cifsSesInfo *ses) | |||
49 | return NULL; | 49 | return NULL; |
50 | } | 50 | } |
51 | 51 | ||
52 | temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp,SLAB_KERNEL | SLAB_NOFS); | 52 | temp = (struct mid_q_entry *) mempool_alloc(cifs_mid_poolp, |
53 | SLAB_KERNEL | SLAB_NOFS); | ||
53 | if (temp == NULL) | 54 | if (temp == NULL) |
54 | return temp; | 55 | return temp; |
55 | else { | 56 | else { |
@@ -179,27 +180,24 @@ smb_send(struct socket *ssocket, struct smb_hdr *smb_buffer, | |||
179 | return rc; | 180 | return rc; |
180 | } | 181 | } |
181 | 182 | ||
182 | #ifdef CIFS_EXPERIMENTAL | 183 | #ifdef CONFIG_CIFS_EXPERIMENTAL |
183 | /* BB finish off this function, adding support for writing set of pages as iovec */ | 184 | static int |
184 | /* and also adding support for operations that need to parse the response smb */ | 185 | smb_send2(struct socket *ssocket, struct smb_hdr *smb_buffer, |
185 | 186 | unsigned int smb_hdr_length, const char * data, unsigned int datalen, | |
186 | int | 187 | struct sockaddr *sin) |
187 | smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, | ||
188 | unsigned int smb_buf_length, struct kvec * write_vector | ||
189 | /* page list */, struct sockaddr *sin) | ||
190 | { | 188 | { |
191 | int rc = 0; | 189 | int rc = 0; |
192 | int i = 0; | 190 | int i = 0; |
193 | struct msghdr smb_msg; | 191 | struct msghdr smb_msg; |
194 | number_of_pages += 1; /* account for SMB header */ | 192 | struct kvec iov[2]; |
195 | struct kvec * piov = kmalloc(number_of_pages * sizeof(struct kvec)); | 193 | unsigned len = smb_hdr_length + 4; |
196 | unsigned len = smb_buf_length + 4; | 194 | |
197 | |||
198 | if(ssocket == NULL) | 195 | if(ssocket == NULL) |
199 | return -ENOTSOCK; /* BB eventually add reconnect code here */ | 196 | return -ENOTSOCK; /* BB eventually add reconnect code here */ |
200 | iov.iov_base = smb_buffer; | 197 | iov[0].iov_base = smb_buffer; |
201 | iov.iov_len = len; | 198 | iov[0].iov_len = len; |
202 | 199 | iov[1].iov_base = data; | |
200 | iov[2].iov_len = datalen; | ||
203 | smb_msg.msg_name = sin; | 201 | smb_msg.msg_name = sin; |
204 | smb_msg.msg_namelen = sizeof (struct sockaddr); | 202 | smb_msg.msg_namelen = sizeof (struct sockaddr); |
205 | smb_msg.msg_control = NULL; | 203 | smb_msg.msg_control = NULL; |
@@ -212,12 +210,11 @@ smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, | |||
212 | Flags2 is converted in SendReceive */ | 210 | Flags2 is converted in SendReceive */ |
213 | 211 | ||
214 | smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); | 212 | smb_buffer->smb_buf_length = cpu_to_be32(smb_buffer->smb_buf_length); |
215 | cFYI(1, ("Sending smb of length %d ", smb_buf_length)); | 213 | cFYI(1, ("Sending smb of length %d ", len + datalen)); |
216 | dump_smb(smb_buffer, len); | 214 | dump_smb(smb_buffer, len); |
217 | 215 | ||
218 | while (len > 0) { | 216 | while (len + datalen > 0) { |
219 | rc = kernel_sendmsg(ssocket, &smb_msg, &iov, number_of_pages, | 217 | rc = kernel_sendmsg(ssocket, &smb_msg, iov, 2, len); |
220 | len); | ||
221 | if ((rc == -ENOSPC) || (rc == -EAGAIN)) { | 218 | if ((rc == -ENOSPC) || (rc == -EAGAIN)) { |
222 | i++; | 219 | i++; |
223 | if(i > 60) { | 220 | if(i > 60) { |
@@ -232,9 +229,22 @@ smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, | |||
232 | } | 229 | } |
233 | if (rc < 0) | 230 | if (rc < 0) |
234 | break; | 231 | break; |
235 | iov.iov_base += rc; | 232 | if(iov[0].iov_len > 0) { |
236 | iov.iov_len -= rc; | 233 | if(rc >= len) { |
237 | len -= rc; | 234 | iov[0].iov_len = 0; |
235 | rc -= len; | ||
236 | } else { /* some of hdr was not sent */ | ||
237 | len -= rc; | ||
238 | iov[0].iov_len -= rc; | ||
239 | iov[0].iov_base += rc; | ||
240 | continue; | ||
241 | } | ||
242 | } | ||
243 | if((iov[0].iov_len == 0) && (rc > 0)){ | ||
244 | iov[1].iov_base += rc; | ||
245 | iov[1].iov_len -= rc; | ||
246 | datalen -= rc; | ||
247 | } | ||
238 | } | 248 | } |
239 | 249 | ||
240 | if (rc < 0) { | 250 | if (rc < 0) { |
@@ -246,14 +256,15 @@ smb_sendv(struct socket *ssocket, struct smb_hdr *smb_buffer, | |||
246 | return rc; | 256 | return rc; |
247 | } | 257 | } |
248 | 258 | ||
249 | |||
250 | int | 259 | int |
251 | CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, | 260 | SendReceive2(const unsigned int xid, struct cifsSesInfo *ses, |
252 | struct smb_hdr *in_buf, struct kvec * write_vector /* page list */, int *pbytes_returned, const int long_op) | 261 | struct smb_hdr *in_buf, int hdrlen, const char * data, |
262 | int datalen, int *pbytes_returned, const int long_op) | ||
253 | { | 263 | { |
254 | int rc = 0; | 264 | int rc = 0; |
255 | unsigned long timeout = 15 * HZ; | 265 | unsigned int receive_len; |
256 | struct mid_q_entry *midQ = NULL; | 266 | unsigned long timeout; |
267 | struct mid_q_entry *midQ; | ||
257 | 268 | ||
258 | if (ses == NULL) { | 269 | if (ses == NULL) { |
259 | cERROR(1,("Null smb session")); | 270 | cERROR(1,("Null smb session")); |
@@ -263,14 +274,8 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, | |||
263 | cERROR(1,("Null tcp session")); | 274 | cERROR(1,("Null tcp session")); |
264 | return -EIO; | 275 | return -EIO; |
265 | } | 276 | } |
266 | if(pbytes_returned == NULL) | ||
267 | return -EIO; | ||
268 | else | ||
269 | *pbytes_returned = 0; | ||
270 | 277 | ||
271 | 278 | if(ses->server->tcpStatus == CifsExiting) | |
272 | |||
273 | if(ses->server->tcpStatus == CIFS_EXITING) | ||
274 | return -ENOENT; | 279 | return -ENOENT; |
275 | 280 | ||
276 | /* Ensure that we do not send more than 50 overlapping requests | 281 | /* Ensure that we do not send more than 50 overlapping requests |
@@ -282,7 +287,8 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, | |||
282 | } else { | 287 | } else { |
283 | spin_lock(&GlobalMid_Lock); | 288 | spin_lock(&GlobalMid_Lock); |
284 | while(1) { | 289 | while(1) { |
285 | if(atomic_read(&ses->server->inFlight) >= cifs_max_pending){ | 290 | if(atomic_read(&ses->server->inFlight) >= |
291 | cifs_max_pending){ | ||
286 | spin_unlock(&GlobalMid_Lock); | 292 | spin_unlock(&GlobalMid_Lock); |
287 | wait_event(ses->server->request_q, | 293 | wait_event(ses->server->request_q, |
288 | atomic_read(&ses->server->inFlight) | 294 | atomic_read(&ses->server->inFlight) |
@@ -314,17 +320,17 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, | |||
314 | 320 | ||
315 | if (ses->server->tcpStatus == CifsExiting) { | 321 | if (ses->server->tcpStatus == CifsExiting) { |
316 | rc = -ENOENT; | 322 | rc = -ENOENT; |
317 | goto cifs_out_label; | 323 | goto out_unlock2; |
318 | } else if (ses->server->tcpStatus == CifsNeedReconnect) { | 324 | } else if (ses->server->tcpStatus == CifsNeedReconnect) { |
319 | cFYI(1,("tcp session dead - return to caller to retry")); | 325 | cFYI(1,("tcp session dead - return to caller to retry")); |
320 | rc = -EAGAIN; | 326 | rc = -EAGAIN; |
321 | goto cifs_out_label; | 327 | goto out_unlock2; |
322 | } else if (ses->status != CifsGood) { | 328 | } else if (ses->status != CifsGood) { |
323 | /* check if SMB session is bad because we are setting it up */ | 329 | /* check if SMB session is bad because we are setting it up */ |
324 | if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && | 330 | if((in_buf->Command != SMB_COM_SESSION_SETUP_ANDX) && |
325 | (in_buf->Command != SMB_COM_NEGOTIATE)) { | 331 | (in_buf->Command != SMB_COM_NEGOTIATE)) { |
326 | rc = -EAGAIN; | 332 | rc = -EAGAIN; |
327 | goto cifs_out_label; | 333 | goto out_unlock2; |
328 | } /* else ok - we are setting up session */ | 334 | } /* else ok - we are setting up session */ |
329 | } | 335 | } |
330 | midQ = AllocMidQEntry(in_buf, ses); | 336 | midQ = AllocMidQEntry(in_buf, ses); |
@@ -352,13 +358,12 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, | |||
352 | return -EIO; | 358 | return -EIO; |
353 | } | 359 | } |
354 | 360 | ||
355 | /* BB can we sign efficiently in this path? */ | 361 | /* BB FIXME */ |
356 | rc = cifs_sign_smb(in_buf, ses->server, &midQ->sequence_number); | 362 | /* rc = cifs_sign_smb2(in_buf, data, ses->server, &midQ->sequence_number); */ |
357 | 363 | ||
358 | midQ->midState = MID_REQUEST_SUBMITTED; | 364 | midQ->midState = MID_REQUEST_SUBMITTED; |
359 | /* rc = smb_sendv(ses->server->ssocket, in_buf, in_buf->smb_buf_length, | 365 | rc = smb_send2(ses->server->ssocket, in_buf, hdrlen, data, datalen, |
360 | piovec, | 366 | (struct sockaddr *) &(ses->server->addr.sockAddr)); |
361 | (struct sockaddr *) &(ses->server->addr.sockAddr));*/ | ||
362 | if(rc < 0) { | 367 | if(rc < 0) { |
363 | DeleteMidQEntry(midQ); | 368 | DeleteMidQEntry(midQ); |
364 | up(&ses->server->tcpSem); | 369 | up(&ses->server->tcpSem); |
@@ -370,19 +375,137 @@ CIFSSendRcv(const unsigned int xid, struct cifsSesInfo *ses, | |||
370 | return rc; | 375 | return rc; |
371 | } else | 376 | } else |
372 | up(&ses->server->tcpSem); | 377 | up(&ses->server->tcpSem); |
373 | cifs_out_label: | 378 | if (long_op == -1) |
374 | if(midQ) | 379 | goto cifs_no_response_exit2; |
375 | DeleteMidQEntry(midQ); | 380 | else if (long_op == 2) /* writes past end of file can take loong time */ |
376 | 381 | timeout = 300 * HZ; | |
382 | else if (long_op == 1) | ||
383 | timeout = 45 * HZ; /* should be greater than | ||
384 | servers oplock break timeout (about 43 seconds) */ | ||
385 | else if (long_op > 2) { | ||
386 | timeout = MAX_SCHEDULE_TIMEOUT; | ||
387 | } else | ||
388 | timeout = 15 * HZ; | ||
389 | /* wait for 15 seconds or until woken up due to response arriving or | ||
390 | due to last connection to this server being unmounted */ | ||
391 | if (signal_pending(current)) { | ||
392 | /* if signal pending do not hold up user for full smb timeout | ||
393 | but we still give response a change to complete */ | ||
394 | timeout = 2 * HZ; | ||
395 | } | ||
396 | |||
397 | /* No user interrupts in wait - wreaks havoc with performance */ | ||
398 | if(timeout != MAX_SCHEDULE_TIMEOUT) { | ||
399 | timeout += jiffies; | ||
400 | wait_event(ses->server->response_q, | ||
401 | (!(midQ->midState & MID_REQUEST_SUBMITTED)) || | ||
402 | time_after(jiffies, timeout) || | ||
403 | ((ses->server->tcpStatus != CifsGood) && | ||
404 | (ses->server->tcpStatus != CifsNew))); | ||
405 | } else { | ||
406 | wait_event(ses->server->response_q, | ||
407 | (!(midQ->midState & MID_REQUEST_SUBMITTED)) || | ||
408 | ((ses->server->tcpStatus != CifsGood) && | ||
409 | (ses->server->tcpStatus != CifsNew))); | ||
410 | } | ||
411 | |||
412 | spin_lock(&GlobalMid_Lock); | ||
413 | if (midQ->resp_buf) { | ||
414 | spin_unlock(&GlobalMid_Lock); | ||
415 | receive_len = be32_to_cpu(*(__be32 *)midQ->resp_buf); | ||
416 | } else { | ||
417 | cERROR(1,("No response buffer")); | ||
418 | if(midQ->midState == MID_REQUEST_SUBMITTED) { | ||
419 | if(ses->server->tcpStatus == CifsExiting) | ||
420 | rc = -EHOSTDOWN; | ||
421 | else { | ||
422 | ses->server->tcpStatus = CifsNeedReconnect; | ||
423 | midQ->midState = MID_RETRY_NEEDED; | ||
424 | } | ||
425 | } | ||
426 | |||
427 | if (rc != -EHOSTDOWN) { | ||
428 | if(midQ->midState == MID_RETRY_NEEDED) { | ||
429 | rc = -EAGAIN; | ||
430 | cFYI(1,("marking request for retry")); | ||
431 | } else { | ||
432 | rc = -EIO; | ||
433 | } | ||
434 | } | ||
435 | spin_unlock(&GlobalMid_Lock); | ||
436 | DeleteMidQEntry(midQ); | ||
437 | /* If not lock req, update # of requests on wire to server */ | ||
438 | if(long_op < 3) { | ||
439 | atomic_dec(&ses->server->inFlight); | ||
440 | wake_up(&ses->server->request_q); | ||
441 | } | ||
442 | return rc; | ||
443 | } | ||
444 | |||
445 | if (receive_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) { | ||
446 | cERROR(1, ("Frame too large received. Length: %d Xid: %d", | ||
447 | receive_len, xid)); | ||
448 | rc = -EIO; | ||
449 | } else { /* rcvd frame is ok */ | ||
450 | |||
451 | if (midQ->resp_buf && | ||
452 | (midQ->midState == MID_RESPONSE_RECEIVED)) { | ||
453 | in_buf->smb_buf_length = receive_len; | ||
454 | /* BB verify that length would not overrun small buf */ | ||
455 | memcpy((char *)in_buf + 4, | ||
456 | (char *)midQ->resp_buf + 4, | ||
457 | receive_len); | ||
458 | |||
459 | dump_smb(in_buf, 80); | ||
460 | /* convert the length into a more usable form */ | ||
461 | if((receive_len > 24) && | ||
462 | (ses->server->secMode & (SECMODE_SIGN_REQUIRED | | ||
463 | SECMODE_SIGN_ENABLED))) { | ||
464 | rc = cifs_verify_signature(in_buf, | ||
465 | ses->server->mac_signing_key, | ||
466 | midQ->sequence_number+1); | ||
467 | if(rc) { | ||
468 | cERROR(1,("Unexpected SMB signature")); | ||
469 | /* BB FIXME add code to kill session */ | ||
470 | } | ||
471 | } | ||
472 | |||
473 | *pbytes_returned = in_buf->smb_buf_length; | ||
474 | |||
475 | /* BB special case reconnect tid and uid here? */ | ||
476 | rc = map_smb_to_linux_error(in_buf); | ||
477 | |||
478 | /* convert ByteCount if necessary */ | ||
479 | if (receive_len >= | ||
480 | sizeof (struct smb_hdr) - | ||
481 | 4 /* do not count RFC1001 header */ + | ||
482 | (2 * in_buf->WordCount) + 2 /* bcc */ ) | ||
483 | BCC(in_buf) = le16_to_cpu(BCC(in_buf)); | ||
484 | } else { | ||
485 | rc = -EIO; | ||
486 | cFYI(1,("Bad MID state? ")); | ||
487 | } | ||
488 | } | ||
489 | cifs_no_response_exit2: | ||
490 | DeleteMidQEntry(midQ); | ||
491 | |||
377 | if(long_op < 3) { | 492 | if(long_op < 3) { |
378 | atomic_dec(&ses->server->inFlight); | 493 | atomic_dec(&ses->server->inFlight); |
379 | wake_up(&ses->server->request_q); | 494 | wake_up(&ses->server->request_q); |
380 | } | 495 | } |
381 | 496 | ||
382 | return rc; | 497 | return rc; |
383 | } | ||
384 | 498 | ||
499 | out_unlock2: | ||
500 | up(&ses->server->tcpSem); | ||
501 | /* If not lock req, update # of requests on wire to server */ | ||
502 | if(long_op < 3) { | ||
503 | atomic_dec(&ses->server->inFlight); | ||
504 | wake_up(&ses->server->request_q); | ||
505 | } | ||
385 | 506 | ||
507 | return rc; | ||
508 | } | ||
386 | #endif /* CIFS_EXPERIMENTAL */ | 509 | #endif /* CIFS_EXPERIMENTAL */ |
387 | 510 | ||
388 | int | 511 | int |