]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
SUNRPC: Don't disconnect more than once if retransmitting NFSv4 requests
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Thu, 17 Apr 2008 20:52:57 +0000 (16:52 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Sat, 19 Apr 2008 20:55:12 +0000 (16:55 -0400)
NFSv4 requires us to ensure that we break the TCP connection before we're
allowed to retransmit a request. However in the case where we're
retransmitting several requests that have been sent on the same
connection, we need to ensure that we don't interfere with the attempt to
reconnect and/or break the connection again once it has been established.

We therefore introduce a 'connection' cookie that is bumped every time a
connection is broken. This allows requests to track if they need to force a
disconnection.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
include/linux/sunrpc/xprt.h
net/sunrpc/clnt.c
net/sunrpc/xprt.c
net/sunrpc/xprtsock.c

index 8a0629abb86a7bcbe1c26e370156388a1ab34659..4d80a118d5383d073d64609c84a91249921d236c 100644 (file)
@@ -86,6 +86,10 @@ struct rpc_rqst {
        unsigned long           rq_majortimeo;  /* major timeout alarm */
        unsigned long           rq_timeout;     /* Current timeout value */
        unsigned int            rq_retries;     /* # of retries */
+       unsigned int            rq_connect_cookie;
+                                               /* A cookie used to track the
+                                                  state of the transport
+                                                  connection */
        
        /*
         * Partial send handling
@@ -152,6 +156,9 @@ struct rpc_xprt {
        unsigned long           connect_timeout,
                                bind_timeout,
                                reestablish_timeout;
+       unsigned int            connect_cookie; /* A cookie that gets bumped
+                                                  every time the transport
+                                                  is reconnected */
 
        /*
         * Disconnection of idle transports
@@ -241,6 +248,7 @@ void                        xprt_complete_rqst(struct rpc_task *task, int copied);
 void                   xprt_release_rqst_cong(struct rpc_task *task);
 void                   xprt_disconnect_done(struct rpc_xprt *xprt);
 void                   xprt_force_disconnect(struct rpc_xprt *xprt);
+void                   xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie);
 
 /*
  * Reserved bit positions in xprt->state
index 3ae5604645135ec665c7415c77f1b043281df89f..8773b4342c92dc6c859af4fa48a653df70fc3228 100644 (file)
@@ -1120,7 +1120,8 @@ call_status(struct rpc_task *task)
        case -ETIMEDOUT:
                task->tk_action = call_timeout;
                if (task->tk_client->cl_discrtry)
-                       xprt_force_disconnect(task->tk_xprt);
+                       xprt_conditional_disconnect(task->tk_xprt,
+                                       req->rq_connect_cookie);
                break;
        case -ECONNREFUSED:
        case -ENOTCONN:
@@ -1245,7 +1246,8 @@ out_retry:
        if (task->tk_rqstp == req) {
                req->rq_received = req->rq_rcv_buf.len = 0;
                if (task->tk_client->cl_discrtry)
-                       xprt_force_disconnect(task->tk_xprt);
+                       xprt_conditional_disconnect(task->tk_xprt,
+                                       req->rq_connect_cookie);
        }
 }
 
index a0646a3b4a39134683ec0d9279466216b82b72c5..75d748eee0eb81d4339b8b258dccc8dd653d7d0f 100644 (file)
@@ -606,6 +606,34 @@ void xprt_force_disconnect(struct rpc_xprt *xprt)
        spin_unlock_bh(&xprt->transport_lock);
 }
 
+/**
+ * xprt_conditional_disconnect - force a transport to disconnect
+ * @xprt: transport to disconnect
+ * @cookie: 'connection cookie'
+ *
+ * This attempts to break the connection if and only if 'cookie' matches
+ * the current transport 'connection cookie'. It ensures that we don't
+ * try to break the connection more than once when we need to retransmit
+ * a batch of RPC requests.
+ *
+ */
+void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie)
+{
+       /* Don't race with the test_bit() in xprt_clear_locked() */
+       spin_lock_bh(&xprt->transport_lock);
+       if (cookie != xprt->connect_cookie)
+               goto out;
+       if (test_bit(XPRT_CLOSING, &xprt->state) || !xprt_connected(xprt))
+               goto out;
+       set_bit(XPRT_CLOSE_WAIT, &xprt->state);
+       /* Try to schedule an autoclose RPC call */
+       if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0)
+               queue_work(rpciod_workqueue, &xprt->task_cleanup);
+       xprt_wake_pending_tasks(xprt, -ENOTCONN);
+out:
+       spin_unlock_bh(&xprt->transport_lock);
+}
+
 static void
 xprt_init_autodisconnect(unsigned long data)
 {
@@ -849,6 +877,7 @@ void xprt_transmit(struct rpc_task *task)
        } else if (!req->rq_bytes_sent)
                return;
 
+       req->rq_connect_cookie = xprt->connect_cookie;
        status = xprt->ops->send_request(task);
        if (status == 0) {
                dprintk("RPC: %5u xmit complete\n", task->tk_pid);
index 4a567a93e6adea4c9296d97acdf3c49fbc43df45..63d79e347c00397584534d651896501056838cb8 100644 (file)
@@ -1142,6 +1142,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();
@@ -1154,6 +1155,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