aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/key.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-04-08 11:56:52 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-04-08 16:44:45 -0400
commit3b96766f0e643f52ae19e134664df6730c737e87 (patch)
treeb1707d94a14c9777f09b1aab33970e7741190d4c /net/mac80211/key.c
parent7d1559f1737d5ca27b267b0392015f42b3bbe2fa (diff)
mac80211: fix key vs. sta locking problems
Up to now, key manipulation is supposed to run under RTNL to avoid concurrent manipulations and also allow the set_key() hardware callback to sleep. This is not feasible because STA structs are rcu-protected and thus a lot of operations there cannot take the RTNL. Also, key references are rcu-protected so we cannot do things atomically. This patch changes key locking completely: * key operations are now atomic * hardware crypto offload is enabled and disabled from a workqueue, due to that key freeing is also delayed * debugfs code is also run from a workqueue * keys reference STAs (and vice versa!) so during STA unlink the STAs key reference is removed but not the keys STA reference, to avoid races key todo work is run before STA destruction. * fewer STA operations now need the RTNL which was required due to key operations This fixes the locking problems lockdep pointed out and also makes things more light-weight because the rtnl isn't required as much. Note that the key todo lock/key mutex are global locks, this is not required, of course, they could be per-hardware instead. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r--net/mac80211/key.c408
1 files changed, 264 insertions, 144 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 5df9e0cc009f..711e36e54ff8 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -2,7 +2,7 @@
2 * Copyright 2002-2005, Instant802 Networks, Inc. 2 * Copyright 2002-2005, Instant802 Networks, Inc.
3 * Copyright 2005-2006, Devicescape Software, Inc. 3 * Copyright 2005-2006, Devicescape Software, Inc.
4 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 4 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
5 * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 5 * Copyright 2007-2008 Johannes Berg <johannes@sipsolutions.net>
6 * 6 *
7 * This program is free software; you can redistribute it and/or modify 7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as 8 * it under the terms of the GNU General Public License version 2 as
@@ -33,17 +33,78 @@
33 * There is currently no way of knowing this except by looking into 33 * There is currently no way of knowing this except by looking into
34 * debugfs. 34 * debugfs.
35 * 35 *
36 * All operations here are called under RTNL so no extra locking is 36 * All key operations are protected internally so you can call them at
37 * required. 37 * any time.
38 * 38 *
39 * NOTE: This code requires that sta info *destruction* is done under 39 * Within mac80211, key references are, just as STA structure references,
40 * RTNL, otherwise it can try to access already freed STA structs 40 * protected by RCU. Note, however, that some things are unprotected,
41 * when a STA key is being freed. 41 * namely the key->sta dereferences within the hardware acceleration
42 * functions. This means that sta_info_destroy() must flush the key todo
43 * list.
44 *
45 * All the direct key list manipulation functions must not sleep because
46 * they can operate on STA info structs that are protected by RCU.
42 */ 47 */
43 48
44static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; 49static const u8 bcast_addr[ETH_ALEN] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
45static const u8 zero_addr[ETH_ALEN]; 50static const u8 zero_addr[ETH_ALEN];
46 51
52/* key mutex: used to synchronise todo runners */
53static DEFINE_MUTEX(key_mutex);
54static DEFINE_SPINLOCK(todo_lock);
55static LIST_HEAD(todo_list);
56
57static void key_todo(struct work_struct *work)
58{
59 ieee80211_key_todo();
60}
61
62static DECLARE_WORK(todo_work, key_todo);
63
64/**
65 * add_todo - add todo item for a key
66 *
67 * @key: key to add to do item for
68 * @flag: todo flag(s)
69 */
70static void add_todo(struct ieee80211_key *key, u32 flag)
71{
72 if (!key)
73 return;
74
75 spin_lock(&todo_lock);
76 key->flags |= flag;
77 /* only add if not already added */
78 if (list_empty(&key->todo))
79 list_add(&key->todo, &todo_list);
80 schedule_work(&todo_work);
81 spin_unlock(&todo_lock);
82}
83
84/**
85 * ieee80211_key_lock - lock the mac80211 key operation lock
86 *
87 * This locks the (global) mac80211 key operation lock, all
88 * key operations must be done under this lock.
89 */
90static void ieee80211_key_lock(void)
91{
92 mutex_lock(&key_mutex);
93}
94
95/**
96 * ieee80211_key_unlock - unlock the mac80211 key operation lock
97 */
98static void ieee80211_key_unlock(void)
99{
100 mutex_unlock(&key_mutex);
101}
102
103static void assert_key_lock(void)
104{
105 WARN_ON(!mutex_is_locked(&key_mutex));
106}
107
47static const u8 *get_mac_for_key(struct ieee80211_key *key) 108static const u8 *get_mac_for_key(struct ieee80211_key *key)
48{ 109{
49 const u8 *addr = bcast_addr; 110 const u8 *addr = bcast_addr;
@@ -70,26 +131,23 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
70 int ret; 131 int ret;
71 DECLARE_MAC_BUF(mac); 132 DECLARE_MAC_BUF(mac);
72 133
134 assert_key_lock();
135 might_sleep();
136
73 if (!key->local->ops->set_key) 137 if (!key->local->ops->set_key)
74 return; 138 return;
75 139
76 /*
77 * This makes sure that all pending flushes have
78 * actually completed prior to uploading new key
79 * material to the hardware. That is necessary to
80 * avoid races between flushing STAs and adding
81 * new keys for them.
82 */
83 __ieee80211_run_pending_flush(key->local);
84
85 addr = get_mac_for_key(key); 140 addr = get_mac_for_key(key);
86 141
87 ret = key->local->ops->set_key(local_to_hw(key->local), SET_KEY, 142 ret = key->local->ops->set_key(local_to_hw(key->local), SET_KEY,
88 key->sdata->dev->dev_addr, addr, 143 key->sdata->dev->dev_addr, addr,
89 &key->conf); 144 &key->conf);
90 145
91 if (!ret) 146 if (!ret) {
147 spin_lock(&todo_lock);
92 key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; 148 key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
149 spin_unlock(&todo_lock);
150 }
93 151
94 if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP) 152 if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
95 printk(KERN_ERR "mac80211-%s: failed to set key " 153 printk(KERN_ERR "mac80211-%s: failed to set key "
@@ -98,26 +156,24 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
98 key->conf.keyidx, print_mac(mac, addr), ret); 156 key->conf.keyidx, print_mac(mac, addr), ret);
99} 157}
100 158
101static void ieee80211_key_mark_hw_accel_off(struct ieee80211_key *key)
102{
103 if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) {
104 key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
105 key->flags |= KEY_FLAG_REMOVE_FROM_HARDWARE;
106 }
107}
108
109static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) 159static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
110{ 160{
111 const u8 *addr; 161 const u8 *addr;
112 int ret; 162 int ret;
113 DECLARE_MAC_BUF(mac); 163 DECLARE_MAC_BUF(mac);
114 164
165 assert_key_lock();
166 might_sleep();
167
115 if (!key || !key->local->ops->set_key) 168 if (!key || !key->local->ops->set_key)
116 return; 169 return;
117 170
118 if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE) && 171 spin_lock(&todo_lock);
119 !(key->flags & KEY_FLAG_REMOVE_FROM_HARDWARE)) 172 if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) {
173 spin_unlock(&todo_lock);
120 return; 174 return;
175 }
176 spin_unlock(&todo_lock);
121 177
122 addr = get_mac_for_key(key); 178 addr = get_mac_for_key(key);
123 179
@@ -131,8 +187,72 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
131 wiphy_name(key->local->hw.wiphy), 187 wiphy_name(key->local->hw.wiphy),
132 key->conf.keyidx, print_mac(mac, addr), ret); 188 key->conf.keyidx, print_mac(mac, addr), ret);
133 189
134 key->flags &= ~(KEY_FLAG_UPLOADED_TO_HARDWARE | 190 spin_lock(&todo_lock);
135 KEY_FLAG_REMOVE_FROM_HARDWARE); 191 key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
192 spin_unlock(&todo_lock);
193}
194
195static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata,
196 int idx)
197{
198 struct ieee80211_key *key = NULL;
199
200 if (idx >= 0 && idx < NUM_DEFAULT_KEYS)
201 key = sdata->keys[idx];
202
203 rcu_assign_pointer(sdata->default_key, key);
204
205 if (key)
206 add_todo(key, KEY_FLAG_TODO_DEFKEY);
207}
208
209void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx)
210{
211 unsigned long flags;
212
213 spin_lock_irqsave(&sdata->local->sta_lock, flags);
214 __ieee80211_set_default_key(sdata, idx);
215 spin_unlock_irqrestore(&sdata->local->sta_lock, flags);
216}
217
218
219static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
220 struct sta_info *sta,
221 struct ieee80211_key *old,
222 struct ieee80211_key *new)
223{
224 int idx, defkey;
225
226 if (new)
227 list_add(&new->list, &sdata->key_list);
228
229 if (sta) {
230 rcu_assign_pointer(sta->key, new);
231 } else {
232 WARN_ON(new && old && new->conf.keyidx != old->conf.keyidx);
233
234 if (old)
235 idx = old->conf.keyidx;
236 else
237 idx = new->conf.keyidx;
238
239 defkey = old && sdata->default_key == old;
240
241 if (defkey && !new)
242 __ieee80211_set_default_key(sdata, -1);
243
244 rcu_assign_pointer(sdata->keys[idx], new);
245 if (defkey && new)
246 __ieee80211_set_default_key(sdata, new->conf.keyidx);
247 }
248
249 if (old) {
250 /*
251 * We'll use an empty list to indicate that the key
252 * has already been removed.
253 */
254 list_del_init(&old->list);
255 }
136} 256}
137 257
138struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg, 258struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
@@ -160,6 +280,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
160 key->conf.keylen = key_len; 280 key->conf.keylen = key_len;
161 memcpy(key->conf.key, key_data, key_len); 281 memcpy(key->conf.key, key_data, key_len);
162 INIT_LIST_HEAD(&key->list); 282 INIT_LIST_HEAD(&key->list);
283 INIT_LIST_HEAD(&key->todo);
163 284
164 if (alg == ALG_CCMP) { 285 if (alg == ALG_CCMP) {
165 /* 286 /*
@@ -168,7 +289,7 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
168 */ 289 */
169 key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data); 290 key->u.ccmp.tfm = ieee80211_aes_key_setup_encrypt(key_data);
170 if (!key->u.ccmp.tfm) { 291 if (!key->u.ccmp.tfm) {
171 ieee80211_key_free(key); 292 kfree(key);
172 return NULL; 293 return NULL;
173 } 294 }
174 } 295 }
@@ -176,56 +297,14 @@ struct ieee80211_key *ieee80211_key_alloc(enum ieee80211_key_alg alg,
176 return key; 297 return key;
177} 298}
178 299
179static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
180 struct sta_info *sta,
181 struct ieee80211_key *key,
182 struct ieee80211_key *new)
183{
184 int idx, defkey;
185
186 if (new)
187 list_add(&new->list, &sdata->key_list);
188
189 if (sta) {
190 rcu_assign_pointer(sta->key, new);
191 } else {
192 WARN_ON(new && key && new->conf.keyidx != key->conf.keyidx);
193
194 if (key)
195 idx = key->conf.keyidx;
196 else
197 idx = new->conf.keyidx;
198
199 defkey = key && sdata->default_key == key;
200
201 if (defkey && !new)
202 ieee80211_set_default_key(sdata, -1);
203
204 rcu_assign_pointer(sdata->keys[idx], new);
205 if (defkey && new)
206 ieee80211_set_default_key(sdata, new->conf.keyidx);
207 }
208
209 if (key) {
210 ieee80211_key_mark_hw_accel_off(key);
211 /*
212 * We'll use an empty list to indicate that the key
213 * has already been removed.
214 */
215 list_del_init(&key->list);
216 }
217}
218
219void ieee80211_key_link(struct ieee80211_key *key, 300void ieee80211_key_link(struct ieee80211_key *key,
220 struct ieee80211_sub_if_data *sdata, 301 struct ieee80211_sub_if_data *sdata,
221 struct sta_info *sta) 302 struct sta_info *sta)
222{ 303{
223 struct ieee80211_key *old_key; 304 struct ieee80211_key *old_key;
305 unsigned long flags;
224 int idx; 306 int idx;
225 307
226 ASSERT_RTNL();
227 might_sleep();
228
229 BUG_ON(!sdata); 308 BUG_ON(!sdata);
230 BUG_ON(!key); 309 BUG_ON(!key);
231 310
@@ -234,11 +313,7 @@ void ieee80211_key_link(struct ieee80211_key *key,
234 key->sdata = sdata; 313 key->sdata = sdata;
235 key->sta = sta; 314 key->sta = sta;
236 315
237 ieee80211_debugfs_key_add(key->local, key);
238
239 if (sta) { 316 if (sta) {
240 ieee80211_debugfs_key_sta_link(key, sta);
241
242 /* 317 /*
243 * some hardware cannot handle TKIP with QoS, so 318 * some hardware cannot handle TKIP with QoS, so
244 * we indicate whether QoS could be in use. 319 * we indicate whether QoS could be in use.
@@ -249,7 +324,10 @@ void ieee80211_key_link(struct ieee80211_key *key,
249 if (sdata->vif.type == IEEE80211_IF_TYPE_STA) { 324 if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
250 struct sta_info *ap; 325 struct sta_info *ap;
251 326
252 rcu_read_lock(); 327 /*
328 * We're getting a sta pointer in,
329 * so must be under RCU read lock.
330 */
253 331
254 /* same here, the AP could be using QoS */ 332 /* same here, the AP could be using QoS */
255 ap = sta_info_get(key->local, key->sdata->u.sta.bssid); 333 ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
@@ -258,11 +336,11 @@ void ieee80211_key_link(struct ieee80211_key *key,
258 key->conf.flags |= 336 key->conf.flags |=
259 IEEE80211_KEY_FLAG_WMM_STA; 337 IEEE80211_KEY_FLAG_WMM_STA;
260 } 338 }
261
262 rcu_read_unlock();
263 } 339 }
264 } 340 }
265 341
342 spin_lock_irqsave(&sdata->local->sta_lock, flags);
343
266 if (sta) 344 if (sta)
267 old_key = sta->key; 345 old_key = sta->key;
268 else 346 else
@@ -270,108 +348,150 @@ void ieee80211_key_link(struct ieee80211_key *key,
270 348
271 __ieee80211_key_replace(sdata, sta, old_key, key); 349 __ieee80211_key_replace(sdata, sta, old_key, key);
272 350
273 if (old_key) { 351 spin_unlock_irqrestore(&sdata->local->sta_lock, flags);
274 synchronize_rcu(); 352
275 ieee80211_key_free(old_key); 353 /* free old key later */
276 } 354 add_todo(old_key, KEY_FLAG_TODO_DELETE);
277 355
356 add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS);
278 if (netif_running(sdata->dev)) 357 if (netif_running(sdata->dev))
279 ieee80211_key_enable_hw_accel(key); 358 add_todo(key, KEY_FLAG_TODO_HWACCEL);
280} 359}
281 360
282void ieee80211_key_free(struct ieee80211_key *key) 361void ieee80211_key_free(struct ieee80211_key *key)
283{ 362{
284 ASSERT_RTNL(); 363 unsigned long flags;
285 might_sleep();
286 364
287 if (!key) 365 if (!key)
288 return; 366 return;
289 367
368 /*
369 * Replace key with nothingness if it was ever used.
370 */
290 if (key->sdata) { 371 if (key->sdata) {
291 /* 372 spin_lock_irqsave(&key->sdata->local->sta_lock, flags);
292 * Replace key with nothingness. 373 __ieee80211_key_replace(key->sdata, key->sta,
293 * 374 key, NULL);
294 * Because other code may have key reference (RCU protected) 375 spin_unlock_irqrestore(&key->sdata->local->sta_lock, flags);
295 * right now, we then wait for a grace period before freeing 376 }
296 * it.
297 * An empty list indicates it was never added to the key list
298 * or has been removed already. It may, however, still be in
299 * hardware for acceleration.
300 */
301 if (!list_empty(&key->list))
302 __ieee80211_key_replace(key->sdata, key->sta,
303 key, NULL);
304 377
305 /* 378 add_todo(key, KEY_FLAG_TODO_DELETE);
306 * Do NOT remove this without looking at sta_info_destroy() 379}
307 */
308 synchronize_rcu();
309 380
310 /* 381void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
311 * Remove from hwaccel if appropriate, this will 382{
312 * only happen when the key is actually unlinked, 383 struct ieee80211_key *key;
313 * it will already be done when the key was replaced.
314 */
315 ieee80211_key_disable_hw_accel(key);
316 }
317 384
318 if (key->conf.alg == ALG_CCMP) 385 might_sleep();
319 ieee80211_aes_key_free(key->u.ccmp.tfm);
320 ieee80211_debugfs_key_remove(key);
321 386
322 kfree(key); 387 if (WARN_ON(!netif_running(sdata->dev)))
388 return;
389
390 ieee80211_key_lock();
391
392 list_for_each_entry(key, &sdata->key_list, list)
393 ieee80211_key_enable_hw_accel(key);
394
395 ieee80211_key_unlock();
323} 396}
324 397
325void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) 398void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
326{ 399{
327 struct ieee80211_key *key = NULL; 400 struct ieee80211_key *key;
328 401
329 if (idx >= 0 && idx < NUM_DEFAULT_KEYS) 402 might_sleep();
330 key = sdata->keys[idx];
331 403
332 if (sdata->default_key != key) { 404 ieee80211_key_lock();
333 ieee80211_debugfs_key_remove_default(sdata);
334 405
335 rcu_assign_pointer(sdata->default_key, key); 406 list_for_each_entry(key, &sdata->key_list, list)
407 ieee80211_key_disable_hw_accel(key);
336 408
337 if (sdata->default_key) 409 ieee80211_key_unlock();
338 ieee80211_debugfs_key_add_default(sdata);
339 }
340} 410}
341 411
342void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) 412static void __ieee80211_key_free(struct ieee80211_key *key)
343{ 413{
344 struct ieee80211_key *key, *tmp; 414 if (!key)
345 LIST_HEAD(tmp_list); 415 return;
346 416
347 ASSERT_RTNL(); 417 ieee80211_key_disable_hw_accel(key);
348 might_sleep();
349 418
350 list_for_each_entry_safe(key, tmp, &sdata->key_list, list) 419 if (key->conf.alg == ALG_CCMP)
351 ieee80211_key_free(key); 420 ieee80211_aes_key_free(key->u.ccmp.tfm);
421 ieee80211_debugfs_key_remove(key);
422
423 kfree(key);
352} 424}
353 425
354void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) 426static void __ieee80211_key_todo(void)
355{ 427{
356 struct ieee80211_key *key; 428 struct ieee80211_key *key;
429 bool work_done;
430 u32 todoflags;
357 431
358 ASSERT_RTNL(); 432 /*
359 might_sleep(); 433 * NB: sta_info_destroy relies on this!
434 */
435 synchronize_rcu();
436
437 spin_lock(&todo_lock);
438 while (!list_empty(&todo_list)) {
439 key = list_first_entry(&todo_list, struct ieee80211_key, todo);
440 list_del_init(&key->todo);
441 todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS |
442 KEY_FLAG_TODO_DEFKEY |
443 KEY_FLAG_TODO_HWACCEL |
444 KEY_FLAG_TODO_DELETE);
445 key->flags &= ~todoflags;
446 spin_unlock(&todo_lock);
447
448 work_done = false;
449
450 if (todoflags & KEY_FLAG_TODO_ADD_DEBUGFS) {
451 ieee80211_debugfs_key_add(key);
452 work_done = true;
453 }
454 if (todoflags & KEY_FLAG_TODO_DEFKEY) {
455 ieee80211_debugfs_key_remove_default(key->sdata);
456 ieee80211_debugfs_key_add_default(key->sdata);
457 work_done = true;
458 }
459 if (todoflags & KEY_FLAG_TODO_HWACCEL) {
460 ieee80211_key_enable_hw_accel(key);
461 work_done = true;
462 }
463 if (todoflags & KEY_FLAG_TODO_DELETE) {
464 __ieee80211_key_free(key);
465 work_done = true;
466 }
360 467
361 if (WARN_ON(!netif_running(sdata->dev))) 468 WARN_ON(!work_done);
362 return;
363 469
364 list_for_each_entry(key, &sdata->key_list, list) 470 spin_lock(&todo_lock);
365 ieee80211_key_enable_hw_accel(key); 471 }
472 spin_unlock(&todo_lock);
366} 473}
367 474
368void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) 475void ieee80211_key_todo(void)
369{ 476{
370 struct ieee80211_key *key; 477 ieee80211_key_lock();
478 __ieee80211_key_todo();
479 ieee80211_key_unlock();
480}
371 481
372 ASSERT_RTNL(); 482void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
373 might_sleep(); 483{
484 struct ieee80211_key *key, *tmp;
485 LIST_HEAD(tmp_list);
374 486
375 list_for_each_entry(key, &sdata->key_list, list) 487 ieee80211_key_lock();
376 ieee80211_key_disable_hw_accel(key); 488
489 ieee80211_debugfs_key_remove_default(sdata);
490
491 list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
492 ieee80211_key_free(key);
493
494 __ieee80211_key_todo();
495
496 ieee80211_key_unlock();
377} 497}