KEYS: Move the unreferenced key reaper to the keys garbage collector file
Move the unreferenced key reaper function to the keys garbage collector file as that's a more appropriate place with the dead key link reaper. Signed-off-by: David Howells <dhowells@redhat.com> Signed-off-by: James Morris <jmorris@namei.org>
This commit is contained in:
parent
012146d072
commit
8bc16deabc
|
@ -10,6 +10,8 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/security.h>
|
||||||
#include <keys/keyring-type.h>
|
#include <keys/keyring-type.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
|
@ -19,12 +21,18 @@
|
||||||
unsigned key_gc_delay = 5 * 60;
|
unsigned key_gc_delay = 5 * 60;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reaper
|
* Reaper for unused keys.
|
||||||
|
*/
|
||||||
|
static void key_gc_unused_keys(struct work_struct *work);
|
||||||
|
DECLARE_WORK(key_gc_unused_work, key_gc_unused_keys);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reaper for links from keyrings to dead keys.
|
||||||
*/
|
*/
|
||||||
static void key_gc_timer_func(unsigned long);
|
static void key_gc_timer_func(unsigned long);
|
||||||
static void key_garbage_collector(struct work_struct *);
|
static void key_gc_dead_links(struct work_struct *);
|
||||||
static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
|
static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
|
||||||
static DECLARE_WORK(key_gc_work, key_garbage_collector);
|
static DECLARE_WORK(key_gc_work, key_gc_dead_links);
|
||||||
static key_serial_t key_gc_cursor; /* the last key the gc considered */
|
static key_serial_t key_gc_cursor; /* the last key the gc considered */
|
||||||
static bool key_gc_again;
|
static bool key_gc_again;
|
||||||
static unsigned long key_gc_executing;
|
static unsigned long key_gc_executing;
|
||||||
|
@ -108,10 +116,12 @@ do_gc:
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Garbage collector for keys. This involves scanning the keyrings for dead,
|
* Garbage collector for links to dead keys.
|
||||||
* expired and revoked keys that have overstayed their welcome
|
*
|
||||||
|
* This involves scanning the keyrings for dead, expired and revoked keys that
|
||||||
|
* have overstayed their welcome
|
||||||
*/
|
*/
|
||||||
static void key_garbage_collector(struct work_struct *work)
|
static void key_gc_dead_links(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct rb_node *rb;
|
struct rb_node *rb;
|
||||||
key_serial_t cursor;
|
key_serial_t cursor;
|
||||||
|
@ -220,3 +230,68 @@ reached_the_end:
|
||||||
}
|
}
|
||||||
kleave(" [end]");
|
kleave(" [end]");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Garbage collector for unused keys.
|
||||||
|
*
|
||||||
|
* This is done in process context so that we don't have to disable interrupts
|
||||||
|
* all over the place. key_put() schedules this rather than trying to do the
|
||||||
|
* cleanup itself, which means key_put() doesn't have to sleep.
|
||||||
|
*/
|
||||||
|
static void key_gc_unused_keys(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct rb_node *_n;
|
||||||
|
struct key *key;
|
||||||
|
|
||||||
|
go_again:
|
||||||
|
/* look for a dead key in the tree */
|
||||||
|
spin_lock(&key_serial_lock);
|
||||||
|
|
||||||
|
for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
|
||||||
|
key = rb_entry(_n, struct key, serial_node);
|
||||||
|
|
||||||
|
if (atomic_read(&key->usage) == 0)
|
||||||
|
goto found_dead_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock(&key_serial_lock);
|
||||||
|
return;
|
||||||
|
|
||||||
|
found_dead_key:
|
||||||
|
/* we found a dead key - once we've removed it from the tree, we can
|
||||||
|
* drop the lock */
|
||||||
|
rb_erase(&key->serial_node, &key_serial_tree);
|
||||||
|
spin_unlock(&key_serial_lock);
|
||||||
|
|
||||||
|
key_check(key);
|
||||||
|
|
||||||
|
security_key_free(key);
|
||||||
|
|
||||||
|
/* deal with the user's key tracking and quota */
|
||||||
|
if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
|
||||||
|
spin_lock(&key->user->lock);
|
||||||
|
key->user->qnkeys--;
|
||||||
|
key->user->qnbytes -= key->quotalen;
|
||||||
|
spin_unlock(&key->user->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic_dec(&key->user->nkeys);
|
||||||
|
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
|
||||||
|
atomic_dec(&key->user->nikeys);
|
||||||
|
|
||||||
|
key_user_put(key->user);
|
||||||
|
|
||||||
|
/* now throw away the key memory */
|
||||||
|
if (key->type->destroy)
|
||||||
|
key->type->destroy(key);
|
||||||
|
|
||||||
|
kfree(key->description);
|
||||||
|
|
||||||
|
#ifdef KEY_DEBUGGING
|
||||||
|
key->magic = KEY_DEBUG_MAGIC_X;
|
||||||
|
#endif
|
||||||
|
kmem_cache_free(key_jar, key);
|
||||||
|
|
||||||
|
/* there may, of course, be more than one key to destroy */
|
||||||
|
goto go_again;
|
||||||
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ extern unsigned key_quota_maxbytes;
|
||||||
#define KEYQUOTA_LINK_BYTES 4 /* a link in a keyring is worth 4 bytes */
|
#define KEYQUOTA_LINK_BYTES 4 /* a link in a keyring is worth 4 bytes */
|
||||||
|
|
||||||
|
|
||||||
|
extern struct kmem_cache *key_jar;
|
||||||
extern struct rb_root key_serial_tree;
|
extern struct rb_root key_serial_tree;
|
||||||
extern spinlock_t key_serial_lock;
|
extern spinlock_t key_serial_lock;
|
||||||
extern struct mutex key_construction_mutex;
|
extern struct mutex key_construction_mutex;
|
||||||
|
@ -146,6 +147,7 @@ extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags,
|
||||||
|
|
||||||
extern long join_session_keyring(const char *name);
|
extern long join_session_keyring(const char *name);
|
||||||
|
|
||||||
|
extern struct work_struct key_gc_unused_work;
|
||||||
extern unsigned key_gc_delay;
|
extern unsigned key_gc_delay;
|
||||||
extern void keyring_gc(struct key *keyring, time_t limit);
|
extern void keyring_gc(struct key *keyring, time_t limit);
|
||||||
extern void key_schedule_gc(time_t expiry_at);
|
extern void key_schedule_gc(time_t expiry_at);
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
#include <linux/user_namespace.h>
|
#include <linux/user_namespace.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
static struct kmem_cache *key_jar;
|
struct kmem_cache *key_jar;
|
||||||
struct rb_root key_serial_tree; /* tree of keys indexed by serial */
|
struct rb_root key_serial_tree; /* tree of keys indexed by serial */
|
||||||
DEFINE_SPINLOCK(key_serial_lock);
|
DEFINE_SPINLOCK(key_serial_lock);
|
||||||
|
|
||||||
|
@ -36,9 +36,6 @@ unsigned int key_quota_maxbytes = 20000; /* general key space quota */
|
||||||
static LIST_HEAD(key_types_list);
|
static LIST_HEAD(key_types_list);
|
||||||
static DECLARE_RWSEM(key_types_sem);
|
static DECLARE_RWSEM(key_types_sem);
|
||||||
|
|
||||||
static void key_cleanup(struct work_struct *work);
|
|
||||||
static DECLARE_WORK(key_cleanup_task, key_cleanup);
|
|
||||||
|
|
||||||
/* We serialise key instantiation and link */
|
/* We serialise key instantiation and link */
|
||||||
DEFINE_MUTEX(key_construction_mutex);
|
DEFINE_MUTEX(key_construction_mutex);
|
||||||
|
|
||||||
|
@ -591,71 +588,6 @@ int key_reject_and_link(struct key *key,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(key_reject_and_link);
|
EXPORT_SYMBOL(key_reject_and_link);
|
||||||
|
|
||||||
/*
|
|
||||||
* Garbage collect keys in process context so that we don't have to disable
|
|
||||||
* interrupts all over the place.
|
|
||||||
*
|
|
||||||
* key_put() schedules this rather than trying to do the cleanup itself, which
|
|
||||||
* means key_put() doesn't have to sleep.
|
|
||||||
*/
|
|
||||||
static void key_cleanup(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct rb_node *_n;
|
|
||||||
struct key *key;
|
|
||||||
|
|
||||||
go_again:
|
|
||||||
/* look for a dead key in the tree */
|
|
||||||
spin_lock(&key_serial_lock);
|
|
||||||
|
|
||||||
for (_n = rb_first(&key_serial_tree); _n; _n = rb_next(_n)) {
|
|
||||||
key = rb_entry(_n, struct key, serial_node);
|
|
||||||
|
|
||||||
if (atomic_read(&key->usage) == 0)
|
|
||||||
goto found_dead_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&key_serial_lock);
|
|
||||||
return;
|
|
||||||
|
|
||||||
found_dead_key:
|
|
||||||
/* we found a dead key - once we've removed it from the tree, we can
|
|
||||||
* drop the lock */
|
|
||||||
rb_erase(&key->serial_node, &key_serial_tree);
|
|
||||||
spin_unlock(&key_serial_lock);
|
|
||||||
|
|
||||||
key_check(key);
|
|
||||||
|
|
||||||
security_key_free(key);
|
|
||||||
|
|
||||||
/* deal with the user's key tracking and quota */
|
|
||||||
if (test_bit(KEY_FLAG_IN_QUOTA, &key->flags)) {
|
|
||||||
spin_lock(&key->user->lock);
|
|
||||||
key->user->qnkeys--;
|
|
||||||
key->user->qnbytes -= key->quotalen;
|
|
||||||
spin_unlock(&key->user->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
atomic_dec(&key->user->nkeys);
|
|
||||||
if (test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
|
|
||||||
atomic_dec(&key->user->nikeys);
|
|
||||||
|
|
||||||
key_user_put(key->user);
|
|
||||||
|
|
||||||
/* now throw away the key memory */
|
|
||||||
if (key->type->destroy)
|
|
||||||
key->type->destroy(key);
|
|
||||||
|
|
||||||
kfree(key->description);
|
|
||||||
|
|
||||||
#ifdef KEY_DEBUGGING
|
|
||||||
key->magic = KEY_DEBUG_MAGIC_X;
|
|
||||||
#endif
|
|
||||||
kmem_cache_free(key_jar, key);
|
|
||||||
|
|
||||||
/* there may, of course, be more than one key to destroy */
|
|
||||||
goto go_again;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* key_put - Discard a reference to a key.
|
* key_put - Discard a reference to a key.
|
||||||
* @key: The key to discard a reference from.
|
* @key: The key to discard a reference from.
|
||||||
|
@ -670,7 +602,7 @@ void key_put(struct key *key)
|
||||||
key_check(key);
|
key_check(key);
|
||||||
|
|
||||||
if (atomic_dec_and_test(&key->usage))
|
if (atomic_dec_and_test(&key->usage))
|
||||||
schedule_work(&key_cleanup_task);
|
schedule_work(&key_gc_unused_work);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(key_put);
|
EXPORT_SYMBOL(key_put);
|
||||||
|
|
Reference in New Issue