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);
}
{
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);
}
* some hardware cannot handle TKIP with QoS, so
* we indicate whether QoS could be in use.
*/
- if (sta->flags & WLAN_STA_WME)
+ if (test_sta_flags(sta, WLAN_STA_WME))
key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
+
+ /*
+ * This key is for a specific sta interface,
+ * inform the driver that it should try to store
+ * this key as pairwise key.
+ */
+ key->conf.flags |= IEEE80211_KEY_FLAG_PAIRWISE;
} else {
if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
struct sta_info *ap;
/* same here, the AP could be using QoS */
ap = sta_info_get(key->local, key->sdata->u.sta.bssid);
if (ap) {
- if (ap->flags & WLAN_STA_WME)
+ if (test_sta_flags(ap, WLAN_STA_WME))
key->conf.flags |=
IEEE80211_KEY_FLAG_WMM_STA;
}
}
}
- spin_lock_irqsave(&sdata->local->sta_lock, flags);
+ spin_lock_irqsave(&sdata->local->key_lock, flags);
if (sta)
old_key = sta->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;
+ unsigned long flags;
- might_sleep();
+ if (!key)
+ return;
- if (WARN_ON(!netif_running(sdata->dev)))
+ if (!key->sdata) {
+ /* The key has not been linked yet, simply free it
+ * and don't Oops */
+ if (key->conf.alg == ALG_CCMP)
+ ieee80211_aes_key_free(key->u.ccmp.tfm);
+ kfree(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;
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);
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;
}
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();