diff options
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r-- | net/mac80211/key.c | 101 |
1 files changed, 62 insertions, 39 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 711e36e54ff8..150d66dbda9d 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c | |||
@@ -74,9 +74,12 @@ static void add_todo(struct ieee80211_key *key, u32 flag) | |||
74 | 74 | ||
75 | spin_lock(&todo_lock); | 75 | spin_lock(&todo_lock); |
76 | key->flags |= flag; | 76 | key->flags |= flag; |
77 | /* only add if not already added */ | 77 | /* |
78 | if (list_empty(&key->todo)) | 78 | * Remove again if already on the list so that we move it to the end. |
79 | list_add(&key->todo, &todo_list); | 79 | */ |
80 | if (!list_empty(&key->todo)) | ||
81 | list_del(&key->todo); | ||
82 | list_add_tail(&key->todo, &todo_list); | ||
80 | schedule_work(&todo_work); | 83 | schedule_work(&todo_work); |
81 | spin_unlock(&todo_lock); | 84 | spin_unlock(&todo_lock); |
82 | } | 85 | } |
@@ -210,9 +213,9 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) | |||
210 | { | 213 | { |
211 | unsigned long flags; | 214 | unsigned long flags; |
212 | 215 | ||
213 | spin_lock_irqsave(&sdata->local->sta_lock, flags); | 216 | spin_lock_irqsave(&sdata->local->key_lock, flags); |
214 | __ieee80211_set_default_key(sdata, idx); | 217 | __ieee80211_set_default_key(sdata, idx); |
215 | spin_unlock_irqrestore(&sdata->local->sta_lock, flags); | 218 | spin_unlock_irqrestore(&sdata->local->key_lock, flags); |
216 | } | 219 | } |
217 | 220 | ||
218 | 221 | ||
@@ -339,7 +342,7 @@ void ieee80211_key_link(struct ieee80211_key *key, | |||
339 | } | 342 | } |
340 | } | 343 | } |
341 | 344 | ||
342 | spin_lock_irqsave(&sdata->local->sta_lock, flags); | 345 | spin_lock_irqsave(&sdata->local->key_lock, flags); |
343 | 346 | ||
344 | if (sta) | 347 | if (sta) |
345 | old_key = sta->key; | 348 | old_key = sta->key; |
@@ -348,68 +351,81 @@ void ieee80211_key_link(struct ieee80211_key *key, | |||
348 | 351 | ||
349 | __ieee80211_key_replace(sdata, sta, old_key, key); | 352 | __ieee80211_key_replace(sdata, sta, old_key, key); |
350 | 353 | ||
351 | spin_unlock_irqrestore(&sdata->local->sta_lock, flags); | 354 | spin_unlock_irqrestore(&sdata->local->key_lock, flags); |
352 | 355 | ||
353 | /* free old key later */ | 356 | /* free old key later */ |
354 | add_todo(old_key, KEY_FLAG_TODO_DELETE); | 357 | add_todo(old_key, KEY_FLAG_TODO_DELETE); |
355 | 358 | ||
356 | add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS); | 359 | add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS); |
357 | if (netif_running(sdata->dev)) | 360 | if (netif_running(sdata->dev)) |
358 | add_todo(key, KEY_FLAG_TODO_HWACCEL); | 361 | add_todo(key, KEY_FLAG_TODO_HWACCEL_ADD); |
359 | } | 362 | } |
360 | 363 | ||
361 | void ieee80211_key_free(struct ieee80211_key *key) | 364 | static void __ieee80211_key_free(struct ieee80211_key *key) |
362 | { | 365 | { |
363 | unsigned long flags; | ||
364 | |||
365 | if (!key) | ||
366 | return; | ||
367 | |||
368 | /* | 366 | /* |
369 | * Replace key with nothingness if it was ever used. | 367 | * Replace key with nothingness if it was ever used. |
370 | */ | 368 | */ |
371 | if (key->sdata) { | 369 | if (key->sdata) |
372 | spin_lock_irqsave(&key->sdata->local->sta_lock, flags); | ||
373 | __ieee80211_key_replace(key->sdata, key->sta, | 370 | __ieee80211_key_replace(key->sdata, key->sta, |
374 | key, NULL); | 371 | key, NULL); |
375 | spin_unlock_irqrestore(&key->sdata->local->sta_lock, flags); | ||
376 | } | ||
377 | 372 | ||
378 | add_todo(key, KEY_FLAG_TODO_DELETE); | 373 | add_todo(key, KEY_FLAG_TODO_DELETE); |
379 | } | 374 | } |
380 | 375 | ||
381 | void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) | 376 | void ieee80211_key_free(struct ieee80211_key *key) |
382 | { | 377 | { |
383 | struct ieee80211_key *key; | 378 | unsigned long flags; |
384 | |||
385 | might_sleep(); | ||
386 | 379 | ||
387 | if (WARN_ON(!netif_running(sdata->dev))) | 380 | if (!key) |
388 | return; | 381 | return; |
389 | 382 | ||
390 | ieee80211_key_lock(); | 383 | spin_lock_irqsave(&key->sdata->local->key_lock, flags); |
384 | __ieee80211_key_free(key); | ||
385 | spin_unlock_irqrestore(&key->sdata->local->key_lock, flags); | ||
386 | } | ||
387 | |||
388 | /* | ||
389 | * To be safe against concurrent manipulations of the list (which shouldn't | ||
390 | * actually happen) we need to hold the spinlock. But under the spinlock we | ||
391 | * can't actually do much, so we defer processing to the todo list. Then run | ||
392 | * the todo list to be sure the operation and possibly previously pending | ||
393 | * operations are completed. | ||
394 | */ | ||
395 | static void ieee80211_todo_for_each_key(struct ieee80211_sub_if_data *sdata, | ||
396 | u32 todo_flags) | ||
397 | { | ||
398 | struct ieee80211_key *key; | ||
399 | unsigned long flags; | ||
391 | 400 | ||
401 | might_sleep(); | ||
402 | |||
403 | spin_lock_irqsave(&sdata->local->key_lock, flags); | ||
392 | list_for_each_entry(key, &sdata->key_list, list) | 404 | list_for_each_entry(key, &sdata->key_list, list) |
393 | ieee80211_key_enable_hw_accel(key); | 405 | add_todo(key, todo_flags); |
406 | spin_unlock_irqrestore(&sdata->local->key_lock, flags); | ||
394 | 407 | ||
395 | ieee80211_key_unlock(); | 408 | ieee80211_key_todo(); |
396 | } | 409 | } |
397 | 410 | ||
398 | void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) | 411 | void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) |
399 | { | 412 | { |
400 | struct ieee80211_key *key; | 413 | ASSERT_RTNL(); |
401 | 414 | ||
402 | might_sleep(); | 415 | if (WARN_ON(!netif_running(sdata->dev))) |
416 | return; | ||
403 | 417 | ||
404 | ieee80211_key_lock(); | 418 | ieee80211_todo_for_each_key(sdata, KEY_FLAG_TODO_HWACCEL_ADD); |
419 | } | ||
405 | 420 | ||
406 | list_for_each_entry(key, &sdata->key_list, list) | 421 | void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) |
407 | ieee80211_key_disable_hw_accel(key); | 422 | { |
423 | ASSERT_RTNL(); | ||
408 | 424 | ||
409 | ieee80211_key_unlock(); | 425 | ieee80211_todo_for_each_key(sdata, KEY_FLAG_TODO_HWACCEL_REMOVE); |
410 | } | 426 | } |
411 | 427 | ||
412 | static void __ieee80211_key_free(struct ieee80211_key *key) | 428 | static void __ieee80211_key_destroy(struct ieee80211_key *key) |
413 | { | 429 | { |
414 | if (!key) | 430 | if (!key) |
415 | return; | 431 | return; |
@@ -440,7 +456,8 @@ static void __ieee80211_key_todo(void) | |||
440 | list_del_init(&key->todo); | 456 | list_del_init(&key->todo); |
441 | todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | | 457 | todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | |
442 | KEY_FLAG_TODO_DEFKEY | | 458 | KEY_FLAG_TODO_DEFKEY | |
443 | KEY_FLAG_TODO_HWACCEL | | 459 | KEY_FLAG_TODO_HWACCEL_ADD | |
460 | KEY_FLAG_TODO_HWACCEL_REMOVE | | ||
444 | KEY_FLAG_TODO_DELETE); | 461 | KEY_FLAG_TODO_DELETE); |
445 | key->flags &= ~todoflags; | 462 | key->flags &= ~todoflags; |
446 | spin_unlock(&todo_lock); | 463 | spin_unlock(&todo_lock); |
@@ -456,12 +473,16 @@ static void __ieee80211_key_todo(void) | |||
456 | ieee80211_debugfs_key_add_default(key->sdata); | 473 | ieee80211_debugfs_key_add_default(key->sdata); |
457 | work_done = true; | 474 | work_done = true; |
458 | } | 475 | } |
459 | if (todoflags & KEY_FLAG_TODO_HWACCEL) { | 476 | if (todoflags & KEY_FLAG_TODO_HWACCEL_ADD) { |
460 | ieee80211_key_enable_hw_accel(key); | 477 | ieee80211_key_enable_hw_accel(key); |
461 | work_done = true; | 478 | work_done = true; |
462 | } | 479 | } |
480 | if (todoflags & KEY_FLAG_TODO_HWACCEL_REMOVE) { | ||
481 | ieee80211_key_disable_hw_accel(key); | ||
482 | work_done = true; | ||
483 | } | ||
463 | if (todoflags & KEY_FLAG_TODO_DELETE) { | 484 | if (todoflags & KEY_FLAG_TODO_DELETE) { |
464 | __ieee80211_key_free(key); | 485 | __ieee80211_key_destroy(key); |
465 | work_done = true; | 486 | work_done = true; |
466 | } | 487 | } |
467 | 488 | ||
@@ -482,14 +503,16 @@ void ieee80211_key_todo(void) | |||
482 | void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) | 503 | void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) |
483 | { | 504 | { |
484 | struct ieee80211_key *key, *tmp; | 505 | struct ieee80211_key *key, *tmp; |
485 | LIST_HEAD(tmp_list); | 506 | unsigned long flags; |
486 | 507 | ||
487 | ieee80211_key_lock(); | 508 | ieee80211_key_lock(); |
488 | 509 | ||
489 | ieee80211_debugfs_key_remove_default(sdata); | 510 | ieee80211_debugfs_key_remove_default(sdata); |
490 | 511 | ||
512 | spin_lock_irqsave(&sdata->local->key_lock, flags); | ||
491 | list_for_each_entry_safe(key, tmp, &sdata->key_list, list) | 513 | list_for_each_entry_safe(key, tmp, &sdata->key_list, list) |
492 | ieee80211_key_free(key); | 514 | __ieee80211_key_free(key); |
515 | spin_unlock_irqrestore(&sdata->local->key_lock, flags); | ||
493 | 516 | ||
494 | __ieee80211_key_todo(); | 517 | __ieee80211_key_todo(); |
495 | 518 | ||