[PATCH 2/2] drivers: add support for async RTE FLOW APIs
ykanaujia at marvell.com
ykanaujia at marvell.com
Wed Jul 15 10:45:20 CEST 2026
From: Yashaswini Kanaujia <ykanaujia at marvell.com>
added support for asynchronous rte_flow APIs
for cn10k device
Signed-off-by: Yashaswini Kanaujia <ykanaujia at marvell.com>
---
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
More information about the dev
mailing list