diff options
Diffstat (limited to 'net')
| -rw-r--r-- | net/ceph/messenger.c | 2 | ||||
| -rw-r--r-- | net/ceph/osd_client.c | 27 | ||||
| -rw-r--r-- | net/ceph/osdmap.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/auth.c | 27 | ||||
| -rw-r--r-- | net/sunrpc/auth_generic.c | 82 | ||||
| -rw-r--r-- | net/sunrpc/auth_gss/auth_gss.c | 442 | ||||
| -rw-r--r-- | net/sunrpc/auth_null.c | 6 | ||||
| -rw-r--r-- | net/sunrpc/auth_unix.c | 6 | ||||
| -rw-r--r-- | net/sunrpc/clnt.c | 157 | ||||
| -rw-r--r-- | net/sunrpc/rpc_pipe.c | 193 | ||||
| -rw-r--r-- | net/sunrpc/sched.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/stats.c | 2 | ||||
| -rw-r--r-- | net/sunrpc/xprtsock.c | 13 | 
13 files changed, 732 insertions, 229 deletions
| diff --git a/net/ceph/messenger.c b/net/ceph/messenger.c index 3be308e14302..4a5df7b1cc9f 100644 --- a/net/ceph/messenger.c +++ b/net/ceph/messenger.c @@ -290,7 +290,7 @@ int ceph_msgr_init(void)  	if (ceph_msgr_slab_init())  		return -ENOMEM; -	ceph_msgr_wq = alloc_workqueue("ceph-msgr", WQ_NON_REENTRANT, 0); +	ceph_msgr_wq = alloc_workqueue("ceph-msgr", 0, 0);  	if (ceph_msgr_wq)  		return 0; diff --git a/net/ceph/osd_client.c b/net/ceph/osd_client.c index dd47889adc4a..1606f740d6ae 100644 --- a/net/ceph/osd_client.c +++ b/net/ceph/osd_client.c @@ -503,7 +503,9 @@ void osd_req_op_extent_init(struct ceph_osd_request *osd_req,  	struct ceph_osd_req_op *op = _osd_req_op_init(osd_req, which, opcode);  	size_t payload_len = 0; -	BUG_ON(opcode != CEPH_OSD_OP_READ && opcode != CEPH_OSD_OP_WRITE); +	BUG_ON(opcode != CEPH_OSD_OP_READ && opcode != CEPH_OSD_OP_WRITE && +	       opcode != CEPH_OSD_OP_DELETE && opcode != CEPH_OSD_OP_ZERO && +	       opcode != CEPH_OSD_OP_TRUNCATE);  	op->extent.offset = offset;  	op->extent.length = length; @@ -631,6 +633,9 @@ static u64 osd_req_encode_op(struct ceph_osd_request *req,  		break;  	case CEPH_OSD_OP_READ:  	case CEPH_OSD_OP_WRITE: +	case CEPH_OSD_OP_ZERO: +	case CEPH_OSD_OP_DELETE: +	case CEPH_OSD_OP_TRUNCATE:  		if (src->op == CEPH_OSD_OP_WRITE)  			request_data_len = src->extent.length;  		dst->extent.offset = cpu_to_le64(src->extent.offset); @@ -715,7 +720,9 @@ struct ceph_osd_request *ceph_osdc_new_request(struct ceph_osd_client *osdc,  	u64 object_base;  	int r; -	BUG_ON(opcode != CEPH_OSD_OP_READ && opcode != CEPH_OSD_OP_WRITE); +	BUG_ON(opcode != CEPH_OSD_OP_READ && opcode != CEPH_OSD_OP_WRITE && +	       opcode != CEPH_OSD_OP_DELETE && opcode != CEPH_OSD_OP_ZERO && +	       opcode != CEPH_OSD_OP_TRUNCATE);  	req = ceph_osdc_alloc_request(osdc, snapc, num_ops, use_mempool,  					GFP_NOFS); @@ -1488,14 +1495,14 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg,  	dout("handle_reply %p tid %llu req %p result %d\n", msg, tid,  	     req, result); -	ceph_decode_need(&p, end, 4, bad); +	ceph_decode_need(&p, end, 4, bad_put);  	numops = ceph_decode_32(&p);  	if (numops > CEPH_OSD_MAX_OP)  		goto bad_put;  	if (numops != req->r_num_ops)  		goto bad_put;  	payload_len = 0; -	ceph_decode_need(&p, end, numops * sizeof(struct ceph_osd_op), bad); +	ceph_decode_need(&p, end, numops * sizeof(struct ceph_osd_op), bad_put);  	for (i = 0; i < numops; i++) {  		struct ceph_osd_op *op = p;  		int len; @@ -1513,7 +1520,7 @@ static void handle_reply(struct ceph_osd_client *osdc, struct ceph_msg *msg,  		goto bad_put;  	} -	ceph_decode_need(&p, end, 4 + numops * 4, bad); +	ceph_decode_need(&p, end, 4 + numops * 4, bad_put);  	retry_attempt = ceph_decode_32(&p);  	for (i = 0; i < numops; i++)  		req->r_reply_op_result[i] = ceph_decode_32(&p); @@ -1786,6 +1793,8 @@ void ceph_osdc_handle_map(struct ceph_osd_client *osdc, struct ceph_msg *msg)  		nr_maps--;  	} +	if (!osdc->osdmap) +		goto bad;  done:  	downgrade_write(&osdc->map_sem);  	ceph_monc_got_osdmap(&osdc->client->monc, osdc->osdmap->epoch); @@ -2129,6 +2138,8 @@ int ceph_osdc_start_request(struct ceph_osd_client *osdc,  			dout("osdc_start_request failed map, "  				" will retry %lld\n", req->r_tid);  			rc = 0; +		} else { +			__unregister_request(osdc, req);  		}  		goto out_unlock;  	} @@ -2253,12 +2264,10 @@ int ceph_osdc_init(struct ceph_osd_client *osdc, struct ceph_client *client)  	if (err < 0)  		goto out_msgpool; +	err = -ENOMEM;  	osdc->notify_wq = create_singlethread_workqueue("ceph-watch-notify"); -	if (IS_ERR(osdc->notify_wq)) { -		err = PTR_ERR(osdc->notify_wq); -		osdc->notify_wq = NULL; +	if (!osdc->notify_wq)  		goto out_msgpool; -	}  	return 0;  out_msgpool: diff --git a/net/ceph/osdmap.c b/net/ceph/osdmap.c index 603ddd92db19..dbd9a4792427 100644 --- a/net/ceph/osdmap.c +++ b/net/ceph/osdmap.c @@ -1129,7 +1129,7 @@ static int *calc_pg_raw(struct ceph_osdmap *osdmap, struct ceph_pg pgid,  	/* pg_temp? */  	pgid.seed = ceph_stable_mod(pgid.seed, pool->pg_num, -				    pool->pgp_num_mask); +				    pool->pg_num_mask);  	pg = __lookup_pg_mapping(&osdmap->pg_temp, pgid);  	if (pg) {  		*num = pg->len; diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index ed2fdd210c0b..415159061cd0 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c @@ -250,11 +250,11 @@ rpcauth_list_flavors(rpc_authflavor_t *array, int size)  EXPORT_SYMBOL_GPL(rpcauth_list_flavors);  struct rpc_auth * -rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) +rpcauth_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)  {  	struct rpc_auth		*auth;  	const struct rpc_authops *ops; -	u32			flavor = pseudoflavor_to_flavor(pseudoflavor); +	u32			flavor = pseudoflavor_to_flavor(args->pseudoflavor);  	auth = ERR_PTR(-EINVAL);  	if (flavor >= RPC_AUTH_MAXFLAVOR) @@ -269,7 +269,7 @@ rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)  		goto out;  	}  	spin_unlock(&rpc_authflavor_lock); -	auth = ops->create(clnt, pseudoflavor); +	auth = ops->create(args, clnt);  	module_put(ops->owner);  	if (IS_ERR(auth))  		return auth; @@ -343,6 +343,27 @@ out_nocache:  EXPORT_SYMBOL_GPL(rpcauth_init_credcache);  /* + * Setup a credential key lifetime timeout notification + */ +int +rpcauth_key_timeout_notify(struct rpc_auth *auth, struct rpc_cred *cred) +{ +	if (!cred->cr_auth->au_ops->key_timeout) +		return 0; +	return cred->cr_auth->au_ops->key_timeout(auth, cred); +} +EXPORT_SYMBOL_GPL(rpcauth_key_timeout_notify); + +bool +rpcauth_cred_key_to_expire(struct rpc_cred *cred) +{ +	if (!cred->cr_ops->crkey_to_expire) +		return false; +	return cred->cr_ops->crkey_to_expire(cred); +} +EXPORT_SYMBOL_GPL(rpcauth_cred_key_to_expire); + +/*   * Destroy a list of credentials   */  static inline diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index b6badafc6494..f6d84be49050 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c @@ -89,6 +89,7 @@ generic_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)  	gcred->acred.uid = acred->uid;  	gcred->acred.gid = acred->gid;  	gcred->acred.group_info = acred->group_info; +	gcred->acred.ac_flags = 0;  	if (gcred->acred.group_info != NULL)  		get_group_info(gcred->acred.group_info);  	gcred->acred.machine_cred = acred->machine_cred; @@ -182,11 +183,78 @@ void rpc_destroy_generic_auth(void)  	rpcauth_destroy_credcache(&generic_auth);  } +/* + * Test the the current time (now) against the underlying credential key expiry + * minus a timeout and setup notification. + * + * The normal case: + * If 'now' is before the key expiry minus RPC_KEY_EXPIRE_TIMEO, set + * the RPC_CRED_NOTIFY_TIMEOUT flag to setup the underlying credential + * rpc_credops crmatch routine to notify this generic cred when it's key + * expiration is within RPC_KEY_EXPIRE_TIMEO, and return 0. + * + * The error case: + * If the underlying cred lookup fails, return -EACCES. + * + * The 'almost' error case: + * If 'now' is within key expiry minus RPC_KEY_EXPIRE_TIMEO, but not within + * key expiry minus RPC_KEY_EXPIRE_FAIL, set the RPC_CRED_EXPIRE_SOON bit + * on the acred ac_flags and return 0. + */ +static int +generic_key_timeout(struct rpc_auth *auth, struct rpc_cred *cred) +{ +	struct auth_cred *acred = &container_of(cred, struct generic_cred, +						gc_base)->acred; +	struct rpc_cred *tcred; +	int ret = 0; + + +	/* Fast track for non crkey_timeout (no key) underlying credentials */ +	if (test_bit(RPC_CRED_NO_CRKEY_TIMEOUT, &acred->ac_flags)) +		return 0; + +	/* Fast track for the normal case */ +	if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags)) +		return 0; + +	/* lookup_cred either returns a valid referenced rpc_cred, or PTR_ERR */ +	tcred = auth->au_ops->lookup_cred(auth, acred, 0); +	if (IS_ERR(tcred)) +		return -EACCES; + +	if (!tcred->cr_ops->crkey_timeout) { +		set_bit(RPC_CRED_NO_CRKEY_TIMEOUT, &acred->ac_flags); +		ret = 0; +		goto out_put; +	} + +	/* Test for the almost error case */ +	ret = tcred->cr_ops->crkey_timeout(tcred); +	if (ret != 0) { +		set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags); +		ret = 0; +	} else { +		/* In case underlying cred key has been reset */ +		if (test_and_clear_bit(RPC_CRED_KEY_EXPIRE_SOON, +					&acred->ac_flags)) +			dprintk("RPC:        UID %d Credential key reset\n", +				tcred->cr_uid); +		/* set up fasttrack for the normal case */ +		set_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags); +	} + +out_put: +	put_rpccred(tcred); +	return ret; +} +  static const struct rpc_authops generic_auth_ops = {  	.owner = THIS_MODULE,  	.au_name = "Generic",  	.lookup_cred = generic_lookup_cred,  	.crcreate = generic_create_cred, +	.key_timeout = generic_key_timeout,  };  static struct rpc_auth generic_auth = { @@ -194,9 +262,23 @@ static struct rpc_auth generic_auth = {  	.au_count = ATOMIC_INIT(0),  }; +static bool generic_key_to_expire(struct rpc_cred *cred) +{ +	struct auth_cred *acred = &container_of(cred, struct generic_cred, +						gc_base)->acred; +	bool ret; + +	get_rpccred(cred); +	ret = test_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags); +	put_rpccred(cred); + +	return ret; +} +  static const struct rpc_credops generic_credops = {  	.cr_name = "Generic cred",  	.crdestroy = generic_destroy_cred,  	.crbind = generic_bind_cred,  	.crmatch = generic_match, +	.crkey_to_expire = generic_key_to_expire,  }; diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c index fc2f78d6a9b4..30eb502135bb 100644 --- a/net/sunrpc/auth_gss/auth_gss.c +++ b/net/sunrpc/auth_gss/auth_gss.c @@ -51,6 +51,7 @@  #include <linux/sunrpc/rpc_pipe_fs.h>  #include <linux/sunrpc/gss_api.h>  #include <asm/uaccess.h> +#include <linux/hashtable.h>  #include "../netns.h" @@ -62,6 +63,9 @@ static const struct rpc_credops gss_nullops;  #define GSS_RETRY_EXPIRED 5  static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED; +#define GSS_KEY_EXPIRE_TIMEO 240 +static unsigned int gss_key_expire_timeo = GSS_KEY_EXPIRE_TIMEO; +  #ifdef RPC_DEBUG  # define RPCDBG_FACILITY	RPCDBG_AUTH  #endif @@ -71,19 +75,33 @@ static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;   * using integrity (two 4-byte integers): */  #define GSS_VERF_SLACK		100 +static DEFINE_HASHTABLE(gss_auth_hash_table, 16); +static DEFINE_SPINLOCK(gss_auth_hash_lock); + +struct gss_pipe { +	struct rpc_pipe_dir_object pdo; +	struct rpc_pipe *pipe; +	struct rpc_clnt *clnt; +	const char *name; +	struct kref kref; +}; +  struct gss_auth {  	struct kref kref; +	struct hlist_node hash;  	struct rpc_auth rpc_auth;  	struct gss_api_mech *mech;  	enum rpc_gss_svc service;  	struct rpc_clnt *client; +	struct net *net;  	/*  	 * There are two upcall pipes; dentry[1], named "gssd", is used  	 * for the new text-based upcall; dentry[0] is named after the  	 * mechanism (for example, "krb5") and exists for  	 * backwards-compatibility with older gssd's.  	 */ -	struct rpc_pipe *pipe[2]; +	struct gss_pipe *gss_pipe[2]; +	const char *target_name;  };  /* pipe_version >= 0 if and only if someone has a pipe open. */ @@ -294,7 +312,7 @@ static void put_pipe_version(struct net *net)  static void  gss_release_msg(struct gss_upcall_msg *gss_msg)  { -	struct net *net = rpc_net_ns(gss_msg->auth->client); +	struct net *net = gss_msg->auth->net;  	if (!atomic_dec_and_test(&gss_msg->count))  		return;  	put_pipe_version(net); @@ -406,8 +424,8 @@ static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg)  }  static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg, -				struct rpc_clnt *clnt, -				const char *service_name) +				const char *service_name, +				const char *target_name)  {  	struct gss_api_mech *mech = gss_msg->auth->mech;  	char *p = gss_msg->databuf; @@ -417,8 +435,8 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,  				   mech->gm_name,  				   from_kuid(&init_user_ns, gss_msg->uid));  	p += gss_msg->msg.len; -	if (clnt->cl_principal) { -		len = sprintf(p, "target=%s ", clnt->cl_principal); +	if (target_name) { +		len = sprintf(p, "target=%s ", target_name);  		p += len;  		gss_msg->msg.len += len;  	} @@ -439,21 +457,8 @@ static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,  	BUG_ON(gss_msg->msg.len > UPCALL_BUF_LEN);  } -static void gss_encode_msg(struct gss_upcall_msg *gss_msg, -				struct rpc_clnt *clnt, -				const char *service_name) -{ -	struct net *net = rpc_net_ns(clnt); -	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); - -	if (sn->pipe_version == 0) -		gss_encode_v0_msg(gss_msg); -	else /* pipe_version == 1 */ -		gss_encode_v1_msg(gss_msg, clnt, service_name); -} -  static struct gss_upcall_msg * -gss_alloc_msg(struct gss_auth *gss_auth, struct rpc_clnt *clnt, +gss_alloc_msg(struct gss_auth *gss_auth,  		kuid_t uid, const char *service_name)  {  	struct gss_upcall_msg *gss_msg; @@ -462,31 +467,36 @@ gss_alloc_msg(struct gss_auth *gss_auth, struct rpc_clnt *clnt,  	gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);  	if (gss_msg == NULL)  		return ERR_PTR(-ENOMEM); -	vers = get_pipe_version(rpc_net_ns(clnt)); +	vers = get_pipe_version(gss_auth->net);  	if (vers < 0) {  		kfree(gss_msg);  		return ERR_PTR(vers);  	} -	gss_msg->pipe = gss_auth->pipe[vers]; +	gss_msg->pipe = gss_auth->gss_pipe[vers]->pipe;  	INIT_LIST_HEAD(&gss_msg->list);  	rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");  	init_waitqueue_head(&gss_msg->waitqueue);  	atomic_set(&gss_msg->count, 1);  	gss_msg->uid = uid;  	gss_msg->auth = gss_auth; -	gss_encode_msg(gss_msg, clnt, service_name); +	switch (vers) { +	case 0: +		gss_encode_v0_msg(gss_msg); +	default: +		gss_encode_v1_msg(gss_msg, service_name, gss_auth->target_name); +	};  	return gss_msg;  }  static struct gss_upcall_msg * -gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cred *cred) +gss_setup_upcall(struct gss_auth *gss_auth, struct rpc_cred *cred)  {  	struct gss_cred *gss_cred = container_of(cred,  			struct gss_cred, gc_base);  	struct gss_upcall_msg *gss_new, *gss_msg;  	kuid_t uid = cred->cr_uid; -	gss_new = gss_alloc_msg(gss_auth, clnt, uid, gss_cred->gc_principal); +	gss_new = gss_alloc_msg(gss_auth, uid, gss_cred->gc_principal);  	if (IS_ERR(gss_new))  		return gss_new;  	gss_msg = gss_add_msg(gss_new); @@ -527,7 +537,7 @@ gss_refresh_upcall(struct rpc_task *task)  	dprintk("RPC: %5u %s for uid %u\n",  		task->tk_pid, __func__, from_kuid(&init_user_ns, cred->cr_uid)); -	gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred); +	gss_msg = gss_setup_upcall(gss_auth, cred);  	if (PTR_ERR(gss_msg) == -EAGAIN) {  		/* XXX: warning on the first, under the assumption we  		 * shouldn't normally hit this case on a refresh. */ @@ -566,7 +576,7 @@ out:  static inline int  gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)  { -	struct net *net = rpc_net_ns(gss_auth->client); +	struct net *net = gss_auth->net;  	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id);  	struct rpc_pipe *pipe;  	struct rpc_cred *cred = &gss_cred->gc_base; @@ -583,7 +593,7 @@ retry:  	timeout = 15 * HZ;  	if (!sn->gssd_running)  		timeout = HZ >> 2; -	gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred); +	gss_msg = gss_setup_upcall(gss_auth, cred);  	if (PTR_ERR(gss_msg) == -EAGAIN) {  		err = wait_event_interruptible_timeout(pipe_version_waitqueue,  				sn->pipe_version >= 0, timeout); @@ -797,83 +807,153 @@ gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)  	}  } -static void gss_pipes_dentries_destroy(struct rpc_auth *auth) +static void gss_pipe_dentry_destroy(struct dentry *dir, +		struct rpc_pipe_dir_object *pdo)  { -	struct gss_auth *gss_auth; +	struct gss_pipe *gss_pipe = pdo->pdo_data; +	struct rpc_pipe *pipe = gss_pipe->pipe; -	gss_auth = container_of(auth, struct gss_auth, rpc_auth); -	if (gss_auth->pipe[0]->dentry) -		rpc_unlink(gss_auth->pipe[0]->dentry); -	if (gss_auth->pipe[1]->dentry) -		rpc_unlink(gss_auth->pipe[1]->dentry); +	if (pipe->dentry != NULL) { +		rpc_unlink(pipe->dentry); +		pipe->dentry = NULL; +	}  } -static int gss_pipes_dentries_create(struct rpc_auth *auth) +static int gss_pipe_dentry_create(struct dentry *dir, +		struct rpc_pipe_dir_object *pdo)  { -	int err; -	struct gss_auth *gss_auth; -	struct rpc_clnt *clnt; +	struct gss_pipe *p = pdo->pdo_data; +	struct dentry *dentry; -	gss_auth = container_of(auth, struct gss_auth, rpc_auth); -	clnt = gss_auth->client; - -	gss_auth->pipe[1]->dentry = rpc_mkpipe_dentry(clnt->cl_dentry, -						      "gssd", -						      clnt, gss_auth->pipe[1]); -	if (IS_ERR(gss_auth->pipe[1]->dentry)) -		return PTR_ERR(gss_auth->pipe[1]->dentry); -	gss_auth->pipe[0]->dentry = rpc_mkpipe_dentry(clnt->cl_dentry, -						      gss_auth->mech->gm_name, -						      clnt, gss_auth->pipe[0]); -	if (IS_ERR(gss_auth->pipe[0]->dentry)) { -		err = PTR_ERR(gss_auth->pipe[0]->dentry); -		goto err_unlink_pipe_1; -	} +	dentry = rpc_mkpipe_dentry(dir, p->name, p->clnt, p->pipe); +	if (IS_ERR(dentry)) +		return PTR_ERR(dentry); +	p->pipe->dentry = dentry;  	return 0; +} -err_unlink_pipe_1: -	rpc_unlink(gss_auth->pipe[1]->dentry); -	return err; +static const struct rpc_pipe_dir_object_ops gss_pipe_dir_object_ops = { +	.create = gss_pipe_dentry_create, +	.destroy = gss_pipe_dentry_destroy, +}; + +static struct gss_pipe *gss_pipe_alloc(struct rpc_clnt *clnt, +		const char *name, +		const struct rpc_pipe_ops *upcall_ops) +{ +	struct gss_pipe *p; +	int err = -ENOMEM; + +	p = kmalloc(sizeof(*p), GFP_KERNEL); +	if (p == NULL) +		goto err; +	p->pipe = rpc_mkpipe_data(upcall_ops, RPC_PIPE_WAIT_FOR_OPEN); +	if (IS_ERR(p->pipe)) { +		err = PTR_ERR(p->pipe); +		goto err_free_gss_pipe; +	} +	p->name = name; +	p->clnt = clnt; +	kref_init(&p->kref); +	rpc_init_pipe_dir_object(&p->pdo, +			&gss_pipe_dir_object_ops, +			p); +	return p; +err_free_gss_pipe: +	kfree(p); +err: +	return ERR_PTR(err); +} + +struct gss_alloc_pdo { +	struct rpc_clnt *clnt; +	const char *name; +	const struct rpc_pipe_ops *upcall_ops; +}; + +static int gss_pipe_match_pdo(struct rpc_pipe_dir_object *pdo, void *data) +{ +	struct gss_pipe *gss_pipe; +	struct gss_alloc_pdo *args = data; + +	if (pdo->pdo_ops != &gss_pipe_dir_object_ops) +		return 0; +	gss_pipe = container_of(pdo, struct gss_pipe, pdo); +	if (strcmp(gss_pipe->name, args->name) != 0) +		return 0; +	if (!kref_get_unless_zero(&gss_pipe->kref)) +		return 0; +	return 1;  } -static void gss_pipes_dentries_destroy_net(struct rpc_clnt *clnt, -					   struct rpc_auth *auth) +static struct rpc_pipe_dir_object *gss_pipe_alloc_pdo(void *data) +{ +	struct gss_pipe *gss_pipe; +	struct gss_alloc_pdo *args = data; + +	gss_pipe = gss_pipe_alloc(args->clnt, args->name, args->upcall_ops); +	if (!IS_ERR(gss_pipe)) +		return &gss_pipe->pdo; +	return NULL; +} + +static struct gss_pipe *gss_pipe_get(struct rpc_clnt *clnt, +		const char *name, +		const struct rpc_pipe_ops *upcall_ops)  {  	struct net *net = rpc_net_ns(clnt); -	struct super_block *sb; +	struct rpc_pipe_dir_object *pdo; +	struct gss_alloc_pdo args = { +		.clnt = clnt, +		.name = name, +		.upcall_ops = upcall_ops, +	}; -	sb = rpc_get_sb_net(net); -	if (sb) { -		if (clnt->cl_dentry) -			gss_pipes_dentries_destroy(auth); -		rpc_put_sb_net(net); -	} +	pdo = rpc_find_or_alloc_pipe_dir_object(net, +			&clnt->cl_pipedir_objects, +			gss_pipe_match_pdo, +			gss_pipe_alloc_pdo, +			&args); +	if (pdo != NULL) +		return container_of(pdo, struct gss_pipe, pdo); +	return ERR_PTR(-ENOMEM);  } -static int gss_pipes_dentries_create_net(struct rpc_clnt *clnt, -					 struct rpc_auth *auth) +static void __gss_pipe_free(struct gss_pipe *p)  { +	struct rpc_clnt *clnt = p->clnt;  	struct net *net = rpc_net_ns(clnt); -	struct super_block *sb; -	int err = 0; -	sb = rpc_get_sb_net(net); -	if (sb) { -		if (clnt->cl_dentry) -			err = gss_pipes_dentries_create(auth); -		rpc_put_sb_net(net); -	} -	return err; +	rpc_remove_pipe_dir_object(net, +			&clnt->cl_pipedir_objects, +			&p->pdo); +	rpc_destroy_pipe_data(p->pipe); +	kfree(p); +} + +static void __gss_pipe_release(struct kref *kref) +{ +	struct gss_pipe *p = container_of(kref, struct gss_pipe, kref); + +	__gss_pipe_free(p); +} + +static void gss_pipe_free(struct gss_pipe *p) +{ +	if (p != NULL) +		kref_put(&p->kref, __gss_pipe_release);  }  /*   * NOTE: we have the opportunity to use different   * parameters based on the input flavor (which must be a pseudoflavor)   */ -static struct rpc_auth * -gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) +static struct gss_auth * +gss_create_new(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)  { +	rpc_authflavor_t flavor = args->pseudoflavor;  	struct gss_auth *gss_auth; +	struct gss_pipe *gss_pipe;  	struct rpc_auth * auth;  	int err = -ENOMEM; /* XXX? */ @@ -883,12 +963,20 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)  		return ERR_PTR(err);  	if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))  		goto out_dec; +	INIT_HLIST_NODE(&gss_auth->hash); +	gss_auth->target_name = NULL; +	if (args->target_name) { +		gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL); +		if (gss_auth->target_name == NULL) +			goto err_free; +	}  	gss_auth->client = clnt; +	gss_auth->net = get_net(rpc_net_ns(clnt));  	err = -EINVAL;  	gss_auth->mech = gss_mech_get_by_pseudoflavor(flavor);  	if (!gss_auth->mech) {  		dprintk("RPC:       Pseudoflavor %d not found!\n", flavor); -		goto err_free; +		goto err_put_net;  	}  	gss_auth->service = gss_pseudoflavor_to_service(gss_auth->mech, flavor);  	if (gss_auth->service == 0) @@ -901,42 +989,41 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)  	atomic_set(&auth->au_count, 1);  	kref_init(&gss_auth->kref); +	err = rpcauth_init_credcache(auth); +	if (err) +		goto err_put_mech;  	/*  	 * Note: if we created the old pipe first, then someone who  	 * examined the directory at the right moment might conclude  	 * that we supported only the old pipe.  So we instead create  	 * the new pipe first.  	 */ -	gss_auth->pipe[1] = rpc_mkpipe_data(&gss_upcall_ops_v1, -					    RPC_PIPE_WAIT_FOR_OPEN); -	if (IS_ERR(gss_auth->pipe[1])) { -		err = PTR_ERR(gss_auth->pipe[1]); -		goto err_put_mech; +	gss_pipe = gss_pipe_get(clnt, "gssd", &gss_upcall_ops_v1); +	if (IS_ERR(gss_pipe)) { +		err = PTR_ERR(gss_pipe); +		goto err_destroy_credcache;  	} +	gss_auth->gss_pipe[1] = gss_pipe; -	gss_auth->pipe[0] = rpc_mkpipe_data(&gss_upcall_ops_v0, -					    RPC_PIPE_WAIT_FOR_OPEN); -	if (IS_ERR(gss_auth->pipe[0])) { -		err = PTR_ERR(gss_auth->pipe[0]); +	gss_pipe = gss_pipe_get(clnt, gss_auth->mech->gm_name, +			&gss_upcall_ops_v0); +	if (IS_ERR(gss_pipe)) { +		err = PTR_ERR(gss_pipe);  		goto err_destroy_pipe_1;  	} -	err = gss_pipes_dentries_create_net(clnt, auth); -	if (err) -		goto err_destroy_pipe_0; -	err = rpcauth_init_credcache(auth); -	if (err) -		goto err_unlink_pipes; +	gss_auth->gss_pipe[0] = gss_pipe; -	return auth; -err_unlink_pipes: -	gss_pipes_dentries_destroy_net(clnt, auth); -err_destroy_pipe_0: -	rpc_destroy_pipe_data(gss_auth->pipe[0]); +	return gss_auth;  err_destroy_pipe_1: -	rpc_destroy_pipe_data(gss_auth->pipe[1]); +	gss_pipe_free(gss_auth->gss_pipe[1]); +err_destroy_credcache: +	rpcauth_destroy_credcache(auth);  err_put_mech:  	gss_mech_put(gss_auth->mech); +err_put_net: +	put_net(gss_auth->net);  err_free: +	kfree(gss_auth->target_name);  	kfree(gss_auth);  out_dec:  	module_put(THIS_MODULE); @@ -946,10 +1033,11 @@ out_dec:  static void  gss_free(struct gss_auth *gss_auth)  { -	gss_pipes_dentries_destroy_net(gss_auth->client, &gss_auth->rpc_auth); -	rpc_destroy_pipe_data(gss_auth->pipe[0]); -	rpc_destroy_pipe_data(gss_auth->pipe[1]); +	gss_pipe_free(gss_auth->gss_pipe[0]); +	gss_pipe_free(gss_auth->gss_pipe[1]);  	gss_mech_put(gss_auth->mech); +	put_net(gss_auth->net); +	kfree(gss_auth->target_name);  	kfree(gss_auth);  	module_put(THIS_MODULE); @@ -966,17 +1054,101 @@ gss_free_callback(struct kref *kref)  static void  gss_destroy(struct rpc_auth *auth)  { -	struct gss_auth *gss_auth; +	struct gss_auth *gss_auth = container_of(auth, +			struct gss_auth, rpc_auth);  	dprintk("RPC:       destroying GSS authenticator %p flavor %d\n",  			auth, auth->au_flavor); +	if (hash_hashed(&gss_auth->hash)) { +		spin_lock(&gss_auth_hash_lock); +		hash_del(&gss_auth->hash); +		spin_unlock(&gss_auth_hash_lock); +	} + +	gss_pipe_free(gss_auth->gss_pipe[0]); +	gss_auth->gss_pipe[0] = NULL; +	gss_pipe_free(gss_auth->gss_pipe[1]); +	gss_auth->gss_pipe[1] = NULL;  	rpcauth_destroy_credcache(auth); -	gss_auth = container_of(auth, struct gss_auth, rpc_auth);  	kref_put(&gss_auth->kref, gss_free_callback);  } +static struct gss_auth * +gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args, +		struct rpc_clnt *clnt, +		struct gss_auth *new) +{ +	struct gss_auth *gss_auth; +	unsigned long hashval = (unsigned long)clnt; + +	spin_lock(&gss_auth_hash_lock); +	hash_for_each_possible(gss_auth_hash_table, +			gss_auth, +			hash, +			hashval) { +		if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor) +			continue; +		if (gss_auth->target_name != args->target_name) { +			if (gss_auth->target_name == NULL) +				continue; +			if (args->target_name == NULL) +				continue; +			if (strcmp(gss_auth->target_name, args->target_name)) +				continue; +		} +		if (!atomic_inc_not_zero(&gss_auth->rpc_auth.au_count)) +			continue; +		goto out; +	} +	if (new) +		hash_add(gss_auth_hash_table, &new->hash, hashval); +	gss_auth = new; +out: +	spin_unlock(&gss_auth_hash_lock); +	return gss_auth; +} + +static struct gss_auth * +gss_create_hashed(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) +{ +	struct gss_auth *gss_auth; +	struct gss_auth *new; + +	gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL); +	if (gss_auth != NULL) +		goto out; +	new = gss_create_new(args, clnt); +	if (IS_ERR(new)) +		return new; +	gss_auth = gss_auth_find_or_add_hashed(args, clnt, new); +	if (gss_auth != new) +		gss_destroy(&new->rpc_auth); +out: +	return gss_auth; +} + +static struct rpc_auth * +gss_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt) +{ +	struct gss_auth *gss_auth; +	struct rpc_xprt *xprt = rcu_access_pointer(clnt->cl_xprt); + +	while (clnt != clnt->cl_parent) { +		struct rpc_clnt *parent = clnt->cl_parent; +		/* Find the original parent for this transport */ +		if (rcu_access_pointer(parent->cl_xprt) != xprt) +			break; +		clnt = parent; +	} + +	gss_auth = gss_create_hashed(args, clnt); +	if (IS_ERR(gss_auth)) +		return ERR_CAST(gss_auth); +	return &gss_auth->rpc_auth; +} +  /*   * gss_destroying_context will cause the RPCSEC_GSS to send a NULL RPC call   * to the server with the GSS control procedure field set to @@ -1126,10 +1298,32 @@ gss_cred_init(struct rpc_auth *auth, struct rpc_cred *cred)  	return err;  } +/* + * Returns -EACCES if GSS context is NULL or will expire within the + * timeout (miliseconds) + */ +static int +gss_key_timeout(struct rpc_cred *rc) +{ +	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); +	unsigned long now = jiffies; +	unsigned long expire; + +	if (gss_cred->gc_ctx == NULL) +		return -EACCES; + +	expire = gss_cred->gc_ctx->gc_expiry - (gss_key_expire_timeo * HZ); + +	if (time_after(now, expire)) +		return -EACCES; +	return 0; +} +  static int  gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)  {  	struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); +	int ret;  	if (test_bit(RPCAUTH_CRED_NEW, &rc->cr_flags))  		goto out; @@ -1142,11 +1336,26 @@ out:  	if (acred->principal != NULL) {  		if (gss_cred->gc_principal == NULL)  			return 0; -		return strcmp(acred->principal, gss_cred->gc_principal) == 0; +		ret = strcmp(acred->principal, gss_cred->gc_principal) == 0; +		goto check_expire;  	}  	if (gss_cred->gc_principal != NULL)  		return 0; -	return uid_eq(rc->cr_uid, acred->uid); +	ret = uid_eq(rc->cr_uid, acred->uid); + +check_expire: +	if (ret == 0) +		return ret; + +	/* Notify acred users of GSS context expiration timeout */ +	if (test_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags) && +	    (gss_key_timeout(rc) != 0)) { +		/* test will now be done from generic cred */ +		test_and_clear_bit(RPC_CRED_NOTIFY_TIMEOUT, &acred->ac_flags); +		/* tell NFS layer that key will expire soon */ +		set_bit(RPC_CRED_KEY_EXPIRE_SOON, &acred->ac_flags); +	} +	return ret;  }  /* @@ -1292,6 +1501,7 @@ gss_validate(struct rpc_task *task, __be32 *p)  	struct xdr_netobj mic;  	u32		flav,len;  	u32		maj_stat; +	__be32		*ret = ERR_PTR(-EIO);  	dprintk("RPC: %5u %s\n", task->tk_pid, __func__); @@ -1307,6 +1517,7 @@ gss_validate(struct rpc_task *task, __be32 *p)  	mic.data = (u8 *)p;  	mic.len = len; +	ret = ERR_PTR(-EACCES);  	maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);  	if (maj_stat == GSS_S_CONTEXT_EXPIRED)  		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); @@ -1324,8 +1535,9 @@ gss_validate(struct rpc_task *task, __be32 *p)  	return p + XDR_QUADLEN(len);  out_bad:  	gss_put_ctx(ctx); -	dprintk("RPC: %5u %s failed.\n", task->tk_pid, __func__); -	return NULL; +	dprintk("RPC: %5u %s failed ret %ld.\n", task->tk_pid, __func__, +		PTR_ERR(ret)); +	return ret;  }  static void gss_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, @@ -1657,8 +1869,6 @@ static const struct rpc_authops authgss_ops = {  	.destroy	= gss_destroy,  	.lookup_cred	= gss_lookup_cred,  	.crcreate	= gss_create_cred, -	.pipes_create	= gss_pipes_dentries_create, -	.pipes_destroy	= gss_pipes_dentries_destroy,  	.list_pseudoflavors = gss_mech_list_pseudoflavors,  	.info2flavor	= gss_mech_info2flavor,  	.flavor2info	= gss_mech_flavor2info, @@ -1675,6 +1885,7 @@ static const struct rpc_credops gss_credops = {  	.crvalidate	= gss_validate,  	.crwrap_req	= gss_wrap_req,  	.crunwrap_resp	= gss_unwrap_resp, +	.crkey_timeout	= gss_key_timeout,  };  static const struct rpc_credops gss_nullops = { @@ -1762,5 +1973,12 @@ module_param_named(expired_cred_retry_delay,  MODULE_PARM_DESC(expired_cred_retry_delay, "Timeout (in seconds) until "  		"the RPC engine retries an expired credential"); +module_param_named(key_expire_timeo, +		   gss_key_expire_timeo, +		   uint, 0644); +MODULE_PARM_DESC(key_expire_timeo, "Time (in seconds) at the end of a " +		"credential keys lifetime where the NFS layer cleans up " +		"prior to key expiration"); +  module_init(init_rpcsec_gss)  module_exit(exit_rpcsec_gss) diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index a5c36c01707b..f0ebe07978a2 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c @@ -18,7 +18,7 @@ static struct rpc_auth null_auth;  static struct rpc_cred null_cred;  static struct rpc_auth * -nul_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) +nul_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)  {  	atomic_inc(&null_auth.au_count);  	return &null_auth; @@ -88,13 +88,13 @@ nul_validate(struct rpc_task *task, __be32 *p)  	flavor = ntohl(*p++);  	if (flavor != RPC_AUTH_NULL) {  		printk("RPC: bad verf flavor: %u\n", flavor); -		return NULL; +		return ERR_PTR(-EIO);  	}  	size = ntohl(*p++);  	if (size != 0) {  		printk("RPC: bad verf size: %u\n", size); -		return NULL; +		return ERR_PTR(-EIO);  	}  	return p; diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c index dc37021fc3e5..d5d692366294 100644 --- a/net/sunrpc/auth_unix.c +++ b/net/sunrpc/auth_unix.c @@ -33,7 +33,7 @@ static struct rpc_auth		unix_auth;  static const struct rpc_credops	unix_credops;  static struct rpc_auth * -unx_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor) +unx_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)  {  	dprintk("RPC:       creating UNIX authenticator for client %p\n",  			clnt); @@ -192,13 +192,13 @@ unx_validate(struct rpc_task *task, __be32 *p)  	    flavor != RPC_AUTH_UNIX &&  	    flavor != RPC_AUTH_SHORT) {  		printk("RPC: bad verf flavor: %u\n", flavor); -		return NULL; +		return ERR_PTR(-EIO);  	}  	size = ntohl(*p++);  	if (size > RPC_MAX_AUTH_SIZE) {  		printk("RPC: giant verf size: %u\n", size); -		return NULL; +		return ERR_PTR(-EIO);  	}  	task->tk_rqstp->rq_cred->cr_auth->au_rslack = (size >> 2) + 2;  	p += (size >> 2); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index ecbc4e3d83ad..77479606a971 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -102,12 +102,7 @@ static void rpc_unregister_client(struct rpc_clnt *clnt)  static void __rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)  { -	if (clnt->cl_dentry) { -		if (clnt->cl_auth && clnt->cl_auth->au_ops->pipes_destroy) -			clnt->cl_auth->au_ops->pipes_destroy(clnt->cl_auth); -		rpc_remove_client_dir(clnt->cl_dentry); -	} -	clnt->cl_dentry = NULL; +	rpc_remove_client_dir(clnt);  }  static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt) @@ -123,10 +118,10 @@ static void rpc_clnt_remove_pipedir(struct rpc_clnt *clnt)  }  static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb, -				    struct rpc_clnt *clnt, -				    const char *dir_name) +				    struct rpc_clnt *clnt)  {  	static uint32_t clntid; +	const char *dir_name = clnt->cl_program->pipe_dir_name;  	char name[15];  	struct dentry *dir, *dentry; @@ -153,28 +148,35 @@ static struct dentry *rpc_setup_pipedir_sb(struct super_block *sb,  }  static int -rpc_setup_pipedir(struct rpc_clnt *clnt, const char *dir_name, -		  struct super_block *pipefs_sb) +rpc_setup_pipedir(struct super_block *pipefs_sb, struct rpc_clnt *clnt)  {  	struct dentry *dentry; -	clnt->cl_dentry = NULL; -	if (dir_name == NULL) -		return 0; -	dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt, dir_name); -	if (IS_ERR(dentry)) -		return PTR_ERR(dentry); -	clnt->cl_dentry = dentry; +	if (clnt->cl_program->pipe_dir_name != NULL) { +		dentry = rpc_setup_pipedir_sb(pipefs_sb, clnt); +		if (IS_ERR(dentry)) +			return PTR_ERR(dentry); +	}  	return 0;  } -static inline int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event) +static int rpc_clnt_skip_event(struct rpc_clnt *clnt, unsigned long event)  { -	if (((event == RPC_PIPEFS_MOUNT) && clnt->cl_dentry) || -	    ((event == RPC_PIPEFS_UMOUNT) && !clnt->cl_dentry)) -		return 1; -	if ((event == RPC_PIPEFS_MOUNT) && atomic_read(&clnt->cl_count) == 0) +	if (clnt->cl_program->pipe_dir_name == NULL)  		return 1; + +	switch (event) { +	case RPC_PIPEFS_MOUNT: +		if (clnt->cl_pipedir_objects.pdh_dentry != NULL) +			return 1; +		if (atomic_read(&clnt->cl_count) == 0) +			return 1; +		break; +	case RPC_PIPEFS_UMOUNT: +		if (clnt->cl_pipedir_objects.pdh_dentry == NULL) +			return 1; +		break; +	}  	return 0;  } @@ -186,18 +188,11 @@ static int __rpc_clnt_handle_event(struct rpc_clnt *clnt, unsigned long event,  	switch (event) {  	case RPC_PIPEFS_MOUNT: -		dentry = rpc_setup_pipedir_sb(sb, clnt, -					      clnt->cl_program->pipe_dir_name); +		dentry = rpc_setup_pipedir_sb(sb, clnt);  		if (!dentry)  			return -ENOENT;  		if (IS_ERR(dentry))  			return PTR_ERR(dentry); -		clnt->cl_dentry = dentry; -		if (clnt->cl_auth->au_ops->pipes_create) { -			err = clnt->cl_auth->au_ops->pipes_create(clnt->cl_auth); -			if (err) -				__rpc_clnt_remove_pipedir(clnt); -		}  		break;  	case RPC_PIPEFS_UMOUNT:  		__rpc_clnt_remove_pipedir(clnt); @@ -230,8 +225,6 @@ static struct rpc_clnt *rpc_get_client_for_event(struct net *net, int event)  	spin_lock(&sn->rpc_client_lock);  	list_for_each_entry(clnt, &sn->all_clients, cl_clients) { -		if (clnt->cl_program->pipe_dir_name == NULL) -			continue;  		if (rpc_clnt_skip_event(clnt, event))  			continue;  		spin_unlock(&sn->rpc_client_lock); @@ -282,7 +275,10 @@ static void rpc_clnt_set_nodename(struct rpc_clnt *clnt, const char *nodename)  static int rpc_client_register(const struct rpc_create_args *args,  			       struct rpc_clnt *clnt)  { -	const struct rpc_program *program = args->program; +	struct rpc_auth_create_args auth_args = { +		.pseudoflavor = args->authflavor, +		.target_name = args->client_name, +	};  	struct rpc_auth *auth;  	struct net *net = rpc_net_ns(clnt);  	struct super_block *pipefs_sb; @@ -290,7 +286,7 @@ static int rpc_client_register(const struct rpc_create_args *args,  	pipefs_sb = rpc_get_sb_net(net);  	if (pipefs_sb) { -		err = rpc_setup_pipedir(clnt, program->pipe_dir_name, pipefs_sb); +		err = rpc_setup_pipedir(pipefs_sb, clnt);  		if (err)  			goto out;  	} @@ -299,7 +295,7 @@ static int rpc_client_register(const struct rpc_create_args *args,  	if (pipefs_sb)  		rpc_put_sb_net(net); -	auth = rpcauth_create(args->authflavor, clnt); +	auth = rpcauth_create(&auth_args, clnt);  	if (IS_ERR(auth)) {  		dprintk("RPC:       Couldn't create auth handle (flavor %u)\n",  				args->authflavor); @@ -317,7 +313,27 @@ out:  	return err;  } -static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, struct rpc_xprt *xprt) +static DEFINE_IDA(rpc_clids); + +static int rpc_alloc_clid(struct rpc_clnt *clnt) +{ +	int clid; + +	clid = ida_simple_get(&rpc_clids, 0, 0, GFP_KERNEL); +	if (clid < 0) +		return clid; +	clnt->cl_clid = clid; +	return 0; +} + +static void rpc_free_clid(struct rpc_clnt *clnt) +{ +	ida_simple_remove(&rpc_clids, clnt->cl_clid); +} + +static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, +		struct rpc_xprt *xprt, +		struct rpc_clnt *parent)  {  	const struct rpc_program *program = args->program;  	const struct rpc_version *version; @@ -343,16 +359,20 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru  	clnt = kzalloc(sizeof(*clnt), GFP_KERNEL);  	if (!clnt)  		goto out_err; -	clnt->cl_parent = clnt; +	clnt->cl_parent = parent ? : clnt; + +	err = rpc_alloc_clid(clnt); +	if (err) +		goto out_no_clid;  	rcu_assign_pointer(clnt->cl_xprt, xprt);  	clnt->cl_procinfo = version->procs;  	clnt->cl_maxproc  = version->nrprocs; -	clnt->cl_protname = program->name;  	clnt->cl_prog     = args->prognumber ? : program->number;  	clnt->cl_vers     = version->number;  	clnt->cl_stats    = program->stats;  	clnt->cl_metrics  = rpc_alloc_iostats(clnt); +	rpc_init_pipe_dir_head(&clnt->cl_pipedir_objects);  	err = -ENOMEM;  	if (clnt->cl_metrics == NULL)  		goto out_no_stats; @@ -372,12 +392,6 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru  	clnt->cl_rtt = &clnt->cl_rtt_default;  	rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval); -	clnt->cl_principal = NULL; -	if (args->client_name) { -		clnt->cl_principal = kstrdup(args->client_name, GFP_KERNEL); -		if (!clnt->cl_principal) -			goto out_no_principal; -	}  	atomic_set(&clnt->cl_count, 1); @@ -387,13 +401,15 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru  	err = rpc_client_register(args, clnt);  	if (err)  		goto out_no_path; +	if (parent) +		atomic_inc(&parent->cl_count);  	return clnt;  out_no_path: -	kfree(clnt->cl_principal); -out_no_principal:  	rpc_free_iostats(clnt->cl_metrics);  out_no_stats: +	rpc_free_clid(clnt); +out_no_clid:  	kfree(clnt);  out_err:  	rpciod_down(); @@ -479,7 +495,7 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)  	if (args->flags & RPC_CLNT_CREATE_NONPRIVPORT)  		xprt->resvport = 0; -	clnt = rpc_new_client(args, xprt); +	clnt = rpc_new_client(args, xprt, NULL);  	if (IS_ERR(clnt))  		return clnt; @@ -526,15 +542,12 @@ static struct rpc_clnt *__rpc_clone_client(struct rpc_create_args *args,  		goto out_err;  	args->servername = xprt->servername; -	new = rpc_new_client(args, xprt); +	new = rpc_new_client(args, xprt, clnt);  	if (IS_ERR(new)) {  		err = PTR_ERR(new);  		goto out_err;  	} -	atomic_inc(&clnt->cl_count); -	new->cl_parent = clnt; -  	/* Turn off autobind on clones */  	new->cl_autobind = 0;  	new->cl_softrtry = clnt->cl_softrtry; @@ -561,7 +574,6 @@ struct rpc_clnt *rpc_clone_client(struct rpc_clnt *clnt)  		.prognumber	= clnt->cl_prog,  		.version	= clnt->cl_vers,  		.authflavor	= clnt->cl_auth->au_flavor, -		.client_name	= clnt->cl_principal,  	};  	return __rpc_clone_client(&args, clnt);  } @@ -583,7 +595,6 @@ rpc_clone_client_set_auth(struct rpc_clnt *clnt, rpc_authflavor_t flavor)  		.prognumber	= clnt->cl_prog,  		.version	= clnt->cl_vers,  		.authflavor	= flavor, -		.client_name	= clnt->cl_principal,  	};  	return __rpc_clone_client(&args, clnt);  } @@ -629,7 +640,7 @@ void rpc_shutdown_client(struct rpc_clnt *clnt)  	might_sleep();  	dprintk_rcu("RPC:       shutting down %s client for %s\n", -			clnt->cl_protname, +			clnt->cl_program->name,  			rcu_dereference(clnt->cl_xprt)->servername);  	while (!list_empty(&clnt->cl_tasks)) { @@ -649,17 +660,17 @@ static void  rpc_free_client(struct rpc_clnt *clnt)  {  	dprintk_rcu("RPC:       destroying %s client for %s\n", -			clnt->cl_protname, +			clnt->cl_program->name,  			rcu_dereference(clnt->cl_xprt)->servername);  	if (clnt->cl_parent != clnt)  		rpc_release_client(clnt->cl_parent);  	rpc_clnt_remove_pipedir(clnt);  	rpc_unregister_client(clnt);  	rpc_free_iostats(clnt->cl_metrics); -	kfree(clnt->cl_principal);  	clnt->cl_metrics = NULL;  	xprt_put(rcu_dereference_raw(clnt->cl_xprt));  	rpciod_down(); +	rpc_free_clid(clnt);  	kfree(clnt);  } @@ -720,7 +731,6 @@ struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *old,  		.prognumber	= program->number,  		.version	= vers,  		.authflavor	= old->cl_auth->au_flavor, -		.client_name	= old->cl_principal,  	};  	struct rpc_clnt *clnt;  	int err; @@ -1299,7 +1309,7 @@ call_start(struct rpc_task *task)  	struct rpc_clnt	*clnt = task->tk_client;  	dprintk("RPC: %5u call_start %s%d proc %s (%s)\n", task->tk_pid, -			clnt->cl_protname, clnt->cl_vers, +			clnt->cl_program->name, clnt->cl_vers,  			rpc_proc_name(task),  			(RPC_IS_ASYNC(task) ? "async" : "sync")); @@ -1423,9 +1433,9 @@ call_refreshresult(struct rpc_task *task)  		return;  	case -ETIMEDOUT:  		rpc_delay(task, 3*HZ); -	case -EKEYEXPIRED:  	case -EAGAIN:  		status = -EACCES; +	case -EKEYEXPIRED:  		if (!task->tk_cred_retry)  			break;  		task->tk_cred_retry--; @@ -1912,7 +1922,7 @@ call_status(struct rpc_task *task)  	default:  		if (clnt->cl_chatty)  			printk("%s: RPC call returned error %d\n", -			       clnt->cl_protname, -status); +			       clnt->cl_program->name, -status);  		rpc_exit(task, status);  	}  } @@ -1943,7 +1953,7 @@ call_timeout(struct rpc_task *task)  		if (clnt->cl_chatty) {  			rcu_read_lock();  			printk(KERN_NOTICE "%s: server %s not responding, timed out\n", -				clnt->cl_protname, +				clnt->cl_program->name,  				rcu_dereference(clnt->cl_xprt)->servername);  			rcu_read_unlock();  		} @@ -1959,7 +1969,7 @@ call_timeout(struct rpc_task *task)  		if (clnt->cl_chatty) {  			rcu_read_lock();  			printk(KERN_NOTICE "%s: server %s not responding, still trying\n", -			clnt->cl_protname, +			clnt->cl_program->name,  			rcu_dereference(clnt->cl_xprt)->servername);  			rcu_read_unlock();  		} @@ -1994,7 +2004,7 @@ call_decode(struct rpc_task *task)  		if (clnt->cl_chatty) {  			rcu_read_lock();  			printk(KERN_NOTICE "%s: server %s OK\n", -				clnt->cl_protname, +				clnt->cl_program->name,  				rcu_dereference(clnt->cl_xprt)->servername);  			rcu_read_unlock();  		} @@ -2019,7 +2029,7 @@ call_decode(struct rpc_task *task)  			goto out_retry;  		}  		dprintk("RPC:       %s: too small RPC reply size (%d bytes)\n", -				clnt->cl_protname, task->tk_status); +				clnt->cl_program->name, task->tk_status);  		task->tk_action = call_timeout;  		goto out_retry;  	} @@ -2091,7 +2101,8 @@ rpc_verify_header(struct rpc_task *task)  		dprintk("RPC: %5u %s: XDR representation not a multiple of"  		       " 4 bytes: 0x%x\n", task->tk_pid, __func__,  		       task->tk_rqstp->rq_rcv_buf.len); -		goto out_eio; +		error = -EIO; +		goto out_err;  	}  	if ((len -= 3) < 0)  		goto out_overflow; @@ -2100,6 +2111,7 @@ rpc_verify_header(struct rpc_task *task)  	if ((n = ntohl(*p++)) != RPC_REPLY) {  		dprintk("RPC: %5u %s: not an RPC reply: %x\n",  			task->tk_pid, __func__, n); +		error = -EIO;  		goto out_garbage;  	} @@ -2118,7 +2130,8 @@ rpc_verify_header(struct rpc_task *task)  			dprintk("RPC: %5u %s: RPC call rejected, "  				"unknown error: %x\n",  				task->tk_pid, __func__, n); -			goto out_eio; +			error = -EIO; +			goto out_err;  		}  		if (--len < 0)  			goto out_overflow; @@ -2163,9 +2176,11 @@ rpc_verify_header(struct rpc_task *task)  				task->tk_pid, __func__, n);  		goto out_err;  	} -	if (!(p = rpcauth_checkverf(task, p))) { -		dprintk("RPC: %5u %s: auth check failed\n", -				task->tk_pid, __func__); +	p = rpcauth_checkverf(task, p); +	if (IS_ERR(p)) { +		error = PTR_ERR(p); +		dprintk("RPC: %5u %s: auth check failed with %d\n", +				task->tk_pid, __func__, error);  		goto out_garbage;		/* bad verifier, retry */  	}  	len = p - (__be32 *)iov->iov_base - 1; @@ -2218,8 +2233,6 @@ out_garbage:  out_retry:  		return ERR_PTR(-EAGAIN);  	} -out_eio: -	error = -EIO;  out_err:  	rpc_exit(task, error);  	dprintk("RPC: %5u %s: call failed with error %d\n", task->tk_pid, @@ -2291,7 +2304,7 @@ static void rpc_show_task(const struct rpc_clnt *clnt,  	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), +		clnt->cl_program->name, clnt->cl_vers, rpc_proc_name(task),  		task->tk_action, rpc_waitq);  } diff --git a/net/sunrpc/rpc_pipe.c b/net/sunrpc/rpc_pipe.c index 406859cc68aa..f94567b45bb3 100644 --- a/net/sunrpc/rpc_pipe.c +++ b/net/sunrpc/rpc_pipe.c @@ -409,7 +409,7 @@ rpc_show_info(struct seq_file *m, void *v)  	rcu_read_lock();  	seq_printf(m, "RPC server: %s\n",  			rcu_dereference(clnt->cl_xprt)->servername); -	seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_protname, +	seq_printf(m, "service: %s (%d) version %d\n", clnt->cl_program->name,  			clnt->cl_prog, clnt->cl_vers);  	seq_printf(m, "address: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_ADDR));  	seq_printf(m, "protocol: %s\n", rpc_peeraddr2str(clnt, RPC_DISPLAY_PROTO)); @@ -480,23 +480,6 @@ static const struct dentry_operations rpc_dentry_operations = {  	.d_delete = rpc_delete_dentry,  }; -/* - * Lookup the data. This is trivial - if the dentry didn't already - * exist, we know it is negative. - */ -static struct dentry * -rpc_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) -{ -	if (dentry->d_name.len > NAME_MAX) -		return ERR_PTR(-ENAMETOOLONG); -	d_add(dentry, NULL); -	return NULL; -} - -static const struct inode_operations rpc_dir_inode_operations = { -	.lookup		= rpc_lookup, -}; -  static struct inode *  rpc_get_inode(struct super_block *sb, umode_t mode)  { @@ -509,7 +492,7 @@ rpc_get_inode(struct super_block *sb, umode_t mode)  	switch (mode & S_IFMT) {  	case S_IFDIR:  		inode->i_fop = &simple_dir_operations; -		inode->i_op = &rpc_dir_inode_operations; +		inode->i_op = &simple_dir_inode_operations;  		inc_nlink(inode);  	default:  		break; @@ -901,6 +884,159 @@ rpc_unlink(struct dentry *dentry)  }  EXPORT_SYMBOL_GPL(rpc_unlink); +/** + * rpc_init_pipe_dir_head - initialise a struct rpc_pipe_dir_head + * @pdh: pointer to struct rpc_pipe_dir_head + */ +void rpc_init_pipe_dir_head(struct rpc_pipe_dir_head *pdh) +{ +	INIT_LIST_HEAD(&pdh->pdh_entries); +	pdh->pdh_dentry = NULL; +} +EXPORT_SYMBOL_GPL(rpc_init_pipe_dir_head); + +/** + * rpc_init_pipe_dir_object - initialise a struct rpc_pipe_dir_object + * @pdo: pointer to struct rpc_pipe_dir_object + * @pdo_ops: pointer to const struct rpc_pipe_dir_object_ops + * @pdo_data: pointer to caller-defined data + */ +void rpc_init_pipe_dir_object(struct rpc_pipe_dir_object *pdo, +		const struct rpc_pipe_dir_object_ops *pdo_ops, +		void *pdo_data) +{ +	INIT_LIST_HEAD(&pdo->pdo_head); +	pdo->pdo_ops = pdo_ops; +	pdo->pdo_data = pdo_data; +} +EXPORT_SYMBOL_GPL(rpc_init_pipe_dir_object); + +static int +rpc_add_pipe_dir_object_locked(struct net *net, +		struct rpc_pipe_dir_head *pdh, +		struct rpc_pipe_dir_object *pdo) +{ +	int ret = 0; + +	if (pdh->pdh_dentry) +		ret = pdo->pdo_ops->create(pdh->pdh_dentry, pdo); +	if (ret == 0) +		list_add_tail(&pdo->pdo_head, &pdh->pdh_entries); +	return ret; +} + +static void +rpc_remove_pipe_dir_object_locked(struct net *net, +		struct rpc_pipe_dir_head *pdh, +		struct rpc_pipe_dir_object *pdo) +{ +	if (pdh->pdh_dentry) +		pdo->pdo_ops->destroy(pdh->pdh_dentry, pdo); +	list_del_init(&pdo->pdo_head); +} + +/** + * rpc_add_pipe_dir_object - associate a rpc_pipe_dir_object to a directory + * @net: pointer to struct net + * @pdh: pointer to struct rpc_pipe_dir_head + * @pdo: pointer to struct rpc_pipe_dir_object + * + */ +int +rpc_add_pipe_dir_object(struct net *net, +		struct rpc_pipe_dir_head *pdh, +		struct rpc_pipe_dir_object *pdo) +{ +	int ret = 0; + +	if (list_empty(&pdo->pdo_head)) { +		struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + +		mutex_lock(&sn->pipefs_sb_lock); +		ret = rpc_add_pipe_dir_object_locked(net, pdh, pdo); +		mutex_unlock(&sn->pipefs_sb_lock); +	} +	return ret; +} +EXPORT_SYMBOL_GPL(rpc_add_pipe_dir_object); + +/** + * rpc_remove_pipe_dir_object - remove a rpc_pipe_dir_object from a directory + * @net: pointer to struct net + * @pdh: pointer to struct rpc_pipe_dir_head + * @pdo: pointer to struct rpc_pipe_dir_object + * + */ +void +rpc_remove_pipe_dir_object(struct net *net, +		struct rpc_pipe_dir_head *pdh, +		struct rpc_pipe_dir_object *pdo) +{ +	if (!list_empty(&pdo->pdo_head)) { +		struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); + +		mutex_lock(&sn->pipefs_sb_lock); +		rpc_remove_pipe_dir_object_locked(net, pdh, pdo); +		mutex_unlock(&sn->pipefs_sb_lock); +	} +} +EXPORT_SYMBOL_GPL(rpc_remove_pipe_dir_object); + +/** + * rpc_find_or_alloc_pipe_dir_object + * @net: pointer to struct net + * @pdh: pointer to struct rpc_pipe_dir_head + * @match: match struct rpc_pipe_dir_object to data + * @alloc: allocate a new struct rpc_pipe_dir_object + * @data: user defined data for match() and alloc() + * + */ +struct rpc_pipe_dir_object * +rpc_find_or_alloc_pipe_dir_object(struct net *net, +		struct rpc_pipe_dir_head *pdh, +		int (*match)(struct rpc_pipe_dir_object *, void *), +		struct rpc_pipe_dir_object *(*alloc)(void *), +		void *data) +{ +	struct sunrpc_net *sn = net_generic(net, sunrpc_net_id); +	struct rpc_pipe_dir_object *pdo; + +	mutex_lock(&sn->pipefs_sb_lock); +	list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head) { +		if (!match(pdo, data)) +			continue; +		goto out; +	} +	pdo = alloc(data); +	if (!pdo) +		goto out; +	rpc_add_pipe_dir_object_locked(net, pdh, pdo); +out: +	mutex_unlock(&sn->pipefs_sb_lock); +	return pdo; +} +EXPORT_SYMBOL_GPL(rpc_find_or_alloc_pipe_dir_object); + +static void +rpc_create_pipe_dir_objects(struct rpc_pipe_dir_head *pdh) +{ +	struct rpc_pipe_dir_object *pdo; +	struct dentry *dir = pdh->pdh_dentry; + +	list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head) +		pdo->pdo_ops->create(dir, pdo); +} + +static void +rpc_destroy_pipe_dir_objects(struct rpc_pipe_dir_head *pdh) +{ +	struct rpc_pipe_dir_object *pdo; +	struct dentry *dir = pdh->pdh_dentry; + +	list_for_each_entry(pdo, &pdh->pdh_entries, pdo_head) +		pdo->pdo_ops->destroy(dir, pdo); +} +  enum {  	RPCAUTH_info,  	RPCAUTH_EOF @@ -941,16 +1077,29 @@ struct dentry *rpc_create_client_dir(struct dentry *dentry,  				   const char *name,  				   struct rpc_clnt *rpc_client)  { -	return rpc_mkdir_populate(dentry, name, S_IRUGO | S_IXUGO, NULL, +	struct dentry *ret; + +	ret = rpc_mkdir_populate(dentry, name, S_IRUGO | S_IXUGO, NULL,  			rpc_clntdir_populate, rpc_client); +	if (!IS_ERR(ret)) { +		rpc_client->cl_pipedir_objects.pdh_dentry = ret; +		rpc_create_pipe_dir_objects(&rpc_client->cl_pipedir_objects); +	} +	return ret;  }  /**   * rpc_remove_client_dir - Remove a directory created with rpc_create_client_dir() - * @dentry: dentry for the pipe + * @rpc_client: rpc_client for the pipe   */ -int rpc_remove_client_dir(struct dentry *dentry) +int rpc_remove_client_dir(struct rpc_clnt *rpc_client)  { +	struct dentry *dentry = rpc_client->cl_pipedir_objects.pdh_dentry; + +	if (dentry == NULL) +		return 0; +	rpc_destroy_pipe_dir_objects(&rpc_client->cl_pipedir_objects); +	rpc_client->cl_pipedir_objects.pdh_dentry = NULL;  	return rpc_rmdir_depopulate(dentry, rpc_clntdir_depopulate);  } diff --git a/net/sunrpc/sched.c b/net/sunrpc/sched.c index 93a7a4e94d80..ff3cc4bf4b24 100644 --- a/net/sunrpc/sched.c +++ b/net/sunrpc/sched.c @@ -258,7 +258,7 @@ static int rpc_wait_bit_killable(void *word)  	return 0;  } -#ifdef RPC_DEBUG +#if defined(RPC_DEBUG) || defined(RPC_TRACEPOINTS)  static void rpc_task_set_debuginfo(struct rpc_task *task)  {  	static atomic_t rpc_pid; diff --git a/net/sunrpc/stats.c b/net/sunrpc/stats.c index 21b75cb08c03..54530490944e 100644 --- a/net/sunrpc/stats.c +++ b/net/sunrpc/stats.c @@ -188,7 +188,7 @@ void rpc_print_iostats(struct seq_file *seq, struct rpc_clnt *clnt)  	seq_printf(seq, "\tRPC iostats version: %s  ", RPC_IOSTATS_VERS);  	seq_printf(seq, "p/v: %u/%u (%s)\n", -			clnt->cl_prog, clnt->cl_vers, clnt->cl_protname); +			clnt->cl_prog, clnt->cl_vers, clnt->cl_program->name);  	rcu_read_lock();  	xprt = rcu_dereference(clnt->cl_xprt); diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index d6656d7768f4..ee03d35677d9 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -47,6 +47,8 @@  #include <net/udp.h>  #include <net/tcp.h> +#include <trace/events/sunrpc.h> +  #include "sunrpc.h"  static void xs_close(struct rpc_xprt *xprt); @@ -665,8 +667,10 @@ static void xs_tcp_shutdown(struct rpc_xprt *xprt)  	struct sock_xprt *transport = container_of(xprt, struct sock_xprt, xprt);  	struct socket *sock = transport->sock; -	if (sock != NULL) +	if (sock != NULL) {  		kernel_sock_shutdown(sock, SHUT_WR); +		trace_rpc_socket_shutdown(xprt, sock); +	}  }  /** @@ -811,6 +815,7 @@ static void xs_reset_transport(struct sock_xprt *transport)  	sk->sk_no_check = 0; +	trace_rpc_socket_close(&transport->xprt, sock);  	sock_release(sock);  } @@ -1492,6 +1497,7 @@ static void xs_tcp_state_change(struct sock *sk)  			sock_flag(sk, SOCK_ZAPPED),  			sk->sk_shutdown); +	trace_rpc_socket_state_change(xprt, sk->sk_socket);  	switch (sk->sk_state) {  	case TCP_ESTABLISHED:  		spin_lock(&xprt->transport_lock); @@ -1896,6 +1902,7 @@ static int xs_local_setup_socket(struct sock_xprt *transport)  			xprt, xprt->address_strings[RPC_DISPLAY_ADDR]);  	status = xs_local_finish_connecting(xprt, sock); +	trace_rpc_socket_connect(xprt, sock, status);  	switch (status) {  	case 0:  		dprintk("RPC:       xprt %p connected to %s\n", @@ -2039,6 +2046,7 @@ static void xs_udp_setup_socket(struct work_struct *work)  			xprt->address_strings[RPC_DISPLAY_PORT]);  	xs_udp_finish_connecting(xprt, sock); +	trace_rpc_socket_connect(xprt, sock, 0);  	status = 0;  out:  	xprt_clear_connecting(xprt); @@ -2064,6 +2072,8 @@ static void xs_abort_connection(struct sock_xprt *transport)  	memset(&any, 0, sizeof(any));  	any.sa_family = AF_UNSPEC;  	result = kernel_connect(transport->sock, &any, sizeof(any), 0); +	trace_rpc_socket_reset_connection(&transport->xprt, +			transport->sock, result);  	if (!result)  		xs_sock_reset_connection_flags(&transport->xprt);  	dprintk("RPC:       AF_UNSPEC connect return code %d\n", result); @@ -2194,6 +2204,7 @@ static void xs_tcp_setup_socket(struct work_struct *work)  			xprt->address_strings[RPC_DISPLAY_PORT]);  	status = xs_tcp_finish_connecting(xprt, sock); +	trace_rpc_socket_connect(xprt, sock, status);  	dprintk("RPC:       %p connect status %d connected %d sock state %d\n",  			xprt, -status, xprt_connected(xprt),  			sock->sk->sk_state); | 
