pipeline: support selector table
authorCristian Dumitrescu <cristian.dumitrescu@intel.com>
Sat, 10 Jul 2021 00:20:34 +0000 (01:20 +0100)
committerThomas Monjalon <thomas@monjalon.net>
Sat, 10 Jul 2021 06:26:12 +0000 (08:26 +0200)
Add pipeline-level support for selector tables.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
lib/pipeline/rte_swx_ctl.c
lib/pipeline/rte_swx_ctl.h
lib/pipeline/rte_swx_pipeline.c
lib/pipeline/rte_swx_pipeline.h
lib/pipeline/rte_swx_pipeline_spec.c
lib/pipeline/version.map

index d1a5ba4..4ee47df 100644 (file)
@@ -10,6 +10,8 @@
 #include <rte_common.h>
 #include <rte_byteorder.h>
 
+#include <rte_swx_table_selector.h>
+
 #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 @@ rollback:
                        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;
+}
index dee788b..f37301c 100644 (file)
@@ -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.
  */
index a2732a1..1ef8ac7 100644 (file)
@@ -15,6 +15,8 @@
 #include <rte_cycles.h>
 #include <rte_meter.h>
 
+#include <rte_swx_table_selector.h>
+
 #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_table_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(&params->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 @@ error:
        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,
index feeb10a..cd395ac 100644 (file)
@@ -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
  *
index 2e867d7..6980b03 100644 (file)
@@ -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(&regarray_spec,
@@ -1651,6 +2002,7 @@ error:
        metadata_spec_free(&metadata_spec);
        action_spec_free(&action_spec);
        table_spec_free(&table_spec);
+       selector_spec_free(&selector_spec);
        regarray_spec_free(&regarray_spec);
        metarray_spec_free(&metarray_spec);
        apply_spec_free(&apply_spec);
index a4d7d97..ff0974c 100644 (file)
@@ -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;
 };