diff options
author | Igor Russkikh <igor.russkikh@aquantia.com> | 2017-09-25 03:48:48 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-09-26 16:44:31 -0400 |
commit | 3aec6412e007b294d4c135f5c7ed5e5ecf37dd2e (patch) | |
tree | 343643f24c6b268e14a8025c2da5701d04caf680 | |
parent | d85fc17beeb06f9979d63fe4d9fbffbb1a00bba4 (diff) |
aquantia: Fix Tx queue hangups
Driver 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
Signed-off-by: Pavel Belous <Pavel.Belous@aquantia.com>
Signed-off-by: Igor Russkikh <igor.russkikh@aquantia.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/aq_cfg.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 91 | ||||
-rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/aq_nic.h | 2 | ||||
-rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/aq_ring.c | 26 | ||||
-rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/aq_ring.h | 4 | ||||
-rw-r--r-- | drivers/net/ethernet/aquantia/atlantic/aq_vec.c | 8 |
6 files changed, 76 insertions, 59 deletions
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h b/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h index 214986436ece..0fdaaa643073 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_cfg.h | |||
@@ -51,6 +51,10 @@ | |||
51 | 51 | ||
52 | #define AQ_CFG_SKB_FRAGS_MAX 32U | 52 | #define AQ_CFG_SKB_FRAGS_MAX 32U |
53 | 53 | ||
54 | /* Number of descriptors available in one ring to resume this ring queue | ||
55 | */ | ||
56 | #define AQ_CFG_RESTART_DESC_THRES (AQ_CFG_SKB_FRAGS_MAX * 2) | ||
57 | |||
54 | #define AQ_CFG_NAPI_WEIGHT 64U | 58 | #define AQ_CFG_NAPI_WEIGHT 64U |
55 | 59 | ||
56 | #define AQ_CFG_MULTICAST_ADDRESS_MAX 32U | 60 | #define AQ_CFG_MULTICAST_ADDRESS_MAX 32U |
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index bf26a59a9d8e..072a55029f04 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c | |||
@@ -119,6 +119,35 @@ int aq_nic_cfg_start(struct aq_nic_s *self) | |||
119 | return 0; | 119 | return 0; |
120 | } | 120 | } |
121 | 121 | ||
122 | static int aq_nic_update_link_status(struct aq_nic_s *self) | ||
123 | { | ||
124 | int err = self->aq_hw_ops.hw_get_link_status(self->aq_hw); | ||
125 | |||
126 | if (err) | ||
127 | return err; | ||
128 | |||
129 | if (self->link_status.mbps != self->aq_hw->aq_link_status.mbps) | ||
130 | pr_info("%s: link change old %d new %d\n", | ||
131 | AQ_CFG_DRV_NAME, self->link_status.mbps, | ||
132 | self->aq_hw->aq_link_status.mbps); | ||
133 | |||
134 | self->link_status = self->aq_hw->aq_link_status; | ||
135 | if (!netif_carrier_ok(self->ndev) && self->link_status.mbps) { | ||
136 | aq_utils_obj_set(&self->header.flags, | ||
137 | AQ_NIC_FLAG_STARTED); | ||
138 | aq_utils_obj_clear(&self->header.flags, | ||
139 | AQ_NIC_LINK_DOWN); | ||
140 | netif_carrier_on(self->ndev); | ||
141 | netif_tx_wake_all_queues(self->ndev); | ||
142 | } | ||
143 | if (netif_carrier_ok(self->ndev) && !self->link_status.mbps) { | ||
144 | netif_carrier_off(self->ndev); | ||
145 | netif_tx_disable(self->ndev); | ||
146 | aq_utils_obj_set(&self->header.flags, AQ_NIC_LINK_DOWN); | ||
147 | } | ||
148 | return 0; | ||
149 | } | ||
150 | |||
122 | static void aq_nic_service_timer_cb(unsigned long param) | 151 | static void aq_nic_service_timer_cb(unsigned long param) |
123 | { | 152 | { |
124 | struct aq_nic_s *self = (struct aq_nic_s *)param; | 153 | struct aq_nic_s *self = (struct aq_nic_s *)param; |
@@ -131,26 +160,13 @@ static void aq_nic_service_timer_cb(unsigned long param) | |||
131 | if (aq_utils_obj_test(&self->header.flags, AQ_NIC_FLAGS_IS_NOT_READY)) | 160 | if (aq_utils_obj_test(&self->header.flags, AQ_NIC_FLAGS_IS_NOT_READY)) |
132 | goto err_exit; | 161 | goto err_exit; |
133 | 162 | ||
134 | err = self->aq_hw_ops.hw_get_link_status(self->aq_hw); | 163 | err = aq_nic_update_link_status(self); |
135 | if (err < 0) | 164 | if (err) |
136 | goto err_exit; | 165 | goto err_exit; |
137 | 166 | ||
138 | self->link_status = self->aq_hw->aq_link_status; | ||
139 | |||
140 | self->aq_hw_ops.hw_interrupt_moderation_set(self->aq_hw, | 167 | self->aq_hw_ops.hw_interrupt_moderation_set(self->aq_hw, |
141 | self->aq_nic_cfg.is_interrupt_moderation); | 168 | self->aq_nic_cfg.is_interrupt_moderation); |
142 | 169 | ||
143 | if (self->link_status.mbps) { | ||
144 | aq_utils_obj_set(&self->header.flags, | ||
145 | AQ_NIC_FLAG_STARTED); | ||
146 | aq_utils_obj_clear(&self->header.flags, | ||
147 | AQ_NIC_LINK_DOWN); | ||
148 | netif_carrier_on(self->ndev); | ||
149 | } else { | ||
150 | netif_carrier_off(self->ndev); | ||
151 | aq_utils_obj_set(&self->header.flags, AQ_NIC_LINK_DOWN); | ||
152 | } | ||
153 | |||
154 | memset(&stats_rx, 0U, sizeof(struct aq_ring_stats_rx_s)); | 170 | memset(&stats_rx, 0U, sizeof(struct aq_ring_stats_rx_s)); |
155 | memset(&stats_tx, 0U, sizeof(struct aq_ring_stats_tx_s)); | 171 | memset(&stats_tx, 0U, sizeof(struct aq_ring_stats_tx_s)); |
156 | for (i = AQ_DIMOF(self->aq_vec); i--;) { | 172 | for (i = AQ_DIMOF(self->aq_vec); i--;) { |
@@ -240,7 +256,6 @@ err_exit: | |||
240 | int aq_nic_ndev_register(struct aq_nic_s *self) | 256 | int aq_nic_ndev_register(struct aq_nic_s *self) |
241 | { | 257 | { |
242 | int err = 0; | 258 | int err = 0; |
243 | unsigned int i = 0U; | ||
244 | 259 | ||
245 | if (!self->ndev) { | 260 | if (!self->ndev) { |
246 | err = -EINVAL; | 261 | err = -EINVAL; |
@@ -262,8 +277,7 @@ int aq_nic_ndev_register(struct aq_nic_s *self) | |||
262 | 277 | ||
263 | netif_carrier_off(self->ndev); | 278 | netif_carrier_off(self->ndev); |
264 | 279 | ||
265 | for (i = AQ_CFG_VECS_MAX; i--;) | 280 | netif_tx_disable(self->ndev); |
266 | aq_nic_ndev_queue_stop(self, i); | ||
267 | 281 | ||
268 | err = register_netdev(self->ndev); | 282 | err = register_netdev(self->ndev); |
269 | if (err < 0) | 283 | if (err < 0) |
@@ -318,12 +332,8 @@ struct aq_nic_s *aq_nic_alloc_hot(struct net_device *ndev) | |||
318 | err = -EINVAL; | 332 | err = -EINVAL; |
319 | goto err_exit; | 333 | goto err_exit; |
320 | } | 334 | } |
321 | if (netif_running(ndev)) { | 335 | if (netif_running(ndev)) |
322 | unsigned int i; | 336 | netif_tx_disable(ndev); |
323 | |||
324 | for (i = AQ_CFG_VECS_MAX; i--;) | ||
325 | netif_stop_subqueue(ndev, i); | ||
326 | } | ||
327 | 337 | ||
328 | for (self->aq_vecs = 0; self->aq_vecs < self->aq_nic_cfg.vecs; | 338 | for (self->aq_vecs = 0; self->aq_vecs < self->aq_nic_cfg.vecs; |
329 | self->aq_vecs++) { | 339 | self->aq_vecs++) { |
@@ -383,16 +393,6 @@ err_exit: | |||
383 | return err; | 393 | return err; |
384 | } | 394 | } |
385 | 395 | ||
386 | void aq_nic_ndev_queue_start(struct aq_nic_s *self, unsigned int idx) | ||
387 | { | ||
388 | netif_start_subqueue(self->ndev, idx); | ||
389 | } | ||
390 | |||
391 | void aq_nic_ndev_queue_stop(struct aq_nic_s *self, unsigned int idx) | ||
392 | { | ||
393 | netif_stop_subqueue(self->ndev, idx); | ||
394 | } | ||
395 | |||
396 | int aq_nic_start(struct aq_nic_s *self) | 396 | int aq_nic_start(struct aq_nic_s *self) |
397 | { | 397 | { |
398 | struct aq_vec_s *aq_vec = NULL; | 398 | struct aq_vec_s *aq_vec = NULL; |
@@ -451,10 +451,6 @@ int aq_nic_start(struct aq_nic_s *self) | |||
451 | goto err_exit; | 451 | goto err_exit; |
452 | } | 452 | } |
453 | 453 | ||
454 | for (i = 0U, aq_vec = self->aq_vec[0]; | ||
455 | self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) | ||
456 | aq_nic_ndev_queue_start(self, i); | ||
457 | |||
458 | err = netif_set_real_num_tx_queues(self->ndev, self->aq_vecs); | 454 | err = netif_set_real_num_tx_queues(self->ndev, self->aq_vecs); |
459 | if (err < 0) | 455 | if (err < 0) |
460 | goto err_exit; | 456 | goto err_exit; |
@@ -463,6 +459,8 @@ int aq_nic_start(struct aq_nic_s *self) | |||
463 | if (err < 0) | 459 | if (err < 0) |
464 | goto err_exit; | 460 | goto err_exit; |
465 | 461 | ||
462 | netif_tx_start_all_queues(self->ndev); | ||
463 | |||
466 | err_exit: | 464 | err_exit: |
467 | return err; | 465 | return err; |
468 | } | 466 | } |
@@ -602,7 +600,6 @@ int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb) | |||
602 | unsigned int vec = skb->queue_mapping % self->aq_nic_cfg.vecs; | 600 | unsigned int vec = skb->queue_mapping % self->aq_nic_cfg.vecs; |
603 | unsigned int tc = 0U; | 601 | unsigned int tc = 0U; |
604 | int err = NETDEV_TX_OK; | 602 | int err = NETDEV_TX_OK; |
605 | bool is_nic_in_bad_state; | ||
606 | 603 | ||
607 | frags = skb_shinfo(skb)->nr_frags + 1; | 604 | frags = skb_shinfo(skb)->nr_frags + 1; |
608 | 605 | ||
@@ -613,13 +610,10 @@ int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb) | |||
613 | goto err_exit; | 610 | goto err_exit; |
614 | } | 611 | } |
615 | 612 | ||
616 | is_nic_in_bad_state = aq_utils_obj_test(&self->header.flags, | 613 | aq_ring_update_queue_state(ring); |
617 | AQ_NIC_FLAGS_IS_NOT_TX_READY) || | ||
618 | (aq_ring_avail_dx(ring) < | ||
619 | AQ_CFG_SKB_FRAGS_MAX); | ||
620 | 614 | ||
621 | if (is_nic_in_bad_state) { | 615 | /* Above status update may stop the queue. Check this. */ |
622 | aq_nic_ndev_queue_stop(self, ring->idx); | 616 | if (__netif_subqueue_stopped(self->ndev, ring->idx)) { |
623 | err = NETDEV_TX_BUSY; | 617 | err = NETDEV_TX_BUSY; |
624 | goto err_exit; | 618 | goto err_exit; |
625 | } | 619 | } |
@@ -631,9 +625,6 @@ int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb) | |||
631 | ring, | 625 | ring, |
632 | frags); | 626 | frags); |
633 | if (err >= 0) { | 627 | if (err >= 0) { |
634 | if (aq_ring_avail_dx(ring) < AQ_CFG_SKB_FRAGS_MAX + 1) | ||
635 | aq_nic_ndev_queue_stop(self, ring->idx); | ||
636 | |||
637 | ++ring->stats.tx.packets; | 628 | ++ring->stats.tx.packets; |
638 | ring->stats.tx.bytes += skb->len; | 629 | ring->stats.tx.bytes += skb->len; |
639 | } | 630 | } |
@@ -898,9 +889,7 @@ int aq_nic_stop(struct aq_nic_s *self) | |||
898 | struct aq_vec_s *aq_vec = NULL; | 889 | struct aq_vec_s *aq_vec = NULL; |
899 | unsigned int i = 0U; | 890 | unsigned int i = 0U; |
900 | 891 | ||
901 | for (i = 0U, aq_vec = self->aq_vec[0]; | 892 | netif_tx_disable(self->ndev); |
902 | self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) | ||
903 | aq_nic_ndev_queue_stop(self, i); | ||
904 | 893 | ||
905 | del_timer_sync(&self->service_timer); | 894 | del_timer_sync(&self->service_timer); |
906 | 895 | ||
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.h b/drivers/net/ethernet/aquantia/atlantic/aq_nic.h index 7fc2a5ecb2b7..0ddd556ff901 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.h | |||
@@ -83,8 +83,6 @@ struct net_device *aq_nic_get_ndev(struct aq_nic_s *self); | |||
83 | int aq_nic_init(struct aq_nic_s *self); | 83 | int aq_nic_init(struct aq_nic_s *self); |
84 | int aq_nic_cfg_start(struct aq_nic_s *self); | 84 | int aq_nic_cfg_start(struct aq_nic_s *self); |
85 | int aq_nic_ndev_register(struct aq_nic_s *self); | 85 | int aq_nic_ndev_register(struct aq_nic_s *self); |
86 | void aq_nic_ndev_queue_start(struct aq_nic_s *self, unsigned int idx); | ||
87 | void aq_nic_ndev_queue_stop(struct aq_nic_s *self, unsigned int idx); | ||
88 | void aq_nic_ndev_free(struct aq_nic_s *self); | 86 | void aq_nic_ndev_free(struct aq_nic_s *self); |
89 | int aq_nic_start(struct aq_nic_s *self); | 87 | int aq_nic_start(struct aq_nic_s *self); |
90 | int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb); | 88 | int aq_nic_xmit(struct aq_nic_s *self, struct sk_buff *skb); |
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c index 4eee1996a825..02f79b0640ba 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.c | |||
@@ -104,6 +104,32 @@ int aq_ring_init(struct aq_ring_s *self) | |||
104 | return 0; | 104 | return 0; |
105 | } | 105 | } |
106 | 106 | ||
107 | void aq_ring_update_queue_state(struct aq_ring_s *ring) | ||
108 | { | ||
109 | if (aq_ring_avail_dx(ring) <= AQ_CFG_SKB_FRAGS_MAX) | ||
110 | aq_ring_queue_stop(ring); | ||
111 | else if (aq_ring_avail_dx(ring) > AQ_CFG_RESTART_DESC_THRES) | ||
112 | aq_ring_queue_wake(ring); | ||
113 | } | ||
114 | |||
115 | void aq_ring_queue_wake(struct aq_ring_s *ring) | ||
116 | { | ||
117 | struct net_device *ndev = aq_nic_get_ndev(ring->aq_nic); | ||
118 | |||
119 | if (__netif_subqueue_stopped(ndev, ring->idx)) { | ||
120 | netif_wake_subqueue(ndev, ring->idx); | ||
121 | ring->stats.tx.queue_restarts++; | ||
122 | } | ||
123 | } | ||
124 | |||
125 | void aq_ring_queue_stop(struct aq_ring_s *ring) | ||
126 | { | ||
127 | struct net_device *ndev = aq_nic_get_ndev(ring->aq_nic); | ||
128 | |||
129 | if (!__netif_subqueue_stopped(ndev, ring->idx)) | ||
130 | netif_stop_subqueue(ndev, ring->idx); | ||
131 | } | ||
132 | |||
107 | void aq_ring_tx_clean(struct aq_ring_s *self) | 133 | void aq_ring_tx_clean(struct aq_ring_s *self) |
108 | { | 134 | { |
109 | struct device *dev = aq_nic_get_dev(self->aq_nic); | 135 | struct device *dev = aq_nic_get_dev(self->aq_nic); |
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h index 782176c5f4f8..24523b5ac68c 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ring.h +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ring.h | |||
@@ -94,6 +94,7 @@ struct aq_ring_stats_tx_s { | |||
94 | u64 errors; | 94 | u64 errors; |
95 | u64 packets; | 95 | u64 packets; |
96 | u64 bytes; | 96 | u64 bytes; |
97 | u64 queue_restarts; | ||
97 | }; | 98 | }; |
98 | 99 | ||
99 | union aq_ring_stats_s { | 100 | union aq_ring_stats_s { |
@@ -147,6 +148,9 @@ struct aq_ring_s *aq_ring_rx_alloc(struct aq_ring_s *self, | |||
147 | int aq_ring_init(struct aq_ring_s *self); | 148 | int aq_ring_init(struct aq_ring_s *self); |
148 | void aq_ring_rx_deinit(struct aq_ring_s *self); | 149 | void aq_ring_rx_deinit(struct aq_ring_s *self); |
149 | void aq_ring_free(struct aq_ring_s *self); | 150 | void aq_ring_free(struct aq_ring_s *self); |
151 | void aq_ring_update_queue_state(struct aq_ring_s *ring); | ||
152 | void aq_ring_queue_wake(struct aq_ring_s *ring); | ||
153 | void aq_ring_queue_stop(struct aq_ring_s *ring); | ||
150 | void aq_ring_tx_clean(struct aq_ring_s *self); | 154 | void aq_ring_tx_clean(struct aq_ring_s *self); |
151 | int aq_ring_rx_clean(struct aq_ring_s *self, | 155 | int aq_ring_rx_clean(struct aq_ring_s *self, |
152 | struct napi_struct *napi, | 156 | struct napi_struct *napi, |
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c index ebf588004c46..305ff8ffac2c 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_vec.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_vec.c | |||
@@ -59,12 +59,7 @@ static int aq_vec_poll(struct napi_struct *napi, int budget) | |||
59 | if (ring[AQ_VEC_TX_ID].sw_head != | 59 | if (ring[AQ_VEC_TX_ID].sw_head != |
60 | ring[AQ_VEC_TX_ID].hw_head) { | 60 | ring[AQ_VEC_TX_ID].hw_head) { |
61 | aq_ring_tx_clean(&ring[AQ_VEC_TX_ID]); | 61 | aq_ring_tx_clean(&ring[AQ_VEC_TX_ID]); |
62 | 62 | aq_ring_update_queue_state(&ring[AQ_VEC_TX_ID]); | |
63 | if (aq_ring_avail_dx(&ring[AQ_VEC_TX_ID]) > | ||
64 | AQ_CFG_SKB_FRAGS_MAX) { | ||
65 | aq_nic_ndev_queue_start(self->aq_nic, | ||
66 | ring[AQ_VEC_TX_ID].idx); | ||
67 | } | ||
68 | was_tx_cleaned = true; | 63 | was_tx_cleaned = true; |
69 | } | 64 | } |
70 | 65 | ||
@@ -364,6 +359,7 @@ void aq_vec_add_stats(struct aq_vec_s *self, | |||
364 | stats_tx->packets += tx->packets; | 359 | stats_tx->packets += tx->packets; |
365 | stats_tx->bytes += tx->bytes; | 360 | stats_tx->bytes += tx->bytes; |
366 | stats_tx->errors += tx->errors; | 361 | stats_tx->errors += tx->errors; |
362 | stats_tx->queue_restarts += tx->queue_restarts; | ||
367 | } | 363 | } |
368 | } | 364 | } |
369 | 365 | ||