pipeline: support learner tables
authorCristian Dumitrescu <cristian.dumitrescu@intel.com>
Mon, 20 Sep 2021 15:01:31 +0000 (16:01 +0100)
committerThomas Monjalon <thomas@monjalon.net>
Mon, 27 Sep 2021 07:52:04 +0000 (09:52 +0200)
Add pipeline level support for learner 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 dc09386..86b58e2 100644 (file)
@@ -123,12 +123,26 @@ struct selector {
        struct rte_swx_table_selector_params params;
 };
 
+struct learner {
+       struct rte_swx_ctl_learner_info info;
+       struct rte_swx_ctl_table_match_field_info *mf;
+       struct rte_swx_ctl_table_action_info *actions;
+       uint32_t action_data_size;
+
+       /* The pending default action: this is NOT the current default action;
+        * this will be the new default action after the next commit, if the
+        * next commit operation is successful.
+        */
+       struct rte_swx_table_entry *pending_default;
+};
+
 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 learner *learners;
        struct rte_swx_table_state *ts;
        struct rte_swx_table_state *ts_next;
        int numa_node;
@@ -924,6 +938,70 @@ selector_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
        return 0;
 }
 
+static void
+learner_pending_default_free(struct learner *l)
+{
+       if (!l->pending_default)
+               return;
+
+       free(l->pending_default->action_data);
+       free(l->pending_default);
+       l->pending_default = NULL;
+}
+
+
+static void
+learner_free(struct rte_swx_ctl_pipeline *ctl)
+{
+       uint32_t i;
+
+       if (!ctl->learners)
+               return;
+
+       for (i = 0; i < ctl->info.n_learners; i++) {
+               struct learner *l = &ctl->learners[i];
+
+               free(l->mf);
+               free(l->actions);
+
+               learner_pending_default_free(l);
+       }
+
+       free(ctl->learners);
+       ctl->learners = NULL;
+}
+
+static struct learner *
+learner_find(struct rte_swx_ctl_pipeline *ctl, const char *learner_name)
+{
+       uint32_t i;
+
+       for (i = 0; i < ctl->info.n_learners; i++) {
+               struct learner *l = &ctl->learners[i];
+
+               if (!strcmp(learner_name, l->info.name))
+                       return l;
+       }
+
+       return NULL;
+}
+
+static uint32_t
+learner_action_data_size_get(struct rte_swx_ctl_pipeline *ctl, struct learner *l)
+{
+       uint32_t action_data_size = 0, i;
+
+       for (i = 0; i < l->info.n_actions; i++) {
+               uint32_t action_id = l->actions[i].action_id;
+               struct action *a = &ctl->actions[action_id];
+
+               if (a->data_size > action_data_size)
+                       action_data_size = a->data_size;
+       }
+
+       return action_data_size;
+}
+
 static void
 table_state_free(struct rte_swx_ctl_pipeline *ctl)
 {
@@ -954,6 +1032,14 @@ table_state_free(struct rte_swx_ctl_pipeline *ctl)
                        rte_swx_table_selector_free(ts->obj);
        }
 
+       /* For each learner table, free its table state. */
+       for (i = 0; i < ctl->info.n_learners; i++) {
+               struct rte_swx_table_state *ts = &ctl->ts_next[i];
+
+               /* Default action data. */
+               free(ts->default_action_data);
+       }
+
        free(ctl->ts_next);
        ctl->ts_next = NULL;
 }
@@ -1020,6 +1106,29 @@ table_state_create(struct rte_swx_ctl_pipeline *ctl)
                }
        }
 
+       /* Learner tables. */
+       for (i = 0; i < ctl->info.n_learners; i++) {
+               struct learner *l = &ctl->learners[i];
+               struct rte_swx_table_state *ts = &ctl->ts[i];
+               struct rte_swx_table_state *ts_next = &ctl->ts_next[i];
+
+               /* Table object: duplicate from the current table state. */
+               ts_next->obj = ts->obj;
+
+               /* Default action data: duplicate from the current table state. */
+               ts_next->default_action_data = malloc(l->action_data_size);
+               if (!ts_next->default_action_data) {
+                       status = -ENOMEM;
+                       goto error;
+               }
+
+               memcpy(ts_next->default_action_data,
+                      ts->default_action_data,
+                      l->action_data_size);
+
+               ts_next->default_action_id = ts->default_action_id;
+       }
+
        return 0;
 
 error:
@@ -1037,6 +1146,8 @@ rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl)
 
        table_state_free(ctl);
 
+       learner_free(ctl);
+
        selector_free(ctl);
 
        table_free(ctl);
@@ -1251,6 +1362,54 @@ rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p)
                        goto error;
        }
 
+       /* learner tables. */
+       ctl->learners = calloc(ctl->info.n_learners, sizeof(struct learner));
+       if (!ctl->learners)
+               goto error;
+
+       for (i = 0; i < ctl->info.n_learners; i++) {
+               struct learner *l = &ctl->learners[i];
+               uint32_t j;
+
+               /* info. */
+               status = rte_swx_ctl_learner_info_get(p, i, &l->info);
+               if (status)
+                       goto error;
+
+               /* mf. */
+               l->mf = calloc(l->info.n_match_fields,
+                              sizeof(struct rte_swx_ctl_table_match_field_info));
+               if (!l->mf)
+                       goto error;
+
+               for (j = 0; j < l->info.n_match_fields; j++) {
+                       status = rte_swx_ctl_learner_match_field_info_get(p,
+                               i,
+                               j,
+                               &l->mf[j]);
+                       if (status)
+                               goto error;
+               }
+
+               /* actions. */
+               l->actions = calloc(l->info.n_actions,
+                       sizeof(struct rte_swx_ctl_table_action_info));
+               if (!l->actions)
+                       goto error;
+
+               for (j = 0; j < l->info.n_actions; j++) {
+                       status = rte_swx_ctl_learner_action_info_get(p,
+                               i,
+                               j,
+                               &l->actions[j]);
+                       if (status || l->actions[j].action_id >= ctl->info.n_actions)
+                               goto error;
+               }
+
+               /* action_data_size. */
+               l->action_data_size = learner_action_data_size_get(ctl, l);
+       }
+
        /* ts. */
        status = rte_swx_pipeline_table_state_get(p, &ctl->ts);
        if (status)
@@ -1685,9 +1844,8 @@ table_rollfwd1(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
        action_data = table->pending_default->action_data;
        a = &ctl->actions[action_id];
 
-       memcpy(ts_next->default_action_data,
-              action_data,
-              a->data_size);
+       if (a->data_size)
+               memcpy(ts_next->default_action_data, action_data, a->data_size);
 
        ts_next->default_action_id = action_id;
 }
@@ -2099,6 +2257,178 @@ selector_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t selector_id)
        memset(s->groups_pending_delete, 0, s->info.n_groups_max * sizeof(int));
 }
 
+static struct rte_swx_table_entry *
+learner_default_entry_alloc(struct learner *l)
+{
+       struct rte_swx_table_entry *entry;
+
+       entry = calloc(1, sizeof(struct rte_swx_table_entry));
+       if (!entry)
+               goto error;
+
+       /* action_data. */
+       if (l->action_data_size) {
+               entry->action_data = calloc(1, l->action_data_size);
+               if (!entry->action_data)
+                       goto error;
+       }
+
+       return entry;
+
+error:
+       table_entry_free(entry);
+       return NULL;
+}
+
+static int
+learner_default_entry_check(struct rte_swx_ctl_pipeline *ctl,
+                           uint32_t learner_id,
+                           struct rte_swx_table_entry *entry)
+{
+       struct learner *l = &ctl->learners[learner_id];
+       struct action *a;
+       uint32_t i;
+
+       CHECK(entry, EINVAL);
+
+       /* action_id. */
+       for (i = 0; i < l->info.n_actions; i++)
+               if (entry->action_id == l->actions[i].action_id)
+                       break;
+
+       CHECK(i < l->info.n_actions, EINVAL);
+
+       /* action_data. */
+       a = &ctl->actions[entry->action_id];
+       CHECK(!(a->data_size && !entry->action_data), EINVAL);
+
+       return 0;
+}
+
+static struct rte_swx_table_entry *
+learner_default_entry_duplicate(struct rte_swx_ctl_pipeline *ctl,
+                               uint32_t learner_id,
+                               struct rte_swx_table_entry *entry)
+{
+       struct learner *l = &ctl->learners[learner_id];
+       struct rte_swx_table_entry *new_entry = NULL;
+       struct action *a;
+       uint32_t i;
+
+       if (!entry)
+               goto error;
+
+       new_entry = calloc(1, sizeof(struct rte_swx_table_entry));
+       if (!new_entry)
+               goto error;
+
+       /* action_id. */
+       for (i = 0; i < l->info.n_actions; i++)
+               if (entry->action_id == l->actions[i].action_id)
+                       break;
+
+       if (i >= l->info.n_actions)
+               goto error;
+
+       new_entry->action_id = entry->action_id;
+
+       /* action_data. */
+       a = &ctl->actions[entry->action_id];
+       if (a->data_size && !entry->action_data)
+               goto error;
+
+       /* The table layer provisions a constant action data size per
+        * entry, which should be the largest data size for all the
+        * actions enabled for the current table, and attempts to copy
+        * this many bytes each time a table entry is added, even if the
+        * specific action requires less data or even no data at all,
+        * hence we always have to allocate the max.
+        */
+       new_entry->action_data = calloc(1, l->action_data_size);
+       if (!new_entry->action_data)
+               goto error;
+
+       if (a->data_size)
+               memcpy(new_entry->action_data, entry->action_data, a->data_size);
+
+       return new_entry;
+
+error:
+       table_entry_free(new_entry);
+       return NULL;
+}
+
+int
+rte_swx_ctl_pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *ctl,
+                                              const char *learner_name,
+                                              struct rte_swx_table_entry *entry)
+{
+       struct learner *l;
+       struct rte_swx_table_entry *new_entry;
+       uint32_t learner_id;
+
+       CHECK(ctl, EINVAL);
+
+       CHECK(learner_name && learner_name[0], EINVAL);
+       l = learner_find(ctl, learner_name);
+       CHECK(l, EINVAL);
+       learner_id = l - ctl->learners;
+       CHECK(!l->info.default_action_is_const, EINVAL);
+
+       CHECK(entry, EINVAL);
+       CHECK(!learner_default_entry_check(ctl, learner_id, entry), EINVAL);
+
+       new_entry = learner_default_entry_duplicate(ctl, learner_id, entry);
+       CHECK(new_entry, ENOMEM);
+
+       learner_pending_default_free(l);
+
+       l->pending_default = new_entry;
+       return 0;
+}
+
+static void
+learner_rollfwd(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id)
+{
+       struct learner *l = &ctl->learners[learner_id];
+       struct rte_swx_table_state *ts_next = &ctl->ts_next[ctl->info.n_tables +
+               ctl->info.n_selectors + learner_id];
+       struct action *a;
+       uint8_t *action_data;
+       uint64_t action_id;
+
+       /* Copy the pending default entry. */
+       if (!l->pending_default)
+               return;
+
+       action_id = l->pending_default->action_id;
+       action_data = l->pending_default->action_data;
+       a = &ctl->actions[action_id];
+
+       if (a->data_size)
+               memcpy(ts_next->default_action_data, action_data, a->data_size);
+
+       ts_next->default_action_id = action_id;
+}
+
+static void
+learner_rollfwd_finalize(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id)
+{
+       struct learner *l = &ctl->learners[learner_id];
+
+       /* Free up the pending default entry, as it is now part of the table. */
+       learner_pending_default_free(l);
+}
+
+static void
+learner_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t learner_id)
+{
+       struct learner *l = &ctl->learners[learner_id];
+
+       /* Free up the pending default entry, as it is no longer going to be added to the table. */
+       learner_pending_default_free(l);
+}
+
 int
 rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail)
 {
@@ -2110,6 +2440,7 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail)
 
        /* 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.
+        * We must be able to fully revert all the changes that can fail as if they never happened.
         */
        for (i = 0; i < ctl->info.n_tables; i++) {
                status = table_rollfwd0(ctl, i, 0);
@@ -2123,9 +2454,15 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail)
                        goto rollback;
        }
 
+       /* Second, operate all the changes that cannot fail. Since nothing can fail from this point
+        * onwards, the transaction is guaranteed to be successful.
+        */
        for (i = 0; i < ctl->info.n_tables; i++)
                table_rollfwd1(ctl, i);
 
+       for (i = 0; i < ctl->info.n_learners; i++)
+               learner_rollfwd(ctl, i);
+
        /* Swap the table state for the data plane. The current ts and ts_next
         * become the new ts_next and ts, respectively.
         */
@@ -2151,6 +2488,11 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail)
                selector_rollfwd_finalize(ctl, i);
        }
 
+       for (i = 0; i < ctl->info.n_learners; i++) {
+               learner_rollfwd(ctl, i);
+               learner_rollfwd_finalize(ctl, i);
+       }
+
        return 0;
 
 rollback:
@@ -2166,6 +2508,10 @@ rollback:
                        selector_abort(ctl, i);
        }
 
+       if (abort_on_fail)
+               for (i = 0; i < ctl->info.n_learners; i++)
+                       learner_abort(ctl, i);
+
        return status;
 }
 
@@ -2182,6 +2528,9 @@ rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl)
 
        for (i = 0; i < ctl->info.n_selectors; i++)
                selector_abort(ctl, i);
+
+       for (i = 0; i < ctl->info.n_learners; i++)
+               learner_abort(ctl, i);
 }
 
 static int
@@ -2460,6 +2809,130 @@ error:
        return NULL;
 }
 
+struct rte_swx_table_entry *
+rte_swx_ctl_pipeline_learner_default_entry_read(struct rte_swx_ctl_pipeline *ctl,
+                                               const char *learner_name,
+                                               const char *string,
+                                               int *is_blank_or_comment)
+{
+       char *token_array[RTE_SWX_CTL_ENTRY_TOKENS_MAX], **tokens;
+       struct learner *l;
+       struct action *action;
+       struct rte_swx_table_entry *entry = NULL;
+       char *s0 = NULL, *s;
+       uint32_t n_tokens = 0, arg_offset = 0, i;
+       int blank_or_comment = 0;
+
+       /* Check input arguments. */
+       if (!ctl)
+               goto error;
+
+       if (!learner_name || !learner_name[0])
+               goto error;
+
+       l = learner_find(ctl, learner_name);
+       if (!l)
+               goto error;
+
+       if (!string || !string[0])
+               goto error;
+
+       /* Memory allocation. */
+       s0 = strdup(string);
+       if (!s0)
+               goto error;
+
+       entry = learner_default_entry_alloc(l);
+       if (!entry)
+               goto error;
+
+       /* Parse the string into tokens. */
+       for (s = s0; ; ) {
+               char *token;
+
+               token = strtok_r(s, " \f\n\r\t\v", &s);
+               if (!token || token_is_comment(token))
+                       break;
+
+               if (n_tokens >= RTE_SWX_CTL_ENTRY_TOKENS_MAX)
+                       goto error;
+
+               token_array[n_tokens] = token;
+               n_tokens++;
+       }
+
+       if (!n_tokens) {
+               blank_or_comment = 1;
+               goto error;
+       }
+
+       tokens = token_array;
+
+       /*
+        * Action.
+        */
+       if (!(n_tokens && !strcmp(tokens[0], "action")))
+               goto other;
+
+       if (n_tokens < 2)
+               goto error;
+
+       action = action_find(ctl, tokens[1]);
+       if (!action)
+               goto error;
+
+       if (n_tokens < 2 + action->info.n_args * 2)
+               goto error;
+
+       /* action_id. */
+       entry->action_id = action - ctl->actions;
+
+       /* action_data. */
+       for (i = 0; i < action->info.n_args; i++) {
+               struct rte_swx_ctl_action_arg_info *arg = &action->args[i];
+               char *arg_name, *arg_val;
+               uint64_t val;
+
+               arg_name = tokens[2 + i * 2];
+               arg_val = tokens[2 + i * 2 + 1];
+
+               if (strcmp(arg_name, arg->name))
+                       goto error;
+
+               val = strtoull(arg_val, &arg_val, 0);
+               if (arg_val[0])
+                       goto error;
+
+               /* Endianness conversion. */
+               if (arg->is_network_byte_order)
+                       val = field_hton(val, arg->n_bits);
+
+               /* Copy to entry. */
+               memcpy(&entry->action_data[arg_offset],
+                      (uint8_t *)&val,
+                      arg->n_bits / 8);
+
+               arg_offset += arg->n_bits / 8;
+       }
+
+       tokens += 2 + action->info.n_args * 2;
+       n_tokens -= 2 + action->info.n_args * 2;
+
+other:
+       if (n_tokens)
+               goto error;
+
+       free(s0);
+       return entry;
+
+error:
+       table_entry_free(entry);
+       free(s0);
+       if (is_blank_or_comment)
+               *is_blank_or_comment = blank_or_comment;
+       return NULL;
+}
+
 static void
 table_entry_printf(FILE *f,
                   struct rte_swx_ctl_pipeline *ctl,
index f37301c..8075972 100644 (file)
@@ -52,6 +52,9 @@ struct rte_swx_ctl_pipeline_info {
        /** Number of selector tables. */
        uint32_t n_selectors;
 
+       /** Number of learner tables. */
+       uint32_t n_learners;
+
        /** Number of register arrays. */
        uint32_t n_regarrays;
 
@@ -512,6 +515,142 @@ rte_swx_ctl_pipeline_selector_stats_read(struct rte_swx_pipeline *p,
                                         const char *selector_name,
                                         struct rte_swx_pipeline_selector_stats *stats);
 
+/*
+ * Learner Table Query API.
+ */
+
+/** Learner table info. */
+struct rte_swx_ctl_learner_info {
+       /** Learner table name. */
+       char name[RTE_SWX_CTL_NAME_SIZE];
+
+       /** Number of match fields. */
+       uint32_t n_match_fields;
+
+       /** Number of actions. */
+       uint32_t n_actions;
+
+       /** Non-zero (true) when the default action is constant, therefore it
+        * cannot be changed; zero (false) when the default action not constant,
+        * therefore it can be changed.
+        */
+       int default_action_is_const;
+
+       /** Learner table size parameter. */
+       uint32_t size;
+};
+
+/**
+ * Learner table info get
+ *
+ * @param[in] p
+ *   Pipeline handle.
+ * @param[in] learner_id
+ *   Learner table ID (0 .. *n_learners* - 1).
+ * @param[out] learner
+ *   Learner table info.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_learner_info_get(struct rte_swx_pipeline *p,
+                            uint32_t learner_id,
+                            struct rte_swx_ctl_learner_info *learner);
+
+/**
+ * Learner table match field info get
+ *
+ * @param[in] p
+ *   Pipeline handle.
+ * @param[in] learner_id
+ *   Learner table ID (0 .. *n_learners* - 1).
+ * @param[in] match_field_id
+ *   Match field ID (0 .. *n_match_fields* - 1).
+ * @param[out] match_field
+ *   Learner table match field info.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_learner_match_field_info_get(struct rte_swx_pipeline *p,
+                                        uint32_t learner_id,
+                                        uint32_t match_field_id,
+                                        struct rte_swx_ctl_table_match_field_info *match_field);
+
+/**
+ * Learner table action info get
+ *
+ * @param[in] p
+ *   Pipeline handle.
+ * @param[in] learner_id
+ *   Learner table ID (0 .. *n_learners* - 1).
+ * @param[in] learner_action_id
+ *   Action index within the set of learner table actions (0 .. learner table n_actions - 1). Not
+ *   to be confused with the pipeline-leve action ID (0 .. pipeline n_actions - 1), which is
+ *   precisely what this function returns as part of the *learner_action*.
+ * @param[out] learner_action
+ *   Learner action info.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_learner_action_info_get(struct rte_swx_pipeline *p,
+                                   uint32_t learner_id,
+                                   uint32_t learner_action_id,
+                                   struct rte_swx_ctl_table_action_info *learner_action);
+
+/** Learner table statistics. */
+struct rte_swx_learner_stats {
+       /** Number of packets with lookup hit. */
+       uint64_t n_pkts_hit;
+
+       /** Number of packets with lookup miss. */
+       uint64_t n_pkts_miss;
+
+       /** Number of packets with successful learning. */
+       uint64_t n_pkts_learn_ok;
+
+       /** Number of packets with learning error. */
+       uint64_t n_pkts_learn_err;
+
+       /** Number of packets with forget event. */
+       uint64_t n_pkts_forget;
+
+       /** Number of packets (with either lookup hit or miss) per pipeline action. Array of
+        * pipeline *n_actions* elements indedex by the pipeline-level *action_id*, therefore this
+        * array has the same size for all the tables within the same pipeline.
+        */
+       uint64_t *n_pkts_action;
+};
+
+/**
+ * Learner table statistics counters read
+ *
+ * @param[in] p
+ *   Pipeline handle.
+ * @param[in] learner_name
+ *   Learner table name.
+ * @param[out] stats
+ *   Learner table stats. Must point to a pre-allocated structure. The *n_pkts_action* field also
+ *   needs to be pre-allocated as array of pipeline *n_actions* elements. The pipeline actions that
+ *   are not valid for the current learner table have their associated *n_pkts_action* element
+ *   always set to zero.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_learner_stats_read(struct rte_swx_pipeline *p,
+                                     const char *learner_name,
+                                     struct rte_swx_learner_stats *stats);
+
 /*
  * Table Update API.
  */
@@ -761,6 +900,27 @@ rte_swx_ctl_pipeline_selector_group_member_delete(struct rte_swx_ctl_pipeline *c
                                                  uint32_t group_id,
                                                  uint32_t member_id);
 
+/**
+ * Pipeline learner table default entry add
+ *
+ * Schedule learner table default entry update as part of the next commit operation.
+ *
+ * @param[in] ctl
+ *   Pipeline control handle.
+ * @param[in] learner_name
+ *   Learner table name.
+ * @param[in] entry
+ *   The new table default entry. The *key* and *key_mask* entry fields are ignored.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_learner_default_entry_add(struct rte_swx_ctl_pipeline *ctl,
+                                              const char *learner_name,
+                                              struct rte_swx_table_entry *entry);
+
 /**
  * Pipeline commit
  *
@@ -819,6 +979,32 @@ rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl,
                                      const char *string,
                                      int *is_blank_or_comment);
 
+/**
+ * Pipeline learner table default entry read
+ *
+ * Read learner table default entry from string.
+ *
+ * @param[in] ctl
+ *   Pipeline control handle.
+ * @param[in] learner_name
+ *   Learner table name.
+ * @param[in] string
+ *   String containing the learner table default entry.
+ * @param[out] is_blank_or_comment
+ *   On error, this argument provides an indication of whether *string* contains
+ *   an invalid table entry (set to zero) or a blank or comment line that should
+ *   typically be ignored (set to a non-zero value).
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument.
+ */
+__rte_experimental
+struct rte_swx_table_entry *
+rte_swx_ctl_pipeline_learner_default_entry_read(struct rte_swx_ctl_pipeline *ctl,
+                                               const char *learner_name,
+                                               const char *string,
+                                               int *is_blank_or_comment);
+
 /**
  * Pipeline table print to file
  *
index 96786fb..f89a134 100644 (file)
@@ -16,6 +16,7 @@
 #include <rte_meter.h>
 
 #include <rte_swx_table_selector.h>
+#include <rte_swx_table_learner.h>
 
 #include "rte_swx_pipeline.h"
 #include "rte_swx_ctl.h"
@@ -511,6 +512,13 @@ enum instruction_type {
        /* table TABLE */
        INSTR_TABLE,
        INSTR_SELECTOR,
+       INSTR_LEARNER,
+
+       /* learn LEARNER ACTION_NAME */
+       INSTR_LEARNER_LEARN,
+
+       /* forget */
+       INSTR_LEARNER_FORGET,
 
        /* extern e.obj.func */
        INSTR_EXTERN_OBJ,
@@ -636,6 +644,10 @@ struct instr_table {
        uint8_t table_id;
 };
 
+struct instr_learn {
+       uint8_t action_id;
+};
+
 struct instr_extern_obj {
        uint8_t ext_obj_id;
        uint8_t func_id;
@@ -726,6 +738,7 @@ struct instruction {
                struct instr_dma dma;
                struct instr_dst_src alu;
                struct instr_table table;
+               struct instr_learn learn;
                struct instr_extern_obj ext_obj;
                struct instr_extern_func ext_func;
                struct instr_jmp jmp;
@@ -746,7 +759,7 @@ struct action {
        TAILQ_ENTRY(action) node;
        char name[RTE_SWX_NAME_SIZE];
        struct struct_type *st;
-       int *args_endianness; /* 0 = Host Byte Order (HBO). */
+       int *args_endianness; /* 0 = Host Byte Order (HBO); 1 = Network Byte Order (NBO). */
        struct instruction *instructions;
        uint32_t n_instructions;
        uint32_t id;
@@ -839,6 +852,47 @@ struct selector_statistics {
        uint64_t n_pkts;
 };
 
+/*
+ * Learner table.
+ */
+struct learner {
+       TAILQ_ENTRY(learner) node;
+       char name[RTE_SWX_NAME_SIZE];
+
+       /* Match. */
+       struct field **fields;
+       uint32_t n_fields;
+       struct header *header;
+
+       /* Action. */
+       struct action **actions;
+       struct field **action_arg;
+       struct action *default_action;
+       uint8_t *default_action_data;
+       uint32_t n_actions;
+       int default_action_is_const;
+       uint32_t action_data_size_max;
+
+       uint32_t size;
+       uint32_t timeout;
+       uint32_t id;
+};
+
+TAILQ_HEAD(learner_tailq, learner);
+
+struct learner_runtime {
+       void *mailbox;
+       uint8_t **key;
+       uint8_t **action_data;
+};
+
+struct learner_statistics {
+       uint64_t n_pkts_hit[2]; /* 0 = Miss, 1 = Hit. */
+       uint64_t n_pkts_learn[2]; /* 0 = Learn OK, 1 = Learn error. */
+       uint64_t n_pkts_forget;
+       uint64_t *n_pkts_action;
+};
+
 /*
  * Register array.
  */
@@ -919,9 +973,12 @@ struct thread {
        /* Tables. */
        struct table_runtime *tables;
        struct selector_runtime *selectors;
+       struct learner_runtime *learners;
        struct rte_swx_table_state *table_state;
        uint64_t action_id;
        int hit; /* 0 = Miss, 1 = Hit. */
+       uint32_t learner_id;
+       uint64_t time;
 
        /* Extern objects and functions. */
        struct extern_obj_runtime *extern_objs;
@@ -1355,6 +1412,7 @@ struct rte_swx_pipeline {
        struct table_type_tailq table_types;
        struct table_tailq tables;
        struct selector_tailq selectors;
+       struct learner_tailq learners;
        struct regarray_tailq regarrays;
        struct meter_profile_tailq meter_profiles;
        struct metarray_tailq metarrays;
@@ -1365,6 +1423,7 @@ struct rte_swx_pipeline {
        struct rte_swx_table_state *table_state;
        struct table_statistics *table_stats;
        struct selector_statistics *selector_stats;
+       struct learner_statistics *learner_stats;
        struct regarray_runtime *regarray_runtime;
        struct metarray_runtime *metarray_runtime;
        struct instruction *instructions;
@@ -1378,6 +1437,7 @@ struct rte_swx_pipeline {
        uint32_t n_actions;
        uint32_t n_tables;
        uint32_t n_selectors;
+       uint32_t n_learners;
        uint32_t n_regarrays;
        uint32_t n_metarrays;
        uint32_t n_headers;
@@ -3625,6 +3685,9 @@ table_find(struct rte_swx_pipeline *p, const char *name);
 static struct selector *
 selector_find(struct rte_swx_pipeline *p, const char *name);
 
+static struct learner *
+learner_find(struct rte_swx_pipeline *p, const char *name);
+
 static int
 instr_table_translate(struct rte_swx_pipeline *p,
                      struct action *action,
@@ -3635,6 +3698,7 @@ instr_table_translate(struct rte_swx_pipeline *p,
 {
        struct table *t;
        struct selector *s;
+       struct learner *l;
 
        CHECK(!action, EINVAL);
        CHECK(n_tokens == 2, EINVAL);
@@ -3653,6 +3717,13 @@ instr_table_translate(struct rte_swx_pipeline *p,
                return 0;
        }
 
+       l = learner_find(p, tokens[1]);
+       if (l) {
+               instr->type = INSTR_LEARNER;
+               instr->table.table_id = l->id;
+               return 0;
+       }
+
        CHECK(0, EINVAL);
 }
 
@@ -3746,6 +3817,168 @@ instr_selector_exec(struct rte_swx_pipeline *p)
        thread_ip_inc(p);
 }
 
+static inline void
+instr_learner_exec(struct rte_swx_pipeline *p)
+{
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+       uint32_t learner_id = ip->table.table_id;
+       struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
+               p->n_selectors + learner_id];
+       struct learner_runtime *l = &t->learners[learner_id];
+       struct learner_statistics *stats = &p->learner_stats[learner_id];
+       uint64_t action_id, n_pkts_hit, n_pkts_action, time;
+       uint8_t *action_data;
+       int done, hit;
+
+       /* Table. */
+       time = rte_get_tsc_cycles();
+
+       done = rte_swx_table_learner_lookup(ts->obj,
+                                           l->mailbox,
+                                           time,
+                                           l->key,
+                                           &action_id,
+                                           &action_data,
+                                           &hit);
+       if (!done) {
+               /* Thread. */
+               TRACE("[Thread %2u] learner %u (not finalized)\n",
+                     p->thread_id,
+                     learner_id);
+
+               thread_yield(p);
+               return;
+       }
+
+       action_id = hit ? action_id : ts->default_action_id;
+       action_data = hit ? action_data : ts->default_action_data;
+       n_pkts_hit = stats->n_pkts_hit[hit];
+       n_pkts_action = stats->n_pkts_action[action_id];
+
+       TRACE("[Thread %2u] learner %u (%s, action %u)\n",
+             p->thread_id,
+             learner_id,
+             hit ? "hit" : "miss",
+             (uint32_t)action_id);
+
+       t->action_id = action_id;
+       t->structs[0] = action_data;
+       t->hit = hit;
+       t->learner_id = learner_id;
+       t->time = time;
+       stats->n_pkts_hit[hit] = n_pkts_hit + 1;
+       stats->n_pkts_action[action_id] = n_pkts_action + 1;
+
+       /* Thread. */
+       thread_ip_action_call(p, t, action_id);
+}
+
+/*
+ * learn.
+ */
+static struct action *
+action_find(struct rte_swx_pipeline *p, const char *name);
+
+static int
+action_has_nbo_args(struct action *a);
+
+static int
+instr_learn_translate(struct rte_swx_pipeline *p,
+                     struct action *action,
+                     char **tokens,
+                     int n_tokens,
+                     struct instruction *instr,
+                     struct instruction_data *data __rte_unused)
+{
+       struct action *a;
+
+       CHECK(action, EINVAL);
+       CHECK(n_tokens == 2, EINVAL);
+
+       a = action_find(p, tokens[1]);
+       CHECK(a, EINVAL);
+       CHECK(!action_has_nbo_args(a), EINVAL);
+
+       instr->type = INSTR_LEARNER_LEARN;
+       instr->learn.action_id = a->id;
+
+       return 0;
+}
+
+static inline void
+instr_learn_exec(struct rte_swx_pipeline *p)
+{
+       struct thread *t = &p->threads[p->thread_id];
+       struct instruction *ip = t->ip;
+       uint64_t action_id = ip->learn.action_id;
+       uint32_t learner_id = t->learner_id;
+       struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
+               p->n_selectors + learner_id];
+       struct learner_runtime *l = &t->learners[learner_id];
+       struct learner_statistics *stats = &p->learner_stats[learner_id];
+       uint32_t status;
+
+       /* Table. */
+       status = rte_swx_table_learner_add(ts->obj,
+                                          l->mailbox,
+                                          t->time,
+                                          action_id,
+                                          l->action_data[action_id]);
+
+       TRACE("[Thread %2u] learner %u learn %s\n",
+             p->thread_id,
+             learner_id,
+             status ? "ok" : "error");
+
+       stats->n_pkts_learn[status] += 1;
+
+       /* Thread. */
+       thread_ip_inc(p);
+}
+
+/*
+ * forget.
+ */
+static int
+instr_forget_translate(struct rte_swx_pipeline *p __rte_unused,
+                      struct action *action,
+                      char **tokens __rte_unused,
+                      int n_tokens,
+                      struct instruction *instr,
+                      struct instruction_data *data __rte_unused)
+{
+       CHECK(action, EINVAL);
+       CHECK(n_tokens == 1, EINVAL);
+
+       instr->type = INSTR_LEARNER_FORGET;
+
+       return 0;
+}
+
+static inline void
+instr_forget_exec(struct rte_swx_pipeline *p)
+{
+       struct thread *t = &p->threads[p->thread_id];
+       uint32_t learner_id = t->learner_id;
+       struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
+               p->n_selectors + learner_id];
+       struct learner_runtime *l = &t->learners[learner_id];
+       struct learner_statistics *stats = &p->learner_stats[learner_id];
+
+       /* Table. */
+       rte_swx_table_learner_delete(ts->obj, l->mailbox);
+
+       TRACE("[Thread %2u] learner %u forget\n",
+             p->thread_id,
+             learner_id);
+
+       stats->n_pkts_forget += 1;
+
+       /* Thread. */
+       thread_ip_inc(p);
+}
+
 /*
  * extern.
  */
@@ -7159,9 +7392,6 @@ instr_meter_imi_exec(struct rte_swx_pipeline *p)
 /*
  * jmp.
  */
-static struct action *
-action_find(struct rte_swx_pipeline *p, const char *name);
-
 static int
 instr_jmp_translate(struct rte_swx_pipeline *p __rte_unused,
                    struct action *action __rte_unused,
@@ -8136,6 +8366,22 @@ instr_translate(struct rte_swx_pipeline *p,
                                             instr,
                                             data);
 
+       if (!strcmp(tokens[tpos], "learn"))
+               return instr_learn_translate(p,
+                                            action,
+                                            &tokens[tpos],
+                                            n_tokens - tpos,
+                                            instr,
+                                            data);
+
+       if (!strcmp(tokens[tpos], "forget"))
+               return instr_forget_translate(p,
+                                             action,
+                                             &tokens[tpos],
+                                             n_tokens - tpos,
+                                             instr,
+                                             data);
+
        if (!strcmp(tokens[tpos], "extern"))
                return instr_extern_translate(p,
                                              action,
@@ -9096,6 +9342,9 @@ static instr_exec_t instruction_table[] = {
 
        [INSTR_TABLE] = instr_table_exec,
        [INSTR_SELECTOR] = instr_selector_exec,
+       [INSTR_LEARNER] = instr_learner_exec,
+       [INSTR_LEARNER_LEARN] = instr_learn_exec,
+       [INSTR_LEARNER_FORGET] = instr_forget_exec,
        [INSTR_EXTERN_OBJ] = instr_extern_obj_exec,
        [INSTR_EXTERN_FUNC] = instr_extern_func_exec,
 
@@ -9191,6 +9440,42 @@ action_field_parse(struct action *action, const char *name)
        return action_field_find(action, &name[2]);
 }
 
+static int
+action_has_nbo_args(struct action *a)
+{
+       uint32_t i;
+
+       /* Return if the action does not have any args. */
+       if (!a->st)
+               return 0; /* FALSE */
+
+       for (i = 0; i < a->st->n_fields; i++)
+               if (a->args_endianness[i])
+                       return 1; /* TRUE */
+
+       return 0; /* FALSE */
+}
+
+static int
+action_does_learning(struct action *a)
+{
+       uint32_t i;
+
+       for (i = 0; i < a->n_instructions; i++)
+               switch (a->instructions[i].type) {
+               case INSTR_LEARNER_LEARN:
+                       return 1; /* TRUE */
+
+               case INSTR_LEARNER_FORGET:
+                       return 1; /* TRUE */
+
+               default:
+                       continue;
+               }
+
+       return 0; /* FALSE */
+}
+
 int
 rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
                               const char *name,
@@ -9546,6 +9831,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(!learner_find(p, name), EEXIST);
 
        CHECK(params, EINVAL);
 
@@ -9566,6 +9852,7 @@ rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
 
                a = action_find(p, action_name);
                CHECK(a, EINVAL);
+               CHECK(!action_does_learning(a), EINVAL);
 
                action_data_size = a->st ? a->st->n_bits / 8 : 0;
                if (action_data_size > action_data_size_max)
@@ -9964,6 +10251,7 @@ rte_swx_pipeline_selector_config(struct rte_swx_pipeline *p,
        CHECK_NAME(name, EINVAL);
        CHECK(!table_find(p, name), EEXIST);
        CHECK(!selector_find(p, name), EEXIST);
+       CHECK(!learner_find(p, name), EEXIST);
 
        CHECK(params, EINVAL);
 
@@ -10221,73 +10509,604 @@ selector_free(struct rte_swx_pipeline *p)
 }
 
 /*
- * Table state.
+ * Learner table.
  */
-static int
-table_state_build(struct rte_swx_pipeline *p)
+static struct learner *
+learner_find(struct rte_swx_pipeline *p, const char *name)
 {
-       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);
+       struct learner *l;
 
-       TAILQ_FOREACH(table, &p->tables, node) {
-               struct rte_swx_table_state *ts = &p->table_state[table->id];
+       TAILQ_FOREACH(l, &p->learners, node)
+               if (!strcmp(l->name, name))
+                       return l;
 
-               if (table->type) {
-                       struct rte_swx_table_params *params;
+       return NULL;
+}
 
-                       /* ts->obj. */
-                       params = table_params_get(table);
-                       CHECK(params, ENOMEM);
+static struct learner *
+learner_find_by_id(struct rte_swx_pipeline *p, uint32_t id)
+{
+       struct learner *l = NULL;
 
-                       ts->obj = table->type->ops.create(params,
-                               NULL,
-                               table->args,
-                               p->numa_node);
+       TAILQ_FOREACH(l, &p->learners, node)
+               if (l->id == id)
+                       return l;
 
-                       table_params_free(params);
-                       CHECK(ts->obj, ENODEV);
-               }
+       return NULL;
+}
 
-               /* 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);
+static int
+learner_match_fields_check(struct rte_swx_pipeline *p,
+                          struct rte_swx_pipeline_learner_params *params,
+                          struct header **header)
+{
+       struct header *h0 = NULL;
+       struct field *hf, *mf;
+       uint32_t i;
 
-                       memcpy(ts->default_action_data,
-                              table->default_action_data,
-                              table->action_data_size_max);
-               }
+       /* Return if no match fields. */
+       if (!params->n_fields || !params->field_names)
+               return -EINVAL;
 
-               /* ts->default_action_id. */
-               ts->default_action_id = table->default_action->id;
-       }
+       /* Check that all the match fields either belong to the same header
+        * or are all meta-data fields.
+        */
+       hf = header_field_parse(p, params->field_names[0], &h0);
+       mf = metadata_field_parse(p, params->field_names[0]);
+       if (!hf && !mf)
+               return -EINVAL;
 
-       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;
+       for (i = 1; i < params->n_fields; i++)
+               if (h0) {
+                       struct header *h;
 
-               /* ts->obj. */
-               params = selector_table_params_get(s);
-               CHECK(params, ENOMEM);
+                       hf = header_field_parse(p, params->field_names[i], &h);
+                       if (!hf || (h->id != h0->id))
+                               return -EINVAL;
+               } else {
+                       mf = metadata_field_parse(p, params->field_names[i]);
+                       if (!mf)
+                               return -EINVAL;
+               }
 
-               ts->obj = rte_swx_table_selector_create(params, NULL, p->numa_node);
+       /* Check that there are no duplicated match fields. */
+       for (i = 0; i < params->n_fields; i++) {
+               const char *field_name = params->field_names[i];
+               uint32_t j;
 
-               selector_params_free(params);
-               CHECK(ts->obj, ENODEV);
+               for (j = i + 1; j < params->n_fields; j++)
+                       if (!strcmp(params->field_names[j], field_name))
+                               return -EINVAL;
        }
 
+       /* Return. */
+       if (header)
+               *header = h0;
+
        return 0;
 }
 
-static void
-table_state_build_free(struct rte_swx_pipeline *p)
+static int
+learner_action_args_check(struct rte_swx_pipeline *p, struct action *a, const char *mf_name)
 {
-       uint32_t i;
+       struct struct_type *mst = p->metadata_st, *ast = a->st;
+       struct field *mf, *af;
+       uint32_t mf_pos, i;
+
+       if (!ast) {
+               if (mf_name)
+                       return -EINVAL;
+
+               return 0;
+       }
+
+       /* Check that mf_name is the name of a valid meta-data field. */
+       CHECK_NAME(mf_name, EINVAL);
+       mf = metadata_field_parse(p, mf_name);
+       CHECK(mf, EINVAL);
+
+       /* Check that there are enough meta-data fields, starting with the mf_name field, to cover
+        * all the action arguments.
+        */
+       mf_pos = mf - mst->fields;
+       CHECK(mst->n_fields - mf_pos >= ast->n_fields, EINVAL);
+
+       /* Check that the size of each of the identified meta-data fields matches exactly the size
+        * of the corresponding action argument.
+        */
+       for (i = 0; i < ast->n_fields; i++) {
+               mf = &mst->fields[mf_pos + i];
+               af = &ast->fields[i];
+
+               CHECK(mf->n_bits == af->n_bits, EINVAL);
+       }
+
+       return 0;
+}
+
+static int
+learner_action_learning_check(struct rte_swx_pipeline *p,
+                             struct action *action,
+                             const char **action_names,
+                             uint32_t n_actions)
+{
+       uint32_t i;
+
+       /* For each "learn" instruction of the current action, check that the learned action (i.e.
+        * the action passed as argument to the "learn" instruction) is also enabled for the
+        * current learner table.
+        */
+       for (i = 0; i < action->n_instructions; i++) {
+               struct instruction *instr = &action->instructions[i];
+               uint32_t found = 0, j;
+
+               if (instr->type != INSTR_LEARNER_LEARN)
+                       continue;
+
+               for (j = 0; j < n_actions; j++) {
+                       struct action *a;
+
+                       a = action_find(p, action_names[j]);
+                       if (!a)
+                               return -EINVAL;
+
+                       if (a->id == instr->learn.action_id)
+                               found = 1;
+               }
+
+               if (!found)
+                       return -EINVAL;
+       }
+
+       return 0;
+}
+
+int
+rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
+                             const char *name,
+                             struct rte_swx_pipeline_learner_params *params,
+                             uint32_t size,
+                             uint32_t timeout)
+{
+       struct learner *l = NULL;
+       struct action *default_action;
+       struct header *header = NULL;
+       uint32_t action_data_size_max = 0, i;
+       int status = 0;
+
+       CHECK(p, EINVAL);
+
+       CHECK_NAME(name, EINVAL);
+       CHECK(!table_find(p, name), EEXIST);
+       CHECK(!selector_find(p, name), EEXIST);
+       CHECK(!learner_find(p, name), EEXIST);
+
+       CHECK(params, EINVAL);
+
+       /* Match checks. */
+       status = learner_match_fields_check(p, params, &header);
+       if (status)
+               return status;
+
+       /* Action checks. */
+       CHECK(params->n_actions, EINVAL);
+
+       CHECK(params->action_names, EINVAL);
+       for (i = 0; i < params->n_actions; i++) {
+               const char *action_name = params->action_names[i];
+               const char *action_field_name = params->action_field_names[i];
+               struct action *a;
+               uint32_t action_data_size;
+
+               CHECK_NAME(action_name, EINVAL);
+
+               a = action_find(p, action_name);
+               CHECK(a, EINVAL);
+
+               status = learner_action_args_check(p, a, action_field_name);
+               if (status)
+                       return status;
+
+               status = learner_action_learning_check(p,
+                                                      a,
+                                                      params->action_names,
+                                                      params->n_actions);
+               if (status)
+                       return status;
+
+               action_data_size = a->st ? a->st->n_bits / 8 : 0;
+               if (action_data_size > action_data_size_max)
+                       action_data_size_max = action_data_size;
+       }
+
+       CHECK_NAME(params->default_action_name, EINVAL);
+       for (i = 0; i < p->n_actions; i++)
+               if (!strcmp(params->action_names[i],
+                           params->default_action_name))
+                       break;
+       CHECK(i < params->n_actions, EINVAL);
+
+       default_action = action_find(p, params->default_action_name);
+       CHECK((default_action->st && params->default_action_data) ||
+             !params->default_action_data, EINVAL);
+
+       /* Any other checks. */
+       CHECK(size, EINVAL);
+       CHECK(timeout, EINVAL);
+
+       /* Memory allocation. */
+       l = calloc(1, sizeof(struct learner));
+       if (!l)
+               goto nomem;
+
+       l->fields = calloc(params->n_fields, sizeof(struct field *));
+       if (!l->fields)
+               goto nomem;
+
+       l->actions = calloc(params->n_actions, sizeof(struct action *));
+       if (!l->actions)
+               goto nomem;
+
+       l->action_arg = calloc(params->n_actions, sizeof(struct field *));
+       if (!l->action_arg)
+               goto nomem;
+
+       if (action_data_size_max) {
+               l->default_action_data = calloc(1, action_data_size_max);
+               if (!l->default_action_data)
+                       goto nomem;
+       }
+
+       /* Node initialization. */
+       strcpy(l->name, name);
+
+       for (i = 0; i < params->n_fields; i++) {
+               const char *field_name = params->field_names[i];
+
+               l->fields[i] = header ?
+                       header_field_parse(p, field_name, NULL) :
+                       metadata_field_parse(p, field_name);
+       }
+
+       l->n_fields = params->n_fields;
+
+       l->header = header;
+
+       for (i = 0; i < params->n_actions; i++) {
+               const char *mf_name = params->action_field_names[i];
+
+               l->actions[i] = action_find(p, params->action_names[i]);
+
+               l->action_arg[i] = mf_name ? metadata_field_parse(p, mf_name) : NULL;
+       }
+
+       l->default_action = default_action;
+
+       if (default_action->st)
+               memcpy(l->default_action_data,
+                      params->default_action_data,
+                      default_action->st->n_bits / 8);
+
+       l->n_actions = params->n_actions;
+
+       l->default_action_is_const = params->default_action_is_const;
+
+       l->action_data_size_max = action_data_size_max;
+
+       l->size = size;
+
+       l->timeout = timeout;
+
+       l->id = p->n_learners;
+
+       /* Node add to tailq. */
+       TAILQ_INSERT_TAIL(&p->learners, l, node);
+       p->n_learners++;
+
+       return 0;
+
+nomem:
+       if (!l)
+               return -ENOMEM;
+
+       free(l->action_arg);
+       free(l->actions);
+       free(l->fields);
+       free(l);
+
+       return -ENOMEM;
+}
+
+static void
+learner_params_free(struct rte_swx_table_learner_params *params)
+{
+       if (!params)
+               return;
+
+       free(params->key_mask0);
+
+       free(params);
+}
+
+static struct rte_swx_table_learner_params *
+learner_params_get(struct learner *l)
+{
+       struct rte_swx_table_learner_params *params = NULL;
+       struct field *first, *last;
+       uint32_t i;
+
+       /* Memory allocation. */
+       params = calloc(1, sizeof(struct rte_swx_table_learner_params));
+       if (!params)
+               goto error;
+
+       /* Find first (smallest offset) and last (biggest offset) match fields. */
+       first = l->fields[0];
+       last = l->fields[0];
+
+       for (i = 0; i < l->n_fields; i++) {
+               struct field *f = l->fields[i];
+
+               if (f->offset < first->offset)
+                       first = f;
+
+               if (f->offset > last->offset)
+                       last = f;
+       }
+
+       /* Key offset and size. */
+       params->key_offset = first->offset / 8;
+       params->key_size = (last->offset + last->n_bits - first->offset) / 8;
+
+       /* Memory allocation. */
+       params->key_mask0 = calloc(1, params->key_size);
+       if (!params->key_mask0)
+               goto error;
+
+       /* Key mask. */
+       for (i = 0; i < l->n_fields; i++) {
+               struct field *f = l->fields[i];
+               uint32_t start = (f->offset - first->offset) / 8;
+               size_t size = f->n_bits / 8;
+
+               memset(&params->key_mask0[start], 0xFF, size);
+       }
+
+       /* Action data size. */
+       params->action_data_size = l->action_data_size_max;
+
+       /* Maximum number of keys. */
+       params->n_keys_max = l->size;
+
+       /* Timeout. */
+       params->key_timeout = l->timeout;
+
+       return params;
+
+error:
+       learner_params_free(params);
+       return NULL;
+}
+
+static void
+learner_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->learners)
+                       continue;
+
+               for (j = 0; j < p->n_learners; j++) {
+                       struct learner_runtime *r = &t->learners[j];
+
+                       free(r->mailbox);
+                       free(r->action_data);
+               }
+
+               free(t->learners);
+               t->learners = NULL;
+       }
+
+       if (p->learner_stats) {
+               for (i = 0; i < p->n_learners; i++)
+                       free(p->learner_stats[i].n_pkts_action);
+
+               free(p->learner_stats);
+       }
+}
+
+static int
+learner_build(struct rte_swx_pipeline *p)
+{
+       uint32_t i;
+       int status = 0;
+
+       /* Per pipeline: learner statistics. */
+       p->learner_stats = calloc(p->n_learners, sizeof(struct learner_statistics));
+       CHECK(p->learner_stats, ENOMEM);
+
+       for (i = 0; i < p->n_learners; i++) {
+               p->learner_stats[i].n_pkts_action = calloc(p->n_actions, sizeof(uint64_t));
+               CHECK(p->learner_stats[i].n_pkts_action, ENOMEM);
+       }
+
+       /* Per thread: learner run-time. */
+       for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
+               struct thread *t = &p->threads[i];
+               struct learner *l;
+
+               t->learners = calloc(p->n_learners, sizeof(struct learner_runtime));
+               if (!t->learners) {
+                       status = -ENOMEM;
+                       goto error;
+               }
+
+               TAILQ_FOREACH(l, &p->learners, node) {
+                       struct learner_runtime *r = &t->learners[l->id];
+                       uint64_t size;
+                       uint32_t j;
+
+                       /* r->mailbox. */
+                       size = rte_swx_table_learner_mailbox_size_get();
+                       if (size) {
+                               r->mailbox = calloc(1, size);
+                               if (!r->mailbox) {
+                                       status = -ENOMEM;
+                                       goto error;
+                               }
+                       }
+
+                       /* r->key. */
+                       r->key = l->header ?
+                               &t->structs[l->header->struct_id] :
+                               &t->structs[p->metadata_struct_id];
+
+                       /* r->action_data. */
+                       r->action_data = calloc(p->n_actions, sizeof(uint8_t *));
+                       if (!r->action_data) {
+                               status = -ENOMEM;
+                               goto error;
+                       }
+
+                       for (j = 0; j < l->n_actions; j++) {
+                               struct action *a = l->actions[j];
+                               struct field *mf = l->action_arg[j];
+                               uint8_t *m = t->structs[p->metadata_struct_id];
+
+                               r->action_data[a->id] = mf ? &m[mf->offset / 8] : NULL;
+                       }
+               }
+       }
+
+       return 0;
+
+error:
+       learner_build_free(p);
+       return status;
+}
+
+static void
+learner_free(struct rte_swx_pipeline *p)
+{
+       learner_build_free(p);
+
+       /* Learner tables. */
+       for ( ; ; ) {
+               struct learner *l;
+
+               l = TAILQ_FIRST(&p->learners);
+               if (!l)
+                       break;
+
+               TAILQ_REMOVE(&p->learners, l, node);
+               free(l->fields);
+               free(l->actions);
+               free(l->action_arg);
+               free(l->default_action_data);
+               free(l);
+       }
+}
+
+/*
+ * Table state.
+ */
+static int
+table_state_build(struct rte_swx_pipeline *p)
+{
+       struct table *table;
+       struct selector *s;
+       struct learner *l;
+
+       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);
+       }
+
+       TAILQ_FOREACH(l, &p->learners, node) {
+               struct rte_swx_table_state *ts = &p->table_state[p->n_tables +
+                       p->n_selectors + l->id];
+               struct rte_swx_table_learner_params *params;
+
+               /* ts->obj. */
+               params = learner_params_get(l);
+               CHECK(params, ENOMEM);
+
+               ts->obj = rte_swx_table_learner_create(params, p->numa_node);
+               learner_params_free(params);
+               CHECK(ts->obj, ENODEV);
+
+               /* ts->default_action_data. */
+               if (l->action_data_size_max) {
+                       ts->default_action_data = malloc(l->action_data_size_max);
+                       CHECK(ts->default_action_data, ENOMEM);
+
+                       memcpy(ts->default_action_data,
+                              l->default_action_data,
+                              l->action_data_size_max);
+               }
+
+               /* ts->default_action_id. */
+               ts->default_action_id = l->default_action->id;
+       }
+
+       return 0;
+}
+
+static void
+table_state_build_free(struct rte_swx_pipeline *p)
+{
+       uint32_t i;
 
        if (!p->table_state)
                return;
@@ -10312,6 +11131,17 @@ table_state_build_free(struct rte_swx_pipeline *p)
                        rte_swx_table_selector_free(ts->obj);
        }
 
+       for (i = 0; i < p->n_learners; i++) {
+               struct rte_swx_table_state *ts = &p->table_state[p->n_tables + p->n_selectors + i];
+
+               /* ts->obj. */
+               if (ts->obj)
+                       rte_swx_table_learner_free(ts->obj);
+
+               /* ts->default_action_data. */
+               free(ts->default_action_data);
+       }
+
        free(p->table_state);
        p->table_state = NULL;
 }
@@ -10653,6 +11483,7 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
        TAILQ_INIT(&pipeline->table_types);
        TAILQ_INIT(&pipeline->tables);
        TAILQ_INIT(&pipeline->selectors);
+       TAILQ_INIT(&pipeline->learners);
        TAILQ_INIT(&pipeline->regarrays);
        TAILQ_INIT(&pipeline->meter_profiles);
        TAILQ_INIT(&pipeline->metarrays);
@@ -10675,6 +11506,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)
        metarray_free(p);
        regarray_free(p);
        table_state_free(p);
+       learner_free(p);
        selector_free(p);
        table_free(p);
        action_free(p);
@@ -10759,6 +11591,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
        if (status)
                goto error;
 
+       status = learner_build(p);
+       if (status)
+               goto error;
+
        status = table_state_build(p);
        if (status)
                goto error;
@@ -10778,6 +11614,7 @@ error:
        metarray_build_free(p);
        regarray_build_free(p);
        table_state_build_free(p);
+       learner_build_free(p);
        selector_build_free(p);
        table_build_free(p);
        action_build_free(p);
@@ -10839,6 +11676,7 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
        pipeline->n_actions = n_actions;
        pipeline->n_tables = n_tables;
        pipeline->n_selectors = p->n_selectors;
+       pipeline->n_learners = p->n_learners;
        pipeline->n_regarrays = p->n_regarrays;
        pipeline->n_metarrays = p->n_metarrays;
 
@@ -11084,6 +11922,75 @@ rte_swx_ctl_selector_member_id_field_info_get(struct rte_swx_pipeline *p,
        return 0;
 }
 
+int
+rte_swx_ctl_learner_info_get(struct rte_swx_pipeline *p,
+                            uint32_t learner_id,
+                            struct rte_swx_ctl_learner_info *learner)
+{
+       struct learner *l = NULL;
+
+       if (!p || !learner)
+               return -EINVAL;
+
+       l = learner_find_by_id(p, learner_id);
+       if (!l)
+               return -EINVAL;
+
+       strcpy(learner->name, l->name);
+
+       learner->n_match_fields = l->n_fields;
+       learner->n_actions = l->n_actions;
+       learner->default_action_is_const = l->default_action_is_const;
+       learner->size = l->size;
+
+       return 0;
+}
+
+int
+rte_swx_ctl_learner_match_field_info_get(struct rte_swx_pipeline *p,
+                                        uint32_t learner_id,
+                                        uint32_t match_field_id,
+                                        struct rte_swx_ctl_table_match_field_info *match_field)
+{
+       struct learner *l;
+       struct field *f;
+
+       if (!p || (learner_id >= p->n_learners) || !match_field)
+               return -EINVAL;
+
+       l = learner_find_by_id(p, learner_id);
+       if (!l || (match_field_id >= l->n_fields))
+               return -EINVAL;
+
+       f = l->fields[match_field_id];
+       match_field->match_type = RTE_SWX_TABLE_MATCH_EXACT;
+       match_field->is_header = l->header ? 1 : 0;
+       match_field->n_bits = f->n_bits;
+       match_field->offset = f->offset;
+
+       return 0;
+}
+
+int
+rte_swx_ctl_learner_action_info_get(struct rte_swx_pipeline *p,
+                                   uint32_t learner_id,
+                                   uint32_t learner_action_id,
+                                   struct rte_swx_ctl_table_action_info *learner_action)
+{
+       struct learner *l;
+
+       if (!p || (learner_id >= p->n_learners) || !learner_action)
+               return -EINVAL;
+
+       l = learner_find_by_id(p, learner_id);
+       if (!l || (learner_action_id >= l->n_actions))
+               return -EINVAL;
+
+       learner_action->action_id = l->actions[learner_action_id]->id;
+
+       return 0;
+}
+
 int
 rte_swx_pipeline_table_state_get(struct rte_swx_pipeline *p,
                                 struct rte_swx_table_state **table_state)
@@ -11188,6 +12095,38 @@ rte_swx_ctl_pipeline_selector_stats_read(struct rte_swx_pipeline *p,
        return 0;
 }
 
+int
+rte_swx_ctl_pipeline_learner_stats_read(struct rte_swx_pipeline *p,
+                                       const char *learner_name,
+                                       struct rte_swx_learner_stats *stats)
+{
+       struct learner *l;
+       struct learner_statistics *learner_stats;
+
+       if (!p || !learner_name || !learner_name[0] || !stats || !stats->n_pkts_action)
+               return -EINVAL;
+
+       l = learner_find(p, learner_name);
+       if (!l)
+               return -EINVAL;
+
+       learner_stats = &p->learner_stats[l->id];
+
+       memcpy(stats->n_pkts_action,
+              learner_stats->n_pkts_action,
+              p->n_actions * sizeof(uint64_t));
+
+       stats->n_pkts_hit = learner_stats->n_pkts_hit[1];
+       stats->n_pkts_miss = learner_stats->n_pkts_hit[0];
+
+       stats->n_pkts_learn_ok = learner_stats->n_pkts_learn[0];
+       stats->n_pkts_learn_err = learner_stats->n_pkts_learn[1];
+
+       stats->n_pkts_forget = learner_stats->n_pkts_forget;
+
+       return 0;
+}
+
 int
 rte_swx_ctl_regarray_info_get(struct rte_swx_pipeline *p,
                              uint32_t regarray_id,
index 5afca2b..2f18a82 100644 (file)
@@ -676,6 +676,83 @@ rte_swx_pipeline_selector_config(struct rte_swx_pipeline *p,
                                 const char *name,
                                 struct rte_swx_pipeline_selector_params *params);
 
+/** Pipeline learner table parameters. */
+struct rte_swx_pipeline_learner_params {
+       /** The set of match fields for the current table.
+        * Restriction: All the match fields of the current table need to be
+        * part of the same struct, i.e. either all the match fields are part of
+        * the same header or all the match fields are part of the meta-data.
+        */
+       const char **field_names;
+
+       /** The number of match fields for the current table. Must be non-zero.
+        */
+       uint32_t n_fields;
+
+       /** The set of actions for the current table. */
+       const char **action_names;
+
+       /** The number of actions for the current table. Must be at least one.
+        */
+       uint32_t n_actions;
+
+       /** This table type allows adding the latest lookup key (typically done
+        * only in the case of lookup miss) to the table with a given action.
+        * The action arguments are picked up from the packet meta-data: for
+        * each action, a set of successive meta-data fields (with the name of
+        * the first such field provided here) is 1:1 mapped to the action
+        * arguments. These meta-data fields must be set with the actual values
+        * of the action arguments before the key add operation.
+        */
+       const char **action_field_names;
+
+       /** The default table action that gets executed on lookup miss. Must be
+        * one of the table actions included in the *action_names*.
+        */
+       const char *default_action_name;
+
+       /** Default action data. The size of this array is the action data size
+        * of the default action. Must be NULL if the default action data size
+        * is zero.
+        */
+       uint8_t *default_action_data;
+
+       /** If non-zero (true), then the default action of the current table
+        * cannot be changed. If zero (false), then the default action can be
+        * changed in the future with another action from the *action_names*
+        * list.
+        */
+       int default_action_is_const;
+};
+
+/**
+ * Pipeline learner table configure
+ *
+ * @param[out] p
+ *   Pipeline handle.
+ * @param[in] name
+ *   Learner table name.
+ * @param[in] params
+ *   Learner table parameters.
+ * @param[in] size
+ *   The maximum number of table entries. Must be non-zero.
+ * @param[in] timeout
+ *   Table entry timeout in seconds. Must be non-zero.
+ * @return
+ *   0 on success or the following error codes otherwise:
+ *   -EINVAL: Invalid argument;
+ *   -ENOMEM: Not enough space/cannot allocate memory;
+ *   -EEXIST: Learner table with this name already exists;
+ *   -ENODEV: Learner table creation error.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
+                               const char *name,
+                               struct rte_swx_pipeline_learner_params *params,
+                               uint32_t size,
+                               uint32_t timeout);
+
 /**
  * Pipeline register array configure
  *
index c57893f..d9cd1d0 100644 (file)
 #define TABLE_ACTIONS_BLOCK 4
 #define SELECTOR_BLOCK 5
 #define SELECTOR_SELECTOR_BLOCK 6
-#define APPLY_BLOCK 7
+#define LEARNER_BLOCK 7
+#define LEARNER_KEY_BLOCK 8
+#define LEARNER_ACTIONS_BLOCK 9
+#define APPLY_BLOCK 10
 
 /*
  * extobj.
@@ -1281,6 +1284,420 @@ selector_block_parse(struct selector_spec *s,
        return -EINVAL;
 }
 
+/*
+ * learner.
+ *
+ * learner {
+ *     key {
+ *             MATCH_FIELD_NAME
+ *             ...
+ *     }
+ *     actions {
+ *             ACTION_NAME args METADATA_FIELD_NAME
+ *             ...
+ *     }
+ *     default_action ACTION_NAME args none | ARGS_BYTE_ARRAY [ const ]
+ *     size SIZE
+ *     timeout TIMEOUT_IN_SECONDS
+ * }
+ */
+struct learner_spec {
+       char *name;
+       struct rte_swx_pipeline_learner_params params;
+       uint32_t size;
+       uint32_t timeout;
+};
+
+static void
+learner_spec_free(struct learner_spec *s)
+{
+       uintptr_t default_action_name;
+       uint32_t i;
+
+       if (!s)
+               return;
+
+       free(s->name);
+       s->name = NULL;
+
+       for (i = 0; i < s->params.n_fields; i++) {
+               uintptr_t name = (uintptr_t)s->params.field_names[i];
+
+               free((void *)name);
+       }
+
+       free(s->params.field_names);
+       s->params.field_names = NULL;
+
+       s->params.n_fields = 0;
+
+       for (i = 0; i < s->params.n_actions; i++) {
+               uintptr_t name = (uintptr_t)s->params.action_names[i];
+
+               free((void *)name);
+       }
+
+       free(s->params.action_names);
+       s->params.action_names = NULL;
+
+       for (i = 0; i < s->params.n_actions; i++) {
+               uintptr_t name = (uintptr_t)s->params.action_field_names[i];
+
+               free((void *)name);
+       }
+
+       free(s->params.action_field_names);
+       s->params.action_field_names = NULL;
+
+       s->params.n_actions = 0;
+
+       default_action_name = (uintptr_t)s->params.default_action_name;
+       free((void *)default_action_name);
+       s->params.default_action_name = NULL;
+
+       free(s->params.default_action_data);
+       s->params.default_action_data = NULL;
+
+       s->params.default_action_is_const = 0;
+
+       s->size = 0;
+
+       s->timeout = 0;
+}
+
+static int
+learner_key_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 key statement.";
+               return -EINVAL;
+       }
+
+       /* block_mask. */
+       *block_mask |= 1 << LEARNER_KEY_BLOCK;
+
+       return 0;
+}
+
+static int
+learner_key_block_parse(struct learner_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_field_names = NULL;
+       char *field_name = NULL;
+
+       /* Handle end of block. */
+       if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
+               *block_mask &= ~(1 << LEARNER_KEY_BLOCK);
+               return 0;
+       }
+
+       /* Check input arguments. */
+       if (n_tokens != 1) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid match field statement.";
+               return -EINVAL;
+       }
+
+       field_name = strdup(tokens[0]);
+       new_field_names = realloc(s->params.field_names, (s->params.n_fields + 1) * sizeof(char *));
+       if (!field_name || !new_field_names) {
+               free(field_name);
+               free(new_field_names);
+
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Memory allocation failed.";
+               return -ENOMEM;
+       }
+
+       s->params.field_names = new_field_names;
+       s->params.field_names[s->params.n_fields] = field_name;
+       s->params.n_fields++;
+
+       return 0;
+}
+
+static int
+learner_actions_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 actions statement.";
+               return -EINVAL;
+       }
+
+       /* block_mask. */
+       *block_mask |= 1 << LEARNER_ACTIONS_BLOCK;
+
+       return 0;
+}
+
+static int
+learner_actions_block_parse(struct learner_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_action_names = NULL;
+       const char **new_action_field_names = NULL;
+       char *action_name = NULL, *action_field_name = NULL;
+       int has_args = 1;
+
+       /* Handle end of block. */
+       if ((n_tokens == 1) && !strcmp(tokens[0], "}")) {
+               *block_mask &= ~(1 << LEARNER_ACTIONS_BLOCK);
+               return 0;
+       }
+
+       /* Check input arguments. */
+       if ((n_tokens != 3) || strcmp(tokens[1], "args")) {
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Invalid action name statement.";
+               return -EINVAL;
+       }
+
+       if (!strcmp(tokens[2], "none"))
+               has_args = 0;
+
+       action_name = strdup(tokens[0]);
+
+       if (has_args)
+               action_field_name = strdup(tokens[2]);
+
+       new_action_names = realloc(s->params.action_names,
+                                  (s->params.n_actions + 1) * sizeof(char *));
+
+       new_action_field_names = realloc(s->params.action_field_names,
+                                        (s->params.n_actions + 1) * sizeof(char *));
+
+       if (!action_name ||
+           (has_args && !action_field_name) ||
+           !new_action_names ||
+           !new_action_field_names) {
+               free(action_name);
+               free(action_field_name);
+               free(new_action_names);
+               free(new_action_field_names);
+
+               if (err_line)
+                       *err_line = n_lines;
+               if (err_msg)
+                       *err_msg = "Memory allocation failed.";
+               return -ENOMEM;
+       }
+
+       s->params.action_names = new_action_names;
+       s->params.action_names[s->params.n_actions] = action_name;
+       s->params.action_field_names = new_action_field_names;
+       s->params.action_field_names[s->params.n_actions] = action_field_name;
+       s->params.n_actions++;
+
+       return 0;
+}
+
+static int
+learner_statement_parse(struct learner_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 learner 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 << LEARNER_BLOCK;
+
+       return 0;
+}
+
+static int
+learner_block_parse(struct learner_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 << LEARNER_KEY_BLOCK))
+               return learner_key_block_parse(s,
+                                              block_mask,
+                                              tokens,
+                                              n_tokens,
+                                              n_lines,
+                                              err_line,
+                                              err_msg);
+
+       if (*block_mask & (1 << LEARNER_ACTIONS_BLOCK))
+               return learner_actions_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 << LEARNER_BLOCK);
+               return 0;
+       }
+
+       if (!strcmp(tokens[0], "key"))
+               return learner_key_statement_parse(block_mask,
+                                                  tokens,
+                                                  n_tokens,
+                                                  n_lines,
+                                                  err_line,
+                                                  err_msg);
+
+       if (!strcmp(tokens[0], "actions"))
+               return learner_actions_statement_parse(block_mask,
+                                                      tokens,
+                                                      n_tokens,
+                                                      n_lines,
+                                                      err_line,
+                                                      err_msg);
+
+       if (!strcmp(tokens[0], "default_action")) {
+               if (((n_tokens != 4) && (n_tokens != 5)) ||
+                   strcmp(tokens[2], "args") ||
+                   strcmp(tokens[3], "none") ||
+                   ((n_tokens == 5) && strcmp(tokens[4], "const"))) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid default_action statement.";
+                       return -EINVAL;
+               }
+
+               if (s->params.default_action_name) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Duplicate default_action stmt.";
+                       return -EINVAL;
+               }
+
+               s->params.default_action_name = strdup(tokens[1]);
+               if (!s->params.default_action_name) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Memory allocation failed.";
+                       return -ENOMEM;
+               }
+
+               if (n_tokens == 5)
+                       s->params.default_action_is_const = 1;
+
+               return 0;
+       }
+
+       if (!strcmp(tokens[0], "size")) {
+               char *p = tokens[1];
+
+               if (n_tokens != 2) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid size statement.";
+                       return -EINVAL;
+               }
+
+               s->size = strtoul(p, &p, 0);
+               if (p[0]) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid size argument.";
+                       return -EINVAL;
+               }
+
+               return 0;
+       }
+
+       if (!strcmp(tokens[0], "timeout")) {
+               char *p = tokens[1];
+
+               if (n_tokens != 2) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid timeout statement.";
+                       return -EINVAL;
+               }
+
+               s->timeout = strtoul(p, &p, 0);
+               if (p[0]) {
+                       if (err_line)
+                               *err_line = n_lines;
+                       if (err_msg)
+                               *err_msg = "Invalid timeout argument.";
+                       return -EINVAL;
+               }
+
+               return 0;
+       }
+
+       /* Anything else. */
+       if (err_line)
+               *err_line = n_lines;
+       if (err_msg)
+               *err_msg = "Invalid statement.";
+       return -EINVAL;
+}
+
 /*
  * regarray.
  *
@@ -1545,6 +1962,7 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
        struct action_spec action_spec = {0};
        struct table_spec table_spec = {0};
        struct selector_spec selector_spec = {0};
+       struct learner_spec learner_spec = {0};
        struct regarray_spec regarray_spec = {0};
        struct metarray_spec metarray_spec = {0};
        struct apply_spec apply_spec = {0};
@@ -1761,6 +2179,40 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
                        continue;
                }
 
+               /* learner block. */
+               if (block_mask & (1 << LEARNER_BLOCK)) {
+                       status = learner_block_parse(&learner_spec,
+                                                    &block_mask,
+                                                    tokens,
+                                                    n_tokens,
+                                                    n_lines,
+                                                    err_line,
+                                                    err_msg);
+                       if (status)
+                               goto error;
+
+                       if (block_mask & (1 << LEARNER_BLOCK))
+                               continue;
+
+                       /* End of block. */
+                       status = rte_swx_pipeline_learner_config(p,
+                               learner_spec.name,
+                               &learner_spec.params,
+                               learner_spec.size,
+                               learner_spec.timeout);
+                       if (status) {
+                               if (err_line)
+                                       *err_line = n_lines;
+                               if (err_msg)
+                                       *err_msg = "Learner table configuration error.";
+                               goto error;
+                       }
+
+                       learner_spec_free(&learner_spec);
+
+                       continue;
+               }
+
                /* apply block. */
                if (block_mask & (1 << APPLY_BLOCK)) {
                        status = apply_block_parse(&apply_spec,
@@ -1934,6 +2386,21 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
                        continue;
                }
 
+               /* learner. */
+               if (!strcmp(tokens[0], "learner")) {
+                       status = learner_statement_parse(&learner_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,
@@ -2042,6 +2509,7 @@ error:
        action_spec_free(&action_spec);
        table_spec_free(&table_spec);
        selector_spec_free(&selector_spec);
+       learner_spec_free(&learner_spec);
        regarray_spec_free(&regarray_spec);
        metarray_spec_free(&metarray_spec);
        apply_spec_free(&apply_spec);
index 2b68f58..8bc90e7 100644 (file)
@@ -129,4 +129,13 @@ EXPERIMENTAL {
        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;
+
+       #added in 21.11
+       rte_swx_ctl_pipeline_learner_default_entry_add;
+       rte_swx_ctl_pipeline_learner_default_entry_read;
+       rte_swx_ctl_pipeline_learner_stats_read;
+       rte_swx_ctl_learner_action_info_get;
+       rte_swx_ctl_learner_info_get;
+       rte_swx_ctl_learner_match_field_info_get;
+       rte_swx_pipeline_learner_config;
 };