iwlwifi: mvm: refactor d3 key update functions
authorEliad Peller <eliad@wizery.com>
Thu, 12 Nov 2015 08:49:38 +0000 (10:49 +0200)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Tue, 1 Dec 2015 19:17:53 +0000 (21:17 +0200)
We need to reuse the key update logic for d0i3
as well.

Add some parameters to deal with the constraints
implied by the d0i3 flow (specifically, support
non-SYNC commands, and don't take mutexes that
might deadlock).

Change some commands to be ASYNC, in order
to simplify locking a bit.

Signed-off-by: Eliad Peller <eliadx.peller@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/intel/iwlwifi/mvm/d3.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h

index 7bb5496405813ac2554beee15dc0a1fa9fc8eb75..478cf82f6331d95efbd3f4b557e39e76c26fe6b7 100644 (file)
@@ -136,7 +136,7 @@ static void iwl_mvm_convert_p1k(u16 *p1k, __le16 *out)
 struct wowlan_key_data {
        struct iwl_wowlan_rsc_tsc_params_cmd *rsc_tsc;
        struct iwl_wowlan_tkip_params_cmd *tkip;
-       bool error, use_rsc_tsc, use_tkip;
+       bool error, use_rsc_tsc, use_tkip, configure_keys;
        int wep_key_idx;
 };
 
@@ -158,8 +158,6 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
        u16 p1k[IWL_P1K_SIZE];
        int ret, i;
 
-       mutex_lock(&mvm->mutex);
-
        switch (key->cipher) {
        case WLAN_CIPHER_SUITE_WEP40:
        case WLAN_CIPHER_SUITE_WEP104: { /* hack it for now */
@@ -195,20 +193,25 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
                        wkc.wep_key.key_offset = data->wep_key_idx;
                }
 
-               ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0, sizeof(wkc), &wkc);
-               data->error = ret != 0;
-
-               mvm->ptk_ivlen = key->iv_len;
-               mvm->ptk_icvlen = key->icv_len;
-               mvm->gtk_ivlen = key->iv_len;
-               mvm->gtk_icvlen = key->icv_len;
+               if (data->configure_keys) {
+                       mutex_lock(&mvm->mutex);
+                       ret = iwl_mvm_send_cmd_pdu(mvm, WEP_KEY, 0,
+                                                  sizeof(wkc), &wkc);
+                       data->error = ret != 0;
+
+                       mvm->ptk_ivlen = key->iv_len;
+                       mvm->ptk_icvlen = key->icv_len;
+                       mvm->gtk_ivlen = key->iv_len;
+                       mvm->gtk_icvlen = key->icv_len;
+                       mutex_unlock(&mvm->mutex);
+               }
 
                /* don't upload key again */
-               goto out_unlock;
+               return;
        }
        default:
                data->error = true;
-               goto out_unlock;
+               return;
        case WLAN_CIPHER_SUITE_AES_CMAC:
                /*
                 * Ignore CMAC keys -- the WoWLAN firmware doesn't support them
@@ -217,7 +220,7 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
                 * IGTK for anything. This means we could spuriously wake up or
                 * be deauthenticated, but that was considered acceptable.
                 */
-               goto out_unlock;
+               return;
        case WLAN_CIPHER_SUITE_TKIP:
                if (sta) {
                        tkip_sc = data->rsc_tsc->all_tsc_rsc.tkip.unicast_rsc;
@@ -304,29 +307,30 @@ static void iwl_mvm_wowlan_program_keys(struct ieee80211_hw *hw,
                break;
        }
 
-       /*
-        * The D3 firmware hardcodes the key offset 0 as the key it uses
-        * to transmit packets to the AP, i.e. the PTK.
-        */
-       if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
-               mvm->ptk_ivlen = key->iv_len;
-               mvm->ptk_icvlen = key->icv_len;
-               ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 0);
-       } else {
+       if (data->configure_keys) {
+               mutex_lock(&mvm->mutex);
                /*
-                * firmware only supports TSC/RSC for a single key,
-                * so if there are multiple keep overwriting them
-                * with new ones -- this relies on mac80211 doing
-                * list_add_tail().
+                * The D3 firmware hardcodes the key offset 0 as the key it
+                * uses to transmit packets to the AP, i.e. the PTK.
                 */
-               mvm->gtk_ivlen = key->iv_len;
-               mvm->gtk_icvlen = key->icv_len;
-               ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 1);
+               if (key->flags & IEEE80211_KEY_FLAG_PAIRWISE) {
+                       mvm->ptk_ivlen = key->iv_len;
+                       mvm->ptk_icvlen = key->icv_len;
+                       ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 0);
+               } else {
+                       /*
+                        * firmware only supports TSC/RSC for a single key,
+                        * so if there are multiple keep overwriting them
+                        * with new ones -- this relies on mac80211 doing
+                        * list_add_tail().
+                        */
+                       mvm->gtk_ivlen = key->iv_len;
+                       mvm->gtk_icvlen = key->icv_len;
+                       ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, 1);
+               }
+               mutex_unlock(&mvm->mutex);
+               data->error = ret != 0;
        }
-
-       data->error = ret != 0;
-out_unlock:
-       mutex_unlock(&mvm->mutex);
 }
 
 static int iwl_mvm_send_patterns(struct iwl_mvm *mvm,
@@ -842,20 +846,89 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
        return 0;
 }
 
-static int
-iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
-                     struct cfg80211_wowlan *wowlan,
-                     struct iwl_wowlan_config_cmd *wowlan_config_cmd,
-                     struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
-                     struct ieee80211_sta *ap_sta)
+int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif,
+                                    bool configure_keys,
+                                    u32 cmd_flags)
 {
        struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {};
        struct iwl_wowlan_tkip_params_cmd tkip_cmd = {};
        struct wowlan_key_data key_data = {
+               .configure_keys = configure_keys,
                .use_rsc_tsc = false,
                .tkip = &tkip_cmd,
                .use_tkip = false,
        };
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int ret;
+
+       key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
+       if (!key_data.rsc_tsc)
+               return -ENOMEM;
+
+       /*
+        * Note that currently we don't propagate cmd_flags
+        * to the iterator. In case of key_data.configure_keys,
+        * all the configured commands are SYNC, and
+        * iwl_mvm_wowlan_program_keys() will take care of
+        * locking/unlocking mvm->mutex.
+        */
+       ieee80211_iter_keys(mvm->hw, vif,
+                           iwl_mvm_wowlan_program_keys,
+                           &key_data);
+
+       if (key_data.error) {
+               ret = -EIO;
+               goto out;
+       }
+
+       if (key_data.use_rsc_tsc) {
+               ret = iwl_mvm_send_cmd_pdu(mvm,
+                                          WOWLAN_TSC_RSC_PARAM, cmd_flags,
+                                          sizeof(*key_data.rsc_tsc),
+                                          key_data.rsc_tsc);
+               if (ret)
+                       goto out;
+       }
+
+       if (key_data.use_tkip) {
+               ret = iwl_mvm_send_cmd_pdu(mvm,
+                                          WOWLAN_TKIP_PARAM,
+                                          cmd_flags, sizeof(tkip_cmd),
+                                          &tkip_cmd);
+               if (ret)
+                       goto out;
+       }
+
+       if (mvmvif->rekey_data.valid) {
+               memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
+               memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck,
+                      NL80211_KCK_LEN);
+               kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
+               memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek,
+                      NL80211_KEK_LEN);
+               kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
+               kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
+
+               ret = iwl_mvm_send_cmd_pdu(mvm,
+                                          WOWLAN_KEK_KCK_MATERIAL, cmd_flags,
+                                          sizeof(kek_kck_cmd),
+                                          &kek_kck_cmd);
+               if (ret)
+                       goto out;
+       }
+out:
+       kfree(key_data.rsc_tsc);
+       return ret;
+}
+
+static int
+iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
+                     struct cfg80211_wowlan *wowlan,
+                     struct iwl_wowlan_config_cmd *wowlan_config_cmd,
+                     struct ieee80211_vif *vif, struct iwl_mvm_vif *mvmvif,
+                     struct ieee80211_sta *ap_sta)
+{
        int ret;
 
        ret = iwl_mvm_switch_to_d3(mvm);
@@ -866,10 +939,6 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
        if (ret)
                return ret;
 
-       key_data.rsc_tsc = kzalloc(sizeof(*key_data.rsc_tsc), GFP_KERNEL);
-       if (!key_data.rsc_tsc)
-               return -ENOMEM;
-
        if (!iwlwifi_mod_params.sw_crypto) {
                /*
                 * This needs to be unlocked due to lock ordering
@@ -877,74 +946,25 @@ iwl_mvm_wowlan_config(struct iwl_mvm *mvm,
                 * that isn't really a problem though.
                 */
                mutex_unlock(&mvm->mutex);
-               ieee80211_iter_keys(mvm->hw, vif,
-                                   iwl_mvm_wowlan_program_keys,
-                                   &key_data);
+               iwl_mvm_wowlan_config_key_params(mvm, vif, true, CMD_ASYNC);
                mutex_lock(&mvm->mutex);
-               if (key_data.error) {
-                       ret = -EIO;
-                       goto out;
-               }
-
-               if (key_data.use_rsc_tsc) {
-                       struct iwl_host_cmd rsc_tsc_cmd = {
-                               .id = WOWLAN_TSC_RSC_PARAM,
-                               .data[0] = key_data.rsc_tsc,
-                               .dataflags[0] = IWL_HCMD_DFL_NOCOPY,
-                               .len[0] = sizeof(*key_data.rsc_tsc),
-                       };
-
-                       ret = iwl_mvm_send_cmd(mvm, &rsc_tsc_cmd);
-                       if (ret)
-                               goto out;
-               }
-
-               if (key_data.use_tkip) {
-                       ret = iwl_mvm_send_cmd_pdu(mvm,
-                                                  WOWLAN_TKIP_PARAM,
-                                                  0, sizeof(tkip_cmd),
-                                                  &tkip_cmd);
-                       if (ret)
-                               goto out;
-               }
-
-               if (mvmvif->rekey_data.valid) {
-                       memset(&kek_kck_cmd, 0, sizeof(kek_kck_cmd));
-                       memcpy(kek_kck_cmd.kck, mvmvif->rekey_data.kck,
-                              NL80211_KCK_LEN);
-                       kek_kck_cmd.kck_len = cpu_to_le16(NL80211_KCK_LEN);
-                       memcpy(kek_kck_cmd.kek, mvmvif->rekey_data.kek,
-                              NL80211_KEK_LEN);
-                       kek_kck_cmd.kek_len = cpu_to_le16(NL80211_KEK_LEN);
-                       kek_kck_cmd.replay_ctr = mvmvif->rekey_data.replay_ctr;
-
-                       ret = iwl_mvm_send_cmd_pdu(mvm,
-                                                  WOWLAN_KEK_KCK_MATERIAL, 0,
-                                                  sizeof(kek_kck_cmd),
-                                                  &kek_kck_cmd);
-                       if (ret)
-                               goto out;
-               }
        }
 
        ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, 0,
                                   sizeof(*wowlan_config_cmd),
                                   wowlan_config_cmd);
        if (ret)
-               goto out;
+               return ret;
 
        ret = iwl_mvm_send_patterns(mvm, wowlan);
        if (ret)
-               goto out;
+               return ret;
 
        ret = iwl_mvm_send_proto_offload(mvm, vif, false, 0);
        if (ret)
-               goto out;
+               return ret;
 
        ret = iwl_mvm_send_remote_wake_cfg(mvm, vif, wowlan->tcp);
-
-out:
-       kfree(key_data.rsc_tsc);
        return ret;
 }
 
index 5692856d3e4253c87aea2ab21dba9a9d83150f41..7dc3af6e06f73b8ce196f934f9ac5b64c6cd2029 100644 (file)
@@ -1249,6 +1249,10 @@ static inline void iwl_mvm_leds_exit(struct iwl_mvm *mvm)
 /* D3 (WoWLAN, NetDetect) */
 int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan);
 int iwl_mvm_resume(struct ieee80211_hw *hw);
+int iwl_mvm_wowlan_config_key_params(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif,
+                                    bool configure_keys,
+                                    u32 cmd_flags);
 void iwl_mvm_set_wakeup(struct ieee80211_hw *hw, bool enabled);
 void iwl_mvm_set_rekey_data(struct ieee80211_hw *hw,
                            struct ieee80211_vif *vif,