diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/sunrpc/xprtsock.c | 28 |
1 files changed, 21 insertions, 7 deletions
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 17c88928b7db..dd9d295813cf 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c | |||
| @@ -393,8 +393,10 @@ static int xs_send_kvec(struct socket *sock, struct sockaddr *addr, int addrlen, | |||
| 393 | return kernel_sendmsg(sock, &msg, NULL, 0, 0); | 393 | return kernel_sendmsg(sock, &msg, NULL, 0, 0); |
| 394 | } | 394 | } |
| 395 | 395 | ||
| 396 | static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned int base, int more) | 396 | static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned int base, int more, bool zerocopy) |
| 397 | { | 397 | { |
| 398 | ssize_t (*do_sendpage)(struct socket *sock, struct page *page, | ||
| 399 | int offset, size_t size, int flags); | ||
| 398 | struct page **ppage; | 400 | struct page **ppage; |
| 399 | unsigned int remainder; | 401 | unsigned int remainder; |
| 400 | int err, sent = 0; | 402 | int err, sent = 0; |
| @@ -403,6 +405,9 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i | |||
| 403 | base += xdr->page_base; | 405 | base += xdr->page_base; |
| 404 | ppage = xdr->pages + (base >> PAGE_SHIFT); | 406 | ppage = xdr->pages + (base >> PAGE_SHIFT); |
| 405 | base &= ~PAGE_MASK; | 407 | base &= ~PAGE_MASK; |
| 408 | do_sendpage = sock->ops->sendpage; | ||
| 409 | if (!zerocopy) | ||
| 410 | do_sendpage = sock_no_sendpage; | ||
| 406 | for(;;) { | 411 | for(;;) { |
| 407 | unsigned int len = min_t(unsigned int, PAGE_SIZE - base, remainder); | 412 | unsigned int len = min_t(unsigned int, PAGE_SIZE - base, remainder); |
| 408 | int flags = XS_SENDMSG_FLAGS; | 413 | int flags = XS_SENDMSG_FLAGS; |
| @@ -410,7 +415,7 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i | |||
| 410 | remainder -= len; | 415 | remainder -= len; |
| 411 | if (remainder != 0 || more) | 416 | if (remainder != 0 || more) |
| 412 | flags |= MSG_MORE; | 417 | flags |= MSG_MORE; |
| 413 | err = sock->ops->sendpage(sock, *ppage, base, len, flags); | 418 | err = do_sendpage(sock, *ppage, base, len, flags); |
| 414 | if (remainder == 0 || err != len) | 419 | if (remainder == 0 || err != len) |
| 415 | break; | 420 | break; |
| 416 | sent += err; | 421 | sent += err; |
| @@ -431,9 +436,10 @@ static int xs_send_pagedata(struct socket *sock, struct xdr_buf *xdr, unsigned i | |||
| 431 | * @addrlen: UDP only -- length of destination address | 436 | * @addrlen: UDP only -- length of destination address |
| 432 | * @xdr: buffer containing this request | 437 | * @xdr: buffer containing this request |
| 433 | * @base: starting position in the buffer | 438 | * @base: starting position in the buffer |
| 439 | * @zerocopy: true if it is safe to use sendpage() | ||
| 434 | * | 440 | * |
| 435 | */ | 441 | */ |
| 436 | static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base) | 442 | static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, struct xdr_buf *xdr, unsigned int base, bool zerocopy) |
| 437 | { | 443 | { |
| 438 | unsigned int remainder = xdr->len - base; | 444 | unsigned int remainder = xdr->len - base; |
| 439 | int err, sent = 0; | 445 | int err, sent = 0; |
| @@ -461,7 +467,7 @@ static int xs_sendpages(struct socket *sock, struct sockaddr *addr, int addrlen, | |||
| 461 | if (base < xdr->page_len) { | 467 | if (base < xdr->page_len) { |
| 462 | unsigned int len = xdr->page_len - base; | 468 | unsigned int len = xdr->page_len - base; |
| 463 | remainder -= len; | 469 | remainder -= len; |
| 464 | err = xs_send_pagedata(sock, xdr, base, remainder != 0); | 470 | err = xs_send_pagedata(sock, xdr, base, remainder != 0, zerocopy); |
| 465 | if (remainder == 0 || err != len) | 471 | if (remainder == 0 || err != len) |
| 466 | goto out; | 472 | goto out; |
| 467 | sent += err; | 473 | sent += err; |
| @@ -564,7 +570,7 @@ static int xs_local_send_request(struct rpc_task *task) | |||
| 564 | req->rq_svec->iov_base, req->rq_svec->iov_len); | 570 | req->rq_svec->iov_base, req->rq_svec->iov_len); |
| 565 | 571 | ||
| 566 | status = xs_sendpages(transport->sock, NULL, 0, | 572 | status = xs_sendpages(transport->sock, NULL, 0, |
| 567 | xdr, req->rq_bytes_sent); | 573 | xdr, req->rq_bytes_sent, true); |
| 568 | dprintk("RPC: %s(%u) = %d\n", | 574 | dprintk("RPC: %s(%u) = %d\n", |
| 569 | __func__, xdr->len - req->rq_bytes_sent, status); | 575 | __func__, xdr->len - req->rq_bytes_sent, status); |
| 570 | if (likely(status >= 0)) { | 576 | if (likely(status >= 0)) { |
| @@ -620,7 +626,7 @@ static int xs_udp_send_request(struct rpc_task *task) | |||
| 620 | status = xs_sendpages(transport->sock, | 626 | status = xs_sendpages(transport->sock, |
| 621 | xs_addr(xprt), | 627 | xs_addr(xprt), |
| 622 | xprt->addrlen, xdr, | 628 | xprt->addrlen, xdr, |
| 623 | req->rq_bytes_sent); | 629 | req->rq_bytes_sent, true); |
| 624 | 630 | ||
| 625 | dprintk("RPC: xs_udp_send_request(%u) = %d\n", | 631 | dprintk("RPC: xs_udp_send_request(%u) = %d\n", |
| 626 | xdr->len - req->rq_bytes_sent, status); | 632 | xdr->len - req->rq_bytes_sent, status); |
| @@ -693,6 +699,7 @@ static int xs_tcp_send_request(struct rpc_task *task) | |||
| 693 | struct rpc_xprt *xprt = req->rq_xprt; | 699 | struct rpc_xprt *xprt = req->rq_xprt; |
| 694 | struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); | 700 | struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt); |
| 695 | struct xdr_buf *xdr = &req->rq_snd_buf; | 701 | struct xdr_buf *xdr = &req->rq_snd_buf; |
| 702 | bool zerocopy = true; | ||
| 696 | int status; | 703 | int status; |
| 697 | 704 | ||
| 698 | xs_encode_stream_record_marker(&req->rq_snd_buf); | 705 | xs_encode_stream_record_marker(&req->rq_snd_buf); |
| @@ -700,13 +707,20 @@ static int xs_tcp_send_request(struct rpc_task *task) | |||
| 700 | xs_pktdump("packet data:", | 707 | xs_pktdump("packet data:", |
| 701 | req->rq_svec->iov_base, | 708 | req->rq_svec->iov_base, |
| 702 | req->rq_svec->iov_len); | 709 | req->rq_svec->iov_len); |
| 710 | /* Don't use zero copy if this is a resend. If the RPC call | ||
| 711 | * completes while the socket holds a reference to the pages, | ||
| 712 | * then we may end up resending corrupted data. | ||
| 713 | */ | ||
| 714 | if (task->tk_flags & RPC_TASK_SENT) | ||
| 715 | zerocopy = false; | ||
| 703 | 716 | ||
| 704 | /* Continue transmitting the packet/record. We must be careful | 717 | /* Continue transmitting the packet/record. We must be careful |
| 705 | * to cope with writespace callbacks arriving _after_ we have | 718 | * to cope with writespace callbacks arriving _after_ we have |
| 706 | * called sendmsg(). */ | 719 | * called sendmsg(). */ |
| 707 | while (1) { | 720 | while (1) { |
| 708 | status = xs_sendpages(transport->sock, | 721 | status = xs_sendpages(transport->sock, |
| 709 | NULL, 0, xdr, req->rq_bytes_sent); | 722 | NULL, 0, xdr, req->rq_bytes_sent, |
| 723 | zerocopy); | ||
| 710 | 724 | ||
| 711 | dprintk("RPC: xs_tcp_send_request(%u) = %d\n", | 725 | dprintk("RPC: xs_tcp_send_request(%u) = %d\n", |
| 712 | xdr->len - req->rq_bytes_sent, status); | 726 | xdr->len - req->rq_bytes_sent, status); |
