| From ee294a8ae4de0b18fe0515300debb08aad03d3e1 Mon Sep 17 00:00:00 2001 |
| From: Dmitry Bogdanov <dmitry.bogdanov@aquantia.com> |
| Date: Fri, 9 Nov 2018 11:53:57 +0000 |
| Subject: net: aquantia: fix potential IOMMU fault after driver unbind |
| |
| [ Upstream commit 7a1bb49461b12b2e6332a4d054256835f45203f3 ] |
| |
| IOMMU fault may occurr on unbind/bind or if_down/if_up sequence. |
| |
| Although driver disables the rings on down, this is not enough. |
| Due to internal HW design, during subsequent initialization |
| NIC sometimes may reuse RX descriptors cache and write to the |
| host memory from the descriptor cache. |
| That's get catched by IOMMU on host. |
| |
| This patch invalidates the descriptor cache in NIC on interface down |
| to prevent writing to the cached descriptors and to the memory pointed |
| in those descriptors. |
| |
| Signed-off-by: Dmitry Bogdanov <dmitry.bogdanov@aquantia.com> |
| Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| .../aquantia/atlantic/hw_atl/hw_atl_b0.c | 6 ++++++ |
| .../aquantia/atlantic/hw_atl/hw_atl_llh.c | 8 ++++++++ |
| .../aquantia/atlantic/hw_atl/hw_atl_llh.h | 3 +++ |
| .../atlantic/hw_atl/hw_atl_llh_internal.h | 18 ++++++++++++++++++ |
| 4 files changed, 35 insertions(+) |
| |
| 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 1d44a386e7d3..0271a0fdfee8 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 |
| @@ -915,6 +915,12 @@ 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) |
| { |
| 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_set(self, 1); |
| + |
| return aq_hw_err_from_flags(self); |
| } |
| |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c |
| index 10ba035dadb1..10ec5dc88e24 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c |
| +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.c |
| @@ -619,6 +619,14 @@ void hw_atl_rpb_rx_flow_ctl_mode_set(struct aq_hw_s *aq_hw, u32 rx_flow_ctl_mode |
| HW_ATL_RPB_RX_FC_MODE_SHIFT, rx_flow_ctl_mode); |
| } |
| |
| +void hw_atl_rdm_rx_dma_desc_cache_init_set(struct aq_hw_s *aq_hw, u32 init) |
| +{ |
| + aq_hw_write_reg_bit(aq_hw, HW_ATL_RDM_RX_DMA_DESC_CACHE_INIT_ADR, |
| + HW_ATL_RDM_RX_DMA_DESC_CACHE_INIT_MSK, |
| + HW_ATL_RDM_RX_DMA_DESC_CACHE_INIT_SHIFT, |
| + init); |
| +} |
| + |
| void hw_atl_rpb_rx_pkt_buff_size_per_tc_set(struct aq_hw_s *aq_hw, |
| u32 rx_pkt_buff_size_per_tc, u32 buffer) |
| { |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h |
| index dfb426f2dc2c..b3bf64b48b93 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h |
| +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh.h |
| @@ -325,6 +325,9 @@ void hw_atl_rpb_rx_pkt_buff_size_per_tc_set(struct aq_hw_s *aq_hw, |
| u32 rx_pkt_buff_size_per_tc, |
| u32 buffer); |
| |
| +/* set rdm rx dma descriptor cache init */ |
| +void hw_atl_rdm_rx_dma_desc_cache_init_set(struct aq_hw_s *aq_hw, u32 init); |
| + |
| /* set rx xoff enable (per tc) */ |
| void hw_atl_rpb_rx_xoff_en_per_tc_set(struct aq_hw_s *aq_hw, u32 rx_xoff_en_per_tc, |
| u32 buffer); |
| diff --git a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h |
| index e0cf70120f1d..e2ecdb1c5a5c 100644 |
| --- a/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h |
| +++ b/drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_llh_internal.h |
| @@ -293,6 +293,24 @@ |
| /* default value of bitfield desc{d}_reset */ |
| #define HW_ATL_RDM_DESCDRESET_DEFAULT 0x0 |
| |
| +/* rdm_desc_init_i bitfield definitions |
| + * preprocessor definitions for the bitfield rdm_desc_init_i. |
| + * port="pif_rdm_desc_init_i" |
| + */ |
| + |
| +/* register address for bitfield rdm_desc_init_i */ |
| +#define HW_ATL_RDM_RX_DMA_DESC_CACHE_INIT_ADR 0x00005a00 |
| +/* bitmask for bitfield rdm_desc_init_i */ |
| +#define HW_ATL_RDM_RX_DMA_DESC_CACHE_INIT_MSK 0xffffffff |
| +/* inverted bitmask for bitfield rdm_desc_init_i */ |
| +#define HW_ATL_RDM_RX_DMA_DESC_CACHE_INIT_MSKN 0x00000000 |
| +/* lower bit position of bitfield rdm_desc_init_i */ |
| +#define HW_ATL_RDM_RX_DMA_DESC_CACHE_INIT_SHIFT 0 |
| +/* width of bitfield rdm_desc_init_i */ |
| +#define HW_ATL_RDM_RX_DMA_DESC_CACHE_INIT_WIDTH 32 |
| +/* default value of bitfield rdm_desc_init_i */ |
| +#define HW_ATL_RDM_RX_DMA_DESC_CACHE_INIT_DEFAULT 0x0 |
| + |
| /* rx int_desc_wrb_en bitfield definitions |
| * preprocessor definitions for the bitfield "int_desc_wrb_en". |
| * port="pif_rdm_int_desc_wrb_en_i" |
| -- |
| 2.17.1 |
| |