]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - net/sunrpc/xprtsock.c
Merge branch 'master' into next
[linux-2.6-omap-h63xx.git] / net / sunrpc / xprtsock.c
index 613daf8c1ff7a8ac309759b50133f504a2206952..0a50361e3d83535492160f7f08a36a5a5c34d4be 100644 (file)
@@ -3,8 +3,8 @@
  *
  * Client-side transport implementation for sockets.
  *
- * TCP callback races fixes (C) 1998 Red Hat Software <alan@redhat.com>
- * TCP send fixes (C) 1998 Red Hat Software <alan@redhat.com>
+ * TCP callback races fixes (C) 1998 Red Hat
+ * TCP send fixes (C) 1998 Red Hat
  * TCP NFS related read + write fixes
  *  (C) 1999 Dave Airlie, University of Limerick, Ireland <airlied@linux.ie>
  *
@@ -135,12 +135,6 @@ static ctl_table sunrpc_table[] = {
 
 #endif
 
-/*
- * How many times to try sending a request on a socket before waiting
- * for the socket buffer to clear.
- */
-#define XS_SENDMSG_RETRY       (10U)
-
 /*
  * Time out for an RPC UDP socket connect.  UDP socket connects are
  * synchronous, but we set a timeout anyway in case of resource
@@ -255,6 +249,7 @@ struct sock_xprt {
        void                    (*old_data_ready)(struct sock *, int);
        void                    (*old_state_change)(struct sock *);
        void                    (*old_write_space)(struct sock *);
+       void                    (*old_error_report)(struct sock *);
 };
 
 /*
@@ -516,6 +511,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
@@ -531,20 +534,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);
 }
 
 /**
@@ -570,7 +580,6 @@ static int xs_udp_send_request(struct rpc_task *task)
                                req->rq_svec->iov_base,
                                req->rq_svec->iov_len);
 
-       req->rq_xtime = jiffies;
        status = xs_sendpages(transport->sock,
                              xs_addr(xprt),
                              xprt->addrlen, xdr,
@@ -588,19 +597,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;
@@ -650,7 +660,6 @@ static int xs_tcp_send_request(struct rpc_task *task)
        struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);
        struct xdr_buf *xdr = &req->rq_snd_buf;
        int status;
-       unsigned int retry = 0;
 
        xs_encode_tcp_record_marker(&req->rq_snd_buf);
 
@@ -662,7 +671,6 @@ static int xs_tcp_send_request(struct rpc_task *task)
         * to cope with writespace callbacks arriving _after_ we have
         * called sendmsg(). */
        while (1) {
-               req->rq_xtime = jiffies;
                status = xs_sendpages(transport->sock,
                                        NULL, 0, xdr, req->rq_bytes_sent);
 
@@ -681,26 +689,29 @@ static int xs_tcp_send_request(struct rpc_task *task)
                        return 0;
                }
 
+               if (status != 0)
+                       continue;
                status = -EAGAIN;
-               if (retry++ > XS_SENDMSG_RETRY)
-                       break;
+               break;
        }
 
        switch (status) {
        case -EAGAIN:
                xs_nospace(task);
                break;
-       case -ECONNREFUSED:
        case -ECONNRESET:
+               xs_tcp_shutdown(xprt);
+       case -ECONNREFUSED:
        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;
@@ -733,6 +744,22 @@ out_release:
        xprt_release_xprt(xprt, task);
 }
 
+static void xs_save_old_callbacks(struct sock_xprt *transport, struct sock *sk)
+{
+       transport->old_data_ready = sk->sk_data_ready;
+       transport->old_state_change = sk->sk_state_change;
+       transport->old_write_space = sk->sk_write_space;
+       transport->old_error_report = sk->sk_error_report;
+}
+
+static void xs_restore_old_callbacks(struct sock_xprt *transport, struct sock *sk)
+{
+       sk->sk_data_ready = transport->old_data_ready;
+       sk->sk_state_change = transport->old_state_change;
+       sk->sk_write_space = transport->old_write_space;
+       sk->sk_error_report = transport->old_error_report;
+}
+
 /**
  * xs_close - close a socket
  * @xprt: transport
@@ -756,9 +783,8 @@ static void xs_close(struct rpc_xprt *xprt)
        transport->sock = NULL;
 
        sk->sk_user_data = NULL;
-       sk->sk_data_ready = transport->old_data_ready;
-       sk->sk_state_change = transport->old_state_change;
-       sk->sk_write_space = transport->old_write_space;
+
+       xs_restore_old_callbacks(transport, sk);
        write_unlock_bh(&sk->sk_callback_lock);
 
        sk->sk_no_check = 0;
@@ -1073,6 +1099,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");
 
@@ -1084,8 +1111,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);
 }
@@ -1128,6 +1157,7 @@ static void xs_tcp_state_change(struct sock *sk)
                break;
        case TCP_FIN_WAIT1:
                /* The client initiated a shutdown of the socket */
+               xprt->connect_cookie++;
                xprt->reestablish_timeout = 0;
                set_bit(XPRT_CLOSING, &xprt->state);
                smp_mb__before_clear_bit();
@@ -1140,6 +1170,7 @@ static void xs_tcp_state_change(struct sock *sk)
                set_bit(XPRT_CLOSING, &xprt->state);
                xprt_force_disconnect(xprt);
        case TCP_SYN_SENT:
+               xprt->connect_cookie++;
        case TCP_CLOSING:
                /*
                 * If the server closed down the connection, make sure that
@@ -1165,6 +1196,28 @@ static void xs_tcp_state_change(struct sock *sk)
        read_unlock(&sk->sk_callback_lock);
 }
 
+/**
+ * xs_tcp_error_report - callback mainly for catching RST events
+ * @sk: socket
+ */
+static void xs_tcp_error_report(struct sock *sk)
+{
+       struct rpc_xprt *xprt;
+
+       read_lock(&sk->sk_callback_lock);
+       if (sk->sk_err != ECONNRESET || sk->sk_state != TCP_ESTABLISHED)
+               goto out;
+       if (!(xprt = xprt_from_sock(sk)))
+               goto out;
+       dprintk("RPC:       %s client %p...\n"
+                       "RPC:       error %d\n",
+                       __func__, xprt, sk->sk_err);
+
+       xprt_force_disconnect(xprt);
+out:
+       read_unlock(&sk->sk_callback_lock);
+}
+
 /**
  * xs_udp_write_space - callback invoked when socket buffer space
  *                             becomes available
@@ -1186,9 +1239,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);
@@ -1219,9 +1274,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);
@@ -1436,10 +1493,9 @@ static void xs_udp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
 
                write_lock_bh(&sk->sk_callback_lock);
 
+               xs_save_old_callbacks(transport, sk);
+
                sk->sk_user_data = xprt;
-               transport->old_data_ready = sk->sk_data_ready;
-               transport->old_state_change = sk->sk_state_change;
-               transport->old_write_space = sk->sk_write_space;
                sk->sk_data_ready = xs_udp_data_ready;
                sk->sk_write_space = xs_udp_write_space;
                sk->sk_no_check = UDP_CSUM_NORCV;
@@ -1571,13 +1627,13 @@ static int xs_tcp_finish_connecting(struct rpc_xprt *xprt, struct socket *sock)
 
                write_lock_bh(&sk->sk_callback_lock);
 
+               xs_save_old_callbacks(transport, sk);
+
                sk->sk_user_data = xprt;
-               transport->old_data_ready = sk->sk_data_ready;
-               transport->old_state_change = sk->sk_state_change;
-               transport->old_write_space = sk->sk_write_space;
                sk->sk_data_ready = xs_tcp_data_ready;
                sk->sk_state_change = xs_tcp_state_change;
                sk->sk_write_space = xs_tcp_write_space;
+               sk->sk_error_report = xs_tcp_error_report;
                sk->sk_allocation = GFP_ATOMIC;
 
                /* socket options */