diff options
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r-- | net/mac80211/key.c | 43 |
1 files changed, 27 insertions, 16 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 178f00cf61b..19e77f626d8 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -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" @@ -120,6 +121,7 @@ struct ieee80211_key *ieee80211_key_alloc(struct ieee80211_sub_if_data *sdata, { struct ieee80211_key *key; + BUG_ON(idx < 0 || idx >= NUM_DEFAULT_KEYS); BUG_ON(alg == ALG_NONE); key = kzalloc(sizeof(struct ieee80211_key) + key_len, GFP_KERNEL); @@ -157,9 +159,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. @@ -179,21 +187,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 +208,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,7 +246,7 @@ 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); |