summaryrefslogtreecommitdiff
path: root/net/mac80211/key.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2013-02-23 03:59:03 +0400
committerJohannes Berg <johannes.berg@intel.com>2013-03-06 19:36:00 +0400
commit8d1f7ecd2af55c0c82ffd2bff0ef0b26f16ea69f (patch)
tree51e4fe05518ea5cb51f938c4e9aa756115c7c949 /net/mac80211/key.c
parenta87121051ce80831a302c67286119013104f7a5a (diff)
downloadlinux-8d1f7ecd2af55c0c82ffd2bff0ef0b26f16ea69f.tar.xz
mac80211: defer tailroom counter manipulation when roaming
During roaming, the crypto_tx_tailroom_needed_cnt counter will often take values 2,1,0,1,2 because first keys are removed and then new keys are added. This is inefficient because during the 0->1 transition, synchronize_net must be called to avoid packet races, although typically no packets would be flowing during that time. To avoid that, defer the decrement (2->1, 1->0) when keys are removed (by half a second). This means the counter will really have the values 2,2,2,3,4 ... 2, thus never reaching 0 and having to do the 0->1 transition. Note that this patch entirely disregards the drivers for which this optimisation was done to start with, for them the key removal itself will be expensive because it has to synchronize_net() after the counter is incremented to remove the key from HW crypto. For them the sequence will look like this: 0,1,0,1,0,1,0,1,0 (*) which is clearly a lot more inefficient. This could be addressed separately, during key removal the 0->1->0 sequence isn't necessary. (*) it starts at 0 because HW crypto is on, then goes to 1 when HW crypto is disabled for a key, then back to 0 because the key is deleted; this happens for both keys in the example. When new keys are added, it goes to 1 first because they're added in software; when a key is moved to hardware it goes back to 0 Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/key.c')
-rw-r--r--net/mac80211/key.c63
1 files changed, 56 insertions, 7 deletions
diff --git a/net/mac80211/key.c b/net/mac80211/key.c
index 6eb4888a70ed..99e9f6ae6a54 100644
--- a/net/mac80211/key.c
+++ b/net/mac80211/key.c
@@ -397,7 +397,8 @@ struct ieee80211_key *ieee80211_key_alloc(u32 cipher, int idx, size_t key_len,
return key;
}
-static void __ieee80211_key_destroy(struct ieee80211_key *key)
+static void __ieee80211_key_destroy(struct ieee80211_key *key,
+ bool delay_tailroom)
{
if (!key)
return;
@@ -416,8 +417,18 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
if (key->local) {
+ struct ieee80211_sub_if_data *sdata = key->sdata;
+
ieee80211_debugfs_key_remove(key);
- key->sdata->crypto_tx_tailroom_needed_cnt--;
+
+ if (delay_tailroom) {
+ /* see ieee80211_delayed_tailroom_dec */
+ sdata->crypto_tx_tailroom_pending_dec++;
+ schedule_delayed_work(&sdata->dec_tailroom_needed_wk,
+ HZ/2);
+ } else {
+ sdata->crypto_tx_tailroom_needed_cnt--;
+ }
}
kfree(key);
@@ -452,7 +463,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
increment_tailroom_need_count(sdata);
__ieee80211_key_replace(sdata, sta, pairwise, old_key, key);
- __ieee80211_key_destroy(old_key);
+ __ieee80211_key_destroy(old_key, true);
ieee80211_debugfs_key_add(key);
@@ -463,7 +474,7 @@ int ieee80211_key_link(struct ieee80211_key *key,
return ret;
}
-void __ieee80211_key_free(struct ieee80211_key *key)
+void __ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom)
{
if (!key)
return;
@@ -475,14 +486,14 @@ void __ieee80211_key_free(struct ieee80211_key *key)
__ieee80211_key_replace(key->sdata, key->sta,
key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
key, NULL);
- __ieee80211_key_destroy(key);
+ __ieee80211_key_destroy(key, delay_tailroom);
}
void ieee80211_key_free(struct ieee80211_local *local,
struct ieee80211_key *key)
{
mutex_lock(&local->key_mtx);
- __ieee80211_key_free(key);
+ __ieee80211_key_free(key, true);
mutex_unlock(&local->key_mtx);
}
@@ -544,18 +555,56 @@ void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
{
struct ieee80211_key *key, *tmp;
+ cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk);
+
mutex_lock(&sdata->local->key_mtx);
+ sdata->crypto_tx_tailroom_needed_cnt -=
+ sdata->crypto_tx_tailroom_pending_dec;
+ sdata->crypto_tx_tailroom_pending_dec = 0;
+
ieee80211_debugfs_key_remove_mgmt_default(sdata);
list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
- __ieee80211_key_free(key);
+ __ieee80211_key_free(key, false);
ieee80211_debugfs_key_update_default(sdata);
+ WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt ||
+ sdata->crypto_tx_tailroom_pending_dec);
+
mutex_unlock(&sdata->local->key_mtx);
}
+void ieee80211_delayed_tailroom_dec(struct work_struct *wk)
+{
+ struct ieee80211_sub_if_data *sdata;
+
+ sdata = container_of(wk, struct ieee80211_sub_if_data,
+ dec_tailroom_needed_wk.work);
+
+ /*
+ * The reason for the delayed tailroom needed decrementing is to
+ * make roaming faster: during roaming, all keys are first deleted
+ * and then new keys are installed. The first new key causes the
+ * crypto_tx_tailroom_needed_cnt to go from 0 to 1, which invokes
+ * the cost of synchronize_net() (which can be slow). Avoid this
+ * by deferring the crypto_tx_tailroom_needed_cnt decrementing on
+ * key removal for a while, so if we roam the value is larger than
+ * zero and no 0->1 transition happens.
+ *
+ * The cost is that if the AP switching was from an AP with keys
+ * to one without, we still allocate tailroom while it would no
+ * longer be needed. However, in the typical (fast) roaming case
+ * within an ESS this usually won't happen.
+ */
+
+ mutex_lock(&sdata->local->key_mtx);
+ sdata->crypto_tx_tailroom_needed_cnt -=
+ sdata->crypto_tx_tailroom_pending_dec;
+ sdata->crypto_tx_tailroom_pending_dec = 0;
+ mutex_unlock(&sdata->local->key_mtx);
+}
void ieee80211_gtk_rekey_notify(struct ieee80211_vif *vif, const u8 *bssid,
const u8 *replay_ctr, gfp_t gfp)