From 96972d24fa6d6aa163e82cff213c1280df0f958c Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Thu, 6 Sep 2018 17:56:58 +0300 Subject: [PATCH 01/38] iwlwifi: bump FW API to 44 for 9000 and 22000 series Start supporting API version 44 where applicable. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/cfg/22000.c | 2 +- drivers/net/wireless/intel/iwlwifi/cfg/9000.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index eb93711d474b..c547288d32aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -56,7 +56,7 @@ #include "iwl-config.h" /* Highest firmware API version supported */ -#define IWL_22000_UCODE_API_MAX 43 +#define IWL_22000_UCODE_API_MAX 44 /* Lowest firmware API version supported */ #define IWL_22000_UCODE_API_MIN 39 diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c index 113bcf7735a0..84adfd902002 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c @@ -57,7 +57,7 @@ #include "fw/file.h" /* Highest firmware API version supported */ -#define IWL9000_UCODE_API_MAX 43 +#define IWL9000_UCODE_API_MAX 44 /* Lowest firmware API version supported */ #define IWL9000_UCODE_API_MIN 30 From a9f802b63d00b333bd28e1378c2d1acbbe06bd1c Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Mon, 26 Nov 2018 10:23:11 +0200 Subject: [PATCH 02/38] iwlwifi dbg_ini: update ini structs doc update ini struct documentation to the structs being used in the FW Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index ab82b7a67967..b7525d62e26f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -155,20 +155,20 @@ struct iwl_fw_ini_region_cfg { }; __le32 size; __le32 start_addr[]; -} __packed; /* FW_INI_REGION_CONFIG_S */ +} __packed; /* FW_DEBUG_TLV_REGION_CONFIG_S */ /** * struct iwl_fw_ini_region_tlv - (IWL_FW_INI_TLV_TYPE_REGION_CFG) * DUMP sections define IDs and triggers that use those IDs TLV * @header: header * @num_regions: how many different region section and IDs are coming next - * @iwl_fw_ini_dump dump_config: list of dump configurations + * @region_config: list of dump configurations */ struct iwl_fw_ini_region_tlv { struct iwl_fw_ini_header header; __le32 num_regions; struct iwl_fw_ini_region_cfg region_config[]; -} __packed; /* FW_INI_REGION_CFG_S */ +} __packed; /* FW_DEBUG_TLV_REGIONS_S_VER_1 */ /** * struct iwl_fw_ini_trigger - (IWL_FW_INI_TLV_TYPE_DUMP_CFG) @@ -196,7 +196,7 @@ struct iwl_fw_ini_trigger { __le32 trigger_data; __le32 num_regions; __le32 data[]; -} __packed; /* FW_INI_TRIGGER_CONFIG_S */ +} __packed; /* FW_TLV_DEBUG_TRIGGER_CONFIG_S */ /** * struct iwl_fw_ini_trigger_tlv - (IWL_FW_INI_TLV_TYPE_TRIGGERS_CFG) @@ -210,7 +210,7 @@ struct iwl_fw_ini_trigger_tlv { struct iwl_fw_ini_header header; __le32 num_triggers; struct iwl_fw_ini_trigger trigger_config[]; -} __packed; /* FW_INI_TRIGGER_CFG_S */ +} __packed; /* FW_TLV_DEBUG_TRIGGERS_S_VER_1 */ /** * enum iwl_fw_ini_trigger_id From 56ea8e3b4ef6a319ac8297bc50f6909dccc684ec Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Mon, 26 Nov 2018 10:25:44 +0200 Subject: [PATCH 03/38] iwlwifi: dbg_ini: fix iwl_fw_ini_buffer_location field enum bad naming change IWL_FW_INI_LOCATION_SRAM_INVALID into IWL_FW_INI_LOCATION_INVALID Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index b7525d62e26f..a2c4a28c46a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -349,7 +349,7 @@ enum iwl_fw_ini_allocation_id { * @IWL_FW_INI_LOCATION_DRAM_PATH: DRAM location */ enum iwl_fw_ini_buffer_location { - IWL_FW_INI_LOCATION_SRAM_INVALID, + IWL_FW_INI_LOCATION_INVALID, IWL_FW_INI_LOCATION_SRAM_PATH, IWL_FW_INI_LOCATION_DRAM_PATH, }; /* FW_INI_BUFFER_LOCATION_E_VER_1 */ From bb0306bd6fa2ef6f4d596cc1e31e7e37ad5f48a3 Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Mon, 26 Nov 2018 10:15:35 +0200 Subject: [PATCH 04/38] iwlwifi: dbg_ini: give better naming to region struct fields Some of the region struct fields have misleading naming change those fields to have an informative naming Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/fw/api/dbg-tlv.h | 12 ++++++----- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 21 ++++++++++--------- 2 files changed, 18 insertions(+), 15 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index a2c4a28c46a3..2b4b902cffc4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -138,11 +138,13 @@ struct iwl_fw_ini_debug_flow_tlv { * @region_id: ID of this dump configuration * @region_type: &enum iwl_fw_ini_region_type * @num_regions: amount of regions in the address array. - * @allocation_id: For DRAM type field substitutes for allocation_id. * @name_len: name length * @name: file name to use for this region - * @size: size of the data, in bytes.(unused for IWL_FW_INI_REGION_DRAM_BUFFER) - * @start_addr: array of addresses. (unused for IWL_FW_INI_REGION_DRAM_BUFFER) + * @num_of_range: the amount of ranges in the region. + * @allocation_id: For DRAM type field substitutes for allocation_id + * @range_data_size: size of the data to read per range, in bytes. + * @start_addr: array of addresses. for type IWL_FW_INI_REGION_DRAM_BUFFER, + * 1 entry. For any other case, num_of_ranges entries. */ struct iwl_fw_ini_region_cfg { __le32 region_id; @@ -150,10 +152,10 @@ struct iwl_fw_ini_region_cfg { __le32 name_len; u8 name[IWL_FW_INI_MAX_NAME]; union { - __le32 num_regions; + __le32 num_of_ranges; __le32 allocation_id; }; - __le32 size; + __le32 range_data_size; __le32 start_addr[]; } __packed; /* FW_DEBUG_TLV_REGION_CONFIG_S */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 22efd94da6d3..35c2b1a23d10 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1065,7 +1065,7 @@ static void iwl_dump_prph_ini(struct iwl_trans *trans, { struct iwl_fw_error_dump_prph *prph; unsigned long flags; - u32 i, size = le32_to_cpu(reg->num_regions); + u32 i, size = le32_to_cpu(reg->num_of_ranges); IWL_DEBUG_INFO(trans, "WRT PRPH dump\n"); @@ -1074,7 +1074,7 @@ static void iwl_dump_prph_ini(struct iwl_trans *trans, for (i = 0; i < size; i++) { (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); - (*data)->len = cpu_to_le32(le32_to_cpu(reg->size) + + (*data)->len = cpu_to_le32(le32_to_cpu(reg->range_data_size) + sizeof(*prph)); prph = (void *)(*data)->data; prph->prph_start = reg->start_addr[i]; @@ -1089,8 +1089,8 @@ static void iwl_dump_csr_ini(struct iwl_trans *trans, struct iwl_fw_error_dump_data **data, struct iwl_fw_ini_region_cfg *reg) { - int i, num = le32_to_cpu(reg->num_regions); - u32 size = le32_to_cpu(reg->size); + int i, num = le32_to_cpu(reg->num_of_ranges); + u32 size = le32_to_cpu(reg->range_data_size); IWL_DEBUG_INFO(trans, "WRT CSR dump\n"); @@ -1133,13 +1133,13 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, continue; type = le32_to_cpu(reg->region_type); - num_entries = le32_to_cpu(reg->num_regions); + num_entries = le32_to_cpu(reg->num_of_ranges); switch (type) { case IWL_FW_INI_REGION_DEVICE_MEMORY: size += hdr_len + sizeof(struct iwl_fw_error_dump_named_mem) + - le32_to_cpu(reg->size); + le32_to_cpu(reg->range_data_size); break; case IWL_FW_INI_REGION_PERIPHERY_MAC: case IWL_FW_INI_REGION_PERIPHERY_PHY: @@ -1165,7 +1165,7 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, break; case IWL_FW_INI_REGION_CSR: size += num_entries * - (hdr_len + le32_to_cpu(reg->size)); + (hdr_len + le32_to_cpu(reg->range_data_size)); break; case IWL_FW_INI_REGION_DRAM_BUFFER: /* Transport takes care of DRAM dumping */ @@ -1202,10 +1202,10 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, type = le32_to_cpu(reg->region_type); switch (type) { case IWL_FW_INI_REGION_DEVICE_MEMORY: - if (WARN_ON(le32_to_cpu(reg->num_regions) > 1)) + if (WARN_ON(le32_to_cpu(reg->num_of_ranges) > 1)) continue; iwl_fw_dump_named_mem(fwrt, data, - le32_to_cpu(reg->size), + le32_to_cpu(reg->range_data_size), le32_to_cpu(reg->start_addr[0]), reg->name, le32_to_cpu(reg->name_len)); @@ -1773,7 +1773,8 @@ static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt, if (le32_to_cpu(reg->region_type) != IWL_FW_INI_REGION_DRAM_BUFFER) - iter += le32_to_cpu(reg->num_regions) * sizeof(__le32); + iter += le32_to_cpu(reg->num_of_ranges) * + sizeof(__le32); iter += sizeof(*reg); } From 7e316572c9c547d5c8b9d86abb37576e2e5a470c Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Tue, 20 Nov 2018 09:46:33 +0200 Subject: [PATCH 05/38] iwlwifi: fix bad dma handling in page_mem dumping flow Prior to gen2 we allocate the paging memory via alloc_pages which requires passing ownership on the memory between the cpu and the device using dma_sync_single_for_cpu and dma_sync_single_for_device. Add missing dma_sync_single_for_device in iwl_dump_paging after copying the memory. since gen2, we allocate the paging memory using dma_alloc_coherent which does not need passing ownership between the cpu and device. Remove unneeded call to dma_sync_single_for_cpu in iwl_trans_pcie_dump_data prior to copying the memory. Signed-off-by: Shahar S Matityahu Fixes: 5538409ba393 ("iwlwifi: pcie: support page dumping in wrt in gen2") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 3 +++ drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 35c2b1a23d10..d16a8a4f0c3f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -815,6 +815,9 @@ static void iwl_dump_paging(struct iwl_fw_runtime *fwrt, DMA_BIDIRECTIONAL); memcpy(paging->data, page_address(pages), PAGING_BLOCK_SIZE); + dma_sync_single_for_device(fwrt->trans->dev, addr, + PAGING_BLOCK_SIZE, + DMA_BIDIRECTIONAL); (*data) = iwl_fw_error_next_data(*data); } } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index f74281508197..f26664b0f0f8 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -3277,16 +3277,12 @@ static struct iwl_trans_dump_data if (trans->cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) { for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) { struct iwl_fw_error_dump_paging *paging; - dma_addr_t addr = - trans_pcie->init_dram.paging[i].physical; u32 page_len = trans_pcie->init_dram.paging[i].size; data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); data->len = cpu_to_le32(sizeof(*paging) + page_len); paging = (void *)data->data; paging->index = cpu_to_le32(i); - dma_sync_single_for_cpu(trans->dev, addr, page_len, - DMA_BIDIRECTIONAL); memcpy(paging->data, trans_pcie->init_dram.paging[i].block, page_len); data = iwl_fw_error_next_data(data); From b2c1bf597ffa7bab523990763fd50a692b53a48a Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Fri, 16 Nov 2018 11:18:26 +0200 Subject: [PATCH 06/38] iwlwifi: mvm: simplify some return conditions Simplify some return conditions found by running a semantic patch to detect unnecessary assignments to local variables. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/coex.c | 7 ++----- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 5 ++--- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 9 +++------ 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 730e37744dc0..3d2abbc5c76c 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -241,7 +241,6 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, struct iwl_bt_coex_reduced_txp_update_cmd cmd = {}; struct iwl_mvm_sta *mvmsta; u32 value; - int ret; mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); if (!mvmsta) @@ -262,10 +261,8 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, cmd.reduced_txp = cpu_to_le32(value); mvmsta->bt_reduced_txpower = enable; - ret = iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, CMD_ASYNC, - sizeof(cmd), &cmd); - - return ret; + return iwl_mvm_send_cmd_pdu(mvm, BT_COEX_UPDATE_REDUCED_TXP, + CMD_ASYNC, sizeof(cmd), &cmd); } struct iwl_bt_iterator_data { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index 86d598d5b68f..af8156ba8084 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1976,9 +1976,8 @@ static int iwl_mvm_scan_stop_wait(struct iwl_mvm *mvm, int type) return ret; } - ret = iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, 1 * HZ); - - return ret; + return iwl_wait_notification(&mvm->notif_wait, &wait_scan_done, + 1 * HZ); } int iwl_mvm_scan_size(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index c5a01470a3bc..a102a16a15f6 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -314,7 +314,6 @@ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue, struct iwl_mvm_sta *mvmsta; u32 status; u8 sta_id; - int ret; if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) return -EINVAL; @@ -349,11 +348,9 @@ static int iwl_mvm_invalidate_sta_queue(struct iwl_mvm *mvm, int queue, /* Notify FW of queue removal from the STA queues */ status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, - iwl_mvm_add_sta_cmd_size(mvm), - &cmd, &status); - - return ret; + return iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA, + iwl_mvm_add_sta_cmd_size(mvm), + &cmd, &status); } static int iwl_mvm_disable_txq(struct iwl_mvm *mvm, struct ieee80211_sta *sta, From 74a102521208554e0daf52b0aeb742c066ad1e34 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 25 Oct 2018 14:12:05 +0300 Subject: [PATCH 07/38] iwlwifi: mvm: support CHANNEL_SWITCH_TIME_EVENT_CMD command When we do channel switch, we used to schedule time events ourselves. This was offloaded to FW. Support the new command and flow. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/fw/api/mac-cfg.h | 27 +++++++ drivers/net/wireless/intel/iwlwifi/fw/file.h | 2 + .../net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 58 +++++++++------ .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 73 ++++++++++++++----- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 5 files changed, 120 insertions(+), 41 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index ca49db786ed6..6b4d59daacd6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -73,6 +73,10 @@ enum iwl_mac_conf_subcmd_ids { * @LOW_LATENCY_CMD: &struct iwl_mac_low_latency_cmd */ LOW_LATENCY_CMD = 0x3, + /** + * @CHANNEL_SWITCH_TIME_EVENT_CMD: &struct iwl_chan_switch_te_cmd + */ + CHANNEL_SWITCH_TIME_EVENT_CMD = 0x4, /** * @PROBE_RESPONSE_DATA_NOTIF: &struct iwl_probe_resp_data_notif */ @@ -135,6 +139,29 @@ struct iwl_channel_switch_noa_notif { __le32 id_and_color; } __packed; /* CHANNEL_SWITCH_START_NTFY_API_S_VER_1 */ +/** + * struct iwl_chan_switch_te_cmd - Channel Switch Time Event command + * + * @mac_id: MAC ID for channel switch + * @action: action to perform, one of FW_CTXT_ACTION_* + * @tsf: beacon tsf + * @cs_count: channel switch count from CSA/eCSA IE + * @cs_delayed_bcn_count: if set to N (!= 0) GO/AP can delay N beacon intervals + * at the new channel after the channel switch, otherwise (N == 0) expect + * beacon right after the channel switch. + * @cs_mode: 1 - quiet, 0 - otherwise + * @reserved: reserved for alignment purposes + */ +struct iwl_chan_switch_te_cmd { + __le32 mac_id; + __le32 action; + __le32 tsf; + u8 cs_count; + u8 cs_delayed_bcn_count; + u8 cs_mode; + u8 reserved; +} __packed; /* MAC_CHANNEL_SWITCH_TIME_EVENT_S_VER_2 */ + /** * struct iwl_mac_low_latency_cmd - set/clear mac to 'low-latency mode' * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index e8b00b795cbb..db392cfc2bd4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -333,6 +333,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_TLC_OFFLOAD: firmware implements rate scaling algorithm * @IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA: firmware implements quota related * @IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2: firmware implements Coex Schema 2 + * IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD: firmware supports CSA command * @IWL_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS: firmware supports ultra high band * (6 GHz). * @IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE: extended DTS measurement @@ -397,6 +398,7 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_TLC_OFFLOAD = (__force iwl_ucode_tlv_capa_t)43, IWL_UCODE_TLV_CAPA_DYNAMIC_QUOTA = (__force iwl_ucode_tlv_capa_t)44, IWL_UCODE_TLV_CAPA_COEX_SCHEMA_2 = (__force iwl_ucode_tlv_capa_t)45, + IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD = (__force iwl_ucode_tlv_capa_t)46, IWL_UCODE_TLV_CAPA_ULTRA_HB_CHANNELS = (__force iwl_ucode_tlv_capa_t)48, IWL_UCODE_TLV_CAPA_EXTENDED_DTS_MEASURE = (__force iwl_ucode_tlv_capa_t)64, IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS = (__force iwl_ucode_tlv_capa_t)65, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 7cfdd07d8736..768a87e0a1c3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1539,42 +1539,58 @@ void iwl_mvm_channel_switch_noa_notif(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_channel_switch_noa_notif *notif = (void *)pkt->data; - struct ieee80211_vif *csa_vif; + struct ieee80211_vif *csa_vif, *vif; struct iwl_mvm_vif *mvmvif; int len = iwl_rx_packet_payload_len(pkt); - u32 id_n_color; + u32 id_n_color, csa_id, mac_id; 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); + mac_id = id_n_color & FW_CTXT_ID_MSK; - 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; + if (WARN_ON_ONCE(mac_id >= NUM_MAC_INDEX_DRIVER)) + return; - IWL_DEBUG_INFO(mvm, "Channel Switch Started Notification\n"); + rcu_read_lock(); + vif = rcu_dereference(mvm->vif_id_to_mac[mac_id]); - schedule_delayed_work(&mvm->cs_tx_unblock_dwork, - msecs_to_jiffies(IWL_MVM_CS_UNBLOCK_TX_TIMEOUT * - csa_vif->bss_conf.beacon_int)); + switch (vif->type) { + case NL80211_IFTYPE_AP: + csa_vif = rcu_dereference(mvm->csa_vif); + if (WARN_ON(!csa_vif || !csa_vif->csa_active || + csa_vif != vif)) + goto out_unlock; - ieee80211_csa_finish(csa_vif); + mvmvif = iwl_mvm_vif_from_mac80211(csa_vif); + csa_id = FW_CMD_ID_AND_COLOR(mvmvif->id, mvmvif->color); + if (WARN(csa_id != id_n_color, + "channel switch noa notification on unexpected vif (csa_vif=%d, notif=%d)", + csa_id, id_n_color)) + goto out_unlock; - rcu_read_unlock(); + IWL_DEBUG_INFO(mvm, "Channel Switch Started Notification\n"); - RCU_INIT_POINTER(mvm->csa_vif, NULL); + schedule_delayed_work(&mvm->cs_tx_unblock_dwork, + msecs_to_jiffies(IWL_MVM_CS_UNBLOCK_TX_TIMEOUT * + csa_vif->bss_conf.beacon_int)); - return; + ieee80211_csa_finish(csa_vif); + rcu_read_unlock(); + + RCU_INIT_POINTER(mvm->csa_vif, NULL); + return; + case NL80211_IFTYPE_STATION: + iwl_mvm_csa_client_absent(mvm, vif); + ieee80211_chswitch_done(vif, true); + break; + default: + /* should never happen */ + WARN_ON_ONCE(1); + break; + } 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 c9effd7f0ef8..8837d7667f36 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3953,25 +3953,30 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, } if (switching_chanctx && vif->type == NL80211_IFTYPE_STATION) { - u32 duration = 3 * vif->bss_conf.beacon_int; - - /* iwl_mvm_protect_session() reads directly from the - * device (the system time), so make sure it is - * available. - */ - ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA); - if (ret) - goto out_remove_binding; - - /* Protect the session to make sure we hear the first - * beacon on the new channel. - */ mvmvif->csa_bcn_pending = true; - iwl_mvm_protect_session(mvm, vif, duration, duration, - vif->bss_conf.beacon_int / 2, - true); - iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA); + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) { + u32 duration = 3 * vif->bss_conf.beacon_int; + + + /* iwl_mvm_protect_session() reads directly from the + * device (the system time), so make sure it is + * available. + */ + ret = iwl_mvm_ref_sync(mvm, IWL_MVM_REF_PROTECT_CSA); + if (ret) + goto out_remove_binding; + + /* Protect the session to make sure we hear the first + * beacon on the new channel. + */ + iwl_mvm_protect_session(mvm, vif, duration, duration, + vif->bss_conf.beacon_int / 2, + true); + + iwl_mvm_unref(mvm, IWL_MVM_REF_PROTECT_CSA); + } iwl_mvm_update_quotas(mvm, false, NULL); } @@ -4041,7 +4046,9 @@ static void __iwl_mvm_unassign_vif_chanctx(struct iwl_mvm *mvm, disabled_vif = vif; - iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL); + if (!fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) + iwl_mvm_mac_ctxt_changed(mvm, vif, true, NULL); break; default: break; @@ -4292,6 +4299,27 @@ static void iwl_mvm_channel_switch(struct ieee80211_hw *hw, "dummy channel switch op\n"); } +static int iwl_mvm_schedule_client_csa(struct iwl_mvm *mvm, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + struct iwl_chan_switch_te_cmd cmd = { + .mac_id = cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, + mvmvif->color)), + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + .tsf = cpu_to_le32(chsw->timestamp), + .cs_count = chsw->count, + }; + + lockdep_assert_held(&mvm->mutex); + + return iwl_mvm_send_cmd_pdu(mvm, + WIDE_ID(MAC_CONF_GROUP, + CHANNEL_SWITCH_TIME_EVENT_CMD), + 0, sizeof(cmd), &cmd); +} + static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel_switch *chsw) @@ -4359,14 +4387,19 @@ static int iwl_mvm_pre_channel_switch(struct ieee80211_hw *hw, if (chsw->block_tx) iwl_mvm_csa_client_absent(mvm, vif); - iwl_mvm_schedule_csa_period(mvm, vif, vif->bss_conf.beacon_int, - apply_time); if (mvmvif->bf_data.bf_enabled) { ret = iwl_mvm_disable_beacon_filter(mvm, vif, 0); if (ret) goto out_unlock; } + if (fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CHANNEL_SWITCH_CMD)) + iwl_mvm_schedule_client_csa(mvm, vif, chsw); + else + iwl_mvm_schedule_csa_period(mvm, vif, + vif->bss_conf.beacon_int, + apply_time); break; default: break; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 5e4f8b767d10..e56b31692a20 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -421,6 +421,7 @@ static const struct iwl_hcmd_names iwl_mvm_system_names[] = { * Access is done through binary search */ static const struct iwl_hcmd_names iwl_mvm_mac_conf_names[] = { + HCMD_NAME(CHANNEL_SWITCH_TIME_EVENT_CMD), HCMD_NAME(CHANNEL_SWITCH_NOA_NOTIF), }; From 67b8261c49a050dccd776f13b28816dcea226d0c Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Thu, 22 Nov 2018 09:12:08 +0200 Subject: [PATCH 08/38] iwlwifi: differentiate between alive timeout and alive flow failure There are two cases that can cause the alive flow to fail, an assert or a timeout. Currently we mask any incoming asserts when we wait for alive. Solve this by differentiating between the two cases: 1. Let the regular error handling to handle a received assert 2. Do a dump collection in the case of a timeout Signed-off-by: Shahar S Matityahu Fixes: f38efdb29389 ("iwlwifi: add dump collection in case alive flow fails") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 31 +++++++++++-------- drivers/net/wireless/intel/iwlwifi/fw/dbg.h | 2 +- .../wireless/intel/iwlwifi/fw/error-dump.h | 4 +-- .../net/wireless/intel/iwlwifi/fw/runtime.h | 1 - drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 5 +-- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 -- 6 files changed, 24 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index d16a8a4f0c3f..0edc5bcdfb82 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1373,29 +1373,35 @@ void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt) } IWL_EXPORT_SYMBOL(iwl_fw_assert_error_dump); -void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt) +void iwl_fw_alive_timeout_dump(struct iwl_fw_runtime *fwrt) { - struct iwl_fw_dump_desc *iwl_dump_desc_no_alive = - kmalloc(sizeof(*iwl_dump_desc_no_alive), GFP_KERNEL); + struct iwl_fw_dump_desc *iwl_dump_desc_alive_timeout; - if (!iwl_dump_desc_no_alive) + if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status)) return; - iwl_dump_desc_no_alive->trig_desc.type = - cpu_to_le32(FW_DBG_TRIGGER_NO_ALIVE); - iwl_dump_desc_no_alive->len = 0; + iwl_dump_desc_alive_timeout = + kmalloc(sizeof(*iwl_dump_desc_alive_timeout), GFP_KERNEL); + if (!iwl_dump_desc_alive_timeout) + return; + + iwl_dump_desc_alive_timeout->trig_desc.type = + cpu_to_le32(FW_DBG_TRIGGER_ALIVE_TIMEOUT); + iwl_dump_desc_alive_timeout->len = 0; if (WARN_ON(fwrt->dump.desc)) iwl_fw_free_dump_desc(fwrt); IWL_WARN(fwrt, "Collecting data: trigger %d fired.\n", - FW_DBG_TRIGGER_NO_ALIVE); + FW_DBG_TRIGGER_ALIVE_TIMEOUT); - fwrt->dump.desc = iwl_dump_desc_no_alive; + /* set STATUS_FW_ERROR to collect all memory regions. */ + set_bit(STATUS_FW_ERROR, &fwrt->trans->status); + + fwrt->dump.desc = iwl_dump_desc_alive_timeout; iwl_fw_error_dump(fwrt); - clear_bit(IWL_FWRT_STATUS_WAIT_ALIVE, &fwrt->status); } -IWL_EXPORT_SYMBOL(iwl_fw_alive_error_dump); +IWL_EXPORT_SYMBOL(iwl_fw_alive_timeout_dump); int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, const struct iwl_fw_dump_desc *desc, @@ -1418,8 +1424,7 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, fwrt->smem_cfg.num_lmacs) return -EIO; - if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status) || - test_bit(IWL_FWRT_STATUS_WAIT_ALIVE, &fwrt->status)) + if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status)) return -EBUSY; if (WARN_ON(fwrt->dump.desc)) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index 330229d2a61d..36e0f40dab3b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -435,7 +435,7 @@ static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {} #endif /* CONFIG_IWLWIFI_DEBUGFS */ void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt); -void iwl_fw_alive_error_dump(struct iwl_fw_runtime *fwrt); +void iwl_fw_alive_timeout_dump(struct iwl_fw_runtime *fwrt); void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt); void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, enum iwl_fw_ini_apply_point apply_point); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index c02425a1e64f..d06707f3a2a4 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -355,7 +355,7 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) * @FW_DBG_TDLS: trigger log collection upon TDLS related events. * @FW_DBG_TRIGGER_TX_STATUS: trigger log collection upon tx status when * the firmware sends a tx reply. - * @FW_DBG_TRIGGER_NO_ALIVE: trigger log collection if alive flow fails + * @FW_DBG_TRIGGER_ALIVE_TIMEOUT: trigger log collection if alive flow timeouts */ enum iwl_fw_dbg_trigger { FW_DBG_TRIGGER_INVALID = 0, @@ -373,7 +373,7 @@ enum iwl_fw_dbg_trigger { FW_DBG_TRIGGER_TX_LATENCY, FW_DBG_TRIGGER_TDLS, FW_DBG_TRIGGER_TX_STATUS, - FW_DBG_TRIGGER_NO_ALIVE, + FW_DBG_TRIGGER_ALIVE_TIMEOUT, /* must be last */ FW_DBG_TRIGGER_MAX, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index a0fcbb28114b..9ff04ffba3fa 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -90,7 +90,6 @@ struct iwl_fwrt_shared_mem_cfg { enum iwl_fw_runtime_status { IWL_FWRT_STATUS_DUMPING = 0, - IWL_FWRT_STATUS_WAIT_ALIVE, }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index d3dc9d276e0f..25c8cea1180e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -299,7 +299,6 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, enum iwl_ucode_type old_type = mvm->fwrt.cur_fw_img; static const u16 alive_cmd[] = { MVM_ALIVE }; - set_bit(IWL_FWRT_STATUS_WAIT_ALIVE, &mvm->fwrt.status); if (ucode_type == IWL_UCODE_REGULAR && iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE) && !(fw_has_capa(&mvm->fw->ucode_capa, @@ -332,6 +331,9 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, if (ret) { struct iwl_trans *trans = mvm->trans; + if (ret == -ETIMEDOUT) + iwl_fw_alive_timeout_dump(&mvm->fwrt); + if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000) IWL_ERR(mvm, "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", @@ -377,7 +379,6 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, #ifdef CONFIG_IWLWIFI_DEBUGFS iwl_fw_set_dbg_rec_on(&mvm->fwrt); #endif - clear_bit(IWL_FWRT_STATUS_WAIT_ALIVE, &mvm->fwrt.status); return 0; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index e56b31692a20..a2719a75249a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -818,8 +818,6 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mutex_lock(&mvm->mutex); iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE); err = iwl_run_init_mvm_ucode(mvm, true); - if (test_bit(IWL_FWRT_STATUS_WAIT_ALIVE, &mvm->fwrt.status)) - iwl_fw_alive_error_dump(&mvm->fwrt); if (!iwlmvm_mod_params.init_dbg || !err) iwl_mvm_stop_device(mvm); iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE); From ffb677dc9816c3431ad13dc893810efa9c5f34c6 Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Thu, 29 Nov 2018 17:53:09 +0200 Subject: [PATCH 09/38] iwlwifi: dbg_ini: update ini triggers enum Align the triggers enum in the driver to the one in the FW. Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/fw/api/dbg-tlv.h | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index 2b4b902cffc4..422f4333ba35 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -217,15 +217,12 @@ struct iwl_fw_ini_trigger_tlv { /** * enum iwl_fw_ini_trigger_id * @IWL_FW_TRIGGER_ID_FW_ASSERT: FW assert - * @IWL_FW_TRIGGER_ID_FW_TFD_Q_HANG: TFD queue hang * @IWL_FW_TRIGGER_ID_FW_HW_ERROR: HW assert - * @IWL_FW_TRIGGER_ID_FW_TRIGGER_ERROR: FW error notification - * @IWL_FW_TRIGGER_ID_FW_TRIGGER_WARNING: FW warning notification - * @IWL_FW_TRIGGER_ID_FW_TRIGGER_INFO: FW info notification - * @IWL_FW_TRIGGER_ID_FW_TRIGGER_DEBUG: FW debug notification + * @IWL_FW_TRIGGER_ID_FW_TFD_Q_HANG: TFD queue hang + * @IWL_FW_TRIGGER_ID_FW_DEBUG_HOST_TRIGGER: FW debug notification + * @IWL_FW_TRIGGER_ID_FW_GENERIC_NOTIFOCATION: FW generic notification * @IWL_FW_TRIGGER_ID_USER_TRIGGER: User trigger * @IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_INACTIVITY: peer inactivity - * @FW_DEBUG_TLV_TRIGGER_ID_HOST_DID_INITIATED_EVENT: undefined * @IWL_FW_TRIGGER_ID_HOST_TX_LATENCY_THRESHOLD_CROSSED: TX latency * threshold was crossed * @IWL_FW_TRIGGER_ID_HOST_TX_RESPONSE_STATUS_FAILED: TX failed @@ -259,50 +256,53 @@ struct iwl_fw_ini_trigger_tlv { * @IWL_FW_TRIGGER_ID_NUM: number of trigger IDs */ enum iwl_fw_ini_trigger_id { + IWL_FW_TRIGGER_ID_INVALID = 0, + /* Errors triggers */ IWL_FW_TRIGGER_ID_FW_ASSERT = 1, - IWL_FW_TRIGGER_ID_FW_TFD_Q_HANG = 2, - IWL_FW_TRIGGER_ID_FW_HW_ERROR = 3, - /* Generic triggers */ - IWL_FW_TRIGGER_ID_FW_TRIGGER_ERROR = 4, - IWL_FW_TRIGGER_ID_FW_TRIGGER_WARNING = 5, - IWL_FW_TRIGGER_ID_FW_TRIGGER_INFO = 6, - IWL_FW_TRIGGER_ID_FW_TRIGGER_DEBUG = 7, - /* User Trigger */ - IWL_FW_TRIGGER_ID_USER_TRIGGER = 8, + IWL_FW_TRIGGER_ID_FW_HW_ERROR = 2, + IWL_FW_TRIGGER_ID_FW_TFD_Q_HANG = 3, + + /* FW triggers */ + IWL_FW_TRIGGER_ID_FW_DEBUG_HOST_TRIGGER = 4, + IWL_FW_TRIGGER_ID_FW_GENERIC_NOTIFOCATION = 5, + + /* User trigger */ + IWL_FW_TRIGGER_ID_USER_TRIGGER = 6, + /* Host triggers */ - IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_INACTIVITY = 9, - IWL_FW_TRIGGER_ID_HOST_DID_INITIATED_EVENT = 10, - IWL_FW_TRIGGER_ID_HOST_TX_LATENCY_THRESHOLD_CROSSED = 11, - IWL_FW_TRIGGER_ID_HOST_TX_RESPONSE_STATUS_FAILED = 12, - IWL_FW_TRIGGER_ID_HOST_OS_REQ_DEAUTH_PEER = 13, - IWL_FW_TRIGGER_ID_HOST_STOP_GO_REQUEST = 14, - IWL_FW_TRIGGER_ID_HOST_START_GO_REQUEST = 15, - IWL_FW_TRIGGER_ID_HOST_JOIN_GROUP_REQUEST = 16, - IWL_FW_TRIGGER_ID_HOST_SCAN_START = 17, - IWL_FW_TRIGGER_ID_HOST_SCAN_SUBITTED = 18, - IWL_FW_TRIGGER_ID_HOST_SCAN_PARAMS = 19, - IWL_FW_TRIGGER_ID_HOST_CHECK_FOR_HANG = 20, - IWL_FW_TRIGGER_ID_HOST_BAR_RECEIVED = 21, - IWL_FW_TRIGGER_ID_HOST_AGG_TX_RESPONSE_STATUS_FAILED = 22, - IWL_FW_TRIGGER_ID_HOST_EAPOL_TX_RESPONSE_FAILED = 23, - IWL_FW_TRIGGER_ID_HOST_FAKE_TX_RESPONSE_SUSPECTED = 24, - IWL_FW_TRIGGER_ID_HOST_AUTH_REQ_FROM_ASSOC_CLIENT = 25, - IWL_FW_TRIGGER_ID_HOST_ROAM_COMPLETE = 26, - IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAST_FAILED = 27, - IWL_FW_TRIGGER_ID_HOST_D3_START = 28, - IWL_FW_TRIGGER_ID_HOST_D3_END = 29, - IWL_FW_TRIGGER_ID_HOST_BSS_MISSED_BEACONS = 30, - IWL_FW_TRIGGER_ID_HOST_P2P_CLIENT_MISSED_BEACONS = 31, - IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_TX_FAILURES = 32, - IWL_FW_TRIGGER_ID_HOST_TX_WFD_ACTION_FRAME_FAILED = 33, - IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAILED = 34, - IWL_FW_TRIGGER_ID_HOST_SCAN_COMPLETE = 35, - IWL_FW_TRIGGER_ID_HOST_SCAN_ABORT = 36, - IWL_FW_TRIGGER_ID_HOST_NIC_ALIVE = 37, - IWL_FW_TRIGGER_ID_HOST_CHANNEL_SWITCH_COMPLETE = 38, + IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_INACTIVITY = 7, + IWL_FW_TRIGGER_ID_HOST_TX_LATENCY_THRESHOLD_CROSSED = 8, + IWL_FW_TRIGGER_ID_HOST_TX_RESPONSE_STATUS_FAILED = 9, + IWL_FW_TRIGGER_ID_HOST_OS_REQ_DEAUTH_PEER = 10, + IWL_FW_TRIGGER_ID_HOST_STOP_GO_REQUEST = 11, + IWL_FW_TRIGGER_ID_HOST_START_GO_REQUEST = 12, + IWL_FW_TRIGGER_ID_HOST_JOIN_GROUP_REQUEST = 13, + IWL_FW_TRIGGER_ID_HOST_SCAN_START = 14, + IWL_FW_TRIGGER_ID_HOST_SCAN_SUBMITTED = 15, + IWL_FW_TRIGGER_ID_HOST_SCAN_PARAMS = 16, + IWL_FW_TRIGGER_ID_HOST_CHECK_FOR_HANG = 17, + IWL_FW_TRIGGER_ID_HOST_BAR_RECEIVED = 18, + IWL_FW_TRIGGER_ID_HOST_AGG_TX_RESPONSE_STATUS_FAILED = 19, + IWL_FW_TRIGGER_ID_HOST_EAPOL_TX_RESPONSE_FAILED = 20, + IWL_FW_TRIGGER_ID_HOST_FAKE_TX_RESPONSE_SUSPECTED = 21, + IWL_FW_TRIGGER_ID_HOST_AUTH_REQ_FROM_ASSOC_CLIENT = 22, + IWL_FW_TRIGGER_ID_HOST_ROAM_COMPLETE = 23, + IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAST_FAILED = 24, + IWL_FW_TRIGGER_ID_HOST_D3_START = 25, + IWL_FW_TRIGGER_ID_HOST_D3_END = 26, + IWL_FW_TRIGGER_ID_HOST_BSS_MISSED_BEACONS = 27, + IWL_FW_TRIGGER_ID_HOST_P2P_CLIENT_MISSED_BEACONS = 28, + IWL_FW_TRIGGER_ID_HOST_PEER_CLIENT_TX_FAILURES = 29, + IWL_FW_TRIGGER_ID_HOST_TX_WFD_ACTION_FRAME_FAILED = 30, + IWL_FW_TRIGGER_ID_HOST_AUTH_ASSOC_FAILED = 31, + IWL_FW_TRIGGER_ID_HOST_SCAN_COMPLETE = 32, + IWL_FW_TRIGGER_ID_HOST_SCAN_ABORT = 33, + IWL_FW_TRIGGER_ID_HOST_NIC_ALIVE = 34, + IWL_FW_TRIGGER_ID_HOST_CHANNEL_SWITCH_COMPLETE = 35, + IWL_FW_TRIGGER_ID_NUM, -}; /* FW_INI_TRIGGER_ID_E_VER_1 */ +}; /* FW_DEBUG_TLV_TRIGGER_ID_E_VER_1 */ /** * enum iwl_fw_ini_apply_point From 2d0c66083d3f9d17e5c107c4427523d3acdd31ce Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Tue, 27 Nov 2018 07:42:33 +0200 Subject: [PATCH 10/38] iwlwifi: dbg_ini: update max region id num to support 64 regions The driver should support 64 region ids. Update IWL_FW_INI_MAX_REGION_ID accordingly. Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index 422f4333ba35..b2ab675b3cf8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -131,7 +131,7 @@ struct iwl_fw_ini_debug_flow_tlv { __le32 debug_flow_cfg; } __packed; /* FW_INI_DEBUG_FLOW_TLV_S_VER_1 */ -#define IWL_FW_INI_MAX_REGION_ID 20 +#define IWL_FW_INI_MAX_REGION_ID 64 #define IWL_FW_INI_MAX_NAME 32 /** * struct iwl_fw_ini_region_cfg From 15e28c78c3864cd7694bc7fca5142ff1f3f8d0e7 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 26 Nov 2018 13:24:55 +0200 Subject: [PATCH 11/38] iwlwifi: mvm: support new format for the beacon notification The firmware is changing the format of the beacon notification to remove the dependency on the Tx response format. Signed-off-by: Emmanuel Grumbach Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/fw/api/tx.h | 18 ++++++++-- drivers/net/wireless/intel/iwlwifi/fw/file.h | 3 ++ .../net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 33 ++++++++++++------- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 6 ++++ 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index 358bdf051e83..8511e735c374 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -847,19 +847,33 @@ struct iwl_beacon_notif { } __packed; /** - * struct iwl_extended_beacon_notif - notifies about beacon transmission + * struct iwl_extended_beacon_notif_v5 - notifies about beacon transmission * @beacon_notify_hdr: tx response command associated with the beacon * @tsf: last beacon tsf * @ibss_mgr_status: whether IBSS is manager * @gp2: last beacon time in gp2 */ -struct iwl_extended_beacon_notif { +struct iwl_extended_beacon_notif_v5 { struct iwl_mvm_tx_resp beacon_notify_hdr; __le64 tsf; __le32 ibss_mgr_status; __le32 gp2; } __packed; /* BEACON_NTFY_API_S_VER_5 */ +/** + * struct iwl_extended_beacon_notif - notifies about beacon transmission + * @status: the status of the Tx response of the beacon + * @tsf: last beacon tsf + * @ibss_mgr_status: whether IBSS is manager + * @gp2: last beacon time in gp2 + */ +struct iwl_extended_beacon_notif { + __le32 status; + __le64 tsf; + __le32 ibss_mgr_status; + __le32 gp2; +} __packed; /* BEACON_NTFY_API_S_VER_6_ */ + /** * enum iwl_dump_control - dump (flush) control flags * @DUMP_TX_FIFO_FLUSH: Dump MSDUs until the the FIFO is empty diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index db392cfc2bd4..a6dd27fbe4aa 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -263,6 +263,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * @IWL_UCODE_TLV_API_FRAG_EBS: This ucode supports fragmented EBS * @IWL_UCODE_TLV_API_REDUCE_TX_POWER: This ucode supports v5 of * the REDUCE_TX_POWER_CMD. + * @IWL_UCODE_TLV_API_SHORT_BEACON_NOTIF: This ucode supports the short + * version of the beacon notification. * * @NUM_IWL_UCODE_TLV_API: number of bits used */ @@ -287,6 +289,7 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_ADAPTIVE_DWELL_V2 = (__force iwl_ucode_tlv_api_t)42, IWL_UCODE_TLV_API_FRAG_EBS = (__force iwl_ucode_tlv_api_t)44, IWL_UCODE_TLV_API_REDUCE_TX_POWER = (__force iwl_ucode_tlv_api_t)45, + IWL_UCODE_TLV_API_SHORT_BEACON_NOTIF = (__force iwl_ucode_tlv_api_t)46, NUM_IWL_UCODE_TLV_API #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 768a87e0a1c3..c1eb07c08827 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1330,7 +1330,7 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_extended_beacon_notif *beacon = (void *)pkt->data; - struct iwl_mvm_tx_resp *beacon_notify_hdr; + struct iwl_extended_beacon_notif_v5 *beacon_v5 = (void *)pkt->data; struct ieee80211_vif *csa_vif; struct ieee80211_vif *tx_blocked_vif; struct agg_tx_status *agg_status; @@ -1338,18 +1338,29 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm, lockdep_assert_held(&mvm->mutex); - beacon_notify_hdr = &beacon->beacon_notify_hdr; mvm->ap_last_beacon_gp2 = le32_to_cpu(beacon->gp2); - mvm->ibss_manager = beacon->ibss_mgr_status != 0; - agg_status = iwl_mvm_get_agg_status(mvm, beacon_notify_hdr); - status = le16_to_cpu(agg_status->status) & TX_STATUS_MSK; - IWL_DEBUG_RX(mvm, - "beacon status %#x retries:%d tsf:0x%016llX gp2:0x%X rate:%d\n", - status, beacon_notify_hdr->failure_frame, - le64_to_cpu(beacon->tsf), - mvm->ap_last_beacon_gp2, - le32_to_cpu(beacon_notify_hdr->initial_rate)); + if (!iwl_mvm_is_short_beacon_notif_supported(mvm)) { + struct iwl_mvm_tx_resp *beacon_notify_hdr = + &beacon_v5->beacon_notify_hdr; + + mvm->ibss_manager = beacon_v5->ibss_mgr_status != 0; + agg_status = iwl_mvm_get_agg_status(mvm, beacon_notify_hdr); + status = le16_to_cpu(agg_status->status) & TX_STATUS_MSK; + IWL_DEBUG_RX(mvm, + "beacon status %#x retries:%d tsf:0x%016llX gp2:0x%X rate:%d\n", + status, beacon_notify_hdr->failure_frame, + le64_to_cpu(beacon->tsf), + mvm->ap_last_beacon_gp2, + le32_to_cpu(beacon_notify_hdr->initial_rate)); + } else { + mvm->ibss_manager = beacon->ibss_mgr_status != 0; + status = le32_to_cpu(beacon->status) & TX_STATUS_MSK; + IWL_DEBUG_RX(mvm, + "beacon status %#x tsf:0x%016llX gp2:0x%X\n", + status, le64_to_cpu(beacon->tsf), + mvm->ap_last_beacon_gp2); + } csa_vif = rcu_dereference_protected(mvm->csa_vif, lockdep_is_held(&mvm->mutex)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index 12e9ecc3ee27..f7176020139d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1311,6 +1311,12 @@ static inline bool iwl_mvm_is_frag_ebs_supported(struct iwl_mvm *mvm) return fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FRAG_EBS); } +static inline bool iwl_mvm_is_short_beacon_notif_supported(struct iwl_mvm *mvm) +{ + return fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_SHORT_BEACON_NOTIF); +} + static inline bool iwl_mvm_enter_d0i3_on_suspend(struct iwl_mvm *mvm) { /* For now we only use this mode to differentiate between From 6032c062723ce5a23eccb7d2bf8c0a2985424f27 Mon Sep 17 00:00:00 2001 From: YueHaibing Date: Fri, 30 Nov 2018 19:24:26 +0800 Subject: [PATCH 12/38] iwlwifi: mvm: remove duplicated include from ops.c Remove duplicated include. Signed-off-by: YueHaibing Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index a2719a75249a..de288962773e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -82,7 +82,6 @@ #include "fw/api/scan.h" #include "time-event.h" #include "fw-api.h" -#include "fw/api/scan.h" #include "fw/acpi.h" #define DRV_DESCRIPTION "The new Intel(R) wireless AGN driver for Linux" From 700b3799b398d00320f40ef1a40d3fe341f98678 Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Mon, 12 Nov 2018 13:27:51 +0200 Subject: [PATCH 13/38] iwlwifi: Fix pre operational dumping flows There are several dumping flows in the driver in case of a fail prior to operational. In some cases we get 2 dumps while in others we get none. Fix this by uniting the different flows. Add a different dump type to driver triggered dumps in case we want a dump but did not got assert, and make all dumping go through iwl_fw_dbg_collect_desc to avoid multiple dumps. Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 89 +++++++++++-------- drivers/net/wireless/intel/iwlwifi/fw/dbg.h | 5 +- .../wireless/intel/iwlwifi/fw/error-dump.h | 3 + drivers/net/wireless/intel/iwlwifi/fw/init.c | 3 + .../net/wireless/intel/iwlwifi/iwl-trans.h | 13 +++ drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 6 +- drivers/net/wireless/intel/iwlwifi/mvm/mvm.h | 9 +- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 2 + 8 files changed, 81 insertions(+), 49 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 0edc5bcdfb82..f8cf12804aee 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1365,44 +1365,6 @@ const struct iwl_fw_dump_desc iwl_dump_desc_assert = { }; IWL_EXPORT_SYMBOL(iwl_dump_desc_assert); -void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt) -{ - IWL_INFO(fwrt, "error dump due to fw assert\n"); - fwrt->dump.desc = &iwl_dump_desc_assert; - iwl_fw_error_dump(fwrt); -} -IWL_EXPORT_SYMBOL(iwl_fw_assert_error_dump); - -void iwl_fw_alive_timeout_dump(struct iwl_fw_runtime *fwrt) -{ - struct iwl_fw_dump_desc *iwl_dump_desc_alive_timeout; - - if (test_and_set_bit(IWL_FWRT_STATUS_DUMPING, &fwrt->status)) - return; - - iwl_dump_desc_alive_timeout = - kmalloc(sizeof(*iwl_dump_desc_alive_timeout), GFP_KERNEL); - if (!iwl_dump_desc_alive_timeout) - return; - - iwl_dump_desc_alive_timeout->trig_desc.type = - cpu_to_le32(FW_DBG_TRIGGER_ALIVE_TIMEOUT); - iwl_dump_desc_alive_timeout->len = 0; - - if (WARN_ON(fwrt->dump.desc)) - iwl_fw_free_dump_desc(fwrt); - - IWL_WARN(fwrt, "Collecting data: trigger %d fired.\n", - FW_DBG_TRIGGER_ALIVE_TIMEOUT); - - /* set STATUS_FW_ERROR to collect all memory regions. */ - set_bit(STATUS_FW_ERROR, &fwrt->trans->status); - - fwrt->dump.desc = iwl_dump_desc_alive_timeout; - iwl_fw_error_dump(fwrt); -} -IWL_EXPORT_SYMBOL(iwl_fw_alive_timeout_dump); - int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, const struct iwl_fw_dump_desc *desc, bool monitor_only, @@ -1442,6 +1404,33 @@ int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, } IWL_EXPORT_SYMBOL(iwl_fw_dbg_collect_desc); +int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt, + enum iwl_fw_dbg_trigger trig_type) +{ + int ret; + struct iwl_fw_dump_desc *iwl_dump_error_desc = + kmalloc(sizeof(*iwl_dump_error_desc), GFP_KERNEL); + + if (!iwl_dump_error_desc) + return -ENOMEM; + + iwl_dump_error_desc->trig_desc.type = cpu_to_le32(trig_type); + iwl_dump_error_desc->len = 0; + + ret = iwl_fw_dbg_collect_desc(fwrt, iwl_dump_error_desc, false, 0); + if (ret) { + kfree(iwl_dump_error_desc); + } else { + set_bit(STATUS_FW_WAIT_DUMP, &fwrt->trans->status); + + /* trigger nmi to halt the fw */ + iwl_force_nmi(fwrt->trans); + } + + return ret; +} +IWL_EXPORT_SYMBOL(iwl_fw_dbg_error_collect); + int _iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, enum iwl_fw_dbg_trigger trig, const char *str, size_t len, @@ -1893,3 +1882,27 @@ void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, _iwl_fw_dbg_apply_point(fwrt, data, apply_point, true); } IWL_EXPORT_SYMBOL(iwl_fw_dbg_apply_point); + +void iwl_fwrt_stop_device(struct iwl_fw_runtime *fwrt) +{ + /* if the wait event timeout elapses instead of wake up then + * the driver did not receive NMI interrupt and can not assume the FW + * is halted + */ + int ret = wait_event_timeout(fwrt->trans->fw_halt_waitq, + !test_bit(STATUS_FW_WAIT_DUMP, + &fwrt->trans->status), + msecs_to_jiffies(2000)); + if (!ret) { + /* failed to receive NMI interrupt, assuming the FW is stuck */ + set_bit(STATUS_FW_ERROR, &fwrt->trans->status); + + clear_bit(STATUS_FW_WAIT_DUMP, &fwrt->trans->status); + } + + /* Assuming the op mode mutex is held at this point */ + iwl_fw_dbg_collect_sync(fwrt); + + iwl_trans_stop_device(fwrt->trans); +} +IWL_EXPORT_SYMBOL(iwl_fwrt_stop_device); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index 36e0f40dab3b..2fa7a19e02b6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -112,6 +112,8 @@ void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt); int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt, const struct iwl_fw_dump_desc *desc, bool monitor_only, unsigned int delay); +int iwl_fw_dbg_error_collect(struct iwl_fw_runtime *fwrt, + enum iwl_fw_dbg_trigger trig_type); int _iwl_fw_dbg_collect(struct iwl_fw_runtime *fwrt, enum iwl_fw_dbg_trigger trig, const char *str, size_t len, @@ -434,10 +436,9 @@ static inline void iwl_fw_resume_timestamp(struct iwl_fw_runtime *fwrt) {} #endif /* CONFIG_IWLWIFI_DEBUGFS */ -void iwl_fw_assert_error_dump(struct iwl_fw_runtime *fwrt); -void iwl_fw_alive_timeout_dump(struct iwl_fw_runtime *fwrt); void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt); void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, enum iwl_fw_ini_apply_point apply_point); +void iwl_fwrt_stop_device(struct iwl_fw_runtime *fwrt); #endif /* __iwl_fw_dbg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index d06707f3a2a4..e1c6aa61ab90 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -356,6 +356,8 @@ iwl_fw_error_next_data(struct iwl_fw_error_dump_data *data) * @FW_DBG_TRIGGER_TX_STATUS: trigger log collection upon tx status when * the firmware sends a tx reply. * @FW_DBG_TRIGGER_ALIVE_TIMEOUT: trigger log collection if alive flow timeouts + * @FW_DBG_TRIGGER_DRIVER: trigger log collection upon a flow failure + * in the driver. */ enum iwl_fw_dbg_trigger { FW_DBG_TRIGGER_INVALID = 0, @@ -374,6 +376,7 @@ enum iwl_fw_dbg_trigger { FW_DBG_TRIGGER_TDLS, FW_DBG_TRIGGER_TX_STATUS, FW_DBG_TRIGGER_ALIVE_TIMEOUT, + FW_DBG_TRIGGER_DRIVER, /* must be last */ FW_DBG_TRIGGER_MAX, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/init.c b/drivers/net/wireless/intel/iwlwifi/fw/init.c index 2efac307909e..7adf4e4e841a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/init.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/init.c @@ -6,6 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -26,6 +27,7 @@ * BSD LICENSE * * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2019 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -74,6 +76,7 @@ void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, fwrt->ops_ctx = ops_ctx; INIT_DELAYED_WORK(&fwrt->dump.wk, iwl_fw_error_dump_wk); iwl_fwrt_dbgfs_register(fwrt, dbgfs_dir); + init_waitqueue_head(&fwrt->trans->fw_halt_waitq); } IWL_EXPORT_SYMBOL(iwl_fw_runtime_init); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index a7009cd4232d..d79025d663df 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -8,6 +8,7 @@ * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 - 2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,6 +31,7 @@ * Copyright(c) 2005 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH + * Copyright(c) 2018 - 2019 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -330,6 +332,7 @@ enum iwl_d3_status { * are sent * @STATUS_TRANS_IDLE: the trans is idle - general commands are not to be sent * @STATUS_TRANS_DEAD: trans is dead - avoid any read/write operation + * @STATUS_FW_WAIT_DUMP: if set, wait until cleared before collecting dump */ enum iwl_trans_status { STATUS_SYNC_HCMD_ACTIVE, @@ -342,6 +345,7 @@ enum iwl_trans_status { STATUS_TRANS_GOING_IDLE, STATUS_TRANS_IDLE, STATUS_TRANS_DEAD, + STATUS_FW_WAIT_DUMP, }; static inline int @@ -796,6 +800,11 @@ struct iwl_trans { bool suspending; bool dbg_rec_on; + u32 lmac_error_event_table[2]; + u32 umac_error_event_table; + unsigned int error_event_table_tlv_status; + wait_queue_head_t fw_halt_waitq; + /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ char trans_specific[0] __aligned(sizeof(void *)); @@ -1202,6 +1211,10 @@ static inline void iwl_trans_fw_error(struct iwl_trans *trans) /* prevent double restarts due to the same erroneous FW */ if (!test_and_set_bit(STATUS_FW_ERROR, &trans->status)) iwl_op_mode_nic_error(trans->op_mode); + + if (test_and_clear_bit(STATUS_FW_WAIT_DUMP, &trans->status)) + wake_up(&trans->fw_halt_waitq); + } /***************************************************** diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 25c8cea1180e..d8ae9561a9b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -332,7 +332,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, struct iwl_trans *trans = mvm->trans; if (ret == -ETIMEDOUT) - iwl_fw_alive_timeout_dump(&mvm->fwrt); + iwl_fw_dbg_error_collect(&mvm->fwrt, + FW_DBG_TRIGGER_ALIVE_TIMEOUT); if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_22000) IWL_ERR(mvm, @@ -408,7 +409,6 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm, bool read_nvm) ret = iwl_mvm_load_ucode_wait_alive(mvm, IWL_UCODE_REGULAR); if (ret) { IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); - iwl_fw_assert_error_dump(&mvm->fwrt); goto error; } @@ -1053,7 +1053,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) ret = iwl_mvm_load_rt_fw(mvm); if (ret) { IWL_ERR(mvm, "Failed to start RT ucode: %d\n", ret); - iwl_fw_assert_error_dump(&mvm->fwrt); + iwl_fw_dbg_error_collect(&mvm->fwrt, FW_DBG_TRIGGER_DRIVER); goto error; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index f7176020139d..ee61f4a00c5e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -2013,15 +2013,12 @@ static inline void iwl_mvm_stop_device(struct iwl_mvm *mvm) &mvm->status)) iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert, false, 0); - /* calling this function without using dump_start/end since at this - * point we already hold the op mode mutex - */ - iwl_fw_dbg_collect_sync(&mvm->fwrt); + iwl_fw_cancel_timestamp(&mvm->fwrt); - iwl_free_fw_paging(&mvm->fwrt); clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); + iwl_fwrt_stop_device(&mvm->fwrt); + iwl_free_fw_paging(&mvm->fwrt); iwl_fw_dump_conf_clear(&mvm->fwrt); - iwl_trans_stop_device(mvm->trans); } /* Re-configure the SCD for a queue that has already been configured */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index de288962773e..ad816007e546 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -817,6 +817,8 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mutex_lock(&mvm->mutex); iwl_mvm_ref(mvm, IWL_MVM_REF_INIT_UCODE); err = iwl_run_init_mvm_ucode(mvm, true); + if (err) + iwl_fw_dbg_error_collect(&mvm->fwrt, FW_DBG_TRIGGER_DRIVER); if (!iwlmvm_mod_params.init_dbg || !err) iwl_mvm_stop_device(mvm); iwl_mvm_unref(mvm, IWL_MVM_REF_INIT_UCODE); From d7febc2e61f91eaa810f1682909928d16f4c6adc Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Thu, 29 Nov 2018 17:55:55 +0200 Subject: [PATCH 14/38] iwlwifi: dbg_ini: update ini structs meta doc Align the documentation of the structs with the latest version in the FW. Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/fw/api/dbg-tlv.h | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index b2ab675b3cf8..4926950d432a 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -70,7 +70,7 @@ struct iwl_fw_ini_header { __le32 tlv_version; __le32 apply_point; u8 data[]; -} __packed; /* FW_INI_HEADER_TLV_S */ +} __packed; /* FW_DEBUG_TLV_HEADER_S */ /** * struct iwl_fw_ini_allocation_tlv - (IWL_FW_INI_TLV_TYPE_BUFFER_ALLOCATION) @@ -92,7 +92,7 @@ struct iwl_fw_ini_allocation_tlv { __le32 size; __le32 max_fragments; __le32 min_frag_size; -} __packed; /* FW_INI_BUFFER_ALLOCATION_TLV_S_VER_1 */ +} __packed; /* FW_DEBUG_TLV_BUFFER_ALLOCATION_TLV_S_VER_1 */ /** * struct iwl_fw_ini_hcmd (IWL_FW_INI_TLV_TYPE_HCMD) @@ -108,7 +108,7 @@ struct iwl_fw_ini_hcmd { u8 group; __le16 padding; u8 data[0]; -} __packed; /* FW_INI_HCMD_S */ +} __packed; /* FW_DEBUG_TLV_HCMD_DATA_S */ /** * struct iwl_fw_ini_hcmd_tlv @@ -118,7 +118,7 @@ struct iwl_fw_ini_hcmd { struct iwl_fw_ini_hcmd_tlv { struct iwl_fw_ini_header header; struct iwl_fw_ini_hcmd hcmd; -} __packed; /* FW_INI_HCMD_TLV_S_VER_1 */ +} __packed; /* FW_DEBUG_TLV_HCMD_S_VER_1 */ /* * struct iwl_fw_ini_debug_flow_tlv (IWL_FW_INI_TLV_TYPE_DEBUG_FLOW) @@ -129,7 +129,7 @@ struct iwl_fw_ini_hcmd_tlv { struct iwl_fw_ini_debug_flow_tlv { struct iwl_fw_ini_header header; __le32 debug_flow_cfg; -} __packed; /* FW_INI_DEBUG_FLOW_TLV_S_VER_1 */ +} __packed; /* FW_DEBUG_TLV_FLOW_TLV_S_VER_1 */ #define IWL_FW_INI_MAX_REGION_ID 64 #define IWL_FW_INI_MAX_NAME 32 @@ -176,7 +176,7 @@ struct iwl_fw_ini_region_tlv { * struct iwl_fw_ini_trigger - (IWL_FW_INI_TLV_TYPE_DUMP_CFG) * Region sections define IDs and triggers that use those IDs TLV * - * @trigger_id: enum &iwl_fw_ini_tigger_id + * @trigger_id: enum &iwl_fw_ini_trigger_id * @ignore_default: override FW TLV with binary TLV * @dump_delay: delay from trigger fire to dump, in usec * @occurrences: max amount of times to be fired @@ -322,7 +322,7 @@ enum iwl_fw_ini_apply_point { IWL_FW_INI_APPLY_MISSED_BEACONS, IWL_FW_INI_APPLY_SCAN_COMPLETE, IWL_FW_INI_APPLY_NUM, -}; /* FW_INI_APPLY_POINT_E_VER_1 */ +}; /* FW_DEBUG_TLV_APPLY_POINT_E_VER_1 */ /** * enum iwl_fw_ini_allocation_id @@ -342,7 +342,7 @@ enum iwl_fw_ini_allocation_id { IWL_FW_INI_ALLOCATION_ID_SDFX, IWL_FW_INI_ALLOCATION_ID_FW_DUMP, IWL_FW_INI_ALLOCATION_ID_USER_DEFINED, -}; /* FW_INI_ALLOCATION_ID_E_VER_1 */ +}; /* FW_DEBUG_TLV_ALLOCATION_ID_E_VER_1 */ /** * enum iwl_fw_ini_buffer_location @@ -354,7 +354,7 @@ enum iwl_fw_ini_buffer_location { IWL_FW_INI_LOCATION_INVALID, IWL_FW_INI_LOCATION_SRAM_PATH, IWL_FW_INI_LOCATION_DRAM_PATH, -}; /* FW_INI_BUFFER_LOCATION_E_VER_1 */ +}; /* FW_DEBUG_TLV_BUFFER_LOCATION_E_VER_1 */ /** * enum iwl_fw_ini_debug_flow @@ -366,7 +366,7 @@ enum iwl_fw_ini_debug_flow { IWL_FW_INI_DEBUG_INVALID, IWL_FW_INI_DEBUG_DBTR_FLOW, IWL_FW_INI_DEBUG_TB2DTF_FLOW, -}; /* FW_INI_DEBUG_FLOW_E_VER_1 */ +}; /* FW_DEBUG_TLV_FLOW_E_VER_1 */ /** * enum iwl_fw_ini_region_type @@ -398,6 +398,6 @@ enum iwl_fw_ini_region_type { IWL_FW_INI_REGION_PAGING, IWL_FW_INI_REGION_CSR, IWL_FW_INI_REGION_NUM -}; /* FW_INI_REGION_TYPE_E_VER_1*/ +}; /* FW_DEBUG_TLV_REGION_TYPE_E_VER_1 */ #endif From 999ced14c783e94f286c72484a01f9947ee94790 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 4 Dec 2018 23:13:15 +0200 Subject: [PATCH 15/38] iwlwifi: dvm: fix some fall through warnings Some case statements were missing a comment about falling through or had more text than the compiler can recognize. Fix that. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/dvm/rx.c | 6 ++++-- drivers/net/wireless/intel/iwlwifi/dvm/scan.c | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c index 6f17a5e24e82..e224b23f0ba8 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c @@ -2,6 +2,7 @@ * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * Portions of this file are derived from the ipw3945 project, as well * as portionhelp of the ieee80211 subsystem header files. @@ -592,7 +593,7 @@ static int iwlagn_set_decrypted_flag(struct iwl_priv *priv, if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == RX_RES_STATUS_BAD_KEY_TTAK) break; - + /* fall through */ case RX_RES_STATUS_SEC_TYPE_WEP: if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == RX_RES_STATUS_BAD_ICV_MIC) { @@ -601,6 +602,7 @@ static int iwlagn_set_decrypted_flag(struct iwl_priv *priv, IWL_DEBUG_RX(priv, "Packet destroyed\n"); return -1; } + /* fall through */ case RX_RES_STATUS_SEC_TYPE_CCMP: if ((decrypt_res & RX_RES_STATUS_DECRYPT_TYPE_MSK) == RX_RES_STATUS_DECRYPT_OK) { @@ -729,7 +731,7 @@ static u32 iwlagn_translate_rx_status(struct iwl_priv *priv, u32 decrypt_in) decrypt_out |= RX_RES_STATUS_BAD_KEY_TTAK; break; } - /* fall through if TTAK OK */ + /* fall through */ default: if (!(decrypt_in & RX_MPDU_RES_STATUS_ICV_OK)) decrypt_out |= RX_RES_STATUS_BAD_ICV_MIC; diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/scan.c b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c index 8d7aafb4d9e9..f190f7beb3a8 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/scan.c @@ -3,6 +3,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -418,7 +419,7 @@ static u16 iwl_limit_dwell(struct iwl_priv *priv, u16 dwell_time) limit = (limits[1] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; limit /= 2; dwell_time = min(limit, dwell_time); - /* fall through to limit further */ + /* fall through */ case 1: limit = (limits[0] * 98) / 100 - IWL_CHANNEL_TUNE_TIME * 2; limit /= n_active; From 244726ec9e1965c3263807c7c22bcdd1f6870e96 Mon Sep 17 00:00:00 2001 From: Luca Coelho Date: Tue, 4 Dec 2018 23:23:49 +0200 Subject: [PATCH 16/38] iwlwifi: mvm: add fall through comments where needed Some switch-cases were missing a fall through comment, so the compiler may warn. Fix that by adding the comments where needed. In other cases there was more text in the comment, which the compiler doesn't recognize, so either remove the extra text or move it to a separate comment line as appropriate. Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/led.c | 3 +++ drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c | 1 + drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 2 +- drivers/net/wireless/intel/iwlwifi/mvm/scan.c | 6 +++--- 6 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c index 9bb1de1cad64..4348bb00e761 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c @@ -7,6 +7,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -28,6 +29,7 @@ * * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2017 Intel Deutschland GmbH + * Copyright(c) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -113,6 +115,7 @@ int iwl_mvm_leds_init(struct iwl_mvm *mvm) switch (mode) { case IWL_LED_BLINK: IWL_ERR(mvm, "Blink led mode not supported, used default\n"); + /* fall through */ case IWL_LED_DEFAULT: case IWL_LED_RF_STATE: mode = IWL_LED_RF_STATE; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 8837d7667f36..4e676a6936c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3901,6 +3901,7 @@ static int __iwl_mvm_assign_vif_chanctx(struct iwl_mvm *mvm, mvmvif->ap_ibss_active = true; break; } + /* fall through */ case NL80211_IFTYPE_ADHOC: /* * The AP binding flow is handled as part of the start_ap flow diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c index f369173db11c..86e40bae57e3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/phy-ctxt.c @@ -109,6 +109,7 @@ u8 iwl_mvm_get_ctrl_pos(struct cfg80211_chan_def *chandef) return PHY_VHT_CTRL_POS_4_ABOVE; default: WARN(1, "Invalid channel definition"); + /* fall through */ case 0: /* * The FW is expected to check the control channel position only diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 235ab26ca429..fbd3014e8b82 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -222,7 +222,7 @@ static u32 iwl_mvm_set_mac80211_rx_flag(struct iwl_mvm *mvm, !(rx_pkt_status & RX_MPDU_RES_STATUS_TTAK_OK)) return 0; *crypt_len = IEEE80211_TKIP_IV_LEN; - /* fall through if TTAK OK */ + /* fall through */ case RX_MPDU_RES_STATUS_SEC_WEP_ENC: if (!(rx_pkt_status & RX_MPDU_RES_STATUS_ICV_OK)) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 2c56f73d688e..b5ff3325c1d3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -310,7 +310,7 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_hdr *hdr, stats->flag |= RX_FLAG_MMIC_ERROR; *crypt_len = IEEE80211_TKIP_IV_LEN; - /* fall through if TTAK OK */ + /* fall through */ case IWL_RX_MPDU_STATUS_SEC_WEP: if (!(status & IWL_RX_MPDU_STATUS_ICV_OK)) return -1; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c index af8156ba8084..853bf679dc26 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/scan.c @@ -1616,10 +1616,10 @@ static int iwl_mvm_check_running_scans(struct iwl_mvm *mvm, int type) if (mvm->scan_status & IWL_MVM_SCAN_SCHED_MASK) return iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_SCHED, true); - - /* fall through, something is wrong if no scan was - * running but we ran out of scans. + /* Something is wrong if no scan was running but we + * ran out of scans. */ + /* fall through */ default: WARN_ON(1); break; From 28916a165aa7e0ceddedabf722808881ba6a3b03 Mon Sep 17 00:00:00 2001 From: Emmanuel Grumbach Date: Mon, 3 Dec 2018 20:13:27 +0200 Subject: [PATCH 17/38] iwlwifi: mvm: fix AP mode in WEP Recently we started to send the WEP keys to the firmware so that we could use hardware Tx encryption also on newer devices that require the keys to be installed in the firmware for encryption (as opposed to older devices that can get the key in the Tx command for each Tx). When we implemented that, we forgot to remove the key when we remove a station leading to a situation where a station that connects and disconnects a lot of times exhausts the key database inside the firmware. A fix was made for that, but we always removed the same key: mvmvif->ap_wep_key which means that we removed the same key entry in the firmware. This can make sense since in WEP, the key is the same for all the stations, but the internal implementation of iwl_mvm_set_sta_key and iwl_mvm_remove_sta_key assumes that each station uses a different key in the firmware's key database. So now we got to the situation where we have a single ieee80211_key_conf instance that means, a single ieee80211_key_conf.hw_key_idx index for several stations and hence for several keys. ieee80211_key_conf.hw_key_idx is set to 0 when the first station associates, and then it is overwritten to 1 when the second station associates which is a buggy of course. This led to the following message upon the removal of the second station: iwlwifi 0000:00:03.0: offset 1 not used in fw key table. WARNING: CPU: 2 PID: 27883 at net/mac80211/sta_info.c:1122 __sta_info_destroy_part2+0x16b/0x180 [mac80211] RIP: 0010:__sta_info_destroy_part2+0x16b/0x180 [mac80211] Call Trace: __sta_info_destroy+0x2a/0x40 [mac80211] sta_info_destroy_addr_bss+0x38/0x60 [mac80211] ieee80211_del_station+0x1d/0x30 [mac80211] nl80211_del_station+0xe0/0x1f0 [cfg80211] Fix this by copying the ieee80211_key_conf structure for each and every station. This is the easiest way to properly remove the keys with the right index. Another solution would have been to allow several stations to use the same key offset in the firmware. That would require to change the way we track keys in iwlmvm and not really worth it. Also, maintain correctly fw_key_table when we add a key for the multicast station. Remove the key when we remove the multicast station. Signed-off-by: Emmanuel Grumbach Fixes: 337bfc9881a2 ("iwlwifi: mvm: set wep key for all stations in soft ap mode") Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/mvm/mac80211.c | 30 ++-- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 132 +++++++++++------- drivers/net/wireless/intel/iwlwifi/mvm/sta.h | 3 + 3 files changed, 102 insertions(+), 63 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 4e676a6936c1..92605aaca937 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3078,6 +3078,24 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, ret = iwl_mvm_update_sta(mvm, vif, sta); } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { + /* if wep is used, need to set the key for the station now */ + if (vif->type == NL80211_IFTYPE_AP && mvmvif->ap_wep_key) { + mvm_sta->wep_key = + kmemdup(mvmvif->ap_wep_key, + sizeof(*mvmvif->ap_wep_key) + + mvmvif->ap_wep_key->keylen, + GFP_KERNEL); + if (!mvm_sta->wep_key) { + ret = -ENOMEM; + goto out_unlock; + } + + ret = iwl_mvm_set_sta_key(mvm, vif, sta, + mvm_sta->wep_key, + STA_KEY_IDX_INVALID); + } else { + ret = 0; + } /* we don't support TDLS during DCM */ if (iwl_mvm_phy_ctx_count(mvm) > 1) @@ -3092,14 +3110,6 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, true); - - /* if wep is used, need to set the key for the station now */ - if (vif->type == NL80211_IFTYPE_AP && mvmvif->ap_wep_key) - ret = iwl_mvm_set_sta_key(mvm, vif, sta, - mvmvif->ap_wep_key, - STA_KEY_IDX_INVALID); - else - ret = 0; } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { /* disable beacon filtering */ @@ -3127,10 +3137,12 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, /* Remove STA key if this is an AP using WEP */ if (vif->type == NL80211_IFTYPE_AP && mvmvif->ap_wep_key) { int rm_ret = iwl_mvm_remove_sta_key(mvm, vif, sta, - mvmvif->ap_wep_key); + mvm_sta->wep_key); if (!ret) ret = rm_ret; + kfree(mvm_sta->wep_key); + mvm_sta->wep_key = NULL; } } else { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index a102a16a15f6..3ea39bb533e8 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -2337,11 +2337,13 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) if (mvmvif->ap_wep_key) { u8 key_offset = iwl_mvm_set_fw_key_idx(mvm); + __set_bit(key_offset, mvm->fw_key_table); + if (key_offset == STA_KEY_IDX_INVALID) return -ENOSPC; ret = iwl_mvm_send_sta_key(mvm, mvmvif->mcast_sta.sta_id, - mvmvif->ap_wep_key, 1, 0, NULL, 0, + mvmvif->ap_wep_key, true, 0, NULL, 0, key_offset, 0); if (ret) return ret; @@ -2350,6 +2352,59 @@ int iwl_mvm_add_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) return 0; } +static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, + struct ieee80211_key_conf *keyconf, + bool mcast) +{ + union { + struct iwl_mvm_add_sta_key_cmd_v1 cmd_v1; + struct iwl_mvm_add_sta_key_cmd cmd; + } u = {}; + bool new_api = fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_TKIP_MIC_KEYS); + __le16 key_flags; + int ret, size; + u32 status; + + /* This is a valid situation for GTK removal */ + if (sta_id == IWL_MVM_INVALID_STA) + return 0; + + key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & + STA_KEY_FLG_KEYID_MSK); + key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP); + key_flags |= cpu_to_le16(STA_KEY_NOT_VALID); + + if (mcast) + key_flags |= cpu_to_le16(STA_KEY_MULTICAST); + + /* + * The fields assigned here are in the same location at the start + * of the command, so we can do this union trick. + */ + u.cmd.common.key_flags = key_flags; + u.cmd.common.key_offset = keyconf->hw_key_idx; + u.cmd.common.sta_id = sta_id; + + size = new_api ? sizeof(u.cmd) : sizeof(u.cmd_v1); + + status = ADD_STA_SUCCESS; + ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, size, &u.cmd, + &status); + + switch (status) { + case ADD_STA_SUCCESS: + IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n"); + break; + default: + ret = -EIO; + IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n"); + break; + } + + return ret; +} + /* * Send the FW a request to remove the station from it's internal data * structures, and in addition remove it from the local data structure. @@ -2365,6 +2420,28 @@ int iwl_mvm_rm_mcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif) iwl_mvm_disable_txq(mvm, NULL, mvmvif->cab_queue, 0, 0); + if (mvmvif->ap_wep_key) { + int i; + + if (!__test_and_clear_bit(mvmvif->ap_wep_key->hw_key_idx, + mvm->fw_key_table)) { + IWL_ERR(mvm, "offset %d not used in fw key table.\n", + mvmvif->ap_wep_key->hw_key_idx); + return -ENOENT; + } + + /* track which key was deleted last */ + for (i = 0; i < STA_KEY_MAX_NUM; i++) { + if (mvm->fw_key_deleted[i] < U8_MAX) + mvm->fw_key_deleted[i]++; + } + mvm->fw_key_deleted[mvmvif->ap_wep_key->hw_key_idx] = 0; + ret = __iwl_mvm_remove_sta_key(mvm, mvmvif->mcast_sta.sta_id, + mvmvif->ap_wep_key, true); + if (ret) + return ret; + } + ret = iwl_mvm_rm_sta_common(mvm, mvmvif->mcast_sta.sta_id); if (ret) IWL_WARN(mvm, "Failed sending remove station\n"); @@ -3396,59 +3473,6 @@ static int __iwl_mvm_set_sta_key(struct iwl_mvm *mvm, return ret; } -static int __iwl_mvm_remove_sta_key(struct iwl_mvm *mvm, u8 sta_id, - struct ieee80211_key_conf *keyconf, - bool mcast) -{ - union { - struct iwl_mvm_add_sta_key_cmd_v1 cmd_v1; - struct iwl_mvm_add_sta_key_cmd cmd; - } u = {}; - bool new_api = fw_has_api(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_API_TKIP_MIC_KEYS); - __le16 key_flags; - int ret, size; - u32 status; - - /* This is a valid situation for GTK removal */ - if (sta_id == IWL_MVM_INVALID_STA) - return 0; - - key_flags = cpu_to_le16((keyconf->keyidx << STA_KEY_FLG_KEYID_POS) & - STA_KEY_FLG_KEYID_MSK); - key_flags |= cpu_to_le16(STA_KEY_FLG_NO_ENC | STA_KEY_FLG_WEP_KEY_MAP); - key_flags |= cpu_to_le16(STA_KEY_NOT_VALID); - - if (mcast) - key_flags |= cpu_to_le16(STA_KEY_MULTICAST); - - /* - * The fields assigned here are in the same location at the start - * of the command, so we can do this union trick. - */ - u.cmd.common.key_flags = key_flags; - u.cmd.common.key_offset = keyconf->hw_key_idx; - u.cmd.common.sta_id = sta_id; - - size = new_api ? sizeof(u.cmd) : sizeof(u.cmd_v1); - - status = ADD_STA_SUCCESS; - ret = iwl_mvm_send_cmd_pdu_status(mvm, ADD_STA_KEY, size, &u.cmd, - &status); - - switch (status) { - case ADD_STA_SUCCESS: - IWL_DEBUG_WEP(mvm, "MODIFY_STA: remove sta key passed\n"); - break; - default: - ret = -EIO; - IWL_ERR(mvm, "MODIFY_STA: remove sta key failed\n"); - break; - } - - return ret; -} - int iwl_mvm_set_sta_key(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_sta *sta, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 0614296244b3..79700c7310a1 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -394,6 +394,7 @@ struct iwl_mvm_rxq_dup_data { * the BA window. To be used for UAPSD only. * @ptk_pn: per-queue PTK PN data structures * @dup_data: per queue duplicate packet detection data + * @wep_key: used in AP mode. Is a duplicate of the WEP key. * @deferred_traffic_tid_map: indication bitmap of deferred traffic per-TID * @tx_ant: the index of the antenna to use for data tx to this station. Only * used during connection establishment (e.g. for the 4 way handshake @@ -425,6 +426,8 @@ struct iwl_mvm_sta { struct iwl_mvm_key_pn __rcu *ptk_pn[4]; struct iwl_mvm_rxq_dup_data *dup_data; + struct ieee80211_key_conf *wep_key; + u8 reserved_queue; /* Temporary, until the new TLC will control the Tx protection */ From 679bff239f51388a61a3cb4a512bc3a1d6e66d74 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Thu, 6 Dec 2018 15:02:11 +0200 Subject: [PATCH 18/38] iwlwifi: mvm: limit AMSDU size to 8K Typically, when not in HE mode, we will not perform well with AMSDUs bigger than 8K. Signed-off-by: Sara Sharon Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rs.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index 09866f50857a..2d7a089d93a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -1757,7 +1757,12 @@ static void rs_set_amsdu_len(struct iwl_mvm *mvm, struct ieee80211_sta *sta, else mvmsta->amsdu_enabled = 0xFFFF; - mvmsta->max_amsdu_len = sta->max_amsdu_len; + if (mvmsta->vif->bss_conf.he_support && + !iwlwifi_mod_params.disable_11ax) + mvmsta->max_amsdu_len = sta->max_amsdu_len; + else + mvmsta->max_amsdu_len = min_t(int, sta->max_amsdu_len, 8500); + sta->max_rc_amsdu_len = mvmsta->max_amsdu_len; for (i = 0; i < IWL_MAX_TID_COUNT; i++) { From f992c61d59fb2eb1a234890ef92f378a47ebe20b Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2018 12:04:31 +0100 Subject: [PATCH 19/38] iwlwifi: mvm: remove redundant condition In iwl_mvm_sta_alloc_queue_tvqm(), we know that we have a station, so no need to check it. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/sta.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 3ea39bb533e8..27839c10d5e3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -791,11 +791,9 @@ static int iwl_mvm_sta_alloc_queue_tvqm(struct iwl_mvm *mvm, if (queue < 0) return queue; - if (sta) { - mvmtxq->txq_id = queue; - mvm->tvqm_info[queue].txq_tid = tid; - mvm->tvqm_info[queue].sta_id = mvmsta->sta_id; - } + mvmtxq->txq_id = queue; + mvm->tvqm_info[queue].txq_tid = tid; + mvm->tvqm_info[queue].sta_id = mvmsta->sta_id; IWL_DEBUG_TX_QUEUES(mvm, "Allocated queue is %d\n", queue); From f4056d713cede3d3f1f574e9c36613c6a1a4e347 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sun, 9 Dec 2018 11:20:39 +0200 Subject: [PATCH 20/38] iwlwifi: mvm: add tlc command name to output Instead of having the command appear as "UNKNOWN" in the dmesg, add the name to it. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/ops.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index ad816007e546..8a16388c3a0b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -445,6 +445,7 @@ static const struct iwl_hcmd_names iwl_mvm_data_path_names[] = { HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD), HCMD_NAME(STA_HE_CTXT_CMD), HCMD_NAME(RFH_QUEUE_CONFIG_CMD), + HCMD_NAME(TLC_MNG_CONFIG_CMD), HCMD_NAME(CHEST_COLLECTOR_FILTER_CONFIG_CMD), HCMD_NAME(STA_PM_NOTIF), HCMD_NAME(MU_GROUP_MGMT_NOTIF), From 9394662ab5e92ef1fcfe108ea4f4d98efa99d84a Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Sun, 9 Dec 2018 12:24:24 +0200 Subject: [PATCH 21/38] iwlwifi: mvm: config mac ctxt to HE before TLC If we have a station connecting HE, make sure that the MAC ctxt is updated with indication of this before setting the TLC rates via the TLC manager command. Signed-off-by: Liad Kaufman Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c | 3 +-- drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index c1eb07c08827..83711b555036 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -706,8 +706,7 @@ static int iwl_mvm_mac_ctxt_cmd_sta(struct iwl_mvm *mvm, if (vif->probe_req_reg && vif->bss_conf.assoc && vif->p2p) cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_PROBE_REQUEST); - if (vif->bss_conf.assoc && vif->bss_conf.he_support && - !iwlwifi_mod_params.disable_11ax) { + if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) { cmd.filter_flags |= cpu_to_le32(MAC_FILTER_IN_11AX); if (vif->bss_conf.twt_requester) ctxt_sta->data_policy |= cpu_to_le32(TWT_SUPPORTED); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index 92605aaca937..fc251cc47b7f 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -3066,11 +3066,15 @@ static int iwl_mvm_mac_sta_state(struct ieee80211_hw *hw, } else if (old_state == IEEE80211_STA_AUTH && new_state == IEEE80211_STA_ASSOC) { if (vif->type == NL80211_IFTYPE_AP) { + vif->bss_conf.he_support = sta->he_cap.has_he; mvmvif->ap_assoc_sta_count++; iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); if (vif->bss_conf.he_support && !iwlwifi_mod_params.disable_11ax) iwl_mvm_cfg_he_sta(mvm, vif, mvm_sta->sta_id); + } else if (vif->type == NL80211_IFTYPE_STATION) { + vif->bss_conf.he_support = sta->he_cap.has_he; + iwl_mvm_mac_ctxt_changed(mvm, vif, false, NULL); } iwl_mvm_rs_rate_init(mvm, sta, mvmvif->phy_ctxt->channel->band, From 138d320a95ce4ecef13e6b3f3b3a8a21af5c6e1d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 Dec 2018 11:30:37 +0100 Subject: [PATCH 22/38] iwlwifi: dvm: use %u for sscanf() into unsigned variable Use %u instead of using %d which looks signed but then won't get signed output if using an unsigned variable. It doesn't matter much, but be consistent. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c index 3d2e44a642de..e64cdafc5623 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c @@ -3,6 +3,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. + * Copyright (C) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -2238,7 +2239,7 @@ static ssize_t iwl_dbgfs_log_event_write(struct file *file, buf_size = min(count, sizeof(buf) - 1); if (copy_from_user(buf, user_buf, buf_size)) return -EFAULT; - if (sscanf(buf, "%d", &event_log_flag) != 1) + if (sscanf(buf, "%u", &event_log_flag) != 1) return -EFAULT; if (event_log_flag == 1) iwl_dump_nic_event_log(priv, true, NULL); From 0ec7bfb26aff91ae60ac8a893fee755287291a55 Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Tue, 20 Nov 2018 13:29:29 +0200 Subject: [PATCH 23/38] iwlwifi: dbg_ini: remove redundant dram buffer allocation There are several flows in that can cause redundant allocation. In case the driver reaches the maximum amount of blocks allowed, it allocates the buffer and only then checks if it reached the maximum amount of blocks and return without freeing the buffer, causing a memory leak. Solve this by moving the check of the amount of buffers being used before the allocation. In case there was an assert, the apply points are being reused, causing that for each assert, the driver allocates a new redundant buffer. Solve this by adding a new is_alloc field to indicate if the driver already allocated memory for the requested buffer. Also, split iwl_fw_dbg_buffer_allocation function into iwl_fw_dbg_buffer_allocation and iwl_fw_dbg_buffer_apply to increase the clearity of the flow. Signed-off-by: Shahar S Matityahu Fixes: d47902f9f71d ("iwlwifi: dbg: add apply point logic") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 101 +++++++++++------- drivers/net/wireless/intel/iwlwifi/fw/img.h | 19 ++-- .../net/wireless/intel/iwlwifi/fw/runtime.h | 2 +- .../net/wireless/intel/iwlwifi/iwl-dbg-tlv.c | 23 +++- 4 files changed, 91 insertions(+), 54 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index f8cf12804aee..5feae549d316 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1131,7 +1131,7 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, if (WARN_ON(reg_id >= ARRAY_SIZE(fwrt->dump.active_regs))) continue; - reg = fwrt->dump.active_regs[reg_id].reg; + reg = fwrt->dump.active_regs[reg_id]; if (WARN(!reg, "Unassigned region %d\n", reg_id)) continue; @@ -1197,7 +1197,7 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, if (reg_id >= ARRAY_SIZE(fwrt->dump.active_regs)) continue; - reg = fwrt->dump.active_regs[reg_id].reg; + reg = fwrt->dump.active_regs[reg_id]; /* Don't warn, get_trigger_len already warned */ if (!reg) continue; @@ -1667,8 +1667,34 @@ void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt) IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data); static void -iwl_fw_dbg_buffer_allocation(struct iwl_fw_runtime *fwrt, - struct iwl_fw_ini_allocation_tlv *alloc) +iwl_fw_dbg_buffer_allocation(struct iwl_fw_runtime *fwrt, u32 size) +{ + struct iwl_trans *trans = fwrt->trans; + void *virtual_addr = NULL; + dma_addr_t phys_addr; + + if (WARN_ON_ONCE(trans->num_blocks == ARRAY_SIZE(trans->fw_mon))) + return; + + virtual_addr = + dma_alloc_coherent(fwrt->trans->dev, size, &phys_addr, + GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO | + __GFP_COMP); + + /* TODO: alloc fragments if needed */ + if (!virtual_addr) + IWL_ERR(fwrt, "Failed to allocate debug memory\n"); + + trans->fw_mon[trans->num_blocks].block = virtual_addr; + trans->fw_mon[trans->num_blocks].physical = phys_addr; + trans->fw_mon[trans->num_blocks].size = size; + trans->num_blocks++; + + IWL_DEBUG_FW(trans, "Allocated debug block of size %d\n", size); +} + +static void iwl_fw_dbg_buffer_apply(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_allocation_data *alloc) { struct iwl_trans *trans = fwrt->trans; struct iwl_ldbg_config_cmd ldbg_cmd = { @@ -1681,43 +1707,30 @@ iwl_fw_dbg_buffer_allocation(struct iwl_fw_runtime *fwrt, .data[0] = &ldbg_cmd, .len[0] = sizeof(ldbg_cmd), }; - void *virtual_addr = NULL; - u32 size = le32_to_cpu(alloc->size); - dma_addr_t phys_addr; + int block_idx = trans->num_blocks; - if (!trans->num_blocks && - le32_to_cpu(alloc->buffer_location) != + if (le32_to_cpu(alloc->tlv.buffer_location) != IWL_FW_INI_LOCATION_DRAM_PATH) return; - virtual_addr = - dma_alloc_coherent(fwrt->trans->dev, size, &phys_addr, - GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO | - __GFP_COMP); - - /* TODO: alloc fragments if needed */ - if (!virtual_addr) - IWL_ERR(fwrt, "Failed to allocate debug memory\n"); - - if (WARN_ON_ONCE(trans->num_blocks == ARRAY_SIZE(trans->fw_mon))) - return; - - trans->fw_mon[trans->num_blocks].block = virtual_addr; - trans->fw_mon[trans->num_blocks].physical = phys_addr; - trans->fw_mon[trans->num_blocks].size = size; - trans->num_blocks++; - - IWL_DEBUG_FW(trans, "Allocated debug block of size %d\n", size); + if (!alloc->is_alloc) { + iwl_fw_dbg_buffer_allocation(fwrt, + le32_to_cpu(alloc->tlv.size)); + if (block_idx == trans->num_blocks) + return; + alloc->is_alloc = 1; + } /* First block is assigned via registers / context info */ if (trans->num_blocks == 1) return; cmd->num_frags = cpu_to_le32(1); - cmd->fragments[0].address = cpu_to_le64(phys_addr); - cmd->fragments[0].size = alloc->size; - cmd->allocation_id = alloc->allocation_id; - cmd->buffer_location = alloc->buffer_location; + cmd->fragments[0].address = + cpu_to_le64(trans->fw_mon[block_idx].physical); + cmd->fragments[0].size = alloc->tlv.size; + cmd->allocation_id = alloc->tlv.allocation_id; + cmd->buffer_location = alloc->tlv.buffer_location; iwl_trans_send_cmd(trans, &hcmd); } @@ -1746,9 +1759,8 @@ static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt, int i, size = le32_to_cpu(tlv->num_regions); for (i = 0; i < size; i++) { - struct iwl_fw_ini_region_cfg *reg = iter; + struct iwl_fw_ini_region_cfg *reg = iter, **active; int id = le32_to_cpu(reg->region_id); - struct iwl_fw_ini_active_regs *active; if (WARN(id >= ARRAY_SIZE(fwrt->dump.active_regs), "Invalid region id %d for apply point %d\n", id, pnt)) @@ -1756,17 +1768,14 @@ static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt, active = &fwrt->dump.active_regs[id]; - if (ext && active->apply_point == pnt) - IWL_WARN(fwrt->trans, - "External region TLV overrides FW default %x\n", - id); + if (*active) + IWL_WARN(fwrt->trans, "region TLV %d override\n", id); IWL_DEBUG_FW(fwrt, "%s: apply point %d, activating region ID %d\n", __func__, pnt, id); - active->reg = reg; - active->apply_point = pnt; + *active = reg; if (le32_to_cpu(reg->region_type) != IWL_FW_INI_REGION_DRAM_BUFFER) @@ -1842,9 +1851,13 @@ static void _iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, u32 type = le32_to_cpu(tlv->type); switch (type) { - case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION: - iwl_fw_dbg_buffer_allocation(fwrt, ini_tlv); + case IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION: { + struct iwl_fw_ini_allocation_data *buf_alloc = ini_tlv; + + iwl_fw_dbg_buffer_apply(fwrt, ini_tlv); + iter += sizeof(buf_alloc->is_alloc); break; + } case IWL_UCODE_TLV_TYPE_HCMD: if (pnt < IWL_FW_INI_APPLY_AFTER_ALIVE) { IWL_ERR(fwrt, @@ -1875,6 +1888,12 @@ void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, enum iwl_fw_ini_apply_point apply_point) { void *data = &fwrt->trans->apply_points[apply_point]; + int i; + + if (apply_point == IWL_FW_INI_APPLY_EARLY) { + for (i = 0; i < IWL_FW_INI_MAX_REGION_ID; i++) + fwrt->dump.active_regs[i] = NULL; + } _iwl_fw_dbg_apply_point(fwrt, data, apply_point, false); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/img.h b/drivers/net/wireless/intel/iwlwifi/fw/img.h index 12333167ea23..23f982be800f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/img.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/img.h @@ -222,6 +222,15 @@ struct iwl_fw_dbg { u32 dump_mask; }; +/** + * @tlv: the buffer allocation tlv + * @is_alloc: indicates if the buffer was already allocated + */ +struct iwl_fw_ini_allocation_data { + struct iwl_fw_ini_allocation_tlv tlv; + u32 is_alloc; +} __packed; + /** * struct iwl_fw_ini_active_triggers * @active: is this trigger active @@ -236,16 +245,6 @@ struct iwl_fw_ini_active_triggers { struct iwl_fw_ini_trigger *conf_ext; }; -/** - * struct iwl_fw_ini_active_regs - * @reg: active region from TLV - * @apply_point: apply point where it became active - */ -struct iwl_fw_ini_active_regs { - struct iwl_fw_ini_region_cfg *reg; - enum iwl_fw_ini_apply_point apply_point; -}; - /** * struct iwl_fw - variables associated with the firmware * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 9ff04ffba3fa..52af848f2eb3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -139,7 +139,7 @@ struct iwl_fw_runtime { /* ts of the beginning of a non-collect fw dbg data period */ unsigned long non_collect_ts_start[IWL_FW_TRIGGER_ID_NUM - 1]; u32 *d3_debug_data; - struct iwl_fw_ini_active_regs active_regs[IWL_FW_INI_MAX_REGION_ID]; + struct iwl_fw_ini_region_cfg *active_regs[IWL_FW_INI_MAX_REGION_ID]; struct iwl_fw_ini_active_triggers active_trigs[IWL_FW_TRIGGER_ID_NUM]; u32 lmac_err_id[MAX_NUM_LMAC]; u32 umac_err_id; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 43d815cb3ce9..5798f434f68f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -71,6 +71,7 @@ void iwl_fw_dbg_copy_tlv(struct iwl_trans *trans, struct iwl_ucode_tlv *tlv, u32 apply_point = le32_to_cpu(header->apply_point); int copy_size = le32_to_cpu(tlv->length) + sizeof(*tlv); + int offset_size = copy_size; if (WARN_ONCE(apply_point >= IWL_FW_INI_APPLY_NUM, "Invalid apply point id %d\n", apply_point)) @@ -81,17 +82,25 @@ void iwl_fw_dbg_copy_tlv(struct iwl_trans *trans, struct iwl_ucode_tlv *tlv, else data = &trans->apply_points[apply_point]; + /* add room for is_alloc field in &iwl_fw_ini_allocation_data struct */ + if (le32_to_cpu(tlv->type) == IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION) { + struct iwl_fw_ini_allocation_data *buf_alloc = + (void *)tlv->data; + + offset_size += sizeof(buf_alloc->is_alloc); + } + /* * Make sure we still have room to copy this TLV. Offset points to the * location the last copy ended. */ - if (WARN_ONCE(data->offset + copy_size > data->size, + if (WARN_ONCE(data->offset + offset_size > data->size, "Not enough memory for apply point %d\n", apply_point)) return; memcpy(data->data + data->offset, (void *)tlv, copy_size); - data->offset += copy_size; + data->offset += offset_size; } void iwl_alloc_dbg_tlv(struct iwl_trans *trans, size_t len, const u8 *data, @@ -129,6 +138,16 @@ void iwl_alloc_dbg_tlv(struct iwl_trans *trans, size_t len, const u8 *data, if (WARN_ON(apply >= IWL_FW_INI_APPLY_NUM)) continue; + /* add room for is_alloc field in &iwl_fw_ini_allocation_data + * struct + */ + if (tlv_type == IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION) { + struct iwl_fw_ini_allocation_data *buf_alloc = + (void *)tlv->data; + + size[apply] += sizeof(buf_alloc->is_alloc); + } + size[apply] += sizeof(*tlv) + tlv_len; } From 74cf15cb69c0beb6cdaae3ff43f5231232a1f1b0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 3 Dec 2018 15:59:40 +0100 Subject: [PATCH 24/38] iwlwifi: mvm: add HE TB PPDU SIG-A BW to radiotap Expose the trigger-based PPDU SIG-A bandwidth to radiotap in the newly defined bits thereof. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 21 +++++++++++++------ include/net/ieee80211_radiotap.h | 6 ++++++ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index b5ff3325c1d3..d2728fe6a041 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -960,6 +960,7 @@ iwl_mvm_decode_he_phy_ru_alloc(struct iwl_mvm_rx_phy_data *phy_data, * the TSF/timers are not be transmitted in HE-MU. */ u8 ru = le32_get_bits(phy_data->d1, IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK); + u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; u8 offs = 0; rx_status->bw = RATE_INFO_BW_HE_RU; @@ -1002,19 +1003,27 @@ iwl_mvm_decode_he_phy_ru_alloc(struct iwl_mvm_rx_phy_data *phy_data, he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC); - if (he_mu) { #define CHECK_BW(bw) \ BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_ ## bw ## MHZ != \ + RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS); \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_ ## bw ## MHZ != \ RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS) - CHECK_BW(20); - CHECK_BW(40); - CHECK_BW(80); - CHECK_BW(160); + CHECK_BW(20); + CHECK_BW(40); + CHECK_BW(80); + CHECK_BW(160); + + if (he_mu) he_mu->flags2 |= le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK, rate_n_flags), IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW); - } + else if (he_type == RATE_MCS_HE_TYPE_TRIG) + he->data6 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_KNOWN) | + le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK, + rate_n_flags), + IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW); } static void iwl_mvm_decode_he_phy_data(struct iwl_mvm *mvm, diff --git a/include/net/ieee80211_radiotap.h b/include/net/ieee80211_radiotap.h index 8014153bdd49..38c48bb80439 100644 --- a/include/net/ieee80211_radiotap.h +++ b/include/net/ieee80211_radiotap.h @@ -291,6 +291,12 @@ enum ieee80211_radiotap_he_bits { IEEE80211_RADIOTAP_HE_DATA6_NSTS = 0x000f, IEEE80211_RADIOTAP_HE_DATA6_DOPPLER = 0x0010, + IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_KNOWN = 0x0020, + IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW = 0x00c0, + IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_20MHZ = 0, + IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_40MHZ = 1, + IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_80MHZ = 2, + IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_160MHZ = 3, IEEE80211_RADIOTAP_HE_DATA6_TXOP = 0x7f00, IEEE80211_RADIOTAP_HE_DATA6_MIDAMBLE_PDCTY = 0x8000, }; From 45108c5d6870f3ccdf0e84518593572eddfd5ad0 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Dec 2018 09:04:55 +0100 Subject: [PATCH 25/38] iwlwifi: move config structs to C file Even if they're static const, there's no need to duplicate the structs every time they're included and used. Move them to an appropriate C file instead. Also remove useless parentheses along the way. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/iwl-config.h | 31 ++---------------- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 32 +++++++++++++++++++ 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index ff942532fac3..f1e493380713 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -445,35 +445,8 @@ struct iwl_cfg { u32 d3_debug_data_length; }; -static const struct iwl_csr_params iwl_csr_v1 = { - .flag_mac_clock_ready = 0, - .flag_val_mac_access_en = 0, - .flag_init_done = 2, - .flag_mac_access_req = 3, - .flag_sw_reset = 7, - .flag_master_dis = 8, - .flag_stop_master = 9, - .addr_sw_reset = (CSR_BASE + 0x020), - .mac_addr0_otp = 0x380, - .mac_addr1_otp = 0x384, - .mac_addr0_strap = 0x388, - .mac_addr1_strap = 0x38C -}; - -static const struct iwl_csr_params iwl_csr_v2 = { - .flag_init_done = 6, - .flag_mac_clock_ready = 20, - .flag_val_mac_access_en = 20, - .flag_mac_access_req = 21, - .flag_master_dis = 28, - .flag_stop_master = 29, - .flag_sw_reset = 31, - .addr_sw_reset = (CSR_BASE + 0x024), - .mac_addr0_otp = 0x30, - .mac_addr1_otp = 0x34, - .mac_addr0_strap = 0x38, - .mac_addr1_strap = 0x3C -}; +extern const struct iwl_csr_params iwl_csr_v1; +extern const struct iwl_csr_params iwl_csr_v2; /* * This list declares the config structures for all devices. diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index ffd1e649bfa0..d62eb2d9389a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -7,6 +7,7 @@ * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -28,6 +29,7 @@ * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 - 2016 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -68,6 +70,36 @@ #include "iwl-prph.h" #include "iwl-fh.h" +const struct iwl_csr_params iwl_csr_v1 = { + .flag_mac_clock_ready = 0, + .flag_val_mac_access_en = 0, + .flag_init_done = 2, + .flag_mac_access_req = 3, + .flag_sw_reset = 7, + .flag_master_dis = 8, + .flag_stop_master = 9, + .addr_sw_reset = CSR_BASE + 0x020, + .mac_addr0_otp = 0x380, + .mac_addr1_otp = 0x384, + .mac_addr0_strap = 0x388, + .mac_addr1_strap = 0x38C +}; + +const struct iwl_csr_params iwl_csr_v2 = { + .flag_init_done = 6, + .flag_mac_clock_ready = 20, + .flag_val_mac_access_en = 20, + .flag_mac_access_req = 21, + .flag_master_dis = 28, + .flag_stop_master = 29, + .flag_sw_reset = 31, + .addr_sw_reset = CSR_BASE + 0x024, + .mac_addr0_otp = 0x30, + .mac_addr1_otp = 0x34, + .mac_addr0_strap = 0x38, + .mac_addr1_strap = 0x3C +}; + void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val) { trace_iwlwifi_dev_iowrite8(trans->dev, ofs, val); From 32a378539a37428903637904a815e79eecd5b8be Mon Sep 17 00:00:00 2001 From: Naftali Goldstein Date: Mon, 10 Dec 2018 08:50:01 +0200 Subject: [PATCH 26/38] iwlwifi: mvm: add description to second BAD_COMMAND assert number Depending on exactly what happens in the FW, an invalid host-command could result in either assert 0x38 or 0x39. Add 0x39 to the assert-name table. Signed-off-by: Naftali Goldstein Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/utils.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 211c4638d690..92f61e8a6284 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -294,6 +294,7 @@ static const struct { { "SYSASSERT", 0x35 }, { "UCODE_VERSION_MISMATCH", 0x37 }, { "BAD_COMMAND", 0x38 }, + { "BAD_COMMAND", 0x39 }, { "NMI_INTERRUPT_DATA_ACTION_PT", 0x3C }, { "FATAL_ERROR", 0x3D }, { "NMI_TRM_HW_ERR", 0x46 }, From 3619b088a87905a28d51b10172ff1e923c692cd7 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Dec 2018 10:36:29 +0100 Subject: [PATCH 27/38] iwlwifi: mvm: don't hide HE radiotap data in SKB Hiding the HE radiotap data for further processing of the SKB just caused another bug when adding the L-SIG data. Simply stop doing this and adjust the skb->data pointer accordingly when we need to get the 802.11 header. While at it, also verify and fix the data alignment, we need to add 2 bytes padding with the vendor data to ensure the whole length of all radiotap headers is a multiple of 4. Signed-off-by: Johannes Berg Fixes: 6721039d5b8a ("iwlwifi: mvm: add L-SIG length to radiotap") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index d2728fe6a041..957d99932e8b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -66,11 +66,37 @@ #include "mvm.h" #include "fw-api.h" +static void *iwl_mvm_skb_get_hdr(struct sk_buff *skb) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + u8 *data = skb->data; + + /* Alignment concerns */ + BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_he) % 4); + BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_he_mu) % 4); + BUILD_BUG_ON(sizeof(struct ieee80211_radiotap_lsig) % 4); + BUILD_BUG_ON(sizeof(struct ieee80211_vendor_radiotap) % 4); + + if (rx_status->flag & RX_FLAG_RADIOTAP_HE) + data += sizeof(struct ieee80211_radiotap_he); + if (rx_status->flag & RX_FLAG_RADIOTAP_HE_MU) + data += sizeof(struct ieee80211_radiotap_he_mu); + if (rx_status->flag & RX_FLAG_RADIOTAP_LSIG) + data += sizeof(struct ieee80211_radiotap_lsig); + if (rx_status->flag & RX_FLAG_RADIOTAP_VENDOR_DATA) { + struct ieee80211_vendor_radiotap *radiotap = (void *)data; + + data += sizeof(*radiotap) + radiotap->len + radiotap->pad; + } + + return data; +} + static inline int iwl_mvm_check_pn(struct iwl_mvm *mvm, struct sk_buff *skb, int queue, struct ieee80211_sta *sta) { struct iwl_mvm_sta *mvmsta; - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_hdr *hdr = iwl_mvm_skb_get_hdr(skb); struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb); struct iwl_mvm_key_pn *ptk_pn; int res; @@ -197,12 +223,15 @@ static void iwl_mvm_add_rtap_sniffer_config(struct iwl_mvm *mvm, { struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); struct ieee80211_vendor_radiotap *radiotap; - int size = sizeof(*radiotap) + sizeof(__le16); + const int size = sizeof(*radiotap) + sizeof(__le16); if (!mvm->cur_aid) return; - radiotap = skb_put(skb, size); + /* ensure alignment */ + BUILD_BUG_ON((size + 2) % 4); + + radiotap = skb_put(skb, size + 2); radiotap->align = 1; /* Intel OUI */ radiotap->oui[0] = 0xf6; @@ -212,10 +241,12 @@ static void iwl_mvm_add_rtap_sniffer_config(struct iwl_mvm *mvm, radiotap->subns = 1; radiotap->present = 0x1; radiotap->len = size - sizeof(*radiotap); - radiotap->pad = 0; + radiotap->pad = 2; /* fill the data now */ memcpy(radiotap->data, &mvm->cur_aid, sizeof(mvm->cur_aid)); + /* and clear the padding */ + memset(radiotap->data + sizeof(__le16), 0, radiotap->pad); rx_status->flag |= RX_FLAG_RADIOTAP_VENDOR_DATA; } @@ -663,7 +694,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_hdr *hdr = iwl_mvm_skb_get_hdr(skb); struct iwl_mvm_sta *mvm_sta; struct iwl_mvm_baid_data *baid_data; struct iwl_mvm_reorder_buffer *buffer; @@ -1174,22 +1205,16 @@ static void iwl_mvm_rx_he(struct iwl_mvm *mvm, struct sk_buff *skb, .flags2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN | IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN), }; - unsigned int radiotap_len = 0; he = skb_put_data(skb, &known, sizeof(known)); - radiotap_len += sizeof(known); rx_status->flag |= RX_FLAG_RADIOTAP_HE; if (phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU || phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU_EXT) { he_mu = skb_put_data(skb, &mu_known, sizeof(mu_known)); - radiotap_len += sizeof(mu_known); rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU; } - /* temporarily hide the radiotap data */ - __skb_pull(skb, radiotap_len); - /* report the AMPDU-EOF bit on single frames */ if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) { rx_status->flag |= RX_FLAG_AMPDU_DETAILS; From 537ea3bb744ebd3cad188aa179a01d07bfb5c6dd Mon Sep 17 00:00:00 2001 From: Avigail Grinstein Date: Sun, 28 Oct 2018 17:16:44 +0200 Subject: [PATCH 28/38] iwlwifi: mvm: support absolute thresholds in bf configuration Update iwl_beacon_filter_cmd to support BEACON_FILTER_CONFIG_API_S_VER_4. Currently driver configs them to be zero (i.e. disable them, so no change is applied). Signed-off-by: Avigail Grinstein Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/fw/api/power.h | 11 +++++++++- drivers/net/wireless/intel/iwlwifi/fw/file.h | 3 +++ .../net/wireless/intel/iwlwifi/mvm/power.c | 20 ++++++++++++++++++- 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index 286a22da232d..5844898ee92c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -470,6 +470,13 @@ struct iwl_geo_tx_power_profiles_resp { * @ba_escape_timer: Fully receive and parse beacon if no beacons were passed * for a longer period of time then this escape-timeout. Units: Beacons. * @ba_enable_beacon_abort: 1, beacon abort is enabled; 0, disabled. + * @bf_threshold_absolute_low: See below. + * @bf_threshold_absolute_high: Send Beacon to driver if Energy value calculated + * for this beacon crossed this absolute threshold. For the 'Increase' + * direction the bf_energy_absolute_low[i] is used. For the 'Decrease' + * direction the bf_energy_absolute_high[i] is used. Zero value means + * that this specific threshold is ignored for beacon filtering, and + * beacon will not be forced to be sent to driver due to this setting. */ struct iwl_beacon_filter_cmd { __le32 bf_energy_delta; @@ -483,7 +490,9 @@ struct iwl_beacon_filter_cmd { __le32 bf_escape_timer; __le32 ba_escape_timer; __le32 ba_enable_beacon_abort; -} __packed; + __le32 bf_threshold_absolute_low[2]; + __le32 bf_threshold_absolute_high[2]; +} __packed; /* BEACON_FILTER_CONFIG_API_S_VER_4 */ /* Beacon filtering and beacon abort */ #define IWL_BF_ENERGY_DELTA_DEFAULT 5 diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index a6dd27fbe4aa..8c6ce4142204 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -265,6 +265,8 @@ typedef unsigned int __bitwise iwl_ucode_tlv_api_t; * the REDUCE_TX_POWER_CMD. * @IWL_UCODE_TLV_API_SHORT_BEACON_NOTIF: This ucode supports the short * version of the beacon notification. + * @IWL_UCODE_TLV_API_BEACON_FILTER_V4: This ucode supports v4 of + * BEACON_FILTER_CONFIG_API_S_VER_4. * * @NUM_IWL_UCODE_TLV_API: number of bits used */ @@ -290,6 +292,7 @@ enum iwl_ucode_tlv_api { IWL_UCODE_TLV_API_FRAG_EBS = (__force iwl_ucode_tlv_api_t)44, IWL_UCODE_TLV_API_REDUCE_TX_POWER = (__force iwl_ucode_tlv_api_t)45, IWL_UCODE_TLV_API_SHORT_BEACON_NOTIF = (__force iwl_ucode_tlv_api_t)46, + IWL_UCODE_TLV_API_BEACON_FILTER_V4 = (__force iwl_ucode_tlv_api_t)47, NUM_IWL_UCODE_TLV_API #ifdef __CHECKER__ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index 5a0a28fd762d..df9f1d6cdf78 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -8,6 +8,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -30,6 +31,7 @@ * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH * Copyright(c) 2015 - 2017 Intel Deutschland GmbH + * Copyright (C) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -79,6 +81,8 @@ int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, struct iwl_beacon_filter_cmd *cmd, u32 flags) { + u16 len; + IWL_DEBUG_POWER(mvm, "ba_enable_beacon_abort is: %d\n", le32_to_cpu(cmd->ba_enable_beacon_abort)); IWL_DEBUG_POWER(mvm, "ba_escape_timer is: %d\n", @@ -101,9 +105,23 @@ int iwl_mvm_beacon_filter_send_cmd(struct iwl_mvm *mvm, le32_to_cpu(cmd->bf_temp_fast_filter)); IWL_DEBUG_POWER(mvm, "bf_temp_slow_filter is: %d\n", le32_to_cpu(cmd->bf_temp_slow_filter)); + IWL_DEBUG_POWER(mvm, "bf_threshold_absolute_low is: %d, %d\n", + le32_to_cpu(cmd->bf_threshold_absolute_low[0]), + le32_to_cpu(cmd->bf_threshold_absolute_low[1])); + + IWL_DEBUG_POWER(mvm, "bf_threshold_absolute_high is: %d, %d\n", + le32_to_cpu(cmd->bf_threshold_absolute_high[0]), + le32_to_cpu(cmd->bf_threshold_absolute_high[1])); + + if (fw_has_api(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_API_BEACON_FILTER_V4)) + len = sizeof(struct iwl_beacon_filter_cmd); + else + len = offsetof(struct iwl_beacon_filter_cmd, + bf_threshold_absolute_low); return iwl_mvm_send_cmd_pdu(mvm, REPLY_BEACON_FILTERING_CMD, flags, - sizeof(struct iwl_beacon_filter_cmd), cmd); + len, cmd); } static From 608dce95db10b8ee1a26dbce3f60204bb69812a5 Mon Sep 17 00:00:00 2001 From: Sara Sharon Date: Wed, 12 Dec 2018 09:45:11 +0200 Subject: [PATCH 29/38] iwlwifi: mvm: fix RSS config command The hash mask is a bitmap, so we should use BIT() on the enum values. Signed-off-by: Sara Sharon Fixes: 43413a975d06 ("iwlwifi: mvm: support rss queues configuration command") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index d8ae9561a9b0..134dab2d774a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -105,12 +105,12 @@ static int iwl_send_rss_cfg_cmd(struct iwl_mvm *mvm) int i; struct iwl_rss_config_cmd cmd = { .flags = cpu_to_le32(IWL_RSS_ENABLE), - .hash_mask = IWL_RSS_HASH_TYPE_IPV4_TCP | - IWL_RSS_HASH_TYPE_IPV4_UDP | - IWL_RSS_HASH_TYPE_IPV4_PAYLOAD | - IWL_RSS_HASH_TYPE_IPV6_TCP | - IWL_RSS_HASH_TYPE_IPV6_UDP | - IWL_RSS_HASH_TYPE_IPV6_PAYLOAD, + .hash_mask = BIT(IWL_RSS_HASH_TYPE_IPV4_TCP) | + BIT(IWL_RSS_HASH_TYPE_IPV4_UDP) | + BIT(IWL_RSS_HASH_TYPE_IPV4_PAYLOAD) | + BIT(IWL_RSS_HASH_TYPE_IPV6_TCP) | + BIT(IWL_RSS_HASH_TYPE_IPV6_UDP) | + BIT(IWL_RSS_HASH_TYPE_IPV6_PAYLOAD), }; if (mvm->trans->num_rx_queues == 1) From 9deb52c68fb5419875745e3f1c78836a331b5e6b Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Wed, 5 Dec 2018 14:02:53 +0200 Subject: [PATCH 30/38] iwlwifi: dbg_ini: align struct iwl_fw_ini_region_cfg to the FW New fields were added to struct iwl_fw_ini_region_cfg. add those field and apply the changes the result from this change offset field is used to differentiate between the different LMACs and any memory access to the region addresses should be base_addr + offset. A fifo struct is to hold the meta data needed for fifo regions Also move range_data_size and num_of_ranges into a struct under the union to be aligned to the struct in the FW. Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/fw/api/dbg-tlv.h | 40 +++++++++++++++--- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 41 ++++++++++++------- 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index 4926950d432a..75e23c426be8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -133,6 +133,33 @@ struct iwl_fw_ini_debug_flow_tlv { #define IWL_FW_INI_MAX_REGION_ID 64 #define IWL_FW_INI_MAX_NAME 32 + +/** + * struct iwl_fw_ini_region_cfg_internal - meta data of internal memory region + * @num_of_range: the amount of ranges in the region + * @range_data_size: size of the data to read per range, in bytes. + */ +struct iwl_fw_ini_region_cfg_internal { + __le32 num_of_ranges; + __le32 range_data_size; +} __packed; /* FW_DEBUG_TLV_REGION_NIC_INTERNAL_RANGES_S */ + +/** + * struct iwl_fw_ini_region_cfg_fifos - meta data of fifos region + * @lmac1_id: bit map of lmac1 fifos to include in the region. + * @lmac2_id: bit map of lmac2 fifos to include in the region. + * @num_of_registers: number of prph registers in the region, each register is + * 4 bytes size. + * @header_only: none zero value indicates that this region does not include + * fifo data and includes only the given registers. + */ +struct iwl_fw_ini_region_cfg_fifos { + __le32 lmac1_id; + __le32 lmac2_id; + __le32 num_of_registers; + __le32 header_only; +} __packed; /* FW_DEBUG_TLV_REGION_FIFOS_S */ + /** * struct iwl_fw_ini_region_cfg * @region_id: ID of this dump configuration @@ -140,11 +167,11 @@ struct iwl_fw_ini_debug_flow_tlv { * @num_regions: amount of regions in the address array. * @name_len: name length * @name: file name to use for this region - * @num_of_range: the amount of ranges in the region. + * @internal: used in case the region uses internal memory. * @allocation_id: For DRAM type field substitutes for allocation_id - * @range_data_size: size of the data to read per range, in bytes. - * @start_addr: array of addresses. for type IWL_FW_INI_REGION_DRAM_BUFFER, - * 1 entry. For any other case, num_of_ranges entries. + * @fifos: used in case of fifos region. + * @offset: offset to use for each memory base address + * @start_addr: array of addresses. */ struct iwl_fw_ini_region_cfg { __le32 region_id; @@ -152,10 +179,11 @@ struct iwl_fw_ini_region_cfg { __le32 name_len; u8 name[IWL_FW_INI_MAX_NAME]; union { - __le32 num_of_ranges; + struct iwl_fw_ini_region_cfg_internal internal; __le32 allocation_id; + struct iwl_fw_ini_region_cfg_fifos fifos; }; - __le32 range_data_size; + __le32 offset; __le32 start_addr[]; } __packed; /* FW_DEBUG_TLV_REGION_CONFIG_S */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 5feae549d316..9bcce27c6bcf 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1068,7 +1068,7 @@ static void iwl_dump_prph_ini(struct iwl_trans *trans, { struct iwl_fw_error_dump_prph *prph; unsigned long flags; - u32 i, size = le32_to_cpu(reg->num_of_ranges); + u32 i, size = le32_to_cpu(reg->internal.num_of_ranges); IWL_DEBUG_INFO(trans, "WRT PRPH dump\n"); @@ -1077,8 +1077,9 @@ static void iwl_dump_prph_ini(struct iwl_trans *trans, for (i = 0; i < size; i++) { (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); - (*data)->len = cpu_to_le32(le32_to_cpu(reg->range_data_size) + - sizeof(*prph)); + (*data)->len = + cpu_to_le32(le32_to_cpu(reg->internal.range_data_size) + + sizeof(*prph)); prph = (void *)(*data)->data; prph->prph_start = reg->start_addr[i]; prph->data[0] = cpu_to_le32(iwl_read_prph_no_grab(trans, @@ -1092,8 +1093,8 @@ static void iwl_dump_csr_ini(struct iwl_trans *trans, struct iwl_fw_error_dump_data **data, struct iwl_fw_ini_region_cfg *reg) { - int i, num = le32_to_cpu(reg->num_of_ranges); - u32 size = le32_to_cpu(reg->range_data_size); + int i, num = le32_to_cpu(reg->internal.num_of_ranges); + u32 size = le32_to_cpu(reg->internal.range_data_size); IWL_DEBUG_INFO(trans, "WRT CSR dump\n"); @@ -1136,13 +1137,13 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, continue; type = le32_to_cpu(reg->region_type); - num_entries = le32_to_cpu(reg->num_of_ranges); + num_entries = le32_to_cpu(reg->internal.num_of_ranges); switch (type) { case IWL_FW_INI_REGION_DEVICE_MEMORY: size += hdr_len + sizeof(struct iwl_fw_error_dump_named_mem) + - le32_to_cpu(reg->range_data_size); + le32_to_cpu(reg->internal.range_data_size); break; case IWL_FW_INI_REGION_PERIPHERY_MAC: case IWL_FW_INI_REGION_PERIPHERY_PHY: @@ -1168,7 +1169,8 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, break; case IWL_FW_INI_REGION_CSR: size += num_entries * - (hdr_len + le32_to_cpu(reg->range_data_size)); + (hdr_len + + le32_to_cpu(reg->internal.range_data_size)); break; case IWL_FW_INI_REGION_DRAM_BUFFER: /* Transport takes care of DRAM dumping */ @@ -1204,15 +1206,20 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, type = le32_to_cpu(reg->region_type); switch (type) { - case IWL_FW_INI_REGION_DEVICE_MEMORY: - if (WARN_ON(le32_to_cpu(reg->num_of_ranges) > 1)) + case IWL_FW_INI_REGION_DEVICE_MEMORY: { + u32 num_of_ranges = + le32_to_cpu(reg->internal.num_of_ranges); + u32 range_data_size = + le32_to_cpu(reg->internal.range_data_size); + + if (WARN_ON(num_of_ranges) > 1) continue; - iwl_fw_dump_named_mem(fwrt, data, - le32_to_cpu(reg->range_data_size), + iwl_fw_dump_named_mem(fwrt, data, range_data_size, le32_to_cpu(reg->start_addr[0]), reg->name, le32_to_cpu(reg->name_len)); break; + } case IWL_FW_INI_REGION_PERIPHERY_MAC: case IWL_FW_INI_REGION_PERIPHERY_PHY: case IWL_FW_INI_REGION_PERIPHERY_AUX: @@ -1761,6 +1768,7 @@ static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt, for (i = 0; i < size; i++) { struct iwl_fw_ini_region_cfg *reg = iter, **active; int id = le32_to_cpu(reg->region_id); + u32 type = le32_to_cpu(reg->region_type); if (WARN(id >= ARRAY_SIZE(fwrt->dump.active_regs), "Invalid region id %d for apply point %d\n", id, pnt)) @@ -1777,9 +1785,12 @@ static void iwl_fw_dbg_update_regions(struct iwl_fw_runtime *fwrt, *active = reg; - if (le32_to_cpu(reg->region_type) != - IWL_FW_INI_REGION_DRAM_BUFFER) - iter += le32_to_cpu(reg->num_of_ranges) * + if (type == IWL_FW_INI_REGION_TXF || + type == IWL_FW_INI_REGION_RXF) + iter += le32_to_cpu(reg->fifos.num_of_registers) * + sizeof(__le32); + else if (type != IWL_FW_INI_REGION_DRAM_BUFFER) + iter += le32_to_cpu(reg->internal.num_of_ranges) * sizeof(__le32); iter += sizeof(*reg); From 8d534e96b5000a20e80f91c2026e6671c7a0c921 Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Mon, 3 Dec 2018 09:19:25 +0200 Subject: [PATCH 31/38] iwlwifi: dbg_ini: create new dump flow and implement prph dump Create a skeleton to unite all memory dumps in ini mode. Implement prph dump with the new skeleton. Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 164 +++++++++++++----- drivers/net/wireless/intel/iwlwifi/fw/dbg.h | 4 + .../wireless/intel/iwlwifi/fw/error-dump.h | 34 ++++ 3 files changed, 163 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 9bcce27c6bcf..7fd7604751ef 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1062,33 +1062,6 @@ out: return dump_file; } -static void iwl_dump_prph_ini(struct iwl_trans *trans, - struct iwl_fw_error_dump_data **data, - struct iwl_fw_ini_region_cfg *reg) -{ - struct iwl_fw_error_dump_prph *prph; - unsigned long flags; - u32 i, size = le32_to_cpu(reg->internal.num_of_ranges); - - IWL_DEBUG_INFO(trans, "WRT PRPH dump\n"); - - if (!iwl_trans_grab_nic_access(trans, &flags)) - return; - - for (i = 0; i < size; i++) { - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PRPH); - (*data)->len = - cpu_to_le32(le32_to_cpu(reg->internal.range_data_size) + - sizeof(*prph)); - prph = (void *)(*data)->data; - prph->prph_start = reg->start_addr[i]; - prph->data[0] = cpu_to_le32(iwl_read_prph_no_grab(trans, - le32_to_cpu(prph->prph_start))); - *data = iwl_fw_error_next_data(*data); - } - iwl_trans_release_nic_access(trans, &flags); -} - static void iwl_dump_csr_ini(struct iwl_trans *trans, struct iwl_fw_error_dump_data **data, struct iwl_fw_ini_region_cfg *reg) @@ -1114,20 +1087,131 @@ static void iwl_dump_csr_ini(struct iwl_trans *trans, } } +static int iwl_dump_ini_prph_iter(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_error_dump_range *range, + struct iwl_fw_ini_region_cfg *reg, + int idx) +{ + __le32 *val = range->data; + u32 addr, prph_val, offset = le32_to_cpu(reg->offset); + int i; + + range->start_addr = reg->start_addr[idx]; + range->range_data_size = reg->internal.range_data_size; + for (i = 0; i < le32_to_cpu(reg->internal.range_data_size); i += 4) { + addr = le32_to_cpu(range->start_addr) + i; + prph_val = iwl_read_prph(fwrt->trans, addr + offset); + if (prph_val == 0x5a5a5a5a) + return -1; + *val++ = cpu_to_le32(prph_val); + } + return le32_to_cpu(range->range_data_size); +} + +static struct iwl_fw_ini_error_dump_range +*iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt, void *data) +{ + struct iwl_fw_ini_error_dump *dump = data; + + return dump->ranges; +} + +static u32 iwl_dump_ini_mem_get_size(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_region_cfg *reg) +{ + return le32_to_cpu(reg->internal.num_of_ranges) * + le32_to_cpu(reg->internal.range_data_size); +} + +static u32 iwl_dump_ini_mem_ranges(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_region_cfg *reg) +{ + return le32_to_cpu(reg->internal.num_of_ranges); +} + +/** + * struct iwl_dump_ini_mem_ops - ini memory dump operations + * @get_num_of_ranges: returns the number of memory ranges in the region. + * @get_size: returns the size of the region data without headers. + * @fill_mem_hdr: fills region type specific headers and returns the first + * range or NULL if failed to fill headers. + * @fill_range: copies a given memory range into the dump. + * Returns the size of the range or -1 otherwise. + */ +struct iwl_dump_ini_mem_ops { + u32 (*get_num_of_ranges)(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_region_cfg *reg); + u32 (*get_size)(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_region_cfg *reg); + struct iwl_fw_ini_error_dump_range * + (*fill_mem_hdr)(struct iwl_fw_runtime *fwrt, void *data); + int (*fill_range)(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_error_dump_range *range, + struct iwl_fw_ini_region_cfg *reg, int idx); +}; + +/** + * iwl_dump_ini_mem - copy a memory region into the dump + * @fwrt: fw runtime struct. + * @data: dump memory data. + * @reg: region to copy to the dump. + */ +static void +iwl_dump_ini_mem(struct iwl_fw_runtime *fwrt, + enum iwl_fw_ini_region_type type, + struct iwl_fw_error_dump_data **data, + struct iwl_fw_ini_region_cfg *reg, + struct iwl_dump_ini_mem_ops *ops) +{ + struct iwl_fw_ini_error_dump_header *header = (void *)(*data)->data; + struct iwl_fw_ini_error_dump_range *range; + u32 num_of_ranges, i; + + if (WARN_ON(!ops || !ops->get_num_of_ranges || !ops->get_size || + !ops->fill_mem_hdr || !ops->fill_range)) + return; + + num_of_ranges = ops->get_num_of_ranges(fwrt, reg); + + (*data)->type = cpu_to_le32(type | INI_DUMP_BIT); + (*data)->len = cpu_to_le32(sizeof(*header) + num_of_ranges * + sizeof(*range) + ops->get_size(fwrt, reg)); + + header->num_of_ranges = cpu_to_le32(num_of_ranges); + header->name_len = cpu_to_le32(min_t(int, IWL_FW_INI_MAX_NAME, + le32_to_cpu(reg->name_len))); + memcpy(header->name, reg->name, le32_to_cpu(header->name_len)); + + range = ops->fill_mem_hdr(fwrt, header); + if (!range) + return; + + for (i = 0; i < num_of_ranges; i++) { + int range_data_size = ops->fill_range(fwrt, range, reg, i); + + if (range_data_size < 0) { + IWL_ERR(fwrt, "Failed to dump region type %d\n", type); + return; + } + range = ((void *)range) + sizeof(*range) + range_data_size; + } + *data = iwl_fw_error_next_data(*data); +} + static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_trigger *trigger) { - int i, num, size = 0, hdr_len = sizeof(struct iwl_fw_error_dump_data); + int i, size = 0, hdr_len = sizeof(struct iwl_fw_error_dump_data); + u32 dump_header_len = sizeof(struct iwl_fw_ini_error_dump); + u32 range_header_len = sizeof(struct iwl_fw_ini_error_dump_range); if (!trigger || !trigger->num_regions) return 0; - num = le32_to_cpu(trigger->num_regions); - for (i = 0; i < num; i++) { + for (i = 0; i < le32_to_cpu(trigger->num_regions); i++) { u32 reg_id = le32_to_cpu(trigger->data[i]); struct iwl_fw_ini_region_cfg *reg; enum iwl_fw_ini_region_type type; - u32 num_entries; if (WARN_ON(reg_id >= ARRAY_SIZE(fwrt->dump.active_regs))) continue; @@ -1137,8 +1221,6 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, continue; type = le32_to_cpu(reg->region_type); - num_entries = le32_to_cpu(reg->internal.num_of_ranges); - switch (type) { case IWL_FW_INI_REGION_DEVICE_MEMORY: size += hdr_len + @@ -1148,10 +1230,9 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, case IWL_FW_INI_REGION_PERIPHERY_MAC: case IWL_FW_INI_REGION_PERIPHERY_PHY: case IWL_FW_INI_REGION_PERIPHERY_AUX: - size += num_entries * - (hdr_len + - sizeof(struct iwl_fw_error_dump_prph) + - sizeof(u32)); + size += hdr_len + dump_header_len + range_header_len * + iwl_dump_ini_mem_ranges(fwrt, reg) + + iwl_dump_ini_mem_get_size(fwrt, reg); break; case IWL_FW_INI_REGION_TXF: size += iwl_fw_txf_len(fwrt, &fwrt->smem_cfg); @@ -1168,7 +1249,7 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, PAGING_BLOCK_SIZE); break; case IWL_FW_INI_REGION_CSR: - size += num_entries * + size += le32_to_cpu(reg->internal.num_of_ranges) * (hdr_len + le32_to_cpu(reg->internal.range_data_size)); break; @@ -1195,6 +1276,7 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, u32 reg_id = le32_to_cpu(trigger->data[i]); enum iwl_fw_ini_region_type type; struct iwl_fw_ini_region_cfg *reg; + struct iwl_dump_ini_mem_ops ops; if (reg_id >= ARRAY_SIZE(fwrt->dump.active_regs)) continue; @@ -1223,7 +1305,11 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, case IWL_FW_INI_REGION_PERIPHERY_MAC: case IWL_FW_INI_REGION_PERIPHERY_PHY: case IWL_FW_INI_REGION_PERIPHERY_AUX: - iwl_dump_prph_ini(fwrt->trans, data, reg); + ops.get_num_of_ranges = iwl_dump_ini_mem_ranges; + ops.get_size = iwl_dump_ini_mem_get_size; + ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; + ops.fill_range = iwl_dump_ini_prph_iter; + iwl_dump_ini_mem(fwrt, type, data, reg, &ops); break; case IWL_FW_INI_REGION_DRAM_BUFFER: *dump_mask |= BIT(IWL_FW_ERROR_DUMP_FW_MONITOR); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index 2fa7a19e02b6..a6133b5f9e83 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -441,4 +441,8 @@ void iwl_fw_dbg_apply_point(struct iwl_fw_runtime *fwrt, enum iwl_fw_ini_apply_point apply_point); void iwl_fwrt_stop_device(struct iwl_fw_runtime *fwrt); + +/* This bit is used to differentiate the legacy dump from the ini dump */ +#define INI_DUMP_BIT BIT(31) + #endif /* __iwl_fw_dbg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index e1c6aa61ab90..fb2ff29762e2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -290,6 +290,40 @@ struct iwl_fw_error_dump_named_mem { u8 data[]; }; +/** + * struct iwl_fw_ini_error_dump_range - range of memory + * @start_addr: the start address of this range + * @range_data_size: the size of this range, in bytes + * @data: the actual memory + */ +struct iwl_fw_ini_error_dump_range { + __le32 start_addr; + __le32 range_data_size; + __le32 data[]; +} __packed; + +/** + * struct iwl_fw_ini_error_dump_header - ini region dump header + * @num_of_ranges: number of ranges in this region + * @name_len: number of bytes allocated to the name string of this region + * @name: name of the region + */ +struct iwl_fw_ini_error_dump_header { + __le32 num_of_ranges; + __le32 name_len; + u8 name[IWL_FW_INI_MAX_NAME]; +}; + +/** + * struct iwl_fw_ini_error_dump - ini region dump + * @header: the header of this region + * @ranges: the memory ranges of this this region + */ +struct iwl_fw_ini_error_dump { + struct iwl_fw_ini_error_dump_header header; + struct iwl_fw_ini_error_dump_range ranges[]; +} __packed; + /** * struct iwl_fw_error_dump_rb - content of an Receive Buffer * @index: the index of the Receive Buffer in the Rx queue From f4f907aa73e0b6389fb92b1c611048838f83c24d Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Mon, 3 Dec 2018 12:13:31 +0200 Subject: [PATCH 32/38] iwlwifi: dbg_ini: implement csr memory dump Implement csr memory dump in the new dump mechanism. Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 56 +++++++++------------ 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index 7fd7604751ef..e34b287a5405 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1062,31 +1062,6 @@ out: return dump_file; } -static void iwl_dump_csr_ini(struct iwl_trans *trans, - struct iwl_fw_error_dump_data **data, - struct iwl_fw_ini_region_cfg *reg) -{ - int i, num = le32_to_cpu(reg->internal.num_of_ranges); - u32 size = le32_to_cpu(reg->internal.range_data_size); - - IWL_DEBUG_INFO(trans, "WRT CSR dump\n"); - - for (i = 0; i < num; i++) { - u32 add = le32_to_cpu(reg->start_addr[i]); - __le32 *val; - int j; - - (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_CSR); - (*data)->len = cpu_to_le32(size); - val = (void *)(*data)->data; - - for (j = 0; j < size; j += 4) - *val++ = cpu_to_le32(iwl_trans_read32(trans, j + add)); - - *data = iwl_fw_error_next_data(*data); - } -} - static int iwl_dump_ini_prph_iter(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_error_dump_range *range, struct iwl_fw_ini_region_cfg *reg, @@ -1108,6 +1083,25 @@ static int iwl_dump_ini_prph_iter(struct iwl_fw_runtime *fwrt, return le32_to_cpu(range->range_data_size); } +static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_error_dump_range *range, + struct iwl_fw_ini_region_cfg *reg, + int idx) +{ + __le32 *val = range->data; + u32 addr, offset = le32_to_cpu(reg->offset); + int i; + + range->start_addr = reg->start_addr[idx]; + range->range_data_size = reg->internal.range_data_size; + for (i = 0; i < le32_to_cpu(reg->internal.range_data_size); i += 4) { + addr = le32_to_cpu(range->start_addr) + i; + *val++ = cpu_to_le32(iwl_trans_read32(fwrt->trans, + addr + offset)); + } + return le32_to_cpu(range->range_data_size); +} + static struct iwl_fw_ini_error_dump_range *iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt, void *data) { @@ -1230,6 +1224,7 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, case IWL_FW_INI_REGION_PERIPHERY_MAC: case IWL_FW_INI_REGION_PERIPHERY_PHY: case IWL_FW_INI_REGION_PERIPHERY_AUX: + case IWL_FW_INI_REGION_CSR: size += hdr_len + dump_header_len + range_header_len * iwl_dump_ini_mem_ranges(fwrt, reg) + iwl_dump_ini_mem_get_size(fwrt, reg); @@ -1248,11 +1243,6 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, sizeof(struct iwl_fw_error_dump_paging) + PAGING_BLOCK_SIZE); break; - case IWL_FW_INI_REGION_CSR: - size += le32_to_cpu(reg->internal.num_of_ranges) * - (hdr_len + - le32_to_cpu(reg->internal.range_data_size)); - break; case IWL_FW_INI_REGION_DRAM_BUFFER: /* Transport takes care of DRAM dumping */ case IWL_FW_INI_REGION_INTERNAL_BUFFER: @@ -1327,7 +1317,11 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, iwl_fw_dump_rxf(fwrt, data); break; case IWL_FW_INI_REGION_CSR: - iwl_dump_csr_ini(fwrt->trans, data, reg); + ops.get_num_of_ranges = iwl_dump_ini_mem_ranges; + ops.get_size = iwl_dump_ini_mem_get_size; + ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; + ops.fill_range = iwl_dump_ini_csr_iter; + iwl_dump_ini_mem(fwrt, type, data, reg, &ops); break; case IWL_FW_INI_REGION_DRAM_IMR: case IWL_FW_INI_REGION_INTERNAL_BUFFER: From 6e9f8f9924393fc12827a1a9362a34bb53b8e64f Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Mon, 3 Dec 2018 13:40:24 +0200 Subject: [PATCH 33/38] iwlwifi: dbg_ini: implement device internal memory dump Implement device internal memory dump in the new dump mechanism. Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 60 +++++++------------ .../wireless/intel/iwlwifi/fw/error-dump.h | 16 ----- 2 files changed, 21 insertions(+), 55 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index e34b287a5405..edad8175c2a3 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -707,28 +707,6 @@ static void iwl_fw_dump_mem(struct iwl_fw_runtime *fwrt, IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n", dump_mem->type); } -static void iwl_fw_dump_named_mem(struct iwl_fw_runtime *fwrt, - struct iwl_fw_error_dump_data **dump_data, - u32 len, u32 ofs, u8 *name, u8 name_len) -{ - struct iwl_fw_error_dump_named_mem *dump_mem; - - if (!len) - return; - - (*dump_data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM); - (*dump_data)->len = cpu_to_le32(len + sizeof(*dump_mem)); - dump_mem = (void *)(*dump_data)->data; - dump_mem->type = cpu_to_le32(IWL_FW_ERROR_DUMP_MEM_NAMED_MEM); - dump_mem->offset = cpu_to_le32(ofs); - dump_mem->name_len = name_len; - memcpy(dump_mem->name, name, name_len); - iwl_trans_read_mem_bytes(fwrt->trans, ofs, dump_mem->data, len); - *dump_data = iwl_fw_error_next_data(*dump_data); - - IWL_DEBUG_INFO(fwrt, "WRT memory dump. Type=%u\n", dump_mem->type); -} - #define ADD_LEN(len, item_len, const_len) \ do {size_t item = item_len; len += (!!item) * const_len + item; } \ while (0) @@ -1102,6 +1080,21 @@ static int iwl_dump_ini_csr_iter(struct iwl_fw_runtime *fwrt, return le32_to_cpu(range->range_data_size); } +static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_error_dump_range *range, + struct iwl_fw_ini_region_cfg *reg, + int idx) +{ + u32 addr = le32_to_cpu(range->start_addr); + u32 offset = le32_to_cpu(reg->offset); + + range->start_addr = reg->start_addr[idx]; + range->range_data_size = reg->internal.range_data_size; + iwl_trans_read_mem_bytes(fwrt->trans, addr + offset, range->data, + le32_to_cpu(reg->internal.range_data_size)); + return le32_to_cpu(range->range_data_size); +} + static struct iwl_fw_ini_error_dump_range *iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt, void *data) { @@ -1217,10 +1210,6 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, type = le32_to_cpu(reg->region_type); switch (type) { case IWL_FW_INI_REGION_DEVICE_MEMORY: - size += hdr_len + - sizeof(struct iwl_fw_error_dump_named_mem) + - le32_to_cpu(reg->internal.range_data_size); - break; case IWL_FW_INI_REGION_PERIPHERY_MAC: case IWL_FW_INI_REGION_PERIPHERY_PHY: case IWL_FW_INI_REGION_PERIPHERY_AUX: @@ -1278,20 +1267,13 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, type = le32_to_cpu(reg->region_type); switch (type) { - case IWL_FW_INI_REGION_DEVICE_MEMORY: { - u32 num_of_ranges = - le32_to_cpu(reg->internal.num_of_ranges); - u32 range_data_size = - le32_to_cpu(reg->internal.range_data_size); - - if (WARN_ON(num_of_ranges) > 1) - continue; - iwl_fw_dump_named_mem(fwrt, data, range_data_size, - le32_to_cpu(reg->start_addr[0]), - reg->name, - le32_to_cpu(reg->name_len)); + case IWL_FW_INI_REGION_DEVICE_MEMORY: + ops.get_num_of_ranges = iwl_dump_ini_mem_ranges; + ops.get_size = iwl_dump_ini_mem_get_size; + ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; + ops.fill_range = iwl_dump_ini_dev_mem_iter; + iwl_dump_ini_mem(fwrt, type, data, reg, &ops); break; - } case IWL_FW_INI_REGION_PERIPHERY_MAC: case IWL_FW_INI_REGION_PERIPHERY_PHY: case IWL_FW_INI_REGION_PERIPHERY_AUX: diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index fb2ff29762e2..42e0c4c93c82 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -274,22 +274,6 @@ struct iwl_fw_error_dump_mem { u8 data[]; }; -/** - * struct iwl_fw_error_dump_named_mem - chunk of memory - * @type: &enum iwl_fw_error_dump_mem_type - * @offset: the offset from which the memory was read - * @name_len: name length - * @name: file name - * @data: the content of the memory - */ -struct iwl_fw_error_dump_named_mem { - __le32 type; - __le32 offset; - u8 name_len; - u8 name[32]; - u8 data[]; -}; - /** * struct iwl_fw_ini_error_dump_range - range of memory * @start_addr: the start address of this range From 505a00c071132689611d66cb61625282df9bee12 Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Wed, 5 Dec 2018 10:08:19 +0200 Subject: [PATCH 34/38] iwlwifi: dbg_ini: implement paging memory dump Implement paging memory dump in the new dump mechanism. To support this change, moved iwl_self_init_dram strcut from trans_pcie to trans so that it will accessible via fw_runtime. Signed-off-by: Shahar S Matityahu Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/fw/dbg.c | 114 ++++++++++++++++-- .../net/wireless/intel/iwlwifi/iwl-trans.h | 15 +++ .../wireless/intel/iwlwifi/pcie/ctxt-info.c | 6 +- .../wireless/intel/iwlwifi/pcie/internal.h | 18 +-- .../net/wireless/intel/iwlwifi/pcie/trans.c | 10 +- 5 files changed, 125 insertions(+), 38 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index edad8175c2a3..390401300fcf 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1095,6 +1095,43 @@ static int iwl_dump_ini_dev_mem_iter(struct iwl_fw_runtime *fwrt, return le32_to_cpu(range->range_data_size); } +static int +iwl_dump_ini_paging_gen2_iter(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_error_dump_range *range, + struct iwl_fw_ini_region_cfg *reg, + int idx) +{ + u32 page_size = fwrt->trans->init_dram.paging[idx].size; + + range->start_addr = cpu_to_le32(idx); + range->range_data_size = cpu_to_le32(page_size); + memcpy(range->data, fwrt->trans->init_dram.paging[idx].block, + page_size); + return le32_to_cpu(range->range_data_size); +} + +static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_error_dump_range *range, + struct iwl_fw_ini_region_cfg *reg, + int idx) +{ + /* increase idx by 1 since the pages are from 1 to + * fwrt->num_of_paging_blk + 1 + */ + struct page *page = fwrt->fw_paging_db[++idx].fw_paging_block; + dma_addr_t addr = fwrt->fw_paging_db[idx].fw_paging_phys; + u32 page_size = fwrt->fw_paging_db[idx].fw_paging_size; + + range->start_addr = cpu_to_le32(idx); + range->range_data_size = cpu_to_le32(page_size); + dma_sync_single_for_cpu(fwrt->trans->dev, addr, page_size, + DMA_BIDIRECTIONAL); + memcpy(range->data, page_address(page), page_size); + dma_sync_single_for_device(fwrt->trans->dev, addr, page_size, + DMA_BIDIRECTIONAL); + return le32_to_cpu(range->range_data_size); +} + static struct iwl_fw_ini_error_dump_range *iwl_dump_ini_mem_fill_header(struct iwl_fw_runtime *fwrt, void *data) { @@ -1110,12 +1147,46 @@ static u32 iwl_dump_ini_mem_get_size(struct iwl_fw_runtime *fwrt, le32_to_cpu(reg->internal.range_data_size); } +static u32 iwl_dump_ini_paging_gen2_get_size(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_region_cfg *reg) +{ + int i; + u32 size = 0; + + for (i = 0; i < fwrt->trans->init_dram.paging_cnt; i++) + size += fwrt->trans->init_dram.paging[i].size; + return size; +} + +static u32 iwl_dump_ini_paging_get_size(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_region_cfg *reg) +{ + int i; + u32 size = 0; + + for (i = 1; i <= fwrt->num_of_paging_blk; i++) + size += fwrt->fw_paging_db[i].fw_paging_size; + return size; +} + static u32 iwl_dump_ini_mem_ranges(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_region_cfg *reg) { return le32_to_cpu(reg->internal.num_of_ranges); } +static u32 iwl_dump_ini_paging_gen2_ranges(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_region_cfg *reg) +{ + return fwrt->trans->init_dram.paging_cnt; +} + +static u32 iwl_dump_ini_paging_ranges(struct iwl_fw_runtime *fwrt, + struct iwl_fw_ini_region_cfg *reg) +{ + return fwrt->num_of_paging_blk; +} + /** * struct iwl_dump_ini_mem_ops - ini memory dump operations * @get_num_of_ranges: returns the number of memory ranges in the region. @@ -1224,14 +1295,21 @@ static int iwl_fw_ini_get_trigger_len(struct iwl_fw_runtime *fwrt, case IWL_FW_INI_REGION_RXF: size += iwl_fw_rxf_len(fwrt, &fwrt->smem_cfg); break; - case IWL_FW_INI_REGION_PAGING: - if (!iwl_fw_dbg_is_paging_enabled(fwrt)) - break; - size += fwrt->num_of_paging_blk * - (hdr_len + - sizeof(struct iwl_fw_error_dump_paging) + - PAGING_BLOCK_SIZE); + case IWL_FW_INI_REGION_PAGING: { + size += hdr_len + dump_header_len; + if (iwl_fw_dbg_is_paging_enabled(fwrt)) { + size += range_header_len * + iwl_dump_ini_paging_ranges(fwrt, reg) + + iwl_dump_ini_paging_get_size(fwrt, reg); + } else { + size += range_header_len * + iwl_dump_ini_paging_gen2_ranges(fwrt, + reg) + + iwl_dump_ini_paging_gen2_get_size(fwrt, + reg); + } break; + } case IWL_FW_INI_REGION_DRAM_BUFFER: /* Transport takes care of DRAM dumping */ case IWL_FW_INI_REGION_INTERNAL_BUFFER: @@ -1286,12 +1364,24 @@ static void iwl_fw_ini_dump_trigger(struct iwl_fw_runtime *fwrt, case IWL_FW_INI_REGION_DRAM_BUFFER: *dump_mask |= BIT(IWL_FW_ERROR_DUMP_FW_MONITOR); break; - case IWL_FW_INI_REGION_PAGING: - if (iwl_fw_dbg_is_paging_enabled(fwrt)) - iwl_dump_paging(fwrt, data); - else - *dump_mask |= BIT(IWL_FW_ERROR_DUMP_PAGING); + case IWL_FW_INI_REGION_PAGING: { + ops.fill_mem_hdr = iwl_dump_ini_mem_fill_header; + if (iwl_fw_dbg_is_paging_enabled(fwrt)) { + ops.get_num_of_ranges = + iwl_dump_ini_paging_ranges; + ops.get_size = iwl_dump_ini_paging_get_size; + ops.fill_range = iwl_dump_ini_paging_iter; + } else { + ops.get_num_of_ranges = + iwl_dump_ini_paging_gen2_ranges; + ops.get_size = + iwl_dump_ini_paging_gen2_get_size; + ops.fill_range = iwl_dump_ini_paging_gen2_iter; + } + + iwl_dump_ini_mem(fwrt, type, data, reg, &ops); break; + } case IWL_FW_INI_REGION_TXF: iwl_fw_dump_txf(fwrt, data); break; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index d79025d663df..36d0addb79e8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -700,6 +700,20 @@ struct iwl_dram_data { int size; }; +/** + * struct iwl_self_init_dram - dram data used by self init process + * @fw: lmac and umac dram data + * @fw_cnt: total number of items in array + * @paging: paging dram data + * @paging_cnt: total number of items in array + */ +struct iwl_self_init_dram { + struct iwl_dram_data *fw; + int fw_cnt; + struct iwl_dram_data *paging; + int paging_cnt; +}; + /** * struct iwl_trans - transport common data * @@ -794,6 +808,7 @@ struct iwl_trans { u8 dbg_n_dest_reg; int num_blocks; struct iwl_dram_data fw_mon[IWL_FW_INI_APPLY_NUM]; + struct iwl_self_init_dram init_dram; enum iwl_plat_pm_mode system_pm_mode; enum iwl_plat_pm_mode runtime_pm_mode; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index 7f4aaa810ea1..9274e317cc77 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -59,8 +59,7 @@ void iwl_pcie_ctxt_info_free_paging(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_self_init_dram *dram = &trans_pcie->init_dram; + struct iwl_self_init_dram *dram = &trans->init_dram; int i; if (!dram->paging) { @@ -83,8 +82,7 @@ int iwl_pcie_init_fw_sec(struct iwl_trans *trans, const struct fw_img *fw, struct iwl_context_info_dram *ctxt_dram) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_self_init_dram *dram = &trans_pcie->init_dram; + struct iwl_self_init_dram *dram = &trans->init_dram; int i, ret, lmac_cnt, umac_cnt, paging_cnt; if (WARN(dram->paging, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 0d16bcc3141f..ee38ae3b0d30 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -453,20 +453,6 @@ enum iwl_image_response_code { IWL_IMAGE_RESP_FAIL = 2, }; -/** - * struct iwl_self_init_dram - dram data used by self init process - * @fw: lmac and umac dram data - * @fw_cnt: total number of items in array - * @paging: paging dram data - * @paging_cnt: total number of items in array - */ -struct iwl_self_init_dram { - struct iwl_dram_data *fw; - int fw_cnt; - struct iwl_dram_data *paging; - int paging_cnt; -}; - /** * struct cont_rec: continuous recording data structure * @prev_wr_ptr: the last address that was read in monitor_data @@ -554,7 +540,6 @@ struct iwl_trans_pcie { dma_addr_t prph_info_dma_addr; dma_addr_t prph_scratch_dma_addr; dma_addr_t iml_dma_addr; - struct iwl_self_init_dram init_dram; struct iwl_trans *trans; struct net_device napi_dev; @@ -813,8 +798,7 @@ static inline int iwl_pcie_ctxt_info_alloc_dma(struct iwl_trans *trans, static inline void iwl_pcie_ctxt_info_free_fw_img(struct iwl_trans *trans) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_self_init_dram *dram = &trans_pcie->init_dram; + struct iwl_self_init_dram *dram = &trans->init_dram; int i; if (!dram->fw) { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index f26664b0f0f8..ac82c891595c 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -3220,10 +3220,10 @@ static struct iwl_trans_dump_data /* Paged memory for gen2 HW */ if (trans->cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) - for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) + for (i = 0; i < trans->init_dram.paging_cnt; i++) len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_paging) + - trans_pcie->init_dram.paging[i].size; + trans->init_dram.paging[i].size; dump_data = vzalloc(len); if (!dump_data) @@ -3275,16 +3275,16 @@ static struct iwl_trans_dump_data /* Paged memory for gen2 HW */ if (trans->cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) { - for (i = 0; i < trans_pcie->init_dram.paging_cnt; i++) { + for (i = 0; i < trans->init_dram.paging_cnt; i++) { struct iwl_fw_error_dump_paging *paging; - u32 page_len = trans_pcie->init_dram.paging[i].size; + u32 page_len = trans->init_dram.paging[i].size; data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_PAGING); data->len = cpu_to_le32(sizeof(*paging) + page_len); paging = (void *)data->data; paging->index = cpu_to_le32(i); memcpy(paging->data, - trans_pcie->init_dram.paging[i].block, page_len); + trans->init_dram.paging[i].block, page_len); data = iwl_fw_error_next_data(data); len += sizeof(*data) + sizeof(*paging) + page_len; From b8a7547d77782ba1d403f2417be9453a3a256e14 Mon Sep 17 00:00:00 2001 From: Shahar S Matityahu Date: Wed, 5 Dec 2018 08:48:26 +0200 Subject: [PATCH 35/38] iwlwifi: fix send hcmd timeout recovery flow Both iwl_trans_fw_error and iwl_force_nmi initiate async recovery flow. Calling them both is redundant and causing a race. Solve this by removing the call to iwl_trans_fw_error. Signed-off-by: Shahar S Matityahu Fixes: cfadc3ffccd5 ("iwlwifi: pcie: stop the firmware when we restart it") Signed-off-by: Luca Coelho --- .../net/wireless/intel/iwlwifi/iwl-trans.h | 3 +++ .../wireless/intel/iwlwifi/pcie/internal.h | 1 + .../net/wireless/intel/iwlwifi/pcie/trans.c | 25 +++++++++++++++++++ .../net/wireless/intel/iwlwifi/pcie/tx-gen2.c | 8 +++--- drivers/net/wireless/intel/iwlwifi/pcie/tx.c | 8 +++--- 5 files changed, 35 insertions(+), 10 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 36d0addb79e8..ef23c6aafb5c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -688,6 +688,9 @@ enum iwl_plat_pm_mode { */ #define IWL_TRANS_IDLE_TIMEOUT 2000 +/* Max time to wait for nmi interrupt */ +#define IWL_TRANS_NMI_TIMEOUT (HZ / 4) + /** * struct iwl_dram_data * @physical: page phy pointer diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index ee38ae3b0d30..9e1bcafad786 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -1036,6 +1036,7 @@ static inline bool iwl_pcie_dbg_on(struct iwl_trans *trans) void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state); void iwl_trans_pcie_dump_regs(struct iwl_trans *trans); +void iwl_trans_sync_nmi(struct iwl_trans *trans); #ifdef CONFIG_IWLWIFI_DEBUGFS int iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index ac82c891595c..0b29b2ebb23d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -3650,3 +3650,28 @@ out_no_pci: iwl_trans_free(trans); return ERR_PTR(ret); } + +void iwl_trans_sync_nmi(struct iwl_trans *trans) +{ + unsigned long timeout = jiffies + IWL_TRANS_NMI_TIMEOUT; + + iwl_disable_interrupts(trans); + iwl_force_nmi(trans); + while (time_after(timeout, jiffies)) { + u32 inta_hw = iwl_read32(trans, + CSR_MSIX_HW_INT_CAUSES_AD); + + /* Error detected by uCode */ + if (inta_hw & MSIX_HW_INT_CAUSES_REG_SW_ERR) { + /* Clear causes register */ + iwl_write32(trans, CSR_MSIX_HW_INT_CAUSES_AD, + inta_hw & + MSIX_HW_INT_CAUSES_REG_SW_ERR); + break; + } + + mdelay(1); + } + iwl_enable_interrupts(trans); + iwl_trans_fw_error(trans); +} diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index f3d2e8fe920b..88530d9f4a54 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -6,7 +6,7 @@ * GPL LICENSE SUMMARY * * Copyright(c) 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as @@ -20,7 +20,7 @@ * BSD LICENSE * * Copyright(c) 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -965,9 +965,7 @@ static int iwl_pcie_gen2_send_hcmd_sync(struct iwl_trans *trans, cmd_str); ret = -ETIMEDOUT; - iwl_force_nmi(trans); - iwl_trans_fw_error(trans); - + iwl_trans_sync_nmi(trans); goto cancel; } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 07395502f419..28a371814387 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -8,7 +8,7 @@ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as @@ -31,7 +31,7 @@ * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH - * Copyright(c) 2018 Intel Corporation + * Copyright(c) 2018 - 2019 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -1942,9 +1942,7 @@ static int iwl_pcie_send_hcmd_sync(struct iwl_trans *trans, iwl_get_cmd_string(trans, cmd->id)); ret = -ETIMEDOUT; - iwl_force_nmi(trans); - iwl_trans_fw_error(trans); - + iwl_trans_sync_nmi(trans); goto cancel; } From c96b5eec2105ccb6a956c8da6bb6deb670ebaf43 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Dec 2018 09:27:47 +0100 Subject: [PATCH 36/38] iwlwifi: refactor NIC init sequence The typical sequence of setting INIT_DONE and then waiting for clock stabilisation is going to need a new workarounds, so first of all refactor it. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- .../wireless/intel/iwlwifi/iwl-eeprom-read.c | 43 +++++------- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 30 +++++++++ drivers/net/wireless/intel/iwlwifi/iwl-io.h | 5 ++ .../net/wireless/intel/iwlwifi/mvm/utils.c | 18 +---- .../wireless/intel/iwlwifi/pcie/trans-gen2.c | 21 +----- .../net/wireless/intel/iwlwifi/pcie/trans.c | 67 +++---------------- 6 files changed, 64 insertions(+), 120 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c index a6db6a814257..82e87192119e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-eeprom-read.c @@ -193,34 +193,25 @@ static int iwl_init_otp_access(struct iwl_trans *trans) { int ret; - /* Enable 40MHz radio clock */ - iwl_write32(trans, CSR_GP_CNTRL, - iwl_read32(trans, CSR_GP_CNTRL) | - BIT(trans->cfg->csr->flag_init_done)); + ret = iwl_finish_nic_init(trans); + if (ret) + return ret; - /* wait for clock to be ready */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_mac_clock_ready), - BIT(trans->cfg->csr->flag_mac_clock_ready), - 25000); - if (ret < 0) { - IWL_ERR(trans, "Time out access OTP\n"); - } else { - iwl_set_bits_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_RESET_REQ); - udelay(5); - iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG, - APMG_PS_CTRL_VAL_RESET_REQ); + iwl_set_bits_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); + udelay(5); + iwl_clear_bits_prph(trans, APMG_PS_CTRL_REG, + APMG_PS_CTRL_VAL_RESET_REQ); - /* - * CSR auto clock gate disable bit - - * this is only applicable for HW with OTP shadow RAM - */ - if (trans->cfg->base_params->shadow_ram_support) - iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, - CSR_RESET_LINK_PWR_MGMT_DISABLED); - } - return ret; + /* + * CSR auto clock gate disable bit - + * this is only applicable for HW with OTP shadow RAM + */ + if (trans->cfg->base_params->shadow_ram_support) + iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + + return 0; } static int iwl_read_otp_word(struct iwl_trans *trans, u16 addr, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index d62eb2d9389a..4ea5883f9175 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -488,3 +488,33 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf) return 0; } + +int iwl_finish_nic_init(struct iwl_trans *trans) +{ + int err; + + /* + * Set "initialization complete" bit to move adapter from + * D0U* --> D0A* (powered-up active) state. + */ + iwl_set_bit(trans, CSR_GP_CNTRL, + BIT(trans->cfg->csr->flag_init_done)); + + if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + udelay(2); + + /* + * Wait for clock stabilization; once stabilized, access to + * device-internal resources is supported, e.g. iwl_write_prph() + * and accesses to uCode SRAM. + */ + err = iwl_poll_bit(trans, CSR_GP_CNTRL, + BIT(trans->cfg->csr->flag_mac_clock_ready), + BIT(trans->cfg->csr->flag_mac_clock_ready), + 25000); + if (err < 0) + IWL_DEBUG_INFO(trans, "Failed to wake NIC\n"); + + return err < 0 ? err : 0; +} +IWL_EXPORT_SYMBOL(iwl_finish_nic_init); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h index 61477e58352d..bf1100837d72 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h @@ -5,6 +5,8 @@ * * GPL LICENSE SUMMARY * + * Copyright (C) 2018 Intel Corporation + * * This program is free software; you can redistribute it and/or modify it * under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. @@ -23,6 +25,7 @@ * * BSD LICENSE * + * Copyright (C) 2018 Intel Corporation * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -96,6 +99,8 @@ void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs, void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask); void iwl_force_nmi(struct iwl_trans *trans); +int iwl_finish_nic_init(struct iwl_trans *trans); + /* Error handling */ int iwl_dump_fh(struct iwl_trans *trans, char **buf); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index 92f61e8a6284..4a18997fb48a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -523,23 +523,9 @@ static void iwl_mvm_dump_lmac_error_log(struct iwl_mvm *mvm, u8 lmac_num) /* reset the device */ iwl_trans_sw_reset(trans); - /* set INIT_DONE flag */ - iwl_set_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_init_done)); - - /* and wait for clock stabilization */ - if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) - udelay(2); - - err = iwl_poll_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_mac_clock_ready), - BIT(trans->cfg->csr->flag_mac_clock_ready), - 25000); - if (err < 0) { - IWL_DEBUG_INFO(trans, - "Failed to reset the card for the dump\n"); + err = iwl_finish_nic_init(trans); + if (err) return; - } } iwl_trans_read_mem_bytes(trans, base, &table, sizeof(table)); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 77f3610e5ca9..e2d64378c932 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -92,26 +92,9 @@ int iwl_pcie_gen2_apm_init(struct iwl_trans *trans) iwl_pcie_apm_config(trans); - /* - * Set "initialization complete" bit to move adapter from - * D0U* --> D0A* (powered-up active) state. - */ - iwl_set_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_init_done)); - - /* - * Wait for clock stabilization; once stabilized, access to - * device-internal resources is supported, e.g. iwl_write_prph() - * and accesses to uCode SRAM. - */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_mac_clock_ready), - BIT(trans->cfg->csr->flag_mac_clock_ready), - 25000); - if (ret < 0) { - IWL_DEBUG_INFO(trans, "Failed to init the card\n"); + ret = iwl_finish_nic_init(trans); + if (ret) return ret; - } set_bit(STATUS_DEVICE_ENABLED, &trans->status); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 0b29b2ebb23d..375d8f25b886 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -364,26 +364,9 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) if (trans->cfg->base_params->pll_cfg) iwl_set_bit(trans, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL); - /* - * Set "initialization complete" bit to move adapter from - * D0U* --> D0A* (powered-up active) state. - */ - iwl_set_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_init_done)); - - /* - * Wait for clock stabilization; once stabilized, access to - * device-internal resources is supported, e.g. iwl_write_prph() - * and accesses to uCode SRAM. - */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_mac_clock_ready), - BIT(trans->cfg->csr->flag_mac_clock_ready), - 25000); - if (ret < 0) { - IWL_ERR(trans, "Failed to init the card\n"); + ret = iwl_finish_nic_init(trans); + if (ret) return ret; - } if (trans->cfg->host_interrupt_operation_mode) { /* @@ -453,23 +436,8 @@ static void iwl_pcie_apm_lp_xtal_enable(struct iwl_trans *trans) iwl_trans_pcie_sw_reset(trans); - /* - * Set "initialization complete" bit to move adapter from - * D0U* --> D0A* (powered-up active) state. - */ - iwl_set_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_init_done)); - - /* - * Wait for clock stabilization; once stabilized, access to - * device-internal resources is possible. - */ - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_mac_clock_ready), - BIT(trans->cfg->csr->flag_mac_clock_ready), - 25000); - if (WARN_ON(ret < 0)) { - IWL_ERR(trans, "Access time out - failed to enable LP XTAL\n"); + ret = iwl_finish_nic_init(trans); + if (WARN_ON(ret)) { /* Release XTAL ON request */ __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_XTAL_ON); @@ -1558,20 +1526,10 @@ static int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, iwl_set_bit(trans, CSR_GP_CNTRL, BIT(trans->cfg->csr->flag_mac_access_req)); - iwl_set_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_init_done)); - if (trans->cfg->device_family >= IWL_DEVICE_FAMILY_8000) - udelay(2); - - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_mac_clock_ready), - BIT(trans->cfg->csr->flag_mac_clock_ready), - 25000); - if (ret < 0) { - IWL_ERR(trans, "Failed to resume the device (mac ready)\n"); + ret = iwl_finish_nic_init(trans); + if (ret) return ret; - } /* * Reconfigure IVAR table in case of MSIX or reset ict table in @@ -3521,18 +3479,9 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, * in-order to recognize C step driver should read chip version * id located at the AUX bus MISC address space. */ - iwl_set_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_init_done)); - udelay(2); - - ret = iwl_poll_bit(trans, CSR_GP_CNTRL, - BIT(trans->cfg->csr->flag_mac_clock_ready), - BIT(trans->cfg->csr->flag_mac_clock_ready), - 25000); - if (ret < 0) { - IWL_DEBUG_INFO(trans, "Failed to wake up the nic\n"); + ret = iwl_finish_nic_init(trans); + if (ret) goto out_no_pci; - } if (iwl_trans_grab_nic_access(trans, &flags)) { u32 hw_step; From dbf592f3d14fb7d532cb7c820b1065cf33e02aaa Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 11 Dec 2018 21:20:43 +0100 Subject: [PATCH 37/38] iwlwifi: mvm: fix RFH config command with >=10 CPUs If we have >=10 (logical) CPUs, our command size exceeds the internal buffer size and the command fails; fix that by using IWL_HCMD_DFL_NOCOPY for the command that's allocated anyway. While at it, also fix the leak of cmd, and use struct_size() to calculate its size. Signed-off-by: Johannes Berg Fixes: 8edbfaa19835 ("iwlwifi: mvm: configure multi RX queue") Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/mvm/fw.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index 134dab2d774a..8140b193cc05 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -127,13 +127,17 @@ static int iwl_send_rss_cfg_cmd(struct iwl_mvm *mvm) static int iwl_configure_rxq(struct iwl_mvm *mvm) { - int i, num_queues, size; + int i, num_queues, size, ret; struct iwl_rfh_queue_config *cmd; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(DATA_PATH_GROUP, RFH_QUEUE_CONFIG_CMD), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; /* Do not configure default queue, it is configured via context info */ num_queues = mvm->trans->num_rx_queues - 1; - size = sizeof(*cmd) + num_queues * sizeof(struct iwl_rfh_queue_data); + size = struct_size(cmd, data, num_queues); cmd = kzalloc(size, GFP_KERNEL); if (!cmd) @@ -154,10 +158,14 @@ static int iwl_configure_rxq(struct iwl_mvm *mvm) cmd->data[i].fr_bd_wid = cpu_to_le32(data.fr_bd_wid); } - return iwl_mvm_send_cmd_pdu(mvm, - WIDE_ID(DATA_PATH_GROUP, - RFH_QUEUE_CONFIG_CMD), - 0, size, cmd); + hcmd.data[0] = cmd; + hcmd.len[0] = size; + + ret = iwl_mvm_send_cmd(mvm, &hcmd); + + kfree(cmd); + + return ret; } static int iwl_mvm_send_dqa_cmd(struct iwl_mvm *mvm) From b998fbbd531fe33d9dd50fd37056f48b4257113d Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 10 Dec 2018 09:36:58 +0100 Subject: [PATCH 38/38] iwlwifi: implement BISR HW workaround for 22260 devices There's a small hardware bug in 22260 devices which thus require a few more delays during initialization. Implement this workaround. Signed-off-by: Johannes Berg Signed-off-by: Luca Coelho --- drivers/net/wireless/intel/iwlwifi/cfg/22000.c | 3 +++ drivers/net/wireless/intel/iwlwifi/iwl-config.h | 4 +++- drivers/net/wireless/intel/iwlwifi/iwl-io.c | 10 ++++++++++ 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index c547288d32aa..28d8c3477371 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -220,6 +220,7 @@ const struct iwl_cfg iwl22260_2ax_cfg = { * HT size; mac80211 would otherwise pick the HE max (256) by default. */ .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, + .bisr_workaround = 1, }; const struct iwl_cfg killer1650x_2ax_cfg = { @@ -232,6 +233,7 @@ const struct iwl_cfg killer1650x_2ax_cfg = { * HT size; mac80211 would otherwise pick the HE max (256) by default. */ .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, + .bisr_workaround = 1, }; const struct iwl_cfg killer1650w_2ax_cfg = { @@ -244,6 +246,7 @@ const struct iwl_cfg killer1650w_2ax_cfg = { * HT size; mac80211 would otherwise pick the HE max (256) by default. */ .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, + .bisr_workaround = 1, }; /* diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index f1e493380713..5e713730165a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -379,6 +379,7 @@ struct iwl_csr_params { * @nvm_type: see &enum iwl_nvm_type * @d3_debug_data_base_addr: base address where D3 debug data is stored * @d3_debug_data_length: length of the D3 debug data + * @bisr_workaround: BISR hardware workaround (for 22260 series devices) * * We enable the driver to be backward compatible wrt. hardware features. * API differences in uCode shouldn't be handled here but through TLVs @@ -428,7 +429,8 @@ struct iwl_cfg { use_tfh:1, gen2:1, cdb:1, - dbgc_supported:1; + dbgc_supported:1, + bisr_workaround:1; u8 valid_tx_ant; u8 valid_rx_ant; u8 non_shared_ant; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index 4ea5883f9175..bf171edad53a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -493,6 +493,11 @@ int iwl_finish_nic_init(struct iwl_trans *trans) { int err; + if (trans->cfg->bisr_workaround) { + /* ensure the TOP FSM isn't still in previous reset */ + mdelay(2); + } + /* * Set "initialization complete" bit to move adapter from * D0U* --> D0A* (powered-up active) state. @@ -515,6 +520,11 @@ int iwl_finish_nic_init(struct iwl_trans *trans) if (err < 0) IWL_DEBUG_INFO(trans, "Failed to wake NIC\n"); + if (trans->cfg->bisr_workaround) { + /* ensure BISR shift has finished */ + udelay(200); + } + return err < 0 ? err : 0; } IWL_EXPORT_SYMBOL(iwl_finish_nic_init);