From patchwork Fri Jul 2 22:40:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Dumitrescu X-Patchwork-Id: 95235 Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 61BEAA0C3F; Sat, 3 Jul 2021 00:40:12 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 4F42F40696; Sat, 3 Jul 2021 00:40:12 +0200 (CEST) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by mails.dpdk.org (Postfix) with ESMTP id BB40C4013F for ; Sat, 3 Jul 2021 00:40:09 +0200 (CEST) X-IronPort-AV: E=McAfee;i="6200,9189,10033"; a="196075869" X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="196075869" Received: from orsmga008.jf.intel.com ([10.7.209.65]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 02 Jul 2021 15:40:08 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.83,320,1616482800"; d="scan'208";a="456057881" Received: from silpixa00400573.ir.intel.com (HELO silpixa00400573.ger.corp.intel.com) ([10.237.223.107]) by orsmga008.jf.intel.com with ESMTP; 02 Jul 2021 15:40:07 -0700 From: Cristian Dumitrescu To: dev@dpdk.org Date: Fri, 2 Jul 2021 23:40:06 +0100 Message-Id: <20210702224006.66043-1-cristian.dumitrescu@intel.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: <20210702204952.61445-1-cristian.dumitrescu@intel.com> References: <20210702204952.61445-1-cristian.dumitrescu@intel.com> Subject: [dpdk-dev] [PATCH V2 3/5] pipeline: add support for selector tables X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Sender: "dev" Add pipeline-level support for selector tables, Signed-off-by: Cristian Dumitrescu --- lib/pipeline/rte_swx_ctl.c | 700 ++++++++++++++++++++++++- lib/pipeline/rte_swx_ctl.h | 253 +++++++++ lib/pipeline/rte_swx_pipeline.c | 748 ++++++++++++++++++++++++--- lib/pipeline/rte_swx_pipeline.h | 51 ++ lib/pipeline/rte_swx_pipeline_spec.c | 354 ++++++++++++- lib/pipeline/version.map | 13 + 6 files changed, 2034 insertions(+), 85 deletions(-) diff --git a/lib/pipeline/rte_swx_ctl.c b/lib/pipeline/rte_swx_ctl.c index 5d04e750f..8cabce2b9 100644 --- a/lib/pipeline/rte_swx_ctl.c +++ b/lib/pipeline/rte_swx_ctl.c @@ -10,6 +10,8 @@ #include #include +#include + #include "rte_swx_ctl.h" #define CHECK(condition, err_code) \ @@ -89,11 +91,44 @@ struct table { uint32_t n_delete; }; +struct selector { + /* Selector table info. */ + struct rte_swx_ctl_selector_info info; + + /* group_id field. */ + struct rte_swx_ctl_table_match_field_info group_id_field; + + /* selector fields. */ + struct rte_swx_ctl_table_match_field_info *selector_fields; + + /* member_id field. */ + struct rte_swx_ctl_table_match_field_info member_id_field; + + /* Current selector table. Array of info.n_groups_max elements.*/ + struct rte_swx_table_selector_group **groups; + + /* Pending selector table subject to the next commit. Array of info.n_groups_max elements. + */ + struct rte_swx_table_selector_group **pending_groups; + + /* Valid flag per group. Array of n_groups_max elements. */ + int *groups_added; + + /* Pending delete flag per group. Group deletion is subject to the next commit. Array of + * info.n_groups_max elements. + */ + int *groups_pending_delete; + + /* Params. */ + struct rte_swx_table_selector_params params; +}; + struct rte_swx_ctl_pipeline { struct rte_swx_ctl_pipeline_info info; struct rte_swx_pipeline *p; struct action *actions; struct table *tables; + struct selector *selectors; struct rte_swx_table_state *ts; struct rte_swx_table_state *ts_next; int numa_node; @@ -709,6 +744,209 @@ table_free(struct rte_swx_ctl_pipeline *ctl) ctl->tables = NULL; } +static void +selector_group_members_free(struct selector *s, uint32_t group_id) +{ + struct rte_swx_table_selector_group *group = s->groups[group_id]; + + if (!group) + return; + + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&group->members); + if (!m) + break; + + TAILQ_REMOVE(&group->members, m, node); + free(m); + } + + free(group); + s->groups[group_id] = NULL; +} + +static void +selector_pending_group_members_free(struct selector *s, uint32_t group_id) +{ + struct rte_swx_table_selector_group *group = s->pending_groups[group_id]; + + if (!group) + return; + + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&group->members); + if (!m) + break; + + TAILQ_REMOVE(&group->members, m, node); + free(m); + } + + free(group); + s->pending_groups[group_id] = NULL; +} + +static int +selector_group_duplicate_to_pending(struct selector *s, uint32_t group_id) +{ + struct rte_swx_table_selector_group *g, *gp; + struct rte_swx_table_selector_member *m; + + selector_pending_group_members_free(s, group_id); + + g = s->groups[group_id]; + gp = s->pending_groups[group_id]; + + if (!gp) { + gp = calloc(1, sizeof(struct rte_swx_table_selector_group)); + if (!gp) + goto error; + + TAILQ_INIT(&gp->members); + + s->pending_groups[group_id] = gp; + } + + if (!g) + return 0; + + TAILQ_FOREACH(m, &g->members, node) { + struct rte_swx_table_selector_member *mp; + + mp = calloc(1, sizeof(struct rte_swx_table_selector_member)); + if (!mp) + goto error; + + memcpy(mp, m, sizeof(struct rte_swx_table_selector_member)); + + TAILQ_INSERT_TAIL(&gp->members, mp, node); + } + + return 0; + +error: + selector_pending_group_members_free(s, group_id); + return -ENOMEM; +} + +static void +selector_free(struct rte_swx_ctl_pipeline *ctl) +{ + uint32_t i; + + if (ctl->selectors) + return; + + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + uint32_t i; + + /* selector_fields. */ + free(s->selector_fields); + + /* groups. */ + if (s->groups) + for (i = 0; i < s->info.n_groups_max; i++) + selector_group_members_free(s, i); + + free(s->groups); + + /* pending_groups. */ + if (s->pending_groups) + for (i = 0; i < s->info.n_groups_max; i++) + selector_pending_group_members_free(s, i); + + free(s->pending_groups); + + /* groups_added. */ + free(s->groups_added); + + /* groups_pending_delete. */ + free(s->groups_pending_delete); + + /* params. */ + free(s->params.selector_mask); + } + + free(ctl->selectors); + ctl->selectors = NULL; +} + +static struct selector * +selector_find(struct rte_swx_ctl_pipeline *ctl, const char *selector_name) +{ + uint32_t i; + + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + + if (!strcmp(selector_name, s->info.name)) + return s; + } + + return NULL; +} + +static int +selector_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + struct rte_swx_ctl_table_match_field_info *first = NULL, *last = NULL; + uint8_t *selector_mask = NULL; + uint32_t selector_size = 0, selector_offset = 0, i; + + /* Find first (smallest offset) and last (biggest offset) match fields. */ + first = &s->selector_fields[0]; + last = &s->selector_fields[0]; + + for (i = 1; i < s->info.n_selector_fields; i++) { + struct rte_swx_ctl_table_match_field_info *f = &s->selector_fields[i]; + + if (f->offset < first->offset) + first = f; + + if (f->offset > last->offset) + last = f; + } + + /* selector_offset. */ + selector_offset = first->offset / 8; + + /* selector_size. */ + selector_size = (last->offset + last->n_bits - first->offset) / 8; + + /* selector_mask. */ + selector_mask = calloc(1, selector_size); + if (!selector_mask) + return -ENOMEM; + + for (i = 0; i < s->info.n_selector_fields; i++) { + struct rte_swx_ctl_table_match_field_info *f = &s->selector_fields[i]; + uint32_t start; + size_t size; + + start = (f->offset - first->offset) / 8; + size = f->n_bits / 8; + + memset(&selector_mask[start], 0xFF, size); + } + + /* Fill in. */ + s->params.group_id_offset = s->group_id_field.offset / 8; + s->params.selector_size = selector_size; + s->params.selector_offset = selector_offset; + s->params.selector_mask = selector_mask; + s->params.member_id_offset = s->member_id_field.offset / 8; + s->params.n_groups_max = s->info.n_groups_max; + s->params.n_members_per_group_max = s->info.n_members_per_group_max; + + return 0; +} + static void table_state_free(struct rte_swx_ctl_pipeline *ctl) { @@ -730,6 +968,15 @@ table_state_free(struct rte_swx_ctl_pipeline *ctl) table->ops.free(ts->obj); } + /* For each selector table, free its table state. */ + for (i = 0; i < ctl->info.n_selectors; i++) { + struct rte_swx_table_state *ts = &ctl->ts_next[i]; + + /* Table object. */ + if (ts->obj) + rte_swx_table_selector_free(ts->obj); + } + free(ctl->ts_next); ctl->ts_next = NULL; } @@ -740,13 +987,14 @@ table_state_create(struct rte_swx_ctl_pipeline *ctl) int status = 0; uint32_t i; - ctl->ts_next = calloc(ctl->info.n_tables, + ctl->ts_next = calloc(ctl->info.n_tables + ctl->info.n_selectors, sizeof(struct rte_swx_table_state)); if (!ctl->ts_next) { status = -ENOMEM; goto error; } + /* Tables. */ for (i = 0; i < ctl->info.n_tables; i++) { struct table *table = &ctl->tables[i]; struct rte_swx_table_state *ts = &ctl->ts[i]; @@ -782,6 +1030,19 @@ table_state_create(struct rte_swx_ctl_pipeline *ctl) ts_next->default_action_id = ts->default_action_id; } + /* Selector tables. */ + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + i]; + + /* Table object. */ + ts_next->obj = rte_swx_table_selector_create(&s->params, NULL, ctl->numa_node); + if (!ts_next->obj) { + status = -ENODEV; + goto error; + } + } + return 0; error: @@ -799,6 +1060,8 @@ rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl) table_state_free(ctl); + selector_free(ctl); + table_free(ctl); free(ctl); @@ -940,6 +1203,77 @@ rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p) goto error; } + /* selector tables. */ + ctl->selectors = calloc(ctl->info.n_selectors, sizeof(struct selector)); + if (!ctl->selectors) + goto error; + + for (i = 0; i < ctl->info.n_selectors; i++) { + struct selector *s = &ctl->selectors[i]; + uint32_t j; + + /* info. */ + status = rte_swx_ctl_selector_info_get(p, i, &s->info); + if (status) + goto error; + + /* group_id field. */ + status = rte_swx_ctl_selector_group_id_field_info_get(p, + i, + &s->group_id_field); + if (status) + goto error; + + /* selector fields. */ + s->selector_fields = calloc(s->info.n_selector_fields, + sizeof(struct rte_swx_ctl_table_match_field_info)); + if (!s->selector_fields) + goto error; + + for (j = 0; j < s->info.n_selector_fields; j++) { + status = rte_swx_ctl_selector_field_info_get(p, + i, + j, + &s->selector_fields[j]); + if (status) + goto error; + } + + /* member_id field. */ + status = rte_swx_ctl_selector_member_id_field_info_get(p, + i, + &s->member_id_field); + if (status) + goto error; + + /* groups. */ + s->groups = calloc(s->info.n_groups_max, + sizeof(struct rte_swx_table_selector_group *)); + if (!s->groups) + goto error; + + /* pending_groups. */ + s->pending_groups = calloc(s->info.n_groups_max, + sizeof(struct rte_swx_table_selector_group *)); + if (!s->pending_groups) + goto error; + + /* groups_added. */ + s->groups_added = calloc(s->info.n_groups_max, sizeof(int)); + if (!s->groups_added) + goto error; + + /* groups_pending_delete. */ + s->groups_pending_delete = calloc(s->info.n_groups_max, sizeof(int)); + if (!s->groups_pending_delete) + goto error; + + /* params. */ + status = selector_params_get(ctl, i); + if (status) + goto error; + } + /* ts. */ status = rte_swx_pipeline_table_state_get(p, &ctl->ts); if (status) @@ -1499,6 +1833,295 @@ table_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id) table_pending_default_free(table); } +int +rte_swx_ctl_pipeline_selector_group_add(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t *group_id) +{ + struct selector *s; + uint32_t i; + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0] || !group_id) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s) + return -EINVAL; + + /* Find an unused group. */ + for (i = 0; i < s->info.n_groups_max; i++) + if (!s->groups_added[i]) { + *group_id = i; + s->groups_added[i] = 1; + return 0; + } + + return -ENOSPC; +} + +int +rte_swx_ctl_pipeline_selector_group_delete(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id) +{ + struct selector *s; + struct rte_swx_table_selector_group *group; + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s || + (group_id >= s->info.n_groups_max) || + !s->groups_added[group_id]) + return -EINVAL; + + /* Check if this group is already scheduled for deletion. */ + if (s->groups_pending_delete[group_id]) + return 0; + + /* Initialize the pending group, if needed. */ + if (!s->pending_groups[group_id]) { + int status; + + status = selector_group_duplicate_to_pending(s, group_id); + if (status) + return status; + } + + group = s->pending_groups[group_id]; + + /* Schedule removal of all the members from the current group. */ + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&group->members); + if (!m) + break; + + TAILQ_REMOVE(&group->members, m, node); + free(m); + } + + /* Schedule the group for deletion. */ + s->groups_pending_delete[group_id] = 1; + + return 0; +} + +int +rte_swx_ctl_pipeline_selector_group_member_add(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id, + uint32_t member_id, + uint32_t member_weight) +{ + struct selector *s; + struct rte_swx_table_selector_group *group; + struct rte_swx_table_selector_member *m; + + if (!member_weight) + return rte_swx_ctl_pipeline_selector_group_member_delete(ctl, + selector_name, + group_id, + member_id); + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s || + (group_id >= s->info.n_groups_max) || + !s->groups_added[group_id] || + s->groups_pending_delete[group_id]) + return -EINVAL; + + /* Initialize the pending group, if needed. */ + if (!s->pending_groups[group_id]) { + int status; + + status = selector_group_duplicate_to_pending(s, group_id); + if (status) + return status; + } + + group = s->pending_groups[group_id]; + + /* If this member is already in this group, then simply update its weight and return. */ + TAILQ_FOREACH(m, &group->members, node) + if (m->member_id == member_id) { + m->member_weight = member_weight; + return 0; + } + + /* Add new member to this group. */ + m = calloc(1, sizeof(struct rte_swx_table_selector_member)); + if (!m) + return -ENOMEM; + + m->member_id = member_id; + m->member_weight = member_weight; + + TAILQ_INSERT_TAIL(&group->members, m, node); + + return 0; +} + +int +rte_swx_ctl_pipeline_selector_group_member_delete(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id __rte_unused, + uint32_t member_id __rte_unused) +{ + struct selector *s; + struct rte_swx_table_selector_group *group; + struct rte_swx_table_selector_member *m; + + /* Check input arguments. */ + if (!ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s || + (group_id >= s->info.n_groups_max) || + !s->groups_added[group_id] || + s->groups_pending_delete[group_id]) + return -EINVAL; + + /* Initialize the pending group, if needed. */ + if (!s->pending_groups[group_id]) { + int status; + + status = selector_group_duplicate_to_pending(s, group_id); + if (status) + return status; + } + + group = s->pending_groups[group_id]; + + /* Look for this member in the group and remove it, if found. */ + TAILQ_FOREACH(m, &group->members, node) + if (m->member_id == member_id) { + TAILQ_REMOVE(&group->members, m, node); + free(m); + return 0; + } + + return 0; +} + +static int +selector_rollfwd(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + selector_id]; + uint32_t group_id; + + /* Push pending group member changes (s->pending_groups[group_id]) to the selector table + * mirror copy (ts_next->obj). + */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *group = s->pending_groups[group_id]; + int status; + + /* Skip this group if no change needed. */ + if (!group) + continue; + + /* Apply the pending changes for the current group. */ + status = rte_swx_table_selector_group_set(ts_next->obj, group_id, group); + if (status) + return status; + } + + return 0; +} + +static void +selector_rollfwd_finalize(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + uint32_t group_id; + + /* Commit pending group member changes (s->pending_groups[group_id]) to the stable group + * records (s->groups[group_id). + */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *g = s->groups[group_id]; + struct rte_swx_table_selector_group *gp = s->pending_groups[group_id]; + + /* Skip this group if no change needed. */ + if (!gp) + continue; + + /* Transition the pending changes to stable. */ + s->groups[group_id] = gp; + s->pending_groups[group_id] = NULL; + + /* Free the old group member list. */ + if (!g) + continue; + + for ( ; ; ) { + struct rte_swx_table_selector_member *m; + + m = TAILQ_FIRST(&g->members); + if (!m) + break; + + TAILQ_REMOVE(&g->members, m, node); + free(m); + } + + free(g); + } + + /* Commit pending group validity changes (from s->groups_pending_delete[group_id] to + * s->groups_added[group_id]. + */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) + if (s->groups_pending_delete[group_id]) { + s->groups_added[group_id] = 0; + s->groups_pending_delete[group_id] = 0; + } +} + +static void +selector_rollback(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + struct rte_swx_table_state *ts = &ctl->ts[ctl->info.n_tables + selector_id]; + struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables + selector_id]; + uint32_t group_id; + + /* Discard any previous changes to the selector table mirror copy (ts_next->obj). */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *gp = s->pending_groups[group_id]; + + if (gp) { + ts_next->obj = ts->obj; + break; + } + } +} + +static void +selector_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id) +{ + struct selector *s = &ctl->selectors[selector_id]; + uint32_t group_id; + + /* Discard any pending group member changes (s->pending_groups[group_id]). */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) + selector_pending_group_members_free(s, group_id); + + /* Discard any pending group deletions. */ + memset(s->groups_pending_delete, 0, s->info.n_groups_max * sizeof(int)); +} + int rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) { @@ -1508,8 +2131,8 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) CHECK(ctl, EINVAL); - /* Operate the changes on the current ts_next before it becomes the new - * ts. + /* Operate the changes on the current ts_next before it becomes the new ts. First, operate + * all the changes that can fail; if no failure, then operate the changes that cannot fail. */ for (i = 0; i < ctl->info.n_tables; i++) { status = table_rollfwd0(ctl, i, 0); @@ -1517,6 +2140,12 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) goto rollback; } + for (i = 0; i < ctl->info.n_selectors; i++) { + status = selector_rollfwd(ctl, i); + if (status) + goto rollback; + } + for (i = 0; i < ctl->info.n_tables; i++) table_rollfwd1(ctl, i); @@ -1529,7 +2158,10 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) ctl->ts = ctl->ts_next; ctl->ts_next = ts; - /* Operate the changes on the current ts_next, which is the previous ts. + /* Operate the changes on the current ts_next, which is the previous ts, in order to get + * the current ts_next in sync with the current ts. Since the changes that can fail did + * not fail on the previous ts_next, it is guaranteed that they will not fail on the + * current ts_next, hence no error checking is needed. */ for (i = 0; i < ctl->info.n_tables; i++) { table_rollfwd0(ctl, i, 1); @@ -1537,6 +2169,11 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) table_rollfwd2(ctl, i); } + for (i = 0; i < ctl->info.n_selectors; i++) { + selector_rollfwd(ctl, i); + selector_rollfwd_finalize(ctl, i); + } + return 0; rollback: @@ -1546,6 +2183,12 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail) table_abort(ctl, i); } + for (i = 0; i < ctl->info.n_selectors; i++) { + selector_rollback(ctl, i); + if (abort_on_fail) + selector_abort(ctl, i); + } + return status; } @@ -1559,6 +2202,9 @@ rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl) for (i = 0; i < ctl->info.n_tables; i++) table_abort(ctl, i); + + for (i = 0; i < ctl->info.n_selectors; i++) + selector_abort(ctl, i); } static int @@ -1858,3 +2504,49 @@ rte_swx_ctl_pipeline_table_fprintf(FILE *f, n_entries); return 0; } + +int +rte_swx_ctl_pipeline_selector_fprintf(FILE *f, + struct rte_swx_ctl_pipeline *ctl, + const char *selector_name) +{ + struct selector *s; + uint32_t group_id; + + if (!f || !ctl || !selector_name || !selector_name[0]) + return -EINVAL; + + s = selector_find(ctl, selector_name); + if (!s) + return -EINVAL; + + /* Selector. */ + fprintf(f, "# Selector %s: max groups %u, max members per group %u\n", + s->info.name, + s->info.n_groups_max, + s->info.n_members_per_group_max); + + /* Groups. */ + for (group_id = 0; group_id < s->info.n_groups_max; group_id++) { + struct rte_swx_table_selector_group *group = s->groups[group_id]; + struct rte_swx_table_selector_member *m; + uint32_t n_members = 0; + + fprintf(f, "Group %u = [", group_id); + + /* Non-empty group. */ + if (group) + TAILQ_FOREACH(m, &group->members, node) { + fprintf(f, "%u:%u ", m->member_id, m->member_weight); + n_members++; + } + + /* Empty group. */ + if (!n_members) + fprintf(f, "0:1 "); + + fprintf(f, "]\n"); + } + + return 0; +} diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h index dee788be8..f37301cf9 100644 --- a/lib/pipeline/rte_swx_ctl.h +++ b/lib/pipeline/rte_swx_ctl.h @@ -22,6 +22,7 @@ extern "C" { #include "rte_swx_port.h" #include "rte_swx_table.h" +#include "rte_swx_table_selector.h" struct rte_swx_pipeline; @@ -48,6 +49,9 @@ struct rte_swx_ctl_pipeline_info { /** Number of tables. */ uint32_t n_tables; + /** Number of selector tables. */ + uint32_t n_selectors; + /** Number of register arrays. */ uint32_t n_regarrays; @@ -385,6 +389,129 @@ rte_swx_ctl_pipeline_table_stats_read(struct rte_swx_pipeline *p, const char *table_name, struct rte_swx_table_stats *stats); +/* + * Selector Table Query API. + */ + +/** Selector info. */ +struct rte_swx_ctl_selector_info { + /** Selector table name. */ + char name[RTE_SWX_CTL_NAME_SIZE]; + + /** Number of selector fields. */ + uint32_t n_selector_fields; + + /** Maximum number of groups. */ + uint32_t n_groups_max; + + /** Maximum number of members per group. */ + uint32_t n_members_per_group_max; +}; + +/** + * Selector table info get + * + * @param[in] p + * Pipeline handle. + * @param[in] selector_id + * Selector table ID (0 .. *n_selectors* - 1). + * @param[out] selector + * Selector table info. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_selector_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_selector_info *selector); + +/** + * Selector table "group ID" field info get + * + * @param[in] p + * Pipeline handle. + * @param[in] selector_id + * Selector table ID (0 .. *n_selectors*). + * @param[out] field + * Selector table "group ID" field info. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_selector_group_id_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_table_match_field_info *field); + +/** + * Sselector table selector field info get + * + * @param[in] p + * Pipeline handle. + * @param[in] selector_id + * Selector table ID (0 .. *n_selectors*). + * @param[in] selector_field_id + * Selector table selector field ID (0 .. *n_selector_fields* - 1). + * @param[out] field + * Selector table selector field info. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_selector_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + uint32_t selector_field_id, + struct rte_swx_ctl_table_match_field_info *field); + +/** + * Selector table "member ID" field info get + * + * @param[in] p + * Pipeline handle. + * @param[in] selector_id + * Selector table ID (0 .. *n_selectors*). + * @param[out] field + * Selector table "member ID" field info. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_selector_member_id_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_table_match_field_info *field); + +/** Selector table statistics. */ +struct rte_swx_pipeline_selector_stats { + /** Number of packets. */ + uint64_t n_pkts; +}; + +/** + * Selector table statistics counters read + * + * @param[in] p + * Pipeline handle. + * @param[in] selector_name + * Selector table name. + * @param[out] stats + * Selector table stats. Must point to a pre-allocated structure. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_stats_read(struct rte_swx_pipeline *p, + const char *selector_name, + struct rte_swx_pipeline_selector_stats *stats); + /* * Table Update API. */ @@ -529,6 +656,111 @@ rte_swx_ctl_pipeline_table_entry_delete(struct rte_swx_ctl_pipeline *ctl, const char *table_name, struct rte_swx_table_entry *entry); +/** + * Pipeline selector table group add + * + * Add a new group to a selector table. This operation is executed before this + * function returns and its result is independent of the result of the next + * commit operation. + * + * @param[in] ctl + * Pipeline control handle. + * @param[in] selector_name + * Selector table name. + * @param[out] group_id + * The ID of the new group. Only valid when the function call is successful. + * This group is initially empty, i.e. it does not contain any members. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument; + * -ENOSPC: All groups are currently in use, no group available. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_group_add(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t *group_id); + +/** + * Pipeline selector table group delete + * + * Schedule a group for deletion as part of the next commit operation. The group + * to be deleted can be empty or non-empty. + * + * @param[in] ctl + * Pipeline control handle. + * @param[in] selector_name + * Selector table name. + * @param[in] group_id + * Group to be deleted from the selector table. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument; + * -ENOMEM: Not enough memory. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_group_delete(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id); + +/** + * Pipeline selector table member add to group + * + * Schedule the operation to add a new member to an existing group as part of + * the next commit operation. If this member is already in this group, the + * member weight is updated to the new value. A weight of zero means this member + * is to be deleted from the group. + * + * @param[in] ctl + * Pipeline control handle. + * @param[in] selector_name + * Selector table name. + * @param[in] group_id + * The group ID. + * @param[in] member_id + * The member to be added to the group. + * @param[in] member_weight + * Member weight. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument; + * -ENOMEM: Not enough memory; + * -ENOSPC: The group is full. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_group_member_add(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id, + uint32_t member_id, + uint32_t member_weight); + +/** + * Pipeline selector table member delete from group + * + * Schedule the operation to delete a member from an existing group as part of + * the next commit operation. + * + * @param[in] ctl + * Pipeline control handle. + * @param[in] selector_name + * Selector table name. + * @param[in] group_id + * The group ID. Must be valid. + * @param[in] member_id + * The member to be added to the group. Must be valid. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_group_member_delete(struct rte_swx_ctl_pipeline *ctl, + const char *selector_name, + uint32_t group_id, + uint32_t member_id); + /** * Pipeline commit * @@ -608,6 +840,27 @@ rte_swx_ctl_pipeline_table_fprintf(FILE *f, struct rte_swx_ctl_pipeline *ctl, const char *table_name); +/** + * Pipeline selector print to file + * + * Print all the selector entries to file. + * + * @param[in] f + * Output file. + * @param[in] ctl + * Pipeline control handle. + * @param[in] selector_name + * Selector table name. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument. + */ +__rte_experimental +int +rte_swx_ctl_pipeline_selector_fprintf(FILE *f, + struct rte_swx_ctl_pipeline *ctl, + const char *selector_name); + /* * Register Array Query API. */ diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c index a2732a1e5..22c860f28 100644 --- a/lib/pipeline/rte_swx_pipeline.c +++ b/lib/pipeline/rte_swx_pipeline.c @@ -15,6 +15,8 @@ #include #include +#include + #include "rte_swx_pipeline.h" #include "rte_swx_ctl.h" @@ -498,6 +500,7 @@ enum instruction_type { /* table TABLE */ INSTR_TABLE, + INSTR_SELECTOR, /* extern e.obj.func */ INSTR_EXTERN_OBJ, @@ -794,6 +797,38 @@ struct table_statistics { uint64_t *n_pkts_action; }; +/* + * Selector. + */ +struct selector { + TAILQ_ENTRY(selector) node; + char name[RTE_SWX_NAME_SIZE]; + + struct field *group_id_field; + struct field **selector_fields; + uint32_t n_selector_fields; + struct header *selector_header; + struct field *member_id_field; + + uint32_t n_groups_max; + uint32_t n_members_per_group_max; + + uint32_t id; +}; + +TAILQ_HEAD(selector_tailq, selector); + +struct selector_runtime { + void *mailbox; + uint8_t **group_id_buffer; + uint8_t **selector_buffer; + uint8_t **member_id_buffer; +}; + +struct selector_statistics { + uint64_t n_pkts; +}; + /* * Register array. */ @@ -873,6 +908,7 @@ struct thread { /* Tables. */ struct table_runtime *tables; + struct selector_runtime *selectors; struct rte_swx_table_state *table_state; uint64_t action_id; int hit; /* 0 = Miss, 1 = Hit. */ @@ -1308,6 +1344,7 @@ struct rte_swx_pipeline { struct action_tailq actions; struct table_type_tailq table_types; struct table_tailq tables; + struct selector_tailq selectors; struct regarray_tailq regarrays; struct meter_profile_tailq meter_profiles; struct metarray_tailq metarrays; @@ -1317,6 +1354,7 @@ struct rte_swx_pipeline { struct instruction **action_instructions; struct rte_swx_table_state *table_state; struct table_statistics *table_stats; + struct selector_statistics *selector_stats; struct regarray_runtime *regarray_runtime; struct metarray_runtime *metarray_runtime; struct instruction *instructions; @@ -1329,6 +1367,7 @@ struct rte_swx_pipeline { uint32_t n_extern_funcs; uint32_t n_actions; uint32_t n_tables; + uint32_t n_selectors; uint32_t n_regarrays; uint32_t n_metarrays; uint32_t n_headers; @@ -3450,6 +3489,9 @@ instr_hdr_invalidate_exec(struct rte_swx_pipeline *p) static struct table * table_find(struct rte_swx_pipeline *p, const char *name); +static struct selector * +selector_find(struct rte_swx_pipeline *p, const char *name); + static int instr_table_translate(struct rte_swx_pipeline *p, struct action *action, @@ -3459,16 +3501,26 @@ instr_table_translate(struct rte_swx_pipeline *p, struct instruction_data *data __rte_unused) { struct table *t; + struct selector *s; CHECK(!action, EINVAL); CHECK(n_tokens == 2, EINVAL); t = table_find(p, tokens[1]); - CHECK(t, EINVAL); + if (t) { + instr->type = INSTR_TABLE; + instr->table.table_id = t->id; + return 0; + } - instr->type = INSTR_TABLE; - instr->table.table_id = t->id; - return 0; + s = selector_find(p, tokens[1]); + if (s) { + instr->type = INSTR_SELECTOR; + instr->table.table_id = s->id; + return 0; + } + + CHECK(0, EINVAL); } static inline void @@ -3522,6 +3574,45 @@ instr_table_exec(struct rte_swx_pipeline *p) thread_ip_action_call(p, t, action_id); } +static inline void +instr_selector_exec(struct rte_swx_pipeline *p) +{ + struct thread *t = &p->threads[p->thread_id]; + struct instruction *ip = t->ip; + uint32_t selector_id = ip->table.table_id; + struct rte_swx_table_state *ts = &t->table_state[p->n_tables + selector_id]; + struct selector_runtime *selector = &t->selectors[selector_id]; + struct selector_statistics *stats = &p->selector_stats[selector_id]; + uint64_t n_pkts = stats->n_pkts; + int done; + + /* Table. */ + done = rte_swx_table_selector_select(ts->obj, + selector->mailbox, + selector->group_id_buffer, + selector->selector_buffer, + selector->member_id_buffer); + if (!done) { + /* Thread. */ + TRACE("[Thread %2u] selector %u (not finalized)\n", + p->thread_id, + selector_id); + + thread_yield(p); + return; + } + + + TRACE("[Thread %2u] selector %u\n", + p->thread_id, + selector_id); + + stats->n_pkts = n_pkts + 1; + + /* Thread. */ + thread_ip_inc(p); +} + /* * extern. */ @@ -8787,6 +8878,7 @@ static instr_exec_t instruction_table[] = { [INSTR_METER_IMI] = instr_meter_imi_exec, [INSTR_TABLE] = instr_table_exec, + [INSTR_SELECTOR] = instr_selector_exec, [INSTR_EXTERN_OBJ] = instr_extern_obj_exec, [INSTR_EXTERN_FUNC] = instr_extern_func_exec, @@ -9253,6 +9345,7 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p, CHECK_NAME(name, EINVAL); CHECK(!table_find(p, name), EEXIST); + CHECK(!selector_find(p, name), EEXIST); CHECK(params, EINVAL); @@ -9455,82 +9548,6 @@ table_params_free(struct rte_swx_table_params *params) free(params); } -static int -table_state_build(struct rte_swx_pipeline *p) -{ - struct table *table; - - p->table_state = calloc(p->n_tables, - sizeof(struct rte_swx_table_state)); - CHECK(p->table_state, ENOMEM); - - TAILQ_FOREACH(table, &p->tables, node) { - struct rte_swx_table_state *ts = &p->table_state[table->id]; - - if (table->type) { - struct rte_swx_table_params *params; - - /* ts->obj. */ - params = table_params_get(table); - CHECK(params, ENOMEM); - - ts->obj = table->type->ops.create(params, - NULL, - table->args, - p->numa_node); - - table_params_free(params); - CHECK(ts->obj, ENODEV); - } - - /* ts->default_action_data. */ - if (table->action_data_size_max) { - ts->default_action_data = - malloc(table->action_data_size_max); - CHECK(ts->default_action_data, ENOMEM); - - memcpy(ts->default_action_data, - table->default_action_data, - table->action_data_size_max); - } - - /* ts->default_action_id. */ - ts->default_action_id = table->default_action->id; - } - - return 0; -} - -static void -table_state_build_free(struct rte_swx_pipeline *p) -{ - uint32_t i; - - if (!p->table_state) - return; - - for (i = 0; i < p->n_tables; i++) { - struct rte_swx_table_state *ts = &p->table_state[i]; - struct table *table = table_find_by_id(p, i); - - /* ts->obj. */ - if (table->type && ts->obj) - table->type->ops.free(ts->obj); - - /* ts->default_action_data. */ - free(ts->default_action_data); - } - - free(p->table_state); - p->table_state = NULL; -} - -static void -table_state_free(struct rte_swx_pipeline *p) -{ - table_state_build_free(p); -} - static int table_stub_lkp(void *table __rte_unused, void *mailbox __rte_unused, @@ -9658,6 +9675,458 @@ table_free(struct rte_swx_pipeline *p) } } +/* + * Selector. + */ +static struct selector * +selector_find(struct rte_swx_pipeline *p, const char *name) +{ + struct selector *s; + + TAILQ_FOREACH(s, &p->selectors, node) + if (strcmp(s->name, name) == 0) + return s; + + return NULL; +} + +static struct selector * +selector_find_by_id(struct rte_swx_pipeline *p, uint32_t id) +{ + struct selector *s = NULL; + + TAILQ_FOREACH(s, &p->selectors, node) + if (s->id == id) + return s; + + return NULL; +} + +static int +selector_fields_check(struct rte_swx_pipeline *p, + struct rte_swx_pipeline_selector_params *params, + struct header **header) +{ + struct header *h0 = NULL; + struct field *hf, *mf; + uint32_t i; + + /* Return if no selector fields. */ + if (!params->n_selector_fields || !params->selector_field_names) + return -EINVAL; + + /* Check that all the selector fields either belong to the same header + * or are all meta-data fields. + */ + hf = header_field_parse(p, params->selector_field_names[0], &h0); + mf = metadata_field_parse(p, params->selector_field_names[0]); + if (!hf && !mf) + return -EINVAL; + + for (i = 1; i < params->n_selector_fields; i++) + if (h0) { + struct header *h; + + hf = header_field_parse(p, params->selector_field_names[i], &h); + if (!hf || (h->id != h0->id)) + return -EINVAL; + } else { + mf = metadata_field_parse(p, params->selector_field_names[i]); + if (!mf) + return -EINVAL; + } + + /* Check that there are no duplicated match fields. */ + for (i = 0; i < params->n_selector_fields; i++) { + const char *field_name = params->selector_field_names[i]; + uint32_t j; + + for (j = i + 1; j < params->n_selector_fields; j++) + if (!strcmp(params->selector_field_names[j], field_name)) + return -EINVAL; + } + + /* Return. */ + if (header) + *header = h0; + + return 0; +} + +int +rte_swx_pipeline_selector_config(struct rte_swx_pipeline *p, + const char *name, + struct rte_swx_pipeline_selector_params *params) +{ + struct selector *s; + struct header *selector_header = NULL; + struct field *group_id_field, *member_id_field; + uint32_t i; + int status = 0; + + CHECK(p, EINVAL); + + CHECK_NAME(name, EINVAL); + CHECK(!table_find(p, name), EEXIST); + CHECK(!selector_find(p, name), EEXIST); + + CHECK(params, EINVAL); + + CHECK_NAME(params->group_id_field_name, EINVAL); + group_id_field = metadata_field_parse(p, params->group_id_field_name); + CHECK(group_id_field, EINVAL); + + for (i = 0; i < params->n_selector_fields; i++) { + const char *field_name = params->selector_field_names[i]; + + CHECK_NAME(field_name, EINVAL); + } + status = selector_fields_check(p, params, &selector_header); + if (status) + return status; + + CHECK_NAME(params->member_id_field_name, EINVAL); + member_id_field = metadata_field_parse(p, params->member_id_field_name); + CHECK(member_id_field, EINVAL); + + CHECK(params->n_groups_max, EINVAL); + + CHECK(params->n_members_per_group_max, EINVAL); + + /* Memory allocation. */ + s = calloc(1, sizeof(struct selector)); + if (!s) { + status = -ENOMEM; + goto error; + } + + s->selector_fields = calloc(params->n_selector_fields, sizeof(struct field *)); + if (!s->selector_fields) { + status = -ENOMEM; + goto error; + } + + /* Node initialization. */ + strcpy(s->name, name); + + s->group_id_field = group_id_field; + + for (i = 0; i < params->n_selector_fields; i++) { + const char *field_name = params->selector_field_names[i]; + + s->selector_fields[i] = selector_header ? + header_field_parse(p, field_name, NULL) : + metadata_field_parse(p, field_name); + } + + s->n_selector_fields = params->n_selector_fields; + + s->selector_header = selector_header; + + s->member_id_field = member_id_field; + + s->n_groups_max = params->n_groups_max; + + s->n_members_per_group_max = params->n_members_per_group_max; + + s->id = p->n_selectors; + + /* Node add to tailq. */ + TAILQ_INSERT_TAIL(&p->selectors, s, node); + p->n_selectors++; + + return 0; + +error: + if (!s) + return status; + + free(s->selector_fields); + + free(s); + + return status; +} + +static void +selector_params_free(struct rte_swx_table_selector_params *params) +{ + if (!params) + return; + + free(params->selector_mask); + + free(params); +} + +static struct rte_swx_table_selector_params * +selector_table_params_get(struct selector *s) +{ + struct rte_swx_table_selector_params *params = NULL; + struct field *first, *last; + uint32_t i; + + /* Memory allocation. */ + params = calloc(1, sizeof(struct rte_swx_pipeline_selector_params)); + if (!params) + goto error; + + /* Group ID. */ + params->group_id_offset = s->group_id_field->offset / 8; + + /* Find first (smallest offset) and last (biggest offset) selector fields. */ + first = s->selector_fields[0]; + last = s->selector_fields[0]; + + for (i = 0; i < s->n_selector_fields; i++) { + struct field *f = s->selector_fields[i]; + + if (f->offset < first->offset) + first = f; + + if (f->offset > last->offset) + last = f; + } + + /* Selector offset and size. */ + params->selector_offset = first->offset / 8; + params->selector_size = (last->offset + last->n_bits - first->offset) / 8; + + /* Memory allocation. */ + params->selector_mask = calloc(1, params->selector_size); + if (!params->selector_mask) + goto error; + + /* Selector mask. */ + for (i = 0; i < s->n_selector_fields; i++) { + struct field *f = s->selector_fields[i]; + uint32_t start = (f->offset - first->offset) / 8; + size_t size = f->n_bits / 8; + + memset(¶ms->selector_mask[start], 0xFF, size); + } + + /* Member ID. */ + params->member_id_offset = s->member_id_field->offset / 8; + + /* Maximum number of groups. */ + params->n_groups_max = s->n_groups_max; + + /* Maximum number of members per group. */ + params->n_members_per_group_max = s->n_members_per_group_max; + + return params; + +error: + selector_params_free(params); + return NULL; +} + +static void +selector_build_free(struct rte_swx_pipeline *p) +{ + uint32_t i; + + for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) { + struct thread *t = &p->threads[i]; + uint32_t j; + + if (!t->selectors) + continue; + + for (j = 0; j < p->n_selectors; j++) { + struct selector_runtime *r = &t->selectors[j]; + + free(r->mailbox); + } + + free(t->selectors); + t->selectors = NULL; + } + + free(p->selector_stats); + p->selector_stats = NULL; +} + +static int +selector_build(struct rte_swx_pipeline *p) +{ + uint32_t i; + int status = 0; + + /* Per pipeline: selector statistics. */ + p->selector_stats = calloc(p->n_selectors, sizeof(struct selector_statistics)); + if (!p->selector_stats) { + status = -ENOMEM; + goto error; + } + + /* Per thread: selector run-time. */ + for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) { + struct thread *t = &p->threads[i]; + struct selector *s; + + t->selectors = calloc(p->n_selectors, sizeof(struct selector_runtime)); + if (!t->selectors) { + status = -ENOMEM; + goto error; + } + + TAILQ_FOREACH(s, &p->selectors, node) { + struct selector_runtime *r = &t->selectors[s->id]; + uint64_t size; + + /* r->mailbox. */ + size = rte_swx_table_selector_mailbox_size_get(); + if (size) { + r->mailbox = calloc(1, size); + if (!r->mailbox) { + status = -ENOMEM; + goto error; + } + } + + /* r->group_id_buffer. */ + r->group_id_buffer = &t->structs[p->metadata_struct_id]; + + /* r->selector_buffer. */ + r->selector_buffer = s->selector_header ? + &t->structs[s->selector_header->struct_id] : + &t->structs[p->metadata_struct_id]; + + /* r->member_id_buffer. */ + r->member_id_buffer = &t->structs[p->metadata_struct_id]; + } + } + + return 0; + +error: + selector_build_free(p); + return status; +} + +static void +selector_free(struct rte_swx_pipeline *p) +{ + selector_build_free(p); + + /* Selector tables. */ + for ( ; ; ) { + struct selector *elem; + + elem = TAILQ_FIRST(&p->selectors); + if (!elem) + break; + + TAILQ_REMOVE(&p->selectors, elem, node); + free(elem->selector_fields); + free(elem); + } +} + +/* + * Table state. + */ +static int +table_state_build(struct rte_swx_pipeline *p) +{ + struct table *table; + struct selector *s; + + p->table_state = calloc(p->n_tables + p->n_selectors, + sizeof(struct rte_swx_table_state)); + CHECK(p->table_state, ENOMEM); + + TAILQ_FOREACH(table, &p->tables, node) { + struct rte_swx_table_state *ts = &p->table_state[table->id]; + + if (table->type) { + struct rte_swx_table_params *params; + + /* ts->obj. */ + params = table_params_get(table); + CHECK(params, ENOMEM); + + ts->obj = table->type->ops.create(params, + NULL, + table->args, + p->numa_node); + + table_params_free(params); + CHECK(ts->obj, ENODEV); + } + + /* ts->default_action_data. */ + if (table->action_data_size_max) { + ts->default_action_data = + malloc(table->action_data_size_max); + CHECK(ts->default_action_data, ENOMEM); + + memcpy(ts->default_action_data, + table->default_action_data, + table->action_data_size_max); + } + + /* ts->default_action_id. */ + ts->default_action_id = table->default_action->id; + } + + TAILQ_FOREACH(s, &p->selectors, node) { + struct rte_swx_table_state *ts = &p->table_state[p->n_tables + s->id]; + struct rte_swx_table_selector_params *params; + + /* ts->obj. */ + params = selector_table_params_get(s); + CHECK(params, ENOMEM); + + ts->obj = rte_swx_table_selector_create(params, NULL, p->numa_node); + + selector_params_free(params); + CHECK(ts->obj, ENODEV); + } + + return 0; +} + +static void +table_state_build_free(struct rte_swx_pipeline *p) +{ + uint32_t i; + + if (!p->table_state) + return; + + for (i = 0; i < p->n_tables; i++) { + struct rte_swx_table_state *ts = &p->table_state[i]; + struct table *table = table_find_by_id(p, i); + + /* ts->obj. */ + if (table->type && ts->obj) + table->type->ops.free(ts->obj); + + /* ts->default_action_data. */ + free(ts->default_action_data); + } + + for (i = 0; i < p->n_selectors; i++) { + struct rte_swx_table_state *ts = &p->table_state[p->n_tables + i]; + + /* ts->obj. */ + if (ts->obj) + rte_swx_table_selector_free(ts->obj); + } + + free(p->table_state); + p->table_state = NULL; +} + +static void +table_state_free(struct rte_swx_pipeline *p) +{ + table_state_build_free(p); +} + /* * Register array. */ @@ -9988,6 +10457,7 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node) TAILQ_INIT(&pipeline->actions); TAILQ_INIT(&pipeline->table_types); TAILQ_INIT(&pipeline->tables); + TAILQ_INIT(&pipeline->selectors); TAILQ_INIT(&pipeline->regarrays); TAILQ_INIT(&pipeline->meter_profiles); TAILQ_INIT(&pipeline->metarrays); @@ -10010,6 +10480,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p) metarray_free(p); regarray_free(p); table_state_free(p); + selector_free(p); table_free(p); action_free(p); metadata_free(p); @@ -10089,6 +10560,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p) if (status) goto error; + status = selector_build(p); + if (status) + goto error; + status = table_state_build(p); if (status) goto error; @@ -10108,6 +10583,7 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p) metarray_build_free(p); regarray_build_free(p); table_state_build_free(p); + selector_build_free(p); table_build_free(p); action_build_free(p); metadata_build_free(p); @@ -10167,6 +10643,7 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p, pipeline->n_ports_out = p->n_ports_out; pipeline->n_actions = n_actions; pipeline->n_tables = n_tables; + pipeline->n_selectors = p->n_selectors; pipeline->n_regarrays = p->n_regarrays; pipeline->n_metarrays = p->n_metarrays; @@ -10320,6 +10797,98 @@ rte_swx_ctl_table_ops_get(struct rte_swx_pipeline *p, return 0; } +int +rte_swx_ctl_selector_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_selector_info *selector) +{ + struct selector *s = NULL; + + if (!p || !selector) + return -EINVAL; + + s = selector_find_by_id(p, selector_id); + if (!s) + return -EINVAL; + + strcpy(selector->name, s->name); + + selector->n_selector_fields = s->n_selector_fields; + selector->n_groups_max = s->n_groups_max; + selector->n_members_per_group_max = s->n_members_per_group_max; + + return 0; +} + +int +rte_swx_ctl_selector_group_id_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_table_match_field_info *field) +{ + struct selector *s; + + if (!p || (selector_id >= p->n_selectors) || !field) + return -EINVAL; + + s = selector_find_by_id(p, selector_id); + if (!s) + return -EINVAL; + + field->match_type = RTE_SWX_TABLE_MATCH_EXACT; + field->is_header = 0; + field->n_bits = s->group_id_field->n_bits; + field->offset = s->group_id_field->offset; + + return 0; +} + +int +rte_swx_ctl_selector_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + uint32_t selector_field_id, + struct rte_swx_ctl_table_match_field_info *field) +{ + struct selector *s; + struct field *f; + + if (!p || (selector_id >= p->n_selectors) || !field) + return -EINVAL; + + s = selector_find_by_id(p, selector_id); + if (!s || (selector_field_id >= s->n_selector_fields)) + return -EINVAL; + + f = s->selector_fields[selector_field_id]; + field->match_type = RTE_SWX_TABLE_MATCH_EXACT; + field->is_header = s->selector_header ? 1 : 0; + field->n_bits = f->n_bits; + field->offset = f->offset; + + return 0; +} + +int +rte_swx_ctl_selector_member_id_field_info_get(struct rte_swx_pipeline *p, + uint32_t selector_id, + struct rte_swx_ctl_table_match_field_info *field) +{ + struct selector *s; + + if (!p || (selector_id >= p->n_selectors) || !field) + return -EINVAL; + + s = selector_find_by_id(p, selector_id); + if (!s) + return -EINVAL; + + field->match_type = RTE_SWX_TABLE_MATCH_EXACT; + field->is_header = 0; + field->n_bits = s->member_id_field->n_bits; + field->offset = s->member_id_field->offset; + + return 0; +} + int rte_swx_pipeline_table_state_get(struct rte_swx_pipeline *p, struct rte_swx_table_state **table_state) @@ -10405,6 +10974,25 @@ rte_swx_ctl_pipeline_table_stats_read(struct rte_swx_pipeline *p, return 0; } +int +rte_swx_ctl_pipeline_selector_stats_read(struct rte_swx_pipeline *p, + const char *selector_name, + struct rte_swx_pipeline_selector_stats *stats) +{ + struct selector *s; + + if (!p || !selector_name || !selector_name[0] || !stats) + return -EINVAL; + + s = selector_find(p, selector_name); + if (!s) + return -EINVAL; + + stats->n_pkts = p->selector_stats[s->id].n_pkts; + + return 0; +} + int rte_swx_ctl_regarray_info_get(struct rte_swx_pipeline *p, uint32_t regarray_id, diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h index feeb10a5c..cd395ac39 100644 --- a/lib/pipeline/rte_swx_pipeline.h +++ b/lib/pipeline/rte_swx_pipeline.h @@ -612,6 +612,57 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p, const char *args, uint32_t size); +/** Pipeline selector table parameters. */ +struct rte_swx_pipeline_selector_params { + /** The group ID field. Input into the selection operation. + * Restriction: This field must be a meta-data field. + */ + const char *group_id_field_name; + + /** The set of fields used to select (through a hashing scheme) the + * member within the current group. Inputs into the seletion operation. + * Restriction: All the selector fields must be part of the same struct, + * i.e. part of the same header or part of the meta-data structure. + */ + const char **selector_field_names; + + /** The number of selector fields. Must be non-zero. */ + uint32_t n_selector_fields; + + /** The member ID field. Output from the selection operation. + * Restriction: This field must be a meta-data field. + */ + const char *member_id_field_name; + + /** Maximum number of groups. Must be non-zero. */ + uint32_t n_groups_max; + + /** Maximum number of members per group. Must be non-zero. */ + uint32_t n_members_per_group_max; +}; + +/** + * Pipeline selector table configure + * + * @param[out] p + * Pipeline handle. + * @param[in] name + * Selector table name. + * @param[in] params + * Selector table parameters. + * @return + * 0 on success or the following error codes otherwise: + * -EINVAL: Invalid argument; + * -ENOMEM: Not enough space/cannot allocate memory; + * -EEXIST: Selector table with this name already exists; + * -ENODEV: Selector table creation error. + */ +__rte_experimental +int +rte_swx_pipeline_selector_config(struct rte_swx_pipeline *p, + const char *name, + struct rte_swx_pipeline_selector_params *params); + /** * Pipeline register array configure * diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c index 2e867d7bf..6980b0390 100644 --- a/lib/pipeline/rte_swx_pipeline_spec.c +++ b/lib/pipeline/rte_swx_pipeline_spec.c @@ -18,7 +18,9 @@ #define TABLE_BLOCK 2 #define TABLE_KEY_BLOCK 3 #define TABLE_ACTIONS_BLOCK 4 -#define APPLY_BLOCK 5 +#define SELECTOR_BLOCK 5 +#define SELECTOR_SELECTOR_BLOCK 6 +#define APPLY_BLOCK 7 /* * extobj. @@ -940,6 +942,307 @@ table_block_parse(struct table_spec *s, return -EINVAL; } +/* + * selector. + * + * selector SELECTOR_NAME { + * group_id FIELD_NAME + * selector { + * FIELD_NAME + * ... + * } + * member_id FIELD_NAME + * n_groups N_GROUPS + * n_members_per_group N_MEMBERS_PER_GROUP + * } + */ +struct selector_spec { + char *name; + struct rte_swx_pipeline_selector_params params; +}; + +static void +selector_spec_free(struct selector_spec *s) +{ + uintptr_t field_name; + uint32_t i; + + if (!s) + return; + + /* name. */ + free(s->name); + s->name = NULL; + + /* params->group_id_field_name. */ + field_name = (uintptr_t)s->params.group_id_field_name; + free((void *)field_name); + s->params.group_id_field_name = NULL; + + /* params->selector_field_names. */ + for (i = 0; i < s->params.n_selector_fields; i++) { + field_name = (uintptr_t)s->params.selector_field_names[i]; + + free((void *)field_name); + } + + free(s->params.selector_field_names); + s->params.selector_field_names = NULL; + + s->params.n_selector_fields = 0; + + /* params->member_id_field_name. */ + field_name = (uintptr_t)s->params.member_id_field_name; + free((void *)field_name); + s->params.member_id_field_name = NULL; + + /* params->n_groups_max. */ + s->params.n_groups_max = 0; + + /* params->n_members_per_group_max. */ + s->params.n_members_per_group_max = 0; +} + +static int +selector_statement_parse(struct selector_spec *s, + uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + /* Check format. */ + if ((n_tokens != 3) || strcmp(tokens[2], "{")) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid selector statement."; + return -EINVAL; + } + + /* spec. */ + s->name = strdup(tokens[1]); + if (!s->name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + /* block_mask. */ + *block_mask |= 1 << SELECTOR_BLOCK; + + return 0; +} + +static int +selector_selector_statement_parse(uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + /* Check format. */ + if ((n_tokens != 2) || strcmp(tokens[1], "{")) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid selector statement."; + return -EINVAL; + } + + /* block_mask. */ + *block_mask |= 1 << SELECTOR_SELECTOR_BLOCK; + + return 0; +} + +static int +selector_selector_block_parse(struct selector_spec *s, + uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + const char **new_fields; + char *name; + + /* Handle end of block. */ + if ((n_tokens == 1) && !strcmp(tokens[0], "}")) { + *block_mask &= ~(1 << SELECTOR_SELECTOR_BLOCK); + return 0; + } + + /* Check input arguments. */ + if (n_tokens != 1) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid selector field statement."; + return -EINVAL; + } + + name = strdup(tokens[0]); + if (!name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + new_fields = realloc(s->params.selector_field_names, + (s->params.n_selector_fields + 1) * sizeof(char *)); + if (!new_fields) { + free(name); + + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + s->params.selector_field_names = new_fields; + s->params.selector_field_names[s->params.n_selector_fields] = name; + s->params.n_selector_fields++; + + return 0; +} + +static int +selector_block_parse(struct selector_spec *s, + uint32_t *block_mask, + char **tokens, + uint32_t n_tokens, + uint32_t n_lines, + uint32_t *err_line, + const char **err_msg) +{ + if (*block_mask & (1 << SELECTOR_SELECTOR_BLOCK)) + return selector_selector_block_parse(s, + block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + + /* Handle end of block. */ + if ((n_tokens == 1) && !strcmp(tokens[0], "}")) { + *block_mask &= ~(1 << SELECTOR_BLOCK); + return 0; + } + + if (!strcmp(tokens[0], "group_id")) { + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid group_id statement."; + return -EINVAL; + } + + s->params.group_id_field_name = strdup(tokens[1]); + if (!s->params.group_id_field_name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + return 0; + } + + if (!strcmp(tokens[0], "selector")) + return selector_selector_statement_parse(block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + + if (!strcmp(tokens[0], "member_id")) { + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid member_id statement."; + return -EINVAL; + } + + s->params.member_id_field_name = strdup(tokens[1]); + if (!s->params.member_id_field_name) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Memory allocation failed."; + return -ENOMEM; + } + + return 0; + } + + if (!strcmp(tokens[0], "n_groups_max")) { + char *p = tokens[1]; + + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid n_groups statement."; + return -EINVAL; + } + + s->params.n_groups_max = strtoul(p, &p, 0); + if (p[0]) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid n_groups argument."; + return -EINVAL; + } + + return 0; + } + + if (!strcmp(tokens[0], "n_members_per_group_max")) { + char *p = tokens[1]; + + if (n_tokens != 2) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid n_members_per_group statement."; + return -EINVAL; + } + + s->params.n_members_per_group_max = strtoul(p, &p, 0); + if (p[0]) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid n_members_per_group argument."; + return -EINVAL; + } + + return 0; + } + + /* Anything else. */ + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Invalid statement."; + return -EINVAL; +} + /* * regarray. * @@ -1203,6 +1506,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p, struct metadata_spec metadata_spec = {0}; struct action_spec action_spec = {0}; struct table_spec table_spec = {0}; + struct selector_spec selector_spec = {0}; struct regarray_spec regarray_spec = {0}; struct metarray_spec metarray_spec = {0}; struct apply_spec apply_spec = {0}; @@ -1386,6 +1690,38 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p, continue; } + /* selector block. */ + if (block_mask & (1 << SELECTOR_BLOCK)) { + status = selector_block_parse(&selector_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + if (block_mask & (1 << SELECTOR_BLOCK)) + continue; + + /* End of block. */ + status = rte_swx_pipeline_selector_config(p, + selector_spec.name, + &selector_spec.params); + if (status) { + if (err_line) + *err_line = n_lines; + if (err_msg) + *err_msg = "Selector configuration error."; + goto error; + } + + selector_spec_free(&selector_spec); + + continue; + } + /* apply block. */ if (block_mask & (1 << APPLY_BLOCK)) { status = apply_block_parse(&apply_spec, @@ -1544,6 +1880,21 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p, continue; } + /* selector. */ + if (!strcmp(tokens[0], "selector")) { + status = selector_statement_parse(&selector_spec, + &block_mask, + tokens, + n_tokens, + n_lines, + err_line, + err_msg); + if (status) + goto error; + + continue; + } + /* regarray. */ if (!strcmp(tokens[0], "regarray")) { status = regarray_statement_parse(®array_spec, @@ -1651,6 +2002,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p, metadata_spec_free(&metadata_spec); action_spec_free(&action_spec); table_spec_free(&table_spec); + selector_spec_free(&selector_spec); regarray_spec_free(®array_spec); metarray_spec_free(&metarray_spec); apply_spec_free(&apply_spec); diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map index a4d7d9788..ff0974c2e 100644 --- a/lib/pipeline/version.map +++ b/lib/pipeline/version.map @@ -116,4 +116,17 @@ EXPERIMENTAL { rte_swx_ctl_regarray_info_get; rte_swx_pipeline_metarray_config; rte_swx_pipeline_regarray_config; + + #added in 21.08 + rte_swx_pipeline_selector_config; + rte_swx_ctl_pipeline_selector_fprintf; + rte_swx_ctl_pipeline_selector_group_add; + rte_swx_ctl_pipeline_selector_group_delete; + rte_swx_ctl_pipeline_selector_group_member_add; + rte_swx_ctl_pipeline_selector_group_member_delete; + rte_swx_ctl_pipeline_selector_stats_read; + rte_swx_ctl_selector_info_get; + rte_swx_ctl_selector_field_info_get; + rte_swx_ctl_selector_group_id_field_info_get; + rte_swx_ctl_selector_member_id_field_info_get; };