]> err.no Git - linux-2.6/blobdiff - net/mac80211/key.c
mac80211: wpa.c remove rx/tx_data ->fc users
[linux-2.6] / net / mac80211 / key.c
index 711e36e54ff82258407c3f2b5f77cea95a3a1fb2..6597c779e35a946147c510b5f0659afb4b40bce7 100644 (file)
@@ -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);
 }
 
 
@@ -318,8 +321,15 @@ void ieee80211_key_link(struct ieee80211_key *key,
                 * 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;
@@ -332,14 +342,14 @@ void ieee80211_key_link(struct ieee80211_key *key,
                        /* 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;
@@ -348,68 +358,90 @@ 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;
+       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;
@@ -440,7 +472,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 +489,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 +519,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();