X-Git-Url: http://git.droids-corp.org/?a=blobdiff_plain;f=drivers%2Fnet%2Fenic%2Fenic_fm_flow.c;h=cd364ee16b38164c7731f5ae14fac418fa337d0b;hb=0604b1f2208f54ae76030e437db40f9da558497b;hp=28ed51857f2e6248cfbc8112f661d27654472f02;hpb=8ca08b702636d4292e12bb593c97e13a4b09a02d;p=dpdk.git diff --git a/drivers/net/enic/enic_fm_flow.c b/drivers/net/enic/enic_fm_flow.c index 28ed51857f..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,11 +95,17 @@ 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) */ @@ -155,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; /* @@ -355,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; } @@ -392,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; /* @@ -401,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); @@ -461,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; } @@ -918,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, ... */ @@ -933,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; @@ -973,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, @@ -981,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; @@ -1002,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 */ @@ -1079,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); @@ -1260,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, @@ -1273,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; @@ -1446,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); @@ -1712,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)); @@ -1761,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, @@ -1898,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 @@ -1986,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) { @@ -2098,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, @@ -2109,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) { @@ -2193,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)); @@ -2204,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)) { @@ -2300,7 +2438,7 @@ add_hairpin_steer(struct enic_flowman *fm, struct rte_flow *flow, if (ret) goto error_with_flow; /* Add the ingress flow */ - fm_flow->action_handle = FM_INVALID_HANDLE; + 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); @@ -2554,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); @@ -2610,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) { @@ -2723,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. @@ -2730,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); @@ -2745,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: @@ -2771,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 */ @@ -2780,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;