Use the new flow graph API and the common parsing framework to implement flow parser for flow director.
Signed-off-by: Anatoly Burakov <[email protected]> --- drivers/net/intel/ixgbe/ixgbe_ethdev.h | 1 + drivers/net/intel/ixgbe/ixgbe_fdir.c | 13 +- drivers/net/intel/ixgbe/ixgbe_flow.c | 1542 +-------------------- drivers/net/intel/ixgbe/ixgbe_flow.h | 4 + drivers/net/intel/ixgbe/ixgbe_flow_fdir.c | 1510 ++++++++++++++++++++ drivers/net/intel/ixgbe/meson.build | 1 + 6 files changed, 1526 insertions(+), 1545 deletions(-) create mode 100644 drivers/net/intel/ixgbe/ixgbe_flow_fdir.c diff --git a/drivers/net/intel/ixgbe/ixgbe_ethdev.h b/drivers/net/intel/ixgbe/ixgbe_ethdev.h index ccfe23c233..17b9fa918f 100644 --- a/drivers/net/intel/ixgbe/ixgbe_ethdev.h +++ b/drivers/net/intel/ixgbe/ixgbe_ethdev.h @@ -50,6 +50,7 @@ #define IXGBE_VMDQ_DCB_NB_QUEUES IXGBE_MAX_RX_QUEUE_NUM #define IXGBE_DCB_NB_QUEUES IXGBE_MAX_RX_QUEUE_NUM #define IXGBE_NONE_MODE_TX_NB_QUEUES 64 +#define IXGBE_MAX_FLX_SOURCE_OFF 62 #ifndef NBBY #define NBBY 8 /* number of bits in a byte */ diff --git a/drivers/net/intel/ixgbe/ixgbe_fdir.c b/drivers/net/intel/ixgbe/ixgbe_fdir.c index 0bdfbd411a..2556b4fb3e 100644 --- a/drivers/net/intel/ixgbe/ixgbe_fdir.c +++ b/drivers/net/intel/ixgbe/ixgbe_fdir.c @@ -36,7 +36,6 @@ #define SIG_BUCKET_256KB_HASH_MASK 0x7FFF /* 15 bits */ #define IXGBE_DEFAULT_FLEXBYTES_OFFSET 12 /* default flexbytes offset in bytes */ #define IXGBE_FDIR_MAX_FLEX_LEN 2 /* len in bytes of flexbytes */ -#define IXGBE_MAX_FLX_SOURCE_OFF 62 #define IXGBE_FDIRCTRL_FLEX_MASK (0x1F << IXGBE_FDIRCTRL_FLEX_SHIFT) #define IXGBE_FDIRCMD_CMD_INTERVAL_US 10 @@ -635,10 +634,11 @@ int ixgbe_fdir_configure(struct rte_eth_dev *dev) { struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + struct rte_eth_fdir_conf *global_fdir_conf = IXGBE_DEV_FDIR_CONF(dev); int err; uint32_t fdirctrl, pbsize; int i; - enum rte_fdir_mode mode = IXGBE_DEV_FDIR_CONF(dev)->mode; + enum rte_fdir_mode mode = global_fdir_conf->mode; PMD_INIT_FUNC_TRACE(); @@ -659,7 +659,10 @@ ixgbe_fdir_configure(struct rte_eth_dev *dev) mode != RTE_FDIR_MODE_PERFECT) return -ENOSYS; - err = configure_fdir_flags(IXGBE_DEV_FDIR_CONF(dev), &fdirctrl); + /* drop queue is always fixed */ + global_fdir_conf->drop_queue = IXGBE_FDIR_DROP_QUEUE; + + err = configure_fdir_flags(global_fdir_conf, &fdirctrl); if (err) return err; @@ -681,12 +684,12 @@ ixgbe_fdir_configure(struct rte_eth_dev *dev) for (i = 1; i < 8; i++) IXGBE_WRITE_REG(hw, IXGBE_RXPBSIZE(i), 0); - err = fdir_set_input_mask(dev, &IXGBE_DEV_FDIR_CONF(dev)->mask); + err = fdir_set_input_mask(dev, &global_fdir_conf->mask); if (err < 0) { PMD_INIT_LOG(ERR, " Error on setting FD mask"); return err; } - err = ixgbe_set_fdir_flex_conf(dev, &IXGBE_DEV_FDIR_CONF(dev)->flex_conf, + err = ixgbe_set_fdir_flex_conf(dev, &global_fdir_conf->flex_conf, &fdirctrl); if (err < 0) { PMD_INIT_LOG(ERR, " Error on setting FD flexible arguments."); diff --git a/drivers/net/intel/ixgbe/ixgbe_flow.c b/drivers/net/intel/ixgbe/ixgbe_flow.c index 74ddc699fa..ea32025079 100644 --- a/drivers/net/intel/ixgbe/ixgbe_flow.c +++ b/drivers/net/intel/ixgbe/ixgbe_flow.c @@ -48,13 +48,6 @@ #include "../common/flow_engine.h" #include "ixgbe_flow.h" -#define IXGBE_MAX_FLX_SOURCE_OFF 62 - -/* fdir filter list structure */ -struct ixgbe_fdir_rule_ele { - TAILQ_ENTRY(ixgbe_fdir_rule_ele) entries; - struct ixgbe_fdir_rule filter_info; -}; /* rss filter list structure */ struct ixgbe_rss_conf_ele { TAILQ_ENTRY(ixgbe_rss_conf_ele) entries; @@ -66,11 +59,9 @@ struct ixgbe_flow_mem { struct rte_flow *flow; }; -TAILQ_HEAD(ixgbe_fdir_rule_filter_list, ixgbe_fdir_rule_ele); TAILQ_HEAD(ixgbe_rss_filter_list, ixgbe_rss_conf_ele); TAILQ_HEAD(ixgbe_flow_mem_list, ixgbe_flow_mem); -static struct ixgbe_fdir_rule_filter_list filter_fdir_list; static struct ixgbe_rss_filter_list filter_rss_list; static struct ixgbe_flow_mem_list ixgbe_flow_list; @@ -81,28 +72,10 @@ const struct ci_flow_engine_list ixgbe_flow_engine_list = { &ixgbe_l2_tunnel_flow_engine, &ixgbe_ntuple_flow_engine, &ixgbe_security_flow_engine, + &ixgbe_fdir_flow_engine, + &ixgbe_fdir_tunnel_flow_engine, }, }; - -/** - * Endless loop will never happen with below assumption - * 1. there is at least one no-void item(END) - * 2. cur is before END. - */ -static inline -const struct rte_flow_item *next_no_void_pattern( - const struct rte_flow_item pattern[], - const struct rte_flow_item *cur) -{ - const struct rte_flow_item *next = - cur ? cur + 1 : &pattern[0]; - while (1) { - if (next->type != RTE_FLOW_ITEM_TYPE_VOID) - return next; - next++; - } -} - /* * All ixgbe engines mostly check the same stuff, so use a common check. */ @@ -158,1403 +131,6 @@ ixgbe_flow_actions_check(const struct ci_flow_actions *actions, * normally the packets should use network order. */ -/* search next no void pattern and skip fuzzy */ -static inline -const struct rte_flow_item *next_no_fuzzy_pattern( - const struct rte_flow_item pattern[], - const struct rte_flow_item *cur) -{ - const struct rte_flow_item *next = - next_no_void_pattern(pattern, cur); - while (1) { - if (next->type != RTE_FLOW_ITEM_TYPE_FUZZY) - return next; - next = next_no_void_pattern(pattern, next); - } -} - -static inline uint8_t signature_match(const struct rte_flow_item pattern[]) -{ - const struct rte_flow_item_fuzzy *spec, *last, *mask; - const struct rte_flow_item *item; - uint32_t sh, lh, mh; - int i = 0; - - while (1) { - item = pattern + i; - if (item->type == RTE_FLOW_ITEM_TYPE_END) - break; - - if (item->type == RTE_FLOW_ITEM_TYPE_FUZZY) { - spec = item->spec; - last = item->last; - mask = item->mask; - - if (!spec || !mask) - return 0; - - sh = spec->thresh; - - if (!last) - lh = sh; - else - lh = last->thresh; - - mh = mask->thresh; - sh = sh & mh; - lh = lh & mh; - - if (!sh || sh > lh) - return 0; - - return 1; - } - - i++; - } - - return 0; -} - -/** - * Parse the rule to see if it is a IP or MAC VLAN flow director rule. - * And get the flow director filter info BTW. - * UDP/TCP/SCTP PATTERN: - * The first not void item can be ETH or IPV4 or IPV6 - * The second not void item must be IPV4 or IPV6 if the first one is ETH. - * The next not void item could be UDP or TCP or SCTP (optional) - * The next not void item could be RAW (for flexbyte, optional) - * The next not void item must be END. - * A Fuzzy Match pattern can appear at any place before END. - * Fuzzy Match is optional for IPV4 but is required for IPV6 - * MAC VLAN PATTERN: - * The first not void item must be ETH. - * The second not void item must be MAC VLAN. - * The next not void item must be END. - * ACTION: - * The first not void action should be QUEUE or DROP. - * The second not void optional action should be MARK, - * mark_id is a uint32_t number. - * The next not void action should be END. - * UDP/TCP/SCTP pattern example: - * ITEM Spec Mask - * ETH NULL NULL - * IPV4 src_addr 192.168.1.20 0xFFFFFFFF - * dst_addr 192.167.3.50 0xFFFFFFFF - * UDP/TCP/SCTP src_port 80 0xFFFF - * dst_port 80 0xFFFF - * FLEX relative 0 0x1 - * search 0 0x1 - * reserved 0 0 - * offset 12 0xFFFFFFFF - * limit 0 0xFFFF - * length 2 0xFFFF - * pattern[0] 0x86 0xFF - * pattern[1] 0xDD 0xFF - * END - * MAC VLAN pattern example: - * ITEM Spec Mask - * ETH dst_addr - {0xAC, 0x7B, 0xA1, {0xFF, 0xFF, 0xFF, - 0x2C, 0x6D, 0x36} 0xFF, 0xFF, 0xFF} - * MAC VLAN tci 0x2016 0xEFFF - * END - * Other members in mask and spec should set to 0x00. - * Item->last should be NULL. - */ -static int -ixgbe_parse_fdir_filter_normal(struct rte_eth_dev *dev, - const struct rte_flow_item pattern[], - const struct ci_flow_actions *parsed_actions, - struct ixgbe_fdir_rule *rule, - struct rte_flow_error *error) -{ - const struct rte_flow_item *item; - const struct rte_flow_item_eth *eth_spec; - const struct rte_flow_item_eth *eth_mask; - const struct rte_flow_item_ipv4 *ipv4_spec; - const struct rte_flow_item_ipv4 *ipv4_mask; - const struct rte_flow_item_ipv6 *ipv6_spec; - const struct rte_flow_item_ipv6 *ipv6_mask; - const struct rte_flow_item_tcp *tcp_spec; - const struct rte_flow_item_tcp *tcp_mask; - const struct rte_flow_item_udp *udp_spec; - const struct rte_flow_item_udp *udp_mask; - const struct rte_flow_item_sctp *sctp_spec; - const struct rte_flow_item_sctp *sctp_mask; - const struct rte_flow_item_vlan *vlan_spec; - const struct rte_flow_item_vlan *vlan_mask; - const struct rte_flow_item_raw *raw_mask; - const struct rte_flow_item_raw *raw_spec; - const struct rte_flow_action *fwd_action, *aux_action; - struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); - uint8_t j; - - fwd_action = parsed_actions->actions[0]; - /* can be NULL */ - aux_action = parsed_actions->actions[1]; - - /* check if this is a signature match */ - if (signature_match(pattern)) - rule->mode = RTE_FDIR_MODE_SIGNATURE; - else - rule->mode = RTE_FDIR_MODE_PERFECT; - - /* set up action */ - if (fwd_action->type == RTE_FLOW_ACTION_TYPE_QUEUE) { - const struct rte_flow_action_queue *q_act = fwd_action->conf; - rule->queue = q_act->index; - } else { - /* signature mode does not support drop action. */ - if (rule->mode == RTE_FDIR_MODE_SIGNATURE) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ACTION, fwd_action, - "Signature mode does not support drop action."); - return -rte_errno; - } - rule->fdirflags = IXGBE_FDIRCMD_DROP; - } - - /* set up mark action */ - if (aux_action != NULL && aux_action->type == RTE_FLOW_ACTION_TYPE_MARK) { - const struct rte_flow_action_mark *m_act = aux_action->conf; - rule->soft_id = m_act->id; - } - - /** - * Some fields may not be provided. Set spec to 0 and mask to default - * value. So, we need not do anything for the not provided fields later. - */ - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - memset(&rule->mask, 0xFF, sizeof(struct ixgbe_hw_fdir_mask)); - rule->mask.vlan_tci_mask = 0; - rule->mask.flex_bytes_mask = 0; - rule->mask.dst_port_mask = 0; - rule->mask.src_port_mask = 0; - - /** - * The first not void item should be - * MAC or IPv4 or TCP or UDP or SCTP. - */ - item = next_no_fuzzy_pattern(pattern, NULL); - if (item->type != RTE_FLOW_ITEM_TYPE_ETH && - item->type != RTE_FLOW_ITEM_TYPE_IPV4 && - item->type != RTE_FLOW_ITEM_TYPE_IPV6 && - item->type != RTE_FLOW_ITEM_TYPE_TCP && - item->type != RTE_FLOW_ITEM_TYPE_UDP && - item->type != RTE_FLOW_ITEM_TYPE_SCTP) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - - /* Get the MAC info. */ - if (item->type == RTE_FLOW_ITEM_TYPE_ETH) { - /** - * Only support vlan and dst MAC address, - * others should be masked. - */ - if (item->spec && !item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - if (item->spec) { - rule->b_spec = TRUE; - eth_spec = item->spec; - - /* Get the dst MAC. */ - for (j = 0; j < RTE_ETHER_ADDR_LEN; j++) { - rule->ixgbe_fdir.formatted.inner_mac[j] = - eth_spec->hdr.dst_addr.addr_bytes[j]; - } - } - - - if (item->mask) { - - rule->b_mask = TRUE; - eth_mask = item->mask; - - /* Ether type should be masked. */ - if (eth_mask->hdr.ether_type || - rule->mode == RTE_FDIR_MODE_SIGNATURE) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - /* If ethernet has meaning, it means MAC VLAN mode. */ - rule->mode = RTE_FDIR_MODE_PERFECT_MAC_VLAN; - - /** - * src MAC address must be masked, - * and don't support dst MAC address mask. - */ - for (j = 0; j < RTE_ETHER_ADDR_LEN; j++) { - if (eth_mask->hdr.src_addr.addr_bytes[j] || - eth_mask->hdr.dst_addr.addr_bytes[j] != 0xFF) { - memset(rule, 0, - sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /* When no VLAN, considered as full mask. */ - rule->mask.vlan_tci_mask = rte_cpu_to_be_16(0xEFFF); - } - /*** If both spec and mask are item, - * it means don't care about ETH. - * Do nothing. - */ - - /** - * Check if the next not void item is vlan or ipv4. - * IPv6 is not supported. - */ - item = next_no_fuzzy_pattern(pattern, item); - if (rule->mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) { - if (item->type != RTE_FLOW_ITEM_TYPE_VLAN) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } else { - if (item->type != RTE_FLOW_ITEM_TYPE_IPV4 && - item->type != RTE_FLOW_ITEM_TYPE_VLAN) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - } - - if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) { - if (!(item->spec && item->mask)) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - - vlan_spec = item->spec; - vlan_mask = item->mask; - - rule->ixgbe_fdir.formatted.vlan_id = vlan_spec->hdr.vlan_tci; - - rule->mask.vlan_tci_mask = vlan_mask->hdr.vlan_tci; - rule->mask.vlan_tci_mask &= rte_cpu_to_be_16(0xEFFF); - /* More than one tags are not supported. */ - - /* Next not void item must be END */ - item = next_no_fuzzy_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_END) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /* Get the IPV4 info. */ - if (item->type == RTE_FLOW_ITEM_TYPE_IPV4) { - /** - * Set the flow type even if there's no content - * as we must have a flow type. - */ - rule->ixgbe_fdir.formatted.flow_type = - IXGBE_ATR_FLOW_TYPE_IPV4; - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - /** - * Only care about src & dst addresses, - * others should be masked. - */ - if (!item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - rule->b_mask = TRUE; - ipv4_mask = item->mask; - if (ipv4_mask->hdr.version_ihl || - ipv4_mask->hdr.type_of_service || - ipv4_mask->hdr.total_length || - ipv4_mask->hdr.packet_id || - ipv4_mask->hdr.fragment_offset || - ipv4_mask->hdr.time_to_live || - ipv4_mask->hdr.next_proto_id || - ipv4_mask->hdr.hdr_checksum) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - rule->mask.dst_ipv4_mask = ipv4_mask->hdr.dst_addr; - rule->mask.src_ipv4_mask = ipv4_mask->hdr.src_addr; - - if (item->spec) { - rule->b_spec = TRUE; - ipv4_spec = item->spec; - rule->ixgbe_fdir.formatted.dst_ip[0] = - ipv4_spec->hdr.dst_addr; - rule->ixgbe_fdir.formatted.src_ip[0] = - ipv4_spec->hdr.src_addr; - } - - /** - * Check if the next not void item is - * TCP or UDP or SCTP or END. - */ - item = next_no_fuzzy_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_TCP && - item->type != RTE_FLOW_ITEM_TYPE_UDP && - item->type != RTE_FLOW_ITEM_TYPE_SCTP && - item->type != RTE_FLOW_ITEM_TYPE_END && - item->type != RTE_FLOW_ITEM_TYPE_RAW) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /* Get the IPV6 info. */ - if (item->type == RTE_FLOW_ITEM_TYPE_IPV6) { - /** - * Set the flow type even if there's no content - * as we must have a flow type. - */ - rule->ixgbe_fdir.formatted.flow_type = - IXGBE_ATR_FLOW_TYPE_IPV6; - - /** - * 1. must signature match - * 2. not support last - * 3. mask must not null - */ - if (rule->mode != RTE_FDIR_MODE_SIGNATURE || - item->last || - !item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - - rule->b_mask = TRUE; - ipv6_mask = item->mask; - if (ipv6_mask->hdr.vtc_flow || - ipv6_mask->hdr.payload_len || - ipv6_mask->hdr.proto || - ipv6_mask->hdr.hop_limits) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - /* check src addr mask */ - for (j = 0; j < 16; j++) { - if (ipv6_mask->hdr.src_addr.a[j] == 0) { - rule->mask.src_ipv6_mask &= ~(1 << j); - } else if (ipv6_mask->hdr.src_addr.a[j] != UINT8_MAX) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /* check dst addr mask */ - for (j = 0; j < 16; j++) { - if (ipv6_mask->hdr.dst_addr.a[j] == 0) { - rule->mask.dst_ipv6_mask &= ~(1 << j); - } else if (ipv6_mask->hdr.dst_addr.a[j] != UINT8_MAX) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - if (item->spec) { - rule->b_spec = TRUE; - ipv6_spec = item->spec; - rte_memcpy(rule->ixgbe_fdir.formatted.src_ip, - &ipv6_spec->hdr.src_addr, 16); - rte_memcpy(rule->ixgbe_fdir.formatted.dst_ip, - &ipv6_spec->hdr.dst_addr, 16); - } - - /** - * Check if the next not void item is - * TCP or UDP or SCTP or END. - */ - item = next_no_fuzzy_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_TCP && - item->type != RTE_FLOW_ITEM_TYPE_UDP && - item->type != RTE_FLOW_ITEM_TYPE_SCTP && - item->type != RTE_FLOW_ITEM_TYPE_END && - item->type != RTE_FLOW_ITEM_TYPE_RAW) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /* Get the TCP info. */ - if (item->type == RTE_FLOW_ITEM_TYPE_TCP) { - /** - * Set the flow type even if there's no content - * as we must have a flow type. - */ - rule->ixgbe_fdir.formatted.flow_type |= - IXGBE_ATR_L4TYPE_TCP; - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - /** - * Only care about src & dst ports, - * others should be masked. - */ - if (!item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - rule->b_mask = TRUE; - tcp_mask = item->mask; - if (tcp_mask->hdr.sent_seq || - tcp_mask->hdr.recv_ack || - tcp_mask->hdr.data_off || - tcp_mask->hdr.tcp_flags || - tcp_mask->hdr.rx_win || - tcp_mask->hdr.cksum || - tcp_mask->hdr.tcp_urp) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - rule->mask.src_port_mask = tcp_mask->hdr.src_port; - rule->mask.dst_port_mask = tcp_mask->hdr.dst_port; - - if (item->spec) { - rule->b_spec = TRUE; - tcp_spec = item->spec; - rule->ixgbe_fdir.formatted.src_port = - tcp_spec->hdr.src_port; - rule->ixgbe_fdir.formatted.dst_port = - tcp_spec->hdr.dst_port; - } - - item = next_no_fuzzy_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_RAW && - item->type != RTE_FLOW_ITEM_TYPE_END) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - } - - /* Get the UDP info */ - if (item->type == RTE_FLOW_ITEM_TYPE_UDP) { - /** - * Set the flow type even if there's no content - * as we must have a flow type. - */ - rule->ixgbe_fdir.formatted.flow_type |= - IXGBE_ATR_L4TYPE_UDP; - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - /** - * Only care about src & dst ports, - * others should be masked. - */ - if (!item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - rule->b_mask = TRUE; - udp_mask = item->mask; - if (udp_mask->hdr.dgram_len || - udp_mask->hdr.dgram_cksum) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - rule->mask.src_port_mask = udp_mask->hdr.src_port; - rule->mask.dst_port_mask = udp_mask->hdr.dst_port; - - if (item->spec) { - rule->b_spec = TRUE; - udp_spec = item->spec; - rule->ixgbe_fdir.formatted.src_port = - udp_spec->hdr.src_port; - rule->ixgbe_fdir.formatted.dst_port = - udp_spec->hdr.dst_port; - } - - item = next_no_fuzzy_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_RAW && - item->type != RTE_FLOW_ITEM_TYPE_END) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - } - - /* Get the SCTP info */ - if (item->type == RTE_FLOW_ITEM_TYPE_SCTP) { - /** - * Set the flow type even if there's no content - * as we must have a flow type. - */ - rule->ixgbe_fdir.formatted.flow_type |= - IXGBE_ATR_L4TYPE_SCTP; - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - - /* only some mac types support sctp port */ - if (hw->mac.type == ixgbe_mac_X550 || - hw->mac.type == ixgbe_mac_X550EM_x || - hw->mac.type == ixgbe_mac_X550EM_a || - hw->mac.type == ixgbe_mac_E610) { - /** - * Only care about src & dst ports, - * others should be masked. - */ - if (!item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - rule->b_mask = TRUE; - sctp_mask = item->mask; - if (sctp_mask->hdr.tag || - sctp_mask->hdr.cksum) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - rule->mask.src_port_mask = sctp_mask->hdr.src_port; - rule->mask.dst_port_mask = sctp_mask->hdr.dst_port; - - if (item->spec) { - rule->b_spec = TRUE; - sctp_spec = item->spec; - rule->ixgbe_fdir.formatted.src_port = - sctp_spec->hdr.src_port; - rule->ixgbe_fdir.formatted.dst_port = - sctp_spec->hdr.dst_port; - } - /* others even sctp port is not supported */ - } else { - sctp_mask = item->mask; - if (sctp_mask && - (sctp_mask->hdr.src_port || - sctp_mask->hdr.dst_port || - sctp_mask->hdr.tag || - sctp_mask->hdr.cksum)) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - item = next_no_fuzzy_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_RAW && - item->type != RTE_FLOW_ITEM_TYPE_END) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /* Get the flex byte info */ - if (item->type == RTE_FLOW_ITEM_TYPE_RAW) { - /* Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - /* mask should not be null */ - if (!item->mask || !item->spec) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - raw_mask = item->mask; - - /* check mask */ - if (raw_mask->relative != 0x1 || - raw_mask->search != 0x1 || - raw_mask->reserved != 0x0 || - (uint32_t)raw_mask->offset != 0xffffffff || - raw_mask->limit != 0xffff || - raw_mask->length != 0xffff) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - raw_spec = item->spec; - - /* check spec */ - if (raw_spec->relative != 0 || - raw_spec->search != 0 || - raw_spec->reserved != 0 || - raw_spec->offset > IXGBE_MAX_FLX_SOURCE_OFF || - raw_spec->offset % 2 || - raw_spec->limit != 0 || - raw_spec->length != 2 || - /* pattern can't be 0xffff */ - (raw_spec->pattern[0] == 0xff && - raw_spec->pattern[1] == 0xff)) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - /* check pattern mask */ - if (raw_mask->pattern[0] != 0xff || - raw_mask->pattern[1] != 0xff) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - rule->mask.flex_bytes_mask = 0xffff; - rule->ixgbe_fdir.formatted.flex_bytes = - (((uint16_t)raw_spec->pattern[1]) << 8) | - raw_spec->pattern[0]; - rule->flex_bytes_offset = raw_spec->offset; - } - - if (item->type != RTE_FLOW_ITEM_TYPE_END) { - /* check if the next not void item is END */ - item = next_no_fuzzy_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_END) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - return 0; -} - -#define NVGRE_PROTOCOL 0x6558 - -/** - * Parse the rule to see if it is a VxLAN or NVGRE flow director rule. - * And get the flow director filter info BTW. - * VxLAN PATTERN: - * The first not void item must be ETH. - * The second not void item must be IPV4/ IPV6. - * The third not void item must be NVGRE. - * The next not void item must be END. - * NVGRE PATTERN: - * The first not void item must be ETH. - * The second not void item must be IPV4/ IPV6. - * The third not void item must be NVGRE. - * The next not void item must be END. - * ACTION: - * The first not void action should be QUEUE or DROP. - * The second not void optional action should be MARK, - * mark_id is a uint32_t number. - * The next not void action should be END. - * VxLAN pattern example: - * ITEM Spec Mask - * ETH NULL NULL - * IPV4/IPV6 NULL NULL - * UDP NULL NULL - * VxLAN vni{0x00, 0x32, 0x54} {0xFF, 0xFF, 0xFF} - * MAC VLAN tci 0x2016 0xEFFF - * END - * NEGRV pattern example: - * ITEM Spec Mask - * ETH NULL NULL - * IPV4/IPV6 NULL NULL - * NVGRE protocol 0x6558 0xFFFF - * tni{0x00, 0x32, 0x54} {0xFF, 0xFF, 0xFF} - * MAC VLAN tci 0x2016 0xEFFF - * END - * other members in mask and spec should set to 0x00. - * item->last should be NULL. - */ -static int -ixgbe_parse_fdir_filter_tunnel(const struct rte_flow_item pattern[], - const struct ci_flow_actions *parsed_actions, - struct ixgbe_fdir_rule *rule, - struct rte_flow_error *error) -{ - const struct rte_flow_item *item; - const struct rte_flow_item_vxlan *vxlan_spec; - const struct rte_flow_item_vxlan *vxlan_mask; - const struct rte_flow_item_nvgre *nvgre_spec; - const struct rte_flow_item_nvgre *nvgre_mask; - const struct rte_flow_item_eth *eth_spec; - const struct rte_flow_item_eth *eth_mask; - const struct rte_flow_item_vlan *vlan_spec; - const struct rte_flow_item_vlan *vlan_mask; - const struct rte_flow_action *fwd_action, *aux_action; - uint32_t j; - - fwd_action = parsed_actions->actions[0]; - /* can be NULL */ - aux_action = parsed_actions->actions[1]; - - /* set up queue/drop action */ - if (fwd_action->type == RTE_FLOW_ACTION_TYPE_QUEUE) { - const struct rte_flow_action_queue *q_act = fwd_action->conf; - rule->queue = q_act->index; - } else { - rule->fdirflags = IXGBE_FDIRCMD_DROP; - } - - /* set up mark action */ - if (aux_action != NULL && aux_action->type == RTE_FLOW_ACTION_TYPE_MARK) { - const struct rte_flow_action_mark *mark = aux_action->conf; - rule->soft_id = mark->id; - } - - /** - * Some fields may not be provided. Set spec to 0 and mask to default - * value. So, we need not do anything for the not provided fields later. - */ - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - memset(&rule->mask, 0xFF, sizeof(struct ixgbe_hw_fdir_mask)); - rule->mask.vlan_tci_mask = 0; - - /** - * The first not void item should be - * MAC or IPv4 or IPv6 or UDP or VxLAN. - */ - item = next_no_void_pattern(pattern, NULL); - if (item->type != RTE_FLOW_ITEM_TYPE_ETH && - item->type != RTE_FLOW_ITEM_TYPE_IPV4 && - item->type != RTE_FLOW_ITEM_TYPE_IPV6 && - item->type != RTE_FLOW_ITEM_TYPE_UDP && - item->type != RTE_FLOW_ITEM_TYPE_VXLAN && - item->type != RTE_FLOW_ITEM_TYPE_NVGRE) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - rule->mode = RTE_FDIR_MODE_PERFECT_TUNNEL; - - /* Skip MAC. */ - if (item->type == RTE_FLOW_ITEM_TYPE_ETH) { - /* Only used to describe the protocol stack. */ - if (item->spec || item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /* Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - - /* Check if the next not void item is IPv4 or IPv6. */ - item = next_no_void_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_IPV4 && - item->type != RTE_FLOW_ITEM_TYPE_IPV6) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /* Skip IP. */ - if (item->type == RTE_FLOW_ITEM_TYPE_IPV4 || - item->type == RTE_FLOW_ITEM_TYPE_IPV6) { - /* Only used to describe the protocol stack. */ - if (item->spec || item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - - /* Check if the next not void item is UDP or NVGRE. */ - item = next_no_void_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_UDP && - item->type != RTE_FLOW_ITEM_TYPE_NVGRE) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /* Skip UDP. */ - if (item->type == RTE_FLOW_ITEM_TYPE_UDP) { - /* Only used to describe the protocol stack. */ - if (item->spec || item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - - /* Check if the next not void item is VxLAN. */ - item = next_no_void_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_VXLAN) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /* Get the VxLAN info */ - if (item->type == RTE_FLOW_ITEM_TYPE_VXLAN) { - rule->ixgbe_fdir.formatted.tunnel_type = - IXGBE_FDIR_VXLAN_TUNNEL_TYPE; - - /* Only care about VNI, others should be masked. */ - if (!item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - rule->b_mask = TRUE; - - /* Tunnel type is always meaningful. */ - rule->mask.tunnel_type_mask = 1; - - vxlan_mask = item->mask; - if (vxlan_mask->hdr.flags) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /* VNI must be totally masked or not. */ - if ((vxlan_mask->hdr.vni[0] || vxlan_mask->hdr.vni[1] || - vxlan_mask->hdr.vni[2]) && - ((vxlan_mask->hdr.vni[0] != 0xFF) || - (vxlan_mask->hdr.vni[1] != 0xFF) || - (vxlan_mask->hdr.vni[2] != 0xFF))) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - rte_memcpy(&rule->mask.tunnel_id_mask, vxlan_mask->hdr.vni, - RTE_DIM(vxlan_mask->hdr.vni)); - - if (item->spec) { - rule->b_spec = TRUE; - vxlan_spec = item->spec; - rte_memcpy(((uint8_t *) - &rule->ixgbe_fdir.formatted.tni_vni), - vxlan_spec->hdr.vni, RTE_DIM(vxlan_spec->hdr.vni)); - } - } - - /* Get the NVGRE info */ - if (item->type == RTE_FLOW_ITEM_TYPE_NVGRE) { - rule->ixgbe_fdir.formatted.tunnel_type = - IXGBE_FDIR_NVGRE_TUNNEL_TYPE; - - /** - * Only care about flags0, flags1, protocol and TNI, - * others should be masked. - */ - if (!item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - rule->b_mask = TRUE; - - /* Tunnel type is always meaningful. */ - rule->mask.tunnel_type_mask = 1; - - nvgre_mask = item->mask; - if (nvgre_mask->flow_id) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - if (nvgre_mask->protocol && - nvgre_mask->protocol != 0xFFFF) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - if (nvgre_mask->c_k_s_rsvd0_ver && - nvgre_mask->c_k_s_rsvd0_ver != - rte_cpu_to_be_16(0xFFFF)) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /* TNI must be totally masked or not. */ - if (nvgre_mask->tni[0] && - ((nvgre_mask->tni[0] != 0xFF) || - (nvgre_mask->tni[1] != 0xFF) || - (nvgre_mask->tni[2] != 0xFF))) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /* tni is a 24-bits bit field */ - rte_memcpy(&rule->mask.tunnel_id_mask, nvgre_mask->tni, - RTE_DIM(nvgre_mask->tni)); - rule->mask.tunnel_id_mask <<= 8; - - if (item->spec) { - rule->b_spec = TRUE; - nvgre_spec = item->spec; - if (nvgre_spec->c_k_s_rsvd0_ver != - rte_cpu_to_be_16(0x2000) && - nvgre_mask->c_k_s_rsvd0_ver) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - if (nvgre_mask->protocol && - nvgre_spec->protocol != - rte_cpu_to_be_16(NVGRE_PROTOCOL)) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /* tni is a 24-bits bit field */ - rte_memcpy(&rule->ixgbe_fdir.formatted.tni_vni, - nvgre_spec->tni, RTE_DIM(nvgre_spec->tni)); - } - } - - /* check if the next not void item is MAC */ - item = next_no_void_pattern(pattern, item); - if (item->type != RTE_FLOW_ITEM_TYPE_ETH) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - /** - * Only support vlan and dst MAC address, - * others should be masked. - */ - - if (!item->mask) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - rule->b_mask = TRUE; - eth_mask = item->mask; - - /* Ether type should be masked. */ - if (eth_mask->hdr.ether_type) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - /* src MAC address should be masked. */ - for (j = 0; j < RTE_ETHER_ADDR_LEN; j++) { - if (eth_mask->hdr.src_addr.addr_bytes[j]) { - memset(rule, 0, - sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - rule->mask.mac_addr_byte_mask = 0; - for (j = 0; j < RTE_ETHER_ADDR_LEN; j++) { - /* It's a per byte mask. */ - if (eth_mask->hdr.dst_addr.addr_bytes[j] == 0xFF) { - rule->mask.mac_addr_byte_mask |= 0x1 << j; - } else if (eth_mask->hdr.dst_addr.addr_bytes[j]) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /* When no vlan, considered as full mask. */ - rule->mask.vlan_tci_mask = rte_cpu_to_be_16(0xEFFF); - - if (item->spec) { - rule->b_spec = TRUE; - eth_spec = item->spec; - - /* Get the dst MAC. */ - for (j = 0; j < RTE_ETHER_ADDR_LEN; j++) { - rule->ixgbe_fdir.formatted.inner_mac[j] = - eth_spec->hdr.dst_addr.addr_bytes[j]; - } - } - - /** - * Check if the next not void item is vlan or ipv4. - * IPv6 is not supported. - */ - item = next_no_void_pattern(pattern, item); - if ((item->type != RTE_FLOW_ITEM_TYPE_VLAN) && - (item->type != RTE_FLOW_ITEM_TYPE_IPV4)) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - /*Not supported last point for range*/ - if (item->last) { - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - item, "Not supported last point for range"); - return -rte_errno; - } - - if (item->type == RTE_FLOW_ITEM_TYPE_VLAN) { - if (!(item->spec && item->mask)) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - - vlan_spec = item->spec; - vlan_mask = item->mask; - - rule->ixgbe_fdir.formatted.vlan_id = vlan_spec->hdr.vlan_tci; - - rule->mask.vlan_tci_mask = vlan_mask->hdr.vlan_tci; - rule->mask.vlan_tci_mask &= rte_cpu_to_be_16(0xEFFF); - /* More than one tags are not supported. */ - - /* check if the next not void item is END */ - item = next_no_void_pattern(pattern, item); - - if (item->type != RTE_FLOW_ITEM_TYPE_END) { - memset(rule, 0, sizeof(struct ixgbe_fdir_rule)); - rte_flow_error_set(error, EINVAL, - RTE_FLOW_ERROR_TYPE_ITEM, - item, "Not supported by fdir filter"); - return -rte_errno; - } - } - - /** - * If the tags is 0, it means don't care about the VLAN. - * Do nothing. - */ - - return 0; -} - -/* - * Check flow director actions - */ -static int -ixgbe_fdir_actions_check(const struct ci_flow_actions *parsed_actions, - const struct ci_flow_actions_check_param *param __rte_unused, - struct rte_flow_error *error) -{ - const enum rte_flow_action_type fwd_actions[] = { - RTE_FLOW_ACTION_TYPE_QUEUE, - RTE_FLOW_ACTION_TYPE_DROP, - RTE_FLOW_ACTION_TYPE_END - }; - const struct rte_flow_action *action, *drop_action = NULL; - - /* do the generic checks first */ - int ret = ixgbe_flow_actions_check(parsed_actions, param, error); - if (ret) - return ret; - - /* first action must be a forwarding action */ - action = parsed_actions->actions[0]; - if (!ci_flow_action_type_in_list(action->type, fwd_actions)) { - return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, - action, "First action must be QUEUE or DROP"); - } - /* remember if we have a drop action */ - if (action->type == RTE_FLOW_ACTION_TYPE_DROP) { - drop_action = action; - } - - /* second action, if specified, must not be a forwarding action */ - action = parsed_actions->actions[1]; - if (action != NULL && ci_flow_action_type_in_list(action->type, fwd_actions)) { - return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, - action, "Conflicting actions"); - } - /* if we didn't have a drop action before but now we do, remember that */ - if (drop_action == NULL && action != NULL && action->type == RTE_FLOW_ACTION_TYPE_DROP) { - drop_action = action; - } - /* drop must be the only action */ - if (drop_action != NULL && action != NULL) { - return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, - action, "Conflicting actions"); - } - return 0; -} - -static int -ixgbe_parse_fdir_filter(struct rte_eth_dev *dev, - const struct rte_flow_attr *attr, - const struct rte_flow_item pattern[], - const struct rte_flow_action actions[], - struct ixgbe_fdir_rule *rule, - struct rte_flow_error *error) -{ - int ret; - struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); - struct rte_eth_fdir_conf *fdir_conf = IXGBE_DEV_FDIR_CONF(dev); - struct ci_flow_actions parsed_actions; - struct ci_flow_actions_check_param ap_param = { - .allowed_types = (const enum rte_flow_action_type[]){ - /* queue/mark/drop allowed here */ - RTE_FLOW_ACTION_TYPE_QUEUE, - RTE_FLOW_ACTION_TYPE_DROP, - RTE_FLOW_ACTION_TYPE_MARK, - RTE_FLOW_ACTION_TYPE_END - }, - .driver_ctx = dev, - .check = ixgbe_fdir_actions_check - }; - - if (hw->mac.type != ixgbe_mac_82599EB && - hw->mac.type != ixgbe_mac_X540 && - hw->mac.type != ixgbe_mac_X550 && - hw->mac.type != ixgbe_mac_X550EM_x && - hw->mac.type != ixgbe_mac_X550EM_a && - hw->mac.type != ixgbe_mac_E610) - return -ENOTSUP; - - /* validate attributes */ - ret = ci_flow_check_attr(attr, NULL, error); - if (ret) - return ret; - - /* parse requested actions */ - ret = ci_flow_check_actions(actions, &ap_param, &parsed_actions, error); - if (ret) - return ret; - - fdir_conf->drop_queue = IXGBE_FDIR_DROP_QUEUE; - - ret = ixgbe_parse_fdir_filter_normal(dev, pattern, &parsed_actions, rule, error); - - if (!ret) - goto step_next; - - ret = ixgbe_parse_fdir_filter_tunnel(pattern, &parsed_actions, rule, error); - - if (ret) - return ret; - -step_next: - - if (hw->mac.type == ixgbe_mac_82599EB && - rule->fdirflags == IXGBE_FDIRCMD_DROP && - (rule->ixgbe_fdir.formatted.src_port != 0 || - rule->ixgbe_fdir.formatted.dst_port != 0)) - return -ENOTSUP; - - if (fdir_conf->mode == RTE_FDIR_MODE_NONE) { - fdir_conf->mode = rule->mode; - ret = ixgbe_fdir_configure(dev); - if (ret) { - fdir_conf->mode = RTE_FDIR_MODE_NONE; - return ret; - } - } else if (fdir_conf->mode != rule->mode) { - return -ENOTSUP; - } - - if (rule->queue >= dev->data->nb_rx_queues) - return -ENOTSUP; - - return ret; -} - /* Flow actions check specific to RSS filter */ static int ixgbe_flow_actions_check_rss(const struct ci_flow_actions *parsed_actions, @@ -1665,7 +241,6 @@ ixgbe_clear_rss_filter(struct rte_eth_dev *dev) void ixgbe_filterlist_init(void) { - TAILQ_INIT(&filter_fdir_list); TAILQ_INIT(&filter_rss_list); TAILQ_INIT(&ixgbe_flow_list); } @@ -1673,17 +248,9 @@ ixgbe_filterlist_init(void) void ixgbe_filterlist_flush(void) { - struct ixgbe_fdir_rule_ele *fdir_rule_ptr; struct ixgbe_flow_mem *ixgbe_flow_mem_ptr; struct ixgbe_rss_conf_ele *rss_filter_ptr; - while ((fdir_rule_ptr = TAILQ_FIRST(&filter_fdir_list))) { - TAILQ_REMOVE(&filter_fdir_list, - fdir_rule_ptr, - entries); - rte_free(fdir_rule_ptr); - } - while ((rss_filter_ptr = TAILQ_FIRST(&filter_rss_list))) { TAILQ_REMOVE(&filter_rss_list, rss_filter_ptr, @@ -1715,15 +282,10 @@ ixgbe_flow_create(struct rte_eth_dev *dev, { struct ixgbe_adapter *ad = dev->data->dev_private; int ret; - struct ixgbe_fdir_rule fdir_rule; - struct ixgbe_hw_fdir_info *fdir_info = - IXGBE_DEV_PRIVATE_TO_FDIR_INFO(dev->data->dev_private); struct ixgbe_rte_flow_rss_conf rss_conf; struct rte_flow *flow = NULL; - struct ixgbe_fdir_rule_ele *fdir_rule_ptr; struct ixgbe_rss_conf_ele *rss_filter_ptr; struct ixgbe_flow_mem *ixgbe_flow_mem_ptr; - uint8_t first_mask = FALSE; /* try the new flow engine first */ flow = ci_flow_create(&ad->flow_engine_conf, &ixgbe_flow_engine_list, @@ -1750,81 +312,6 @@ ixgbe_flow_create(struct rte_eth_dev *dev, TAILQ_INSERT_TAIL(&ixgbe_flow_list, ixgbe_flow_mem_ptr, entries); - memset(&fdir_rule, 0, sizeof(struct ixgbe_fdir_rule)); - ret = ixgbe_parse_fdir_filter(dev, attr, pattern, - actions, &fdir_rule, error); - if (!ret) { - /* A mask cannot be deleted. */ - if (fdir_rule.b_mask) { - if (!fdir_info->mask_added) { - /* It's the first time the mask is set. */ - *&fdir_info->mask = *&fdir_rule.mask; - - if (fdir_rule.mask.flex_bytes_mask) { - ret = ixgbe_fdir_set_flexbytes_offset(dev, - fdir_rule.flex_bytes_offset); - if (ret) - goto out; - } - ret = ixgbe_fdir_set_input_mask(dev); - if (ret) - goto out; - - fdir_info->mask_added = TRUE; - first_mask = TRUE; - } else { - /** - * Only support one global mask, - * all the masks should be the same. - */ - ret = memcmp(&fdir_info->mask, - &fdir_rule.mask, - sizeof(struct ixgbe_hw_fdir_mask)); - if (ret) - goto out; - - if (fdir_rule.mask.flex_bytes_mask && - fdir_info->flex_bytes_offset != - fdir_rule.flex_bytes_offset) - goto out; - } - } - - if (fdir_rule.b_spec) { - ret = ixgbe_fdir_filter_program(dev, &fdir_rule, - FALSE, FALSE); - if (!ret) { - fdir_rule_ptr = rte_zmalloc("ixgbe_fdir_filter", - sizeof(struct ixgbe_fdir_rule_ele), 0); - if (!fdir_rule_ptr) { - PMD_DRV_LOG(ERR, "failed to allocate memory"); - goto out; - } - rte_memcpy(&fdir_rule_ptr->filter_info, - &fdir_rule, - sizeof(struct ixgbe_fdir_rule)); - TAILQ_INSERT_TAIL(&filter_fdir_list, - fdir_rule_ptr, entries); - flow->rule = fdir_rule_ptr; - flow->filter_type = RTE_ETH_FILTER_FDIR; - - return flow; - } - - if (ret) { - /** - * clean the mask_added flag if fail to - * program - **/ - if (first_mask) - fdir_info->mask_added = FALSE; - goto out; - } - } - - goto out; - } - memset(&rss_conf, 0, sizeof(struct ixgbe_rte_flow_rss_conf)); ret = ixgbe_parse_rss_filter(dev, attr, actions, &rss_conf, error); @@ -1871,7 +358,6 @@ ixgbe_flow_validate(struct rte_eth_dev *dev, struct rte_flow_error *error) { struct ixgbe_adapter *ad = dev->data->dev_private; - struct ixgbe_fdir_rule fdir_rule; struct ixgbe_rte_flow_rss_conf rss_conf; int ret; @@ -1883,12 +369,6 @@ ixgbe_flow_validate(struct rte_eth_dev *dev, /* fall back to legacy engines */ - memset(&fdir_rule, 0, sizeof(struct ixgbe_fdir_rule)); - ret = ixgbe_parse_fdir_filter(dev, attr, pattern, - actions, &fdir_rule, error); - if (!ret) - return 0; - memset(&rss_conf, 0, sizeof(struct ixgbe_rte_flow_rss_conf)); ret = ixgbe_parse_rss_filter(dev, attr, actions, &rss_conf, error); @@ -1906,11 +386,7 @@ ixgbe_flow_destroy(struct rte_eth_dev *dev, int ret; struct rte_flow *pmd_flow = flow; enum rte_filter_type filter_type = pmd_flow->filter_type; - struct ixgbe_fdir_rule fdir_rule; - struct ixgbe_fdir_rule_ele *fdir_rule_ptr; struct ixgbe_flow_mem *ixgbe_flow_mem_ptr; - struct ixgbe_hw_fdir_info *fdir_info = - IXGBE_DEV_PRIVATE_TO_FDIR_INFO(dev->data->dev_private); struct ixgbe_rss_conf_ele *rss_filter_ptr; /* try the new flow engine first */ @@ -1923,20 +399,6 @@ ixgbe_flow_destroy(struct rte_eth_dev *dev, /* fall back to legacy engines */ switch (filter_type) { - case RTE_ETH_FILTER_FDIR: - fdir_rule_ptr = (struct ixgbe_fdir_rule_ele *)pmd_flow->rule; - rte_memcpy(&fdir_rule, - &fdir_rule_ptr->filter_info, - sizeof(struct ixgbe_fdir_rule)); - ret = ixgbe_fdir_filter_program(dev, &fdir_rule, TRUE, FALSE); - if (!ret) { - TAILQ_REMOVE(&filter_fdir_list, - fdir_rule_ptr, entries); - rte_free(fdir_rule_ptr); - if (TAILQ_EMPTY(&filter_fdir_list)) - fdir_info->mask_added = false; - } - break; case RTE_ETH_FILTER_HASH: rss_filter_ptr = (struct ixgbe_rss_conf_ele *) pmd_flow->rule; diff --git a/drivers/net/intel/ixgbe/ixgbe_flow.h b/drivers/net/intel/ixgbe/ixgbe_flow.h index daff23e227..91ee5106e3 100644 --- a/drivers/net/intel/ixgbe/ixgbe_flow.h +++ b/drivers/net/intel/ixgbe/ixgbe_flow.h @@ -14,6 +14,8 @@ enum ixgbe_flow_engine_type { IXGBE_FLOW_ENGINE_TYPE_L2_TUNNEL, IXGBE_FLOW_ENGINE_TYPE_NTUPLE, IXGBE_FLOW_ENGINE_TYPE_SECURITY, + IXGBE_FLOW_ENGINE_TYPE_FDIR, + IXGBE_FLOW_ENGINE_TYPE_FDIR_TUNNEL, }; int @@ -28,5 +30,7 @@ extern const struct ci_flow_engine ixgbe_syn_flow_engine; extern const struct ci_flow_engine ixgbe_l2_tunnel_flow_engine; extern const struct ci_flow_engine ixgbe_ntuple_flow_engine; extern const struct ci_flow_engine ixgbe_security_flow_engine; +extern const struct ci_flow_engine ixgbe_fdir_flow_engine; +extern const struct ci_flow_engine ixgbe_fdir_tunnel_flow_engine; #endif /* _IXGBE_FLOW_H_ */ diff --git a/drivers/net/intel/ixgbe/ixgbe_flow_fdir.c b/drivers/net/intel/ixgbe/ixgbe_flow_fdir.c new file mode 100644 index 0000000000..c0c43ddbff --- /dev/null +++ b/drivers/net/intel/ixgbe/ixgbe_flow_fdir.c @@ -0,0 +1,1510 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(c) 2026 Intel Corporation + */ + +#include <rte_common.h> +#include <rte_flow.h> +#include <rte_flow_graph.h> +#include <rte_ether.h> + +#include "ixgbe_ethdev.h" +#include "ixgbe_flow.h" +#include "../common/flow_check.h" +#include "../common/flow_util.h" +#include "../common/flow_engine.h" + +struct ixgbe_fdir_flow { + struct rte_flow flow; + struct ixgbe_fdir_rule rule; +}; + +struct ixgbe_fdir_ctx { + struct ci_flow_engine_ctx base; + struct ixgbe_fdir_rule rule; + bool supports_sctp_ports; + const struct rte_flow_action *fwd_action; + const struct rte_flow_action *aux_action; +}; + +#define IXGBE_FDIR_VLAN_TCI_MASK rte_cpu_to_be_16(0xEFFF) + +/** + * FDIR normal graph implementation + * Pattern: START -> [ETH] -> (IPv4|IPv6) -> [TCP|UDP|SCTP] -> [RAW] -> END + * Pattern: START -> ETH -> VLAN -> END + */ + +enum ixgbe_fdir_normal_node_id { + IXGBE_FDIR_NORMAL_NODE_START = RTE_FLOW_NODE_FIRST, + /* special node to report fuzzy matches */ + IXGBE_FDIR_NORMAL_NODE_FUZZY, + IXGBE_FDIR_NORMAL_NODE_ETH, + IXGBE_FDIR_NORMAL_NODE_VLAN, + IXGBE_FDIR_NORMAL_NODE_IPV4, + IXGBE_FDIR_NORMAL_NODE_IPV6, + IXGBE_FDIR_NORMAL_NODE_TCP, + IXGBE_FDIR_NORMAL_NODE_UDP, + IXGBE_FDIR_NORMAL_NODE_SCTP, + IXGBE_FDIR_NORMAL_NODE_RAW, + IXGBE_FDIR_NORMAL_NODE_END, + IXGBE_FDIR_NORMAL_NODE_MAX, +}; + +static inline uint8_t +signature_match(const struct rte_flow_item *item) +{ + const struct rte_flow_item_fuzzy *spec, *last, *mask; + uint32_t sh, lh, mh; + + spec = item->spec; + last = item->last; + mask = item->mask; + + if (spec == NULL && mask == NULL) + return 0; + + sh = spec->thresh; + + if (last == NULL) + lh = sh; + else + lh = last->thresh; + + mh = mask->thresh; + sh = sh & mh; + lh = lh & mh; + + /* + * A fuzzy item selects signature mode only when the masked threshold range + * is non-empty. Otherwise this stays a perfect-match rule. + */ + if (!sh || sh > lh) + return 0; + + return 1; +} + +static int +ixgbe_process_fdir_normal_fuzzy(void *ctx, const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + + fdir_ctx->rule.mode = RTE_FDIR_MODE_PERFECT; + + /* spec and mask are optional */ + if (item->spec == NULL && item->mask == NULL) + return 0; + + if (signature_match(item)) { + fdir_ctx->rule.mode = RTE_FDIR_MODE_SIGNATURE; + } + + return 0; +} + +static int +ixgbe_validate_fdir_normal_eth(const void *ctx, const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + const struct ixgbe_fdir_ctx *fdir_ctx = (const struct ixgbe_fdir_ctx *)ctx; + const struct rte_flow_item_eth *eth_mask = item->mask; + + if (item->spec == NULL && item->mask == NULL) + return 0; + + /* we cannot have ETH item in signature mode */ + if (fdir_ctx->rule.mode == RTE_FDIR_MODE_SIGNATURE) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "ETH item not supported in signature mode"); + } + /* ethertype isn't supported by FDIR */ + if (!CI_FIELD_IS_ZERO(ð_mask->hdr.ether_type)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Ethertype filtering not supported"); + } + /* source address mask must be all zeroes */ + if (!CI_FIELD_IS_ZERO(ð_mask->hdr.src_addr)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Source MAC filtering not supported"); + } + /* destination address mask must be all ones */ + if (!CI_FIELD_IS_MASKED(ð_mask->hdr.dst_addr)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Destination MAC filtering must be exact match"); + } + return 0; +} + +static int +ixgbe_process_fdir_normal_eth(void *ctx, const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + const struct rte_flow_item_eth *eth_spec = item->spec; + const struct rte_flow_item_eth *eth_mask = item->mask; + + + if (eth_spec == NULL && eth_mask == NULL) + return 0; + + /* copy dst MAC */ + rule->b_spec = TRUE; + memcpy(rule->ixgbe_fdir.formatted.inner_mac, eth_spec->hdr.dst_addr.addr_bytes, + RTE_ETHER_ADDR_LEN); + + /* set tunnel type */ + rule->mode = RTE_FDIR_MODE_PERFECT_MAC_VLAN; + /* when no VLAN specified, set full mask */ + rule->b_mask = TRUE; + rule->mask.vlan_tci_mask = IXGBE_FDIR_VLAN_TCI_MASK; + + return 0; +} + +static int +ixgbe_process_fdir_normal_vlan(void *ctx, const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + const struct rte_flow_item_vlan *vlan_spec = item->spec; + const struct rte_flow_item_vlan *vlan_mask = item->mask; + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + rule->ixgbe_fdir.formatted.vlan_id = vlan_spec->hdr.vlan_tci; + + rule->mask.vlan_tci_mask = vlan_mask->hdr.vlan_tci; + rule->mask.vlan_tci_mask &= IXGBE_FDIR_VLAN_TCI_MASK; + + return 0; +} + +static int +ixgbe_validate_fdir_normal_ipv4(const void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + const struct rte_flow_item_ipv4 *ipv4_mask = item->mask; + const struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + if (rule->mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "IPv4 not supported with ETH/VLAN items"); + } + + if (ipv4_mask->hdr.version_ihl || + ipv4_mask->hdr.type_of_service || + ipv4_mask->hdr.total_length || + ipv4_mask->hdr.packet_id || + ipv4_mask->hdr.fragment_offset || + ipv4_mask->hdr.time_to_live || + ipv4_mask->hdr.next_proto_id || + ipv4_mask->hdr.hdr_checksum) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Only src/dst addresses supported"); + } + + return 0; +} + +static int +ixgbe_process_fdir_normal_ipv4(void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_ipv4 *ipv4_spec = item->spec; + const struct rte_flow_item_ipv4 *ipv4_mask = item->mask; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + rule->ixgbe_fdir.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_IPV4; + + /* spec may not be present */ + if (ipv4_spec) { + rule->b_spec = TRUE; + rule->ixgbe_fdir.formatted.dst_ip[0] = ipv4_spec->hdr.dst_addr; + rule->ixgbe_fdir.formatted.src_ip[0] = ipv4_spec->hdr.src_addr; + } + + rule->b_mask = TRUE; + rule->mask.dst_ipv4_mask = ipv4_mask->hdr.dst_addr; + rule->mask.src_ipv4_mask = ipv4_mask->hdr.src_addr; + + return 0; +} + +static int +ixgbe_validate_fdir_normal_ipv6(const void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + const struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_ipv6 *ipv6_mask = item->mask; + const struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + if (rule->mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "IPv6 not supported with ETH/VLAN items"); + } + + if (rule->mode != RTE_FDIR_MODE_SIGNATURE) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "IPv6 only supported in signature mode"); + } + + ipv6_mask = item->mask; + + if (ipv6_mask->hdr.vtc_flow || + ipv6_mask->hdr.payload_len || + ipv6_mask->hdr.proto || + ipv6_mask->hdr.hop_limits) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Only src/dst addresses supported"); + } + + if (!CI_FIELD_IS_ZERO_OR_MASKED(&ipv6_mask->hdr.src_addr)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Partial src address masks not supported"); + } + if (!CI_FIELD_IS_ZERO_OR_MASKED(&ipv6_mask->hdr.dst_addr)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Partial dst address masks not supported"); + } + + return 0; +} + +static int +ixgbe_process_fdir_normal_ipv6(void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_ipv6 *ipv6_spec = item->spec; + const struct rte_flow_item_ipv6 *ipv6_mask = item->mask; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + uint8_t j; + + rule->ixgbe_fdir.formatted.flow_type = IXGBE_ATR_FLOW_TYPE_IPV6; + + /* spec may not be present */ + if (ipv6_spec) { + rule->b_spec = TRUE; + memcpy(rule->ixgbe_fdir.formatted.src_ip, &ipv6_spec->hdr.src_addr, + sizeof(struct rte_ipv6_addr)); + memcpy(rule->ixgbe_fdir.formatted.dst_ip, &ipv6_spec->hdr.dst_addr, + sizeof(struct rte_ipv6_addr)); + } + + rule->b_mask = TRUE; + for (j = 0; j < sizeof(struct rte_ipv6_addr); j++) { + if (ipv6_mask->hdr.src_addr.a[j] == 0) + rule->mask.src_ipv6_mask &= ~(1 << j); + if (ipv6_mask->hdr.dst_addr.a[j] == 0) + rule->mask.dst_ipv6_mask &= ~(1 << j); + } + + return 0; +} + +static int +ixgbe_validate_fdir_normal_tcp(const void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + const struct rte_flow_item_tcp *tcp_mask = item->mask; + const struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + if (rule->mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "TCP not supported with ETH/VLAN items"); + } + + if (tcp_mask->hdr.sent_seq || + tcp_mask->hdr.recv_ack || + tcp_mask->hdr.data_off || + tcp_mask->hdr.tcp_flags || + tcp_mask->hdr.rx_win || + tcp_mask->hdr.cksum || + tcp_mask->hdr.tcp_urp) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Only src/dst ports supported"); + } + + return 0; +} + +static int +ixgbe_process_fdir_normal_tcp(void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_tcp *tcp_spec = item->spec; + const struct rte_flow_item_tcp *tcp_mask = item->mask; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + rule->ixgbe_fdir.formatted.flow_type |= IXGBE_ATR_L4TYPE_TCP; + + /* spec is optional */ + if (tcp_spec) { + rule->b_spec = TRUE; + rule->ixgbe_fdir.formatted.src_port = tcp_spec->hdr.src_port; + rule->ixgbe_fdir.formatted.dst_port = tcp_spec->hdr.dst_port; + } + + rule->b_mask = TRUE; + rule->mask.src_port_mask = tcp_mask->hdr.src_port; + rule->mask.dst_port_mask = tcp_mask->hdr.dst_port; + + return 0; +} + +static int +ixgbe_validate_fdir_normal_udp(const void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + const struct rte_flow_item_udp *udp_mask = item->mask; + const struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + if (rule->mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "IPv4 not supported with ETH/VLAN items"); + } + + if (udp_mask->hdr.dgram_len || + udp_mask->hdr.dgram_cksum) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Only src/dst ports supported"); + } + + return 0; +} + +static int +ixgbe_process_fdir_normal_udp(void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_udp *udp_spec = item->spec; + const struct rte_flow_item_udp *udp_mask = item->mask; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + rule->ixgbe_fdir.formatted.flow_type |= IXGBE_ATR_L4TYPE_UDP; + + /* spec is optional */ + if (udp_spec) { + rule->b_spec = TRUE; + rule->ixgbe_fdir.formatted.src_port = udp_spec->hdr.src_port; + rule->ixgbe_fdir.formatted.dst_port = udp_spec->hdr.dst_port; + } + + rule->b_mask = TRUE; + rule->mask.src_port_mask = udp_mask->hdr.src_port; + rule->mask.dst_port_mask = udp_mask->hdr.dst_port; + + return 0; +} + +static int +ixgbe_validate_fdir_normal_sctp(const void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + const struct rte_flow_item_sctp *sctp_mask = item->mask; + const struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + if (rule->mode == RTE_FDIR_MODE_PERFECT_MAC_VLAN) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "IPv4 not supported with ETH/VLAN items"); + } + + /* mask is optional */ + if (sctp_mask == NULL) + return 0; + + /* mask can only be specified for some hardware */ + if (!fdir_ctx->supports_sctp_ports) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "SCTP mask not supported"); + } + + /* Tag and checksum not supported */ + if (sctp_mask->hdr.tag || + sctp_mask->hdr.cksum) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "SCTP tag/cksum not supported"); + } + + return 0; +} + +static int +ixgbe_process_fdir_normal_sctp(void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_sctp *sctp_spec = item->spec; + const struct rte_flow_item_sctp *sctp_mask = item->mask; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + fdir_ctx->rule.ixgbe_fdir.formatted.flow_type |= IXGBE_ATR_L4TYPE_SCTP; + + /* spec is optional */ + if (sctp_spec) { + rule->b_spec = TRUE; + rule->ixgbe_fdir.formatted.src_port = sctp_spec->hdr.src_port; + rule->ixgbe_fdir.formatted.dst_port = sctp_spec->hdr.dst_port; + } + + /* mask is optional */ + if (sctp_mask) { + rule->b_mask = TRUE; + rule->mask.src_port_mask = sctp_mask->hdr.src_port; + rule->mask.dst_port_mask = sctp_mask->hdr.dst_port; + } + + return 0; +} + +static int +ixgbe_validate_fdir_normal_raw(const void *ctx __rte_unused, + const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + const struct rte_flow_item_raw *raw_spec; + const struct rte_flow_item_raw *raw_mask; + + raw_mask = item->mask; + + if (raw_mask->relative != 0x1 || + raw_mask->search != 0x1 || + raw_mask->reserved != 0x0 || + (uint32_t)raw_mask->offset != 0xffffffff || + raw_mask->limit != 0xffff || + raw_mask->length != 0xffff) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Invalid RAW mask"); + } + + raw_spec = item->spec; + + if (raw_spec->relative != 0 || + raw_spec->search != 0 || + raw_spec->reserved != 0 || + raw_spec->offset > IXGBE_MAX_FLX_SOURCE_OFF || + raw_spec->offset % 2 || + raw_spec->limit != 0 || + raw_spec->length != 2 || + (raw_spec->pattern[0] == 0xff && + raw_spec->pattern[1] == 0xff)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Invalid RAW spec"); + } + + if (raw_mask->pattern[0] != 0xff || + raw_mask->pattern[1] != 0xff) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "RAW pattern must be fully masked"); + } + + return 0; +} + +static int +ixgbe_process_fdir_normal_raw(void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_raw *raw_spec = item->spec; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + rule->b_spec = TRUE; + rule->ixgbe_fdir.formatted.flex_bytes = + (((uint16_t)raw_spec->pattern[1]) << 8) | raw_spec->pattern[0]; + rule->flex_bytes_offset = raw_spec->offset; + + rule->b_mask = TRUE; + rule->mask.flex_bytes_mask = 0xffff; + + return 0; +} + +const struct rte_flow_graph ixgbe_fdir_normal_graph = { + .nodes = (struct rte_flow_graph_node[]) { + [IXGBE_FDIR_NORMAL_NODE_START] = { + .name = "START", + }, + [IXGBE_FDIR_NORMAL_NODE_FUZZY] = { + .name = "FUZZY", + .type = RTE_FLOW_ITEM_TYPE_FUZZY, + .process = ixgbe_process_fdir_normal_fuzzy, + .constraints = RTE_FLOW_NODE_EXPECT_EMPTY | + RTE_FLOW_NODE_EXPECT_SPEC_MASK | + RTE_FLOW_NODE_EXPECT_RANGE, + }, + [IXGBE_FDIR_NORMAL_NODE_ETH] = { + .name = "ETH", + .type = RTE_FLOW_ITEM_TYPE_ETH, + .validate = ixgbe_validate_fdir_normal_eth, + .process = ixgbe_process_fdir_normal_eth, + .constraints = RTE_FLOW_NODE_EXPECT_EMPTY | + RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_NORMAL_NODE_VLAN] = { + .name = "VLAN", + .type = RTE_FLOW_ITEM_TYPE_VLAN, + .process = ixgbe_process_fdir_normal_vlan, + .constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_NORMAL_NODE_IPV4] = { + .name = "IPV4", + .type = RTE_FLOW_ITEM_TYPE_IPV4, + .validate = ixgbe_validate_fdir_normal_ipv4, + .process = ixgbe_process_fdir_normal_ipv4, + .constraints = RTE_FLOW_NODE_EXPECT_MASK | + RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_NORMAL_NODE_IPV6] = { + .name = "IPV6", + .type = RTE_FLOW_ITEM_TYPE_IPV6, + .validate = ixgbe_validate_fdir_normal_ipv6, + .process = ixgbe_process_fdir_normal_ipv6, + .constraints = RTE_FLOW_NODE_EXPECT_MASK | + RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_NORMAL_NODE_TCP] = { + .name = "TCP", + .type = RTE_FLOW_ITEM_TYPE_TCP, + .validate = ixgbe_validate_fdir_normal_tcp, + .process = ixgbe_process_fdir_normal_tcp, + .constraints = RTE_FLOW_NODE_EXPECT_MASK | + RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_NORMAL_NODE_UDP] = { + .name = "UDP", + .type = RTE_FLOW_ITEM_TYPE_UDP, + .validate = ixgbe_validate_fdir_normal_udp, + .process = ixgbe_process_fdir_normal_udp, + .constraints = RTE_FLOW_NODE_EXPECT_MASK | + RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_NORMAL_NODE_SCTP] = { + .name = "SCTP", + .type = RTE_FLOW_ITEM_TYPE_SCTP, + .validate = ixgbe_validate_fdir_normal_sctp, + .process = ixgbe_process_fdir_normal_sctp, + .constraints = RTE_FLOW_NODE_EXPECT_EMPTY | + RTE_FLOW_NODE_EXPECT_MASK | + RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_NORMAL_NODE_RAW] = { + .name = "RAW", + .type = RTE_FLOW_ITEM_TYPE_RAW, + .constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK, + .validate = ixgbe_validate_fdir_normal_raw, + .process = ixgbe_process_fdir_normal_raw, + }, + [IXGBE_FDIR_NORMAL_NODE_END] = { + .name = "END", + .type = RTE_FLOW_ITEM_TYPE_END, + }, + }, + .edges = (struct rte_flow_graph_edge[]) { + [IXGBE_FDIR_NORMAL_NODE_START] = { + .next = (const size_t[]) { + IXGBE_FDIR_NORMAL_NODE_ETH, + IXGBE_FDIR_NORMAL_NODE_IPV4, + IXGBE_FDIR_NORMAL_NODE_IPV6, + IXGBE_FDIR_NORMAL_NODE_TCP, + IXGBE_FDIR_NORMAL_NODE_UDP, + IXGBE_FDIR_NORMAL_NODE_SCTP, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_NORMAL_NODE_ETH] = { + .next = (const size_t[]) { + IXGBE_FDIR_NORMAL_NODE_VLAN, + IXGBE_FDIR_NORMAL_NODE_IPV4, + IXGBE_FDIR_NORMAL_NODE_IPV6, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_NORMAL_NODE_VLAN] = { + .next = (const size_t[]) { + IXGBE_FDIR_NORMAL_NODE_END, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_NORMAL_NODE_IPV4] = { + .next = (const size_t[]) { + IXGBE_FDIR_NORMAL_NODE_TCP, + IXGBE_FDIR_NORMAL_NODE_UDP, + IXGBE_FDIR_NORMAL_NODE_SCTP, + IXGBE_FDIR_NORMAL_NODE_RAW, + IXGBE_FDIR_NORMAL_NODE_END, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_NORMAL_NODE_IPV6] = { + .next = (const size_t[]) { + IXGBE_FDIR_NORMAL_NODE_TCP, + IXGBE_FDIR_NORMAL_NODE_UDP, + IXGBE_FDIR_NORMAL_NODE_SCTP, + IXGBE_FDIR_NORMAL_NODE_RAW, + IXGBE_FDIR_NORMAL_NODE_END, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_NORMAL_NODE_TCP] = { + .next = (const size_t[]) { + IXGBE_FDIR_NORMAL_NODE_RAW, + IXGBE_FDIR_NORMAL_NODE_END, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_NORMAL_NODE_UDP] = { + .next = (const size_t[]) { + IXGBE_FDIR_NORMAL_NODE_RAW, + IXGBE_FDIR_NORMAL_NODE_END, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_NORMAL_NODE_SCTP] = { + .next = (const size_t[]) { + IXGBE_FDIR_NORMAL_NODE_RAW, + IXGBE_FDIR_NORMAL_NODE_END, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_NORMAL_NODE_RAW] = { + .next = (const size_t[]) { + IXGBE_FDIR_NORMAL_NODE_END, + RTE_FLOW_NODE_EDGE_END + } + }, + }, +}; + +/** + * FDIR tunnel graph implementation (VxLAN and NVGRE) + * Pattern: START -> [OUTER_ETH] -> (OUTER_IPv4|OUTER_IPv6) -> [UDP] -> (VXLAN|NVGRE) -> INNER_ETH -> [VLAN] -> END + * VxLAN: START -> [OUTER_ETH] -> (OUTER_IPv4|OUTER_IPv6) -> UDP -> VXLAN -> INNER_ETH -> [VLAN] -> END + * NVGRE: START -> [OUTER_ETH] -> (OUTER_IPv4|OUTER_IPv6) -> NVGRE -> INNER_ETH -> [VLAN] -> END + */ + +enum ixgbe_fdir_tunnel_node_id { + IXGBE_FDIR_TUNNEL_NODE_START = RTE_FLOW_NODE_FIRST, + IXGBE_FDIR_TUNNEL_NODE_OUTER_ETH, + IXGBE_FDIR_TUNNEL_NODE_OUTER_IPV4, + IXGBE_FDIR_TUNNEL_NODE_OUTER_IPV6, + IXGBE_FDIR_TUNNEL_NODE_UDP, + IXGBE_FDIR_TUNNEL_NODE_VXLAN, + IXGBE_FDIR_TUNNEL_NODE_NVGRE, + IXGBE_FDIR_TUNNEL_NODE_INNER_ETH, + IXGBE_FDIR_TUNNEL_NODE_INNER_IPV4, + IXGBE_FDIR_TUNNEL_NODE_VLAN, + IXGBE_FDIR_TUNNEL_NODE_END, + IXGBE_FDIR_TUNNEL_NODE_MAX, +}; + +static int +ixgbe_validate_fdir_tunnel_vxlan(const void *ctx __rte_unused, + const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + const struct rte_flow_item_vxlan *vxlan_mask = item->mask; + + if (vxlan_mask->hdr.flags) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "VxLAN flags must be masked"); + } + + if (!CI_FIELD_IS_ZERO_OR_MASKED(&vxlan_mask->hdr.vni)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Partial VNI mask not supported"); + } + + return 0; +} + +static int +ixgbe_process_fdir_tunnel_vxlan(void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_vxlan *vxlan_spec = item->spec; + const struct rte_flow_item_vxlan *vxlan_mask = item->mask; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + rule->ixgbe_fdir.formatted.tunnel_type = IXGBE_FDIR_VXLAN_TUNNEL_TYPE; + + /* spec is optional */ + if (vxlan_spec != NULL) { + rule->b_spec = TRUE; + memcpy(((uint8_t *)&rule->ixgbe_fdir.formatted.tni_vni), vxlan_spec->hdr.vni, + RTE_DIM(vxlan_spec->hdr.vni)); + } + + rule->b_mask = TRUE; + rule->mask.tunnel_type_mask = 1; + memcpy(&rule->mask.tunnel_id_mask, vxlan_mask->hdr.vni, RTE_DIM(vxlan_mask->hdr.vni)); + + return 0; +} + +static int +ixgbe_validate_fdir_tunnel_nvgre(const void *ctx __rte_unused, + const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + const struct rte_flow_item_nvgre *nvgre_mask; + + nvgre_mask = item->mask; + + if (nvgre_mask->flow_id) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "NVGRE flow ID must not be masked"); + } + + if (!CI_FIELD_IS_ZERO_OR_MASKED(&nvgre_mask->protocol)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "NVGRE protocol must be fully masked or unmasked"); + } + + if (!CI_FIELD_IS_ZERO_OR_MASKED(&nvgre_mask->c_k_s_rsvd0_ver)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "NVGRE flags must be fully masked or unmasked"); + } + + if (!CI_FIELD_IS_ZERO_OR_MASKED(&nvgre_mask->tni)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Partial TNI mask not supported"); + } + + /* if spec is present, validate flags and protocol values */ + if (item->spec) { + const struct rte_flow_item_nvgre *nvgre_spec = item->spec; + + if (nvgre_mask->c_k_s_rsvd0_ver && + nvgre_spec->c_k_s_rsvd0_ver != rte_cpu_to_be_16(0x2000)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "NVGRE flags must be 0x2000"); + } + if (nvgre_mask->protocol && + nvgre_spec->protocol != rte_cpu_to_be_16(0x6558)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "NVGRE protocol must be 0x6558"); + } + } + + return 0; +} + +#define NVGRE_FLAGS 0x2000 +#define NVGRE_PROTOCOL 0x6558 +static int +ixgbe_process_fdir_tunnel_nvgre(void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_nvgre *nvgre_spec = item->spec; + const struct rte_flow_item_nvgre *nvgre_mask = item->mask; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + rule->ixgbe_fdir.formatted.tunnel_type = IXGBE_FDIR_NVGRE_TUNNEL_TYPE; + + /* spec is optional */ + if (nvgre_spec != NULL) { + rule->b_spec = TRUE; + memcpy(&fdir_ctx->rule.ixgbe_fdir.formatted.tni_vni, + nvgre_spec->tni, RTE_DIM(nvgre_spec->tni)); + } + + rule->b_mask = TRUE; + rule->mask.tunnel_type_mask = 1; + memcpy(&rule->mask.tunnel_id_mask, nvgre_mask->tni, RTE_DIM(nvgre_mask->tni)); + rule->mask.tunnel_id_mask <<= 8; + return 0; +} + +static int +ixgbe_validate_fdir_tunnel_inner_eth(const void *ctx __rte_unused, + const struct rte_flow_item *item, + struct rte_flow_error *error) +{ + const struct rte_flow_item_eth *eth_mask = item->mask; + + if (eth_mask->hdr.ether_type) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Ether type mask not supported"); + } + + /* src addr must not be masked */ + if (!CI_FIELD_IS_ZERO(ð_mask->hdr.src_addr)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Masking not supported for src MAC address"); + } + + /* dst addr must be either fully masked or fully unmasked */ + if (!CI_FIELD_IS_ZERO_OR_MASKED(ð_mask->hdr.dst_addr)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ITEM, item, + "Partial masks not supported for dst MAC address"); + } + + return 0; +} + +static int +ixgbe_process_fdir_tunnel_inner_eth(void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_eth *eth_spec = item->spec; + const struct rte_flow_item_eth *eth_mask = item->mask; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + uint8_t j; + + /* spec is optional */ + if (eth_spec != NULL) { + rule->b_spec = TRUE; + memcpy(&rule->ixgbe_fdir.formatted.inner_mac, eth_spec->hdr.src_addr.addr_bytes, + RTE_ETHER_ADDR_LEN); + } + + rule->b_mask = TRUE; + rule->mask.mac_addr_byte_mask = 0; + for (j = 0; j < RTE_ETHER_ADDR_LEN; j++) { + if (eth_mask->hdr.dst_addr.addr_bytes[j] == 0xFF) { + rule->mask.mac_addr_byte_mask |= 0x1 << j; + } + } + + /* When no vlan, considered as full mask. */ + rule->mask.vlan_tci_mask = IXGBE_FDIR_VLAN_TCI_MASK; + + return 0; +} + +static int +ixgbe_process_fdir_tunnel_vlan(void *ctx, + const struct rte_flow_item *item, + struct rte_flow_error *error __rte_unused) +{ + struct ixgbe_fdir_ctx *fdir_ctx = ctx; + const struct rte_flow_item_vlan *vlan_spec = item->spec; + const struct rte_flow_item_vlan *vlan_mask = item->mask; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + + rule->ixgbe_fdir.formatted.vlan_id = vlan_spec->hdr.vlan_tci; + + rule->mask.vlan_tci_mask = vlan_mask->hdr.vlan_tci; + rule->mask.vlan_tci_mask &= IXGBE_FDIR_VLAN_TCI_MASK; + + return 0; +} + +const struct rte_flow_graph ixgbe_fdir_tunnel_graph = { + .nodes = (struct rte_flow_graph_node[]) { + [IXGBE_FDIR_TUNNEL_NODE_START] = { + .name = "START", + }, + [IXGBE_FDIR_TUNNEL_NODE_OUTER_ETH] = { + .name = "OUTER_ETH", + .type = RTE_FLOW_ITEM_TYPE_ETH, + .constraints = RTE_FLOW_NODE_EXPECT_EMPTY, + }, + [IXGBE_FDIR_TUNNEL_NODE_OUTER_IPV4] = { + .name = "OUTER_IPV4", + .type = RTE_FLOW_ITEM_TYPE_IPV4, + .constraints = RTE_FLOW_NODE_EXPECT_EMPTY, + }, + [IXGBE_FDIR_TUNNEL_NODE_OUTER_IPV6] = { + .name = "OUTER_IPV6", + .type = RTE_FLOW_ITEM_TYPE_IPV6, + .constraints = RTE_FLOW_NODE_EXPECT_EMPTY, + }, + [IXGBE_FDIR_TUNNEL_NODE_UDP] = { + .name = "UDP", + .type = RTE_FLOW_ITEM_TYPE_UDP, + .constraints = RTE_FLOW_NODE_EXPECT_EMPTY, + }, + [IXGBE_FDIR_TUNNEL_NODE_VXLAN] = { + .name = "VXLAN", + .type = RTE_FLOW_ITEM_TYPE_VXLAN, + .validate = ixgbe_validate_fdir_tunnel_vxlan, + .process = ixgbe_process_fdir_tunnel_vxlan, + .constraints = RTE_FLOW_NODE_EXPECT_MASK | + RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_TUNNEL_NODE_NVGRE] = { + .name = "NVGRE", + .type = RTE_FLOW_ITEM_TYPE_NVGRE, + .validate = ixgbe_validate_fdir_tunnel_nvgre, + .process = ixgbe_process_fdir_tunnel_nvgre, + .constraints = RTE_FLOW_NODE_EXPECT_MASK | + RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_TUNNEL_NODE_INNER_ETH] = { + .name = "INNER_ETH", + .type = RTE_FLOW_ITEM_TYPE_ETH, + .validate = ixgbe_validate_fdir_tunnel_inner_eth, + .process = ixgbe_process_fdir_tunnel_inner_eth, + .constraints = RTE_FLOW_NODE_EXPECT_MASK | + RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_TUNNEL_NODE_INNER_IPV4] = { + .name = "INNER_IPV4", + .type = RTE_FLOW_ITEM_TYPE_IPV4, + .constraints = RTE_FLOW_NODE_EXPECT_EMPTY, + }, + [IXGBE_FDIR_TUNNEL_NODE_VLAN] = { + .name = "VLAN", + .type = RTE_FLOW_ITEM_TYPE_VLAN, + .process = ixgbe_process_fdir_tunnel_vlan, + .constraints = RTE_FLOW_NODE_EXPECT_SPEC_MASK, + }, + [IXGBE_FDIR_TUNNEL_NODE_END] = { + .name = "END", + .type = RTE_FLOW_ITEM_TYPE_END, + }, + }, + .edges = (struct rte_flow_graph_edge[]) { + [IXGBE_FDIR_TUNNEL_NODE_START] = { + .next = (const size_t[]) { + IXGBE_FDIR_TUNNEL_NODE_OUTER_ETH, + IXGBE_FDIR_TUNNEL_NODE_OUTER_IPV4, + IXGBE_FDIR_TUNNEL_NODE_OUTER_IPV6, + IXGBE_FDIR_TUNNEL_NODE_UDP, + IXGBE_FDIR_TUNNEL_NODE_VXLAN, + IXGBE_FDIR_TUNNEL_NODE_NVGRE, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_TUNNEL_NODE_OUTER_ETH] = { + .next = (const size_t[]) { + IXGBE_FDIR_TUNNEL_NODE_OUTER_IPV4, + IXGBE_FDIR_TUNNEL_NODE_OUTER_IPV6, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_TUNNEL_NODE_OUTER_IPV4] = { + .next = (const size_t[]) { + IXGBE_FDIR_TUNNEL_NODE_UDP, + IXGBE_FDIR_TUNNEL_NODE_NVGRE, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_TUNNEL_NODE_OUTER_IPV6] = { + .next = (const size_t[]) { + IXGBE_FDIR_TUNNEL_NODE_UDP, + IXGBE_FDIR_TUNNEL_NODE_NVGRE, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_TUNNEL_NODE_UDP] = { + .next = (const size_t[]) { + IXGBE_FDIR_TUNNEL_NODE_VXLAN, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_TUNNEL_NODE_VXLAN] = { + .next = (const size_t[]) { + IXGBE_FDIR_TUNNEL_NODE_INNER_ETH, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_TUNNEL_NODE_NVGRE] = { + .next = (const size_t[]) { + IXGBE_FDIR_TUNNEL_NODE_INNER_ETH, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_TUNNEL_NODE_INNER_ETH] = { + .next = (const size_t[]) { + IXGBE_FDIR_TUNNEL_NODE_VLAN, + IXGBE_FDIR_TUNNEL_NODE_INNER_IPV4, + IXGBE_FDIR_TUNNEL_NODE_END, + RTE_FLOW_NODE_EDGE_END + } + }, + [IXGBE_FDIR_TUNNEL_NODE_VLAN] = { + .next = (const size_t[]) { + IXGBE_FDIR_TUNNEL_NODE_END, + RTE_FLOW_NODE_EDGE_END + } + }, + }, +}; + +static int +ixgbe_fdir_actions_check(const struct ci_flow_actions *parsed_actions, + const struct ci_flow_actions_check_param *param __rte_unused, + struct rte_flow_error *error) +{ + const enum rte_flow_action_type fwd_actions[] = { + RTE_FLOW_ACTION_TYPE_QUEUE, + RTE_FLOW_ACTION_TYPE_DROP, + RTE_FLOW_ACTION_TYPE_END + }; + const struct rte_flow_action *action, *drop_action = NULL; + + /* do the generic checks first */ + int ret = ixgbe_flow_actions_check(parsed_actions, param, error); + if (ret) + return ret; + + /* first action must be a forwarding action */ + action = parsed_actions->actions[0]; + if (!ci_flow_action_type_in_list(action->type, fwd_actions)) { + return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + action, "First action must be QUEUE or DROP"); + } + /* remember if we have a drop action */ + if (action->type == RTE_FLOW_ACTION_TYPE_DROP) { + drop_action = action; + } + + /* second action, if specified, must not be a forwarding action */ + action = parsed_actions->actions[1]; + if (action != NULL && ci_flow_action_type_in_list(action->type, fwd_actions)) { + return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + action, "Conflicting actions"); + } + /* if we didn't have a drop action before but now we do, remember that */ + if (drop_action == NULL && action != NULL && action->type == RTE_FLOW_ACTION_TYPE_DROP) { + drop_action = action; + } + /* drop must be the only action */ + if (drop_action != NULL && action != NULL) { + return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + action, "Conflicting actions"); + } + return 0; +} + +static int +ixgbe_flow_fdir_ctx_validate(struct ci_flow_engine_ctx *ctx, struct rte_flow_error *error) +{ + struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(ctx->dev->data->dev_private); + struct ixgbe_fdir_ctx *fdir_ctx = (struct ixgbe_fdir_ctx *)ctx; + struct rte_eth_fdir_conf *global_fdir_conf = IXGBE_DEV_FDIR_CONF(ctx->dev); + + /* DROP action cannot be used with signature matches */ + if ((fdir_ctx->rule.mode == RTE_FDIR_MODE_SIGNATURE) && + (fdir_ctx->fwd_action->type == RTE_FLOW_ACTION_TYPE_DROP)) { + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "DROP action not supported with signature mode"); + } + + /* 82599 does not support port drop with port match */ + if (hw->mac.type == ixgbe_mac_82599EB && + fdir_ctx->fwd_action->type == RTE_FLOW_ACTION_TYPE_DROP && + (fdir_ctx->rule.ixgbe_fdir.formatted.src_port != 0 || + fdir_ctx->rule.ixgbe_fdir.formatted.dst_port != 0)) { + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_ACTION, fdir_ctx->fwd_action, + "82599 does not support drop action with port match."); + } + + /* check for conflicting filter modes */ + if (global_fdir_conf->mode != fdir_ctx->rule.mode) { + return rte_flow_error_set(error, ENOTSUP, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "Conflicting filter modes"); + } + + return 0; +} + +static int +ixgbe_flow_fdir_ctx_parse_common(const struct rte_flow_action *actions, + const struct rte_flow_attr *attr, + struct ci_flow_engine_ctx *ctx, + struct rte_flow_error *error) +{ + struct ixgbe_fdir_ctx *fdir_ctx = (struct ixgbe_fdir_ctx *)ctx; + struct ci_flow_actions parsed_actions; + struct ci_flow_actions_check_param ap_param = { + .allowed_types = (const enum rte_flow_action_type[]){ + /* queue/mark/drop allowed here */ + RTE_FLOW_ACTION_TYPE_QUEUE, + RTE_FLOW_ACTION_TYPE_DROP, + RTE_FLOW_ACTION_TYPE_MARK, + RTE_FLOW_ACTION_TYPE_END + }, + .driver_ctx = ctx->dev, + .check = ixgbe_fdir_actions_check + }; + struct ixgbe_fdir_rule *rule = &fdir_ctx->rule; + int ret; + + /* validate attributes */ + ret = ci_flow_check_attr(attr, NULL, error); + if (ret) + return ret; + + /* parse requested actions */ + ret = ci_flow_check_actions(actions, &ap_param, &parsed_actions, error); + if (ret) + return ret; + + fdir_ctx->fwd_action = parsed_actions.actions[0]; + /* can be NULL */ + fdir_ctx->aux_action = parsed_actions.actions[1]; + + /* set up forward/drop action */ + if (fdir_ctx->fwd_action->type == RTE_FLOW_ACTION_TYPE_QUEUE) { + const struct rte_flow_action_queue *q_act = fdir_ctx->fwd_action->conf; + rule->queue = q_act->index; + } else { + rule->fdirflags = IXGBE_FDIRCMD_DROP; + } + + /* set up mark action */ + if (fdir_ctx->aux_action != NULL && fdir_ctx->aux_action->type == RTE_FLOW_ACTION_TYPE_MARK) { + const struct rte_flow_action_mark *m_act = fdir_ctx->aux_action->conf; + rule->soft_id = m_act->id; + } + + return ret; +} + +static int +ixgbe_flow_fdir_ctx_parse(const struct rte_flow_action *actions, + const struct rte_flow_attr *attr, + struct ci_flow_engine_ctx *ctx, + struct rte_flow_error *error) +{ + struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(ctx->dev->data->dev_private); + struct ixgbe_fdir_ctx *fdir_ctx = (struct ixgbe_fdir_ctx *)ctx; + int ret; + + /* call into common part first */ + ret = ixgbe_flow_fdir_ctx_parse_common(actions, attr, ctx, error); + if (ret) + return ret; + + /* some hardware does not support SCTP matching */ + if (hw->mac.type == ixgbe_mac_X550 || + hw->mac.type == ixgbe_mac_X550EM_x || + hw->mac.type == ixgbe_mac_X550EM_a || + hw->mac.type == ixgbe_mac_E610) + fdir_ctx->supports_sctp_ports = true; + + /* + * Some fields may not be provided. Set spec to 0 and mask to default + * value. So, we need not do anything for the not provided fields later. + */ + memset(&fdir_ctx->rule.mask, 0xFF, sizeof(struct ixgbe_hw_fdir_mask)); + fdir_ctx->rule.mask.vlan_tci_mask = 0; + fdir_ctx->rule.mask.flex_bytes_mask = 0; + fdir_ctx->rule.mask.dst_port_mask = 0; + fdir_ctx->rule.mask.src_port_mask = 0; + + return 0; +} + +static int +ixgbe_flow_fdir_tunnel_ctx_parse(const struct rte_flow_action *actions, + const struct rte_flow_attr *attr, + struct ci_flow_engine_ctx *ctx, + struct rte_flow_error *error) +{ + struct ixgbe_fdir_ctx *fdir_ctx = (struct ixgbe_fdir_ctx *)ctx; + int ret; + + /* call into common part first */ + ret = ixgbe_flow_fdir_ctx_parse_common(actions, attr, ctx, error); + if (ret) + return ret; + + /** + * Some fields may not be provided. Set spec to 0 and mask to default + * value. So, we need not do anything for the not provided fields later. + */ + memset(&fdir_ctx->rule.mask, 0xFF, sizeof(struct ixgbe_hw_fdir_mask)); + fdir_ctx->rule.mask.vlan_tci_mask = 0; + + fdir_ctx->rule.mode = RTE_FDIR_MODE_PERFECT_TUNNEL; + + return 0; +} + +static int +ixgbe_flow_fdir_ctx_to_flow(const struct ci_flow_engine_ctx *ctx, + struct ci_flow *flow, + struct rte_flow_error *error __rte_unused) +{ + const struct ixgbe_fdir_ctx *fdir_ctx = (const struct ixgbe_fdir_ctx *)ctx; + struct ixgbe_fdir_flow *fdir_flow = (struct ixgbe_fdir_flow *)flow; + + fdir_flow->rule = fdir_ctx->rule; + + return 0; +} + +/* 1 if needs mask install, 0 if doesn't, -1 if incompatible */ +static int +ixgbe_flow_fdir_needs_mask_install(struct ixgbe_fdir_flow *fdir_flow) +{ + struct ixgbe_adapter *adapter = fdir_flow->flow.flow.dev->data->dev_private; + struct ixgbe_hw_fdir_info *global_fdir_info = IXGBE_DEV_PRIVATE_TO_FDIR_INFO(adapter); + struct ixgbe_fdir_rule *rule = &fdir_flow->rule; + int ret; + + /* if rule doesn't have a mask, don't do anything */ + if (rule->b_mask == 0) + return 0; + + /* rule has a mask, check if global config doesn't */ + if (!global_fdir_info->mask_added) + return 1; + + /* global config has a mask, check if it matches */ + ret = memcmp(&global_fdir_info->mask, &rule->mask, sizeof(rule->mask)); + if (ret) + return -1; + + /* does rule specify flex bytes mask? */ + if (rule->mask.flex_bytes_mask == 0) + /* compatible */ + return 0; + + /* if flex bytes mask is set, check if offset matches */ + if (global_fdir_info->flex_bytes_offset != rule->flex_bytes_offset) + return -1; + + /* compatible */ + return 0; +} + +static int +ixgbe_flow_fdir_install_mask(struct ixgbe_fdir_flow *fdir_flow, struct rte_flow_error *error) +{ + struct rte_eth_dev *dev = fdir_flow->flow.flow.dev; + struct ixgbe_adapter *adapter = dev->data->dev_private; + struct ixgbe_hw_fdir_info *global_fdir_info = IXGBE_DEV_PRIVATE_TO_FDIR_INFO(adapter); + struct ixgbe_fdir_rule *rule = &fdir_flow->rule; + int ret; + + /* store mask */ + global_fdir_info->mask = rule->mask; + + /* do we need flex byte mask? */ + if (rule->mask.flex_bytes_mask != 0) { + ret = ixgbe_fdir_set_flexbytes_offset(dev, rule->flex_bytes_offset); + if (ret != 0) { + return rte_flow_error_set(error, ret, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "Failed to set flex bytes offset"); + } + } + + /* set mask */ + ret = ixgbe_fdir_set_input_mask(dev); + if (ret != 0) { + return rte_flow_error_set(error, ret, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "Failed to set input mask"); + } + + return 0; +} + +static int +ixgbe_flow_fdir_flow_install(struct ci_flow *flow, + struct rte_flow_error *error) +{ + struct rte_eth_dev *dev = flow->dev; + struct ixgbe_adapter *adapter = dev->data->dev_private; + struct rte_eth_fdir_conf *global_fdir_conf = IXGBE_DEV_FDIR_CONF(dev); + struct ixgbe_hw_fdir_info *global_fdir_info = IXGBE_DEV_PRIVATE_TO_FDIR_INFO(adapter); + struct ixgbe_fdir_flow *fdir_flow = (struct ixgbe_fdir_flow *)flow; + struct ixgbe_fdir_rule *rule = &fdir_flow->rule; + bool mask_installed = false; + int ret; + + /* if flow director isn't configured, configure it */ + if (global_fdir_conf->mode == RTE_FDIR_MODE_NONE) { + global_fdir_conf->mode = rule->mode; + ret = ixgbe_fdir_configure(dev); + if (ret) { + global_fdir_conf->mode = RTE_FDIR_MODE_NONE; + + return rte_flow_error_set(error, ret, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "Failed to configure flow director"); + } + } + + /* check if we need to install the mask first */ + ret = ixgbe_flow_fdir_needs_mask_install(fdir_flow); + if (ret < 0) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "Flow mask is incompatible with existing rules"); + } else if (ret > 0) { + /* no mask yet, install it */ + ret = ixgbe_flow_fdir_install_mask(fdir_flow, error); + if (ret != 0) + return ret; + mask_installed = true; + } + + /* now install the rule */ + if (rule->b_spec) { + ret = ixgbe_fdir_filter_program(dev, rule, FALSE, FALSE); + if (ret) { + return rte_flow_error_set(error, ret, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, + "Failed to program flow director filter"); + } + } + + /* if we installed a mask, mark it as installed */ + if (mask_installed) + global_fdir_info->mask_added = TRUE; + + return 0; +} + +static int +ixgbe_flow_fdir_flow_uninstall(struct ci_flow *flow, + struct rte_flow_error *error) +{ + struct rte_eth_dev *dev = flow->dev; + struct ixgbe_adapter *adapter = dev->data->dev_private; + struct rte_eth_fdir_conf *global_fdir_conf = IXGBE_DEV_FDIR_CONF(dev); + struct ixgbe_hw_fdir_info *global_fdir_info = IXGBE_DEV_PRIVATE_TO_FDIR_INFO(adapter); + struct ixgbe_fdir_flow *fdir_flow = (struct ixgbe_fdir_flow *)flow; + struct ixgbe_fdir_rule *rule = &fdir_flow->rule; + int ret; + + /* uninstall the rule */ + ret = ixgbe_fdir_filter_program(dev, rule, TRUE, FALSE); + if (ret != 0) { + return rte_flow_error_set(error, ret, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, + "Failed to remove flow director filter"); + } + + /* when last filter is removed, also remove the mask */ + if (!TAILQ_EMPTY(&global_fdir_info->fdir_list)) + return 0; + + global_fdir_info->mask_added = FALSE; + global_fdir_info->mask = (struct ixgbe_hw_fdir_mask){0}; + global_fdir_conf->mode = RTE_FDIR_MODE_NONE; + + return 0; +} + +static bool +ixgbe_flow_fdir_is_available(const struct ci_flow_engine *engine __rte_unused, + const struct rte_eth_dev *dev) +{ + struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + return hw->mac.type == ixgbe_mac_82599EB || + hw->mac.type == ixgbe_mac_X540 || + hw->mac.type == ixgbe_mac_X550 || + hw->mac.type == ixgbe_mac_X550EM_x || + hw->mac.type == ixgbe_mac_X550EM_a || + hw->mac.type == ixgbe_mac_E610; +} + +static bool +ixgbe_flow_fdir_tunnel_is_available(const struct ci_flow_engine *engine __rte_unused, + const struct rte_eth_dev *dev) +{ + struct ixgbe_hw *hw = IXGBE_DEV_PRIVATE_TO_HW(dev->data->dev_private); + + return hw->mac.type == ixgbe_mac_X550 || + hw->mac.type == ixgbe_mac_X550EM_x || + hw->mac.type == ixgbe_mac_X550EM_a || + hw->mac.type == ixgbe_mac_E610; +} + +const struct ci_flow_engine_ops ixgbe_fdir_ops = { + .is_available = ixgbe_flow_fdir_is_available, + .ctx_parse = ixgbe_flow_fdir_ctx_parse, + .ctx_validate = ixgbe_flow_fdir_ctx_validate, + .ctx_to_flow = ixgbe_flow_fdir_ctx_to_flow, + .flow_install = ixgbe_flow_fdir_flow_install, + .flow_uninstall = ixgbe_flow_fdir_flow_uninstall, +}; + +const struct ci_flow_engine_ops ixgbe_fdir_tunnel_ops = { + .is_available = ixgbe_flow_fdir_tunnel_is_available, + .ctx_parse = ixgbe_flow_fdir_tunnel_ctx_parse, + .ctx_validate = ixgbe_flow_fdir_ctx_validate, + .ctx_to_flow = ixgbe_flow_fdir_ctx_to_flow, + .flow_install = ixgbe_flow_fdir_flow_install, + .flow_uninstall = ixgbe_flow_fdir_flow_uninstall, +}; + +const struct ci_flow_engine ixgbe_fdir_flow_engine = { + .name = "ixgbe_fdir", + .ctx_size = sizeof(struct ixgbe_fdir_ctx), + .flow_size = sizeof(struct ixgbe_fdir_flow), + .type = IXGBE_FLOW_ENGINE_TYPE_FDIR, + .ops = &ixgbe_fdir_ops, + .graph = &ixgbe_fdir_normal_graph, +}; + +const struct ci_flow_engine ixgbe_fdir_tunnel_flow_engine = { + .name = "ixgbe_fdir_tunnel", + .ctx_size = sizeof(struct ixgbe_fdir_ctx), + .flow_size = sizeof(struct ixgbe_fdir_flow), + .type = IXGBE_FLOW_ENGINE_TYPE_FDIR_TUNNEL, + .ops = &ixgbe_fdir_tunnel_ops, + .graph = &ixgbe_fdir_tunnel_graph, +}; diff --git a/drivers/net/intel/ixgbe/meson.build b/drivers/net/intel/ixgbe/meson.build index 65ffe19939..770125350e 100644 --- a/drivers/net/intel/ixgbe/meson.build +++ b/drivers/net/intel/ixgbe/meson.build @@ -16,6 +16,7 @@ sources += files( 'ixgbe_flow_l2tun.c', 'ixgbe_flow_ntuple.c', 'ixgbe_flow_security.c', + 'ixgbe_flow_fdir.c', 'ixgbe_ipsec.c', 'ixgbe_pf.c', 'ixgbe_rxtx.c', -- 2.47.3

