From: Yashaswini Kanaujia <[email protected]> added support for asynchronous rte_flow APIs for cn10k device
Signed-off-by: Yashaswini Kanaujia <[email protected]> --- drivers/common/cnxk/meson.build | 1 + drivers/common/cnxk/roc_npc.h | 97 ++ drivers/common/cnxk/roc_npc_flow_template.c | 1326 ++++++++++++++++ drivers/common/cnxk/roc_npc_mcam.c | 45 +- drivers/common/cnxk/roc_npc_priv.h | 98 ++ drivers/common/cnxk/roc_npc_utils.c | 4 +- .../common/cnxk/roc_platform_base_symbols.c | 16 + drivers/net/cnxk/cn10k_ethdev.c | 23 + drivers/net/cnxk/cn10k_flow.c | 43 + drivers/net/cnxk/cn10k_flow.h | 2 + drivers/net/cnxk/cn20k_ethdev.c | 24 +- drivers/net/cnxk/cn20k_flow.c | 43 + drivers/net/cnxk/cn20k_flow.h | 2 + drivers/net/cnxk/cnxk_ethdev.h | 44 + drivers/net/cnxk/cnxk_flow.c | 1338 ++++++++++++++++- drivers/net/cnxk/cnxk_flow.h | 77 + 16 files changed, 3161 insertions(+), 22 deletions(-) create mode 100644 drivers/common/cnxk/roc_npc_flow_template.c diff --git a/drivers/common/cnxk/meson.build b/drivers/common/cnxk/meson.build index 3303ad9354..68374ee135 100644 --- a/drivers/common/cnxk/meson.build +++ b/drivers/common/cnxk/meson.build @@ -59,6 +59,7 @@ sources = files( 'roc_npa_type.c', 'roc_npc.c', 'roc_npc_aging.c', + 'roc_npc_flow_template.c', 'roc_npc_mcam.c', 'roc_npc_mcam_dump.c', 'roc_npc_parse.c', diff --git a/drivers/common/cnxk/roc_npc.h b/drivers/common/cnxk/roc_npc.h index a7254f35ca..21fa493c74 100644 --- a/drivers/common/cnxk/roc_npc.h +++ b/drivers/common/cnxk/roc_npc.h @@ -473,6 +473,9 @@ int __roc_api roc_npc_mcam_alloc_entries(struct roc_npc *roc_npc, int ref_entry, int __roc_api roc_npc_mcam_ena_dis_entry(struct roc_npc *roc_npc, struct roc_npc_flow *mcam, bool enable); int __roc_api roc_npc_mcam_write_entry(struct roc_npc *roc_npc, struct roc_npc_flow *mcam); + +void __roc_api roc_npc_mcam_write_rx_finalize(struct roc_npc *roc_npc, struct roc_npc_flow *flow); + void __roc_api roc_npc_defrag_mcam_banks(struct roc_npc *roc_npc); uint8_t __roc_api roc_npc_get_key_type(struct roc_npc *roc_npc, struct roc_npc_flow *flow); uint8_t __roc_api roc_npc_kex_key_type_config_get(struct roc_npc *roc_npc); @@ -508,4 +511,98 @@ void __roc_api roc_npc_sdp_channel_get(struct roc_npc *roc_npc, uint16_t *chan_b int __roc_api roc_npc_mcam_get_stats(struct roc_npc *roc_npc, struct roc_npc_flow *flow, uint64_t *count); int __roc_api roc_npc_skip_size_pkind_get(struct roc_npc *roc_npc); + +/* + * Queue-based (async) template flow API. + */ +enum roc_npc_template_table_insertion_type { + /* HW MCAM entry allocated per rule at create time (match based). */ + ROC_NPC_TEMPLATE_INSERTION_PATTERN, + /* MCAM range reserved up front; rules placed at an app-chosen index. */ + ROC_NPC_TEMPLATE_INSERTION_INDEX_WITH_PATTERN, +}; + +struct roc_npc_flow_queue_attr { + uint32_t size; /**< Number of in-flight async ops the queue can hold. */ +}; + +struct roc_npc_pattern_template_attr { + uint32_t relaxed_matching : 1; + uint32_t ingress : 1; + uint32_t egress : 1; + uint32_t transfer : 1; + uint32_t reserved : 28; +}; + +struct roc_npc_actions_template_attr { + uint32_t ingress : 1; + uint32_t egress : 1; + uint32_t transfer : 1; + uint32_t reserved : 29; +}; + +struct roc_npc_template_table_attr { + struct roc_npc_attr flow_attr; /**< Priority / ingress / egress. */ + uint32_t transfer : 1; /**< roc_npc_attr has no transfer bit. */ + uint32_t reserved : 31; + enum roc_npc_template_table_insertion_type insertion_type; + uint32_t nb_flows; /**< Max rules the table can hold. */ + void *cookie; /**< Opaque consumer context, retrievable per rule. */ +}; + +/** Result of one completed async operation, returned by roc_npc_flow_pull. */ +struct roc_npc_flow_op_result { + void *user_data; /**< user_data passed at async_create/destroy time. */ + int rc; /**< 0 on success, negative errno on failure. */ +}; + +/* Opaque template/table/flow handles owned by the roc layer. */ +struct roc_npc_pattern_template; +struct roc_npc_actions_template; +struct roc_npc_template_table; +struct roc_npc_template_flow; + +int __roc_api roc_npc_flow_configure(struct roc_npc *roc_npc, uint16_t nb_queues, + const struct roc_npc_flow_queue_attr *queue_attr[]); + +struct roc_npc_pattern_template *__roc_api roc_npc_pattern_template_create( + struct roc_npc *roc_npc, const struct roc_npc_pattern_template_attr *attr, + const struct roc_npc_item_info pattern[], int *errcode); +int __roc_api roc_npc_pattern_template_destroy(struct roc_npc *roc_npc, + struct roc_npc_pattern_template *tmpl); + +struct roc_npc_actions_template *__roc_api roc_npc_actions_template_create( + struct roc_npc *roc_npc, const struct roc_npc_actions_template_attr *attr, + const struct roc_npc_action actions[], const struct roc_npc_action masks[], int *errcode); +int __roc_api roc_npc_actions_template_destroy(struct roc_npc *roc_npc, + struct roc_npc_actions_template *tmpl); + +struct roc_npc_template_table *__roc_api roc_npc_template_table_create( + struct roc_npc *roc_npc, const struct roc_npc_template_table_attr *attr, + struct roc_npc_pattern_template *pattern_templates[], uint8_t nb_pattern_templates, + struct roc_npc_actions_template *actions_templates[], uint8_t nb_actions_templates, + int *errcode); +int __roc_api roc_npc_template_table_destroy(struct roc_npc *roc_npc, + struct roc_npc_template_table *table); + +struct roc_npc_template_flow *__roc_api roc_npc_async_flow_create( + struct roc_npc *roc_npc, uint32_t queue_id, struct roc_npc_template_table *table, + const struct roc_npc_item_info pattern[], const struct roc_npc_action actions[], + uint16_t dst_pf_func, uint64_t npc_default_action, void *user_data, int *errcode); +int __roc_api roc_npc_async_flow_destroy(struct roc_npc *roc_npc, uint32_t queue_id, + struct roc_npc_template_flow *flow, void *user_data); +struct roc_npc_template_flow *__roc_api roc_npc_async_flow_create_by_index_with_pattern( + struct roc_npc *roc_npc, uint32_t queue_id, struct roc_npc_template_table *table, + uint32_t rule_index, const struct roc_npc_item_info pattern[], + const struct roc_npc_action actions[], void *user_data, int *errcode); +int __roc_api roc_npc_async_flow_actions_update(struct roc_npc *roc_npc, uint32_t queue_id, + struct roc_npc_template_flow *flow, + const struct roc_npc_action actions[], + void *user_data); +int __roc_api roc_npc_flow_push(struct roc_npc *roc_npc, uint32_t queue_id); +int __roc_api roc_npc_flow_pull(struct roc_npc *roc_npc, uint32_t queue_id, + struct roc_npc_flow_op_result res[], uint16_t n_res); +bool __roc_api roc_npc_async_flow_resolve(struct roc_npc *roc_npc, void *handle, + struct roc_npc_flow **flow); +void *__roc_api roc_npc_async_flow_table_cookie(struct roc_npc_template_flow *flow); #endif /* _ROC_NPC_H_ */ diff --git a/drivers/common/cnxk/roc_npc_flow_template.c b/drivers/common/cnxk/roc_npc_flow_template.c new file mode 100644 index 0000000000..5c10cfe39a --- /dev/null +++ b/drivers/common/cnxk/roc_npc_flow_template.c @@ -0,0 +1,1326 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright(C) 2024 Marvell. + */ + +#include "roc_api.h" +#include "roc_priv.h" + +/* Size of an action's conf for the supported set; -1 = nested/unsupported. */ +static int +roc_npc_action_conf_size(enum roc_npc_action_type type) +{ + switch (type) { + case ROC_NPC_ACTION_TYPE_END: + case ROC_NPC_ACTION_TYPE_VOID: + case ROC_NPC_ACTION_TYPE_FLAG: + case ROC_NPC_ACTION_TYPE_DROP: + case ROC_NPC_ACTION_TYPE_COUNT: + case ROC_NPC_ACTION_TYPE_PF: + case ROC_NPC_ACTION_TYPE_VLAN_STRIP: + return 0; /* no conf */ + case ROC_NPC_ACTION_TYPE_MARK: + return sizeof(struct roc_npc_action_mark); + case ROC_NPC_ACTION_TYPE_VF: + return sizeof(struct roc_npc_action_vf); + case ROC_NPC_ACTION_TYPE_PORT_ID: + return sizeof(struct roc_npc_action_port_id); + case ROC_NPC_ACTION_TYPE_QUEUE: + return sizeof(struct roc_npc_action_queue); + case ROC_NPC_ACTION_TYPE_VLAN_INSERT: + return sizeof(struct roc_npc_action_of_set_vlan_vid); + case ROC_NPC_ACTION_TYPE_VLAN_ETHTYPE_INSERT: + return sizeof(struct roc_npc_action_of_push_vlan); + case ROC_NPC_ACTION_TYPE_VLAN_PCP_INSERT: + return sizeof(struct roc_npc_action_of_set_vlan_pcp); + case ROC_NPC_ACTION_TYPE_AGE: + return sizeof(struct roc_npc_action_age); + default: + return -1; /* RSS handled separately; others unsupported */ + } +} + +/* Deep-copy an item spec/last/mask payload; *dst = NULL when nothing to copy. */ +static int +roc_npc_dup_item_field(const void *src, uint32_t sz, const void **dst) +{ + void *copy; + + *dst = NULL; + if (src == NULL || sz == 0) + return 0; + copy = plt_zmalloc(sz, 0); + if (copy == NULL) + return -ENOMEM; + memcpy(copy, src, sz); + *dst = copy; + return 0; +} + +/* Deep-copy an action conf; RSS packs queue[]/key[] into one blob. */ +static void * +roc_npc_dup_action_conf(const struct roc_npc_action *act) +{ + if (act->conf == NULL) + return NULL; + + if (act->type == ROC_NPC_ACTION_TYPE_RSS) { + const struct roc_npc_action_rss *rss = act->conf; + size_t rss_queue_bytes = (size_t)rss->queue_num * sizeof(uint16_t); + size_t rss_key_bytes = rss->key_len; + size_t rss_blob_bytes = sizeof(*rss); + struct roc_npc_action_rss *rss_copy; + uint8_t *rss_blob; + + if ((rss_queue_bytes && rss->queue == NULL) || (rss_key_bytes && rss->key == NULL)) + return NULL; + + rss_blob = plt_zmalloc(rss_blob_bytes + rss_queue_bytes + rss_key_bytes, 0); + if (rss_blob == NULL) + return NULL; + rss_copy = (struct roc_npc_action_rss *)rss_blob; + *rss_copy = *rss; + if (rss_queue_bytes) { + memcpy(rss_blob + rss_blob_bytes, rss->queue, rss_queue_bytes); + rss_copy->queue = (const uint16_t *)(rss_blob + rss_blob_bytes); + } else { + rss_copy->queue = NULL; + } + if (rss_key_bytes) { + memcpy(rss_blob + rss_blob_bytes + rss_queue_bytes, rss->key, + rss_key_bytes); + rss_copy->key = + (const uint8_t *)(rss_blob + rss_blob_bytes + rss_queue_bytes); + } else { + rss_copy->key = NULL; + } + return rss_copy; + } + + { + int conf_size = roc_npc_action_conf_size(act->type); + void *copy; + + if (conf_size <= 0) + return NULL; + copy = plt_zmalloc(conf_size, 0); + if (copy != NULL) + memcpy(copy, act->conf, conf_size); + return copy; + } +} + +/* Free the deep-copied items/actions arrays retained for a rule. */ +static void +roc_npc_template_flow_free_rule(struct roc_npc_template_flow *flow) +{ + uint16_t k; + + if (flow->items != NULL) { + for (k = 0; k < flow->nb_items; k++) { + plt_free((void *)(uintptr_t)flow->items[k].spec); + plt_free((void *)(uintptr_t)flow->items[k].last); + plt_free((void *)(uintptr_t)flow->items[k].mask); + } + + plt_free(flow->items); + flow->items = NULL; + } + if (flow->actions != NULL) { + for (k = 0; k < flow->nb_actions; k++) + plt_free((void *)(uintptr_t)flow->actions[k].conf); + + plt_free(flow->actions); + flow->actions = NULL; + } +} + +int +roc_npc_flow_configure(struct roc_npc *roc_npc, uint16_t nb_queues, + const struct roc_npc_flow_queue_attr *queue_attr[]) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + struct roc_npc_flow_queue *queues; + int rc = -ENOMEM; + uint16_t i; + + if (nb_queues == 0) + return 0; + + if (queue_attr == NULL) + return -EINVAL; + + if (npc->flow_queues != NULL) { + /* Don't tear down live queues while async ops are in flight. */ + for (i = 0; i < npc->nb_flow_queues; i++) { + if (npc->flow_queues[i].head != npc->flow_queues[i].tail) + return -EBUSY; + } + } + + if (npc->flow_queues != NULL) { + for (i = 0; i < npc->nb_flow_queues; i++) + plt_free(npc->flow_queues[i].ops); + + plt_free(npc->flow_queues); + npc->flow_queues = NULL; + npc->nb_flow_queues = 0; + } + + queues = plt_zmalloc(nb_queues * sizeof(struct roc_npc_flow_queue), 0); + if (queues == NULL) + return -ENOMEM; + + for (i = 0; i < nb_queues; i++) { + if (queue_attr[i] == NULL || queue_attr[i]->size == 0) { + rc = -EINVAL; + goto err_free; + } + queues[i].size = plt_align32pow2(queue_attr[i]->size); + queues[i].ops = plt_zmalloc(queues[i].size * sizeof(struct roc_npc_async_op), 0); + if (queues[i].ops == NULL) { + rc = -ENOMEM; + goto err_free; + } + queues[i].head = 0; + queues[i].pushed = 0; + queues[i].tail = 0; + } + + npc->flow_queues = queues; + npc->nb_flow_queues = nb_queues; + return 0; + +err_free: + for (i = 0; i < nb_queues; i++) + plt_free(queues[i].ops); + + plt_free(queues); + return rc; +} + +struct roc_npc_pattern_template * +roc_npc_pattern_template_create(struct roc_npc *roc_npc, + const struct roc_npc_pattern_template_attr *attr, + const struct roc_npc_item_info pattern[], int *errcode) +{ + struct roc_npc_pattern_template *tmpl = NULL; + int i, nb_items = 0, rc = -ENOMEM; + uint16_t j; + + PLT_SET_USED(roc_npc); + + if (attr == NULL || pattern == NULL) { + *errcode = -EINVAL; + return NULL; + } + + while (pattern[nb_items].type != ROC_NPC_ITEM_TYPE_END) + nb_items++; + + /* Bound template size to the fixed-size rule arrays. */ + if (nb_items > ROC_NPC_ITEM_TYPE_END) { + *errcode = -ENOTSUP; + return NULL; + } + + tmpl = plt_zmalloc(sizeof(*tmpl), 0); + if (tmpl == NULL) + goto err; + + tmpl->attr = *attr; + tmpl->nb_items = nb_items; + + tmpl->pattern = plt_zmalloc((nb_items + 1) * sizeof(struct roc_npc_item_info), 0); + if (tmpl->pattern == NULL) + goto err; + + /* Reserve up to 3 payload copies per item. */ + tmpl->copies = plt_zmalloc(3 * (nb_items + 1) * sizeof(void *), 0); + if (tmpl->copies == NULL) + goto err; + + for (i = 0; i < nb_items; i++) { + enum roc_npc_item_type t = pattern[i].type; + uint32_t sz = pattern[i].size; + + /* RAW is not flat-copyable. */ + if (t == ROC_NPC_ITEM_TYPE_RAW) { + rc = -ENOTSUP; + goto err; + } + + tmpl->pattern[i].type = t; + tmpl->pattern[i].size = sz; + /* Track each copy for the error path. */ + if (roc_npc_dup_item_field(pattern[i].spec, sz, &tmpl->pattern[i].spec)) + goto err; + if (tmpl->pattern[i].spec != NULL) + tmpl->copies[tmpl->nb_copies++] = (void *)(uintptr_t)tmpl->pattern[i].spec; + if (roc_npc_dup_item_field(pattern[i].last, sz, &tmpl->pattern[i].last)) + goto err; + if (tmpl->pattern[i].last != NULL) + tmpl->copies[tmpl->nb_copies++] = (void *)(uintptr_t)tmpl->pattern[i].last; + if (roc_npc_dup_item_field(pattern[i].mask, sz, &tmpl->pattern[i].mask)) + goto err; + if (tmpl->pattern[i].mask != NULL) + tmpl->copies[tmpl->nb_copies++] = (void *)(uintptr_t)tmpl->pattern[i].mask; + } + tmpl->pattern[nb_items].type = ROC_NPC_ITEM_TYPE_END; + tmpl->refcnt = 0; + + return tmpl; + +err: + if (tmpl != NULL) { + for (j = 0; j < tmpl->nb_copies; j++) + plt_free(tmpl->copies[j]); + plt_free(tmpl->copies); + plt_free(tmpl->pattern); + plt_free(tmpl); + } + *errcode = rc; + return NULL; +} + +int +roc_npc_pattern_template_destroy(struct roc_npc *roc_npc, struct roc_npc_pattern_template *tmpl) +{ + uint16_t i; + + PLT_SET_USED(roc_npc); + + if (tmpl == NULL) + return 0; + + if (tmpl->refcnt != 0) + return -EBUSY; + + for (i = 0; i < tmpl->nb_copies; i++) + plt_free(tmpl->copies[i]); + + plt_free(tmpl->copies); + plt_free(tmpl->pattern); + plt_free(tmpl); + return 0; +} + +struct roc_npc_actions_template * +roc_npc_actions_template_create(struct roc_npc *roc_npc, + const struct roc_npc_actions_template_attr *attr, + const struct roc_npc_action actions[], + const struct roc_npc_action masks[], int *errcode) +{ + struct roc_npc_actions_template *tmpl = NULL; + int i, nb_actions = 0, rc = -ENOMEM; + uint16_t j; + + PLT_SET_USED(roc_npc); + + if (attr == NULL || actions == NULL) { + *errcode = -EINVAL; + return NULL; + } + + while (actions[nb_actions].type != ROC_NPC_ACTION_TYPE_END) + nb_actions++; + + /* Bound template size to the fixed-size rule arrays. */ + if (nb_actions > ROC_NPC_MAX_ACTION_COUNT - 1) { + *errcode = -ENOTSUP; + return NULL; + } + + tmpl = plt_zmalloc(sizeof(*tmpl), 0); + if (tmpl == NULL) + goto err; + + tmpl->attr = *attr; + tmpl->nb_actions = nb_actions; + + tmpl->actions = plt_zmalloc((nb_actions + 1) * sizeof(struct roc_npc_action), 0); + if (tmpl->actions == NULL) + goto err; + + tmpl->masks = plt_zmalloc((nb_actions + 1) * sizeof(struct roc_npc_action), 0); + if (tmpl->masks == NULL) + goto err; + + /* Reserve up to 2 conf copies per action. */ + tmpl->copies = plt_zmalloc(2 * (nb_actions + 1) * sizeof(void *), 0); + if (tmpl->copies == NULL) + goto err; + + for (i = 0; i < nb_actions; i++) { + enum roc_npc_action_type t = actions[i].type; + int sz = roc_npc_action_conf_size(t); + + if (sz < 0 && t != ROC_NPC_ACTION_TYPE_RSS) { + rc = -ENOTSUP; + goto err; + } + + tmpl->actions[i].type = t; + tmpl->actions[i].rss_repte_pf_func = actions[i].rss_repte_pf_func; + + /* A malformed RSS conf is invalid input, not an allocation failure. */ + if (t == ROC_NPC_ACTION_TYPE_RSS && actions[i].conf != NULL) { + const struct roc_npc_action_rss *rss = actions[i].conf; + + if ((rss->queue_num && rss->queue == NULL) || + (rss->key_len && rss->key == NULL)) { + rc = -EINVAL; + goto err; + } + } + + tmpl->actions[i].conf = roc_npc_dup_action_conf(&actions[i]); + if (tmpl->actions[i].conf == NULL && actions[i].conf != NULL && + (sz > 0 || t == ROC_NPC_ACTION_TYPE_RSS)) + goto err; + if (tmpl->actions[i].conf != NULL) + tmpl->copies[tmpl->nb_copies++] = (void *)(uintptr_t)tmpl->actions[i].conf; + + if (masks != NULL) { + if (masks[i].type != t) { + rc = -EINVAL; + goto err; + } + tmpl->masks[i].type = t; + /* RSS masks cannot carry a nested pointer. */ + if (t == ROC_NPC_ACTION_TYPE_RSS) { + if (masks[i].conf != NULL) { + rc = -ENOTSUP; + goto err; + } + tmpl->masks[i].conf = NULL; + } else { + tmpl->masks[i].conf = roc_npc_dup_action_conf(&masks[i]); + if (tmpl->masks[i].conf == NULL && masks[i].conf != NULL && sz > 0) + goto err; + if (tmpl->masks[i].conf != NULL) + tmpl->copies[tmpl->nb_copies++] = + (void *)(uintptr_t)tmpl->masks[i].conf; + } + } + } + + tmpl->actions[nb_actions].type = ROC_NPC_ACTION_TYPE_END; + tmpl->masks[nb_actions].type = ROC_NPC_ACTION_TYPE_END; + tmpl->refcnt = 0; + + return tmpl; + +err: + if (tmpl != NULL) { + for (j = 0; j < tmpl->nb_copies; j++) + plt_free(tmpl->copies[j]); + plt_free(tmpl->copies); + plt_free(tmpl->masks); + plt_free(tmpl->actions); + plt_free(tmpl); + } + *errcode = rc; + return NULL; +} + +int +roc_npc_actions_template_destroy(struct roc_npc *roc_npc, struct roc_npc_actions_template *tmpl) +{ + uint16_t i; + + PLT_SET_USED(roc_npc); + + if (tmpl == NULL) + return 0; + + if (tmpl->refcnt != 0) + return -EBUSY; + + for (i = 0; i < tmpl->nb_copies; i++) + plt_free(tmpl->copies[i]); + + plt_free(tmpl->copies); + plt_free(tmpl->masks); + plt_free(tmpl->actions); + plt_free(tmpl); + return 0; +} + +struct roc_npc_template_table * +roc_npc_template_table_create(struct roc_npc *roc_npc, + const struct roc_npc_template_table_attr *attr, + struct roc_npc_pattern_template *pattern_templates[], + uint8_t nb_pattern_templates, + struct roc_npc_actions_template *actions_templates[], + uint8_t nb_actions_templates, int *errcode) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + struct roc_npc_template_table *table; + uint32_t nb_flows; + uint8_t i; + + if (attr == NULL) { + *errcode = -EINVAL; + return NULL; + } + + nb_flows = attr->nb_flows; + + table = plt_zmalloc(sizeof(*table), 0); + if (table == NULL) + goto enomem; + + table->insertion_type = attr->insertion_type; + table->nb_flows = nb_flows; + table->nb_live = 0; + table->cookie = attr->cookie; + plt_spinlock_init(&table->lock); + + table->flow_attr = attr->flow_attr; + table->transfer = attr->transfer; + + if (attr->insertion_type == ROC_NPC_TEMPLATE_INSERTION_INDEX_WITH_PATTERN) { + int resp_count = 0; + uint32_t j; + int rc; + + table->mcam_ids = plt_zmalloc(nb_flows * sizeof(int), 0); + if (table->mcam_ids == NULL) + goto free_index; + for (j = 0; j < nb_flows; j++) + table->mcam_ids[j] = -1; + + table->flow_pool = plt_zmalloc(nb_flows * sizeof(struct roc_npc_template_flow), 0); + if (table->flow_pool == NULL) + goto free_index; + + rc = roc_npc_mcam_alloc_entries(roc_npc, 0, table->mcam_ids, nb_flows, + NPC_MCAM_ANY_PRIO, &resp_count, false); + if (rc != 0 || resp_count < (int)nb_flows) { + for (j = 0; j < (uint32_t)resp_count; j++) { + if (table->mcam_ids[j] != -1) + roc_npc_mcam_free_entry(roc_npc, table->mcam_ids[j]); + } + + *errcode = -ENOSPC; + goto free_index; + } + + for (j = 0; j < nb_flows; j++) { + table->flow_pool[j].table = table; + table->flow_pool[j].nb_items = 0; + table->flow_pool[j].nb_actions = 0; + } + } + /* PATTERN: nothing reserved; each push allocates at the right priority. */ + + if (nb_pattern_templates != 0) { + table->pattern_templates = + plt_zmalloc(nb_pattern_templates * sizeof(*table->pattern_templates), 0); + if (table->pattern_templates == NULL) + goto free_table; + } + if (nb_actions_templates != 0) { + table->actions_templates = + plt_zmalloc(nb_actions_templates * sizeof(*table->actions_templates), 0); + if (table->actions_templates == NULL) + goto free_table; + } + + for (i = 0; i < nb_pattern_templates; i++) { + table->pattern_templates[i] = pattern_templates[i]; + table->pattern_templates[i]->refcnt++; + } + table->nb_pattern_templates = nb_pattern_templates; + for (i = 0; i < nb_actions_templates; i++) { + table->actions_templates[i] = actions_templates[i]; + table->actions_templates[i]->refcnt++; + } + table->nb_actions_templates = nb_actions_templates; + + /* Track the table on the port so a flush can reconcile it. */ + TAILQ_INIT(&table->live_flows); + table->next = npc->flow_tables; + npc->flow_tables = table; + + return table; + +free_index: + plt_free(table->mcam_ids); + plt_free(table->flow_pool); + table->mcam_ids = NULL; + table->flow_pool = NULL; + plt_free(table->pattern_templates); + plt_free(table->actions_templates); + plt_free(table); + if (*errcode == 0) + *errcode = -ENOMEM; + return NULL; + +free_table: + if (attr->insertion_type == ROC_NPC_TEMPLATE_INSERTION_INDEX_WITH_PATTERN) { + uint32_t j; + + if (table->mcam_ids != NULL) { + for (j = 0; j < nb_flows; j++) { + if (table->mcam_ids[j] != -1) + roc_npc_mcam_free_entry(roc_npc, table->mcam_ids[j]); + } + + plt_free(table->mcam_ids); + } + plt_free(table->flow_pool); + } + plt_free(table->pattern_templates); + plt_free(table->actions_templates); + plt_free(table); + +enomem: + *errcode = -ENOMEM; + return NULL; +} + +int +roc_npc_template_table_destroy(struct roc_npc *roc_npc, struct roc_npc_template_table *table) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + struct roc_npc_template_table **pp; + uint8_t i; + + if (table == NULL) + return 0; + + if (table->insertion_type == ROC_NPC_TEMPLATE_INSERTION_PATTERN) { + if (table->nb_live != 0) + return -EBUSY; + } else { + uint32_t j; + + /* A live (enabled) index rule still owns its MCAM entry. */ + if (!table->hw_released && table->flow_pool != NULL) { + for (j = 0; j < table->nb_flows; j++) { + if (table->flow_pool[j].flow.enable != 0) + return -EBUSY; + } + } + + if (table->mcam_ids != NULL) { + /* Skip if a flush already freed the entries. */ + if (!table->hw_released) { + for (j = 0; j < table->nb_flows; j++) { + int mcam_id = table->mcam_ids[j]; + + if (mcam_id != -1) + roc_npc_mcam_free_entry(roc_npc, mcam_id); + } + } + plt_free(table->mcam_ids); + } + plt_free(table->flow_pool); + } + + for (i = 0; i < table->nb_pattern_templates; i++) + table->pattern_templates[i]->refcnt--; + + for (i = 0; i < table->nb_actions_templates; i++) + table->actions_templates[i]->refcnt--; + + /* Unlink from the port's table list. */ + pp = &npc->flow_tables; + while (*pp != NULL) { + if (*pp == table) { + *pp = table->next; + break; + } + pp = &(*pp)->next; + } + + plt_free(table->pattern_templates); + plt_free(table->actions_templates); + plt_free(table); + return 0; +} + +struct roc_npc_template_flow * +roc_npc_async_flow_create(struct roc_npc *roc_npc, uint32_t queue_id, + struct roc_npc_template_table *table, + const struct roc_npc_item_info pattern[], + const struct roc_npc_action actions[], uint16_t dst_pf_func, + uint64_t npc_default_action, void *user_data, int *errcode) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + struct roc_npc_template_flow *flow = NULL; + int nb_items = 0, nb_actions = 0; + struct roc_npc_flow_queue *q; + struct roc_npc_async_op *op; + int k; + + if (table == NULL || pattern == NULL || actions == NULL || npc->flow_queues == NULL || + queue_id >= npc->nb_flow_queues) { + *errcode = -EINVAL; + return NULL; + } + if (table->insertion_type != ROC_NPC_TEMPLATE_INSERTION_PATTERN) { + *errcode = -EINVAL; + return NULL; + } + + while (pattern[nb_items].type != ROC_NPC_ITEM_TYPE_END) + nb_items++; + while (actions[nb_actions].type != ROC_NPC_ACTION_TYPE_END) + nb_actions++; + if (nb_items > ROC_NPC_ITEM_TYPE_END || nb_actions > ROC_NPC_MAX_ACTION_COUNT - 1) { + *errcode = -ENOTSUP; + return NULL; + } + + q = &npc->flow_queues[queue_id]; + if (q->head - q->tail >= q->size) { + *errcode = -ENOSPC; + return NULL; + } + + flow = plt_zmalloc(sizeof(*flow), 0); + if (flow == NULL) { + *errcode = -ENOMEM; + return NULL; + } + flow->table = table; + flow->nb_items = nb_items; + flow->nb_actions = nb_actions; + flow->items = plt_zmalloc((nb_items + 1) * sizeof(struct roc_npc_item_info), 0); + if (flow->items == NULL) { + *errcode = -ENOMEM; + goto err; + } + + flow->actions = plt_zmalloc((nb_actions + 1) * sizeof(struct roc_npc_action), 0); + if (flow->actions == NULL) { + *errcode = -ENOMEM; + goto err; + } + + /* Deep-copy each item so push() owns a self-contained rule. */ + for (k = 0; k < nb_items; k++) { + uint32_t sz = pattern[k].size; + + flow->items[k].type = pattern[k].type; + flow->items[k].size = sz; + if (roc_npc_dup_item_field(pattern[k].spec, sz, &flow->items[k].spec) || + roc_npc_dup_item_field(pattern[k].last, sz, &flow->items[k].last) || + roc_npc_dup_item_field(pattern[k].mask, sz, &flow->items[k].mask)) { + *errcode = -ENOMEM; + goto err; + } + } + flow->items[nb_items].type = ROC_NPC_ITEM_TYPE_END; + + /* Deep-copy each action conf. */ + for (k = 0; k < nb_actions; k++) { + int sz = roc_npc_action_conf_size(actions[k].type); + + flow->actions[k].type = actions[k].type; + flow->actions[k].rss_repte_pf_func = actions[k].rss_repte_pf_func; + + /* A malformed RSS conf (non-zero length with a NULL nested + * pointer) is invalid input, not an allocation failure. + */ + if (actions[k].type == ROC_NPC_ACTION_TYPE_RSS && actions[k].conf != NULL) { + const struct roc_npc_action_rss *rss = actions[k].conf; + + if ((rss->queue_num && rss->queue == NULL) || + (rss->key_len && rss->key == NULL)) { + *errcode = -EINVAL; + goto err; + } + } + + flow->actions[k].conf = roc_npc_dup_action_conf(&actions[k]); + /* A NULL copy from a non-NULL conf now means the deep copy failed. */ + if (flow->actions[k].conf == NULL && actions[k].conf != NULL && + (sz > 0 || actions[k].type == ROC_NPC_ACTION_TYPE_RSS)) { + *errcode = -ENOMEM; + goto err; + } + } + flow->actions[nb_actions].type = ROC_NPC_ACTION_TYPE_END; + + /* Capture the rule attributes used by push() to program hardware. */ + flow->attr = table->flow_attr; + if (table->transfer) + flow->attr.ingress = 1; + flow->dst_pf_func = dst_pf_func; + flow->npc_default_action = npc_default_action; + /* Capture the caller's RSS flow key so batched rules keep their own. */ + flow->flowkey_cfg = roc_npc->flowkey_cfg_state; + + op = &q->ops[q->head & (q->size - 1)]; + op->flow = flow; + op->is_create = true; + op->is_update = false; + op->user_data = user_data; + op->done = false; + op->rc = 0; + q->head++; + + return flow; + +err: + roc_npc_template_flow_free_rule(flow); + plt_free(flow); + return NULL; +} + +int +roc_npc_async_flow_destroy(struct roc_npc *roc_npc, uint32_t queue_id, + struct roc_npc_template_flow *flow, void *user_data) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + struct roc_npc_flow_queue *q; + struct roc_npc_async_op *op; + + if (flow == NULL || npc->flow_queues == NULL || queue_id >= npc->nb_flow_queues) + return -EINVAL; + + q = &npc->flow_queues[queue_id]; + if (q->head - q->tail >= q->size) + return -ENOSPC; + + op = &q->ops[q->head & (q->size - 1)]; + op->flow = flow; + op->is_create = false; + op->is_update = false; + op->user_data = user_data; + op->done = false; + op->rc = 0; + q->head++; + + return 0; +} + +/* Create a rule at an app-chosen index in an INDEX_WITH_PATTERN table. */ +struct roc_npc_template_flow * +roc_npc_async_flow_create_by_index_with_pattern(struct roc_npc *roc_npc, uint32_t queue_id, + struct roc_npc_template_table *table, + uint32_t rule_index, + const struct roc_npc_item_info pattern[], + const struct roc_npc_action actions[], + void *user_data, int *errcode) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + struct roc_npc_template_flow *flow; + uint32_t mark_save, vtag_save; + struct roc_npc_flow_queue *q; + struct roc_npc_async_op *op; + struct roc_npc_attr attr; + int rc; + + if (table == NULL || pattern == NULL || actions == NULL || npc->flow_queues == NULL || + queue_id >= npc->nb_flow_queues) { + *errcode = -EINVAL; + return NULL; + } + if (table->insertion_type != ROC_NPC_TEMPLATE_INSERTION_INDEX_WITH_PATTERN) { + *errcode = -EINVAL; + return NULL; + } + if (rule_index >= table->nb_flows) { + *errcode = -EINVAL; + return NULL; + } + + /* Reject creates into a flushed table. */ + if (table->hw_released) { + *errcode = -EINVAL; + return NULL; + } + + q = &npc->flow_queues[queue_id]; + if (q->head - q->tail >= q->size) { + *errcode = -ENOSPC; + return NULL; + } + + /* App owns the index: use the reserved pool slot directly. */ + flow = &table->flow_pool[rule_index]; + flow->table = table; + flow->slot = rule_index; + memset(&flow->flow, 0, sizeof(flow->flow)); + + attr = table->flow_attr; + if (table->transfer) + attr.ingress = 1; + + /* Snapshot MARK/VLAN refcounts before parsing. */ + mark_save = npc->mark_actions; + vtag_save = npc->vtag_strip_actions; + + rc = roc_npc_flow_parse(roc_npc, &attr, pattern, actions, &flow->flow); + if (rc != 0) { + npc->mark_actions = mark_save; + npc->vtag_strip_actions = vtag_save; + *errcode = rc; + return NULL; + } + + roc_npc_mcam_write_rx_finalize(roc_npc, &flow->flow); + flow->flow.mcam_id = table->mcam_ids[rule_index]; + flow->flow.enable = 1; + + op = &q->ops[q->head & (q->size - 1)]; + op->flow = flow; + op->is_create = true; + op->is_update = false; + op->user_data = user_data; + op->done = false; + op->rc = 0; + q->head++; + + return flow; +} + +/* Update a rule's actions in place and swap them in at push. */ +int +roc_npc_async_flow_actions_update(struct roc_npc *roc_npc, uint32_t queue_id, + struct roc_npc_template_flow *flow, + const struct roc_npc_action actions[], void *user_data) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + struct roc_npc_item_info end_pattern[1]; + struct roc_npc_template_table *table; + struct roc_npc_flow *fptr, tmpflow; + uint32_t mark_save, vtag_save; + struct roc_npc_flow_queue *q; + struct roc_npc_async_op *op; + struct roc_npc_attr attr; + uint16_t old_match_id; + bool old_vtag; + int rc; + + if (flow == NULL || actions == NULL || npc->flow_queues == NULL || + queue_id >= npc->nb_flow_queues) + return -EINVAL; + + table = flow->table; + if (table == NULL) + return -EINVAL; + /* PATTERN rules have roc_flow only after create commit. */ + if (table->insertion_type == ROC_NPC_TEMPLATE_INSERTION_PATTERN && flow->roc_flow == NULL) + return -EINVAL; + + /* Reject a second update until the pending one is flushed. */ + if (flow->pending_update.pending) + return -EBUSY; + + q = &npc->flow_queues[queue_id]; + if (q->head - q->tail >= q->size) + return -ENOSPC; + + end_pattern[0].type = ROC_NPC_ITEM_TYPE_END; + + attr = table->flow_attr; + if (table->transfer) + attr.ingress = 1; + + memset(&tmpflow, 0, sizeof(tmpflow)); + + /* Resolve the live flow before parsing the new actions. */ + fptr = flow->roc_flow ? flow->roc_flow : &flow->flow; + old_match_id = (fptr->npc_action >> NPC_RX_ACT_MATCH_OFFSET) & NPC_RX_ACT_MATCH_MASK; + old_vtag = fptr->nix_intf == ROC_NPC_INTF_RX && fptr->vtag_action != 0; + mark_save = npc->mark_actions; + vtag_save = npc->vtag_strip_actions; + + rc = roc_npc_flow_parse(roc_npc, &attr, end_pattern, actions, &tmpflow); + if (rc != 0) { + /* Undo any refcount bumps a partial parse made on failure. */ + npc->mark_actions = mark_save; + npc->vtag_strip_actions = vtag_save; + return rc; + } + + /* Stash the parsed update so push can commit or roll it back. */ + flow->pending_update.npc_action = tmpflow.npc_action; + flow->pending_update.npc_action2 = tmpflow.npc_action2; + flow->pending_update.vtag_action = tmpflow.vtag_action; + flow->pending_update.recv_queue = tmpflow.recv_queue; + flow->pending_update.old_match_id = old_match_id != 0; + flow->pending_update.old_vtag = old_vtag; + flow->pending_update.new_match_id = + ((tmpflow.npc_action >> NPC_RX_ACT_MATCH_OFFSET) & NPC_RX_ACT_MATCH_MASK) != 0; + flow->pending_update.new_vtag = + tmpflow.nix_intf == ROC_NPC_INTF_RX && tmpflow.vtag_action != 0; + flow->pending_update.pending = true; + + op = &q->ops[q->head & (q->size - 1)]; + op->flow = flow; + op->is_create = false; + op->is_update = true; + op->user_data = user_data; + op->done = false; + op->rc = 0; + q->head++; + + return 0; +} + +/* Commit one retained create to hardware. */ +static int +roc_npc_flow_commit_create(struct roc_npc *roc_npc, struct roc_npc_template_flow *flow) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + uint32_t mark_save, vtag_save; + struct roc_npc_flow *rflow; + int errcode = 0; + + /* Restore this rule's RSS flow key before programming (see create). */ + roc_npc->flowkey_cfg_state = flow->flowkey_cfg; + + /* Restore MARK/VLAN refcounts if create fails. */ + mark_save = npc->mark_actions; + vtag_save = npc->vtag_strip_actions; + + rflow = roc_npc_flow_create(roc_npc, &flow->attr, flow->items, flow->actions, + flow->dst_pf_func, flow->npc_default_action, &errcode); + if (rflow == NULL) { + npc->mark_actions = mark_save; + npc->vtag_strip_actions = vtag_save; + return errcode ? errcode : -EINVAL; + } + + flow->roc_flow = rflow; + return 0; +} + +/* Apply a pending actions update and roll it back on failure. */ +static int +roc_npc_flow_apply_pattern_update(struct roc_npc *roc_npc, struct npc *npc, + struct roc_npc_template_flow *flow) +{ + struct roc_npc_flow *fptr = flow->roc_flow; + uint64_t save_action2, save_action, save_vtag; + uint32_t save_rq; + int rc; + + if (fptr == NULL) + return -EINVAL; + + save_action2 = fptr->npc_action2; + save_action = fptr->npc_action; + save_vtag = fptr->vtag_action; + save_rq = fptr->recv_queue; + + fptr->npc_action = flow->pending_update.npc_action; + fptr->npc_action2 = flow->pending_update.npc_action2; + fptr->vtag_action = flow->pending_update.vtag_action; + fptr->recv_queue = flow->pending_update.recv_queue; + fptr->enable = 1; + roc_npc_mcam_write_rx_finalize(roc_npc, fptr); + rc = roc_npc_mcam_write_entry(roc_npc, fptr); + if (rc == 0) { + /* Drop the old action set's refcount contribution. */ + if (flow->pending_update.old_match_id && npc->mark_actions > 0) + npc->mark_actions--; + if (flow->pending_update.old_vtag && npc->vtag_strip_actions > 0) + npc->vtag_strip_actions--; + } else { + /* Restore the live entry and undo the new contribution. */ + fptr->npc_action = save_action; + fptr->npc_action2 = save_action2; + fptr->vtag_action = save_vtag; + fptr->recv_queue = save_rq; + if (flow->pending_update.new_match_id && npc->mark_actions > 0) + npc->mark_actions--; + if (flow->pending_update.new_vtag && npc->vtag_strip_actions > 0) + npc->vtag_strip_actions--; + } + return rc; +} + +int +roc_npc_flow_push(struct roc_npc *roc_npc, uint32_t queue_id) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + struct roc_npc_flow_queue *q; + uint32_t i; + + if (npc->flow_queues == NULL || queue_id >= npc->nb_flow_queues) + return -EINVAL; + + q = &npc->flow_queues[queue_id]; + + for (i = q->pushed; i != q->head; i++) { + struct roc_npc_async_op *op = &q->ops[i & (q->size - 1)]; + struct roc_npc_template_flow *flow = op->flow; + struct roc_npc_template_table *table = flow->table; + int rc; + + if (table->insertion_type == ROC_NPC_TEMPLATE_INSERTION_PATTERN) { + if (op->is_update) { + rc = roc_npc_flow_apply_pattern_update(roc_npc, npc, flow); + /* Clear the pending update after push. */ + flow->pending_update.pending = false; + } else if (op->is_create) { + rc = roc_npc_flow_commit_create(roc_npc, flow); + /* Rule arrays are consumed on push. */ + roc_npc_template_flow_free_rule(flow); + if (rc == 0) { + plt_spinlock_lock(&table->lock); + table->nb_live++; + TAILQ_INSERT_TAIL(&table->live_flows, flow, link); + plt_spinlock_unlock(&table->lock); + } + /* Keep the handle valid on failure. */ + } else if (flow->roc_flow == NULL) { + /* Never committed (or a failed create): just free it. */ + rc = 0; + roc_npc_template_flow_free_rule(flow); + plt_free(flow); + } else { + uint16_t match_id; + uint8_t nix_intf; + bool had_vtag; + + /* Capture match_id and interface before destroy. */ + match_id = (flow->roc_flow->npc_action >> NPC_RX_ACT_MATCH_OFFSET) & + NPC_RX_ACT_MATCH_MASK; + nix_intf = flow->roc_flow->nix_intf; + had_vtag = flow->roc_flow->vtag_action != 0; + + rc = roc_npc_flow_destroy(roc_npc, flow->roc_flow); + if (rc == 0 && match_id) + roc_npc_mark_actions_sub_return(roc_npc, 1); + if (rc == 0 && nix_intf == ROC_NPC_INTF_RX && had_vtag) + roc_npc_vtag_actions_sub_return(roc_npc, 1); + if (rc == 0) { + plt_spinlock_lock(&table->lock); + table->nb_live--; + TAILQ_REMOVE(&table->live_flows, flow, link); + plt_spinlock_unlock(&table->lock); + flow->roc_flow = NULL; + roc_npc_template_flow_free_rule(flow); + plt_free(flow); + } + /* Keep the handle on failure so the caller can retry. */ + } + } else { + /* INDEX_WITH_PATTERN: entry fixed at the app-chosen index. */ + struct roc_npc_flow *fptr = &flow->flow; + bool destroy = !op->is_create && !op->is_update; + uint16_t match_id = (fptr->npc_action >> NPC_RX_ACT_MATCH_OFFSET) & + NPC_RX_ACT_MATCH_MASK; + uint64_t save_action2 = fptr->npc_action2; + uint64_t save_action = fptr->npc_action; + uint64_t save_vtag = fptr->vtag_action; + uint32_t save_rq = fptr->recv_queue; + + /* Swap in the stashed update before writing hardware. */ + if (op->is_update) { + fptr->npc_action = flow->pending_update.npc_action; + fptr->npc_action2 = flow->pending_update.npc_action2; + fptr->vtag_action = flow->pending_update.vtag_action; + fptr->recv_queue = flow->pending_update.recv_queue; + } + + /* Set enable before writing hardware. */ + fptr->enable = destroy ? 0 : 1; + if (op->is_update) + roc_npc_mcam_write_rx_finalize(roc_npc, fptr); + rc = roc_npc_mcam_write_entry(roc_npc, fptr); + if (rc != 0) { + /* Restore the previous enable state on failure. */ + fptr->enable = destroy ? 1 : 0; + /* Roll back the swapped-in action words too. */ + if (op->is_update) { + fptr->npc_action = save_action; + fptr->npc_action2 = save_action2; + fptr->vtag_action = save_vtag; + fptr->recv_queue = save_rq; + } + } + + /* Commit or roll back the update refcount change. */ + if (op->is_update) { + if (rc == 0) { + if (flow->pending_update.old_match_id && + npc->mark_actions > 0) + npc->mark_actions--; + if (flow->pending_update.old_vtag && + npc->vtag_strip_actions > 0) + npc->vtag_strip_actions--; + } else { + if (flow->pending_update.new_match_id && + npc->mark_actions > 0) + npc->mark_actions--; + if (flow->pending_update.new_vtag && + npc->vtag_strip_actions > 0) + npc->vtag_strip_actions--; + } + flow->pending_update.pending = false; + } + + /* Release the COUNT counter on destroy. */ + if (destroy && rc == 0 && fptr->use_ctr && + fptr->ctr_id != NPC_COUNTER_NONE) { + roc_npc_mcam_free_counter(roc_npc, fptr->ctr_id); + fptr->ctr_id = NPC_COUNTER_NONE; + } + + /* Balance the mark_actions refcount for MARK/FLAG rules. */ + if (destroy && rc == 0 && match_id) + roc_npc_mark_actions_sub_return(roc_npc, 1); + + /* Balance the vtag_strip_actions refcount for RX VLAN strip rules. */ + if (destroy && rc == 0 && fptr->nix_intf == ROC_NPC_INTF_RX && + fptr->vtag_action != 0) + roc_npc_vtag_actions_sub_return(roc_npc, 1); + } + + op->rc = rc; + op->done = true; + } + + q->pushed = q->head; + return 0; +} + +int +roc_npc_flow_pull(struct roc_npc *roc_npc, uint32_t queue_id, struct roc_npc_flow_op_result res[], + uint16_t n_res) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + struct roc_npc_flow_queue *q; + uint16_t cnt = 0; + + if (npc->flow_queues == NULL || queue_id >= npc->nb_flow_queues) + return -EINVAL; + + q = &npc->flow_queues[queue_id]; + + /* Return ops that were pushed but not yet reported. */ + while (q->tail != q->pushed && cnt < n_res) { + struct roc_npc_async_op *op = &q->ops[q->tail & (q->size - 1)]; + + res[cnt].user_data = op->user_data; + res[cnt].rc = op->rc; + cnt++; + q->tail++; + } + + return cnt; +} + +/* Resolve a candidate handle to a template flow or a sync flow. */ +bool +roc_npc_async_flow_resolve(struct roc_npc *roc_npc, void *handle, struct roc_npc_flow **flow) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + struct roc_npc_template_flow *cand = handle; + struct roc_npc_template_table *table; + uint16_t i; + + if (handle == NULL || flow == NULL) + return false; + + for (table = npc->flow_tables; table != NULL; table = table->next) { + struct roc_npc_template_flow *match; + + /* Integer range check: pointer comparison across objects is UB. */ + if (table->flow_pool != NULL) { + uintptr_t p = (uintptr_t)cand; + uintptr_t base = (uintptr_t)table->flow_pool; + uintptr_t end = (uintptr_t)(table->flow_pool + table->nb_flows); + + if (p >= base && p < end) { + *flow = &cand->flow; + return true; + } + } + + plt_spinlock_lock(&table->lock); + /* clang-format off */ + TAILQ_FOREACH(match, &table->live_flows, link) { + if (match == cand) { + plt_spinlock_unlock(&table->lock); + *flow = cand->roc_flow; + return true; + } + } + /* clang-format on */ + plt_spinlock_unlock(&table->lock); + } + + /* Handle still queued: a PATTERN rule not yet committed. */ + for (i = 0; i < npc->nb_flow_queues; i++) { + struct roc_npc_flow_queue *q = &npc->flow_queues[i]; + uint32_t j; + + for (j = q->tail; j != q->head; j++) { + struct roc_npc_async_op *op = &q->ops[j & (q->size - 1)]; + + if (op->flow == cand) { + *flow = cand->roc_flow; + return true; + } + } + } + + return false; +} + +/* Return the table cookie used to recover update context. */ +void * +roc_npc_async_flow_table_cookie(struct roc_npc_template_flow *flow) +{ + if (flow == NULL || flow->table == NULL) + return NULL; + + return flow->table->cookie; +} + +/* Reconcile template-table bookkeeping after flush or port close. */ +void +npc_template_tables_flush_reconcile(struct npc *npc) +{ + struct roc_npc_template_table *table; + struct roc_npc_template_flow *flow; + struct roc_npc_flow *fptr; + uint16_t match_id; + uint32_t j; + + for (table = npc->flow_tables; table != NULL; table = table->next) { + plt_spinlock_lock(&table->lock); + while ((flow = TAILQ_FIRST(&table->live_flows)) != NULL) { + TAILQ_REMOVE(&table->live_flows, flow, link); + /* roc_flow is already freed by free_all_resources. */ + roc_npc_template_flow_free_rule(flow); + plt_free(flow); + } + + /* INDEX_WITH_PATTERN rules live in flow_pool[]; clear stale state. */ + if (table->flow_pool != NULL) { + for (j = 0; j < table->nb_flows; j++) { + fptr = &table->flow_pool[j].flow; + + if (fptr->enable == 0) + continue; + + match_id = (fptr->npc_action >> NPC_RX_ACT_MATCH_OFFSET) & + NPC_RX_ACT_MATCH_MASK; + if (match_id && npc->mark_actions > 0) + npc->mark_actions--; + + if (fptr->nix_intf == ROC_NPC_INTF_RX && fptr->vtag_action != 0 && + npc->vtag_strip_actions > 0) + npc->vtag_strip_actions--; + + memset(fptr, 0, sizeof(*fptr)); + fptr->ctr_id = NPC_COUNTER_NONE; + } + } + + table->nb_live = 0; + table->hw_released = true; + plt_spinlock_unlock(&table->lock); + } +} diff --git a/drivers/common/cnxk/roc_npc_mcam.c b/drivers/common/cnxk/roc_npc_mcam.c index 286cfd2dbd..34665f1bc3 100644 --- a/drivers/common/cnxk/roc_npc_mcam.c +++ b/drivers/common/cnxk/roc_npc_mcam.c @@ -714,7 +714,6 @@ npc_mcam_fetch_kex_cfg(struct npc *npc) struct mbox *mbox = mbox_get(npc->mbox); int rc = 0; - if (!roc_model_is_cn20k()) { mbox_alloc_msg_npc_get_kex_cfg(mbox); rc = mbox_process_msg(mbox, (void *)&kex_rsp); @@ -786,6 +785,46 @@ npc_mcam_set_channel(struct roc_npc_flow *flow, struct npc_cn20k_mcam_write_entr flow->mcam_mask[0] |= (uint64_t)mask; } +/* Fold RQ into the action word and program the RX channel into kw[0], + * matching the RX steps the sync path does in npc_mcam_alloc_and_write(). + */ +void +roc_npc_mcam_write_rx_finalize(struct roc_npc *roc_npc, struct roc_npc_flow *flow) +{ + struct npc *npc = roc_npc_to_npc_priv(roc_npc); + uint16_t chan, mask; + uint64_t op; + + if (flow->nix_intf != NIX_INTF_RX) + return; + + /* Set the RQ in the action index field, but only for UCAST* ops that + * use it as a receive queue. Clear then set so a re-finalize on an + * async QUEUE update leaves no stale bits. + */ + op = flow->npc_action & 0xFULL; + if (op == NIX_RX_ACTIONOP_UCAST || op == NIX_RX_ACTIONOP_UCAST_IPSEC || + op == NIX_RX_ACTIONOP_UCAST_CPT) { + flow->npc_action &= ~GENMASK_ULL(39, 20); + flow->npc_action |= ((uint64_t)flow->recv_queue << 20) & GENMASK_ULL(39, 20); + } + + /* Program the RX channel into kw[0]. */ + chan = npc->channel; + mask = BIT_ULL(12) - 1; + + flow->mcam_data[0] &= ~(GENMASK(11, 0)); + flow->mcam_mask[0] &= ~(GENMASK(11, 0)); + + if (roc_model_runtime_is_cn10k() && !roc_npc_action_is_rx_inline(flow->npc_action)) { + chan = (chan & NIX_CHAN_CPT_X2P_MASK); + mask = (mask & NIX_CHAN_CPT_X2P_MASK); + } + + flow->mcam_data[0] |= (uint64_t)chan; + flow->mcam_mask[0] |= (uint64_t)mask; +} + #define NPC_PF_FUNC_WIDTH 2 #define NPC_KEX_PF_FUNC_MASK 0xFFFF @@ -1430,5 +1469,9 @@ npc_flow_free_all_resources(struct npc *npc) plt_free(flow); } } + + /* Reconcile template-table bookkeeping for all teardown paths. */ + npc_template_tables_flush_reconcile(npc); + return rc; } diff --git a/drivers/common/cnxk/roc_npc_priv.h b/drivers/common/cnxk/roc_npc_priv.h index c7e7d2d8f6..c6ab033d9f 100644 --- a/drivers/common/cnxk/roc_npc_priv.h +++ b/drivers/common/cnxk/roc_npc_priv.h @@ -427,6 +427,99 @@ struct npc_age_flow_entry { TAILQ_HEAD(npc_age_flow_list_head, npc_age_flow_entry); +/* Queue-based async template flow engine types. */ + +/* One in-flight async op in a queue ring. */ +struct roc_npc_async_op { + struct roc_npc_template_flow *flow; /**< Rule the op acts on. */ + void *user_data; /**< Echoed back by flow_pull. */ + bool is_create; /**< true = create, false = destroy. */ + bool is_update; /**< true = in-place actions rewrite. */ + bool done; /**< Set once pushed to hardware. */ + int rc; /**< Result captured at push time. */ +}; + +/* Fixed-size op ring; head/pushed/tail are free-running indices. */ +struct roc_npc_flow_queue { + struct roc_npc_async_op *ops; /**< size entries, power-of-two. */ + uint32_t size; /**< Ring capacity (power of two). */ + uint32_t head; /**< Next slot to enqueue into. */ + uint32_t pushed; /**< Ops up to here are committed. */ + uint32_t tail; /**< Next slot to return via pull. */ +}; + +/* A deep-copied, retained rule awaiting (or holding) a hardware entry. */ +struct roc_npc_template_flow { + struct roc_npc_template_table *table; /**< Owning table. */ + struct roc_npc_flow *roc_flow; /**< HW flow (PATTERN, once committed). */ + struct roc_npc_item_info *items; /**< Deep-copied merged pattern. */ + struct roc_npc_action *actions; /**< Deep-copied merged actions. */ + struct roc_npc_flow flow; /**< Embedded HW flow (INDEX path). */ + struct roc_npc_attr attr; /**< Priority / ingress / egress. */ + uint64_t npc_default_action; /**< Resolved port default action. */ + uint32_t flowkey_cfg; /**< RSS flow key captured at create. */ + uint32_t slot; /**< Pre-reserved index (INDEX path). */ + uint16_t dst_pf_func; /**< Resolved destination pf_func. */ + uint16_t nb_items; /**< Item count (excluding END). */ + uint16_t nb_actions; /**< Action count (excluding END). */ + /* Pending in-place actions update, applied at push. */ + struct { + uint64_t npc_action; + uint64_t npc_action2; + uint64_t vtag_action; + uint32_t recv_queue; + bool pending; /**< An update is enqueued, awaiting push. */ + bool old_match_id; /**< Old action set carried a mark/match_id. */ + bool old_vtag; /**< Old action set carried RX vtag strip. */ + bool new_match_id; /**< New action set carries a mark/match_id. */ + bool new_vtag; /**< New action set carries RX vtag strip. */ + } pending_update; + TAILQ_ENTRY(roc_npc_template_flow) link; +}; + +TAILQ_HEAD(roc_npc_template_flow_list, roc_npc_template_flow); + +/* Pattern template blueprint. */ +struct roc_npc_pattern_template { + struct roc_npc_pattern_template_attr attr; + struct roc_npc_item_info *pattern; /**< nb_items + END terminator. */ + void **copies; /**< Tracked payload allocations. */ + uint16_t nb_copies; /**< Entries used in copies[]. */ + uint16_t nb_items; /**< Item count (excluding END). */ + uint32_t refcnt; /**< Tables referencing this template. */ +}; + +/* Actions template blueprint. */ +struct roc_npc_actions_template { + struct roc_npc_actions_template_attr attr; + struct roc_npc_action *actions; /**< nb_actions + END terminator. */ + struct roc_npc_action *masks; /**< Per-action masks (optional). */ + void **copies; /**< Tracked conf allocations. */ + uint16_t nb_copies; /**< Entries used in copies[]. */ + uint16_t nb_actions; /**< Action count (excluding END). */ + uint32_t refcnt; /**< Tables referencing this template. */ +}; + +/* Template table and rule storage. */ +struct roc_npc_template_table { + int *mcam_ids; /**< Reserved entries (index). */ + struct roc_npc_template_flow *flow_pool; /**< Per-index rule slots. */ + struct roc_npc_pattern_template **pattern_templates; + struct roc_npc_actions_template **actions_templates; + struct roc_npc_template_flow_list live_flows; /**< Live pattern rules. */ + struct roc_npc_template_table *next; /**< Port table list link. */ + void *cookie; /**< Opaque consumer context. */ + struct roc_npc_attr flow_attr; /**< Priority/ingress/egress. */ + enum roc_npc_template_table_insertion_type insertion_type; + plt_spinlock_t lock; /**< Guards live_flows/nb_live. */ + uint32_t nb_flows; /**< Max rules the table holds. */ + uint32_t nb_live; /**< Live pattern rules count. */ + uint32_t transfer; /**< Transfer (port) rule. */ + uint8_t nb_pattern_templates; /**< pattern_templates entries. */ + uint8_t nb_actions_templates; /**< actions_templates entries. */ + bool hw_released; /**< Entries freed by a flush. */ +}; + struct npc { struct mbox *mbox; /* Mbox */ uint64_t keyx_supp_nmask[NPC_MAX_INTF]; /* nibble mask */ @@ -459,6 +552,10 @@ struct npc { struct plt_bitmap *rss_grp_entries; struct npc_flow_list ipsec_list; uint8_t enable_debug; + /* Queue-based async template flow state owned by roc. */ + struct roc_npc_flow_queue *flow_queues; + uint16_t nb_flow_queues; + struct roc_npc_template_table *flow_tables; }; #define NPC_HASH_FIELD_LEN 16 @@ -533,6 +630,7 @@ int npc_mcam_fetch_hw_cap(struct npc *npc, uint8_t *npc_hw_cap); int npc_get_free_mcam_entry(struct mbox *mbox, struct roc_npc_flow *flow, struct npc *npc); void npc_delete_prio_list_entry(struct npc *npc, struct roc_npc_flow *flow); int npc_flow_free_all_resources(struct npc *npc); +void npc_template_tables_flush_reconcile(struct npc *npc); const struct roc_npc_item_info * npc_parse_skip_void_and_any_items(const struct roc_npc_item_info *pattern); int npc_program_mcam(struct npc *npc, struct npc_parse_state *pst, bool mcam_alloc); diff --git a/drivers/common/cnxk/roc_npc_utils.c b/drivers/common/cnxk/roc_npc_utils.c index 8e83b8662d..7ae877aa98 100644 --- a/drivers/common/cnxk/roc_npc_utils.c +++ b/drivers/common/cnxk/roc_npc_utils.c @@ -1088,7 +1088,7 @@ npc_get_free_mcam_entry(struct mbox *mbox, struct roc_npc_flow *flow, struct npc flow->key_type); if (rc) { plt_npc_dbg("npc: failed to allocate MCAM entry."); - return rc; + goto err; } new_entry->flow = flow; @@ -1096,7 +1096,7 @@ npc_get_free_mcam_entry(struct mbox *mbox, struct roc_npc_flow *flow, struct npc rc = npc_alloc_mcam_by_ref_entry(mbox, flow, npc, &rsp_local); if (rc) - return rc; + goto err; new_entry->flow = flow; diff --git a/drivers/common/cnxk/roc_platform_base_symbols.c b/drivers/common/cnxk/roc_platform_base_symbols.c index 00746abc06..de8de910f6 100644 --- a/drivers/common/cnxk/roc_platform_base_symbols.c +++ b/drivers/common/cnxk/roc_platform_base_symbols.c @@ -487,6 +487,7 @@ RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_mcam_enable_all_entries) RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_mcam_alloc_entry) RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_mcam_ena_dis_entry) RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_mcam_write_entry) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_mcam_write_rx_finalize) RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_get_low_priority_mcam) RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_profile_name_get) RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_kex_capa_get) @@ -505,6 +506,21 @@ RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_aged_flow_ctx_get) RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_defrag_mcam_banks) RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_get_key_type) RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_flow_mcam_dump) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_flow_configure) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_pattern_template_create) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_pattern_template_destroy) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_actions_template_create) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_actions_template_destroy) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_template_table_create) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_template_table_destroy) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_async_flow_create) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_async_flow_destroy) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_async_flow_create_by_index_with_pattern) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_async_flow_actions_update) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_flow_push) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_flow_pull) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_async_flow_resolve) +RTE_EXPORT_INTERNAL_SYMBOL(roc_npc_async_flow_table_cookie) RTE_EXPORT_INTERNAL_SYMBOL(roc_re_ml_zeta_get) RTE_EXPORT_INTERNAL_SYMBOL(roc_re_ml_zeta_put) RTE_EXPORT_INTERNAL_SYMBOL(roc_ree_queues_attach) diff --git a/drivers/net/cnxk/cn10k_ethdev.c b/drivers/net/cnxk/cn10k_ethdev.c index 23a2341c8b..bcf4e23286 100644 --- a/drivers/net/cnxk/cn10k_ethdev.c +++ b/drivers/net/cnxk/cn10k_ethdev.c @@ -5,6 +5,7 @@ #include "cn10k_flow.h" #include "cn10k_rx.h" #include "cn10k_tx.h" +#include "cnxk_flow.h" static uint16_t nix_rx_offload_flags(struct rte_eth_dev *eth_dev) @@ -916,8 +917,27 @@ npc_flow_ops_override(void) cnxk_flow_ops.create = cn10k_flow_create; cnxk_flow_ops.destroy = cn10k_flow_destroy; cnxk_flow_ops.info_get = cn10k_flow_info_get; + + cnxk_flow_ops.configure = cnxk_flow_configure; + cnxk_flow_ops.pattern_template_create = cnxk_flow_pattern_template_create; + cnxk_flow_ops.pattern_template_destroy = cnxk_flow_pattern_template_destroy; + cnxk_flow_ops.actions_template_create = cnxk_flow_actions_template_create; + cnxk_flow_ops.actions_template_destroy = cnxk_flow_actions_template_destroy; + cnxk_flow_ops.template_table_create = cnxk_flow_template_table_create; + cnxk_flow_ops.template_table_destroy = cnxk_flow_template_table_destroy; } +/* Fast-path (async) flow ops. Installed per-port in cn10k_nix_probe(). */ +static const struct rte_flow_fp_ops cn10k_flow_fp_ops = { + .async_create = cnxk_flow_async_create, + .async_create_by_index = cnxk_flow_async_create_by_index, + .async_create_by_index_with_pattern = cnxk_flow_async_create_by_index_with_pattern, + .async_actions_update = cnxk_flow_async_actions_update, + .async_destroy = cnxk_flow_async_destroy, + .push = cn10k_flow_push, + .pull = cnxk_flow_pull, +}; + static int cn10k_nix_remove(struct rte_pci_device *pci_dev) { @@ -966,6 +986,9 @@ cn10k_nix_probe(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev) dev = cnxk_eth_pmd_priv(eth_dev); + /* Install fast-path (async) flow ops for this port. */ + eth_dev->flow_fp_ops = &cn10k_flow_fp_ops; + /* DROP_RE is not supported with inline IPSec for CN10K A0 and * when vector mode is enabled. */ diff --git a/drivers/net/cnxk/cn10k_flow.c b/drivers/net/cnxk/cn10k_flow.c index b95fb83f08..02bc002507 100644 --- a/drivers/net/cnxk/cn10k_flow.c +++ b/drivers/net/cnxk/cn10k_flow.c @@ -48,6 +48,49 @@ cn10k_flow_info_get(struct rte_eth_dev *dev, struct rte_flow_port_info *port_inf return cnxk_flow_info_get_common(dev, port_info, queue_info, err); } +int +cn10k_flow_push(struct rte_eth_dev *eth_dev, uint32_t queue, struct rte_flow_error *error) +{ + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct roc_npc *npc = &dev->npc; + bool reset_rx = false; + int rc; + + /* Flush the pending async ops to hardware first. */ + rc = cnxk_flow_push(eth_dev, queue, error); + if (rc) + return rc; + + /* Async push, unlike sync create/destroy, does not toggle the MARK and + * VLAN-strip RX offloads; reconcile each with its refcount so the Rx + * function matches the live rules. + */ + if (roc_npc_mark_actions_get(npc) && + !(dev->rx_offload_flags & NIX_RX_OFFLOAD_MARK_UPDATE_F)) { + dev->rx_offload_flags |= NIX_RX_OFFLOAD_MARK_UPDATE_F; + reset_rx = true; + } else if (!roc_npc_mark_actions_get(npc) && + (dev->rx_offload_flags & NIX_RX_OFFLOAD_MARK_UPDATE_F)) { + dev->rx_offload_flags &= ~NIX_RX_OFFLOAD_MARK_UPDATE_F; + reset_rx = true; + } + + if (roc_npc_vtag_actions_get(npc) && + !(dev->rx_offload_flags & NIX_RX_OFFLOAD_VLAN_STRIP_F)) { + dev->rx_offload_flags |= NIX_RX_OFFLOAD_VLAN_STRIP_F; + reset_rx = true; + } else if (!roc_npc_vtag_actions_get(npc) && + (dev->rx_offload_flags & NIX_RX_OFFLOAD_VLAN_STRIP_F)) { + dev->rx_offload_flags &= ~NIX_RX_OFFLOAD_VLAN_STRIP_F; + reset_rx = true; + } + + if (reset_rx) + cn10k_eth_set_rx_function(eth_dev); + + return 0; +} + int cn10k_flow_destroy(struct rte_eth_dev *eth_dev, struct rte_flow *rte_flow, struct rte_flow_error *error) diff --git a/drivers/net/cnxk/cn10k_flow.h b/drivers/net/cnxk/cn10k_flow.h index c9e8c86e96..c0d239c20f 100644 --- a/drivers/net/cnxk/cn10k_flow.h +++ b/drivers/net/cnxk/cn10k_flow.h @@ -16,6 +16,8 @@ int cn10k_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow, int cn10k_flow_info_get(struct rte_eth_dev *dev, struct rte_flow_port_info *port_info, struct rte_flow_queue_info *queue_info, struct rte_flow_error *err); +int cn10k_flow_push(struct rte_eth_dev *dev, uint32_t queue, struct rte_flow_error *error); + #define CNXK_NPC_COUNTERS_MAX 512 #endif /* __CN10K_FLOW_H__ */ diff --git a/drivers/net/cnxk/cn20k_ethdev.c b/drivers/net/cnxk/cn20k_ethdev.c index f08079f612..9fac027650 100644 --- a/drivers/net/cnxk/cn20k_ethdev.c +++ b/drivers/net/cnxk/cn20k_ethdev.c @@ -5,7 +5,7 @@ #include "cn20k_flow.h" #include "cn20k_rx.h" #include "cn20k_tx.h" - +#include "cnxk_flow.h" static uint16_t nix_rx_offload_flags(struct rte_eth_dev *eth_dev) { @@ -962,8 +962,27 @@ npc_flow_ops_override(void) cnxk_flow_ops.create = cn20k_flow_create; cnxk_flow_ops.destroy = cn20k_flow_destroy; cnxk_flow_ops.info_get = cn20k_flow_info_get; + + cnxk_flow_ops.configure = cnxk_flow_configure; + cnxk_flow_ops.pattern_template_create = cnxk_flow_pattern_template_create; + cnxk_flow_ops.pattern_template_destroy = cnxk_flow_pattern_template_destroy; + cnxk_flow_ops.actions_template_create = cnxk_flow_actions_template_create; + cnxk_flow_ops.actions_template_destroy = cnxk_flow_actions_template_destroy; + cnxk_flow_ops.template_table_create = cnxk_flow_template_table_create; + cnxk_flow_ops.template_table_destroy = cnxk_flow_template_table_destroy; } +/* Fast-path (async) flow ops. Installed per-port in cn20k_nix_probe(). */ +static const struct rte_flow_fp_ops cn20k_flow_fp_ops = { + .async_create = cnxk_flow_async_create, + .async_create_by_index = cnxk_flow_async_create_by_index, + .async_create_by_index_with_pattern = cnxk_flow_async_create_by_index_with_pattern, + .async_actions_update = cnxk_flow_async_actions_update, + .async_destroy = cnxk_flow_async_destroy, + .push = cn20k_flow_push, + .pull = cnxk_flow_pull, +}; + static int cn20k_nix_remove(struct rte_pci_device *pci_dev) { @@ -1012,6 +1031,9 @@ cn20k_nix_probe(struct rte_pci_driver *pci_drv, struct rte_pci_device *pci_dev) dev = cnxk_eth_pmd_priv(eth_dev); + /* Install fast-path (async) flow ops for this port. */ + eth_dev->flow_fp_ops = &cn20k_flow_fp_ops; + /* Register up msg callbacks for PTP information */ roc_nix_ptp_info_cb_register(&dev->nix, cn20k_nix_ptp_info_update_cb); diff --git a/drivers/net/cnxk/cn20k_flow.c b/drivers/net/cnxk/cn20k_flow.c index fd50f516ee..ff6e220103 100644 --- a/drivers/net/cnxk/cn20k_flow.c +++ b/drivers/net/cnxk/cn20k_flow.c @@ -48,6 +48,49 @@ cn20k_flow_info_get(struct rte_eth_dev *dev, struct rte_flow_port_info *port_inf return cnxk_flow_info_get_common(dev, port_info, queue_info, err); } +int +cn20k_flow_push(struct rte_eth_dev *eth_dev, uint32_t queue, struct rte_flow_error *error) +{ + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct roc_npc *npc = &dev->npc; + bool reset_rx = false; + int rc; + + /* Flush the pending async ops to hardware first. */ + rc = cnxk_flow_push(eth_dev, queue, error); + if (rc) + return rc; + + /* Async push, unlike sync create/destroy, does not toggle the MARK and + * VLAN-strip RX offloads; reconcile each with its refcount so the Rx + * function matches the live rules. + */ + if (roc_npc_mark_actions_get(npc) && + !(dev->rx_offload_flags & NIX_RX_OFFLOAD_MARK_UPDATE_F)) { + dev->rx_offload_flags |= NIX_RX_OFFLOAD_MARK_UPDATE_F; + reset_rx = true; + } else if (!roc_npc_mark_actions_get(npc) && + (dev->rx_offload_flags & NIX_RX_OFFLOAD_MARK_UPDATE_F)) { + dev->rx_offload_flags &= ~NIX_RX_OFFLOAD_MARK_UPDATE_F; + reset_rx = true; + } + + if (roc_npc_vtag_actions_get(npc) && + !(dev->rx_offload_flags & NIX_RX_OFFLOAD_VLAN_STRIP_F)) { + dev->rx_offload_flags |= NIX_RX_OFFLOAD_VLAN_STRIP_F; + reset_rx = true; + } else if (!roc_npc_vtag_actions_get(npc) && + (dev->rx_offload_flags & NIX_RX_OFFLOAD_VLAN_STRIP_F)) { + dev->rx_offload_flags &= ~NIX_RX_OFFLOAD_VLAN_STRIP_F; + reset_rx = true; + } + + if (reset_rx) + cn20k_eth_set_rx_function(eth_dev); + + return 0; +} + int cn20k_flow_destroy(struct rte_eth_dev *eth_dev, struct rte_flow *rte_flow, struct rte_flow_error *error) diff --git a/drivers/net/cnxk/cn20k_flow.h b/drivers/net/cnxk/cn20k_flow.h index 1e4bd6214a..ed8a2aa1f3 100644 --- a/drivers/net/cnxk/cn20k_flow.h +++ b/drivers/net/cnxk/cn20k_flow.h @@ -16,6 +16,8 @@ int cn20k_flow_destroy(struct rte_eth_dev *dev, struct rte_flow *flow, int cn20k_flow_info_get(struct rte_eth_dev *dev, struct rte_flow_port_info *port_info, struct rte_flow_queue_info *queue_info, struct rte_flow_error *err); +int cn20k_flow_push(struct rte_eth_dev *dev, uint32_t queue, struct rte_flow_error *error); + #define CNXK_NPC_COUNTERS_MAX 512 #endif /* __CN20K_FLOW_H__ */ diff --git a/drivers/net/cnxk/cnxk_ethdev.h b/drivers/net/cnxk/cnxk_ethdev.h index fc66135c66..342d249cf4 100644 --- a/drivers/net/cnxk/cnxk_ethdev.h +++ b/drivers/net/cnxk/cnxk_ethdev.h @@ -328,6 +328,50 @@ struct cnxk_macsec_sess { }; TAILQ_HEAD(cnxk_macsec_sess_list, cnxk_macsec_sess); +/* Pattern template: owns deep copies tracked in copies[]. */ +struct cnxk_flow_pattern_template { + struct rte_flow_pattern_template_attr attr; + struct rte_flow_item *pattern; + /* Mirrored roc handle for table-level ownership/lifecycle in roc. */ + struct roc_npc_pattern_template *roc_tmpl; + uint16_t nb_items; + void **copies; + uint16_t nb_copies; + uint32_t refcnt; +}; + +/* Actions template: owns deep copies tracked in copies[]. */ +struct cnxk_flow_actions_template { + struct rte_flow_actions_template_attr attr; + struct rte_flow_action *actions; + struct rte_flow_action *masks; + /* Mirrored roc handle for table-level ownership/lifecycle in roc. */ + struct roc_npc_actions_template *roc_tmpl; + uint16_t nb_actions; + void **copies; + uint16_t nb_copies; + uint32_t refcnt; +}; + +/* Template table wrapper. The roc layer owns rule storage, MCAM reservation + * and rule lifecycle; cnxk keeps rte templates for per-rule translation. + */ +struct cnxk_flow_table { + /* roc engine table handle: storage, MCAM, rule lifecycle. */ + struct roc_npc_template_table *roc_table; + + struct cnxk_flow_pattern_template **pattern_templates; + struct cnxk_flow_actions_template **actions_templates; + uint8_t nb_pattern_templates; + uint8_t nb_actions_templates; + + uint32_t nb_flows; + struct roc_npc_attr flow_attr; + /* roc_npc_attr has no transfer field; replay it into each rule attr. */ + bool transfer; + enum rte_flow_table_insertion_type insertion_type; +}; + struct cnxk_eth_dev { /* ROC NIX */ struct roc_nix nix; diff --git a/drivers/net/cnxk/cnxk_flow.c b/drivers/net/cnxk/cnxk_flow.c index c1c48eb7ab..1562b7389e 100644 --- a/drivers/net/cnxk/cnxk_flow.c +++ b/drivers/net/cnxk/cnxk_flow.c @@ -8,6 +8,7 @@ #define TNL_DCP_MATCH_ID 5 #define NRML_MATCH_ID 1 + const struct cnxk_rte_flow_term_info term[] = { [RTE_FLOW_ITEM_TYPE_ETH] = {ROC_NPC_ITEM_TYPE_ETH, sizeof(struct rte_flow_item_eth)}, [RTE_FLOW_ITEM_TYPE_VLAN] = {ROC_NPC_ITEM_TYPE_VLAN, sizeof(struct rte_flow_item_vlan)}, @@ -49,8 +50,7 @@ const struct cnxk_rte_flow_term_info term[] = { [RTE_FLOW_ITEM_TYPE_REPRESENTED_PORT] = {ROC_NPC_ITEM_TYPE_REPRESENTED_PORT, sizeof(struct rte_flow_item_ethdev)}, [RTE_FLOW_ITEM_TYPE_PPPOES] = {ROC_NPC_ITEM_TYPE_PPPOES, - sizeof(struct rte_flow_item_pppoe)} -}; + sizeof(struct rte_flow_item_pppoe)}}; static int npc_rss_action_validate(struct rte_eth_dev *eth_dev, const struct rte_flow_attr *attr, @@ -239,7 +239,7 @@ append_rss_action(struct cnxk_eth_dev *dev, struct roc_npc_action *in_actions, u /* Add RSS action */ rss_conf->queue_num = nb_rxq; - queue_arr = calloc(1, rss_conf->queue_num * sizeof(uint16_t)); + queue_arr = plt_zmalloc(rss_conf->queue_num * sizeof(uint16_t), 0); if (!queue_arr) { plt_err("Failed to allocate memory for rss queue"); rc = -ENOMEM; @@ -266,10 +266,12 @@ append_rss_action(struct cnxk_eth_dev *dev, struct roc_npc_action *in_actions, u while (free_allocs[j] != 0) j++; free_allocs[j] = (uint64_t)rss_conf; + j++; + free_allocs[j] = (uint64_t)queue_arr; return 0; free_rss: - rte_free(rss_conf); + plt_free(rss_conf); fail: return rc; } @@ -1070,22 +1072,18 @@ cnxk_flow_query_common(struct rte_eth_dev *eth_dev, struct rte_flow *flow, const struct rte_flow_action *action, void *data, struct rte_flow_error *error, bool is_rep) { - struct roc_npc_flow *in_flow = (struct roc_npc_flow *)flow; struct rte_flow_query_count *query = data; + struct roc_npc_flow *in_flow; struct cnxk_rep_dev *rep_dev; - struct cnxk_eth_dev *dev; - struct roc_npc *npc; const char *errmsg = NULL; + struct cnxk_eth_dev *dev; int errcode = ENOTSUP; + struct roc_npc *npc; int rc; - if (action->type != RTE_FLOW_ACTION_TYPE_COUNT) { - errmsg = "Only COUNT is supported in query"; - goto err_exit; - } - - if (in_flow->ctr_id == NPC_COUNTER_NONE) { - errmsg = "Counter is not available"; + if (flow == NULL) { + errmsg = "Invalid flow handle"; + errcode = EINVAL; goto err_exit; } @@ -1098,6 +1096,29 @@ cnxk_flow_query_common(struct rte_eth_dev *eth_dev, struct rte_flow *flow, npc = &rep_dev->parent_dev->npc; } + /* Let the roc layer resolve async/template handles to the underlying + * roc_npc_flow (NULL until committed); else treat as a sync flow. + */ + in_flow = (struct roc_npc_flow *)flow; + if (!is_rep) + roc_npc_async_flow_resolve(npc, flow, &in_flow); + + if (in_flow == NULL) { + errmsg = "Flow is not committed yet"; + errcode = EINVAL; + goto err_exit; + } + + if (action->type != RTE_FLOW_ACTION_TYPE_COUNT) { + errmsg = "Only COUNT is supported in query"; + goto err_exit; + } + + if (in_flow->ctr_id == NPC_COUNTER_NONE) { + errmsg = "Counter is not available"; + goto err_exit; + } + if (in_flow->use_pre_alloc) { rc = roc_npc_inl_mcam_read_counter(in_flow->ctr_id, &query->hits); } else { @@ -1149,8 +1170,8 @@ cnxk_flow_isolate(struct rte_eth_dev *eth_dev __rte_unused, int enable __rte_unu * entry for this port. */ - rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, - NULL, "Flow isolation not supported"); + rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "Flow isolation not supported"); return -rte_errno; } @@ -1159,8 +1180,9 @@ int cnxk_flow_dev_dump_common(struct rte_eth_dev *eth_dev, struct rte_flow *flow, FILE *file, struct rte_flow_error *error, bool is_rep) { + struct roc_npc_flow *in_flow = NULL; + struct cnxk_eth_dev *dev = NULL; struct cnxk_rep_dev *rep_dev; - struct cnxk_eth_dev *dev; struct roc_npc *npc; /* is_rep set for operation performed via representor ports */ @@ -1179,7 +1201,17 @@ cnxk_flow_dev_dump_common(struct rte_eth_dev *eth_dev, struct rte_flow *flow, FI } if (flow != NULL) { - roc_npc_flow_mcam_dump(file, npc, (struct roc_npc_flow *)flow); + /* Resolve async/template handles to the underlying roc_npc_flow; + * else treat as a sync flow. + */ + if (is_rep || !roc_npc_async_flow_resolve(npc, flow, &in_flow)) + in_flow = (struct roc_npc_flow *)flow; + + if (in_flow == NULL) + return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "flow is not committed yet"); + + roc_npc_flow_mcam_dump(file, npc, in_flow); return 0; } @@ -1187,6 +1219,1276 @@ cnxk_flow_dev_dump_common(struct rte_eth_dev *eth_dev, struct rte_flow *flow, FI return 0; } +/* Configure async flow queues for the port. */ +int +cnxk_flow_configure(struct rte_eth_dev *eth_dev, const struct rte_flow_port_attr *port_attr, + uint16_t nb_queue, const struct rte_flow_queue_attr *queue_attr[], + struct rte_flow_error *err) +{ + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + const struct roc_npc_flow_queue_attr **rptrs = NULL; + struct roc_npc_flow_queue_attr *rattr = NULL; + struct roc_npc *npc = &dev->npc; + uint16_t i; + int rc; + + RTE_SET_USED(port_attr); + + /* Translate the rte queue attributes into roc_npc types; the roc + * engine validates nb_queue == 0 and a NULL queue_attr. + */ + if (nb_queue != 0 && queue_attr != NULL) { + rattr = plt_zmalloc(nb_queue * sizeof(*rattr), 0); + if (rattr == NULL) + return rte_flow_error_set(err, ENOMEM, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, "Failed to allocate flow queues"); + + rptrs = plt_zmalloc(nb_queue * sizeof(*rptrs), 0); + if (rptrs == NULL) { + plt_free(rattr); + return rte_flow_error_set(err, ENOMEM, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, "Failed to allocate flow queues"); + } + for (i = 0; i < nb_queue; i++) { + if (queue_attr[i] != NULL) { + rattr[i].size = queue_attr[i]->size; + rptrs[i] = &rattr[i]; + } else { + rptrs[i] = NULL; + } + } + } + + rc = roc_npc_flow_configure(npc, nb_queue, rptrs); + + plt_free(rattr); + plt_free(rptrs); + + if (rc) + return rte_flow_error_set(err, -rc, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "Failed to configure flow queues"); + return 0; +} + +/* Deep-copy a spec/last/mask/conf payload into template-owned memory. */ +static int +cnxk_flow_dup_conf(void **copies, uint16_t *nb_copies, const void *src, size_t sz, const void **dst) +{ + void *copy; + + if (src == NULL || sz == 0) { + *dst = NULL; + return 0; + } + copy = plt_zmalloc(sz, 0); + if (copy == NULL) + return -ENOMEM; + memcpy(copy, src, sz); + copies[(*nb_copies)++] = copy; + *dst = copy; + return 0; +} + +struct rte_flow_pattern_template * +cnxk_flow_pattern_template_create(struct rte_eth_dev *eth_dev, + const struct rte_flow_pattern_template_attr *attr, + const struct rte_flow_item pattern[], + struct rte_flow_error *error) +{ + const char *errmsg = "Failed to allocate pattern template"; + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct cnxk_flow_pattern_template *tmpl = NULL; + struct roc_npc_pattern_template_attr roc_attr; + struct roc_npc_item_info *roc_pattern = NULL; + int i, nb_items = 0, errcode = ENOMEM; + struct roc_npc *npc = &dev->npc; + int roc_err; + + if (attr == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "NULL template attr"); + return NULL; + } + + if (pattern == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "NULL pattern"); + return NULL; + } + + while (pattern[nb_items].type != RTE_FLOW_ITEM_TYPE_END) + nb_items++; + + /* Reject oversized templates; rules are built into fixed-size stack + * arrays (combined_items[ROC_NPC_ITEM_TYPE_END + 1]). + */ + if (nb_items > ROC_NPC_ITEM_TYPE_END) { + errcode = ENOTSUP; + errmsg = "too many pattern items for hardware"; + goto err_nomem; + } + + tmpl = plt_zmalloc(sizeof(*tmpl), 0); + if (tmpl == NULL) + goto err_nomem; + + tmpl->attr = *attr; + tmpl->nb_items = nb_items; + + tmpl->pattern = plt_zmalloc((nb_items + 1) * sizeof(struct rte_flow_item), 0); + if (tmpl->pattern == NULL) + goto err_free; + + /* At most 3 payload copies (spec/last/mask) per item. */ + tmpl->copies = plt_zmalloc(3 * (nb_items + 1) * sizeof(void *), 0); + if (tmpl->copies == NULL) + goto err_free; + + for (i = 0; i < nb_items; i++) { + enum rte_flow_item_type t = pattern[i].type; + size_t sz = (t < (int)RTE_DIM(term)) ? term[t].item_size : 0; + + /* Reject unrepresentable item types (sz 0), which would drop + * spec/last/mask. VOID/ANY legitimately carry no payload. + */ + if (t != RTE_FLOW_ITEM_TYPE_VOID && t != RTE_FLOW_ITEM_TYPE_ANY && sz == 0) { + errcode = ENOTSUP; + errmsg = "unsupported pattern item type in template"; + goto err_free; + } + + /* Reject items with nested variable-length payloads; a flat + * memcpy would retain a dangling pointer into caller memory. + */ + if (t == RTE_FLOW_ITEM_TYPE_RAW) { + errcode = ENOTSUP; + errmsg = "Item type with nested pointers is not supported " + "in pattern template (e.g. RAW)"; + goto err_free; + } + + tmpl->pattern[i].type = t; + if (cnxk_flow_dup_conf(tmpl->copies, &tmpl->nb_copies, pattern[i].spec, sz, + &tmpl->pattern[i].spec) || + cnxk_flow_dup_conf(tmpl->copies, &tmpl->nb_copies, pattern[i].last, sz, + &tmpl->pattern[i].last) || + cnxk_flow_dup_conf(tmpl->copies, &tmpl->nb_copies, pattern[i].mask, sz, + &tmpl->pattern[i].mask)) + goto err_free; + } + tmpl->pattern[nb_items].type = RTE_FLOW_ITEM_TYPE_END; + tmpl->roc_tmpl = NULL; + tmpl->refcnt = 0; + + roc_pattern = plt_zmalloc((nb_items + 1) * sizeof(*roc_pattern), 0); + if (roc_pattern == NULL) + goto err_free; + + for (i = 0; i < nb_items; i++) { + enum rte_flow_item_type t = tmpl->pattern[i].type; + size_t sz = (t < (int)RTE_DIM(term)) ? term[t].item_size : 0; + + roc_pattern[i].type = + (t < (int)RTE_DIM(term)) ? term[t].item_type : ROC_NPC_ITEM_TYPE_VOID; + roc_pattern[i].size = sz; + roc_pattern[i].spec = tmpl->pattern[i].spec; + roc_pattern[i].last = tmpl->pattern[i].last; + roc_pattern[i].mask = tmpl->pattern[i].mask; + } + roc_pattern[nb_items].type = ROC_NPC_ITEM_TYPE_END; + + memset(&roc_attr, 0, sizeof(roc_attr)); + roc_attr.relaxed_matching = attr->relaxed_matching; + roc_attr.ingress = attr->ingress; + roc_attr.egress = attr->egress; + roc_attr.transfer = attr->transfer; + + roc_err = 0; + tmpl->roc_tmpl = roc_npc_pattern_template_create(npc, &roc_attr, roc_pattern, &roc_err); + if (tmpl->roc_tmpl == NULL) { + errcode = (roc_err < 0) ? -roc_err : ENOMEM; + errmsg = "Failed to create ROC pattern template"; + goto err_free; + } + + plt_free(roc_pattern); + + return (struct rte_flow_pattern_template *)tmpl; + +err_free: + plt_free(roc_pattern); + if (tmpl != NULL) { + uint16_t j; + if (tmpl->roc_tmpl != NULL) + roc_npc_pattern_template_destroy(npc, tmpl->roc_tmpl); + for (j = 0; j < tmpl->nb_copies; j++) + plt_free(tmpl->copies[j]); + plt_free(tmpl->copies); + plt_free(tmpl->pattern); + plt_free(tmpl); + } +err_nomem: + rte_flow_error_set(error, errcode, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, errmsg); + return NULL; +} + +int +cnxk_flow_pattern_template_destroy(struct rte_eth_dev *eth_dev, + struct rte_flow_pattern_template *templ, + struct rte_flow_error *error) +{ + struct cnxk_flow_pattern_template *tmpl = (struct cnxk_flow_pattern_template *)templ; + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct roc_npc *npc = &dev->npc; + uint16_t i; + int rc; + + if (tmpl == NULL) + return 0; + + if (tmpl->refcnt != 0) + return rte_flow_error_set(error, EBUSY, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "pattern template still in use by a table"); + + rc = roc_npc_pattern_template_destroy(npc, tmpl->roc_tmpl); + if (rc) + return rte_flow_error_set(error, -rc, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "failed to destroy ROC pattern template"); + + for (i = 0; i < tmpl->nb_copies; i++) + plt_free(tmpl->copies[i]); + plt_free(tmpl->copies); + plt_free(tmpl->pattern); + plt_free(tmpl); + return 0; +} +/* Size of an action's conf for the v1-supported set; -1 = unsupported. */ +static int +cnxk_flow_action_conf_size(enum rte_flow_action_type type) +{ + switch (type) { + case RTE_FLOW_ACTION_TYPE_VOID: + case RTE_FLOW_ACTION_TYPE_DROP: + case RTE_FLOW_ACTION_TYPE_PF: + case RTE_FLOW_ACTION_TYPE_FLAG: + case RTE_FLOW_ACTION_TYPE_OF_POP_VLAN: + case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP: + return 0; /* no conf */ + case RTE_FLOW_ACTION_TYPE_QUEUE: + return sizeof(struct rte_flow_action_queue); + case RTE_FLOW_ACTION_TYPE_VF: + return sizeof(struct rte_flow_action_vf); + case RTE_FLOW_ACTION_TYPE_MARK: + return sizeof(struct rte_flow_action_mark); + case RTE_FLOW_ACTION_TYPE_COUNT: + return sizeof(struct rte_flow_action_count); + case RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN: + return sizeof(struct rte_flow_action_of_push_vlan); + case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_VID: + return sizeof(struct rte_flow_action_of_set_vlan_vid); + case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_PCP: + return sizeof(struct rte_flow_action_of_set_vlan_pcp); + case RTE_FLOW_ACTION_TYPE_AGE: + return sizeof(struct rte_flow_action_age); + case RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT: + case RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR: + return sizeof(struct rte_flow_action_ethdev); + case RTE_FLOW_ACTION_TYPE_PORT_ID: + return sizeof(struct rte_flow_action_port_id); + default: + return -1; + } +} + +/* Deep-copy an action conf. RSS nests queue[]/key[] pointers, so pack them + * into one blob and fix the pointers; other confs use a flat memcpy. + */ +static void * +cnxk_flow_dup_action_conf(const struct rte_flow_action *act) +{ + if (act->conf == NULL) + return NULL; + + if (act->type == RTE_FLOW_ACTION_TYPE_RSS) { + const struct rte_flow_action_rss *rss = act->conf; + size_t rss_blob_bytes = sizeof(*rss); + size_t rss_queue_bytes = (size_t)rss->queue_num * sizeof(uint16_t); + size_t rss_key_bytes = rss->key_len; + struct rte_flow_action_rss *rss_copy; + uint8_t *rss_blob; + + /* Reject a non-zero length paired with a NULL pointer, which + * would make the memcpy below dereference NULL. + */ + if ((rss_queue_bytes && rss->queue == NULL) || (rss_key_bytes && rss->key == NULL)) + return NULL; + + rss_blob = plt_zmalloc(rss_blob_bytes + rss_queue_bytes + rss_key_bytes, 0); + if (rss_blob == NULL) + return NULL; + rss_copy = (struct rte_flow_action_rss *)rss_blob; + *rss_copy = *rss; + if (rss_queue_bytes) { + memcpy(rss_blob + rss_blob_bytes, rss->queue, rss_queue_bytes); + rss_copy->queue = (const uint16_t *)(rss_blob + rss_blob_bytes); + } else { + rss_copy->queue = NULL; + } + if (rss_key_bytes) { + memcpy(rss_blob + rss_blob_bytes + rss_queue_bytes, rss->key, + rss_key_bytes); + rss_copy->key = + (const uint8_t *)(rss_blob + rss_blob_bytes + rss_queue_bytes); + } else { + rss_copy->key = NULL; + } + return rss_copy; + } + + { + int conf_size = cnxk_flow_action_conf_size(act->type); + void *copy; + + if (conf_size <= 0) + return NULL; + copy = plt_zmalloc(conf_size, 0); + if (copy != NULL) + memcpy(copy, act->conf, conf_size); + return copy; + } +} + +/* Map a supported rte_flow action type to its roc_npc action type, to mirror + * the actions template into the roc engine for lifecycle/refcount ownership. + */ +static int +cnxk_flow_roc_action_type(enum rte_flow_action_type type, enum roc_npc_action_type *roc_type) +{ + switch (type) { + case RTE_FLOW_ACTION_TYPE_VOID: + case RTE_FLOW_ACTION_TYPE_VXLAN_DECAP: + *roc_type = ROC_NPC_ACTION_TYPE_VOID; + return 0; + case RTE_FLOW_ACTION_TYPE_DROP: + *roc_type = ROC_NPC_ACTION_TYPE_DROP; + return 0; + case RTE_FLOW_ACTION_TYPE_PF: + *roc_type = ROC_NPC_ACTION_TYPE_PF; + return 0; + case RTE_FLOW_ACTION_TYPE_VF: + *roc_type = ROC_NPC_ACTION_TYPE_VF; + return 0; + case RTE_FLOW_ACTION_TYPE_FLAG: + *roc_type = ROC_NPC_ACTION_TYPE_FLAG; + return 0; + case RTE_FLOW_ACTION_TYPE_MARK: + *roc_type = ROC_NPC_ACTION_TYPE_MARK; + return 0; + case RTE_FLOW_ACTION_TYPE_COUNT: + *roc_type = ROC_NPC_ACTION_TYPE_COUNT; + return 0; + case RTE_FLOW_ACTION_TYPE_QUEUE: + *roc_type = ROC_NPC_ACTION_TYPE_QUEUE; + return 0; + case RTE_FLOW_ACTION_TYPE_RSS: + *roc_type = ROC_NPC_ACTION_TYPE_RSS; + return 0; + case RTE_FLOW_ACTION_TYPE_OF_POP_VLAN: + *roc_type = ROC_NPC_ACTION_TYPE_VLAN_STRIP; + return 0; + case RTE_FLOW_ACTION_TYPE_OF_PUSH_VLAN: + *roc_type = ROC_NPC_ACTION_TYPE_VLAN_ETHTYPE_INSERT; + return 0; + case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_VID: + *roc_type = ROC_NPC_ACTION_TYPE_VLAN_INSERT; + return 0; + case RTE_FLOW_ACTION_TYPE_OF_SET_VLAN_PCP: + *roc_type = ROC_NPC_ACTION_TYPE_VLAN_PCP_INSERT; + return 0; + case RTE_FLOW_ACTION_TYPE_AGE: + *roc_type = ROC_NPC_ACTION_TYPE_AGE; + return 0; + case RTE_FLOW_ACTION_TYPE_PORT_ID: + case RTE_FLOW_ACTION_TYPE_REPRESENTED_PORT: + case RTE_FLOW_ACTION_TYPE_PORT_REPRESENTOR: + *roc_type = ROC_NPC_ACTION_TYPE_PORT_ID; + return 0; + default: + return -1; + } +} + +struct rte_flow_actions_template * +cnxk_flow_actions_template_create(struct rte_eth_dev *eth_dev, + const struct rte_flow_actions_template_attr *attr, + const struct rte_flow_action actions[], + const struct rte_flow_action masks[], + struct rte_flow_error *error) +{ + const char *errmsg = "Failed to allocate actions template"; + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct cnxk_flow_actions_template *tmpl = NULL; + struct roc_npc_actions_template_attr roc_attr; + struct roc_npc_action *roc_actions = NULL; + int i, nb_actions = 0, errcode = ENOMEM; + struct roc_npc *npc = &dev->npc; + int roc_err = 0; + + if (attr == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "NULL template attr"); + return NULL; + } + + if (actions == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "NULL actions"); + return NULL; + } + + while (actions[nb_actions].type != RTE_FLOW_ACTION_TYPE_END) + nb_actions++; + + /* Reject oversized templates; rules are built into fixed-size stack + * arrays (combined_actions[ROC_NPC_MAX_ACTION_COUNT] plus END). + */ + if (nb_actions > ROC_NPC_MAX_ACTION_COUNT - 1) { + errcode = ENOTSUP; + errmsg = "too many actions for hardware"; + goto err; + } + + tmpl = plt_zmalloc(sizeof(*tmpl), 0); + if (tmpl == NULL) + goto err; + + tmpl->attr = *attr; + tmpl->nb_actions = nb_actions; + + tmpl->actions = plt_zmalloc((nb_actions + 1) * sizeof(struct rte_flow_action), 0); + if (tmpl->actions == NULL) + goto err; + + tmpl->masks = plt_zmalloc((nb_actions + 1) * sizeof(struct rte_flow_action), 0); + if (tmpl->masks == NULL) + goto err; + + /* up to 2 conf copies (action + mask) per action */ + tmpl->copies = plt_zmalloc(2 * (nb_actions + 1) * sizeof(void *), 0); + if (tmpl->copies == NULL) + goto err; + + for (i = 0; i < nb_actions; i++) { + enum rte_flow_action_type t = actions[i].type; + int sz = cnxk_flow_action_conf_size(t); + + if (sz < 0 && t != RTE_FLOW_ACTION_TYPE_RSS) { + errcode = ENOTSUP; + errmsg = "Unsupported action in template " + "(supports VOID/DROP/QUEUE/PF/VF/MARK/FLAG/COUNT/RSS/" + "OF_POP_VLAN/OF_PUSH_VLAN/OF_SET_VLAN_VID/" + "OF_SET_VLAN_PCP/AGE/REPRESENTED_PORT/" + "PORT_REPRESENTOR/PORT_ID/VXLAN_DECAP)"; + goto err; + } + + tmpl->actions[i].type = t; + + if (t == RTE_FLOW_ACTION_TYPE_RSS) { + /* RSS has nested pointers — use deep-copy helper. */ + void *copy = cnxk_flow_dup_action_conf(&actions[i]); + + if (copy == NULL && actions[i].conf != NULL) { + const struct rte_flow_action_rss *rss = actions[i].conf; + + /* Malformed conf vs allocation failure: report a + * precise errno instead of the default ENOMEM. + */ + if ((rss->queue_num && rss->queue == NULL) || + (rss->key_len && rss->key == NULL)) { + errcode = EINVAL; + errmsg = "Malformed RSS action conf in template"; + } + goto err; + } + tmpl->actions[i].conf = copy; + if (copy) + tmpl->copies[tmpl->nb_copies++] = copy; + } else { + if (cnxk_flow_dup_conf(tmpl->copies, &tmpl->nb_copies, actions[i].conf, sz, + &tmpl->actions[i].conf)) + goto err; + } + + if (masks != NULL) { + if (masks[i].type != t) { + errcode = EINVAL; + errmsg = "actions/masks type mismatch in template"; + goto err; + } + tmpl->masks[i].type = t; + if (t == RTE_FLOW_ACTION_TYPE_RSS) { + /* RSS nests pointers and cannot be copied by the flat + * helper; reject a non-NULL RSS mask. + */ + if (masks[i].conf != NULL) { + errcode = ENOTSUP; + errmsg = "Non-NULL RSS mask conf is not " + "supported in template"; + goto err; + } + tmpl->masks[i].conf = NULL; + } else if (cnxk_flow_dup_conf(tmpl->copies, &tmpl->nb_copies, masks[i].conf, + sz > 0 ? sz : 0, &tmpl->masks[i].conf)) { + goto err; + } + } + } + + tmpl->actions[nb_actions].type = RTE_FLOW_ACTION_TYPE_END; + tmpl->masks[nb_actions].type = RTE_FLOW_ACTION_TYPE_END; + tmpl->roc_tmpl = NULL; + tmpl->refcnt = 0; + + /* Mirror only action types into the roc engine for template lifecycle + * and refcount ownership; per-rule conf translation stays in cnxk. + */ + roc_actions = plt_zmalloc((nb_actions + 1) * sizeof(*roc_actions), 0); + if (roc_actions == NULL) + goto err; + + for (i = 0; i < nb_actions; i++) { + enum roc_npc_action_type rt; + + if (cnxk_flow_roc_action_type(tmpl->actions[i].type, &rt)) { + errcode = ENOTSUP; + errmsg = "Unsupported action in template"; + goto err; + } + roc_actions[i].type = rt; + roc_actions[i].conf = NULL; + } + roc_actions[nb_actions].type = ROC_NPC_ACTION_TYPE_END; + + memset(&roc_attr, 0, sizeof(roc_attr)); + roc_attr.ingress = attr->ingress; + roc_attr.egress = attr->egress; + roc_attr.transfer = attr->transfer; + + tmpl->roc_tmpl = + roc_npc_actions_template_create(npc, &roc_attr, roc_actions, NULL, &roc_err); + plt_free(roc_actions); + roc_actions = NULL; + if (tmpl->roc_tmpl == NULL) { + errcode = (roc_err < 0) ? -roc_err : ENOMEM; + errmsg = "Failed to create ROC actions template"; + goto err; + } + + return (struct rte_flow_actions_template *)tmpl; + +err: + plt_free(roc_actions); + if (tmpl != NULL) { + uint16_t j; + if (tmpl->roc_tmpl != NULL) + roc_npc_actions_template_destroy(npc, tmpl->roc_tmpl); + for (j = 0; j < tmpl->nb_copies; j++) + plt_free(tmpl->copies[j]); + plt_free(tmpl->copies); + plt_free(tmpl->masks); + plt_free(tmpl->actions); + plt_free(tmpl); + } + rte_flow_error_set(error, errcode, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, errmsg); + return NULL; +} + +int +cnxk_flow_actions_template_destroy(struct rte_eth_dev *eth_dev, + struct rte_flow_actions_template *templ, + struct rte_flow_error *error) +{ + struct cnxk_flow_actions_template *tmpl = (struct cnxk_flow_actions_template *)templ; + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct roc_npc *npc = &dev->npc; + uint16_t i; + int rc; + + if (tmpl == NULL) + return 0; + + if (tmpl->refcnt != 0) + return rte_flow_error_set(error, EBUSY, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "actions template still in use by a table"); + + rc = roc_npc_actions_template_destroy(npc, tmpl->roc_tmpl); + if (rc) + return rte_flow_error_set(error, -rc, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "failed to destroy ROC actions template"); + + for (i = 0; i < tmpl->nb_copies; i++) + plt_free(tmpl->copies[i]); + plt_free(tmpl->copies); + plt_free(tmpl->masks); + plt_free(tmpl->actions); + plt_free(tmpl); + return 0; +} + +struct rte_flow_template_table * +cnxk_flow_template_table_create(struct rte_eth_dev *eth_dev, + const struct rte_flow_template_table_attr *table_attr, + struct rte_flow_pattern_template *pattern_templates[], + uint8_t nb_pattern_templates, + struct rte_flow_actions_template *actions_templates[], + uint8_t nb_actions_templates, struct rte_flow_error *error) +{ + struct roc_npc_pattern_template **roc_pattern_templates = NULL; + struct roc_npc_actions_template **roc_actions_templates = NULL; + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct roc_npc_template_table_attr roc_attr; + enum rte_flow_table_insertion_type itype; + const struct rte_flow_attr *fattr; + struct roc_npc *npc = &dev->npc; + struct cnxk_flow_table *table; + uint32_t nb_flows; + int errcode = 0; + uint8_t i; + + if (table_attr == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "NULL table_attr"); + return NULL; + } + + itype = table_attr->insertion_type; + fattr = &table_attr->flow_attr; + nb_flows = table_attr->nb_flows; + + if (itype == RTE_FLOW_TABLE_INSERTION_TYPE_INDEX) { + rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "pure-index insertion not supported; NPC is match-based"); + return NULL; + } + + /* Validate the template handles before allocating anything. */ + for (i = 0; i < nb_pattern_templates; i++) { + if (pattern_templates[i] == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "invalid template handle"); + return NULL; + } + } + for (i = 0; i < nb_actions_templates; i++) { + if (actions_templates[i] == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "invalid template handle"); + return NULL; + } + } + + table = plt_zmalloc(sizeof(*table), 0); + if (table == NULL) + goto enomem; + + table->insertion_type = itype; + table->nb_flows = nb_flows; + + memset(&table->flow_attr, 0, sizeof(table->flow_attr)); + table->flow_attr.priority = fattr->priority; + table->flow_attr.ingress = fattr->ingress; + table->flow_attr.egress = fattr->egress; + table->transfer = fattr->transfer; + + if (nb_pattern_templates != 0) { + table->pattern_templates = + plt_zmalloc(nb_pattern_templates * sizeof(*table->pattern_templates), 0); + if (table->pattern_templates == NULL) + goto free_table; + roc_pattern_templates = + plt_zmalloc(nb_pattern_templates * sizeof(*roc_pattern_templates), 0); + if (roc_pattern_templates == NULL) + goto free_table; + } + if (nb_actions_templates != 0) { + table->actions_templates = + plt_zmalloc(nb_actions_templates * sizeof(*table->actions_templates), 0); + if (table->actions_templates == NULL) + goto free_table; + roc_actions_templates = + plt_zmalloc(nb_actions_templates * sizeof(*roc_actions_templates), 0); + if (roc_actions_templates == NULL) + goto free_table; + } + + /* Hand the table to the roc engine, which owns rule storage, MCAM + * reservation and the rule lifecycle; cnxk keeps per-rule translation. + */ + memset(&roc_attr, 0, sizeof(roc_attr)); + roc_attr.flow_attr = table->flow_attr; + roc_attr.transfer = table->transfer; + roc_attr.nb_flows = nb_flows; + roc_attr.insertion_type = (itype == RTE_FLOW_TABLE_INSERTION_TYPE_INDEX_WITH_PATTERN) ? + ROC_NPC_TEMPLATE_INSERTION_INDEX_WITH_PATTERN : + ROC_NPC_TEMPLATE_INSERTION_PATTERN; + /* Stash this wrapper as the table cookie so async_actions_update can + * recover the rte templates from just the flow handle. + */ + roc_attr.cookie = table; + + for (i = 0; i < nb_pattern_templates; i++) { + table->pattern_templates[i] = + (struct cnxk_flow_pattern_template *)pattern_templates[i]; + roc_pattern_templates[i] = table->pattern_templates[i]->roc_tmpl; + if (roc_pattern_templates[i] == NULL) + goto bad_handle; + } + for (i = 0; i < nb_actions_templates; i++) { + table->actions_templates[i] = + (struct cnxk_flow_actions_template *)actions_templates[i]; + roc_actions_templates[i] = table->actions_templates[i]->roc_tmpl; + if (roc_actions_templates[i] == NULL) + goto bad_handle; + } + + /* clang-format off */ + table->roc_table = roc_npc_template_table_create(npc, &roc_attr, + roc_pattern_templates, nb_pattern_templates, + roc_actions_templates, nb_actions_templates, &errcode); + /* clang-format on */ + plt_free(roc_pattern_templates); + plt_free(roc_actions_templates); + roc_pattern_templates = NULL; + roc_actions_templates = NULL; + if (table->roc_table == NULL) { + plt_free(table->pattern_templates); + plt_free(table->actions_templates); + plt_free(table); + rte_flow_error_set(error, errcode < 0 ? -errcode : ENOMEM, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "could not create template table"); + return NULL; + } + + for (i = 0; i < nb_pattern_templates; i++) + table->pattern_templates[i]->refcnt++; + table->nb_pattern_templates = nb_pattern_templates; + for (i = 0; i < nb_actions_templates; i++) + table->actions_templates[i]->refcnt++; + table->nb_actions_templates = nb_actions_templates; + + return (struct rte_flow_template_table *)table; + +bad_handle: + plt_free(roc_pattern_templates); + plt_free(roc_actions_templates); + plt_free(table->pattern_templates); + plt_free(table->actions_templates); + plt_free(table); + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "invalid template handle"); + return NULL; + +free_table: + plt_free(roc_pattern_templates); + plt_free(roc_actions_templates); + plt_free(table->pattern_templates); + plt_free(table->actions_templates); + plt_free(table); + +enomem: + rte_flow_error_set(error, ENOMEM, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, "out of memory"); + return NULL; +} + +int +cnxk_flow_template_table_destroy(struct rte_eth_dev *eth_dev, + struct rte_flow_template_table *tbl_handle, + struct rte_flow_error *error) +{ + struct cnxk_flow_table *table = (struct cnxk_flow_table *)tbl_handle; + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct roc_npc *npc = &dev->npc; + uint8_t i; + int rc; + + if (table == NULL) + return 0; + + /* The roc engine guards teardown: a busy table returns -EBUSY and + * nothing is freed, so the cnxk wrapper and refcounts stay intact. + */ + rc = roc_npc_template_table_destroy(npc, table->roc_table); + if (rc == -EBUSY) + return rte_flow_error_set(error, EBUSY, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "table still has live flows; destroy them first"); + if (rc) + return rte_flow_error_set(error, -rc, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "failed to destroy template table"); + + for (i = 0; i < table->nb_pattern_templates; i++) + table->pattern_templates[i]->refcnt--; + for (i = 0; i < table->nb_actions_templates; i++) + table->actions_templates[i]->refcnt--; + + plt_free(table->pattern_templates); + plt_free(table->actions_templates); + plt_free(table); + return 0; +} + +/* Reject per-rule pattern/actions arrays whose types/order do not mirror the + * chosen templates; the merge takes the type from the template but the + * spec/conf from the per-rule array. A NULL array uses template defaults. + */ +static int +cnxk_flow_check_items(const struct cnxk_flow_pattern_template *pt, + const struct rte_flow_item items[]) +{ + uint16_t k; + + if (items == NULL) + return 0; + for (k = 0; k < pt->nb_items; k++) + if (items[k].type != pt->pattern[k].type) + return -EINVAL; + if (items[pt->nb_items].type != RTE_FLOW_ITEM_TYPE_END) + return -EINVAL; + return 0; +} + +static int +cnxk_flow_check_actions(const struct cnxk_flow_actions_template *at, + const struct rte_flow_action actions[]) +{ + uint16_t k; + + if (actions == NULL) + return 0; + for (k = 0; k < at->nb_actions; k++) + if (actions[k].type != at->actions[k].type) + return -EINVAL; + if (actions[at->nb_actions].type != RTE_FLOW_ACTION_TYPE_END) + return -EINVAL; + return 0; +} + +/* Enqueue a PATTERN create: merge the per-rule spec with the templates, + * translate to roc_npc types, and hand to the roc engine (programmed at push). + */ +struct rte_flow * +cnxk_flow_async_create(struct rte_eth_dev *eth_dev, uint32_t queue, + const struct rte_flow_op_attr *attr, + struct rte_flow_template_table *tbl_handle, + const struct rte_flow_item items[], uint8_t pattern_template_index, + const struct rte_flow_action actions[], uint8_t action_template_index, + void *user_data, struct rte_flow_error *error) +{ + struct cnxk_flow_table *table = (struct cnxk_flow_table *)tbl_handle; + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct cnxk_flow_pattern_template *pt; + struct cnxk_flow_actions_template *at; + struct roc_npc_template_flow *flow; + struct roc_npc *npc = &dev->npc; + + uint64_t free_allocs[ROC_NPC_MAX_ACTION_COUNT + ROC_NPC_ITEM_TYPE_END + 1] = {0}; + struct roc_npc_item_info in_pattern[ROC_NPC_ITEM_TYPE_END + 1] = {0}; + struct rte_flow_action combined_actions[ROC_NPC_MAX_ACTION_COUNT]; + struct roc_npc_action in_actions[ROC_NPC_MAX_ACTION_COUNT] = {0}; + struct rte_flow_item combined_items[ROC_NPC_ITEM_TYPE_END + 1]; + struct roc_npc_action_sample in_sample = {0}; + struct rte_flow_attr rte_attr; + struct roc_npc_attr in_attr; + uint16_t dst_pf_func = 0; + uint64_t def_action = 0; + int errcode = 0; + uint16_t k; + int rc, j; + + RTE_SET_USED(attr); + + if (table == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "bad table"); + return NULL; + } + if (table->insertion_type != RTE_FLOW_TABLE_INSERTION_TYPE_PATTERN) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "table is index-insertion; use create_by_index_with_pattern"); + return NULL; + } + if (pattern_template_index >= table->nb_pattern_templates || + action_template_index >= table->nb_actions_templates) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "template index out of range"); + return NULL; + } + + pt = table->pattern_templates[pattern_template_index]; + at = table->actions_templates[action_template_index]; + + if (cnxk_flow_check_items(pt, items) != 0 || cnxk_flow_check_actions(at, actions) != 0) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "pattern/actions do not match the selected templates"); + return NULL; + } + + /* Merge: spec comes from the per-rule call, last/mask from the template. */ + for (k = 0; k < pt->nb_items; k++) { + combined_items[k].type = pt->pattern[k].type; + combined_items[k].spec = items ? items[k].spec : NULL; + combined_items[k].last = pt->pattern[k].last; + combined_items[k].mask = pt->pattern[k].mask; + } + combined_items[pt->nb_items].type = RTE_FLOW_ITEM_TYPE_END; + + /* Merge: per-rule conf overrides the template conf. */ + for (k = 0; k < at->nb_actions; k++) { + combined_actions[k].type = at->actions[k].type; + combined_actions[k].conf = + (actions && actions[k].conf) ? actions[k].conf : at->actions[k].conf; + } + combined_actions[at->nb_actions].type = RTE_FLOW_ACTION_TYPE_END; + + memset(&rte_attr, 0, sizeof(rte_attr)); + rte_attr.priority = table->flow_attr.priority; + rte_attr.ingress = table->flow_attr.ingress; + rte_attr.egress = table->flow_attr.egress; + rte_attr.transfer = table->transfer; + + /* Translate into roc_npc types. The RSS flow key is stashed in + * npc->flowkey_cfg_state so the engine can restore it at push(). + */ + memset(&in_attr, 0, sizeof(in_attr)); + rc = cnxk_map_flow_data(eth_dev, &rte_attr, combined_items, combined_actions, &in_attr, + in_pattern, in_actions, &in_sample, &npc->flowkey_cfg_state, + &dst_pf_func, &def_action, false, free_allocs, 0); + if (rc == 0) + flow = roc_npc_async_flow_create(npc, queue, table->roc_table, in_pattern, + in_actions, dst_pf_func, def_action, user_data, + &errcode); + else + flow = NULL; + + for (j = 0; j < (int)RTE_DIM(free_allocs) && free_allocs[j]; j++) + plt_free((void *)free_allocs[j]); + + if (rc != 0) { + rte_flow_error_set(error, rc < 0 ? -rc : rc, RTE_FLOW_ERROR_TYPE_ACTION_NUM, NULL, + "Failed to map flow data"); + return NULL; + } + if (flow == NULL) { + rte_flow_error_set(error, errcode < 0 ? -errcode : EINVAL, + RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "async flow create failed"); + return NULL; + } + + return (struct rte_flow *)flow; +} + +/* Enqueue a destroy operation. */ +int +cnxk_flow_async_destroy(struct rte_eth_dev *eth_dev, uint32_t queue, + const struct rte_flow_op_attr *attr, struct rte_flow *flow, void *user_data, + struct rte_flow_error *error) +{ + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct roc_npc *npc = &dev->npc; + int rc; + + RTE_SET_USED(attr); + + rc = roc_npc_async_flow_destroy(npc, queue, (struct roc_npc_template_flow *)flow, + user_data); + if (rc) + return rte_flow_error_set(error, -rc, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "bad queue or flow handle"); + return 0; +} + +/* Flush all pending async operations to hardware in one batch. */ +int +cnxk_flow_push(struct rte_eth_dev *eth_dev, uint32_t queue, struct rte_flow_error *error) +{ + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct roc_npc *npc = &dev->npc; + int rc; + + rc = roc_npc_flow_push(npc, queue); + if (rc) + return rte_flow_error_set(error, -rc, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "bad queue"); + return 0; +} + +/* Return results for completed ops back to the application. */ +int +cnxk_flow_pull(struct rte_eth_dev *eth_dev, uint32_t queue, struct rte_flow_op_result res[], + uint16_t n_res, struct rte_flow_error *error) +{ + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct roc_npc *npc = &dev->npc; + uint16_t done = 0; + + /* Drain roc engine results in fixed-size batches, translating each + * roc op result into the rte op result without a per-pull allocation. + */ + while (done < n_res) { + struct roc_npc_flow_op_result rres[64]; + uint16_t batch = RTE_MIN((uint16_t)(n_res - done), (uint16_t)RTE_DIM(rres)); + int got = roc_npc_flow_pull(npc, queue, rres, batch); + uint16_t k; + + if (got < 0) { + if (done != 0) + break; + return rte_flow_error_set(error, -got, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, + NULL, "bad queue"); + } + + for (k = 0; k < (uint16_t)got; k++) { + res[done + k].status = + (rres[k].rc == 0) ? RTE_FLOW_OP_SUCCESS : RTE_FLOW_OP_ERROR; + res[done + k].user_data = rres[k].user_data; + } + done += (uint16_t)got; + + if (got < batch) + break; + } + + return done; +} + +struct rte_flow * +cnxk_flow_async_create_by_index(struct rte_eth_dev *eth_dev, uint32_t queue, + const struct rte_flow_op_attr *attr, + struct rte_flow_template_table *table, uint32_t rule_index, + const struct rte_flow_action actions[], + uint8_t action_template_index, void *user_data, + struct rte_flow_error *error) +{ + RTE_SET_USED(eth_dev); + RTE_SET_USED(queue); + RTE_SET_USED(attr); + RTE_SET_USED(table); + RTE_SET_USED(rule_index); + RTE_SET_USED(actions); + RTE_SET_USED(action_template_index); + RTE_SET_USED(user_data); + + rte_flow_error_set(error, ENOTSUP, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "index-only insertion needs a match key; NPC is match-based"); + return NULL; +} + +/* Like async_create but with an app-specified index; the app must avoid + * concurrent creates to the same index and use a valid table index. + */ +struct rte_flow * +/* clang-format off */ +cnxk_flow_async_create_by_index_with_pattern(struct rte_eth_dev *eth_dev, + uint32_t queue, const struct rte_flow_op_attr *attr, + struct rte_flow_template_table *tbl_handle, uint32_t rule_index, + const struct rte_flow_item items[], uint8_t pattern_template_index, + const struct rte_flow_action actions[], + uint8_t action_template_index, void *user_data, + struct rte_flow_error *error) +/* clang-format on */ +{ + struct cnxk_flow_table *table = (struct cnxk_flow_table *)tbl_handle; + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct cnxk_flow_pattern_template *pt; + struct cnxk_flow_actions_template *at; + struct roc_npc_template_flow *flow; + struct roc_npc *npc = &dev->npc; + + uint64_t free_allocs[ROC_NPC_MAX_ACTION_COUNT + ROC_NPC_ITEM_TYPE_END + 1] = {0}; + struct roc_npc_item_info in_pattern[ROC_NPC_ITEM_TYPE_END + 1] = {0}; + struct rte_flow_action combined_actions[ROC_NPC_MAX_ACTION_COUNT]; + struct roc_npc_action in_actions[ROC_NPC_MAX_ACTION_COUNT] = {0}; + struct rte_flow_item combined_items[ROC_NPC_ITEM_TYPE_END + 1]; + struct roc_npc_action_sample in_sample = {0}; + struct rte_flow_attr rte_attr; + struct roc_npc_attr in_attr; + uint32_t flowkey_cfg = 0; + uint16_t dst_pf_func = 0; + uint64_t def_action = 0; + int errcode = 0; + uint16_t k; + int rc, j; + + RTE_SET_USED(attr); + + if (table == NULL) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "bad table"); + return NULL; + } + if (table->insertion_type != RTE_FLOW_TABLE_INSERTION_TYPE_INDEX_WITH_PATTERN) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "table is not index-with-pattern insertion"); + return NULL; + } + if (pattern_template_index >= table->nb_pattern_templates || + action_template_index >= table->nb_actions_templates) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "template index out of range"); + return NULL; + } + if (rule_index >= table->nb_flows) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "rule_index out of range"); + return NULL; + } + + pt = table->pattern_templates[pattern_template_index]; + at = table->actions_templates[action_template_index]; + + if (cnxk_flow_check_items(pt, items) != 0 || cnxk_flow_check_actions(at, actions) != 0) { + rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "pattern/actions do not match the selected templates"); + return NULL; + } + + for (k = 0; k < pt->nb_items; k++) { + combined_items[k].type = pt->pattern[k].type; + combined_items[k].spec = items ? items[k].spec : NULL; + combined_items[k].last = pt->pattern[k].last; + combined_items[k].mask = pt->pattern[k].mask; + } + combined_items[pt->nb_items].type = RTE_FLOW_ITEM_TYPE_END; + + for (k = 0; k < at->nb_actions; k++) { + combined_actions[k].type = at->actions[k].type; + combined_actions[k].conf = + (actions && actions[k].conf) ? actions[k].conf : at->actions[k].conf; + } + combined_actions[at->nb_actions].type = RTE_FLOW_ACTION_TYPE_END; + + memset(&rte_attr, 0, sizeof(rte_attr)); + rte_attr.priority = table->flow_attr.priority; + rte_attr.ingress = table->flow_attr.ingress; + rte_attr.egress = table->flow_attr.egress; + rte_attr.transfer = table->transfer; + + memset(&in_attr, 0, sizeof(in_attr)); + rc = cnxk_map_flow_data(eth_dev, &rte_attr, combined_items, combined_actions, &in_attr, + in_pattern, in_actions, &in_sample, &flowkey_cfg, &dst_pf_func, + &def_action, false, free_allocs, 0); + if (rc == 0) + /* clang-format off */ + flow = roc_npc_async_flow_create_by_index_with_pattern(npc, queue, + table->roc_table, rule_index, in_pattern, in_actions, + user_data, &errcode); + /* clang-format on */ + else + flow = NULL; + + for (j = 0; j < (int)RTE_DIM(free_allocs) && free_allocs[j]; j++) + plt_free((void *)free_allocs[j]); + + if (rc != 0) { + rte_flow_error_set(error, rc < 0 ? -rc : rc, RTE_FLOW_ERROR_TYPE_ACTION_NUM, NULL, + "Failed to map flow data"); + return NULL; + } + if (flow == NULL) { + rte_flow_error_set(error, errcode < 0 ? -errcode : EINVAL, + RTE_FLOW_ERROR_TYPE_HANDLE, NULL, "flow parse failed"); + return NULL; + } + + return (struct rte_flow *)flow; +} + +/* Update the actions of an existing flow, keeping key/mask/mcam_id/enable. */ +int +cnxk_flow_async_actions_update(struct rte_eth_dev *eth_dev, uint32_t queue, + const struct rte_flow_op_attr *attr, struct rte_flow *flow, + const struct rte_flow_action actions[], + uint8_t action_template_index, void *user_data, + struct rte_flow_error *error) +{ + struct cnxk_eth_dev *dev = cnxk_eth_pmd_priv(eth_dev); + struct cnxk_flow_actions_template *at; + struct roc_npc *npc = &dev->npc; + struct cnxk_flow_table *table; + + uint64_t free_allocs[ROC_NPC_MAX_ACTION_COUNT + ROC_NPC_ITEM_TYPE_END + 1] = {0}; + struct roc_npc_item_info in_pattern[ROC_NPC_ITEM_TYPE_END + 1] = {0}; + struct rte_flow_action combined_actions[ROC_NPC_MAX_ACTION_COUNT]; + struct roc_npc_action in_actions[ROC_NPC_MAX_ACTION_COUNT] = {0}; + struct roc_npc_action_sample in_sample = {0}; + struct rte_flow_item end_pattern[1]; + struct rte_flow_attr rte_attr; + struct roc_npc_attr in_attr; + uint32_t flowkey_cfg = 0; + uint16_t dst_pf_func = 0; + uint64_t def_action = 0; + uint16_t k; + int rc, j; + + RTE_SET_USED(attr); + + if (flow == NULL) + return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "bad flow handle"); + + /* Recover the cnxk table context from the cookie the engine kept for + * this rule's table. + */ + table = roc_npc_async_flow_table_cookie((struct roc_npc_template_flow *)flow); + if (table == NULL) + return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_HANDLE, NULL, + "flow handle has no associated table"); + if (action_template_index >= table->nb_actions_templates) + return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_UNSPECIFIED, NULL, + "actions template index out of range"); + + at = table->actions_templates[action_template_index]; + + if (cnxk_flow_check_actions(at, actions) != 0) + return rte_flow_error_set(error, EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, NULL, + "actions do not match the selected template"); + + for (k = 0; k < at->nb_actions; k++) { + combined_actions[k].type = at->actions[k].type; + combined_actions[k].conf = + (actions && actions[k].conf) ? actions[k].conf : at->actions[k].conf; + } + combined_actions[at->nb_actions].type = RTE_FLOW_ACTION_TYPE_END; + + end_pattern[0].type = RTE_FLOW_ITEM_TYPE_END; + + memset(&rte_attr, 0, sizeof(rte_attr)); + rte_attr.priority = table->flow_attr.priority; + rte_attr.ingress = table->flow_attr.ingress; + rte_attr.egress = table->flow_attr.egress; + rte_attr.transfer = table->transfer; + + memset(&in_attr, 0, sizeof(in_attr)); + rc = cnxk_map_flow_data(eth_dev, &rte_attr, end_pattern, combined_actions, &in_attr, + in_pattern, in_actions, &in_sample, &flowkey_cfg, &dst_pf_func, + &def_action, false, free_allocs, 0); + if (rc == 0) + /* clang-format off */ + rc = roc_npc_async_flow_actions_update(npc, queue, + (struct roc_npc_template_flow *)flow, in_actions, user_data); + /* clang-format on */ + + for (j = 0; j < (int)RTE_DIM(free_allocs) && free_allocs[j]; j++) + plt_free((void *)free_allocs[j]); + + if (rc != 0) + return rte_flow_error_set(error, rc < 0 ? -rc : EINVAL, RTE_FLOW_ERROR_TYPE_ACTION, + NULL, "failed to update flow actions"); + + return 0; +} static int cnxk_flow_dev_dump(struct rte_eth_dev *eth_dev, struct rte_flow *flow, FILE *file, diff --git a/drivers/net/cnxk/cnxk_flow.h b/drivers/net/cnxk/cnxk_flow.h index 2986ea81d1..2202f4f3b8 100644 --- a/drivers/net/cnxk/cnxk_flow.h +++ b/drivers/net/cnxk/cnxk_flow.h @@ -48,4 +48,81 @@ int cnxk_flow_dev_dump_common(struct rte_eth_dev *eth_dev, struct rte_flow *flow struct rte_flow_error *error, bool is_rep); int cnxk_mtr_destroy(struct rte_eth_dev *eth_dev, uint32_t mtr_id); +int cnxk_flow_configure(struct rte_eth_dev *eth_dev, const struct rte_flow_port_attr *port_attr, + uint16_t nb_queue, const struct rte_flow_queue_attr *queue_attr[], + struct rte_flow_error *err); + +/* clang-format off */ +struct rte_flow_pattern_template *cnxk_flow_pattern_template_create(struct rte_eth_dev *eth_dev, + const struct rte_flow_pattern_template_attr *attr, + const struct rte_flow_item pattern[], + struct rte_flow_error *error); +/* clang-format on */ + +int cnxk_flow_pattern_template_destroy(struct rte_eth_dev *eth_dev, + struct rte_flow_pattern_template *templ, + struct rte_flow_error *error); + +/* clang-format off */ +struct rte_flow_actions_template *cnxk_flow_actions_template_create(struct rte_eth_dev *eth_dev, + const struct rte_flow_actions_template_attr *attr, + const struct rte_flow_action actions[], + const struct rte_flow_action masks[], + struct rte_flow_error *error); +/* clang-format on */ + +int cnxk_flow_actions_template_destroy(struct rte_eth_dev *eth_dev, + struct rte_flow_actions_template *templ, + struct rte_flow_error *error); + +/* clang-format off */ +struct rte_flow_template_table *cnxk_flow_template_table_create(struct rte_eth_dev *eth_dev, + const struct rte_flow_template_table_attr *table_attr, + struct rte_flow_pattern_template *pattern_templates[], + uint8_t nb_pattern_templates, + struct rte_flow_actions_template *actions_templates[], + uint8_t nb_actions_templates, + struct rte_flow_error *error); +/* clang-format on */ + +int cnxk_flow_template_table_destroy(struct rte_eth_dev *eth_dev, + struct rte_flow_template_table *tbl_handle, + struct rte_flow_error *error); +struct rte_flow * +cnxk_flow_async_create(struct rte_eth_dev *eth_dev, uint32_t queue, + const struct rte_flow_op_attr *attr, struct rte_flow_template_table *table, + const struct rte_flow_item items[], uint8_t pattern_template_index, + const struct rte_flow_action actions[], uint8_t action_template_index, + void *user_data, struct rte_flow_error *error); +int cnxk_flow_async_destroy(struct rte_eth_dev *eth_dev, uint32_t queue, + const struct rte_flow_op_attr *attr, struct rte_flow *flow, + void *user_data, struct rte_flow_error *error); + +int cnxk_flow_push(struct rte_eth_dev *eth_dev, uint32_t queue, struct rte_flow_error *error); +int cnxk_flow_pull(struct rte_eth_dev *eth_dev, uint32_t queue, struct rte_flow_op_result res[], + uint16_t n_res, struct rte_flow_error *error); + +struct rte_flow *cnxk_flow_async_create_by_index(struct rte_eth_dev *eth_dev, uint32_t queue, + const struct rte_flow_op_attr *attr, + struct rte_flow_template_table *table, + uint32_t rule_index, + const struct rte_flow_action actions[], + uint8_t action_template_index, void *user_data, + struct rte_flow_error *error); + +/* clang-format off */ +struct rte_flow *cnxk_flow_async_create_by_index_with_pattern(struct rte_eth_dev *eth_dev, + uint32_t queue, const struct rte_flow_op_attr *attr, + struct rte_flow_template_table *tbl_handle, uint32_t rule_index, + const struct rte_flow_item items[], uint8_t pattern_template_index, + const struct rte_flow_action actions[], + uint8_t action_template_index, void *user_data, + struct rte_flow_error *error); +/* clang-format on */ + +int cnxk_flow_async_actions_update(struct rte_eth_dev *eth_dev, uint32_t queue, + const struct rte_flow_op_attr *attr, struct rte_flow *flow, + const struct rte_flow_action actions[], + uint8_t action_template_index, void *user_data, + struct rte_flow_error *error); #endif /* __CNXK_RTE_FLOW_H__ */ -- 2.43.0

