net: hns: Add self-adaptive interrupt coalesce support in hns driver
authorLin Yun Sheng <linyunsheng@huawei.com>
Wed, 2 Aug 2017 09:57:37 +0000 (17:57 +0800)
committerDavid S. Miller <davem@davemloft.net>
Thu, 3 Aug 2017 16:21:14 +0000 (09:21 -0700)
When deal with low and high throughput, it is hard to achiece both
high performance and low latency. In order to achiece that, this patch
calculates the rx rate, and adjust the interrupt coalesce parameter
accordingly.

Signed-off-by: Yunsheng Lin <linyunsheng@huawei.com>
Tested-by: Weiwei Deng <dengweiwei@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/hisilicon/hns/hnae.c
drivers/net/ethernet/hisilicon/hns/hnae.h
drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
drivers/net/ethernet/hisilicon/hns/hns_enet.c
drivers/net/ethernet/hisilicon/hns/hns_enet.h
drivers/net/ethernet/hisilicon/hns/hns_ethtool.c

index 9d9b6e6dd9884fdb835e80043f2cdc83d5283bbf..a051e582d541ad2e2191567b9b3b3d7d69a90fc0 100644 (file)
@@ -202,6 +202,7 @@ hnae_init_ring(struct hnae_queue *q, struct hnae_ring *ring, int flags)
        ring->q = q;
        ring->flags = flags;
        spin_lock_init(&ring->lock);
+       ring->coal_param = q->handle->coal_param;
        assert(!ring->desc && !ring->desc_cb && !ring->desc_dma_addr);
 
        /* not matter for tx or rx ring, the ntc and ntc start from 0 */
index 7ba653af19cb980a5e28a59ab2844cac1d7813c5..3e62692af0119e0e4f3dfe85d74457500951f19b 100644 (file)
@@ -89,6 +89,10 @@ do { \
 
 #define RCB_RING_NAME_LEN 16
 
+#define HNAE_LOWEST_LATENCY_COAL_PARAM 30
+#define HNAE_LOW_LATENCY_COAL_PARAM    80
+#define HNAE_BULK_LATENCY_COAL_PARAM   150
+
 enum hnae_led_state {
        HNAE_LED_INACTIVE,
        HNAE_LED_ACTIVE,
@@ -292,6 +296,12 @@ struct hnae_ring {
 
        int flags;          /* ring attribute */
        int irq_init_flag;
+
+       /* total rx bytes after last rx rate calucated */
+       u64 coal_last_rx_bytes;
+       unsigned long coal_last_jiffies;
+       u32 coal_param;
+       u32 coal_rx_rate;       /* rx rate in MB */
 };
 
 #define ring_ptr_move_fw(ring, p) \
@@ -548,8 +558,13 @@ struct hnae_handle {
        u32 if_support;
        int q_num;
        int vf_id;
+       unsigned long coal_last_jiffies;
+       u32 coal_param;         /* self adapt coalesce param */
+       /* the ring index of last ring that set coal param */
+       u32 coal_ring_idx;
        u32 eport_id;
        u32 dport_id;   /* v2 tx bd should fill the dport_id */
+       bool coal_adapt_en;
        enum hnae_port_type port_type;
        enum hnae_media_type media_type;
        struct list_head node;    /* list to hnae_ae_dev->handle_list */
index a37166ee577b71f4fc2ea07e82d1097c19b4bf66..bd68379d2beab60415cf5d071e598b36fd9871ed 100644 (file)
@@ -99,6 +99,7 @@ struct hnae_handle *hns_ae_get_handle(struct hnae_ae_dev *dev,
        ae_handle->owner_dev = dsaf_dev->dev;
        ae_handle->dev = dev;
        ae_handle->q_num = qnum_per_vf;
+       ae_handle->coal_param = HNAE_LOWEST_LATENCY_COAL_PARAM;
 
        /* find ring pair, and set vf id*/
        for (ae_handle->vf_id = 0;
index 3987699f8fe6ae4da67762d9f344e8acc5563dae..832f27792e3f9f7c99e392f8f5a73436b8bd42df 100644 (file)
@@ -812,6 +812,112 @@ static int hns_desc_unused(struct hnae_ring *ring)
        return ((ntc >= ntu) ? 0 : ring->desc_num) + ntc - ntu;
 }
 
+#define HNS_LOWEST_LATENCY_RATE                27      /* 27 MB/s */
+#define HNS_LOW_LATENCY_RATE                   80      /* 80 MB/s */
+
+#define HNS_COAL_BDNUM                 3
+
+static u32 hns_coal_rx_bdnum(struct hnae_ring *ring)
+{
+       bool coal_enable = ring->q->handle->coal_adapt_en;
+
+       if (coal_enable &&
+           ring->coal_last_rx_bytes > HNS_LOWEST_LATENCY_RATE)
+               return HNS_COAL_BDNUM;
+       else
+               return 0;
+}
+
+static void hns_update_rx_rate(struct hnae_ring *ring)
+{
+       bool coal_enable = ring->q->handle->coal_adapt_en;
+       u32 time_passed_ms;
+       u64 total_bytes;
+
+       if (!coal_enable ||
+           time_before(jiffies, ring->coal_last_jiffies + (HZ >> 4)))
+               return;
+
+       /* ring->stats.rx_bytes overflowed */
+       if (ring->coal_last_rx_bytes > ring->stats.rx_bytes) {
+               ring->coal_last_rx_bytes = ring->stats.rx_bytes;
+               ring->coal_last_jiffies = jiffies;
+               return;
+       }
+
+       total_bytes = ring->stats.rx_bytes - ring->coal_last_rx_bytes;
+       time_passed_ms = jiffies_to_msecs(jiffies - ring->coal_last_jiffies);
+       ring->coal_rx_rate = (total_bytes / time_passed_ms) >> 10;
+
+       ring->coal_last_rx_bytes = ring->stats.rx_bytes;
+       ring->coal_last_jiffies = jiffies;
+}
+
+/**
+ * smooth_alg - smoothing algrithm for adjusting coalesce parameter
+ **/
+static u32 smooth_alg(u32 new_param, u32 old_param)
+{
+       u32 gap = (new_param > old_param) ? new_param - old_param
+                                         : old_param - new_param;
+
+       if (gap > 8)
+               gap >>= 3;
+
+       if (new_param > old_param)
+               return old_param + gap;
+       else
+               return old_param - gap;
+}
+
+/**
+ * hns_nic_adp_coalesce - self adapte coalesce according to rx rate
+ * @ring_data: pointer to hns_nic_ring_data
+ **/
+static void hns_nic_adpt_coalesce(struct hns_nic_ring_data *ring_data)
+{
+       struct hnae_ring *ring = ring_data->ring;
+       struct hnae_handle *handle = ring->q->handle;
+       u32 new_coal_param, old_coal_param = ring->coal_param;
+
+       if (ring->coal_rx_rate < HNS_LOWEST_LATENCY_RATE)
+               new_coal_param = HNAE_LOWEST_LATENCY_COAL_PARAM;
+       else if (ring->coal_rx_rate < HNS_LOW_LATENCY_RATE)
+               new_coal_param = HNAE_LOW_LATENCY_COAL_PARAM;
+       else
+               new_coal_param = HNAE_BULK_LATENCY_COAL_PARAM;
+
+       if (new_coal_param == old_coal_param &&
+           new_coal_param == handle->coal_param)
+               return;
+
+       new_coal_param = smooth_alg(new_coal_param, old_coal_param);
+       ring->coal_param = new_coal_param;
+
+       /**
+        * Because all ring in one port has one coalesce param, when one ring
+        * calculate its own coalesce param, it cannot write to hardware at
+        * once. There are three conditions as follows:
+        *       1. current ring's coalesce param is larger than the hardware.
+        *       2. or ring which adapt last time can change again.
+        *       3. timeout.
+        */
+       if (new_coal_param == handle->coal_param) {
+               handle->coal_last_jiffies = jiffies;
+               handle->coal_ring_idx = ring_data->queue_index;
+       } else if (new_coal_param > handle->coal_param ||
+                  handle->coal_ring_idx == ring_data->queue_index ||
+                  time_after(jiffies, handle->coal_last_jiffies + (HZ >> 4))) {
+               handle->dev->ops->set_coalesce_usecs(handle,
+                                       new_coal_param);
+               handle->dev->ops->set_coalesce_frames(handle,
+                                       1, new_coal_param);
+               handle->coal_param = new_coal_param;
+               handle->coal_ring_idx = ring_data->queue_index;
+               handle->coal_last_jiffies = jiffies;
+       }
+}
+
 static int hns_nic_rx_poll_one(struct hns_nic_ring_data *ring_data,
                               int budget, void *v)
 {
@@ -868,20 +974,27 @@ static bool hns_nic_rx_fini_pro(struct hns_nic_ring_data *ring_data)
 {
        struct hnae_ring *ring = ring_data->ring;
        int num = 0;
+       bool rx_stopped;
 
-       ring_data->ring->q->handle->dev->ops->toggle_ring_irq(ring, 0);
+       hns_update_rx_rate(ring);
 
        /* for hardware bug fixed */
+       ring_data->ring->q->handle->dev->ops->toggle_ring_irq(ring, 0);
        num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
 
-       if (num > 0) {
+       if (num <= hns_coal_rx_bdnum(ring)) {
+               if (ring->q->handle->coal_adapt_en)
+                       hns_nic_adpt_coalesce(ring_data);
+
+               rx_stopped = true;
+       } else {
                ring_data->ring->q->handle->dev->ops->toggle_ring_irq(
                        ring_data->ring, 1);
 
-               return false;
-       } else {
-               return true;
+               rx_stopped = false;
        }
+
+       return rx_stopped;
 }
 
 static bool hns_nic_rx_fini_pro_v2(struct hns_nic_ring_data *ring_data)
@@ -889,12 +1002,17 @@ static bool hns_nic_rx_fini_pro_v2(struct hns_nic_ring_data *ring_data)
        struct hnae_ring *ring = ring_data->ring;
        int num;
 
+       hns_update_rx_rate(ring);
        num = readl_relaxed(ring->io_base + RCB_REG_FBDNUM);
 
-       if (!num)
+       if (num <= hns_coal_rx_bdnum(ring)) {
+               if (ring->q->handle->coal_adapt_en)
+                       hns_nic_adpt_coalesce(ring_data);
+
                return true;
-       else
-               return false;
+       }
+
+       return false;
 }
 
 static inline void hns_nic_reclaim_one_desc(struct hnae_ring *ring,
index 9cb4c7884201e1eb4c1c160a85517f62f397646a..26e9afcbdd50f77642d6455a6cea20b2ffc8032c 100644 (file)
@@ -38,7 +38,7 @@ struct hns_nic_ring_data {
        struct hnae_ring *ring;
        struct napi_struct napi;
        cpumask_t mask; /* affinity mask */
-       int queue_index;
+       u32 queue_index;
        int (*poll_one)(struct hns_nic_ring_data *, int, void *);
        void (*ex_process)(struct hns_nic_ring_data *, struct sk_buff *);
        bool (*fini_process)(struct hns_nic_ring_data *);
index 78cb20c67aa6bab93562bb2957483cfd062d6d49..7ea7f8a4aa2a9456f2d71cceccae9eff2b83421a 100644 (file)
@@ -735,8 +735,8 @@ static int hns_get_coalesce(struct net_device *net_dev,
 
        ops = priv->ae_handle->dev->ops;
 
-       ec->use_adaptive_rx_coalesce = 1;
-       ec->use_adaptive_tx_coalesce = 1;
+       ec->use_adaptive_rx_coalesce = priv->ae_handle->coal_adapt_en;
+       ec->use_adaptive_tx_coalesce = priv->ae_handle->coal_adapt_en;
 
        if ((!ops->get_coalesce_usecs) ||
            (!ops->get_max_coalesced_frames))
@@ -787,6 +787,9 @@ static int hns_set_coalesce(struct net_device *net_dev,
            (!ops->set_coalesce_frames))
                return -ESRCH;
 
+       if (ec->use_adaptive_rx_coalesce != priv->ae_handle->coal_adapt_en)
+               priv->ae_handle->coal_adapt_en = ec->use_adaptive_rx_coalesce;
+
        rc1 = ops->set_coalesce_usecs(priv->ae_handle,
                                      ec->rx_coalesce_usecs);