mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00
wifi: iwlwifi: pcie: point invalid TFDs to invalid data
There are occasionally bugs which cause the device to try to use a TFD that it wasn't supposed to, and these are very hard to diagnose. Fill all unused TFDs with a debug command that immediately causes an error to be detected in these cases. Signed-off-by: Johannes Berg <johannes.berg@intel.com> Signed-off-by: Gregory Greenman <gregory.greenman@intel.com> Link: https://lore.kernel.org/r/20230816104355.10a9af1ca91f.Ifc790d62c52b4bc9a74c9581610af498509f5759@changeid Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
parent
80fa8377f5
commit
c83031afaa
4 changed files with 71 additions and 6 deletions
|
@ -29,6 +29,11 @@ enum iwl_debug_cmds {
|
|||
* &struct iwl_dbg_host_event_cfg_cmd
|
||||
*/
|
||||
HOST_EVENT_CFG = 0x3,
|
||||
/**
|
||||
* @INVALID_WR_PTR_CMD: invalid write pointer, set in the TFD
|
||||
* when it's not in use
|
||||
*/
|
||||
INVALID_WR_PTR_CMD = 0x6,
|
||||
/**
|
||||
* @DBGC_SUSPEND_RESUME:
|
||||
* DBGC suspend/resume commad. Uses a single dword as data:
|
||||
|
|
|
@ -1069,6 +1069,7 @@ struct iwl_trans_txqs {
|
|||
* @mbx_addr_1_step: step address data 1
|
||||
* @pcie_link_speed: current PCIe link speed (%PCI_EXP_LNKSTA_CLS_*),
|
||||
* only valid for discrete (not integrated) NICs
|
||||
* @invalid_tx_cmd: invalid TX command buffer
|
||||
*/
|
||||
struct iwl_trans {
|
||||
bool csme_own;
|
||||
|
@ -1133,6 +1134,8 @@ struct iwl_trans {
|
|||
|
||||
u8 pcie_link_speed;
|
||||
|
||||
struct iwl_dma_ptr invalid_tx_cmd;
|
||||
|
||||
/* pointer to trans specific struct */
|
||||
/*Ensure that this pointer will always be aligned to sizeof pointer */
|
||||
char trans_specific[] __aligned(sizeof(void *));
|
||||
|
|
|
@ -2018,6 +2018,30 @@ void iwl_trans_pcie_free_pnvm_dram_regions(struct iwl_dram_regions *dram_regions
|
|||
memset(desc_dram, 0, sizeof(*desc_dram));
|
||||
}
|
||||
|
||||
static void iwl_pcie_free_invalid_tx_cmd(struct iwl_trans *trans)
|
||||
{
|
||||
iwl_pcie_free_dma_ptr(trans, &trans->invalid_tx_cmd);
|
||||
}
|
||||
|
||||
static int iwl_pcie_alloc_invalid_tx_cmd(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_cmd_header_wide bad_cmd = {
|
||||
.cmd = INVALID_WR_PTR_CMD,
|
||||
.group_id = DEBUG_GROUP,
|
||||
.sequence = cpu_to_le16(0xffff),
|
||||
.length = cpu_to_le16(0),
|
||||
.version = 0,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = iwl_pcie_alloc_dma_ptr(trans, &trans->invalid_tx_cmd,
|
||||
sizeof(bad_cmd));
|
||||
if (ret)
|
||||
return ret;
|
||||
memcpy(trans->invalid_tx_cmd.addr, &bad_cmd, sizeof(bad_cmd));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iwl_trans_pcie_free(struct iwl_trans *trans)
|
||||
{
|
||||
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
||||
|
@ -2048,6 +2072,8 @@ void iwl_trans_pcie_free(struct iwl_trans *trans)
|
|||
iwl_pcie_free_ict(trans);
|
||||
}
|
||||
|
||||
iwl_pcie_free_invalid_tx_cmd(trans);
|
||||
|
||||
iwl_pcie_free_fw_monitor(trans);
|
||||
|
||||
iwl_trans_pcie_free_pnvm_dram_regions(&trans_pcie->pnvm_data,
|
||||
|
@ -3684,6 +3710,9 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
|
|||
|
||||
init_waitqueue_head(&trans_pcie->sx_waitq);
|
||||
|
||||
ret = iwl_pcie_alloc_invalid_tx_cmd(trans);
|
||||
if (ret)
|
||||
goto out_no_pci;
|
||||
|
||||
if (trans_pcie->msix_enabled) {
|
||||
ret = iwl_pcie_init_msix_handler(pdev, trans_pcie);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "fw/api/commands.h"
|
||||
#include "fw/api/tx.h"
|
||||
#include "fw/api/datapath.h"
|
||||
#include "fw/api/debug.h"
|
||||
#include "queue/tx.h"
|
||||
#include "iwl-fh.h"
|
||||
#include "iwl-scd.h"
|
||||
|
@ -119,6 +120,15 @@ int iwl_txq_gen2_set_tb(struct iwl_trans *trans, struct iwl_tfh_tfd *tfd,
|
|||
return idx;
|
||||
}
|
||||
|
||||
static void iwl_txq_set_tfd_invalid_gen2(struct iwl_trans *trans,
|
||||
struct iwl_tfh_tfd *tfd)
|
||||
{
|
||||
tfd->num_tbs = 0;
|
||||
|
||||
iwl_txq_gen2_set_tb(trans, tfd, trans->invalid_tx_cmd.dma,
|
||||
trans->invalid_tx_cmd.size);
|
||||
}
|
||||
|
||||
void iwl_txq_gen2_tfd_unmap(struct iwl_trans *trans, struct iwl_cmd_meta *meta,
|
||||
struct iwl_tfh_tfd *tfd)
|
||||
{
|
||||
|
@ -146,7 +156,7 @@ void iwl_txq_gen2_tfd_unmap(struct iwl_trans *trans, struct iwl_cmd_meta *meta,
|
|||
DMA_TO_DEVICE);
|
||||
}
|
||||
|
||||
tfd->num_tbs = 0;
|
||||
iwl_txq_set_tfd_invalid_gen2(trans, tfd);
|
||||
}
|
||||
|
||||
void iwl_txq_gen2_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq)
|
||||
|
@ -1025,11 +1035,21 @@ static void iwl_txq_stuck_timer(struct timer_list *t)
|
|||
iwl_force_nmi(trans);
|
||||
}
|
||||
|
||||
static void iwl_txq_set_tfd_invalid_gen1(struct iwl_trans *trans,
|
||||
struct iwl_tfd *tfd)
|
||||
{
|
||||
tfd->num_tbs = 0;
|
||||
|
||||
iwl_pcie_gen1_tfd_set_tb(trans, tfd, 0, trans->invalid_tx_cmd.dma,
|
||||
trans->invalid_tx_cmd.size);
|
||||
}
|
||||
|
||||
int iwl_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num,
|
||||
bool cmd_queue)
|
||||
{
|
||||
size_t tfd_sz = trans->txqs.tfd.size *
|
||||
trans->trans_cfg->base_params->max_tfd_queue_size;
|
||||
size_t num_entries = trans->trans_cfg->gen2 ?
|
||||
slots_num : trans->trans_cfg->base_params->max_tfd_queue_size;
|
||||
size_t tfd_sz;
|
||||
size_t tb0_buf_sz;
|
||||
int i;
|
||||
|
||||
|
@ -1039,8 +1059,7 @@ int iwl_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num,
|
|||
if (WARN_ON(txq->entries || txq->tfds))
|
||||
return -EINVAL;
|
||||
|
||||
if (trans->trans_cfg->gen2)
|
||||
tfd_sz = trans->txqs.tfd.size * slots_num;
|
||||
tfd_sz = trans->txqs.tfd.size * num_entries;
|
||||
|
||||
timer_setup(&txq->stuck_timer, iwl_txq_stuck_timer, 0);
|
||||
txq->trans = trans;
|
||||
|
@ -1080,6 +1099,15 @@ int iwl_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num,
|
|||
if (!txq->first_tb_bufs)
|
||||
goto err_free_tfds;
|
||||
|
||||
for (i = 0; i < num_entries; i++) {
|
||||
void *tfd = iwl_txq_get_tfd(trans, txq, i);
|
||||
|
||||
if (trans->trans_cfg->gen2)
|
||||
iwl_txq_set_tfd_invalid_gen2(trans, tfd);
|
||||
else
|
||||
iwl_txq_set_tfd_invalid_gen1(trans, tfd);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err_free_tfds:
|
||||
dma_free_coherent(trans->dev, tfd_sz, txq->tfds, txq->dma_addr);
|
||||
|
@ -1397,7 +1425,7 @@ void iwl_txq_gen1_tfd_unmap(struct iwl_trans *trans,
|
|||
|
||||
meta->tbs = 0;
|
||||
|
||||
tfd->num_tbs = 0;
|
||||
iwl_txq_set_tfd_invalid_gen1(trans, tfd);
|
||||
}
|
||||
|
||||
#define IWL_TX_CRC_SIZE 4
|
||||
|
|
Loading…
Add table
Reference in a new issue