--- 25-akpm/include/linux/sunrpc/xprt.h | 5 25-akpm/net/sunrpc/xprt.c | 182 +++++++++++++++++------------------- 2 files changed, 92 insertions(+), 95 deletions(-) diff -puN include/linux/sunrpc/xprt.h~nfs-reconnect-fix include/linux/sunrpc/xprt.h --- 25/include/linux/sunrpc/xprt.h~nfs-reconnect-fix Fri Feb 27 14:11:21 2004 +++ 25-akpm/include/linux/sunrpc/xprt.h Fri Feb 27 14:11:21 2004 @@ -164,6 +164,11 @@ struct rpc_xprt { unsigned long tcp_copied, /* copied to request */ tcp_flags; /* + * Connection of sockets + */ + struct work_struct sock_connect; + unsigned short port; + /* * Disconnection of idle sockets */ struct work_struct task_cleanup; diff -puN net/sunrpc/xprt.c~nfs-reconnect-fix net/sunrpc/xprt.c --- 25/net/sunrpc/xprt.c~nfs-reconnect-fix Fri Feb 27 14:11:21 2004 +++ 25-akpm/net/sunrpc/xprt.c Fri Feb 27 14:11:21 2004 @@ -74,6 +74,7 @@ #define XPRT_MAX_BACKOFF (8) #define XPRT_IDLE_TIMEOUT (5*60*HZ) +#define XPRT_MAX_RESVPORT (800) /* * Local functions @@ -84,7 +85,7 @@ static void xprt_disconnect(struct rpc_x static void xprt_connect_status(struct rpc_task *task); static struct rpc_xprt * xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to); -static struct socket *xprt_create_socket(int, struct rpc_timeout *, int); +static struct socket *xprt_create_socket(struct rpc_xprt *, int, int); static void xprt_bind_socket(struct rpc_xprt *, struct socket *); static int __xprt_get_cong(struct rpc_xprt *, struct rpc_task *); @@ -452,6 +453,68 @@ out_abort: spin_unlock(&xprt->sock_lock); } +static void +xprt_socket_connect(void *args) +{ + struct rpc_xprt *xprt = (struct rpc_xprt *)args; + struct socket *sock = xprt->sock; + int status = -EIO; + + if (xprt->shutdown) { + rpc_wake_up_status(&xprt->pending, -EIO); + return; + } + if (!xprt->addr.sin_port) + goto out_err; + + /* + * Start by resetting any existing state + */ + xprt_close(xprt); + sock = xprt_create_socket(xprt, xprt->prot, xprt->resvport); + if (sock == NULL) { + /* couldn't create socket or bind to reserved port; + * this is likely a permanent error, so cause an abort */ + goto out_err; + return; + } + xprt_bind_socket(xprt, sock); + xprt_sock_setbufsize(xprt); + + if (!xprt->stream) + goto out; + + /* + * Tell the socket layer to start connecting... + */ + status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr, + sizeof(xprt->addr), O_NONBLOCK); + dprintk("RPC: %p connect status %d connected %d sock state %d\n", + xprt, -status, xprt_connected(xprt), sock->sk->sk_state); + if (status >= 0) + goto out; + switch (status) { + case -EINPROGRESS: + case -EALREADY: + return; + default: + goto out_err; + } +out: + spin_lock_bh(&xprt->sock_lock); + if (xprt->snd_task) + rpc_wake_up_task(xprt->snd_task); + spin_unlock_bh(&xprt->sock_lock); + return; +out_err: + spin_lock_bh(&xprt->sock_lock); + if (xprt->snd_task) { + xprt->snd_task->tk_status = status; + rpc_wake_up_task(xprt->snd_task); + } + spin_unlock_bh(&xprt->sock_lock); +} + /* * Attempt to connect a TCP socket. * @@ -460,9 +523,6 @@ void xprt_connect(struct rpc_task *task) { struct rpc_xprt *xprt = task->tk_xprt; - struct socket *sock = xprt->sock; - struct sock *inet; - int status; dprintk("RPC: %4d xprt_connect xprt %p %s connected\n", task->tk_pid, xprt, (xprt_connected(xprt) ? "is" : "is not")); @@ -483,79 +543,9 @@ xprt_connect(struct rpc_task *task) if (task->tk_rqstp) task->tk_rqstp->rq_bytes_sent = 0; - /* - * We're here because the xprt was marked disconnected. - * Start by resetting any existing state. - */ - xprt_close(xprt); - if (!(sock = xprt_create_socket(xprt->prot, &xprt->timeout, xprt->resvport))) { - /* couldn't create socket or bind to reserved port; - * this is likely a permanent error, so cause an abort */ - task->tk_status = -EIO; - goto out_write; - } - xprt_bind_socket(xprt, sock); - xprt_sock_setbufsize(xprt); - - if (!xprt->stream) - goto out_write; - - inet = sock->sk; - - /* - * Tell the socket layer to start connecting... - */ - status = sock->ops->connect(sock, (struct sockaddr *) &xprt->addr, - sizeof(xprt->addr), O_NONBLOCK); - dprintk("RPC: %4d connect status %d connected %d sock state %d\n", - task->tk_pid, -status, xprt_connected(xprt), inet->sk_state); - - if (status >= 0) - return; - - switch (status) { - case -EINPROGRESS: - case -EALREADY: - /* Protect against TCP socket state changes */ - lock_sock(inet); - if (inet->sk_state != TCP_ESTABLISHED) { - dprintk("RPC: %4d waiting for connection\n", - task->tk_pid); - task->tk_timeout = RPC_CONNECT_TIMEOUT; - /* if the socket is already closing, delay briefly */ - if ((1 << inet->sk_state) & - ~(TCPF_SYN_SENT | TCPF_SYN_RECV)) - task->tk_timeout = RPC_REESTABLISH_TIMEOUT; - rpc_sleep_on(&xprt->pending, task, xprt_connect_status, - NULL); - } - release_sock(inet); - break; - case -ECONNREFUSED: - case -ECONNRESET: - case -ENOTCONN: - if (!RPC_IS_SOFT(task)) { - rpc_delay(task, RPC_REESTABLISH_TIMEOUT); - task->tk_status = -ENOTCONN; - break; - } - default: - /* Report myriad other possible returns. If this file - * system is soft mounted, just error out, like Solaris. */ - if (RPC_IS_SOFT(task)) { - printk(KERN_WARNING - "RPC: error %d connecting to server %s, exiting\n", - -status, task->tk_client->cl_server); - task->tk_status = -EIO; - goto out_write; - } - printk(KERN_WARNING "RPC: error %d connecting to server %s\n", - -status, task->tk_client->cl_server); - /* This will prevent anybody else from reconnecting */ - rpc_delay(task, RPC_REESTABLISH_TIMEOUT); - task->tk_status = status; - break; - } + task->tk_timeout = RPC_CONNECT_TIMEOUT; + rpc_sleep_on(&xprt->pending, task, xprt_connect_status, NULL); + schedule_work(&xprt->sock_connect); return; out_write: xprt_release_write(xprt, task); @@ -580,6 +570,8 @@ xprt_connect_status(struct rpc_task *tas task->tk_status = -EIO; switch (task->tk_status) { + case -ECONNREFUSED: + case -ECONNRESET: case -ENOTCONN: rpc_delay(task, RPC_REESTABLISH_TIMEOUT); return; @@ -1454,11 +1446,13 @@ xprt_setup(int proto, struct sockaddr_in init_waitqueue_head(&xprt->cong_wait); INIT_LIST_HEAD(&xprt->recv); + INIT_WORK(&xprt->sock_connect, xprt_socket_connect, xprt); INIT_WORK(&xprt->task_cleanup, xprt_socket_autoclose, xprt); init_timer(&xprt->timer); xprt->timer.function = xprt_init_autodisconnect; xprt->timer.data = (unsigned long) xprt; xprt->last_used = jiffies; + xprt->port = XPRT_MAX_RESVPORT; /* Set timeout parameters */ if (to) { @@ -1490,30 +1484,28 @@ xprt_setup(int proto, struct sockaddr_in * Bind to a reserved port */ static inline int -xprt_bindresvport(struct socket *sock) +xprt_bindresvport(struct rpc_xprt *xprt, struct socket *sock) { - struct sockaddr_in myaddr; + struct sockaddr_in myaddr = { + .sin_family = AF_INET, + }; int err, port; - kernel_cap_t saved_cap = current->cap_effective; - /* Override capabilities. - * They were checked in xprt_create_proto i.e. at mount time - */ - cap_raise(current->cap_effective, CAP_NET_BIND_SERVICE); - - memset(&myaddr, 0, sizeof(myaddr)); - myaddr.sin_family = AF_INET; - port = 800; + /* Were we already bound to a given port? Try to reuse it */ + port = xprt->port; do { myaddr.sin_port = htons(port); err = sock->ops->bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)); - } while (err == -EADDRINUSE && --port > 0); - current->cap_effective = saved_cap; - - if (err < 0) - printk("RPC: Can't bind to reserved port (%d).\n", -err); + if (err == 0) { + xprt->port = port; + return 0; + } + if (--port == 0) + port = XPRT_MAX_RESVPORT; + } while (err == -EADDRINUSE && port != xprt->port); + printk("RPC: Can't bind to reserved port (%d).\n", -err); return err; } @@ -1577,7 +1569,7 @@ xprt_sock_setbufsize(struct rpc_xprt *xp * and connect stream sockets. */ static struct socket * -xprt_create_socket(int proto, struct rpc_timeout *to, int resvport) +xprt_create_socket(struct rpc_xprt *xprt, int proto, int resvport) { struct socket *sock; int type, err; @@ -1593,7 +1585,7 @@ xprt_create_socket(int proto, struct rpc } /* If the caller has the capability, bind to a reserved port */ - if (resvport && xprt_bindresvport(sock) < 0) { + if (resvport && xprt_bindresvport(xprt, sock) < 0) { printk("RPC: can't bind to reserved port.\n"); goto failed; } _