From: Renyong Wan <wa...@3snic.com>

Signed-off-by: Steven Song <steven.s...@3snic.com>
Signed-off-by: Renyong Wan <wa...@3snic.com>
---
v2:
* Removed error.h from including files.
---
 doc/guides/nics/features/sssnic.ini    |   4 +
 drivers/net/sssnic/base/sssnic_api.c   | 338 ++++++++++++++++++++++
 drivers/net/sssnic/base/sssnic_api.h   |  36 +++
 drivers/net/sssnic/base/sssnic_cmd.h   |  58 ++++
 drivers/net/sssnic/meson.build         |   1 +
 drivers/net/sssnic/sssnic_ethdev.c     |  16 ++
 drivers/net/sssnic/sssnic_ethdev.h     |   2 +
 drivers/net/sssnic/sssnic_ethdev_rss.c | 377 +++++++++++++++++++++++++
 drivers/net/sssnic/sssnic_ethdev_rss.h |  20 ++
 drivers/net/sssnic/sssnic_ethdev_rx.c  |  13 +
 10 files changed, 865 insertions(+)
 create mode 100644 drivers/net/sssnic/sssnic_ethdev_rss.c
 create mode 100644 drivers/net/sssnic/sssnic_ethdev_rss.h

diff --git a/doc/guides/nics/features/sssnic.ini 
b/doc/guides/nics/features/sssnic.ini
index 7e6b70684a..020a9e7056 100644
--- a/doc/guides/nics/features/sssnic.ini
+++ b/doc/guides/nics/features/sssnic.ini
@@ -15,6 +15,10 @@ Promiscuous mode     = Y
 Allmulticast mode    = Y
 Unicast MAC filter   = Y
 Multicast MAC filter = Y
+RSS hash             = Y
+RSS key update       = Y
+RSS reta update      = Y
+Inner RSS            = Y
 L3 checksum offload  = Y
 L4 checksum offload  = Y
 Inner L3 checksum    = Y
diff --git a/drivers/net/sssnic/base/sssnic_api.c 
b/drivers/net/sssnic/base/sssnic_api.c
index 9f063112f2..32b24e841c 100644
--- a/drivers/net/sssnic/base/sssnic_api.c
+++ b/drivers/net/sssnic/base/sssnic_api.c
@@ -1159,3 +1159,341 @@ sssnic_mac_stats_clear(struct sssnic_hw *hw)
 
        return 0;
 }
+
+int
+sssnic_rss_enable_set(struct sssnic_hw *hw, bool state)
+{
+       int ret;
+       struct sssnic_rss_enable_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.state = state ? 1 : 0;
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       cmd_len = sizeof(cmd);
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, SSSNIC_ENABLE_RSS_CMD,
+               SSSNIC_MPU_FUNC_IDX, SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               PMD_DRV_LOG(ERR,
+                       "Bad response to SSSNIC_ENABLE_RSS_CMD, len=%u, 
status=%u",
+                       cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int
+sssnic_rss_profile_config(struct sssnic_hw *hw, bool new)
+{
+       int ret;
+       struct sssnic_rss_profile_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.opcode = new ? SSSNIC_RSS_PROFILE_CMD_OP_NEW :
+                                SSSNIC_RSS_PROFILE_CMD_OP_DEL;
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       cmd_len = sizeof(cmd);
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, SSSNIC_RSS_PROFILE_CMD,
+               SSSNIC_MPU_FUNC_IDX, SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               PMD_DRV_LOG(ERR,
+                       "Bad response to SSSNIC_RSS_PROFILE_CMD, len=%u, 
status=%u",
+                       cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int
+sssnic_rss_profile_create(struct sssnic_hw *hw)
+{
+       return sssnic_rss_profile_config(hw, true);
+}
+
+int
+sssnic_rss_profile_destroy(struct sssnic_hw *hw)
+{
+       return sssnic_rss_profile_config(hw, false);
+}
+
+int
+sssnic_rss_hash_key_set(struct sssnic_hw *hw, uint8_t *key, uint16_t len)
+{
+       int ret;
+       struct sssnic_rss_hash_key_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+
+       if (len > sizeof(cmd.key)) {
+               PMD_DRV_LOG(ERR, "Invalid rss hash key length: %u", len);
+               return -EINVAL;
+       }
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.opcode = SSSNIC_CMD_OPCODE_SET;
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       rte_memcpy(cmd.key, key, len);
+       cmd_len = sizeof(cmd);
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, SSSNIC_RSS_HASH_KEY_CMD,
+               SSSNIC_MPU_FUNC_IDX, SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               PMD_DRV_LOG(ERR,
+                       "Bad response to SSSNIC_RSS_PROFILE_CMD, len=%u, 
status=%u",
+                       cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int
+sssnic_rss_type_set_by_mbox(struct sssnic_hw *hw, struct sssnic_rss_type *type)
+{
+       int ret;
+       struct sssnic_rss_type_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       cmd.mask = type->mask;
+       cmd_len = sizeof(cmd);
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, SSSNIC_SET_RSS_TYPE_CMD,
+               SSSNIC_MPU_FUNC_IDX, SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd.common.status == 0xff)
+               return -EOPNOTSUPP;
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               PMD_DRV_LOG(ERR,
+                       "Bad response to SSSNIC_SET_RSS_TYPE_CMD, len=%u, 
status=%u",
+                       cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int
+sssnic_rss_type_set_by_ctrlq(struct sssnic_hw *hw, struct sssnic_rss_type 
*type)
+{
+       struct sssnic_ctrlq_cmd cmd;
+       struct sssnic_rss_hash_type_ctrlq_cmd data;
+       int ret;
+
+       memset(&data, 0, sizeof(data));
+       data.mask = rte_cpu_to_be_32(type->mask);
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.data = &data;
+       cmd.module = SSSNIC_LAN_MODULE;
+       cmd.data_len = sizeof(data);
+       cmd.cmd = SSSNIC_SET_RSS_KEY_CTRLQ_CMD;
+
+       ret = sssnic_ctrlq_cmd_exec(hw, &cmd, 0);
+       if (ret || cmd.result) {
+               PMD_DRV_LOG(ERR,
+                       "Failed to execulte ctrlq command %s, ret=%d, result=%" 
PRIu64,
+                       "SSSNIC_SET_RSS_KEY_CTRLQ_CMD", ret, cmd.result);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int
+sssnic_rss_type_set(struct sssnic_hw *hw, struct sssnic_rss_type *type)
+{
+       int ret;
+
+       ret = sssnic_rss_type_set_by_mbox(hw, type);
+       if (ret == -EOPNOTSUPP)
+               ret = sssnic_rss_type_set_by_ctrlq(hw, type);
+
+       return ret;
+}
+
+int
+sssnic_rss_type_get(struct sssnic_hw *hw, struct sssnic_rss_type *type)
+{
+       int ret;
+       struct sssnic_rss_type_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       cmd_len = sizeof(cmd);
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, SSSNIC_GET_RSS_TYPE_CMD,
+               SSSNIC_MPU_FUNC_IDX, SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               PMD_DRV_LOG(ERR,
+                       "Bad response to SSSNIC_GET_RSS_TYPE_CMD, len=%u, 
status=%u",
+                       cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       type->mask = cmd.mask;
+
+       return 0;
+}
+
+int
+sssnic_rss_hash_engine_set(struct sssnic_hw *hw,
+       enum sssnic_rss_hash_engine_type engine)
+{
+       int ret;
+       struct sssnic_rss_hash_engine_cmd cmd;
+       struct sssnic_msg msg;
+       uint32_t cmd_len;
+
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.function = SSSNIC_FUNC_IDX(hw);
+       cmd.engine = engine;
+       cmd.opcode = SSSNIC_CMD_OPCODE_SET;
+       cmd_len = sizeof(cmd);
+
+       sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len,
+               SSSNIC_RSS_HASH_ENGINE_CMD, SSSNIC_MPU_FUNC_IDX,
+               SSSNIC_LAN_MODULE, SSSNIC_MSG_TYPE_REQ);
+
+       ret = sssnic_mbox_send(hw, &msg, (uint8_t *)&cmd, &cmd_len, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to send mbox message, ret=%d", ret);
+               return ret;
+       }
+
+       if (cmd_len == 0 || cmd.common.status != 0) {
+               PMD_DRV_LOG(ERR,
+                       "Bad response to SSSNIC_RSS_HASH_ENGINE_CMD, len=%u, 
status=%u",
+                       cmd_len, cmd.common.status);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int
+sssnic_rss_indir_table_set(struct sssnic_hw *hw, const uint16_t *entry,
+       uint32_t num_entries)
+{
+       struct sssnic_ctrlq_cmd *cmd;
+       struct sssnic_rss_indir_table_cmd *data;
+       uint32_t i;
+       int ret;
+
+       cmd = sssnic_ctrlq_cmd_alloc(hw);
+       if (cmd == NULL) {
+               PMD_DRV_LOG(ERR, "Failed to alloc ctrlq command");
+               return -ENOMEM;
+       }
+
+       data = cmd->data;
+       memset(data, 0, sizeof(struct sssnic_rss_indir_table_cmd));
+       for (i = 0; i < num_entries; i++)
+               data->entry[i] = entry[i];
+
+       rte_wmb();
+
+       sssnic_mem_cpu_to_be_32(data->entry, data->entry, sizeof(data->entry));
+
+       cmd->data_len = sizeof(struct sssnic_rss_indir_table_cmd);
+       cmd->module = SSSNIC_LAN_MODULE;
+       cmd->cmd = SSSNIC_SET_RSS_INDIR_TABLE_CMD;
+
+       ret = sssnic_ctrlq_cmd_exec(hw, cmd, 0);
+       if (ret != 0 || cmd->result != 0) {
+               PMD_DRV_LOG(ERR,
+                       "Failed to execulte ctrlq command %s, ret=%d, result=%" 
PRIu64,
+                       "SSSNIC_SET_RSS_INDIR_TABLE_CMD", ret, cmd->result);
+               ret = -EIO;
+       }
+
+       sssnic_ctrlq_cmd_destroy(hw, cmd);
+
+       return ret;
+}
+
+int
+sssnic_rss_indir_table_get(struct sssnic_hw *hw, uint16_t *entry,
+       uint32_t num_entries)
+{
+       struct sssnic_ctrlq_cmd *cmd;
+       struct sssnic_rss_indir_table_cmd *data;
+       uint32_t i;
+       int ret = 0;
+
+       cmd = sssnic_ctrlq_cmd_alloc(hw);
+       if (cmd == NULL) {
+               PMD_DRV_LOG(ERR, "Failed to alloc ctrlq command");
+               return -ENOMEM;
+       }
+
+       data = cmd->data;
+       memset(data, 0, sizeof(struct sssnic_rss_indir_table_cmd));
+       cmd->data_len = sizeof(struct sssnic_rss_indir_table_cmd);
+       cmd->module = SSSNIC_LAN_MODULE;
+       cmd->cmd = SSSNIC_GET_RSS_INDIR_TABLE_CMD;
+       cmd->response_len = sizeof(data->entry);
+       cmd->response_data = data->entry;
+
+       ret = sssnic_ctrlq_cmd_exec(hw, cmd, 0);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR,
+                       "Failed to execulte ctrlq command %s, ret=%d, result=%" 
PRIu64,
+                       "SSSNIC_GET_RSS_INDIR_TABLE_CMD", ret, cmd->result);
+               ret = -EIO;
+               goto out;
+       }
+
+       for (i = 0; i < num_entries; i++)
+               entry[i] = data->entry[i];
+
+out:
+       sssnic_ctrlq_cmd_destroy(hw, cmd);
+       return ret;
+}
diff --git a/drivers/net/sssnic/base/sssnic_api.h 
b/drivers/net/sssnic/base/sssnic_api.h
index c2f4f90209..1d80b93e38 100644
--- a/drivers/net/sssnic/base/sssnic_api.h
+++ b/drivers/net/sssnic/base/sssnic_api.h
@@ -378,6 +378,30 @@ struct sssnic_mac_stats {
        uint64_t rx_unfilter_pkts;
 };
 
+struct sssnic_rss_type {
+       union {
+               uint32_t mask;
+               struct {
+                       uint32_t resvd : 23;
+                       uint32_t valid : 1;
+                       uint32_t ipv6_tcp_ex : 1;
+                       uint32_t ipv6_ex : 1;
+                       uint32_t ipv6_tcp : 1;
+                       uint32_t ipv6 : 1;
+                       uint32_t ipv4_tcp : 1;
+                       uint32_t ipv4 : 1;
+                       uint32_t ipv6_udp : 1;
+                       uint32_t ipv4_udp : 1;
+               };
+       };
+};
+
+enum sssnic_rss_hash_engine_type {
+       SSSNIC_RSS_HASH_ENGINE_XOR,
+       SSSNIC_RSS_HASH_ENGINE_TOEP,
+       SSSNIC_RSS_HASH_ENGINE_COUNT,
+};
+
 int sssnic_msix_attr_get(struct sssnic_hw *hw, uint16_t msix_idx,
        struct sssnic_msix_attr *attr);
 int sssnic_msix_attr_set(struct sssnic_hw *hw, uint16_t msix_idx,
@@ -420,5 +444,17 @@ int sssnic_port_stats_get(struct sssnic_hw *hw,
 int sssnic_port_stats_clear(struct sssnic_hw *hw);
 int sssnic_mac_stats_get(struct sssnic_hw *hw, struct sssnic_mac_stats *stats);
 int sssnic_mac_stats_clear(struct sssnic_hw *hw);
+int sssnic_rss_enable_set(struct sssnic_hw *hw, bool state);
+int sssnic_rss_profile_create(struct sssnic_hw *hw);
+int sssnic_rss_profile_destroy(struct sssnic_hw *hw);
+int sssnic_rss_hash_key_set(struct sssnic_hw *hw, uint8_t *key, uint16_t len);
+int sssnic_rss_type_set(struct sssnic_hw *hw, struct sssnic_rss_type *type);
+int sssnic_rss_type_get(struct sssnic_hw *hw, struct sssnic_rss_type *type);
+int sssnic_rss_hash_engine_set(struct sssnic_hw *hw,
+       enum sssnic_rss_hash_engine_type engine);
+int sssnic_rss_indir_table_set(struct sssnic_hw *hw, const uint16_t *entry,
+       uint32_t num_entries);
+int sssnic_rss_indir_table_get(struct sssnic_hw *hw, uint16_t *entry,
+       uint32_t num_entries);
 
 #endif /* _SSSNIC_API_H_ */
diff --git a/drivers/net/sssnic/base/sssnic_cmd.h 
b/drivers/net/sssnic/base/sssnic_cmd.h
index bc7303ff57..56818471b6 100644
--- a/drivers/net/sssnic/base/sssnic_cmd.h
+++ b/drivers/net/sssnic/base/sssnic_cmd.h
@@ -66,6 +66,15 @@ enum sssnic_ctrlq_cmd_id {
        SSSNIC_FLUSH_RXQ_CMD = 10,
 };
 
+enum sssnic_rss_cmd_id {
+       SSSNIC_ENABLE_RSS_CMD = 60,
+       SSSNIC_RSS_PROFILE_CMD = 61,
+       SSSNIC_GET_RSS_TYPE_CMD = 62,
+       SSSNIC_RSS_HASH_KEY_CMD = 63,
+       SSSNIC_RSS_HASH_ENGINE_CMD = 64,
+       SSSNIC_SET_RSS_TYPE_CMD = 65,
+};
+
 struct sssnic_cmd_common {
        uint8_t status;
        uint8_t version;
@@ -348,4 +357,53 @@ struct sssnic_mac_stats_cmd {
        uint8_t resvd[3];
 };
 
+struct sssnic_rss_enable_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint8_t state;
+       uint8_t resvd[13];
+};
+
+#define SSSNIC_RSS_PROFILE_CMD_OP_NEW 1 /* Allocate RSS profile */
+#define SSSNIC_RSS_PROFILE_CMD_OP_DEL 2 /* Delete RSS profile */
+struct sssnic_rss_profile_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint8_t opcode; /* see SSSNIC_RSS_PROFILE_CMD_OP_xx */
+       uint8_t profile;
+       uint32_t resvd[4];
+};
+
+struct sssnic_rss_hash_key_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint8_t opcode;
+       uint8_t resvd;
+       uint8_t key[40];
+};
+
+struct sssnic_rss_type_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint16_t resvd0;
+       uint32_t mask; /* mask of types */
+};
+
+struct sssnic_rss_hash_type_ctrlq_cmd {
+       uint32_t resvd[4];
+       uint32_t mask;
+};
+struct sssnic_rss_hash_engine_cmd {
+       struct sssnic_cmd_common common;
+       uint16_t function;
+       uint8_t opcode;
+       uint8_t engine;
+       uint8_t resvd[4];
+};
+
+struct sssnic_rss_indir_table_cmd {
+       uint32_t resvd[4];
+       uint16_t entry[256];
+};
+
 #endif /* _SSSNIC_CMD_H_ */
diff --git a/drivers/net/sssnic/meson.build b/drivers/net/sssnic/meson.build
index dea24f4b06..3541b75c30 100644
--- a/drivers/net/sssnic/meson.build
+++ b/drivers/net/sssnic/meson.build
@@ -22,4 +22,5 @@ sources = files(
         'sssnic_ethdev_rx.c',
         'sssnic_ethdev_tx.c',
         'sssnic_ethdev_stats.c',
+        'sssnic_ethdev_rss.c',
 )
diff --git a/drivers/net/sssnic/sssnic_ethdev.c 
b/drivers/net/sssnic/sssnic_ethdev.c
index 328fb85d30..a00e96bebe 100644
--- a/drivers/net/sssnic/sssnic_ethdev.c
+++ b/drivers/net/sssnic/sssnic_ethdev.c
@@ -13,6 +13,7 @@
 #include "sssnic_ethdev_rx.h"
 #include "sssnic_ethdev_tx.h"
 #include "sssnic_ethdev_stats.h"
+#include "sssnic_ethdev_rss.h"
 
 static int sssnic_ethdev_init(struct rte_eth_dev *ethdev);
 
@@ -542,6 +543,13 @@ sssnic_ethdev_start(struct rte_eth_dev *ethdev)
                goto rx_mode_reset;
        }
 
+       /* setup RSS */
+       ret = sssnic_ethdev_rss_setup(ethdev);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to setup RSS");
+               goto rx_mode_reset;
+       }
+
        /* start all rx queues */
        ret = sssnic_ethdev_rx_queue_all_start(ethdev);
        if (ret != 0) {
@@ -572,6 +580,7 @@ sssnic_ethdev_start(struct rte_eth_dev *ethdev)
 clean_port_res:
        sssnic_ethdev_resource_clean(ethdev);
 rx_mode_reset:
+       sssnic_ethdev_rss_shutdown(ethdev);
        sssnic_ethdev_rx_mode_set(ethdev, SSSNIC_ETHDEV_RX_MODE_NONE);
 rxtx_ctx_clean:
        sssnic_ethdev_rxtx_ctx_clean(ethdev);
@@ -614,6 +623,9 @@ sssnic_ethdev_stop(struct rte_eth_dev *ethdev)
        /* shut down rx queue interrupt */
        sssnic_ethdev_rx_intr_shutdown(ethdev);
 
+       /* Disable RSS */
+       sssnic_ethdev_rss_shutdown(ethdev);
+
        /* clean rxtx context */
        sssnic_ethdev_rxtx_ctx_clean(ethdev);
 
@@ -754,6 +766,10 @@ static const struct eth_dev_ops sssnic_ethdev_ops = {
        .xstats_get_names = sssnic_ethdev_xstats_get_names,
        .xstats_get = sssnic_ethdev_xstats_get,
        .xstats_reset = sssnic_ethdev_xstats_reset,
+       .rss_hash_conf_get = sssnic_ethdev_rss_hash_config_get,
+       .rss_hash_update = sssnic_ethdev_rss_hash_update,
+       .reta_update = sssnic_ethdev_rss_reta_update,
+       .reta_query = sssnic_ethdev_rss_reta_query,
 };
 
 static int
diff --git a/drivers/net/sssnic/sssnic_ethdev.h 
b/drivers/net/sssnic/sssnic_ethdev.h
index 1f1e991780..f19b2bd88f 100644
--- a/drivers/net/sssnic/sssnic_ethdev.h
+++ b/drivers/net/sssnic/sssnic_ethdev.h
@@ -88,6 +88,8 @@ struct sssnic_netdev {
        uint16_t num_started_txqs;
        uint16_t max_rx_size;
        uint32_t rx_mode;
+       uint32_t rss_enable;
+       uint8_t rss_hash_key[SSSNIC_ETHDEV_RSS_KEY_SZ];
 };
 
 #define SSSNIC_ETHDEV_PRIVATE(eth_dev)                                         
\
diff --git a/drivers/net/sssnic/sssnic_ethdev_rss.c 
b/drivers/net/sssnic/sssnic_ethdev_rss.c
new file mode 100644
index 0000000000..690a30d7bc
--- /dev/null
+++ b/drivers/net/sssnic/sssnic_ethdev_rss.c
@@ -0,0 +1,377 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2022 Shenzhen 3SNIC Information Technology Co., Ltd.
+ */
+
+#include <rte_common.h>
+#include <ethdev_pci.h>
+
+#include "sssnic_log.h"
+#include "sssnic_ethdev.h"
+#include "sssnic_ethdev_rx.h"
+#include "sssnic_ethdev_tx.h"
+#include "sssnic_ethdev_stats.h"
+#include "sssnic_ethdev_rss.h"
+#include "base/sssnic_hw.h"
+#include "base/sssnic_api.h"
+
+static uint8_t default_rss_hash_key[SSSNIC_ETHDEV_RSS_KEY_SZ] = { 0x6d, 0x5a,
+       0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2, 0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3,
+       0x8f, 0xb0, 0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4, 0x77, 0xcb,
+       0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c, 0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac,
+       0x01, 0xfa };
+
+#define SSSNIC_ETHDEV_RSS_IPV4                                                 
\
+       (RTE_ETH_RSS_IPV4 | RTE_ETH_RSS_FRAG_IPV4 |                            \
+               RTE_ETH_RSS_NONFRAG_IPV4_OTHER)
+#define SSSNIC_ETHDEV_RSS_IPV6                                                 
\
+       (RTE_ETH_RSS_IPV6 | RTE_ETH_RSS_FRAG_IPV6 |                            \
+               RTE_ETH_RSS_NONFRAG_IPV6_OTHER)
+
+static inline void
+sssnic_ethdev_rss_type_from_rss_hf(struct sssnic_rss_type *rss_type,
+       uint64_t rss_hf)
+{
+       rss_type->mask = 0;
+       rss_type->ipv4 = (rss_hf & SSSNIC_ETHDEV_RSS_IPV4) ? 1 : 0;
+       rss_type->ipv6 = (rss_hf & SSSNIC_ETHDEV_RSS_IPV6) ? 1 : 0;
+       rss_type->ipv6_ex = (rss_hf & RTE_ETH_RSS_IPV6_EX) ? 1 : 0;
+       rss_type->ipv4_tcp = (rss_hf & RTE_ETH_RSS_NONFRAG_IPV4_TCP) ? 1 : 0;
+       rss_type->ipv6_tcp = (rss_hf & RTE_ETH_RSS_NONFRAG_IPV6_TCP) ? 1 : 0;
+       rss_type->ipv6_tcp_ex = (rss_hf & RTE_ETH_RSS_IPV6_TCP_EX) ? 1 : 0;
+       rss_type->ipv4_udp = (rss_hf & RTE_ETH_RSS_NONFRAG_IPV4_UDP) ? 1 : 0;
+       rss_type->ipv6_udp = (rss_hf & RTE_ETH_RSS_NONFRAG_IPV6_UDP) ? 1 : 0;
+}
+
+static inline uint64_t
+sssnic_ethdev_rss_type_to_rss_hf(struct sssnic_rss_type *rss_type)
+{
+       uint64_t rss_hf = 0;
+
+       rss_hf |= (rss_type->ipv4 == 0) ? 0 : SSSNIC_ETHDEV_RSS_IPV4;
+       rss_hf |= (rss_type->ipv6 == 0) ? 0 : SSSNIC_ETHDEV_RSS_IPV6;
+       rss_hf |= (rss_type->ipv6_ex == 0) ? 0 : RTE_ETH_RSS_IPV6_EX;
+       rss_hf |= (rss_type->ipv4_tcp == 0) ? 0 : RTE_ETH_RSS_NONFRAG_IPV4_TCP;
+       rss_hf |= (rss_type->ipv6_tcp == 0) ? 0 : RTE_ETH_RSS_NONFRAG_IPV6_TCP;
+       rss_hf |= (rss_type->ipv6_tcp_ex == 0) ? 0 : RTE_ETH_RSS_IPV6_TCP_EX;
+       rss_hf |= (rss_type->ipv4_udp == 0) ? 0 : RTE_ETH_RSS_NONFRAG_IPV4_UDP;
+       rss_hf |= (rss_type->ipv6_udp == 0) ? 0 : RTE_ETH_RSS_NONFRAG_IPV6_UDP;
+
+       return rss_hf;
+}
+
+int
+sssnic_ethdev_rss_hash_update(struct rte_eth_dev *ethdev,
+       struct rte_eth_rss_conf *rss_conf)
+{
+       struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev);
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       struct sssnic_rss_type rss_type;
+       uint64_t rss_hf;
+       uint8_t *rss_key;
+       uint16_t rss_key_len;
+       int ret;
+
+       rss_key = rss_conf->rss_key;
+       rss_key_len = rss_conf->rss_key_len;
+       if (rss_key == NULL) {
+               rss_key = default_rss_hash_key;
+               rss_key_len = SSSNIC_ETHDEV_RSS_KEY_SZ;
+       } else if (rss_key_len > SSSNIC_ETHDEV_RSS_KEY_SZ) {
+               PMD_DRV_LOG(ERR, "RSS hash key length too long");
+               return -EINVAL;
+       }
+
+       ret = sssnic_rss_hash_key_set(hw, rss_key, rss_key_len);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to set RSS hash key");
+               return ret;
+       }
+
+       rte_memcpy(netdev->rss_hash_key, rss_key, rss_key_len);
+
+       rss_hf = rss_conf->rss_hf;
+
+       if (rss_hf == 0)
+               rss_hf = SSSNIC_ETHDEV_RSS_OFFLOAD_FLOW_TYPES;
+       else
+               rss_hf &= SSSNIC_ETHDEV_RSS_OFFLOAD_FLOW_TYPES;
+
+       sssnic_ethdev_rss_type_from_rss_hf(&rss_type, rss_hf);
+       rss_type.valid = 1;
+       ret = sssnic_rss_type_set(hw, &rss_type);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to set RSS type: %x", rss_type.mask);
+               return ret;
+       }
+
+       return 0;
+}
+
+int
+sssnic_ethdev_rss_hash_config_get(struct rte_eth_dev *ethdev,
+       struct rte_eth_rss_conf *rss_conf)
+{
+       struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev);
+       struct sssnic_hw *hw;
+       struct sssnic_rss_type rss_type;
+       int ret;
+
+       hw = SSSNIC_NETDEV_TO_HW(netdev);
+
+       if (!netdev->rss_enable) {
+               PMD_DRV_LOG(NOTICE, "Port %u RSS is not enabled",
+                       ethdev->data->port_id);
+               rss_conf->rss_hf = 0;
+               return 0;
+       }
+
+       ret = sssnic_rss_type_get(hw, &rss_type);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to get RSS type");
+               return ret;
+       }
+       rss_conf->rss_hf = sssnic_ethdev_rss_type_to_rss_hf(&rss_type);
+
+       if (rss_conf->rss_key != NULL &&
+               rss_conf->rss_key_len >= SSSNIC_ETHDEV_RSS_KEY_SZ) {
+               rte_memcpy(rss_conf->rss_key, netdev->rss_hash_key,
+                       SSSNIC_ETHDEV_RSS_KEY_SZ);
+               rss_conf->rss_key_len = SSSNIC_ETHDEV_RSS_KEY_SZ;
+       }
+
+       return 0;
+}
+
+int
+sssnic_ethdev_rss_reta_update(struct rte_eth_dev *ethdev,
+       struct rte_eth_rss_reta_entry64 *reta_conf, uint16_t reta_size)
+{
+       struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev);
+       struct sssnic_hw *hw;
+       uint16_t *entries;
+       int i, group, idx;
+       int ret;
+
+       if (!netdev->rss_enable) {
+               PMD_DRV_LOG(ERR, "Port %u RSS is not enabled",
+                       ethdev->data->port_id);
+               return -EINVAL;
+       }
+
+       if (reta_size != SSSNIC_ETHDEV_RSS_RETA_SZ) {
+               PMD_DRV_LOG(ERR, "Invalid reta size:%u, expected reta size:%u ",
+                       reta_size, SSSNIC_ETHDEV_RSS_RETA_SZ);
+               return -EINVAL;
+       }
+
+       hw = SSSNIC_NETDEV_TO_HW(netdev);
+
+       entries = rte_zmalloc(NULL,
+               SSSNIC_ETHDEV_RSS_RETA_SZ * sizeof(uint16_t), 0);
+       if (entries == NULL) {
+               PMD_DRV_LOG(ERR, "Could not allocate memory");
+               return -ENOMEM;
+       }
+
+       ret = sssnic_rss_indir_table_get(hw, entries,
+               SSSNIC_ETHDEV_RSS_RETA_SZ);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to get RSS indirect table");
+               goto out;
+       }
+
+       for (i = 0; i < SSSNIC_ETHDEV_RSS_RETA_SZ; i++) {
+               group = i / RTE_ETH_RETA_GROUP_SIZE;
+               idx = i % RTE_ETH_RETA_GROUP_SIZE;
+               if ((reta_conf[group].mask & RTE_BIT64(idx)) != 0)
+                       entries[i] = reta_conf[group].reta[idx];
+       }
+
+       ret = sssnic_rss_indir_table_set(hw, entries,
+               SSSNIC_ETHDEV_RSS_RETA_SZ);
+       if (ret != 0)
+               PMD_DRV_LOG(ERR, "Failed to set RSS indirect table");
+
+out:
+       rte_free(entries);
+       return ret;
+}
+
+int
+sssnic_ethdev_rss_reta_query(struct rte_eth_dev *ethdev,
+       struct rte_eth_rss_reta_entry64 *reta_conf, uint16_t reta_size)
+{
+       struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev);
+       struct sssnic_hw *hw;
+       uint16_t *entries;
+       int i, group, idx;
+       int ret;
+
+       if (!netdev->rss_enable) {
+               PMD_DRV_LOG(ERR, "Port %u RSS is not enabled",
+                       ethdev->data->port_id);
+               return -EINVAL;
+       }
+
+       if (reta_size != SSSNIC_ETHDEV_RSS_RETA_SZ) {
+               PMD_DRV_LOG(ERR, "Invalid reta size:%u, expected reta size:%u ",
+                       reta_size, SSSNIC_ETHDEV_RSS_RETA_SZ);
+               return -EINVAL;
+       }
+
+       hw = SSSNIC_NETDEV_TO_HW(netdev);
+
+       entries = rte_zmalloc(NULL,
+               SSSNIC_ETHDEV_RSS_RETA_SZ * sizeof(uint16_t), 0);
+       if (entries == NULL) {
+               PMD_DRV_LOG(ERR, "Could not allocate memory");
+               return -ENOMEM;
+       }
+
+       ret = sssnic_rss_indir_table_get(hw, entries,
+               SSSNIC_ETHDEV_RSS_RETA_SZ);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to get RSS indirect table");
+               goto out;
+       }
+
+       for (i = 0; i < SSSNIC_ETHDEV_RSS_RETA_SZ; i++) {
+               group = i / RTE_ETH_RETA_GROUP_SIZE;
+               idx = i % RTE_ETH_RETA_GROUP_SIZE;
+               if ((reta_conf[group].mask & RTE_BIT64(idx)) != 0)
+                       reta_conf[group].reta[idx] = entries[i];
+       }
+
+out:
+       rte_free(entries);
+       return ret;
+}
+
+int
+sssnic_ethdev_rss_reta_reset(struct rte_eth_dev *ethdev)
+{
+       struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev);
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       uint16_t *entries;
+       uint16_t nb_rxq;
+       uint8_t rxq_state;
+       uint16_t qid, i = 0;
+       int ret;
+
+       if (!netdev->rss_enable)
+               return 0;
+
+       entries = rte_zmalloc(NULL,
+               SSSNIC_ETHDEV_RSS_RETA_SZ * sizeof(uint16_t), 0);
+       if (entries == NULL) {
+               PMD_DRV_LOG(ERR, "Could not allocate memory");
+               return -ENOMEM;
+       }
+
+       nb_rxq = ethdev->data->nb_rx_queues;
+
+       if (netdev->num_started_rxqs == 0) {
+               while (i < SSSNIC_ETHDEV_RSS_RETA_SZ)
+                       entries[i++] = 0xffff;
+       } else {
+               while (i < SSSNIC_ETHDEV_RSS_RETA_SZ) {
+                       for (qid = 0; qid < nb_rxq; qid++) {
+                               if (i >= SSSNIC_ETHDEV_RSS_RETA_SZ)
+                                       break;
+                               rxq_state = ethdev->data->rx_queue_state[qid];
+                               if (rxq_state == RTE_ETH_QUEUE_STATE_STARTED)
+                                       entries[i++] = qid;
+                       }
+               }
+       }
+
+       ret = sssnic_rss_indir_table_set(hw, entries,
+               SSSNIC_ETHDEV_RSS_RETA_SZ);
+       if (ret != 0)
+               PMD_DRV_LOG(ERR, "Failed to set RSS indirect table");
+
+       rte_free(entries);
+
+       return ret;
+}
+
+int
+sssnic_ethdev_rss_setup(struct rte_eth_dev *ethdev)
+{
+       struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev);
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       struct rte_eth_conf *dev_conf = &ethdev->data->dev_conf;
+       struct rte_eth_rss_conf *rss_conf;
+       int ret;
+
+       if (!((dev_conf->rxmode.offloads & RTE_ETH_RX_OFFLOAD_RSS_HASH) &&
+                   ethdev->data->nb_rx_queues > 1)) {
+               PMD_DRV_LOG(INFO, "RSS is not enabled");
+               return 0;
+       }
+
+       if (netdev->rss_enable)
+               return 0;
+
+       ret = sssnic_rss_profile_create(hw);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to create RSS profile");
+               return ret;
+       }
+
+       rss_conf = &dev_conf->rx_adv_conf.rss_conf;
+       ret = sssnic_ethdev_rss_hash_update(ethdev, rss_conf);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to setup RSS config");
+               goto err_out;
+       }
+
+       ret = sssnic_rss_hash_engine_set(hw, SSSNIC_RSS_HASH_ENGINE_TOEP);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to set RSS hash engine");
+               goto err_out;
+       }
+
+       ret = sssnic_rss_enable_set(hw, true);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to enable RSS");
+               goto err_out;
+       }
+
+       netdev->rss_enable = true;
+
+       PMD_DRV_LOG(INFO, "Enabled RSS");
+
+       return 0;
+
+err_out:
+       sssnic_rss_profile_destroy(hw);
+       return ret;
+}
+
+int
+sssnic_ethdev_rss_shutdown(struct rte_eth_dev *ethdev)
+{
+       struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev);
+       struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev);
+       int ret;
+
+       if (!netdev->rss_enable)
+               return 0;
+
+       ret = sssnic_rss_enable_set(hw, false);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to disable rss");
+               return ret;
+       }
+
+       ret = sssnic_rss_profile_destroy(hw);
+       if (ret != 0) {
+               PMD_DRV_LOG(ERR, "Failed to delete rss profile");
+               return ret;
+       }
+
+       netdev->rss_enable = false;
+
+       return 0;
+}
diff --git a/drivers/net/sssnic/sssnic_ethdev_rss.h 
b/drivers/net/sssnic/sssnic_ethdev_rss.h
new file mode 100644
index 0000000000..559722eec7
--- /dev/null
+++ b/drivers/net/sssnic/sssnic_ethdev_rss.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: BSD-3-Clause
+ * Copyright(c) 2018-2022 Shenzhen 3SNIC Information Technology Co., Ltd.
+ */
+
+#ifndef _SSSNIC_ETHDEV_RSS_H_
+#define _SSSNIC_ETHDEV_RSS_H_
+
+int sssnic_ethdev_rss_hash_update(struct rte_eth_dev *ethdev,
+       struct rte_eth_rss_conf *rss_conf);
+int sssnic_ethdev_rss_hash_config_get(struct rte_eth_dev *ethdev,
+       struct rte_eth_rss_conf *rss_conf);
+int sssnic_ethdev_rss_reta_update(struct rte_eth_dev *ethdev,
+       struct rte_eth_rss_reta_entry64 *reta_conf, uint16_t reta_size);
+int sssnic_ethdev_rss_reta_query(struct rte_eth_dev *ethdev,
+       struct rte_eth_rss_reta_entry64 *reta_conf, uint16_t reta_size);
+int sssnic_ethdev_rss_reta_reset(struct rte_eth_dev *ethdev);
+int sssnic_ethdev_rss_setup(struct rte_eth_dev *ethdev);
+int sssnic_ethdev_rss_shutdown(struct rte_eth_dev *ethdev);
+
+#endif /* _SSSNIC_ETHDEV_RSS_H_ */
diff --git a/drivers/net/sssnic/sssnic_ethdev_rx.c 
b/drivers/net/sssnic/sssnic_ethdev_rx.c
index 82e65f2482..2874a93a54 100644
--- a/drivers/net/sssnic/sssnic_ethdev_rx.c
+++ b/drivers/net/sssnic/sssnic_ethdev_rx.c
@@ -10,6 +10,7 @@
 #include "sssnic_log.h"
 #include "sssnic_ethdev.h"
 #include "sssnic_ethdev_rx.h"
+#include "sssnic_ethdev_rss.h"
 #include "base/sssnic_hw.h"
 #include "base/sssnic_workq.h"
 #include "base/sssnic_api.h"
@@ -640,6 +641,10 @@ sssnic_ethdev_rx_queue_start(struct rte_eth_dev *ethdev, 
uint16_t queue_id)
        netdev->num_started_rxqs++;
        ethdev->data->rx_queue_state[queue_id] = RTE_ETH_QUEUE_STATE_STARTED;
 
+       ret = sssnic_ethdev_rss_reta_reset(ethdev);
+       if (ret)
+               PMD_DRV_LOG(WARNING, "Failed to reset RSS reta");
+
        PMD_DRV_LOG(DEBUG, "port %u rxq %u started", ethdev->data->port_id,
                queue_id);
 
@@ -673,6 +678,10 @@ sssnic_ethdev_rx_queue_stop(struct rte_eth_dev *ethdev, 
uint16_t queue_id)
        netdev->num_started_rxqs--;
        ethdev->data->rx_queue_state[queue_id] = RTE_ETH_QUEUE_STATE_STOPPED;
 
+       ret = sssnic_ethdev_rss_reta_reset(ethdev);
+       if (ret)
+               PMD_DRV_LOG(WARNING, "Failed to reset RSS reta");
+
        PMD_DRV_LOG(DEBUG, "port %u rxq %u stopped", ethdev->data->port_id,
                queue_id);
 
@@ -704,6 +713,10 @@ sssnic_ethdev_rx_queue_all_start(struct rte_eth_dev 
*ethdev)
                        ethdev->data->port_id, qid);
        }
 
+       ret = sssnic_ethdev_rss_reta_reset(ethdev);
+       if (ret)
+               PMD_DRV_LOG(WARNING, "Failed to reset RSS reta");
+
        ret = sssnic_port_enable_set(hw, true);
        if (ret) {
                PMD_DRV_LOG(ERR, "Failed to enable port:%u",
-- 
2.27.0

Reply via email to