pipeline: support learner tables

Add pipeline level support for learner tables.

Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
This commit is contained in:
Cristian Dumitrescu 2021-09-20 16:01:31 +01:00 committed by Thomas Monjalon
parent 0c06fa3bfa
commit 4f59d37261
6 changed files with 2160 additions and 8 deletions

View 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 @@ rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail)
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 @@ rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl,
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,

View 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
*

File diff suppressed because it is too large Load Diff

View 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
*

View File

@ -20,7 +20,10 @@
#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 @@ rte_swx_pipeline_build_from_spec(struct rte_swx_pipeline *p,
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);

View 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;
};