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 --- --- 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; _