net:hns: Add Hip06 "RSS(Receive Side Scaling)" support to HNS Driver
authorSalil <salil.mehta@huawei.com>
Thu, 3 Dec 2015 12:17:54 +0000 (12:17 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 4 Dec 2015 19:36:15 +0000 (14:36 -0500)
This patch adds the support of "RSS (Receive Side Scaling)" feature
provided by the Hip06 ethernet hardware to the HNS ethernet
driver.

This feature helps in distributing the different flows (mapped as
hash by hardware using Toeplitz Hash) to different Queues asssociated
with the processor cores. The mapping of flow-hash values to the
different queues is stored in indirection table (which is per Packet-
parse-Engine/PPE). This patch also provides the changes to re-program
the (flow-hash<->Qid) mapping using the ethtool.

Signed-off-by: Salil Mehta <salil.mehta@huawei.com>
Reviewed-by: Kenneth Lee <liguozhu@huawei.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/hisilicon/hns/hnae.h
drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
drivers/net/ethernet/hisilicon/hns/hns_ethtool.c

index 6c9a68c464447747446ecd56b59c7f1ffac49a1a..76dd71563cc193d51bae0ebca469a7e6b0f218f0 100644 (file)
@@ -485,6 +485,12 @@ struct hnae_ae_ops {
                          enum hnae_led_state status);
        void (*get_regs)(struct hnae_handle *handle, void *data);
        int (*get_regs_len)(struct hnae_handle *handle);
+       u32     (*get_rss_key_size)(struct hnae_handle *handle);
+       u32     (*get_rss_indir_size)(struct hnae_handle *handle);
+       int     (*get_rss)(struct hnae_handle *handle, u32 *indir, u8 *key,
+                          u8 *hfunc);
+       int     (*set_rss)(struct hnae_handle *handle, const u32 *indir,
+                          const u8 *key, const u8 hfunc);
 };
 
 struct hnae_ae_dev {
index 043b9e52084d4871d5813f9c37ef54c76ee3897d..77e17351ade5862e7e0bd339bf89fb6894c043b7 100644 (file)
@@ -749,6 +749,53 @@ int hns_ae_get_regs_len(struct hnae_handle *handle)
        return total_num;
 }
 
+static u32 hns_ae_get_rss_key_size(struct hnae_handle *handle)
+{
+       return HNS_PPEV2_RSS_KEY_SIZE;
+}
+
+static u32 hns_ae_get_rss_indir_size(struct hnae_handle *handle)
+{
+       return HNS_PPEV2_RSS_IND_TBL_SIZE;
+}
+
+static int hns_ae_get_rss(struct hnae_handle *handle, u32 *indir, u8 *key,
+                         u8 *hfunc)
+{
+       struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
+
+       /* currently we support only one type of hash function i.e. Toep hash */
+       if (hfunc)
+               *hfunc = ETH_RSS_HASH_TOP;
+
+       /* get the RSS Key required by the user */
+       if (key)
+               memcpy(key, ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE);
+
+       /* update the current hash->queue mappings from the shadow RSS table */
+       memcpy(indir, ppe_cb->rss_indir_table, HNS_PPEV2_RSS_IND_TBL_SIZE);
+
+       return 0;
+}
+
+static int hns_ae_set_rss(struct hnae_handle *handle, const u32 *indir,
+                         const u8 *key, const u8 hfunc)
+{
+       struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
+
+       /* set the RSS Hash Key if specififed by the user */
+       if (key)
+               hns_ppe_set_rss_key(ppe_cb, (int *)key);
+
+       /* update the shadow RSS table with user specified qids */
+       memcpy(ppe_cb->rss_indir_table, indir, HNS_PPEV2_RSS_IND_TBL_SIZE);
+
+       /* now update the hardware */
+       hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
+
+       return 0;
+}
+
 static struct hnae_ae_ops hns_dsaf_ops = {
        .get_handle = hns_ae_get_handle,
        .put_handle = hns_ae_put_handle,
@@ -783,7 +830,11 @@ static struct hnae_ae_ops hns_dsaf_ops = {
        .update_led_status = hns_ae_update_led_status,
        .set_led_id = hns_ae_cpld_set_led_id,
        .get_regs = hns_ae_get_regs,
-       .get_regs_len = hns_ae_get_regs_len
+       .get_regs_len = hns_ae_get_regs_len,
+       .get_rss_key_size = hns_ae_get_rss_key_size,
+       .get_rss_indir_size = hns_ae_get_rss_indir_size,
+       .get_rss = hns_ae_get_rss,
+       .set_rss = hns_ae_set_rss
 };
 
 int hns_dsaf_ae_init(struct dsaf_device *dsaf_dev)
index 9531992853753a37f01e34398703a7c1eaf4b47c..7af0858f1fcbddd54935076964ddc9ec06c00fd9 100644 (file)
 
 #include "hns_dsaf_ppe.h"
 
+void hns_ppe_set_rss_key(struct hns_ppe_cb *ppe_cb,
+                        const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM])
+{
+       int key_item = 0;
+
+       for (key_item = 0; key_item < HNS_PPEV2_RSS_KEY_NUM; key_item++)
+               dsaf_write_dev(ppe_cb, PPEV2_RSS_KEY_REG + key_item * 0x4,
+                              rss_key[key_item]);
+}
+
+void hns_ppe_set_indir_table(struct hns_ppe_cb *ppe_cb,
+                            const u32 rss_tab[HNS_PPEV2_RSS_IND_TBL_SIZE])
+{
+       int i;
+       int reg_value;
+
+       for (i = 0; i < (HNS_PPEV2_RSS_IND_TBL_SIZE / 4); i++) {
+               reg_value = dsaf_read_dev(ppe_cb,
+                                         PPEV2_INDRECTION_TBL_REG + i * 0x4);
+
+               dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N0_M,
+                              PPEV2_CFG_RSS_TBL_4N0_S,
+                              rss_tab[i * 4 + 0] & 0x1F);
+               dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N1_M,
+                              PPEV2_CFG_RSS_TBL_4N1_S,
+                               rss_tab[i * 4 + 1] & 0x1F);
+               dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N2_M,
+                              PPEV2_CFG_RSS_TBL_4N2_S,
+                               rss_tab[i * 4 + 2] & 0x1F);
+               dsaf_set_field(reg_value, PPEV2_CFG_RSS_TBL_4N3_M,
+                              PPEV2_CFG_RSS_TBL_4N3_S,
+                               rss_tab[i * 4 + 3] & 0x1F);
+               dsaf_write_dev(
+                       ppe_cb, PPEV2_INDRECTION_TBL_REG + i * 0x4, reg_value);
+       }
+}
+
 static void __iomem *hns_ppe_common_get_ioaddr(
        struct ppe_common_cb *ppe_common)
 {
@@ -266,13 +303,17 @@ static void hns_ppe_exc_irq_en(struct hns_ppe_cb *ppe_cb, int en)
 
 /**
  * ppe_init_hw - init ppe
- * @ppe_device: ppe device
+ * @ppe_cb: ppe device
  */
 static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb)
 {
        struct ppe_common_cb *ppe_common_cb = ppe_cb->ppe_common_cb;
        u32 port = ppe_cb->port;
        struct dsaf_device *dsaf_dev = ppe_common_cb->dsaf_dev;
+       int i;
+
+       /* get default RSS key */
+       netdev_rss_key_fill(ppe_cb->rss_key, HNS_PPEV2_RSS_KEY_SIZE);
 
        hns_ppe_srst_by_port(dsaf_dev, port, 0);
        mdelay(10);
@@ -285,8 +326,19 @@ static void hns_ppe_init_hw(struct hns_ppe_cb *ppe_cb)
                hns_ppe_set_port_mode(ppe_cb, PPE_MODE_GE);
        else
                hns_ppe_set_port_mode(ppe_cb, PPE_MODE_XGE);
+
        hns_ppe_checksum_hw(ppe_cb, 0xffffffff);
        hns_ppe_cnt_clr_ce(ppe_cb);
+
+       if (!AE_IS_VER1(dsaf_dev->dsaf_ver)) {
+               /* set default RSS key in h/w */
+               hns_ppe_set_rss_key(ppe_cb, ppe_cb->rss_key);
+
+               /* Set default indrection table in h/w */
+               for (i = 0; i < HNS_PPEV2_RSS_IND_TBL_SIZE; i++)
+                       ppe_cb->rss_indir_table[i] = i;
+               hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
+       }
 }
 
 /**
index 4894f9a0d39ff576c7e3a87af10f5ec5867db1d3..dac853255ecaef2e0cb3f3d89df053c4dd2b9313 100644 (file)
 
 #define ETH_PPE_DUMP_NUM 576
 #define ETH_PPE_STATIC_NUM 12
+
+#define HNS_PPEV2_RSS_IND_TBL_SIZE 256
+#define HNS_PPEV2_RSS_KEY_SIZE 40 /* in bytes or 320 bits */
+#define HNS_PPEV2_RSS_KEY_NUM (HNS_PPEV2_RSS_KEY_SIZE / sizeof(u32))
+
 enum ppe_qid_mode {
-       PPE_QID_MODE0 = 0,      /* fixed queue id mode */
-       PPE_QID_MODE1,          /* switch:128VM non switch:6Port/4VM/4TC */
-       PPE_QID_MODE2,          /* switch:32VM/4TC non switch:6Port/16VM */
-       PPE_QID_MODE3,          /* switch:4TC/8TAG non switch:2Port/64VM */
-       PPE_QID_MODE4,          /* switch:8VM/16TAG non switch:2Port/16VM/4TC */
-       PPE_QID_MODE5,          /* non switch:6Port/16TAG */
-       PPE_QID_MODE6,          /* non switch:6Port/2VM/8TC */
-       PPE_QID_MODE7,          /* non switch:2Port/8VM/8TC */
+       PPE_QID_MODE0 = 0, /* fixed queue id mode */
+       PPE_QID_MODE1,     /* switch:128VM non switch:6Port/4VM/4TC */
+       PPE_QID_MODE2,     /* switch:32VM/4TC non switch:6Port/16VM */
+       PPE_QID_MODE3,     /* switch:4TC/8RSS non switch:2Port/64VM */
+       PPE_QID_MODE4,     /* switch:8VM/16RSS non switch:2Port/16VM/4TC */
+       PPE_QID_MODE5,     /* switch:16VM/8TC non switch:6Port/16RSS */
+       PPE_QID_MODE6,     /* switch:32VM/4RSS non switch:6Port/2VM/8TC */
+       PPE_QID_MODE7,     /* switch:32RSS non switch:2Port/8VM/8TC */
+       PPE_QID_MODE8,     /* switch:6VM/4TC/4RSS non switch:2Port/16VM/4RSS */
+       PPE_QID_MODE9,     /* non switch:2Port/32VM/2RSS */
+       PPE_QID_MODE10,    /* non switch:2Port/32RSS */
+       PPE_QID_MODE11,    /* non switch:2Port/4TC/16RSS */
 };
 
 enum ppe_port_mode {
@@ -72,6 +81,8 @@ struct hns_ppe_cb {
        u8 port;                         /* port id in dsaf  */
        void __iomem *io_base;
        int virq;
+       u32 rss_indir_table[HNS_PPEV2_RSS_IND_TBL_SIZE]; /*shadow indir tab */
+       u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]; /* rss hash key */
 };
 
 struct ppe_common_cb {
@@ -102,4 +113,9 @@ void hns_ppe_get_regs(struct hns_ppe_cb *ppe_cb, void *data);
 
 void hns_ppe_get_strings(struct hns_ppe_cb *ppe_cb, int stringset, u8 *data);
 void hns_ppe_get_stats(struct hns_ppe_cb *ppe_cb, u64 *data);
+
+void hns_ppe_set_rss_key(struct hns_ppe_cb *ppe_cb,
+                        const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM]);
+void hns_ppe_set_indir_table(struct hns_ppe_cb *ppe_cb,
+                            const u32 rss_tab[HNS_PPEV2_RSS_IND_TBL_SIZE]);
 #endif /* _HNS_DSAF_PPE_H */
index cb0e9e1ecf2c0223b0a2bba4e49a32ab9bbfef44..b070d57b204d5e5d26d5209b9053b8669688de16 100644 (file)
 #define PPE_ECO0_REG                           0x32C
 #define PPE_ECO1_REG                           0x330
 #define PPE_ECO2_REG                           0x334
+#define PPEV2_INDRECTION_TBL_REG               0x800
+#define PPEV2_RSS_KEY_REG                      0x900
 
 #define RCB_COM_CFG_ENDIAN_REG                 0x0
 #define RCB_COM_CFG_SYS_FSH_REG                        0xC
 #define PPE_CFG_QID_MODE_CF_QID_MODE_S 8
 #define PPE_CFG_QID_MODE_CF_QID_MODE_M (0x7 << PPE_CFG_QID_MODE_CF_QID_MODE_S)
 
+#define PPEV2_CFG_RSS_TBL_4N0_S        0
+#define PPEV2_CFG_RSS_TBL_4N0_M        (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N0_S)
+
+#define PPEV2_CFG_RSS_TBL_4N1_S        8
+#define PPEV2_CFG_RSS_TBL_4N1_M        (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N1_S)
+
+#define PPEV2_CFG_RSS_TBL_4N2_S        16
+#define PPEV2_CFG_RSS_TBL_4N2_M        (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N2_S)
+
+#define PPEV2_CFG_RSS_TBL_4N3_S        24
+#define PPEV2_CFG_RSS_TBL_4N3_M        (((1UL << 5) - 1) << PPEV2_CFG_RSS_TBL_4N3_S)
+
 #define PPE_CNT_CLR_CE_B       0
 #define PPE_CNT_CLR_SNAP_EN_B  1
 
index 9df63ae139154848c600081d3d9f4f6f0eb97641..3b234176dd36bdb2eb78a673abca5075a47f8d53 100644 (file)
@@ -1187,6 +1187,95 @@ static int hns_nic_nway_reset(struct net_device *netdev)
        return ret;
 }
 
+static u32
+hns_get_rss_key_size(struct net_device *netdev)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_ae_ops *ops;
+       u32 ret;
+
+       if (AE_IS_VER1(priv->enet_ver)) {
+               netdev_err(netdev,
+                          "RSS feature is not supported on this hardware\n");
+               return -EOPNOTSUPP;
+       }
+
+       ops = priv->ae_handle->dev->ops;
+       ret = ops->get_rss_key_size(priv->ae_handle);
+
+       return ret;
+}
+
+static u32
+hns_get_rss_indir_size(struct net_device *netdev)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_ae_ops *ops;
+       u32 ret;
+
+       if (AE_IS_VER1(priv->enet_ver)) {
+               netdev_err(netdev,
+                          "RSS feature is not supported on this hardware\n");
+               return -EOPNOTSUPP;
+       }
+
+       ops = priv->ae_handle->dev->ops;
+       ret = ops->get_rss_indir_size(priv->ae_handle);
+
+       return ret;
+}
+
+static int
+hns_get_rss(struct net_device *netdev, u32 *indir, u8 *key, u8 *hfunc)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_ae_ops *ops;
+       int ret;
+
+       if (AE_IS_VER1(priv->enet_ver)) {
+               netdev_err(netdev,
+                          "RSS feature is not supported on this hardware\n");
+               return -EOPNOTSUPP;
+       }
+
+       ops = priv->ae_handle->dev->ops;
+
+       if (!indir)
+               return 0;
+
+       ret = ops->get_rss(priv->ae_handle, indir, key, hfunc);
+
+       return 0;
+}
+
+static int
+hns_set_rss(struct net_device *netdev, const u32 *indir, const u8 *key,
+           const u8 hfunc)
+{
+       struct hns_nic_priv *priv = netdev_priv(netdev);
+       struct hnae_ae_ops *ops;
+       int ret;
+
+       if (AE_IS_VER1(priv->enet_ver)) {
+               netdev_err(netdev,
+                          "RSS feature is not supported on this hardware\n");
+               return -EOPNOTSUPP;
+       }
+
+       ops = priv->ae_handle->dev->ops;
+
+       /* currently hfunc can only be Toeplitz hash */
+       if (key ||
+           (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP))
+               return -EOPNOTSUPP;
+       if (!indir)
+               return 0;
+
+       ret = ops->set_rss(priv->ae_handle, indir, key, hfunc);
+
+       return 0;
+}
+
 static struct ethtool_ops hns_ethtool_ops = {
        .get_drvinfo = hns_nic_get_drvinfo,
        .get_link  = hns_nic_get_link,
@@ -1206,6 +1295,10 @@ static struct ethtool_ops hns_ethtool_ops = {
        .get_regs_len = hns_get_regs_len,
        .get_regs = hns_get_regs,
        .nway_reset = hns_nic_nway_reset,
+       .get_rxfh_key_size = hns_get_rss_key_size,
+       .get_rxfh_indir_size = hns_get_rss_indir_size,
+       .get_rxfh = hns_get_rss,
+       .set_rxfh = hns_set_rss,
 };
 
 void hns_ethtool_set_ops(struct net_device *ndev)