From: Johannes Berg Add functions to be able to do memory read under NIC access, in order to use them later during firmware dump. These may drop and re-acquire the spinlock, but will not acquire and release the NIC access. Signed-off-by: Johannes Berg Link: https://patch.msgid.link/20260517100550.7bb1ea51c347.I91420a24fb0c481c75a2600d60e1365c15c1c5a9@changeid Signed-off-by: Miri Korenblit --- .../net/wireless/intel/iwlwifi/iwl-trans.c | 6 +++ .../net/wireless/intel/iwlwifi/iwl-trans.h | 16 ++++++ .../intel/iwlwifi/pcie/gen1_2/internal.h | 2 + .../intel/iwlwifi/pcie/gen1_2/trans.c | 54 +++++++++++++++++++ 4 files changed, 78 insertions(+) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 5b44e15fe64d..0009488ca51b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -459,6 +459,12 @@ int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr, } IWL_EXPORT_SYMBOL(iwl_trans_read_mem); +int iwl_trans_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords) +{ + return iwl_trans_pcie_read_mem_no_grab(trans, addr, buf, dwords); +} + int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr, const void *buf, int dwords) { diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index 1ed6bcb7882c..3ae840e546e8 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -919,6 +919,14 @@ void iwl_trans_write_prph(struct iwl_trans *trans, u32 ofs, u32 val); int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr, void *buf, int dwords); +/* + * Note the special calling convention - it's allowed to drop the + * internal transport lock and re-enable BHs temporarily, but will + * not release NIC access. + */ +int iwl_trans_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords); + int iwl_trans_read_config32(struct iwl_trans *trans, u32 ofs, u32 *val); @@ -934,6 +942,14 @@ void iwl_trans_debugfs_cleanup(struct iwl_trans *trans); (bufsize) / sizeof(u32)); \ }) +static inline int +iwl_trans_read_mem_bytes_no_grab(struct iwl_trans *trans, + u32 addr, void *buf, u32 bufsize) +{ + return iwl_trans_read_mem_no_grab(trans, addr, buf, + bufsize / sizeof(u32)); +} + int iwl_trans_write_imr_mem(struct iwl_trans *trans, u32 dst_addr, u64 src_addr, u32 byte_cnt); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h index 24f8714eae9f..abc0c831d1ca 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/internal.h @@ -1186,6 +1186,8 @@ u32 iwl_trans_pcie_read_prph(struct iwl_trans *trans, u32 reg); void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, u32 val); int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, void *buf, int dwords); +int iwl_trans_pcie_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords); int iwl_trans_pcie_sw_reset(struct iwl_trans *trans, bool retake_ownership); struct iwl_trans_dump_data * iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask, diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c index 50342604491d..1c4ee76d8387 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/trans.c @@ -2424,6 +2424,15 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) return false; } +static void iwl_trans_pcie_resched_with_nic_access(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + spin_unlock_bh(&trans_pcie->reg_lock); + cond_resched(); + spin_lock_bh(&trans_pcie->reg_lock); +} + void __releases(nic_access_nobh) iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) { @@ -2506,6 +2515,51 @@ int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, return 0; } +int iwl_trans_pcie_read_mem_no_grab(struct iwl_trans *trans, u32 addr, + void *buf, u32 dwords) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); +#define IWL_MAX_HW_ERRS 5 + unsigned int num_consec_hw_errors = 0; + u32 offs = 0; + u32 *vals = buf; + + lockdep_assert_held(&trans_pcie->reg_lock); + + while (offs < dwords) { + /* limit the time we spin here under lock to 1/2s */ + unsigned long end = jiffies + HZ / 2; + bool resched = false; + + iwl_write32(trans, HBUS_TARG_MEM_RADDR, + addr + 4 * offs); + + while (offs < dwords) { + vals[offs] = iwl_read32(trans, HBUS_TARG_MEM_RDAT); + + if (iwl_trans_is_hw_error_value(vals[offs])) + num_consec_hw_errors++; + else + num_consec_hw_errors = 0; + + if (num_consec_hw_errors >= IWL_MAX_HW_ERRS) + return -EIO; + + offs++; + + if (time_after(jiffies, end)) { + resched = true; + break; + } + } + + if (resched) + iwl_trans_pcie_resched_with_nic_access(trans); + } + + return 0; +} + int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs, u32 *val) { -- 2.34.1