From: Johannes Berg johannes.berg@intel.com
[ Upstream commit 3d372c4edfd4dffb7dea71c6b096fb414782b776 ]
If we spin for a long time in memory reads that (for some reason in hardware) take a long time, then we'll eventually get messages such as
watchdog: BUG: soft lockup - CPU#2 stuck for 24s! [kworker/2:2:272]
This is because the reading really does take a very long time, and we don't schedule, so we're hogging the CPU with this task, at least if CONFIG_PREEMPT is not set, e.g. with CONFIG_PREEMPT_VOLUNTARY=y.
Previously I misinterpreted the situation and thought that this was only going to happen if we had interrupts disabled, and then fixed this (which is good anyway, however), but that didn't always help; looking at it again now I realized that the spin unlock will only reschedule if CONFIG_PREEMPT is used.
In order to avoid this issue, change the code to cond_resched() if we've been spinning for too long here.
Signed-off-by: Johannes Berg johannes.berg@intel.com Fixes: 04516706bb99 ("iwlwifi: pcie: limit memory read spin time") Signed-off-by: Luca Coelho luciano.coelho@intel.com Signed-off-by: Kalle Valo kvalo@codeaurora.org Link: https://lore.kernel.org/r/iwlwifi.20210115130253.217a9d6a6a12.If964cb582ab0a... Signed-off-by: Sasha Levin sashal@kernel.org --- drivers/net/wireless/intel/iwlwifi/pcie/trans.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index 99c324af0981f..0110620fd1078 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -2127,6 +2127,7 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, while (offs < dwords) { /* limit the time we spin here under lock to 1/2s */ unsigned long end = jiffies + HZ / 2; + bool resched = false;
if (iwl_trans_grab_nic_access(trans, &flags)) { iwl_write32(trans, HBUS_TARG_MEM_RADDR, @@ -2137,10 +2138,15 @@ static int iwl_trans_pcie_read_mem(struct iwl_trans *trans, u32 addr, HBUS_TARG_MEM_RDAT); offs++;
- if (time_after(jiffies, end)) + if (time_after(jiffies, end)) { + resched = true; break; + } } iwl_trans_release_nic_access(trans, &flags); + + if (resched) + cond_resched(); } else { return -EBUSY; }