+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);
+}
+