]> err.no Git - linux-2.6/blobdiff - net/sunrpc/xprtsock.c
SUNRPC: Fix up xprt_write_space()
[linux-2.6] / net / sunrpc / xprtsock.c
index 6ba329d339a28f615d7403101334244e9c865cc3..4c2462e2f2b28a351463e8c4a85bb70d29988dd7 100644 (file)
@@ -280,7 +280,9 @@ static inline struct sockaddr_in6 *xs_addr_in6(struct rpc_xprt *xprt)
        return (struct sockaddr_in6 *) &xprt->addr;
 }
 
-static void xs_format_ipv4_peer_addresses(struct rpc_xprt *xprt)
+static void xs_format_ipv4_peer_addresses(struct rpc_xprt *xprt,
+                                         const char *protocol,
+                                         const char *netid)
 {
        struct sockaddr_in *addr = xs_addr_in(xprt);
        char *buf;
@@ -299,21 +301,14 @@ static void xs_format_ipv4_peer_addresses(struct rpc_xprt *xprt)
        }
        xprt->address_strings[RPC_DISPLAY_PORT] = buf;
 
-       buf = kzalloc(8, GFP_KERNEL);
-       if (buf) {
-               if (xprt->prot == IPPROTO_UDP)
-                       snprintf(buf, 8, "udp");
-               else
-                       snprintf(buf, 8, "tcp");
-       }
-       xprt->address_strings[RPC_DISPLAY_PROTO] = buf;
+       xprt->address_strings[RPC_DISPLAY_PROTO] = protocol;
 
        buf = kzalloc(48, GFP_KERNEL);
        if (buf) {
                snprintf(buf, 48, "addr="NIPQUAD_FMT" port=%u proto=%s",
                        NIPQUAD(addr->sin_addr.s_addr),
                        ntohs(addr->sin_port),
-                       xprt->prot == IPPROTO_UDP ? "udp" : "tcp");
+                       protocol);
        }
        xprt->address_strings[RPC_DISPLAY_ALL] = buf;
 
@@ -340,12 +335,12 @@ static void xs_format_ipv4_peer_addresses(struct rpc_xprt *xprt)
        }
        xprt->address_strings[RPC_DISPLAY_UNIVERSAL_ADDR] = buf;
 
-       xprt->address_strings[RPC_DISPLAY_NETID] =
-               kstrdup(xprt->prot == IPPROTO_UDP ?
-                       RPCBIND_NETID_UDP : RPCBIND_NETID_TCP, GFP_KERNEL);
+       xprt->address_strings[RPC_DISPLAY_NETID] = netid;
 }
 
-static void xs_format_ipv6_peer_addresses(struct rpc_xprt *xprt)
+static void xs_format_ipv6_peer_addresses(struct rpc_xprt *xprt,
+                                         const char *protocol,
+                                         const char *netid)
 {
        struct sockaddr_in6 *addr = xs_addr_in6(xprt);
        char *buf;
@@ -364,21 +359,14 @@ static void xs_format_ipv6_peer_addresses(struct rpc_xprt *xprt)
        }
        xprt->address_strings[RPC_DISPLAY_PORT] = buf;
 
-       buf = kzalloc(8, GFP_KERNEL);
-       if (buf) {
-               if (xprt->prot == IPPROTO_UDP)
-                       snprintf(buf, 8, "udp");
-               else
-                       snprintf(buf, 8, "tcp");
-       }
-       xprt->address_strings[RPC_DISPLAY_PROTO] = buf;
+       xprt->address_strings[RPC_DISPLAY_PROTO] = protocol;
 
        buf = kzalloc(64, GFP_KERNEL);
        if (buf) {
                snprintf(buf, 64, "addr="NIP6_FMT" port=%u proto=%s",
                                NIP6(addr->sin6_addr),
                                ntohs(addr->sin6_port),
-                               xprt->prot == IPPROTO_UDP ? "udp" : "tcp");
+                               protocol);
        }
        xprt->address_strings[RPC_DISPLAY_ALL] = buf;
 
@@ -405,17 +393,21 @@ static void xs_format_ipv6_peer_addresses(struct rpc_xprt *xprt)
        }
        xprt->address_strings[RPC_DISPLAY_UNIVERSAL_ADDR] = buf;
 
-       xprt->address_strings[RPC_DISPLAY_NETID] =
-               kstrdup(xprt->prot == IPPROTO_UDP ?
-                       RPCBIND_NETID_UDP6 : RPCBIND_NETID_TCP6, GFP_KERNEL);
+       xprt->address_strings[RPC_DISPLAY_NETID] = netid;
 }
 
 static void xs_free_peer_addresses(struct rpc_xprt *xprt)
 {
-       int i;
+       unsigned int i;
 
        for (i = 0; i < RPC_DISPLAY_MAX; i++)
-               kfree(xprt->address_strings[i]);
+               switch (i) {
+               case RPC_DISPLAY_PROTO:
+               case RPC_DISPLAY_NETID:
+                       continue;
+               default:
+                       kfree(xprt->address_strings[i]);
+               }
 }
 
 #define XS_SENDMSG_FLAGS       (MSG_DONTWAIT | MSG_NOSIGNAL)
@@ -524,6 +516,14 @@ out:
        return sent;
 }
 
+static void xs_nospace_callback(struct rpc_task *task)
+{
+       struct sock_xprt *transport = container_of(task->tk_rqstp->rq_xprt, struct sock_xprt, xprt);
+
+       transport->inet->sk_write_pending--;
+       clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
+}
+
 /**
  * xs_nospace - place task on wait queue if transmit was incomplete
  * @task: task to put to sleep
@@ -539,20 +539,27 @@ static void xs_nospace(struct rpc_task *task)
                        task->tk_pid, req->rq_slen - req->rq_bytes_sent,
                        req->rq_slen);
 
-       if (test_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags)) {
-               /* Protect against races with write_space */
-               spin_lock_bh(&xprt->transport_lock);
-
-               /* Don't race with disconnect */
-               if (!xprt_connected(xprt))
-                       task->tk_status = -ENOTCONN;
-               else if (test_bit(SOCK_NOSPACE, &transport->sock->flags))
-                       xprt_wait_for_buffer_space(task);
+       /* Protect against races with write_space */
+       spin_lock_bh(&xprt->transport_lock);
+
+       /* Don't race with disconnect */
+       if (xprt_connected(xprt)) {
+               if (test_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags)) {
+                       /*
+                        * Notify TCP that we're limited by the application
+                        * window size
+                        */
+                       set_bit(SOCK_NOSPACE, &transport->sock->flags);
+                       transport->inet->sk_write_pending++;
+                       /* ...and wait for more buffer space */
+                       xprt_wait_for_buffer_space(task, xs_nospace_callback);
+               }
+       } else {
+               clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
+               task->tk_status = -ENOTCONN;
+       }
 
-               spin_unlock_bh(&xprt->transport_lock);
-       } else
-               /* Keep holding the socket if it is blocked */
-               rpc_delay(task, HZ>>4);
+       spin_unlock_bh(&xprt->transport_lock);
 }
 
 /**
@@ -596,19 +603,20 @@ static int xs_udp_send_request(struct rpc_task *task)
        }
 
        switch (status) {
+       case -EAGAIN:
+               xs_nospace(task);
+               break;
        case -ENETUNREACH:
        case -EPIPE:
        case -ECONNREFUSED:
                /* When the server has died, an ICMP port unreachable message
                 * prompts ECONNREFUSED. */
-               break;
-       case -EAGAIN:
-               xs_nospace(task);
+               clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
                break;
        default:
+               clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
                dprintk("RPC:       sendmsg returned unrecognized error %d\n",
                        -status);
-               break;
        }
 
        return status;
@@ -703,12 +711,13 @@ static int xs_tcp_send_request(struct rpc_task *task)
        case -ENOTCONN:
        case -EPIPE:
                status = -ENOTCONN;
+               clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
                break;
        default:
                dprintk("RPC:       sendmsg returned unrecognized error %d\n",
                        -status);
+               clear_bit(SOCK_ASYNC_NOSPACE, &transport->sock->flags);
                xs_tcp_shutdown(xprt);
-               break;
        }
 
        return status;
@@ -1081,6 +1090,7 @@ static void xs_tcp_data_ready(struct sock *sk, int bytes)
 {
        struct rpc_xprt *xprt;
        read_descriptor_t rd_desc;
+       int read;
 
        dprintk("RPC:       xs_tcp_data_ready...\n");
 
@@ -1092,8 +1102,10 @@ static void xs_tcp_data_ready(struct sock *sk, int bytes)
 
        /* We use rd_desc to pass struct xprt to xs_tcp_data_recv */
        rd_desc.arg.data = xprt;
-       rd_desc.count = 65536;
-       tcp_read_sock(sk, &rd_desc, xs_tcp_data_recv);
+       do {
+               rd_desc.count = 65536;
+               read = tcp_read_sock(sk, &rd_desc, xs_tcp_data_recv);
+       } while (read > 0);
 out:
        read_unlock(&sk->sk_callback_lock);
 }
@@ -1194,9 +1206,11 @@ static void xs_udp_write_space(struct sock *sk)
 
                if (unlikely(!(sock = sk->sk_socket)))
                        goto out;
+               clear_bit(SOCK_NOSPACE, &sock->flags);
+
                if (unlikely(!(xprt = xprt_from_sock(sk))))
                        goto out;
-               if (unlikely(!test_and_clear_bit(SOCK_NOSPACE, &sock->flags)))
+               if (test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags) == 0)
                        goto out;
 
                xprt_write_space(xprt);
@@ -1227,9 +1241,11 @@ static void xs_tcp_write_space(struct sock *sk)
 
                if (unlikely(!(sock = sk->sk_socket)))
                        goto out;
+               clear_bit(SOCK_NOSPACE, &sock->flags);
+
                if (unlikely(!(xprt = xprt_from_sock(sk))))
                        goto out;
-               if (unlikely(!test_and_clear_bit(SOCK_NOSPACE, &sock->flags)))
+               if (test_and_clear_bit(SOCK_ASYNC_NOSPACE, &sock->flags) == 0)
                        goto out;
 
                xprt_write_space(xprt);
@@ -1939,7 +1955,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
 
                INIT_DELAYED_WORK(&transport->connect_worker,
                                        xs_udp_connect_worker4);
-               xs_format_ipv4_peer_addresses(xprt);
+               xs_format_ipv4_peer_addresses(xprt, "udp", RPCBIND_NETID_UDP);
                break;
        case AF_INET6:
                if (((struct sockaddr_in6 *)addr)->sin6_port != htons(0))
@@ -1947,7 +1963,7 @@ static struct rpc_xprt *xs_setup_udp(struct xprt_create *args)
 
                INIT_DELAYED_WORK(&transport->connect_worker,
                                        xs_udp_connect_worker6);
-               xs_format_ipv6_peer_addresses(xprt);
+               xs_format_ipv6_peer_addresses(xprt, "udp", RPCBIND_NETID_UDP6);
                break;
        default:
                kfree(xprt);
@@ -2005,14 +2021,14 @@ static struct rpc_xprt *xs_setup_tcp(struct xprt_create *args)
                        xprt_set_bound(xprt);
 
                INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_connect_worker4);
-               xs_format_ipv4_peer_addresses(xprt);
+               xs_format_ipv4_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP);
                break;
        case AF_INET6:
                if (((struct sockaddr_in6 *)addr)->sin6_port != htons(0))
                        xprt_set_bound(xprt);
 
                INIT_DELAYED_WORK(&transport->connect_worker, xs_tcp_connect_worker6);
-               xs_format_ipv6_peer_addresses(xprt);
+               xs_format_ipv6_peer_addresses(xprt, "tcp", RPCBIND_NETID_TCP6);
                break;
        default:
                kfree(xprt);