aboutsummaryrefslogtreecommitdiffstats
path: root/security/keys/gc.c
diff options
context:
space:
mode:
authorDavid Howells <dhowells@redhat.com>2009-09-14 12:26:13 -0400
committerJames Morris <jmorris@namei.org>2009-09-14 19:11:02 -0400
commitc08ef808ef24df32e25fbd949fe5310172f3c408 (patch)
tree12bae6fd48e1cdcc1b792c221376c727d9472cc6 /security/keys/gc.c
parent5c84342a3e147a23752276650340801c237d0e56 (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/gc.c')
-rw-r--r--security/keys/gc.c78
1 files changed, 51 insertions, 27 deletions
diff --git a/security/keys/gc.c b/security/keys/gc.c
index 1e616aef55fd..485fc6233c38 100644
--- a/security/keys/gc.c
+++ b/security/keys/gc.c
@@ -26,8 +26,10 @@ static void key_garbage_collector(struct work_struct *);
26static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0); 26static DEFINE_TIMER(key_gc_timer, key_gc_timer_func, 0, 0);
27static DECLARE_WORK(key_gc_work, key_garbage_collector); 27static DECLARE_WORK(key_gc_work, key_garbage_collector);
28static key_serial_t key_gc_cursor; /* the last key the gc considered */ 28static key_serial_t key_gc_cursor; /* the last key the gc considered */
29static bool key_gc_again;
29static unsigned long key_gc_executing; 30static unsigned long key_gc_executing;
30static time_t key_gc_next_run = LONG_MAX; 31static time_t key_gc_next_run = LONG_MAX;
32static time_t key_gc_new_timer;
31 33
32/* 34/*
33 * Schedule a garbage collection run 35 * Schedule a garbage collection run
@@ -40,9 +42,7 @@ void key_schedule_gc(time_t gc_at)
40 42
41 kenter("%ld", gc_at - now); 43 kenter("%ld", gc_at - now);
42 44
43 gc_at += key_gc_delay; 45 if (gc_at <= now) {
44
45 if (now >= gc_at) {
46 schedule_work(&key_gc_work); 46 schedule_work(&key_gc_work);
47 } else if (gc_at < key_gc_next_run) { 47 } else if (gc_at < key_gc_next_run) {
48 expires = jiffies + (gc_at - now) * HZ; 48 expires = jiffies + (gc_at - now) * HZ;
@@ -112,16 +112,18 @@ static void key_garbage_collector(struct work_struct *work)
112 struct rb_node *rb; 112 struct rb_node *rb;
113 key_serial_t cursor; 113 key_serial_t cursor;
114 struct key *key, *xkey; 114 struct key *key, *xkey;
115 time_t new_timer = LONG_MAX, limit; 115 time_t new_timer = LONG_MAX, limit, now;
116 116
117 kenter(""); 117 now = current_kernel_time().tv_sec;
118 kenter("[%x,%ld]", key_gc_cursor, key_gc_new_timer - now);
118 119
119 if (test_and_set_bit(0, &key_gc_executing)) { 120 if (test_and_set_bit(0, &key_gc_executing)) {
120 key_schedule_gc(current_kernel_time().tv_sec); 121 key_schedule_gc(current_kernel_time().tv_sec + 1);
122 kleave(" [busy; deferring]");
121 return; 123 return;
122 } 124 }
123 125
124 limit = current_kernel_time().tv_sec; 126 limit = now;
125 if (limit > key_gc_delay) 127 if (limit > key_gc_delay)
126 limit -= key_gc_delay; 128 limit -= key_gc_delay;
127 else 129 else
@@ -129,12 +131,19 @@ static void key_garbage_collector(struct work_struct *work)
129 131
130 spin_lock(&key_serial_lock); 132 spin_lock(&key_serial_lock);
131 133
132 if (RB_EMPTY_ROOT(&key_serial_tree)) 134 if (unlikely(RB_EMPTY_ROOT(&key_serial_tree))) {
133 goto reached_the_end; 135 spin_unlock(&key_serial_lock);
136 clear_bit(0, &key_gc_executing);
137 return;
138 }
134 139
135 cursor = key_gc_cursor; 140 cursor = key_gc_cursor;
136 if (cursor < 0) 141 if (cursor < 0)
137 cursor = 0; 142 cursor = 0;
143 if (cursor > 0)
144 new_timer = key_gc_new_timer;
145 else
146 key_gc_again = false;
138 147
139 /* find the first key above the cursor */ 148 /* find the first key above the cursor */
140 key = NULL; 149 key = NULL;
@@ -160,35 +169,50 @@ static void key_garbage_collector(struct work_struct *work)
160 169
161 /* trawl through the keys looking for keyrings */ 170 /* trawl through the keys looking for keyrings */
162 for (;;) { 171 for (;;) {
163 if (key->expiry > 0 && key->expiry < new_timer) 172 if (key->expiry > now && key->expiry < new_timer) {
173 kdebug("will expire %x in %ld",
174 key_serial(key), key->expiry - now);
164 new_timer = key->expiry; 175 new_timer = key->expiry;
176 }
165 177
166 if (key->type == &key_type_keyring && 178 if (key->type == &key_type_keyring &&
167 key_gc_keyring(key, limit)) { 179 key_gc_keyring(key, limit))
168 /* the gc ate our lock */ 180 /* the gc had to release our lock so that the keyring
169 schedule_work(&key_gc_work); 181 * could be modified, so we have to get it again */
170 goto no_unlock; 182 goto gc_released_our_lock;
171 }
172 183
173 rb = rb_next(&key->serial_node); 184 rb = rb_next(&key->serial_node);
174 if (!rb) { 185 if (!rb)
175 key_gc_cursor = 0; 186 goto reached_the_end;
176 break;
177 }
178 key = rb_entry(rb, struct key, serial_node); 187 key = rb_entry(rb, struct key, serial_node);
179 } 188 }
180 189
181out: 190gc_released_our_lock:
182 spin_unlock(&key_serial_lock); 191 kdebug("gc_released_our_lock");
183no_unlock: 192 key_gc_new_timer = new_timer;
193 key_gc_again = true;
184 clear_bit(0, &key_gc_executing); 194 clear_bit(0, &key_gc_executing);
185 if (new_timer < LONG_MAX) 195 schedule_work(&key_gc_work);
186 key_schedule_gc(new_timer); 196 kleave(" [continue]");
187
188 kleave("");
189 return; 197 return;
190 198
199 /* when we reach the end of the run, we set the timer for the next one */
191reached_the_end: 200reached_the_end:
201 kdebug("reached_the_end");
202 spin_unlock(&key_serial_lock);
203 key_gc_new_timer = new_timer;
192 key_gc_cursor = 0; 204 key_gc_cursor = 0;
193 goto out; 205 clear_bit(0, &key_gc_executing);
206
207 if (key_gc_again) {
208 /* there may have been a key that expired whilst we were
209 * scanning, so if we discarded any links we should do another
210 * scan */
211 new_timer = now + 1;
212 key_schedule_gc(new_timer);
213 } else if (new_timer < LONG_MAX) {
214 new_timer += key_gc_delay;
215 key_schedule_gc(new_timer);
216 }
217 kleave(" [end]");
194} 218}