From patchwork Fri Sep 1 03:28:41 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Renyong Wan X-Patchwork-Id: 131040 X-Patchwork-Delegate: ferruh.yigit@amd.com Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 55A8142219; Fri, 1 Sep 2023 05:32:29 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 88E4940ED9; Fri, 1 Sep 2023 05:29:57 +0200 (CEST) Received: from VLXDG1SPAM1.ramaxel.com (email.ramaxel.com [221.4.138.186]) by mails.dpdk.org (Postfix) with ESMTP id 32CCB402DA for ; Fri, 1 Sep 2023 05:29:51 +0200 (CEST) Received: from V12DG1MBS03.ramaxel.local ([172.26.18.33]) by VLXDG1SPAM1.ramaxel.com with ESMTP id 3813T4Ii033406; Fri, 1 Sep 2023 11:29:05 +0800 (GMT-8) (envelope-from wanry@3snic.com) Received: from localhost.localdomain (10.64.136.151) by V12DG1MBS03.ramaxel.local (172.26.18.33) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256_P256) id 15.1.2375.17; Fri, 1 Sep 2023 11:29:03 +0800 From: To: CC: , Renyong Wan , Steven Song Subject: [PATCH v3 31/32] net/sssnic: add generic flow ops Date: Fri, 1 Sep 2023 11:28:41 +0800 Message-ID: <20230901032842.223547-32-wanry@3snic.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20230901032842.223547-1-wanry@3snic.com> References: <20230901032842.223547-1-wanry@3snic.com> MIME-Version: 1.0 X-Originating-IP: [10.64.136.151] X-ClientProxiedBy: V12DG1MBS03.ramaxel.local (172.26.18.33) To V12DG1MBS03.ramaxel.local (172.26.18.33) X-DNSRBL: X-SPAM-SOURCE-CHECK: pass X-MAIL: VLXDG1SPAM1.ramaxel.com 3813T4Ii033406 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org From: Renyong Wan Signed-off-by: Steven Song Signed-off-by: Renyong Wan --- v2: * Fixed 'mask->hdr.src_addr' will always evaluate to 'true'. * Removed error.h from including files. --- doc/guides/nics/features/sssnic.ini | 12 + drivers/net/sssnic/base/sssnic_api.c | 264 ++++++ drivers/net/sssnic/base/sssnic_api.h | 22 + drivers/net/sssnic/base/sssnic_cmd.h | 71 ++ drivers/net/sssnic/base/sssnic_hw.h | 3 + drivers/net/sssnic/base/sssnic_misc.h | 7 + drivers/net/sssnic/meson.build | 2 + drivers/net/sssnic/sssnic_ethdev.c | 12 + drivers/net/sssnic/sssnic_ethdev.h | 1 + drivers/net/sssnic/sssnic_ethdev_fdir.c | 1017 +++++++++++++++++++++++ drivers/net/sssnic/sssnic_ethdev_fdir.h | 332 ++++++++ drivers/net/sssnic/sssnic_ethdev_flow.c | 981 ++++++++++++++++++++++ drivers/net/sssnic/sssnic_ethdev_flow.h | 11 + drivers/net/sssnic/sssnic_ethdev_rx.c | 18 + 14 files changed, 2753 insertions(+) create mode 100644 drivers/net/sssnic/sssnic_ethdev_fdir.c create mode 100644 drivers/net/sssnic/sssnic_ethdev_fdir.h create mode 100644 drivers/net/sssnic/sssnic_ethdev_flow.c create mode 100644 drivers/net/sssnic/sssnic_ethdev_flow.h diff --git a/doc/guides/nics/features/sssnic.ini b/doc/guides/nics/features/sssnic.ini index f5738ac934..57e7440d86 100644 --- a/doc/guides/nics/features/sssnic.ini +++ b/doc/guides/nics/features/sssnic.ini @@ -33,3 +33,15 @@ FW version = Y Linux = Y ARMv8 = Y x86-64 = Y + +[rte_flow items] +any = Y +eth = Y +ipv4 = Y +ipv6 = Y +tcp = Y +udp = Y +vxlan = Y + +[rte_flow actions] +queue = Y diff --git a/drivers/net/sssnic/base/sssnic_api.c b/drivers/net/sssnic/base/sssnic_api.c index 68c16c9c1e..0e965442fd 100644 --- a/drivers/net/sssnic/base/sssnic_api.c +++ b/drivers/net/sssnic/base/sssnic_api.c @@ -1635,3 +1635,267 @@ sssnic_vlan_filter_set(struct sssnic_hw *hw, uint16_t vid, bool add) return 0; } + +int +sssnic_tcam_enable_set(struct sssnic_hw *hw, bool enabled) +{ + struct sssnic_tcam_enable_set_cmd cmd; + struct sssnic_msg msg; + uint32_t cmd_len; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd_len = sizeof(cmd); + cmd.function = SSSNIC_FUNC_IDX(hw); + cmd.enabled = enabled ? 1 : 0; + + sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, + SSSNIC_SET_TCAM_ENABLE_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) { + if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED) + PMD_DRV_LOG(WARNING, + "SSSNIC_SET_TCAM_ENABLED_CMD is unsupported"); + else + PMD_DRV_LOG(ERR, + "Bad response to SSSNIC_SET_TCAM_ENABLE_CMD, len=%u, status=%u", + cmd_len, cmd.common.status); + return -EIO; + } + + return 0; +} + +int +sssnic_tcam_flush(struct sssnic_hw *hw) +{ + struct sssnic_tcam_flush_cmd cmd; + struct sssnic_msg msg; + uint32_t cmd_len; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd_len = sizeof(cmd); + cmd.function = SSSNIC_FUNC_IDX(hw); + + sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, SSSNIC_FLUSH_TCAM_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) { + if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED) + PMD_DRV_LOG(WARNING, + "SSSNIC_FLUSH_TCAM_CMD is unsupported"); + else + PMD_DRV_LOG(ERR, + "Bad response to SSSNIC_FLUSH_TCAM_CMD, len=%u, status=%u", + cmd_len, cmd.common.status); + return -EIO; + } + return 0; +} + +int +sssnic_tcam_disable_and_flush(struct sssnic_hw *hw) +{ + int ret; + + ret = sssnic_tcam_enable_set(hw, 0); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Could not disable TCAM"); + return ret; + } + + ret = sssnic_tcam_flush(hw); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Could not flush TCAM"); + return ret; + } + + return 0; +} + +static int +sssnic_tcam_block_cfg(struct sssnic_hw *hw, uint8_t flag, uint16_t *block_idx) +{ + struct sssnic_tcam_block_cfg_cmd cmd; + struct sssnic_msg msg; + uint32_t cmd_len; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd_len = sizeof(cmd); + cmd.function = SSSNIC_FUNC_IDX(hw); + cmd.flag = flag; + if (flag == SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_FREE) + cmd.idx = *block_idx; + + sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, + SSSNIC_TCAM_CFG_BLOCK_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) { + if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED) + PMD_DRV_LOG(WARNING, + "SSSNIC_CFG_TCAM_BLOCK_CMD is unsupported"); + else + PMD_DRV_LOG(ERR, + "Bad response to SSSNIC_CFG_TCAM_BLOCK_CMD, len=%u, status=%u", + cmd_len, cmd.common.status); + return -EIO; + } + + if (flag == SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_ALLOC) + *block_idx = cmd.idx; + + return 0; +} + +int +sssnic_tcam_block_alloc(struct sssnic_hw *hw, uint16_t *block_idx) +{ + if (block_idx == NULL) + return -EINVAL; + + return sssnic_tcam_block_cfg(hw, SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_ALLOC, + block_idx); +} + +int +sssnic_tcam_block_free(struct sssnic_hw *hw, uint16_t block_idx) +{ + return sssnic_tcam_block_cfg(hw, SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_FREE, + &block_idx); +} + +int +sssnic_tcam_packet_type_filter_set(struct sssnic_hw *hw, uint8_t ptype, + uint16_t qid, bool enabled) +{ + struct sssnic_tcam_ptype_filter_set_cmd cmd; + struct sssnic_msg msg; + uint32_t cmd_len; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd_len = sizeof(cmd); + cmd.function = SSSNIC_FUNC_IDX(hw); + cmd.ptype = ptype; + cmd.qid = qid; + cmd.enable = enabled ? 1 : 0; + + sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, + SSSNIC_TCAM_SET_PTYPE_FILTER_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) { + if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED) + PMD_DRV_LOG(WARNING, + "SSSNIC_TCAM_SET_PTYPE_FILTER_CMD is unsupported"); + else + PMD_DRV_LOG(ERR, + "Bad response to SSSNIC_TCAM_SET_PTYPE_FILTER_CMD, len=%u, status=%u", + cmd_len, cmd.common.status); + return -EIO; + } + + return 0; +} + +int +sssnic_tcam_entry_add(struct sssnic_hw *hw, struct sssnic_tcam_entry *entry) +{ + struct sssnic_tcam_entry_add_cmd cmd; + struct sssnic_msg msg; + uint32_t cmd_len; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd_len = sizeof(cmd); + cmd.function = SSSNIC_FUNC_IDX(hw); + rte_memcpy(&cmd.data, entry, sizeof(cmd.data)); + + if (entry->index >= SSSNIC_TCAM_MAX_ENTRY_NUM) { + PMD_DRV_LOG(ERR, "Invalid TCAM entry index: %u", entry->index); + return -EINVAL; + } + + sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, + SSSNIC_ADD_TCAM_ENTRY_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) { + if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED) + PMD_DRV_LOG(WARNING, + "SSSNIC_ADD_TCAM_ENTRY_CMD is unsupported"); + else + PMD_DRV_LOG(ERR, + "Bad response to SSSNIC_ADD_TCAM_ENTRY_CMD, len=%u, status=%u", + cmd_len, cmd.common.status); + return -EIO; + } + + return 0; +} + +int +sssnic_tcam_entry_del(struct sssnic_hw *hw, uint32_t entry_idx) +{ + struct sssnic_tcam_entry_del_cmd cmd; + struct sssnic_msg msg; + uint32_t cmd_len; + int ret; + + memset(&cmd, 0, sizeof(cmd)); + cmd_len = sizeof(cmd); + cmd.function = SSSNIC_FUNC_IDX(hw); + cmd.start = entry_idx; + cmd.num = 1; + + sssnic_msg_init(&msg, (uint8_t *)&cmd, cmd_len, + SSSNIC_DEL_TCAM_ENTRY_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) { + if (cmd.common.status == SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED) + PMD_DRV_LOG(WARNING, + "SSSNIC_ADD_TCAM_ENTRY_CMD is unsupported"); + else + PMD_DRV_LOG(ERR, + "Bad response to SSSNIC_ADD_TCAM_ENTRY_CMD, len=%u, status=%u", + cmd_len, cmd.common.status); + return -EIO; + } + + return 0; +} diff --git a/drivers/net/sssnic/base/sssnic_api.h b/drivers/net/sssnic/base/sssnic_api.h index 28b235dda2..7a02ec61ee 100644 --- a/drivers/net/sssnic/base/sssnic_api.h +++ b/drivers/net/sssnic/base/sssnic_api.h @@ -409,6 +409,18 @@ struct sssnic_fw_version { char time[SSSNIC_FW_VERSION_LEN]; }; +struct sssnic_tcam_entry { + uint32_t index; + struct { + uint32_t qid; + uint32_t resvd; + } result; + struct { + uint8_t data0[SSSNIC_TCAM_KEY_SIZE]; + uint8_t data1[SSSNIC_TCAM_KEY_SIZE]; + } key; +}; + 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, @@ -470,5 +482,15 @@ int sssnic_flow_ctrl_set(struct sssnic_hw *hw, bool autoneg, bool rx_en, int sssnic_flow_ctrl_get(struct sssnic_hw *hw, bool *autoneg, bool *rx_en, bool *tx_en); int sssnic_vlan_filter_set(struct sssnic_hw *hw, uint16_t vid, bool add); +int sssnic_tcam_enable_set(struct sssnic_hw *hw, bool enabled); +int sssnic_tcam_flush(struct sssnic_hw *hw); +int sssnic_tcam_disable_and_flush(struct sssnic_hw *hw); +int sssnic_tcam_block_alloc(struct sssnic_hw *hw, uint16_t *block_idx); +int sssnic_tcam_block_free(struct sssnic_hw *hw, uint16_t block_idx); +int sssnic_tcam_packet_type_filter_set(struct sssnic_hw *hw, uint8_t ptype, + uint16_t qid, bool enabled); +int sssnic_tcam_entry_add(struct sssnic_hw *hw, + struct sssnic_tcam_entry *entry); +int sssnic_tcam_entry_del(struct sssnic_hw *hw, uint32_t entry_idx); #endif /* _SSSNIC_API_H_ */ diff --git a/drivers/net/sssnic/base/sssnic_cmd.h b/drivers/net/sssnic/base/sssnic_cmd.h index 3e70d0e223..c75cb0dad3 100644 --- a/drivers/net/sssnic/base/sssnic_cmd.h +++ b/drivers/net/sssnic/base/sssnic_cmd.h @@ -75,6 +75,16 @@ enum sssnic_rss_cmd_id { SSSNIC_SET_RSS_TYPE_CMD = 65, }; +#define SSSNIC_TCAM_CMD_STATUS_UNSUPPORTED 0xff +enum sssnic_tcam_cmd_id { + SSSNIC_ADD_TCAM_ENTRY_CMD = 80, + SSSNIC_DEL_TCAM_ENTRY_CMD = 81, + SSSNIC_FLUSH_TCAM_CMD = 83, + SSSNIC_TCAM_CFG_BLOCK_CMD = 84, + SSSNIC_SET_TCAM_ENABLE_CMD = 85, + SSSNIC_TCAM_SET_PTYPE_FILTER_CMD = 91, +}; + struct sssnic_cmd_common { uint8_t status; uint8_t version; @@ -434,4 +444,65 @@ struct sssnic_vlan_filter_set_cmd { uint16_t resvd1; }; +struct sssnic_tcam_enable_set_cmd { + struct sssnic_cmd_common common; + uint16_t function; + uint8_t enabled; + uint8_t resvd[5]; +}; + +struct sssnic_tcam_flush_cmd { + struct sssnic_cmd_common common; + uint16_t function; + uint16_t resvd; +}; + +#define SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_ALLOC 1 +#define SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_FREE 0 +struct sssnic_tcam_block_cfg_cmd { + struct sssnic_cmd_common common; + uint16_t function; + uint8_t flag; /* SSSNIC_TCAM_BLOCK_CFG_CMD_FLAG_XX */ + uint8_t type; + uint16_t idx; + uint16_t resvd; +}; + +struct sssnic_tcam_ptype_filter_set_cmd { + struct sssnic_cmd_common common; + uint16_t function; + uint16_t resvd0; + uint8_t enable; + uint8_t ptype; + uint8_t qid; + uint8_t resvd1; +}; + +struct sssnic_tcam_entry_add_cmd { + struct sssnic_cmd_common common; + uint16_t function; + uint8_t type; + uint8_t resv; + struct { + uint32_t index; + struct { + uint32_t qid; + uint32_t resvd; + } result; + struct { + uint8_t d0[SSSNIC_TCAM_KEY_SIZE]; + uint8_t d1[SSSNIC_TCAM_KEY_SIZE]; + } key; + } data; +}; + +struct sssnic_tcam_entry_del_cmd { + struct sssnic_cmd_common common; + uint16_t function; + uint8_t type; + uint8_t resv; + uint32_t start; /* start index of entry to be deleted */ + uint32_t num; /* number of entries to be deleted */ +}; + #endif /* _SSSNIC_CMD_H_ */ diff --git a/drivers/net/sssnic/base/sssnic_hw.h b/drivers/net/sssnic/base/sssnic_hw.h index 4820212543..6a2d980d5a 100644 --- a/drivers/net/sssnic/base/sssnic_hw.h +++ b/drivers/net/sssnic/base/sssnic_hw.h @@ -96,6 +96,9 @@ enum sssnic_module { SSSNIC_NETIF_MODULE = 14, }; +#define SSSNIC_TCAM_KEY_SIZE 44 +#define SSSNIC_TCAM_MAX_ENTRY_NUM 4096 + int sssnic_hw_init(struct sssnic_hw *hw); void sssnic_hw_shutdown(struct sssnic_hw *hw); void sssnic_msix_state_set(struct sssnic_hw *hw, uint16_t msix_id, int state); diff --git a/drivers/net/sssnic/base/sssnic_misc.h b/drivers/net/sssnic/base/sssnic_misc.h index e30691caef..a1e268710e 100644 --- a/drivers/net/sssnic/base/sssnic_misc.h +++ b/drivers/net/sssnic/base/sssnic_misc.h @@ -42,4 +42,11 @@ sssnic_mem_be_to_cpu_32(void *in, void *out, int size) } } +static inline bool +sssnic_is_zero_ipv6_addr(const void *ipv6_addr) +{ + const uint64_t *ddw = ipv6_addr; + return ddw[0] == 0 && ddw[1] == 0; +} + #endif /* _SSSNIC_MISC_H_ */ diff --git a/drivers/net/sssnic/meson.build b/drivers/net/sssnic/meson.build index 3541b75c30..03d60f08ec 100644 --- a/drivers/net/sssnic/meson.build +++ b/drivers/net/sssnic/meson.build @@ -23,4 +23,6 @@ sources = files( 'sssnic_ethdev_tx.c', 'sssnic_ethdev_stats.c', 'sssnic_ethdev_rss.c', + 'sssnic_ethdev_fdir.c', + 'sssnic_ethdev_flow.c', ) diff --git a/drivers/net/sssnic/sssnic_ethdev.c b/drivers/net/sssnic/sssnic_ethdev.c index 8a1ccff70b..545833fb55 100644 --- a/drivers/net/sssnic/sssnic_ethdev.c +++ b/drivers/net/sssnic/sssnic_ethdev.c @@ -14,6 +14,8 @@ #include "sssnic_ethdev_tx.h" #include "sssnic_ethdev_stats.h" #include "sssnic_ethdev_rss.h" +#include "sssnic_ethdev_fdir.h" +#include "sssnic_ethdev_flow.h" static int sssnic_ethdev_init(struct rte_eth_dev *ethdev); static void sssnic_ethdev_vlan_filter_clean(struct rte_eth_dev *ethdev); @@ -345,6 +347,7 @@ sssnic_ethdev_release(struct rte_eth_dev *ethdev) sssnic_ethdev_link_intr_disable(ethdev); sssnic_ethdev_tx_queue_all_release(ethdev); sssnic_ethdev_rx_queue_all_release(ethdev); + sssnic_ethdev_fdir_shutdown(ethdev); sssnic_ethdev_mac_addrs_clean(ethdev); sssnic_hw_shutdown(hw); rte_free(hw); @@ -951,6 +954,7 @@ static const struct eth_dev_ops sssnic_ethdev_ops = { .flow_ctrl_get = sssnic_ethdev_flow_ctrl_get, .vlan_offload_set = sssnic_ethdev_vlan_offload_set, .vlan_filter_set = sssnic_ethdev_vlan_filter_set, + .flow_ops_get = sssnic_ethdev_flow_ops_get, }; static int @@ -991,6 +995,12 @@ sssnic_ethdev_init(struct rte_eth_dev *ethdev) goto mac_addrs_init_fail; } + ret = sssnic_ethdev_fdir_init(ethdev); + if (ret) { + PMD_DRV_LOG(ERR, "Failed to initialize fdir info"); + goto fdir_init_fail; + } + netdev->max_num_rxq = SSSNIC_MAX_NUM_RXQ(hw); netdev->max_num_txq = SSSNIC_MAX_NUM_TXQ(hw); @@ -1001,6 +1011,8 @@ sssnic_ethdev_init(struct rte_eth_dev *ethdev) return 0; +fdir_init_fail: + sssnic_ethdev_mac_addrs_clean(ethdev); mac_addrs_init_fail: sssnic_hw_shutdown(0); return ret; diff --git a/drivers/net/sssnic/sssnic_ethdev.h b/drivers/net/sssnic/sssnic_ethdev.h index f19b2bd88f..0ca933b53b 100644 --- a/drivers/net/sssnic/sssnic_ethdev.h +++ b/drivers/net/sssnic/sssnic_ethdev.h @@ -82,6 +82,7 @@ struct sssnic_netdev { void *hw; struct rte_ether_addr *mcast_addrs; struct rte_ether_addr default_addr; + struct sssnic_ethdev_fdir_info *fdir_info; uint16_t max_num_txq; uint16_t max_num_rxq; uint16_t num_started_rxqs; diff --git a/drivers/net/sssnic/sssnic_ethdev_fdir.c b/drivers/net/sssnic/sssnic_ethdev_fdir.c new file mode 100644 index 0000000000..cec9fb219f --- /dev/null +++ b/drivers/net/sssnic/sssnic_ethdev_fdir.c @@ -0,0 +1,1017 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2022 Shenzhen 3SNIC Information Technology Co., Ltd. + */ + +#include +#include +#include + +#include "sssnic_log.h" +#include "sssnic_ethdev.h" +#include "sssnic_ethdev_fdir.h" +#include "base/sssnic_hw.h" +#include "base/sssnic_api.h" + +#define SSSNIC_NETDEV_FDIR_INFO(netdev) ((netdev)->fdir_info) +#define SSSNIC_ETHDEV_FDIR_INFO(ethdev) \ + (SSSNIC_NETDEV_FDIR_INFO(SSSNIC_ETHDEV_PRIVATE(ethdev))) + +enum { + SSSNIC_ETHDEV_PTYPE_INVAL = 0, + SSSNIC_ETHDEV_PTYPE_ARP = 1, + SSSNIC_ETHDEV_PTYPE_ARP_REQ = 2, + SSSNIC_ETHDEV_PTYPE_ARP_REP = 3, + SSSNIC_ETHDEV_PTYPE_RARP = 4, + SSSNIC_ETHDEV_PTYPE_LACP = 5, + SSSNIC_ETHDEV_PTYPE_LLDP = 6, + SSSNIC_ETHDEV_PTYPE_OAM = 7, + SSSNIC_ETHDEV_PTYPE_CDCP = 8, + SSSNIC_ETHDEV_PTYPE_CNM = 9, + SSSNIC_ETHDEV_PTYPE_ECP = 10, +}; + +#define SSSNIC_ETHDEV_TCAM_ENTRY_INVAL_IDX 0xffff +struct sssnic_ethdev_fdir_entry { + TAILQ_ENTRY(sssnic_ethdev_fdir_entry) node; + struct sssnic_ethdev_tcam_block *tcam_block; + uint32_t tcam_entry_idx; + int enabled; + struct sssnic_ethdev_fdir_rule *rule; +}; + +#define SSSNIC_ETHDEV_TCAM_BLOCK_SZ 16 +struct sssnic_ethdev_tcam_block { + TAILQ_ENTRY(sssnic_ethdev_tcam_block) node; + uint16_t id; + uint16_t used_entries; + uint8_t entries_status[SSSNIC_ETHDEV_TCAM_BLOCK_SZ]; /* 0: IDLE, 1: USED */ +}; + +struct sssnic_ethdev_tcam { + TAILQ_HEAD(, sssnic_ethdev_tcam_block) block_list; + uint16_t num_blocks; + uint16_t used_entries; /* Count of used entries */ + int enabled; +}; + +struct sssnic_ethdev_fdir_info { + struct rte_eth_dev *ethdev; + struct sssnic_ethdev_tcam tcam; + uint32_t num_entries; + TAILQ_HEAD(, sssnic_ethdev_fdir_entry) ethertype_entry_list; + TAILQ_HEAD(, sssnic_ethdev_fdir_entry) flow_entry_list; +}; + +static int +sssnic_ethdev_tcam_init(struct rte_eth_dev *ethdev) +{ + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + struct sssnic_ethdev_fdir_info *fdir_info; + + fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev); + TAILQ_INIT(&fdir_info->tcam.block_list); + + sssnic_tcam_disable_and_flush(hw); + + return 0; +} + +static void +sssnic_ethdev_tcam_shutdown(struct rte_eth_dev *ethdev) +{ + struct sssnic_ethdev_fdir_info *fdir_info; + struct sssnic_ethdev_tcam *tcam; + struct sssnic_ethdev_tcam_block *block, *tmp; + + fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev); + tcam = &fdir_info->tcam; + + RTE_TAILQ_FOREACH_SAFE(block, &tcam->block_list, node, tmp) + { + TAILQ_REMOVE(&tcam->block_list, block, node); + rte_free(block); + } +} + +static int +sssnic_ethdev_tcam_enable(struct rte_eth_dev *ethdev) +{ + struct sssnic_ethdev_fdir_info *fdir_info; + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + int ret; + + fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev); + + if (!fdir_info->tcam.enabled) { + ret = sssnic_tcam_enable_set(hw, 1); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to enable TCAM"); + return ret; + } + + fdir_info->tcam.enabled = 1; + } + + return 0; +} + +static int +sssnic_ethdev_tcam_disable(struct rte_eth_dev *ethdev) +{ + struct sssnic_ethdev_fdir_info *fdir_info; + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + int ret; + + fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev); + + if (fdir_info->tcam.enabled) { + ret = sssnic_tcam_enable_set(hw, 0); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to enable TCAM"); + return ret; + } + + fdir_info->tcam.enabled = 0; + } + + return 0; +} + +static int +sssnic_ethdev_tcam_block_alloc(struct rte_eth_dev *ethdev, + struct sssnic_ethdev_tcam_block **block) +{ + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + struct sssnic_ethdev_fdir_info *fdir_info = + SSSNIC_ETHDEV_FDIR_INFO(ethdev); + struct sssnic_ethdev_tcam_block *new; + int ret; + + new = rte_zmalloc("sssnic_tcam_block", sizeof(*new), 0); + if (new == NULL) { + PMD_DRV_LOG(ERR, + "Failed to allocate memory for tcam block struct!"); + return -ENOMEM; + } + + ret = sssnic_tcam_block_alloc(hw, &new->id); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to alloc tcam block!"); + rte_free(new); + return ret; + } + + TAILQ_INSERT_HEAD(&fdir_info->tcam.block_list, new, node); + fdir_info->tcam.num_blocks++; + + if (block != NULL) + *block = new; + + return 0; +} + +static int +sssnic_ethdev_tcam_block_free(struct rte_eth_dev *ethdev, + struct sssnic_ethdev_tcam_block *block) +{ + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + struct sssnic_ethdev_fdir_info *fdir_info = + SSSNIC_ETHDEV_FDIR_INFO(ethdev); + int ret; + + ret = sssnic_tcam_block_free(hw, block->id); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to free tcam block:%u!", block->id); + return ret; + } + + TAILQ_REMOVE(&fdir_info->tcam.block_list, block, node); + fdir_info->tcam.num_blocks--; + rte_free(block); + + return 0; +} + +static struct sssnic_ethdev_tcam_block * +sssnic_ethdev_available_tcam_block_lookup(struct sssnic_ethdev_tcam *tcam) +{ + struct sssnic_ethdev_tcam_block *block; + + TAILQ_FOREACH(block, &tcam->block_list, node) + { + if (block->used_entries < SSSNIC_ETHDEV_TCAM_BLOCK_SZ) + return block; + } + + return NULL; +} + +static int +sssnic_ethdev_tcam_block_entry_alloc(struct sssnic_ethdev_tcam_block *block, + uint32_t *entry_idx) +{ + uint32_t i; + + for (i = 0; i < SSSNIC_ETHDEV_TCAM_BLOCK_SZ; i++) { + if (block->entries_status[i] == 0) { + *entry_idx = i; + block->entries_status[i] = 1; + block->used_entries++; + return 0; + } + } + + return -ENOMEM; +} + +static int +sssnic_ethdev_tcam_block_entry_free(struct sssnic_ethdev_tcam_block *block, + uint32_t entry_idx) +{ + if (block != NULL && entry_idx < SSSNIC_ETHDEV_TCAM_BLOCK_SZ) { + if (block->entries_status[entry_idx] == 1) { + block->entries_status[entry_idx] = 0; + block->used_entries--; + return 0; /* found and freed */ + } + } + return -1; /* not found */ +} + +static int +sssnic_ethdev_tcam_entry_alloc(struct rte_eth_dev *ethdev, + struct sssnic_ethdev_tcam_block **block, uint32_t *entry_idx) +{ + struct sssnic_ethdev_fdir_info *fdir_info = + SSSNIC_ETHDEV_FDIR_INFO(ethdev); + struct sssnic_ethdev_tcam *tcam; + struct sssnic_ethdev_tcam_block *tcam_block; + int new_block = 0; + uint32_t eid; + int ret; + + tcam = &fdir_info->tcam; + + if (tcam->num_blocks == 0 || + tcam->used_entries >= + tcam->num_blocks * SSSNIC_ETHDEV_TCAM_BLOCK_SZ) { + ret = sssnic_ethdev_tcam_block_alloc(ethdev, &tcam_block); + if (ret != 0) { + PMD_DRV_LOG(ERR, + "No TCAM memory, used block count: %u, used entries count:%u", + tcam->num_blocks, tcam->used_entries); + return ret; + } + new_block = 1; + } else { + tcam_block = sssnic_ethdev_available_tcam_block_lookup(tcam); + if (tcam_block == NULL) { + PMD_DRV_LOG(CRIT, + "No available TCAM block, used block count:%u, used entries count:%u", + tcam->num_blocks, tcam->used_entries); + return -ENOMEM; + } + } + + ret = sssnic_ethdev_tcam_block_entry_alloc(tcam_block, &eid); + if (ret != 0) { + PMD_DRV_LOG(CRIT, + "No available entry in TCAM block, block idx:%u, used entries:%u", + tcam_block->id, tcam_block->used_entries); + if (unlikely(new_block)) + sssnic_ethdev_tcam_block_free(ethdev, tcam_block); + + return -ENOMEM; + } + + tcam->used_entries++; + + *block = tcam_block; + *entry_idx = eid; + + return 0; +} + +static int +sssnic_ethdev_tcam_entry_free(struct rte_eth_dev *ethdev, + struct sssnic_ethdev_tcam_block *tcam_block, uint32_t entry_idx) +{ + int ret; + struct sssnic_ethdev_fdir_info *fdir_info = + SSSNIC_ETHDEV_FDIR_INFO(ethdev); + struct sssnic_ethdev_tcam *tcam; + + tcam = &fdir_info->tcam; + + ret = sssnic_ethdev_tcam_block_entry_free(tcam_block, entry_idx); + if (ret != 0) + return 0; /* not found was considered as success */ + + if (tcam_block->used_entries == 0) { + ret = sssnic_ethdev_tcam_block_free(ethdev, tcam_block); + if (ret != 0) + PMD_DRV_LOG(ERR, "Failed to free TCAM block:%u", + tcam_block->id); + } + + tcam->used_entries--; + return 0; +} + +static void +sssnic_ethdev_tcam_entry_init(struct sssnic_ethdev_fdir_flow_match *flow, + struct sssnic_tcam_entry *entry) +{ + uint8_t i; + uint8_t *flow_key; + uint8_t *flow_mask; + + flow_key = (uint8_t *)&flow->key; + flow_mask = (uint8_t *)&flow->mask; + + for (i = 0; i < sizeof(entry->key.data0); i++) { + entry->key.data1[i] = flow_key[i] & flow_mask[i]; + entry->key.data0[i] = + entry->key.data1[i] ^ flow_mask[i]; + } +} + + +static struct sssnic_ethdev_fdir_entry * +sssnic_ethdev_fdir_entry_lookup(struct sssnic_ethdev_fdir_info *fdir_info, + struct sssnic_ethdev_fdir_rule *rule) +{ + struct sssnic_ethdev_fdir_entry *e; + struct sssnic_ethdev_fdir_match *m; + struct sssnic_ethdev_fdir_match *match = &rule->match; + + /* fast lookup */ + if (rule->cookie != NULL) + return (struct sssnic_ethdev_fdir_entry *)rule->cookie; + + if (rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_FLOW) { + TAILQ_FOREACH(e, &fdir_info->flow_entry_list, node) + { + m = &e->rule->match; + if (memcmp(&match->flow, &m->flow, sizeof(m->flow)) == + 0) + return e; + } + } else if (rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE) { + TAILQ_FOREACH(e, &fdir_info->ethertype_entry_list, node) + { + m = &e->rule->match; + if (match->ethertype.key.ether_type == + m->ethertype.key.ether_type) + return e; + } + } + + return NULL; +} + +static inline void +sssnic_ethdev_fdir_entry_add(struct sssnic_ethdev_fdir_info *fdir_info, + struct sssnic_ethdev_fdir_entry *entry) +{ + if (entry->rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE) + TAILQ_INSERT_TAIL(&fdir_info->ethertype_entry_list, entry, + node); + else + TAILQ_INSERT_TAIL(&fdir_info->flow_entry_list, entry, node); + + fdir_info->num_entries++; +} + +static inline void +sssnic_ethdev_fdir_entry_del(struct sssnic_ethdev_fdir_info *fdir_info, + struct sssnic_ethdev_fdir_entry *entry) +{ + if (entry->rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE) + TAILQ_REMOVE(&fdir_info->ethertype_entry_list, entry, node); + else + TAILQ_REMOVE(&fdir_info->flow_entry_list, entry, node); + + fdir_info->num_entries--; +} + +static int +sssnic_ethdev_fdir_arp_pkt_filter_set(struct rte_eth_dev *ethdev, uint16_t qid, + int enabled) +{ + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + int ret; + + ret = sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_ARP, + qid, enabled); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to %s ARP packet filter!", + enabled ? "enable" : "disable"); + return ret; + } + + ret = sssnic_tcam_packet_type_filter_set(hw, + SSSNIC_ETHDEV_PTYPE_ARP_REQ, qid, enabled); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to %s ARP request packet filter!", + enabled ? "enable" : "disable"); + goto set_arp_req_fail; + } + + ret = sssnic_tcam_packet_type_filter_set(hw, + SSSNIC_ETHDEV_PTYPE_ARP_REP, qid, enabled); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to %s ARP reply packet filter!", + enabled ? "enable" : "disable"); + goto set_arp_rep_fail; + } + + return 0; + +set_arp_rep_fail: + sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_ARP_REQ, qid, + !enabled); +set_arp_req_fail: + sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_ARP, qid, + !enabled); + + return ret; +} + +static int +sssnic_ethdev_fdir_slow_pkt_filter_set(struct rte_eth_dev *ethdev, uint16_t qid, + int enabled) +{ + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + int ret; + + ret = sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_LACP, + qid, enabled); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to %s LACP packet filter!", + enabled ? "enable" : "disable"); + return ret; + } + + ret = sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_OAM, + qid, enabled); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to %s OAM packet filter!", + enabled ? "enable" : "disable"); + + sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_LACP, + qid, !enabled); + } + + return ret; +} + +static int +sssnic_ethdev_fdir_lldp_pkt_filter_set(struct rte_eth_dev *ethdev, uint16_t qid, + int enabled) +{ + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + int ret; + + ret = sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_LLDP, + qid, enabled); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to %s LLDP packet filter!", + enabled ? "enable" : "disable"); + return ret; + } + + ret = sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_CDCP, + qid, enabled); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to %s CDCP packet filter!", + enabled ? "enable" : "disable"); + + sssnic_tcam_packet_type_filter_set(hw, SSSNIC_ETHDEV_PTYPE_LLDP, + qid, !enabled); + } + + return ret; +} + +static int +sssnic_ethdev_fdir_pkt_filter_set(struct rte_eth_dev *ethdev, + uint16_t ether_type, uint16_t qid, int enabled) +{ + int ret; + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + + switch (ether_type) { + case RTE_ETHER_TYPE_ARP: + ret = sssnic_ethdev_fdir_arp_pkt_filter_set(ethdev, qid, + enabled); + break; + case RTE_ETHER_TYPE_RARP: + ret = sssnic_tcam_packet_type_filter_set(hw, + SSSNIC_ETHDEV_PTYPE_RARP, qid, enabled); + break; + case RTE_ETHER_TYPE_SLOW: + ret = sssnic_ethdev_fdir_slow_pkt_filter_set(ethdev, qid, + enabled); + break; + case RTE_ETHER_TYPE_LLDP: + ret = sssnic_ethdev_fdir_lldp_pkt_filter_set(ethdev, qid, + enabled); + break; + case 0x22e7: /* CNM ether type */ + ret = sssnic_tcam_packet_type_filter_set(hw, + SSSNIC_ETHDEV_PTYPE_CNM, qid, enabled); + break; + case 0x8940: /* ECP ether type */ + ret = sssnic_tcam_packet_type_filter_set(hw, + SSSNIC_ETHDEV_PTYPE_ECP, qid, enabled); + break; + default: + PMD_DRV_LOG(ERR, "Ethertype 0x%x is not supported to filter!", + ether_type); + return -EINVAL; + } + + if (ret != 0) + PMD_DRV_LOG(ERR, "Failed to %s filter for ether type: %x.", + enabled ? "enable" : "disable", ether_type); + + return ret; +} + +static inline struct sssnic_ethdev_fdir_entry * +sssnic_ethdev_fdir_entry_alloc(void) +{ + struct sssnic_ethdev_fdir_entry *e; + + e = rte_zmalloc("sssnic_fdir_entry", sizeof(*e), 0); + if (e != NULL) + e->tcam_entry_idx = SSSNIC_ETHDEV_TCAM_ENTRY_INVAL_IDX; + else + PMD_DRV_LOG(ERR, + "Failed to allocate memory for fdir entry struct!"); + + return e; +} + +static inline void +sssnic_ethdev_fdir_entry_free(struct sssnic_ethdev_fdir_entry *e) +{ + if (e != NULL) + rte_free(e); +} + +/* Apply fdir rule to HW */ +static int +sssnic_ethdev_fdir_entry_enable(struct rte_eth_dev *ethdev, + struct sssnic_ethdev_fdir_entry *entry) +{ + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + struct sssnic_tcam_entry tcam_entry; + int ret; + + if (unlikely(entry->rule == NULL)) { + PMD_DRV_LOG(ERR, "fdir rule is null!"); + return -EINVAL; + } + + if (entry->enabled) + return 0; + + if (entry->tcam_entry_idx != SSSNIC_ETHDEV_TCAM_ENTRY_INVAL_IDX) { + memset(&tcam_entry, 0, sizeof(tcam_entry)); + sssnic_ethdev_tcam_entry_init(&entry->rule->match.flow, + &tcam_entry); + tcam_entry.result.qid = entry->rule->action.qid; + tcam_entry.index = + entry->tcam_entry_idx + + (entry->tcam_block->id * SSSNIC_ETHDEV_TCAM_BLOCK_SZ); + + ret = sssnic_tcam_entry_add(hw, &tcam_entry); + if (ret != 0) + PMD_DRV_LOG(ERR, + "Failed to add TCAM entry, block:%u, entry:%u, tcam_entry:%u", + entry->tcam_block->id, entry->tcam_entry_idx, + tcam_entry.index); + + } else { + ret = sssnic_ethdev_fdir_pkt_filter_set(ethdev, + entry->rule->match.ethertype.key.ether_type, + entry->rule->action.qid, 1); + if (ret != 0) + PMD_DRV_LOG(ERR, "Failed to enable ethertype(%x) filter", + entry->rule->match.ethertype.key.ether_type); + } + + entry->enabled = 1; + + return ret; +} + +/* remove fdir rule from HW */ +static int +sssnic_ethdev_fdir_entry_disable(struct rte_eth_dev *ethdev, + struct sssnic_ethdev_fdir_entry *entry) +{ + struct sssnic_hw *hw = SSSNIC_ETHDEV_TO_HW(ethdev); + uint32_t tcam_entry_idx; + int ret; + + if (unlikely(entry->rule == NULL)) { + PMD_DRV_LOG(ERR, "fdir rule is null!"); + return -EINVAL; + } + + if (!entry->enabled) + return 0; + + if (entry->tcam_entry_idx != SSSNIC_ETHDEV_TCAM_ENTRY_INVAL_IDX) { + tcam_entry_idx = + entry->tcam_entry_idx + + (entry->tcam_block->id * SSSNIC_ETHDEV_TCAM_BLOCK_SZ); + + ret = sssnic_tcam_entry_del(hw, tcam_entry_idx); + if (ret != 0) { + PMD_DRV_LOG(ERR, + "Failed to del TCAM entry, block:%u, entry:%u", + entry->tcam_block->id, entry->tcam_entry_idx); + return ret; + } + } else { + ret = sssnic_ethdev_fdir_pkt_filter_set(ethdev, + entry->rule->match.ethertype.key.ether_type, + entry->rule->action.qid, 0); + if (ret != 0) { + PMD_DRV_LOG(ERR, + "Failed to disable ethertype(%x) filter", + entry->rule->match.ethertype.key.ether_type); + return ret; + } + } + + entry->enabled = 0; + + return 0; +} + +static int +sssnic_ethdev_fdir_ethertype_rule_add(struct sssnic_ethdev_fdir_info *fdir_info, + struct sssnic_ethdev_fdir_rule *rule) +{ + struct sssnic_ethdev_fdir_entry *fdir_entry; + int ret; + + fdir_entry = sssnic_ethdev_fdir_entry_alloc(); + if (fdir_entry == NULL) + return -ENOMEM; + + fdir_entry->rule = rule; + + ret = sssnic_ethdev_fdir_entry_enable(fdir_info->ethdev, fdir_entry); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to enable ethertype(%u) entry", + rule->match.ethertype.key.ether_type); + + sssnic_ethdev_fdir_entry_free(fdir_entry); + + return ret; + } + + rule->cookie = fdir_entry; + sssnic_ethdev_fdir_entry_add(fdir_info, fdir_entry); + + return 0; +} + +static int +sssnic_ethdev_fdir_ethertype_rule_del(struct sssnic_ethdev_fdir_info *fdir_info, + struct sssnic_ethdev_fdir_rule *rule) +{ + struct sssnic_ethdev_fdir_entry *fdir_entry; + int ret; + + fdir_entry = (struct sssnic_ethdev_fdir_entry *)rule->cookie; + + ret = sssnic_ethdev_fdir_entry_disable(fdir_info->ethdev, fdir_entry); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to disable ethertype(%u) entry", + rule->match.ethertype.key.ether_type); + return ret; + } + + rule->cookie = NULL; + sssnic_ethdev_fdir_entry_del(fdir_info, fdir_entry); + sssnic_ethdev_fdir_entry_free(fdir_entry); + + return 0; +} + +static int +sssnic_ethdev_fdir_flow_rule_add(struct sssnic_ethdev_fdir_info *fdir_info, + struct sssnic_ethdev_fdir_rule *rule) +{ + struct sssnic_ethdev_fdir_entry *fdir_entry; + int ret; + + fdir_entry = sssnic_ethdev_fdir_entry_alloc(); + if (fdir_entry == NULL) + return -ENOMEM; + + fdir_entry->rule = rule; + + ret = sssnic_ethdev_tcam_entry_alloc(fdir_info->ethdev, + &fdir_entry->tcam_block, &fdir_entry->tcam_entry_idx); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to alloc TCAM entry"); + goto tcam_entry_alloc_fail; + } + + ret = sssnic_ethdev_fdir_entry_enable(fdir_info->ethdev, fdir_entry); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to enable fdir flow entry"); + goto fdir_entry_enable_fail; + } + + rule->cookie = fdir_entry; + sssnic_ethdev_fdir_entry_add(fdir_info, fdir_entry); + + return 0; + +fdir_entry_enable_fail: + sssnic_ethdev_tcam_entry_free(fdir_info->ethdev, fdir_entry->tcam_block, + fdir_entry->tcam_entry_idx); +tcam_entry_alloc_fail: + sssnic_ethdev_fdir_entry_free(fdir_entry); + + return ret; +} + +static int +sssnic_ethdev_fdir_flow_rule_del(struct sssnic_ethdev_fdir_info *fdir_info, + struct sssnic_ethdev_fdir_rule *rule) +{ + struct sssnic_ethdev_fdir_entry *fdir_entry; + int ret; + + fdir_entry = (struct sssnic_ethdev_fdir_entry *)rule->cookie; + + ret = sssnic_ethdev_fdir_entry_disable(fdir_info->ethdev, fdir_entry); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to disable fdir flow entry"); + return ret; + } + + rule->cookie = NULL; + sssnic_ethdev_fdir_entry_del(fdir_info, fdir_entry); + sssnic_ethdev_fdir_entry_free(fdir_entry); + + return 0; +} + +int +sssnic_ethdev_fdir_rule_add(struct rte_eth_dev *ethdev, + struct sssnic_ethdev_fdir_rule *rule) +{ + struct sssnic_ethdev_fdir_info *fdir_info; + int ret; + + fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev); + + if (sssnic_ethdev_fdir_entry_lookup(fdir_info, rule) != NULL) { + PMD_DRV_LOG(ERR, "FDIR rule exists!"); + return -EEXIST; + } + + if (rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE) { + ret = sssnic_ethdev_fdir_ethertype_rule_add(fdir_info, rule); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to add fdir ethertype rule"); + return ret; + } + PMD_DRV_LOG(DEBUG, + "Added fdir ethertype rule, total number of rules: %u", + fdir_info->num_entries); + } else { + ret = sssnic_ethdev_fdir_flow_rule_add(fdir_info, rule); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to add fdir flow rule"); + return ret; + } + PMD_DRV_LOG(DEBUG, + "Added fdir flow rule, total number of rules: %u", + fdir_info->num_entries); + } + + ret = sssnic_ethdev_tcam_enable(ethdev); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to enable TCAM"); + sssnic_ethdev_fdir_flow_rule_del(fdir_info, rule); + } + + return ret; +} + +int +sssnic_ethdev_fdir_rule_del(struct rte_eth_dev *ethdev, + struct sssnic_ethdev_fdir_rule *fdir_rule) +{ + struct sssnic_ethdev_fdir_info *fdir_info; + struct sssnic_ethdev_fdir_entry *entry; + struct sssnic_ethdev_fdir_rule *rule; + int ret; + + fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev); + + entry = sssnic_ethdev_fdir_entry_lookup(fdir_info, fdir_rule); + if (entry == NULL) + return 0; + + rule = entry->rule; + if (rule != fdir_rule) + return 0; + + if (rule->match.type == SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE) { + ret = sssnic_ethdev_fdir_ethertype_rule_del(fdir_info, rule); + if (ret != 0) { + PMD_DRV_LOG(ERR, + "Failed to delete fdir ethertype rule!"); + return ret; + } + PMD_DRV_LOG(DEBUG, + "Deleted fdir ethertype rule, total number of rules: %u", + fdir_info->num_entries); + } else { + ret = sssnic_ethdev_fdir_flow_rule_del(fdir_info, rule); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to delete fdir flow rule!"); + return ret; + } + PMD_DRV_LOG(DEBUG, + "Deleted fdir flow rule, total number of rules: %u", + fdir_info->num_entries); + } + + /* if there are no added rules, then disable TCAM */ + if (fdir_info->num_entries == 0) { + ret = sssnic_ethdev_tcam_disable(ethdev); + if (ret != 0) { + PMD_DRV_LOG(NOTICE, + "There are no added rules, but failed to disable TCAM"); + ret = 0; + } + } + + return ret; +} + +int +sssnic_ethdev_fdir_rules_disable_by_queue(struct rte_eth_dev *ethdev, + uint16_t qid) +{ + struct sssnic_ethdev_fdir_info *fdir_info; + struct sssnic_ethdev_fdir_entry *entry; + int ret; + + fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev); + + TAILQ_FOREACH(entry, &fdir_info->flow_entry_list, node) + { + if (entry->rule->action.qid == qid) { + ret = sssnic_ethdev_fdir_entry_disable(ethdev, entry); + if (ret != 0) { + PMD_DRV_LOG(ERR, + "Failed to disable flow rule of queue:%u", + qid); + + return ret; + } + } + } + + return 0; +} + +int +sssnic_ethdev_fdir_rules_enable_by_queue(struct rte_eth_dev *ethdev, + uint16_t qid) +{ + struct sssnic_ethdev_fdir_info *fdir_info; + struct sssnic_ethdev_fdir_entry *entry; + int ret; + + fdir_info = SSSNIC_ETHDEV_FDIR_INFO(ethdev); + + TAILQ_FOREACH(entry, &fdir_info->flow_entry_list, node) + { + if (entry->rule->action.qid == qid) { + ret = sssnic_ethdev_fdir_entry_enable(ethdev, entry); + if (ret != 0) { + PMD_DRV_LOG(ERR, + "Failed to enable flow rule of queue:%u", + qid); + + return ret; + } + } + } + + return 0; +} + +int +sssnic_ethdev_fdir_rules_flush(struct rte_eth_dev *ethdev) +{ + struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev); + struct sssnic_ethdev_fdir_entry *entry, *tmp; + struct sssnic_ethdev_fdir_rule *rule; + int ret; + + RTE_TAILQ_FOREACH_SAFE(entry, &netdev->fdir_info->flow_entry_list, node, + tmp) + { + rule = entry->rule; + ret = sssnic_ethdev_fdir_entry_disable(ethdev, entry); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to disable fdir flow entry"); + return ret; + } + TAILQ_REMOVE(&netdev->fdir_info->flow_entry_list, entry, node); + sssnic_ethdev_fdir_entry_free(entry); + sssnic_ethdev_fdir_rule_free(rule); + } + + RTE_TAILQ_FOREACH_SAFE(entry, &netdev->fdir_info->ethertype_entry_list, + node, tmp) + { + rule = entry->rule; + ret = sssnic_ethdev_fdir_entry_disable(ethdev, entry); + if (ret != 0) { + PMD_DRV_LOG(ERR, "Failed to disable ethertype(%u) entry", + rule->match.ethertype.key.ether_type); + return ret; + } + TAILQ_REMOVE(&netdev->fdir_info->ethertype_entry_list, entry, + node); + sssnic_ethdev_fdir_entry_free(entry); + sssnic_ethdev_fdir_rule_free(rule); + } + + return 0; +} + +int +sssnic_ethdev_fdir_init(struct rte_eth_dev *ethdev) +{ + struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev); + + PMD_INIT_FUNC_TRACE(); + + netdev->fdir_info = rte_zmalloc("sssnic_fdir_info", + sizeof(struct sssnic_ethdev_fdir_info), 0); + + if (netdev->fdir_info == NULL) { + PMD_DRV_LOG(ERR, "Failed to alloc fdir info memory for port %u", + ethdev->data->port_id); + return -ENOMEM; + } + + netdev->fdir_info->ethdev = ethdev; + + TAILQ_INIT(&netdev->fdir_info->ethertype_entry_list); + TAILQ_INIT(&netdev->fdir_info->flow_entry_list); + + sssnic_ethdev_tcam_init(ethdev); + + return 0; +} + +void +sssnic_ethdev_fdir_shutdown(struct rte_eth_dev *ethdev) +{ + struct sssnic_netdev *netdev = SSSNIC_ETHDEV_PRIVATE(ethdev); + struct sssnic_ethdev_fdir_entry *entry, *tmp; + + PMD_INIT_FUNC_TRACE(); + + if (netdev->fdir_info == NULL) + return; + + RTE_TAILQ_FOREACH_SAFE(entry, &netdev->fdir_info->flow_entry_list, node, + tmp) + { + TAILQ_REMOVE(&netdev->fdir_info->flow_entry_list, entry, node); + sssnic_ethdev_fdir_entry_free(entry); + } + + RTE_TAILQ_FOREACH_SAFE(entry, &netdev->fdir_info->ethertype_entry_list, + node, tmp) + { + TAILQ_REMOVE(&netdev->fdir_info->ethertype_entry_list, entry, + node); + sssnic_ethdev_fdir_entry_free(entry); + } + + sssnic_ethdev_tcam_shutdown(ethdev); + + rte_free(netdev->fdir_info); +} diff --git a/drivers/net/sssnic/sssnic_ethdev_fdir.h b/drivers/net/sssnic/sssnic_ethdev_fdir.h new file mode 100644 index 0000000000..aaf426b8f2 --- /dev/null +++ b/drivers/net/sssnic/sssnic_ethdev_fdir.h @@ -0,0 +1,332 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2022 Shenzhen 3SNIC Information Technology Co., Ltd. + */ + +#ifndef _SSSNIC_ETHDEV_FDIR_H_ +#define _SSSNIC_ETHDEV_FDIR_H_ + +#define SSSINC_ETHDEV_FDIR_FLOW_KEY_SIZE 44 +#define SSSNIC_ETHDEV_FDIR_FLOW_KEY_NUM_DW \ + (SSSINC_ETHDEV_FDIR_FLOW_KEY_SIZE / sizeof(uint32_t)) + +enum sssnic_ethdev_fdir_match_type { + SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE = RTE_ETH_FILTER_ETHERTYPE, + SSSNIC_ETHDEV_FDIR_MATCH_FLOW = RTE_ETH_FILTER_FDIR, +}; + +enum sssnic_ethdev_fdir_flow_ip_type { + SSSNIC_ETHDEV_FDIR_FLOW_IPV4 = 0, + SSSNIC_ETHDEV_FDIR_FLOW_IPV6 = 1, +}; + +enum sssnic_ethdev_fdir_flow_tunnel_type { + SSSNIC_ETHDEV_FDIR_FLOW_TUNNEL_NONE = 0, + SSSNIC_ETHDEV_FDIR_FLOW_TUNNEL_VXLAN = 1, +}; + +#define SSSNIC_ETHDEV_FDIR_FLOW_FUNC_ID_MASK 0x7fff +#define SSSNIC_ETHDEV_FDIR_FLOW_IP_TYPE_MASK 0x1 +#define SSSNIC_ETHDEV_FDIR_FLOW_TUNNEL_TYPE_MASK 0xf + +struct sssnic_ethdev_fdir_ethertype_key { + uint16_t ether_type; +}; + +struct sssnic_ethdev_fdir_ipv4_flow_key { +#if (RTE_BYTE_ORDER == RTE_BIG_ENDIAN) + uint32_t resvd0 : 16; + uint32_t ip_proto : 8; + uint32_t tunnel_type : 4; + uint32_t resvd1 : 4; + + uint32_t func_id : 15; + uint32_t ip_type : 1; + uint32_t sip_w1 : 16; + + uint32_t sip_w0 : 16; + uint32_t dip_w1 : 16; + + uint32_t dip_w0 : 16; + uint32_t resvd2 : 16; + + uint32_t resvd3; + + uint32_t resvd4 : 16; + uint32_t dport : 16; + + uint32_t sport : 16; + uint32_t resvd5 : 16; + + uint32_t resvd6 : 16; + uint32_t outer_sip_w1 : 16; + + uint32_t outer_sip_w0 : 16; + uint32_t outer_dip_w1 : 16; + + uint32_t outer_dip_w0 : 16; + uint32_t vni_w1 : 16; + + uint32_t vni_w0 : 16; + uint32_t resvd7 : 16; +#else + uint32_t resvd1 : 4; + uint32_t tunnel_type : 4; + uint32_t ip_proto : 8; + uint32_t resvd0 : 16; + + uint32_t sip_w1 : 16; + uint32_t ip_type : 1; + uint32_t func_id : 15; + + uint32_t dip_w1 : 16; + uint32_t sip_w0 : 16; + + uint32_t resvd2 : 16; + uint32_t dip_w0 : 16; + + uint32_t rsvd3; + + uint32_t dport : 16; + uint32_t resvd4 : 16; + + uint32_t resvd5 : 16; + uint32_t sport : 16; + + uint32_t outer_sip_w1 : 16; + uint32_t resvd6 : 16; + + uint32_t outer_dip_w1 : 16; + uint32_t outer_sip_w0 : 16; + + uint32_t vni_w1 : 16; + uint32_t outer_dip_w0 : 16; + + uint32_t resvd7 : 16; + uint32_t vni_w0 : 16; +#endif +}; + +struct sssnic_ethdev_fdir_ipv6_flow_key { +#if (RTE_BYTE_ORDER == RTE_BIG_ENDIAN) + uint32_t resvd0 : 16; + uint32_t ip_proto : 8; + uint32_t tunnel_type : 4; + uint32_t resvd1 : 4; + + uint32_t func_id : 15; + uint32_t ip_type : 1; + uint32_t sip6_w0 : 16; + + uint32_t sip6_w1 : 16; + uint32_t sip6_w2 : 16; + + uint32_t sip6_w3 : 16; + uint32_t sip6_w4 : 16; + + uint32_t sip6_w5 : 16; + uint32_t sip6_w6 : 16; + + uint32_t sip6_w7 : 16; + uint32_t dport : 16; + + uint32_t sport : 16; + uint32_t dip6_w0 : 16; + + uint32_t dip6_w1 : 16; + uint32_t dip6_w2 : 16; + + uint32_t dip6_w3 : 16; + uint32_t dip6_w4 : 16; + + uint32_t dip6_w5 : 16; + uint32_t dip6_w6 : 16; + + uint32_t dip6_w7 : 16; + uint32_t resvd2 : 16; +#else + uint32_t resvd1 : 4; + uint32_t tunnel_type : 4; + uint32_t ip_proto : 8; + uint32_t resvd0 : 16; + + uint32_t sip6_w0 : 16; + uint32_t ip_type : 1; + uint32_t func_id : 15; + + uint32_t sip6_w2 : 16; + uint32_t sip6_w1 : 16; + + uint32_t sip6_w4 : 16; + uint32_t sip6_w3 : 16; + + uint32_t sip6_w6 : 16; + uint32_t sip6_w5 : 16; + + uint32_t dport : 16; + uint32_t sip6_w7 : 16; + + uint32_t dip6_w0 : 16; + uint32_t sport : 16; + + uint32_t dip6_w2 : 16; + uint32_t dip6_w1 : 16; + + uint32_t dip6_w4 : 16; + uint32_t dip6_w3 : 16; + + uint32_t dip6_w6 : 16; + uint32_t dip6_w5 : 16; + + uint32_t resvd2 : 16; + uint32_t dip6_w7 : 16; +#endif +}; + +struct sssnic_ethdev_fdir_vxlan_ipv6_flow_key { +#if (RTE_BYTE_ORDER == RTE_BIG_ENDIAN) + uint32_t resvd0 : 16; + uint32_t ip_proto : 8; + uint32_t tunnel_type : 4; + uint32_t resvd1 : 4; + + uint32_t func_id : 15; + uint32_t ip_type : 1; + uint32_t dip6_w0 : 16; + + uint32_t dip6_w1 : 16; + uint32_t dip6_w2 : 16; + + uint32_t dip6_w3 : 16; + uint32_t dip6_w4 : 16; + + uint32_t dip6_w5 : 16; + uint32_t dip6_w6 : 16; + + uint32_t dip6_w7 : 16; + uint32_t dport : 16; + + uint32_t sport : 16; + uint32_t resvd2 : 16; + + uint32_t resvd3 : 16; + uint32_t outer_sip_w1 : 16; + + uint32_t outer_sip_w0 : 16; + uint32_t outer_dip_w1 : 16; + + uint32_t outer_dip_w0 : 16; + uint32_t vni_w1 : 16; + + uint32_t vni_w0 : 16; + uint32_t resvd4 : 16; +#else + uint32_t rsvd1 : 4; + uint32_t tunnel_type : 4; + uint32_t ip_proto : 8; + uint32_t resvd0 : 16; + + uint32_t dip6_w0 : 16; + uint32_t ip_type : 1; + uint32_t function_id : 15; + + uint32_t dip6_w2 : 16; + uint32_t dip6_w1 : 16; + + uint32_t dip6_w4 : 16; + uint32_t dip6_w3 : 16; + + uint32_t dip6_w6 : 16; + uint32_t dip6_w5 : 16; + + uint32_t dport : 16; + uint32_t dip6_w7 : 16; + + uint32_t resvd2 : 16; + uint32_t sport : 16; + + uint32_t outer_sip_w1 : 16; + uint32_t resvd3 : 16; + + uint32_t outer_dip_w1 : 16; + uint32_t outer_sip_w0 : 16; + + uint32_t vni_w1 : 16; + uint32_t outer_dip_w0 : 16; + + uint32_t resvd4 : 16; + uint32_t vni_w0 : 16; +#endif +}; + +struct sssnic_ethdev_fdir_flow_key { + union { + uint32_t dword[SSSNIC_ETHDEV_FDIR_FLOW_KEY_NUM_DW]; + struct { + struct sssnic_ethdev_fdir_ipv4_flow_key ipv4; + struct sssnic_ethdev_fdir_ipv6_flow_key ipv6; + struct sssnic_ethdev_fdir_vxlan_ipv6_flow_key vxlan_ipv6; + }; + }; +}; + +struct sssnic_ethdev_fdir_flow_match { + struct sssnic_ethdev_fdir_flow_key key; + struct sssnic_ethdev_fdir_flow_key mask; +}; + +struct sssnic_ethdev_fdir_ethertype_match { + struct sssnic_ethdev_fdir_ethertype_key key; +}; + +struct sssnic_ethdev_fdir_match { + enum sssnic_ethdev_fdir_match_type type; + union { + struct sssnic_ethdev_fdir_flow_match flow; + struct sssnic_ethdev_fdir_ethertype_match ethertype; + }; +}; + +struct sssnic_ethdev_fdir_action { + uint16_t qid; +}; + +/* struct sssnic_ethdev_fdir_rule must be dynamically allocated in the heap */ +struct sssnic_ethdev_fdir_rule { + struct sssnic_ethdev_fdir_match match; + struct sssnic_ethdev_fdir_action action; + void *cookie; /* low level data, initial value must be set to NULL*/ +}; + +struct sssnic_ethdev_fdir_info; + +static inline struct sssnic_ethdev_fdir_rule * +sssnic_ethdev_fdir_rule_alloc(void) +{ + struct sssnic_ethdev_fdir_rule *rule; + + rule = rte_zmalloc("sssnic_fdir_rule", + sizeof(struct sssnic_ethdev_fdir_rule), 0); + + return rule; +} + +static inline void +sssnic_ethdev_fdir_rule_free(struct sssnic_ethdev_fdir_rule *rule) +{ + if (rule != NULL) + rte_free(rule); +} + +int sssnic_ethdev_fdir_rules_disable_by_queue(struct rte_eth_dev *ethdev, + uint16_t qid); +int sssnic_ethdev_fdir_rules_enable_by_queue(struct rte_eth_dev *ethdev, + uint16_t qid); +int sssnic_ethdev_fdir_rule_add(struct rte_eth_dev *ethdev, + struct sssnic_ethdev_fdir_rule *rule); +int sssnic_ethdev_fdir_rule_del(struct rte_eth_dev *ethdev, + struct sssnic_ethdev_fdir_rule *fdir_rule); +int sssnic_ethdev_fdir_rules_flush(struct rte_eth_dev *ethdev); +int sssnic_ethdev_fdir_init(struct rte_eth_dev *ethdev); +void sssnic_ethdev_fdir_shutdown(struct rte_eth_dev *ethdev); + +#endif /* _SSSNIC_ETHDEV_FDIR_H_ */ diff --git a/drivers/net/sssnic/sssnic_ethdev_flow.c b/drivers/net/sssnic/sssnic_ethdev_flow.c new file mode 100644 index 0000000000..372a5bed6b --- /dev/null +++ b/drivers/net/sssnic/sssnic_ethdev_flow.c @@ -0,0 +1,981 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2022 Shenzhen 3SNIC Information Technology Co., Ltd. + */ + +#include +#include +#include + +#include "sssnic_log.h" +#include "sssnic_ethdev.h" +#include "sssnic_ethdev_fdir.h" +#include "sssnic_ethdev_flow.h" +#include "base/sssnic_hw.h" +#include "base/sssnic_api.h" +#include "base/sssnic_misc.h" + +struct rte_flow { + struct sssnic_ethdev_fdir_rule rule; +}; + +static enum rte_flow_item_type pattern_ethertype[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_tcp[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_TCP, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_any[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_ANY, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv4[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_udp[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_tcp[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_TCP, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_any[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_ANY, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv4_tcp[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_TCP, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv4_udp[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv6[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV6, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv6_tcp[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV6, + RTE_FLOW_ITEM_TYPE_TCP, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv4_udp_vxlan_eth_ipv6_udp[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV4, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_VXLAN, + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV6, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv6[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV6, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv6_udp[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV6, + RTE_FLOW_ITEM_TYPE_UDP, + RTE_FLOW_ITEM_TYPE_END, +}; + +static enum rte_flow_item_type pattern_eth_ipv6_tcp[] = { + RTE_FLOW_ITEM_TYPE_ETH, + RTE_FLOW_ITEM_TYPE_IPV6, + RTE_FLOW_ITEM_TYPE_TCP, + RTE_FLOW_ITEM_TYPE_END, +}; + +enum sssnic_ethdev_flow_type { + SSSNIC_ETHDEV_FLOW_TYPE_UNKNOWN = -1, + SSSNIC_ETHDEV_FLOW_TYPE_ETHERTYPE, + SSSNIC_ETHDEV_FLOW_TYPE_FDIR, + SSSNIC_ETHDEV_FLOW_TYPE_COUNT, +}; + +struct sssnic_ethdev_flow_pattern { + enum rte_flow_item_type *flow_items; + enum sssnic_ethdev_flow_type type; + bool is_tunnel; +}; + +static struct sssnic_ethdev_flow_pattern supported_flow_patterns[] = { + { pattern_ethertype, SSSNIC_ETHDEV_FLOW_TYPE_ETHERTYPE, false }, + { pattern_eth_ipv4, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false }, + { pattern_eth_ipv4_udp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false }, + { pattern_eth_ipv4_tcp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false }, + { pattern_eth_ipv4_any, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false }, + { pattern_eth_ipv4_udp_vxlan, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, true }, + { pattern_eth_ipv4_udp_vxlan_udp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, true }, + { pattern_eth_ipv4_udp_vxlan_tcp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, true }, + { pattern_eth_ipv4_udp_vxlan_any, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, true }, + { pattern_eth_ipv4_udp_vxlan_eth_ipv4, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, + true }, + { pattern_eth_ipv4_udp_vxlan_eth_ipv4_tcp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, + true }, + { pattern_eth_ipv4_udp_vxlan_eth_ipv4_udp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, + true }, + { pattern_eth_ipv4_udp_vxlan_eth_ipv6, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, + true }, + { pattern_eth_ipv4_udp_vxlan_eth_ipv6_tcp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, + true }, + { pattern_eth_ipv4_udp_vxlan_eth_ipv6_udp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, + true }, + { pattern_eth_ipv6, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false }, + { pattern_eth_ipv6_udp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false }, + { pattern_eth_ipv6_tcp, SSSNIC_ETHDEV_FLOW_TYPE_FDIR, false }, +}; + +static bool +sssnic_ethdev_flow_pattern_match(enum rte_flow_item_type *item_array, + const struct rte_flow_item *pattern) +{ + const struct rte_flow_item *item = pattern; + + /* skip void items in the head of pattern */ + while (item->type == RTE_FLOW_ITEM_TYPE_VOID) + item++; + + while ((*item_array == item->type) && + (*item_array != RTE_FLOW_ITEM_TYPE_END)) { + item_array++; + item++; + } + + return (*item_array == RTE_FLOW_ITEM_TYPE_END && + item->type == RTE_FLOW_ITEM_TYPE_END); +} + +static struct sssnic_ethdev_flow_pattern * +sssnic_ethdev_flow_pattern_lookup(const struct rte_flow_item *pattern) +{ + struct sssnic_ethdev_flow_pattern *flow_pattern; + enum rte_flow_item_type *flow_items; + size_t i; + + for (i = 0; i < RTE_DIM(supported_flow_patterns); i++) { + flow_pattern = &supported_flow_patterns[i]; + flow_items = flow_pattern->flow_items; + if (sssnic_ethdev_flow_pattern_match(flow_items, pattern)) + return flow_pattern; + } + + return NULL; +} + +static int +sssnic_ethdev_flow_action_parse(struct rte_eth_dev *ethdev, + const struct rte_flow_action *actions, struct rte_flow_error *error, + struct sssnic_ethdev_fdir_rule *fdir_rule) +{ + const struct rte_flow_action_queue *action_queue; + const struct rte_flow_action *action = actions; + + if (action->type != RTE_FLOW_ACTION_TYPE_QUEUE) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + NULL, + "Unsupported action type, only support action queue"); + return -EINVAL; + } + + action_queue = (const struct rte_flow_action_queue *)action->conf; + if (action_queue->index >= ethdev->data->nb_rx_queues) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + NULL, "Invalid queue index"); + return -EINVAL; + } + + if (fdir_rule != NULL) + fdir_rule->action.qid = action_queue->index; + + return 0; +} + +static int +sssnic_ethdev_flow_ethertype_pattern_parse(const struct rte_flow_item *pattern, + struct rte_flow_error *error, struct sssnic_ethdev_fdir_rule *fdir_rule) +{ + const struct rte_flow_item *item = pattern; + const struct rte_flow_item_eth *spec, *mask; + struct sssnic_ethdev_fdir_ethertype_match *fdir_match; + + while (item->type != RTE_FLOW_ITEM_TYPE_ETH) + item++; + + spec = (const struct rte_flow_item_eth *)item->spec; + mask = (const struct rte_flow_item_eth *)item->mask; + + if (item->last != NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_LAST, + item, "Not support range"); + return -rte_errno; + } + + if (spec == NULL || mask == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_LAST, + item, "Ether mask or spec is NULL"); + return -rte_errno; + } + + if (!rte_is_zero_ether_addr(&mask->src) || + !rte_is_zero_ether_addr(&mask->dst)) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + item, "Invalid ether address mask"); + return -rte_errno; + } + + if (mask->type != 0xffff) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, + item, "Invalid ether type mask"); + return -rte_errno; + } + + if (fdir_rule != NULL) { + fdir_rule->match.type = SSSNIC_ETHDEV_FDIR_MATCH_ETHERTYPE; + fdir_match = &fdir_rule->match.ethertype; + fdir_match->key.ether_type = rte_be_to_cpu_16(spec->type); + } + + return 0; +} + +static int +sssnic_ethdev_flow_eth_parse(const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + if (item->spec != NULL || item->mask != NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + item, "Not support eth match in fdir flow"); + return -rte_errno; + } + + return 0; +} + +static int +sssnic_ethdev_flow_ipv4_parse(const struct rte_flow_item *item, + struct rte_flow_error *error, bool outer, + struct sssnic_ethdev_fdir_flow_match *fdir_match) +{ + const struct rte_flow_item_ipv4 *spec, *mask; + uint32_t ip_addr; + + spec = (const struct rte_flow_item_ipv4 *)item->spec; + mask = (const struct rte_flow_item_ipv4 *)item->mask; + + if (outer) { + /* only tunnel flow has outer ipv4 */ + if (spec == NULL && mask == NULL) + return 0; + + if (spec == NULL || mask == NULL) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Invalid IPV4 spec or mask"); + return -rte_errno; + } + + if (mask->hdr.version_ihl || mask->hdr.type_of_service || + mask->hdr.total_length || mask->hdr.packet_id || + mask->hdr.fragment_offset || mask->hdr.time_to_live || + mask->hdr.next_proto_id || mask->hdr.hdr_checksum) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Only support outer IPv4 src and dest address for tunnel flow"); + return -rte_errno; + } + + if (fdir_match != NULL) { + ip_addr = rte_be_to_cpu_32(spec->hdr.src_addr); + fdir_match->key.ipv4.outer_sip_w0 = (uint16_t)ip_addr; + fdir_match->key.ipv4.outer_sip_w1 = + (uint16_t)(ip_addr >> 16); + + ip_addr = rte_be_to_cpu_32(mask->hdr.src_addr); + fdir_match->mask.ipv4.outer_sip_w0 = (uint16_t)ip_addr; + fdir_match->mask.ipv4.outer_sip_w1 = + (uint16_t)(ip_addr >> 16); + } + } else { + /* inner ip of tunnel flow or ip of non tunnel flow */ + if (spec == NULL && mask == NULL) + return 0; + + if (spec == NULL || mask == NULL) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Invalid IPV4 spec or mask"); + return -rte_errno; + } + + if (mask->hdr.version_ihl || mask->hdr.type_of_service || + mask->hdr.total_length || mask->hdr.packet_id || + mask->hdr.fragment_offset || mask->hdr.time_to_live || + mask->hdr.hdr_checksum) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Only support IPv4 address and ipproto"); + return -rte_errno; + } + + if (fdir_match != NULL) { + ip_addr = rte_be_to_cpu_32(spec->hdr.src_addr); + fdir_match->key.ipv4.sip_w0 = (uint16_t)ip_addr; + fdir_match->key.ipv4.sip_w1 = (uint16_t)(ip_addr >> 16); + + ip_addr = rte_be_to_cpu_32(mask->hdr.src_addr); + fdir_match->mask.ipv4.sip_w0 = (uint16_t)ip_addr; + fdir_match->mask.ipv4.sip_w1 = + (uint16_t)(ip_addr >> 16); + + fdir_match->key.ipv4.ip_proto = spec->hdr.next_proto_id; + fdir_match->mask.ipv4.ip_proto = + mask->hdr.next_proto_id; + + fdir_match->key.ipv4.ip_type = + SSSNIC_ETHDEV_FDIR_FLOW_IPV4; + fdir_match->mask.ipv4.ip_type = 0x1; + } + } + + return 0; +} + +static int +sssnic_ethdev_flow_ipv6_parse(const struct rte_flow_item *item, + struct rte_flow_error *error, bool is_tunnel, + struct sssnic_ethdev_fdir_flow_match *fdir_match) +{ + const struct rte_flow_item_ipv6 *spec, *mask; + uint32_t ipv6_addr[4]; + int i; + + mask = (const struct rte_flow_item_ipv6 *)item->mask; + spec = (const struct rte_flow_item_ipv6 *)item->spec; + + if (fdir_match != NULL) { + /* ip_type of ipv6 flow_match can share with other flow_matches */ + fdir_match->key.ipv6.ip_type = SSSNIC_ETHDEV_FDIR_FLOW_IPV6; + fdir_match->mask.ipv6.ip_type = 0x1; + } + + if (is_tunnel) { + if (mask == NULL && spec == NULL) + return 0; + + if (spec == NULL || mask == NULL) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Invalid IPV6 spec or mask"); + return -rte_errno; + } + + if (mask->hdr.vtc_flow || mask->hdr.payload_len || + mask->hdr.hop_limits || + !sssnic_is_zero_ipv6_addr(mask->hdr.src_addr)) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Only support IPv6 dest_addr and ipproto in tunnel flow"); + return -rte_errno; + } + + if (fdir_match != NULL) { + rte_memcpy(ipv6_addr, spec->hdr.dst_addr, + sizeof(ipv6_addr)); + for (i = 0; i < 4; i++) + ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]); + + fdir_match->key.vxlan_ipv6.dip6_w0 = + (uint16_t)ipv6_addr[0]; + fdir_match->key.vxlan_ipv6.dip6_w1 = + (uint16_t)(ipv6_addr[0] >> 16); + fdir_match->key.vxlan_ipv6.dip6_w2 = + (uint16_t)ipv6_addr[1]; + fdir_match->key.vxlan_ipv6.dip6_w3 = + (uint16_t)(ipv6_addr[1] >> 16); + fdir_match->key.vxlan_ipv6.dip6_w4 = + (uint16_t)ipv6_addr[2]; + fdir_match->key.vxlan_ipv6.dip6_w5 = + (uint16_t)(ipv6_addr[2] >> 16); + fdir_match->key.vxlan_ipv6.dip6_w6 = + (uint16_t)ipv6_addr[3]; + fdir_match->key.vxlan_ipv6.dip6_w7 = + (uint16_t)(ipv6_addr[3] >> 16); + + rte_memcpy(ipv6_addr, mask->hdr.dst_addr, + sizeof(ipv6_addr)); + for (i = 0; i < 4; i++) + ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]); + + fdir_match->mask.vxlan_ipv6.dip6_w0 = + (uint16_t)ipv6_addr[0]; + fdir_match->mask.vxlan_ipv6.dip6_w1 = + (uint16_t)(ipv6_addr[0] >> 16); + fdir_match->mask.vxlan_ipv6.dip6_w2 = + (uint16_t)ipv6_addr[1]; + fdir_match->mask.vxlan_ipv6.dip6_w3 = + (uint16_t)(ipv6_addr[1] >> 16); + fdir_match->mask.vxlan_ipv6.dip6_w4 = + (uint16_t)ipv6_addr[2]; + fdir_match->mask.vxlan_ipv6.dip6_w5 = + (uint16_t)(ipv6_addr[2] >> 16); + fdir_match->mask.vxlan_ipv6.dip6_w6 = + (uint16_t)ipv6_addr[3]; + fdir_match->mask.vxlan_ipv6.dip6_w7 = + (uint16_t)(ipv6_addr[3] >> 16); + + fdir_match->key.vxlan_ipv6.ip_proto = spec->hdr.proto; + fdir_match->mask.vxlan_ipv6.ip_proto = mask->hdr.proto; + } + } else { /* non tunnel */ + if (spec == NULL || mask == NULL) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Invalid IPV6 spec or mask"); + return -rte_errno; + } + + if (mask->hdr.vtc_flow || mask->hdr.payload_len || + mask->hdr.hop_limits) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Only support IPv6 addr and ipproto"); + return -rte_errno; + } + + if (fdir_match != NULL) { + rte_memcpy(ipv6_addr, spec->hdr.dst_addr, + sizeof(ipv6_addr)); + for (i = 0; i < 4; i++) + ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]); + + fdir_match->key.ipv6.dip6_w0 = (uint16_t)ipv6_addr[0]; + fdir_match->key.ipv6.dip6_w1 = + (uint16_t)(ipv6_addr[0] >> 16); + fdir_match->key.ipv6.dip6_w2 = (uint16_t)ipv6_addr[1]; + fdir_match->key.ipv6.dip6_w3 = + (uint16_t)(ipv6_addr[1] >> 16); + fdir_match->key.ipv6.dip6_w4 = (uint16_t)ipv6_addr[2]; + fdir_match->key.ipv6.dip6_w5 = + (uint16_t)(ipv6_addr[2] >> 16); + fdir_match->key.ipv6.dip6_w6 = (uint16_t)ipv6_addr[3]; + fdir_match->key.ipv6.dip6_w7 = + (uint16_t)(ipv6_addr[3] >> 16); + + rte_memcpy(ipv6_addr, spec->hdr.src_addr, + sizeof(ipv6_addr)); + for (i = 0; i < 4; i++) + ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]); + + fdir_match->key.ipv6.sip6_w0 = (uint16_t)ipv6_addr[0]; + fdir_match->key.ipv6.sip6_w1 = + (uint16_t)(ipv6_addr[0] >> 16); + fdir_match->key.ipv6.sip6_w2 = (uint16_t)ipv6_addr[1]; + fdir_match->key.ipv6.sip6_w3 = + (uint16_t)(ipv6_addr[1] >> 16); + fdir_match->key.ipv6.sip6_w4 = (uint16_t)ipv6_addr[2]; + fdir_match->key.ipv6.sip6_w5 = + (uint16_t)(ipv6_addr[2] >> 16); + fdir_match->key.ipv6.sip6_w6 = (uint16_t)ipv6_addr[3]; + fdir_match->key.ipv6.sip6_w7 = + (uint16_t)(ipv6_addr[3] >> 16); + + rte_memcpy(ipv6_addr, mask->hdr.dst_addr, + sizeof(ipv6_addr)); + for (i = 0; i < 4; i++) + ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]); + + fdir_match->mask.ipv6.dip6_w0 = (uint16_t)ipv6_addr[0]; + fdir_match->mask.ipv6.dip6_w1 = + (uint16_t)(ipv6_addr[0] >> 16); + fdir_match->mask.ipv6.dip6_w2 = (uint16_t)ipv6_addr[1]; + fdir_match->mask.ipv6.dip6_w3 = + (uint16_t)(ipv6_addr[1] >> 16); + fdir_match->mask.ipv6.dip6_w4 = (uint16_t)ipv6_addr[2]; + fdir_match->mask.ipv6.dip6_w5 = + (uint16_t)(ipv6_addr[2] >> 16); + fdir_match->mask.ipv6.dip6_w6 = (uint16_t)ipv6_addr[3]; + fdir_match->mask.ipv6.dip6_w7 = + (uint16_t)(ipv6_addr[3] >> 16); + + rte_memcpy(ipv6_addr, mask->hdr.src_addr, + sizeof(ipv6_addr)); + for (i = 0; i < 4; i++) + ipv6_addr[i] = rte_be_to_cpu_32(ipv6_addr[i]); + + fdir_match->mask.ipv6.sip6_w0 = (uint16_t)ipv6_addr[0]; + fdir_match->mask.ipv6.sip6_w1 = + (uint16_t)(ipv6_addr[0] >> 16); + fdir_match->mask.ipv6.sip6_w2 = (uint16_t)ipv6_addr[1]; + fdir_match->mask.ipv6.sip6_w3 = + (uint16_t)(ipv6_addr[1] >> 16); + fdir_match->mask.ipv6.sip6_w4 = (uint16_t)ipv6_addr[2]; + fdir_match->mask.ipv6.sip6_w5 = + (uint16_t)(ipv6_addr[2] >> 16); + fdir_match->mask.ipv6.sip6_w6 = (uint16_t)ipv6_addr[3]; + fdir_match->mask.ipv6.sip6_w7 = + (uint16_t)(ipv6_addr[3] >> 16); + + fdir_match->key.ipv6.ip_proto = spec->hdr.proto; + fdir_match->mask.ipv6.ip_proto = mask->hdr.proto; + } + } + + return 0; +} + +static int +sssnic_ethdev_flow_udp_parse(const struct rte_flow_item *item, + struct rte_flow_error *error, bool outer, + struct sssnic_ethdev_fdir_flow_match *fdir_match) +{ + const struct rte_flow_item_udp *spec, *mask; + + spec = (const struct rte_flow_item_udp *)item->spec; + mask = (const struct rte_flow_item_udp *)item->mask; + + if (outer) { + if (spec != NULL || mask != NULL) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Both of outer UDP spec and mask must be NULL in tunnel flow"); + return -rte_errno; + } + + return 0; + } + + if (fdir_match != NULL) { + /* ipv6 match can share ip_proto with ipv4 match */ + fdir_match->key.ipv4.ip_proto = IPPROTO_UDP; + fdir_match->mask.ipv4.ip_proto = 0xff; + } + + if (spec == NULL && mask == NULL) + return 0; + + if (spec == NULL || mask == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + item, "Invalid UDP spec or mask"); + return -rte_errno; + } + + if (fdir_match != NULL) { + /* Other types of fdir match can share sport and dport with ipv4 match */ + fdir_match->key.ipv4.sport = + rte_be_to_cpu_16(spec->hdr.src_port); + fdir_match->mask.ipv4.sport = + rte_be_to_cpu_16(mask->hdr.src_port); + fdir_match->key.ipv4.dport = + rte_be_to_cpu_16(spec->hdr.dst_port); + fdir_match->mask.ipv4.dport = + rte_be_to_cpu_16(mask->hdr.dst_port); + } + + return 0; +} + +static int +sssnic_ethdev_flow_tcp_parse(const struct rte_flow_item *item, + struct rte_flow_error *error, bool outer, + struct sssnic_ethdev_fdir_flow_match *fdir_match) +{ + const struct rte_flow_item_tcp *spec, *mask; + + spec = (const struct rte_flow_item_tcp *)item->spec; + mask = (const struct rte_flow_item_tcp *)item->mask; + + if (outer) { + if (spec != NULL || mask != NULL) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Both of outer TCP spec and mask must be NULL in tunnel flow"); + return -rte_errno; + } + + return 0; + } + + if (fdir_match != NULL) { + /* ipv6 match can share ip_proto with ipv4 match */ + fdir_match->key.ipv4.ip_proto = IPPROTO_TCP; + fdir_match->mask.ipv6.ip_proto = 0xff; + } + + if (spec == NULL && mask == NULL) + return 0; + + if (spec == NULL || mask == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + item, "Invalid TCP spec or mask."); + return -rte_errno; + } + + if (mask->hdr.sent_seq || mask->hdr.recv_ack || mask->hdr.data_off || + mask->hdr.rx_win || mask->hdr.tcp_flags || mask->hdr.cksum || + mask->hdr.tcp_urp) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + item, + "Invalid TCP item, support src_port and dst_port only"); + return -rte_errno; + } + + if (fdir_match != NULL) { + /* Other types of fdir match can share sport and dport with ipv4 match */ + fdir_match->key.ipv4.sport = + rte_be_to_cpu_16(spec->hdr.src_port); + fdir_match->mask.ipv4.sport = + rte_be_to_cpu_16(mask->hdr.src_port); + fdir_match->key.ipv4.dport = + rte_be_to_cpu_16(spec->hdr.dst_port); + fdir_match->mask.ipv4.dport = + rte_be_to_cpu_16(mask->hdr.dst_port); + } + + return 0; +} + +static int +sssnic_ethdev_flow_vxlan_parse(const struct rte_flow_item *item, + struct rte_flow_error *error, + struct sssnic_ethdev_fdir_flow_match *fdir_match) +{ + const struct rte_flow_item_vxlan *spec, *mask; + uint32_t vni; + + spec = (const struct rte_flow_item_vxlan *)item->spec; + mask = (const struct rte_flow_item_vxlan *)item->mask; + + if (spec == NULL && mask == NULL) + return 0; + + if (spec == NULL || mask == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + item, "Invalid VXLAN spec or mask"); + return -rte_errno; + } + + /* vxlan-ipv6 match can share vni with vxlan-ipv4 match */ + if (fdir_match != NULL) { + rte_memcpy(((uint8_t *)&vni) + 1, spec->vni, 3); + vni = rte_be_to_cpu_32(vni); + fdir_match->key.ipv4.vni_w0 = (uint16_t)vni; + fdir_match->key.ipv4.vni_w1 = (uint16_t)(vni >> 16); + rte_memcpy(((uint8_t *)&vni) + 1, mask->vni, 3); + vni = rte_be_to_cpu_32(vni); + fdir_match->mask.ipv4.vni_w0 = (uint16_t)vni; + fdir_match->mask.ipv4.vni_w1 = (uint16_t)(vni >> 16); + } + + return 0; +} + +static int +sssnic_ethdev_flow_fdir_pattern_parse(const struct rte_flow_item *pattern, + struct rte_flow_error *error, bool is_tunnel, + struct sssnic_ethdev_fdir_rule *fdir_rule) +{ + struct sssnic_ethdev_fdir_flow_match *fdir_match = NULL; + const struct rte_flow_item *flow_item; + bool outer_ip; + int ret = 0; + + fdir_rule->match.type = SSSNIC_ETHDEV_FDIR_MATCH_FLOW; + if (fdir_rule != NULL) + fdir_match = &fdir_rule->match.flow; + + if (is_tunnel) + outer_ip = true; + else + outer_ip = false; + + flow_item = pattern; + while (flow_item->type != RTE_FLOW_ITEM_TYPE_END) { + switch (flow_item->type) { + case RTE_FLOW_ITEM_TYPE_ETH: + ret = sssnic_ethdev_flow_eth_parse(flow_item, error); + break; + case RTE_FLOW_ITEM_TYPE_IPV4: + ret = sssnic_ethdev_flow_ipv4_parse(flow_item, error, + outer_ip, fdir_match); + break; + case RTE_FLOW_ITEM_TYPE_IPV6: + ret = sssnic_ethdev_flow_ipv6_parse(flow_item, error, + is_tunnel, fdir_match); + break; + case RTE_FLOW_ITEM_TYPE_UDP: + ret = sssnic_ethdev_flow_udp_parse(flow_item, error, + outer_ip, fdir_match); + break; + case RTE_FLOW_ITEM_TYPE_TCP: + ret = sssnic_ethdev_flow_tcp_parse(flow_item, error, + outer_ip, fdir_match); + break; + case RTE_FLOW_ITEM_TYPE_VXLAN: + ret = sssnic_ethdev_flow_vxlan_parse(flow_item, error, + fdir_match); + outer_ip = false; /* next parsing is inner_ip */ + break; + default: + break; + } + + if (ret != 0) + return ret; + + flow_item++; + } + + if (is_tunnel) { + if (fdir_match != NULL) { + /* tunnel_type of ipv4 flow_match can share with other flow_matches */ + fdir_match->key.ipv4.tunnel_type = + SSSNIC_ETHDEV_FDIR_FLOW_TUNNEL_VXLAN; + fdir_match->mask.ipv4.tunnel_type = 0x1; + } + } + + return 0; +} + +static int +sssnic_ethdev_flow_attr_parse(const struct rte_flow_attr *attr, + struct rte_flow_error *error) +{ + if (attr->egress != 0 || attr->priority != 0 || attr->group != 0) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ATTR, + attr, "Invalid flow attr, support ingress only"); + return -rte_errno; + } + + if (attr->ingress == 0) { + rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ATTR_INGRESS, attr, + "Ingress of flow attr is not set"); + return -rte_errno; + } + + return 0; +} + +static int +sssnic_ethdev_flow_parse(struct rte_eth_dev *ethdev, + const struct rte_flow_attr *attr, const struct rte_flow_item *pattern, + const struct rte_flow_action *actions, struct rte_flow_error *error, + struct sssnic_ethdev_fdir_rule *fdir_rule) +{ + int ret; + struct sssnic_ethdev_flow_pattern *flow_pattern; + + flow_pattern = sssnic_ethdev_flow_pattern_lookup(pattern); + if (flow_pattern == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ITEM, + NULL, "Unsupported pattern"); + return -rte_errno; + } + + if (flow_pattern->type == SSSNIC_ETHDEV_FLOW_TYPE_FDIR) + ret = sssnic_ethdev_flow_fdir_pattern_parse(pattern, error, + flow_pattern->is_tunnel, fdir_rule); + else + ret = sssnic_ethdev_flow_ethertype_pattern_parse(pattern, error, + fdir_rule); + if (ret != 0) + return ret; + + ret = sssnic_ethdev_flow_action_parse(ethdev, actions, error, + fdir_rule); + if (ret != 0) + return ret; + + ret = sssnic_ethdev_flow_attr_parse(attr, error); + if (ret != 0) + return ret; + + return 0; +} + +static struct rte_flow * +sssnic_ethdev_flow_create(struct rte_eth_dev *ethdev, + const struct rte_flow_attr *attr, const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], struct rte_flow_error *error) +{ + struct sssnic_ethdev_fdir_rule *rule; + int ret; + + rule = sssnic_ethdev_fdir_rule_alloc(); + if (rule == NULL) { + rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "Failed to allocate fdir rule memory"); + return NULL; + } + + ret = sssnic_ethdev_flow_parse(ethdev, attr, pattern, actions, error, + rule); + if (ret != 0) { + sssnic_ethdev_fdir_rule_free(rule); + return NULL; + } + + ret = sssnic_ethdev_fdir_rule_add(ethdev, rule); + if (ret != 0) { + sssnic_ethdev_fdir_rule_free(rule); + rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Failed to add fdir rule"); + return NULL; + } + + return (struct rte_flow *)rule; +} + +static int +sssnic_ethdev_flow_destroy(struct rte_eth_dev *ethdev, struct rte_flow *flow, + struct rte_flow_error *error) +{ + int ret; + + if (flow == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "Invalid parameter"); + return -rte_errno; + } + + ret = sssnic_ethdev_fdir_rule_del(ethdev, + (struct sssnic_ethdev_fdir_rule *)flow); + + if (ret != 0) { + rte_flow_error_set(error, EIO, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Failed to delete fdir rule"); + return -rte_errno; + } + + sssnic_ethdev_fdir_rule_free((struct sssnic_ethdev_fdir_rule *)flow); + + return 0; +} + +static int +sssnic_ethdev_flow_validate(struct rte_eth_dev *ethdev, + const struct rte_flow_attr *attr, const struct rte_flow_item pattern[], + const struct rte_flow_action actions[], struct rte_flow_error *error) +{ + return sssnic_ethdev_flow_parse(ethdev, attr, pattern, actions, error, + NULL); +} + +static int +sssnic_ethdev_flow_flush(struct rte_eth_dev *ethdev, + struct rte_flow_error *error) +{ + int ret; + + ret = sssnic_ethdev_fdir_rules_flush(ethdev); + if (ret != 0) { + rte_flow_error_set(error, EIO, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "Failed to flush fdir rules"); + return -rte_errno; + } + + return 0; +} + +static const struct rte_flow_ops sssnic_ethdev_flow_ops = { + .validate = sssnic_ethdev_flow_validate, + .create = sssnic_ethdev_flow_create, + .destroy = sssnic_ethdev_flow_destroy, + .flush = sssnic_ethdev_flow_flush, +}; + +int +sssnic_ethdev_flow_ops_get(struct rte_eth_dev *ethdev, + const struct rte_flow_ops **ops) +{ + RTE_SET_USED(ethdev); + + *ops = &sssnic_ethdev_flow_ops; + + return 0; +} diff --git a/drivers/net/sssnic/sssnic_ethdev_flow.h b/drivers/net/sssnic/sssnic_ethdev_flow.h new file mode 100644 index 0000000000..2812b783e2 --- /dev/null +++ b/drivers/net/sssnic/sssnic_ethdev_flow.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2018-2022 Shenzhen 3SNIC Information Technology Co., Ltd. + */ + +#ifndef _SSSNIC_ETHDEV_FLOW_H_ +#define _SSSNIC_ETHDEV_FLOW_H_ + +int sssnic_ethdev_flow_ops_get(struct rte_eth_dev *ethdev, + const struct rte_flow_ops **ops); + +#endif /* _SSSNIC_ETHDEV_FLOW_H_ */ diff --git a/drivers/net/sssnic/sssnic_ethdev_rx.c b/drivers/net/sssnic/sssnic_ethdev_rx.c index 6c5f209262..46a1d5fd23 100644 --- a/drivers/net/sssnic/sssnic_ethdev_rx.c +++ b/drivers/net/sssnic/sssnic_ethdev_rx.c @@ -11,6 +11,7 @@ #include "sssnic_ethdev.h" #include "sssnic_ethdev_rx.h" #include "sssnic_ethdev_rss.h" +#include "sssnic_ethdev_fdir.h" #include "base/sssnic_hw.h" #include "base/sssnic_workq.h" #include "base/sssnic_api.h" @@ -593,9 +594,18 @@ static int sssnic_ethdev_rxq_enable(struct rte_eth_dev *ethdev, uint16_t queue_id) { struct sssnic_ethdev_rxq *rxq = ethdev->data->rx_queues[queue_id]; + int ret; sssnic_ethdev_rxq_pktmbufs_fill(rxq); + pthread_mutex_lock(ðdev->data->flow_ops_mutex); + ret = sssnic_ethdev_fdir_rules_enable_by_queue(ethdev, queue_id); + if (ret) + PMD_DRV_LOG(WARNING, + "Failed to enable fdir rules of rxq:%u, port:%u", + queue_id, ethdev->data->port_id); + pthread_mutex_unlock(ðdev->data->flow_ops_mutex); + return 0; } @@ -605,6 +615,14 @@ sssnic_ethdev_rxq_disable(struct rte_eth_dev *ethdev, uint16_t queue_id) struct sssnic_ethdev_rxq *rxq = ethdev->data->rx_queues[queue_id]; int ret; + pthread_mutex_lock(ðdev->data->flow_ops_mutex); + ret = sssnic_ethdev_fdir_rules_disable_by_queue(ethdev, queue_id); + if (ret != 0) + PMD_DRV_LOG(WARNING, + "Failed to disable fdir rules of rxq:%u, port:%u", + queue_id, ethdev->data->port_id); + pthread_mutex_unlock(ðdev->data->flow_ops_mutex); + ret = sssnic_ethdev_rxq_flush(rxq); if (ret != 0) { PMD_DRV_LOG(ERR, "Failed to flush rxq:%u, port:%u", queue_id,