X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=drivers%2Fnet%2Fenic%2Fenic_fm_flow.c;h=cd364ee16b38164c7731f5ae14fac418fa337d0b;hb=b43045eedeebb15354d85babdb84171a1073aa9d;hp=e299b3247d41e07cf8e79ceacda96082d482ad91;hpb=859540e719668fe935f553eedd1114c5a6ce1ba6;p=dpdk.git diff --git a/drivers/net/enic/enic_fm_flow.c b/drivers/net/enic/enic_fm_flow.c index e299b3247d..cd364ee16b 100644 --- a/drivers/net/enic/enic_fm_flow.c +++ b/drivers/net/enic/enic_fm_flow.c @@ -5,9 +5,11 @@ #include #include #include -#include +#include #include #include +#include +#include #include #include #include @@ -43,6 +45,9 @@ /* Tag used for implicit VF <-> representor flows */ #define FM_VF_REP_TAG 1 +/* Max number of actions supported by VIC is 2K. Make hash table double that. */ +#define FM_MAX_ACTION_TABLE_SIZE 4096 + /* * Flow exact match tables (FET) in the VIC and rte_flow groups. * Use a simple scheme to map groups to tables. @@ -90,13 +95,21 @@ struct enic_fm_counter { uint32_t handle; }; +struct enic_fm_action { + int ref; + uint64_t handle; + struct fm_action key; +}; + /* rte_flow.fm */ struct enic_fm_flow { bool counter_valid; uint64_t entry_handle; - uint64_t action_handle; + struct enic_fm_action *action; struct enic_fm_counter *counter; struct enic_fm_fet *fet; + /* Auto-added steer action for hairpin flows (e.g. vnic->vnic) */ + struct enic_fm_flow *hairpin_steer_flow; }; struct enic_fm_jump_flow { @@ -153,6 +166,8 @@ struct enic_flowman { */ struct enic_fm_fet *default_eg_fet; struct enic_fm_fet *default_ig_fet; + /* hash table for Action reuse */ + struct rte_hash *action_hash; /* Flows that jump to the default table above */ TAILQ_HEAD(jump_flow_list, enic_fm_jump_flow) jump_list; /* @@ -166,6 +181,9 @@ struct enic_flowman { int action_op_count; /* Tags used for representor flows */ uint8_t vf_rep_tag; + /* For auto-added steer action for hairpin */ + int need_hairpin_steer; + uint64_t hairpin_steer_vnic_h; }; static int enic_fm_tbl_free(struct enic_flowman *fm, uint64_t handle); @@ -244,6 +262,7 @@ static const enum rte_flow_action_type enic_fm_supported_eg_actions[] = { RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN, RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_PCP, RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_VID, + RTE_FLOW_ACTION_TYPE_PORT_ID, RTE_FLOW_ACTION_TYPE_PASSTHRU, RTE_FLOW_ACTION_TYPE_VOID, RTE_FLOW_ACTION_TYPE_VXLAN_ENCAP, @@ -349,8 +368,8 @@ enic_fm_copy_item_eth(struct copy_item_args *arg) fm_mask = &entry->ftm_mask.fk_hdrset[lvl]; fm_data->fk_header_select |= FKH_ETHER; fm_mask->fk_header_select |= FKH_ETHER; - memcpy(&fm_data->l2.eth, spec, sizeof(*spec)); - memcpy(&fm_mask->l2.eth, mask, sizeof(*mask)); + memcpy(&fm_data->l2.eth, spec, sizeof(struct rte_ether_hdr)); + memcpy(&fm_mask->l2.eth, mask, sizeof(struct rte_ether_hdr)); return 0; } @@ -386,8 +405,11 @@ enic_fm_copy_item_vlan(struct copy_item_args *arg) eth_mask = (void *)&fm_mask->l2.eth; eth_val = (void *)&fm_data->l2.eth; - /* Outer TPID cannot be matched */ - if (eth_mask->ether_type) + /* + * Outer TPID cannot be matched. If inner_type is 0, use what is + * in the eth header. + */ + if (eth_mask->ether_type && mask->inner_type) return -ENOTSUP; /* @@ -395,8 +417,10 @@ enic_fm_copy_item_vlan(struct copy_item_args *arg) * L2, regardless of vlan stripping settings. So, the inner type * from vlan becomes the ether type of the eth header. */ - eth_mask->ether_type = mask->inner_type; - eth_val->ether_type = spec->inner_type; + if (mask->inner_type) { + eth_mask->ether_type = mask->inner_type; + eth_val->ether_type = spec->inner_type; + } fm_data->fk_header_select |= FKH_ETHER | FKH_QTAG; fm_mask->fk_header_select |= FKH_ETHER | FKH_QTAG; fm_data->fk_vlan = rte_be_to_cpu_16(spec->tci); @@ -455,8 +479,8 @@ enic_fm_copy_item_ipv6(struct copy_item_args *arg) fm_data->fk_header_select |= FKH_IPV6; fm_mask->fk_header_select |= FKH_IPV6; - memcpy(&fm_data->l3.ip6, spec, sizeof(*spec)); - memcpy(&fm_mask->l3.ip6, mask, sizeof(*mask)); + memcpy(&fm_data->l3.ip6, spec, sizeof(struct rte_ipv6_hdr)); + memcpy(&fm_mask->l3.ip6, mask, sizeof(struct rte_ipv6_hdr)); return 0; } @@ -912,6 +936,20 @@ enic_fm_append_action_op(struct enic_flowman *fm, return 0; } +static struct fm_action_op * +find_prev_action_op(struct enic_flowman *fm, uint32_t opcode) +{ + struct fm_action_op *op; + int i; + + for (i = 0; i < fm->action_op_count; i++) { + op = &fm->action.fma_action_ops[i]; + if (op->fa_op == opcode) + return op; + } + return NULL; +} + /* NIC requires that 1st steer appear before decap. * Correct example: steer, decap, steer, steer, ... */ @@ -927,7 +965,8 @@ enic_fm_reorder_action_op(struct enic_flowman *fm) steer = NULL; decap = NULL; while (op->fa_op != FMOP_END) { - if (!decap && op->fa_op == FMOP_DECAP_NOSTRIP) + if (!decap && (op->fa_op == FMOP_DECAP_NOSTRIP || + op->fa_op == FMOP_DECAP_STRIP)) decap = op; else if (!steer && op->fa_op == FMOP_RQ_STEER) steer = op; @@ -967,6 +1006,17 @@ enic_fm_copy_vxlan_decap(struct enic_flowman *fm, return enic_fm_append_action_op(fm, &fm_op, error); } +/* Generate a reasonable source port number */ +static uint16_t +gen_src_port(void) +{ + /* Min/max below are the default values in OVS-DPDK and Linux */ + uint16_t p = rte_rand(); + p = RTE_MAX(p, 32768); + p = RTE_MIN(p, 61000); + return rte_cpu_to_be_16(p); +} + /* VXLAN encap is done via flowman compound action */ static int enic_fm_copy_vxlan_encap(struct enic_flowman *fm, @@ -975,6 +1025,7 @@ enic_fm_copy_vxlan_encap(struct enic_flowman *fm, { struct fm_action_op fm_op; struct rte_ether_hdr *eth; + struct rte_udp_hdr *udp; uint16_t *ethertype; void *template; uint8_t off; @@ -996,7 +1047,7 @@ enic_fm_copy_vxlan_encap(struct enic_flowman *fm, eth = (struct rte_ether_hdr *)template; ethertype = ð->ether_type; append_template(&template, &off, item->spec, - sizeof(struct rte_flow_item_eth)); + sizeof(struct rte_ether_hdr)); item++; flow_item_skip_void(&item); /* Optional VLAN */ @@ -1073,8 +1124,17 @@ enic_fm_copy_vxlan_encap(struct enic_flowman *fm, off + offsetof(struct rte_udp_hdr, dgram_len); fm_op.encap.len2_delta = sizeof(struct rte_udp_hdr) + sizeof(struct rte_vxlan_hdr); + udp = (struct rte_udp_hdr *)template; append_template(&template, &off, item->spec, sizeof(struct rte_udp_hdr)); + /* + * Firmware does not hash/fill source port yet. Generate a + * random port, as there is *usually* one rte_flow for the + * given inner packet stream (i.e. a single stream has one + * random port). + */ + if (udp->src_port == 0) + udp->src_port = gen_src_port(); item++; flow_item_skip_void(&item); @@ -1120,6 +1180,68 @@ enic_fm_find_vnic(struct enic *enic, const struct rte_pci_addr *addr, return 0; } +/* + * Egress: target port should be either PF uplink or VF. + * Supported cases + * 1. VF egress -> PF uplink + * PF may be this VF's PF, or another PF, as long as they are on the same VIC. + * 2. VF egress -> VF + * + * Unsupported cases + * 1. PF egress -> VF + * App should be using representor to pass packets to VF + */ +static int +vf_egress_port_id_action(struct enic_flowman *fm, + struct rte_eth_dev *dst_dev, + uint64_t dst_vnic_h, + struct fm_action_op *fm_op, + struct rte_flow_error *error) +{ + struct enic *src_enic, *dst_enic; + struct enic_vf_representor *vf; + uint8_t uif; + int ret; + + ENICPMD_FUNC_TRACE(); + src_enic = fm->user_enic; + dst_enic = pmd_priv(dst_dev); + if (!(src_enic->rte_dev->data->dev_flags & RTE_ETH_DEV_REPRESENTOR)) { + return rte_flow_error_set(error, EINVAL, + RTE_FLOW_ERROR_TYPE_ACTION, + NULL, "source port is not VF representor"); + } + + /* VF -> PF uplink. dst is not VF representor */ + if (!(dst_dev->data->dev_flags & RTE_ETH_DEV_REPRESENTOR)) { + /* PF is the VF's PF? Then nothing to do */ + vf = VF_ENIC_TO_VF_REP(src_enic); + if (vf->pf == dst_enic) { + ENICPMD_LOG(DEBUG, "destination port is VF's PF"); + return 0; + } + /* If not, steer to the remote PF's uplink */ + uif = dst_enic->fm_vnic_uif; + ENICPMD_LOG(DEBUG, "steer to uplink %u", uif); + memset(fm_op, 0, sizeof(*fm_op)); + fm_op->fa_op = FMOP_SET_EGPORT; + fm_op->set_egport.egport = uif; + ret = enic_fm_append_action_op(fm, fm_op, error); + return ret; + } + + /* VF -> VF loopback. Hairpin and steer to vnic */ + memset(fm_op, 0, sizeof(*fm_op)); + fm_op->fa_op = FMOP_EG_HAIRPIN; + ret = enic_fm_append_action_op(fm, fm_op, error); + if (ret) + return ret; + ENICPMD_LOG(DEBUG, "egress hairpin"); + fm->hairpin_steer_vnic_h = dst_vnic_h; + fm->need_hairpin_steer = 1; + return 0; +} + /* Translate flow actions to flowman TCAM entry actions */ static int enic_fm_copy_action(struct enic_flowman *fm, @@ -1192,6 +1314,8 @@ enic_fm_copy_action(struct enic_flowman *fm, const struct rte_flow_action_mark *mark = actions->conf; + if (enic->use_noscatter_vec_rx_handler) + goto unsupported; if (mark->id >= ENIC_MAGIC_FILTER_ID - 1) return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, @@ -1205,6 +1329,8 @@ enic_fm_copy_action(struct enic_flowman *fm, break; } case RTE_FLOW_ACTION_TYPE_FLAG: { + if (enic->use_noscatter_vec_rx_handler) + goto unsupported; /* ENIC_MAGIC_FILTER_ID is reserved for flagging */ memset(&fm_op, 0, sizeof(fm_op)); fm_op.fa_op = FMOP_MARK; @@ -1311,6 +1437,10 @@ enic_fm_copy_action(struct enic_flowman *fm, const struct rte_flow_action_port_id *port; struct rte_eth_dev *dev; + if (!ingress && (overlap & PORT_ID)) { + ENICPMD_LOG(DEBUG, "cannot have multiple egress PORT_ID actions"); + goto unsupported; + } port = actions->conf; if (port->original) { vnic_h = enic->fm_vnic_handle; /* This port */ @@ -1340,6 +1470,13 @@ enic_fm_copy_action(struct enic_flowman *fm, * Ingress. Nothing more to do. We add an implicit * steer at the end if needed. */ + if (ingress) + break; + /* Egress */ + ret = vf_egress_port_id_action(fm, dev, vnic_h, &fm_op, + error); + if (ret) + return ret; break; } case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP: { @@ -1367,6 +1504,19 @@ enic_fm_copy_action(struct enic_flowman *fm, break; } case RTE_FLOW_ACTION_TYPE_OF_POP_VLAN: { + struct fm_action_op *decap; + + /* + * If decap-nostrip appears before pop vlan, this pop + * applies to the inner packet vlan. Turn it into + * decap-strip. + */ + decap = find_prev_action_op(fm, FMOP_DECAP_NOSTRIP); + if (decap) { + ENICPMD_LOG(DEBUG, "pop-vlan inner: decap-nostrip => decap-strip"); + decap->fa_op = FMOP_DECAP_STRIP; + break; + } memset(&fm_op, 0, sizeof(fm_op)); fm_op.fa_op = FMOP_POP_VLAN; ret = enic_fm_append_action_op(fm, &fm_op, error); @@ -1633,9 +1783,10 @@ enic_fm_dump_tcam_match(const struct fm_tcam_match_entry *match, memset(buf, 0, sizeof(buf)); __enic_fm_dump_tcam_match(&match->ftm_mask.fk_hdrset[0], buf, sizeof(buf)); - ENICPMD_LOG(DEBUG, " TCAM %s Outer: %s %scounter", + ENICPMD_LOG(DEBUG, " TCAM %s Outer: %s %scounter position %u", (ingress) ? "IG" : "EG", buf, - (match->ftm_flags & FMEF_COUNTER) ? "" : "no "); + (match->ftm_flags & FMEF_COUNTER) ? "" : "no ", + match->ftm_position); memset(buf, 0, sizeof(buf)); __enic_fm_dump_tcam_match(&match->ftm_mask.fk_hdrset[1], buf, sizeof(buf)); @@ -1682,11 +1833,11 @@ enic_fm_flow_parse(struct enic_flowman *fm, } if (attrs) { - if (attrs->priority) { + if (attrs->group != FM_TCAM_RTE_GROUP && attrs->priority) { rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, NULL, - "priorities are not supported"); + "priorities are not supported for non-default (0) groups"); return -rte_errno; } else if (!fm->owner_enic->switchdev_mode && attrs->transfer) { rte_flow_error_set(error, ENOTSUP, @@ -1819,19 +1970,26 @@ enic_fm_counter_alloc(struct enic_flowman *fm, struct rte_flow_error *error, } static int -enic_fm_action_free(struct enic_flowman *fm, uint64_t handle) +enic_fm_action_free(struct enic_flowman *fm, struct enic_fm_action *ah) { uint64_t args[2]; - int rc; + int ret = 0; ENICPMD_FUNC_TRACE(); - args[0] = FM_ACTION_FREE; - args[1] = handle; - rc = flowman_cmd(fm, args, 2); - if (rc) - ENICPMD_LOG(ERR, "cannot free action: rc=%d handle=0x%" PRIx64, - rc, handle); - return rc; + RTE_ASSERT(ah->ref > 0); + ah->ref--; + if (ah->ref == 0) { + args[0] = FM_ACTION_FREE; + args[1] = ah->handle; + ret = flowman_cmd(fm, args, 2); + if (ret) + /* This is a "should never happen" error. */ + ENICPMD_LOG(ERR, "freeing action rc=%d handle=0x%" + PRIx64, ret, ah->handle); + rte_hash_del_key(fm->action_hash, (const void *)&ah->key); + free(ah); + } + return ret; } static int @@ -1907,9 +2065,9 @@ __enic_fm_flow_free(struct enic_flowman *fm, struct enic_fm_flow *fm_flow) enic_fm_entry_free(fm, fm_flow->entry_handle); fm_flow->entry_handle = FM_INVALID_HANDLE; } - if (fm_flow->action_handle != FM_INVALID_HANDLE) { - enic_fm_action_free(fm, fm_flow->action_handle); - fm_flow->action_handle = FM_INVALID_HANDLE; + if (fm_flow->action != NULL) { + enic_fm_action_free(fm, fm_flow->action); + fm_flow->action = NULL; } enic_fm_counter_free(fm, fm_flow); if (fm_flow->fet) { @@ -1921,9 +2079,15 @@ __enic_fm_flow_free(struct enic_flowman *fm, struct enic_fm_flow *fm_flow) static void enic_fm_flow_free(struct enic_flowman *fm, struct rte_flow *flow) { + struct enic_fm_flow *steer = flow->fm->hairpin_steer_flow; + if (flow->fm->fet && flow->fm->fet->default_key) remove_jump_flow(fm, flow); __enic_fm_flow_free(fm, flow->fm); + if (steer) { + __enic_fm_flow_free(fm, steer); + free(steer); + } free(flow->fm); free(flow); } @@ -2013,6 +2177,75 @@ enic_fm_add_exact_entry(struct enic_flowman *fm, return 0; } +static int +enic_action_handle_get(struct enic_flowman *fm, struct fm_action *action_in, + struct rte_flow_error *error, + struct enic_fm_action **ah_o) +{ + struct enic_fm_action *ah; + struct fm_action *fma; + uint64_t args[2]; + int ret = 0; + + ret = rte_hash_lookup_data(fm->action_hash, action_in, + (void **)&ah); + if (ret < 0 && ret != -ENOENT) + return rte_flow_error_set(error, -ret, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, "enic: rte_hash_lookup(aciton)"); + + if (ret == -ENOENT) { + /* Allocate a new action on the NIC. */ + fma = &fm->cmd.va->fm_action; + memcpy(fma, action_in, sizeof(*fma)); + + ah = calloc(1, sizeof(*ah)); + memcpy(&ah->key, action_in, sizeof(struct fm_action)); + if (ah == NULL) + return rte_flow_error_set(error, ENOMEM, + RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "enic: calloc(fm-action)"); + args[0] = FM_ACTION_ALLOC; + args[1] = fm->cmd.pa; + ret = flowman_cmd(fm, args, 2); + if (ret != 0) { + rte_flow_error_set(error, -ret, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, "enic: devcmd(action-alloc)"); + goto error_with_ah; + } + ah->handle = args[0]; + ret = rte_hash_add_key_data(fm->action_hash, + (const void *)action_in, + (void *)ah); + if (ret != 0) { + rte_flow_error_set(error, -ret, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, + "enic: rte_hash_add_key_data(actn)"); + goto error_with_action_handle; + } + ENICPMD_LOG(DEBUG, "action allocated: handle=0x%" PRIx64, + ah->handle); + } + + /* Action handle struct is valid, increment reference count. */ + ah->ref++; + *ah_o = ah; + return 0; +error_with_action_handle: + args[0] = FM_ACTION_FREE; + args[1] = ah->handle; + ret = flowman_cmd(fm, args, 2); + if (ret != 0) + rte_flow_error_set(error, -ret, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, "enic: devcmd(action-free)"); +error_with_ah: + free(ah); + return ret; +} + /* Push match-action to the NIC. */ static int __enic_fm_flow_add_entry(struct enic_flowman *fm, @@ -2024,29 +2257,18 @@ __enic_fm_flow_add_entry(struct enic_flowman *fm, struct rte_flow_error *error) { struct enic_fm_counter *ctr; - struct fm_action *fma; - uint64_t action_h; + struct enic_fm_action *ah = NULL; uint64_t entry_h; - uint64_t args[3]; int ret; ENICPMD_FUNC_TRACE(); - /* Allocate action. */ - fma = &fm->cmd.va->fm_action; - memcpy(fma, action_in, sizeof(*fma)); - args[0] = FM_ACTION_ALLOC; - args[1] = fm->cmd.pa; - ret = flowman_cmd(fm, args, 2); - if (ret != 0) { - ENICPMD_LOG(ERR, "allocating TCAM table action rc=%d", ret); - rte_flow_error_set(error, ret, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - NULL, "enic: devcmd(action-alloc)"); + + /* Get or create an aciton handle. */ + ret = enic_action_handle_get(fm, action_in, error, &ah); + if (ret) return ret; - } - action_h = args[0]; - fm_flow->action_handle = action_h; - match_in->ftm_action = action_h; - ENICPMD_LOG(DEBUG, "action allocated: handle=0x%" PRIx64, action_h); + match_in->ftm_action = ah->handle; + fm_flow->action = ah; /* Allocate counter if requested. */ if (match_in->ftm_flags & FMEF_COUNTER) { @@ -2108,6 +2330,7 @@ enic_fm_flow_add_entry(struct enic_flowman *fm, struct rte_flow *flow; ENICPMD_FUNC_TRACE(); + match_in->ftm_position = attrs->priority; enic_fm_dump_tcam_entry(match_in, action_in, attrs->ingress); flow = calloc(1, sizeof(*flow)); fm_flow = calloc(1, sizeof(*fm_flow)); @@ -2119,7 +2342,7 @@ enic_fm_flow_add_entry(struct enic_flowman *fm, return NULL; } flow->fm = fm_flow; - fm_flow->action_handle = FM_INVALID_HANDLE; + fm_flow->action = NULL; fm_flow->entry_handle = FM_INVALID_HANDLE; if (__enic_fm_flow_add_entry(fm, fm_flow, match_in, action_in, attrs->group, attrs->ingress, error)) { @@ -2174,11 +2397,71 @@ convert_jump_flows(struct enic_flowman *fm, struct enic_fm_fet *fet, } } +static int +add_hairpin_steer(struct enic_flowman *fm, struct rte_flow *flow, + struct rte_flow_error *error) +{ + struct fm_tcam_match_entry *fm_tcam_entry; + struct enic_fm_flow *fm_flow; + struct fm_action *fm_action; + struct fm_action_op fm_op; + int ret; + + ENICPMD_FUNC_TRACE(); + fm_flow = calloc(1, sizeof(*fm_flow)); + if (fm_flow == NULL) { + rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_HANDLE, + NULL, "enic: cannot allocate rte_flow"); + return -ENOMEM; + } + /* Original egress hairpin flow */ + fm_tcam_entry = &fm->tcam_entry; + fm_action = &fm->action; + /* Use the match pattern of the egress flow as is, without counters */ + fm_tcam_entry->ftm_flags &= ~FMEF_COUNTER; + /* The only action is steer to vnic */ + fm->action_op_count = 0; + memset(fm_action, 0, sizeof(*fm_action)); + memset(&fm_op, 0, sizeof(fm_op)); + /* Always to queue 0 for now */ + fm_op.fa_op = FMOP_RQ_STEER; + fm_op.rq_steer.rq_index = 0; + fm_op.rq_steer.vnic_handle = fm->hairpin_steer_vnic_h; + ret = enic_fm_append_action_op(fm, &fm_op, error); + if (ret) + goto error_with_flow; + ENICPMD_LOG(DEBUG, "add steer op"); + /* Add required END */ + memset(&fm_op, 0, sizeof(fm_op)); + fm_op.fa_op = FMOP_END; + ret = enic_fm_append_action_op(fm, &fm_op, error); + if (ret) + goto error_with_flow; + /* Add the ingress flow */ + fm_flow->action = NULL; + fm_flow->entry_handle = FM_INVALID_HANDLE; + ret = __enic_fm_flow_add_entry(fm, fm_flow, fm_tcam_entry, fm_action, + FM_TCAM_RTE_GROUP, 1 /* ingress */, error); + if (ret) { + ENICPMD_LOG(ERR, "cannot add hairpin-steer flow"); + goto error_with_flow; + } + /* The new flow is now the egress flow's paired flow */ + flow->fm->hairpin_steer_flow = fm_flow; + return 0; + +error_with_flow: + free(fm_flow); + return ret; +} + static void enic_fm_open_scratch(struct enic_flowman *fm) { fm->action_op_count = 0; fm->fet = NULL; + fm->need_hairpin_steer = 0; + fm->hairpin_steer_vnic_h = 0; memset(&fm->tcam_entry, 0, sizeof(fm->tcam_entry)); memset(&fm->action, 0, sizeof(fm->action)); } @@ -2326,6 +2609,15 @@ enic_fm_flow_create(struct rte_eth_dev *dev, flow = enic_fm_flow_add_entry(fm, fm_tcam_entry, fm_action, attrs, error); if (flow) { + /* Add ingress rule that pairs with hairpin rule */ + if (fm->need_hairpin_steer) { + ret = add_hairpin_steer(fm, flow, error); + if (ret) { + enic_fm_flow_free(fm, flow); + flow = NULL; + goto error_with_scratch; + } + } LIST_INSERT_HEAD(&enic->flows, flow, next); fet = flow->fm->fet; if (fet && fet->default_key) { @@ -2400,7 +2692,7 @@ enic_fm_flow_flush(struct rte_eth_dev *dev, */ if (fm->ig_tcam_hndl == FM_INVALID_HANDLE) { fm_flow->entry_handle = FM_INVALID_HANDLE; - fm_flow->action_handle = FM_INVALID_HANDLE; + fm_flow->action = NULL; fm_flow->fet = NULL; } enic_fm_flow_free(fm, flow); @@ -2456,6 +2748,31 @@ enic_fm_tcam_tbl_alloc(struct enic_flowman *fm, uint32_t direction, return 0; } +static int +enic_fm_init_actions(struct enic_flowman *fm) +{ + struct rte_hash *a_hash; + char name[RTE_HASH_NAMESIZE]; + struct rte_hash_parameters params = { + .entries = FM_MAX_ACTION_TABLE_SIZE, + .key_len = sizeof(struct fm_action), + .hash_func = rte_jhash, + .hash_func_init_val = 0, + .socket_id = rte_socket_id(), + }; + + ENICPMD_FUNC_TRACE(); + snprintf((char *)name, sizeof(name), "fm-ah-%s", + fm->owner_enic->bdf_name); + params.name = name; + + a_hash = rte_hash_create(¶ms); + if (a_hash == NULL) + return -rte_errno; + fm->action_hash = a_hash; + return 0; +} + static int enic_fm_init_counters(struct enic_flowman *fm) { @@ -2569,6 +2886,12 @@ enic_fm_init(struct enic *enic) ENICPMD_LOG(ERR, "cannot alloc counters"); goto error_tables; } + /* set up action handle hash */ + rc = enic_fm_init_actions(fm); + if (rc) { + ENICPMD_LOG(ERR, "cannot create action hash, error:%d", rc); + goto error_counters; + } /* * One default exact match table for each direction. We hold onto * it until close. @@ -2576,7 +2899,7 @@ enic_fm_init(struct enic *enic) rc = enic_fet_alloc(fm, 1, NULL, 128, &fm->default_ig_fet); if (rc) { ENICPMD_LOG(ERR, "cannot alloc default IG exact match table"); - goto error_counters; + goto error_actions; } fm->default_ig_fet->ref = 1; rc = enic_fet_alloc(fm, 0, NULL, 128, &fm->default_eg_fet); @@ -2591,6 +2914,8 @@ enic_fm_init(struct enic *enic) error_ig_fet: enic_fet_free(fm, fm->default_ig_fet); +error_actions: + rte_hash_free(fm->action_hash); error_counters: enic_fm_free_all_counters(fm); error_tables: @@ -2617,6 +2942,7 @@ enic_fm_destroy(struct enic *enic) if (enic->fm == NULL) return; fm = enic->fm; + enic_fm_flow_flush(enic->rte_dev, NULL); enic_fet_free(fm, fm->default_eg_fet); enic_fet_free(fm, fm->default_ig_fet); /* Free all exact match tables still open */ @@ -2626,6 +2952,7 @@ enic_fm_destroy(struct enic *enic) } enic_fm_free_tcam_tables(fm); enic_fm_free_all_counters(fm); + rte_hash_free(fm->action_hash); enic_free_consistent(enic, sizeof(union enic_flowman_cmd_mem), fm->cmd.va, fm->cmd.pa); fm->cmd.va = NULL;