diff options
author | David Howells <dhowells@redhat.com> | 2009-09-14 17:26:13 +0100 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2009-09-15 09:11:02 +1000 |
commit | c08ef808ef24df32e25fbd949fe5310172f3c408 (patch) | |
tree | 12bae6fd48e1cdcc1b792c221376c727d9472cc6 /security/keys/keyring.c | |
parent | 5c84342a3e147a23752276650340801c237d0e56 (diff) |
KEYS: Fix garbage collector
Fix a number of problems with the new key garbage collector:
(1) A rogue semicolon in keyring_gc() was causing the initial count of dead
keys to be miscalculated.
(2) A missing return in keyring_gc() meant that under certain circumstances,
the keyring semaphore would be unlocked twice.
(3) The key serial tree iterator (key_garbage_collector()) part of the garbage
collector has been modified to:
(a) Complete each scan of the keyrings before setting the new timer.
(b) Only set the new timer for keys that have yet to expire. This means
that the new timer is now calculated correctly, and the gc doesn't
get into a loop continually scanning for keys that have expired, and
preventing other things from happening, like RCU cleaning up the old
keyring contents.
(c) Perform an extra scan if any keys were garbage collected in this one
as a key might become garbage during a scan, and (b) could mean we
don't set the timer again.
(4) Made key_schedule_gc() take the time at which to do a collection run,
rather than the time at which the key expires. This means the collection
of dead keys (key type unregistered) can happen immediately.
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/keys/keyring.c')
-rw-r--r-- | security/keys/keyring.c | 24 |
1 files changed, 19 insertions, 5 deletions
diff --git a/security/keys/keyring.c b/security/keys/keyring.c index ac977f661a79..8ec02746ca99 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1019,18 +1019,18 @@ void keyring_gc(struct key *keyring, time_t limit) struct key *key; int loop, keep, max; - kenter("%x", key_serial(keyring)); + kenter("{%x,%s}", key_serial(keyring), keyring->description); down_write(&keyring->sem); klist = keyring->payload.subscriptions; if (!klist) - goto just_return; + goto no_klist; /* work out how many subscriptions we're keeping */ keep = 0; for (loop = klist->nkeys - 1; loop >= 0; loop--) - if (!key_is_dead(klist->keys[loop], limit)); + if (!key_is_dead(klist->keys[loop], limit)) keep++; if (keep == klist->nkeys) @@ -1041,7 +1041,7 @@ void keyring_gc(struct key *keyring, time_t limit) new = kmalloc(sizeof(struct keyring_list) + max * sizeof(struct key *), GFP_KERNEL); if (!new) - goto just_return; + goto nomem; new->maxkeys = max; new->nkeys = 0; new->delkey = 0; @@ -1081,7 +1081,21 @@ void keyring_gc(struct key *keyring, time_t limit) discard_new: new->nkeys = keep; keyring_clear_rcu_disposal(&new->rcu); + up_write(&keyring->sem); + kleave(" [discard]"); + return; + just_return: up_write(&keyring->sem); - kleave(" [no]"); + kleave(" [no dead]"); + return; + +no_klist: + up_write(&keyring->sem); + kleave(" [no_klist]"); + return; + +nomem: + up_write(&keyring->sem); + kleave(" [oom]"); } |