Subject: [PATCH net-next 2/4] net:hns: Add Hip06 "RSS(Receive Side Scaling)" support to HNS Driver

From: [email-masked] (Yisen.Zhuang(Zhuangyuzeng))
Date: Thu, 22 Oct 2015 10:01:07 +0800

Please check my comment below. thanks.

Yisen

On 2015/10/20 23:01, Salil wrote:
> From: Salil Mehta <[email-masked]>
> 
> 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 <[email-masked]>
> ---
>  drivers/net/ethernet/hisilicon/hns/hnae.h         |    6 ++
>  drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c |   81 ++++++++++++++++++++-
>  drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c |   74 ++++++++++++++++++-
>  drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h |   33 +++++++--
>  drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h |   14 ++++
>  drivers/net/ethernet/hisilicon/hns/hns_ethtool.c  |   69 ++++++++++++++++++
>  6 files changed, 267 insertions(+), 10 deletions(-)
> 
> diff --git a/drivers/net/ethernet/hisilicon/hns/hnae.h b/drivers/net/ethernet/hisilicon/hns/hnae.h
> index 70a662c..3edcade 100644
> --- a/drivers/net/ethernet/hisilicon/hns/hnae.h
> +++ b/drivers/net/ethernet/hisilicon/hns/hnae.h
> @@ -483,6 +483,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 {
> diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
> index c3d64ce..791c289 100644
> --- a/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
> +++ b/drivers/net/ethernet/hisilicon/hns/hns_ae_adapt.c
> @@ -748,6 +748,81 @@ 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)
> +{
> +	struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
> +
> +	if (!hns_ppe_is_rss_supported(ppe_cb)) {
> +		pr_err("RSS feature is not supported on this hardware\n");
> +		return 0;

use dev_err rather than pr_err.

> +	}
> +
> +	return HNS_PPEV2_RSS_KEY_SIZE;
> +}
> +
> +static u32 hns_ae_get_rss_indir_size(struct hnae_handle *handle)
> +{
> +	struct hns_ppe_cb *ppe_cb = hns_get_ppe_cb(handle);
> +
> +	if (!hns_ppe_is_rss_supported(ppe_cb)) {
> +		pr_err("RSS feature is not supported on this hardware\n");
> +		return 0;

use dev_err rather than pr_err.

> +	}
> +
> +	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);
> +	u32 i;
> +
> +	if (!hns_ppe_is_rss_supported(ppe_cb)) {
> +		pr_err("RSS feature is not supported on this hardware\n");
> +		return -EOPNOTSUPP;

use dev_err rather than pr_err.

> +	}
> +
> +	/* 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 */
> +	for (i = 0; i < HNS_PPEV2_RSS_IND_TBL_SIZE; i++)
> +		indir[i] = ppe_cb->rss_indir_table[i];
> +
> +	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);
> +	u32 i;
> +
> +	if (!hns_ppe_is_rss_supported(ppe_cb)) {
> +		pr_err("RSS feature is not supported on this hardware\n");
> +		return -EOPNOTSUPP;

use dev_err rather than pr_err.

> +	}
> +
> +	/* 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 */
> +	for (i = 0; i < HNS_PPEV2_RSS_IND_TBL_SIZE; i++)
> +		ppe_cb->rss_indir_table[i] = i;

Moving this into hns_ppe_set_indir_table will be better.

> +
> +	/* 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,
> @@ -782,7 +857,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)
> diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
> index 9531992..adaece3 100644
> --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
> +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.c
> @@ -19,6 +19,56 @@
>  
>  #include "hns_dsaf_ppe.h"
>  
> +int hns_ppe_is_rss_supported(struct hns_ppe_cb *ppe_cb)

return bool.

> +{
> +	struct ppe_common_cb *ppe_common_cb = ppe_cb->ppe_common_cb;
> +	struct dsaf_device *dsaf_dev = ppe_common_cb->dsaf_dev;
> +	int ret = 0;
> +
> +	/* Not all versions support RSS feature */
> +	if (!AE_IS_VER1(dsaf_dev->dsaf_ver))
> +		ret = 1;
> +
> +	return ret;
> +}
> +
> +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]);

also need modify ppe_cb->rss_key.

> +}
> +
> +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 +316,26 @@ 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;
> +	/* Set default RSS key and indrection table*/
> +	const u32 rss_key[HNS_PPEV2_RSS_KEY_NUM] = {
> +		0x6d5a56da, 0x255b0ec2,
> +		0x4167253d, 0x43a38fb0,
> +		0xd0ca2bcb, 0xae7b30b4,
> +		0x77cb2da3, 0x8030f20c,
> +		0x6a42b73b, 0xbeac01fa,
> +	};
> +
> +	/* set default RSS key and remember it */
> +	for (i = 0; i < HNS_PPEV2_RSS_KEY_NUM; i++)
> +		ppe_cb->rss_key[i]  = rss_key[i];

Moving this into hns_ppe_set_rss_key.

>  
>  	hns_ppe_srst_by_port(dsaf_dev, port, 0);
>  	mdelay(10);
> @@ -285,8 +348,17 @@ 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)) {
> +		hns_ppe_set_rss_key(ppe_cb, rss_key);
> +
> +		for (i = 0; i < HNS_PPEV2_RSS_IND_TBL_SIZE; i++)
> +			ppe_cb->rss_indir_table[i] = i;

Moving this into hns_ppe_set_indir_table.

> +		hns_ppe_set_indir_table(ppe_cb, ppe_cb->rss_indir_table);
> +	}
>  }
>  
>  /**
> diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
> index 4894f9a..6d3545d 100644
> --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
> +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_ppe.h
> @@ -25,15 +25,24 @@
>  
>  #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,10 @@ 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]);
> +int hns_ppe_is_rss_supported(struct hns_ppe_cb *ppe_cb);
>  #endif /* _HNS_DSAF_PPE_H */
> diff --git a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
> index 8b1ad00..a5ebcc6 100644
> --- a/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
> +++ b/drivers/net/ethernet/hisilicon/hns/hns_dsaf_reg.h
> @@ -348,6 +348,8 @@
>  #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
> @@ -834,6 +836,18 @@
>  #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
>  
> diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
> index 9df63ae..b379810 100644
> --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
> +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c
> @@ -1187,6 +1187,71 @@ 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;
> +
> +	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;
> +
> +	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;
> +
> +	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;
> +
> +	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 +1271,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)
>