This patch Rx/Tx configuration including Rx csum offload, LRO, RSS,
VLAN filter and VLAN offload.
Signed-off-by: Yanling Song <son...@ramaxel.com>
---
 drivers/net/spnic/base/spnic_nic_cfg.c | 525 +++++++++++++++++++++++++
 drivers/net/spnic/base/spnic_nic_cfg.h | 387 ++++++++++++++++++
 drivers/net/spnic/spnic_ethdev.c       | 187 ++++++++-
 drivers/net/spnic/spnic_ethdev.h       |   2 +
 drivers/net/spnic/spnic_rx.c           | 221 +++++++++++
 drivers/net/spnic/spnic_rx.h           |  31 ++
 6 files changed, 1349 insertions(+), 4 deletions(-)

diff --git a/drivers/net/spnic/base/spnic_nic_cfg.c 
b/drivers/net/spnic/base/spnic_nic_cfg.c
index f6914f6f6d..6c22c4fb3d 100644
--- a/drivers/net/spnic/base/spnic_nic_cfg.c
+++ b/drivers/net/spnic/base/spnic_nic_cfg.c
@@ -271,6 +271,37 @@ int spnic_get_default_mac(void *hwdev, u8 *mac_addr, int 
ether_len)
        return 0;
 }
 
+static int spnic_config_vlan(void *hwdev, u8 opcode, u16 vlan_id, u16 func_id)
+{
+       struct spnic_cmd_vlan_config vlan_info;
+       u16 out_size = sizeof(vlan_info);
+       int err;
+
+       memset(&vlan_info, 0, sizeof(vlan_info));
+       vlan_info.opcode = opcode;
+       vlan_info.func_id = func_id;
+       vlan_info.vlan_id = vlan_id;
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_CFG_FUNC_VLAN, &vlan_info,
+                                    sizeof(vlan_info), &vlan_info, &out_size);
+       if (err || !out_size || vlan_info.msg_head.status) {
+               PMD_DRV_LOG(ERR, "%s vlan failed, err: %d, status: 0x%x, out 
size: 0x%x",
+                           opcode == SPNIC_CMD_OP_ADD ? "Add" : "Delete",
+                           err, vlan_info.msg_head.status, out_size);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int spnic_del_vlan(void *hwdev, u16 vlan_id, u16 func_id)
+{
+       if (!hwdev)
+               return -EINVAL;
+
+       return spnic_config_vlan(hwdev, SPNIC_CMD_OP_DEL, vlan_id, func_id);
+}
+
 int spnic_get_port_info(void *hwdev, struct nic_port_info *port_info)
 {
        struct spnic_cmd_port_info port_msg;
@@ -564,6 +595,500 @@ void spnic_free_nic_hwdev(void *hwdev)
        spnic_vf_func_free(hwdev);
 }
 
+int spnic_set_rx_mode(void *hwdev, u32 enable)
+{
+       struct spnic_rx_mode_config rx_mode_cfg;
+       u16 out_size = sizeof(rx_mode_cfg);
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       memset(&rx_mode_cfg, 0, sizeof(rx_mode_cfg));
+       rx_mode_cfg.func_id = spnic_global_func_id(hwdev);
+       rx_mode_cfg.rx_mode = enable;
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_SET_RX_MODE,
+                                    &rx_mode_cfg, sizeof(rx_mode_cfg),
+                                    &rx_mode_cfg, &out_size);
+       if (err || !out_size || rx_mode_cfg.msg_head.status) {
+               PMD_DRV_LOG(ERR, "Set rx mode failed, err: %d, status: 0x%x, 
out size: 0x%x",
+                           err, rx_mode_cfg.msg_head.status, out_size);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int spnic_set_rx_vlan_offload(void *hwdev, u8 en)
+{
+       struct spnic_cmd_vlan_offload vlan_cfg;
+       u16 out_size = sizeof(vlan_cfg);
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.func_id = spnic_global_func_id(hwdev);
+       vlan_cfg.vlan_offload = en;
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_SET_RX_VLAN_OFFLOAD,
+                                    &vlan_cfg, sizeof(vlan_cfg),
+                                    &vlan_cfg, &out_size);
+       if (err || !out_size || vlan_cfg.msg_head.status) {
+               PMD_DRV_LOG(ERR, "Set rx vlan offload failed, err: %d, status: 
0x%x, out size: 0x%x",
+                           err, vlan_cfg.msg_head.status, out_size);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int spnic_set_vlan_fliter(void *hwdev, u32 vlan_filter_ctrl)
+{
+       struct spnic_cmd_set_vlan_filter vlan_filter;
+       u16 out_size = sizeof(vlan_filter);
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       memset(&vlan_filter, 0, sizeof(vlan_filter));
+       vlan_filter.func_id = spnic_global_func_id(hwdev);
+       vlan_filter.vlan_filter_ctrl = vlan_filter_ctrl;
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_SET_VLAN_FILTER_EN,
+                                    &vlan_filter, sizeof(vlan_filter),
+                                    &vlan_filter, &out_size);
+       if (err || !out_size || vlan_filter.msg_head.status) {
+               PMD_DRV_LOG(ERR, "Failed to set vlan filter, err: %d, status: 
0x%x, out size: 0x%x",
+                           err, vlan_filter.msg_head.status, out_size);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int spnic_set_rx_lro(void *hwdev, u8 ipv4_en, u8 ipv6_en,
+                           u8 lro_max_pkt_len)
+{
+       struct spnic_cmd_lro_config lro_cfg;
+       u16 out_size = sizeof(lro_cfg);
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       memset(&lro_cfg, 0, sizeof(lro_cfg));
+       lro_cfg.func_id = spnic_global_func_id(hwdev);
+       lro_cfg.opcode = SPNIC_CMD_OP_SET;
+       lro_cfg.lro_ipv4_en = ipv4_en;
+       lro_cfg.lro_ipv6_en = ipv6_en;
+       lro_cfg.lro_max_pkt_len = lro_max_pkt_len;
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_CFG_RX_LRO, &lro_cfg,
+                                    sizeof(lro_cfg), &lro_cfg, &out_size);
+       if (err || !out_size || lro_cfg.msg_head.status) {
+               PMD_DRV_LOG(ERR, "Set lro offload failed, err: %d, status: 
0x%x, out size: 0x%x",
+                           err, lro_cfg.msg_head.status, out_size);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int spnic_set_rx_lro_timer(void *hwdev, u32 timer_value)
+{
+       struct spnic_cmd_lro_timer lro_timer;
+       u16 out_size = sizeof(lro_timer);
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       memset(&lro_timer, 0, sizeof(lro_timer));
+       lro_timer.opcode = SPNIC_CMD_OP_SET;
+       lro_timer.timer = timer_value;
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_CFG_LRO_TIMER, &lro_timer,
+                                    sizeof(lro_timer), &lro_timer, &out_size);
+       if (err || !out_size || lro_timer.msg_head.status) {
+               PMD_DRV_LOG(ERR, "Set lro timer failed, err: %d, status: 0x%x, 
out size: 0x%x",
+                           err, lro_timer.msg_head.status, out_size);
+
+               return -EIO;
+       }
+
+       return 0;
+}
+
+int spnic_set_rx_lro_state(void *hwdev, u8 lro_en, u32 lro_timer,
+                           u32 lro_max_pkt_len)
+{
+       u8 ipv4_en = 0, ipv6_en = 0;
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       ipv4_en = lro_en ? 1 : 0;
+       ipv6_en = lro_en ? 1 : 0;
+
+       PMD_DRV_LOG(INFO, "Set LRO max coalesce packet size to %uK",
+                   lro_max_pkt_len);
+
+       err = spnic_set_rx_lro(hwdev, ipv4_en, ipv6_en, (u8)lro_max_pkt_len);
+       if (err)
+               return err;
+
+       /* We don't set LRO timer for VF */
+       if (spnic_func_type(hwdev) == TYPE_VF)
+               return 0;
+
+       PMD_DRV_LOG(INFO, "Set LRO timer to %u", lro_timer);
+
+       return spnic_set_rx_lro_timer(hwdev, lro_timer);
+}
+
+/* RSS config */
+int spnic_rss_template_alloc(void *hwdev)
+{
+       struct spnic_rss_template_mgmt template_mgmt;
+       u16 out_size = sizeof(template_mgmt);
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       memset(&template_mgmt, 0, sizeof(struct spnic_rss_template_mgmt));
+       template_mgmt.func_id = spnic_global_func_id(hwdev);
+       template_mgmt.cmd = NIC_RSS_CMD_TEMP_ALLOC;
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_RSS_TEMP_MGR,
+                                    &template_mgmt, sizeof(template_mgmt),
+                                    &template_mgmt, &out_size);
+       if (err || !out_size || template_mgmt.msg_head.status) {
+               if (template_mgmt.msg_head.status ==
+                   SPNIC_MGMT_STATUS_TABLE_FULL) {
+                       PMD_DRV_LOG(ERR, "There is no more template available");
+                       return -ENOSPC;
+               }
+               PMD_DRV_LOG(ERR, "Alloc rss template failed, err: %d, "
+                           "status: 0x%x, out size: 0x%x",
+                           err, template_mgmt.msg_head.status, out_size);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+int spnic_rss_template_free(void *hwdev)
+{
+       struct spnic_rss_template_mgmt template_mgmt;
+       u16 out_size = sizeof(template_mgmt);
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       memset(&template_mgmt, 0, sizeof(struct spnic_rss_template_mgmt));
+       template_mgmt.func_id = spnic_global_func_id(hwdev);
+       template_mgmt.cmd = NIC_RSS_CMD_TEMP_FREE;
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_RSS_TEMP_MGR,
+                                    &template_mgmt, sizeof(template_mgmt),
+                                    &template_mgmt, &out_size);
+       if (err || !out_size || template_mgmt.msg_head.status) {
+               PMD_DRV_LOG(ERR, "Free rss template failed, err: %d, "
+                           "status: 0x%x, out size: 0x%x",
+                           err, template_mgmt.msg_head.status, out_size);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+static int spnic_rss_cfg_hash_key(void *hwdev, u8 opcode, u8 *key)
+{
+       struct spnic_cmd_rss_hash_key hash_key;
+       u16 out_size = sizeof(hash_key);
+       int err;
+
+       if (!hwdev || !key)
+               return -EINVAL;
+
+       memset(&hash_key, 0, sizeof(struct spnic_cmd_rss_hash_key));
+       hash_key.func_id = spnic_global_func_id(hwdev);
+       hash_key.opcode = opcode;
+       if (opcode == SPNIC_CMD_OP_SET)
+               memcpy(hash_key.key, key, SPNIC_RSS_KEY_SIZE);
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_CFG_RSS_HASH_KEY,
+                                    &hash_key, sizeof(hash_key),
+                                    &hash_key, &out_size);
+       if (err || !out_size || hash_key.msg_head.status) {
+               PMD_DRV_LOG(ERR, "%s hash key failed, err: %d, "
+                           "status: 0x%x, out size: 0x%x",
+                           opcode == SPNIC_CMD_OP_SET ? "Set" : "Get",
+                           err, hash_key.msg_head.status, out_size);
+               return -EFAULT;
+       }
+
+       if (opcode == SPNIC_CMD_OP_GET)
+               memcpy(key, hash_key.key, SPNIC_RSS_KEY_SIZE);
+
+       return 0;
+}
+
+int spnic_rss_set_hash_key(void *hwdev, u8 *key)
+{
+       if (!hwdev || !key)
+               return -EINVAL;
+
+       return spnic_rss_cfg_hash_key(hwdev, SPNIC_CMD_OP_SET, key);
+}
+
+int spnic_rss_get_hash_key(void *hwdev, u8 *key)
+{
+       if (!hwdev || !key)
+               return -EINVAL;
+
+       return spnic_rss_cfg_hash_key(hwdev, SPNIC_CMD_OP_GET, key);
+}
+
+int spnic_rss_get_indir_tbl(void *hwdev, u32 *indir_table)
+{
+       struct spnic_cmd_buf *cmd_buf = NULL;
+       u16 *indir_tbl = NULL;
+       int err, i;
+
+       if (!hwdev || !indir_table)
+               return -EINVAL;
+
+       cmd_buf = spnic_alloc_cmd_buf(hwdev);
+       if (!cmd_buf) {
+               PMD_DRV_LOG(ERR, "Allocate cmd buf failed");
+               return -ENOMEM;
+       }
+
+       cmd_buf->size = sizeof(struct nic_rss_indirect_tbl);
+       err = spnic_cmdq_detail_resp(hwdev, SPNIC_MOD_L2NIC,
+                                    SPNIC_UCODE_CMD_GET_RSS_INDIR_TABLE,
+                                    cmd_buf, cmd_buf, 0);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Get rss indir table failed");
+               spnic_free_cmd_buf(cmd_buf);
+               return err;
+       }
+
+       indir_tbl = (u16 *)cmd_buf->buf;
+       for (i = 0; i < SPNIC_RSS_INDIR_SIZE; i++)
+               indir_table[i] = *(indir_tbl + i);
+
+       spnic_free_cmd_buf(cmd_buf);
+       return 0;
+}
+
+int spnic_rss_set_indir_tbl(void *hwdev, const u32 *indir_table)
+{
+       struct nic_rss_indirect_tbl *indir_tbl = NULL;
+       struct spnic_cmd_buf *cmd_buf = NULL;
+       u32 i, size;
+       u32 *temp = NULL;
+       u64 out_param = 0;
+       int err;
+
+       if (!hwdev || !indir_table)
+               return -EINVAL;
+
+       cmd_buf = spnic_alloc_cmd_buf(hwdev);
+       if (!cmd_buf) {
+               PMD_DRV_LOG(ERR, "Allocate cmd buf failed");
+               return -ENOMEM;
+       }
+
+       cmd_buf->size = sizeof(struct nic_rss_indirect_tbl);
+       indir_tbl = (struct nic_rss_indirect_tbl *)cmd_buf->buf;
+       memset(indir_tbl, 0, sizeof(*indir_tbl));
+
+       for (i = 0; i < SPNIC_RSS_INDIR_SIZE; i++)
+               indir_tbl->entry[i] = (u16)(*(indir_table + i));
+
+       size = sizeof(indir_tbl->entry) / sizeof(u32);
+       temp = (u32 *)indir_tbl->entry;
+       for (i = 0; i < size; i++)
+               temp[i] = cpu_to_be32(temp[i]);
+
+       err = spnic_cmdq_direct_resp(hwdev, SPNIC_MOD_L2NIC,
+                                    SPNIC_UCODE_CMD_SET_RSS_INDIR_TABLE,
+                                    cmd_buf, &out_param, 0);
+       if (err || out_param != 0) {
+               PMD_DRV_LOG(ERR, "Set rss indir table failed");
+               err = -EFAULT;
+       }
+
+       spnic_free_cmd_buf(cmd_buf);
+       return err;
+}
+
+int spnic_set_rss_type(void *hwdev, struct spnic_rss_type rss_type)
+{
+       struct nic_rss_context_tbl *ctx_tbl = NULL;
+       struct spnic_cmd_buf *cmd_buf = NULL;
+       u32 ctx = 0;
+       u64 out_param = 0;
+       int err;
+
+       if (!hwdev)
+               return -EINVAL;
+
+       cmd_buf = spnic_alloc_cmd_buf(hwdev);
+       if (!cmd_buf) {
+               PMD_DRV_LOG(ERR, "Allocate cmd buf failed");
+               return -ENOMEM;
+       }
+
+       ctx |= SPNIC_RSS_TYPE_SET(1, VALID) |
+              SPNIC_RSS_TYPE_SET(rss_type.ipv4, IPV4) |
+              SPNIC_RSS_TYPE_SET(rss_type.ipv6, IPV6) |
+              SPNIC_RSS_TYPE_SET(rss_type.ipv6_ext, IPV6_EXT) |
+              SPNIC_RSS_TYPE_SET(rss_type.tcp_ipv4, TCP_IPV4) |
+              SPNIC_RSS_TYPE_SET(rss_type.tcp_ipv6, TCP_IPV6) |
+              SPNIC_RSS_TYPE_SET(rss_type.tcp_ipv6_ext, TCP_IPV6_EXT) |
+              SPNIC_RSS_TYPE_SET(rss_type.udp_ipv4, UDP_IPV4) |
+              SPNIC_RSS_TYPE_SET(rss_type.udp_ipv6, UDP_IPV6);
+
+       cmd_buf->size = sizeof(struct nic_rss_context_tbl);
+       ctx_tbl = (struct nic_rss_context_tbl *)cmd_buf->buf;
+       memset(ctx_tbl, 0, sizeof(*ctx_tbl));
+       ctx_tbl->ctx = cpu_to_be32(ctx);
+
+       /* Cfg the RSS context table by command queue */
+       err = spnic_cmdq_direct_resp(hwdev, SPNIC_MOD_L2NIC,
+                                    SPNIC_UCODE_CMD_SET_RSS_CONTEXT_TABLE,
+                                    cmd_buf, &out_param, 0);
+
+       spnic_free_cmd_buf(cmd_buf);
+
+       if (err || out_param != 0) {
+               PMD_DRV_LOG(ERR, "Set rss context table failed, err: %d", err);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+int spnic_get_rss_type(void *hwdev, struct spnic_rss_type *rss_type)
+{
+       struct spnic_rss_context_table ctx_tbl;
+       u16 out_size = sizeof(ctx_tbl);
+       int err;
+
+       if (!hwdev || !rss_type)
+               return -EINVAL;
+
+       memset(&ctx_tbl, 0, sizeof(struct spnic_rss_context_table));
+       ctx_tbl.func_id = spnic_global_func_id(hwdev);
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_GET_RSS_CTX_TBL,
+                                    &ctx_tbl, sizeof(ctx_tbl),
+                                    &ctx_tbl, &out_size);
+       if (err || !out_size || ctx_tbl.msg_head.status) {
+               PMD_DRV_LOG(ERR, "Get hash type failed, err: %d, status: 0x%x, 
out size: 0x%x",
+                           err, ctx_tbl.msg_head.status, out_size);
+               return -EFAULT;
+       }
+
+       rss_type->ipv4         = SPNIC_RSS_TYPE_GET(ctx_tbl.context, IPV4);
+       rss_type->ipv6         = SPNIC_RSS_TYPE_GET(ctx_tbl.context, IPV6);
+       rss_type->ipv6_ext     = SPNIC_RSS_TYPE_GET(ctx_tbl.context, IPV6_EXT);
+       rss_type->tcp_ipv4     = SPNIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV4);
+       rss_type->tcp_ipv6     = SPNIC_RSS_TYPE_GET(ctx_tbl.context, TCP_IPV6);
+       rss_type->tcp_ipv6_ext = SPNIC_RSS_TYPE_GET(ctx_tbl.context,
+                                                    TCP_IPV6_EXT);
+       rss_type->udp_ipv4     = SPNIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV4);
+       rss_type->udp_ipv6     = SPNIC_RSS_TYPE_GET(ctx_tbl.context, UDP_IPV6);
+
+       return 0;
+}
+
+static int spnic_rss_cfg_hash_engine(void *hwdev, u8 opcode, u8 *type)
+{
+       struct spnic_cmd_rss_engine_type hash_type;
+       u16 out_size = sizeof(hash_type);
+       int err;
+
+       if (!hwdev || !type)
+               return -EINVAL;
+
+       memset(&hash_type, 0, sizeof(struct spnic_cmd_rss_engine_type));
+       hash_type.func_id = spnic_global_func_id(hwdev);
+       hash_type.opcode = opcode;
+       if (opcode == SPNIC_CMD_OP_SET)
+               hash_type.hash_engine = *type;
+
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_CFG_RSS_HASH_ENGINE,
+                                    &hash_type, sizeof(hash_type),
+                                    &hash_type, &out_size);
+       if (err || !out_size || hash_type.msg_head.status) {
+               PMD_DRV_LOG(ERR, "%s hash engine failed, err: %d, "
+                           "status: 0x%x, out size: 0x%x",
+                           opcode == SPNIC_CMD_OP_SET ? "Set" : "Get",
+                           err, hash_type.msg_head.status, out_size);
+               return -EFAULT;
+       }
+
+       if (opcode == SPNIC_CMD_OP_GET)
+               *type = hash_type.hash_engine;
+
+       return 0;
+}
+
+int spnic_rss_get_hash_engine(void *hwdev, u8 *type)
+{
+       if (!hwdev || !type)
+               return -EINVAL;
+
+       return spnic_rss_cfg_hash_engine(hwdev, SPNIC_CMD_OP_GET, type);
+}
+
+int spnic_rss_set_hash_engine(void *hwdev, u8 type)
+{
+       if (!hwdev)
+               return -EINVAL;
+
+       return spnic_rss_cfg_hash_engine(hwdev, SPNIC_CMD_OP_SET, &type);
+}
+
+int spnic_rss_cfg(void *hwdev, u8 rss_en, u8 tc_num, u8 *prio_tc)
+{
+       struct spnic_cmd_rss_config rss_cfg;
+       u16 out_size = sizeof(rss_cfg);
+       int err;
+
+       /* Ucode requires number of TC should be power of 2 */
+       if (!hwdev || !prio_tc || (tc_num & (tc_num - 1)))
+               return -EINVAL;
+
+       memset(&rss_cfg, 0, sizeof(struct spnic_cmd_rss_config));
+       rss_cfg.func_id = spnic_global_func_id(hwdev);
+       rss_cfg.rss_en = rss_en;
+       rss_cfg.rq_priority_number = tc_num ? (u8)ilog2(tc_num) : 0;
+
+       memcpy(rss_cfg.prio_tc, prio_tc, SPNIC_DCB_UP_MAX);
+       err = l2nic_msg_to_mgmt_sync(hwdev, SPNIC_CMD_RSS_CFG, &rss_cfg,
+                                    sizeof(rss_cfg), &rss_cfg, &out_size);
+       if (err || !out_size || rss_cfg.msg_head.status) {
+               PMD_DRV_LOG(ERR, "Set rss cfg failed, err: %d, "
+                           "status: 0x%x, out size: 0x%x",
+                           err, rss_cfg.msg_head.status, out_size);
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
 int spnic_vf_get_default_cos(void *hwdev, u8 *cos_id)
 {
        struct spnic_cmd_vf_dcb_state vf_dcb;
diff --git a/drivers/net/spnic/base/spnic_nic_cfg.h 
b/drivers/net/spnic/base/spnic_nic_cfg.h
index 746c1c342d..3a906b4bc3 100644
--- a/drivers/net/spnic/base/spnic_nic_cfg.h
+++ b/drivers/net/spnic/base/spnic_nic_cfg.h
@@ -12,6 +12,8 @@
 #define OS_VF_ID_TO_HW(os_vf_id) ((os_vf_id) + 1)
 #define HW_VF_ID_TO_OS(hw_vf_id) ((hw_vf_id) - 1)
 
+#define SPNIC_VLAN_PRIORITY_SHIFT      13
+
 #define SPNIC_DCB_UP_MAX               0x8
 
 #define SPNIC_MAX_NUM_RQ               256
@@ -36,6 +38,38 @@
 #define SPNIC_MGMT_STATUS_EXIST                0x6
 #define CHECK_IPSU_15BIT               0x8000
 
+#define SPNIC_MGMT_STATUS_TABLE_EMPTY  0xB
+#define SPNIC_MGMT_STATUS_TABLE_FULL   0xC
+
+#define SPNIC_MGMT_CMD_UNSUPPORTED     0xFF
+
+#define SPNIC_MAX_UC_MAC_ADDRS         128
+#define SPNIC_MAX_MC_MAC_ADDRS         128
+
+/* Structures for RSS config */
+#define SPNIC_RSS_INDIR_SIZE           256
+#define SPNIC_RSS_INDIR_CMDQ_SIZE      128
+#define SPNIC_RSS_KEY_SIZE             40
+#define SPNIC_RSS_ENABLE               0x01
+#define SPNIC_RSS_DISABLE              0x00
+
+struct spnic_rss_type {
+       u8 tcp_ipv6_ext;
+       u8 ipv6_ext;
+       u8 tcp_ipv6;
+       u8 ipv6;
+       u8 tcp_ipv4;
+       u8 ipv4;
+       u8 udp_ipv6;
+       u8 udp_ipv4;
+};
+
+enum spnic_rss_hash_type {
+       SPNIC_RSS_HASH_ENGINE_TYPE_XOR = 0,
+       SPNIC_RSS_HASH_ENGINE_TYPE_TOEP,
+       SPNIC_RSS_HASH_ENGINE_TYPE_MAX,
+};
+
 struct spnic_cmd_feature_nego {
        struct mgmt_msg_head msg_head;
 
@@ -121,6 +155,29 @@ struct spnic_port_mac_update {
        u16 rsvd2;
        u8 new_mac[ETH_ALEN];
 };
+
+#define SPNIC_CMD_OP_ADD       1
+#define SPNIC_CMD_OP_DEL       0
+
+struct spnic_cmd_vlan_config {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u8 opcode;
+       u8 rsvd1;
+       u16 vlan_id;
+       u16 rsvd2;
+};
+
+struct spnic_cmd_set_vlan_filter {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u8 resvd[2];
+       /* Bit0: vlan filter en; bit1: broadcast filter en */
+       u32 vlan_filter_ctrl;
+};
+
 struct spnic_cmd_port_info {
        struct mgmt_msg_head msg_head;
 
@@ -225,9 +282,109 @@ struct spnic_cmd_set_func_tbl {
        struct spnic_func_tbl_cfg tbl_cfg;
 };
 
+struct spnic_rx_mode_config {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u16 rsvd1;
+       u32 rx_mode;
+};
+
+struct spnic_cmd_vlan_offload {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u8 vlan_offload;
+       u8 rsvd1[5];
+};
+
 #define SPNIC_CMD_OP_GET       0
 #define SPNIC_CMD_OP_SET       1
 
+struct spnic_cmd_lro_config {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u8 opcode;
+       u8 rsvd1;
+       u8 lro_ipv4_en;
+       u8 lro_ipv6_en;
+       u8 lro_max_pkt_len; /* Unit size is 1K */
+       u8 resv2[13];
+};
+
+struct spnic_cmd_lro_timer {
+       struct mgmt_msg_head msg_head;
+
+       u8 opcode; /* 1: set timer value, 0: get timer value */
+       u8 rsvd1;
+       u16 rsvd2;
+       u32 timer;
+};
+
+struct spnic_rss_template_mgmt {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u8 cmd;
+       u8 template_id;
+       u8 rsvd1[4];
+};
+
+struct spnic_cmd_rss_hash_key {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u8 opcode;
+       u8 rsvd1;
+       u8 key[SPNIC_RSS_KEY_SIZE];
+};
+
+struct spnic_rss_indir_table {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u16 rsvd1;
+       u8 indir[SPNIC_RSS_INDIR_SIZE];
+};
+
+struct nic_rss_indirect_tbl {
+       u32 rsvd[4]; /* Make sure that 16B beyond entry[] */
+       u16 entry[SPNIC_RSS_INDIR_SIZE];
+};
+
+struct nic_rss_context_tbl {
+       u32 rsvd[4];
+       u32 ctx;
+};
+
+struct spnic_rss_context_table {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u16 rsvd1;
+       u32 context;
+};
+
+struct spnic_cmd_rss_engine_type {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u8 opcode;
+       u8 hash_engine;
+       u8 rsvd1[4];
+};
+
+struct spnic_cmd_rss_config {
+       struct mgmt_msg_head msg_head;
+
+       u16 func_id;
+       u8 rss_en;
+       u8 rq_priority_number;
+       u8 prio_tc[SPNIC_DCB_UP_MAX];
+       u32 rsvd1;
+};
+
 enum {
        SPNIC_IFLA_VF_LINK_STATE_AUTO,  /* Link state of the uplink */
        SPNIC_IFLA_VF_LINK_STATE_ENABLE, /* Link always up */
@@ -423,6 +580,50 @@ int spnic_init_nic_hwdev(void *hwdev);
  */
 void spnic_free_nic_hwdev(void *hwdev);
 
+/**
+ * Set function rx mode
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] enable
+ *   Rx mode state, 0-disable, 1-enable
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_set_rx_mode(void *hwdev, u32 enable);
+
+/**
+ * Set function vlan offload valid state
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] enable
+ *   Rx mode state, 0-disable, 1-enable
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_set_rx_vlan_offload(void *hwdev, u8 en);
+
+/**
+ * Set rx LRO configuration
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] lro_en
+ *   LRO enable state, 0-disable, 1-enable
+ * @param[in] lro_timer
+ *   LRO aggregation timeout
+ * @param[in] lro_max_pkt_len
+ *   LRO coalesce packet size(unit size is 1K)
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_set_rx_lro_state(void *hwdev, u8 lro_en, u32 lro_timer,
+                          u32 lro_max_pkt_len);
+
 /**
  * Get port info
  *
@@ -438,6 +639,192 @@ int spnic_get_port_info(void *hwdev, struct nic_port_info 
*port_info);
 
 int spnic_init_function_table(void *hwdev, u16 rx_buff_len);
 
+/**
+ * Alloc RSS template table
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_rss_template_alloc(void *hwdev);
+
+/**
+ * Free RSS template table
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_rss_template_free(void *hwdev);
+
+/**
+ * Set RSS indirect table
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] indir_table
+ *   RSS indirect table
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_rss_set_indir_tbl(void *hwdev, const u32 *indir_table);
+
+/**
+ * Get RSS indirect table
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[out] indir_table
+ *   RSS indirect table
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_rss_get_indir_tbl(void *hwdev, u32 *indir_table);
+
+/**
+ * Set RSS type
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] rss_type
+ *   RSS type, including ipv4, tcpv4, ipv6, tcpv6 and etc.
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_set_rss_type(void *hwdev, struct spnic_rss_type rss_type);
+
+/**
+ * Get RSS type
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[out] rss_type
+ *   RSS type, including ipv4, tcpv4, ipv6, tcpv6 and etc.
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_get_rss_type(void *hwdev, struct spnic_rss_type *rss_type);
+
+/**
+ * Get RSS hash engine
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[out] type
+ *   RSS hash engine, pmd driver only supports Toeplitz
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_rss_get_hash_engine(void *hwdev, u8 *type);
+
+/**
+ * Set RSS hash engine
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] type
+ *   RSS hash engine, pmd driver only supports Toeplitz
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_rss_set_hash_engine(void *hwdev, u8 type);
+
+/**
+ * Set RSS configuration
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] rss_en
+ *   RSS enable lag, 0-disable, 1-enable
+ * @param[in] tc_num
+ *   Number of TC
+ * @param[in] prio_tc
+ *   Priority of TC
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_rss_cfg(void *hwdev, u8 rss_en, u8 tc_num, u8 *prio_tc);
+
+/**
+ * Set RSS hash key
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] key
+ *   RSS hash key
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_rss_set_hash_key(void *hwdev, u8 *key);
+
+/**
+ * Get RSS hash key
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[out] key
+ *   RSS hash key
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_rss_get_hash_key(void *hwdev, u8 *key);
+
+/**
+ * Add vlan to hardware
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] vlan_id
+ *   Vlan id
+ * @param[in] func_id
+ *   Function id
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_add_vlan(void *hwdev, u16 vlan_id, u16 func_id);
+
+/**
+ * Delete vlan
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] vlan_id
+ *   Vlan id
+ * @param[in] func_id
+ *   Function id
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_del_vlan(void *hwdev, u16 vlan_id, u16 func_id);
+
+/**
+ * Set vlan filter
+ *
+ * @param[in] hwdev
+ *   Device pointer to hwdev
+ * @param[in] vlan_filter_ctrl
+ *   Vlan filter enable flag, 0-disable, 1-enable
+ *
+ * @retval zero : Success
+ * @retval non-zero : Failure
+ */
+int spnic_set_vlan_fliter(void *hwdev, u32 vlan_filter_ctrl);
+
 /**
  * Get VF function default cos
  *
diff --git a/drivers/net/spnic/spnic_ethdev.c b/drivers/net/spnic/spnic_ethdev.c
index 27942e5d68..db16d4038d 100644
--- a/drivers/net/spnic/spnic_ethdev.c
+++ b/drivers/net/spnic/spnic_ethdev.c
@@ -5,7 +5,10 @@
 #include <rte_pci.h>
 #include <rte_bus_pci.h>
 #include <ethdev_pci.h>
+#include <rte_mbuf.h>
 #include <rte_malloc.h>
+#include <rte_memcpy.h>
+#include <rte_mempool.h>
 #include <rte_errno.h>
 #include <rte_ether.h>
 
@@ -27,14 +30,41 @@
 #include "spnic_rx.h"
 #include "spnic_ethdev.h"
 
-/* Driver-specific log messages type */
-int spnic_logtype;
+#define SPNIC_MIN_RX_BUF_SIZE          1024
+
+#define SPNIC_DEFAULT_BURST_SIZE       32
+#define SPNIC_DEFAULT_NB_QUEUES                1
+#define SPNIC_DEFAULT_RING_SIZE                1024
+#define SPNIC_MAX_LRO_SIZE             65536
 
 #define SPNIC_DEFAULT_RX_FREE_THRESH   32
 #define SPNIC_DEFAULT_TX_FREE_THRESH   32
 
-#define SPNIC_MAX_UC_MAC_ADDRS         128
-#define SPNIC_MAX_MC_MAC_ADDRS         128
+/*
+ * Vlan_id is a 12 bit number. The VFTA array is actually a 4096 bit array,
+ * 128 of 32bit elements. 2^5 = 32. The val of lower 5 bits specifies the bit
+ * in the 32bit element. The higher 7 bit val specifies VFTA array index.
+ */
+#define SPNIC_VFTA_BIT(vlan_id)    (1 << ((vlan_id) & 0x1F))
+#define SPNIC_VFTA_IDX(vlan_id)    ((vlan_id) >> 5)
+
+#define SPNIC_LRO_DEFAULT_COAL_PKT_SIZE                32
+#define SPNIC_LRO_DEFAULT_TIME_LIMIT           16
+#define SPNIC_LRO_UNIT_WQE_SIZE                        1024 /* Bytes */
+
+/* Driver-specific log messages type */
+int spnic_logtype;
+
+enum spnic_rx_mod {
+       SPNIC_RX_MODE_UC = 1 << 0,
+       SPNIC_RX_MODE_MC = 1 << 1,
+       SPNIC_RX_MODE_BC = 1 << 2,
+       SPNIC_RX_MODE_MC_ALL = 1 << 3,
+       SPNIC_RX_MODE_PROMISC = 1 << 4,
+};
+
+#define SPNIC_DEFAULT_RX_MODE  (SPNIC_RX_MODE_UC | SPNIC_RX_MODE_MC | \
+                               SPNIC_RX_MODE_BC)
 
 #define SPNIC_MAX_QUEUE_DEPTH          16384
 #define SPNIC_MIN_QUEUE_DEPTH          128
@@ -638,6 +668,139 @@ static void spnic_deinit_mac_addr(struct rte_eth_dev 
*eth_dev)
        spnic_delete_mc_addr_list(nic_dev);
 }
 
+static int spnic_set_rxtx_configure(struct rte_eth_dev *dev)
+{
+       struct spnic_nic_dev *nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(dev);
+       struct rte_eth_conf *dev_conf = &dev->data->dev_conf;
+       struct rte_eth_rss_conf *rss_conf = NULL;
+       bool lro_en, vlan_filter, vlan_strip;
+       int max_lro_size, lro_max_pkt_len;
+       int err;
+
+       /* Config rx mode */
+       err = spnic_set_rx_mode(nic_dev->hwdev, SPNIC_DEFAULT_RX_MODE);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Set rx_mode: 0x%x failed",
+                           SPNIC_DEFAULT_RX_MODE);
+               return err;
+       }
+       nic_dev->rx_mode = SPNIC_DEFAULT_RX_MODE;
+
+       /* Config rx checksum offload */
+       if (dev_conf->rxmode.offloads & DEV_RX_OFFLOAD_CHECKSUM)
+               nic_dev->rx_csum_en = SPNIC_DEFAULT_RX_CSUM_OFFLOAD;
+
+       /* Config lro */
+       lro_en = dev_conf->rxmode.offloads & DEV_RX_OFFLOAD_TCP_LRO ?
+                true : false;
+       max_lro_size = dev->data->dev_conf.rxmode.max_lro_pkt_size;
+       lro_max_pkt_len = max_lro_size / SPNIC_LRO_UNIT_WQE_SIZE ?
+                         max_lro_size / SPNIC_LRO_UNIT_WQE_SIZE : 1;
+
+       PMD_DRV_LOG(INFO, "max_lro_size: %d, rx_buff_len: %d, lro_max_pkt_len: 
%d mtu: %d",
+                   max_lro_size, nic_dev->rx_buff_len, lro_max_pkt_len,
+                   dev->data->dev_conf.rxmode.mtu);
+
+       err = spnic_set_rx_lro_state(nic_dev->hwdev, lro_en,
+                                    SPNIC_LRO_DEFAULT_TIME_LIMIT,
+                                    lro_max_pkt_len);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Set lro state failed, err: %d", err);
+               return err;
+       }
+
+       /* Config RSS */
+       if ((dev_conf->rxmode.mq_mode & ETH_MQ_RX_RSS_FLAG) &&
+           nic_dev->num_rqs > 1) {
+               rss_conf = &(dev_conf->rx_adv_conf.rss_conf);
+               err = spnic_update_rss_config(dev, rss_conf);
+               if (err) {
+                       PMD_DRV_LOG(ERR, "Set rss config failed, err: %d", err);
+                       return err;
+               }
+       }
+
+       /* Config vlan filter */
+       vlan_filter = dev_conf->rxmode.offloads & DEV_RX_OFFLOAD_VLAN_FILTER ?
+                     true : false;
+
+       err = spnic_set_vlan_fliter(nic_dev->hwdev, vlan_filter);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Config vlan filter failed, device: %s, 
port_id: %d, err: %d",
+                           nic_dev->dev_name, dev->data->port_id, err);
+               return err;
+       }
+
+       /* Config vlan stripping */
+       vlan_strip = dev_conf->rxmode.offloads & DEV_RX_OFFLOAD_VLAN_STRIP ?
+                    true : false;
+
+       err = spnic_set_rx_vlan_offload(nic_dev->hwdev, vlan_strip);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Config vlan strip failed, device: %s, 
port_id: %d, err: %d",
+                           nic_dev->dev_name, dev->data->port_id, err);
+               return err;
+       }
+
+       spnic_init_rx_queue_list(nic_dev);
+
+       return 0;
+}
+
+static void spnic_remove_rxtx_configure(struct rte_eth_dev *dev)
+{
+       struct spnic_nic_dev *nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(dev);
+       u8 prio_tc[SPNIC_DCB_UP_MAX] = {0};
+
+       spnic_set_rx_mode(nic_dev->hwdev, 0);
+
+       if (nic_dev->rss_state == SPNIC_RSS_ENABLE) {
+               spnic_rss_cfg(nic_dev->hwdev, SPNIC_RSS_DISABLE, 0, prio_tc);
+               spnic_rss_template_free(nic_dev->hwdev);
+       }
+}
+
+static bool spnic_find_vlan_filter(struct spnic_nic_dev *nic_dev,
+                                  uint16_t vlan_id)
+{
+       u32 vid_idx, vid_bit;
+
+       vid_idx = SPNIC_VFTA_IDX(vlan_id);
+       vid_bit = SPNIC_VFTA_BIT(vlan_id);
+
+       return (nic_dev->vfta[vid_idx] & vid_bit) ? true : false;
+}
+
+static void spnic_store_vlan_filter(struct spnic_nic_dev *nic_dev,
+                                   u16 vlan_id, bool on)
+{
+       u32 vid_idx, vid_bit;
+
+       vid_idx = SPNIC_VFTA_IDX(vlan_id);
+       vid_bit = SPNIC_VFTA_BIT(vlan_id);
+
+       if (on)
+               nic_dev->vfta[vid_idx] |= vid_bit;
+       else
+               nic_dev->vfta[vid_idx] &= ~vid_bit;
+}
+
+static void spnic_remove_all_vlanid(struct rte_eth_dev *dev)
+{
+       struct spnic_nic_dev *nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(dev);
+       int vlan_id;
+       u16 func_id;
+
+       func_id = spnic_global_func_id(nic_dev->hwdev);
+
+       for (vlan_id = 1; vlan_id < RTE_ETHER_MAX_VLAN_ID; vlan_id++) {
+               if (spnic_find_vlan_filter(nic_dev, vlan_id)) {
+                       spnic_del_vlan(nic_dev->hwdev, vlan_id, func_id);
+                       spnic_store_vlan_filter(nic_dev, vlan_id, false);
+               }
+       }
+}
+
 static int spnic_init_sw_rxtxqs(struct spnic_nic_dev *nic_dev)
 {
        u32 txq_size;
@@ -736,6 +899,14 @@ static int spnic_dev_start(struct rte_eth_dev *eth_dev)
                goto set_mtu_fail;
        }
 
+       /* Set rx configuration: rss/checksum/rxmode/lro */
+       err = spnic_set_rxtx_configure(eth_dev);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Set rx config failed, dev_name: %s",
+                           eth_dev->data->name);
+               goto set_rxtx_config_fail;
+       }
+
        err = spnic_start_all_rqs(eth_dev);
        if (err) {
                PMD_DRV_LOG(ERR, "Set rx config failed, dev_name: %s",
@@ -754,6 +925,9 @@ static int spnic_dev_start(struct rte_eth_dev *eth_dev)
        return 0;
 
 start_rqs_fail:
+       spnic_remove_rxtx_configure(eth_dev);
+
+set_rxtx_config_fail:
 set_mtu_fail:
        spnic_free_qp_ctxts(nic_dev->hwdev);
 
@@ -793,6 +967,10 @@ static int spnic_dev_stop(struct rte_eth_dev *dev)
        spnic_flush_txqs(nic_dev);
 
        spnic_flush_qps_res(nic_dev->hwdev);
+
+       /* Clean RSS table and rx_mode */
+       spnic_remove_rxtx_configure(dev);
+
        /* Clean root context */
        spnic_free_qp_ctxts(nic_dev->hwdev);
 
@@ -833,6 +1011,7 @@ static int spnic_dev_close(struct rte_eth_dev *eth_dev)
        spnic_deinit_sw_rxtxqs(nic_dev);
        spnic_deinit_mac_addr(eth_dev);
        rte_free(nic_dev->mc_list);
+       spnic_remove_all_vlanid(eth_dev);
 
        rte_bit_relaxed_clear32(SPNIC_DEV_INTR_EN, &nic_dev->dev_status);
 
diff --git a/drivers/net/spnic/spnic_ethdev.h b/drivers/net/spnic/spnic_ethdev.h
index 321db389dc..996b4e4b8f 100644
--- a/drivers/net/spnic/spnic_ethdev.h
+++ b/drivers/net/spnic/spnic_ethdev.h
@@ -63,6 +63,8 @@ struct spnic_nic_dev {
        u32 default_cos;
        u32 rx_csum_en;
 
+       u8 rss_key[SPNIC_RSS_KEY_SIZE];
+
        u32 dev_status;
 
        bool pause_set;
diff --git a/drivers/net/spnic/spnic_rx.c b/drivers/net/spnic/spnic_rx.c
index 20cd50c0c4..4d8c6c7e60 100644
--- a/drivers/net/spnic/spnic_rx.c
+++ b/drivers/net/spnic/spnic_rx.c
@@ -284,19 +284,240 @@ static inline void spnic_rearm_rxq_mbuf(struct spnic_rxq 
*rxq)
 #endif
 }
 
+static int spnic_init_rss_key(struct spnic_nic_dev *nic_dev,
+                              struct rte_eth_rss_conf *rss_conf)
+{
+       u8 default_rss_key[SPNIC_RSS_KEY_SIZE] = {
+                        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};
+       u8 hashkey[SPNIC_RSS_KEY_SIZE] = {0};
+       int err;
+
+       if (rss_conf->rss_key == NULL ||
+           rss_conf->rss_key_len > SPNIC_RSS_KEY_SIZE)
+               memcpy(hashkey, default_rss_key, SPNIC_RSS_KEY_SIZE);
+       else
+               memcpy(hashkey, rss_conf->rss_key, rss_conf->rss_key_len);
+
+       err = spnic_rss_set_hash_key(nic_dev->hwdev, hashkey);
+       if (err)
+               return err;
+
+       memcpy(nic_dev->rss_key, hashkey, SPNIC_RSS_KEY_SIZE);
+       return 0;
+}
+
+void spnic_add_rq_to_rx_queue_list(struct spnic_nic_dev *nic_dev,
+                                   u16 queue_id)
+{
+       u8 rss_queue_count = nic_dev->num_rss;
+
+       RTE_ASSERT(rss_queue_count <= (RTE_DIM(nic_dev->rx_queue_list) - 1));
+
+       nic_dev->rx_queue_list[rss_queue_count] = (u8)queue_id;
+       nic_dev->num_rss++;
+}
+
+void spnic_init_rx_queue_list(struct spnic_nic_dev *nic_dev)
+{
+       nic_dev->num_rss = 0;
+}
+
+static void spnic_fill_indir_tbl(struct spnic_nic_dev *nic_dev,
+                                 u32 *indir_tbl)
+{
+       u8 rss_queue_count = nic_dev->num_rss;
+       int i = 0;
+       int j;
+
+       if (rss_queue_count == 0) {
+               /* delete q_id from indir tbl */
+               for (i = 0; i < SPNIC_RSS_INDIR_SIZE; i++)
+                       indir_tbl[i] = 0xFF; /* Invalid value in indir tbl */
+       } else {
+               while (i < SPNIC_RSS_INDIR_SIZE)
+                       for (j = 0; (j < rss_queue_count) &&
+                                   (i < SPNIC_RSS_INDIR_SIZE); j++)
+                               indir_tbl[i++] = nic_dev->rx_queue_list[j];
+       }
+}
+
+int spnic_refill_indir_rqid(struct spnic_rxq *rxq)
+{
+       struct spnic_nic_dev *nic_dev = rxq->nic_dev;
+       u32 *indir_tbl;
+       int err;
+
+       indir_tbl = rte_zmalloc(NULL, SPNIC_RSS_INDIR_SIZE * sizeof(u32), 0);
+       if (!indir_tbl) {
+               PMD_DRV_LOG(ERR, "Alloc indir_tbl mem failed, eth_dev:%s, 
queue_idx:%d\n",
+                           nic_dev->dev_name, rxq->q_id);
+               return -ENOMEM;
+       }
+
+       /* build indir tbl according to the number of rss queue */
+       spnic_fill_indir_tbl(nic_dev, indir_tbl);
+
+       err = spnic_rss_set_indir_tbl(nic_dev->hwdev, indir_tbl);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Set indrect table failed, eth_dev:%s, 
queue_idx:%d\n",
+                           nic_dev->dev_name, rxq->q_id);
+               goto out;
+       }
+
+out:
+       rte_free(indir_tbl);
+       return err;
+}
+
+static int spnic_init_rss_type(struct spnic_nic_dev *nic_dev,
+                              struct rte_eth_rss_conf *rss_conf)
+{
+       struct spnic_rss_type rss_type = {0};
+       u64 rss_hf = rss_conf->rss_hf;
+       int err;
+
+       rss_type.ipv4 = (rss_hf & (ETH_RSS_IPV4 | ETH_RSS_FRAG_IPV4)) ? 1 : 0;
+       rss_type.tcp_ipv4 = (rss_hf & ETH_RSS_NONFRAG_IPV4_TCP) ? 1 : 0;
+       rss_type.ipv6 = (rss_hf & (ETH_RSS_IPV6 | ETH_RSS_FRAG_IPV6)) ? 1 : 0;
+       rss_type.ipv6_ext = (rss_hf & ETH_RSS_IPV6_EX) ? 1 : 0;
+       rss_type.tcp_ipv6 = (rss_hf & ETH_RSS_NONFRAG_IPV6_TCP) ? 1 : 0;
+       rss_type.tcp_ipv6_ext = (rss_hf & ETH_RSS_IPV6_TCP_EX) ? 1 : 0;
+       rss_type.udp_ipv4 = (rss_hf & ETH_RSS_NONFRAG_IPV4_UDP) ? 1 : 0;
+       rss_type.udp_ipv6 = (rss_hf & ETH_RSS_NONFRAG_IPV6_UDP) ? 1 : 0;
+
+       err = spnic_set_rss_type(nic_dev->hwdev, rss_type);
+       return err;
+}
+
+int spnic_update_rss_config(struct rte_eth_dev *dev,
+                           struct rte_eth_rss_conf *rss_conf)
+{
+       struct spnic_nic_dev *nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(dev);
+       u8 prio_tc[SPNIC_DCB_UP_MAX] = {0};
+       u8 num_tc = 0;
+       int err;
+
+       if (rss_conf->rss_hf == 0) {
+               rss_conf->rss_hf = SPNIC_RSS_OFFLOAD_ALL;
+       } else if ((rss_conf->rss_hf & SPNIC_RSS_OFFLOAD_ALL) == 0) {
+               PMD_DRV_LOG(ERR, "Does't support rss hash type: %"PRIu64"",
+                           rss_conf->rss_hf);
+               return -EINVAL;
+       }
+
+       err = spnic_rss_template_alloc(nic_dev->hwdev);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Alloc rss template failed, err: %d", err);
+               return err;
+       }
+
+       err = spnic_init_rss_key(nic_dev, rss_conf);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Init rss hash key failed, err: %d", err);
+               goto init_rss_fail;
+       }
+
+       err = spnic_init_rss_type(nic_dev, rss_conf);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Init rss hash type failed, err: %d", err);
+               goto init_rss_fail;
+       }
+
+       err = spnic_rss_set_hash_engine(nic_dev->hwdev,
+                                        SPNIC_RSS_HASH_ENGINE_TYPE_TOEP);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Init rss hash function failed, err: %d", err);
+               goto init_rss_fail;
+       }
+
+       err = spnic_rss_cfg(nic_dev->hwdev, SPNIC_RSS_ENABLE, num_tc,
+                            prio_tc);
+       if (err) {
+               PMD_DRV_LOG(ERR, "Enable rss failed, err: %d", err);
+               goto init_rss_fail;
+       }
+
+       nic_dev->rss_state = SPNIC_RSS_ENABLE;
+       return 0;
+
+init_rss_fail:
+       if (spnic_rss_template_free(nic_dev->hwdev))
+               PMD_DRV_LOG(WARNING, "Free rss template failed");
+
+       return err;
+}
+
+static u8 spnic_find_queue_pos_by_rq_id(u8 *queues, u8 queues_count,
+                                        u8 queue_id)
+{
+       u8 pos;
+
+       for (pos = 0; pos < queues_count; pos++) {
+               if (queue_id == queues[pos])
+                       break;
+       }
+
+       return pos;
+}
+
+void spnic_remove_rq_from_rx_queue_list(struct spnic_nic_dev *nic_dev,
+                                        u16 queue_id)
+{
+       u8 queue_pos;
+       u8 rss_queue_count = nic_dev->num_rss;
+
+       queue_pos = spnic_find_queue_pos_by_rq_id(nic_dev->rx_queue_list,
+                                                  rss_queue_count,
+                                                  (u8)queue_id);
+
+       if (queue_pos < rss_queue_count) {
+               rss_queue_count--;
+               memmove(nic_dev->rx_queue_list + queue_pos,
+                       nic_dev->rx_queue_list + queue_pos + 1,
+                       (rss_queue_count - queue_pos) *
+                       sizeof(nic_dev->rx_queue_list[0]));
+       }
+
+       RTE_ASSERT(rss_queue_count < RTE_DIM(nic_dev->rx_queue_list));
+       nic_dev->num_rss = rss_queue_count;
+}
+
 int spnic_start_all_rqs(struct rte_eth_dev *eth_dev)
 {
        struct spnic_nic_dev *nic_dev = NULL;
        struct spnic_rxq *rxq = NULL;
+       int err = 0;
        int i;
 
        nic_dev = SPNIC_ETH_DEV_TO_PRIVATE_NIC_DEV(eth_dev);
 
        for (i = 0; i < nic_dev->num_rqs; i++) {
                rxq = eth_dev->data->rx_queues[i];
+               spnic_add_rq_to_rx_queue_list(nic_dev, rxq->q_id);
                spnic_rearm_rxq_mbuf(rxq);
                eth_dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STARTED;
        }
 
+       if (nic_dev->rss_state == SPNIC_RSS_ENABLE) {
+               err = spnic_refill_indir_rqid(rxq);
+               if (err) {
+                       PMD_DRV_LOG(ERR, "Refill rq to indrect table failed, 
eth_dev:%s, queue_idx:%d err:%d\n",
+                                   rxq->nic_dev->dev_name, rxq->q_id, err);
+                       goto out;
+               }
+       }
+
        return 0;
+out:
+       for (i = 0; i < nic_dev->num_rqs; i++) {
+               rxq = eth_dev->data->rx_queues[i];
+               spnic_remove_rq_from_rx_queue_list(nic_dev, rxq->q_id);
+               spnic_free_rxq_mbufs(rxq);
+               eth_dev->data->rx_queue_state[i] = RTE_ETH_QUEUE_STATE_STOPPED;
+       }
+       return err;
 }
diff --git a/drivers/net/spnic/spnic_rx.h b/drivers/net/spnic/spnic_rx.h
index 46f4e1276d..0b534f1904 100644
--- a/drivers/net/spnic/spnic_rx.h
+++ b/drivers/net/spnic/spnic_rx.h
@@ -5,6 +5,23 @@
 #ifndef _SPNIC_RX_H_
 #define _SPNIC_RX_H_
 
+#define SPNIC_DEFAULT_RX_CSUM_OFFLOAD  0xFFF
+
+#define SPNIC_RSS_OFFLOAD_ALL ( \
+       ETH_RSS_IPV4 | \
+       ETH_RSS_FRAG_IPV4 | \
+       ETH_RSS_NONFRAG_IPV4_TCP | \
+       ETH_RSS_NONFRAG_IPV4_UDP | \
+       ETH_RSS_NONFRAG_IPV4_OTHER | \
+       ETH_RSS_IPV6 | \
+       ETH_RSS_FRAG_IPV6 | \
+       ETH_RSS_NONFRAG_IPV6_TCP | \
+       ETH_RSS_NONFRAG_IPV6_UDP | \
+       ETH_RSS_NONFRAG_IPV6_OTHER | \
+       ETH_RSS_IPV6_EX | \
+       ETH_RSS_IPV6_TCP_EX | \
+       ETH_RSS_IPV6_UDP_EX)
+
 struct spnic_rxq_stats {
        u64 packets;
        u64 bytes;
@@ -118,7 +135,21 @@ void spnic_free_rxq_mbufs(struct spnic_rxq *rxq);
 
 void spnic_free_all_rxq_mbufs(struct spnic_nic_dev *nic_dev);
 
+int spnic_update_rss_config(struct rte_eth_dev *dev,
+                           struct rte_eth_rss_conf *rss_conf);
+
 int spnic_start_all_rqs(struct rte_eth_dev *eth_dev);
+
+void spnic_add_rq_to_rx_queue_list(struct spnic_nic_dev *nic_dev,
+                                   u16 queue_id);
+
+int spnic_refill_indir_rqid(struct spnic_rxq *rxq);
+
+void spnic_init_rx_queue_list(struct spnic_nic_dev *nic_dev);
+
+void spnic_remove_rq_from_rx_queue_list(struct spnic_nic_dev *nic_dev,
+                                        u16 queue_id);
+
 /**
  * Get receive queue local ci
  *
-- 
2.27.0

Reply via email to