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) spin_lock(&todo_lock); key->flags |= flag; - /* only add if not already added */ - if (list_empty(&key->todo)) - list_add(&key->todo, &todo_list); + /* + * Remove again if already on the list so that we move it to the end. + */ + if (!list_empty(&key->todo)) + list_del(&key->todo); + list_add_tail(&key->todo, &todo_list); schedule_work(&todo_work); spin_unlock(&todo_lock); } @@ -210,9 +213,9 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx) { unsigned long flags; - spin_lock_irqsave(&sdata->local->sta_lock, flags); + spin_lock_irqsave(&sdata->local->key_lock, flags); __ieee80211_set_default_key(sdata, idx); - spin_unlock_irqrestore(&sdata->local->sta_lock, flags); + spin_unlock_irqrestore(&sdata->local->key_lock, flags); } @@ -339,7 +342,7 @@ void ieee80211_key_link(struct ieee80211_key *key, } } - spin_lock_irqsave(&sdata->local->sta_lock, flags); + spin_lock_irqsave(&sdata->local->key_lock, flags); if (sta) old_key = sta->key; @@ -348,68 +351,81 @@ void ieee80211_key_link(struct ieee80211_key *key, __ieee80211_key_replace(sdata, sta, old_key, key); - spin_unlock_irqrestore(&sdata->local->sta_lock, flags); + spin_unlock_irqrestore(&sdata->local->key_lock, flags); /* free old key later */ add_todo(old_key, KEY_FLAG_TODO_DELETE); add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS); if (netif_running(sdata->dev)) - add_todo(key, KEY_FLAG_TODO_HWACCEL); + add_todo(key, KEY_FLAG_TODO_HWACCEL_ADD); } -void ieee80211_key_free(struct ieee80211_key *key) +static void __ieee80211_key_free(struct ieee80211_key *key) { - unsigned long flags; - - if (!key) - return; - /* * Replace key with nothingness if it was ever used. */ - if (key->sdata) { - spin_lock_irqsave(&key->sdata->local->sta_lock, flags); + if (key->sdata) __ieee80211_key_replace(key->sdata, key->sta, key, NULL); - spin_unlock_irqrestore(&key->sdata->local->sta_lock, flags); - } add_todo(key, KEY_FLAG_TODO_DELETE); } -void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) +void ieee80211_key_free(struct ieee80211_key *key) { - struct ieee80211_key *key; - - might_sleep(); + unsigned long flags; - if (WARN_ON(!netif_running(sdata->dev))) + if (!key) return; - ieee80211_key_lock(); + spin_lock_irqsave(&key->sdata->local->key_lock, flags); + __ieee80211_key_free(key); + spin_unlock_irqrestore(&key->sdata->local->key_lock, flags); +} + +/* + * To be safe against concurrent manipulations of the list (which shouldn't + * actually happen) we need to hold the spinlock. But under the spinlock we + * can't actually do much, so we defer processing to the todo list. Then run + * the todo list to be sure the operation and possibly previously pending + * operations are completed. + */ +static void ieee80211_todo_for_each_key(struct ieee80211_sub_if_data *sdata, + u32 todo_flags) +{ + struct ieee80211_key *key; + unsigned long flags; + might_sleep(); + + spin_lock_irqsave(&sdata->local->key_lock, flags); list_for_each_entry(key, &sdata->key_list, list) - ieee80211_key_enable_hw_accel(key); + add_todo(key, todo_flags); + spin_unlock_irqrestore(&sdata->local->key_lock, flags); - ieee80211_key_unlock(); + ieee80211_key_todo(); } -void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) +void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata) { - struct ieee80211_key *key; + ASSERT_RTNL(); - might_sleep(); + if (WARN_ON(!netif_running(sdata->dev))) + return; - ieee80211_key_lock(); + ieee80211_todo_for_each_key(sdata, KEY_FLAG_TODO_HWACCEL_ADD); +} - list_for_each_entry(key, &sdata->key_list, list) - ieee80211_key_disable_hw_accel(key); +void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata) +{ + ASSERT_RTNL(); - ieee80211_key_unlock(); + ieee80211_todo_for_each_key(sdata, KEY_FLAG_TODO_HWACCEL_REMOVE); } -static void __ieee80211_key_free(struct ieee80211_key *key) +static void __ieee80211_key_destroy(struct ieee80211_key *key) { if (!key) return; @@ -440,7 +456,8 @@ static void __ieee80211_key_todo(void) list_del_init(&key->todo); todoflags = key->flags & (KEY_FLAG_TODO_ADD_DEBUGFS | KEY_FLAG_TODO_DEFKEY | - KEY_FLAG_TODO_HWACCEL | + KEY_FLAG_TODO_HWACCEL_ADD | + KEY_FLAG_TODO_HWACCEL_REMOVE | KEY_FLAG_TODO_DELETE); key->flags &= ~todoflags; spin_unlock(&todo_lock); @@ -456,12 +473,16 @@ static void __ieee80211_key_todo(void) ieee80211_debugfs_key_add_default(key->sdata); work_done = true; } - if (todoflags & KEY_FLAG_TODO_HWACCEL) { + if (todoflags & KEY_FLAG_TODO_HWACCEL_ADD) { ieee80211_key_enable_hw_accel(key); work_done = true; } + if (todoflags & KEY_FLAG_TODO_HWACCEL_REMOVE) { + ieee80211_key_disable_hw_accel(key); + work_done = true; + } if (todoflags & KEY_FLAG_TODO_DELETE) { - __ieee80211_key_free(key); + __ieee80211_key_destroy(key); work_done = true; } @@ -482,14 +503,16 @@ void ieee80211_key_todo(void) void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata) { struct ieee80211_key *key, *tmp; - LIST_HEAD(tmp_list); + unsigned long flags; ieee80211_key_lock(); ieee80211_debugfs_key_remove_default(sdata); + spin_lock_irqsave(&sdata->local->key_lock, flags); list_for_each_entry_safe(key, tmp, &sdata->key_list, list) - ieee80211_key_free(key); + __ieee80211_key_free(key); + spin_unlock_irqrestore(&sdata->local->key_lock, flags); __ieee80211_key_todo(); |