[SRU,B,1/1] qlcnic: fix Tx descriptor corruption on 82xx devices

Message ID 20190207124422.21952-2-gpiccoli@canonical.com
State New
Headers show
Series
  • qlcnic: Firmware aborts/hangs in QLogic NIC
Related show

Commit Message

Guilherme Piccoli Feb. 7, 2019, 12:44 p.m.
From: Shahed Shaikh <shahed.shaikh@cavium.com>

BugLink: http://bugs.launchpad.net/bugs/1815033

In regular NIC transmission flow, driver always configures MAC using
Tx queue zero descriptor as a part of MAC learning flow.
But with multi Tx queue supported NIC, regular transmission can occur on
any non-zero Tx queue and from that context it uses
Tx queue zero descriptor to configure MAC, at the same time TX queue
zero could be used by another CPU for regular transmission
which could lead to Tx queue zero descriptor corruption and cause FW
abort.

This patch fixes this in such a way that driver always configures
learned MAC address from the same Tx queue which is used for
regular transmission.

Fixes: 7e2cf4feba05 ("qlcnic: change driver hardware interface mechanism")
Signed-off-by: Shahed Shaikh <shahed.shaikh@cavium.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
(cherry picked from commit c333fa0c4f220f8f7ea5acd6b0ebf3bf13fd684d)
Signed-off-by: Guilherme G. Piccoli <gpiccoli@canonical.com>
---
 drivers/net/ethernet/qlogic/qlcnic/qlcnic.h         |  8 +++++---
 drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c |  3 ++-
 drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h |  3 ++-
 drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h      |  3 ++-
 drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c      | 12 ++++++------
 5 files changed, 17 insertions(+), 12 deletions(-)

Comments

Stefan Bader Feb. 7, 2019, 1:18 p.m. | #1
On 07.02.19 13:44, Guilherme G. Piccoli wrote:
> From: Shahed Shaikh <shahed.shaikh@cavium.com>
> 
> BugLink: http://bugs.launchpad.net/bugs/1815033
> 
> In regular NIC transmission flow, driver always configures MAC using
> Tx queue zero descriptor as a part of MAC learning flow.
> But with multi Tx queue supported NIC, regular transmission can occur on
> any non-zero Tx queue and from that context it uses
> Tx queue zero descriptor to configure MAC, at the same time TX queue
> zero could be used by another CPU for regular transmission
> which could lead to Tx queue zero descriptor corruption and cause FW
> abort.
> 
> This patch fixes this in such a way that driver always configures
> learned MAC address from the same Tx queue which is used for
> regular transmission.
> 
> Fixes: 7e2cf4feba05 ("qlcnic: change driver hardware interface mechanism")
> Signed-off-by: Shahed Shaikh <shahed.shaikh@cavium.com>
> Signed-off-by: David S. Miller <davem@davemloft.net>
> (cherry picked from commit c333fa0c4f220f8f7ea5acd6b0ebf3bf13fd684d)
> Signed-off-by: Guilherme G. Piccoli <gpiccoli@canonical.com>
Acked-by: Stefan Bader <stefan.bader@canonical.com>
> ---

In Xenial and Cosmic already.

>  drivers/net/ethernet/qlogic/qlcnic/qlcnic.h         |  8 +++++---
>  drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c |  3 ++-
>  drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h |  3 ++-
>  drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h      |  3 ++-
>  drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c      | 12 ++++++------
>  5 files changed, 17 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
> index 81312924df14..0c443ea98479 100644
> --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
> +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
> @@ -1800,7 +1800,8 @@ struct qlcnic_hardware_ops {
>  	int (*config_loopback) (struct qlcnic_adapter *, u8);
>  	int (*clear_loopback) (struct qlcnic_adapter *, u8);
>  	int (*config_promisc_mode) (struct qlcnic_adapter *, u32);
> -	void (*change_l2_filter) (struct qlcnic_adapter *, u64 *, u16);
> +	void (*change_l2_filter)(struct qlcnic_adapter *adapter, u64 *addr,
> +				 u16 vlan, struct qlcnic_host_tx_ring *tx_ring);
>  	int (*get_board_info) (struct qlcnic_adapter *);
>  	void (*set_mac_filter_count) (struct qlcnic_adapter *);
>  	void (*free_mac_list) (struct qlcnic_adapter *);
> @@ -2064,9 +2065,10 @@ static inline int qlcnic_nic_set_promisc(struct qlcnic_adapter *adapter,
>  }
>  
>  static inline void qlcnic_change_filter(struct qlcnic_adapter *adapter,
> -					u64 *addr, u16 id)
> +					u64 *addr, u16 vlan,
> +					struct qlcnic_host_tx_ring *tx_ring)
>  {
> -	adapter->ahw->hw_ops->change_l2_filter(adapter, addr, id);
> +	adapter->ahw->hw_ops->change_l2_filter(adapter, addr, vlan, tx_ring);
>  }
>  
>  static inline int qlcnic_get_board_info(struct qlcnic_adapter *adapter)
> diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
> index 46b0372dd032..1fc84d8f891b 100644
> --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
> +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
> @@ -2134,7 +2134,8 @@ int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
>  }
>  
>  void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *adapter, u64 *addr,
> -				  u16 vlan_id)
> +				  u16 vlan_id,
> +				  struct qlcnic_host_tx_ring *tx_ring)
>  {
>  	u8 mac[ETH_ALEN];
>  	memcpy(&mac, addr, ETH_ALEN);
> diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
> index b75a81246856..73fe2f64491d 100644
> --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
> +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
> @@ -550,7 +550,8 @@ int qlcnic_83xx_wrt_reg_indirect(struct qlcnic_adapter *, ulong, u32);
>  int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *, u32);
>  int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *, int);
>  int qlcnic_83xx_config_rss(struct qlcnic_adapter *, int);
> -void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *, u64 *, u16);
> +void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *adapter, u64 *addr,
> +				  u16 vlan, struct qlcnic_host_tx_ring *ring);
>  int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *, struct qlcnic_pci_info *);
>  int qlcnic_83xx_set_nic_info(struct qlcnic_adapter *, struct qlcnic_info *);
>  void qlcnic_83xx_initialize_nic(struct qlcnic_adapter *, int);
> diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
> index 4bb33af8e2b3..56a3bd9e37dc 100644
> --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
> +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
> @@ -173,7 +173,8 @@ int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
>  			 struct net_device *netdev);
>  void qlcnic_82xx_get_beacon_state(struct qlcnic_adapter *);
>  void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter,
> -			       u64 *uaddr, u16 vlan_id);
> +			       u64 *uaddr, u16 vlan_id,
> +			       struct qlcnic_host_tx_ring *tx_ring);
>  int qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *,
>  				     struct ethtool_coalesce *);
>  int qlcnic_82xx_set_rx_coalesce(struct qlcnic_adapter *);
> diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
> index 84dd83031a1b..9647578cbe6a 100644
> --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
> +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
> @@ -268,13 +268,12 @@ static void qlcnic_add_lb_filter(struct qlcnic_adapter *adapter,
>  }
>  
>  void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr,
> -			       u16 vlan_id)
> +			       u16 vlan_id, struct qlcnic_host_tx_ring *tx_ring)
>  {
>  	struct cmd_desc_type0 *hwdesc;
>  	struct qlcnic_nic_req *req;
>  	struct qlcnic_mac_req *mac_req;
>  	struct qlcnic_vlan_req *vlan_req;
> -	struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring;
>  	u32 producer;
>  	u64 word;
>  
> @@ -301,7 +300,8 @@ void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr,
>  
>  static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
>  			       struct cmd_desc_type0 *first_desc,
> -			       struct sk_buff *skb)
> +			       struct sk_buff *skb,
> +			       struct qlcnic_host_tx_ring *tx_ring)
>  {
>  	struct vlan_ethhdr *vh = (struct vlan_ethhdr *)(skb->data);
>  	struct ethhdr *phdr = (struct ethhdr *)(skb->data);
> @@ -335,7 +335,7 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
>  		    tmp_fil->vlan_id == vlan_id) {
>  			if (jiffies > (QLCNIC_READD_AGE * HZ + tmp_fil->ftime))
>  				qlcnic_change_filter(adapter, &src_addr,
> -						     vlan_id);
> +						     vlan_id, tx_ring);
>  			tmp_fil->ftime = jiffies;
>  			return;
>  		}
> @@ -350,7 +350,7 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
>  	if (!fil)
>  		return;
>  
> -	qlcnic_change_filter(adapter, &src_addr, vlan_id);
> +	qlcnic_change_filter(adapter, &src_addr, vlan_id, tx_ring);
>  	fil->ftime = jiffies;
>  	fil->vlan_id = vlan_id;
>  	memcpy(fil->faddr, &src_addr, ETH_ALEN);
> @@ -766,7 +766,7 @@ netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
>  	}
>  
>  	if (adapter->drv_mac_learn)
> -		qlcnic_send_filter(adapter, first_desc, skb);
> +		qlcnic_send_filter(adapter, first_desc, skb, tx_ring);
>  
>  	tx_ring->tx_stats.tx_bytes += skb->len;
>  	tx_ring->tx_stats.xmit_called++;
>

Patch

diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
index 81312924df14..0c443ea98479 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h
@@ -1800,7 +1800,8 @@  struct qlcnic_hardware_ops {
 	int (*config_loopback) (struct qlcnic_adapter *, u8);
 	int (*clear_loopback) (struct qlcnic_adapter *, u8);
 	int (*config_promisc_mode) (struct qlcnic_adapter *, u32);
-	void (*change_l2_filter) (struct qlcnic_adapter *, u64 *, u16);
+	void (*change_l2_filter)(struct qlcnic_adapter *adapter, u64 *addr,
+				 u16 vlan, struct qlcnic_host_tx_ring *tx_ring);
 	int (*get_board_info) (struct qlcnic_adapter *);
 	void (*set_mac_filter_count) (struct qlcnic_adapter *);
 	void (*free_mac_list) (struct qlcnic_adapter *);
@@ -2064,9 +2065,10 @@  static inline int qlcnic_nic_set_promisc(struct qlcnic_adapter *adapter,
 }
 
 static inline void qlcnic_change_filter(struct qlcnic_adapter *adapter,
-					u64 *addr, u16 id)
+					u64 *addr, u16 vlan,
+					struct qlcnic_host_tx_ring *tx_ring)
 {
-	adapter->ahw->hw_ops->change_l2_filter(adapter, addr, id);
+	adapter->ahw->hw_ops->change_l2_filter(adapter, addr, vlan, tx_ring);
 }
 
 static inline int qlcnic_get_board_info(struct qlcnic_adapter *adapter)
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
index 46b0372dd032..1fc84d8f891b 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c
@@ -2134,7 +2134,8 @@  int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,
 }
 
 void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *adapter, u64 *addr,
-				  u16 vlan_id)
+				  u16 vlan_id,
+				  struct qlcnic_host_tx_ring *tx_ring)
 {
 	u8 mac[ETH_ALEN];
 	memcpy(&mac, addr, ETH_ALEN);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
index b75a81246856..73fe2f64491d 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h
@@ -550,7 +550,8 @@  int qlcnic_83xx_wrt_reg_indirect(struct qlcnic_adapter *, ulong, u32);
 int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *, u32);
 int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *, int);
 int qlcnic_83xx_config_rss(struct qlcnic_adapter *, int);
-void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *, u64 *, u16);
+void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *adapter, u64 *addr,
+				  u16 vlan, struct qlcnic_host_tx_ring *ring);
 int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *, struct qlcnic_pci_info *);
 int qlcnic_83xx_set_nic_info(struct qlcnic_adapter *, struct qlcnic_info *);
 void qlcnic_83xx_initialize_nic(struct qlcnic_adapter *, int);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
index 4bb33af8e2b3..56a3bd9e37dc 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h
@@ -173,7 +173,8 @@  int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,
 			 struct net_device *netdev);
 void qlcnic_82xx_get_beacon_state(struct qlcnic_adapter *);
 void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter,
-			       u64 *uaddr, u16 vlan_id);
+			       u64 *uaddr, u16 vlan_id,
+			       struct qlcnic_host_tx_ring *tx_ring);
 int qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *,
 				     struct ethtool_coalesce *);
 int qlcnic_82xx_set_rx_coalesce(struct qlcnic_adapter *);
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
index 84dd83031a1b..9647578cbe6a 100644
--- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
+++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c
@@ -268,13 +268,12 @@  static void qlcnic_add_lb_filter(struct qlcnic_adapter *adapter,
 }
 
 void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr,
-			       u16 vlan_id)
+			       u16 vlan_id, struct qlcnic_host_tx_ring *tx_ring)
 {
 	struct cmd_desc_type0 *hwdesc;
 	struct qlcnic_nic_req *req;
 	struct qlcnic_mac_req *mac_req;
 	struct qlcnic_vlan_req *vlan_req;
-	struct qlcnic_host_tx_ring *tx_ring = adapter->tx_ring;
 	u32 producer;
 	u64 word;
 
@@ -301,7 +300,8 @@  void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr,
 
 static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
 			       struct cmd_desc_type0 *first_desc,
-			       struct sk_buff *skb)
+			       struct sk_buff *skb,
+			       struct qlcnic_host_tx_ring *tx_ring)
 {
 	struct vlan_ethhdr *vh = (struct vlan_ethhdr *)(skb->data);
 	struct ethhdr *phdr = (struct ethhdr *)(skb->data);
@@ -335,7 +335,7 @@  static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
 		    tmp_fil->vlan_id == vlan_id) {
 			if (jiffies > (QLCNIC_READD_AGE * HZ + tmp_fil->ftime))
 				qlcnic_change_filter(adapter, &src_addr,
-						     vlan_id);
+						     vlan_id, tx_ring);
 			tmp_fil->ftime = jiffies;
 			return;
 		}
@@ -350,7 +350,7 @@  static void qlcnic_send_filter(struct qlcnic_adapter *adapter,
 	if (!fil)
 		return;
 
-	qlcnic_change_filter(adapter, &src_addr, vlan_id);
+	qlcnic_change_filter(adapter, &src_addr, vlan_id, tx_ring);
 	fil->ftime = jiffies;
 	fil->vlan_id = vlan_id;
 	memcpy(fil->faddr, &src_addr, ETH_ALEN);
@@ -766,7 +766,7 @@  netdev_tx_t qlcnic_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
 	}
 
 	if (adapter->drv_mac_learn)
-		qlcnic_send_filter(adapter, first_desc, skb);
+		qlcnic_send_filter(adapter, first_desc, skb, tx_ring);
 
 	tx_ring->tx_stats.tx_bytes += skb->len;
 	tx_ring->tx_stats.xmit_called++;