diff mbox

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

Message ID 1445353278-130498-3-git-send-email-salil.mehta@huawei.com
State New
Headers show

Commit Message

Salil Mehta Oct. 20, 2015, 3:01 p.m. UTC
From: Salil Mehta <salil.mehta@huawei.com>

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>
---
 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(-)

Comments

Yisen Zhuang Oct. 22, 2015, 2:01 a.m. UTC | #1
Please check my comment below. thanks.

Yisen

On 2015/10/20 23:01, Salil wrote:
> From: Salil Mehta <salil.mehta@huawei.com>
> 
> 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>
> ---
>  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)
> 

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

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;
+	}
+
+	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;
+	}
+
+	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;
+	}
+
+	/* 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;
+	}
+
+	/* 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;
+
+	/* 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)
+{
+	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]);
+}
+
+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];
 
 	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;
+		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)