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 <ndani...@aquantia.com>
Signed-off-by: Igor Russkikh <igor.russk...@aquantia.com>
---
 .../net/ethernet/aquantia/atlantic/aq_nic.c   | 25 ++++++++++++++-----
 .../net/ethernet/aquantia/atlantic/aq_nic.h   |  1 +
 2 files changed, 20 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.c 
b/drivers/net/ethernet/aquantia/atlantic/aq_nic.c
index 6de0d1c0ed79..b8f6f4129f44 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)
@@ -354,6 +363,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);
 
@@ -904,6 +916,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);
 
diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_nic.h 
b/drivers/net/ethernet/aquantia/atlantic/aq_nic.h
index be56ace6274b..c03d38ed105d 100644
--- a/drivers/net/ethernet/aquantia/atlantic/aq_nic.h
+++ b/drivers/net/ethernet/aquantia/atlantic/aq_nic.h
@@ -93,6 +93,7 @@ struct aq_nic_s {
        const struct aq_fw_ops *aq_fw_ops;
        struct aq_nic_cfg_s aq_nic_cfg;
        struct timer_list service_timer;
+       struct work_struct service_task;
        struct timer_list polling_timer;
        struct aq_hw_link_status_s link_status;
        struct {
-- 
2.17.1

Reply via email to