numam-dpdk/drivers/net/octeontx2/otx2_flow_parse.c
Andrew Rybchenko 92ef4b8f16 ethdev: remove deprecated shared counter attribute
Indirect actions should be used to do shared counters.

Signed-off-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Acked-by: Thomas Monjalon <thomas@monjalon.net>
Acked-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
Acked-by: Somnath Kotur <somnath.kotur@broadcom.com>
Acked-by: Ori Kam <orika@nvidia.com>
Acked-by: Matan Azrad <matan@nvidia.com>
2021-10-12 19:20:57 +02:00

1232 lines
32 KiB
C

/* SPDX-License-Identifier: BSD-3-Clause
* Copyright(C) 2019 Marvell International Ltd.
*/
#include "otx2_ethdev.h"
#include "otx2_flow.h"
const struct rte_flow_item *
otx2_flow_skip_void_and_any_items(const struct rte_flow_item *pattern)
{
while ((pattern->type == RTE_FLOW_ITEM_TYPE_VOID) ||
(pattern->type == RTE_FLOW_ITEM_TYPE_ANY))
pattern++;
return pattern;
}
/*
* Tunnel+ESP, Tunnel+ICMP4/6, Tunnel+TCP, Tunnel+UDP,
* Tunnel+SCTP
*/
int
otx2_flow_parse_lh(struct otx2_parse_state *pst)
{
struct otx2_flow_item_info info;
char hw_mask[64];
int lid, lt;
int rc;
if (!pst->tunnel)
return 0;
info.hw_mask = &hw_mask;
info.spec = NULL;
info.mask = NULL;
info.hw_hdr_len = 0;
lid = NPC_LID_LH;
switch (pst->pattern->type) {
case RTE_FLOW_ITEM_TYPE_UDP:
lt = NPC_LT_LH_TU_UDP;
info.def_mask = &rte_flow_item_udp_mask;
info.len = sizeof(struct rte_flow_item_udp);
break;
case RTE_FLOW_ITEM_TYPE_TCP:
lt = NPC_LT_LH_TU_TCP;
info.def_mask = &rte_flow_item_tcp_mask;
info.len = sizeof(struct rte_flow_item_tcp);
break;
case RTE_FLOW_ITEM_TYPE_SCTP:
lt = NPC_LT_LH_TU_SCTP;
info.def_mask = &rte_flow_item_sctp_mask;
info.len = sizeof(struct rte_flow_item_sctp);
break;
case RTE_FLOW_ITEM_TYPE_ESP:
lt = NPC_LT_LH_TU_ESP;
info.def_mask = &rte_flow_item_esp_mask;
info.len = sizeof(struct rte_flow_item_esp);
break;
default:
return 0;
}
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
if (rc != 0)
return rc;
return otx2_flow_update_parse_state(pst, &info, lid, lt, 0);
}
/* Tunnel+IPv4, Tunnel+IPv6 */
int
otx2_flow_parse_lg(struct otx2_parse_state *pst)
{
struct otx2_flow_item_info info;
char hw_mask[64];
int lid, lt;
int rc;
if (!pst->tunnel)
return 0;
info.hw_mask = &hw_mask;
info.spec = NULL;
info.mask = NULL;
info.hw_hdr_len = 0;
lid = NPC_LID_LG;
if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_IPV4) {
lt = NPC_LT_LG_TU_IP;
info.def_mask = &rte_flow_item_ipv4_mask;
info.len = sizeof(struct rte_flow_item_ipv4);
} else if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_IPV6) {
lt = NPC_LT_LG_TU_IP6;
info.def_mask = &rte_flow_item_ipv6_mask;
info.len = sizeof(struct rte_flow_item_ipv6);
} else {
/* There is no tunneled IP header */
return 0;
}
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
if (rc != 0)
return rc;
return otx2_flow_update_parse_state(pst, &info, lid, lt, 0);
}
/* Tunnel+Ether */
int
otx2_flow_parse_lf(struct otx2_parse_state *pst)
{
const struct rte_flow_item *pattern, *last_pattern;
struct rte_flow_item_eth hw_mask;
struct otx2_flow_item_info info;
int lid, lt, lflags;
int nr_vlans = 0;
int rc;
/* We hit this layer if there is a tunneling protocol */
if (!pst->tunnel)
return 0;
if (pst->pattern->type != RTE_FLOW_ITEM_TYPE_ETH)
return 0;
lid = NPC_LID_LF;
lt = NPC_LT_LF_TU_ETHER;
lflags = 0;
info.def_mask = &rte_flow_item_vlan_mask;
/* No match support for vlan tags */
info.hw_mask = NULL;
info.len = sizeof(struct rte_flow_item_vlan);
info.spec = NULL;
info.mask = NULL;
info.hw_hdr_len = 0;
/* Look ahead and find out any VLAN tags. These can be
* detected but no data matching is available.
*/
last_pattern = pst->pattern;
pattern = pst->pattern + 1;
pattern = otx2_flow_skip_void_and_any_items(pattern);
while (pattern->type == RTE_FLOW_ITEM_TYPE_VLAN) {
nr_vlans++;
rc = otx2_flow_parse_item_basic(pattern, &info, pst->error);
if (rc != 0)
return rc;
last_pattern = pattern;
pattern++;
pattern = otx2_flow_skip_void_and_any_items(pattern);
}
otx2_npc_dbg("Nr_vlans = %d", nr_vlans);
switch (nr_vlans) {
case 0:
break;
case 1:
lflags = NPC_F_TU_ETHER_CTAG;
break;
case 2:
lflags = NPC_F_TU_ETHER_STAG_CTAG;
break;
default:
rte_flow_error_set(pst->error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ITEM,
last_pattern,
"more than 2 vlans with tunneled Ethernet "
"not supported");
return -rte_errno;
}
info.def_mask = &rte_flow_item_eth_mask;
info.hw_mask = &hw_mask;
info.len = sizeof(struct rte_flow_item_eth);
info.hw_hdr_len = 0;
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
info.spec = NULL;
info.mask = NULL;
rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
if (rc != 0)
return rc;
pst->pattern = last_pattern;
return otx2_flow_update_parse_state(pst, &info, lid, lt, lflags);
}
int
otx2_flow_parse_le(struct otx2_parse_state *pst)
{
/*
* We are positioned at UDP. Scan ahead and look for
* UDP encapsulated tunnel protocols. If available,
* parse them. In that case handle this:
* - RTE spec assumes we point to tunnel header.
* - NPC parser provides offset from UDP header.
*/
/*
* Note: Add support to GENEVE, VXLAN_GPE when we
* upgrade DPDK
*
* Note: Better to split flags into two nibbles:
* - Higher nibble can have flags
* - Lower nibble to further enumerate protocols
* and have flags based extraction
*/
const struct rte_flow_item *pattern = pst->pattern;
struct otx2_flow_item_info info;
int lid, lt, lflags;
char hw_mask[64];
int rc;
if (pst->tunnel)
return 0;
if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_MPLS)
return otx2_flow_parse_mpls(pst, NPC_LID_LE);
info.spec = NULL;
info.mask = NULL;
info.hw_mask = NULL;
info.def_mask = NULL;
info.len = 0;
info.hw_hdr_len = 0;
lid = NPC_LID_LE;
lflags = 0;
/* Ensure we are not matching anything in UDP */
rc = otx2_flow_parse_item_basic(pattern, &info, pst->error);
if (rc)
return rc;
info.hw_mask = &hw_mask;
pattern = otx2_flow_skip_void_and_any_items(pattern);
otx2_npc_dbg("Pattern->type = %d", pattern->type);
switch (pattern->type) {
case RTE_FLOW_ITEM_TYPE_VXLAN:
lflags = NPC_F_UDP_VXLAN;
info.def_mask = &rte_flow_item_vxlan_mask;
info.len = sizeof(struct rte_flow_item_vxlan);
lt = NPC_LT_LE_VXLAN;
break;
case RTE_FLOW_ITEM_TYPE_ESP:
lt = NPC_LT_LE_ESP;
info.def_mask = &rte_flow_item_esp_mask;
info.len = sizeof(struct rte_flow_item_esp);
break;
case RTE_FLOW_ITEM_TYPE_GTPC:
lflags = NPC_F_UDP_GTP_GTPC;
info.def_mask = &rte_flow_item_gtp_mask;
info.len = sizeof(struct rte_flow_item_gtp);
lt = NPC_LT_LE_GTPC;
break;
case RTE_FLOW_ITEM_TYPE_GTPU:
lflags = NPC_F_UDP_GTP_GTPU_G_PDU;
info.def_mask = &rte_flow_item_gtp_mask;
info.len = sizeof(struct rte_flow_item_gtp);
lt = NPC_LT_LE_GTPU;
break;
case RTE_FLOW_ITEM_TYPE_GENEVE:
lflags = NPC_F_UDP_GENEVE;
info.def_mask = &rte_flow_item_geneve_mask;
info.len = sizeof(struct rte_flow_item_geneve);
lt = NPC_LT_LE_GENEVE;
break;
case RTE_FLOW_ITEM_TYPE_VXLAN_GPE:
lflags = NPC_F_UDP_VXLANGPE;
info.def_mask = &rte_flow_item_vxlan_gpe_mask;
info.len = sizeof(struct rte_flow_item_vxlan_gpe);
lt = NPC_LT_LE_VXLANGPE;
break;
default:
return 0;
}
pst->tunnel = 1;
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
rc = otx2_flow_parse_item_basic(pattern, &info, pst->error);
if (rc != 0)
return rc;
return otx2_flow_update_parse_state(pst, &info, lid, lt, lflags);
}
static int
flow_parse_mpls_label_stack(struct otx2_parse_state *pst, int *flag)
{
int nr_labels = 0;
const struct rte_flow_item *pattern = pst->pattern;
struct otx2_flow_item_info info;
int rc;
uint8_t flag_list[] = {0, NPC_F_MPLS_2_LABELS,
NPC_F_MPLS_3_LABELS, NPC_F_MPLS_4_LABELS};
/*
* pst->pattern points to first MPLS label. We only check
* that subsequent labels do not have anything to match.
*/
info.def_mask = &rte_flow_item_mpls_mask;
info.hw_mask = NULL;
info.len = sizeof(struct rte_flow_item_mpls);
info.spec = NULL;
info.mask = NULL;
info.hw_hdr_len = 0;
while (pattern->type == RTE_FLOW_ITEM_TYPE_MPLS) {
nr_labels++;
/* Basic validation of 2nd/3rd/4th mpls item */
if (nr_labels > 1) {
rc = otx2_flow_parse_item_basic(pattern, &info,
pst->error);
if (rc != 0)
return rc;
}
pst->last_pattern = pattern;
pattern++;
pattern = otx2_flow_skip_void_and_any_items(pattern);
}
if (nr_labels > 4) {
rte_flow_error_set(pst->error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ITEM,
pst->last_pattern,
"more than 4 mpls labels not supported");
return -rte_errno;
}
*flag = flag_list[nr_labels - 1];
return 0;
}
int
otx2_flow_parse_mpls(struct otx2_parse_state *pst, int lid)
{
/* Find number of MPLS labels */
struct rte_flow_item_mpls hw_mask;
struct otx2_flow_item_info info;
int lt, lflags;
int rc;
lflags = 0;
if (lid == NPC_LID_LC)
lt = NPC_LT_LC_MPLS;
else if (lid == NPC_LID_LD)
lt = NPC_LT_LD_TU_MPLS_IN_IP;
else
lt = NPC_LT_LE_TU_MPLS_IN_UDP;
/* Prepare for parsing the first item */
info.def_mask = &rte_flow_item_mpls_mask;
info.hw_mask = &hw_mask;
info.len = sizeof(struct rte_flow_item_mpls);
info.spec = NULL;
info.mask = NULL;
info.hw_hdr_len = 0;
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
if (rc != 0)
return rc;
/*
* Parse for more labels.
* This sets lflags and pst->last_pattern correctly.
*/
rc = flow_parse_mpls_label_stack(pst, &lflags);
if (rc != 0)
return rc;
pst->tunnel = 1;
pst->pattern = pst->last_pattern;
return otx2_flow_update_parse_state(pst, &info, lid, lt, lflags);
}
/*
* ICMP, ICMP6, UDP, TCP, SCTP, VXLAN, GRE, NVGRE,
* GTP, GTPC, GTPU, ESP
*
* Note: UDP tunnel protocols are identified by flags.
* LPTR for these protocol still points to UDP
* header. Need flag based extraction to support
* this.
*/
int
otx2_flow_parse_ld(struct otx2_parse_state *pst)
{
char hw_mask[NPC_MAX_EXTRACT_DATA_LEN];
uint32_t gre_key_mask = 0xffffffff;
struct otx2_flow_item_info info;
int lid, lt, lflags;
int rc;
if (pst->tunnel) {
/* We have already parsed MPLS or IPv4/v6 followed
* by MPLS or IPv4/v6. Subsequent TCP/UDP etc
* would be parsed as tunneled versions. Skip
* this layer, except for tunneled MPLS. If LC is
* MPLS, we have anyway skipped all stacked MPLS
* labels.
*/
if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_MPLS)
return otx2_flow_parse_mpls(pst, NPC_LID_LD);
return 0;
}
info.hw_mask = &hw_mask;
info.spec = NULL;
info.mask = NULL;
info.def_mask = NULL;
info.len = 0;
info.hw_hdr_len = 0;
lid = NPC_LID_LD;
lflags = 0;
otx2_npc_dbg("Pst->pattern->type = %d", pst->pattern->type);
switch (pst->pattern->type) {
case RTE_FLOW_ITEM_TYPE_ICMP:
if (pst->lt[NPC_LID_LC] == NPC_LT_LC_IP6)
lt = NPC_LT_LD_ICMP6;
else
lt = NPC_LT_LD_ICMP;
info.def_mask = &rte_flow_item_icmp_mask;
info.len = sizeof(struct rte_flow_item_icmp);
break;
case RTE_FLOW_ITEM_TYPE_UDP:
lt = NPC_LT_LD_UDP;
info.def_mask = &rte_flow_item_udp_mask;
info.len = sizeof(struct rte_flow_item_udp);
break;
case RTE_FLOW_ITEM_TYPE_TCP:
lt = NPC_LT_LD_TCP;
info.def_mask = &rte_flow_item_tcp_mask;
info.len = sizeof(struct rte_flow_item_tcp);
break;
case RTE_FLOW_ITEM_TYPE_SCTP:
lt = NPC_LT_LD_SCTP;
info.def_mask = &rte_flow_item_sctp_mask;
info.len = sizeof(struct rte_flow_item_sctp);
break;
case RTE_FLOW_ITEM_TYPE_GRE:
lt = NPC_LT_LD_GRE;
info.def_mask = &rte_flow_item_gre_mask;
info.len = sizeof(struct rte_flow_item_gre);
break;
case RTE_FLOW_ITEM_TYPE_GRE_KEY:
lt = NPC_LT_LD_GRE;
info.def_mask = &gre_key_mask;
info.len = sizeof(gre_key_mask);
info.hw_hdr_len = 4;
break;
case RTE_FLOW_ITEM_TYPE_NVGRE:
lt = NPC_LT_LD_NVGRE;
lflags = NPC_F_GRE_NVGRE;
info.def_mask = &rte_flow_item_nvgre_mask;
info.len = sizeof(struct rte_flow_item_nvgre);
/* Further IP/Ethernet are parsed as tunneled */
pst->tunnel = 1;
break;
default:
return 0;
}
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
if (rc != 0)
return rc;
return otx2_flow_update_parse_state(pst, &info, lid, lt, lflags);
}
static inline void
flow_check_lc_ip_tunnel(struct otx2_parse_state *pst)
{
const struct rte_flow_item *pattern = pst->pattern + 1;
pattern = otx2_flow_skip_void_and_any_items(pattern);
if (pattern->type == RTE_FLOW_ITEM_TYPE_MPLS ||
pattern->type == RTE_FLOW_ITEM_TYPE_IPV4 ||
pattern->type == RTE_FLOW_ITEM_TYPE_IPV6)
pst->tunnel = 1;
}
static int
otx2_flow_raw_item_prepare(const struct rte_flow_item_raw *raw_spec,
const struct rte_flow_item_raw *raw_mask,
struct otx2_flow_item_info *info,
uint8_t *spec_buf, uint8_t *mask_buf)
{
uint32_t custom_hdr_size = 0;
memset(spec_buf, 0, NPC_MAX_RAW_ITEM_LEN);
memset(mask_buf, 0, NPC_MAX_RAW_ITEM_LEN);
custom_hdr_size = raw_spec->offset + raw_spec->length;
memcpy(spec_buf + raw_spec->offset, raw_spec->pattern,
raw_spec->length);
if (raw_mask->pattern) {
memcpy(mask_buf + raw_spec->offset, raw_mask->pattern,
raw_spec->length);
} else {
memset(mask_buf + raw_spec->offset, 0xFF, raw_spec->length);
}
info->len = custom_hdr_size;
info->spec = spec_buf;
info->mask = mask_buf;
return 0;
}
/* Outer IPv4, Outer IPv6, MPLS, ARP */
int
otx2_flow_parse_lc(struct otx2_parse_state *pst)
{
uint8_t raw_spec_buf[NPC_MAX_RAW_ITEM_LEN];
uint8_t raw_mask_buf[NPC_MAX_RAW_ITEM_LEN];
uint8_t hw_mask[NPC_MAX_EXTRACT_DATA_LEN];
const struct rte_flow_item_raw *raw_spec;
struct otx2_flow_item_info info;
int lid, lt, len;
int rc;
if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_MPLS)
return otx2_flow_parse_mpls(pst, NPC_LID_LC);
info.hw_mask = &hw_mask;
info.spec = NULL;
info.mask = NULL;
info.hw_hdr_len = 0;
lid = NPC_LID_LC;
switch (pst->pattern->type) {
case RTE_FLOW_ITEM_TYPE_IPV4:
lt = NPC_LT_LC_IP;
info.def_mask = &rte_flow_item_ipv4_mask;
info.len = sizeof(struct rte_flow_item_ipv4);
break;
case RTE_FLOW_ITEM_TYPE_IPV6:
lid = NPC_LID_LC;
lt = NPC_LT_LC_IP6;
info.def_mask = &rte_flow_item_ipv6_mask;
info.len = sizeof(struct rte_flow_item_ipv6);
break;
case RTE_FLOW_ITEM_TYPE_ARP_ETH_IPV4:
lt = NPC_LT_LC_ARP;
info.def_mask = &rte_flow_item_arp_eth_ipv4_mask;
info.len = sizeof(struct rte_flow_item_arp_eth_ipv4);
break;
case RTE_FLOW_ITEM_TYPE_IPV6_EXT:
lid = NPC_LID_LC;
lt = NPC_LT_LC_IP6_EXT;
info.def_mask = &rte_flow_item_ipv6_ext_mask;
info.len = sizeof(struct rte_flow_item_ipv6_ext);
info.hw_hdr_len = 40;
break;
case RTE_FLOW_ITEM_TYPE_RAW:
raw_spec = pst->pattern->spec;
if (!raw_spec->relative)
return 0;
len = raw_spec->length + raw_spec->offset;
if (len > NPC_MAX_RAW_ITEM_LEN) {
rte_flow_error_set(pst->error, EINVAL,
RTE_FLOW_ERROR_TYPE_ITEM, NULL,
"Spec length too big");
return -rte_errno;
}
otx2_flow_raw_item_prepare((const struct rte_flow_item_raw *)
pst->pattern->spec,
(const struct rte_flow_item_raw *)
pst->pattern->mask, &info,
raw_spec_buf, raw_mask_buf);
lid = NPC_LID_LC;
lt = NPC_LT_LC_NGIO;
info.hw_mask = &hw_mask;
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
break;
default:
/* No match at this layer */
return 0;
}
/* Identify if IP tunnels MPLS or IPv4/v6 */
flow_check_lc_ip_tunnel(pst);
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
if (rc != 0)
return rc;
return otx2_flow_update_parse_state(pst, &info, lid, lt, 0);
}
/* VLAN, ETAG */
int
otx2_flow_parse_lb(struct otx2_parse_state *pst)
{
const struct rte_flow_item *pattern = pst->pattern;
uint8_t raw_spec_buf[NPC_MAX_RAW_ITEM_LEN];
uint8_t raw_mask_buf[NPC_MAX_RAW_ITEM_LEN];
const struct rte_flow_item *last_pattern;
const struct rte_flow_item_raw *raw_spec;
char hw_mask[NPC_MAX_EXTRACT_DATA_LEN];
struct otx2_flow_item_info info;
int lid, lt, lflags, len;
int nr_vlans = 0;
int rc;
info.spec = NULL;
info.mask = NULL;
info.hw_hdr_len = NPC_TPID_LENGTH;
lid = NPC_LID_LB;
lflags = 0;
last_pattern = pattern;
if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_VLAN) {
/* RTE vlan is either 802.1q or 802.1ad,
* this maps to either CTAG/STAG. We need to decide
* based on number of VLANS present. Matching is
* supported on first tag only.
*/
info.def_mask = &rte_flow_item_vlan_mask;
info.hw_mask = NULL;
info.len = sizeof(struct rte_flow_item_vlan);
pattern = pst->pattern;
while (pattern->type == RTE_FLOW_ITEM_TYPE_VLAN) {
nr_vlans++;
/* Basic validation of 2nd/3rd vlan item */
if (nr_vlans > 1) {
otx2_npc_dbg("Vlans = %d", nr_vlans);
rc = otx2_flow_parse_item_basic(pattern, &info,
pst->error);
if (rc != 0)
return rc;
}
last_pattern = pattern;
pattern++;
pattern = otx2_flow_skip_void_and_any_items(pattern);
}
switch (nr_vlans) {
case 1:
lt = NPC_LT_LB_CTAG;
break;
case 2:
lt = NPC_LT_LB_STAG_QINQ;
lflags = NPC_F_STAG_CTAG;
break;
case 3:
lt = NPC_LT_LB_STAG_QINQ;
lflags = NPC_F_STAG_STAG_CTAG;
break;
default:
rte_flow_error_set(pst->error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ITEM,
last_pattern,
"more than 3 vlans not supported");
return -rte_errno;
}
} else if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_E_TAG) {
/* we can support ETAG and match a subsequent CTAG
* without any matching support.
*/
lt = NPC_LT_LB_ETAG;
lflags = 0;
last_pattern = pst->pattern;
pattern = otx2_flow_skip_void_and_any_items(pst->pattern + 1);
if (pattern->type == RTE_FLOW_ITEM_TYPE_VLAN) {
info.def_mask = &rte_flow_item_vlan_mask;
/* set supported mask to NULL for vlan tag */
info.hw_mask = NULL;
info.len = sizeof(struct rte_flow_item_vlan);
rc = otx2_flow_parse_item_basic(pattern, &info,
pst->error);
if (rc != 0)
return rc;
lflags = NPC_F_ETAG_CTAG;
last_pattern = pattern;
}
info.def_mask = &rte_flow_item_e_tag_mask;
info.len = sizeof(struct rte_flow_item_e_tag);
} else if (pst->pattern->type == RTE_FLOW_ITEM_TYPE_RAW) {
raw_spec = pst->pattern->spec;
if (raw_spec->relative)
return 0;
len = raw_spec->length + raw_spec->offset;
if (len > NPC_MAX_RAW_ITEM_LEN) {
rte_flow_error_set(pst->error, EINVAL,
RTE_FLOW_ERROR_TYPE_ITEM, NULL,
"Spec length too big");
return -rte_errno;
}
if (pst->npc->switch_header_type ==
OTX2_PRIV_FLAGS_VLAN_EXDSA) {
lt = NPC_LT_LB_VLAN_EXDSA;
} else if (pst->npc->switch_header_type ==
OTX2_PRIV_FLAGS_EXDSA) {
lt = NPC_LT_LB_EXDSA;
} else {
rte_flow_error_set(pst->error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ITEM, NULL,
"exdsa or vlan_exdsa not enabled on"
" port");
return -rte_errno;
}
otx2_flow_raw_item_prepare((const struct rte_flow_item_raw *)
pst->pattern->spec,
(const struct rte_flow_item_raw *)
pst->pattern->mask, &info,
raw_spec_buf, raw_mask_buf);
info.hw_hdr_len = 0;
} else {
return 0;
}
info.hw_mask = &hw_mask;
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
if (rc != 0)
return rc;
/* Point pattern to last item consumed */
pst->pattern = last_pattern;
return otx2_flow_update_parse_state(pst, &info, lid, lt, lflags);
}
int
otx2_flow_parse_la(struct otx2_parse_state *pst)
{
struct rte_flow_item_eth hw_mask;
struct otx2_flow_item_info info;
int lid, lt;
int rc;
/* Identify the pattern type into lid, lt */
if (pst->pattern->type != RTE_FLOW_ITEM_TYPE_ETH)
return 0;
lid = NPC_LID_LA;
lt = NPC_LT_LA_ETHER;
info.hw_hdr_len = 0;
if (pst->flow->nix_intf == NIX_INTF_TX) {
lt = NPC_LT_LA_IH_NIX_ETHER;
info.hw_hdr_len = NPC_IH_LENGTH;
if (pst->npc->switch_header_type == OTX2_PRIV_FLAGS_HIGIG) {
lt = NPC_LT_LA_IH_NIX_HIGIG2_ETHER;
info.hw_hdr_len += NPC_HIGIG2_LENGTH;
}
} else {
if (pst->npc->switch_header_type == OTX2_PRIV_FLAGS_HIGIG) {
lt = NPC_LT_LA_HIGIG2_ETHER;
info.hw_hdr_len = NPC_HIGIG2_LENGTH;
}
}
/* Prepare for parsing the item */
info.def_mask = &rte_flow_item_eth_mask;
info.hw_mask = &hw_mask;
info.len = sizeof(struct rte_flow_item_eth);
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
info.spec = NULL;
info.mask = NULL;
/* Basic validation of item parameters */
rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
if (rc)
return rc;
/* Update pst if not validate only? clash check? */
return otx2_flow_update_parse_state(pst, &info, lid, lt, 0);
}
int
otx2_flow_parse_higig2_hdr(struct otx2_parse_state *pst)
{
struct rte_flow_item_higig2_hdr hw_mask;
struct otx2_flow_item_info info;
int lid, lt;
int rc;
/* Identify the pattern type into lid, lt */
if (pst->pattern->type != RTE_FLOW_ITEM_TYPE_HIGIG2)
return 0;
lid = NPC_LID_LA;
lt = NPC_LT_LA_HIGIG2_ETHER;
info.hw_hdr_len = 0;
if (pst->flow->nix_intf == NIX_INTF_TX) {
lt = NPC_LT_LA_IH_NIX_HIGIG2_ETHER;
info.hw_hdr_len = NPC_IH_LENGTH;
}
/* Prepare for parsing the item */
info.def_mask = &rte_flow_item_higig2_hdr_mask;
info.hw_mask = &hw_mask;
info.len = sizeof(struct rte_flow_item_higig2_hdr);
otx2_flow_get_hw_supp_mask(pst, &info, lid, lt);
info.spec = NULL;
info.mask = NULL;
/* Basic validation of item parameters */
rc = otx2_flow_parse_item_basic(pst->pattern, &info, pst->error);
if (rc)
return rc;
/* Update pst if not validate only? clash check? */
return otx2_flow_update_parse_state(pst, &info, lid, lt, 0);
}
static int
parse_rss_action(struct rte_eth_dev *dev,
const struct rte_flow_attr *attr,
const struct rte_flow_action *act,
struct rte_flow_error *error)
{
struct otx2_eth_dev *hw = dev->data->dev_private;
struct otx2_rss_info *rss_info = &hw->rss_info;
const struct rte_flow_action_rss *rss;
uint32_t i;
rss = (const struct rte_flow_action_rss *)act->conf;
/* Not supported */
if (attr->egress) {
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ATTR_EGRESS,
attr, "No support of RSS in egress");
}
if (dev->data->dev_conf.rxmode.mq_mode != ETH_MQ_RX_RSS)
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION,
act, "multi-queue mode is disabled");
/* Parse RSS related parameters from configuration */
if (!rss || !rss->queue_num)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
act, "no valid queues");
if (rss->func != RTE_ETH_HASH_FUNCTION_DEFAULT)
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION, act,
"non-default RSS hash functions"
" are not supported");
if (rss->key_len && rss->key_len > RTE_DIM(rss_info->key))
return rte_flow_error_set(error, ENOTSUP,
RTE_FLOW_ERROR_TYPE_ACTION, act,
"RSS hash key too large");
if (rss->queue_num > rss_info->rss_size)
return rte_flow_error_set
(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ACTION, act,
"too many queues for RSS context");
for (i = 0; i < rss->queue_num; i++) {
if (rss->queue[i] >= dev->data->nb_rx_queues)
return rte_flow_error_set(error, EINVAL,
RTE_FLOW_ERROR_TYPE_ACTION,
act,
"queue id > max number"
" of queues");
}
return 0;
}
int
otx2_flow_parse_actions(struct rte_eth_dev *dev,
const struct rte_flow_attr *attr,
const struct rte_flow_action actions[],
struct rte_flow_error *error,
struct rte_flow *flow)
{
struct otx2_eth_dev *hw = dev->data->dev_private;
struct otx2_npc_flow_info *npc = &hw->npc_flow;
const struct rte_flow_action_port_id *port_act;
const struct rte_flow_action_mark *act_mark;
const struct rte_flow_action_queue *act_q;
const struct rte_flow_action_vf *vf_act;
uint16_t pf_func, vf_id, port_id, pf_id;
char if_name[RTE_ETH_NAME_MAX_LEN];
bool vlan_insert_action = false;
struct rte_eth_dev *eth_dev;
const char *errmsg = NULL;
int sel_act, req_act = 0;
int errcode = 0;
int mark = 0;
int rq = 0;
/* Initialize actions */
flow->ctr_id = NPC_COUNTER_NONE;
pf_func = otx2_pfvf_func(hw->pf, hw->vf);
for (; actions->type != RTE_FLOW_ACTION_TYPE_END; actions++) {
otx2_npc_dbg("Action type = %d", actions->type);
switch (actions->type) {
case RTE_FLOW_ACTION_TYPE_VOID:
break;
case RTE_FLOW_ACTION_TYPE_MARK:
act_mark =
(const struct rte_flow_action_mark *)actions->conf;
/* We have only 16 bits. Use highest val for flag */
if (act_mark->id > (OTX2_FLOW_FLAG_VAL - 2)) {
errmsg = "mark value must be < 0xfffe";
errcode = ENOTSUP;
goto err_exit;
}
mark = act_mark->id + 1;
req_act |= OTX2_FLOW_ACT_MARK;
rte_atomic32_inc(&npc->mark_actions);
break;
case RTE_FLOW_ACTION_TYPE_FLAG:
mark = OTX2_FLOW_FLAG_VAL;
req_act |= OTX2_FLOW_ACT_FLAG;
rte_atomic32_inc(&npc->mark_actions);
break;
case RTE_FLOW_ACTION_TYPE_COUNT:
/* Indicates, need a counter */
flow->ctr_id = 1;
req_act |= OTX2_FLOW_ACT_COUNT;
break;
case RTE_FLOW_ACTION_TYPE_DROP:
req_act |= OTX2_FLOW_ACT_DROP;
break;
case RTE_FLOW_ACTION_TYPE_PF:
req_act |= OTX2_FLOW_ACT_PF;
pf_func &= (0xfc00);
break;
case RTE_FLOW_ACTION_TYPE_VF:
vf_act = (const struct rte_flow_action_vf *)
actions->conf;
req_act |= OTX2_FLOW_ACT_VF;
if (vf_act->original == 0) {
vf_id = vf_act->id & RVU_PFVF_FUNC_MASK;
if (vf_id >= hw->maxvf) {
errmsg = "invalid vf specified";
errcode = EINVAL;
goto err_exit;
}
pf_func &= (0xfc00);
pf_func = (pf_func | (vf_id + 1));
}
break;
case RTE_FLOW_ACTION_TYPE_PORT_ID:
port_act = (const struct rte_flow_action_port_id *)
actions->conf;
port_id = port_act->id;
if (rte_eth_dev_get_name_by_port(port_id, if_name)) {
errmsg = "Name not found for output port id";
errcode = EINVAL;
goto err_exit;
}
eth_dev = rte_eth_dev_allocated(if_name);
if (!eth_dev) {
errmsg = "eth_dev not found for output port id";
errcode = EINVAL;
goto err_exit;
}
if (!otx2_ethdev_is_same_driver(eth_dev)) {
errmsg = "Output port id unsupported type";
errcode = ENOTSUP;
goto err_exit;
}
if (!otx2_dev_is_vf(otx2_eth_pmd_priv(eth_dev))) {
errmsg = "Output port should be VF";
errcode = ENOTSUP;
goto err_exit;
}
vf_id = otx2_eth_pmd_priv(eth_dev)->vf;
if (vf_id >= hw->maxvf) {
errmsg = "Invalid vf for output port";
errcode = EINVAL;
goto err_exit;
}
pf_id = otx2_eth_pmd_priv(eth_dev)->pf;
if (pf_id != hw->pf) {
errmsg = "Output port unsupported PF";
errcode = ENOTSUP;
goto err_exit;
}
pf_func &= (0xfc00);
pf_func = (pf_func | (vf_id + 1));
req_act |= OTX2_FLOW_ACT_VF;
break;
case RTE_FLOW_ACTION_TYPE_QUEUE:
/* Applicable only to ingress flow */
act_q = (const struct rte_flow_action_queue *)
actions->conf;
rq = act_q->index;
if (rq >= dev->data->nb_rx_queues) {
errmsg = "invalid queue index";
errcode = EINVAL;
goto err_exit;
}
req_act |= OTX2_FLOW_ACT_QUEUE;
break;
case RTE_FLOW_ACTION_TYPE_RSS:
errcode = parse_rss_action(dev, attr, actions, error);
if (errcode)
return -rte_errno;
req_act |= OTX2_FLOW_ACT_RSS;
break;
case RTE_FLOW_ACTION_TYPE_SECURITY:
/* Assumes user has already configured security
* session for this flow. Associated conf is
* opaque. When RTE security is implemented for otx2,
* we need to verify that for specified security
* session:
* action_type ==
* RTE_SECURITY_ACTION_TYPE_INLINE_PROTOCOL &&
* session_protocol ==
* RTE_SECURITY_PROTOCOL_IPSEC
*
* RSS is not supported with inline ipsec. Get the
* rq from associated conf, or make
* RTE_FLOW_ACTION_TYPE_QUEUE compulsory with this
* action.
* Currently, rq = 0 is assumed.
*/
req_act |= OTX2_FLOW_ACT_SEC;
rq = 0;
break;
case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_VID:
req_act |= OTX2_FLOW_ACT_VLAN_INSERT;
break;
case RTE_FLOW_ACTION_TYPE_OF_POP_VLAN:
req_act |= OTX2_FLOW_ACT_VLAN_STRIP;
break;
case RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN:
req_act |= OTX2_FLOW_ACT_VLAN_ETHTYPE_INSERT;
break;
case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_PCP:
req_act |= OTX2_FLOW_ACT_VLAN_PCP_INSERT;
break;
default:
errmsg = "Unsupported action specified";
errcode = ENOTSUP;
goto err_exit;
}
}
if (req_act &
(OTX2_FLOW_ACT_VLAN_INSERT | OTX2_FLOW_ACT_VLAN_ETHTYPE_INSERT |
OTX2_FLOW_ACT_VLAN_PCP_INSERT))
vlan_insert_action = true;
if ((req_act &
(OTX2_FLOW_ACT_VLAN_INSERT | OTX2_FLOW_ACT_VLAN_ETHTYPE_INSERT |
OTX2_FLOW_ACT_VLAN_PCP_INSERT)) ==
OTX2_FLOW_ACT_VLAN_PCP_INSERT) {
errmsg = " PCP insert action can't be supported alone";
errcode = ENOTSUP;
goto err_exit;
}
/* Both STRIP and INSERT actions are not supported */
if (vlan_insert_action && (req_act & OTX2_FLOW_ACT_VLAN_STRIP)) {
errmsg = "Both VLAN insert and strip actions not supported"
" together";
errcode = ENOTSUP;
goto err_exit;
}
/* Check if actions specified are compatible */
if (attr->egress) {
if (req_act & OTX2_FLOW_ACT_VLAN_STRIP) {
errmsg = "VLAN pop action is not supported on Egress";
errcode = ENOTSUP;
goto err_exit;
}
if (req_act & OTX2_FLOW_ACT_DROP) {
flow->npc_action = NIX_TX_ACTIONOP_DROP;
} else if ((req_act & OTX2_FLOW_ACT_COUNT) ||
vlan_insert_action) {
flow->npc_action = NIX_TX_ACTIONOP_UCAST_DEFAULT;
} else {
errmsg = "Unsupported action for egress";
errcode = EINVAL;
goto err_exit;
}
goto set_pf_func;
}
/* We have already verified the attr, this is ingress.
* - Exactly one terminating action is supported
* - Exactly one of MARK or FLAG is supported
* - If terminating action is DROP, only count is valid.
*/
sel_act = req_act & OTX2_FLOW_ACT_TERM;
if ((sel_act & (sel_act - 1)) != 0) {
errmsg = "Only one terminating action supported";
errcode = EINVAL;
goto err_exit;
}
if (req_act & OTX2_FLOW_ACT_DROP) {
sel_act = req_act & ~OTX2_FLOW_ACT_COUNT;
if ((sel_act & (sel_act - 1)) != 0) {
errmsg = "Only COUNT action is supported "
"with DROP ingress action";
errcode = ENOTSUP;
goto err_exit;
}
}
if ((req_act & (OTX2_FLOW_ACT_FLAG | OTX2_FLOW_ACT_MARK))
== (OTX2_FLOW_ACT_FLAG | OTX2_FLOW_ACT_MARK)) {
errmsg = "Only one of FLAG or MARK action is supported";
errcode = ENOTSUP;
goto err_exit;
}
if (vlan_insert_action) {
errmsg = "VLAN push/Insert action is not supported on Ingress";
errcode = ENOTSUP;
goto err_exit;
}
if (req_act & OTX2_FLOW_ACT_VLAN_STRIP)
npc->vtag_actions++;
/* Only VLAN action is provided */
if (req_act == OTX2_FLOW_ACT_VLAN_STRIP)
flow->npc_action = NIX_RX_ACTIONOP_UCAST;
/* Set NIX_RX_ACTIONOP */
else if (req_act & (OTX2_FLOW_ACT_PF | OTX2_FLOW_ACT_VF)) {
flow->npc_action = NIX_RX_ACTIONOP_UCAST;
if (req_act & OTX2_FLOW_ACT_QUEUE)
flow->npc_action |= (uint64_t)rq << 20;
} else if (req_act & OTX2_FLOW_ACT_DROP) {
flow->npc_action = NIX_RX_ACTIONOP_DROP;
} else if (req_act & OTX2_FLOW_ACT_QUEUE) {
flow->npc_action = NIX_RX_ACTIONOP_UCAST;
flow->npc_action |= (uint64_t)rq << 20;
} else if (req_act & OTX2_FLOW_ACT_RSS) {
/* When user added a rule for rss, first we will add the
*rule in MCAM and then update the action, once if we have
*FLOW_KEY_ALG index. So, till we update the action with
*flow_key_alg index, set the action to drop.
*/
if (dev->data->dev_conf.rxmode.mq_mode == ETH_MQ_RX_RSS)
flow->npc_action = NIX_RX_ACTIONOP_DROP;
else
flow->npc_action = NIX_RX_ACTIONOP_UCAST;
} else if (req_act & OTX2_FLOW_ACT_SEC) {
flow->npc_action = NIX_RX_ACTIONOP_UCAST_IPSEC;
flow->npc_action |= (uint64_t)rq << 20;
} else if (req_act & (OTX2_FLOW_ACT_FLAG | OTX2_FLOW_ACT_MARK)) {
flow->npc_action = NIX_RX_ACTIONOP_UCAST;
} else if (req_act & OTX2_FLOW_ACT_COUNT) {
/* Keep OTX2_FLOW_ACT_COUNT always at the end
* This is default action, when user specify only
* COUNT ACTION
*/
flow->npc_action = NIX_RX_ACTIONOP_UCAST;
} else {
/* Should never reach here */
errmsg = "Invalid action specified";
errcode = EINVAL;
goto err_exit;
}
if (mark)
flow->npc_action |= (uint64_t)mark << 40;
if (rte_atomic32_read(&npc->mark_actions) == 1) {
hw->rx_offload_flags |=
NIX_RX_OFFLOAD_MARK_UPDATE_F;
otx2_eth_set_rx_function(dev);
}
if (npc->vtag_actions == 1) {
hw->rx_offload_flags |= NIX_RX_OFFLOAD_VLAN_STRIP_F;
otx2_eth_set_rx_function(dev);
}
set_pf_func:
/* Ideally AF must ensure that correct pf_func is set */
if (attr->egress)
flow->npc_action |= (uint64_t)pf_func << 48;
else
flow->npc_action |= (uint64_t)pf_func << 4;
return 0;
err_exit:
rte_flow_error_set(error, errcode,
RTE_FLOW_ERROR_TYPE_ACTION_NUM, NULL,
errmsg);
return -rte_errno;
}