Merge remote-tracking branch 'net-next/master' into mac80211-next
authorJohannes Berg <johannes.berg@intel.com>
Wed, 10 Jun 2015 10:44:58 +0000 (12:44 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 10 Jun 2015 10:45:09 +0000 (12:45 +0200)
Merge back net-next to get wireless driver changes (from Kalle)
to be able to create the API change across all trees properly.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1  2 
drivers/net/wireless/iwlwifi/mvm/mac80211.c
net/mac80211/cfg.c
net/mac80211/key.c
net/mac80211/key.h

@@@ -80,7 -80,6 +80,6 @@@
  #include "sta.h"
  #include "time-event.h"
  #include "iwl-eeprom-parse.h"
- #include "fw-api-scan.h"
  #include "iwl-phy-db.h"
  #include "testmode.h"
  #include "iwl-fw-error-dump.h"
@@@ -319,7 -318,7 +318,7 @@@ struct ieee80211_regdomain *iwl_mvm_get
        resp = iwl_mvm_update_mcc(mvm, alpha2, src_id);
        if (IS_ERR_OR_NULL(resp)) {
                IWL_DEBUG_LAR(mvm, "Could not get update from FW %d\n",
-                             PTR_RET(resp));
+                             PTR_ERR_OR_ZERO(resp));
                goto out;
        }
  
        kfree(resp);
        if (IS_ERR_OR_NULL(regd)) {
                IWL_DEBUG_LAR(mvm, "Could not get parse update from FW %d\n",
-                             PTR_RET(regd));
+                             PTR_ERR_OR_ZERO(regd));
                goto out;
        }
  
@@@ -416,6 -415,12 +415,12 @@@ int iwl_mvm_mac_setup_register(struct i
  {
        struct ieee80211_hw *hw = mvm->hw;
        int num_mac, ret, i;
+       static const u32 mvm_ciphers[] = {
+               WLAN_CIPHER_SUITE_WEP40,
+               WLAN_CIPHER_SUITE_WEP104,
+               WLAN_CIPHER_SUITE_TKIP,
+               WLAN_CIPHER_SUITE_CCMP,
+       };
  
        /* Tell mac80211 our characteristics */
        hw->flags = IEEE80211_HW_SIGNAL_DBM |
                    IEEE80211_HW_TIMING_BEACON_ONLY |
                    IEEE80211_HW_CONNECTION_MONITOR |
                    IEEE80211_HW_CHANCTX_STA_CSA |
+                   IEEE80211_HW_SUPPORT_FAST_XMIT |
                    IEEE80211_HW_SUPPORTS_CLONED_SKBS;
  
        hw->queues = mvm->first_agg_queue;
        hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES;
        hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP;
  
+       BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 2);
+       memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers));
+       hw->wiphy->n_cipher_suites = ARRAY_SIZE(mvm_ciphers);
+       hw->wiphy->cipher_suites = mvm->ciphers;
        /*
         * Enable 11w if advertised by firmware and software crypto
         * is not enabled (as the firmware will interpret some mgmt
         * packets, so enabling it with software crypto isn't safe)
         */
        if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_MFP &&
-           !iwlwifi_mod_params.sw_crypto)
+           !iwlwifi_mod_params.sw_crypto) {
                hw->flags |= IEEE80211_HW_MFP_CAPABLE;
+               mvm->ciphers[hw->wiphy->n_cipher_suites] =
+                       WLAN_CIPHER_SUITE_AES_CMAC;
+               hw->wiphy->n_cipher_suites++;
+       }
+       /* currently FW API supports only one optional cipher scheme */
+       if (mvm->fw->cs[0].cipher) {
+               mvm->hw->n_cipher_schemes = 1;
+               mvm->hw->cipher_schemes = &mvm->fw->cs[0];
+               mvm->ciphers[hw->wiphy->n_cipher_suites] =
+                       mvm->fw->cs[0].cipher;
+               hw->wiphy->n_cipher_suites++;
+       }
  
 -      hw->flags |= IEEE80211_SINGLE_HW_SCAN_ON_ALL_BANDS;
 +      hw->flags |= IEEE80211_HW_SINGLE_SCAN_ON_ALL_BANDS;
        hw->wiphy->features |=
                NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR |
-               NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR;
+               NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR |
+               NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
  
        hw->sta_data_size = sizeof(struct iwl_mvm_sta);
        hw->vif_data_size = sizeof(struct iwl_mvm_vif);
  
        iwl_mvm_reset_phy_ctxts(mvm);
  
-       hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm, false);
+       hw->wiphy->max_scan_ie_len = iwl_mvm_max_scan_ie_len(mvm);
  
        hw->wiphy->max_scan_ssids = PROBE_OPTION_MAX;
  
+       BUILD_BUG_ON(IWL_MVM_SCAN_STOPPING_MASK & IWL_MVM_SCAN_MASK);
+       BUILD_BUG_ON(IWL_MVM_MAX_UMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK) ||
+                    IWL_MVM_MAX_LMAC_SCANS > HWEIGHT32(IWL_MVM_SCAN_MASK));
+       if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN))
+               mvm->max_scans = IWL_MVM_MAX_UMAC_SCANS;
+       else
+               mvm->max_scans = IWL_MVM_MAX_LMAC_SCANS;
        if (mvm->nvm_data->bands[IEEE80211_BAND_2GHZ].n_channels)
                hw->wiphy->bands[IEEE80211_BAND_2GHZ] =
                        &mvm->nvm_data->bands[IEEE80211_BAND_2GHZ];
                hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
                        &mvm->nvm_data->bands[IEEE80211_BAND_5GHZ];
  
-               if ((mvm->fw->ucode_capa.capa[0] &
-                    IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
-                   (mvm->fw->ucode_capa.api[0] &
-                    IWL_UCODE_TLV_API_LQ_SS_PARAMS))
+               if (fw_has_capa(&mvm->fw->ucode_capa,
+                               IWL_UCODE_TLV_CAPA_BEAMFORMER) &&
+                   fw_has_api(&mvm->fw->ucode_capa,
+                              IWL_UCODE_TLV_API_LQ_SS_PARAMS))
                        hw->wiphy->bands[IEEE80211_BAND_5GHZ]->vht_cap.cap |=
                                IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
        }
        else
                hw->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
  
-       if (IWL_UCODE_API(mvm->fw->ucode_ver) >= 10) {
-               hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
-               hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
-               hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
-               /* we create the 802.11 header and zero length SSID IE. */
-               hw->wiphy->max_sched_scan_ie_len =
-                       SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
-       }
+       hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN;
+       hw->wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX;
+       hw->wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES;
+       /* we create the 802.11 header and zero length SSID IE. */
+       hw->wiphy->max_sched_scan_ie_len =
+               SCAN_OFFLOAD_PROBE_REQ_SIZE - 24 - 2;
  
        hw->wiphy->features |= NL80211_FEATURE_P2P_GO_CTWIN |
                               NL80211_FEATURE_LOW_PRIORITY_SCAN |
                               NL80211_FEATURE_STATIC_SMPS |
                               NL80211_FEATURE_SUPPORTS_WMM_ADMISSION;
  
-       if (mvm->fw->ucode_capa.capa[0] &
-           IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT)
+       if (fw_has_capa(&mvm->fw->ucode_capa,
+                       IWL_UCODE_TLV_CAPA_TXPOWER_INSERTION_SUPPORT))
                hw->wiphy->features |= NL80211_FEATURE_TX_POWER_INSERTION;
-       if (mvm->fw->ucode_capa.capa[0] &
-           IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT)
+       if (fw_has_capa(&mvm->fw->ucode_capa,
+                       IWL_UCODE_TLV_CAPA_QUIET_PERIOD_SUPPORT))
                hw->wiphy->features |= NL80211_FEATURE_QUIET;
  
-       if (mvm->fw->ucode_capa.capa[0] &
-           IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT)
+       if (fw_has_capa(&mvm->fw->ucode_capa,
+                       IWL_UCODE_TLV_CAPA_DS_PARAM_SET_IE_SUPPORT))
                hw->wiphy->features |=
                        NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES;
  
-       if (mvm->fw->ucode_capa.capa[0] &
-           IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT)
+       if (fw_has_capa(&mvm->fw->ucode_capa,
+                       IWL_UCODE_TLV_CAPA_WFA_TPC_REP_IE_SUPPORT))
                hw->wiphy->features |= NL80211_FEATURE_WFA_TPC_IE_IN_PROBES;
  
        mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD;
  
-       /* currently FW API supports only one optional cipher scheme */
-       if (mvm->fw->cs[0].cipher) {
-               mvm->hw->n_cipher_schemes = 1;
-               mvm->hw->cipher_schemes = &mvm->fw->cs[0];
-       }
  #ifdef CONFIG_PM_SLEEP
        if (iwl_mvm_is_d0i3_supported(mvm) &&
            device_can_wakeup(mvm->trans->dev)) {
        if (ret)
                return ret;
  
-       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_TDLS_SUPPORT) {
+       if (fw_has_capa(&mvm->fw->ucode_capa,
+                       IWL_UCODE_TLV_CAPA_TDLS_SUPPORT)) {
                IWL_DEBUG_TDLS(mvm, "TDLS supported\n");
                hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
        }
  
-       if (mvm->fw->ucode_capa.capa[0] &
-           IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH) {
+       if (fw_has_capa(&mvm->fw->ucode_capa,
+                       IWL_UCODE_TLV_CAPA_TDLS_CHANNEL_SWITCH)) {
                IWL_DEBUG_TDLS(mvm, "TDLS channel switch supported\n");
                hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH;
        }
@@@ -730,6 -757,60 +757,60 @@@ static inline bool iwl_enable_tx_ampdu(
        return true;
  }
  
+ #define CHECK_BA_TRIGGER(_mvm, _trig, _tid_bm, _tid, _fmt...) \
+       do {                                                    \
+               if (!(le16_to_cpu(_tid_bm) & BIT(_tid)))        \
+                       break;                                  \
+               iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt); \
+       } while (0)
+ static void
+ iwl_mvm_ampdu_check_trigger(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                           struct ieee80211_sta *sta, u16 tid, u16 rx_ba_ssn,
+                           enum ieee80211_ampdu_mlme_action action)
+ {
+       struct iwl_fw_dbg_trigger_tlv *trig;
+       struct iwl_fw_dbg_trigger_ba *ba_trig;
+       if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
+               return;
+       trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
+       ba_trig = (void *)trig->data;
+       if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+               return;
+       switch (action) {
+       case IEEE80211_AMPDU_TX_OPERATIONAL: {
+               struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
+               struct iwl_mvm_tid_data *tid_data = &mvmsta->tid_data[tid];
+               CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_start, tid,
+                                "TX AGG START: MAC %pM tid %d ssn %d\n",
+                                sta->addr, tid, tid_data->ssn);
+               break;
+               }
+       case IEEE80211_AMPDU_TX_STOP_CONT:
+               CHECK_BA_TRIGGER(mvm, trig, ba_trig->tx_ba_stop, tid,
+                                "TX AGG STOP: MAC %pM tid %d\n",
+                                sta->addr, tid);
+               break;
+       case IEEE80211_AMPDU_RX_START:
+               CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_start, tid,
+                                "RX AGG START: MAC %pM tid %d ssn %d\n",
+                                sta->addr, tid, rx_ba_ssn);
+               break;
+       case IEEE80211_AMPDU_RX_STOP:
+               CHECK_BA_TRIGGER(mvm, trig, ba_trig->rx_ba_stop, tid,
+                                "RX AGG STOP: MAC %pM tid %d\n",
+                                sta->addr, tid);
+               break;
+       default:
+               break;
+       }
+ }
  static int iwl_mvm_mac_ampdu_action(struct ieee80211_hw *hw,
                                    struct ieee80211_vif *vif,
                                    enum ieee80211_ampdu_mlme_action action,
                ret = -EINVAL;
                break;
        }
+       if (!ret) {
+               u16 rx_ba_ssn = 0;
+               if (action == IEEE80211_AMPDU_RX_START)
+                       rx_ba_ssn = *ssn;
+               iwl_mvm_ampdu_check_trigger(mvm, vif, sta, tid,
+                                           rx_ba_ssn, action);
+       }
        mutex_unlock(&mvm->mutex);
  
        /*
@@@ -1227,22 -1318,23 +1318,23 @@@ static void iwl_mvm_restart_cleanup(str
  
        iwl_trans_stop_device(mvm->trans);
  
-       mvm->scan_status = IWL_MVM_SCAN_NONE;
+       mvm->scan_status = 0;
        mvm->ps_disabled = false;
        mvm->calibrating = false;
  
        /* just in case one was running */
        ieee80211_remain_on_channel_expired(mvm->hw);
  
-       ieee80211_iterate_active_interfaces_atomic(
-               mvm->hw, IEEE80211_IFACE_ITER_RESUME_ALL,
-               iwl_mvm_cleanup_iterator, mvm);
+       /*
+        * cleanup all interfaces, even inactive ones, as some might have
+        * gone down during the HW restart
+        */
+       ieee80211_iterate_interfaces(mvm->hw, 0, iwl_mvm_cleanup_iterator, mvm);
  
        mvm->p2p_device_vif = NULL;
        mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT;
  
        iwl_mvm_reset_phy_ctxts(mvm);
-       memset(mvm->fw_key_table, 0, sizeof(mvm->fw_key_table));
        memset(mvm->sta_drained, 0, sizeof(mvm->sta_drained));
        memset(mvm->tfd_drained, 0, sizeof(mvm->tfd_drained));
        memset(&mvm->last_bt_notif, 0, sizeof(mvm->last_bt_notif));
@@@ -1322,7 -1414,7 +1414,7 @@@ static void iwl_mvm_restart_complete(st
  
        clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
        iwl_mvm_d0i3_enable_tx(mvm, NULL);
-       ret = iwl_mvm_update_quotas(mvm, false, NULL);
+       ret = iwl_mvm_update_quotas(mvm, true, NULL);
        if (ret)
                IWL_ERR(mvm, "Failed to update quotas after restart (%d)\n",
                        ret);
@@@ -1404,7 -1496,7 +1496,7 @@@ void __iwl_mvm_mac_stop(struct iwl_mvm 
         * The work item could be running or queued if the
         * ROC time event stops just as we get here.
         */
-       cancel_work_sync(&mvm->roc_done_wk);
+       flush_work(&mvm->roc_done_wk);
  
        iwl_trans_stop_device(mvm->trans);
  
        /*
         * Clear IN_HW_RESTART flag when stopping the hw (as restart_complete()
         * won't be called in this case).
+        * But make sure to cleanup interfaces that have gone down before/during
+        * HW restart was requested.
         */
-       clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status);
+       if (test_and_clear_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
+               ieee80211_iterate_interfaces(mvm->hw, 0,
+                                            iwl_mvm_cleanup_iterator, mvm);
  
        /* We shouldn't have any UIDs still set.  Loop over all the UIDs to
         * make sure there's nothing left there and warn if any is found.
         */
-       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN) {
+       if (fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
                int i;
  
-               for (i = 0; i < IWL_MVM_MAX_SIMULTANEOUS_SCANS; i++) {
-                       if (WARN_ONCE(mvm->scan_uid[i],
-                                     "UMAC scan UID %d was not cleaned\n",
-                                     mvm->scan_uid[i]))
-                               mvm->scan_uid[i] = 0;
+               for (i = 0; i < mvm->max_scans; i++) {
+                       if (WARN_ONCE(mvm->scan_uid_status[i],
+                                     "UMAC scan UID %d status was not cleaned\n",
+                                     i))
+                               mvm->scan_uid_status[i] = 0;
                }
        }
  
@@@ -1471,8 -1567,8 +1567,8 @@@ static struct iwl_mvm_phy_ctxt *iwl_mvm
        return NULL;
  }
  
- static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                               s8 tx_power)
+ static int iwl_mvm_set_tx_power_old(struct iwl_mvm *mvm,
+                                   struct ieee80211_vif *vif, s8 tx_power)
  {
        /* FW is in charge of regulatory enforcement */
        struct iwl_reduce_tx_power_cmd reduce_txpwr_cmd = {
                                    &reduce_txpwr_cmd);
  }
  
+ static int iwl_mvm_set_tx_power(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                               s16 tx_power)
+ {
+       struct iwl_dev_tx_power_cmd cmd = {
+               .set_mode = 0,
+               .mac_context_id =
+                       cpu_to_le32(iwl_mvm_vif_from_mac80211(vif)->id),
+               .pwr_restriction = cpu_to_le16(8 * tx_power),
+       };
+       if (!fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_TX_POWER_DEV))
+               return iwl_mvm_set_tx_power_old(mvm, vif, tx_power);
+       if (tx_power == IWL_DEFAULT_MAX_TX_POWER)
+               cmd.pwr_restriction = cpu_to_le16(IWL_DEV_MAX_TX_POWER);
+       return iwl_mvm_send_cmd_pdu(mvm, REDUCE_TX_POWER_CMD, 0,
+                                   sizeof(cmd), &cmd);
+ }
  static int iwl_mvm_mac_add_interface(struct ieee80211_hw *hw,
                                     struct ieee80211_vif *vif)
  {
@@@ -2334,7 -2450,7 +2450,7 @@@ static void iwl_mvm_bss_info_changed(st
        mutex_lock(&mvm->mutex);
  
        if (changes & BSS_CHANGED_IDLE && !bss_conf->idle)
-               iwl_mvm_scan_offload_stop(mvm, true);
+               iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true);
  
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
        iwl_mvm_unref(mvm, IWL_MVM_REF_BSS_CHANGED);
  }
  
- static int iwl_mvm_cancel_scan_wait_notif(struct iwl_mvm *mvm,
-                                         enum iwl_scan_status scan_type)
- {
-       int ret;
-       bool wait_for_handlers = false;
-       mutex_lock(&mvm->mutex);
-       if (mvm->scan_status != scan_type) {
-               ret = 0;
-               /* make sure there are no pending notifications */
-               wait_for_handlers = true;
-               goto out;
-       }
-       switch (scan_type) {
-       case IWL_MVM_SCAN_SCHED:
-               ret = iwl_mvm_scan_offload_stop(mvm, true);
-               break;
-       case IWL_MVM_SCAN_OS:
-               ret = iwl_mvm_cancel_scan(mvm);
-               break;
-       case IWL_MVM_SCAN_NONE:
-       default:
-               WARN_ON_ONCE(1);
-               ret = -EINVAL;
-               break;
-       }
-       if (ret)
-               goto out;
-       wait_for_handlers = true;
- out:
-       mutex_unlock(&mvm->mutex);
-       /* make sure we consume the completion notification */
-       if (wait_for_handlers)
-               iwl_mvm_wait_for_async_handlers(mvm);
-       return ret;
- }
  static int iwl_mvm_mac_hw_scan(struct ieee80211_hw *hw,
                               struct ieee80211_vif *vif,
                               struct ieee80211_scan_request *hw_req)
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       struct cfg80211_scan_request *req = &hw_req->req;
        int ret;
  
-       if (req->n_channels == 0 ||
-           req->n_channels > mvm->fw->ucode_capa.n_scan_channels)
+       if (hw_req->req.n_channels == 0 ||
+           hw_req->req.n_channels > mvm->fw->ucode_capa.n_scan_channels)
                return -EINVAL;
  
-       if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
-               ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_SCHED);
-               if (ret)
-                       return ret;
-       }
        mutex_lock(&mvm->mutex);
-       if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
-               IWL_ERR(mvm, "scan while LAR regdomain is not set\n");
-               ret = -EBUSY;
-               goto out;
-       }
-       if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
-               ret = -EBUSY;
-               goto out;
-       }
-       iwl_mvm_ref(mvm, IWL_MVM_REF_SCAN);
-       if (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)
-               ret = iwl_mvm_scan_umac(mvm, vif, hw_req);
-       else
-               ret = iwl_mvm_unified_scan_lmac(mvm, vif, hw_req);
-       if (ret)
-               iwl_mvm_unref(mvm, IWL_MVM_REF_SCAN);
- out:
+       ret = iwl_mvm_reg_scan_start(mvm, vif, &hw_req->req, &hw_req->ies);
        mutex_unlock(&mvm->mutex);
        return ret;
  }
  
@@@ -2453,12 -2501,8 +2501,8 @@@ static void iwl_mvm_mac_cancel_hw_scan(
         * cancel scan scan before ieee80211_scan_work() could run.
         * To handle that, simply return if the scan is not running.
        */
-       /* FIXME: for now, we ignore this race for UMAC scans, since
-        * they don't set the scan_status.
-        */
-       if ((mvm->scan_status == IWL_MVM_SCAN_OS) ||
-           (mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN))
-               iwl_mvm_cancel_scan(mvm);
+       if (mvm->scan_status & IWL_MVM_SCAN_REGULAR)
+               iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_REGULAR, true);
  
        mutex_unlock(&mvm->mutex);
  }
@@@ -2774,35 -2818,17 +2818,17 @@@ static int iwl_mvm_mac_sched_scan_start
                                        struct ieee80211_scan_ies *ies)
  {
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
-       int ret;
  
-       if (!(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
-               ret = iwl_mvm_cancel_scan_wait_notif(mvm, IWL_MVM_SCAN_OS);
-               if (ret)
-                       return ret;
-       }
+       int ret;
  
        mutex_lock(&mvm->mutex);
  
-       if (iwl_mvm_is_lar_supported(mvm) && !mvm->lar_regdom_set) {
-               IWL_ERR(mvm, "sched-scan while LAR regdomain is not set\n");
-               ret = -EBUSY;
-               goto out;
-       }
        if (!vif->bss_conf.idle) {
                ret = -EBUSY;
                goto out;
        }
  
-       if (mvm->scan_status != IWL_MVM_SCAN_NONE) {
-               ret = -EBUSY;
-               goto out;
-       }
-       ret = iwl_mvm_scan_offload_start(mvm, vif, req, ies);
-       if (ret)
-               mvm->scan_status = IWL_MVM_SCAN_NONE;
+       ret = iwl_mvm_sched_scan_start(mvm, vif, req, ies, IWL_MVM_SCAN_SCHED);
  
  out:
        mutex_unlock(&mvm->mutex);
@@@ -2825,16 -2851,12 +2851,12 @@@ static int iwl_mvm_mac_sched_scan_stop(
         * could run.  To handle this, simply return if the scan is
         * not running.
        */
-       /* FIXME: for now, we ignore this race for UMAC scans, since
-        * they don't set the scan_status.
-        */
-       if (mvm->scan_status != IWL_MVM_SCAN_SCHED &&
-           !(mvm->fw->ucode_capa.capa[0] & IWL_UCODE_TLV_CAPA_UMAC_SCAN)) {
+       if (!(mvm->scan_status & IWL_MVM_SCAN_SCHED)) {
                mutex_unlock(&mvm->mutex);
                return 0;
        }
  
-       ret = iwl_mvm_scan_offload_stop(mvm, false);
+       ret = iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, false);
        mutex_unlock(&mvm->mutex);
        iwl_mvm_wait_for_async_handlers(mvm);
  
@@@ -2902,8 -2924,21 +2924,21 @@@ static int iwl_mvm_mac_set_key(struct i
                        break;
                }
  
+               /* During FW restart, in order to restore the state as it was,
+                * don't try to reprogram keys we previously failed for.
+                */
+               if (test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status) &&
+                   key->hw_key_idx == STA_KEY_IDX_INVALID) {
+                       IWL_DEBUG_MAC80211(mvm,
+                                          "skip invalid idx key programming during restart\n");
+                       ret = 0;
+                       break;
+               }
                IWL_DEBUG_MAC80211(mvm, "set hwcrypto key\n");
-               ret = iwl_mvm_set_sta_key(mvm, vif, sta, key, false);
+               ret = iwl_mvm_set_sta_key(mvm, vif, sta, key,
+                                         test_bit(IWL_MVM_STATUS_IN_HW_RESTART,
+                                                  &mvm->status));
                if (ret) {
                        IWL_WARN(mvm, "set key failed\n");
                        /*
@@@ -2981,7 -3016,7 +3016,7 @@@ static bool iwl_mvm_rx_aux_roc(struct i
        return true;
  }
  
- #define AUX_ROC_MAX_DELAY_ON_CHANNEL 5000
+ #define AUX_ROC_MAX_DELAY_ON_CHANNEL 200
  static int iwl_mvm_send_aux_roc_cmd(struct iwl_mvm *mvm,
                                    struct ieee80211_channel *channel,
                                    struct ieee80211_vif *vif,
@@@ -3086,8 -3121,8 +3121,8 @@@ static int iwl_mvm_roc(struct ieee80211
  
        switch (vif->type) {
        case NL80211_IFTYPE_STATION:
-               if (mvm->fw->ucode_capa.capa[0] &
-                   IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT) {
+               if (fw_has_capa(&mvm->fw->ucode_capa,
+                               IWL_UCODE_TLV_CAPA_HOTSPOT_SUPPORT)) {
                        /* Use aux roc framework (HS20) */
                        ret = iwl_mvm_send_aux_roc_cmd(mvm, channel,
                                                       vif, duration);
@@@ -3879,7 -3914,7 +3914,7 @@@ static int iwl_mvm_mac_get_survey(struc
        if (idx != 0)
                return -ENOENT;
  
-       if (!(mvm->fw->ucode_capa.capa[0] &
+       if (fw_has_capa(&mvm->fw->ucode_capa,
                        IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
                return -ENOENT;
  
@@@ -3926,8 -3961,8 +3961,8 @@@ static void iwl_mvm_mac_sta_statistics(
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta);
  
-       if (!(mvm->fw->ucode_capa.capa[0] &
-                               IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
+       if (fw_has_capa(&mvm->fw->ucode_capa,
+                       IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS))
                return;
  
        /* if beacon filtering isn't on mac80211 does it anyway */
        mutex_unlock(&mvm->mutex);
  }
  
- static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
-                                      struct ieee80211_vif *vif,
-                                      const struct ieee80211_event *event)
+ static void iwl_mvm_event_mlme_callback(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       const struct ieee80211_event *event)
  {
  #define CHECK_MLME_TRIGGER(_mvm, _trig, _buf, _cnt, _fmt...)  \
        do {                                                    \
                iwl_mvm_fw_dbg_collect_trig(_mvm, _trig, _fmt);\
        } while (0)
  
-       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        struct iwl_fw_dbg_trigger_tlv *trig;
        struct iwl_fw_dbg_trigger_mlme *trig_mlme;
  
        if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_MLME))
                return;
  
-       if (event->u.mlme.status == MLME_SUCCESS)
-               return;
        trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_MLME);
        trig_mlme = (void *)trig->data;
        if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
  #undef CHECK_MLME_TRIGGER
  }
  
+ static void iwl_mvm_event_bar_rx_callback(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         const struct ieee80211_event *event)
+ {
+       struct iwl_fw_dbg_trigger_tlv *trig;
+       struct iwl_fw_dbg_trigger_ba *ba_trig;
+       if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
+               return;
+       trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
+       ba_trig = (void *)trig->data;
+       if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+               return;
+       if (!(le16_to_cpu(ba_trig->rx_bar) & BIT(event->u.ba.tid)))
+               return;
+       iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+                                   "BAR received from %pM, tid %d, ssn %d",
+                                   event->u.ba.sta->addr, event->u.ba.tid,
+                                   event->u.ba.ssn);
+ }
+ static void
+ iwl_mvm_event_frame_timeout_callback(struct iwl_mvm *mvm,
+                                    struct ieee80211_vif *vif,
+                                    const struct ieee80211_event *event)
+ {
+       struct iwl_fw_dbg_trigger_tlv *trig;
+       struct iwl_fw_dbg_trigger_ba *ba_trig;
+       if (!iwl_fw_dbg_trigger_enabled(mvm->fw, FW_DBG_TRIGGER_BA))
+               return;
+       trig = iwl_fw_dbg_get_trigger(mvm->fw, FW_DBG_TRIGGER_BA);
+       ba_trig = (void *)trig->data;
+       if (!iwl_fw_dbg_trigger_check_stop(mvm, vif, trig))
+               return;
+       if (!(le16_to_cpu(ba_trig->frame_timeout) & BIT(event->u.ba.tid)))
+               return;
+       iwl_mvm_fw_dbg_collect_trig(mvm, trig,
+                                   "Frame from %pM timed out, tid %d",
+                                   event->u.ba.sta->addr, event->u.ba.tid);
+ }
+ static void iwl_mvm_mac_event_callback(struct ieee80211_hw *hw,
+                                      struct ieee80211_vif *vif,
+                                      const struct ieee80211_event *event)
+ {
+       struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
+       switch (event->type) {
+       case MLME_EVENT:
+               iwl_mvm_event_mlme_callback(mvm, vif, event);
+               break;
+       case BAR_RX_EVENT:
+               iwl_mvm_event_bar_rx_callback(mvm, vif, event);
+               break;
+       case BA_FRAME_TIMEOUT:
+               iwl_mvm_event_frame_timeout_callback(mvm, vif, event);
+               break;
+       default:
+               break;
+       }
+ }
  const struct ieee80211_ops iwl_mvm_hw_ops = {
        .tx = iwl_mvm_mac_tx,
        .ampdu_action = iwl_mvm_mac_ampdu_action,
diff --combined net/mac80211/cfg.c
@@@ -2,7 -2,7 +2,7 @@@
   * mac80211 configuration hooks for cfg80211
   *
   * Copyright 2006-2010        Johannes Berg <johannes@sipsolutions.net>
 - * Copyright 2013-2014  Intel Mobile Communications GmbH
 + * Copyright 2013-2015  Intel Mobile Communications GmbH
   *
   * This file is GPLv2 as found in COPYING.
   */
@@@ -361,25 -361,66 +361,25 @@@ static int ieee80211_get_key(struct wip
                break;
        case WLAN_CIPHER_SUITE_CCMP:
        case WLAN_CIPHER_SUITE_CCMP_256:
 -              if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
 -                  !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
 -                      drv_get_key_seq(sdata->local, key, &kseq);
 -                      memcpy(seq, kseq.ccmp.pn, 6);
 -              } else {
 -                      pn64 = atomic64_read(&key->u.ccmp.tx_pn);
 -                      seq[0] = pn64;
 -                      seq[1] = pn64 >> 8;
 -                      seq[2] = pn64 >> 16;
 -                      seq[3] = pn64 >> 24;
 -                      seq[4] = pn64 >> 32;
 -                      seq[5] = pn64 >> 40;
 -              }
 -              params.seq = seq;
 -              params.seq_len = 6;
 -              break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
        case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 -              if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
 -                  !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
 -                      drv_get_key_seq(sdata->local, key, &kseq);
 -                      memcpy(seq, kseq.aes_cmac.pn, 6);
 -              } else {
 -                      pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
 -                      seq[0] = pn64;
 -                      seq[1] = pn64 >> 8;
 -                      seq[2] = pn64 >> 16;
 -                      seq[3] = pn64 >> 24;
 -                      seq[4] = pn64 >> 32;
 -                      seq[5] = pn64 >> 40;
 -              }
 -              params.seq = seq;
 -              params.seq_len = 6;
 -              break;
 +              BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
 +                           offsetof(typeof(kseq), aes_cmac));
        case WLAN_CIPHER_SUITE_BIP_GMAC_128:
        case WLAN_CIPHER_SUITE_BIP_GMAC_256:
 -              if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
 -                  !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
 -                      drv_get_key_seq(sdata->local, key, &kseq);
 -                      memcpy(seq, kseq.aes_gmac.pn, 6);
 -              } else {
 -                      pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
 -                      seq[0] = pn64;
 -                      seq[1] = pn64 >> 8;
 -                      seq[2] = pn64 >> 16;
 -                      seq[3] = pn64 >> 24;
 -                      seq[4] = pn64 >> 32;
 -                      seq[5] = pn64 >> 40;
 -              }
 -              params.seq = seq;
 -              params.seq_len = 6;
 -              break;
 +              BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
 +                           offsetof(typeof(kseq), aes_gmac));
        case WLAN_CIPHER_SUITE_GCMP:
        case WLAN_CIPHER_SUITE_GCMP_256:
 +              BUILD_BUG_ON(offsetof(typeof(kseq), ccmp) !=
 +                           offsetof(typeof(kseq), gcmp));
 +
                if (key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE &&
                    !(key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV)) {
                        drv_get_key_seq(sdata->local, key, &kseq);
 -                      memcpy(seq, kseq.gcmp.pn, 6);
 +                      memcpy(seq, kseq.ccmp.pn, 6);
                } else {
 -                      pn64 = atomic64_read(&key->u.gcmp.tx_pn);
 +                      pn64 = atomic64_read(&key->conf.tx_pn);
                        seq[0] = pn64;
                        seq[1] = pn64 >> 8;
                        seq[2] = pn64 >> 16;
@@@ -2498,67 -2539,25 +2498,38 @@@ static bool ieee80211_coalesce_started_
                                           struct ieee80211_roc_work *new_roc,
                                           struct ieee80211_roc_work *cur_roc)
  {
-       unsigned long j = jiffies;
-       unsigned long cur_roc_end = cur_roc->hw_start_time +
-                                   msecs_to_jiffies(cur_roc->duration);
-       struct ieee80211_roc_work *next_roc;
-       int new_dur;
+       unsigned long now = jiffies;
+       unsigned long remaining = cur_roc->hw_start_time +
+                                 msecs_to_jiffies(cur_roc->duration) -
+                                 now;
  
        if (WARN_ON(!cur_roc->started || !cur_roc->hw_begun))
                return false;
  
-       if (time_after(j + IEEE80211_ROC_MIN_LEFT, cur_roc_end))
+       /* if it doesn't fit entirely, schedule a new one */
+       if (new_roc->duration > jiffies_to_msecs(remaining))
                return false;
  
        ieee80211_handle_roc_started(new_roc);
  
-       new_dur = new_roc->duration - jiffies_to_msecs(cur_roc_end - j);
-       /* cur_roc is long enough - add new_roc to the dependents list. */
-       if (new_dur <= 0) {
-               list_add_tail(&new_roc->list, &cur_roc->dependents);
-               return true;
-       }
-       new_roc->duration = new_dur;
-       /*
-        * if cur_roc was already coalesced before, we might
-        * want to extend the next roc instead of adding
-        * a new one.
-        */
-       next_roc = list_entry(cur_roc->list.next,
-                             struct ieee80211_roc_work, list);
-       if (&next_roc->list != &local->roc_list &&
-           next_roc->chan == new_roc->chan &&
-           next_roc->sdata == new_roc->sdata &&
-           !WARN_ON(next_roc->started)) {
-               list_add_tail(&new_roc->list, &next_roc->dependents);
-               next_roc->duration = max(next_roc->duration,
-                                        new_roc->duration);
-               next_roc->type = max(next_roc->type, new_roc->type);
-               return true;
-       }
-       /* add right after cur_roc */
-       list_add(&new_roc->list, &cur_roc->list);
+       /* add to dependents so we send the expired event properly */
+       list_add_tail(&new_roc->list, &cur_roc->dependents);
        return true;
  }
  
 +static u64 ieee80211_mgmt_tx_cookie(struct ieee80211_local *local)
 +{
 +      lockdep_assert_held(&local->mtx);
 +
 +      local->roc_cookie_counter++;
 +
 +      /* wow, you wrapped 64 bits ... more likely a bug */
 +      if (WARN_ON(local->roc_cookie_counter == 0))
 +              local->roc_cookie_counter++;
 +
 +      return local->roc_cookie_counter;
 +}
 +
  static int ieee80211_start_roc_work(struct ieee80211_local *local,
                                    struct ieee80211_sub_if_data *sdata,
                                    struct ieee80211_channel *channel,
        roc->req_duration = duration;
        roc->frame = txskb;
        roc->type = type;
 -      roc->mgmt_tx_cookie = (unsigned long)txskb;
        roc->sdata = sdata;
        INIT_DELAYED_WORK(&roc->work, ieee80211_sw_roc_work);
        INIT_LIST_HEAD(&roc->dependents);
         * or the SKB (for mgmt TX)
         */
        if (!txskb) {
 -              /* local->mtx protects this */
 -              local->roc_cookie_counter++;
 -              roc->cookie = local->roc_cookie_counter;
 -              /* wow, you wrapped 64 bits ... more likely a bug */
 -              if (WARN_ON(roc->cookie == 0)) {
 -                      roc->cookie = 1;
 -                      local->roc_cookie_counter++;
 -              }
 +              roc->cookie = ieee80211_mgmt_tx_cookie(local);
                *cookie = roc->cookie;
        } else {
 -              *cookie = (unsigned long)txskb;
 +              roc->mgmt_tx_cookie = *cookie;
        }
  
        /* if there's one pending or we're scanning, queue this one */
                         * In the offloaded ROC case, if it hasn't begun, add
                         * this new one to the dependent list to be handled
                         * when the master one begins. If it has begun,
-                        * check that there's still a minimum time left and
-                        * if so, start this one, transmitting the frame, but
-                        * add it to the list directly after this one with
-                        * a reduced time so we'll ask the driver to execute
-                        * it right after finishing the previous one, in the
-                        * hope that it'll also be executed right afterwards,
-                        * effectively extending the old one.
-                        * If there's no minimum time left, just add it to the
-                        * normal list.
-                        * TODO: the ROC type is ignored here, assuming that it
-                        * is better to immediately use the current ROC.
+                        * check if it fits entirely within the existing one,
+                        * in which case it will just be dependent as well.
+                        * Otherwise, schedule it by itself.
                         */
                        if (!tmp->hw_begun) {
                                list_add_tail(&roc->list, &tmp->dependents);
@@@ -3289,43 -3288,13 +3252,43 @@@ int ieee80211_channel_switch(struct wip
        return err;
  }
  
 +static struct sk_buff *ieee80211_make_ack_skb(struct ieee80211_local *local,
 +                                            struct sk_buff *skb, u64 *cookie,
 +                                            gfp_t gfp)
 +{
 +      unsigned long spin_flags;
 +      struct sk_buff *ack_skb;
 +      int id;
 +
 +      ack_skb = skb_copy(skb, gfp);
 +      if (!ack_skb)
 +              return ERR_PTR(-ENOMEM);
 +
 +      spin_lock_irqsave(&local->ack_status_lock, spin_flags);
 +      id = idr_alloc(&local->ack_status_frames, ack_skb,
 +                     1, 0x10000, GFP_ATOMIC);
 +      spin_unlock_irqrestore(&local->ack_status_lock, spin_flags);
 +
 +      if (id < 0) {
 +              kfree_skb(ack_skb);
 +              return ERR_PTR(-ENOMEM);
 +      }
 +
 +      IEEE80211_SKB_CB(skb)->ack_frame_id = id;
 +
 +      *cookie = ieee80211_mgmt_tx_cookie(local);
 +      IEEE80211_SKB_CB(ack_skb)->ack.cookie = *cookie;
 +
 +      return ack_skb;
 +}
 +
  static int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
                             struct cfg80211_mgmt_tx_params *params,
                             u64 *cookie)
  {
        struct ieee80211_sub_if_data *sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
        struct ieee80211_local *local = sdata->local;
 -      struct sk_buff *skb;
 +      struct sk_buff *skb, *ack_skb;
        struct sta_info *sta;
        const struct ieee80211_mgmt *mgmt = (void *)params->buf;
        bool need_offchan = false;
        /* Update CSA counters */
        if (sdata->vif.csa_active &&
            (sdata->vif.type == NL80211_IFTYPE_AP ||
 +           sdata->vif.type == NL80211_IFTYPE_MESH_POINT ||
             sdata->vif.type == NL80211_IFTYPE_ADHOC) &&
            params->n_csa_offsets) {
                int i;
  
        skb->dev = sdata->dev;
  
 +      if (!params->dont_wait_for_ack) {
 +              /* make a copy to preserve the frame contents
 +               * in case of encryption.
 +               */
 +              ack_skb = ieee80211_make_ack_skb(local, skb, cookie,
 +                                               GFP_KERNEL);
 +              if (IS_ERR(ack_skb)) {
 +                      ret = PTR_ERR(ack_skb);
 +                      kfree_skb(skb);
 +                      goto out_unlock;
 +              }
 +      } else {
 +              /* for cookie below */
 +              ack_skb = skb;
 +      }
 +
        if (!need_offchan) {
 -              *cookie = (unsigned long) skb;
                ieee80211_tx_skb(sdata, skb);
                ret = 0;
                goto out_unlock;
@@@ -3573,7 -3526,7 +3536,7 @@@ static int ieee80211_probe_client(struc
        struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_qos_hdr *nullfunc;
 -      struct sk_buff *skb;
 +      struct sk_buff *skb, *ack_skb;
        int size = sizeof(*nullfunc);
        __le16 fc;
        bool qos;
        struct sta_info *sta;
        struct ieee80211_chanctx_conf *chanctx_conf;
        enum ieee80211_band band;
 +      int ret;
 +
 +      /* the lock is needed to assign the cookie later */
 +      mutex_lock(&local->mtx);
  
        rcu_read_lock();
        chanctx_conf = rcu_dereference(sdata->vif.chanctx_conf);
        if (WARN_ON(!chanctx_conf)) {
 -              rcu_read_unlock();
 -              return -EINVAL;
 +              ret = -EINVAL;
 +              goto unlock;
        }
        band = chanctx_conf->def.chan->band;
        sta = sta_info_get_bss(sdata, peer);
        if (sta) {
                qos = sta->sta.wme;
        } else {
 -              rcu_read_unlock();
 -              return -ENOLINK;
 +              ret = -ENOLINK;
 +              goto unlock;
        }
  
        if (qos) {
  
        skb = dev_alloc_skb(local->hw.extra_tx_headroom + size);
        if (!skb) {
 -              rcu_read_unlock();
 -              return -ENOMEM;
 +              ret = -ENOMEM;
 +              goto unlock;
        }
  
        skb->dev = dev;
        if (qos)
                nullfunc->qos_ctrl = cpu_to_le16(7);
  
 +      ack_skb = ieee80211_make_ack_skb(local, skb, cookie, GFP_ATOMIC);
 +      if (IS_ERR(ack_skb)) {
 +              kfree_skb(skb);
 +              ret = PTR_ERR(ack_skb);
 +              goto unlock;
 +      }
 +
        local_bh_disable();
        ieee80211_xmit(sdata, sta, skb);
        local_bh_enable();
 +
 +      ret = 0;
 +unlock:
        rcu_read_unlock();
 +      mutex_unlock(&local->mtx);
  
 -      *cookie = (unsigned long) skb;
 -      return 0;
 +      return ret;
  }
  
  static int ieee80211_cfg_get_channel(struct wiphy *wiphy,
diff --combined net/mac80211/key.c
@@@ -58,6 -58,22 +58,22 @@@ static void assert_key_lock(struct ieee
        lockdep_assert_held(&local->key_mtx);
  }
  
+ static void
+ update_vlan_tailroom_need_count(struct ieee80211_sub_if_data *sdata, int delta)
+ {
+       struct ieee80211_sub_if_data *vlan;
+       if (sdata->vif.type != NL80211_IFTYPE_AP)
+               return;
+       mutex_lock(&sdata->local->mtx);
+       list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+               vlan->crypto_tx_tailroom_needed_cnt += delta;
+       mutex_unlock(&sdata->local->mtx);
+ }
  static void increment_tailroom_need_count(struct ieee80211_sub_if_data *sdata)
  {
        /*
@@@ -79,6 -95,8 +95,8 @@@
         * http://mid.gmane.org/1308590980.4322.19.camel@jlt3.sipsolutions.net
         */
  
+       update_vlan_tailroom_need_count(sdata, 1);
        if (!sdata->crypto_tx_tailroom_needed_cnt++) {
                /*
                 * Flush all XMIT packets currently using HW encryption or no
        }
  }
  
+ static void decrease_tailroom_need_count(struct ieee80211_sub_if_data *sdata,
+                                        int delta)
+ {
+       WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt < delta);
+       update_vlan_tailroom_need_count(sdata, -delta);
+       sdata->crypto_tx_tailroom_needed_cnt -= delta;
+ }
  static int ieee80211_key_enable_hw_accel(struct ieee80211_key *key)
  {
        struct ieee80211_sub_if_data *sdata;
  
                if (!((key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_MMIC) ||
                      (key->conf.flags & IEEE80211_KEY_FLAG_RESERVE_TAILROOM)))
-                       sdata->crypto_tx_tailroom_needed_cnt--;
+                       decrease_tailroom_need_count(sdata, 1);
  
                WARN_ON((key->conf.flags & IEEE80211_KEY_FLAG_PUT_IV_SPACE) &&
                        (key->conf.flags & IEEE80211_KEY_FLAG_GENERATE_IV));
@@@ -545,7 -572,7 +572,7 @@@ static void __ieee80211_key_destroy(str
                        schedule_delayed_work(&sdata->dec_tailroom_needed_wk,
                                              HZ/2);
                } else {
-                       sdata->crypto_tx_tailroom_needed_cnt--;
+                       decrease_tailroom_need_count(sdata, 1);
                }
        }
  
@@@ -635,6 -662,7 +662,7 @@@ void ieee80211_key_free(struct ieee8021
  void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata)
  {
        struct ieee80211_key *key;
+       struct ieee80211_sub_if_data *vlan;
  
        ASSERT_RTNL();
  
  
        mutex_lock(&sdata->local->key_mtx);
  
-       sdata->crypto_tx_tailroom_needed_cnt = 0;
+       WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt ||
+                    sdata->crypto_tx_tailroom_pending_dec);
+       if (sdata->vif.type == NL80211_IFTYPE_AP) {
+               list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+                       WARN_ON_ONCE(vlan->crypto_tx_tailroom_needed_cnt ||
+                                    vlan->crypto_tx_tailroom_pending_dec);
+       }
  
        list_for_each_entry(key, &sdata->key_list, list) {
                increment_tailroom_need_count(sdata);
        mutex_unlock(&sdata->local->key_mtx);
  }
  
+ void ieee80211_reset_crypto_tx_tailroom(struct ieee80211_sub_if_data *sdata)
+ {
+       struct ieee80211_sub_if_data *vlan;
+       mutex_lock(&sdata->local->key_mtx);
+       sdata->crypto_tx_tailroom_needed_cnt = 0;
+       if (sdata->vif.type == NL80211_IFTYPE_AP) {
+               list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
+                       vlan->crypto_tx_tailroom_needed_cnt = 0;
+       }
+       mutex_unlock(&sdata->local->key_mtx);
+ }
  void ieee80211_iter_keys(struct ieee80211_hw *hw,
                         struct ieee80211_vif *vif,
                         void (*iter)(struct ieee80211_hw *hw,
@@@ -692,8 -743,8 +743,8 @@@ static void ieee80211_free_keys_iface(s
  {
        struct ieee80211_key *key, *tmp;
  
-       sdata->crypto_tx_tailroom_needed_cnt -=
-               sdata->crypto_tx_tailroom_pending_dec;
+       decrease_tailroom_need_count(sdata,
+                                    sdata->crypto_tx_tailroom_pending_dec);
        sdata->crypto_tx_tailroom_pending_dec = 0;
  
        ieee80211_debugfs_key_remove_mgmt_default(sdata);
@@@ -713,6 -764,7 +764,7 @@@ void ieee80211_free_keys(struct ieee802
  {
        struct ieee80211_local *local = sdata->local;
        struct ieee80211_sub_if_data *vlan;
+       struct ieee80211_sub_if_data *master;
        struct ieee80211_key *key, *tmp;
        LIST_HEAD(keys);
  
        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);
+       if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN) {
+               if (sdata->bss) {
+                       master = container_of(sdata->bss,
+                                             struct ieee80211_sub_if_data,
+                                             u.ap);
+                       WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt !=
+                                    master->crypto_tx_tailroom_needed_cnt);
+               }
+       } else {
+               WARN_ON_ONCE(sdata->crypto_tx_tailroom_needed_cnt ||
+                            sdata->crypto_tx_tailroom_pending_dec);
+       }
        if (sdata->vif.type == NL80211_IFTYPE_AP) {
                list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
                        WARN_ON_ONCE(vlan->crypto_tx_tailroom_needed_cnt ||
@@@ -797,8 -861,8 +861,8 @@@ void ieee80211_delayed_tailroom_dec(str
         */
  
        mutex_lock(&sdata->local->key_mtx);
-       sdata->crypto_tx_tailroom_needed_cnt -=
-               sdata->crypto_tx_tailroom_pending_dec;
+       decrease_tailroom_need_count(sdata,
+                                    sdata->crypto_tx_tailroom_pending_dec);
        sdata->crypto_tx_tailroom_pending_dec = 0;
        mutex_unlock(&sdata->local->key_mtx);
  }
@@@ -832,19 -896,27 +896,19 @@@ void ieee80211_get_key_tx_seq(struct ie
                break;
        case WLAN_CIPHER_SUITE_CCMP:
        case WLAN_CIPHER_SUITE_CCMP_256:
 -              pn64 = atomic64_read(&key->u.ccmp.tx_pn);
 -              seq->ccmp.pn[5] = pn64;
 -              seq->ccmp.pn[4] = pn64 >> 8;
 -              seq->ccmp.pn[3] = pn64 >> 16;
 -              seq->ccmp.pn[2] = pn64 >> 24;
 -              seq->ccmp.pn[1] = pn64 >> 32;
 -              seq->ccmp.pn[0] = pn64 >> 40;
 -              break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
        case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 -              pn64 = atomic64_read(&key->u.aes_cmac.tx_pn);
 -              seq->ccmp.pn[5] = pn64;
 -              seq->ccmp.pn[4] = pn64 >> 8;
 -              seq->ccmp.pn[3] = pn64 >> 16;
 -              seq->ccmp.pn[2] = pn64 >> 24;
 -              seq->ccmp.pn[1] = pn64 >> 32;
 -              seq->ccmp.pn[0] = pn64 >> 40;
 -              break;
 +              BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
 +                           offsetof(typeof(*seq), aes_cmac));
        case WLAN_CIPHER_SUITE_BIP_GMAC_128:
        case WLAN_CIPHER_SUITE_BIP_GMAC_256:
 -              pn64 = atomic64_read(&key->u.aes_gmac.tx_pn);
 +              BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
 +                           offsetof(typeof(*seq), aes_gmac));
 +      case WLAN_CIPHER_SUITE_GCMP:
 +      case WLAN_CIPHER_SUITE_GCMP_256:
 +              BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
 +                           offsetof(typeof(*seq), gcmp));
 +              pn64 = atomic64_read(&key->conf.tx_pn);
                seq->ccmp.pn[5] = pn64;
                seq->ccmp.pn[4] = pn64 >> 8;
                seq->ccmp.pn[3] = pn64 >> 16;
                seq->ccmp.pn[1] = pn64 >> 32;
                seq->ccmp.pn[0] = pn64 >> 40;
                break;
 -      case WLAN_CIPHER_SUITE_GCMP:
 -      case WLAN_CIPHER_SUITE_GCMP_256:
 -              pn64 = atomic64_read(&key->u.gcmp.tx_pn);
 -              seq->gcmp.pn[5] = pn64;
 -              seq->gcmp.pn[4] = pn64 >> 8;
 -              seq->gcmp.pn[3] = pn64 >> 16;
 -              seq->gcmp.pn[2] = pn64 >> 24;
 -              seq->gcmp.pn[1] = pn64 >> 32;
 -              seq->gcmp.pn[0] = pn64 >> 40;
 -              break;
        default:
                WARN_ON(1);
        }
@@@ -926,25 -1008,43 +990,25 @@@ void ieee80211_set_key_tx_seq(struct ie
                break;
        case WLAN_CIPHER_SUITE_CCMP:
        case WLAN_CIPHER_SUITE_CCMP_256:
 -              pn64 = (u64)seq->ccmp.pn[5] |
 -                     ((u64)seq->ccmp.pn[4] << 8) |
 -                     ((u64)seq->ccmp.pn[3] << 16) |
 -                     ((u64)seq->ccmp.pn[2] << 24) |
 -                     ((u64)seq->ccmp.pn[1] << 32) |
 -                     ((u64)seq->ccmp.pn[0] << 40);
 -              atomic64_set(&key->u.ccmp.tx_pn, pn64);
 -              break;
        case WLAN_CIPHER_SUITE_AES_CMAC:
        case WLAN_CIPHER_SUITE_BIP_CMAC_256:
 -              pn64 = (u64)seq->aes_cmac.pn[5] |
 -                     ((u64)seq->aes_cmac.pn[4] << 8) |
 -                     ((u64)seq->aes_cmac.pn[3] << 16) |
 -                     ((u64)seq->aes_cmac.pn[2] << 24) |
 -                     ((u64)seq->aes_cmac.pn[1] << 32) |
 -                     ((u64)seq->aes_cmac.pn[0] << 40);
 -              atomic64_set(&key->u.aes_cmac.tx_pn, pn64);
 -              break;
 +              BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
 +                           offsetof(typeof(*seq), aes_cmac));
        case WLAN_CIPHER_SUITE_BIP_GMAC_128:
        case WLAN_CIPHER_SUITE_BIP_GMAC_256:
 -              pn64 = (u64)seq->aes_gmac.pn[5] |
 -                     ((u64)seq->aes_gmac.pn[4] << 8) |
 -                     ((u64)seq->aes_gmac.pn[3] << 16) |
 -                     ((u64)seq->aes_gmac.pn[2] << 24) |
 -                     ((u64)seq->aes_gmac.pn[1] << 32) |
 -                     ((u64)seq->aes_gmac.pn[0] << 40);
 -              atomic64_set(&key->u.aes_gmac.tx_pn, pn64);
 -              break;
 +              BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
 +                           offsetof(typeof(*seq), aes_gmac));
        case WLAN_CIPHER_SUITE_GCMP:
        case WLAN_CIPHER_SUITE_GCMP_256:
 -              pn64 = (u64)seq->gcmp.pn[5] |
 -                     ((u64)seq->gcmp.pn[4] << 8) |
 -                     ((u64)seq->gcmp.pn[3] << 16) |
 -                     ((u64)seq->gcmp.pn[2] << 24) |
 -                     ((u64)seq->gcmp.pn[1] << 32) |
 -                     ((u64)seq->gcmp.pn[0] << 40);
 -              atomic64_set(&key->u.gcmp.tx_pn, pn64);
 +              BUILD_BUG_ON(offsetof(typeof(*seq), ccmp) !=
 +                           offsetof(typeof(*seq), gcmp));
 +              pn64 = (u64)seq->ccmp.pn[5] |
 +                     ((u64)seq->ccmp.pn[4] << 8) |
 +                     ((u64)seq->ccmp.pn[3] << 16) |
 +                     ((u64)seq->ccmp.pn[2] << 24) |
 +                     ((u64)seq->ccmp.pn[1] << 32) |
 +                     ((u64)seq->ccmp.pn[0] << 40);
 +              atomic64_set(&key->conf.tx_pn, pn64);
                break;
        default:
                WARN_ON(1);
diff --combined net/mac80211/key.h
@@@ -77,6 -77,7 +77,6 @@@ struct ieee80211_key 
                        u32 mic_failures;
                } tkip;
                struct {
 -                      atomic64_t tx_pn;
                        /*
                         * Last received packet number. The first
                         * IEEE80211_NUM_TIDS counters are used with Data
                        u32 replays; /* dot11RSNAStatsCCMPReplays */
                } ccmp;
                struct {
 -                      atomic64_t tx_pn;
                        u8 rx_pn[IEEE80211_CMAC_PN_LEN];
                        struct crypto_cipher *tfm;
                        u32 replays; /* dot11RSNAStatsCMACReplays */
                        u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
                } aes_cmac;
                struct {
 -                      atomic64_t tx_pn;
                        u8 rx_pn[IEEE80211_GMAC_PN_LEN];
                        struct crypto_aead *tfm;
                        u32 replays; /* dot11RSNAStatsCMACReplays */
                        u32 icverrors; /* dot11RSNAStatsCMACICVErrors */
                } aes_gmac;
                struct {
 -                      atomic64_t tx_pn;
                        /* Last received packet number. The first
                         * IEEE80211_NUM_TIDS counters are used with Data
                         * frames and the last counter is used with Robust
@@@ -156,6 -160,7 +156,7 @@@ void ieee80211_free_keys(struct ieee802
  void ieee80211_free_sta_keys(struct ieee80211_local *local,
                             struct sta_info *sta);
  void ieee80211_enable_keys(struct ieee80211_sub_if_data *sdata);
+ void ieee80211_reset_crypto_tx_tailroom(struct ieee80211_sub_if_data *sdata);
  
  #define key_mtx_dereference(local, ref) \
        rcu_dereference_protected(ref, lockdep_is_held(&((local)->key_mtx)))