From 943021c9fc40af1e7afd1a5665da0a8b2e55069f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 8 Jan 2024 18:42:03 +0100 Subject: [PATCH] 5.4-stable patches added patches: ath10k-add-interrupt-summary-based-ce-processing.patch ath10k-get-rid-of-per_ce_irq-hw-param.patch ath10k-keep-track-of-which-interrupts-fired-don-t-poll-them.patch ath10k-wait-until-copy-complete-is-actually-done-before-completing.patch --- ...nterrupt-summary-based-ce-processing.patch | 170 +++++++++++ ...th10k-get-rid-of-per_ce_irq-hw-param.patch | 146 ++++++++++ ...ich-interrupts-fired-don-t-poll-them.patch | 268 ++++++++++++++++++ ...e-is-actually-done-before-completing.patch | 98 +++++++ queue-5.4/series | 4 + 5 files changed, 686 insertions(+) create mode 100644 queue-5.4/ath10k-add-interrupt-summary-based-ce-processing.patch create mode 100644 queue-5.4/ath10k-get-rid-of-per_ce_irq-hw-param.patch create mode 100644 queue-5.4/ath10k-keep-track-of-which-interrupts-fired-don-t-poll-them.patch create mode 100644 queue-5.4/ath10k-wait-until-copy-complete-is-actually-done-before-completing.patch diff --git a/queue-5.4/ath10k-add-interrupt-summary-based-ce-processing.patch b/queue-5.4/ath10k-add-interrupt-summary-based-ce-processing.patch new file mode 100644 index 00000000000..aea48af510d --- /dev/null +++ b/queue-5.4/ath10k-add-interrupt-summary-based-ce-processing.patch @@ -0,0 +1,170 @@ +From stable+bounces-10188-greg=kroah.com@vger.kernel.org Mon Jan 8 16:37:56 2024 +From: Amit Pundir +Date: Mon, 8 Jan 2024 21:07:35 +0530 +Subject: ath10k: Add interrupt summary based CE processing +To: Greg KH , Sasha Levin , Douglas Anderson , Rakesh Pillai +Cc: Yongqin Liu , Stable , Kalle Valo +Message-ID: <20240108153737.3538218-3-amit.pundir@linaro.org> + +From: Rakesh Pillai + +[ Upstream commit b92aba35d39d10d8a6bdf2495172fd490c598b4a ] + +Currently the NAPI processing loops through all +the copy engines and processes a particular copy +engine is the copy completion is set for that copy +engine. The host driver is not supposed to access +any copy engine register after clearing the interrupt +status register. + +This might result in kernel crash like the one below +[ 1159.220143] Call trace: +[ 1159.220170] ath10k_snoc_read32+0x20/0x40 [ath10k_snoc] +[ 1159.220193] ath10k_ce_per_engine_service_any+0x78/0x130 [ath10k_core] +[ 1159.220203] ath10k_snoc_napi_poll+0x38/0x8c [ath10k_snoc] +[ 1159.220270] net_rx_action+0x100/0x3b0 +[ 1159.220312] __do_softirq+0x164/0x30c +[ 1159.220345] run_ksoftirqd+0x2c/0x64 +[ 1159.220380] smpboot_thread_fn+0x1b0/0x288 +[ 1159.220405] kthread+0x11c/0x12c +[ 1159.220423] ret_from_fork+0x10/0x18 + +To avoid such a scenario, we generate an interrupt +summary by reading the copy completion for all the +copy engine before actually processing any of them. +This will avoid reading the interrupt status register +for any CE after the interrupt status is cleared. + +Tested-on: WCN3990 hw1.0 SNOC WLAN.HL.3.1-01040-QCAHLSWMTPLZ-1 + +Signed-off-by: Rakesh Pillai +Reviewed-by: Douglas Anderson +Tested-by: Douglas Anderson +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/1593193967-29897-1-git-send-email-pillair@codeaurora.org +Stable-dep-of: 170c75d43a77 ("ath10k: Don't touch the CE interrupt registers after power up") +Signed-off-by: Amit Pundir +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/ath/ath10k/ce.c | 63 +++++++++++++++++++++-------------- + drivers/net/wireless/ath/ath10k/ce.h | 5 +- + 2 files changed, 42 insertions(+), 26 deletions(-) + +--- a/drivers/net/wireless/ath/ath10k/ce.c ++++ b/drivers/net/wireless/ath/ath10k/ce.c +@@ -481,15 +481,38 @@ static inline void ath10k_ce_engine_int_ + ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask); + } + +-static inline bool ath10k_ce_engine_int_status_check(struct ath10k *ar, +- u32 ce_ctrl_addr, +- unsigned int mask) ++static bool ath10k_ce_engine_int_status_check(struct ath10k *ar, u32 ce_ctrl_addr, ++ unsigned int mask) + { + struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; + + return ath10k_ce_read32(ar, ce_ctrl_addr + wm_regs->addr) & mask; + } + ++u32 ath10k_ce_gen_interrupt_summary(struct ath10k *ar) ++{ ++ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; ++ struct ath10k_ce_pipe *ce_state; ++ struct ath10k_ce *ce; ++ u32 irq_summary = 0; ++ u32 ctrl_addr; ++ u32 ce_id; ++ ++ ce = ath10k_ce_priv(ar); ++ ++ for (ce_id = 0; ce_id < CE_COUNT; ce_id++) { ++ ce_state = &ce->ce_states[ce_id]; ++ ctrl_addr = ce_state->ctrl_addr; ++ if (ath10k_ce_engine_int_status_check(ar, ctrl_addr, ++ wm_regs->cc_mask)) { ++ irq_summary |= BIT(ce_id); ++ } ++ } ++ ++ return irq_summary; ++} ++EXPORT_SYMBOL(ath10k_ce_gen_interrupt_summary); ++ + /* + * Guts of ath10k_ce_send. + * The caller takes responsibility for any needed locking. +@@ -1308,32 +1331,24 @@ void ath10k_ce_per_engine_service(struct + struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; + u32 ctrl_addr = ce_state->ctrl_addr; + +- spin_lock_bh(&ce->ce_lock); +- +- if (ath10k_ce_engine_int_status_check(ar, ctrl_addr, +- wm_regs->cc_mask)) { +- /* Clear before handling */ +- ath10k_ce_engine_int_status_clear(ar, ctrl_addr, +- wm_regs->cc_mask); +- +- spin_unlock_bh(&ce->ce_lock); +- +- if (ce_state->recv_cb) +- ce_state->recv_cb(ce_state); +- +- if (ce_state->send_cb) +- ce_state->send_cb(ce_state); +- +- spin_lock_bh(&ce->ce_lock); +- } +- + /* ++ * Clear before handling ++ * + * Misc CE interrupts are not being handled, but still need + * to be cleared. ++ * ++ * NOTE: When the last copy engine interrupt is cleared the ++ * hardware will go to sleep. Once this happens any access to ++ * the CE registers can cause a hardware fault. + */ +- ath10k_ce_engine_int_status_clear(ar, ctrl_addr, wm_regs->wm_mask); ++ ath10k_ce_engine_int_status_clear(ar, ctrl_addr, ++ wm_regs->cc_mask | wm_regs->wm_mask); + +- spin_unlock_bh(&ce->ce_lock); ++ if (ce_state->recv_cb) ++ ce_state->recv_cb(ce_state); ++ ++ if (ce_state->send_cb) ++ ce_state->send_cb(ce_state); + } + EXPORT_SYMBOL(ath10k_ce_per_engine_service); + +--- a/drivers/net/wireless/ath/ath10k/ce.h ++++ b/drivers/net/wireless/ath/ath10k/ce.h +@@ -259,6 +259,8 @@ int ath10k_ce_disable_interrupts(struct + void ath10k_ce_enable_interrupts(struct ath10k *ar); + void ath10k_ce_dump_registers(struct ath10k *ar, + struct ath10k_fw_crash_data *crash_data); ++ ++u32 ath10k_ce_gen_interrupt_summary(struct ath10k *ar); + void ath10k_ce_alloc_rri(struct ath10k *ar); + void ath10k_ce_free_rri(struct ath10k *ar); + +@@ -369,7 +371,6 @@ static inline u32 ath10k_ce_base_address + (((x) & CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_MASK) >> \ + CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_LSB) + #define CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS 0x0000 +-#define CE_INTERRUPT_SUMMARY (GENMASK(CE_COUNT_MAX - 1, 0)) + + static inline u32 ath10k_ce_interrupt_summary(struct ath10k *ar) + { +@@ -380,7 +381,7 @@ static inline u32 ath10k_ce_interrupt_su + ce->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS + + CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS)); + else +- return CE_INTERRUPT_SUMMARY; ++ return ath10k_ce_gen_interrupt_summary(ar); + } + + /* Host software's Copy Engine configuration. */ diff --git a/queue-5.4/ath10k-get-rid-of-per_ce_irq-hw-param.patch b/queue-5.4/ath10k-get-rid-of-per_ce_irq-hw-param.patch new file mode 100644 index 00000000000..8c1d6ba0184 --- /dev/null +++ b/queue-5.4/ath10k-get-rid-of-per_ce_irq-hw-param.patch @@ -0,0 +1,146 @@ +From stable+bounces-10191-greg=kroah.com@vger.kernel.org Mon Jan 8 16:37:58 2024 +From: Amit Pundir +Date: Mon, 8 Jan 2024 21:07:37 +0530 +Subject: ath10k: Get rid of "per_ce_irq" hw param +To: Greg KH , Sasha Levin , Douglas Anderson , Rakesh Pillai +Cc: Yongqin Liu , Stable , Brian Norris , Kalle Valo +Message-ID: <20240108153737.3538218-5-amit.pundir@linaro.org> + +From: Douglas Anderson + +[ Upstream commit 7f86551665121931ecd6d327e019e7a69782bfcd ] + +As of the patch ("ath10k: Keep track of which interrupts fired, don't +poll them") we now have no users of this hardware parameter. Remove +it. + +Suggested-by: Brian Norris +Signed-off-by: Douglas Anderson +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20200709082024.v2.2.I083faa4e62e69f863311c89ae5eb28ec5a229b70@changeid +Stable-dep-of: 170c75d43a77 ("ath10k: Don't touch the CE interrupt registers after power up") +Signed-off-by: Amit Pundir +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/ath/ath10k/core.c | 13 ------------- + drivers/net/wireless/ath/ath10k/hw.h | 3 --- + 2 files changed, 16 deletions(-) + +--- a/drivers/net/wireless/ath/ath10k/core.c ++++ b/drivers/net/wireless/ath/ath10k/core.c +@@ -118,7 +118,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -154,7 +153,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -217,7 +215,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -252,7 +249,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -287,7 +283,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -325,7 +320,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -366,7 +360,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -414,7 +407,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -459,7 +451,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -494,7 +485,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -531,7 +521,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -573,7 +562,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = 0x20, + .target_64bit = false, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL, +- .per_ce_irq = false, + .shadow_reg_support = false, + .rri_on_ddr = false, + .hw_filter_reset_required = true, +@@ -601,7 +589,6 @@ static const struct ath10k_hw_params ath + .num_wds_entries = TARGET_HL_TLV_NUM_WDS_ENTRIES, + .target_64bit = true, + .rx_ring_fill_level = HTT_RX_RING_FILL_LEVEL_DUAL_MAC, +- .per_ce_irq = true, + .shadow_reg_support = true, + .rri_on_ddr = true, + .hw_filter_reset_required = false, +--- a/drivers/net/wireless/ath/ath10k/hw.h ++++ b/drivers/net/wireless/ath/ath10k/hw.h +@@ -590,9 +590,6 @@ struct ath10k_hw_params { + /* Target rx ring fill level */ + u32 rx_ring_fill_level; + +- /* target supporting per ce IRQ */ +- bool per_ce_irq; +- + /* target supporting shadow register for ce write */ + bool shadow_reg_support; + diff --git a/queue-5.4/ath10k-keep-track-of-which-interrupts-fired-don-t-poll-them.patch b/queue-5.4/ath10k-keep-track-of-which-interrupts-fired-don-t-poll-them.patch new file mode 100644 index 00000000000..f09d0eecf33 --- /dev/null +++ b/queue-5.4/ath10k-keep-track-of-which-interrupts-fired-don-t-poll-them.patch @@ -0,0 +1,268 @@ +From stable+bounces-10189-greg=kroah.com@vger.kernel.org Mon Jan 8 16:37:55 2024 +From: Amit Pundir +Date: Mon, 8 Jan 2024 21:07:36 +0530 +Subject: ath10k: Keep track of which interrupts fired, don't poll them +To: Greg KH , Sasha Levin , Douglas Anderson , Rakesh Pillai +Cc: Yongqin Liu , Stable , Brian Norris , Kalle Valo +Message-ID: <20240108153737.3538218-4-amit.pundir@linaro.org> + +From: Douglas Anderson + +[ Upstream commit d66d24ac300cf41c6b88367fc9b4b6348679273d ] + +If we have a per CE (Copy Engine) IRQ then we have no summary +register. Right now the code generates a summary register by +iterating over all copy engines and seeing if they have an interrupt +pending. + +This has a problem. Specifically if _none_ if the Copy Engines have +an interrupt pending then they might go into low power mode and +reading from their address space will cause a full system crash. This +was seen to happen when two interrupts went off at nearly the same +time. Both were handled by a single call of ath10k_snoc_napi_poll() +but, because there were two interrupts handled and thus two calls to +napi_schedule() there was still a second call to +ath10k_snoc_napi_poll() which ran with no interrupts pending. + +Instead of iterating over all the copy engines, let's just keep track +of the IRQs that fire. Then we can effectively generate our own +summary without ever needing to read the Copy Engines. + +Tested-on: WCN3990 SNOC WLAN.HL.3.2.2-00490-QCAHLSWMTPL-1 + +Signed-off-by: Douglas Anderson +Reviewed-by: Rakesh Pillai +Reviewed-by: Brian Norris +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20200709082024.v2.1.I4d2f85ffa06f38532631e864a3125691ef5ffe06@changeid +Stable-dep-of: 170c75d43a77 ("ath10k: Don't touch the CE interrupt registers after power up") +Signed-off-by: Amit Pundir +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/ath/ath10k/ce.c | 84 ++++++++++++--------------------- + drivers/net/wireless/ath/ath10k/ce.h | 14 ++--- + drivers/net/wireless/ath/ath10k/snoc.c | 19 +++++-- + drivers/net/wireless/ath/ath10k/snoc.h | 1 + 4 files changed, 52 insertions(+), 66 deletions(-) + +--- a/drivers/net/wireless/ath/ath10k/ce.c ++++ b/drivers/net/wireless/ath/ath10k/ce.c +@@ -481,38 +481,6 @@ static inline void ath10k_ce_engine_int_ + ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask); + } + +-static bool ath10k_ce_engine_int_status_check(struct ath10k *ar, u32 ce_ctrl_addr, +- unsigned int mask) +-{ +- struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; +- +- return ath10k_ce_read32(ar, ce_ctrl_addr + wm_regs->addr) & mask; +-} +- +-u32 ath10k_ce_gen_interrupt_summary(struct ath10k *ar) +-{ +- struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; +- struct ath10k_ce_pipe *ce_state; +- struct ath10k_ce *ce; +- u32 irq_summary = 0; +- u32 ctrl_addr; +- u32 ce_id; +- +- ce = ath10k_ce_priv(ar); +- +- for (ce_id = 0; ce_id < CE_COUNT; ce_id++) { +- ce_state = &ce->ce_states[ce_id]; +- ctrl_addr = ce_state->ctrl_addr; +- if (ath10k_ce_engine_int_status_check(ar, ctrl_addr, +- wm_regs->cc_mask)) { +- irq_summary |= BIT(ce_id); +- } +- } +- +- return irq_summary; +-} +-EXPORT_SYMBOL(ath10k_ce_gen_interrupt_summary); +- + /* + * Guts of ath10k_ce_send. + * The caller takes responsibility for any needed locking. +@@ -1399,45 +1367,55 @@ static void ath10k_ce_per_engine_handler + ath10k_ce_watermark_intr_disable(ar, ctrl_addr); + } + +-int ath10k_ce_disable_interrupts(struct ath10k *ar) ++void ath10k_ce_disable_interrupt(struct ath10k *ar, int ce_id) + { + struct ath10k_ce *ce = ath10k_ce_priv(ar); + struct ath10k_ce_pipe *ce_state; + u32 ctrl_addr; +- int ce_id; + +- for (ce_id = 0; ce_id < CE_COUNT; ce_id++) { +- ce_state = &ce->ce_states[ce_id]; +- if (ce_state->attr_flags & CE_ATTR_POLL) +- continue; ++ ce_state = &ce->ce_states[ce_id]; ++ if (ce_state->attr_flags & CE_ATTR_POLL) ++ return; + +- ctrl_addr = ath10k_ce_base_address(ar, ce_id); ++ ctrl_addr = ath10k_ce_base_address(ar, ce_id); + +- ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); +- ath10k_ce_error_intr_disable(ar, ctrl_addr); +- ath10k_ce_watermark_intr_disable(ar, ctrl_addr); +- } ++ ath10k_ce_copy_complete_intr_disable(ar, ctrl_addr); ++ ath10k_ce_error_intr_disable(ar, ctrl_addr); ++ ath10k_ce_watermark_intr_disable(ar, ctrl_addr); ++} ++EXPORT_SYMBOL(ath10k_ce_disable_interrupt); + +- return 0; ++void ath10k_ce_disable_interrupts(struct ath10k *ar) ++{ ++ int ce_id; ++ ++ for (ce_id = 0; ce_id < CE_COUNT; ce_id++) ++ ath10k_ce_disable_interrupt(ar, ce_id); + } + EXPORT_SYMBOL(ath10k_ce_disable_interrupts); + +-void ath10k_ce_enable_interrupts(struct ath10k *ar) ++void ath10k_ce_enable_interrupt(struct ath10k *ar, int ce_id) + { + struct ath10k_ce *ce = ath10k_ce_priv(ar); +- int ce_id; + struct ath10k_ce_pipe *ce_state; + ++ ce_state = &ce->ce_states[ce_id]; ++ if (ce_state->attr_flags & CE_ATTR_POLL) ++ return; ++ ++ ath10k_ce_per_engine_handler_adjust(ce_state); ++} ++EXPORT_SYMBOL(ath10k_ce_enable_interrupt); ++ ++void ath10k_ce_enable_interrupts(struct ath10k *ar) ++{ ++ int ce_id; ++ + /* Enable interrupts for copy engine that + * are not using polling mode. + */ +- for (ce_id = 0; ce_id < CE_COUNT; ce_id++) { +- ce_state = &ce->ce_states[ce_id]; +- if (ce_state->attr_flags & CE_ATTR_POLL) +- continue; +- +- ath10k_ce_per_engine_handler_adjust(ce_state); +- } ++ for (ce_id = 0; ce_id < CE_COUNT; ce_id++) ++ ath10k_ce_enable_interrupt(ar, ce_id); + } + EXPORT_SYMBOL(ath10k_ce_enable_interrupts); + +--- a/drivers/net/wireless/ath/ath10k/ce.h ++++ b/drivers/net/wireless/ath/ath10k/ce.h +@@ -255,12 +255,13 @@ int ath10k_ce_cancel_send_next(struct at + /*==================CE Interrupt Handlers====================*/ + void ath10k_ce_per_engine_service_any(struct ath10k *ar); + void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id); +-int ath10k_ce_disable_interrupts(struct ath10k *ar); ++void ath10k_ce_disable_interrupt(struct ath10k *ar, int ce_id); ++void ath10k_ce_disable_interrupts(struct ath10k *ar); ++void ath10k_ce_enable_interrupt(struct ath10k *ar, int ce_id); + void ath10k_ce_enable_interrupts(struct ath10k *ar); + void ath10k_ce_dump_registers(struct ath10k *ar, + struct ath10k_fw_crash_data *crash_data); + +-u32 ath10k_ce_gen_interrupt_summary(struct ath10k *ar); + void ath10k_ce_alloc_rri(struct ath10k *ar); + void ath10k_ce_free_rri(struct ath10k *ar); + +@@ -376,12 +377,9 @@ static inline u32 ath10k_ce_interrupt_su + { + struct ath10k_ce *ce = ath10k_ce_priv(ar); + +- if (!ar->hw_params.per_ce_irq) +- return CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( +- ce->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS + +- CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS)); +- else +- return ath10k_ce_gen_interrupt_summary(ar); ++ return CE_WRAPPER_INTERRUPT_SUMMARY_HOST_MSI_GET( ++ ce->bus_ops->read32((ar), CE_WRAPPER_BASE_ADDRESS + ++ CE_WRAPPER_INTERRUPT_SUMMARY_ADDRESS)); + } + + /* Host software's Copy Engine configuration. */ +--- a/drivers/net/wireless/ath/ath10k/snoc.c ++++ b/drivers/net/wireless/ath/ath10k/snoc.c +@@ -3,6 +3,7 @@ + * Copyright (c) 2018 The Linux Foundation. All rights reserved. + */ + ++#include + #include + #include + #include +@@ -927,6 +928,7 @@ static int ath10k_snoc_hif_start(struct + { + struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + ++ bitmap_clear(ar_snoc->pending_ce_irqs, 0, CE_COUNT_MAX); + napi_enable(&ar->napi); + ath10k_snoc_irq_enable(ar); + ath10k_snoc_rx_post(ar); +@@ -1166,7 +1168,9 @@ static irqreturn_t ath10k_snoc_per_engin + return IRQ_HANDLED; + } + +- ath10k_snoc_irq_disable(ar); ++ ath10k_ce_disable_interrupt(ar, ce_id); ++ set_bit(ce_id, ar_snoc->pending_ce_irqs); ++ + napi_schedule(&ar->napi); + + return IRQ_HANDLED; +@@ -1175,20 +1179,25 @@ static irqreturn_t ath10k_snoc_per_engin + static int ath10k_snoc_napi_poll(struct napi_struct *ctx, int budget) + { + struct ath10k *ar = container_of(ctx, struct ath10k, napi); ++ struct ath10k_snoc *ar_snoc = ath10k_snoc_priv(ar); + int done = 0; ++ int ce_id; + + if (test_bit(ATH10K_FLAG_CRASH_FLUSH, &ar->dev_flags)) { + napi_complete(ctx); + return done; + } + +- ath10k_ce_per_engine_service_any(ar); ++ for (ce_id = 0; ce_id < CE_COUNT; ce_id++) ++ if (test_and_clear_bit(ce_id, ar_snoc->pending_ce_irqs)) { ++ ath10k_ce_per_engine_service(ar, ce_id); ++ ath10k_ce_enable_interrupt(ar, ce_id); ++ } ++ + done = ath10k_htt_txrx_compl_task(ar, budget); + +- if (done < budget) { ++ if (done < budget) + napi_complete(ctx); +- ath10k_snoc_irq_enable(ar); +- } + + return done; + } +--- a/drivers/net/wireless/ath/ath10k/snoc.h ++++ b/drivers/net/wireless/ath/ath10k/snoc.h +@@ -81,6 +81,7 @@ struct ath10k_snoc { + struct ath10k_clk_info *clk; + struct ath10k_qmi *qmi; + unsigned long flags; ++ DECLARE_BITMAP(pending_ce_irqs, CE_COUNT_MAX); + }; + + static inline struct ath10k_snoc *ath10k_snoc_priv(struct ath10k *ar) diff --git a/queue-5.4/ath10k-wait-until-copy-complete-is-actually-done-before-completing.patch b/queue-5.4/ath10k-wait-until-copy-complete-is-actually-done-before-completing.patch new file mode 100644 index 00000000000..7ef8609ee74 --- /dev/null +++ b/queue-5.4/ath10k-wait-until-copy-complete-is-actually-done-before-completing.patch @@ -0,0 +1,98 @@ +From stable+bounces-10185-greg=kroah.com@vger.kernel.org Mon Jan 8 16:37:49 2024 +From: Amit Pundir +Date: Mon, 8 Jan 2024 21:07:34 +0530 +Subject: ath10k: Wait until copy complete is actually done before completing +To: Greg KH , Sasha Levin , Douglas Anderson , Rakesh Pillai +Cc: Yongqin Liu , Stable , Kalle Valo +Message-ID: <20240108153737.3538218-2-amit.pundir@linaro.org> + +From: Douglas Anderson + +[ Upstream commit 8f9ed93d09a97444733d492a3bbf66bcb786a777 ] + +On wcn3990 we have "per_ce_irq = true". That makes the +ath10k_ce_interrupt_summary() function always return 0xfff. The +ath10k_ce_per_engine_service_any() function will see this and think +that _all_ copy engines have an interrupt. Without checking, the +ath10k_ce_per_engine_service() assumes that if it's called that the +"copy complete" (cc) interrupt fired. This combination seems bad. + +Let's add a check to make sure that the "copy complete" interrupt +actually fired in ath10k_ce_per_engine_service(). + +This might fix a hard-to-reproduce failure where it appears that the +copy complete handlers run before the copy is really complete. +Specifically a symptom was that we were seeing this on a Qualcomm +sc7180 board: + arm-smmu 15000000.iommu: Unhandled context fault: + fsr=0x402, iova=0x7fdd45780, fsynr=0x30003, cbfrsynra=0xc1, cb=10 + +Even on platforms that don't have wcn3990 this still seems like it +would be a sane thing to do. Specifically the current IRQ handler +comments indicate that there might be other misc interrupt sources +firing that need to be cleared. If one of those sources was the one +that caused the IRQ handler to be called it would also be important to +double-check that the interrupt we cared about actually fired. + +Tested-on: WCN3990 SNOC WLAN.HL.3.2.2-00490-QCAHLSWMTPL-1 + +Signed-off-by: Douglas Anderson +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20200609082015.1.Ife398994e5a0a6830e4d4a16306ef36e0144e7ba@changeid +Stable-dep-of: 170c75d43a77 ("ath10k: Don't touch the CE interrupt registers after power up") +Signed-off-by: Amit Pundir +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/ath/ath10k/ce.c | 30 +++++++++++++++++++++--------- + 1 file changed, 21 insertions(+), 9 deletions(-) + +--- a/drivers/net/wireless/ath/ath10k/ce.c ++++ b/drivers/net/wireless/ath/ath10k/ce.c +@@ -481,6 +481,15 @@ static inline void ath10k_ce_engine_int_ + ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask); + } + ++static inline bool ath10k_ce_engine_int_status_check(struct ath10k *ar, ++ u32 ce_ctrl_addr, ++ unsigned int mask) ++{ ++ struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; ++ ++ return ath10k_ce_read32(ar, ce_ctrl_addr + wm_regs->addr) & mask; ++} ++ + /* + * Guts of ath10k_ce_send. + * The caller takes responsibility for any needed locking. +@@ -1301,19 +1310,22 @@ void ath10k_ce_per_engine_service(struct + + spin_lock_bh(&ce->ce_lock); + +- /* Clear the copy-complete interrupts that will be handled here. */ +- ath10k_ce_engine_int_status_clear(ar, ctrl_addr, +- wm_regs->cc_mask); ++ if (ath10k_ce_engine_int_status_check(ar, ctrl_addr, ++ wm_regs->cc_mask)) { ++ /* Clear before handling */ ++ ath10k_ce_engine_int_status_clear(ar, ctrl_addr, ++ wm_regs->cc_mask); + +- spin_unlock_bh(&ce->ce_lock); ++ spin_unlock_bh(&ce->ce_lock); + +- if (ce_state->recv_cb) +- ce_state->recv_cb(ce_state); ++ if (ce_state->recv_cb) ++ ce_state->recv_cb(ce_state); + +- if (ce_state->send_cb) +- ce_state->send_cb(ce_state); ++ if (ce_state->send_cb) ++ ce_state->send_cb(ce_state); + +- spin_lock_bh(&ce->ce_lock); ++ spin_lock_bh(&ce->ce_lock); ++ } + + /* + * Misc CE interrupts are not being handled, but still need diff --git a/queue-5.4/series b/queue-5.4/series index 0d4e2cbda99..4df9b084a84 100644 --- a/queue-5.4/series +++ b/queue-5.4/series @@ -25,3 +25,7 @@ mm-fix-unmap_mapping_range-high-bits-shift-bug.patch mmc-rpmb-fixes-pause-retune-on-all-rpmb-partitions.patch mmc-core-cancel-delayed-work-before-releasing-host.patch mmc-sdhci-sprd-fix-emmc-init-failure-after-hw-reset.patch +ath10k-wait-until-copy-complete-is-actually-done-before-completing.patch +ath10k-add-interrupt-summary-based-ce-processing.patch +ath10k-keep-track-of-which-interrupts-fired-don-t-poll-them.patch +ath10k-get-rid-of-per_ce_irq-hw-param.patch -- 2.47.3