]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
sunrpc/auth: allow lockless (rcu) lookup of credential cache.
authorNeilBrown <neilb@suse.de>
Mon, 14 Jul 2014 01:28:20 +0000 (11:28 +1000)
committerTrond Myklebust <trond.myklebust@primarydata.com>
Sun, 3 Aug 2014 21:14:12 +0000 (17:14 -0400)
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>
include/linux/sunrpc/auth.h
net/sunrpc/auth.c
net/sunrpc/auth_generic.c
net/sunrpc/auth_null.c

index c683b9a06913d87ea1aa4d835801c7cf2276b11f..8e030075fe7906bbf46db5296e0abaf12c7c6131 100644 (file)
@@ -103,6 +103,7 @@ struct rpc_auth_create_args {
 
 /* Flags for rpcauth_lookupcred() */
 #define RPCAUTH_LOOKUP_NEW             0x01    /* Accept an uninitialised cred */
+#define RPCAUTH_LOOKUP_RCU             0x02    /* lock-less lookup */
 
 /*
  * Client authentication ops
@@ -154,6 +155,7 @@ void                        rpc_destroy_generic_auth(void);
 void                   rpc_destroy_authunix(void);
 
 struct rpc_cred *      rpc_lookup_cred(void);
+struct rpc_cred *      rpc_lookup_cred_nonblock(void);
 struct rpc_cred *      rpc_lookup_machine_cred(const char *service_name);
 int                    rpcauth_register(const struct rpc_authops *);
 int                    rpcauth_unregister(const struct rpc_authops *);
index 360decdddc783405b9527f32dcf240f9878809fe..24fcbd23ae6c7e96af1f393f7e1c9a4650dabe1c 100644 (file)
@@ -557,6 +557,12 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
        hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) {
                if (!entry->cr_ops->crmatch(acred, entry, flags))
                        continue;
+               if (flags & RPCAUTH_LOOKUP_RCU) {
+                       if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) &&
+                           !test_bit(RPCAUTH_CRED_NEW, &entry->cr_flags))
+                               cred = entry;
+                       break;
+               }
                spin_lock(&cache->lock);
                if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) {
                        spin_unlock(&cache->lock);
@@ -571,6 +577,9 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
        if (cred != NULL)
                goto found;
 
+       if (flags & RPCAUTH_LOOKUP_RCU)
+               return ERR_PTR(-ECHILD);
+
        new = auth->au_ops->crcreate(auth, acred, flags);
        if (IS_ERR(new)) {
                cred = new;
@@ -621,10 +630,14 @@ rpcauth_lookupcred(struct rpc_auth *auth, int flags)
        memset(&acred, 0, sizeof(acred));
        acred.uid = cred->fsuid;
        acred.gid = cred->fsgid;
-       acred.group_info = get_group_info(((struct cred *)cred)->group_info);
+       if (flags & RPCAUTH_LOOKUP_RCU)
+               acred.group_info = rcu_dereference(cred->group_info);
+       else
+               acred.group_info = get_group_info(((struct cred *)cred)->group_info);
 
        ret = auth->au_ops->lookup_cred(auth, &acred, flags);
-       put_group_info(acred.group_info);
+       if (!(flags & RPCAUTH_LOOKUP_RCU))
+               put_group_info(acred.group_info);
        return ret;
 }
 EXPORT_SYMBOL_GPL(rpcauth_lookupcred);
index ed04869b2d4f4f097ea85e8fe899c2ea262a0aca..6f6b829c9e8ee2bab63ba5f30d03d3c39dc5ba52 100644 (file)
@@ -38,6 +38,12 @@ struct rpc_cred *rpc_lookup_cred(void)
 }
 EXPORT_SYMBOL_GPL(rpc_lookup_cred);
 
+struct rpc_cred *rpc_lookup_cred_nonblock(void)
+{
+       return rpcauth_lookupcred(&generic_auth, RPCAUTH_LOOKUP_RCU);
+}
+EXPORT_SYMBOL_GPL(rpc_lookup_cred_nonblock);
+
 /*
  * Public call interface for looking up machine creds.
  */
index f0ebe07978a236e66744bc2dfe332a92bfb85d05..712c123e04e9ec43464581115b7bfb4cf42e514b 100644 (file)
@@ -35,6 +35,8 @@ nul_destroy(struct rpc_auth *auth)
 static struct rpc_cred *
 nul_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
 {
+       if (flags & RPCAUTH_LOOKUP_RCU)
+               return &null_cred;
        return get_rpccred(&null_cred);
 }