diff -Nur linux-2.6.0.orig/fs/lockd/host.c linux-2.6.0/fs/lockd/host.c --- linux-2.6.0.orig/fs/lockd/host.c 2003-12-18 10:58:56.000000000 +0800 +++ linux-2.6.0/fs/lockd/host.c 2003-12-21 22:39:40.000000000 +0800 @@ -25,7 +25,6 @@ #define NLM_HOST_REBIND (60 * HZ) #define NLM_HOST_EXPIRE ((nrhosts > NLM_HOST_MAX)? 300 * HZ : 120 * HZ) #define NLM_HOST_COLLECT ((nrhosts > NLM_HOST_MAX)? 120 * HZ : 60 * HZ) -#define NLM_HOST_ADDR(sv) (&(sv)->s_nlmclnt->cl_xprt->addr) static struct nlm_host * nlm_hosts[NLM_HOST_NRHASH]; static unsigned long next_gc; @@ -166,7 +165,6 @@ nlm_bind_host(struct nlm_host *host) { struct rpc_clnt *clnt; - struct rpc_xprt *xprt; dprintk("lockd: nlm_bind_host(%08x)\n", (unsigned)ntohl(host->h_addr.sin_addr.s_addr)); @@ -179,29 +177,23 @@ * Note: why keep rebinding if we're on a tcp connection? */ if ((clnt = host->h_rpcclnt) != NULL) { - xprt = clnt->cl_xprt; - if (!xprt->stream && time_after_eq(jiffies, host->h_nextrebind)) { - clnt->cl_port = 0; + if (clnt->cl_prot == IPPROTO_UDP + && time_after_eq(jiffies, host->h_nextrebind)) { + rpc_do_rebind(clnt); host->h_nextrebind = jiffies + NLM_HOST_REBIND; dprintk("lockd: next rebind in %ld jiffies\n", host->h_nextrebind - jiffies); } } else { - xprt = xprt_create_proto(host->h_proto, &host->h_addr, NULL); - if (xprt == NULL) + clnt = rpc_create_client(host->h_name, host->h_proto, + &host->h_addr, &nlm_program, + host->h_version, host->h_authflavor); + if (clnt == NULL) goto forgetit; - xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout); - - clnt = rpc_create_client(xprt, host->h_name, &nlm_program, - host->h_version, host->h_authflavor); - if (clnt == NULL) { - xprt_destroy(xprt); - goto forgetit; - } + // xprt_set_timeout(&xprt->timeout, 5, nlmsvc_timeout); clnt->cl_autobind = 1; /* turn on pmap queries */ - xprt->nocong = 1; /* No congestion control for NLM */ - xprt->resvport = 1; /* NLM requires a reserved port */ + clnt->cl_resvport = 1; /* NLM requires a reserved port */ host->h_rpcclnt = clnt; } @@ -223,7 +215,7 @@ { dprintk("lockd: rebind host %s\n", host->h_name); if (host->h_rpcclnt && time_after_eq(jiffies, host->h_nextrebind)) { - host->h_rpcclnt->cl_port = 0; + rpc_do_rebind(host->h_rpcclnt); host->h_nextrebind = jiffies + NLM_HOST_REBIND; } } diff -Nur linux-2.6.0.orig/fs/lockd/mon.c linux-2.6.0/fs/lockd/mon.c --- linux-2.6.0.orig/fs/lockd/mon.c 2003-12-18 10:59:06.000000000 +0800 +++ linux-2.6.0/fs/lockd/mon.c 2003-12-21 22:39:40.000000000 +0800 @@ -103,7 +103,6 @@ static struct rpc_clnt * nsm_create(void) { - struct rpc_xprt *xprt; struct rpc_clnt *clnt = NULL; struct sockaddr_in sin; @@ -111,25 +110,17 @@ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); sin.sin_port = 0; - xprt = xprt_create_proto(IPPROTO_UDP, &sin, NULL); - if (!xprt) - goto out; - - clnt = rpc_create_client(xprt, "localhost", + clnt = rpc_create_client("localhost", IPPROTO_UDP, &sin, &nsm_program, SM_VERSION, RPC_AUTH_NULL); if (!clnt) - goto out_destroy; + goto out; clnt->cl_softrtry = 1; clnt->cl_chatty = 1; clnt->cl_oneshot = 1; - xprt->resvport = 1; /* NSM requires a reserved port */ + clnt->cl_resvport = 1; /* NSM requires a reserved port */ out: return clnt; - -out_destroy: - xprt_destroy(xprt); - goto out; } /* diff -Nur linux-2.6.0.orig/fs/nfs/inode.c linux-2.6.0/fs/nfs/inode.c --- linux-2.6.0.orig/fs/nfs/inode.c 2003-12-18 10:59:18.000000000 +0800 +++ linux-2.6.0/fs/nfs/inode.c 2003-12-21 22:39:40.000000000 +0800 @@ -391,6 +391,7 @@ struct rpc_xprt *xprt = NULL; struct rpc_clnt *clnt = NULL; int tcp = (data->flags & NFS_MOUNT_TCP); + int proto; /* Initialize timeout values */ timeparms.to_initval = data->timeo * HZ / 10; @@ -403,20 +404,17 @@ if (!timeparms.to_retries) timeparms.to_retries = 5; - /* create transport and client */ - xprt = xprt_create_proto(tcp ? IPPROTO_TCP : IPPROTO_UDP, - &server->addr, &timeparms); - if (xprt == NULL) { - printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); - goto out_fail; - } - clnt = rpc_create_client(xprt, server->hostname, &nfs_program, + proto = tcp? IPPROTO_TCP : IPPROTO_UDP; + /* Now create transport and client */ + clnt = rpc_create_client(server->hostname, proto, + &server->addr, &nfs_program, server->rpc_ops->version, data->pseudoflavor); if (clnt == NULL) { printk(KERN_WARNING "NFS: cannot create RPC client.\n"); goto out_fail; } + rpc_set_timeout(clnt, &timeparms); clnt->cl_intr = (server->flags & NFS_MOUNT_INTR) ? 1 : 0; clnt->cl_softrtry = (server->flags & NFS_MOUNT_SOFT) ? 1 : 0; clnt->cl_droppriv = (server->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0; @@ -1340,7 +1338,6 @@ static int nfs4_fill_super(struct super_block *sb, struct nfs4_mount_data *data, int silent) { struct nfs_server *server; - struct rpc_xprt *xprt = NULL; struct rpc_clnt *clnt = NULL; struct rpc_timeout timeparms; rpc_authflavor_t authflavour; @@ -1389,13 +1386,6 @@ return -EINVAL; } - /* Now create transport and client */ - xprt = xprt_create_proto(proto, &server->addr, &timeparms); - if (xprt == NULL) { - printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); - goto out_fail; - } - authflavour = RPC_AUTH_UNIX; if (data->auth_flavourlen != 0) { if (data->auth_flavourlen > 1) @@ -1405,14 +1395,17 @@ goto out_fail; } } - clnt = rpc_create_client(xprt, server->hostname, &nfs_program, - server->rpc_ops->version, authflavour); + + /* Now create transport and client */ + clnt = rpc_create_client(server->hostname, proto, + &server->addr, &nfs_program, + server->rpc_ops->version, authflavour); if (clnt == NULL) { printk(KERN_WARNING "NFS: cannot create RPC client.\n"); - xprt_destroy(xprt); goto out_fail; } + rpc_set_timeout(clnt, &timeparms); clnt->cl_intr = (server->flags & NFS4_MOUNT_INTR) ? 1 : 0; clnt->cl_softrtry = (server->flags & NFS4_MOUNT_SOFT) ? 1 : 0; clnt->cl_chatty = 1; diff -Nur linux-2.6.0.orig/include/linux/sunrpc/clnt.h linux-2.6.0/include/linux/sunrpc/clnt.h --- linux-2.6.0.orig/include/linux/sunrpc/clnt.h 2003-12-18 10:58:57.000000000 +0800 +++ linux-2.6.0/include/linux/sunrpc/clnt.h 2003-12-21 22:39:40.000000000 +0800 @@ -41,6 +41,7 @@ char * cl_server; /* server machine name */ char * cl_protname; /* protocol name */ + struct sockaddr_in cl_addr; /* server address */ struct rpc_auth * cl_auth; /* authenticator */ struct rpc_stat * cl_stats; /* statistics */ @@ -51,9 +52,11 @@ cl_binding : 1,/* doing a getport() */ cl_droppriv : 1,/* enable NFS suid hack */ cl_oneshot : 1,/* dispose after use */ + cl_resvport : 1,/* use a reserved port */ cl_dead : 1;/* abandoned */ struct rpc_rtt cl_rtt; /* RTO estimator data */ + struct rpc_timeout cl_timeout; /* timeout parms */ struct rpc_portmap cl_pmap; /* port mapping */ struct rpc_wait_queue cl_bindwait; /* waiting on getport() */ @@ -62,8 +65,9 @@ char cl_nodename[UNX_MAXNODENAME]; char cl_pathname[30];/* Path in rpc_pipe_fs */ struct dentry * cl_dentry; /* inode */ + + rwlock_t cl_rwlock; }; -#define cl_timeout cl_xprt->timeout #define cl_prog cl_pmap.pm_prog #define cl_vers cl_pmap.pm_vers #define cl_port cl_pmap.pm_port @@ -105,7 +109,8 @@ #ifdef __KERNEL__ -struct rpc_clnt *rpc_create_client(struct rpc_xprt *xprt, char *servname, +struct rpc_clnt *rpc_create_client(char *servname, int proto, + struct sockaddr_in *addr, struct rpc_program *info, u32 version, rpc_authflavor_t authflavor); int rpc_shutdown_client(struct rpc_clnt *); @@ -124,8 +129,25 @@ void rpc_clnt_sigmask(struct rpc_clnt *clnt, sigset_t *oldset); void rpc_clnt_sigunmask(struct rpc_clnt *clnt, sigset_t *oldset); void rpc_setbufsize(struct rpc_clnt *, unsigned int, unsigned int); +void rpc_set_timeout(struct rpc_clnt *, struct rpc_timeout *); + +static inline int rpc_need_rebind(struct rpc_clnt *clnt) +{ + if (!clnt->cl_autobind) + return 0; + smp_rmb(); + return clnt->cl_addr.sin_port == 0; +} + +static inline void rpc_do_rebind(struct rpc_clnt *clnt) +{ + if (clnt->cl_autobind) { + clnt->cl_addr.sin_port = 0; + smp_wmb(); + } +} -static __inline__ +static inline int rpc_call(struct rpc_clnt *clnt, u32 proc, void *argp, void *resp, int flags) { struct rpc_message msg = { diff -Nur linux-2.6.0.orig/include/linux/sunrpc/sched.h linux-2.6.0/include/linux/sunrpc/sched.h --- linux-2.6.0.orig/include/linux/sunrpc/sched.h 2003-12-18 10:59:05.000000000 +0800 +++ linux-2.6.0/include/linux/sunrpc/sched.h 2003-12-21 22:39:40.000000000 +0800 @@ -35,6 +35,7 @@ #endif struct list_head tk_task; /* global list of tasks */ struct rpc_clnt * tk_client; /* RPC client */ + struct rpc_xprt * tk_xprt; /* RPC transport */ struct rpc_rqst * tk_rqstp; /* RPC request */ int tk_status; /* result of last operation */ struct rpc_wait_queue * tk_rpcwait; /* RPC wait queue we're on */ @@ -80,7 +81,6 @@ #endif }; #define tk_auth tk_client->cl_auth -#define tk_xprt tk_client->cl_xprt /* support walking a list of tasks on a wait queue */ #define task_for_each(task, pos, head) \ @@ -119,6 +119,7 @@ #define RPC_ASSASSINATED(t) ((t)->tk_flags & RPC_TASK_KILLED) #define RPC_IS_ACTIVATED(t) ((t)->tk_active) #define RPC_DO_CALLBACK(t) ((t)->tk_callback != NULL) +#define RPC_IS_SOFT(t) ((t)->tk_client->cl_softrtry) #define RPC_TASK_SLEEPING 0 #define RPC_TASK_RUNNING 1 diff -Nur linux-2.6.0.orig/include/linux/sunrpc/xprt.h linux-2.6.0/include/linux/sunrpc/xprt.h --- linux-2.6.0.orig/include/linux/sunrpc/xprt.h 2003-12-18 10:59:15.000000000 +0800 +++ linux-2.6.0/include/linux/sunrpc/xprt.h 2003-12-21 22:39:40.000000000 +0800 @@ -128,10 +128,10 @@ #define XPRT_COPY_DATA (1 << 3) struct rpc_xprt { + struct list_head list; /* Global list */ struct socket * sock; /* BSD socket layer */ struct sock * inet; /* INET layer */ - struct rpc_timeout timeout; /* timeout parms */ struct sockaddr_in addr; /* server address */ int prot; /* IP protocol */ @@ -153,6 +153,8 @@ resvport : 1, /* use a reserved port */ stream : 1; /* TCP */ + atomic_t count; + /* * State of TCP reply receive stuff */ @@ -183,8 +185,8 @@ #ifdef __KERNEL__ struct rpc_xprt * xprt_create_proto(int proto, struct sockaddr_in *addr, - struct rpc_timeout *toparms); -int xprt_destroy(struct rpc_xprt *); + int resvport); +void put_xprt(struct rpc_xprt *); void xprt_shutdown(struct rpc_xprt *); void xprt_default_timeout(struct rpc_timeout *, int); void xprt_set_timeout(struct rpc_timeout *, unsigned int, @@ -200,12 +202,23 @@ int xprt_clear_backlog(struct rpc_xprt *); void xprt_sock_setbufsize(struct rpc_xprt *); +static inline struct rpc_xprt * +get_xprt(struct rpc_xprt *xprt) +{ + if (xprt) + atomic_inc(&xprt->count); + return xprt; +} + + #define XPRT_CONNECT 0 +#define XPRT_DISCONNECT 1 #define xprt_connected(xp) (test_bit(XPRT_CONNECT, &(xp)->sockstate)) #define xprt_set_connected(xp) (set_bit(XPRT_CONNECT, &(xp)->sockstate)) #define xprt_test_and_set_connected(xp) (test_and_set_bit(XPRT_CONNECT, &(xp)->sockstate)) -#define xprt_clear_connected(xp) (clear_bit(XPRT_CONNECT, &(xp)->sockstate)) +#define xprt_set_disconnected(xp) (set_bit(XPRT_DISCONNECT, &(xp)->sockstate)) +#define xprt_disconnected(xp) (test_bit(XPRT_DISCONNECT, &(xp)->sockstate)) #endif /* __KERNEL__*/ diff -Nur linux-2.6.0.orig/net/sunrpc/auth_gss/auth_gss.c linux-2.6.0/net/sunrpc/auth_gss/auth_gss.c --- linux-2.6.0.orig/net/sunrpc/auth_gss/auth_gss.c 2003-12-18 10:58:48.000000000 +0800 +++ linux-2.6.0/net/sunrpc/auth_gss/auth_gss.c 2003-12-21 22:39:40.000000000 +0800 @@ -640,8 +640,7 @@ struct gss_cl_ctx *ctx = gss_cred_get_ctx(cred); u32 *cred_len; struct rpc_rqst *req = task->tk_rqstp; - struct rpc_clnt *clnt = task->tk_client; - struct rpc_xprt *xprt = clnt->cl_xprt; + struct rpc_xprt *xprt = task->tk_xprt; u32 *verfbase = req->rq_svec[0].iov_base; u32 maj_stat = 0; struct xdr_netobj bufin,bufout; @@ -706,11 +705,10 @@ struct rpc_clnt *clnt = task->tk_client; struct gss_auth *gss_auth = container_of(clnt->cl_auth, struct gss_auth, rpc_auth); - struct rpc_xprt *xprt = task->tk_xprt; struct rpc_cred *cred = task->tk_msg.rpc_cred; int err = 0; - task->tk_timeout = xprt->timeout.to_current; + task->tk_timeout = clnt->cl_timeout.to_current; spin_lock(&gss_auth->lock); if (gss_cred_get_uptodate_ctx(cred)) goto out; diff -Nur linux-2.6.0.orig/net/sunrpc/clnt.c linux-2.6.0/net/sunrpc/clnt.c --- linux-2.6.0.orig/net/sunrpc/clnt.c 2003-12-18 10:58:45.000000000 +0800 +++ linux-2.6.0/net/sunrpc/clnt.c 2003-12-21 22:39:40.000000000 +0800 @@ -51,6 +51,7 @@ static void call_encode(struct rpc_task *task); static void call_decode(struct rpc_task *task); static void call_bind(struct rpc_task *task); +static void call_xprt(struct rpc_task *task); static void call_transmit(struct rpc_task *task); static void call_status(struct rpc_task *task); static void call_refresh(struct rpc_task *task); @@ -95,43 +96,42 @@ * made to sleep too long. */ struct rpc_clnt * -rpc_create_client(struct rpc_xprt *xprt, char *servname, +rpc_create_client(char *servname, int proto, struct sockaddr_in *addr, struct rpc_program *program, u32 vers, rpc_authflavor_t flavor) { struct rpc_version *version; - struct rpc_clnt *clnt = NULL; + struct rpc_clnt *clnt; - dprintk("RPC: creating %s client for %s (xprt %p)\n", - program->name, servname, xprt); + dprintk("RPC: creating %s client for %s\n", program->name, servname); - if (!xprt) - goto out; if (vers >= program->nrvers || !(version = program->version[vers])) - goto out; + return NULL; - clnt = (struct rpc_clnt *) kmalloc(sizeof(*clnt), GFP_KERNEL); + clnt = (struct rpc_clnt *)kmalloc(sizeof(*clnt), GFP_KERNEL); if (!clnt) goto out_no_clnt; memset(clnt, 0, sizeof(*clnt)); atomic_set(&clnt->cl_users, 0); - clnt->cl_xprt = xprt; clnt->cl_procinfo = version->procs; clnt->cl_maxproc = version->nrprocs; clnt->cl_server = servname; clnt->cl_protname = program->name; - clnt->cl_port = xprt->addr.sin_port; - clnt->cl_prog = program->number; - clnt->cl_vers = version->number; - clnt->cl_prot = xprt->prot; + memcpy(&clnt->cl_addr, addr, sizeof(clnt->cl_addr)); + clnt->cl_pmap.pm_port = addr->sin_port; + clnt->cl_pmap.pm_prog = program->number; + clnt->cl_pmap.pm_vers = version->number; + clnt->cl_pmap.pm_prot = proto; clnt->cl_stats = program->stats; INIT_RPC_WAITQ(&clnt->cl_bindwait, "bindwait"); + rwlock_init(&clnt->cl_rwlock); - if (!clnt->cl_port) + if (!clnt->cl_addr.sin_port) clnt->cl_autobind = 1; - rpc_init_rtt(&clnt->cl_rtt, xprt->timeout.to_initval); + if (capable(CAP_NET_BIND_SERVICE)) + clnt->cl_resvport = 1; if (rpc_setup_pipedir(clnt, program->pipe_dir_name) < 0) goto out_no_path; @@ -161,6 +161,13 @@ goto out; } +void +rpc_set_timeout(struct rpc_clnt *clnt, struct rpc_timeout *timeout) +{ + memcpy(&clnt->cl_timeout, timeout, sizeof(clnt->cl_timeout)); + rpc_init_rtt(&clnt->cl_rtt, clnt->cl_timeout.to_initval); +} + /* * Properly shut down an RPC client, terminating all outstanding * requests. Note that we must be certain that cl_oneshot and @@ -209,10 +216,8 @@ } if (clnt->cl_pathname[0]) rpc_rmdir(clnt->cl_pathname); - if (clnt->cl_xprt) { - xprt_destroy(clnt->cl_xprt); - clnt->cl_xprt = NULL; - } + if (clnt->cl_xprt) + put_xprt(clnt->cl_xprt); kfree(clnt); return 0; } @@ -377,6 +382,7 @@ void rpc_setbufsize(struct rpc_clnt *clnt, unsigned int sndsize, unsigned int rcvsize) { +#if 0 struct rpc_xprt *xprt = clnt->cl_xprt; xprt->sndsize = 0; @@ -387,6 +393,7 @@ xprt->rcvsize = rcvsize + RPC_SLACK_SPACE; if (xprt_connected(xprt)) xprt_sock_setbufsize(xprt); +#endif } /* @@ -420,11 +427,11 @@ /* Increment call count */ task->tk_msg.rpc_proc->p_count++; clnt->cl_stats->rpccnt++; - task->tk_action = call_reserve; + task->tk_action = call_bind; } /* - * 1. Reserve an RPC call slot + * 2. Reserve an RPC call slot */ static void call_reserve(struct rpc_task *task) @@ -436,13 +443,18 @@ return; } + if (task->tk_rqstp) { + task->tk_action = call_allocate; + return; + } + task->tk_status = 0; task->tk_action = call_reserveresult; xprt_reserve(task); } /* - * 1b. Grok the result of xprt_reserve() + * 2b. Grok the result of xprt_reserve() */ static void call_reserveresult(struct rpc_task *task) @@ -480,6 +492,9 @@ } switch (status) { + case -ENOTCONN: + task->tk_action = call_bind; + return; case -EAGAIN: /* woken up; retry */ task->tk_action = call_reserve; return; @@ -494,7 +509,7 @@ } /* - * 2. Allocate the buffer. For details, see sched.c:rpc_malloc. + * 3. Allocate the buffer. For details, see sched.c:rpc_malloc. * (Note: buffer memory is freed in rpc_task_release). */ static void @@ -504,7 +519,7 @@ dprintk("RPC: %4d call_allocate (status %d)\n", task->tk_pid, task->tk_status); - task->tk_action = call_bind; + task->tk_action = call_transmit; if (task->tk_buffer) return; @@ -527,7 +542,7 @@ } /* - * 3. Encode arguments of an RPC call + * 4. Encode arguments of an RPC call */ static void call_encode(struct rpc_task *task) @@ -575,39 +590,91 @@ } /* - * 4. Get the server port number if not yet set + * 1. Get the server port number if not yet set */ static void call_bind(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; - struct rpc_xprt *xprt = clnt->cl_xprt; + int status = task->tk_status; - dprintk("RPC: %4d call_bind xprt %p %s connected\n", task->tk_pid, - xprt, (xprt_connected(xprt) ? "is" : "is not")); + dprintk("RPC: %4d call_bind\n", task->tk_pid); - task->tk_action = (xprt_connected(xprt)) ? call_transmit : call_connect; + task->tk_status = 0; + if (rpc_need_rebind(clnt)) { + if (status >= 0 || !RPC_IS_SOFT(task)) { + task->tk_timeout = RPC_CONNECT_TIMEOUT; + rpc_getport(task, clnt); + } else + rpc_exit(task, -EIO); + } else + task->tk_action = call_xprt; +} - if (!clnt->cl_port) { - task->tk_action = call_connect; - task->tk_timeout = RPC_CONNECT_TIMEOUT; - rpc_getport(task, clnt); +/* + * 1a. Bind the task to the current RPC transport + */ +static void call_xprt(struct rpc_task *task) +{ + struct rpc_clnt *clnt = task->tk_client; + struct rpc_xprt *new, *xprt; + struct sockaddr_in addr; + + /* Check the common case: the client has a valid RPC transport */ + read_lock(&clnt->cl_rwlock); + xprt = get_xprt(clnt->cl_xprt); + read_unlock(&clnt->cl_rwlock); + if (likely(xprt != NULL)) { + if (!xprt_disconnected(xprt)) + goto out; + put_xprt(xprt); + } + + /* Sigh... We need to create and install a new RPC transport */ + memcpy(&addr, &clnt->cl_addr, sizeof(addr)); + /* Hey, has someone asked us to bind to a new port too? */ + if (addr.sin_port == 0) { + task->tk_action = call_bind; + return; + } + new = xprt_create_proto(clnt->cl_prot, &addr, clnt->cl_resvport); + if (new == NULL) { + rpc_exit(task, -EIO); + return; + } + write_lock(&clnt->cl_rwlock); + xprt = clnt->cl_xprt; + if (xprt == NULL || xprt_disconnected(xprt)) { + clnt->cl_xprt = new; + new = xprt; + } + xprt = get_xprt(clnt->cl_xprt); + write_unlock(&clnt->cl_rwlock); + if (new != NULL) + put_xprt(new); +out: + if (task->tk_xprt) { + /* Release the old slot if the transports differ */ + if (unlikely(xprt != task->tk_xprt)) + xprt_release(task); + else + put_xprt(xprt); } + task->tk_xprt = xprt; + task->tk_action = call_connect; } /* - * 4a. Connect to the RPC server (TCP case) + * 1b. Connect to the RPC server (TCP case) */ static void call_connect(struct rpc_task *task) { - struct rpc_clnt *clnt = task->tk_client; - dprintk("RPC: %4d call_connect status %d\n", task->tk_pid, task->tk_status); - if (xprt_connected(clnt->cl_xprt)) { - task->tk_action = call_transmit; + if (xprt_connected(task->tk_xprt)) { + task->tk_action = call_reserve; return; } task->tk_action = call_connect_status; @@ -617,7 +684,7 @@ } /* - * 4b. Sort out connect result + * 1c. Sort out connect result */ static void call_connect_status(struct rpc_task *task) @@ -628,19 +695,23 @@ task->tk_status = 0; if (status >= 0) { clnt->cl_stats->netreconn++; - task->tk_action = call_transmit; + task->tk_action = call_reserve; return; } /* Something failed: we may have to rebind */ - if (clnt->cl_autobind) - clnt->cl_port = 0; + rpc_do_rebind(clnt); switch (status) { case -ENOTCONN: case -ETIMEDOUT: case -EAGAIN: - task->tk_action = (clnt->cl_port == 0) ? call_bind : call_connect; - break; + if (!RPC_IS_SOFT(task)) { + if (rpc_need_rebind(clnt)) + task->tk_action = call_bind; + else + task->tk_action = call_xprt; + break; + } default: rpc_exit(task, -EIO); } @@ -704,9 +775,7 @@ break; case -ECONNREFUSED: case -ENOTCONN: - req->rq_bytes_sent = 0; - if (clnt->cl_autobind) - clnt->cl_port = 0; + rpc_do_rebind(clnt); task->tk_action = call_bind; break; case -EAGAIN: @@ -743,7 +812,7 @@ to->to_retries = clnt->cl_timeout.to_retries; dprintk("RPC: %4d call_timeout (major)\n", task->tk_pid); - if (clnt->cl_softrtry) { + if (RPC_IS_SOFT(task)) { if (clnt->cl_chatty) printk(KERN_NOTICE "%s: server %s not responding, timed out\n", clnt->cl_protname, clnt->cl_server); @@ -756,8 +825,7 @@ printk(KERN_NOTICE "%s: server %s not responding, still trying\n", clnt->cl_protname, clnt->cl_server); } - if (clnt->cl_autobind) - clnt->cl_port = 0; + rpc_do_rebind(clnt); retry: clnt->cl_stats->rpcretrans++; @@ -786,7 +854,7 @@ } if (task->tk_status < 12) { - if (!clnt->cl_softrtry) { + if (!RPC_IS_SOFT(task)) { task->tk_action = call_bind; clnt->cl_stats->rpcretrans++; goto out_retry; @@ -861,7 +929,7 @@ task->tk_pid, task->tk_status); task->tk_status = 0; - task->tk_action = call_reserve; + task->tk_action = call_bind; if (status >= 0 && rpcauth_uptodatecred(task)) return; if (rpcauth_deadcred(task)) { @@ -881,7 +949,7 @@ call_header(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; - struct rpc_xprt *xprt = clnt->cl_xprt; + struct rpc_xprt *xprt = task->tk_xprt; struct rpc_rqst *req = task->tk_rqstp; u32 *p = req->rq_svec[0].iov_base; diff -Nur linux-2.6.0.orig/net/sunrpc/pmap_clnt.c linux-2.6.0/net/sunrpc/pmap_clnt.c --- linux-2.6.0.orig/net/sunrpc/pmap_clnt.c 2003-12-18 10:58:49.000000000 +0800 +++ linux-2.6.0/net/sunrpc/pmap_clnt.c 2003-12-21 22:39:40.000000000 +0800 @@ -32,7 +32,6 @@ static struct rpc_clnt * pmap_create(char *, struct sockaddr_in *, int); static void pmap_getport_done(struct rpc_task *); extern struct rpc_program pmap_program; -static spinlock_t pmap_lock = SPIN_LOCK_UNLOCKED; /* * Obtain the port for a given RPC service on a given host. This one can @@ -42,11 +41,11 @@ rpc_getport(struct rpc_task *task, struct rpc_clnt *clnt) { struct rpc_portmap *map = &clnt->cl_pmap; - struct sockaddr_in *sap = &clnt->cl_xprt->addr; + struct sockaddr_in *sap = &clnt->cl_addr; struct rpc_message msg = { .rpc_proc = &pmap_procedures[PMAP_GETPORT], .rpc_argp = map, - .rpc_resp = &clnt->cl_port, + .rpc_resp = &map->pm_port, .rpc_cred = NULL }; struct rpc_clnt *pmap_clnt; @@ -56,18 +55,19 @@ task->tk_pid, clnt->cl_server, map->pm_prog, map->pm_vers, map->pm_prot); - spin_lock(&pmap_lock); + write_lock(&clnt->cl_rwlock); if (clnt->cl_binding) { rpc_sleep_on(&clnt->cl_bindwait, task, NULL, 0); - spin_unlock(&pmap_lock); + write_unlock(&clnt->cl_rwlock); return; } clnt->cl_binding = 1; - spin_unlock(&pmap_lock); + write_unlock(&clnt->cl_rwlock); task->tk_status = -EACCES; /* why set this? returns -EIO below */ if (!(pmap_clnt = pmap_create(clnt->cl_server, sap, map->pm_prot))) goto bailout; + pmap_clnt->cl_resvport = 0; task->tk_status = 0; /* @@ -84,10 +84,10 @@ return; bailout: - spin_lock(&pmap_lock); + write_lock(&clnt->cl_rwlock); clnt->cl_binding = 0; + write_unlock(&clnt->cl_rwlock); rpc_wake_up(&clnt->cl_bindwait); - spin_unlock(&pmap_lock); task->tk_status = -EIO; task->tk_action = NULL; } @@ -112,6 +112,7 @@ sprintf(hostname, "%u.%u.%u.%u", NIPQUAD(sin->sin_addr.s_addr)); if (!(pmap_clnt = pmap_create(hostname, sin, prot))) return -EACCES; + pmap_clnt->cl_resvport = 0; /* Setup the call info struct */ status = rpc_call(pmap_clnt, PMAP_GETPORT, &map, &map.pm_port, 0); @@ -129,25 +130,34 @@ pmap_getport_done(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; + struct rpc_portmap *map = &clnt->cl_pmap; + struct rpc_xprt *xprt; dprintk("RPC: %4d pmap_getport_done(status %d, port %d)\n", - task->tk_pid, task->tk_status, clnt->cl_port); + task->tk_pid, task->tk_status, map->pm_port); + write_lock(&clnt->cl_rwlock); if (task->tk_status < 0) { /* Make the calling task exit with an error */ task->tk_action = NULL; - } else if (clnt->cl_port == 0) { + } else if (map->pm_port == 0) { /* Program not registered */ task->tk_status = -EACCES; task->tk_action = NULL; } else { /* byte-swap port number first */ - clnt->cl_port = htons(clnt->cl_port); - clnt->cl_xprt->addr.sin_port = clnt->cl_port; + clnt->cl_addr.sin_port = htons(map->pm_port); + wmb(); } - spin_lock(&pmap_lock); + xprt = clnt->cl_xprt; + if (xprt && xprt->addr.sin_port != clnt->cl_addr.sin_port) + clnt->cl_xprt = NULL; + else + xprt = NULL; clnt->cl_binding = 0; + write_unlock(&clnt->cl_rwlock); rpc_wake_up(&clnt->cl_bindwait); - spin_unlock(&pmap_lock); + if (xprt) + put_xprt(xprt); } /* @@ -194,21 +204,13 @@ static struct rpc_clnt * pmap_create(char *hostname, struct sockaddr_in *srvaddr, int proto) { - struct rpc_xprt *xprt; struct rpc_clnt *clnt; - /* printk("pmap: create xprt\n"); */ - if (!(xprt = xprt_create_proto(proto, srvaddr, NULL))) - return NULL; - xprt->addr.sin_port = htons(RPC_PMAP_PORT); - - /* printk("pmap: create clnt\n"); */ - clnt = rpc_create_client(xprt, hostname, + clnt = rpc_create_client(hostname, proto, srvaddr, &pmap_program, RPC_PMAP_VERSION, RPC_AUTH_NULL); - if (!clnt) { - xprt_destroy(xprt); - } else { + if (clnt) { + clnt->cl_addr.sin_port = htons(RPC_PMAP_PORT); clnt->cl_softrtry = 1; clnt->cl_chatty = 1; clnt->cl_oneshot = 1; diff -Nur linux-2.6.0.orig/net/sunrpc/rpc_pipe.c linux-2.6.0/net/sunrpc/rpc_pipe.c --- linux-2.6.0.orig/net/sunrpc/rpc_pipe.c 2003-12-18 10:58:39.000000000 +0800 +++ linux-2.6.0/net/sunrpc/rpc_pipe.c 2003-12-21 22:39:40.000000000 +0800 @@ -257,9 +257,9 @@ seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_protname, clnt->cl_prog, clnt->cl_vers); seq_printf(m, "address: %u.%u.%u.%u\n", - NIPQUAD(clnt->cl_xprt->addr.sin_addr.s_addr)); + NIPQUAD(clnt->cl_addr.sin_addr)); seq_printf(m, "protocol: %s\n", - clnt->cl_xprt->prot == IPPROTO_UDP ? "udp" : "tcp"); + clnt->cl_prot == IPPROTO_UDP ? "udp" : "tcp"); return 0; } diff -Nur linux-2.6.0.orig/net/sunrpc/sunrpc_syms.c linux-2.6.0/net/sunrpc/sunrpc_syms.c --- linux-2.6.0.orig/net/sunrpc/sunrpc_syms.c 2003-12-18 10:59:29.000000000 +0800 +++ linux-2.6.0/net/sunrpc/sunrpc_syms.c 2003-12-21 22:39:40.000000000 +0800 @@ -52,6 +52,9 @@ EXPORT_SYMBOL(rpc_delay); EXPORT_SYMBOL(rpc_restart_call); EXPORT_SYMBOL(rpc_setbufsize); +EXPORT_SYMBOL(rpc_set_timeout); + +/* RPC pipefs functions */ EXPORT_SYMBOL(rpc_unlink); EXPORT_SYMBOL(rpc_wake_up); EXPORT_SYMBOL(rpc_queue_upcall); @@ -59,8 +62,7 @@ /* Client transport */ EXPORT_SYMBOL(xprt_create_proto); -EXPORT_SYMBOL(xprt_destroy); -EXPORT_SYMBOL(xprt_set_timeout); +EXPORT_SYMBOL(put_xprt); /* Client credential cache */ EXPORT_SYMBOL(rpcauth_register); diff -Nur linux-2.6.0.orig/net/sunrpc/xprt.c linux-2.6.0/net/sunrpc/xprt.c --- linux-2.6.0.orig/net/sunrpc/xprt.c 2003-12-18 10:59:05.000000000 +0800 +++ linux-2.6.0/net/sunrpc/xprt.c 2003-12-21 22:41:02.000000000 +0800 @@ -84,11 +84,15 @@ static void xprt_disconnect(struct rpc_xprt *); 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); + int resvport); +static struct socket *xprt_create_socket(int, int); static void xprt_bind_socket(struct rpc_xprt *, struct socket *); static int __xprt_get_cong(struct rpc_xprt *, struct rpc_task *); +/* Global list of xprt structs */ +static LIST_HEAD(all_xprts); +static spinlock_t xprt_global_lock = SPIN_LOCK_UNLOCKED; + #ifdef RPC_DEBUG_DATA /* * Print the buffer contents (first 128 bytes only--just enough for @@ -139,6 +143,10 @@ { struct rpc_rqst *req = task->tk_rqstp; + if (xprt_disconnected(xprt)) { + task->tk_status = -ENOTCONN; + return 0; + } if (!xprt->snd_task) { if (xprt->nocong || __xprt_get_cong(xprt, task)) { xprt->snd_task = task; @@ -387,7 +395,6 @@ sk->sk_write_space = xprt->old_write_space; write_unlock_bh(&sk->sk_callback_lock); - xprt_disconnect(xprt); sk->sk_no_check = 0; sock_release(sock); @@ -401,8 +408,10 @@ { dprintk("RPC: disconnected transport %p\n", xprt); spin_lock_bh(&xprt->sock_lock); - xprt_clear_connected(xprt); + xprt_set_disconnected(xprt); rpc_wake_up_status(&xprt->pending, -ENOTCONN); + rpc_wake_up_status(&xprt->sending, -ENOTCONN); + rpc_wake_up_status(&xprt->resend, -ENOTCONN); spin_unlock_bh(&xprt->sock_lock); } @@ -434,23 +443,6 @@ if (xprt_connected(xprt)) goto out_write; - 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; @@ -477,8 +469,7 @@ 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)) + if (xprt_disconnected(xprt)) task->tk_timeout = RPC_REESTABLISH_TIMEOUT; rpc_sleep_on(&xprt->pending, task, xprt_connect_status, NULL); @@ -488,7 +479,7 @@ case -ECONNREFUSED: case -ECONNRESET: case -ENOTCONN: - if (!task->tk_client->cl_softrtry) { + if (!RPC_IS_SOFT(task)) { rpc_delay(task, RPC_REESTABLISH_TIMEOUT); task->tk_status = -ENOTCONN; break; @@ -496,7 +487,7 @@ default: /* Report myriad other possible returns. If this file * system is soft mounted, just error out, like Solaris. */ - if (task->tk_client->cl_softrtry) { + if (RPC_IS_SOFT(task)) { printk(KERN_WARNING "RPC: error %d connecting to server %s, exiting\n", -status, task->tk_client->cl_server); @@ -530,7 +521,7 @@ } /* if soft mounted, just cause this RPC to fail */ - if (task->tk_client->cl_softrtry) + if (RPC_IS_SOFT(task)) task->tk_status = -EIO; switch (task->tk_status) { @@ -998,12 +989,10 @@ } spin_unlock_bh(&xprt->sock_lock); break; - case TCP_SYN_SENT: - case TCP_SYN_RECV: - break; - default: + case TCP_CLOSING: + case TCP_CLOSE_WAIT: + case TCP_CLOSE: xprt_disconnect(xprt); - break; } out: if (sk->sk_sleep && waitqueue_active(sk->sk_sleep)) @@ -1101,12 +1090,7 @@ goto out_unlock; } if (!__xprt_lock_write(xprt, task)) { - err = -EAGAIN; - goto out_unlock; - } - - if (!xprt_connected(xprt)) { - err = -ENOTCONN; + err = task->tk_status; goto out_unlock; } out_unlock: @@ -1187,6 +1171,9 @@ * hence there is no danger of the waking up task being put on * schedq, and being picked up by a parallel run of rpciod(). */ + if (req->rq_received && !req->rq_bytes_sent) + goto out_release; + task->tk_status = status; switch (status) { @@ -1195,7 +1182,7 @@ /* Protect against races with xprt_write_space */ spin_lock_bh(&xprt->sock_lock); /* Don't race with disconnect */ - if (!xprt_connected(xprt)) + if (xprt_disconnected(xprt)) task->tk_status = -ENOTCONN; else if (test_bit(SOCK_NOSPACE, &xprt->sock->flags)) { task->tk_timeout = req->rq_timeout.to_current; @@ -1216,9 +1203,10 @@ if (xprt->stream) xprt_disconnect(xprt); } +out_release: xprt_release_write(xprt, task); return; - out_receive: +out_receive: dprintk("RPC: %4d xmit complete\n", task->tk_pid); /* Set the task's receive timeout value */ spin_lock_bh(&xprt->sock_lock); @@ -1233,7 +1221,7 @@ } else task->tk_timeout = req->rq_timeout.to_current; /* Don't race with disconnect */ - if (!xprt_connected(xprt)) + if (xprt_disconnected(xprt)) task->tk_status = -ENOTCONN; else if (!req->rq_received) rpc_sleep_on(&xprt->pending, task, NULL, xprt_timer); @@ -1308,7 +1296,7 @@ { struct rpc_rqst *req = task->tk_rqstp; - req->rq_timeout = xprt->timeout; + memcpy(&req->rq_timeout, &task->tk_client->cl_timeout, sizeof(req->rq_timeout)); req->rq_task = task; req->rq_xprt = xprt; req->rq_xid = xprt_alloc_xid(); @@ -1326,8 +1314,10 @@ struct rpc_xprt *xprt = task->tk_xprt; struct rpc_rqst *req; - if (!(req = task->tk_rqstp)) + if (!xprt) return; + if (!(req = task->tk_rqstp)) + goto out; spin_lock_bh(&xprt->sock_lock); __xprt_release_write(xprt, task); __xprt_put_cong(xprt, req); @@ -1345,6 +1335,9 @@ xprt_clear_backlog(xprt); spin_unlock(&xprt->xprt_lock); +out: + put_xprt(xprt); + task->tk_xprt = NULL; } /* @@ -1373,14 +1366,35 @@ to->to_exponential = 0; } +struct rpc_xprt * +__xprt_find(int proto, struct sockaddr_in *ap, int resvport) +{ + struct rpc_xprt *xprt; + + list_for_each_entry(xprt, &all_xprts, list) { + if (xprt_disconnected(xprt)) + continue; + if (proto != xprt->prot) + continue; + if (memcmp(&xprt->addr, ap, sizeof(xprt->addr))) + continue; + if (resvport != xprt->resvport) + continue; + atomic_inc(&xprt->count); + return xprt; + } + return NULL; +} + /* * Initialize an RPC client */ static struct rpc_xprt * -xprt_setup(int proto, struct sockaddr_in *ap, struct rpc_timeout *to) +xprt_setup(int proto, struct sockaddr_in *ap, int resvport) { struct rpc_xprt *xprt; struct rpc_rqst *req; + struct socket *sock; int i; dprintk("RPC: setting up %s transport...\n", @@ -1390,7 +1404,7 @@ return NULL; memset(xprt, 0, sizeof(*xprt)); /* Nnnngh! */ - xprt->addr = *ap; + memcpy(&xprt->addr, ap, sizeof(xprt->addr)); xprt->prot = proto; xprt->stream = (proto == IPPROTO_TCP)? 1 : 0; if (xprt->stream) { @@ -1402,14 +1416,10 @@ spin_lock_init(&xprt->xprt_lock); init_waitqueue_head(&xprt->cong_wait); - INIT_LIST_HEAD(&xprt->recv); + atomic_set(&xprt->count, 1); - /* Set timeout parameters */ - if (to) { - xprt->timeout = *to; - xprt->timeout.to_current = to->to_initval; - } else - xprt_default_timeout(&xprt->timeout, xprt->prot); + INIT_LIST_HEAD(&xprt->list); + INIT_LIST_HEAD(&xprt->recv); INIT_RPC_WAITQ(&xprt->pending, "xprt_pending"); INIT_RPC_WAITQ(&xprt->sending, "xprt_sending"); @@ -1422,12 +1432,20 @@ req->rq_next = NULL; xprt->free = xprt->slot; - /* Check whether we want to use a reserved port */ - xprt->resvport = capable(CAP_NET_BIND_SERVICE) ? 1 : 0; + xprt->resvport = resvport; + + /* Create the socket */ + sock = xprt_create_socket(xprt->prot, resvport); + if (!sock) + goto out_nosock; + xprt_bind_socket(xprt, sock); dprintk("RPC: created transport %p\n", xprt); return xprt; +out_nosock: + kfree(xprt); + return NULL; } /* @@ -1483,7 +1501,6 @@ tp->nonagle = 1; /* disable Nagle's algorithm */ sk->sk_data_ready = tcp_data_ready; sk->sk_state_change = tcp_state_change; - xprt_clear_connected(xprt); } sk->sk_write_space = xprt_write_space; @@ -1491,8 +1508,6 @@ xprt->sock = sock; xprt->inet = sk; write_unlock_bh(&sk->sk_callback_lock); - - return; } /* @@ -1521,7 +1536,7 @@ * and connect stream sockets. */ static struct socket * -xprt_create_socket(int proto, struct rpc_timeout *to, int resvport) +xprt_create_socket(int proto, int resvport) { struct socket *sock; int type, err; @@ -1553,20 +1568,35 @@ * Create an RPC client transport given the protocol and peer address. */ struct rpc_xprt * -xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to) +xprt_create_proto(int proto, struct sockaddr_in *sap, int resvport) { - struct rpc_xprt *xprt; + struct rpc_xprt *xprt, *new_xprt = NULL; - xprt = xprt_setup(proto, sap, to); - if (!xprt) - goto out_bad; + spin_lock(&xprt_global_lock); + do { + xprt = __xprt_find(proto, sap, resvport); + if (xprt) + break; + if (!new_xprt) { + spin_unlock(&xprt_global_lock); + new_xprt = xprt_setup(proto, sap, resvport); + if (!new_xprt) + goto out_bad; + spin_lock(&xprt_global_lock); + continue; + } + list_add(&new_xprt->list, &all_xprts); + xprt = new_xprt; + new_xprt = NULL; + } while (xprt == NULL); + spin_unlock(&xprt_global_lock); + if (new_xprt) + put_xprt(new_xprt); dprintk("RPC: xprt_create_proto created xprt %p\n", xprt); return xprt; out_bad: dprintk("RPC: xprt_create_proto failed\n"); - if (xprt) - kfree(xprt); return NULL; } @@ -1597,15 +1627,18 @@ } /* - * Destroy an RPC transport, killing off all requests. + * Release an RPC transport. */ -int -xprt_destroy(struct rpc_xprt *xprt) +void +put_xprt(struct rpc_xprt *xprt) { + if (!atomic_dec_and_lock(&xprt->count, &xprt_global_lock)) + return; + if (!list_empty(&xprt->list)) + list_del(&xprt->list); + spin_unlock(&xprt_global_lock); dprintk("RPC: destroying transport %p\n", xprt); xprt_shutdown(xprt); xprt_close(xprt); kfree(xprt); - - return 0; }