| From f6f30312746dc9fbda75909231ecad06c43609db Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Thu, 20 Nov 2025 12:15:33 +0800 |
| Subject: net: aquantia: Add missing descriptor cache invalidation on ATL2 |
| |
| From: Kai-Heng Feng <kaihengf@nvidia.com> |
| |
| [ Upstream commit 7526183cfdbe352c51c285762f0e15b7c428ea06 ] |
| |
| ATL2 hardware was missing descriptor cache invalidation in hw_stop(), |
| causing SMMU translation faults during device shutdown and module removal: |
| [ 70.355743] arm-smmu-v3 arm-smmu-v3.5.auto: event 0x10 received: |
| [ 70.361893] arm-smmu-v3 arm-smmu-v3.5.auto: 0x0002060000000010 |
| [ 70.367948] arm-smmu-v3 arm-smmu-v3.5.auto: 0x0000020000000000 |
| [ 70.374002] arm-smmu-v3 arm-smmu-v3.5.auto: 0x00000000ff9bc000 |
| [ 70.380055] arm-smmu-v3 arm-smmu-v3.5.auto: 0x0000000000000000 |
| [ 70.386109] arm-smmu-v3 arm-smmu-v3.5.auto: event: F_TRANSLATION client: 0001:06:00.0 sid: 0x20600 ssid: 0x0 iova: 0xff9bc000 ipa: 0x0 |
| [ 70.398531] arm-smmu-v3 arm-smmu-v3.5.auto: unpriv data write s1 "Input address caused fault" stag: 0x0 |
| |
| Commit 7a1bb49461b1 ("net: aquantia: fix potential IOMMU fault after |
| driver unbind") and commit ed4d81c4b3f2 ("net: aquantia: when cleaning |
| hw cache it should be toggled") fixed cache invalidation for ATL B0, but |
| ATL2 was left with only interrupt disabling. This allowed hardware to |
| write to cached descriptors after DMA memory was unmapped, triggering |
| SMMU faults. Once cache invalidation is applied to ATL2, the translation |
| fault can't be observed anymore. |
| |
| Add shared aq_hw_invalidate_descriptor_cache() helper and use it in both |
| ATL B0 and ATL2 hw_stop() implementations for consistent behavior. |
| |
| Fixes: e54dcf4bba3e ("net: atlantic: basic A2 init/deinit hw_ops") |
| Tested-by: Carol Soto <csoto@nvidia.com> |
| Signed-off-by: Kai-Heng Feng <kaihengf@nvidia.com> |
| Reviewed-by: Simon Horman <horms@kernel.org> |
| Link: https://patch.msgid.link/20251120041537.62184-1-kaihengf@nvidia.com |
| Signed-off-by: Paolo Abeni <pabeni@redhat.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| .../ethernet/aquantia/atlantic/aq_hw_utils.c | 22 +++++++++++++++++++ |
| .../ethernet/aquantia/atlantic/aq_hw_utils.h | 1 + |
| .../aquantia/atlantic/hw_atl/hw_atl_b0.c | 19 +--------------- |
| .../aquantia/atlantic/hw_atl2/hw_atl2.c | 2 +- |
| 4 files changed, 25 insertions(+), 19 deletions(-) |
| |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c b/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c |
| index 1921741f7311d..18b08277d2e1a 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c |
| +++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.c |
| @@ -15,6 +15,7 @@ |
| |
| #include "aq_hw.h" |
| #include "aq_nic.h" |
| +#include "hw_atl/hw_atl_llh.h" |
| |
| void aq_hw_write_reg_bit(struct aq_hw_s *aq_hw, u32 addr, u32 msk, |
| u32 shift, u32 val) |
| @@ -81,6 +82,27 @@ void aq_hw_write_reg64(struct aq_hw_s *hw, u32 reg, u64 value) |
| lo_hi_writeq(value, hw->mmio + reg); |
| } |
| |
| +int aq_hw_invalidate_descriptor_cache(struct aq_hw_s *hw) |
| +{ |
| + int err; |
| + u32 val; |
| + |
| + /* Invalidate Descriptor Cache to prevent writing to the cached |
| + * descriptors and to the data pointer of those descriptors |
| + */ |
| + hw_atl_rdm_rx_dma_desc_cache_init_tgl(hw); |
| + |
| + err = aq_hw_err_from_flags(hw); |
| + if (err) |
| + goto err_exit; |
| + |
| + readx_poll_timeout_atomic(hw_atl_rdm_rx_dma_desc_cache_init_done_get, |
| + hw, val, val == 1, 1000U, 10000U); |
| + |
| +err_exit: |
| + return err; |
| +} |
| + |
| int aq_hw_err_from_flags(struct aq_hw_s *hw) |
| { |
| int err = 0; |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.h b/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.h |
| index ffa6e4067c211..d89c63d88e4a4 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.h |
| +++ b/drivers/net/ethernet/aquantia/atlantic/aq_hw_utils.h |
| @@ -35,6 +35,7 @@ u32 aq_hw_read_reg(struct aq_hw_s *hw, u32 reg); |
| void aq_hw_write_reg(struct aq_hw_s *hw, u32 reg, u32 value); |
| u64 aq_hw_read_reg64(struct aq_hw_s *hw, u32 reg); |
| void aq_hw_write_reg64(struct aq_hw_s *hw, u32 reg, u64 value); |
| +int aq_hw_invalidate_descriptor_cache(struct aq_hw_s *hw); |
| int aq_hw_err_from_flags(struct aq_hw_s *hw); |
| int aq_hw_num_tcs(struct aq_hw_s *hw); |
| int aq_hw_q_per_tc(struct aq_hw_s *hw); |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c |
| index 56c46266bb0ae..3a0b6d65d5fca 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c |
| +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0.c |
| @@ -1198,26 +1198,9 @@ static int hw_atl_b0_hw_interrupt_moderation_set(struct aq_hw_s *self) |
| |
| static int hw_atl_b0_hw_stop(struct aq_hw_s *self) |
| { |
| - int err; |
| - u32 val; |
| - |
| hw_atl_b0_hw_irq_disable(self, HW_ATL_B0_INT_MASK); |
| |
| - /* Invalidate Descriptor Cache to prevent writing to the cached |
| - * descriptors and to the data pointer of those descriptors |
| - */ |
| - hw_atl_rdm_rx_dma_desc_cache_init_tgl(self); |
| - |
| - err = aq_hw_err_from_flags(self); |
| - |
| - if (err) |
| - goto err_exit; |
| - |
| - readx_poll_timeout_atomic(hw_atl_rdm_rx_dma_desc_cache_init_done_get, |
| - self, val, val == 1, 1000U, 10000U); |
| - |
| -err_exit: |
| - return err; |
| + return aq_hw_invalidate_descriptor_cache(self); |
| } |
| |
| int hw_atl_b0_hw_ring_tx_stop(struct aq_hw_s *self, struct aq_ring_s *ring) |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c |
| index b0ed572e88c67..0ce9caae8799c 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c |
| +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl2/hw_atl2.c |
| @@ -759,7 +759,7 @@ static int hw_atl2_hw_stop(struct aq_hw_s *self) |
| { |
| hw_atl_b0_hw_irq_disable(self, HW_ATL2_INT_MASK); |
| |
| - return 0; |
| + return aq_hw_invalidate_descriptor_cache(self); |
| } |
| |
| static struct aq_stats_s *hw_atl2_utils_get_hw_stats(struct aq_hw_s *self) |
| -- |
| 2.51.0 |
| |