};
static LIST_HEAD(cred_unused);
+static unsigned long number_cred_unused;
static u32
pseudoflavor_to_flavor(u32 flavor) {
static DEFINE_SPINLOCK(rpc_credcache_lock);
+static void
+rpcauth_unhash_cred_locked(struct rpc_cred *cred)
+{
+ hlist_del_rcu(&cred->cr_hash);
+ smp_mb__before_clear_bit();
+ clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
+}
+
+static void
+rpcauth_unhash_cred(struct rpc_cred *cred)
+{
+ spinlock_t *cache_lock;
+
+ cache_lock = &cred->cr_auth->au_credcache->lock;
+ spin_lock(cache_lock);
+ if (atomic_read(&cred->cr_count) == 0)
+ rpcauth_unhash_cred_locked(cred);
+ spin_unlock(cache_lock);
+}
+
/*
* Initialize RPC credential cache
*/
int
-rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire)
+rpcauth_init_credcache(struct rpc_auth *auth)
{
struct rpc_cred_cache *new;
int i;
return -ENOMEM;
for (i = 0; i < RPC_CREDCACHE_NR; i++)
INIT_HLIST_HEAD(&new->hashtable[i]);
- new->expire = expire;
- new->nextgc = jiffies + (expire >> 1);
+ spin_lock_init(&new->lock);
auth->au_credcache = new;
return 0;
}
int i;
spin_lock(&rpc_credcache_lock);
+ spin_lock(&cache->lock);
for (i = 0; i < RPC_CREDCACHE_NR; i++) {
head = &cache->hashtable[i];
while (!hlist_empty(head)) {
cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
get_rpccred(cred);
- list_move_tail(&cred->cr_lru, &free);
- smp_wmb();
- hlist_del_init(&cred->cr_hash);
+ if (!list_empty(&cred->cr_lru)) {
+ list_del(&cred->cr_lru);
+ number_cred_unused--;
+ }
+ list_add_tail(&cred->cr_lru, &free);
+ rpcauth_unhash_cred_locked(cred);
}
}
+ spin_unlock(&cache->lock);
spin_unlock(&rpc_credcache_lock);
rpcauth_destroy_credlist(&free);
}
/*
* Remove stale credentials. Avoid sleeping inside the loop.
*/
-static void
-rpcauth_prune_expired(struct list_head *free)
+static int
+rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
{
+ spinlock_t *cache_lock;
struct rpc_cred *cred;
while (!list_empty(&cred_unused)) {
cred = list_entry(cred_unused.next, struct rpc_cred, cr_lru);
- if (time_after(jiffies, cred->cr_expire +
- cred->cr_auth->au_credcache->expire))
- break;
list_del_init(&cred->cr_lru);
+ number_cred_unused--;
if (atomic_read(&cred->cr_count) != 0)
continue;
- get_rpccred(cred);
- list_add_tail(&cred->cr_lru, free);
- smp_wmb();
- hlist_del_init(&cred->cr_hash);
+ cache_lock = &cred->cr_auth->au_credcache->lock;
+ spin_lock(cache_lock);
+ if (atomic_read(&cred->cr_count) == 0) {
+ get_rpccred(cred);
+ list_add_tail(&cred->cr_lru, free);
+ rpcauth_unhash_cred_locked(cred);
+ nr_to_scan--;
+ }
+ spin_unlock(cache_lock);
+ if (nr_to_scan == 0)
+ break;
}
+ return nr_to_scan;
}
/*
- * Run garbage collector.
+ * Run memory cache shrinker.
*/
-static void
-rpcauth_gc_credcache(struct rpc_cred_cache *cache, struct list_head *free)
+static int
+rpcauth_cache_shrinker(int nr_to_scan, gfp_t gfp_mask)
{
- if (time_before(jiffies, cache->nextgc))
- return;
- cache->nextgc = jiffies + cache->expire;
- rpcauth_prune_expired(free);
+ LIST_HEAD(free);
+ int res;
+
+ if (list_empty(&cred_unused))
+ return 0;
+ spin_lock(&rpc_credcache_lock);
+ nr_to_scan = rpcauth_prune_expired(&free, nr_to_scan);
+ res = (number_cred_unused / 100) * sysctl_vfs_cache_pressure;
+ spin_unlock(&rpc_credcache_lock);
+ rpcauth_destroy_credlist(&free);
+ return res;
}
/*
LIST_HEAD(free);
struct rpc_cred_cache *cache = auth->au_credcache;
struct hlist_node *pos;
- struct rpc_cred *new = NULL,
- *cred = NULL,
- *entry;
+ struct rpc_cred *cred = NULL,
+ *entry, *new;
int nr = 0;
if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS))
nr = acred->uid & RPC_CREDCACHE_MASK;
-retry:
- spin_lock(&rpc_credcache_lock);
- hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) {
+
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(entry, pos, &cache->hashtable[nr], cr_hash) {
if (!entry->cr_ops->crmatch(acred, entry, flags))
continue;
+ spin_lock(&cache->lock);
+ if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) {
+ spin_unlock(&cache->lock);
+ continue;
+ }
cred = get_rpccred(entry);
- hlist_del(&entry->cr_hash);
+ spin_unlock(&cache->lock);
break;
}
- if (new) {
- if (cred)
- list_add_tail(&new->cr_lru, &free);
- else
- cred = new;
- }
- if (cred) {
- hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]);
- }
- rpcauth_gc_credcache(cache, &free);
- spin_unlock(&rpc_credcache_lock);
+ rcu_read_unlock();
- rpcauth_destroy_credlist(&free);
+ if (cred != NULL)
+ goto found;
- if (!cred) {
- new = auth->au_ops->crcreate(auth, acred, flags);
- if (!IS_ERR(new))
- goto retry;
+ new = auth->au_ops->crcreate(auth, acred, flags);
+ if (IS_ERR(new)) {
+ cred = new;
+ goto out;
+ }
+
+ spin_lock(&cache->lock);
+ hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) {
+ if (!entry->cr_ops->crmatch(acred, entry, flags))
+ continue;
+ cred = get_rpccred(entry);
+ break;
+ }
+ if (cred == NULL) {
cred = new;
- } else if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)
+ set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
+ hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]);
+ } else
+ list_add_tail(&new->cr_lru, &free);
+ spin_unlock(&cache->lock);
+found:
+ if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags)
&& cred->cr_ops->cr_init != NULL
&& !(flags & RPCAUTH_LOOKUP_NEW)) {
int res = cred->cr_ops->cr_init(auth, cred);
cred = ERR_PTR(res);
}
}
-
- return (struct rpc_cred *) cred;
+ rpcauth_destroy_credlist(&free);
+out:
+ return cred;
}
struct rpc_cred *
struct rpc_cred *
rpcauth_bindcred(struct rpc_task *task)
{
- struct rpc_auth *auth = task->tk_auth;
+ struct rpc_auth *auth = task->tk_client->cl_auth;
struct auth_cred acred = {
.uid = current->fsuid,
.gid = current->fsgid,
int flags = 0;
dprintk("RPC: %5u looking up %s cred\n",
- task->tk_pid, task->tk_auth->au_ops->au_name);
+ task->tk_pid, task->tk_client->cl_auth->au_ops->au_name);
get_group_info(acred.group_info);
if (task->tk_flags & RPC_TASK_ROOTCREDS)
flags |= RPCAUTH_LOOKUP_ROOTCREDS;
void
rpcauth_holdcred(struct rpc_task *task)
{
- dprintk("RPC: %5u holding %s cred %p\n",
- task->tk_pid, task->tk_auth->au_ops->au_name,
- task->tk_msg.rpc_cred);
- if (task->tk_msg.rpc_cred)
- get_rpccred(task->tk_msg.rpc_cred);
+ struct rpc_cred *cred = task->tk_msg.rpc_cred;
+ if (cred != NULL) {
+ get_rpccred(cred);
+ dprintk("RPC: %5u holding %s cred %p\n", task->tk_pid,
+ cred->cr_auth->au_ops->au_name, cred);
+ }
}
void
put_rpccred(struct rpc_cred *cred)
{
/* Fast path for unhashed credentials */
- if (!hlist_unhashed(&cred->cr_hash))
+ if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0)
goto need_lock;
if (!atomic_dec_and_test(&cred->cr_count))
return;
goto out_destroy;
-
need_lock:
if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock))
return;
- if (!list_empty(&cred->cr_lru))
+ if (!list_empty(&cred->cr_lru)) {
+ number_cred_unused--;
list_del_init(&cred->cr_lru);
+ }
if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0)
- hlist_del(&cred->cr_hash);
- else if (!hlist_unhashed(&cred->cr_hash)) {
+ rpcauth_unhash_cred(cred);
+ else if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) {
cred->cr_expire = jiffies;
list_add_tail(&cred->cr_lru, &cred_unused);
+ number_cred_unused++;
spin_unlock(&rpc_credcache_lock);
return;
}
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %5u releasing %s cred %p\n",
- task->tk_pid, task->tk_auth->au_ops->au_name, cred);
+ task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
put_rpccred(cred);
task->tk_msg.rpc_cred = NULL;
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %5u marshaling %s cred %p\n",
- task->tk_pid, task->tk_auth->au_ops->au_name, cred);
+ task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
return cred->cr_ops->crmarshal(task, p);
}
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %5u validating %s cred %p\n",
- task->tk_pid, task->tk_auth->au_ops->au_name, cred);
+ task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
return cred->cr_ops->crvalidate(task, p);
}
if (cred->cr_ops->crwrap_req)
return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
/* By default, we encode the arguments normally. */
- return encode(rqstp, data, obj);
+ return rpc_call_xdrproc(encode, rqstp, data, obj);
}
int
return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
data, obj);
/* By default, we decode the arguments normally. */
- return decode(rqstp, data, obj);
+ return rpc_call_xdrproc(decode, rqstp, data, obj);
}
int
int err;
dprintk("RPC: %5u refreshing %s cred %p\n",
- task->tk_pid, task->tk_auth->au_ops->au_name, cred);
+ task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
err = cred->cr_ops->crrefresh(task);
if (err < 0)
struct rpc_cred *cred = task->tk_msg.rpc_cred;
dprintk("RPC: %5u invalidating %s cred %p\n",
- task->tk_pid, task->tk_auth->au_ops->au_name, cred);
+ task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
if (cred)
clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
}
return cred == NULL ||
test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0;
}
+
+static struct shrinker rpc_cred_shrinker = {
+ .shrink = rpcauth_cache_shrinker,
+ .seeks = DEFAULT_SEEKS,
+};
+
+void __init rpcauth_init_module(void)
+{
+ rpc_init_authunix();
+ register_shrinker(&rpc_cred_shrinker);
+}
+
+void __exit rpcauth_remove_module(void)
+{
+ unregister_shrinker(&rpc_cred_shrinker);
+}