From 46f4c29d9de6e4a9d4ed7de9a37dd42501d89f86 Mon Sep 17 00:00:00 2001 From: Igor Russkikh Date: Sat, 23 Mar 2019 15:23:32 +0000 Subject: net: aquantia: optimize rx performance by page reuse strategy We introduce internal aq_rxpage wrapper over regular page where extra field is tracked: rxpage offset inside of allocated page. This offset allows to reuse one page for multiple packets. When needed (for example with large frames processing), allocated pageorder could be customized. This gives even larger page reuse efficiency. page_ref_count is used to track page users. If during rx refill underlying page has users, we increase pg_off by rx frame size thus the top half of the page is reused. Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller --- drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/net/ethernet/aquantia/atlantic/aq_nic.c') diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index ff83667410bd..059df86e8e37 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -73,6 +73,7 @@ void aq_nic_cfg_start(struct aq_nic_s *self) cfg->tx_itr = aq_itr_tx; cfg->rx_itr = aq_itr_rx; + cfg->rxpageorder = AQ_CFG_RX_PAGEORDER; cfg->is_rss = AQ_CFG_IS_RSS_DEF; cfg->num_rss_queues = AQ_CFG_NUM_RSS_QUEUES_DEF; cfg->aq_rss.base_cpu_number = AQ_CFG_RSS_BASE_CPU_NUM_DEF; -- cgit From 1d2a8a138c2a1462099ba45d1c5bcd9a576ced2a Mon Sep 17 00:00:00 2001 From: Igor Russkikh Date: Mon, 29 Apr 2019 10:04:43 +0000 Subject: net: aquantia: link interrupt handling function Define link interrupt handler Signed-off-by: Nikita Danilov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller --- drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/net/ethernet/aquantia/atlantic/aq_nic.c') diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index 059df86e8e37..4851fc0a3ae5 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -161,6 +161,20 @@ static int aq_nic_update_link_status(struct aq_nic_s *self) return 0; } +static irqreturn_t aq_linkstate_threaded_isr(int irq, void *private) +{ + struct aq_nic_s *self = private; + + if (!self) + return IRQ_NONE; + + aq_nic_update_link_status(self); + + self->aq_hw_ops->hw_irq_enable(self->aq_hw, + BIT(self->aq_nic_cfg.link_irq_vec)); + return IRQ_HANDLED; +} + static void aq_nic_service_timer_cb(struct timer_list *t) { struct aq_nic_s *self = from_timer(self, t, service_timer); -- cgit From 4c83f170b3ac08357de253097d95b6942393f63b Mon Sep 17 00:00:00 2001 From: Igor Russkikh Date: Mon, 29 Apr 2019 10:04:48 +0000 Subject: net: aquantia: link status irq handling Here we define and request an extra interrupt line, assign it on link isr handler and restructure abit aq_pci code to better support that. We also remove logic for using different timer intervals depending on link state, since thats now useless. Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller --- drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 36 ++++++++++++++++++------- 1 file changed, 27 insertions(+), 9 deletions(-) (limited to 'drivers/net/ethernet/aquantia/atlantic/aq_nic.c') diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index 4851fc0a3ae5..0251566b66af 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -14,6 +14,7 @@ #include "aq_vec.h" #include "aq_hw.h" #include "aq_pci_func.h" +#include "aq_main.h" #include #include @@ -92,7 +93,8 @@ void aq_nic_cfg_start(struct aq_nic_s *self) /*rss rings */ cfg->vecs = min(cfg->aq_hw_caps->vecs, AQ_CFG_VECS_DEF); cfg->vecs = min(cfg->vecs, num_online_cpus()); - cfg->vecs = min(cfg->vecs, self->irqvecs); + if (self->irqvecs > AQ_HW_SERVICE_IRQS) + cfg->vecs = min(cfg->vecs, self->irqvecs - AQ_HW_SERVICE_IRQS); /* cfg->vecs should be power of 2 for RSS */ if (cfg->vecs >= 8U) cfg->vecs = 8U; @@ -116,6 +118,15 @@ void aq_nic_cfg_start(struct aq_nic_s *self) cfg->vecs = 1U; } + /* Check if we have enough vectors allocated for + * link status IRQ. If no - we'll know link state from + * slower service task. + */ + if (AQ_HW_SERVICE_IRQS > 0 && cfg->vecs + 1 <= self->irqvecs) + cfg->link_irq_vec = cfg->vecs; + else + cfg->link_irq_vec = 0; + cfg->link_speed_msk &= cfg->aq_hw_caps->link_speed_msk; cfg->features = cfg->aq_hw_caps->hw_features; } @@ -178,7 +189,6 @@ static irqreturn_t aq_linkstate_threaded_isr(int irq, void *private) static void aq_nic_service_timer_cb(struct timer_list *t) { struct aq_nic_s *self = from_timer(self, t, service_timer); - int ctimer = AQ_CFG_SERVICE_TIMER_INTERVAL; int err = 0; if (aq_utils_obj_test(&self->flags, AQ_NIC_FLAGS_IS_NOT_READY)) @@ -193,12 +203,8 @@ static void aq_nic_service_timer_cb(struct timer_list *t) aq_nic_update_ndev_stats(self); - /* If no link - use faster timer rate to detect link up asap */ - if (!netif_carrier_ok(self->ndev)) - ctimer = max(ctimer / 2, 1); - err_exit: - mod_timer(&self->service_timer, jiffies + ctimer); + mod_timer(&self->service_timer, jiffies + AQ_CFG_SERVICE_TIMER_INTERVAL); } static void aq_nic_polling_timer_cb(struct timer_list *t) @@ -359,13 +365,25 @@ int aq_nic_start(struct aq_nic_s *self) } else { for (i = 0U, aq_vec = self->aq_vec[0]; self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) { - err = aq_pci_func_alloc_irq(self, i, - self->ndev->name, aq_vec, + err = aq_pci_func_alloc_irq(self, i, self->ndev->name, + aq_vec_isr, aq_vec, aq_vec_get_affinity_mask(aq_vec)); if (err < 0) goto err_exit; } + if (self->aq_nic_cfg.link_irq_vec) { + int irqvec = pci_irq_vector(self->pdev, + self->aq_nic_cfg.link_irq_vec); + err = request_threaded_irq(irqvec, NULL, + aq_linkstate_threaded_isr, + IRQF_SHARED, + self->ndev->name, self); + if (err < 0) + goto err_exit; + self->msix_entry_mask |= (1 << self->aq_nic_cfg.link_irq_vec); + } + err = self->aq_hw_ops->hw_irq_enable(self->aq_hw, AQ_CFG_IRQ_MASK); if (err < 0) -- cgit From 6775878823bf622f5143e77fd8fdf4bcd91cfd96 Mon Sep 17 00:00:00 2001 From: Igor Russkikh Date: Mon, 29 Apr 2019 10:04:50 +0000 Subject: net: aquantia: improve ifup link detection Original code detected link only after 1 sec is passed after up. Here we replace this with direct service callback which updates link status immediately Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller --- drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/net/ethernet/aquantia/atlantic/aq_nic.c') diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index 0251566b66af..6de0d1c0ed79 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -355,8 +355,7 @@ int aq_nic_start(struct aq_nic_s *self) if (err) goto err_exit; timer_setup(&self->service_timer, aq_nic_service_timer_cb, 0); - mod_timer(&self->service_timer, jiffies + - AQ_CFG_SERVICE_TIMER_INTERVAL); + aq_nic_service_timer_cb(&self->service_timer); if (self->aq_nic_cfg.is_polling) { timer_setup(&self->polling_timer, aq_nic_polling_timer_cb, 0); -- cgit From f5dce08ab179459f3f622a63dfa446a930b84192 Mon Sep 17 00:00:00 2001 From: Nikita Danilov Date: Mon, 29 Apr 2019 10:04:57 +0000 Subject: net: aquantia: introduce fwreq mutex Some of FW operations could be invoked simultaneously, from f.e. ethtool context and from service service activity work. Here we introduce a fw mutex to secure and serialize access to FW logic. Signed-off-by: Nikita Danilov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller --- drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 26 +++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'drivers/net/ethernet/aquantia/atlantic/aq_nic.c') diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index 6de0d1c0ed79..b038e2e9af3a 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -234,8 +234,10 @@ int aq_nic_ndev_register(struct aq_nic_s *self) if (err) goto err_exit; + mutex_lock(&self->fwreq_mutex); err = self->aq_fw_ops->get_mac_permanent(self->aq_hw, self->ndev->dev_addr); + mutex_unlock(&self->fwreq_mutex); if (err) goto err_exit; @@ -304,7 +306,9 @@ int aq_nic_init(struct aq_nic_s *self) unsigned int i = 0U; self->power_state = AQ_HW_POWER_STATE_D0; + mutex_lock(&self->fwreq_mutex); err = self->aq_hw_ops->hw_reset(self->aq_hw); + mutex_unlock(&self->fwreq_mutex); if (err < 0) goto err_exit; @@ -871,7 +875,9 @@ int aq_nic_set_link_ksettings(struct aq_nic_s *self, self->aq_nic_cfg.is_autoneg = false; } + mutex_lock(&self->fwreq_mutex); err = self->aq_fw_ops->set_link_speed(self->aq_hw, rate); + mutex_unlock(&self->fwreq_mutex); if (err < 0) goto err_exit; @@ -931,14 +937,22 @@ void aq_nic_deinit(struct aq_nic_s *self) self->aq_vecs > i; ++i, aq_vec = self->aq_vec[i]) aq_vec_deinit(aq_vec); - self->aq_fw_ops->deinit(self->aq_hw); + if (likely(self->aq_fw_ops->deinit)) { + mutex_lock(&self->fwreq_mutex); + self->aq_fw_ops->deinit(self->aq_hw); + mutex_unlock(&self->fwreq_mutex); + } if (self->power_state != AQ_HW_POWER_STATE_D0 || - self->aq_hw->aq_nic_cfg->wol) { - self->aq_fw_ops->set_power(self->aq_hw, - self->power_state, - self->ndev->dev_addr); - } + self->aq_hw->aq_nic_cfg->wol) + if (likely(self->aq_fw_ops->set_power)) { + mutex_lock(&self->fwreq_mutex); + self->aq_fw_ops->set_power(self->aq_hw, + self->power_state, + self->ndev->dev_addr); + mutex_unlock(&self->fwreq_mutex); + } + err_exit:; } -- cgit From 49544935a78cec813e1a8b17ea40d589138baf99 Mon Sep 17 00:00:00 2001 From: Igor Russkikh Date: Mon, 29 Apr 2019 10:05:00 +0000 Subject: net: aquantia: extract timer cb into work job Service timer callback fetches statistics from FW and that may cause a long delay in error cases. We also now need to use fw mutex to prevent concurrent access to FW, thus - extract that logic from timer callback into the job in the separate work queue. Signed-off-by: Nikita Danilov Signed-off-by: Igor Russkikh Signed-off-by: David S. Miller --- drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'drivers/net/ethernet/aquantia/atlantic/aq_nic.c') diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index b038e2e9af3a..454a44bb148e 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -186,25 +186,34 @@ static irqreturn_t aq_linkstate_threaded_isr(int irq, void *private) return IRQ_HANDLED; } -static void aq_nic_service_timer_cb(struct timer_list *t) +static void aq_nic_service_task(struct work_struct *work) { - struct aq_nic_s *self = from_timer(self, t, service_timer); - int err = 0; + struct aq_nic_s *self = container_of(work, struct aq_nic_s, + service_task); + int err; if (aq_utils_obj_test(&self->flags, AQ_NIC_FLAGS_IS_NOT_READY)) - goto err_exit; + return; err = aq_nic_update_link_status(self); if (err) - goto err_exit; + return; + mutex_lock(&self->fwreq_mutex); if (self->aq_fw_ops->update_stats) self->aq_fw_ops->update_stats(self->aq_hw); + mutex_unlock(&self->fwreq_mutex); aq_nic_update_ndev_stats(self); +} + +static void aq_nic_service_timer_cb(struct timer_list *t) +{ + struct aq_nic_s *self = from_timer(self, t, service_timer); -err_exit: mod_timer(&self->service_timer, jiffies + AQ_CFG_SERVICE_TIMER_INTERVAL); + + aq_ndev_schedule_work(&self->service_task); } static void aq_nic_polling_timer_cb(struct timer_list *t) @@ -358,6 +367,9 @@ int aq_nic_start(struct aq_nic_s *self) err = aq_nic_update_interrupt_moderation_settings(self); if (err) goto err_exit; + + INIT_WORK(&self->service_task, aq_nic_service_task); + timer_setup(&self->service_timer, aq_nic_service_timer_cb, 0); aq_nic_service_timer_cb(&self->service_timer); @@ -910,6 +922,7 @@ int aq_nic_stop(struct aq_nic_s *self) netif_carrier_off(self->ndev); del_timer_sync(&self->service_timer); + cancel_work_sync(&self->service_task); self->aq_hw_ops->hw_irq_disable(self->aq_hw, AQ_CFG_IRQ_MASK); -- cgit From 190f34384c6cfed30fe8a1f94f7a25de129ccaa9 Mon Sep 17 00:00:00 2001 From: Dmitry Bogdanov Date: Mon, 29 Apr 2019 10:05:02 +0000 Subject: net: aquantia: fetch up to date statistics on ethtool request This improves ethtool -S usage, where stats are now actual on each request. Before that stats only were updated at service timer period. Tested-by: Nikita Danilov Signed-off-by: Igor Russkikh Signed-off-by: Dmitry Bogdanov Signed-off-by: David S. Miller --- drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'drivers/net/ethernet/aquantia/atlantic/aq_nic.c') diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index 454a44bb148e..8018f483ae45 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -700,7 +700,14 @@ void aq_nic_get_stats(struct aq_nic_s *self, u64 *data) unsigned int i = 0U; unsigned int count = 0U; struct aq_vec_s *aq_vec = NULL; - struct aq_stats_s *stats = self->aq_hw_ops->hw_get_hw_stats(self->aq_hw); + struct aq_stats_s *stats; + + if (self->aq_fw_ops->update_stats) { + mutex_lock(&self->fwreq_mutex); + self->aq_fw_ops->update_stats(self->aq_hw); + mutex_unlock(&self->fwreq_mutex); + } + stats = self->aq_hw_ops->hw_get_hw_stats(self->aq_hw); if (!stats) goto err_exit; -- cgit From f55d477bb513c9267a46d7a795fb09f73084f76f Mon Sep 17 00:00:00 2001 From: Dmitry Bogdanov Date: Mon, 29 Apr 2019 10:05:05 +0000 Subject: net: aquantia: get total counters from DMA block aq_nic_update_ndev_stats pushes statistics to ndev->stats from system interface. This is not always good because it counts packets/bytes before any of rx filters (including mac filter). Its better to report the packet/bytes statistics from DMA counters which gives actual values of data transferred over pci. System level stats is still available via ethtool. Signed-off-by: Nikita Danilov Signed-off-by: Igor Russkikh Signed-off-by: Dmitry Bogdanov Signed-off-by: David S. Miller --- drivers/net/ethernet/aquantia/atlantic/aq_nic.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers/net/ethernet/aquantia/atlantic/aq_nic.c') diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c index 8018f483ae45..e82d25a91bc1 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c @@ -753,11 +753,12 @@ static void aq_nic_update_ndev_stats(struct aq_nic_s *self) struct net_device *ndev = self->ndev; struct aq_stats_s *stats = self->aq_hw_ops->hw_get_hw_stats(self->aq_hw); - ndev->stats.rx_packets = stats->uprc + stats->mprc + stats->bprc; - ndev->stats.rx_bytes = stats->ubrc + stats->mbrc + stats->bbrc; + ndev->stats.rx_packets = stats->dma_pkt_rc; + ndev->stats.rx_bytes = stats->dma_oct_rc; ndev->stats.rx_errors = stats->erpr; - ndev->stats.tx_packets = stats->uptc + stats->mptc + stats->bptc; - ndev->stats.tx_bytes = stats->ubtc + stats->mbtc + stats->bbtc; + ndev->stats.rx_dropped = stats->dpc; + ndev->stats.tx_packets = stats->dma_pkt_tc; + ndev->stats.tx_bytes = stats->dma_oct_tc; ndev->stats.tx_errors = stats->erpt; ndev->stats.multicast = stats->mprc; } -- cgit