]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blobdiff - net/mac80211/key.c
mac80211: batch key free synchronize_net()
[mirror_ubuntu-jammy-kernel.git] / net / mac80211 / key.c
index ef252eb58c36b0740e9ecbb6f9b93aae66e6b054..67059b88fea5f28619c97ad2ca8c150840045055 100644 (file)
@@ -248,11 +248,11 @@ void ieee80211_set_default_mgmt_key(struct ieee80211_sub_if_data *sdata,
 }
 
 
-static void __ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
-                                   struct sta_info *sta,
-                                   bool pairwise,
-                                   struct ieee80211_key *old,
-                                   struct ieee80211_key *new)
+static void ieee80211_key_replace(struct ieee80211_sub_if_data *sdata,
+                                 struct sta_info *sta,
+                                 bool pairwise,
+                                 struct ieee80211_key *old,
+                                 struct ieee80211_key *new)
 {
        int idx;
        bool defunikey, defmultikey, defmgmtkey;
@@ -397,7 +397,41 @@ 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_free_common(struct ieee80211_key *key)
+{
+       if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP)
+               ieee80211_aes_key_free(key->u.ccmp.tfm);
+       if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
+               ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
+       kfree(key);
+}
+
+static void __ieee80211_key_destroy(struct ieee80211_key *key,
+                                   bool delay_tailroom)
+{
+       if (key->local)
+               ieee80211_key_disable_hw_accel(key);
+
+       if (key->local) {
+               struct ieee80211_sub_if_data *sdata = key->sdata;
+
+               ieee80211_debugfs_key_remove(key);
+
+               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--;
+               }
+       }
+
+       ieee80211_key_free_common(key);
+}
+
+static void ieee80211_key_destroy(struct ieee80211_key *key,
+                                 bool delay_tailroom)
 {
        if (!key)
                return;
@@ -408,19 +442,13 @@ static void __ieee80211_key_destroy(struct ieee80211_key *key)
         */
        synchronize_net();
 
-       if (key->local)
-               ieee80211_key_disable_hw_accel(key);
-
-       if (key->conf.cipher == WLAN_CIPHER_SUITE_CCMP)
-               ieee80211_aes_key_free(key->u.ccmp.tfm);
-       if (key->conf.cipher == WLAN_CIPHER_SUITE_AES_CMAC)
-               ieee80211_aes_cmac_key_free(key->u.aes_cmac.tfm);
-       if (key->local) {
-               ieee80211_debugfs_key_remove(key);
-               key->sdata->crypto_tx_tailroom_needed_cnt--;
-       }
+       __ieee80211_key_destroy(key, delay_tailroom);
+}
 
-       kfree(key);
+void ieee80211_key_free_unused(struct ieee80211_key *key)
+{
+       WARN_ON(key->sdata || key->local);
+       ieee80211_key_free_common(key);
 }
 
 int ieee80211_key_link(struct ieee80211_key *key,
@@ -440,32 +468,6 @@ int ieee80211_key_link(struct ieee80211_key *key,
        key->sdata = sdata;
        key->sta = sta;
 
-       if (sta) {
-               /*
-                * some hardware cannot handle TKIP with QoS, so
-                * we indicate whether QoS could be in use.
-                */
-               if (test_sta_flag(sta, WLAN_STA_WME))
-                       key->conf.flags |= IEEE80211_KEY_FLAG_WMM_STA;
-       } else {
-               if (sdata->vif.type == NL80211_IFTYPE_STATION) {
-                       struct sta_info *ap;
-
-                       /*
-                        * We're getting a sta pointer in, so must be under
-                        * appropriate locking for sta_info_get().
-                        */
-
-                       /* same here, the AP could be using QoS */
-                       ap = sta_info_get(key->sdata, key->sdata->u.mgd.bssid);
-                       if (ap) {
-                               if (test_sta_flag(ap, WLAN_STA_WME))
-                                       key->conf.flags |=
-                                               IEEE80211_KEY_FLAG_WMM_STA;
-                       }
-               }
-       }
-
        mutex_lock(&sdata->local->key_mtx);
 
        if (sta && pairwise)
@@ -477,19 +479,22 @@ 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_replace(sdata, sta, pairwise, old_key, key);
+       ieee80211_key_destroy(old_key, true);
 
        ieee80211_debugfs_key_add(key);
 
        ret = ieee80211_key_enable_hw_accel(key);
 
+       if (ret)
+               ieee80211_key_free(key, true);
+
        mutex_unlock(&sdata->local->key_mtx);
 
        return ret;
 }
 
-void __ieee80211_key_free(struct ieee80211_key *key)
+void ieee80211_key_free(struct ieee80211_key *key, bool delay_tailroom)
 {
        if (!key)
                return;
@@ -498,18 +503,10 @@ void __ieee80211_key_free(struct ieee80211_key *key)
         * Replace key with nothingness if it was ever used.
         */
        if (key->sdata)
-               __ieee80211_key_replace(key->sdata, key->sta,
+               ieee80211_key_replace(key->sdata, key->sta,
                                key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
                                key, NULL);
-       __ieee80211_key_destroy(key);
-}
-
-void ieee80211_key_free(struct ieee80211_local *local,
-                       struct ieee80211_key *key)
-{
-       mutex_lock(&local->key_mtx);
-       __ieee80211_key_free(key);
-       mutex_unlock(&local->key_mtx);
+       ieee80211_key_destroy(key, delay_tailroom);
 }
 
 void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
@@ -566,36 +563,109 @@ void ieee80211_iter_keys(struct ieee80211_hw *hw,
 }
 EXPORT_SYMBOL(ieee80211_iter_keys);
 
-void ieee80211_disable_keys(struct ieee80211_sub_if_data *sdata)
+void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
 {
-       struct ieee80211_key *key;
+       struct ieee80211_key *key, *tmp;
+       LIST_HEAD(keys);
 
-       ASSERT_RTNL();
+       cancel_delayed_work_sync(&sdata->dec_tailroom_needed_wk);
 
        mutex_lock(&sdata->local->key_mtx);
 
-       list_for_each_entry(key, &sdata->key_list, list)
-               ieee80211_key_disable_hw_accel(key);
+       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_replace(key->sdata, key->sta,
+                               key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+                               key, NULL);
+               list_add_tail(&key->list, &keys);
+       }
+
+       ieee80211_debugfs_key_update_default(sdata);
+
+       if (!list_empty(&keys)) {
+               synchronize_net();
+               list_for_each_entry_safe(key, tmp, &keys, list)
+                       __ieee80211_key_destroy(key, false);
+       }
+
+       WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt ||
+                    sdata->crypto_tx_tailroom_pending_dec);
 
        mutex_unlock(&sdata->local->key_mtx);
 }
 
-void ieee80211_free_keys(struct ieee80211_sub_if_data *sdata)
+void ieee80211_free_sta_keys(struct ieee80211_local *local,
+                            struct sta_info *sta)
 {
        struct ieee80211_key *key, *tmp;
+       LIST_HEAD(keys);
+       int i;
 
-       mutex_lock(&sdata->local->key_mtx);
+       mutex_lock(&local->key_mtx);
+       for (i = 0; i < NUM_DEFAULT_KEYS; i++) {
+               key = key_mtx_dereference(local, sta->gtk[i]);
+               if (!key)
+                       continue;
+               ieee80211_key_replace(key->sdata, key->sta,
+                               key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+                               key, NULL);
+               list_add(&key->list, &keys);
+       }
 
-       ieee80211_debugfs_key_remove_mgmt_default(sdata);
+       key = key_mtx_dereference(local, sta->ptk);
+       if (key) {
+               ieee80211_key_replace(key->sdata, key->sta,
+                               key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE,
+                               key, NULL);
+               list_add(&key->list, &keys);
+       }
 
-       list_for_each_entry_safe(key, tmp, &sdata->key_list, list)
-               __ieee80211_key_free(key);
+       /*
+        * NB: the station code relies on this being
+        * done even if there aren't any keys
+        */
+       synchronize_net();
 
-       ieee80211_debugfs_key_update_default(sdata);
+       list_for_each_entry_safe(key, tmp, &keys, list)
+               __ieee80211_key_destroy(key, true);
 
-       mutex_unlock(&sdata->local->key_mtx);
+       mutex_unlock(&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)