diff -Nur linux-2.4.22.orig/fs/lockd/host.c linux-2.4.22/fs/lockd/host.c --- linux-2.4.22.orig/fs/lockd/host.c 2003-08-25 19:44:43.000000000 +0800 +++ linux-2.4.22/fs/lockd/host.c 2003-12-21 15:25:45.000000000 +0800 @@ -26,7 +26,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.4.22.orig/fs/lockd/mon.c linux-2.4.22/fs/lockd/mon.c --- linux-2.4.22.orig/fs/lockd/mon.c 2003-08-25 19:44:43.000000000 +0800 +++ linux-2.4.22/fs/lockd/mon.c 2003-12-21 15:25:45.000000000 +0800 @@ -102,7 +102,6 @@ static struct rpc_clnt * nsm_create(void) { - struct rpc_xprt *xprt; struct rpc_clnt *clnt = NULL; struct sockaddr_in sin; @@ -110,25 +109,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.4.22.orig/fs/nfs/inode.c linux-2.4.22/fs/nfs/inode.c --- linux-2.4.22.orig/fs/nfs/inode.c 2002-11-29 07:53:15.000000000 +0800 +++ linux-2.4.22/fs/nfs/inode.c 2003-12-21 15:47:43.000000000 +0800 @@ -261,7 +261,6 @@ { struct nfs_mount_data *data = (struct nfs_mount_data *) raw_data; struct nfs_server *server; - struct rpc_xprt *xprt = NULL; struct rpc_clnt *clnt = NULL; struct nfs_fh *root = &data->root, fh; struct inode *root_inode = NULL; @@ -270,6 +269,7 @@ struct rpc_timeout timeparms; struct nfs_fsinfo fsinfo; int tcp, version, maxlen; + int proto; memset(&sb->u.nfs_sb, 0, sizeof(sb->u.nfs_sb)); if (!data) @@ -359,12 +359,6 @@ if (!timeparms.to_retries) timeparms.to_retries = 5; - /* Now create transport and client */ - xprt = xprt_create_proto(tcp? IPPROTO_TCP : IPPROTO_UDP, - &srvaddr, &timeparms); - if (xprt == NULL) - goto out_no_xprt; - /* Choose authentication flavor */ authflavor = RPC_AUTH_UNIX; if (data->flags & NFS_MOUNT_SECURE) @@ -372,11 +366,18 @@ else if (data->flags & NFS_MOUNT_KERBEROS) authflavor = RPC_AUTH_KRB; - clnt = rpc_create_client(xprt, server->hostname, &nfs_program, - version, authflavor); - if (clnt == NULL) + 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, authflavor); + + if (clnt == NULL) { + printk(KERN_WARNING "NFS: cannot create RPC client.\n"); goto out_no_client; + } + rpc_set_timeout(clnt, &timeparms); clnt->cl_intr = (data->flags & NFS_MOUNT_INTR)? 1 : 0; clnt->cl_softrtry = (data->flags & NFS_MOUNT_SOFT)? 1 : 0; clnt->cl_droppriv = (data->flags & NFS_MOUNT_BROKEN_SUID) ? 1 : 0; @@ -497,12 +498,8 @@ out_no_client: printk(KERN_WARNING "NFS: cannot create RPC client.\n"); - xprt_destroy(xprt); goto out_free_host; -out_no_xprt: - printk(KERN_WARNING "NFS: cannot create RPC transport.\n"); - out_free_host: nfs_reqlist_free(server); kfree(server->hostname); diff -Nur linux-2.4.22.orig/include/linux/sunrpc/clnt.h linux-2.4.22/include/linux/sunrpc/clnt.h --- linux-2.4.22.orig/include/linux/sunrpc/clnt.h 2002-11-29 07:53:15.000000000 +0800 +++ linux-2.4.22/include/linux/sunrpc/clnt.h 2003-12-21 15:53:51.000000000 +0800 @@ -38,6 +38,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 */ @@ -48,19 +49,22 @@ 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 */ unsigned int cl_flags; /* misc client flags */ unsigned long cl_hardmax; /* max hard timeout */ 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() */ int cl_nodelen; /* nodename length */ char cl_nodename[UNX_MAXNODENAME]; + + 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 @@ -108,7 +112,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, int authflavor); int rpc_shutdown_client(struct rpc_clnt *); @@ -127,8 +132,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 = { proc, argp, resp, NULL }; diff -Nur linux-2.4.22.orig/include/linux/sunrpc/sched.h linux-2.4.22/include/linux/sunrpc/sched.h --- linux-2.4.22.orig/include/linux/sunrpc/sched.h 2002-11-29 07:53:15.000000000 +0800 +++ linux-2.4.22/include/linux/sunrpc/sched.h 2003-12-21 15:25:46.000000000 +0800 @@ -40,6 +40,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 */ @@ -82,7 +83,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) \ @@ -121,6 +121,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.4.22.orig/include/linux/sunrpc/xprt.h linux-2.4.22/include/linux/sunrpc/xprt.h --- linux-2.4.22.orig/include/linux/sunrpc/xprt.h 2003-08-25 19:44:44.000000000 +0800 +++ linux-2.4.22/include/linux/sunrpc/xprt.h 2003-12-21 15:25:46.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, @@ -199,12 +201,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.4.22.orig/net/sunrpc/clnt.c linux-2.4.22/net/sunrpc/clnt.c --- linux-2.4.22.orig/net/sunrpc/clnt.c 2003-08-25 19:44:44.000000000 +0800 +++ linux-2.4.22/net/sunrpc/clnt.c 2003-12-23 21:53:27.000000000 +0800 @@ -50,6 +50,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); @@ -69,21 +70,18 @@ * 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, int 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 *) rpc_allocate(0, sizeof(*clnt)); + clnt = (struct rpc_clnt *)kmalloc(sizeof(*clnt), GFP_KERNEL); if (!clnt) goto out_no_clnt; memset(clnt, 0, sizeof(*clnt)); @@ -94,17 +92,22 @@ 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; + + rpc_init_rtt(&clnt->cl_rtt, clnt->cl_timeout.to_initval); if (!rpcauth_create(flavor, clnt)) goto out_no_auth; @@ -128,6 +131,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 @@ -166,10 +176,6 @@ rpcauth_destroy(clnt->cl_auth); clnt->cl_auth = NULL; } - if (clnt->cl_xprt) { - xprt_destroy(clnt->cl_xprt); - clnt->cl_xprt = NULL; - } rpc_free(clnt); return 0; } @@ -334,6 +340,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; @@ -343,6 +350,7 @@ if (rcvsize) xprt->rcvsize = rcvsize + RPC_SLACK_SPACE; xprt_sock_setbufsize(xprt); +#endif } /* @@ -384,11 +392,11 @@ /* Increment call count */ rpcproc_count(clnt, task->tk_msg.rpc_proc)++; 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) @@ -400,13 +408,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) @@ -444,6 +457,9 @@ } switch (status) { + case -ENOTCONN: + task->tk_action = call_bind; + return; case -EAGAIN: /* woken up; retry */ task->tk_action = call_reserve; return; @@ -458,7 +474,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 @@ -469,7 +485,7 @@ dprintk("RPC: %4d call_allocate (status %d)\n", task->tk_pid, task->tk_status); - task->tk_action = call_encode; + task->tk_action = call_transmit; if (task->tk_buffer) return; @@ -492,7 +508,7 @@ } /* - * 3. Encode arguments of an RPC call + * 4. Encode arguments of an RPC call */ static void call_encode(struct rpc_task *task) @@ -541,40 +557,94 @@ } /* - * 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; - if (!clnt->cl_port) { - task->tk_action = call_connect; - task->tk_timeout = clnt->cl_timeout.to_maxval; - rpc_getport(task, clnt); + 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; +} + +/* + * 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. Establish socket - * 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; @@ -584,9 +654,10 @@ } /* - * 4b. Sort out reconnection result + * 1c. Sort out reconnection result */ -static void call_connect_status(struct rpc_task *task) +static void +call_connect_status(struct rpc_task *task) { struct rpc_clnt *clnt = task->tk_client; int status = task->tk_status; @@ -594,21 +665,25 @@ 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 -ECONNREFUSED: case -ECONNRESET: 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, status); } @@ -666,19 +741,9 @@ break; case -ECONNREFUSED: case -ENOTCONN: - req->rq_bytes_sent = 0; - if (clnt->cl_autobind || !clnt->cl_port) { - clnt->cl_port = 0; - task->tk_action = call_bind; - break; - } - task->tk_action = call_connect; + rpc_do_rebind(clnt); + task->tk_action = call_bind; break; - /* - * Sleep and dream of an open connection - */ - task->tk_timeout = 5 * HZ; - rpc_sleep_on(&xprt->sending, task, NULL, NULL); case -ENOMEM: case -EAGAIN: task->tk_action = call_transmit; @@ -709,8 +774,8 @@ to->to_retries = clnt->cl_timeout.to_retries; dprintk("RPC: %4d call_timeout (major)\n", task->tk_pid); - if (clnt->cl_softrtry) { - if (clnt->cl_chatty) + if (RPC_IS_SOFT(task)) { + if (clnt->cl_chatty && !task->tk_exit) printk(KERN_NOTICE "%s: server %s not responding, timed out\n", clnt->cl_protname, clnt->cl_server); rpc_exit(task, -EIO); @@ -722,8 +787,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++; @@ -752,7 +816,7 @@ } if (task->tk_status < 12) { - if (!clnt->cl_softrtry) { + if (!RPC_IS_SOFT(task)) { task->tk_action = call_transmit; clnt->cl_stats->rpcretrans++; goto out_retry; @@ -834,7 +898,7 @@ if (task->tk_status < 0) rpc_exit(task, -EACCES); else - task->tk_action = call_reserve; + task->tk_action = call_bind; } /* @@ -844,7 +908,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.4.22.orig/net/sunrpc/pmap_clnt.c linux-2.4.22/net/sunrpc/pmap_clnt.c --- linux-2.4.22.orig/net/sunrpc/pmap_clnt.c 2002-08-03 08:39:46.000000000 +0800 +++ linux-2.4.22/net/sunrpc/pmap_clnt.c 2003-12-21 17:29:54.000000000 +0800 @@ -31,7 +31,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 @@ -41,8 +40,7 @@ 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 rpc_message msg = { PMAP_GETPORT, map, &clnt->cl_port, NULL }; + struct rpc_message msg = { PMAP_GETPORT, map, &clnt->pm_port, NULL }; struct rpc_clnt *pmap_clnt; struct rpc_task *child; @@ -50,18 +48,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; /* @@ -78,10 +77,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; } @@ -101,6 +100,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); @@ -118,25 +118,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); } /* @@ -183,21 +192,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.4.22.orig/net/sunrpc/sunrpc_syms.c linux-2.4.22/net/sunrpc/sunrpc_syms.c --- linux-2.4.22.orig/net/sunrpc/sunrpc_syms.c 2003-06-13 22:51:39.000000000 +0800 +++ linux-2.4.22/net/sunrpc/sunrpc_syms.c 2003-12-21 17:32:12.000000000 +0800 @@ -50,11 +50,11 @@ EXPORT_SYMBOL(rpc_delay); EXPORT_SYMBOL(rpc_restart_call); EXPORT_SYMBOL(rpc_setbufsize); +EXPORT_SYMBOL(rpc_set_timeout); /* 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.4.22.orig/net/sunrpc/xprt.c linux-2.4.22/net/sunrpc/xprt.c --- linux-2.4.22.orig/net/sunrpc/xprt.c 2003-08-25 19:44:44.000000000 +0800 +++ linux-2.4.22/net/sunrpc/xprt.c 2003-12-21 22:27:53.000000000 +0800 @@ -86,10 +86,16 @@ static inline void do_xprt_reserve(struct rpc_task *); static void xprt_disconnect(struct rpc_xprt *); static void xprt_connect_status(struct rpc_task *task); -static struct socket *xprt_create_socket(int, struct rpc_timeout *, int); +static struct rpc_xprt * xprt_setup(int proto, struct sockaddr_in *ap, + int resvport); +static struct socket *xprt_create_socket(int, int); static int 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 @@ -138,11 +144,16 @@ static int __xprt_lock_write(struct rpc_xprt *xprt, struct rpc_task *task) { + if (xprt_disconnected(xprt)) { + task->tk_status = -ENOTCONN; + return 0; + } if (!xprt->snd_task) { if (xprt->nocong || __xprt_get_cong(xprt, task)) { + struct rpc_rqst *req = task->tk_rqstp; + if (req) + req->rq_bytes_sent = 0; xprt->snd_task = task; - if (task->tk_rqstp) - task->tk_rqstp->rq_bytes_sent = 0; } } if (xprt->snd_task != task) { @@ -183,9 +194,10 @@ return; } if (xprt->nocong || __xprt_get_cong(xprt, task)) { + struct rpc_rqst *req = task->tk_rqstp; + if (req) + req->rq_bytes_sent = 0; xprt->snd_task = task; - if (task->tk_rqstp) - task->tk_rqstp->rq_bytes_sent = 0; } } @@ -404,7 +416,6 @@ sk->write_space = xprt->old_write_space; write_unlock_bh(&sk->callback_lock); - xprt_disconnect(xprt); sk->no_check = 0; sock_release(sock); @@ -418,8 +429,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); } @@ -449,21 +462,6 @@ return; if (xprt_connected(xprt)) goto out_write; - - if (task->tk_rqstp) - task->tk_rqstp->rq_bytes_sent = 0; - - xprt_close(xprt); - /* Create an unconnected socket */ - sock = xprt_create_socket(xprt->prot, &xprt->timeout, xprt->resvport); - if (!sock) { - /* 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); - if (!xprt->stream) goto out_write; @@ -489,7 +487,7 @@ task->tk_pid); task->tk_timeout = RPC_CONNECT_TIMEOUT; /* if the socket is already closing, delay briefly */ - if ((1<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); @@ -499,7 +497,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; @@ -507,7 +505,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); @@ -541,7 +539,7 @@ } /* if soft mounted, cause this RPC to fail */ - if (task->tk_client->cl_softrtry) + if (RPC_IS_SOFT(task)) task->tk_status = -EIO; switch (task->tk_status) { @@ -994,12 +992,10 @@ rpc_wake_up_task(xprt->snd_task); 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->sleep && waitqueue_active(sk->sleep)) @@ -1181,6 +1177,9 @@ * Note, though, that we can't do this if we've already started * resending down a TCP stream. */ + if (req->rq_received && !req->rq_bytes_sent) + goto out_release; + task->tk_status = status; switch (status) { @@ -1189,7 +1188,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; @@ -1210,6 +1209,7 @@ if (xprt->stream) xprt_disconnect(xprt); } +out_release: xprt_release_write(xprt, task); return; out_receive: @@ -1226,7 +1226,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); @@ -1301,7 +1301,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(); @@ -1319,8 +1319,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); @@ -1338,6 +1340,9 @@ xprt_clear_backlog(xprt); spin_unlock(&xprt->xprt_lock); +out: + put_xprt(xprt); + task->tk_xprt = NULL; } /* @@ -1366,14 +1371,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", @@ -1383,7 +1409,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) { @@ -1395,14 +1421,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"); @@ -1415,12 +1437,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; } /* @@ -1476,7 +1506,6 @@ tp->nonagle = 1; /* disable Nagle's algorithm */ sk->data_ready = tcp_data_ready; sk->state_change = tcp_state_change; - xprt_clear_connected(xprt); } sk->write_space = xprt_write_space; @@ -1513,7 +1542,7 @@ * Create a client socket given the protocol and peer address. */ 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; @@ -1543,14 +1572,31 @@ * 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: @@ -1587,15 +1633,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; }