diff options
author | NeilBrown <neilb@suse.de> | 2014-07-13 21:28:20 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2014-08-03 17:14:12 -0400 |
commit | bd95608053b7f7813351b0defc0e3e7ef8cf2803 (patch) | |
tree | fde08b1ecc2c4aedd30a68244ca502ce5f81f0fe /net/sunrpc | |
parent | d51ac1a8e9b86b2d17d349bb256869cab6522787 (diff) |
sunrpc/auth: allow lockless (rcu) lookup of credential cache.
The new flag RPCAUTH_LOOKUP_RCU to credential lookup avoids locking,
does not take a reference on the returned credential, and returns
-ECHILD if a simple lookup was not possible.
The returned value can only be used within an rcu_read_lock protected
region.
The main user of this is the new rpc_lookup_cred_nonblock() which
returns a pointer to the current credential which is only rcu-safe (no
ref-count held), and might return -ECHILD if allocation was required.
Signed-off-by: NeilBrown <neilb@suse.de>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'net/sunrpc')
-rw-r--r-- | net/sunrpc/auth.c | 17 | ||||
-rw-r--r-- | net/sunrpc/auth_generic.c | 6 | ||||
-rw-r--r-- | net/sunrpc/auth_null.c | 2 |
3 files changed, 23 insertions, 2 deletions
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c index 360decdddc78..24fcbd23ae6c 100644 --- a/net/sunrpc/auth.c +++ b/net/sunrpc/auth.c | |||
@@ -557,6 +557,12 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, | |||
557 | hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) { | 557 | hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) { |
558 | if (!entry->cr_ops->crmatch(acred, entry, flags)) | 558 | if (!entry->cr_ops->crmatch(acred, entry, flags)) |
559 | continue; | 559 | continue; |
560 | if (flags & RPCAUTH_LOOKUP_RCU) { | ||
561 | if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) && | ||
562 | !test_bit(RPCAUTH_CRED_NEW, &entry->cr_flags)) | ||
563 | cred = entry; | ||
564 | break; | ||
565 | } | ||
560 | spin_lock(&cache->lock); | 566 | spin_lock(&cache->lock); |
561 | if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) { | 567 | if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) { |
562 | spin_unlock(&cache->lock); | 568 | spin_unlock(&cache->lock); |
@@ -571,6 +577,9 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, | |||
571 | if (cred != NULL) | 577 | if (cred != NULL) |
572 | goto found; | 578 | goto found; |
573 | 579 | ||
580 | if (flags & RPCAUTH_LOOKUP_RCU) | ||
581 | return ERR_PTR(-ECHILD); | ||
582 | |||
574 | new = auth->au_ops->crcreate(auth, acred, flags); | 583 | new = auth->au_ops->crcreate(auth, acred, flags); |
575 | if (IS_ERR(new)) { | 584 | if (IS_ERR(new)) { |
576 | cred = new; | 585 | cred = new; |
@@ -621,10 +630,14 @@ rpcauth_lookupcred(struct rpc_auth *auth, int flags) | |||
621 | memset(&acred, 0, sizeof(acred)); | 630 | memset(&acred, 0, sizeof(acred)); |
622 | acred.uid = cred->fsuid; | 631 | acred.uid = cred->fsuid; |
623 | acred.gid = cred->fsgid; | 632 | acred.gid = cred->fsgid; |
624 | acred.group_info = get_group_info(((struct cred *)cred)->group_info); | 633 | if (flags & RPCAUTH_LOOKUP_RCU) |
634 | acred.group_info = rcu_dereference(cred->group_info); | ||
635 | else | ||
636 | acred.group_info = get_group_info(((struct cred *)cred)->group_info); | ||
625 | 637 | ||
626 | ret = auth->au_ops->lookup_cred(auth, &acred, flags); | 638 | ret = auth->au_ops->lookup_cred(auth, &acred, flags); |
627 | put_group_info(acred.group_info); | 639 | if (!(flags & RPCAUTH_LOOKUP_RCU)) |
640 | put_group_info(acred.group_info); | ||
628 | return ret; | 641 | return ret; |
629 | } | 642 | } |
630 | EXPORT_SYMBOL_GPL(rpcauth_lookupcred); | 643 | EXPORT_SYMBOL_GPL(rpcauth_lookupcred); |
diff --git a/net/sunrpc/auth_generic.c b/net/sunrpc/auth_generic.c index ed04869b2d4f..6f6b829c9e8e 100644 --- a/net/sunrpc/auth_generic.c +++ b/net/sunrpc/auth_generic.c | |||
@@ -38,6 +38,12 @@ struct rpc_cred *rpc_lookup_cred(void) | |||
38 | } | 38 | } |
39 | EXPORT_SYMBOL_GPL(rpc_lookup_cred); | 39 | EXPORT_SYMBOL_GPL(rpc_lookup_cred); |
40 | 40 | ||
41 | struct rpc_cred *rpc_lookup_cred_nonblock(void) | ||
42 | { | ||
43 | return rpcauth_lookupcred(&generic_auth, RPCAUTH_LOOKUP_RCU); | ||
44 | } | ||
45 | EXPORT_SYMBOL_GPL(rpc_lookup_cred_nonblock); | ||
46 | |||
41 | /* | 47 | /* |
42 | * Public call interface for looking up machine creds. | 48 | * Public call interface for looking up machine creds. |
43 | */ | 49 | */ |
diff --git a/net/sunrpc/auth_null.c b/net/sunrpc/auth_null.c index f0ebe07978a2..712c123e04e9 100644 --- a/net/sunrpc/auth_null.c +++ b/net/sunrpc/auth_null.c | |||
@@ -35,6 +35,8 @@ nul_destroy(struct rpc_auth *auth) | |||
35 | static struct rpc_cred * | 35 | static struct rpc_cred * |
36 | nul_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) | 36 | nul_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags) |
37 | { | 37 | { |
38 | if (flags & RPCAUTH_LOOKUP_RCU) | ||
39 | return &null_cred; | ||
38 | return get_rpccred(&null_cred); | 40 | return get_rpccred(&null_cred); |
39 | } | 41 | } |
40 | 42 | ||