
The current design of per-fsid client credential caches, each storing a single
security mechanism/service credential per uid will not support security
negotiations required by the NFSv4 SECINFO operation.

Create a common credential cache shared by all struct rpc_clnt that use the
same socket. The common credential cache will live in struct nfs_client's
rpc_clnt, and will be referred to by cloned struct nfs_server rpc_clnt.
The credential cache will still be hashed by uid, and can 
hold multiple credentials per uid-pseudoflavor pair.

Move struct rpc_cred_cache from struct rpc_auth to struct rpc_clnt.
Move the cache expiry interval and nextgc timer from struct rpc_cred_cache
to struct rpc_auth, and add a flavor to struct rpc_cred.

Add a flavor check when searching the common cred cache.

Signed-off-by: Andy Adamson <andros@citi.umich.edu>
---

---


diff -puN include/linux/sunrpc/auth.h~rpc-common-credcache-init include/linux/sunrpc/auth.h
--- linux-2.6.18-rc5/include/linux/sunrpc/auth.h~rpc-common-credcache-init	2006-10-13 16:29:47.000000000 -0400
+++ linux-2.6.18-rc5-andros/include/linux/sunrpc/auth.h	2006-10-13 16:29:47.000000000 -0400
@@ -36,6 +36,7 @@ struct auth_cred {
 struct rpc_cred {
 	struct hlist_node	cr_hash;	/* hash chain */
 	struct rpc_credops *	cr_ops;
+	rpc_authflavor_t        cr_flavor;
 	unsigned long		cr_expire;	/* when to gc */
 	atomic_t		cr_count;	/* ref count */
 	unsigned short		cr_flags;	/* various flags */
@@ -59,8 +60,6 @@ struct rpc_cred {
 #define RPC_CREDCACHE_MASK	(RPC_CREDCACHE_NR - 1)
 struct rpc_cred_cache {
 	struct hlist_head	hashtable[RPC_CREDCACHE_NR];
-	unsigned long		nextgc;		/* next garbage collection */
-	unsigned long		expire;		/* cache expiry interval */
 };
 
 struct rpc_auth {
@@ -79,6 +78,8 @@ struct rpc_auth {
 						 * case) */
 	atomic_t		au_count;	/* Reference counter */
 
+	unsigned long		au_nextgc;	/* next garbage collection */
+	unsigned long		au_expire;	/* cache expiry interval */
 	struct rpc_cred_cache *	au_credcache;
 	/* per-flavor data */
 };
@@ -141,7 +142,7 @@ int			rpcauth_unwrap_resp(struct rpc_tas
 int			rpcauth_refreshcred(struct rpc_task *);
 void			rpcauth_invalcred(struct rpc_task *);
 int			rpcauth_uptodatecred(struct rpc_task *);
-int			rpcauth_init_credcache(struct rpc_auth *, unsigned long);
+int			rpcauth_init_credcache(struct rpc_clnt *);
 void			rpcauth_free_credcache(struct rpc_auth *);
 
 static inline
diff -puN include/linux/sunrpc/clnt.h~rpc-common-credcache-init include/linux/sunrpc/clnt.h
--- linux-2.6.18-rc5/include/linux/sunrpc/clnt.h~rpc-common-credcache-init	2006-10-13 16:29:47.000000000 -0400
+++ linux-2.6.18-rc5-andros/include/linux/sunrpc/clnt.h	2006-10-13 16:29:47.000000000 -0400
@@ -34,6 +34,7 @@ struct rpc_clnt {
 
 	char *			cl_server;	/* server machine name */
 	char *			cl_protname;	/* protocol name */
+	struct rpc_cred_cache * cl_credcache;   /* all-flavor cred cache */
 	struct rpc_auth *	cl_auth;	/* authenticator */
 	struct rpc_stat *	cl_stats;	/* per-program statistics */
 	struct rpc_iostats *	cl_metrics;	/* per-client statistics */
diff -puN net/sunrpc/auth.c~rpc-common-credcache-init net/sunrpc/auth.c
--- linux-2.6.18-rc5/net/sunrpc/auth.c~rpc-common-credcache-init	2006-10-13 16:29:47.000000000 -0400
+++ linux-2.6.18-rc5-andros/net/sunrpc/auth.c	2006-10-13 16:30:36.000000000 -0400
@@ -69,7 +69,7 @@ rpcauth_create(rpc_authflavor_t pseudofl
 	u32			flavor = pseudoflavor_to_flavor(pseudoflavor);
 
 	auth = ERR_PTR(-EINVAL);
-	if (flavor >= RPC_AUTH_MAXFLAVOR)
+	if (!clnt->cl_credcache || flavor >= RPC_AUTH_MAXFLAVOR)
 		goto out;
 
 	/* FIXME - auth_flavors[] really needs an rw lock,
@@ -85,6 +85,7 @@ rpcauth_create(rpc_authflavor_t pseudofl
 		return auth;
 	if (clnt->cl_auth)
 		rpcauth_destroy(clnt->cl_auth);
+	auth->au_credcache = clnt->cl_credcache;
 	clnt->cl_auth = auth;
 
 out:
@@ -105,20 +106,20 @@ static DEFINE_SPINLOCK(rpc_credcache_loc
  * Initialize RPC credential cache
  */
 int
-rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire)
+rpcauth_init_credcache(struct rpc_clnt *clnt)
 {
 	struct rpc_cred_cache *new;
-	int i;
+	int i, err = -ENOMEM;
 
 	new = kmalloc(sizeof(*new), GFP_KERNEL);
 	if (!new)
-		return -ENOMEM;
+		goto done;
+	err = 0;
 	for (i = 0; i < RPC_CREDCACHE_NR; i++)
 		INIT_HLIST_HEAD(&new->hashtable[i]);
-	new->expire = expire;
-	new->nextgc = jiffies + (expire >> 1);
-	auth->au_credcache = new;
-	return 0;
+done:
+	clnt->cl_credcache = new;
+	return err;
 }
 
 /*
@@ -166,7 +167,7 @@ rpcauth_prune_expired(struct rpc_auth *a
 {
 	if (atomic_read(&cred->cr_count) != 1)
 	       return;
-	if (time_after(jiffies, cred->cr_expire + auth->au_credcache->expire))
+	if (time_after(jiffies, cred->cr_expire + auth->au_expire))
 		cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
 	if (!(cred->cr_flags & RPCAUTH_CRED_UPTODATE)) {
 		__hlist_del(&cred->cr_hash);
@@ -189,10 +190,11 @@ rpcauth_gc_credcache(struct rpc_auth *au
 	for (i = 0; i < RPC_CREDCACHE_NR; i++) {
 		hlist_for_each_safe(pos, next, &cache->hashtable[i]) {
 			cred = hlist_entry(pos, struct rpc_cred, cr_hash);
-			rpcauth_prune_expired(auth, cred, free);
+			if (cred->cr_flavor == auth->au_flavor)
+				rpcauth_prune_expired(auth, cred, free);
 		}
 	}
-	cache->nextgc = jiffies + cache->expire;
+	auth->au_nextgc = jiffies + auth->au_expire;
 }
 
 /*
@@ -214,11 +216,13 @@ rpcauth_lookup_credcache(struct rpc_auth
 retry:
 	dprintk("RPC:      rpcauth_lookup_credcache at retry label\n");
 	spin_lock(&rpc_credcache_lock);
-	if (time_before(cache->nextgc, jiffies))
+	if (time_before(auth->au_nextgc, jiffies))
 		rpcauth_gc_credcache(auth, &free);
 	hlist_for_each_safe(pos, next, &cache->hashtable[nr]) {
 		struct rpc_cred *entry;
 	       	entry = hlist_entry(pos, struct rpc_cred, cr_hash);
+		if (entry->cr_flavor != auth->au_flavor)
+			continue;
 		if (entry->cr_ops->crmatch(acred, entry, flags, auth)) {
 			hlist_del(&entry->cr_hash);
 			cred = entry;
diff -puN net/sunrpc/auth_gss/auth_gss.c~rpc-common-credcache-init net/sunrpc/auth_gss/auth_gss.c
--- linux-2.6.18-rc5/net/sunrpc/auth_gss/auth_gss.c~rpc-common-credcache-init	2006-10-13 16:29:47.000000000 -0400
+++ linux-2.6.18-rc5-andros/net/sunrpc/auth_gss/auth_gss.c	2006-10-13 16:29:47.000000000 -0400
@@ -866,12 +866,9 @@ gss_create(struct rpc_clnt *clnt, rpc_au
 	auth->au_rslack = GSS_VERF_SLACK >> 2;
 	auth->au_ops = &authgss_ops;
 	auth->au_flavor = flavor;
+	auth->au_expire = GSS_CRED_EXPIRE;
 	atomic_set(&auth->au_count, 1);
 
-	err = rpcauth_init_credcache(auth, GSS_CRED_EXPIRE);
-	if (err)
-		goto err_put_mech;
-
 	snprintf(gss_auth->key_name, sizeof(gss_auth->key_name),
 			"mechanism=\"%s\" pseudoflavor=\"%s\" service=\"%s%u\" host=\"%s\"",
 			gss_auth->mech->gm_name,
@@ -947,6 +944,7 @@ gss_alloc_cred(struct gss_auth *gss_auth
 		atomic_set(&cred->gc_count, 1);
 		cred->gc_base.cr_ops = &gss_credops;
 		cred->gc_service = gss_auth->service;
+		cred->gc_base.cr_flavor = gss_auth->rpc_auth.au_flavor;
 		get_current_keyrings(&cred->gc_skey, &cred->gc_pkey,
 				     &cred->gc_tkey);
 		snprintf(cred->gc_keyname, sizeof(cred->gc_keyname),
diff -puN net/sunrpc/auth_null.c~rpc-common-credcache-init net/sunrpc/auth_null.c
--- linux-2.6.18-rc5/net/sunrpc/auth_null.c~rpc-common-credcache-init	2006-10-13 16:29:47.000000000 -0400
+++ linux-2.6.18-rc5-andros/net/sunrpc/auth_null.c	2006-10-13 16:29:47.000000000 -0400
@@ -137,6 +137,7 @@ struct rpc_cred null_cred = {
 	.cr_ops		= &null_credops,
 	.cr_count	= ATOMIC_INIT(1),
 	.cr_flags	= RPCAUTH_CRED_UPTODATE,
+	.cr_flavor	= RPC_AUTH_NULL,
 #ifdef RPC_DEBUG
 	.cr_magic	= RPCAUTH_CRED_MAGIC,
 #endif
diff -puN net/sunrpc/auth_unix.c~rpc-common-credcache-init net/sunrpc/auth_unix.c
--- linux-2.6.18-rc5/net/sunrpc/auth_unix.c~rpc-common-credcache-init	2006-10-13 16:29:47.000000000 -0400
+++ linux-2.6.18-rc5-andros/net/sunrpc/auth_unix.c	2006-10-13 16:29:47.000000000 -0400
@@ -33,7 +33,6 @@ struct unx_cred {
 #endif
 
 static struct rpc_auth		unix_auth;
-static struct rpc_cred_cache	unix_cred_cache;
 static struct rpc_credops	unix_credops;
 
 static struct rpc_auth *
@@ -41,7 +40,7 @@ unx_create(struct rpc_clnt *clnt, rpc_au
 {
 	dprintk("RPC: creating UNIX authenticator for client %p\n", clnt);
 	if (atomic_inc_return(&unix_auth.au_count) == 0)
-		unix_cred_cache.nextgc = jiffies + (unix_cred_cache.expire >> 1);
+		unix_auth.au_nextgc = jiffies + (unix_auth.au_expire >> 1);
 	return &unix_auth;
 }
 
@@ -91,6 +90,7 @@ unx_create_cred(struct rpc_auth *auth, s
 		if (i < NFS_NGROUPS)
 		  cred->uc_gids[i] = NOGROUP;
 	}
+	cred->uc_base.cr_flavor = RPC_AUTH_UNIX;
 	cred->uc_base.cr_ops = &unix_credops;
 
 	return (struct rpc_cred *) cred;
@@ -216,18 +216,13 @@ struct rpc_authops	authunix_ops = {
 };
 
 static
-struct rpc_cred_cache	unix_cred_cache = {
-	.expire		= UNX_CRED_EXPIRE,
-};
-
-static
 struct rpc_auth		unix_auth = {
 	.au_cslack	= UNX_WRITESLACK,
 	.au_rslack	= 2,			/* assume AUTH_NULL verf */
 	.au_ops		= &authunix_ops,
 	.au_flavor	= RPC_AUTH_UNIX,
 	.au_count	= ATOMIC_INIT(0),
-	.au_credcache	= &unix_cred_cache,
+	.au_expire	= UNX_CRED_EXPIRE,
 };
 
 static
diff -puN net/sunrpc/clnt.c~rpc-common-credcache-init net/sunrpc/clnt.c
--- linux-2.6.18-rc5/net/sunrpc/clnt.c~rpc-common-credcache-init	2006-10-13 16:29:47.000000000 -0400
+++ linux-2.6.18-rc5-andros/net/sunrpc/clnt.c	2006-10-13 16:29:47.000000000 -0400
@@ -148,6 +148,10 @@ static struct rpc_clnt * rpc_new_client(
 	clnt->cl_rtt = &clnt->cl_rtt_default;
 	rpc_init_rtt(&clnt->cl_rtt_default, xprt->timeout.to_initval);
 
+	err = rpcauth_init_credcache(clnt);
+	if (err < 0)
+		goto out_no_path;
+
 	err = rpc_setup_pipedir(clnt, program->pipe_dir_name);
 	if (err < 0)
 		goto out_no_path;
_
