struct table {
struct rte_swx_ctl_table_info info;
struct rte_swx_ctl_table_match_field_info *mf;
+
+ /* Match field with the smallest offset. */
+ struct rte_swx_ctl_table_match_field_info *mf_first;
+
+ /* Match field with the biggest offset. */
+ struct rte_swx_ctl_table_match_field_info *mf_last;
+
struct rte_swx_ctl_table_action_info *actions;
struct rte_swx_table_ops ops;
struct rte_swx_table_params params;
+ /* Set of "stable" keys: these keys are currently part of the table;
+ * these keys will be preserved with no action data changes after the
+ * next commit.
+ */
struct rte_swx_table_entry_list entries;
+
+ /* Set of new keys: these keys are currently NOT part of the table;
+ * these keys will be added to the table on the next commit, if
+ * the commit operation is successful.
+ */
struct rte_swx_table_entry_list pending_add;
+
+ /* Set of keys to be modified: these keys are currently part of the
+ * table; these keys are still going to be part of the table after the
+ * next commit, but their action data will be modified if the commit
+ * operation is successful. The modify0 list contains the keys with the
+ * current action data, the modify1 list contains the keys with the
+ * modified action data.
+ */
struct rte_swx_table_entry_list pending_modify0;
struct rte_swx_table_entry_list pending_modify1;
+
+ /* Set of keys to be deleted: these keys are currently part of the
+ * table; these keys are to be deleted from the table on the next
+ * commit, if the commit operation is successful.
+ */
struct rte_swx_table_entry_list pending_delete;
+
+ /* 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;
int is_stub;
table_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
{
struct table *table = &ctl->tables[table_id];
+ struct rte_swx_ctl_table_match_field_info *first = NULL, *last = NULL;
uint8_t *key_mask = NULL;
enum rte_swx_table_match_type match_type = RTE_SWX_TABLE_MATCH_WILDCARD;
uint32_t key_size = 0, key_offset = 0, action_data_size = 0, i;
if (table->info.n_match_fields) {
- struct rte_swx_ctl_table_match_field_info *first, *last;
- uint32_t i;
+ uint32_t n_match_fields_em = 0, i;
+ /* Find first (smallest offset) and last (biggest offset) match fields. */
first = &table->mf[0];
- last = &table->mf[table->info.n_match_fields - 1];
+ last = &table->mf[0];
+
+ for (i = 1; i < table->info.n_match_fields; i++) {
+ struct rte_swx_ctl_table_match_field_info *f = &table->mf[i];
+
+ if (f->offset < first->offset)
+ first = f;
+
+ if (f->offset > last->offset)
+ last = f;
+ }
/* match_type. */
for (i = 0; i < table->info.n_match_fields; i++) {
- struct rte_swx_ctl_table_match_field_info *f;
+ struct rte_swx_ctl_table_match_field_info *f = &table->mf[i];
- f = &table->mf[i];
- if (f->match_type != RTE_SWX_TABLE_MATCH_EXACT)
- break;
+ if (f->match_type == RTE_SWX_TABLE_MATCH_EXACT)
+ n_match_fields_em++;
}
- if (i == table->info.n_match_fields)
+ if (n_match_fields_em == table->info.n_match_fields)
match_type = RTE_SWX_TABLE_MATCH_EXACT;
- else if ((i == table->info.n_match_fields - 1) &&
+ else if ((n_match_fields_em == table->info.n_match_fields - 1) &&
(last->match_type == RTE_SWX_TABLE_MATCH_LPM))
match_type = RTE_SWX_TABLE_MATCH_LPM;
CHECK(key_mask, ENOMEM);
for (i = 0; i < table->info.n_match_fields; i++) {
- struct rte_swx_ctl_table_match_field_info *f;
+ struct rte_swx_ctl_table_match_field_info *f = &table->mf[i];
uint32_t start;
size_t size;
- f = &table->mf[i];
start = (f->offset - first->offset) / 8;
size = f->n_bits / 8;
table->params.action_data_size = action_data_size;
table->params.n_keys_max = table->info.size;
+ table->mf_first = first;
+ table->mf_last = last;
+
return 0;
}
/* action_data. */
a = &ctl->actions[entry->action_id];
- CHECK((a->data_size && entry->action_data) ||
- (!a->data_size && !entry->action_data), EINVAL);
+ CHECK(!(a->data_size && !entry->action_data), EINVAL);
}
return 0;
entry->key_mask,
table->params.key_size);
}
+
+ /* key_priority. */
+ new_entry->key_priority = entry->key_priority;
}
if (data_duplicate) {
table->pending_default = NULL;
}
+static int
+table_is_update_pending(struct table *table, int consider_pending_default)
+{
+ struct rte_swx_table_entry *e;
+ uint32_t n = 0;
+
+ /* Pending add. */
+ TAILQ_FOREACH(e, &table->pending_add, node)
+ n++;
+
+ /* Pending modify. */
+ TAILQ_FOREACH(e, &table->pending_modify1, node)
+ n++;
+
+ /* Pending delete. */
+ TAILQ_FOREACH(e, &table->pending_delete, node)
+ n++;
+
+ /* Pending default. */
+ if (consider_pending_default && table->pending_default)
+ n++;
+
+ return n;
+}
+
static void
table_free(struct rte_swx_ctl_pipeline *ctl)
{
struct rte_swx_table_state *ts_next = &ctl->ts_next[i];
/* Table object. */
- if (!table->is_stub) {
+ if (!table->is_stub && table->ops.add) {
ts_next->obj = table->ops.create(&table->params,
&table->entries,
table->info.args,
}
}
+ if (!table->is_stub && !table->ops.add)
+ ts_next->obj = ts->obj;
+
/* Default action data: duplicate from current table state. */
ts_next->default_action_data =
malloc(table->params.action_data_size);
return 0;
}
+
+static void
+table_entry_list_free(struct rte_swx_table_entry_list *list)
+{
+ for ( ; ; ) {
+ struct rte_swx_table_entry *entry;
+
+ entry = TAILQ_FIRST(list);
+ if (!entry)
+ break;
+
+ TAILQ_REMOVE(list, entry, node);
+ table_entry_free(entry);
+ }
+}
+
static int
-table_rollfwd0(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
+table_entry_list_duplicate(struct rte_swx_ctl_pipeline *ctl,
+ uint32_t table_id,
+ struct rte_swx_table_entry_list *dst,
+ struct rte_swx_table_entry_list *src)
+{
+ struct rte_swx_table_entry *src_entry;
+
+ TAILQ_FOREACH(src_entry, src, node) {
+ struct rte_swx_table_entry *dst_entry;
+
+ dst_entry = table_entry_duplicate(ctl, table_id, src_entry, 1, 1);
+ if (!dst_entry)
+ goto error;
+
+ TAILQ_INSERT_TAIL(dst, dst_entry, node);
+ }
+
+ return 0;
+
+error:
+ table_entry_list_free(dst);
+ return -ENOMEM;
+}
+
+/* This commit stage contains all the operations that can fail; in case ANY of
+ * them fails for ANY table, ALL of them are rolled back for ALL the tables.
+ */
+static int
+table_rollfwd0(struct rte_swx_ctl_pipeline *ctl,
+ uint32_t table_id,
+ uint32_t after_swap)
{
struct table *table = &ctl->tables[table_id];
+ struct rte_swx_table_state *ts = &ctl->ts[table_id];
struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
- struct rte_swx_table_entry *entry;
- /* Reset counters. */
- table->n_add = 0;
- table->n_modify = 0;
- table->n_delete = 0;
+ if (table->is_stub || !table_is_update_pending(table, 0))
+ return 0;
- /* Add pending rules. */
- TAILQ_FOREACH(entry, &table->pending_add, node) {
- int status;
+ /*
+ * Current table supports incremental update.
+ */
+ if (table->ops.add) {
+ /* Reset counters. */
+ table->n_add = 0;
+ table->n_modify = 0;
+ table->n_delete = 0;
- status = table->ops.add(ts_next->obj, entry);
- if (status)
- return status;
+ /* Add pending rules. */
+ struct rte_swx_table_entry *entry;
- table->n_add++;
- }
+ TAILQ_FOREACH(entry, &table->pending_add, node) {
+ int status;
- /* Modify pending rules. */
- TAILQ_FOREACH(entry, &table->pending_modify1, node) {
- int status;
+ status = table->ops.add(ts_next->obj, entry);
+ if (status)
+ return status;
- status = table->ops.add(ts_next->obj, entry);
- if (status)
- return status;
+ table->n_add++;
+ }
+
+ /* Modify pending rules. */
+ TAILQ_FOREACH(entry, &table->pending_modify1, node) {
+ int status;
+
+ status = table->ops.add(ts_next->obj, entry);
+ if (status)
+ return status;
- table->n_modify++;
+ table->n_modify++;
+ }
+
+ /* Delete pending rules. */
+ TAILQ_FOREACH(entry, &table->pending_delete, node) {
+ int status;
+
+ status = table->ops.del(ts_next->obj, entry);
+ if (status)
+ return status;
+
+ table->n_delete++;
+ }
+
+ return 0;
}
- /* Delete pending rules. */
- TAILQ_FOREACH(entry, &table->pending_delete, node) {
+ /*
+ * Current table does NOT support incremental update.
+ */
+ if (!after_swap) {
+ struct rte_swx_table_entry_list list;
int status;
- status = table->ops.del(ts_next->obj, entry);
+ /* Create updated list of entries included. */
+ TAILQ_INIT(&list);
+
+ status = table_entry_list_duplicate(ctl,
+ table_id,
+ &list,
+ &table->entries);
if (status)
- return status;
+ goto error;
+
+ status = table_entry_list_duplicate(ctl,
+ table_id,
+ &list,
+ &table->pending_add);
+ if (status)
+ goto error;
+
+ status = table_entry_list_duplicate(ctl,
+ table_id,
+ &list,
+ &table->pending_modify1);
+ if (status)
+ goto error;
+
+ /* Create new table object with the updates included. */
+ ts_next->obj = table->ops.create(&table->params,
+ &list,
+ table->info.args,
+ ctl->numa_node);
+ if (!ts_next->obj) {
+ status = -ENODEV;
+ goto error;
+ }
+
+ table_entry_list_free(&list);
+
+ return 0;
- table->n_delete++;
+error:
+ table_entry_list_free(&list);
+ return status;
}
+ /* Free the old table object. */
+ if (ts_next->obj && table->ops.free)
+ table->ops.free(ts_next->obj);
+
+ /* Copy over the new table object. */
+ ts_next->obj = ts->obj;
+
return 0;
}
+/* This commit stage contains all the operations that cannot fail. They are
+ * executed only if the previous stage was successful for ALL the tables. Hence,
+ * none of these operations has to be rolled back for ANY table.
+ */
static void
table_rollfwd1(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
{
ts_next->default_action_id = action_id;
}
+/* This last commit stage is simply finalizing a successful commit operation.
+ * This stage is only executed if all the previous stages were successful. This
+ * stage cannot fail.
+ */
static void
table_rollfwd2(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
{
table_pending_default_free(table);
}
+/* The rollback stage is only executed when the commit failed, i.e. ANY of the
+ * commit operations that can fail did fail for ANY table. It reverts ALL the
+ * tables to their state before the commit started, as if the commit never
+ * happened.
+ */
static void
table_rollback(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
{
struct table *table = &ctl->tables[table_id];
struct rte_swx_table_state *ts_next = &ctl->ts_next[table_id];
- struct rte_swx_table_entry *entry;
- /* Add back all the entries that were just deleted. */
- TAILQ_FOREACH(entry, &table->pending_delete, node) {
- if (!table->n_delete)
- break;
+ if (table->is_stub || !table_is_update_pending(table, 0))
+ return;
- table->ops.add(ts_next->obj, entry);
- table->n_delete--;
- }
+ if (table->ops.add) {
+ struct rte_swx_table_entry *entry;
- /* Add back the old copy for all the entries that were just
- * modified.
- */
- TAILQ_FOREACH(entry, &table->pending_modify0, node) {
- if (!table->n_modify)
- break;
+ /* Add back all the entries that were just deleted. */
+ TAILQ_FOREACH(entry, &table->pending_delete, node) {
+ if (!table->n_delete)
+ break;
- table->ops.add(ts_next->obj, entry);
- table->n_modify--;
- }
+ table->ops.add(ts_next->obj, entry);
+ table->n_delete--;
+ }
- /* Delete all the entries that were just added. */
- TAILQ_FOREACH(entry, &table->pending_add, node) {
- if (!table->n_add)
- break;
+ /* Add back the old copy for all the entries that were just
+ * modified.
+ */
+ TAILQ_FOREACH(entry, &table->pending_modify0, node) {
+ if (!table->n_modify)
+ break;
+
+ table->ops.add(ts_next->obj, entry);
+ table->n_modify--;
+ }
- table->ops.del(ts_next->obj, entry);
- table->n_add--;
+ /* Delete all the entries that were just added. */
+ TAILQ_FOREACH(entry, &table->pending_add, node) {
+ if (!table->n_add)
+ break;
+
+ table->ops.del(ts_next->obj, entry);
+ table->n_add--;
+ }
+ } else {
+ struct rte_swx_table_state *ts = &ctl->ts[table_id];
+
+ /* Free the new table object, as update was cancelled. */
+ if (ts_next->obj && table->ops.free)
+ table->ops.free(ts_next->obj);
+
+ /* Reinstate the old table object. */
+ ts_next->obj = ts->obj;
}
}
+/* This stage is conditionally executed (as instructed by the user) after a
+ * failed commit operation to remove ALL the pending work for ALL the tables.
+ */
static void
table_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
{
* ts.
*/
for (i = 0; i < ctl->info.n_tables; i++) {
- status = table_rollfwd0(ctl, i);
+ status = table_rollfwd0(ctl, i, 0);
if (status)
goto rollback;
}
/* Operate the changes on the current ts_next, which is the previous ts.
*/
for (i = 0; i < ctl->info.n_tables; i++) {
- table_rollfwd0(ctl, i);
+ table_rollfwd0(ctl, i, 1);
table_rollfwd1(ctl, i);
table_rollfwd2(ctl, i);
}
table_abort(ctl, i);
}
+static int
+token_is_comment(const char *token)
+{
+ if ((token[0] == '#') ||
+ (token[0] == ';') ||
+ ((token[0] == '/') && (token[1] == '/')))
+ return 1; /* TRUE. */
+
+ return 0; /* FALSE. */
+}
+
#define RTE_SWX_CTL_ENTRY_TOKENS_MAX 256
struct rte_swx_table_entry *
rte_swx_ctl_pipeline_table_entry_read(struct rte_swx_ctl_pipeline *ctl,
const char *table_name,
- const char *string)
+ const char *string,
+ int *is_blank_or_comment)
{
- char *tokens[RTE_SWX_CTL_ENTRY_TOKENS_MAX];
+ char *token_array[RTE_SWX_CTL_ENTRY_TOKENS_MAX], **tokens;
struct table *table;
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)
char *token;
token = strtok_r(s, " \f\n\r\t\v", &s);
- if (!token)
+ if (!token || token_is_comment(token))
break;
if (n_tokens >= RTE_SWX_CTL_ENTRY_TOKENS_MAX)
goto error;
- tokens[n_tokens] = token;
+ token_array[n_tokens] = token;
n_tokens++;
}
- if ((n_tokens < 3 + table->info.n_match_fields) ||
- strcmp(tokens[0], "match") ||
- strcmp(tokens[1 + table->info.n_match_fields], "action"))
- goto error;
-
- action = action_find(ctl, tokens[2 + table->info.n_match_fields]);
- if (!action)
+ if (!n_tokens) {
+ blank_or_comment = 1;
goto error;
+ }
- if (n_tokens != 3 + table->info.n_match_fields +
- action->info.n_args * 2)
- goto error;
+ tokens = token_array;
/*
* Match.
*/
+ if (n_tokens && strcmp(tokens[0], "match"))
+ goto action;
+
+ if (n_tokens < 1 + table->info.n_match_fields)
+ goto error;
+
for (i = 0; i < table->info.n_match_fields; i++) {
struct rte_swx_ctl_table_match_field_info *mf = &table->mf[i];
- char *mf_val = tokens[1 + i];
- uint64_t val;
+ char *mf_val = tokens[1 + i], *mf_mask = NULL;
+ uint64_t val, mask = UINT64_MAX;
+ uint32_t offset = (mf->offset - table->mf_first->offset) / 8;
+
+ /*
+ * Mask.
+ */
+ mf_mask = strchr(mf_val, '/');
+ if (mf_mask) {
+ *mf_mask = 0;
+ mf_mask++;
+
+ /* Parse. */
+ mask = strtoull(mf_mask, &mf_mask, 0);
+ if (mf_mask[0])
+ goto error;
+
+ /* Endianness conversion. */
+ if (mf->is_header)
+ mask = field_hton(mask, mf->n_bits);
+ }
+ /* Copy to entry. */
+ if (entry->key_mask)
+ memcpy(&entry->key_mask[offset],
+ (uint8_t *)&mask,
+ mf->n_bits / 8);
+
+ /*
+ * Value.
+ */
+ /* Parse. */
val = strtoull(mf_val, &mf_val, 0);
if (mf_val[0])
goto error;
if (mf->is_header)
val = field_hton(val, mf->n_bits);
- /* Copy key and key_mask to entry. */
- memcpy(&entry->key[(mf->offset - table->mf[0].offset) / 8],
+ /* Copy to entry. */
+ memcpy(&entry->key[offset],
(uint8_t *)&val,
mf->n_bits / 8);
+ }
- /* TBD Set entry->key_mask for wildcard and LPM tables. */
+ tokens += 1 + table->info.n_match_fields;
+ n_tokens -= 1 + table->info.n_match_fields;
+
+ /*
+ * Match priority.
+ */
+ if (n_tokens && !strcmp(tokens[0], "priority")) {
+ char *priority = tokens[1];
+ uint32_t val;
+
+ if (n_tokens < 2)
+ goto error;
+
+ /* Parse. */
+ val = strtoul(priority, &priority, 0);
+ if (priority[0])
+ goto error;
+
+ /* Copy to entry. */
+ entry->key_priority = val;
+
+ tokens += 2;
+ n_tokens -= 2;
}
/*
* Action.
*/
+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;
struct rte_swx_ctl_action_arg_info *arg = &action->args[i];
char *arg_name, *arg_val;
uint64_t val;
- int is_nbo = 0;
- arg_name = tokens[3 + table->info.n_match_fields + i * 2];
- arg_val = tokens[3 + table->info.n_match_fields + i * 2 + 1];
+ arg_name = tokens[2 + i * 2];
+ arg_val = tokens[2 + i * 2 + 1];
- if (strcmp(arg_name, arg->name) ||
- (strlen(arg_val) < 4) ||
- ((arg_val[0] != 'H') && (arg_val[0] != 'N')) ||
- (arg_val[1] != '(') ||
- (arg_val[strlen(arg_val) - 1] != ')'))
+ if (strcmp(arg_name, arg->name))
goto error;
- if (arg_val[0] == 'N')
- is_nbo = 1;
-
- arg_val[strlen(arg_val) - 1] = 0; /* Remove the ')'. */
- arg_val += 2; /* Remove the "H(" or "N(". */
-
val = strtoull(arg_val, &arg_val, 0);
if (arg_val[0])
goto error;
/* Endianness conversion. */
- if (is_nbo)
+ if (arg->is_network_byte_order)
val = field_hton(val, arg->n_bits);
/* Copy to entry. */
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,
+ struct table *table,
+ struct rte_swx_table_entry *entry)
+{
+ struct action *action = &ctl->actions[entry->action_id];
+ uint32_t i;
+
+ fprintf(f, "match ");
+ for (i = 0; i < table->params.key_size; i++)
+ fprintf(f, "%02x", entry->key[i]);
+
+ if (entry->key_mask) {
+ fprintf(f, "/");
+ for (i = 0; i < table->params.key_size; i++)
+ fprintf(f, "%02x", entry->key_mask[i]);
+ }
+
+ fprintf(f, " priority %u", entry->key_priority);
+
+ fprintf(f, " action %s ", action->info.name);
+ for (i = 0; i < action->data_size; i++)
+ fprintf(f, "%02x", entry->action_data[i]);
+
+ fprintf(f, "\n");
+}
+
int
rte_swx_ctl_pipeline_table_fprintf(FILE *f,
struct rte_swx_ctl_pipeline *ctl,
/* Table entries. */
TAILQ_FOREACH(entry, &table->entries, node) {
- struct action *action = &ctl->actions[entry->action_id];
-
- fprintf(f, "match ");
- for (i = 0; i < table->params.key_size; i++)
- fprintf(f, "%02x", entry->key[i]);
-
- fprintf(f, " action %s ", action->info.name);
- for (i = 0; i < action->data_size; i++)
- fprintf(f, "%02x", entry->action_data[i]);
-
- fprintf(f, "\n");
+ table_entry_printf(f, ctl, table, entry);
n_entries++;
}
TAILQ_FOREACH(entry, &table->pending_modify0, node) {
- struct action *action = &ctl->actions[entry->action_id];
-
- fprintf(f, "match ");
- for (i = 0; i < table->params.key_size; i++)
- fprintf(f, "%02x", entry->key[i]);
-
- fprintf(f, " action %s ", action->info.name);
- for (i = 0; i < action->data_size; i++)
- fprintf(f, "%02x", entry->action_data[i]);
-
- fprintf(f, "\n");
+ table_entry_printf(f, ctl, table, entry);
n_entries++;
}
TAILQ_FOREACH(entry, &table->pending_delete, node) {
- struct action *action = &ctl->actions[entry->action_id];
-
- fprintf(f, "match ");
- for (i = 0; i < table->params.key_size; i++)
- fprintf(f, "%02x", entry->key[i]);
-
- fprintf(f, " action %s ", action->info.name);
- for (i = 0; i < action->data_size; i++)
- fprintf(f, "%02x", entry->action_data[i]);
-
- fprintf(f, "\n");
+ table_entry_printf(f, ctl, table, entry);
n_entries++;
}