From 4bdd4dfe7a893594a75ca324057f7010b1762bd2 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Thu, 7 Apr 2016 16:44:42 +0300 Subject: [PATCH 01/13] iwlwifi: advertise maximal MPDU length when Rx MQ is supported The new hardware that supports multiple queue also de-aggregates A-MSDUs. This means that we can advertise the maximal size of A-MSDUs regardless of the receive buffer's size. In order to be able to forcefully use a lower A-MSDU size, add a default value for the module parameter. Pre-9000 will have a default of 4K, and 9000 will have 12K. Setting the amsdu_size module parameter to 4K will limit the A-MSDU on 9000 as well. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/dvm/main.c | 1 + drivers/net/wireless/intel/iwlwifi/iwl-drv.c | 3 ++- drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c | 4 +++- drivers/net/wireless/intel/iwlwifi/iwl-modparams.h | 9 +++++---- drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c | 7 +++++++ drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 5 +++++ 6 files changed, 23 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index 37b32a6f60fd..591591418316 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -1317,6 +1317,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); switch (iwlwifi_mod_params.amsdu_size) { + case IWL_AMSDU_DEF: case IWL_AMSDU_4K: trans_cfg.rx_buf_size = IWL_AMSDU_4K; break; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index 60a6c36904e4..5c5b9f228ac6 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1655,7 +1655,8 @@ MODULE_PARM_DESC(11n_disable, "disable 11n functionality, bitmap: 1: full, 2: disable agg TX, 4: disable agg RX, 8 enable agg TX"); module_param_named(amsdu_size, iwlwifi_mod_params.amsdu_size, int, S_IRUGO); -MODULE_PARM_DESC(amsdu_size, "amsdu size 0:4K 1:8K 2:12K (default 0)"); +MODULE_PARM_DESC(amsdu_size, + "amsdu size 0: 12K for multi Rx queue devices, 4K for other devices 1:4K 2:8K 3:12K (default 0)"); module_param_named(fw_restart, iwlwifi_mod_params.restart_fw, bool, S_IRUGO); MODULE_PARM_DESC(fw_restart, "restart firmware in case of error (default true)"); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c index bf1b69aec813..3199d345b427 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-parse.c @@ -766,7 +766,9 @@ void iwl_init_ht_hw_capab(const struct iwl_cfg *cfg, if (cfg->ht_params->ldpc) ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; - if (iwlwifi_mod_params.amsdu_size >= IWL_AMSDU_8K) + if ((cfg->mq_rx_supported && + iwlwifi_mod_params.amsdu_size != IWL_AMSDU_4K) || + iwlwifi_mod_params.amsdu_size >= IWL_AMSDU_8K) ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; ht_info->ampdu_factor = cfg->max_ht_ampdu_exponent; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h index 6c5c2f9f73a2..0379899dc847 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-modparams.h @@ -87,9 +87,10 @@ enum iwl_disable_11n { }; enum iwl_amsdu_size { - IWL_AMSDU_4K = 0, - IWL_AMSDU_8K = 1, - IWL_AMSDU_12K = 2, + IWL_AMSDU_DEF = 0, + IWL_AMSDU_4K = 1, + IWL_AMSDU_8K = 2, + IWL_AMSDU_12K = 3, }; enum iwl_uapsd_disable { @@ -105,7 +106,7 @@ enum iwl_uapsd_disable { * @sw_crypto: using hardware encryption, default = 0 * @disable_11n: disable 11n capabilities, default = 0, * use IWL_[DIS,EN]ABLE_HT_* constants - * @amsdu_size: enable 8K amsdu size, default = 4K. enum iwl_amsdu_size. + * @amsdu_size: See &enum iwl_amsdu_size. * @restart_fw: restart firmware, default = 1 * @bt_coex_active: enable bt coex, default = true * @led_mode: system default, default = 0 diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 21653fee806c..43f8f7d45ddb 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -397,6 +397,13 @@ static void iwl_init_vht_hw_capab(const struct iwl_cfg *cfg, vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; switch (iwlwifi_mod_params.amsdu_size) { + case IWL_AMSDU_DEF: + if (cfg->mq_rx_supported) + vht_cap->cap |= + IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; + else + vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; + break; case IWL_AMSDU_4K: vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; break; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index a68054f127fa..64d2b4f04ac2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -603,6 +603,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, trans_cfg.no_reclaim_cmds = no_reclaim_cmds; trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); switch (iwlwifi_mod_params.amsdu_size) { + case IWL_AMSDU_DEF: case IWL_AMSDU_4K: trans_cfg.rx_buf_size = IWL_AMSDU_4K; break; @@ -617,6 +618,10 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, iwlwifi_mod_params.amsdu_size); trans_cfg.rx_buf_size = IWL_AMSDU_4K; } + + /* the hardware splits the A-MSDU */ + if (mvm->cfg->mq_rx_supported) + trans_cfg.rx_buf_size = IWL_AMSDU_4K; trans_cfg.wide_cmd_header = fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_WIDE_CMD_HDR); From e7e14089e9a8e84fd8e2885ae9cacba2c30a3403 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 17 Apr 2016 14:15:17 +0300 Subject: [PATCH 02/13] iwlwifi: mvm: do not trust NSSN for amsdu sub-frames We cannot trust NSSN for AMSDU sub-frames that are not the last. The reason is that NSSN advances on the first sub-frame, and may cause the reorder buffer to advance before all the sub-frames arrive. Example: Reorder buffer contains SN 0 & 2. We receive AMSDU with SN 1 and NSSN for first sub frame 3. The result us that driver releases SN 0,1, 2. When sub-frame 1 arrives - reorder buffer is already ahead and it will be dropped. If the last sub-frame is not on this queue - we will get frame release notification with up to date NSSN. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index ac2c5718e454..0f26c7004ce5 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -587,6 +587,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, struct sk_buff *tail; u32 reorder = le32_to_cpu(desc->reorder_data); bool amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU; + bool last_subframe = + desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME; u8 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK; u8 sub_frame_idx = desc->amsdu_info & IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; @@ -651,7 +653,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, /* release immediately if allowed by nssn and no stored frames */ if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) { if (iwl_mvm_is_sn_less(buffer->head_sn, nssn, - buffer->buf_size)) + buffer->buf_size) && + (!amsdu || last_subframe)) buffer->head_sn = nssn; /* No need to update AMSDU last SN - we are moving the head */ spin_unlock_bh(&buffer->lock); @@ -685,7 +688,20 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, buffer->last_sub_index = sub_frame_idx; } - iwl_mvm_release_frames(mvm, sta, napi, buffer, nssn); + /* + * We cannot trust NSSN for AMSDU sub-frames that are not the last. + * The reason is that NSSN advances on the first sub-frame, and may + * cause the reorder buffer to advance before all the sub-frames arrive. + * Example: reorder buffer contains SN 0 & 2, and we receive AMSDU with + * SN 1. NSSN for first sub frame will be 3 with the result of driver + * releasing SN 0,1, 2. When sub-frame 1 arrives - reorder buffer is + * already ahead and it will be dropped. + * If the last sub-frame is not on this queue - we will get frame + * release notification with up to date NSSN. + */ + if (!amsdu || last_subframe) + iwl_mvm_release_frames(mvm, sta, napi, buffer, nssn); + spin_unlock_bh(&buffer->lock); return true; From 7bde4c683127d62a0c6342075fae8b46b7b000f5 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 13 Apr 2016 14:31:40 +0200 Subject: [PATCH 03/13] iwlwifi: mvm: fix comment indentation Somehow we ended up without leading spaces here, fix that. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/mvm/fw-api-rx.h | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index 1ca8e4988b88..bb8ff404e7b9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -435,26 +435,26 @@ struct iwl_rxq_sync_notification { } __packed; /* MULTI_QUEUE_DRV_SYNC_HDR_CMD_API_S_VER_1 */ /** -* Internal message identifier -* -* @IWL_MVM_RXQ_EMPTY: empty sync notification -* @IWL_MVM_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA -*/ + * Internal message identifier + * + * @IWL_MVM_RXQ_EMPTY: empty sync notification + * @IWL_MVM_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA + */ enum iwl_mvm_rxq_notif_type { IWL_MVM_RXQ_EMPTY, IWL_MVM_RXQ_NOTIF_DEL_BA, }; /** -* struct iwl_mvm_internal_rxq_notif - Internal representation of the data sent -* in &iwl_rxq_sync_cmd. Should be DWORD aligned. -* FW is agnostic to the payload, so there are no endianity requirements. -* -* @type: value from &iwl_mvm_rxq_notif_type -* @sync: ctrl path is waiting for all notifications to be received -* @cookie: internal cookie to identify old notifications -* @data: payload -*/ + * struct iwl_mvm_internal_rxq_notif - Internal representation of the data sent + * in &iwl_rxq_sync_cmd. Should be DWORD aligned. + * FW is agnostic to the payload, so there are no endianity requirements. + * + * @type: value from &iwl_mvm_rxq_notif_type + * @sync: ctrl path is waiting for all notifications to be received + * @cookie: internal cookie to identify old notifications + * @data: payload + */ struct iwl_mvm_internal_rxq_notif { u16 type; u16 sync; From c5241b0c8c0bb2bfd69effaa81e30fa26a16adda Mon Sep 17 00:00:00 2001 From: Avraham Stern Date: Wed, 20 Apr 2016 09:29:18 +0300 Subject: [PATCH 04/13] iwlwifi: rename CAPA_P2P_STANDALONE_UAPSD to CAPA_P2P_SCM_UAPSD Ucode capability bit 26 indicates support for UAPSD on P2P interface even with a simultaneous BSS station interface, as long as both interfaces are in the same binding. Change the name of the capability bit to reflect that. Signed-off-by: Avraham Stern Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h | 5 +++-- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 4 ++-- drivers/net/wireless/intel/iwlwifi/mvm/power.c | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 843232bd8bbe..3f5029ee1425 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -299,7 +299,8 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_DC2DC_SUPPORT: supports DC2DC Command * @IWL_UCODE_TLV_CAPA_CSUM_SUPPORT: supports TCP Checksum Offload * @IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS: support radio and beacon statistics - * @IWL_UCODE_TLV_CAPA_P2P_STANDALONE_UAPSD: support p2p standalone U-APSD + * @IWL_UCODE_TLV_CAPA_P2P_SCM_UAPSD: supports U-APSD on p2p interface when it + * is standalone or with a BSS station interface in the same binding. * @IWL_UCODE_TLV_CAPA_BT_COEX_PLCR: enabled BT Coex packet level co-running * @IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC: ucode supports LAR updates with different * sources for the MCC. This TLV bit is a future replacement to @@ -345,7 +346,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_DC2DC_CONFIG_SUPPORT = (__force iwl_ucode_tlv_capa_t)19, IWL_UCODE_TLV_CAPA_CSUM_SUPPORT = (__force iwl_ucode_tlv_capa_t)21, IWL_UCODE_TLV_CAPA_RADIO_BEACON_STATS = (__force iwl_ucode_tlv_capa_t)22, - IWL_UCODE_TLV_CAPA_P2P_STANDALONE_UAPSD = (__force iwl_ucode_tlv_capa_t)26, + IWL_UCODE_TLV_CAPA_P2P_SCM_UAPSD = (__force iwl_ucode_tlv_capa_t)26, IWL_UCODE_TLV_CAPA_BT_COEX_PLCR = (__force iwl_ucode_tlv_capa_t)28, IWL_UCODE_TLV_CAPA_LAR_MULTI_MCC = (__force iwl_ucode_tlv_capa_t)29, IWL_UCODE_TLV_CAPA_BT_COEX_RRC = (__force iwl_ucode_tlv_capa_t)30, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index e5f267b21316..b7014bc3fbd4 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -2360,7 +2360,7 @@ static void iwl_mvm_check_uapsd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (!(mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_UAPSD_SUPPORT)) return; - if (vif->p2p && !iwl_mvm_is_p2p_standalone_uapsd_supported(mvm)) { + if (vif->p2p && !iwl_mvm_is_p2p_scm_uapsd_supported(mvm)) { vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; return; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index ffbd41dcc0d4..4843e02e3b0e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1158,10 +1158,10 @@ static inline bool iwl_mvm_is_mplut_supported(struct iwl_mvm *mvm) } static inline -bool iwl_mvm_is_p2p_standalone_uapsd_supported(struct iwl_mvm *mvm) +bool iwl_mvm_is_p2p_scm_uapsd_supported(struct iwl_mvm *mvm) { return fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_P2P_STANDALONE_UAPSD) && + IWL_UCODE_TLV_CAPA_P2P_SCM_UAPSD) && !(iwlwifi_mod_params.uapsd_disable & IWL_DISABLE_UAPSD_P2P_CLIENT); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index 7b1f6ad6062b..ff85865b1dda 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -308,7 +308,7 @@ static bool iwl_mvm_power_allow_uapsd(struct iwl_mvm *mvm, /* Allow U-APSD only if p2p is stand alone */ bool is_p2p_standalone = true; - if (!iwl_mvm_is_p2p_standalone_uapsd_supported(mvm)) + if (!iwl_mvm_is_p2p_scm_uapsd_supported(mvm)) return false; ieee80211_iterate_active_interfaces_atomic(mvm->hw, From 6d99c88f6003062796cade71997731caec0dd8dc Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 3 Apr 2016 15:27:55 +0300 Subject: [PATCH 05/13] iwlwifi: mvm: remove RX_PHY support for 9000 device In multiple RX queues architecture, the RX_PHY notification is no longer useful as it is received in the default queue even for packets that are received on RSS queue, and cannot be accessed without locking. All the needed data is in the new RX packet metadata and firmware will no longer send this notification for 9000 devices. Remove support of it. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 - drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 -- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 13 ------------- 3 files changed, 16 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 4843e02e3b0e..dd0b8f8d3378 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1321,7 +1321,6 @@ bool iwl_mvm_bcast_filter_build_cmd(struct iwl_mvm *mvm, void iwl_mvm_rx_rx_phy_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue); void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 64d2b4f04ac2..55f114d24148 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -958,8 +958,6 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode, if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) iwl_mvm_rx_mpdu_mq(mvm, napi, rxb, 0); - else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) - iwl_mvm_rx_phy_cmd_mq(mvm, rxb); else if (unlikely(pkt->hdr.group_id == DATA_PATH_GROUP && pkt->hdr.cmd == RX_QUEUES_NOTIFICATION)) iwl_mvm_rx_queue_notif(mvm, rxb, 0); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 0f26c7004ce5..67924fc38364 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -65,19 +65,6 @@ #include "fw-api.h" #include "fw-dbg.h" -void iwl_mvm_rx_phy_cmd_mq(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) -{ - mvm->ampdu_ref++; - -#ifdef CONFIG_IWLWIFI_DEBUGFS - if (mvm->last_phy_info.phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_AGG)) { - spin_lock(&mvm->drv_stats_lock); - mvm->drv_rx_stats.ampdu_count++; - spin_unlock(&mvm->drv_stats_lock); - } -#endif -} - static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb, int queue, struct ieee80211_sta *sta) { From fbe4112791b8fe12bb3cae0b1e11830519f9dcfa Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Mon, 4 Apr 2016 19:28:45 +0300 Subject: [PATCH 06/13] iwlwifi: mvm: update mpdu metadata API rx_phy notification is no longer sent in devices with multiple rx queues. All the needed data is now set in the metadata - update code accordingly to reflect all the features as in the previous RX path. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/iwl-trans.h | 5 +- .../wireless/intel/iwlwifi/mvm/fw-api-rx.h | 24 ++++++--- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 1 + drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 49 +++++++++++++------ 4 files changed, 57 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 8193d36ae2dd..2f024c737e4f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -211,6 +211,7 @@ struct iwl_cmd_header_wide { #define FH_RSCSR_FRAME_SIZE_MSK 0x00003FFF /* bits 0-13 */ #define FH_RSCSR_FRAME_INVALID 0x55550000 #define FH_RSCSR_FRAME_ALIGN 0x40 +#define FH_RSCSR_RPA_EN BIT(25) struct iwl_rx_packet { /* @@ -220,7 +221,9 @@ struct iwl_rx_packet { * 31: flag flush RB request * 30: flag ignore TC (terminal counter) request * 29: flag fast IRQ request - * 28-14: Reserved + * 28-26: Reserved + * 25: Offload enabled + * 24-14: Reserved * 13-00: RX frame size */ __le32 len_n_flags; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h index bb8ff404e7b9..1994331593cf 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-rx.h @@ -336,6 +336,18 @@ enum iwl_rx_mpdu_reorder_data { IWL_RX_MPDU_REORDER_BA_OLD_SN = 0x80000000, }; +enum iwl_rx_mpdu_phy_info { + IWL_RX_MPDU_PHY_AMPDU = BIT(5), + IWL_RX_MPDU_PHY_AMPDU_TOGGLE = BIT(6), + IWL_RX_MPDU_PHY_SHORT_PREAMBLE = BIT(7), + IWL_RX_MPDU_PHY_TSF_OVERLOAD = BIT(8), +}; + +enum iwl_rx_mpdu_mac_info { + IWL_RX_MPDU_PHY_MAC_INDEX_MASK = 0x0f, + IWL_RX_MPDU_PHY_PHY_INDEX_MASK = 0xf0, +}; + struct iwl_rx_mpdu_desc { /* DW2 */ __le16 mpdu_len; @@ -343,9 +355,9 @@ struct iwl_rx_mpdu_desc { u8 mac_flags2; /* DW3 */ u8 amsdu_info; - __le16 reserved_for_software; + __le16 phy_info; u8 mac_phy_idx; - /* DW4 */ + /* DW4 - carries csum data only when rpa_en == 1 */ __le16 raw_csum; /* alledgedly unreliable */ __le16 l3l4_flags; /* DW5 */ @@ -354,17 +366,17 @@ struct iwl_rx_mpdu_desc { u8 sta_id_flags; /* DW6 */ __le32 reorder_data; - /* DW7 */ + /* DW7 - carries rss_hash only when rpa_en == 1 */ __le32 rss_hash; - /* DW8 */ + /* DW8 - carries filter_match only when rpa_en == 1 */ __le32 filter_match; /* DW9 */ __le32 rate_n_flags; /* DW10 */ - u8 energy_a, energy_b, channel, reserved; + u8 energy_a, energy_b, channel, mac_context; /* DW11 */ __le32 gp2_on_air_rise; - /* DW12 & DW13 */ + /* DW12 & DW13 - carries TSF only TSF_OVERLOAD bit == 0 */ __le64 tsf_on_air_rise; } __packed; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index dd0b8f8d3378..65f9a4b4b3b8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -731,6 +731,7 @@ struct iwl_mvm { struct iwl_sf_region sf_space; u32 ampdu_ref; + bool ampdu_toggle; struct iwl_notif_wait_data notif_wait; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 67924fc38364..1f4fef4469d1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -737,6 +737,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, struct ieee80211_hdr *hdr = (void *)(pkt->data + sizeof(*desc)); u32 len = le16_to_cpu(desc->mpdu_len); u32 rate_n_flags = le32_to_cpu(desc->rate_n_flags); + u16 phy_info = le16_to_cpu(desc->phy_info); struct ieee80211_sta *sta = NULL; struct sk_buff *skb; u8 crypt_len = 0; @@ -767,16 +768,34 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, le16_to_cpu(desc->status)); rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; } + /* set the preamble flag if appropriate */ + if (phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE) + rx_status->flag |= RX_FLAG_SHORTPRE; - rx_status->mactime = le64_to_cpu(desc->tsf_on_air_rise); + if (likely(!(phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) { + rx_status->mactime = le64_to_cpu(desc->tsf_on_air_rise); + /* TSF as indicated by the firmware is at INA time */ + rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; + } rx_status->device_timestamp = le32_to_cpu(desc->gp2_on_air_rise); rx_status->band = desc->channel > 14 ? NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; rx_status->freq = ieee80211_channel_to_frequency(desc->channel, rx_status->band); iwl_mvm_get_signal_strength(mvm, desc, rx_status); - /* TSF as indicated by the firmware is at INA time */ - rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; + + /* update aggregation data for monitor sake on default queue */ + if (!queue && (phy_info & IWL_RX_MPDU_PHY_AMPDU)) { + bool toggle_bit = phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE; + + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->ampdu_reference = mvm->ampdu_ref; + /* toggle is switched whenever new aggregation starts */ + if (toggle_bit != mvm->ampdu_toggle) { + mvm->ampdu_ref++; + mvm->ampdu_toggle = toggle_bit; + } + } rcu_read_lock(); @@ -831,8 +850,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, iwl_mvm_fw_dbg_collect_trig(mvm, trig, NULL); } - /* TODO: multi queue TCM */ - if (ieee80211_is_data(hdr->frame_control)) iwl_mvm_rx_csum(sta, skb, desc); @@ -857,14 +874,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, iwl_mvm_agg_rx_received(mvm, baid); } - /* - * TODO: PHY info. - * Verify we don't have the information in the MPDU descriptor and - * that it is not needed. - * Make sure for monitor mode that we are on default queue, update - * ampdu_ref and the rest of phy info then - */ - /* Set up the HT phy flags */ switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { case RATE_MCS_CHAN_WIDTH_20: @@ -908,8 +917,18 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, rx_status->band); } - /* TODO: PHY info - update ampdu queue statistics (for debugfs) */ - /* TODO: PHY info - gscan */ + /* management stuff on default queue */ + if (!queue) { + if (unlikely((ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control)) && + mvm->sched_scan_pass_all == + SCHED_SCAN_PASS_ALL_ENABLED)) + mvm->sched_scan_pass_all = SCHED_SCAN_PASS_ALL_FOUND; + + if (unlikely(ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control))) + rx_status->boottime_ns = ktime_get_boot_ns(); + } iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb); if (!iwl_mvm_reorder(mvm, napi, queue, sta, skb, desc)) From a6f035a008366a03378ed27560794d84a237895b Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Mon, 24 Aug 2015 15:23:14 +0300 Subject: [PATCH 07/13] iwlwifi: mvm: free dqa queues on STA removal also in non-bss Support queue removal in DQA mode in iwl_mvm_rm_sta() also when the device isn't a STA connected to an AP. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index fea4d3437e2f..64b07b114d3c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -819,8 +819,9 @@ int iwl_mvm_rm_sta(struct iwl_mvm *mvm, if (iwl_mvm_has_new_rx_api(mvm)) kfree(mvm_sta->dup_data); - if (vif->type == NL80211_IFTYPE_STATION && - mvmvif->ap_sta_id == mvm_sta->sta_id) { + if ((vif->type == NL80211_IFTYPE_STATION && + mvmvif->ap_sta_id == mvm_sta->sta_id) || + iwl_mvm_is_dqa_supported(mvm)){ ret = iwl_mvm_drain_sta(mvm, mvm_sta, true); if (ret) return ret; From 1316d5957be3b311b1494382172d4acb2f36ea59 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Sun, 17 Apr 2016 16:28:18 +0300 Subject: [PATCH 08/13] iwlwifi: pcie: workaround HW shadow registers bug Integrated 9000 devices have a bug with shadow registers value retention. If driver writes RBD registers while MAC is asleep the values are stored in shadow registers to be copied whenever MAC wakes up. However, in 9000 devices a MAC wakeup is not triggered and when the bus powers down due to inactivity the shadow values and dirty bits are lost. Turn on the chicken-bits that cause MAC wakeup for RX-related values as well when the device is in D0. When the device is in low power mode turn the RX wakeup chicken bits off since driver is idle and this W/A is not needed. Remove previous W/A which was ineffective. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-9000.c | 1 + .../net/wireless/intel/iwlwifi/iwl-config.h | 4 ++- drivers/net/wireless/intel/iwlwifi/iwl-csr.h | 6 +++-- .../wireless/intel/iwlwifi/pcie/internal.h | 2 ++ drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 27 ++++++++++++++----- .../net/wireless/intel/iwlwifi/pcie/trans.c | 4 +++ 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c index 3ac298fdd3cd..5c1e71fe45f3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-9000.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-9000.c @@ -178,6 +178,7 @@ const struct iwl_cfg iwl5165_2ac_cfg = { .nvm_ver = IWL9000_NVM_VERSION, .nvm_calib_ver = IWL9000_TX_POWER_VERSION, .max_ht_ampdu_exponent = IEEE80211_HT_MAX_AMPDU_64K, + .integrated = true, }; MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 4a0af7de82fd..57b14f4dcb89 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -319,6 +319,7 @@ struct iwl_pwr_tx_backoff { * @mq_rx_supported: multi-queue rx support * @vht_mu_mimo_supported: VHT MU-MIMO support * @rf_id: need to read rf_id to determine the firmware image + * @integrated: discrete or integrated * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -362,7 +363,8 @@ struct iwl_cfg { apmg_not_supported:1, mq_rx_supported:1, vht_mu_mimo_supported:1, - rf_id:1; + rf_id:1, + integrated:1; u8 valid_tx_ant; u8 valid_rx_ant; u8 non_shared_ant; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index b52913448c4a..871ad02fdb17 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -145,8 +145,10 @@ #define CSR_LED_REG (CSR_BASE+0x094) #define CSR_DRAM_INT_TBL_REG (CSR_BASE+0x0A0) -#define CSR_MAC_SHADOW_REG_CTRL (CSR_BASE+0x0A8) /* 6000 and up */ - +#define CSR_MAC_SHADOW_REG_CTRL (CSR_BASE + 0x0A8) /* 6000 and up */ +#define CSR_MAC_SHADOW_REG_CTRL_RX_WAKE BIT(20) +#define CSR_MAC_SHADOW_REG_CTL2 (CSR_BASE + 0x0AC) +#define CSR_MAC_SHADOW_REG_CTL2_RX_WAKE 0xFFFF /* GIO Chicken Bits (PCI Express bus link power management) */ #define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index de6974f9c52f..482836ee94f2 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -673,4 +673,6 @@ static inline int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) int iwl_pci_fw_exit_d0i3(struct iwl_trans *trans); int iwl_pci_fw_enter_d0i3(struct iwl_trans *trans); +void iwl_pcie_enable_rx_wake(struct iwl_trans *trans, bool enable); + #endif /* __iwl_trans_int_pcie_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 0a4a3c502c3c..f1e309d3deee 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -211,12 +211,8 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, if (trans->cfg->mq_rx_supported) iwl_write32(trans, RFH_Q_FRBDCB_WIDX_TRG(rxq->id), rxq->write_actual); - /* - * write to FH_RSCSR_CHNL0_WPTR register even in MQ as a W/A to - * hardware shadow registers bug - writing to RFH_Q_FRBDCB_WIDX will - * not wake the NIC. - */ - iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); + else + iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); } static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans) @@ -764,6 +760,23 @@ static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) iwl_set_bit(trans, CSR_INT_COALESCING, IWL_HOST_INT_OPER_MODE); } +void iwl_pcie_enable_rx_wake(struct iwl_trans *trans, bool enable) +{ + /* + * Turn on the chicken-bits that cause MAC wakeup for RX-related + * values. + * This costs some power, but needed for W/A 9000 integrated A-step + * bug where shadow registers are not in the retention list and their + * value is lost when NIC powers down + */ + if (trans->cfg->integrated) { + iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, + CSR_MAC_SHADOW_REG_CTRL_RX_WAKE); + iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTL2, + CSR_MAC_SHADOW_REG_CTL2_RX_WAKE); + } +} + static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -849,6 +862,8 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) /* Set interrupt coalescing timer to default (2048 usecs) */ iwl_write8(trans, CSR_INT_COALESCING, IWL_HOST_INT_TIMEOUT_DEF); + + iwl_pcie_enable_rx_wake(trans, true); } static void iwl_pcie_rx_init_rxb_lists(struct iwl_rxq *rxq) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index f603d7830a6b..33fd2175e046 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -1286,6 +1286,8 @@ static void iwl_trans_pcie_d3_suspend(struct iwl_trans *trans, bool test, iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_INIT_DONE); + iwl_pcie_enable_rx_wake(trans, false); + if (reset) { /* * reset TX queues -- some of their registers reset during S3 @@ -1311,6 +1313,8 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, return 0; } + iwl_pcie_enable_rx_wake(trans, true); + /* * Also enables interrupts - none will happen as the device doesn't * know we're waking it up, only when the opmode actually tells it From b0262f07f47f782b804963f0a7ce3e5e0eba3491 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 21 Apr 2016 16:38:43 +0300 Subject: [PATCH 09/13] iwlwifi: pcie: set RB chunk size per bus For 9000 devices we can have PCIe bus for discrete devices and IOSF bus for integrated devices. PCIe supports maximum transfer size of 128B while IOSF bus supports maximum transfer size of 64B. Configure RB size accordingly. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-fh.h | 4 +++- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 8 ++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index 270f39ecd2d4..f08cdeef2d30 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -384,7 +384,9 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(unsigned int chnl) #define RFH_GEN_CFG 0xA09800 #define RFH_GEN_CFG_SERVICE_DMA_SNOOP BIT(0) #define RFH_GEN_CFG_RFH_DMA_SNOOP BIT(1) -#define RFH_GEN_CFG_RB_CHUNK_SIZE BIT(4) /* 0 - 64B, 1- 128B */ +#define RFH_GEN_CFG_RB_CHUNK_SIZE_POS 4 +#define RFH_GEN_CFG_RB_CHUNK_SIZE_128 1 +#define RFH_GEN_CFG_RB_CHUNK_SIZE_64 0 #define RFH_GEN_CFG_DEFAULT_RXQ_NUM_MASK 0xF00 #define RFH_GEN_CFG_DEFAULT_RXQ_NUM_POS 8 diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index f1e309d3deee..27ff74d6ad66 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -848,13 +848,17 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) /* * Activate DMA snooping. - * Set RX DMA chunk size to 64B + * Set RX DMA chunk size to 64B for IOSF and 128B for PCIe * Default queue is 0 */ iwl_write_prph_no_grab(trans, RFH_GEN_CFG, RFH_GEN_CFG_RFH_DMA_SNOOP | (DEFAULT_RXQ_NUM << RFH_GEN_CFG_DEFAULT_RXQ_NUM_POS) | - RFH_GEN_CFG_SERVICE_DMA_SNOOP); + RFH_GEN_CFG_SERVICE_DMA_SNOOP | + (trans->cfg->integrated ? + RFH_GEN_CFG_RB_CHUNK_SIZE_64 : + RFH_GEN_CFG_RB_CHUNK_SIZE_128) << + RFH_GEN_CFG_RB_CHUNK_SIZE_POS); /* Enable the relevant rx queues */ iwl_write_prph_no_grab(trans, RFH_RXF_RXQ_ACTIVE, enabled); From 630443355a31ebf2f80cb5206b68cf2ee945a368 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 21 Apr 2016 17:41:39 +0300 Subject: [PATCH 10/13] iwlwifi: pcie: allow more than one frame in RB for 9000 devices We now have 9000 devices that support multiple frames in a single RB. Enable it. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 27ff74d6ad66..4fad80813d75 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -833,15 +833,13 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) /* * Enable Rx DMA - * Single frame mode * Rx buffer size 4 or 8k or 12k * Min RB size 4 or 8 * Drop frames that exceed RB size * 512 RBDs */ iwl_write_prph_no_grab(trans, RFH_RXF_DMA_CFG, - RFH_DMA_EN_ENABLE_VAL | - rb_size | RFH_RXF_DMA_SINGLE_FRAME_MASK | + RFH_DMA_EN_ENABLE_VAL | rb_size | RFH_RXF_DMA_MIN_RB_4_8 | RFH_RXF_DMA_DROP_TOO_LARGE_MASK | RFH_RXF_DMA_RBDCB_SIZE_512); From ab2e696bd25c11bf4baf84f285555b069ae2dd30 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 21 Apr 2016 20:15:40 +0300 Subject: [PATCH 11/13] iwlwifi: pcie: make sure packet arrived to destined queue Add a warning in case packet didn't end up in the HW destined queue. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/iwl-trans.h | 8 +++++++- drivers/net/wireless/intel/iwlwifi/pcie/rx.c | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 2f024c737e4f..a45be1db179b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -212,6 +212,8 @@ struct iwl_cmd_header_wide { #define FH_RSCSR_FRAME_INVALID 0x55550000 #define FH_RSCSR_FRAME_ALIGN 0x40 #define FH_RSCSR_RPA_EN BIT(25) +#define FH_RSCSR_RXQ_POS 16 +#define FH_RSCSR_RXQ_MASK 0x3F0000 struct iwl_rx_packet { /* @@ -223,7 +225,11 @@ struct iwl_rx_packet { * 29: flag fast IRQ request * 28-26: Reserved * 25: Offload enabled - * 24-14: Reserved + * 24: RPF enabled + * 23: RSS enabled + * 22: Checksum enabled + * 21-16: RX queue + * 15-14: Reserved * 13-00: RX frame size */ __le32 len_n_flags; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 4fad80813d75..70e39e4ec0c0 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -1104,6 +1104,9 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, if (pkt->len_n_flags == cpu_to_le32(FH_RSCSR_FRAME_INVALID)) break; + WARN_ON((le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_RXQ_MASK) >> + FH_RSCSR_RXQ_POS != rxq->id); + IWL_DEBUG_RX(trans, "cmd at offset %d: %s (0x%.2x, seq 0x%x)\n", rxcb._offset, From d3a108a48dc670d539c58d4339d211b914a1e1b5 Mon Sep 17 00:00:00 2001 From: Andrei Otcheretianski Date: Sun, 28 Feb 2016 17:12:21 +0200 Subject: [PATCH 12/13] iwlwifi: mvm: Support CSA countdown offloading Add support CSA countdown offloading. When CSA starts, the driver specifies the offsets to the eCSA and CSA IEs in the beacon template command and the fw performs the countdown. The fw notifies the driver when the channel switch flow should be performed. Beacon sent notifications are not used anymore. Signed-off-by: Andrei Otcheretianski Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/iwl-fw-file.h | 4 + .../wireless/intel/iwlwifi/mvm/fw-api-tx.h | 29 ++++- .../net/wireless/intel/iwlwifi/mvm/fw-api.h | 12 +- .../net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 108 +++++++++++++++--- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 10 ++ drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 5 + drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 26 +++++ drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 13 ++- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 13 ++- 9 files changed, 198 insertions(+), 22 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h index 3f5029ee1425..eb18de800c2c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h @@ -311,6 +311,9 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement * @IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS: supports short PM timeouts * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT + * @IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD: the firmware supports CSA + * countdown offloading. Beacon notifications are not sent to the host. + * The fw also offloads TBTT alignment. * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what * antenna the beacon should be transmitted * @IWL_UCODE_TLV_CAPA_BEACON_STORING: firmware will store the latest beacon @@ -355,6 +358,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT = (__force iwl_ucode_tlv_capa_t)67, IWL_UCODE_TLV_CAPA_MULTI_QUEUE_RX_SUPPORT = (__force iwl_ucode_tlv_capa_t)68, + IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD = (__force iwl_ucode_tlv_capa_t)70, IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION = (__force iwl_ucode_tlv_capa_t)71, IWL_UCODE_TLV_CAPA_BEACON_STORING = (__force iwl_ucode_tlv_capa_t)72, IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2 = (__force iwl_ucode_tlv_capa_t)73, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h index dadcccd88255..ee59511323c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api-tx.h @@ -562,8 +562,8 @@ struct iwl_mvm_ba_notif { u8 reserved1; } __packed; -/* - * struct iwl_mac_beacon_cmd - beacon template command +/** + * struct iwl_mac_beacon_cmd_v6 - beacon template command * @tx: the tx commands associated with the beacon frame * @template_id: currently equal to the mac context id of the coresponding * mac. @@ -571,13 +571,34 @@ struct iwl_mvm_ba_notif { * @tim_size: the length of the tim IE * @frame: the template of the beacon frame */ -struct iwl_mac_beacon_cmd { +struct iwl_mac_beacon_cmd_v6 { struct iwl_tx_cmd tx; __le32 template_id; __le32 tim_idx; __le32 tim_size; struct ieee80211_hdr frame[0]; -} __packed; +} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_6 */ + +/** + * struct iwl_mac_beacon_cmd - beacon template command with offloaded CSA + * @tx: the tx commands associated with the beacon frame + * @template_id: currently equal to the mac context id of the coresponding + * mac. + * @tim_idx: the offset of the tim IE in the beacon + * @tim_size: the length of the tim IE + * @ecsa_offset: offset to the ECSA IE if present + * @csa_offset: offset to the CSA IE if present + * @frame: the template of the beacon frame + */ +struct iwl_mac_beacon_cmd { + struct iwl_tx_cmd tx; + __le32 template_id; + __le32 tim_idx; + __le32 tim_size; + __le32 ecsa_offset; + __le32 csa_offset; + struct ieee80211_hdr frame[0]; +} __packed; /* BEACON_TEMPLATE_CMD_API_S_VER_7 */ struct iwl_beacon_notif { struct iwl_mvm_tx_resp beacon_notify_hdr; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h index 41b80ae2d5f8..b06380d8473b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h @@ -314,6 +314,7 @@ enum { enum iwl_mac_conf_subcmd_ids { LINK_QUALITY_MEASUREMENT_CMD = 0x1, LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF = 0xFE, + CHANNEL_SWITCH_NOA_NOTIF = 0xFF, }; enum iwl_phy_ops_subcmd_ids { @@ -732,7 +733,7 @@ enum iwl_time_event_type { /* P2P GO Events */ TE_P2P_GO_ASSOC_PROT, - TE_P2P_GO_REPETITIVE_NOA, + TE_P2P_GO_REPETITIVET_NOA, TE_P2P_GO_CT_WINDOW, /* WiDi Sync Events */ @@ -2111,4 +2112,13 @@ struct iwl_link_qual_msrmnt_notif { __le32 reserved[3]; } __packed; /* LQM_MEASUREMENT_COMPLETE_NTF_API_S_VER1 */ +/** + * Channel switch NOA notification + * + * @id_and_color: ID and color of the MAC + */ +struct iwl_channel_switch_noa_notif { + __le32 id_and_color; +} __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */ + #endif /* __fw_api_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 7aae068c02e5..69c42ce45b8a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1006,7 +1006,7 @@ static int iwl_mvm_mac_ctxt_cmd_p2p_device(struct iwl_mvm *mvm, } static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm, - struct iwl_mac_beacon_cmd *beacon_cmd, + struct iwl_mac_beacon_cmd_v6 *beacon_cmd, u8 *beacon, u32 frame_size) { u32 tim_idx; @@ -1030,6 +1030,23 @@ static void iwl_mvm_mac_ctxt_set_tim(struct iwl_mvm *mvm, } } +static u32 iwl_mvm_find_ie_offset(u8 *beacon, u8 eid, u32 frame_size) +{ + struct ieee80211_mgmt *mgmt = (void *)beacon; + const u8 *ie; + + if (WARN_ON_ONCE(frame_size <= (mgmt->u.beacon.variable - beacon))) + return 0; + + frame_size -= mgmt->u.beacon.variable - beacon; + + ie = cfg80211_find_ie(eid, mgmt->u.beacon.variable, frame_size); + if (!ie) + return 0; + + return ie - beacon; +} + static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct sk_buff *beacon) @@ -1039,7 +1056,10 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, .id = BEACON_TEMPLATE_CMD, .flags = CMD_ASYNC, }; - struct iwl_mac_beacon_cmd beacon_cmd = {}; + union { + struct iwl_mac_beacon_cmd_v6 beacon_cmd_v6; + struct iwl_mac_beacon_cmd beacon_cmd; + } u = {}; struct ieee80211_tx_info *info; u32 beacon_skb_len; u32 rate, tx_flags; @@ -1051,18 +1071,18 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, /* TODO: for now the beacon template id is set to be the mac context id. * Might be better to handle it as another resource ... */ - beacon_cmd.template_id = cpu_to_le32((u32)mvmvif->id); + u.beacon_cmd_v6.template_id = cpu_to_le32((u32)mvmvif->id); info = IEEE80211_SKB_CB(beacon); /* Set up TX command fields */ - beacon_cmd.tx.len = cpu_to_le16((u16)beacon_skb_len); - beacon_cmd.tx.sta_id = mvmvif->bcast_sta.sta_id; - beacon_cmd.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); + u.beacon_cmd_v6.tx.len = cpu_to_le16((u16)beacon_skb_len); + u.beacon_cmd_v6.tx.sta_id = mvmvif->bcast_sta.sta_id; + u.beacon_cmd_v6.tx.life_time = cpu_to_le32(TX_CMD_LIFE_TIME_INFINITE); tx_flags = TX_CMD_FLG_SEQ_CTL | TX_CMD_FLG_TSF; tx_flags |= iwl_mvm_bt_coex_tx_prio(mvm, (void *)beacon->data, info, 0) << TX_CMD_FLG_BT_PRIO_POS; - beacon_cmd.tx.tx_flags = cpu_to_le32(tx_flags); + u.beacon_cmd_v6.tx.tx_flags = cpu_to_le32(tx_flags); if (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION)) { @@ -1071,7 +1091,7 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, mvm->mgmt_last_antenna_idx); } - beacon_cmd.tx.rate_n_flags = + u.beacon_cmd_v6.tx.rate_n_flags = cpu_to_le32(BIT(mvm->mgmt_last_antenna_idx) << RATE_MCS_ANT_POS); @@ -1079,20 +1099,37 @@ static int iwl_mvm_mac_ctxt_send_beacon(struct iwl_mvm *mvm, rate = IWL_FIRST_OFDM_RATE; } else { rate = IWL_FIRST_CCK_RATE; - beacon_cmd.tx.rate_n_flags |= cpu_to_le32(RATE_MCS_CCK_MSK); + u.beacon_cmd_v6.tx.rate_n_flags |= + cpu_to_le32(RATE_MCS_CCK_MSK); } - beacon_cmd.tx.rate_n_flags |= + u.beacon_cmd_v6.tx.rate_n_flags |= cpu_to_le32(iwl_mvm_mac80211_idx_to_hwrate(rate)); /* Set up TX beacon command fields */ if (vif->type == NL80211_IFTYPE_AP) - iwl_mvm_mac_ctxt_set_tim(mvm, &beacon_cmd, + iwl_mvm_mac_ctxt_set_tim(mvm, &u.beacon_cmd_v6, beacon->data, beacon_skb_len); /* Submit command */ - cmd.len[0] = sizeof(beacon_cmd); - cmd.data[0] = &beacon_cmd; + + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CSA_AND_TBTT_OFFLOAD)) { + u.beacon_cmd.csa_offset = + cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, + WLAN_EID_CHANNEL_SWITCH, + beacon_skb_len)); + u.beacon_cmd.ecsa_offset = + cpu_to_le32(iwl_mvm_find_ie_offset(beacon->data, + WLAN_EID_EXT_CHANSWITCH_ANN, + beacon_skb_len)); + + cmd.len[0] = sizeof(u.beacon_cmd); + } else { + cmd.len[0] = sizeof(u.beacon_cmd_v6); + } + + cmd.data[0] = &u; cmd.dataflags[0] = 0; cmd.len[1] = beacon_skb_len; cmd.data[1] = beacon->data; @@ -1538,3 +1575,48 @@ void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm, /* pass it as regular rx to mac80211 */ ieee80211_rx_napi(mvm->hw, NULL, skb, NULL); } + +void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_channel_switch_noa_notif *notif = (void *)pkt->data; + struct ieee80211_vif *csa_vif; + struct iwl_mvm_vif *mvmvif; + int len = iwl_rx_packet_payload_len(pkt); + u32 id_n_color; + + if (WARN_ON_ONCE(len < sizeof(*notif))) + return; + + rcu_read_lock(); + + csa_vif = rcu_dereference(mvm->csa_vif); + if (WARN_ON(!csa_vif || !csa_vif->csa_active)) + goto out_unlock; + + id_n_color = le32_to_cpu(notif->id_and_color); + + mvmvif = iwl_mvm_vif_from_mac80211(csa_vif); + if (WARN(FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color) != id_n_color, + "channel switch noa notification on unexpected vif (csa_vif=%d, notif=%d)", + FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color), id_n_color)) + goto out_unlock; + + IWL_DEBUG_INFO(mvm, "Channel Switch Started Notification\n"); + + queue_delayed_work(system_wq, &mvm->cs_tx_unblock_dwork, + msecs_to_jiffies(IWL_MVM_CS_UNBLOCK_TX_TIMEOUT * + csa_vif->bss_conf.beacon_int)); + + ieee80211_csa_finish(csa_vif); + + rcu_read_unlock(); + + RCU_INIT_POINTER(mvm->csa_vif, NULL); + + return; + +out_unlock: + rcu_read_unlock(); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index b7014bc3fbd4..e5cb7dbcd05c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1199,6 +1199,7 @@ static void iwl_mvm_mac_stop(struct ieee80211_hw *hw) flush_work(&mvm->async_handlers_wk); flush_work(&mvm->add_stream_wk); cancel_delayed_work_sync(&mvm->fw_dump_wk); + cancel_delayed_work_sync(&mvm->cs_tx_unblock_dwork); iwl_mvm_free_fw_dump_desc(mvm); mutex_lock(&mvm->mutex); @@ -3687,6 +3688,13 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, goto out_unlock; } + /* we still didn't unblock tx. prevent new CS meanwhile */ + if (rcu_dereference_protected(mvm->csa_tx_blocked_vif, + lockdep_is_held(&mvm->mutex))) { + ret = -EBUSY; + goto out_unlock; + } + rcu_assign_pointer(mvm->csa_vif, vif); if (WARN_ONCE(mvmvif->csa_countdown, @@ -3695,6 +3703,8 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, goto out_unlock; } + mvmvif->csa_target_freq = chsw->chandef.chan->center_freq; + break; case NL80211_IFTYPE_STATION: if (mvmvif->lqm_active) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 65f9a4b4b3b8..4b75b9226898 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -452,6 +452,7 @@ struct iwl_mvm_vif { /* Indicates that CSA countdown may be started */ bool csa_countdown; bool csa_failed; + u16 csa_target_freq; /* TCP Checksum Offload */ netdev_features_t features; @@ -1007,6 +1008,8 @@ struct iwl_mvm { * clients. */ bool drop_bcn_ap_mode; + + struct delayed_work cs_tx_unblock_dwork; }; /* Extract MVM priv from op_mode and _hw */ @@ -1381,6 +1384,8 @@ void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, struct ieee80211_vif *vif); unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm, struct ieee80211_vif *exclude_vif); +void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm, + struct iwl_rx_cmd_buffer *rxb); /* Bindings */ int iwl_mvm_binding_add_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_binding_remove_vif(struct iwl_mvm *mvm, struct ieee80211_vif *vif); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 55f114d24148..ddc400436782 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -431,6 +431,7 @@ static const struct iwl_hcmd_names iwl_mvm_system_names[] = { static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = { HCMD_NAME(LINK_QUALITY_MEASUREMENT_CMD), HCMD_NAME(LINK_QUALITY_MEASUREMENT_COMPLETE_NOTIF), + HCMD_NAME(CHANNEL_SWITCH_NOA_NOTIF), }; /* Please keep this array *SORTED* by hex value. @@ -494,6 +495,29 @@ static u32 calc_min_backoff(struct iwl_trans *trans, const struct iwl_cfg *cfg) static void iwl_mvm_fw_error_dump_wk(struct work_struct *work); +static void iwl_mvm_tx_unblock_dwork(struct work_struct *work) +{ + struct iwl_mvm *mvm = + container_of(work, struct iwl_mvm, cs_tx_unblock_dwork.work); + struct ieee80211_vif *tx_blocked_vif; + struct iwl_mvm_vif *mvmvif; + + mutex_lock(&mvm->mutex); + + tx_blocked_vif = + rcu_dereference_protected(mvm->csa_tx_blocked_vif, + lockdep_is_held(&mvm->mutex)); + + if (!tx_blocked_vif) + goto unlock; + + mvmvif = iwl_mvm_vif_from_mac80211(tx_blocked_vif); + iwl_mvm_modify_all_sta_disable_tx(mvm, mvmvif, false); + RCU_INIT_POINTER(mvm->csa_tx_blocked_vif, NULL); +unlock: + mutex_unlock(&mvm->mutex); +} + static struct iwl_op_mode * iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir) @@ -595,6 +619,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, SET_IEEE80211_DEV(mvm->hw, mvm->trans->dev); + INIT_DELAYED_WORK(&mvm->cs_tx_unblock_dwork, iwl_mvm_tx_unblock_dwork); + /* * Populate the state variables that the transport layer needs * to know about. diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index ab7f7eda9c13..6d096b6c3d50 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -354,13 +354,22 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, if (sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct ieee80211_vif *tx_blocked_vif = + rcu_dereference(mvm->csa_tx_blocked_vif); /* We have tx blocked stations (with CS bit). If we heard * frames from a blocked station on a new channel we can * TX to it again. */ - if (unlikely(mvm->csa_tx_block_bcn_timeout)) - iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); + if (unlikely(tx_blocked_vif) && + mvmsta->vif == tx_blocked_vif) { + struct iwl_mvm_vif *mvmvif = + iwl_mvm_vif_from_mac80211(tx_blocked_vif); + + if (mvmvif->csa_target_freq == rx_status->freq) + iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, + false); + } rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 1f4fef4469d1..d13397a17365 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -817,6 +817,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (sta) { struct iwl_mvm_sta *mvmsta = iwl_mvm_sta_from_mac80211(sta); + struct ieee80211_vif *tx_blocked_vif = + rcu_dereference(mvm->csa_tx_blocked_vif); u8 baid = (u8)((le32_to_cpu(desc->reorder_data) & IWL_RX_MPDU_REORDER_BAID_MASK) >> IWL_RX_MPDU_REORDER_BAID_SHIFT); @@ -826,8 +828,15 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, * frames from a blocked station on a new channel we can * TX to it again. */ - if (unlikely(mvm->csa_tx_block_bcn_timeout)) - iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, false); + if (unlikely(tx_blocked_vif) && + tx_blocked_vif == mvmsta->vif) { + struct iwl_mvm_vif *mvmvif = + iwl_mvm_vif_from_mac80211(tx_blocked_vif); + + if (mvmvif->csa_target_freq == rx_status->freq) + iwl_mvm_sta_modify_disable_tx_ap(mvm, sta, + false); + } rs_update_last_rssi(mvm, &mvmsta->lq_sta, rx_status); From 58035432d60616cc2ef6514a3d0e6d6ad01bf705 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 27 Apr 2016 13:33:26 +0200 Subject: [PATCH 13/13] iwlwifi: mvm: handle FRAME_RELEASE in MQ code For some reason, the FRAME_RELEASE message handling for the default queue ended up being in the only/default queue for non-RSS devices; fix that and handle FRAME_RELEASE properly on the default queue for RSS devices. Fixes: 585a6fccf5b8 ("iwlwifi: mvm: infrastructure for frame-release message") Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index ddc400436782..632b1dc552a0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -967,8 +967,6 @@ static void iwl_mvm_rx(struct iwl_op_mode *op_mode, if (likely(pkt->hdr.cmd == REPLY_RX_MPDU_CMD)) iwl_mvm_rx_rx_mpdu(mvm, napi, rxb); - else if (pkt->hdr.cmd == FRAME_RELEASE) - iwl_mvm_rx_frame_release(mvm, napi, rxb, 0); else if (pkt->hdr.cmd == REPLY_RX_PHY_CMD) iwl_mvm_rx_rx_phy_cmd(mvm, rxb); else @@ -987,6 +985,8 @@ static void iwl_mvm_rx_mq(struct iwl_op_mode *op_mode, else if (unlikely(pkt->hdr.group_id == DATA_PATH_GROUP && pkt->hdr.cmd == RX_QUEUES_NOTIFICATION)) iwl_mvm_rx_queue_notif(mvm, rxb, 0); + else if (pkt->hdr.cmd == FRAME_RELEASE) + iwl_mvm_rx_frame_release(mvm, napi, rxb, 0); else iwl_mvm_rx_common(mvm, rxb, pkt); }