aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-04-09 10:45:37 -0400
committerJohn W. Linville <linville@tuxdriver.com>2008-04-16 14:53:22 -0400
commit3a245766901a9dfdc3f53457a7954b369b50f281 (patch)
tree33bc4ef4dfb1bdf0eec46d320fd4333f55dffaf8
parent51e8b885902fc8cc2ded48322ad9402bbcff23fe (diff)
mac80211: fix key hwaccel race
The previous key locking patch left a small race: it would be possible to add a key and take the interface down before the key todo is run so that hwaccel for that key is enabled on an interface that is down. Avoid this by running the todo list when an interface is brought up or down. This patch also fixes a small bug: before this change, a few functions used the key list without the lock that protects it. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--net/mac80211/key.c84
-rw-r--r--net/mac80211/key.h11
2 files changed, 59 insertions, 36 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 711e36e54ff8..acf8d0370a37 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -355,61 +355,74 @@ void ieee80211_key_link(struct ieee80211_key *key,
355 355
356 add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS); 356 add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS);
357 if (netif_running(sdata->dev)) 357 if (netif_running(sdata->dev))
358 add_todo(key, KEY_FLAG_TODO_HWACCEL); 358 add_todo(key, KEY_FLAG_TODO_HWACCEL_ADD);
359} 359}
360 360
361void ieee80211_key_free(struct ieee80211_key *key) 361static void __ieee80211_key_free(struct ieee80211_key *key)
362{ 362{
363 unsigned long flags;
364
365 if (!key)
366 return;
367
368 /* 363 /*
369 * Replace key with nothingness if it was ever used. 364 * Replace key with nothingness if it was ever used.
370 */ 365 */
371 if (key->sdata) { 366 if (key->sdata)
372 spin_lock_irqsave(&key->sdata->local->sta_lock, flags);
373 __ieee80211_key_replace(key->sdata, key->sta, 367 __ieee80211_key_replace(key->sdata, key->sta,
374 key, NULL); 368 key, NULL);
375 spin_unlock_irqrestore(&key->sdata->local->sta_lock, flags);
376 }
377 369
378 add_todo(key, KEY_FLAG_TODO_DELETE); 370 add_todo(key, KEY_FLAG_TODO_DELETE);
379} 371}
380 372
381void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) 373void ieee80211_key_free(struct ieee80211_key *key)
382{ 374{
383 struct ieee80211_key *key; 375 unsigned long flags;
384
385 might_sleep();
386 376
387 if (WARN_ON(!netif_running(sdata->dev))) 377 if (!key)
388 return; 378 return;
389 379
390 ieee80211_key_lock(); 380 spin_lock_irqsave(&key->sdata->local->sta_lock, flags);
381 __ieee80211_key_free(key);
382 spin_unlock_irqrestore(&key->sdata->local->sta_lock, flags);
383}
384
385/*
386 * To be safe against concurrent manipulations of the list (which shouldn't
387 * actually happen) we need to hold the spinlock. But under the spinlock we
388 * can't actually do much, so we defer processing to the todo list. Then run
389 * the todo list to be sure the operation and possibly previously pending
390 * operations are completed.
391 */
392static void ieee80211_todo_for_each_key(struct ieee80211_sub_if_data *sdata,
393 u32 todo_flags)
394{
395 struct ieee80211_key *key;
396 unsigned long flags;
391 397
398 might_sleep();
399
400 spin_lock_irqsave(&sdata->local->sta_lock, flags);
392 list_for_each_entry(key, &sdata->key_list, list) 401 list_for_each_entry(key, &sdata->key_list, list)
393 ieee80211_key_enable_hw_accel(key); 402 add_todo(key, todo_flags);
403 spin_unlock_irqrestore(&sdata->local->sta_lock, flags);
394 404
395 ieee80211_key_unlock(); 405 ieee80211_key_todo();
396} 406}
397 407
398void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) 408void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
399{ 409{
400 struct ieee80211_key *key; 410 ASSERT_RTNL();
401 411
402 might_sleep(); 412 if (WARN_ON(!netif_running(sdata->dev)))
413 return;
403 414
404 ieee80211_key_lock(); 415 ieee80211_todo_for_each_key(sdata, KEY_FLAG_TODO_HWACCEL_ADD);
416}
405 417
406 list_for_each_entry(key, &sdata->key_list, list) 418void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
407 ieee80211_key_disable_hw_accel(key); 419{
420 ASSERT_RTNL();
408 421
409 ieee80211_key_unlock(); 422 ieee80211_todo_for_each_key(sdata, KEY_FLAG_TODO_HWACCEL_REMOVE);
410} 423}
411 424
412static void __ieee80211_key_free(struct ieee80211_key *key) 425static void __ieee80211_key_destroy(struct ieee80211_key *key)
413{ 426{
414 if (!key) 427 if (!key)
415 return; 428 return;
@@ -440,7 +453,8 @@ static void __ieee80211_key_todo(void)
440 list_del_init(&key->todo); 453 list_del_init(&key->todo);
441 todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | 454 todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS |
442 KEY_FLAG_TODO_DEFKEY | 455 KEY_FLAG_TODO_DEFKEY |
443 KEY_FLAG_TODO_HWACCEL | 456 KEY_FLAG_TODO_HWACCEL_ADD |
457 KEY_FLAG_TODO_HWACCEL_REMOVE |
444 KEY_FLAG_TODO_DELETE); 458 KEY_FLAG_TODO_DELETE);
445 key->flags &= ~todoflags; 459 key->flags &= ~todoflags;
446 spin_unlock(&todo_lock); 460 spin_unlock(&todo_lock);
@@ -456,12 +470,16 @@ static void __ieee80211_key_todo(void)
456 ieee80211_debugfs_key_add_default(key->sdata); 470 ieee80211_debugfs_key_add_default(key->sdata);
457 work_done = true; 471 work_done = true;
458 } 472 }
459 if (todoflags & KEY_FLAG_TODO_HWACCEL) { 473 if (todoflags & KEY_FLAG_TODO_HWACCEL_ADD) {
460 ieee80211_key_enable_hw_accel(key); 474 ieee80211_key_enable_hw_accel(key);
461 work_done = true; 475 work_done = true;
462 } 476 }
477 if (todoflags & KEY_FLAG_TODO_HWACCEL_REMOVE) {
478 ieee80211_key_disable_hw_accel(key);
479 work_done = true;
480 }
463 if (todoflags & KEY_FLAG_TODO_DELETE) { 481 if (todoflags & KEY_FLAG_TODO_DELETE) {
464 __ieee80211_key_free(key); 482 __ieee80211_key_destroy(key);
465 work_done = true; 483 work_done = true;
466 } 484 }
467 485
@@ -482,14 +500,16 @@ void ieee80211_key_todo(void)
482void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) 500void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
483{ 501{
484 struct ieee80211_key *key, *tmp; 502 struct ieee80211_key *key, *tmp;
485 LIST_HEAD(tmp_list); 503 unsigned long flags;
486 504
487 ieee80211_key_lock(); 505 ieee80211_key_lock();
488 506
489 ieee80211_debugfs_key_remove_default(sdata); 507 ieee80211_debugfs_key_remove_default(sdata);
490 508
509 spin_lock_irqsave(&sdata->local->sta_lock, flags);
491 list_for_each_entry_safe(key, tmp, &sdata->key_list, list) 510 list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
492 ieee80211_key_free(key); 511 __ieee80211_key_free(key);
512 spin_unlock_irqrestore(&sdata->local->sta_lock, flags);
493 513
494 __ieee80211_key_todo(); 514 __ieee80211_key_todo();
495 515
diff --git a/net/mac80211/key.h b/net/mac80211/key.h
index 5d48518985b3..f52c3df1fe9a 100644
--- a/net/mac80211/key.h
+++ b/net/mac80211/key.h
@@ -54,16 +54,19 @@ struct sta_info;
54 * @KEY_FLAG_TODO_DELETE: Key is marked for deletion and will, after an 54 * @KEY_FLAG_TODO_DELETE: Key is marked for deletion and will, after an
55 * RCU grace period, no longer be reachable other than from the 55 * RCU grace period, no longer be reachable other than from the
56 * todo list. 56 * todo list.
57 * @KEY_FLAG_TODO_HWACCEL: Key needs to be added to hardware acceleration. 57 * @KEY_FLAG_TODO_HWACCEL_ADD: Key needs to be added to hardware acceleration.
58 * @KEY_FLAG_TODO_HWACCEL_REMOVE: Key needs to be removed from hardware
59 * acceleration.
58 * @KEY_FLAG_TODO_DEFKEY: Key is default key and debugfs needs to be updated. 60 * @KEY_FLAG_TODO_DEFKEY: Key is default key and debugfs needs to be updated.
59 * @KEY_FLAG_TODO_ADD_DEBUGFS: Key needs to be added to debugfs. 61 * @KEY_FLAG_TODO_ADD_DEBUGFS: Key needs to be added to debugfs.
60 */ 62 */
61enum ieee80211_internal_key_flags { 63enum ieee80211_internal_key_flags {
62 KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0), 64 KEY_FLAG_UPLOADED_TO_HARDWARE = BIT(0),
63 KEY_FLAG_TODO_DELETE = BIT(1), 65 KEY_FLAG_TODO_DELETE = BIT(1),
64 KEY_FLAG_TODO_HWACCEL = BIT(2), 66 KEY_FLAG_TODO_HWACCEL_ADD = BIT(2),
65 KEY_FLAG_TODO_DEFKEY = BIT(3), 67 KEY_FLAG_TODO_HWACCEL_REMOVE = BIT(3),
66 KEY_FLAG_TODO_ADD_DEBUGFS = BIT(4), 68 KEY_FLAG_TODO_DEFKEY = BIT(4),
69 KEY_FLAG_TODO_ADD_DEBUGFS = BIT(5),
67}; 70};
68 71
69struct ieee80211_key { 72struct ieee80211_key {