]> err.no Git - linux-2.6/blobdiff - net/mac80211/key.c
Merge branch 'upstream-davem' of master.kernel.org:/pub/scm/linux/kernel/git/jgarzik...
[linux-2.6] / net / mac80211 / key.c
index 178f00cf61b925110e4efd865ece28854e97f837..ed57fb8e82fc13ab9052fb4992524c572b1d2c6e 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/if_ether.h>
 #include <linux/etherdevice.h>
 #include <linux/list.h>
+#include <linux/rcupdate.h>
 #include <net/mac80211.h>
 #include "ieee80211_i.h"
 #include "debugfs_key.h"
@@ -48,8 +49,8 @@ static const u8 *get_mac_for_key(struct ieee80211_key *key)
         * address to indicate a transmit-only key.
         */
        if (key->conf.alg != ALG_WEP &&
-           (key->sdata->type == IEEE80211_IF_TYPE_AP ||
-            key->sdata->type == IEEE80211_IF_TYPE_VLAN))
+           (key->sdata->vif.type == IEEE80211_IF_TYPE_AP ||
+            key->sdata->vif.type == IEEE80211_IF_TYPE_VLAN))
                addr = zero_addr;
 
        if (key->sta)
@@ -62,6 +63,7 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
 {
        const u8 *addr;
        int ret;
+       DECLARE_MAC_BUF(mac);
 
        if (!key->local->ops->set_key)
                return;
@@ -72,22 +74,21 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
                                       key->sdata->dev->dev_addr, addr,
                                       &key->conf);
 
-       WARN_ON(!ret && (key->conf.hw_key_idx == HW_KEY_IDX_INVALID));
-
        if (!ret)
                key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE;
 
        if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP)
                printk(KERN_ERR "mac80211-%s: failed to set key "
-                      "(%d, " MAC_FMT ") to hardware (%d)\n",
+                      "(%d, %s) to hardware (%d)\n",
                       wiphy_name(key->local->hw.wiphy),
-                      key->conf.keyidx, MAC_ARG(addr), ret);
+                      key->conf.keyidx, print_mac(mac, addr), ret);
 }
 
 static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
 {
        const u8 *addr;
        int ret;
+       DECLARE_MAC_BUF(mac);
 
        if (!key->local->ops->set_key)
                return;
@@ -103,24 +104,23 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key)
 
        if (ret)
                printk(KERN_ERR "mac80211-%s: failed to remove key "
-                      "(%d, " MAC_FMT ") from hardware (%d)\n",
+                      "(%d, %s) from hardware (%d)\n",
                       wiphy_name(key->local->hw.wiphy),
-                      key->conf.keyidx, MAC_ARG(addr), ret);
+                      key->conf.keyidx, print_mac(mac, addr), ret);
 
        key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE;
-       key->conf.hw_key_idx = HW_KEY_IDX_INVALID;
 }
 
 struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
                                          struct sta_info *sta,
-                                         ieee80211_key_alg alg,
+                                         enum ieee80211_key_alg alg,
                                          int idx,
                                          size_t key_len,
                                          const u8 *key_data)
 {
        struct ieee80211_key *key;
 
-       BUG_ON(alg == ALG_NONE);
+       BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS);
 
        key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL);
        if (!key)
@@ -130,7 +130,6 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
         * Default to software encryption; we'll later upload the
         * key to the hardware if possible.
         */
-       key->conf.hw_key_idx = HW_KEY_IDX_INVALID;
        key->conf.flags = 0;
        key->flags = 0;
 
@@ -157,9 +156,15 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
 
        ieee80211_debugfs_key_add(key->local, key);
 
+       /* remove key first */
+       if (sta)
+               ieee80211_key_free(sta->key);
+       else
+               ieee80211_key_free(sdata->keys[idx]);
+
        if (sta) {
                ieee80211_debugfs_key_sta_link(key, sta);
-               sta->key = key;
+
                /*
                 * some hardware cannot handle TKIP with QoS, so
                 * we indicate whether QoS could be in use.
@@ -167,7 +172,7 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
                if (sta->flags & WLAN_STA_WME)
                        key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
        } else {
-               if (sdata->type == IEEE80211_IF_TYPE_STA) {
+               if (sdata->vif.type == IEEE80211_IF_TYPE_STA) {
                        struct sta_info *ap;
 
                        /* same here, the AP could be using QoS */
@@ -179,21 +184,19 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata,
                                sta_info_put(ap);
                        }
                }
-
-               if (idx >= 0 && idx < NUM_DEFAULT_KEYS) {
-                       if (!sdata->keys[idx])
-                               sdata->keys[idx] = key;
-                       else
-                               WARN_ON(1);
-               } else
-                       WARN_ON(1);
        }
 
-       list_add(&key->list, &sdata->key_list);
-
+       /* enable hwaccel if appropriate */
        if (netif_running(key->sdata->dev))
                ieee80211_key_enable_hw_accel(key);
 
+       if (sta)
+               rcu_assign_pointer(sta->key, key);
+       else
+               rcu_assign_pointer(sdata->keys[idx], key);
+
+       list_add(&key->list, &sdata->key_list);
+
        return key;
 }
 
@@ -202,20 +205,25 @@ void ieee80211_key_free(struct ieee80211_key *key)
        if (!key)
                return;
 
-       ieee80211_key_disable_hw_accel(key);
-
        if (key->sta) {
-               key->sta->key = NULL;
+               rcu_assign_pointer(key->sta->key, NULL);
        } else {
                if (key->sdata->default_key == key)
                        ieee80211_set_default_key(key->sdata, -1);
                if (key->conf.keyidx >= 0 &&
                    key->conf.keyidx < NUM_DEFAULT_KEYS)
-                       key->sdata->keys[key->conf.keyidx] = NULL;
+                       rcu_assign_pointer(key->sdata->keys[key->conf.keyidx],
+                                          NULL);
                else
                        WARN_ON(1);
        }
 
+       /* wait for all key users to complete */
+       synchronize_rcu();
+
+       /* remove from hwaccel if appropriate */
+       ieee80211_key_disable_hw_accel(key);
+
        if (key->conf.alg == ALG_CCMP)
                ieee80211_aes_key_free(key->u.ccmp.tfm);
        ieee80211_debugfs_key_remove(key);
@@ -235,14 +243,10 @@ void ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, int idx)
        if (sdata->default_key != key) {
                ieee80211_debugfs_key_remove_default(sdata);
 
-               sdata->default_key = key;
+               rcu_assign_pointer(sdata->default_key, key);
 
                if (sdata->default_key)
                        ieee80211_debugfs_key_add_default(sdata);
-
-               if (sdata->local->ops->set_key_idx)
-                       sdata->local->ops->set_key_idx(
-                               local_to_hw(sdata->local), idx);
        }
 }