SUNRPC: Mark auth and cred operation tables as constant.
[cascardo/linux.git] / net / sunrpc / auth.c
index 9527f2b..d3f0f94 100644 (file)
@@ -18,7 +18,8 @@
 # define RPCDBG_FACILITY       RPCDBG_AUTH
 #endif
 
-static struct rpc_authops *    auth_flavors[RPC_AUTH_MAXFLAVOR] = {
+static DEFINE_SPINLOCK(rpc_authflavor_lock);
+static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
        &authnull_ops,          /* AUTH_NULL */
        &authunix_ops,          /* AUTH_UNIX */
        NULL,                   /* others can be loadable modules */
@@ -32,55 +33,67 @@ pseudoflavor_to_flavor(u32 flavor) {
 }
 
 int
-rpcauth_register(struct rpc_authops *ops)
+rpcauth_register(const struct rpc_authops *ops)
 {
        rpc_authflavor_t flavor;
+       int ret = -EPERM;
 
        if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
                return -EINVAL;
-       if (auth_flavors[flavor] != NULL)
-               return -EPERM;          /* what else? */
-       auth_flavors[flavor] = ops;
-       return 0;
+       spin_lock(&rpc_authflavor_lock);
+       if (auth_flavors[flavor] == NULL) {
+               auth_flavors[flavor] = ops;
+               ret = 0;
+       }
+       spin_unlock(&rpc_authflavor_lock);
+       return ret;
 }
 
 int
-rpcauth_unregister(struct rpc_authops *ops)
+rpcauth_unregister(const struct rpc_authops *ops)
 {
        rpc_authflavor_t flavor;
+       int ret = -EPERM;
 
        if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
                return -EINVAL;
-       if (auth_flavors[flavor] != ops)
-               return -EPERM;          /* what else? */
-       auth_flavors[flavor] = NULL;
-       return 0;
+       spin_lock(&rpc_authflavor_lock);
+       if (auth_flavors[flavor] == ops) {
+               auth_flavors[flavor] = NULL;
+               ret = 0;
+       }
+       spin_unlock(&rpc_authflavor_lock);
+       return ret;
 }
 
 struct rpc_auth *
 rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
 {
        struct rpc_auth         *auth;
-       struct rpc_authops      *ops;
+       const struct rpc_authops *ops;
        u32                     flavor = pseudoflavor_to_flavor(pseudoflavor);
 
        auth = ERR_PTR(-EINVAL);
        if (flavor >= RPC_AUTH_MAXFLAVOR)
                goto out;
 
-       /* FIXME - auth_flavors[] really needs an rw lock,
-        * and module refcounting. */
 #ifdef CONFIG_KMOD
        if ((ops = auth_flavors[flavor]) == NULL)
                request_module("rpc-auth-%u", flavor);
 #endif
-       if ((ops = auth_flavors[flavor]) == NULL)
+       spin_lock(&rpc_authflavor_lock);
+       ops = auth_flavors[flavor];
+       if (ops == NULL || !try_module_get(ops->owner)) {
+               spin_unlock(&rpc_authflavor_lock);
                goto out;
+       }
+       spin_unlock(&rpc_authflavor_lock);
        auth = ops->create(clnt, pseudoflavor);
+       module_put(ops->owner);
        if (IS_ERR(auth))
                return auth;
        if (clnt->cl_auth)
-               rpcauth_destroy(clnt->cl_auth);
+               rpcauth_release(clnt->cl_auth);
        clnt->cl_auth = auth;
 
 out:
@@ -88,7 +101,7 @@ out:
 }
 
 void
-rpcauth_destroy(struct rpc_auth *auth)
+rpcauth_release(struct rpc_auth *auth)
 {
        if (!atomic_dec_and_test(&auth->au_count))
                return;
@@ -137,9 +150,8 @@ void rpcauth_destroy_credlist(struct hlist_head *head)
  * that are not referenced.
  */
 void
-rpcauth_free_credcache(struct rpc_auth *auth)
+rpcauth_clear_credcache(struct rpc_cred_cache *cache)
 {
-       struct rpc_cred_cache *cache = auth->au_credcache;
        HLIST_HEAD(free);
        struct hlist_node *pos, *next;
        struct rpc_cred *cred;
@@ -157,6 +169,21 @@ rpcauth_free_credcache(struct rpc_auth *auth)
        rpcauth_destroy_credlist(&free);
 }
 
+/*
+ * Destroy the RPC credential cache
+ */
+void
+rpcauth_destroy_credcache(struct rpc_auth *auth)
+{
+       struct rpc_cred_cache *cache = auth->au_credcache;
+
+       if (cache) {
+               auth->au_credcache = NULL;
+               rpcauth_clear_credcache(cache);
+               kfree(cache);
+       }
+}
+
 static void
 rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free)
 {