diff options
Diffstat (limited to 'net/sunrpc')
| -rw-r--r-- | net/sunrpc/auth.c | 28 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/Makefile | 4 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 44 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/gss_krb5_crypto.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/svcauth_gss.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/bc_svc.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/cache.c | 54 | ||||
| -rw-r--r-- | net/sunrpc/clnt.c | 21 | ||||
| -rw-r--r-- | net/sunrpc/rpc_pipe.c | 16 | ||||
| -rw-r--r-- | net/sunrpc/rpcb_clnt.c | 147 | ||||
| -rw-r--r-- | net/sunrpc/sched.c | 75 | ||||
| -rw-r--r-- | net/sunrpc/svc.c | 39 | ||||
| -rw-r--r-- | net/sunrpc/svc_xprt.c | 97 | ||||
| -rw-r--r-- | net/sunrpc/svcauth.c | 1 | ||||
| -rw-r--r-- | net/sunrpc/svcauth_unix.c | 17 | ||||
| -rw-r--r-- | net/sunrpc/svcsock.c | 122 | ||||
| -rw-r--r-- | net/sunrpc/xdr.c | 155 | ||||
| -rw-r--r-- | net/sunrpc/xprt.c | 5 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_transport.c | 1 | ||||
| -rw-r--r-- | net/sunrpc/xprtsock.c | 39 | 
20 files changed, 561 insertions, 310 deletions
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index afe67849269..67e31276682 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -563,8 +563,17 @@ rpcauth_checkverf(struct rpc_task *task, __be32 *p)  	return cred->cr_ops->crvalidate(task, p);  } +static void rpcauth_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, +				   __be32 *data, void *obj) +{ +	struct xdr_stream xdr; + +	xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data); +	encode(rqstp, &xdr, obj); +} +  int -rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, +rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp,  		__be32 *data, void *obj)  {  	struct rpc_cred *cred = task->tk_rqstp->rq_cred; @@ -574,11 +583,22 @@ rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,  	if (cred->cr_ops->crwrap_req)  		return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);  	/* By default, we encode the arguments normally. */ -	return encode(rqstp, data, obj); +	rpcauth_wrap_req_encode(encode, rqstp, data, obj); +	return 0; +} + +static int +rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, +			  __be32 *data, void *obj) +{ +	struct xdr_stream xdr; + +	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data); +	return decode(rqstp, &xdr, obj);  }  int -rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, +rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp,  		__be32 *data, void *obj)  {  	struct rpc_cred *cred = task->tk_rqstp->rq_cred; @@ -589,7 +609,7 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,  		return cred->cr_ops->crunwrap_resp(task, decode, rqstp,  						   data, obj);  	/* By default, we decode the arguments normally. */ -	return decode(rqstp, data, obj); +	return rpcauth_unwrap_req_decode(decode, rqstp, data, obj);  }  int diff --git a/net/sunrpc/auth_gss/Makefile b/net/sunrpc/auth_gss/Makefile index 7350d86a32e..9e4cb59ef9f 100644 --- a/net/sunrpc/auth_gss/Makefile +++ b/net/sunrpc/auth_gss/Makefile @@ -4,10 +4,10 @@  obj-$(CONFIG_SUNRPC_GSS) += auth_rpcgss.o -auth_rpcgss-objs := auth_gss.o gss_generic_token.o \ +auth_rpcgss-y := auth_gss.o gss_generic_token.o \  	gss_mech_switch.o svcauth_gss.o  obj-$(CONFIG_RPCSEC_GSS_KRB5) += rpcsec_gss_krb5.o -rpcsec_gss_krb5-objs := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \ +rpcsec_gss_krb5-y := gss_krb5_mech.o gss_krb5_seal.o gss_krb5_unseal.o \  	gss_krb5_seqnum.o gss_krb5_wrap.o gss_krb5_crypto.o gss_krb5_keys.o diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index 3835ce35e22..45dbf1521b9 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -1231,9 +1231,19 @@ out_bad:  	return NULL;  } +static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, +				__be32 *p, void *obj) +{ +	struct xdr_stream xdr; + +	xdr_init_encode(&xdr, &rqstp->rq_snd_buf, p); +	encode(rqstp, &xdr, obj); +} +  static inline int  gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx, -		kxdrproc_t encode, struct rpc_rqst *rqstp, __be32 *p, void *obj) +		   kxdreproc_t encode, struct rpc_rqst *rqstp, +		   __be32 *p, void *obj)  {  	struct xdr_buf	*snd_buf = &rqstp->rq_snd_buf;  	struct xdr_buf	integ_buf; @@ -1249,9 +1259,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,  	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;  	*p++ = htonl(rqstp->rq_seqno); -	status = encode(rqstp, p, obj); -	if (status) -		return status; +	gss_wrap_req_encode(encode, rqstp, p, obj);  	if (xdr_buf_subsegment(snd_buf, &integ_buf,  				offset, snd_buf->len - offset)) @@ -1325,7 +1333,8 @@ out:  static inline int  gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx, -		kxdrproc_t encode, struct rpc_rqst *rqstp, __be32 *p, void *obj) +		  kxdreproc_t encode, struct rpc_rqst *rqstp, +		  __be32 *p, void *obj)  {  	struct xdr_buf	*snd_buf = &rqstp->rq_snd_buf;  	u32		offset; @@ -1342,9 +1351,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,  	offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;  	*p++ = htonl(rqstp->rq_seqno); -	status = encode(rqstp, p, obj); -	if (status) -		return status; +	gss_wrap_req_encode(encode, rqstp, p, obj);  	status = alloc_enc_pages(rqstp);  	if (status) @@ -1394,7 +1401,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,  static int  gss_wrap_req(struct rpc_task *task, -	     kxdrproc_t encode, void *rqstp, __be32 *p, void *obj) +	     kxdreproc_t encode, void *rqstp, __be32 *p, void *obj)  {  	struct rpc_cred *cred = task->tk_rqstp->rq_cred;  	struct gss_cred	*gss_cred = container_of(cred, struct gss_cred, @@ -1407,12 +1414,14 @@ gss_wrap_req(struct rpc_task *task,  		/* The spec seems a little ambiguous here, but I think that not  		 * wrapping context destruction requests makes the most sense.  		 */ -		status = encode(rqstp, p, obj); +		gss_wrap_req_encode(encode, rqstp, p, obj); +		status = 0;  		goto out;  	}  	switch (gss_cred->gc_service) {  		case RPC_GSS_SVC_NONE: -			status = encode(rqstp, p, obj); +			gss_wrap_req_encode(encode, rqstp, p, obj); +			status = 0;  			break;  		case RPC_GSS_SVC_INTEGRITY:  			status = gss_wrap_req_integ(cred, ctx, encode, @@ -1494,10 +1503,19 @@ gss_unwrap_resp_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,  	return 0;  } +static int +gss_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, +		      __be32 *p, void *obj) +{ +	struct xdr_stream xdr; + +	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p); +	return decode(rqstp, &xdr, obj); +}  static int  gss_unwrap_resp(struct rpc_task *task, -		kxdrproc_t decode, void *rqstp, __be32 *p, void *obj) +		kxdrdproc_t decode, void *rqstp, __be32 *p, void *obj)  {  	struct rpc_cred *cred = task->tk_rqstp->rq_cred;  	struct gss_cred *gss_cred = container_of(cred, struct gss_cred, @@ -1528,7 +1546,7 @@ gss_unwrap_resp(struct rpc_task *task,  	cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)  						+ (savedlen - head->iov_len);  out_decode: -	status = decode(rqstp, p, obj); +	status = gss_unwrap_req_decode(decode, rqstp, p, obj);  out:  	gss_put_ctx(ctx);  	dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid, diff --git a/net/sunrpc/auth_gss/gss_krb5_crypto.c b/net/sunrpc/auth_gss/gss_krb5_crypto.c index 75ee993ea05..9576f35ab70 100644 --- a/net/sunrpc/auth_gss/gss_krb5_crypto.c +++ b/net/sunrpc/auth_gss/gss_krb5_crypto.c @@ -137,7 +137,7 @@ arcfour_hmac_md5_usage_to_salt(unsigned int usage, u8 salt[4])  		ms_usage = 13;  		break;  	default: -		return EINVAL;; +		return -EINVAL;  	}  	salt[0] = (ms_usage >> 0) & 0xff;  	salt[1] = (ms_usage >> 8) & 0xff; diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index dec2a6fc7c1..bcdae78fdfc 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -67,7 +67,6 @@ static int netobj_equal(struct xdr_netobj *a, struct xdr_netobj *b)  #define	RSI_HASHBITS	6  #define	RSI_HASHMAX	(1<<RSI_HASHBITS) -#define	RSI_HASHMASK	(RSI_HASHMAX-1)  struct rsi {  	struct cache_head	h; @@ -319,7 +318,6 @@ static struct rsi *rsi_update(struct rsi *new, struct rsi *old)  #define	RSC_HASHBITS	10  #define	RSC_HASHMAX	(1<<RSC_HASHBITS) -#define	RSC_HASHMASK	(RSC_HASHMAX-1)  #define GSS_SEQ_WIN	128 diff --git a/net/sunrpc/bc_svc.c b/net/sunrpc/bc_svc.c index 7dcfe0cc350..1dd1a689000 100644 --- a/net/sunrpc/bc_svc.c +++ b/net/sunrpc/bc_svc.c @@ -59,8 +59,8 @@ int bc_send(struct rpc_rqst *req)  		ret = task->tk_status;  		rpc_put_task(task);  	} -	return ret;  	dprintk("RPC:       bc_send ret= %d\n", ret); +	return ret;  }  #endif /* CONFIG_NFS_V4_1 */ diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index e433e7580e2..72ad836e4fe 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c @@ -37,7 +37,7 @@  #define	 RPCDBG_FACILITY RPCDBG_CACHE -static void cache_defer_req(struct cache_req *req, struct cache_head *item); +static bool cache_defer_req(struct cache_req *req, struct cache_head *item);  static void cache_revisit_request(struct cache_head *item);  static void cache_init(struct cache_head *h) @@ -128,6 +128,7 @@ static void cache_fresh_locked(struct cache_head *head, time_t expiry)  {  	head->expiry_time = expiry;  	head->last_refresh = seconds_since_boot(); +	smp_wmb(); /* paired with smp_rmb() in cache_is_valid() */  	set_bit(CACHE_VALID, &head->flags);  } @@ -208,11 +209,36 @@ static inline int cache_is_valid(struct cache_detail *detail, struct cache_head  		/* entry is valid */  		if (test_bit(CACHE_NEGATIVE, &h->flags))  			return -ENOENT; -		else +		else { +			/* +			 * In combination with write barrier in +			 * sunrpc_cache_update, ensures that anyone +			 * using the cache entry after this sees the +			 * updated contents: +			 */ +			smp_rmb();  			return 0; +		}  	}  } +static int try_to_negate_entry(struct cache_detail *detail, struct cache_head *h) +{ +	int rv; + +	write_lock(&detail->hash_lock); +	rv = cache_is_valid(detail, h); +	if (rv != -EAGAIN) { +		write_unlock(&detail->hash_lock); +		return rv; +	} +	set_bit(CACHE_NEGATIVE, &h->flags); +	cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY); +	write_unlock(&detail->hash_lock); +	cache_fresh_unlocked(h, detail); +	return -ENOENT; +} +  /*   * This is the generic cache management routine for all   * the authentication caches. @@ -251,14 +277,8 @@ int cache_check(struct cache_detail *detail,  			case -EINVAL:  				clear_bit(CACHE_PENDING, &h->flags);  				cache_revisit_request(h); -				if (rv == -EAGAIN) { -					set_bit(CACHE_NEGATIVE, &h->flags); -					cache_fresh_locked(h, seconds_since_boot()+CACHE_NEW_EXPIRY); -					cache_fresh_unlocked(h, detail); -					rv = -ENOENT; -				} +				rv = try_to_negate_entry(detail, h);  				break; -  			case -EAGAIN:  				clear_bit(CACHE_PENDING, &h->flags);  				cache_revisit_request(h); @@ -268,9 +288,11 @@ int cache_check(struct cache_detail *detail,  	}  	if (rv == -EAGAIN) { -		cache_defer_req(rqstp, h); -		if (!test_bit(CACHE_PENDING, &h->flags)) { -			/* Request is not deferred */ +		if (!cache_defer_req(rqstp, h)) { +			/* +			 * Request was not deferred; handle it as best +			 * we can ourselves: +			 */  			rv = cache_is_valid(detail, h);  			if (rv == -EAGAIN)  				rv = -ETIMEDOUT; @@ -618,18 +640,19 @@ static void cache_limit_defers(void)  		discard->revisit(discard, 1);  } -static void cache_defer_req(struct cache_req *req, struct cache_head *item) +/* Return true if and only if a deferred request is queued. */ +static bool cache_defer_req(struct cache_req *req, struct cache_head *item)  {  	struct cache_deferred_req *dreq;  	if (req->thread_wait) {  		cache_wait_req(req, item);  		if (!test_bit(CACHE_PENDING, &item->flags)) -			return; +			return false;  	}  	dreq = req->defer(req);  	if (dreq == NULL) -		return; +		return false;  	setup_deferral(dreq, item, 1);  	if (!test_bit(CACHE_PENDING, &item->flags))  		/* Bit could have been cleared before we managed to @@ -638,6 +661,7 @@ static void cache_defer_req(struct cache_req *req, struct cache_head *item)  		cache_revisit_request(item);  	cache_limit_defers(); +	return true;  }  static void cache_revisit_request(struct cache_head *item) diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 92ce94f5146..57d344cf225 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1095,7 +1095,7 @@ static void  rpc_xdr_encode(struct rpc_task *task)  {  	struct rpc_rqst	*req = task->tk_rqstp; -	kxdrproc_t	encode; +	kxdreproc_t	encode;  	__be32		*p;  	dprint_status(task); @@ -1535,7 +1535,7 @@ call_decode(struct rpc_task *task)  {  	struct rpc_clnt	*clnt = task->tk_client;  	struct rpc_rqst	*req = task->tk_rqstp; -	kxdrproc_t	decode = task->tk_msg.rpc_proc->p_decode; +	kxdrdproc_t	decode = task->tk_msg.rpc_proc->p_decode;  	__be32		*p;  	dprintk("RPC: %5u call_decode (status %d)\n", @@ -1776,12 +1776,11 @@ out_overflow:  	goto out_garbage;  } -static int rpcproc_encode_null(void *rqstp, __be32 *data, void *obj) +static void rpcproc_encode_null(void *rqstp, struct xdr_stream *xdr, void *obj)  { -	return 0;  } -static int rpcproc_decode_null(void *rqstp, __be32 *data, void *obj) +static int rpcproc_decode_null(void *rqstp, struct xdr_stream *xdr, void *obj)  {  	return 0;  } @@ -1830,23 +1829,15 @@ static void rpc_show_task(const struct rpc_clnt *clnt,  			  const struct rpc_task *task)  {  	const char *rpc_waitq = "none"; -	char *p, action[KSYM_SYMBOL_LEN];  	if (RPC_IS_QUEUED(task))  		rpc_waitq = rpc_qname(task->tk_waitqueue); -	/* map tk_action pointer to a function name; then trim off -	 * the "+0x0 [sunrpc]" */ -	sprint_symbol(action, (unsigned long)task->tk_action); -	p = strchr(action, '+'); -	if (p) -		*p = '\0'; - -	printk(KERN_INFO "%5u %04x %6d %8p %8p %8ld %8p %sv%u %s a:%s q:%s\n", +	printk(KERN_INFO "%5u %04x %6d %8p %8p %8ld %8p %sv%u %s a:%ps q:%s\n",  		task->tk_pid, task->tk_flags, task->tk_status,  		clnt, task->tk_rqstp, task->tk_timeout, task->tk_ops,  		clnt->cl_protname, clnt->cl_vers, rpc_proc_name(task), -		action, rpc_waitq); +		task->tk_action, rpc_waitq);  }  void rpc_show_tasks(void) diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 10a17a37ec4..72bc5368396 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -162,11 +162,19 @@ rpc_alloc_inode(struct super_block *sb)  }  static void -rpc_destroy_inode(struct inode *inode) +rpc_i_callback(struct rcu_head *head)  { +	struct inode *inode = container_of(head, struct inode, i_rcu); +	INIT_LIST_HEAD(&inode->i_dentry);  	kmem_cache_free(rpc_inode_cachep, RPC_I(inode));  } +static void +rpc_destroy_inode(struct inode *inode) +{ +	call_rcu(&inode->i_rcu, rpc_i_callback); +} +  static int  rpc_pipe_open(struct inode *inode, struct file *filp)  { @@ -430,7 +438,7 @@ void rpc_put_mount(void)  }  EXPORT_SYMBOL_GPL(rpc_put_mount); -static int rpc_delete_dentry(struct dentry *dentry) +static int rpc_delete_dentry(const struct dentry *dentry)  {  	return 1;  } @@ -466,7 +474,7 @@ static int __rpc_create_common(struct inode *dir, struct dentry *dentry,  {  	struct inode *inode; -	BUG_ON(!d_unhashed(dentry)); +	d_drop(dentry);  	inode = rpc_get_inode(dir->i_sb, mode);  	if (!inode)  		goto out_err; @@ -583,7 +591,7 @@ static struct dentry *__rpc_lookup_create(struct dentry *parent,  		}  	}  	if (!dentry->d_inode) -		dentry->d_op = &rpc_dentry_operations; +		d_set_d_op(dentry, &rpc_dentry_operations);  out_err:  	return dentry;  } diff --git a/net/sunrpc/rpcb_clnt.c b/net/sunrpc/rpcb_clnt.c index fa6d7ca2c85..c652e4cc9fe 100644 --- a/net/sunrpc/rpcb_clnt.c +++ b/net/sunrpc/rpcb_clnt.c @@ -57,10 +57,6 @@ enum {  	RPCBPROC_GETSTAT,  }; -#define RPCB_HIGHPROC_2		RPCBPROC_CALLIT -#define RPCB_HIGHPROC_3		RPCBPROC_TADDR2UADDR -#define RPCB_HIGHPROC_4		RPCBPROC_GETSTAT -  /*   * r_owner   * @@ -693,46 +689,37 @@ static void rpcb_getport_done(struct rpc_task *child, void *data)   * XDR functions for rpcbind   */ -static int rpcb_enc_mapping(struct rpc_rqst *req, __be32 *p, -			    const struct rpcbind_args *rpcb) +static void rpcb_enc_mapping(struct rpc_rqst *req, struct xdr_stream *xdr, +			     const struct rpcbind_args *rpcb)  {  	struct rpc_task *task = req->rq_task; -	struct xdr_stream xdr; +	__be32 *p;  	dprintk("RPC: %5u encoding PMAP_%s call (%u, %u, %d, %u)\n",  			task->tk_pid, task->tk_msg.rpc_proc->p_name,  			rpcb->r_prog, rpcb->r_vers, rpcb->r_prot, rpcb->r_port); -	xdr_init_encode(&xdr, &req->rq_snd_buf, p); - -	p = xdr_reserve_space(&xdr, sizeof(__be32) * RPCB_mappingargs_sz); -	if (unlikely(p == NULL)) -		return -EIO; - -	*p++ = htonl(rpcb->r_prog); -	*p++ = htonl(rpcb->r_vers); -	*p++ = htonl(rpcb->r_prot); -	*p   = htonl(rpcb->r_port); - -	return 0; +	p = xdr_reserve_space(xdr, RPCB_mappingargs_sz << 2); +	*p++ = cpu_to_be32(rpcb->r_prog); +	*p++ = cpu_to_be32(rpcb->r_vers); +	*p++ = cpu_to_be32(rpcb->r_prot); +	*p   = cpu_to_be32(rpcb->r_port);  } -static int rpcb_dec_getport(struct rpc_rqst *req, __be32 *p, +static int rpcb_dec_getport(struct rpc_rqst *req, struct xdr_stream *xdr,  			    struct rpcbind_args *rpcb)  {  	struct rpc_task *task = req->rq_task; -	struct xdr_stream xdr;  	unsigned long port; - -	xdr_init_decode(&xdr, &req->rq_rcv_buf, p); +	__be32 *p;  	rpcb->r_port = 0; -	p = xdr_inline_decode(&xdr, sizeof(__be32)); +	p = xdr_inline_decode(xdr, 4);  	if (unlikely(p == NULL))  		return -EIO; -	port = ntohl(*p); +	port = be32_to_cpup(p);  	dprintk("RPC: %5u PMAP_%s result: %lu\n", task->tk_pid,  			task->tk_msg.rpc_proc->p_name, port);  	if (unlikely(port > USHRT_MAX)) @@ -742,20 +729,18 @@ static int rpcb_dec_getport(struct rpc_rqst *req, __be32 *p,  	return 0;  } -static int rpcb_dec_set(struct rpc_rqst *req, __be32 *p, +static int rpcb_dec_set(struct rpc_rqst *req, struct xdr_stream *xdr,  			unsigned int *boolp)  {  	struct rpc_task *task = req->rq_task; -	struct xdr_stream xdr; - -	xdr_init_decode(&xdr, &req->rq_rcv_buf, p); +	__be32 *p; -	p = xdr_inline_decode(&xdr, sizeof(__be32)); +	p = xdr_inline_decode(xdr, 4);  	if (unlikely(p == NULL))  		return -EIO;  	*boolp = 0; -	if (*p) +	if (*p != xdr_zero)  		*boolp = 1;  	dprintk("RPC: %5u RPCB_%s call %s\n", @@ -764,73 +749,53 @@ static int rpcb_dec_set(struct rpc_rqst *req, __be32 *p,  	return 0;  } -static int encode_rpcb_string(struct xdr_stream *xdr, const char *string, -				const u32 maxstrlen) +static void encode_rpcb_string(struct xdr_stream *xdr, const char *string, +			       const u32 maxstrlen)  { -	u32 len;  	__be32 *p; +	u32 len; -	if (unlikely(string == NULL)) -		return -EIO;  	len = strlen(string); -	if (unlikely(len > maxstrlen)) -		return -EIO; - -	p = xdr_reserve_space(xdr, sizeof(__be32) + len); -	if (unlikely(p == NULL)) -		return -EIO; +	BUG_ON(len > maxstrlen); +	p = xdr_reserve_space(xdr, 4 + len);  	xdr_encode_opaque(p, string, len); - -	return 0;  } -static int rpcb_enc_getaddr(struct rpc_rqst *req, __be32 *p, -			    const struct rpcbind_args *rpcb) +static void rpcb_enc_getaddr(struct rpc_rqst *req, struct xdr_stream *xdr, +			     const struct rpcbind_args *rpcb)  {  	struct rpc_task *task = req->rq_task; -	struct xdr_stream xdr; +	__be32 *p;  	dprintk("RPC: %5u encoding RPCB_%s call (%u, %u, '%s', '%s')\n",  			task->tk_pid, task->tk_msg.rpc_proc->p_name,  			rpcb->r_prog, rpcb->r_vers,  			rpcb->r_netid, rpcb->r_addr); -	xdr_init_encode(&xdr, &req->rq_snd_buf, p); - -	p = xdr_reserve_space(&xdr, -			sizeof(__be32) * (RPCB_program_sz + RPCB_version_sz)); -	if (unlikely(p == NULL)) -		return -EIO; -	*p++ = htonl(rpcb->r_prog); -	*p = htonl(rpcb->r_vers); - -	if (encode_rpcb_string(&xdr, rpcb->r_netid, RPCBIND_MAXNETIDLEN)) -		return -EIO; -	if (encode_rpcb_string(&xdr, rpcb->r_addr, RPCBIND_MAXUADDRLEN)) -		return -EIO; -	if (encode_rpcb_string(&xdr, rpcb->r_owner, RPCB_MAXOWNERLEN)) -		return -EIO; +	p = xdr_reserve_space(xdr, (RPCB_program_sz + RPCB_version_sz) << 2); +	*p++ = cpu_to_be32(rpcb->r_prog); +	*p = cpu_to_be32(rpcb->r_vers); -	return 0; +	encode_rpcb_string(xdr, rpcb->r_netid, RPCBIND_MAXNETIDLEN); +	encode_rpcb_string(xdr, rpcb->r_addr, RPCBIND_MAXUADDRLEN); +	encode_rpcb_string(xdr, rpcb->r_owner, RPCB_MAXOWNERLEN);  } -static int rpcb_dec_getaddr(struct rpc_rqst *req, __be32 *p, +static int rpcb_dec_getaddr(struct rpc_rqst *req, struct xdr_stream *xdr,  			    struct rpcbind_args *rpcb)  {  	struct sockaddr_storage address;  	struct sockaddr *sap = (struct sockaddr *)&address;  	struct rpc_task *task = req->rq_task; -	struct xdr_stream xdr; +	__be32 *p;  	u32 len;  	rpcb->r_port = 0; -	xdr_init_decode(&xdr, &req->rq_rcv_buf, p); - -	p = xdr_inline_decode(&xdr, sizeof(__be32)); +	p = xdr_inline_decode(xdr, 4);  	if (unlikely(p == NULL))  		goto out_fail; -	len = ntohl(*p); +	len = be32_to_cpup(p);  	/*  	 * If the returned universal address is a null string, @@ -845,7 +810,7 @@ static int rpcb_dec_getaddr(struct rpc_rqst *req, __be32 *p,  	if (unlikely(len > RPCBIND_MAXUADDRLEN))  		goto out_fail; -	p = xdr_inline_decode(&xdr, len); +	p = xdr_inline_decode(xdr, len);  	if (unlikely(p == NULL))  		goto out_fail;  	dprintk("RPC: %5u RPCB_%s reply: %s\n", task->tk_pid, @@ -871,8 +836,8 @@ out_fail:  static struct rpc_procinfo rpcb_procedures2[] = {  	[RPCBPROC_SET] = {  		.p_proc		= RPCBPROC_SET, -		.p_encode	= (kxdrproc_t)rpcb_enc_mapping, -		.p_decode	= (kxdrproc_t)rpcb_dec_set, +		.p_encode	= (kxdreproc_t)rpcb_enc_mapping, +		.p_decode	= (kxdrdproc_t)rpcb_dec_set,  		.p_arglen	= RPCB_mappingargs_sz,  		.p_replen	= RPCB_setres_sz,  		.p_statidx	= RPCBPROC_SET, @@ -881,8 +846,8 @@ static struct rpc_procinfo rpcb_procedures2[] = {  	},  	[RPCBPROC_UNSET] = {  		.p_proc		= RPCBPROC_UNSET, -		.p_encode	= (kxdrproc_t)rpcb_enc_mapping, -		.p_decode	= (kxdrproc_t)rpcb_dec_set, +		.p_encode	= (kxdreproc_t)rpcb_enc_mapping, +		.p_decode	= (kxdrdproc_t)rpcb_dec_set,  		.p_arglen	= RPCB_mappingargs_sz,  		.p_replen	= RPCB_setres_sz,  		.p_statidx	= RPCBPROC_UNSET, @@ -891,8 +856,8 @@ static struct rpc_procinfo rpcb_procedures2[] = {  	},  	[RPCBPROC_GETPORT] = {  		.p_proc		= RPCBPROC_GETPORT, -		.p_encode	= (kxdrproc_t)rpcb_enc_mapping, -		.p_decode	= (kxdrproc_t)rpcb_dec_getport, +		.p_encode	= (kxdreproc_t)rpcb_enc_mapping, +		.p_decode	= (kxdrdproc_t)rpcb_dec_getport,  		.p_arglen	= RPCB_mappingargs_sz,  		.p_replen	= RPCB_getportres_sz,  		.p_statidx	= RPCBPROC_GETPORT, @@ -904,8 +869,8 @@ static struct rpc_procinfo rpcb_procedures2[] = {  static struct rpc_procinfo rpcb_procedures3[] = {  	[RPCBPROC_SET] = {  		.p_proc		= RPCBPROC_SET, -		.p_encode	= (kxdrproc_t)rpcb_enc_getaddr, -		.p_decode	= (kxdrproc_t)rpcb_dec_set, +		.p_encode	= (kxdreproc_t)rpcb_enc_getaddr, +		.p_decode	= (kxdrdproc_t)rpcb_dec_set,  		.p_arglen	= RPCB_getaddrargs_sz,  		.p_replen	= RPCB_setres_sz,  		.p_statidx	= RPCBPROC_SET, @@ -914,8 +879,8 @@ static struct rpc_procinfo rpcb_procedures3[] = {  	},  	[RPCBPROC_UNSET] = {  		.p_proc		= RPCBPROC_UNSET, -		.p_encode	= (kxdrproc_t)rpcb_enc_getaddr, -		.p_decode	= (kxdrproc_t)rpcb_dec_set, +		.p_encode	= (kxdreproc_t)rpcb_enc_getaddr, +		.p_decode	= (kxdrdproc_t)rpcb_dec_set,  		.p_arglen	= RPCB_getaddrargs_sz,  		.p_replen	= RPCB_setres_sz,  		.p_statidx	= RPCBPROC_UNSET, @@ -924,8 +889,8 @@ static struct rpc_procinfo rpcb_procedures3[] = {  	},  	[RPCBPROC_GETADDR] = {  		.p_proc		= RPCBPROC_GETADDR, -		.p_encode	= (kxdrproc_t)rpcb_enc_getaddr, -		.p_decode	= (kxdrproc_t)rpcb_dec_getaddr, +		.p_encode	= (kxdreproc_t)rpcb_enc_getaddr, +		.p_decode	= (kxdrdproc_t)rpcb_dec_getaddr,  		.p_arglen	= RPCB_getaddrargs_sz,  		.p_replen	= RPCB_getaddrres_sz,  		.p_statidx	= RPCBPROC_GETADDR, @@ -937,8 +902,8 @@ static struct rpc_procinfo rpcb_procedures3[] = {  static struct rpc_procinfo rpcb_procedures4[] = {  	[RPCBPROC_SET] = {  		.p_proc		= RPCBPROC_SET, -		.p_encode	= (kxdrproc_t)rpcb_enc_getaddr, -		.p_decode	= (kxdrproc_t)rpcb_dec_set, +		.p_encode	= (kxdreproc_t)rpcb_enc_getaddr, +		.p_decode	= (kxdrdproc_t)rpcb_dec_set,  		.p_arglen	= RPCB_getaddrargs_sz,  		.p_replen	= RPCB_setres_sz,  		.p_statidx	= RPCBPROC_SET, @@ -947,8 +912,8 @@ static struct rpc_procinfo rpcb_procedures4[] = {  	},  	[RPCBPROC_UNSET] = {  		.p_proc		= RPCBPROC_UNSET, -		.p_encode	= (kxdrproc_t)rpcb_enc_getaddr, -		.p_decode	= (kxdrproc_t)rpcb_dec_set, +		.p_encode	= (kxdreproc_t)rpcb_enc_getaddr, +		.p_decode	= (kxdrdproc_t)rpcb_dec_set,  		.p_arglen	= RPCB_getaddrargs_sz,  		.p_replen	= RPCB_setres_sz,  		.p_statidx	= RPCBPROC_UNSET, @@ -957,8 +922,8 @@ static struct rpc_procinfo rpcb_procedures4[] = {  	},  	[RPCBPROC_GETADDR] = {  		.p_proc		= RPCBPROC_GETADDR, -		.p_encode	= (kxdrproc_t)rpcb_enc_getaddr, -		.p_decode	= (kxdrproc_t)rpcb_dec_getaddr, +		.p_encode	= (kxdreproc_t)rpcb_enc_getaddr, +		.p_decode	= (kxdrdproc_t)rpcb_dec_getaddr,  		.p_arglen	= RPCB_getaddrargs_sz,  		.p_replen	= RPCB_getaddrres_sz,  		.p_statidx	= RPCBPROC_GETADDR, @@ -993,19 +958,19 @@ static struct rpcb_info rpcb_next_version6[] = {  static struct rpc_version rpcb_version2 = {  	.number		= RPCBVERS_2, -	.nrprocs	= RPCB_HIGHPROC_2, +	.nrprocs	= ARRAY_SIZE(rpcb_procedures2),  	.procs		= rpcb_procedures2  };  static struct rpc_version rpcb_version3 = {  	.number		= RPCBVERS_3, -	.nrprocs	= RPCB_HIGHPROC_3, +	.nrprocs	= ARRAY_SIZE(rpcb_procedures3),  	.procs		= rpcb_procedures3  };  static struct rpc_version rpcb_version4 = {  	.number		= RPCBVERS_4, -	.nrprocs	= RPCB_HIGHPROC_4, +	.nrprocs	= ARRAY_SIZE(rpcb_procedures4),  	.procs		= rpcb_procedures4  }; diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 243fc09b164..59e599498e3 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -252,23 +252,37 @@ static void rpc_set_active(struct rpc_task *task)  /*   * Mark an RPC call as having completed by clearing the 'active' bit + * and then waking up all tasks that were sleeping.   */ -static void rpc_mark_complete_task(struct rpc_task *task) +static int rpc_complete_task(struct rpc_task *task)  { -	smp_mb__before_clear_bit(); +	void *m = &task->tk_runstate; +	wait_queue_head_t *wq = bit_waitqueue(m, RPC_TASK_ACTIVE); +	struct wait_bit_key k = __WAIT_BIT_KEY_INITIALIZER(m, RPC_TASK_ACTIVE); +	unsigned long flags; +	int ret; + +	spin_lock_irqsave(&wq->lock, flags);  	clear_bit(RPC_TASK_ACTIVE, &task->tk_runstate); -	smp_mb__after_clear_bit(); -	wake_up_bit(&task->tk_runstate, RPC_TASK_ACTIVE); +	ret = atomic_dec_and_test(&task->tk_count); +	if (waitqueue_active(wq)) +		__wake_up_locked_key(wq, TASK_NORMAL, &k); +	spin_unlock_irqrestore(&wq->lock, flags); +	return ret;  }  /*   * Allow callers to wait for completion of an RPC call + * + * Note the use of out_of_line_wait_on_bit() rather than wait_on_bit() + * to enforce taking of the wq->lock and hence avoid races with + * rpc_complete_task().   */  int __rpc_wait_for_completion_task(struct rpc_task *task, int (*action)(void *))  {  	if (action == NULL)  		action = rpc_wait_bit_killable; -	return wait_on_bit(&task->tk_runstate, RPC_TASK_ACTIVE, +	return out_of_line_wait_on_bit(&task->tk_runstate, RPC_TASK_ACTIVE,  			action, TASK_KILLABLE);  }  EXPORT_SYMBOL_GPL(__rpc_wait_for_completion_task); @@ -857,34 +871,67 @@ static void rpc_async_release(struct work_struct *work)  	rpc_free_task(container_of(work, struct rpc_task, u.tk_work));  } -void rpc_put_task(struct rpc_task *task) +static void rpc_release_resources_task(struct rpc_task *task)  { -	if (!atomic_dec_and_test(&task->tk_count)) -		return; -	/* Release resources */  	if (task->tk_rqstp)  		xprt_release(task);  	if (task->tk_msg.rpc_cred)  		put_rpccred(task->tk_msg.rpc_cred);  	rpc_task_release_client(task); -	if (task->tk_workqueue != NULL) { +} + +static void rpc_final_put_task(struct rpc_task *task, +		struct workqueue_struct *q) +{ +	if (q != NULL) {  		INIT_WORK(&task->u.tk_work, rpc_async_release); -		queue_work(task->tk_workqueue, &task->u.tk_work); +		queue_work(q, &task->u.tk_work);  	} else  		rpc_free_task(task);  } + +static void rpc_do_put_task(struct rpc_task *task, struct workqueue_struct *q) +{ +	if (atomic_dec_and_test(&task->tk_count)) { +		rpc_release_resources_task(task); +		rpc_final_put_task(task, q); +	} +} + +void rpc_put_task(struct rpc_task *task) +{ +	rpc_do_put_task(task, NULL); +}  EXPORT_SYMBOL_GPL(rpc_put_task); +void rpc_put_task_async(struct rpc_task *task) +{ +	rpc_do_put_task(task, task->tk_workqueue); +} +EXPORT_SYMBOL_GPL(rpc_put_task_async); +  static void rpc_release_task(struct rpc_task *task)  {  	dprintk("RPC: %5u release task\n", task->tk_pid);  	BUG_ON (RPC_IS_QUEUED(task)); -	/* Wake up anyone who is waiting for task completion */ -	rpc_mark_complete_task(task); +	rpc_release_resources_task(task); -	rpc_put_task(task); +	/* +	 * Note: at this point we have been removed from rpc_clnt->cl_tasks, +	 * so it should be safe to use task->tk_count as a test for whether +	 * or not any other processes still hold references to our rpc_task. +	 */ +	if (atomic_read(&task->tk_count) != 1 + !RPC_IS_ASYNC(task)) { +		/* Wake up anyone who may be waiting for task completion */ +		if (!rpc_complete_task(task)) +			return; +	} else { +		if (!atomic_dec_and_test(&task->tk_count)) +			return; +	} +	rpc_final_put_task(task, task->tk_workqueue);  }  int rpciod_up(void) diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 6359c42c494..08e05a8ce02 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -488,10 +488,6 @@ svc_destroy(struct svc_serv *serv)  	if (svc_serv_is_pooled(serv))  		svc_pool_map_put(); -#if defined(CONFIG_NFS_V4_1) -	svc_sock_destroy(serv->bc_xprt); -#endif /* CONFIG_NFS_V4_1 */ -  	svc_unregister(serv);  	kfree(serv->sv_pools);  	kfree(serv); @@ -1005,6 +1001,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)  	rqstp->rq_splice_ok = 1;  	/* Will be turned off only when NFSv4 Sessions are used */  	rqstp->rq_usedeferral = 1; +	rqstp->rq_dropme = false;  	/* Setup reply header */  	rqstp->rq_xprt->xpt_ops->xpo_prep_reply_hdr(rqstp); @@ -1106,7 +1103,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)  		*statp = procp->pc_func(rqstp, rqstp->rq_argp, rqstp->rq_resp);  		/* Encode reply */ -		if (*statp == rpc_drop_reply) { +		if (rqstp->rq_dropme) {  			if (procp->pc_release)  				procp->pc_release(rqstp, NULL, rqstp->rq_resp);  			goto dropit; @@ -1147,7 +1144,6 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)   dropit:  	svc_authorise(rqstp);	/* doesn't hurt to call this twice */  	dprintk("svc: svc_process dropit\n"); -	svc_drop(rqstp);  	return 0;  err_short_len: @@ -1218,7 +1214,6 @@ svc_process(struct svc_rqst *rqstp)  	struct kvec		*resv = &rqstp->rq_res.head[0];  	struct svc_serv		*serv = rqstp->rq_server;  	u32			dir; -	int			error;  	/*  	 * Setup response xdr_buf. @@ -1246,11 +1241,13 @@ svc_process(struct svc_rqst *rqstp)  		return 0;  	} -	error = svc_process_common(rqstp, argv, resv); -	if (error <= 0) -		return error; - -	return svc_send(rqstp); +	/* Returns 1 for send, 0 for drop */ +	if (svc_process_common(rqstp, argv, resv)) +		return svc_send(rqstp); +	else { +		svc_drop(rqstp); +		return 0; +	}  }  #if defined(CONFIG_NFS_V4_1) @@ -1264,10 +1261,9 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,  {  	struct kvec	*argv = &rqstp->rq_arg.head[0];  	struct kvec	*resv = &rqstp->rq_res.head[0]; -	int 		error;  	/* Build the svc_rqst used by the common processing routine */ -	rqstp->rq_xprt = serv->bc_xprt; +	rqstp->rq_xprt = serv->sv_bc_xprt;  	rqstp->rq_xid = req->rq_xid;  	rqstp->rq_prot = req->rq_xprt->prot;  	rqstp->rq_server = serv; @@ -1292,12 +1288,15 @@ bc_svc_process(struct svc_serv *serv, struct rpc_rqst *req,  	svc_getu32(argv);	/* XID */  	svc_getnl(argv);	/* CALLDIR */ -	error = svc_process_common(rqstp, argv, resv); -	if (error <= 0) -		return error; - -	memcpy(&req->rq_snd_buf, &rqstp->rq_res, sizeof(req->rq_snd_buf)); -	return bc_send(req); +	/* Returns 1 for send, 0 for drop */ +	if (svc_process_common(rqstp, argv, resv)) { +		memcpy(&req->rq_snd_buf, &rqstp->rq_res, +						sizeof(req->rq_snd_buf)); +		return bc_send(req); +	} else { +		/* Nothing to do to drop request */ +		return 0; +	}  }  EXPORT_SYMBOL(bc_svc_process);  #endif /* CONFIG_NFS_V4_1 */ diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 3f2c5559ca1..ab86b7927f8 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -13,6 +13,7 @@  #include <linux/sunrpc/stats.h>  #include <linux/sunrpc/svc_xprt.h>  #include <linux/sunrpc/svcsock.h> +#include <linux/sunrpc/xprt.h>  #define RPCDBG_FACILITY	RPCDBG_SVCXPRT @@ -128,6 +129,9 @@ static void svc_xprt_free(struct kref *kref)  	if (test_bit(XPT_CACHE_AUTH, &xprt->xpt_flags))  		svcauth_unix_info_release(xprt);  	put_net(xprt->xpt_net); +	/* See comment on corresponding get in xs_setup_bc_tcp(): */ +	if (xprt->xpt_bc_xprt) +		xprt_put(xprt->xpt_bc_xprt);  	xprt->xpt_ops->xpo_free(xprt);  	module_put(owner);  } @@ -303,6 +307,15 @@ static void svc_thread_dequeue(struct svc_pool *pool, struct svc_rqst *rqstp)  	list_del(&rqstp->rq_list);  } +static bool svc_xprt_has_something_to_do(struct svc_xprt *xprt) +{ +	if (xprt->xpt_flags & ((1<<XPT_CONN)|(1<<XPT_CLOSE))) +		return true; +	if (xprt->xpt_flags & ((1<<XPT_DATA)|(1<<XPT_DEFERRED))) +		return xprt->xpt_ops->xpo_has_wspace(xprt); +	return false; +} +  /*   * Queue up a transport with data pending. If there are idle nfsd   * processes, wake 'em up. @@ -315,8 +328,7 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)  	struct svc_rqst	*rqstp;  	int cpu; -	if (!(xprt->xpt_flags & -	      ((1<<XPT_CONN)|(1<<XPT_DATA)|(1<<XPT_CLOSE)|(1<<XPT_DEFERRED)))) +	if (!svc_xprt_has_something_to_do(xprt))  		return;  	cpu = get_cpu(); @@ -343,28 +355,7 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)  		dprintk("svc: transport %p busy, not enqueued\n", xprt);  		goto out_unlock;  	} -	BUG_ON(xprt->xpt_pool != NULL); -	xprt->xpt_pool = pool; - -	/* Handle pending connection */ -	if (test_bit(XPT_CONN, &xprt->xpt_flags)) -		goto process; -	/* Handle close in-progress */ -	if (test_bit(XPT_CLOSE, &xprt->xpt_flags)) -		goto process; - -	/* Check if we have space to reply to a request */ -	if (!xprt->xpt_ops->xpo_has_wspace(xprt)) { -		/* Don't enqueue while not enough space for reply */ -		dprintk("svc: no write space, transport %p  not enqueued\n", -			xprt); -		xprt->xpt_pool = NULL; -		clear_bit(XPT_BUSY, &xprt->xpt_flags); -		goto out_unlock; -	} - - process:  	if (!list_empty(&pool->sp_threads)) {  		rqstp = list_entry(pool->sp_threads.next,  				   struct svc_rqst, @@ -381,13 +372,11 @@ void svc_xprt_enqueue(struct svc_xprt *xprt)  		rqstp->rq_reserved = serv->sv_max_mesg;  		atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved);  		pool->sp_stats.threads_woken++; -		BUG_ON(xprt->xpt_pool != pool);  		wake_up(&rqstp->rq_wait);  	} else {  		dprintk("svc: transport %p put into queue\n", xprt);  		list_add_tail(&xprt->xpt_ready, &pool->sp_sockets);  		pool->sp_stats.sockets_queued++; -		BUG_ON(xprt->xpt_pool != pool);  	}  out_unlock: @@ -426,7 +415,6 @@ static struct svc_xprt *svc_xprt_dequeue(struct svc_pool *pool)  void svc_xprt_received(struct svc_xprt *xprt)  {  	BUG_ON(!test_bit(XPT_BUSY, &xprt->xpt_flags)); -	xprt->xpt_pool = NULL;  	/* As soon as we clear busy, the xprt could be closed and  	 * 'put', so we need a reference to call svc_xprt_enqueue with:  	 */ @@ -722,7 +710,10 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)  	if (test_bit(XPT_CLOSE, &xprt->xpt_flags)) {  		dprintk("svc_recv: found XPT_CLOSE\n");  		svc_delete_xprt(xprt); -	} else if (test_bit(XPT_LISTENER, &xprt->xpt_flags)) { +		/* Leave XPT_BUSY set on the dead xprt: */ +		goto out; +	} +	if (test_bit(XPT_LISTENER, &xprt->xpt_flags)) {  		struct svc_xprt *newxpt;  		newxpt = xprt->xpt_ops->xpo_accept(xprt);  		if (newxpt) { @@ -747,28 +738,23 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)  			spin_unlock_bh(&serv->sv_lock);  			svc_xprt_received(newxpt);  		} -		svc_xprt_received(xprt); -	} else { +	} else if (xprt->xpt_ops->xpo_has_wspace(xprt)) {  		dprintk("svc: server %p, pool %u, transport %p, inuse=%d\n",  			rqstp, pool->sp_id, xprt,  			atomic_read(&xprt->xpt_ref.refcount));  		rqstp->rq_deferred = svc_deferred_dequeue(xprt); -		if (rqstp->rq_deferred) { -			svc_xprt_received(xprt); +		if (rqstp->rq_deferred)  			len = svc_deferred_recv(rqstp); -		} else { +		else  			len = xprt->xpt_ops->xpo_recvfrom(rqstp); -			svc_xprt_received(xprt); -		}  		dprintk("svc: got len=%d\n", len);  	} +	svc_xprt_received(xprt);  	/* No data, incomplete (TCP) read, or accept() */ -	if (len == 0 || len == -EAGAIN) { -		rqstp->rq_res.len = 0; -		svc_xprt_release(rqstp); -		return -EAGAIN; -	} +	if (len == 0 || len == -EAGAIN) +		goto out; +  	clear_bit(XPT_OLD, &xprt->xpt_flags);  	rqstp->rq_secure = svc_port_is_privileged(svc_addr(rqstp)); @@ -777,6 +763,10 @@ int svc_recv(struct svc_rqst *rqstp, long timeout)  	if (serv->sv_stats)  		serv->sv_stats->netcnt++;  	return len; +out: +	rqstp->rq_res.len = 0; +	svc_xprt_release(rqstp); +	return -EAGAIN;  }  EXPORT_SYMBOL_GPL(svc_recv); @@ -935,7 +925,12 @@ void svc_close_xprt(struct svc_xprt *xprt)  	if (test_and_set_bit(XPT_BUSY, &xprt->xpt_flags))  		/* someone else will have to effect the close */  		return; - +	/* +	 * We expect svc_close_xprt() to work even when no threads are +	 * running (e.g., while configuring the server before starting +	 * any threads), so if the transport isn't busy, we delete +	 * it ourself: +	 */  	svc_delete_xprt(xprt);  }  EXPORT_SYMBOL_GPL(svc_close_xprt); @@ -945,16 +940,16 @@ void svc_close_all(struct list_head *xprt_list)  	struct svc_xprt *xprt;  	struct svc_xprt *tmp; +	/* +	 * The server is shutting down, and no more threads are running. +	 * svc_xprt_enqueue() might still be running, but at worst it +	 * will re-add the xprt to sp_sockets, which will soon get +	 * freed.  So we don't bother with any more locking, and don't +	 * leave the close to the (nonexistent) server threads: +	 */  	list_for_each_entry_safe(xprt, tmp, xprt_list, xpt_list) {  		set_bit(XPT_CLOSE, &xprt->xpt_flags); -		if (test_bit(XPT_BUSY, &xprt->xpt_flags)) { -			/* Waiting to be processed, but no threads left, -			 * So just remove it from the waiting list -			 */ -			list_del_init(&xprt->xpt_ready); -			clear_bit(XPT_BUSY, &xprt->xpt_flags); -		} -		svc_close_xprt(xprt); +		svc_delete_xprt(xprt);  	}  } @@ -1028,6 +1023,7 @@ static struct cache_deferred_req *svc_defer(struct cache_req *req)  	}  	svc_xprt_get(rqstp->rq_xprt);  	dr->xprt = rqstp->rq_xprt; +	rqstp->rq_dropme = true;  	dr->handle.revisit = svc_revisit;  	return &dr->handle; @@ -1065,14 +1061,13 @@ static struct svc_deferred_req *svc_deferred_dequeue(struct svc_xprt *xprt)  	if (!test_bit(XPT_DEFERRED, &xprt->xpt_flags))  		return NULL;  	spin_lock(&xprt->xpt_lock); -	clear_bit(XPT_DEFERRED, &xprt->xpt_flags);  	if (!list_empty(&xprt->xpt_deferred)) {  		dr = list_entry(xprt->xpt_deferred.next,  				struct svc_deferred_req,  				handle.recent);  		list_del_init(&dr->handle.recent); -		set_bit(XPT_DEFERRED, &xprt->xpt_flags); -	} +	} else +		clear_bit(XPT_DEFERRED, &xprt->xpt_flags);  	spin_unlock(&xprt->xpt_lock);  	return dr;  } diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c index 4e9393c2468..7963569fc04 100644 --- a/net/sunrpc/svcauth.c +++ b/net/sunrpc/svcauth.c @@ -118,7 +118,6 @@ EXPORT_SYMBOL_GPL(svc_auth_unregister);  #define	DN_HASHBITS	6  #define	DN_HASHMAX	(1<<DN_HASHBITS) -#define	DN_HASHMASK	(DN_HASHMAX-1)  static struct hlist_head	auth_domain_table[DN_HASHMAX];  static spinlock_t	auth_domain_lock = diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 560677d187f..30916b06c12 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -30,7 +30,9 @@  struct unix_domain {  	struct auth_domain	h; +#ifdef CONFIG_NFSD_DEPRECATED  	int	addr_changes; +#endif /* CONFIG_NFSD_DEPRECATED */  	/* other stuff later */  }; @@ -64,7 +66,9 @@ struct auth_domain *unix_domain_find(char *name)  			return NULL;  		}  		new->h.flavour = &svcauth_unix; +#ifdef CONFIG_NFSD_DEPRECATED  		new->addr_changes = 0; +#endif /* CONFIG_NFSD_DEPRECATED */  		rv = auth_domain_lookup(name, &new->h);  	}  } @@ -85,14 +89,15 @@ static void svcauth_unix_domain_release(struct auth_domain *dom)   */  #define	IP_HASHBITS	8  #define	IP_HASHMAX	(1<<IP_HASHBITS) -#define	IP_HASHMASK	(IP_HASHMAX-1)  struct ip_map {  	struct cache_head	h;  	char			m_class[8]; /* e.g. "nfsd" */  	struct in6_addr		m_addr;  	struct unix_domain	*m_client; +#ifdef CONFIG_NFSD_DEPRECATED  	int			m_add_change; +#endif /* CONFIG_NFSD_DEPRECATED */  };  static void ip_map_put(struct kref *kref) @@ -146,7 +151,9 @@ static void update(struct cache_head *cnew, struct cache_head *citem)  	kref_get(&item->m_client->h.ref);  	new->m_client = item->m_client; +#ifdef CONFIG_NFSD_DEPRECATED  	new->m_add_change = item->m_add_change; +#endif /* CONFIG_NFSD_DEPRECATED */  }  static struct cache_head *ip_map_alloc(void)  { @@ -331,6 +338,7 @@ static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,  	ip.h.flags = 0;  	if (!udom)  		set_bit(CACHE_NEGATIVE, &ip.h.flags); +#ifdef CONFIG_NFSD_DEPRECATED  	else {  		ip.m_add_change = udom->addr_changes;  		/* if this is from the legacy set_client system call, @@ -339,6 +347,7 @@ static int __ip_map_update(struct cache_detail *cd, struct ip_map *ipm,  		if (expiry == NEVER)  			ip.m_add_change++;  	} +#endif /* CONFIG_NFSD_DEPRECATED */  	ip.h.expiry_time = expiry;  	ch = sunrpc_cache_update(cd, &ip.h, &ipm->h,  				 hash_str(ipm->m_class, IP_HASHBITS) ^ @@ -358,6 +367,7 @@ static inline int ip_map_update(struct net *net, struct ip_map *ipm,  	return __ip_map_update(sn->ip_map_cache, ipm, udom, expiry);  } +#ifdef CONFIG_NFSD_DEPRECATED  int auth_unix_add_addr(struct net *net, struct in6_addr *addr, struct auth_domain *dom)  {  	struct unix_domain *udom; @@ -402,8 +412,7 @@ struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr)  		return NULL;  	if ((ipm->m_client->addr_changes - ipm->m_add_change) >0) { -		if (test_and_set_bit(CACHE_NEGATIVE, &ipm->h.flags) == 0) -			auth_domain_put(&ipm->m_client->h); +		sunrpc_invalidate(&ipm->h, sn->ip_map_cache);  		rv = NULL;  	} else {  		rv = &ipm->m_client->h; @@ -413,6 +422,7 @@ struct auth_domain *auth_unix_lookup(struct net *net, struct in6_addr *addr)  	return rv;  }  EXPORT_SYMBOL_GPL(auth_unix_lookup); +#endif /* CONFIG_NFSD_DEPRECATED */  void svcauth_unix_purge(void)  { @@ -497,7 +507,6 @@ svcauth_unix_info_release(struct svc_xprt *xpt)   */  #define	GID_HASHBITS	8  #define	GID_HASHMAX	(1<<GID_HASHBITS) -#define	GID_HASHMASK	(GID_HASHMAX - 1)  struct unix_gid {  	struct cache_head	h; diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index 07919e16be3..d802e941d36 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c @@ -66,6 +66,13 @@ static void		svc_sock_free(struct svc_xprt *);  static struct svc_xprt *svc_create_socket(struct svc_serv *, int,  					  struct net *, struct sockaddr *,  					  int, int); +#if defined(CONFIG_NFS_V4_1) +static struct svc_xprt *svc_bc_create_socket(struct svc_serv *, int, +					     struct net *, struct sockaddr *, +					     int, int); +static void svc_bc_sock_free(struct svc_xprt *xprt); +#endif /* CONFIG_NFS_V4_1 */ +  #ifdef CONFIG_DEBUG_LOCK_ALLOC  static struct lock_class_key svc_key[2];  static struct lock_class_key svc_slock_key[2]; @@ -324,19 +331,21 @@ int svc_sock_names(struct svc_serv *serv, char *buf, const size_t buflen,  			len = onelen;  			break;  		} -		if (toclose && strcmp(toclose, buf + len) == 0) +		if (toclose && strcmp(toclose, buf + len) == 0) {  			closesk = svsk; -		else +			svc_xprt_get(&closesk->sk_xprt); +		} else  			len += onelen;  	}  	spin_unlock_bh(&serv->sv_lock); -	if (closesk) +	if (closesk) {  		/* Should unregister with portmap, but you cannot  		 * unregister just one protocol...  		 */  		svc_close_xprt(&closesk->sk_xprt); -	else if (toclose) +		svc_xprt_put(&closesk->sk_xprt); +	} else if (toclose)  		return -ENOENT;  	return len;  } @@ -985,15 +994,17 @@ static int svc_process_calldir(struct svc_sock *svsk, struct svc_rqst *rqstp,  		vec[0] = rqstp->rq_arg.head[0];  	} else {  		/* REPLY */ -		if (svsk->sk_bc_xprt) -			req = xprt_lookup_rqst(svsk->sk_bc_xprt, xid); +		struct rpc_xprt *bc_xprt = svsk->sk_xprt.xpt_bc_xprt; + +		if (bc_xprt) +			req = xprt_lookup_rqst(bc_xprt, xid);  		if (!req) {  			printk(KERN_NOTICE  				"%s: Got unrecognized reply: " -				"calldir 0x%x sk_bc_xprt %p xid %08x\n", +				"calldir 0x%x xpt_bc_xprt %p xid %08x\n",  				__func__, ntohl(calldir), -				svsk->sk_bc_xprt, xid); +				bc_xprt, xid);  			vec[0] = rqstp->rq_arg.head[0];  			goto out;  		} @@ -1184,6 +1195,57 @@ static struct svc_xprt *svc_tcp_create(struct svc_serv *serv,  	return svc_create_socket(serv, IPPROTO_TCP, net, sa, salen, flags);  } +#if defined(CONFIG_NFS_V4_1) +static struct svc_xprt *svc_bc_create_socket(struct svc_serv *, int, +					     struct net *, struct sockaddr *, +					     int, int); +static void svc_bc_sock_free(struct svc_xprt *xprt); + +static struct svc_xprt *svc_bc_tcp_create(struct svc_serv *serv, +				       struct net *net, +				       struct sockaddr *sa, int salen, +				       int flags) +{ +	return svc_bc_create_socket(serv, IPPROTO_TCP, net, sa, salen, flags); +} + +static void svc_bc_tcp_sock_detach(struct svc_xprt *xprt) +{ +} + +static struct svc_xprt_ops svc_tcp_bc_ops = { +	.xpo_create = svc_bc_tcp_create, +	.xpo_detach = svc_bc_tcp_sock_detach, +	.xpo_free = svc_bc_sock_free, +	.xpo_prep_reply_hdr = svc_tcp_prep_reply_hdr, +}; + +static struct svc_xprt_class svc_tcp_bc_class = { +	.xcl_name = "tcp-bc", +	.xcl_owner = THIS_MODULE, +	.xcl_ops = &svc_tcp_bc_ops, +	.xcl_max_payload = RPCSVC_MAXPAYLOAD_TCP, +}; + +static void svc_init_bc_xprt_sock(void) +{ +	svc_reg_xprt_class(&svc_tcp_bc_class); +} + +static void svc_cleanup_bc_xprt_sock(void) +{ +	svc_unreg_xprt_class(&svc_tcp_bc_class); +} +#else /* CONFIG_NFS_V4_1 */ +static void svc_init_bc_xprt_sock(void) +{ +} + +static void svc_cleanup_bc_xprt_sock(void) +{ +} +#endif /* CONFIG_NFS_V4_1 */ +  static struct svc_xprt_ops svc_tcp_ops = {  	.xpo_create = svc_tcp_create,  	.xpo_recvfrom = svc_tcp_recvfrom, @@ -1207,12 +1269,14 @@ void svc_init_xprt_sock(void)  {  	svc_reg_xprt_class(&svc_tcp_class);  	svc_reg_xprt_class(&svc_udp_class); +	svc_init_bc_xprt_sock();  }  void svc_cleanup_xprt_sock(void)  {  	svc_unreg_xprt_class(&svc_tcp_class);  	svc_unreg_xprt_class(&svc_udp_class); +	svc_cleanup_bc_xprt_sock();  }  static void svc_tcp_init(struct svc_sock *svsk, struct svc_serv *serv) @@ -1509,41 +1573,43 @@ static void svc_sock_free(struct svc_xprt *xprt)  	kfree(svsk);  } +#if defined(CONFIG_NFS_V4_1)  /* - * Create a svc_xprt. - * - * For internal use only (e.g. nfsv4.1 backchannel). - * Callers should typically use the xpo_create() method. + * Create a back channel svc_xprt which shares the fore channel socket.   */ -struct svc_xprt *svc_sock_create(struct svc_serv *serv, int prot) +static struct svc_xprt *svc_bc_create_socket(struct svc_serv *serv, +					     int protocol, +					     struct net *net, +					     struct sockaddr *sin, int len, +					     int flags)  {  	struct svc_sock *svsk; -	struct svc_xprt *xprt = NULL; +	struct svc_xprt *xprt; + +	if (protocol != IPPROTO_TCP) { +		printk(KERN_WARNING "svc: only TCP sockets" +			" supported on shared back channel\n"); +		return ERR_PTR(-EINVAL); +	} -	dprintk("svc: %s\n", __func__);  	svsk = kzalloc(sizeof(*svsk), GFP_KERNEL);  	if (!svsk) -		goto out; +		return ERR_PTR(-ENOMEM);  	xprt = &svsk->sk_xprt; -	if (prot == IPPROTO_TCP) -		svc_xprt_init(&svc_tcp_class, xprt, serv); -	else if (prot == IPPROTO_UDP) -		svc_xprt_init(&svc_udp_class, xprt, serv); -	else -		BUG(); -out: -	dprintk("svc: %s return %p\n", __func__, xprt); +	svc_xprt_init(&svc_tcp_bc_class, xprt, serv); + +	serv->sv_bc_xprt = xprt; +  	return xprt;  } -EXPORT_SYMBOL_GPL(svc_sock_create);  /* - * Destroy a svc_sock. + * Free a back channel svc_sock.   */ -void svc_sock_destroy(struct svc_xprt *xprt) +static void svc_bc_sock_free(struct svc_xprt *xprt)  {  	if (xprt)  		kfree(container_of(xprt, struct svc_sock, sk_xprt));  } -EXPORT_SYMBOL_GPL(svc_sock_destroy); +#endif /* CONFIG_NFS_V4_1 */ diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c index cd9e841e749..679cd674b81 100644 --- a/net/sunrpc/xdr.c +++ b/net/sunrpc/xdr.c @@ -552,6 +552,74 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b  }  EXPORT_SYMBOL_GPL(xdr_write_pages); +static void xdr_set_iov(struct xdr_stream *xdr, struct kvec *iov, +		__be32 *p, unsigned int len) +{ +	if (len > iov->iov_len) +		len = iov->iov_len; +	if (p == NULL) +		p = (__be32*)iov->iov_base; +	xdr->p = p; +	xdr->end = (__be32*)(iov->iov_base + len); +	xdr->iov = iov; +	xdr->page_ptr = NULL; +} + +static int xdr_set_page_base(struct xdr_stream *xdr, +		unsigned int base, unsigned int len) +{ +	unsigned int pgnr; +	unsigned int maxlen; +	unsigned int pgoff; +	unsigned int pgend; +	void *kaddr; + +	maxlen = xdr->buf->page_len; +	if (base >= maxlen) +		return -EINVAL; +	maxlen -= base; +	if (len > maxlen) +		len = maxlen; + +	base += xdr->buf->page_base; + +	pgnr = base >> PAGE_SHIFT; +	xdr->page_ptr = &xdr->buf->pages[pgnr]; +	kaddr = page_address(*xdr->page_ptr); + +	pgoff = base & ~PAGE_MASK; +	xdr->p = (__be32*)(kaddr + pgoff); + +	pgend = pgoff + len; +	if (pgend > PAGE_SIZE) +		pgend = PAGE_SIZE; +	xdr->end = (__be32*)(kaddr + pgend); +	xdr->iov = NULL; +	return 0; +} + +static void xdr_set_next_page(struct xdr_stream *xdr) +{ +	unsigned int newbase; + +	newbase = (1 + xdr->page_ptr - xdr->buf->pages) << PAGE_SHIFT; +	newbase -= xdr->buf->page_base; + +	if (xdr_set_page_base(xdr, newbase, PAGE_SIZE) < 0) +		xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len); +} + +static bool xdr_set_next_buffer(struct xdr_stream *xdr) +{ +	if (xdr->page_ptr != NULL) +		xdr_set_next_page(xdr); +	else if (xdr->iov == xdr->buf->head) { +		if (xdr_set_page_base(xdr, 0, PAGE_SIZE) < 0) +			xdr_set_iov(xdr, xdr->buf->tail, NULL, xdr->buf->len); +	} +	return xdr->p != xdr->end; +} +  /**   * xdr_init_decode - Initialize an xdr_stream for decoding data.   * @xdr: pointer to xdr_stream struct @@ -560,41 +628,67 @@ EXPORT_SYMBOL_GPL(xdr_write_pages);   */  void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)  { -	struct kvec *iov = buf->head; -	unsigned int len = iov->iov_len; - -	if (len > buf->len) -		len = buf->len;  	xdr->buf = buf; -	xdr->iov = iov; -	xdr->p = p; -	xdr->end = (__be32 *)((char *)iov->iov_base + len); +	xdr->scratch.iov_base = NULL; +	xdr->scratch.iov_len = 0; +	if (buf->head[0].iov_len != 0) +		xdr_set_iov(xdr, buf->head, p, buf->len); +	else if (buf->page_len != 0) +		xdr_set_page_base(xdr, 0, buf->len);  }  EXPORT_SYMBOL_GPL(xdr_init_decode); -/** - * xdr_inline_peek - Allow read-ahead in the XDR data stream - * @xdr: pointer to xdr_stream struct - * @nbytes: number of bytes of data to decode - * - * Check if the input buffer is long enough to enable us to decode - * 'nbytes' more bytes of data starting at the current position. - * If so return the current pointer without updating the current - * pointer position. - */ -__be32 * xdr_inline_peek(struct xdr_stream *xdr, size_t nbytes) +static __be32 * __xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)  {  	__be32 *p = xdr->p;  	__be32 *q = p + XDR_QUADLEN(nbytes);  	if (unlikely(q > xdr->end || q < p))  		return NULL; +	xdr->p = q;  	return p;  } -EXPORT_SYMBOL_GPL(xdr_inline_peek);  /** - * xdr_inline_decode - Retrieve non-page XDR data to decode + * xdr_set_scratch_buffer - Attach a scratch buffer for decoding data. + * @xdr: pointer to xdr_stream struct + * @buf: pointer to an empty buffer + * @buflen: size of 'buf' + * + * The scratch buffer is used when decoding from an array of pages. + * If an xdr_inline_decode() call spans across page boundaries, then + * we copy the data into the scratch buffer in order to allow linear + * access. + */ +void xdr_set_scratch_buffer(struct xdr_stream *xdr, void *buf, size_t buflen) +{ +	xdr->scratch.iov_base = buf; +	xdr->scratch.iov_len = buflen; +} +EXPORT_SYMBOL_GPL(xdr_set_scratch_buffer); + +static __be32 *xdr_copy_to_scratch(struct xdr_stream *xdr, size_t nbytes) +{ +	__be32 *p; +	void *cpdest = xdr->scratch.iov_base; +	size_t cplen = (char *)xdr->end - (char *)xdr->p; + +	if (nbytes > xdr->scratch.iov_len) +		return NULL; +	memcpy(cpdest, xdr->p, cplen); +	cpdest += cplen; +	nbytes -= cplen; +	if (!xdr_set_next_buffer(xdr)) +		return NULL; +	p = __xdr_inline_decode(xdr, nbytes); +	if (p == NULL) +		return NULL; +	memcpy(cpdest, p, nbytes); +	return xdr->scratch.iov_base; +} + +/** + * xdr_inline_decode - Retrieve XDR data to decode   * @xdr: pointer to xdr_stream struct   * @nbytes: number of bytes of data to decode   * @@ -605,13 +699,16 @@ EXPORT_SYMBOL_GPL(xdr_inline_peek);   */  __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)  { -	__be32 *p = xdr->p; -	__be32 *q = p + XDR_QUADLEN(nbytes); +	__be32 *p; -	if (unlikely(q > xdr->end || q < p)) +	if (nbytes == 0) +		return xdr->p; +	if (xdr->p == xdr->end && !xdr_set_next_buffer(xdr))  		return NULL; -	xdr->p = q; -	return p; +	p = __xdr_inline_decode(xdr, nbytes); +	if (p != NULL) +		return p; +	return xdr_copy_to_scratch(xdr, nbytes);  }  EXPORT_SYMBOL_GPL(xdr_inline_decode); @@ -671,16 +768,12 @@ EXPORT_SYMBOL_GPL(xdr_read_pages);   */  void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)  { -	char * kaddr = page_address(xdr->buf->pages[0]);  	xdr_read_pages(xdr, len);  	/*  	 * Position current pointer at beginning of tail, and  	 * set remaining message length.  	 */ -	if (len > PAGE_CACHE_SIZE - xdr->buf->page_base) -		len = PAGE_CACHE_SIZE - xdr->buf->page_base; -	xdr->p = (__be32 *)(kaddr + xdr->buf->page_base); -	xdr->end = (__be32 *)((char *)xdr->p + len); +	xdr_set_page_base(xdr, 0, len);  }  EXPORT_SYMBOL_GPL(xdr_enter_page); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 4c8f18aff7c..856274d7e85 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -965,6 +965,7 @@ struct rpc_xprt *xprt_alloc(struct net *net, int size, int max_req)  	xprt = kzalloc(size, GFP_KERNEL);  	if (xprt == NULL)  		goto out; +	kref_init(&xprt->kref);  	xprt->max_reqs = max_req;  	xprt->slot = kcalloc(max_req, sizeof(struct rpc_rqst), GFP_KERNEL); @@ -1101,8 +1102,10 @@ found:  				-PTR_ERR(xprt));  		return xprt;  	} +	if (test_and_set_bit(XPRT_INITIALIZED, &xprt->state)) +		/* ->setup returned a pre-initialized xprt: */ +		return xprt; -	kref_init(&xprt->kref);  	spin_lock_init(&xprt->transport_lock);  	spin_lock_init(&xprt->reserve_lock); diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 9df1eadc912..1a10dcd999e 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -1335,6 +1335,7 @@ void svc_rdma_send_error(struct svcxprt_rdma *xprt, struct rpcrdma_msg *rmsgp,  					    p, 0, length, DMA_FROM_DEVICE);  	if (ib_dma_mapping_error(xprt->sc_cm_id->device, ctxt->sge[0].addr)) {  		put_page(p); +		svc_rdma_put_context(ctxt, 1);  		return;  	}  	atomic_inc(&xprt->sc_dma_used); diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index dfcab5ac65a..be96d429b47 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -770,7 +770,7 @@ static void xs_destroy(struct rpc_xprt *xprt)  	dprintk("RPC:       xs_destroy xprt %p\n", xprt); -	cancel_rearming_delayed_work(&transport->connect_worker); +	cancel_delayed_work_sync(&transport->connect_worker);  	xs_close(xprt);  	xs_free_peer_addresses(xprt); @@ -1631,7 +1631,8 @@ static struct socket *xs_create_sock(struct rpc_xprt *xprt,  	}  	xs_reclassify_socket(family, sock); -	if (xs_bind(transport, sock)) { +	err = xs_bind(transport, sock); +	if (err) {  		sock_release(sock);  		goto out;  	} @@ -2359,6 +2360,15 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)  	struct svc_sock *bc_sock;  	struct rpc_xprt *ret; +	if (args->bc_xprt->xpt_bc_xprt) { +		/* +		 * This server connection already has a backchannel +		 * export; we can't create a new one, as we wouldn't be +		 * able to match replies based on xid any more.  So, +		 * reuse the already-existing one: +		 */ +		 return args->bc_xprt->xpt_bc_xprt; +	}  	xprt = xs_setup_xprt(args, xprt_tcp_slot_table_entries);  	if (IS_ERR(xprt))  		return xprt; @@ -2375,16 +2385,6 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)  	xprt->reestablish_timeout = 0;  	xprt->idle_timeout = 0; -	/* -	 * The backchannel uses the same socket connection as the -	 * forechannel -	 */ -	xprt->bc_xprt = args->bc_xprt; -	bc_sock = container_of(args->bc_xprt, struct svc_sock, sk_xprt); -	bc_sock->sk_bc_xprt = xprt; -	transport->sock = bc_sock->sk_sock; -	transport->inet = bc_sock->sk_sk; -  	xprt->ops = &bc_tcp_ops;  	switch (addr->sa_family) { @@ -2407,6 +2407,20 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)  			xprt->address_strings[RPC_DISPLAY_PROTO]);  	/* +	 * Once we've associated a backchannel xprt with a connection, +	 * we want to keep it around as long as long as the connection +	 * lasts, in case we need to start using it for a backchannel +	 * again; this reference won't be dropped until bc_xprt is +	 * destroyed. +	 */ +	xprt_get(xprt); +	args->bc_xprt->xpt_bc_xprt = xprt; +	xprt->bc_xprt = args->bc_xprt; +	bc_sock = container_of(args->bc_xprt, struct svc_sock, sk_xprt); +	transport->sock = bc_sock->sk_sock; +	transport->inet = bc_sock->sk_sk; + +	/*  	 * Since we don't want connections for the backchannel, we set  	 * the xprt status to connected  	 */ @@ -2415,6 +2429,7 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)  	if (try_module_get(THIS_MODULE))  		return xprt; +	xprt_put(xprt);  	ret = ERR_PTR(-EINVAL);  out_err:  	xprt_free(xprt);  |