diff options
Diffstat (limited to 'net/sunrpc')
| -rw-r--r-- | net/sunrpc/auth_gss/gss_rpc_upcall.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/svcauth_gss.c | 49 | ||||
| -rw-r--r-- | net/sunrpc/clnt.c | 66 | ||||
| -rw-r--r-- | net/sunrpc/debugfs.c | 75 | ||||
| -rw-r--r-- | net/sunrpc/fail.h | 25 | ||||
| -rw-r--r-- | net/sunrpc/rpc_pipe.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/svc.c | 83 | ||||
| -rw-r--r-- | net/sunrpc/svc_xprt.c | 6 | ||||
| -rw-r--r-- | net/sunrpc/svcauth.c | 8 | ||||
| -rw-r--r-- | net/sunrpc/svcauth_unix.c | 18 | ||||
| -rw-r--r-- | net/sunrpc/sysfs.c | 36 | ||||
| -rw-r--r-- | net/sunrpc/xprt.c | 46 | ||||
| -rw-r--r-- | net/sunrpc/xprtmultipath.c | 1 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/backchannel.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/frwr_ops.c | 14 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_rw.c | 56 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_sendto.c | 41 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_transport.c | 11 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/transport.c | 13 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/verbs.c | 28 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/xprt_rdma.h | 2 | ||||
| -rw-r--r-- | net/sunrpc/xprtsock.c | 33 | 
22 files changed, 362 insertions, 255 deletions
diff --git a/net/sunrpc/auth_gss/gss_rpc_upcall.c b/net/sunrpc/auth_gss/gss_rpc_upcall.c index d1c003a25b0f..61c276bddaf2 100644 --- a/net/sunrpc/auth_gss/gss_rpc_upcall.c +++ b/net/sunrpc/auth_gss/gss_rpc_upcall.c @@ -160,7 +160,7 @@ static struct rpc_clnt *get_gssp_clnt(struct sunrpc_net *sn)  	mutex_lock(&sn->gssp_lock);  	clnt = sn->gssp_clnt;  	if (clnt) -		atomic_inc(&clnt->cl_count); +		refcount_inc(&clnt->cl_count);  	mutex_unlock(&sn->gssp_lock);  	return clnt;  } diff --git a/net/sunrpc/auth_gss/svcauth_gss.c b/net/sunrpc/auth_gss/svcauth_gss.c index a81be45f40d9..9de41e7f2d07 100644 --- a/net/sunrpc/auth_gss/svcauth_gss.c +++ b/net/sunrpc/auth_gss/svcauth_gss.c @@ -707,11 +707,11 @@ svc_safe_putnetobj(struct kvec *resv, struct xdr_netobj *o)  /*   * Verify the checksum on the header and return SVC_OK on success.   * Otherwise, return SVC_DROP (in the case of a bad sequence number) - * or return SVC_DENIED and indicate error in authp. + * or return SVC_DENIED and indicate error in rqstp->rq_auth_stat.   */  static int  gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci, -		  __be32 *rpcstart, struct rpc_gss_wire_cred *gc, __be32 *authp) +		  __be32 *rpcstart, struct rpc_gss_wire_cred *gc)  {  	struct gss_ctx		*ctx_id = rsci->mechctx;  	struct xdr_buf		rpchdr; @@ -725,7 +725,7 @@ gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,  	iov.iov_len = (u8 *)argv->iov_base - (u8 *)rpcstart;  	xdr_buf_from_iov(&iov, &rpchdr); -	*authp = rpc_autherr_badverf; +	rqstp->rq_auth_stat = rpc_autherr_badverf;  	if (argv->iov_len < 4)  		return SVC_DENIED;  	flavor = svc_getnl(argv); @@ -737,13 +737,13 @@ gss_verify_header(struct svc_rqst *rqstp, struct rsc *rsci,  	if (rqstp->rq_deferred) /* skip verification of revisited request */  		return SVC_OK;  	if (gss_verify_mic(ctx_id, &rpchdr, &checksum) != GSS_S_COMPLETE) { -		*authp = rpcsec_gsserr_credproblem; +		rqstp->rq_auth_stat = rpcsec_gsserr_credproblem;  		return SVC_DENIED;  	}  	if (gc->gc_seq > MAXSEQ) {  		trace_rpcgss_svc_seqno_large(rqstp, gc->gc_seq); -		*authp = rpcsec_gsserr_ctxproblem; +		rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem;  		return SVC_DENIED;  	}  	if (!gss_check_seq_num(rqstp, rsci, gc->gc_seq)) @@ -1038,6 +1038,8 @@ svcauth_gss_set_client(struct svc_rqst *rqstp)  	struct rpc_gss_wire_cred *gc = &svcdata->clcred;  	int stat; +	rqstp->rq_auth_stat = rpc_autherr_badcred; +  	/*  	 * A gss export can be specified either by:  	 * 	export	*(sec=krb5,rw) @@ -1053,6 +1055,8 @@ svcauth_gss_set_client(struct svc_rqst *rqstp)  	stat = svcauth_unix_set_client(rqstp);  	if (stat == SVC_DROP || stat == SVC_CLOSE)  		return stat; + +	rqstp->rq_auth_stat = rpc_auth_ok;  	return SVC_OK;  } @@ -1142,7 +1146,7 @@ static void gss_free_in_token_pages(struct gssp_in_token *in_token)  }  static int gss_read_proxy_verf(struct svc_rqst *rqstp, -			       struct rpc_gss_wire_cred *gc, __be32 *authp, +			       struct rpc_gss_wire_cred *gc,  			       struct xdr_netobj *in_handle,  			       struct gssp_in_token *in_token)  { @@ -1151,7 +1155,7 @@ static int gss_read_proxy_verf(struct svc_rqst *rqstp,  	int pages, i, res, pgto, pgfrom;  	size_t inlen, to_offs, from_offs; -	res = gss_read_common_verf(gc, argv, authp, in_handle); +	res = gss_read_common_verf(gc, argv, &rqstp->rq_auth_stat, in_handle);  	if (res)  		return res; @@ -1227,7 +1231,7 @@ gss_write_resv(struct kvec *resv, size_t size_limit,   * Otherwise, drop the request pending an answer to the upcall.   */  static int svcauth_gss_legacy_init(struct svc_rqst *rqstp, -			struct rpc_gss_wire_cred *gc, __be32 *authp) +				   struct rpc_gss_wire_cred *gc)  {  	struct kvec *argv = &rqstp->rq_arg.head[0];  	struct kvec *resv = &rqstp->rq_res.head[0]; @@ -1236,7 +1240,7 @@ static int svcauth_gss_legacy_init(struct svc_rqst *rqstp,  	struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id);  	memset(&rsikey, 0, sizeof(rsikey)); -	ret = gss_read_verf(gc, argv, authp, +	ret = gss_read_verf(gc, argv, &rqstp->rq_auth_stat,  			    &rsikey.in_handle, &rsikey.in_token);  	if (ret)  		return ret; @@ -1339,7 +1343,7 @@ out:  }  static int svcauth_gss_proxy_init(struct svc_rqst *rqstp, -			struct rpc_gss_wire_cred *gc, __be32 *authp) +				  struct rpc_gss_wire_cred *gc)  {  	struct kvec *resv = &rqstp->rq_res.head[0];  	struct xdr_netobj cli_handle; @@ -1351,8 +1355,7 @@ static int svcauth_gss_proxy_init(struct svc_rqst *rqstp,  	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);  	memset(&ud, 0, sizeof(ud)); -	ret = gss_read_proxy_verf(rqstp, gc, authp, -				  &ud.in_handle, &ud.in_token); +	ret = gss_read_proxy_verf(rqstp, gc, &ud.in_handle, &ud.in_token);  	if (ret)  		return ret; @@ -1525,7 +1528,7 @@ static void destroy_use_gss_proxy_proc_entry(struct net *net) {}   * response here and return SVC_COMPLETE.   */  static int -svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp) +svcauth_gss_accept(struct svc_rqst *rqstp)  {  	struct kvec	*argv = &rqstp->rq_arg.head[0];  	struct kvec	*resv = &rqstp->rq_res.head[0]; @@ -1538,7 +1541,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)  	int		ret;  	struct sunrpc_net *sn = net_generic(SVC_NET(rqstp), sunrpc_net_id); -	*authp = rpc_autherr_badcred; +	rqstp->rq_auth_stat = rpc_autherr_badcred;  	if (!svcdata)  		svcdata = kmalloc(sizeof(*svcdata), GFP_KERNEL);  	if (!svcdata) @@ -1575,22 +1578,22 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)  	if ((gc->gc_proc != RPC_GSS_PROC_DATA) && (rqstp->rq_proc != 0))  		goto auth_err; -	*authp = rpc_autherr_badverf; +	rqstp->rq_auth_stat = rpc_autherr_badverf;  	switch (gc->gc_proc) {  	case RPC_GSS_PROC_INIT:  	case RPC_GSS_PROC_CONTINUE_INIT:  		if (use_gss_proxy(SVC_NET(rqstp))) -			return svcauth_gss_proxy_init(rqstp, gc, authp); +			return svcauth_gss_proxy_init(rqstp, gc);  		else -			return svcauth_gss_legacy_init(rqstp, gc, authp); +			return svcauth_gss_legacy_init(rqstp, gc);  	case RPC_GSS_PROC_DATA:  	case RPC_GSS_PROC_DESTROY:  		/* Look up the context, and check the verifier: */ -		*authp = rpcsec_gsserr_credproblem; +		rqstp->rq_auth_stat = rpcsec_gsserr_credproblem;  		rsci = gss_svc_searchbyctx(sn->rsc_cache, &gc->gc_ctx);  		if (!rsci)  			goto auth_err; -		switch (gss_verify_header(rqstp, rsci, rpcstart, gc, authp)) { +		switch (gss_verify_header(rqstp, rsci, rpcstart, gc)) {  		case SVC_OK:  			break;  		case SVC_DENIED: @@ -1600,7 +1603,7 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)  		}  		break;  	default: -		*authp = rpc_autherr_rejectedcred; +		rqstp->rq_auth_stat = rpc_autherr_rejectedcred;  		goto auth_err;  	} @@ -1616,13 +1619,13 @@ svcauth_gss_accept(struct svc_rqst *rqstp, __be32 *authp)  		svc_putnl(resv, RPC_SUCCESS);  		goto complete;  	case RPC_GSS_PROC_DATA: -		*authp = rpcsec_gsserr_ctxproblem; +		rqstp->rq_auth_stat = rpcsec_gsserr_ctxproblem;  		svcdata->verf_start = resv->iov_base + resv->iov_len;  		if (gss_write_verf(rqstp, rsci->mechctx, gc->gc_seq))  			goto auth_err;  		rqstp->rq_cred = rsci->cred;  		get_group_info(rsci->cred.cr_group_info); -		*authp = rpc_autherr_badcred; +		rqstp->rq_auth_stat = rpc_autherr_badcred;  		switch (gc->gc_svc) {  		case RPC_GSS_SVC_NONE:  			break; @@ -1980,7 +1983,7 @@ gss_svc_init_net(struct net *net)  		goto out2;  	return 0;  out2: -	destroy_use_gss_proxy_proc_entry(net); +	rsi_cache_destroy_net(net);  out1:  	rsc_cache_destroy_net(net);  	return rv; diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index 8b4de70e8ead..f056ff931444 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -167,7 +167,7 @@ static int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event)  	case RPC_PIPEFS_MOUNT:  		if (clnt->cl_pipedir_objects.pdh_dentry != NULL)  			return 1; -		if (atomic_read(&clnt->cl_count) == 0) +		if (refcount_read(&clnt->cl_count) == 0)  			return 1;  		break;  	case RPC_PIPEFS_UMOUNT: @@ -419,7 +419,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args,  	clnt->cl_rtt = &clnt->cl_rtt_default;  	rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval); -	atomic_set(&clnt->cl_count, 1); +	refcount_set(&clnt->cl_count, 1);  	if (nodename == NULL)  		nodename = utsname()->nodename; @@ -431,7 +431,7 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args,  	if (err)  		goto out_no_path;  	if (parent) -		atomic_inc(&parent->cl_count); +		refcount_inc(&parent->cl_count);  	trace_rpc_clnt_new(clnt, xprt, program->name, args->servername);  	return clnt; @@ -918,18 +918,16 @@ rpc_free_client(struct rpc_clnt *clnt)  static struct rpc_clnt *  rpc_free_auth(struct rpc_clnt *clnt)  { -	if (clnt->cl_auth == NULL) -		return rpc_free_client(clnt); -  	/*  	 * Note: RPCSEC_GSS may need to send NULL RPC calls in order to  	 *       release remaining GSS contexts. This mechanism ensures  	 *       that it can do so safely.  	 */ -	atomic_inc(&clnt->cl_count); -	rpcauth_release(clnt->cl_auth); -	clnt->cl_auth = NULL; -	if (atomic_dec_and_test(&clnt->cl_count)) +	if (clnt->cl_auth != NULL) { +		rpcauth_release(clnt->cl_auth); +		clnt->cl_auth = NULL; +	} +	if (refcount_dec_and_test(&clnt->cl_count))  		return rpc_free_client(clnt);  	return NULL;  } @@ -943,7 +941,7 @@ rpc_release_client(struct rpc_clnt *clnt)  	do {  		if (list_empty(&clnt->cl_tasks))  			wake_up(&destroy_wait); -		if (!atomic_dec_and_test(&clnt->cl_count)) +		if (refcount_dec_not_one(&clnt->cl_count))  			break;  		clnt = rpc_free_auth(clnt);  	} while (clnt != NULL); @@ -1082,7 +1080,7 @@ void rpc_task_set_client(struct rpc_task *task, struct rpc_clnt *clnt)  	if (clnt != NULL) {  		rpc_task_set_transport(task, clnt);  		task->tk_client = clnt; -		atomic_inc(&clnt->cl_count); +		refcount_inc(&clnt->cl_count);  		if (clnt->cl_softrtry)  			task->tk_flags |= RPC_TASK_SOFT;  		if (clnt->cl_softerr) @@ -2694,17 +2692,18 @@ static const struct rpc_procinfo rpcproc_null = {  	.p_decode = rpcproc_decode_null,  }; -static int rpc_ping(struct rpc_clnt *clnt) +static void +rpc_null_call_prepare(struct rpc_task *task, void *data)  { -	struct rpc_message msg = { -		.rpc_proc = &rpcproc_null, -	}; -	int err; -	err = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN | -			    RPC_TASK_NULLCREDS); -	return err; +	task->tk_flags &= ~RPC_TASK_NO_RETRANS_TIMEOUT; +	rpc_call_start(task);  } +static const struct rpc_call_ops rpc_null_ops = { +	.rpc_call_prepare = rpc_null_call_prepare, +	.rpc_call_done = rpc_default_callback, +}; +  static  struct rpc_task *rpc_call_null_helper(struct rpc_clnt *clnt,  		struct rpc_xprt *xprt, struct rpc_cred *cred, int flags, @@ -2718,7 +2717,7 @@ struct rpc_task *rpc_call_null_helper(struct rpc_clnt *clnt,  		.rpc_xprt = xprt,  		.rpc_message = &msg,  		.rpc_op_cred = cred, -		.callback_ops = (ops != NULL) ? ops : &rpc_default_ops, +		.callback_ops = ops ?: &rpc_null_ops,  		.callback_data = data,  		.flags = flags | RPC_TASK_SOFT | RPC_TASK_SOFTCONN |  			 RPC_TASK_NULLCREDS, @@ -2733,6 +2732,19 @@ struct rpc_task *rpc_call_null(struct rpc_clnt *clnt, struct rpc_cred *cred, int  }  EXPORT_SYMBOL_GPL(rpc_call_null); +static int rpc_ping(struct rpc_clnt *clnt) +{ +	struct rpc_task	*task; +	int status; + +	task = rpc_call_null_helper(clnt, NULL, NULL, 0, NULL, NULL); +	if (IS_ERR(task)) +		return PTR_ERR(task); +	status = task->tk_status; +	rpc_put_task(task); +	return status; +} +  struct rpc_cb_add_xprt_calldata {  	struct rpc_xprt_switch *xps;  	struct rpc_xprt *xprt; @@ -2756,6 +2768,7 @@ static void rpc_cb_add_xprt_release(void *calldata)  }  static const struct rpc_call_ops rpc_cb_add_xprt_call_ops = { +	.rpc_call_prepare = rpc_null_call_prepare,  	.rpc_call_done = rpc_cb_add_xprt_done,  	.rpc_release = rpc_cb_add_xprt_release,  }; @@ -2774,6 +2787,15 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt,  	struct rpc_cb_add_xprt_calldata *data;  	struct rpc_task *task; +	if (xps->xps_nunique_destaddr_xprts + 1 > clnt->cl_max_connect) { +		rcu_read_lock(); +		pr_warn("SUNRPC: reached max allowed number (%d) did not add " +			"transport to server: %s\n", clnt->cl_max_connect, +			rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR)); +		rcu_read_unlock(); +		return -EINVAL; +	} +  	data = kmalloc(sizeof(*data), GFP_NOFS);  	if (!data)  		return -ENOMEM; @@ -2786,7 +2808,7 @@ int rpc_clnt_test_and_add_xprt(struct rpc_clnt *clnt,  	task = rpc_call_null_helper(clnt, xprt, NULL, RPC_TASK_ASYNC,  			&rpc_cb_add_xprt_call_ops, data); - +	data->xps->xps_nunique_destaddr_xprts++;  	rpc_put_task(task);  success:  	return 1; diff --git a/net/sunrpc/debugfs.c b/net/sunrpc/debugfs.c index 56029e3af6ff..7dc9cc929bfd 100644 --- a/net/sunrpc/debugfs.c +++ b/net/sunrpc/debugfs.c @@ -8,14 +8,14 @@  #include <linux/debugfs.h>  #include <linux/sunrpc/sched.h>  #include <linux/sunrpc/clnt.h> +  #include "netns.h" +#include "fail.h"  static struct dentry *topdir;  static struct dentry *rpc_clnt_dir;  static struct dentry *rpc_xprt_dir; -unsigned int rpc_inject_disconnect; -  static int  tasks_show(struct seq_file *f, void *v)  { @@ -90,7 +90,7 @@ static int tasks_open(struct inode *inode, struct file *filp)  		struct seq_file *seq = filp->private_data;  		struct rpc_clnt *clnt = seq->private = inode->i_private; -		if (!atomic_inc_not_zero(&clnt->cl_count)) { +		if (!refcount_inc_not_zero(&clnt->cl_count)) {  			seq_release(inode, filp);  			ret = -EINVAL;  		} @@ -235,8 +235,6 @@ rpc_xprt_debugfs_register(struct rpc_xprt *xprt)  	/* make tasks file */  	debugfs_create_file("info", S_IFREG | 0400, xprt->debugfs, xprt,  			    &xprt_info_fops); - -	atomic_set(&xprt->inject_disconnect, rpc_inject_disconnect);  }  void @@ -246,56 +244,30 @@ rpc_xprt_debugfs_unregister(struct rpc_xprt *xprt)  	xprt->debugfs = NULL;  } -static int -fault_open(struct inode *inode, struct file *filp) -{ -	filp->private_data = kmalloc(128, GFP_KERNEL); -	if (!filp->private_data) -		return -ENOMEM; -	return 0; -} +#if IS_ENABLED(CONFIG_FAIL_SUNRPC) +struct fail_sunrpc_attr fail_sunrpc = { +	.attr			= FAULT_ATTR_INITIALIZER, +}; +EXPORT_SYMBOL_GPL(fail_sunrpc); -static int -fault_release(struct inode *inode, struct file *filp) +static void fail_sunrpc_init(void)  { -	kfree(filp->private_data); -	return 0; -} +	struct dentry *dir; -static ssize_t -fault_disconnect_read(struct file *filp, char __user *user_buf, -		      size_t len, loff_t *offset) -{ -	char *buffer = (char *)filp->private_data; -	size_t size; +	dir = fault_create_debugfs_attr("fail_sunrpc", NULL, +					&fail_sunrpc.attr); -	size = sprintf(buffer, "%u\n", rpc_inject_disconnect); -	return simple_read_from_buffer(user_buf, len, offset, buffer, size); -} +	debugfs_create_bool("ignore-client-disconnect", S_IFREG | 0600, dir, +			    &fail_sunrpc.ignore_client_disconnect); -static ssize_t -fault_disconnect_write(struct file *filp, const char __user *user_buf, -		       size_t len, loff_t *offset) +	debugfs_create_bool("ignore-server-disconnect", S_IFREG | 0600, dir, +			    &fail_sunrpc.ignore_server_disconnect); +} +#else +static void fail_sunrpc_init(void)  { -	char buffer[16]; - -	if (len >= sizeof(buffer)) -		len = sizeof(buffer) - 1; -	if (copy_from_user(buffer, user_buf, len)) -		return -EFAULT; -	buffer[len] = '\0'; -	if (kstrtouint(buffer, 10, &rpc_inject_disconnect)) -		return -EINVAL; -	return len;  } - -static const struct file_operations fault_disconnect_fops = { -	.owner		= THIS_MODULE, -	.open		= fault_open, -	.read		= fault_disconnect_read, -	.write		= fault_disconnect_write, -	.release	= fault_release, -}; +#endif  void __exit  sunrpc_debugfs_exit(void) @@ -309,16 +281,11 @@ sunrpc_debugfs_exit(void)  void __init  sunrpc_debugfs_init(void)  { -	struct dentry *rpc_fault_dir; -  	topdir = debugfs_create_dir("sunrpc", NULL);  	rpc_clnt_dir = debugfs_create_dir("rpc_clnt", topdir);  	rpc_xprt_dir = debugfs_create_dir("rpc_xprt", topdir); -	rpc_fault_dir = debugfs_create_dir("inject_fault", topdir); - -	debugfs_create_file("disconnect", S_IFREG | 0400, rpc_fault_dir, NULL, -			    &fault_disconnect_fops); +	fail_sunrpc_init();  } diff --git a/net/sunrpc/fail.h b/net/sunrpc/fail.h new file mode 100644 index 000000000000..69dc30cc44b8 --- /dev/null +++ b/net/sunrpc/fail.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021, Oracle. All rights reserved. + */ + +#ifndef _NET_SUNRPC_FAIL_H_ +#define _NET_SUNRPC_FAIL_H_ + +#include <linux/fault-inject.h> + +#if IS_ENABLED(CONFIG_FAULT_INJECTION) + +struct fail_sunrpc_attr { +	struct fault_attr	attr; + +	bool			ignore_client_disconnect; + +	bool			ignore_server_disconnect; +}; + +extern struct fail_sunrpc_attr fail_sunrpc; + +#endif /* CONFIG_FAULT_INJECTION */ + +#endif /* _NET_SUNRPC_FAIL_H_ */ diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 09c000d490a1..ee5336d73fdd 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -423,7 +423,7 @@ rpc_info_open(struct inode *inode, struct file *file)  		spin_lock(&file->f_path.dentry->d_lock);  		if (!d_unhashed(file->f_path.dentry))  			clnt = RPC_I(inode)->private; -		if (clnt != NULL && atomic_inc_not_zero(&clnt->cl_count)) { +		if (clnt != NULL && refcount_inc_not_zero(&clnt->cl_count)) {  			spin_unlock(&file->f_path.dentry->d_lock);  			m->private = clnt;  		} else { diff --git a/net/sunrpc/svc.c b/net/sunrpc/svc.c index 0de918cb3d90..a3bbe5ce4570 100644 --- a/net/sunrpc/svc.c +++ b/net/sunrpc/svc.c @@ -31,6 +31,8 @@  #include <trace/events/sunrpc.h> +#include "fail.h" +  #define RPCDBG_FACILITY	RPCDBG_SVCDSP  static void svc_unregister(const struct svc_serv *serv, struct net *net); @@ -838,6 +840,27 @@ svc_set_num_threads_sync(struct svc_serv *serv, struct svc_pool *pool, int nrser  }  EXPORT_SYMBOL_GPL(svc_set_num_threads_sync); +/** + * svc_rqst_replace_page - Replace one page in rq_pages[] + * @rqstp: svc_rqst with pages to replace + * @page: replacement page + * + * When replacing a page in rq_pages, batch the release of the + * replaced pages to avoid hammering the page allocator. + */ +void svc_rqst_replace_page(struct svc_rqst *rqstp, struct page *page) +{ +	if (*rqstp->rq_next_page) { +		if (!pagevec_space(&rqstp->rq_pvec)) +			__pagevec_release(&rqstp->rq_pvec); +		pagevec_add(&rqstp->rq_pvec, *rqstp->rq_next_page); +	} + +	get_page(page); +	*(rqstp->rq_next_page++) = page; +} +EXPORT_SYMBOL_GPL(svc_rqst_replace_page); +  /*   * Called from a server thread as it's exiting. Caller must hold the "service   * mutex" for the service. @@ -1163,22 +1186,6 @@ void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...)  static __printf(2,3) void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) {}  #endif -__be32 -svc_return_autherr(struct svc_rqst *rqstp, __be32 auth_err) -{ -	set_bit(RQ_AUTHERR, &rqstp->rq_flags); -	return auth_err; -} -EXPORT_SYMBOL_GPL(svc_return_autherr); - -static __be32 -svc_get_autherr(struct svc_rqst *rqstp, __be32 *statp) -{ -	if (test_and_clear_bit(RQ_AUTHERR, &rqstp->rq_flags)) -		return *statp; -	return rpc_auth_ok; -} -  static int  svc_generic_dispatch(struct svc_rqst *rqstp, __be32 *statp)  { @@ -1202,7 +1209,7 @@ svc_generic_dispatch(struct svc_rqst *rqstp, __be32 *statp)  	    test_bit(RQ_DROPME, &rqstp->rq_flags))  		return 0; -	if (test_bit(RQ_AUTHERR, &rqstp->rq_flags)) +	if (rqstp->rq_auth_stat != rpc_auth_ok)  		return 1;  	if (*statp != rpc_success) @@ -1283,7 +1290,7 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)  	struct svc_process_info process;  	__be32			*statp;  	u32			prog, vers; -	__be32			auth_stat, rpc_stat; +	__be32			rpc_stat;  	int			auth_res;  	__be32			*reply_statp; @@ -1326,14 +1333,12 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)  	 * We do this before anything else in order to get a decent  	 * auth verifier.  	 */ -	auth_res = svc_authenticate(rqstp, &auth_stat); +	auth_res = svc_authenticate(rqstp);  	/* Also give the program a chance to reject this call: */ -	if (auth_res == SVC_OK && progp) { -		auth_stat = rpc_autherr_badcred; +	if (auth_res == SVC_OK && progp)  		auth_res = progp->pg_authenticate(rqstp); -	}  	if (auth_res != SVC_OK) -		trace_svc_authenticate(rqstp, auth_res, auth_stat); +		trace_svc_authenticate(rqstp, auth_res);  	switch (auth_res) {  	case SVC_OK:  		break; @@ -1392,15 +1397,15 @@ svc_process_common(struct svc_rqst *rqstp, struct kvec *argv, struct kvec *resv)  			goto release_dropit;  		if (*statp == rpc_garbage_args)  			goto err_garbage; -		auth_stat = svc_get_autherr(rqstp, statp); -		if (auth_stat != rpc_auth_ok) -			goto err_release_bad_auth;  	} else {  		dprintk("svc: calling dispatcher\n");  		if (!process.dispatch(rqstp, statp))  			goto release_dropit; /* Release reply info */  	} +	if (rqstp->rq_auth_stat != rpc_auth_ok) +		goto err_release_bad_auth; +  	/* Check RPC status result */  	if (*statp != rpc_success)  		resv->iov_len = ((void*)statp)  - resv->iov_base + 4; @@ -1450,13 +1455,14 @@ err_release_bad_auth:  	if (procp->pc_release)  		procp->pc_release(rqstp);  err_bad_auth: -	dprintk("svc: authentication failed (%d)\n", ntohl(auth_stat)); +	dprintk("svc: authentication failed (%d)\n", +		be32_to_cpu(rqstp->rq_auth_stat));  	serv->sv_stats->rpcbadauth++;  	/* Restore write pointer to location of accept status: */  	xdr_ressize_check(rqstp, reply_statp);  	svc_putnl(resv, 1);	/* REJECT */  	svc_putnl(resv, 1);	/* AUTH_ERROR */ -	svc_putnl(resv, ntohl(auth_stat));	/* status */ +	svc_putu32(resv, rqstp->rq_auth_stat);	/* status */  	goto sendit;  err_bad_prog: @@ -1503,6 +1509,12 @@ svc_process(struct svc_rqst *rqstp)  	struct svc_serv		*serv = rqstp->rq_server;  	u32			dir; +#if IS_ENABLED(CONFIG_FAIL_SUNRPC) +	if (!fail_sunrpc.ignore_server_disconnect && +	    should_fail(&fail_sunrpc.attr, 1)) +		svc_xprt_deferred_close(rqstp->rq_xprt); +#endif +  	/*  	 * Setup response xdr_buf.  	 * Initially it has just one page @@ -1630,6 +1642,21 @@ u32 svc_max_payload(const struct svc_rqst *rqstp)  EXPORT_SYMBOL_GPL(svc_max_payload);  /** + * svc_proc_name - Return RPC procedure name in string form + * @rqstp: svc_rqst to operate on + * + * Return value: + *   Pointer to a NUL-terminated string + */ +const char *svc_proc_name(const struct svc_rqst *rqstp) +{ +	if (rqstp && rqstp->rq_procinfo) +		return rqstp->rq_procinfo->pc_name; +	return "unknown"; +} + + +/**   * svc_encode_result_payload - mark a range of bytes as a result payload   * @rqstp: svc_rqst to operate on   * @offset: payload's byte offset in rqstp->rq_res diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index d66a8e44a1ae..e1153cba9cc6 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c @@ -539,6 +539,7 @@ static void svc_xprt_release(struct svc_rqst *rqstp)  	kfree(rqstp->rq_deferred);  	rqstp->rq_deferred = NULL; +	pagevec_release(&rqstp->rq_pvec);  	svc_free_res_pages(rqstp);  	rqstp->rq_res.page_len = 0;  	rqstp->rq_res.page_base = 0; @@ -664,6 +665,8 @@ static int svc_alloc_arg(struct svc_rqst *rqstp)  	struct xdr_buf *arg = &rqstp->rq_arg;  	unsigned long pages, filled; +	pagevec_init(&rqstp->rq_pvec); +  	pages = (serv->sv_max_mesg + 2 * PAGE_SIZE) >> PAGE_SHIFT;  	if (pages > RPCSVC_MAXPAGES) {  		pr_warn_once("svc: warning: pages=%lu > RPCSVC_MAXPAGES=%lu\n", @@ -835,7 +838,8 @@ static int svc_handle_xprt(struct svc_rqst *rqstp, struct svc_xprt *xprt)  		rqstp->rq_stime = ktime_get();  		rqstp->rq_reserved = serv->sv_max_mesg;  		atomic_add(rqstp->rq_reserved, &xprt->xpt_reserved); -	} +	} else +		svc_xprt_received(xprt);  out:  	trace_svc_handle_xprt(xprt, len);  	return len; diff --git a/net/sunrpc/svcauth.c b/net/sunrpc/svcauth.c index 998b196b6176..5a8b8e03fdd4 100644 --- a/net/sunrpc/svcauth.c +++ b/net/sunrpc/svcauth.c @@ -59,12 +59,12 @@ svc_put_auth_ops(struct auth_ops *aops)  }  int -svc_authenticate(struct svc_rqst *rqstp, __be32 *authp) +svc_authenticate(struct svc_rqst *rqstp)  {  	rpc_authflavor_t	flavor;  	struct auth_ops		*aops; -	*authp = rpc_auth_ok; +	rqstp->rq_auth_stat = rpc_auth_ok;  	flavor = svc_getnl(&rqstp->rq_arg.head[0]); @@ -72,7 +72,7 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp)  	aops = svc_get_auth_ops(flavor);  	if (aops == NULL) { -		*authp = rpc_autherr_badcred; +		rqstp->rq_auth_stat = rpc_autherr_badcred;  		return SVC_DENIED;  	} @@ -80,7 +80,7 @@ svc_authenticate(struct svc_rqst *rqstp, __be32 *authp)  	init_svc_cred(&rqstp->rq_cred);  	rqstp->rq_authop = aops; -	return aops->accept(rqstp, authp); +	return aops->accept(rqstp);  }  EXPORT_SYMBOL_GPL(svc_authenticate); diff --git a/net/sunrpc/svcauth_unix.c b/net/sunrpc/svcauth_unix.c index 35b7966ac3b3..d7ed7d49115a 100644 --- a/net/sunrpc/svcauth_unix.c +++ b/net/sunrpc/svcauth_unix.c @@ -681,8 +681,9 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)  	rqstp->rq_client = NULL;  	if (rqstp->rq_proc == 0) -		return SVC_OK; +		goto out; +	rqstp->rq_auth_stat = rpc_autherr_badcred;  	ipm = ip_map_cached_get(xprt);  	if (ipm == NULL)  		ipm = __ip_map_lookup(sn->ip_map_cache, rqstp->rq_server->sv_program->pg_class, @@ -719,13 +720,16 @@ svcauth_unix_set_client(struct svc_rqst *rqstp)  		put_group_info(cred->cr_group_info);  		cred->cr_group_info = gi;  	} + +out: +	rqstp->rq_auth_stat = rpc_auth_ok;  	return SVC_OK;  }  EXPORT_SYMBOL_GPL(svcauth_unix_set_client);  static int -svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp) +svcauth_null_accept(struct svc_rqst *rqstp)  {  	struct kvec	*argv = &rqstp->rq_arg.head[0];  	struct kvec	*resv = &rqstp->rq_res.head[0]; @@ -736,12 +740,12 @@ svcauth_null_accept(struct svc_rqst *rqstp, __be32 *authp)  	if (svc_getu32(argv) != 0) {  		dprintk("svc: bad null cred\n"); -		*authp = rpc_autherr_badcred; +		rqstp->rq_auth_stat = rpc_autherr_badcred;  		return SVC_DENIED;  	}  	if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) {  		dprintk("svc: bad null verf\n"); -		*authp = rpc_autherr_badverf; +		rqstp->rq_auth_stat = rpc_autherr_badverf;  		return SVC_DENIED;  	} @@ -785,7 +789,7 @@ struct auth_ops svcauth_null = {  static int -svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp) +svcauth_unix_accept(struct svc_rqst *rqstp)  {  	struct kvec	*argv = &rqstp->rq_arg.head[0];  	struct kvec	*resv = &rqstp->rq_res.head[0]; @@ -827,7 +831,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)  	}  	groups_sort(cred->cr_group_info);  	if (svc_getu32(argv) != htonl(RPC_AUTH_NULL) || svc_getu32(argv) != 0) { -		*authp = rpc_autherr_badverf; +		rqstp->rq_auth_stat = rpc_autherr_badverf;  		return SVC_DENIED;  	} @@ -839,7 +843,7 @@ svcauth_unix_accept(struct svc_rqst *rqstp, __be32 *authp)  	return SVC_OK;  badcred: -	*authp = rpc_autherr_badcred; +	rqstp->rq_auth_stat = rpc_autherr_badcred;  	return SVC_DENIED;  } diff --git a/net/sunrpc/sysfs.c b/net/sunrpc/sysfs.c index 64da3bfd28e6..9a6f17e18f73 100644 --- a/net/sunrpc/sysfs.c +++ b/net/sunrpc/sysfs.c @@ -100,6 +100,28 @@ static ssize_t rpc_sysfs_xprt_dstaddr_show(struct kobject *kobj,  	return ret + 1;  } +static ssize_t rpc_sysfs_xprt_srcaddr_show(struct kobject *kobj, +					   struct kobj_attribute *attr, +					   char *buf) +{ +	struct rpc_xprt *xprt = rpc_sysfs_xprt_kobj_get_xprt(kobj); +	struct sockaddr_storage saddr; +	struct sock_xprt *sock; +	ssize_t ret = -1; + +	if (!xprt) +		return 0; + +	sock = container_of(xprt, struct sock_xprt, xprt); +	if (kernel_getsockname(sock->sock, (struct sockaddr *)&saddr) < 0) +		goto out; + +	ret = sprintf(buf, "%pISc\n", &saddr); +out: +	xprt_put(xprt); +	return ret + 1; +} +  static ssize_t rpc_sysfs_xprt_info_show(struct kobject *kobj,  					struct kobj_attribute *attr,  					char *buf) @@ -114,14 +136,16 @@ static ssize_t rpc_sysfs_xprt_info_show(struct kobject *kobj,  		       "max_num_slots=%u\nmin_num_slots=%u\nnum_reqs=%u\n"  		       "binding_q_len=%u\nsending_q_len=%u\npending_q_len=%u\n"  		       "backlog_q_len=%u\nmain_xprt=%d\nsrc_port=%u\n" -		       "tasks_queuelen=%ld\n", +		       "tasks_queuelen=%ld\ndst_port=%s\n",  		       xprt->last_used, xprt->cong, xprt->cwnd, xprt->max_reqs,  		       xprt->min_reqs, xprt->num_reqs, xprt->binding.qlen,  		       xprt->sending.qlen, xprt->pending.qlen,  		       xprt->backlog.qlen, xprt->main,  		       (xprt->xprt_class->ident == XPRT_TRANSPORT_TCP) ?  		       get_srcport(xprt) : 0, -		       atomic_long_read(&xprt->queuelen)); +		       atomic_long_read(&xprt->queuelen), +		       (xprt->xprt_class->ident == XPRT_TRANSPORT_TCP) ? +				xprt->address_strings[RPC_DISPLAY_PORT] : "0");  	xprt_put(xprt);  	return ret + 1;  } @@ -183,8 +207,10 @@ static ssize_t rpc_sysfs_xprt_switch_info_show(struct kobject *kobj,  	if (!xprt_switch)  		return 0; -	ret = sprintf(buf, "num_xprts=%u\nnum_active=%u\nqueue_len=%ld\n", +	ret = sprintf(buf, "num_xprts=%u\nnum_active=%u\n" +		      "num_unique_destaddr=%u\nqueue_len=%ld\n",  		      xprt_switch->xps_nxprts, xprt_switch->xps_nactive, +		      xprt_switch->xps_nunique_destaddr_xprts,  		      atomic_long_read(&xprt_switch->xps_queuelen));  	xprt_switch_put(xprt_switch);  	return ret + 1; @@ -376,6 +402,9 @@ static const void *rpc_sysfs_xprt_namespace(struct kobject *kobj)  static struct kobj_attribute rpc_sysfs_xprt_dstaddr = __ATTR(dstaddr,  	0644, rpc_sysfs_xprt_dstaddr_show, rpc_sysfs_xprt_dstaddr_store); +static struct kobj_attribute rpc_sysfs_xprt_srcaddr = __ATTR(srcaddr, +	0644, rpc_sysfs_xprt_srcaddr_show, NULL); +  static struct kobj_attribute rpc_sysfs_xprt_info = __ATTR(xprt_info,  	0444, rpc_sysfs_xprt_info_show, NULL); @@ -384,6 +413,7 @@ static struct kobj_attribute rpc_sysfs_xprt_change_state = __ATTR(xprt_state,  static struct attribute *rpc_sysfs_xprt_attrs[] = {  	&rpc_sysfs_xprt_dstaddr.attr, +	&rpc_sysfs_xprt_srcaddr.attr,  	&rpc_sysfs_xprt_info.attr,  	&rpc_sysfs_xprt_change_state.attr,  	NULL, diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index fb6db09725c7..cfd681700d1a 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -56,6 +56,7 @@  #include "sunrpc.h"  #include "sysfs.h" +#include "fail.h"  /*   * Local variables @@ -761,6 +762,20 @@ void xprt_disconnect_done(struct rpc_xprt *xprt)  EXPORT_SYMBOL_GPL(xprt_disconnect_done);  /** + * xprt_schedule_autoclose_locked - Try to schedule an autoclose RPC call + * @xprt: transport to disconnect + */ +static void xprt_schedule_autoclose_locked(struct rpc_xprt *xprt) +{ +	set_bit(XPRT_CLOSE_WAIT, &xprt->state); +	if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0) +		queue_work(xprtiod_workqueue, &xprt->task_cleanup); +	else if (xprt->snd_task && !test_bit(XPRT_SND_IS_COOKIE, &xprt->state)) +		rpc_wake_up_queued_task_set_status(&xprt->pending, +						   xprt->snd_task, -ENOTCONN); +} + +/**   * xprt_force_disconnect - force a transport to disconnect   * @xprt: transport to disconnect   * @@ -771,13 +786,7 @@ void xprt_force_disconnect(struct rpc_xprt *xprt)  	/* Don't race with the test_bit() in xprt_clear_locked() */  	spin_lock(&xprt->transport_lock); -	set_bit(XPRT_CLOSE_WAIT, &xprt->state); -	/* Try to schedule an autoclose RPC call */ -	if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0) -		queue_work(xprtiod_workqueue, &xprt->task_cleanup); -	else if (xprt->snd_task) -		rpc_wake_up_queued_task_set_status(&xprt->pending, -				xprt->snd_task, -ENOTCONN); +	xprt_schedule_autoclose_locked(xprt);  	spin_unlock(&xprt->transport_lock);  }  EXPORT_SYMBOL_GPL(xprt_force_disconnect); @@ -817,11 +826,7 @@ void xprt_conditional_disconnect(struct rpc_xprt *xprt, unsigned int cookie)  		goto out;  	if (test_bit(XPRT_CLOSING, &xprt->state))  		goto out; -	set_bit(XPRT_CLOSE_WAIT, &xprt->state); -	/* Try to schedule an autoclose RPC call */ -	if (test_and_set_bit(XPRT_LOCKED, &xprt->state) == 0) -		queue_work(xprtiod_workqueue, &xprt->task_cleanup); -	xprt_wake_pending_tasks(xprt, -EAGAIN); +	xprt_schedule_autoclose_locked(xprt);  out:  	spin_unlock(&xprt->transport_lock);  } @@ -855,6 +860,19 @@ xprt_init_autodisconnect(struct timer_list *t)  	queue_work(xprtiod_workqueue, &xprt->task_cleanup);  } +#if IS_ENABLED(CONFIG_FAIL_SUNRPC) +static void xprt_inject_disconnect(struct rpc_xprt *xprt) +{ +	if (!fail_sunrpc.ignore_client_disconnect && +	    should_fail(&fail_sunrpc.attr, 1)) +		xprt->ops->inject_disconnect(xprt); +} +#else +static inline void xprt_inject_disconnect(struct rpc_xprt *xprt) +{ +} +#endif +  bool xprt_lock_connect(struct rpc_xprt *xprt,  		struct rpc_task *task,  		void *cookie) @@ -866,12 +884,14 @@ bool xprt_lock_connect(struct rpc_xprt *xprt,  		goto out;  	if (xprt->snd_task != task)  		goto out; +	set_bit(XPRT_SND_IS_COOKIE, &xprt->state);  	xprt->snd_task = cookie;  	ret = true;  out:  	spin_unlock(&xprt->transport_lock);  	return ret;  } +EXPORT_SYMBOL_GPL(xprt_lock_connect);  void xprt_unlock_connect(struct rpc_xprt *xprt, void *cookie)  { @@ -881,12 +901,14 @@ void xprt_unlock_connect(struct rpc_xprt *xprt, void *cookie)  	if (!test_bit(XPRT_LOCKED, &xprt->state))  		goto out;  	xprt->snd_task =NULL; +	clear_bit(XPRT_SND_IS_COOKIE, &xprt->state);  	xprt->ops->release_xprt(xprt, NULL);  	xprt_schedule_autodisconnect(xprt);  out:  	spin_unlock(&xprt->transport_lock);  	wake_up_bit(&xprt->state, XPRT_LOCKED);  } +EXPORT_SYMBOL_GPL(xprt_unlock_connect);  /**   * xprt_connect - schedule a transport connect operation diff --git a/net/sunrpc/xprtmultipath.c b/net/sunrpc/xprtmultipath.c index c60820e45082..1693f81aae37 100644 --- a/net/sunrpc/xprtmultipath.c +++ b/net/sunrpc/xprtmultipath.c @@ -139,6 +139,7 @@ struct rpc_xprt_switch *xprt_switch_alloc(struct rpc_xprt *xprt,  		xps->xps_iter_ops = &rpc_xprt_iter_singular;  		rpc_sysfs_xprt_switch_setup(xps, xprt, gfp_flags);  		xprt_switch_add_xprt_locked(xps, xprt); +		xps->xps_nunique_destaddr_xprts = 1;  		rpc_sysfs_xprt_setup(xps, xprt, gfp_flags);  	} diff --git a/net/sunrpc/xprtrdma/backchannel.c b/net/sunrpc/xprtrdma/backchannel.c index 1151efd09b27..17f174d6ea3b 100644 --- a/net/sunrpc/xprtrdma/backchannel.c +++ b/net/sunrpc/xprtrdma/backchannel.c @@ -115,7 +115,7 @@ int xprt_rdma_bc_send_reply(struct rpc_rqst *rqst)  	if (rc < 0)  		goto failed_marshal; -	if (rpcrdma_post_sends(r_xprt, req)) +	if (frwr_send(r_xprt, req))  		goto drop_connection;  	return 0; diff --git a/net/sunrpc/xprtrdma/frwr_ops.c b/net/sunrpc/xprtrdma/frwr_ops.c index 229fcc9a9064..f700b34a5bfd 100644 --- a/net/sunrpc/xprtrdma/frwr_ops.c +++ b/net/sunrpc/xprtrdma/frwr_ops.c @@ -394,6 +394,7 @@ int frwr_send(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)  	struct rpcrdma_ep *ep = r_xprt->rx_ep;  	struct rpcrdma_mr *mr;  	unsigned int num_wrs; +	int ret;  	num_wrs = 1;  	post_wr = send_wr; @@ -420,7 +421,10 @@ int frwr_send(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)  	}  	trace_xprtrdma_post_send(req); -	return ib_post_send(ep->re_id->qp, post_wr, NULL); +	ret = ib_post_send(ep->re_id->qp, post_wr, NULL); +	if (ret) +		trace_xprtrdma_post_send_err(r_xprt, req, ret); +	return ret;  }  /** @@ -557,6 +561,10 @@ void frwr_unmap_sync(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)  	/* On error, the MRs get destroyed once the QP has drained. */  	trace_xprtrdma_post_linv_err(req, rc); + +	/* Force a connection loss to ensure complete recovery. +	 */ +	rpcrdma_force_disconnect(ep);  }  /** @@ -653,4 +661,8 @@ void frwr_unmap_async(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req)  	 * retransmission.  	 */  	rpcrdma_unpin_rqst(req->rl_reply); + +	/* Force a connection loss to ensure complete recovery. +	 */ +	rpcrdma_force_disconnect(ep);  } diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 1e651447dc4e..e27433f08ca7 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -35,6 +35,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc);   * controlling svcxprt_rdma is destroyed.   */  struct svc_rdma_rw_ctxt { +	struct llist_node	rw_node;  	struct list_head	rw_list;  	struct rdma_rw_ctx	rw_ctx;  	unsigned int		rw_nents; @@ -53,19 +54,19 @@ static struct svc_rdma_rw_ctxt *  svc_rdma_get_rw_ctxt(struct svcxprt_rdma *rdma, unsigned int sges)  {  	struct svc_rdma_rw_ctxt *ctxt; +	struct llist_node *node;  	spin_lock(&rdma->sc_rw_ctxt_lock); - -	ctxt = svc_rdma_next_ctxt(&rdma->sc_rw_ctxts); -	if (ctxt) { -		list_del(&ctxt->rw_list); -		spin_unlock(&rdma->sc_rw_ctxt_lock); +	node = llist_del_first(&rdma->sc_rw_ctxts); +	spin_unlock(&rdma->sc_rw_ctxt_lock); +	if (node) { +		ctxt = llist_entry(node, struct svc_rdma_rw_ctxt, rw_node);  	} else { -		spin_unlock(&rdma->sc_rw_ctxt_lock);  		ctxt = kmalloc(struct_size(ctxt, rw_first_sgl, SG_CHUNK_SIZE),  			       GFP_KERNEL);  		if (!ctxt)  			goto out_noctx; +  		INIT_LIST_HEAD(&ctxt->rw_list);  	} @@ -83,14 +84,18 @@ out_noctx:  	return NULL;  } -static void svc_rdma_put_rw_ctxt(struct svcxprt_rdma *rdma, -				 struct svc_rdma_rw_ctxt *ctxt) +static void __svc_rdma_put_rw_ctxt(struct svcxprt_rdma *rdma, +				   struct svc_rdma_rw_ctxt *ctxt, +				   struct llist_head *list)  {  	sg_free_table_chained(&ctxt->rw_sg_table, SG_CHUNK_SIZE); +	llist_add(&ctxt->rw_node, list); +} -	spin_lock(&rdma->sc_rw_ctxt_lock); -	list_add(&ctxt->rw_list, &rdma->sc_rw_ctxts); -	spin_unlock(&rdma->sc_rw_ctxt_lock); +static void svc_rdma_put_rw_ctxt(struct svcxprt_rdma *rdma, +				 struct svc_rdma_rw_ctxt *ctxt) +{ +	__svc_rdma_put_rw_ctxt(rdma, ctxt, &rdma->sc_rw_ctxts);  }  /** @@ -101,9 +106,10 @@ static void svc_rdma_put_rw_ctxt(struct svcxprt_rdma *rdma,  void svc_rdma_destroy_rw_ctxts(struct svcxprt_rdma *rdma)  {  	struct svc_rdma_rw_ctxt *ctxt; +	struct llist_node *node; -	while ((ctxt = svc_rdma_next_ctxt(&rdma->sc_rw_ctxts)) != NULL) { -		list_del(&ctxt->rw_list); +	while ((node = llist_del_first(&rdma->sc_rw_ctxts)) != NULL) { +		ctxt = llist_entry(node, struct svc_rdma_rw_ctxt, rw_node);  		kfree(ctxt);  	}  } @@ -171,20 +177,35 @@ static void svc_rdma_cc_init(struct svcxprt_rdma *rdma,  	cc->cc_sqecount = 0;  } +/* + * The consumed rw_ctx's are cleaned and placed on a local llist so + * that only one atomic llist operation is needed to put them all + * back on the free list. + */  static void svc_rdma_cc_release(struct svc_rdma_chunk_ctxt *cc,  				enum dma_data_direction dir)  {  	struct svcxprt_rdma *rdma = cc->cc_rdma; +	struct llist_node *first, *last;  	struct svc_rdma_rw_ctxt *ctxt; +	LLIST_HEAD(free); +	first = last = NULL;  	while ((ctxt = svc_rdma_next_ctxt(&cc->cc_rwctxts)) != NULL) {  		list_del(&ctxt->rw_list);  		rdma_rw_ctx_destroy(&ctxt->rw_ctx, rdma->sc_qp,  				    rdma->sc_port_num, ctxt->rw_sg_table.sgl,  				    ctxt->rw_nents, dir); -		svc_rdma_put_rw_ctxt(rdma, ctxt); +		__svc_rdma_put_rw_ctxt(rdma, ctxt, &free); + +		ctxt->rw_node.next = first; +		first = &ctxt->rw_node; +		if (!last) +			last = first;  	} +	if (first) +		llist_add_batch(first, last, &rdma->sc_rw_ctxts);  }  /* State for sending a Write or Reply chunk. @@ -248,8 +269,7 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc)  	trace_svcrdma_wc_write(wc, &cc->cc_cid); -	atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail); -	wake_up(&rdma->sc_send_wait); +	svc_rdma_wake_send_waiters(rdma, cc->cc_sqecount);  	if (unlikely(wc->status != IB_WC_SUCCESS))  		svc_xprt_deferred_close(&rdma->sc_xprt); @@ -304,9 +324,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc)  	trace_svcrdma_wc_read(wc, &cc->cc_cid); -	atomic_add(cc->cc_sqecount, &rdma->sc_sq_avail); -	wake_up(&rdma->sc_send_wait); - +	svc_rdma_wake_send_waiters(rdma, cc->cc_sqecount);  	cc->cc_status = wc->status;  	complete(&cc->cc_done);  	return; diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index d6bbafb773e1..599021b2391d 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -113,13 +113,6 @@  static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc); -static inline struct svc_rdma_send_ctxt * -svc_rdma_next_send_ctxt(struct list_head *list) -{ -	return list_first_entry_or_null(list, struct svc_rdma_send_ctxt, -					sc_list); -} -  static void svc_rdma_send_cid_init(struct svcxprt_rdma *rdma,  				   struct rpc_rdma_cid *cid)  { @@ -182,9 +175,10 @@ fail0:  void svc_rdma_send_ctxts_destroy(struct svcxprt_rdma *rdma)  {  	struct svc_rdma_send_ctxt *ctxt; +	struct llist_node *node; -	while ((ctxt = svc_rdma_next_send_ctxt(&rdma->sc_send_ctxts))) { -		list_del(&ctxt->sc_list); +	while ((node = llist_del_first(&rdma->sc_send_ctxts)) != NULL) { +		ctxt = llist_entry(node, struct svc_rdma_send_ctxt, sc_node);  		ib_dma_unmap_single(rdma->sc_pd->device,  				    ctxt->sc_sges[0].addr,  				    rdma->sc_max_req_size, @@ -204,12 +198,13 @@ void svc_rdma_send_ctxts_destroy(struct svcxprt_rdma *rdma)  struct svc_rdma_send_ctxt *svc_rdma_send_ctxt_get(struct svcxprt_rdma *rdma)  {  	struct svc_rdma_send_ctxt *ctxt; +	struct llist_node *node;  	spin_lock(&rdma->sc_send_lock); -	ctxt = svc_rdma_next_send_ctxt(&rdma->sc_send_ctxts); -	if (!ctxt) +	node = llist_del_first(&rdma->sc_send_ctxts); +	if (!node)  		goto out_empty; -	list_del(&ctxt->sc_list); +	ctxt = llist_entry(node, struct svc_rdma_send_ctxt, sc_node);  	spin_unlock(&rdma->sc_send_lock);  out: @@ -253,9 +248,21 @@ void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma,  					     ctxt->sc_sges[i].length);  	} -	spin_lock(&rdma->sc_send_lock); -	list_add(&ctxt->sc_list, &rdma->sc_send_ctxts); -	spin_unlock(&rdma->sc_send_lock); +	llist_add(&ctxt->sc_node, &rdma->sc_send_ctxts); +} + +/** + * svc_rdma_wake_send_waiters - manage Send Queue accounting + * @rdma: controlling transport + * @avail: Number of additional SQEs that are now available + * + */ +void svc_rdma_wake_send_waiters(struct svcxprt_rdma *rdma, int avail) +{ +	atomic_add(avail, &rdma->sc_sq_avail); +	smp_mb__after_atomic(); +	if (unlikely(waitqueue_active(&rdma->sc_send_wait))) +		wake_up(&rdma->sc_send_wait);  }  /** @@ -275,11 +282,9 @@ static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc)  	trace_svcrdma_wc_send(wc, &ctxt->sc_cid); +	svc_rdma_wake_send_waiters(rdma, 1);  	complete(&ctxt->sc_done); -	atomic_inc(&rdma->sc_sq_avail); -	wake_up(&rdma->sc_send_wait); -  	if (unlikely(wc->status != IB_WC_SUCCESS))  		svc_xprt_deferred_close(&rdma->sc_xprt);  } diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index d94b7759ada1..94b20fb47135 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -136,9 +136,9 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv,  	svc_xprt_init(net, &svc_rdma_class, &cma_xprt->sc_xprt, serv);  	INIT_LIST_HEAD(&cma_xprt->sc_accept_q);  	INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q); -	INIT_LIST_HEAD(&cma_xprt->sc_send_ctxts); +	init_llist_head(&cma_xprt->sc_send_ctxts);  	init_llist_head(&cma_xprt->sc_recv_ctxts); -	INIT_LIST_HEAD(&cma_xprt->sc_rw_ctxts); +	init_llist_head(&cma_xprt->sc_rw_ctxts);  	init_waitqueue_head(&cma_xprt->sc_send_wait);  	spin_lock_init(&cma_xprt->sc_lock); @@ -545,7 +545,6 @@ static void __svc_rdma_free(struct work_struct *work)  {  	struct svcxprt_rdma *rdma =  		container_of(work, struct svcxprt_rdma, sc_work); -	struct svc_xprt *xprt = &rdma->sc_xprt;  	/* This blocks until the Completion Queues are empty */  	if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) @@ -553,12 +552,6 @@ static void __svc_rdma_free(struct work_struct *work)  	svc_rdma_flush_recv_queues(rdma); -	/* Final put of backchannel client transport */ -	if (xprt->xpt_bc_xprt) { -		xprt_put(xprt->xpt_bc_xprt); -		xprt->xpt_bc_xprt = NULL; -	} -  	svc_rdma_destroy_rw_ctxts(rdma);  	svc_rdma_send_ctxts_destroy(rdma);  	svc_rdma_recv_ctxts_destroy(rdma); diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 9c2ffc67c0fd..16e5696314a4 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -250,12 +250,9 @@ xprt_rdma_connect_worker(struct work_struct *work)  					   xprt->stat.connect_start;  		xprt_set_connected(xprt);  		rc = -EAGAIN; -	} else { -		/* Force a call to xprt_rdma_close to clean up */ -		spin_lock(&xprt->transport_lock); -		set_bit(XPRT_CLOSE_WAIT, &xprt->state); -		spin_unlock(&xprt->transport_lock); -	} +	} else +		rpcrdma_xprt_disconnect(r_xprt); +	xprt_unlock_connect(xprt, r_xprt);  	xprt_wake_pending_tasks(xprt, rc);  } @@ -489,6 +486,8 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task)  	struct rpcrdma_ep *ep = r_xprt->rx_ep;  	unsigned long delay; +	WARN_ON_ONCE(!xprt_lock_connect(xprt, task, r_xprt)); +  	delay = 0;  	if (ep && ep->re_connect_status != 0) {  		delay = xprt_reconnect_delay(xprt); @@ -661,7 +660,7 @@ xprt_rdma_send_request(struct rpc_rqst *rqst)  		goto drop_connection;  	rqst->rq_xtime = ktime_get(); -	if (rpcrdma_post_sends(r_xprt, req)) +	if (frwr_send(r_xprt, req))  		goto drop_connection;  	rqst->rq_xmit_bytes_sent += rqst->rq_snd_buf.len; diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index 649c23518ec0..aaec3c9be8db 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -124,7 +124,7 @@ static void rpcrdma_xprt_drain(struct rpcrdma_xprt *r_xprt)   * connection is closed or lost. (The important thing is it needs   * to be invoked "at least" once).   */ -static void rpcrdma_force_disconnect(struct rpcrdma_ep *ep) +void rpcrdma_force_disconnect(struct rpcrdma_ep *ep)  {  	if (atomic_add_unless(&ep->re_force_disconnect, 1, 1))  		xprt_force_disconnect(ep->re_xprt); @@ -1350,21 +1350,6 @@ static void rpcrdma_regbuf_free(struct rpcrdma_regbuf *rb)  }  /** - * rpcrdma_post_sends - Post WRs to a transport's Send Queue - * @r_xprt: controlling transport instance - * @req: rpcrdma_req containing the Send WR to post - * - * Returns 0 if the post was successful, otherwise -ENOTCONN - * is returned. - */ -int rpcrdma_post_sends(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req) -{ -	if (frwr_send(r_xprt, req)) -		return -ENOTCONN; -	return 0; -} - -/**   * rpcrdma_post_recvs - Refill the Receive Queue   * @r_xprt: controlling transport instance   * @needed: current credit grant @@ -1416,12 +1401,8 @@ void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed, bool temp)  	rc = ib_post_recv(ep->re_id->qp, wr,  			  (const struct ib_recv_wr **)&bad_wr); -	if (atomic_dec_return(&ep->re_receiving) > 0) -		complete(&ep->re_done); - -out: -	trace_xprtrdma_post_recvs(r_xprt, count, rc);  	if (rc) { +		trace_xprtrdma_post_recvs_err(r_xprt, rc);  		for (wr = bad_wr; wr;) {  			struct rpcrdma_rep *rep; @@ -1431,6 +1412,11 @@ out:  			--count;  		}  	} +	if (atomic_dec_return(&ep->re_receiving) > 0) +		complete(&ep->re_done); + +out: +	trace_xprtrdma_post_recvs(r_xprt, count);  	ep->re_receive_count += count;  	return;  } diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index 5d231d94e944..d91f54eae00b 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -454,11 +454,11 @@ extern unsigned int xprt_rdma_memreg_strategy;  /*   * Endpoint calls - xprtrdma/verbs.c   */ +void rpcrdma_force_disconnect(struct rpcrdma_ep *ep);  void rpcrdma_flush_disconnect(struct rpcrdma_xprt *r_xprt, struct ib_wc *wc);  int rpcrdma_xprt_connect(struct rpcrdma_xprt *r_xprt);  void rpcrdma_xprt_disconnect(struct rpcrdma_xprt *r_xprt); -int rpcrdma_post_sends(struct rpcrdma_xprt *r_xprt, struct rpcrdma_req *req);  void rpcrdma_post_recvs(struct rpcrdma_xprt *r_xprt, int needed, bool temp);  /* diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index e573dcecdd66..04f1b78bcbca 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -1656,7 +1656,7 @@ static int xs_get_srcport(struct sock_xprt *transport)  unsigned short get_srcport(struct rpc_xprt *xprt)  {  	struct sock_xprt *sock = container_of(xprt, struct sock_xprt, xprt); -	return sock->srcport; +	return xs_sock_getport(sock->sock);  }  EXPORT_SYMBOL(get_srcport); @@ -2099,13 +2099,20 @@ static void xs_tcp_shutdown(struct rpc_xprt *xprt)  	if (sock == NULL)  		return; +	if (!xprt->reuseport) { +		xs_close(xprt); +		return; +	}  	switch (skst) { -	default: +	case TCP_FIN_WAIT1: +	case TCP_FIN_WAIT2: +		break; +	case TCP_ESTABLISHED: +	case TCP_CLOSE_WAIT:  		kernel_sock_shutdown(sock, SHUT_RDWR);  		trace_rpc_socket_shutdown(xprt, sock);  		break; -	case TCP_CLOSE: -	case TCP_TIME_WAIT: +	default:  		xs_reset_transport(transport);  	}  } @@ -3149,24 +3156,6 @@ void cleanup_socket_xprt(void)  	xprt_unregister_transport(&xs_bc_tcp_transport);  } -static int param_set_uint_minmax(const char *val, -		const struct kernel_param *kp, -		unsigned int min, unsigned int max) -{ -	unsigned int num; -	int ret; - -	if (!val) -		return -EINVAL; -	ret = kstrtouint(val, 0, &num); -	if (ret) -		return ret; -	if (num < min || num > max) -		return -EINVAL; -	*((unsigned int *)kp->arg) = num; -	return 0; -} -  static int param_set_portnr(const char *val, const struct kernel_param *kp)  {  	return param_set_uint_minmax(val, kp,  | 
