Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

SUNRPC: Don't disconnect more than once if retransmitting NFSv4 requests

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>

+43 -2
+8
include/linux/sunrpc/xprt.h
··· 86 86 unsigned long rq_majortimeo; /* major timeout alarm */ 87 87 unsigned long rq_timeout; /* Current timeout value */ 88 88 unsigned int rq_retries; /* # of retries */ 89 + unsigned int rq_connect_cookie; 90 + /* A cookie used to track the 91 + state of the transport 92 + connection */ 89 93 90 94 /* 91 95 * Partial send handling ··· 156 152 unsigned long connect_timeout, 157 153 bind_timeout, 158 154 reestablish_timeout; 155 + unsigned int connect_cookie; /* A cookie that gets bumped 156 + every time the transport 157 + is reconnected */ 159 158 160 159 /* 161 160 * Disconnection of idle transports ··· 248 241 void xprt_release_rqst_cong(struct rpc_task *task); 249 242 void xprt_disconnect_done(struct rpc_xprt *xprt); 250 243 void xprt_force_disconnect(struct rpc_xprt *xprt); 244 + void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie); 251 245 252 246 /* 253 247 * Reserved bit positions in xprt->state
+4 -2
net/sunrpc/clnt.c
··· 1120 1120 case -ETIMEDOUT: 1121 1121 task->tk_action = call_timeout; 1122 1122 if (task->tk_client->cl_discrtry) 1123 - xprt_force_disconnect(task->tk_xprt); 1123 + xprt_conditional_disconnect(task->tk_xprt, 1124 + req->rq_connect_cookie); 1124 1125 break; 1125 1126 case -ECONNREFUSED: 1126 1127 case -ENOTCONN: ··· 1246 1245 if (task->tk_rqstp == req) { 1247 1246 req->rq_received = req->rq_rcv_buf.len = 0; 1248 1247 if (task->tk_client->cl_discrtry) 1249 - xprt_force_disconnect(task->tk_xprt); 1248 + xprt_conditional_disconnect(task->tk_xprt, 1249 + req->rq_connect_cookie); 1250 1250 } 1251 1251 } 1252 1252
+29
net/sunrpc/xprt.c
··· 606 606 spin_unlock_bh(&xprt->transport_lock); 607 607 } 608 608 609 + /** 610 + * xprt_conditional_disconnect - force a transport to disconnect 611 + * @xprt: transport to disconnect 612 + * @cookie: 'connection cookie' 613 + * 614 + * This attempts to break the connection if and only if 'cookie' matches 615 + * the current transport 'connection cookie'. It ensures that we don't 616 + * try to break the connection more than once when we need to retransmit 617 + * a batch of RPC requests. 618 + * 619 + */ 620 + void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie) 621 + { 622 + /* Don't race with the test_bit() in xprt_clear_locked() */ 623 + spin_lock_bh(&xprt->transport_lock); 624 + if (cookie != xprt->connect_cookie) 625 + goto out; 626 + if (test_bit(XPRT_CLOSING, &xprt->state) || !xprt_connected(xprt)) 627 + goto out; 628 + set_bit(XPRT_CLOSE_WAIT, &xprt->state); 629 + /* Try to schedule an autoclose RPC call */ 630 + if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0) 631 + queue_work(rpciod_workqueue, &xprt->task_cleanup); 632 + xprt_wake_pending_tasks(xprt, -ENOTCONN); 633 + out: 634 + spin_unlock_bh(&xprt->transport_lock); 635 + } 636 + 609 637 static void 610 638 xprt_init_autodisconnect(unsigned long data) 611 639 { ··· 877 849 } else if (!req->rq_bytes_sent) 878 850 return; 879 851 852 + req->rq_connect_cookie = xprt->connect_cookie; 880 853 status = xprt->ops->send_request(task); 881 854 if (status == 0) { 882 855 dprintk("RPC: %5u xmit complete\n", task->tk_pid);
+2
net/sunrpc/xprtsock.c
··· 1142 1142 break; 1143 1143 case TCP_FIN_WAIT1: 1144 1144 /* The client initiated a shutdown of the socket */ 1145 + xprt->connect_cookie++; 1145 1146 xprt->reestablish_timeout = 0; 1146 1147 set_bit(XPRT_CLOSING, &xprt->state); 1147 1148 smp_mb__before_clear_bit(); ··· 1155 1154 set_bit(XPRT_CLOSING, &xprt->state); 1156 1155 xprt_force_disconnect(xprt); 1157 1156 case TCP_SYN_SENT: 1157 + xprt->connect_cookie++; 1158 1158 case TCP_CLOSING: 1159 1159 /* 1160 1160 * If the server closed down the connection, make sure that