net/sfc: support action VXLAN encap in MAE backend
authorIvan Malov <ivan.malov@oktetlabs.ru>
Fri, 12 Mar 2021 11:07:43 +0000 (14:07 +0300)
committerFerruh Yigit <ferruh.yigit@intel.com>
Mon, 22 Mar 2021 16:19:16 +0000 (17:19 +0100)
Provide necessary facilities for handling this action.

Signed-off-by: Ivan Malov <ivan.malov@oktetlabs.ru>
Reviewed-by: Andrew Rybchenko <andrew.rybchenko@oktetlabs.ru>
Reviewed-by: Andy Moreton <amoreton@xilinx.com>
drivers/net/sfc/sfc_mae.c
drivers/net/sfc/sfc_mae.h

index bbf328b..5eb3f4a 100644 (file)
@@ -9,7 +9,9 @@
 
 #include <stdbool.h>
 
+#include <rte_bitops.h>
 #include <rte_common.h>
+#include <rte_vxlan.h>
 
 #include "efx.h"
 
@@ -35,6 +37,7 @@ sfc_mae_attach(struct sfc_adapter *sa)
        const efx_nic_cfg_t *encp = efx_nic_cfg_get(sa->nic);
        efx_mport_sel_t entity_mport;
        struct sfc_mae *mae = &sa->mae;
+       struct sfc_mae_bounce_eh *bounce_eh = &mae->bounce_eh;
        efx_mae_limits_t limits;
        int rc;
 
@@ -80,17 +83,26 @@ sfc_mae_attach(struct sfc_adapter *sa)
        if (rc != 0)
                goto fail_mae_assign_switch_port;
 
+       sfc_log_init(sa, "allocate encap. header bounce buffer");
+       bounce_eh->buf_size = limits.eml_encap_header_size_limit;
+       bounce_eh->buf = rte_malloc("sfc_mae_bounce_eh",
+                                   bounce_eh->buf_size, 0);
+       if (bounce_eh->buf == NULL)
+               goto fail_mae_alloc_bounce_eh;
+
        mae->status = SFC_MAE_STATUS_SUPPORTED;
        mae->nb_outer_rule_prios_max = limits.eml_max_n_outer_prios;
        mae->nb_action_rule_prios_max = limits.eml_max_n_action_prios;
        mae->encap_types_supported = limits.eml_encap_types_supported;
        TAILQ_INIT(&mae->outer_rules);
+       TAILQ_INIT(&mae->encap_headers);
        TAILQ_INIT(&mae->action_sets);
 
        sfc_log_init(sa, "done");
 
        return 0;
 
+fail_mae_alloc_bounce_eh:
 fail_mae_assign_switch_port:
 fail_mae_assign_switch_domain:
 fail_mae_assign_entity_mport:
@@ -117,6 +129,8 @@ sfc_mae_detach(struct sfc_adapter *sa)
        if (status_prev != SFC_MAE_STATUS_SUPPORTED)
                return;
 
+       rte_free(mae->bounce_eh.buf);
+
        efx_mae_fini(sa->nic);
 
        sfc_log_init(sa, "done");
@@ -254,8 +268,165 @@ sfc_mae_outer_rule_disable(struct sfc_adapter *sa,
        return 0;
 }
 
+static struct sfc_mae_encap_header *
+sfc_mae_encap_header_attach(struct sfc_adapter *sa,
+                           const struct sfc_mae_bounce_eh *bounce_eh)
+{
+       struct sfc_mae_encap_header *encap_header;
+       struct sfc_mae *mae = &sa->mae;
+
+       SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+       TAILQ_FOREACH(encap_header, &mae->encap_headers, entries) {
+               if (encap_header->size == bounce_eh->size &&
+                   memcmp(encap_header->buf, bounce_eh->buf,
+                          bounce_eh->size) == 0) {
+                       ++(encap_header->refcnt);
+                       return encap_header;
+               }
+       }
+
+       return NULL;
+}
+
+static int
+sfc_mae_encap_header_add(struct sfc_adapter *sa,
+                        const struct sfc_mae_bounce_eh *bounce_eh,
+                        struct sfc_mae_encap_header **encap_headerp)
+{
+       struct sfc_mae_encap_header *encap_header;
+       struct sfc_mae *mae = &sa->mae;
+
+       SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+       encap_header = rte_zmalloc("sfc_mae_encap_header",
+                                  sizeof(*encap_header), 0);
+       if (encap_header == NULL)
+               return ENOMEM;
+
+       encap_header->size = bounce_eh->size;
+
+       encap_header->buf = rte_malloc("sfc_mae_encap_header_buf",
+                                      encap_header->size, 0);
+       if (encap_header->buf == NULL) {
+               rte_free(encap_header);
+               return ENOMEM;
+       }
+
+       rte_memcpy(encap_header->buf, bounce_eh->buf, bounce_eh->size);
+
+       encap_header->refcnt = 1;
+       encap_header->type = bounce_eh->type;
+       encap_header->fw_rsrc.eh_id.id = EFX_MAE_RSRC_ID_INVALID;
+
+       TAILQ_INSERT_TAIL(&mae->encap_headers, encap_header, entries);
+
+       *encap_headerp = encap_header;
+
+       return 0;
+}
+
+static void
+sfc_mae_encap_header_del(struct sfc_adapter *sa,
+                      struct sfc_mae_encap_header *encap_header)
+{
+       struct sfc_mae *mae = &sa->mae;
+
+       if (encap_header == NULL)
+               return;
+
+       SFC_ASSERT(sfc_adapter_is_locked(sa));
+       SFC_ASSERT(encap_header->refcnt != 0);
+
+       --(encap_header->refcnt);
+
+       if (encap_header->refcnt != 0)
+               return;
+
+       SFC_ASSERT(encap_header->fw_rsrc.eh_id.id == EFX_MAE_RSRC_ID_INVALID);
+       SFC_ASSERT(encap_header->fw_rsrc.refcnt == 0);
+
+       TAILQ_REMOVE(&mae->encap_headers, encap_header, entries);
+       rte_free(encap_header->buf);
+       rte_free(encap_header);
+}
+
+static int
+sfc_mae_encap_header_enable(struct sfc_adapter *sa,
+                           struct sfc_mae_encap_header *encap_header,
+                           efx_mae_actions_t *action_set_spec)
+{
+       struct sfc_mae_fw_rsrc *fw_rsrc;
+       int rc;
+
+       if (encap_header == NULL)
+               return 0;
+
+       SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+       fw_rsrc = &encap_header->fw_rsrc;
+
+       if (fw_rsrc->refcnt == 0) {
+               SFC_ASSERT(fw_rsrc->eh_id.id == EFX_MAE_RSRC_ID_INVALID);
+               SFC_ASSERT(encap_header->buf != NULL);
+               SFC_ASSERT(encap_header->size != 0);
+
+               rc = efx_mae_encap_header_alloc(sa->nic, encap_header->type,
+                                               encap_header->buf,
+                                               encap_header->size,
+                                               &fw_rsrc->eh_id);
+               if (rc != 0)
+                       return rc;
+       }
+
+       rc = efx_mae_action_set_fill_in_eh_id(action_set_spec,
+                                             &fw_rsrc->eh_id);
+       if (rc != 0) {
+               if (fw_rsrc->refcnt == 0) {
+                       (void)efx_mae_encap_header_free(sa->nic,
+                                                       &fw_rsrc->eh_id);
+               }
+               return rc;
+       }
+
+       ++(fw_rsrc->refcnt);
+
+       return 0;
+}
+
+static int
+sfc_mae_encap_header_disable(struct sfc_adapter *sa,
+                            struct sfc_mae_encap_header *encap_header)
+{
+       struct sfc_mae_fw_rsrc *fw_rsrc;
+       int rc;
+
+       if (encap_header == NULL)
+               return 0;
+
+       SFC_ASSERT(sfc_adapter_is_locked(sa));
+
+       fw_rsrc = &encap_header->fw_rsrc;
+
+       SFC_ASSERT(fw_rsrc->eh_id.id != EFX_MAE_RSRC_ID_INVALID);
+       SFC_ASSERT(fw_rsrc->refcnt != 0);
+
+       if (fw_rsrc->refcnt == 1) {
+               rc = efx_mae_encap_header_free(sa->nic, &fw_rsrc->eh_id);
+               if (rc != 0)
+                       return rc;
+
+               fw_rsrc->eh_id.id = EFX_MAE_RSRC_ID_INVALID;
+       }
+
+       --(fw_rsrc->refcnt);
+
+       return 0;
+}
+
 static struct sfc_mae_action_set *
 sfc_mae_action_set_attach(struct sfc_adapter *sa,
+                         const struct sfc_mae_encap_header *encap_header,
                          const efx_mae_actions_t *spec)
 {
        struct sfc_mae_action_set *action_set;
@@ -264,7 +435,8 @@ sfc_mae_action_set_attach(struct sfc_adapter *sa,
        SFC_ASSERT(sfc_adapter_is_locked(sa));
 
        TAILQ_FOREACH(action_set, &mae->action_sets, entries) {
-               if (efx_mae_action_set_specs_equal(action_set->spec, spec)) {
+               if (action_set->encap_header == encap_header &&
+                   efx_mae_action_set_specs_equal(action_set->spec, spec)) {
                        ++(action_set->refcnt);
                        return action_set;
                }
@@ -276,6 +448,7 @@ sfc_mae_action_set_attach(struct sfc_adapter *sa,
 static int
 sfc_mae_action_set_add(struct sfc_adapter *sa,
                       efx_mae_actions_t *spec,
+                      struct sfc_mae_encap_header *encap_header,
                       struct sfc_mae_action_set **action_setp)
 {
        struct sfc_mae_action_set *action_set;
@@ -289,6 +462,7 @@ sfc_mae_action_set_add(struct sfc_adapter *sa,
 
        action_set->refcnt = 1;
        action_set->spec = spec;
+       action_set->encap_header = encap_header;
 
        action_set->fw_rsrc.aset_id.id = EFX_MAE_RSRC_ID_INVALID;
 
@@ -317,6 +491,7 @@ sfc_mae_action_set_del(struct sfc_adapter *sa,
        SFC_ASSERT(action_set->fw_rsrc.refcnt == 0);
 
        efx_mae_action_set_spec_fini(sa->nic, action_set->spec);
+       sfc_mae_encap_header_del(sa, action_set->encap_header);
        TAILQ_REMOVE(&mae->action_sets, action_set, entries);
        rte_free(action_set);
 }
@@ -325,6 +500,7 @@ static int
 sfc_mae_action_set_enable(struct sfc_adapter *sa,
                          struct sfc_mae_action_set *action_set)
 {
+       struct sfc_mae_encap_header *encap_header = action_set->encap_header;
        struct sfc_mae_fw_rsrc *fw_rsrc = &action_set->fw_rsrc;
        int rc;
 
@@ -334,10 +510,18 @@ sfc_mae_action_set_enable(struct sfc_adapter *sa,
                SFC_ASSERT(fw_rsrc->aset_id.id == EFX_MAE_RSRC_ID_INVALID);
                SFC_ASSERT(action_set->spec != NULL);
 
+               rc = sfc_mae_encap_header_enable(sa, encap_header,
+                                                action_set->spec);
+               if (rc != 0)
+                       return rc;
+
                rc = efx_mae_action_set_alloc(sa->nic, action_set->spec,
                                              &fw_rsrc->aset_id);
-               if (rc != 0)
+               if (rc != 0) {
+                       (void)sfc_mae_encap_header_disable(sa, encap_header);
+
                        return rc;
+               }
        }
 
        ++(fw_rsrc->refcnt);
@@ -362,6 +546,10 @@ sfc_mae_action_set_disable(struct sfc_adapter *sa,
                        return rc;
 
                fw_rsrc->aset_id.id = EFX_MAE_RSRC_ID_INVALID;
+
+               rc = sfc_mae_encap_header_disable(sa, action_set->encap_header);
+               if (rc != 0)
+                       return rc;
        }
 
        --(fw_rsrc->refcnt);
@@ -1936,6 +2124,307 @@ sfc_mae_rule_parse_action_of_set_vlan_pcp(
        bundle->vlan_push_tci |= rte_cpu_to_be_16(vlan_tci_pcp);
 }
 
+struct sfc_mae_parsed_item {
+       const struct rte_flow_item      *item;
+       size_t                          proto_header_ofst;
+       size_t                          proto_header_size;
+};
+
+/*
+ * For each 16-bit word of the given header, override
+ * bits enforced by the corresponding 16-bit mask.
+ */
+static void
+sfc_mae_header_force_item_masks(uint8_t *header_buf,
+                               const struct sfc_mae_parsed_item *parsed_items,
+                               unsigned int nb_parsed_items)
+{
+       unsigned int item_idx;
+
+       for (item_idx = 0; item_idx < nb_parsed_items; ++item_idx) {
+               const struct sfc_mae_parsed_item *parsed_item;
+               const struct rte_flow_item *item;
+               size_t proto_header_size;
+               size_t ofst;
+
+               parsed_item = &parsed_items[item_idx];
+               proto_header_size = parsed_item->proto_header_size;
+               item = parsed_item->item;
+
+               for (ofst = 0; ofst < proto_header_size;
+                    ofst += sizeof(rte_be16_t)) {
+                       rte_be16_t *wp = RTE_PTR_ADD(header_buf, ofst);
+                       const rte_be16_t *w_maskp;
+                       const rte_be16_t *w_specp;
+
+                       w_maskp = RTE_PTR_ADD(item->mask, ofst);
+                       w_specp = RTE_PTR_ADD(item->spec, ofst);
+
+                       *wp &= ~(*w_maskp);
+                       *wp |= (*w_specp & *w_maskp);
+               }
+
+               header_buf += proto_header_size;
+       }
+}
+
+#define SFC_IPV4_TTL_DEF       0x40
+#define SFC_IPV6_VTC_FLOW_DEF  0x60000000
+#define SFC_IPV6_HOP_LIMITS_DEF        0xff
+#define SFC_VXLAN_FLAGS_DEF    0x08000000
+
+static int
+sfc_mae_rule_parse_action_vxlan_encap(
+                           struct sfc_mae *mae,
+                           const struct rte_flow_action_vxlan_encap *conf,
+                           efx_mae_actions_t *spec,
+                           struct rte_flow_error *error)
+{
+       struct sfc_mae_bounce_eh *bounce_eh = &mae->bounce_eh;
+       struct rte_flow_item *pattern = conf->definition;
+       uint8_t *buf = bounce_eh->buf;
+
+       /* This array will keep track of non-VOID pattern items. */
+       struct sfc_mae_parsed_item parsed_items[1 /* Ethernet */ +
+                                               2 /* VLAN tags */ +
+                                               1 /* IPv4 or IPv6 */ +
+                                               1 /* UDP */ +
+                                               1 /* VXLAN */];
+       unsigned int nb_parsed_items = 0;
+
+       size_t eth_ethertype_ofst = offsetof(struct rte_ether_hdr, ether_type);
+       uint8_t dummy_buf[RTE_MAX(sizeof(struct rte_ipv4_hdr),
+                                 sizeof(struct rte_ipv6_hdr))];
+       struct rte_ipv4_hdr *ipv4 = (void *)dummy_buf;
+       struct rte_ipv6_hdr *ipv6 = (void *)dummy_buf;
+       struct rte_vxlan_hdr *vxlan = NULL;
+       struct rte_udp_hdr *udp = NULL;
+       unsigned int nb_vlan_tags = 0;
+       size_t next_proto_ofst = 0;
+       size_t ethertype_ofst = 0;
+       uint64_t exp_items;
+
+       if (pattern == NULL) {
+               return rte_flow_error_set(error, EINVAL,
+                               RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                               "The encap. header definition is NULL");
+       }
+
+       bounce_eh->type = EFX_TUNNEL_PROTOCOL_VXLAN;
+       bounce_eh->size = 0;
+
+       /*
+        * Process pattern items and remember non-VOID ones.
+        * Defer applying masks until after the complete header
+        * has been built from the pattern items.
+        */
+       exp_items = RTE_BIT64(RTE_FLOW_ITEM_TYPE_ETH);
+
+       for (; pattern->type != RTE_FLOW_ITEM_TYPE_END; ++pattern) {
+               struct sfc_mae_parsed_item *parsed_item;
+               const uint64_t exp_items_extra_vlan[] = {
+                       RTE_BIT64(RTE_FLOW_ITEM_TYPE_VLAN), 0
+               };
+               size_t proto_header_size;
+               rte_be16_t *ethertypep;
+               uint8_t *next_protop;
+               uint8_t *buf_cur;
+
+               if (pattern->spec == NULL) {
+                       return rte_flow_error_set(error, EINVAL,
+                                       RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                                       "NULL item spec in the encap. header");
+               }
+
+               if (pattern->mask == NULL) {
+                       return rte_flow_error_set(error, EINVAL,
+                                       RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                                       "NULL item mask in the encap. header");
+               }
+
+               if (pattern->last != NULL) {
+                       /* This is not a match pattern, so disallow range. */
+                       return rte_flow_error_set(error, EINVAL,
+                                       RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                                       "Range item in the encap. header");
+               }
+
+               if (pattern->type == RTE_FLOW_ITEM_TYPE_VOID) {
+                       /* Handle VOID separately, for clarity. */
+                       continue;
+               }
+
+               if ((exp_items & RTE_BIT64(pattern->type)) == 0) {
+                       return rte_flow_error_set(error, ENOTSUP,
+                                       RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                                       "Unexpected item in the encap. header");
+               }
+
+               parsed_item = &parsed_items[nb_parsed_items];
+               buf_cur = buf + bounce_eh->size;
+
+               switch (pattern->type) {
+               case RTE_FLOW_ITEM_TYPE_ETH:
+                       SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ITEM_TYPE_ETH,
+                                              exp_items);
+                       RTE_BUILD_BUG_ON(offsetof(struct rte_flow_item_eth,
+                                                 hdr) != 0);
+
+                       proto_header_size = sizeof(struct rte_ether_hdr);
+
+                       ethertype_ofst = eth_ethertype_ofst;
+
+                       exp_items = RTE_BIT64(RTE_FLOW_ITEM_TYPE_VLAN) |
+                                   RTE_BIT64(RTE_FLOW_ITEM_TYPE_IPV4) |
+                                   RTE_BIT64(RTE_FLOW_ITEM_TYPE_IPV6);
+                       break;
+               case RTE_FLOW_ITEM_TYPE_VLAN:
+                       SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ITEM_TYPE_VLAN,
+                                              exp_items);
+                       RTE_BUILD_BUG_ON(offsetof(struct rte_flow_item_vlan,
+                                                 hdr) != 0);
+
+                       proto_header_size = sizeof(struct rte_vlan_hdr);
+
+                       ethertypep = RTE_PTR_ADD(buf, eth_ethertype_ofst);
+                       *ethertypep = RTE_BE16(RTE_ETHER_TYPE_QINQ);
+
+                       ethertypep = RTE_PTR_ADD(buf, ethertype_ofst);
+                       *ethertypep = RTE_BE16(RTE_ETHER_TYPE_VLAN);
+
+                       ethertype_ofst =
+                           bounce_eh->size +
+                           offsetof(struct rte_vlan_hdr, eth_proto);
+
+                       exp_items = RTE_BIT64(RTE_FLOW_ITEM_TYPE_IPV4) |
+                                   RTE_BIT64(RTE_FLOW_ITEM_TYPE_IPV6);
+                       exp_items |= exp_items_extra_vlan[nb_vlan_tags];
+
+                       ++nb_vlan_tags;
+                       break;
+               case RTE_FLOW_ITEM_TYPE_IPV4:
+                       SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ITEM_TYPE_IPV4,
+                                              exp_items);
+                       RTE_BUILD_BUG_ON(offsetof(struct rte_flow_item_ipv4,
+                                                 hdr) != 0);
+
+                       proto_header_size = sizeof(struct rte_ipv4_hdr);
+
+                       ethertypep = RTE_PTR_ADD(buf, ethertype_ofst);
+                       *ethertypep = RTE_BE16(RTE_ETHER_TYPE_IPV4);
+
+                       next_proto_ofst =
+                           bounce_eh->size +
+                           offsetof(struct rte_ipv4_hdr, next_proto_id);
+
+                       ipv4 = (struct rte_ipv4_hdr *)buf_cur;
+
+                       exp_items = RTE_BIT64(RTE_FLOW_ITEM_TYPE_UDP);
+                       break;
+               case RTE_FLOW_ITEM_TYPE_IPV6:
+                       SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ITEM_TYPE_IPV6,
+                                              exp_items);
+                       RTE_BUILD_BUG_ON(offsetof(struct rte_flow_item_ipv6,
+                                                 hdr) != 0);
+
+                       proto_header_size = sizeof(struct rte_ipv6_hdr);
+
+                       ethertypep = RTE_PTR_ADD(buf, ethertype_ofst);
+                       *ethertypep = RTE_BE16(RTE_ETHER_TYPE_IPV6);
+
+                       next_proto_ofst = bounce_eh->size +
+                                         offsetof(struct rte_ipv6_hdr, proto);
+
+                       ipv6 = (struct rte_ipv6_hdr *)buf_cur;
+
+                       exp_items = RTE_BIT64(RTE_FLOW_ITEM_TYPE_UDP);
+                       break;
+               case RTE_FLOW_ITEM_TYPE_UDP:
+                       SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ITEM_TYPE_UDP,
+                                              exp_items);
+                       RTE_BUILD_BUG_ON(offsetof(struct rte_flow_item_udp,
+                                                 hdr) != 0);
+
+                       proto_header_size = sizeof(struct rte_udp_hdr);
+
+                       next_protop = RTE_PTR_ADD(buf, next_proto_ofst);
+                       *next_protop = IPPROTO_UDP;
+
+                       udp = (struct rte_udp_hdr *)buf_cur;
+
+                       exp_items = RTE_BIT64(RTE_FLOW_ITEM_TYPE_VXLAN);
+                       break;
+               case RTE_FLOW_ITEM_TYPE_VXLAN:
+                       SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ITEM_TYPE_VXLAN,
+                                              exp_items);
+                       RTE_BUILD_BUG_ON(offsetof(struct rte_flow_item_vxlan,
+                                                 hdr) != 0);
+
+                       proto_header_size = sizeof(struct rte_vxlan_hdr);
+
+                       vxlan = (struct rte_vxlan_hdr *)buf_cur;
+
+                       udp->dst_port = RTE_BE16(RTE_VXLAN_DEFAULT_PORT);
+                       udp->dgram_len = RTE_BE16(sizeof(*udp) +
+                                                 sizeof(*vxlan));
+                       udp->dgram_cksum = 0;
+
+                       exp_items = 0;
+                       break;
+               default:
+                       return rte_flow_error_set(error, ENOTSUP,
+                                       RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                                       "Unknown item in the encap. header");
+               }
+
+               if (bounce_eh->size + proto_header_size > bounce_eh->buf_size) {
+                       return rte_flow_error_set(error, E2BIG,
+                                       RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                                       "The encap. header is too big");
+               }
+
+               if ((proto_header_size & 1) != 0) {
+                       return rte_flow_error_set(error, EINVAL,
+                                       RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                                       "Odd layer size in the encap. header");
+               }
+
+               rte_memcpy(buf_cur, pattern->spec, proto_header_size);
+               bounce_eh->size += proto_header_size;
+
+               parsed_item->item = pattern;
+               parsed_item->proto_header_size = proto_header_size;
+               ++nb_parsed_items;
+       }
+
+       if (exp_items != 0) {
+               /* Parsing item VXLAN would have reset exp_items to 0. */
+               return rte_flow_error_set(error, ENOTSUP,
+                                       RTE_FLOW_ERROR_TYPE_ACTION_CONF, NULL,
+                                       "No item VXLAN in the encap. header");
+       }
+
+       /* One of the pointers (ipv4, ipv6) refers to a dummy area. */
+       ipv4->version_ihl = RTE_IPV4_VHL_DEF;
+       ipv4->time_to_live = SFC_IPV4_TTL_DEF;
+       ipv4->total_length = RTE_BE16(sizeof(*ipv4) + sizeof(*udp) +
+                                     sizeof(*vxlan));
+       /* The HW cannot compute this checksum. */
+       ipv4->hdr_checksum = 0;
+       ipv4->hdr_checksum = rte_ipv4_cksum(ipv4);
+
+       ipv6->vtc_flow = RTE_BE32(SFC_IPV6_VTC_FLOW_DEF);
+       ipv6->hop_limits = SFC_IPV6_HOP_LIMITS_DEF;
+       ipv6->payload_len = udp->dgram_len;
+
+       vxlan->vx_flags = RTE_BE32(SFC_VXLAN_FLAGS_DEF);
+
+       /* Take care of the masks. */
+       sfc_mae_header_force_item_masks(buf, parsed_items, nb_parsed_items);
+
+       return (spec != NULL) ? efx_mae_action_set_populate_encap(spec) : 0;
+}
+
 static int
 sfc_mae_rule_parse_action_mark(const struct rte_flow_action_mark *conf,
                               efx_mae_actions_t *spec)
@@ -2016,6 +2505,7 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,
                          efx_mae_actions_t *spec,
                          struct rte_flow_error *error)
 {
+       bool custom_error = B_FALSE;
        int rc = 0;
 
        switch (action->type) {
@@ -2039,6 +2529,14 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,
                                       bundle->actions_mask);
                sfc_mae_rule_parse_action_of_set_vlan_pcp(action->conf, bundle);
                break;
+       case RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP:
+               SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP,
+                                      bundle->actions_mask);
+               rc = sfc_mae_rule_parse_action_vxlan_encap(&sa->mae,
+                                                          action->conf,
+                                                          spec, error);
+               custom_error = B_TRUE;
+               break;
        case RTE_FLOW_ACTION_TYPE_FLAG:
                SFC_BUILD_SET_OVERFLOW(RTE_FLOW_ACTION_TYPE_FLAG,
                                       bundle->actions_mask);
@@ -2080,24 +2578,49 @@ sfc_mae_rule_parse_action(struct sfc_adapter *sa,
                                "Unsupported action");
        }
 
-       if (rc != 0) {
+       if (rc == 0) {
+               bundle->actions_mask |= (1ULL << action->type);
+       } else if (!custom_error) {
                rc = rte_flow_error_set(error, rc, RTE_FLOW_ERROR_TYPE_ACTION,
                                NULL, "Failed to request the action");
-       } else {
-               bundle->actions_mask |= (1ULL << action->type);
        }
 
        return rc;
 }
 
+static void
+sfc_mae_bounce_eh_invalidate(struct sfc_mae_bounce_eh *bounce_eh)
+{
+       bounce_eh->type = EFX_TUNNEL_PROTOCOL_NONE;
+}
+
+static int
+sfc_mae_process_encap_header(struct sfc_adapter *sa,
+                            const struct sfc_mae_bounce_eh *bounce_eh,
+                            struct sfc_mae_encap_header **encap_headerp)
+{
+       if (bounce_eh->type == EFX_TUNNEL_PROTOCOL_NONE) {
+               encap_headerp = NULL;
+               return 0;
+       }
+
+       *encap_headerp = sfc_mae_encap_header_attach(sa, bounce_eh);
+       if (*encap_headerp != NULL)
+               return 0;
+
+       return sfc_mae_encap_header_add(sa, bounce_eh, encap_headerp);
+}
+
 int
 sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
                           const struct rte_flow_action actions[],
                           struct sfc_flow_spec_mae *spec_mae,
                           struct rte_flow_error *error)
 {
+       struct sfc_mae_encap_header *encap_header = NULL;
        struct sfc_mae_actions_bundle bundle = {0};
        const struct rte_flow_action *action;
+       struct sfc_mae *mae = &sa->mae;
        efx_mae_actions_t *spec;
        int rc;
 
@@ -2111,6 +2634,9 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
        if (rc != 0)
                goto fail_action_set_spec_init;
 
+       /* Cleanup after previous encap. header bounce buffer usage. */
+       sfc_mae_bounce_eh_invalidate(&mae->bounce_eh);
+
        for (action = actions;
             action->type != RTE_FLOW_ACTION_TYPE_END; ++action) {
                rc = sfc_mae_actions_bundle_sync(action, &bundle, spec, error);
@@ -2127,19 +2653,29 @@ sfc_mae_rule_parse_actions(struct sfc_adapter *sa,
        if (rc != 0)
                goto fail_rule_parse_action;
 
-       spec_mae->action_set = sfc_mae_action_set_attach(sa, spec);
+       rc = sfc_mae_process_encap_header(sa, &mae->bounce_eh, &encap_header);
+       if (rc != 0)
+               goto fail_process_encap_header;
+
+       spec_mae->action_set = sfc_mae_action_set_attach(sa, encap_header,
+                                                        spec);
        if (spec_mae->action_set != NULL) {
+               sfc_mae_encap_header_del(sa, encap_header);
                efx_mae_action_set_spec_fini(sa->nic, spec);
                return 0;
        }
 
-       rc = sfc_mae_action_set_add(sa, spec, &spec_mae->action_set);
+       rc = sfc_mae_action_set_add(sa, spec, encap_header,
+                                   &spec_mae->action_set);
        if (rc != 0)
                goto fail_action_set_add;
 
        return 0;
 
 fail_action_set_add:
+       sfc_mae_encap_header_del(sa, encap_header);
+
+fail_process_encap_header:
 fail_rule_parse_action:
        efx_mae_action_set_spec_fini(sa->nic, spec);
 
index 379055b..9740e54 100644 (file)
@@ -27,6 +27,7 @@ struct sfc_mae_fw_rsrc {
        union {
                efx_mae_aset_id_t       aset_id;
                efx_mae_rule_id_t       rule_id;
+               efx_mae_eh_id_t         eh_id;
        };
 };
 
@@ -41,11 +42,24 @@ struct sfc_mae_outer_rule {
 
 TAILQ_HEAD(sfc_mae_outer_rules, sfc_mae_outer_rule);
 
+/** Encap. header registry entry */
+struct sfc_mae_encap_header {
+       TAILQ_ENTRY(sfc_mae_encap_header)       entries;
+       unsigned int                            refcnt;
+       uint8_t                                 *buf;
+       size_t                                  size;
+       efx_tunnel_protocol_t                   type;
+       struct sfc_mae_fw_rsrc                  fw_rsrc;
+};
+
+TAILQ_HEAD(sfc_mae_encap_headers, sfc_mae_encap_header);
+
 /** Action set registry entry */
 struct sfc_mae_action_set {
        TAILQ_ENTRY(sfc_mae_action_set) entries;
        unsigned int                    refcnt;
        efx_mae_actions_t               *spec;
+       struct sfc_mae_encap_header     *encap_header;
        struct sfc_mae_fw_rsrc          fw_rsrc;
 };
 
@@ -58,6 +72,17 @@ enum sfc_mae_status {
        SFC_MAE_STATUS_SUPPORTED
 };
 
+/*
+ * Encap. header bounce buffer. It is used to store header data
+ * when parsing the header definition in the action VXLAN_ENCAP.
+ */
+struct sfc_mae_bounce_eh {
+       uint8_t                         *buf;
+       size_t                          buf_size;
+       size_t                          size;
+       efx_tunnel_protocol_t           type;
+};
+
 struct sfc_mae {
        /** Assigned switch domain identifier */
        uint16_t                        switch_domain_id;
@@ -73,8 +98,12 @@ struct sfc_mae {
        uint32_t                        encap_types_supported;
        /** Outer rule registry */
        struct sfc_mae_outer_rules      outer_rules;
+       /** Encap. header registry */
+       struct sfc_mae_encap_headers    encap_headers;
        /** Action set registry */
        struct sfc_mae_action_sets      action_sets;
+       /** Encap. header bounce buffer */
+       struct sfc_mae_bounce_eh        bounce_eh;
 };
 
 struct sfc_adapter;