net:ethernet:aquantia: Atlantic driver bugfixes und improvements
Subsystems (3)
v2 cover net:ethernet:aquantia: Atlantic driver bugfixes und improvements
This series contains bugfixes for aQuantia Atlantic driver.
Changes in v2:
Review comments applied:
- min_mtu set removed
- extra mtu range check is removed
- err codes handling improved
Igor Russkikh (3):
net:ethernet:aquantia: Setup max_mtu in ndev to enable jumbo frames net:ethernet:aquantia: Fix Tx queue hangups net:ethernet:aquantia: Fix transient invalid link down/up indications
Pavel Belous (1):
net:ethernet:atlantic: fix iommu errors
drivers/net/ethernet/aquantia/atlantic/aq_cfg.h | 4 + drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 145 ++++++++++----------- drivers/net/ethernet/aquantia/atlantic/aq_nic.h | 2 - drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 53 ++++++-- drivers/net/ethernet/aquantia/atlantic/aq_ring.h | 10 +- drivers/net/ethernet/aquantia/atlantic/aq_vec.c | 8 +- .../aquantia/atlantic/hw_atl/hw_atl_b0_internal.h | 2 +- .../aquantia/atlantic/hw_atl/hw_atl_utils.c | 3 +- 8 files changed, 129 insertions(+), 98 deletions(-)
Signature · 1 line
2.7.4
PATCH v2 1/4 net:ethernet:aquantia: Setup max_mtu in ndev to enable jumbo frames
Although hardware is capable for almost 16K MTU, without max_mtu field correctly set it only allows standard MTU to be used.
This patch enables max MTU, calculating it from hardware maximum frame size of 16352 octets (including FCS).
--- drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 11 ++--------- .../ethernet/aquantia/atlantic/hw_atl/hw_atl_b0_internal.h | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-)
drivers/net/ethernet/aquantia/atlantic/aq_nic.c +2 -9
@@ -214,7 +214,6 @@ struct aq_nic_s *aq_nic_alloc_cold(const struct net_device_ops *ndev_ops,SET_NETDEV_DEV (ndev, dev); ndev->if_port = port; - ndev->min_mtu =ETH_MIN_MTU ; self->ndev = ndev; self->aq_pci_func = aq_pci_func;
@@ -283,6 +282,7 @@ int aq_nic_ndev_init(struct aq_nic_s *self) self->ndev->features = aq_hw_caps->hw_features; self->ndev->priv_flags = aq_hw_caps->hw_priv_flags; self->ndev->mtu = aq_nic_cfg->mtu -ETH_HLEN ; + self->ndev->max_mtu = self->aq_hw_caps.mtu -ETH_FCS_LEN -ETH_HLEN ;return 0 ; }
@@ -693,16 +693,9 @@ int aq_nic_set_multicast_list(struct aq_nic_s *self, struct net_device *ndev)int aq_nic_set_mtu (struct aq_nic_s *self,int new_mtu) { -int err =0 ; - -if (new_mtu > self->aq_hw_caps.mtu) { - err = -EINVAL ; - goto err_exit; - } self->aq_nic_cfg.mtu = new_mtu; -err_exit: -return err; +return 0 ; }int aq_nic_set_mac (struct aq_nic_s *self,struct net_device *ndev)
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_b0_internal.h +1 -1
@@ -16,7 +16,7 @@#include "../aq_common.h" -#define HW_ATL_B0_MTU_JUMBO (16000U) +#define HW_ATL_B0_MTU_JUMBO 16352U#define HW_ATL_B0_MTU 1514U#define HW_ATL_B0_TX_RINGS 4U
Signature · 1 line
2.7.4
PATCH v2 2/4 net:ethernet:aquantia: Fix Tx queue hangups
In reply to Igor RusskikhDriver did a poor job in managing its Tx queues: Sometimes it could stop tx queues due to link down condition in aq_nic_xmit - but never waked up them. That led to Tx path total suspend.
This patch fixes this and improves generic queue management:
- introduces queue restart counter
- uses generic netif_ interface to disable and enable tx path
- refactors link up/down condition and introduces dmesg log event when link changes.
- introduces new constant for minimum descriptors count required for queue wakeup
--- drivers/net/ethernet/aquantia/atlantic/aq_cfg.h | 4 ++ drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 91 +++++++++++------------- drivers/net/ethernet/aquantia/atlantic/aq_nic.h | 2 - drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 26 +++++++ drivers/net/ethernet/aquantia/atlantic/aq_ring.h | 4 ++ drivers/net/ethernet/aquantia/atlantic/aq_vec.c | 8 +-- 6 files changed, 76 insertions(+), 59 deletions(-)
drivers/net/ethernet/aquantia/atlantic/aq_cfg.h +4 -0
@@ -51,6 +51,10 @@#define AQ_CFG_SKB_FRAGS_MAX 32U +/* Number of descriptors available in one ring to resume this ring queue +*/ +#define AQ_CFG_RESTART_DESC_THRES (AQ_CFG_SKB_FRAGS_MAX * 2) +#define AQ_CFG_NAPI_WEIGHT 64U#define AQ_CFG_MULTICAST_ADDRESS_MAX 32U
drivers/net/ethernet/aquantia/atlantic/aq_nic.c +40 -51
169 lines not on this page · raw message
drivers/net/ethernet/aquantia/atlantic/aq_nic.h +0 -2
@@ -83,8 +83,6 @@ struct net_device *aq_nic_get_ndev(struct aq_nic_s *self);int aq_nic_init (struct aq_nic_s *self);int aq_nic_cfg_start (struct aq_nic_s *self);int aq_nic_ndev_register (struct aq_nic_s *self); -void aq_nic_ndev_queue_start (struct aq_nic_s *self,unsigned int idx); -void aq_nic_ndev_queue_stop (struct aq_nic_s *self,unsigned int idx);void aq_nic_ndev_free (struct aq_nic_s *self);int aq_nic_start (struct aq_nic_s *self);int aq_nic_xmit (struct aq_nic_s *self,struct sk_buff *skb);
drivers/net/ethernet/aquantia/atlantic/aq_ring.c +26 -0
@@ -104,6 +104,32 @@ int aq_ring_init(struct aq_ring_s *self)return 0 ; } +void aq_ring_update_queue_state (struct aq_ring_s *ring) +{ +if (aq_ring_avail_dx (ring) <=AQ_CFG_SKB_FRAGS_MAX ) +aq_ring_queue_stop (ring); +else if (aq_ring_avail_dx (ring) >AQ_CFG_RESTART_DESC_THRES ) +aq_ring_queue_wake (ring); +} + +void aq_ring_queue_wake (struct aq_ring_s *ring) +{ +struct net_device *ndev =aq_nic_get_ndev (ring->aq_nic); + +if (__netif_subqueue_stopped (ndev, ring->idx)) { +netif_wake_subqueue (ndev, ring->idx); + ring->stats.tx.queue_restarts++; + } +} + +void aq_ring_queue_stop (struct aq_ring_s *ring) +{ +struct net_device *ndev =aq_nic_get_ndev (ring->aq_nic); + +if (!__netif_subqueue_stopped (ndev, ring->idx)) +netif_stop_subqueue (ndev, ring->idx); +} +void aq_ring_tx_clean (struct aq_ring_s *self) {struct device *dev =aq_nic_get_dev (self->aq_nic);
drivers/net/ethernet/aquantia/atlantic/aq_ring.h +4 -0
@@ -94,6 +94,7 @@ struct aq_ring_stats_tx_s {u64 errors;u64 packets;u64 bytes; +u64 queue_restarts; };union aq_ring_stats_s {
@@ -147,6 +148,9 @@ struct aq_ring_s *aq_ring_rx_alloc(struct aq_ring_s *self,int aq_ring_init (struct aq_ring_s *self);void aq_ring_rx_deinit (struct aq_ring_s *self);void aq_ring_free (struct aq_ring_s *self); +void aq_ring_update_queue_state (struct aq_ring_s *ring); +void aq_ring_queue_wake (struct aq_ring_s *ring); +void aq_ring_queue_stop (struct aq_ring_s *ring);void aq_ring_tx_clean (struct aq_ring_s *self); int aq_ring_rx_clean(struct aq_ring_s *self, struct napi_struct *napi,
drivers/net/ethernet/aquantia/atlantic/aq_vec.c +2 -6
@@ -59,12 +59,7 @@ static int aq_vec_poll(struct napi_struct *napi, int budget)if (ring[AQ_VEC_TX_ID ].sw_head != ring[AQ_VEC_TX_ID ].hw_head) {aq_ring_tx_clean (&ring[AQ_VEC_TX_ID ]); - -if (aq_ring_avail_dx (&ring[AQ_VEC_TX_ID ]) > -AQ_CFG_SKB_FRAGS_MAX ) { -aq_nic_ndev_queue_start (self->aq_nic, - ring[AQ_VEC_TX_ID ].idx); - } +aq_ring_update_queue_state (&ring[AQ_VEC_TX_ID ]); was_tx_cleaned = true; }
@@ -364,6 +359,7 @@ void aq_vec_add_stats(struct aq_vec_s *self, stats_tx->packets += tx->packets; stats_tx->bytes += tx->bytes; stats_tx->errors += tx->errors; + stats_tx->queue_restarts += tx->queue_restarts; } }
Signature · 1 line
2.7.4
PATCH v2 3/4 net:ethernet:aquantia: Fix transient invalid link down/up indications
In reply to Igor RusskikhDue to a bug in aquantia atlantic card firmware, it sometimes reports invalid link speed bits. That caused driver to report link down events, although link itself is totally fine.
This patch ignores such out of blue readings.
--- drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
drivers/net/ethernet/aquantia/atlantic/hw_atl/hw_atl_utils.c +1 -2
@@ -351,8 +351,7 @@ int hw_atl_utils_mpi_get_link_status(struct aq_hw_s *self)break ;default : - link_status->mbps =0U ; -break ; +return -EBUSY ; } }
Signature · 1 line
2.7.4
PATCH v2 4/4 net:ethernet:atlantic: fix iommu errors
In reply to Igor RusskikhFrom: Pavel Belous <[email protected]>
Call skb_frag_dma_map multiple times if tx length is greater than device max and avoid processing tx ring until entire packet has been sent.
--- drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 43 ++++++++++++++---------- drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 27 ++++++++++----- drivers/net/ethernet/aquantia/atlantic/aq_ring.h | 6 ++-- 3 files changed, 49 insertions(+), 27 deletions(-)
drivers/net/ethernet/aquantia/atlantic/aq_nic.c +26 -17
@@ -473,6 +473,7 @@ static unsigned int aq_nic_map_skb(struct aq_nic_s *self,unsigned int nr_frags =skb_shinfo (skb)->nr_frags;unsigned int frag_count =0U ;unsigned int dx = ring->sw_tail; +struct aq_ring_buff_s *first =NULL ;struct aq_ring_buff_s *dx_buff = &ring->buff_ring[dx];if (unlikely (skb_is_gso (skb))) {
@@ -483,6 +484,7 @@ static unsigned int aq_nic_map_skb(struct aq_nic_s *self, dx_buff->len_l4 =tcp_hdrlen (skb); dx_buff->mss =skb_shinfo (skb)->gso_size; dx_buff->is_txc =1U ; + dx_buff->eop_index =0xffffU ; dx_buff->is_ipv6 = (ip_hdr (skb)->version ==6 ) ?1U :0U ;
@@ -502,6 +504,7 @@ static unsigned int aq_nic_map_skb(struct aq_nic_s *self,if (unlikely (dma_mapping_error (aq_nic_get_dev (self), dx_buff->pa))) goto exit; + first = dx_buff; dx_buff->len_pkt = skb->len; dx_buff->is_sop =1U ; dx_buff->is_mapped =1U ;
@@ -530,40 +533,46 @@ static unsigned int aq_nic_map_skb(struct aq_nic_s *self,for (; nr_frags--; ++frag_count) {unsigned int frag_len =0U ; +unsigned int buff_offset =0U ; +unsigned int buff_size =0U ;dma_addr_t frag_pa;skb_frag_t *frag = &skb_shinfo (skb)->frags[frag_count]; frag_len =skb_frag_size (frag); - frag_pa =skb_frag_dma_map (aq_nic_get_dev (self), frag,0 , - frag_len,DMA_TO_DEVICE ); -if (unlikely (dma_mapping_error (aq_nic_get_dev (self), frag_pa))) - goto mapping_error; +while (frag_len) { +if (frag_len >AQ_CFG_TX_FRAME_MAX ) + buff_size =AQ_CFG_TX_FRAME_MAX ; +else + buff_size = frag_len; + + frag_pa =skb_frag_dma_map (aq_nic_get_dev (self), + frag, + buff_offset, + buff_size, +DMA_TO_DEVICE ); + +if (unlikely (dma_mapping_error (aq_nic_get_dev (self), + frag_pa))) + goto mapping_error; -while (frag_len >AQ_CFG_TX_FRAME_MAX ) { dx =aq_ring_next_dx (ring, dx); dx_buff = &ring->buff_ring[dx]; dx_buff->flags =0U ; - dx_buff->len =AQ_CFG_TX_FRAME_MAX ; + dx_buff->len = buff_size; dx_buff->pa = frag_pa; dx_buff->is_mapped =1U ; + dx_buff->eop_index =0xffffU ; + + frag_len -= buff_size; + buff_offset += buff_size; - frag_len -=AQ_CFG_TX_FRAME_MAX ; - frag_pa +=AQ_CFG_TX_FRAME_MAX ; ++ret; } - - dx =aq_ring_next_dx (ring, dx); - dx_buff = &ring->buff_ring[dx]; - - dx_buff->flags =0U ; - dx_buff->len = frag_len; - dx_buff->pa = frag_pa; - dx_buff->is_mapped =1U ; - ++ret; } + first->eop_index = dx; dx_buff->is_eop =1U ; dx_buff->skb = skb; goto exit;
drivers/net/ethernet/aquantia/atlantic/aq_ring.c +19 -8
@@ -104,6 +104,12 @@ int aq_ring_init(struct aq_ring_s *self)return 0 ; } +static inline bool aq_ring_dx_in_range (unsigned int h,unsigned int i, +unsigned int t) +{ +return (h < t) ? ((h < i) && (i < t)) : ((h < i) || (i < t)); +} +void aq_ring_update_queue_state (struct aq_ring_s *ring) {if (aq_ring_avail_dx (ring) <=AQ_CFG_SKB_FRAGS_MAX )
@@ -139,23 +145,28 @@ void aq_ring_tx_clean(struct aq_ring_s *self)struct aq_ring_buff_s *buff = &self->buff_ring[self->sw_head];if (likely (buff->is_mapped)) { -if (unlikely (buff->is_sop)) +if (unlikely (buff->is_sop)) { +if (!buff->is_eop && + buff->eop_index !=0xffffU && + (!aq_ring_dx_in_range (self->sw_head, + buff->eop_index, + self->hw_head))) +break ; +dma_unmap_single (dev, buff->pa, buff->len,DMA_TO_DEVICE ); -else + }else {dma_unmap_page (dev, buff->pa, buff->len,DMA_TO_DEVICE ); + } }if (unlikely (buff->is_eop))dev_kfree_skb_any (buff->skb); - } -} -static inline unsigned int aq_ring_dx_in_range (unsigned int h,unsigned int i, -unsigned int t) -{ -return (h < t) ? ((h < i) && (i < t)) : ((h < i) || (i < t)); + buff->pa =0U ; + buff->eop_index =0xffffU ; + } }#define AQ_SKB_ALIGN SKB_DATA_ALIGN(sizeof(struct skb_shared_info))
drivers/net/ethernet/aquantia/atlantic/aq_ring.h +4 -2
@@ -65,7 +65,7 @@ struct __packed aq_ring_buff_s { };union {struct { -u32 len:16 ; +u16 len;u32 is_ip_cso:1 ;u32 is_udp_cso:1 ;u32 is_tcp_cso:1 ;
@@ -77,8 +77,10 @@ struct __packed aq_ring_buff_s { u32 is_cleaned:1; u32 is_error:1; u32 rsvd3:6; +u16 eop_index; +u16 rsvd4; }; -u32 flags; +u64 flags; }; };
Signature · 1 line
2.7.4
David Miller
maintainerFrom: Igor Russkikh <[email protected]>
Date: Mon, 25 Sep 2017 10:48:46 +0300
Quoted 7 lines
This series contains bugfixes for aQuantia Atlantic driver.
Changes in v2:
Review comments applied:
-min_mtuset removed
- extra mtu range check is removed
- err codes handling improved
I'll apply this series but simply "aquantia: " is a sufficient subsystem prefix all by itself so I have edited your Subject lines to use this before applying.
Even if multiple subsystem tags were appropriate here, they should be separated by spaces like "net: ethernet: ...".
Thanks.