This patch allows for sharing of xprts. This is done by keeping a list of current xprts and passing them back to the caller of xprt_create_proto if they match the specifications required (IP X port X protocol X timeout). We do this multiplexing at the xprt layer as it handles transport creation and destruction. This patch has been tested in a test-only environment but has been able to handle a couple hundreds distinct nfs mounts from the same server over a single tcp stream. This effectively gets rid of the 800 nfs mounts max problem, as long as you aren't mounting from many (800) nfs servers. Signed-off-by: Mike Waychison Index: linux-2.6.9-nfs_portsharing/include/linux/sunrpc/xprt.h =================================================================== --- linux-2.6.9-nfs_portsharing.orig/include/linux/sunrpc/xprt.h 2004-10-18 14:54:40.000000000 -0700 +++ linux-2.6.9-nfs_portsharing/include/linux/sunrpc/xprt.h 2004-10-26 13:22:41.000000000 -0700 @@ -15,6 +15,8 @@ #include #include +#include + /* * The transport code maintains an estimate on the maximum number of out- * standing RPC requests, using a smoothed version of the congestion @@ -194,6 +196,9 @@ struct rpc_xprt { void (*old_write_space)(struct sock *); wait_queue_head_t cong_wait; + + atomic_t count; /* shared xprt refcount */ + struct list_head shared; /* link to shared list */ }; #ifdef __KERNEL__ Index: linux-2.6.9-nfs_portsharing/net/sunrpc/xprt.c =================================================================== --- linux-2.6.9-nfs_portsharing.orig/net/sunrpc/xprt.c 2004-10-18 14:54:39.000000000 -0700 +++ linux-2.6.9-nfs_portsharing/net/sunrpc/xprt.c 2004-10-26 15:27:56.713490488 -0700 @@ -78,6 +78,12 @@ #define XPRT_MAX_RESVPORT (800) /* + * List of shared xprt + */ +static DECLARE_MUTEX(shared_xprt_sem); +static LIST_HEAD(shared_xprt_list); + +/* * Local functions */ static void xprt_request_init(struct rpc_task *, struct rpc_xprt *); @@ -1395,6 +1401,30 @@ xprt_release(struct rpc_task *task) } /* + * Compare two rpc_timeout to see if they are the same. + */ +static int +xprt_is_same_timeout(struct rpc_timeout *left, struct rpc_timeout *right) +{ + return left->to_initval == right->to_initval + && left->to_maxval == right->to_maxval + && left->to_increment == right->to_increment + && left->to_retries == right->to_retries + && left->to_exponential == right->to_exponential; +} +/* + * Check to see if the timeout is the default timeout. + */ +static int +xprt_is_default_timeout(struct rpc_timeout *to, int proto) +{ + struct rpc_timeout defaultto; + + xprt_default_timeout(&defaultto, proto); + return xprt_is_same_timeout(&defaultto, to); +} + +/* * Set default timeout parameters */ void @@ -1472,6 +1502,8 @@ xprt_setup(int proto, struct sockaddr_in xprt->timer.data = (unsigned long) xprt; xprt->last_used = jiffies; xprt->port = XPRT_MAX_RESVPORT; + INIT_LIST_HEAD(&xprt->shared); + atomic_set(&xprt->count, 1); /* Set timeout parameters */ if (to) { @@ -1617,8 +1649,8 @@ failed: /* * 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) +static struct rpc_xprt * +__xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to) { struct rpc_xprt *xprt; @@ -1631,6 +1663,43 @@ xprt_create_proto(int proto, struct sock } /* + * Create an RPC client transport that is shared given the protocol and peer + * address. + */ +struct rpc_xprt * +xprt_create_proto(int proto, struct sockaddr_in *sap, struct rpc_timeout *to) +{ + struct rpc_xprt *xprt; + + down(&shared_xprt_sem); + /* walk the list and find an existing mathing xprt */ + list_for_each_entry(xprt, &shared_xprt_list, shared) { + /* Filter out mismatches */ + if (sap->sin_addr.s_addr != xprt->addr.sin_addr.s_addr) + continue; + if (sap->sin_port != xprt->addr.sin_port) + continue; + if (xprt->prot != proto) + continue; + if (to == NULL && !xprt_is_default_timeout(&xprt->timeout, proto)) + continue; + if (to && !xprt_is_same_timeout(&xprt->timeout, to)) + continue; + + atomic_inc(&xprt->count); + goto out; + } + + /* make a new one */ + xprt = __xprt_create_proto(proto, sap, to); + if (!IS_ERR(xprt)) + list_add(&xprt->shared, &shared_xprt_list); +out: + up(&shared_xprt_sem); + return xprt; +} + +/* * Prepare for transport shutdown. */ void @@ -1658,8 +1727,8 @@ xprt_clear_backlog(struct rpc_xprt *xprt /* * Destroy an RPC transport, killing off all requests. */ -int -xprt_destroy(struct rpc_xprt *xprt) +static int +__xprt_destroy(struct rpc_xprt *xprt) { dprintk("RPC: destroying transport %p\n", xprt); xprt_shutdown(xprt); @@ -1670,3 +1739,20 @@ xprt_destroy(struct rpc_xprt *xprt) return 0; } + +/* + * Destroy a shared RPC transport. + * (XXX: what about the remaining live requests?) + */ +int +xprt_destroy(struct rpc_xprt *xprt) +{ + int ret = 0; + down(&shared_xprt_sem); + if (atomic_dec_and_test(&xprt->count)) { + list_del_init(&xprt->shared); + ret = __xprt_destroy(xprt); + } + up(&shared_xprt_sem); + return ret; +}