diff options
-rw-r--r-- | include/linux/sunrpc/xprt.h | 2 | ||||
-rw-r--r-- | net/sunrpc/xprt.c | 4 | ||||
-rw-r--r-- | net/sunrpc/xprtsock.c | 61 |
3 files changed, 44 insertions, 23 deletions
diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index b3ff9a815e6f..8a0629abb86a 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h | |||
@@ -232,7 +232,7 @@ int xprt_unregister_transport(struct xprt_class *type); | |||
232 | void xprt_set_retrans_timeout_def(struct rpc_task *task); | 232 | void xprt_set_retrans_timeout_def(struct rpc_task *task); |
233 | void xprt_set_retrans_timeout_rtt(struct rpc_task *task); | 233 | void xprt_set_retrans_timeout_rtt(struct rpc_task *task); |
234 | void xprt_wake_pending_tasks(struct rpc_xprt *xprt, int status); | 234 | void xprt_wake_pending_tasks(struct rpc_xprt *xprt, int status); |
235 | void xprt_wait_for_buffer_space(struct rpc_task *task); | 235 | void xprt_wait_for_buffer_space(struct rpc_task *task, rpc_action action); |
236 | void xprt_write_space(struct rpc_xprt *xprt); | 236 | void xprt_write_space(struct rpc_xprt *xprt); |
237 | void xprt_update_rtt(struct rpc_task *task); | 237 | void xprt_update_rtt(struct rpc_task *task); |
238 | void xprt_adjust_cwnd(struct rpc_task *task, int result); | 238 | void xprt_adjust_cwnd(struct rpc_task *task, int result); |
diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 85199c647022..3ba64f9f84ba 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c | |||
@@ -447,13 +447,13 @@ EXPORT_SYMBOL_GPL(xprt_wake_pending_tasks); | |||
447 | * @task: task to be put to sleep | 447 | * @task: task to be put to sleep |
448 | * | 448 | * |
449 | */ | 449 | */ |
450 | void xprt_wait_for_buffer_space(struct rpc_task *task) | 450 | void xprt_wait_for_buffer_space(struct rpc_task *task, rpc_action action) |
451 | { | 451 | { |
452 | struct rpc_rqst *req = task->tk_rqstp; | 452 | struct rpc_rqst *req = task->tk_rqstp; |
453 | struct rpc_xprt *xprt = req->rq_xprt; | 453 | struct rpc_xprt *xprt = req->rq_xprt; |
454 | 454 | ||
455 | task->tk_timeout = req->rq_timeout; | 455 | task->tk_timeout = req->rq_timeout; |
456 | rpc_sleep_on(&xprt->pending, task, NULL); | 456 | rpc_sleep_on(&xprt->pending, task, action); |
457 | } | 457 | } |
458 | EXPORT_SYMBOL_GPL(xprt_wait_for_buffer_space); | 458 | EXPORT_SYMBOL_GPL(xprt_wait_for_buffer_space); |
459 | 459 | ||
diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index 8bd3b0f73ac0..4c2462e2f2b2 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c | |||
@@ -516,6 +516,14 @@ out: | |||
516 | return sent; | 516 | return sent; |
517 | } | 517 | } |
518 | 518 | ||
519 | static void xs_nospace_callback(struct rpc_task *task) | ||
520 | { | ||
521 | struct sock_xprt *transport = container_of(task->tk_rqstp->rq_xprt, struct sock_xprt, xprt); | ||
522 | |||
523 | transport->inet->sk_write_pending--; | ||
524 | clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags); | ||
525 | } | ||
526 | |||
519 | /** | 527 | /** |
520 | * xs_nospace - place task on wait queue if transmit was incomplete | 528 | * xs_nospace - place task on wait queue if transmit was incomplete |
521 | * @task: task to put to sleep | 529 | * @task: task to put to sleep |
@@ -531,20 +539,27 @@ static void xs_nospace(struct rpc_task *task) | |||
531 | task->tk_pid, req->rq_slen - req->rq_bytes_sent, | 539 | task->tk_pid, req->rq_slen - req->rq_bytes_sent, |
532 | req->rq_slen); | 540 | req->rq_slen); |
533 | 541 | ||
534 | if (test_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags)) { | 542 | /* Protect against races with write_space */ |
535 | /* Protect against races with write_space */ | 543 | spin_lock_bh(&xprt->transport_lock); |
536 | spin_lock_bh(&xprt->transport_lock); | 544 | |
537 | 545 | /* Don't race with disconnect */ | |
538 | /* Don't race with disconnect */ | 546 | if (xprt_connected(xprt)) { |
539 | if (!xprt_connected(xprt)) | 547 | if (test_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags)) { |
540 | task->tk_status = -ENOTCONN; | 548 | /* |
541 | else if (test_bit(SOCK_NOSPACE, &transport->sock->flags)) | 549 | * Notify TCP that we're limited by the application |
542 | xprt_wait_for_buffer_space(task); | 550 | * window size |
551 | */ | ||
552 | set_bit(SOCK_NOSPACE, &transport->sock->flags); | ||
553 | transport->inet->sk_write_pending++; | ||
554 | /* ...and wait for more buffer space */ | ||
555 | xprt_wait_for_buffer_space(task, xs_nospace_callback); | ||
556 | } | ||
557 | } else { | ||
558 | clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags); | ||
559 | task->tk_status = -ENOTCONN; | ||
560 | } | ||
543 | 561 | ||
544 | spin_unlock_bh(&xprt->transport_lock); | 562 | spin_unlock_bh(&xprt->transport_lock); |
545 | } else | ||
546 | /* Keep holding the socket if it is blocked */ | ||
547 | rpc_delay(task, HZ>>4); | ||
548 | } | 563 | } |
549 | 564 | ||
550 | /** | 565 | /** |
@@ -588,19 +603,20 @@ static int xs_udp_send_request(struct rpc_task *task) | |||
588 | } | 603 | } |
589 | 604 | ||
590 | switch (status) { | 605 | switch (status) { |
606 | case -EAGAIN: | ||
607 | xs_nospace(task); | ||
608 | break; | ||
591 | case -ENETUNREACH: | 609 | case -ENETUNREACH: |
592 | case -EPIPE: | 610 | case -EPIPE: |
593 | case -ECONNREFUSED: | 611 | case -ECONNREFUSED: |
594 | /* When the server has died, an ICMP port unreachable message | 612 | /* When the server has died, an ICMP port unreachable message |
595 | * prompts ECONNREFUSED. */ | 613 | * prompts ECONNREFUSED. */ |
596 | break; | 614 | clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags); |
597 | case -EAGAIN: | ||
598 | xs_nospace(task); | ||
599 | break; | 615 | break; |
600 | default: | 616 | default: |
617 | clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags); | ||
601 | dprintk("RPC: sendmsg returned unrecognized error %d\n", | 618 | dprintk("RPC: sendmsg returned unrecognized error %d\n", |
602 | -status); | 619 | -status); |
603 | break; | ||
604 | } | 620 | } |
605 | 621 | ||
606 | return status; | 622 | return status; |
@@ -695,12 +711,13 @@ static int xs_tcp_send_request(struct rpc_task *task) | |||
695 | case -ENOTCONN: | 711 | case -ENOTCONN: |
696 | case -EPIPE: | 712 | case -EPIPE: |
697 | status = -ENOTCONN; | 713 | status = -ENOTCONN; |
714 | clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags); | ||
698 | break; | 715 | break; |
699 | default: | 716 | default: |
700 | dprintk("RPC: sendmsg returned unrecognized error %d\n", | 717 | dprintk("RPC: sendmsg returned unrecognized error %d\n", |
701 | -status); | 718 | -status); |
719 | clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags); | ||
702 | xs_tcp_shutdown(xprt); | 720 | xs_tcp_shutdown(xprt); |
703 | break; | ||
704 | } | 721 | } |
705 | 722 | ||
706 | return status; | 723 | return status; |
@@ -1189,9 +1206,11 @@ static void xs_udp_write_space(struct sock *sk) | |||
1189 | 1206 | ||
1190 | if (unlikely(!(sock = sk->sk_socket))) | 1207 | if (unlikely(!(sock = sk->sk_socket))) |
1191 | goto out; | 1208 | goto out; |
1209 | clear_bit(SOCK_NOSPACE, &sock->flags); | ||
1210 | |||
1192 | if (unlikely(!(xprt = xprt_from_sock(sk)))) | 1211 | if (unlikely(!(xprt = xprt_from_sock(sk)))) |
1193 | goto out; | 1212 | goto out; |
1194 | if (unlikely(!test_and_clear_bit(SOCK_NOSPACE, &sock->flags))) | 1213 | if (test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags) == 0) |
1195 | goto out; | 1214 | goto out; |
1196 | 1215 | ||
1197 | xprt_write_space(xprt); | 1216 | xprt_write_space(xprt); |
@@ -1222,9 +1241,11 @@ static void xs_tcp_write_space(struct sock *sk) | |||
1222 | 1241 | ||
1223 | if (unlikely(!(sock = sk->sk_socket))) | 1242 | if (unlikely(!(sock = sk->sk_socket))) |
1224 | goto out; | 1243 | goto out; |
1244 | clear_bit(SOCK_NOSPACE, &sock->flags); | ||
1245 | |||
1225 | if (unlikely(!(xprt = xprt_from_sock(sk)))) | 1246 | if (unlikely(!(xprt = xprt_from_sock(sk)))) |
1226 | goto out; | 1247 | goto out; |
1227 | if (unlikely(!test_and_clear_bit(SOCK_NOSPACE, &sock->flags))) | 1248 | if (test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags) == 0) |
1228 | goto out; | 1249 | goto out; |
1229 | 1250 | ||
1230 | xprt_write_space(xprt); | 1251 | xprt_write_space(xprt); |