mirror of https://github.com/F-Stack/f-stack.git
1554 lines
34 KiB
C
1554 lines
34 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2020 Intel Corporation
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <sys/queue.h>
|
|
#include <unistd.h>
|
|
|
|
#include <rte_common.h>
|
|
#include <rte_byteorder.h>
|
|
|
|
#include "rte_swx_ctl.h"
|
|
|
|
#define CHECK(condition, err_code) \
|
|
do { \
|
|
if (!(condition)) \
|
|
return -(err_code); \
|
|
} while (0)
|
|
|
|
#define ntoh64(x) rte_be_to_cpu_64(x)
|
|
#define hton64(x) rte_cpu_to_be_64(x)
|
|
|
|
#if RTE_BYTE_ORDER == RTE_LITTLE_ENDIAN
|
|
#define field_ntoh(val, n_bits) (ntoh64((val) << (64 - n_bits)))
|
|
#define field_hton(val, n_bits) (hton64((val) << (64 - n_bits)))
|
|
#else
|
|
#define field_ntoh(val, n_bits) (val)
|
|
#define field_hton(val, n_bits) (val)
|
|
#endif
|
|
|
|
struct action {
|
|
struct rte_swx_ctl_action_info info;
|
|
struct rte_swx_ctl_action_arg_info *args;
|
|
uint32_t data_size;
|
|
};
|
|
|
|
struct table {
|
|
struct rte_swx_ctl_table_info info;
|
|
struct rte_swx_ctl_table_match_field_info *mf;
|
|
struct rte_swx_ctl_table_action_info *actions;
|
|
struct rte_swx_table_ops ops;
|
|
struct rte_swx_table_params params;
|
|
|
|
struct rte_swx_table_entry_list entries;
|
|
struct rte_swx_table_entry_list pending_add;
|
|
struct rte_swx_table_entry_list pending_modify0;
|
|
struct rte_swx_table_entry_list pending_modify1;
|
|
struct rte_swx_table_entry_list pending_delete;
|
|
struct rte_swx_table_entry *pending_default;
|
|
|
|
int is_stub;
|
|
uint32_t n_add;
|
|
uint32_t n_modify;
|
|
uint32_t n_delete;
|
|
};
|
|
|
|
struct rte_swx_ctl_pipeline {
|
|
struct rte_swx_ctl_pipeline_info info;
|
|
struct rte_swx_pipeline *p;
|
|
struct action *actions;
|
|
struct table *tables;
|
|
struct rte_swx_table_state *ts;
|
|
struct rte_swx_table_state *ts_next;
|
|
int numa_node;
|
|
};
|
|
|
|
static struct action *
|
|
action_find(struct rte_swx_ctl_pipeline *ctl, const char *action_name)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < ctl->info.n_actions; i++) {
|
|
struct action *a = &ctl->actions[i];
|
|
|
|
if (!strcmp(action_name, a->info.name))
|
|
return a;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
action_free(struct rte_swx_ctl_pipeline *ctl)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (!ctl->actions)
|
|
return;
|
|
|
|
for (i = 0; i < ctl->info.n_actions; i++) {
|
|
struct action *action = &ctl->actions[i];
|
|
|
|
free(action->args);
|
|
}
|
|
|
|
free(ctl->actions);
|
|
ctl->actions = NULL;
|
|
}
|
|
|
|
static struct table *
|
|
table_find(struct rte_swx_ctl_pipeline *ctl, const char *table_name)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < ctl->info.n_tables; i++) {
|
|
struct table *table = &ctl->tables[i];
|
|
|
|
if (!strcmp(table_name, table->info.name))
|
|
return table;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
table_params_get(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
|
|
{
|
|
struct table *table = &ctl->tables[table_id];
|
|
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;
|
|
|
|
first = &table->mf[0];
|
|
last = &table->mf[table->info.n_match_fields - 1];
|
|
|
|
/* match_type. */
|
|
for (i = 0; i < table->info.n_match_fields; i++) {
|
|
struct rte_swx_ctl_table_match_field_info *f;
|
|
|
|
f = &table->mf[i];
|
|
if (f->match_type != RTE_SWX_TABLE_MATCH_EXACT)
|
|
break;
|
|
}
|
|
|
|
if (i == table->info.n_match_fields)
|
|
match_type = RTE_SWX_TABLE_MATCH_EXACT;
|
|
else if ((i == table->info.n_match_fields - 1) &&
|
|
(last->match_type == RTE_SWX_TABLE_MATCH_LPM))
|
|
match_type = RTE_SWX_TABLE_MATCH_LPM;
|
|
|
|
/* key_offset. */
|
|
key_offset = first->offset / 8;
|
|
|
|
/* key_size. */
|
|
key_size = (last->offset + last->n_bits - first->offset) / 8;
|
|
|
|
/* key_mask. */
|
|
key_mask = calloc(1, key_size);
|
|
CHECK(key_mask, ENOMEM);
|
|
|
|
for (i = 0; i < table->info.n_match_fields; i++) {
|
|
struct rte_swx_ctl_table_match_field_info *f;
|
|
uint32_t start;
|
|
size_t size;
|
|
|
|
f = &table->mf[i];
|
|
start = (f->offset - first->offset) / 8;
|
|
size = f->n_bits / 8;
|
|
|
|
memset(&key_mask[start], 0xFF, size);
|
|
}
|
|
}
|
|
|
|
/* action_data_size. */
|
|
for (i = 0; i < table->info.n_actions; i++) {
|
|
uint32_t action_id = table->actions[i].action_id;
|
|
struct action *a = &ctl->actions[action_id];
|
|
|
|
if (a->data_size > action_data_size)
|
|
action_data_size = a->data_size;
|
|
}
|
|
|
|
/* Fill in. */
|
|
table->params.match_type = match_type;
|
|
table->params.key_size = key_size;
|
|
table->params.key_offset = key_offset;
|
|
table->params.key_mask0 = key_mask;
|
|
table->params.action_data_size = action_data_size;
|
|
table->params.n_keys_max = table->info.size;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
table_entry_free(struct rte_swx_table_entry *entry)
|
|
{
|
|
if (!entry)
|
|
return;
|
|
|
|
free(entry->key);
|
|
free(entry->key_mask);
|
|
free(entry->action_data);
|
|
free(entry);
|
|
}
|
|
|
|
static struct rte_swx_table_entry *
|
|
table_entry_alloc(struct table *table)
|
|
{
|
|
struct rte_swx_table_entry *entry;
|
|
|
|
entry = calloc(1, sizeof(struct rte_swx_table_entry));
|
|
if (!entry)
|
|
goto error;
|
|
|
|
/* key, key_mask. */
|
|
if (!table->is_stub) {
|
|
entry->key = calloc(1, table->params.key_size);
|
|
if (!entry->key)
|
|
goto error;
|
|
|
|
if (table->params.match_type != RTE_SWX_TABLE_MATCH_EXACT) {
|
|
entry->key_mask = calloc(1, table->params.key_size);
|
|
if (!entry->key_mask)
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* action_data. */
|
|
if (table->params.action_data_size) {
|
|
entry->action_data = calloc(1, table->params.action_data_size);
|
|
if (!entry->action_data)
|
|
goto error;
|
|
}
|
|
|
|
return entry;
|
|
|
|
error:
|
|
table_entry_free(entry);
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
table_entry_check(struct rte_swx_ctl_pipeline *ctl,
|
|
uint32_t table_id,
|
|
struct rte_swx_table_entry *entry,
|
|
int key_check,
|
|
int data_check)
|
|
{
|
|
struct table *table = &ctl->tables[table_id];
|
|
|
|
CHECK(entry, EINVAL);
|
|
|
|
if (key_check) {
|
|
if (table->is_stub) {
|
|
/* key. */
|
|
CHECK(!entry->key, EINVAL);
|
|
|
|
/* key_mask. */
|
|
CHECK(!entry->key_mask, EINVAL);
|
|
} else {
|
|
/* key. */
|
|
CHECK(entry->key, EINVAL);
|
|
|
|
/* key_mask. */
|
|
switch (table->params.match_type) {
|
|
case RTE_SWX_TABLE_MATCH_WILDCARD:
|
|
break;
|
|
|
|
case RTE_SWX_TABLE_MATCH_LPM:
|
|
/* TBD Check that key mask is prefix. */
|
|
break;
|
|
|
|
case RTE_SWX_TABLE_MATCH_EXACT:
|
|
CHECK(!entry->key_mask, EINVAL);
|
|
break;
|
|
|
|
default:
|
|
CHECK(0, EINVAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (data_check) {
|
|
struct action *a;
|
|
uint32_t i;
|
|
|
|
/* action_id. */
|
|
for (i = 0; i < table->info.n_actions; i++)
|
|
if (entry->action_id == table->actions[i].action_id)
|
|
break;
|
|
|
|
CHECK(i < table->info.n_actions, EINVAL);
|
|
|
|
/* action_data. */
|
|
a = &ctl->actions[entry->action_id];
|
|
CHECK((a->data_size && entry->action_data) ||
|
|
(!a->data_size && !entry->action_data), EINVAL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct rte_swx_table_entry *
|
|
table_entry_duplicate(struct rte_swx_ctl_pipeline *ctl,
|
|
uint32_t table_id,
|
|
struct rte_swx_table_entry *entry,
|
|
int key_duplicate,
|
|
int data_duplicate)
|
|
{
|
|
struct table *table = &ctl->tables[table_id];
|
|
struct rte_swx_table_entry *new_entry = NULL;
|
|
|
|
if (!entry)
|
|
goto error;
|
|
|
|
new_entry = calloc(1, sizeof(struct rte_swx_table_entry));
|
|
if (!new_entry)
|
|
goto error;
|
|
|
|
if (key_duplicate && !table->is_stub) {
|
|
/* key. */
|
|
if (!entry->key)
|
|
goto error;
|
|
|
|
new_entry->key = malloc(table->params.key_size);
|
|
if (!new_entry->key)
|
|
goto error;
|
|
|
|
memcpy(new_entry->key, entry->key, table->params.key_size);
|
|
|
|
/* key_signature. */
|
|
new_entry->key_signature = entry->key_signature;
|
|
|
|
/* key_mask. */
|
|
if (table->params.match_type != RTE_SWX_TABLE_MATCH_EXACT) {
|
|
if (!entry->key_mask)
|
|
goto error;
|
|
|
|
new_entry->key_mask = malloc(table->params.key_size);
|
|
if (!new_entry->key_mask)
|
|
goto error;
|
|
|
|
memcpy(new_entry->key_mask,
|
|
entry->key_mask,
|
|
table->params.key_size);
|
|
}
|
|
}
|
|
|
|
if (data_duplicate) {
|
|
struct action *a;
|
|
uint32_t i;
|
|
|
|
/* action_id. */
|
|
for (i = 0; i < table->info.n_actions; i++)
|
|
if (entry->action_id == table->actions[i].action_id)
|
|
break;
|
|
|
|
if (i >= table->info.n_actions)
|
|
goto error;
|
|
|
|
new_entry->action_id = entry->action_id;
|
|
|
|
/* action_data. */
|
|
a = &ctl->actions[entry->action_id];
|
|
if (a->data_size) {
|
|
if (!entry->action_data)
|
|
goto error;
|
|
|
|
new_entry->action_data = malloc(a->data_size);
|
|
if (!new_entry->action_data)
|
|
goto error;
|
|
|
|
memcpy(new_entry->action_data,
|
|
entry->action_data,
|
|
a->data_size);
|
|
}
|
|
}
|
|
|
|
return new_entry;
|
|
|
|
error:
|
|
table_entry_free(new_entry);
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
entry_keycmp_em(struct rte_swx_table_entry *e0,
|
|
struct rte_swx_table_entry *e1,
|
|
uint32_t key_size)
|
|
{
|
|
if (e0->key_signature != e1->key_signature)
|
|
return 1; /* Not equal. */
|
|
|
|
if (memcmp(e0->key, e1->key, key_size))
|
|
return 1; /* Not equal. */
|
|
|
|
return 0; /* Equal */
|
|
}
|
|
|
|
static int
|
|
entry_keycmp_wm(struct rte_swx_table_entry *e0 __rte_unused,
|
|
struct rte_swx_table_entry *e1 __rte_unused,
|
|
uint32_t key_size __rte_unused)
|
|
{
|
|
/* TBD */
|
|
|
|
return 1; /* Not equal */
|
|
}
|
|
|
|
static int
|
|
entry_keycmp_lpm(struct rte_swx_table_entry *e0 __rte_unused,
|
|
struct rte_swx_table_entry *e1 __rte_unused,
|
|
uint32_t key_size __rte_unused)
|
|
{
|
|
/* TBD */
|
|
|
|
return 1; /* Not equal */
|
|
}
|
|
|
|
static int
|
|
table_entry_keycmp(struct table *table,
|
|
struct rte_swx_table_entry *e0,
|
|
struct rte_swx_table_entry *e1)
|
|
{
|
|
switch (table->params.match_type) {
|
|
case RTE_SWX_TABLE_MATCH_EXACT:
|
|
return entry_keycmp_em(e0, e1, table->params.key_size);
|
|
|
|
case RTE_SWX_TABLE_MATCH_WILDCARD:
|
|
return entry_keycmp_wm(e0, e1, table->params.key_size);
|
|
|
|
case RTE_SWX_TABLE_MATCH_LPM:
|
|
return entry_keycmp_lpm(e0, e1, table->params.key_size);
|
|
|
|
default:
|
|
return 1; /* Not equal. */
|
|
}
|
|
}
|
|
|
|
static struct rte_swx_table_entry *
|
|
table_entries_find(struct table *table, struct rte_swx_table_entry *entry)
|
|
{
|
|
struct rte_swx_table_entry *e;
|
|
|
|
TAILQ_FOREACH(e, &table->entries, node)
|
|
if (!table_entry_keycmp(table, entry, e))
|
|
return e; /* Found. */
|
|
|
|
return NULL; /* Not found. */
|
|
}
|
|
|
|
static void
|
|
table_entries_free(struct table *table)
|
|
{
|
|
for ( ; ; ) {
|
|
struct rte_swx_table_entry *entry;
|
|
|
|
entry = TAILQ_FIRST(&table->entries);
|
|
if (!entry)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&table->entries, entry, node);
|
|
table_entry_free(entry);
|
|
}
|
|
}
|
|
|
|
static struct rte_swx_table_entry *
|
|
table_pending_add_find(struct table *table, struct rte_swx_table_entry *entry)
|
|
{
|
|
struct rte_swx_table_entry *e;
|
|
|
|
TAILQ_FOREACH(e, &table->pending_add, node)
|
|
if (!table_entry_keycmp(table, entry, e))
|
|
return e; /* Found. */
|
|
|
|
return NULL; /* Not found. */
|
|
}
|
|
|
|
static void
|
|
table_pending_add_admit(struct table *table)
|
|
{
|
|
TAILQ_CONCAT(&table->entries, &table->pending_add, node);
|
|
}
|
|
|
|
static void
|
|
table_pending_add_free(struct table *table)
|
|
{
|
|
for ( ; ; ) {
|
|
struct rte_swx_table_entry *entry;
|
|
|
|
entry = TAILQ_FIRST(&table->pending_add);
|
|
if (!entry)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&table->pending_add, entry, node);
|
|
table_entry_free(entry);
|
|
}
|
|
}
|
|
|
|
static struct rte_swx_table_entry *
|
|
table_pending_modify0_find(struct table *table,
|
|
struct rte_swx_table_entry *entry)
|
|
{
|
|
struct rte_swx_table_entry *e;
|
|
|
|
TAILQ_FOREACH(e, &table->pending_modify0, node)
|
|
if (!table_entry_keycmp(table, entry, e))
|
|
return e; /* Found. */
|
|
|
|
return NULL; /* Not found. */
|
|
}
|
|
|
|
static void
|
|
table_pending_modify0_admit(struct table *table)
|
|
{
|
|
TAILQ_CONCAT(&table->entries, &table->pending_modify0, node);
|
|
}
|
|
|
|
static void
|
|
table_pending_modify0_free(struct table *table)
|
|
{
|
|
for ( ; ; ) {
|
|
struct rte_swx_table_entry *entry;
|
|
|
|
entry = TAILQ_FIRST(&table->pending_modify0);
|
|
if (!entry)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&table->pending_modify0, entry, node);
|
|
table_entry_free(entry);
|
|
}
|
|
}
|
|
|
|
static struct rte_swx_table_entry *
|
|
table_pending_modify1_find(struct table *table,
|
|
struct rte_swx_table_entry *entry)
|
|
{
|
|
struct rte_swx_table_entry *e;
|
|
|
|
TAILQ_FOREACH(e, &table->pending_modify1, node)
|
|
if (!table_entry_keycmp(table, entry, e))
|
|
return e; /* Found. */
|
|
|
|
return NULL; /* Not found. */
|
|
}
|
|
|
|
static void
|
|
table_pending_modify1_admit(struct table *table)
|
|
{
|
|
TAILQ_CONCAT(&table->entries, &table->pending_modify1, node);
|
|
}
|
|
|
|
static void
|
|
table_pending_modify1_free(struct table *table)
|
|
{
|
|
for ( ; ; ) {
|
|
struct rte_swx_table_entry *entry;
|
|
|
|
entry = TAILQ_FIRST(&table->pending_modify1);
|
|
if (!entry)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&table->pending_modify1, entry, node);
|
|
table_entry_free(entry);
|
|
}
|
|
}
|
|
|
|
static struct rte_swx_table_entry *
|
|
table_pending_delete_find(struct table *table,
|
|
struct rte_swx_table_entry *entry)
|
|
{
|
|
struct rte_swx_table_entry *e;
|
|
|
|
TAILQ_FOREACH(e, &table->pending_delete, node)
|
|
if (!table_entry_keycmp(table, entry, e))
|
|
return e; /* Found. */
|
|
|
|
return NULL; /* Not found. */
|
|
}
|
|
|
|
static void
|
|
table_pending_delete_admit(struct table *table)
|
|
{
|
|
TAILQ_CONCAT(&table->entries, &table->pending_delete, node);
|
|
}
|
|
|
|
static void
|
|
table_pending_delete_free(struct table *table)
|
|
{
|
|
for ( ; ; ) {
|
|
struct rte_swx_table_entry *entry;
|
|
|
|
entry = TAILQ_FIRST(&table->pending_delete);
|
|
if (!entry)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&table->pending_delete, entry, node);
|
|
table_entry_free(entry);
|
|
}
|
|
}
|
|
|
|
static void
|
|
table_pending_default_free(struct table *table)
|
|
{
|
|
if (!table->pending_default)
|
|
return;
|
|
|
|
free(table->pending_default->action_data);
|
|
free(table->pending_default);
|
|
table->pending_default = NULL;
|
|
}
|
|
|
|
static void
|
|
table_free(struct rte_swx_ctl_pipeline *ctl)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (!ctl->tables)
|
|
return;
|
|
|
|
for (i = 0; i < ctl->info.n_tables; i++) {
|
|
struct table *table = &ctl->tables[i];
|
|
|
|
free(table->mf);
|
|
free(table->actions);
|
|
free(table->params.key_mask0);
|
|
|
|
table_entries_free(table);
|
|
table_pending_add_free(table);
|
|
table_pending_modify0_free(table);
|
|
table_pending_modify1_free(table);
|
|
table_pending_delete_free(table);
|
|
table_pending_default_free(table);
|
|
}
|
|
|
|
free(ctl->tables);
|
|
ctl->tables = NULL;
|
|
}
|
|
|
|
static void
|
|
table_state_free(struct rte_swx_ctl_pipeline *ctl)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (!ctl->ts_next)
|
|
return;
|
|
|
|
/* For each table, free its table state. */
|
|
for (i = 0; i < ctl->info.n_tables; i++) {
|
|
struct table *table = &ctl->tables[i];
|
|
struct rte_swx_table_state *ts = &ctl->ts_next[i];
|
|
|
|
/* Default action data. */
|
|
free(ts->default_action_data);
|
|
|
|
/* Table object. */
|
|
if (!table->is_stub && table->ops.free && ts->obj)
|
|
table->ops.free(ts->obj);
|
|
}
|
|
|
|
free(ctl->ts_next);
|
|
ctl->ts_next = NULL;
|
|
}
|
|
|
|
static int
|
|
table_state_create(struct rte_swx_ctl_pipeline *ctl)
|
|
{
|
|
int status = 0;
|
|
uint32_t i;
|
|
|
|
ctl->ts_next = calloc(ctl->info.n_tables,
|
|
sizeof(struct rte_swx_table_state));
|
|
if (!ctl->ts_next) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; i < ctl->info.n_tables; i++) {
|
|
struct table *table = &ctl->tables[i];
|
|
struct rte_swx_table_state *ts = &ctl->ts[i];
|
|
struct rte_swx_table_state *ts_next = &ctl->ts_next[i];
|
|
|
|
/* Table object. */
|
|
if (!table->is_stub) {
|
|
ts_next->obj = table->ops.create(&table->params,
|
|
&table->entries,
|
|
table->info.args,
|
|
ctl->numa_node);
|
|
if (!ts_next->obj) {
|
|
status = -ENODEV;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* Default action data: duplicate from current table state. */
|
|
ts_next->default_action_data =
|
|
malloc(table->params.action_data_size);
|
|
if (!ts_next->default_action_data) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
memcpy(ts_next->default_action_data,
|
|
ts->default_action_data,
|
|
table->params.action_data_size);
|
|
|
|
ts_next->default_action_id = ts->default_action_id;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
table_state_free(ctl);
|
|
return status;
|
|
}
|
|
|
|
void
|
|
rte_swx_ctl_pipeline_free(struct rte_swx_ctl_pipeline *ctl)
|
|
{
|
|
if (!ctl)
|
|
return;
|
|
|
|
action_free(ctl);
|
|
|
|
table_state_free(ctl);
|
|
|
|
table_free(ctl);
|
|
|
|
free(ctl);
|
|
}
|
|
|
|
struct rte_swx_ctl_pipeline *
|
|
rte_swx_ctl_pipeline_create(struct rte_swx_pipeline *p)
|
|
{
|
|
struct rte_swx_ctl_pipeline *ctl = NULL;
|
|
uint32_t i;
|
|
int status;
|
|
|
|
if (!p)
|
|
goto error;
|
|
|
|
ctl = calloc(1, sizeof(struct rte_swx_ctl_pipeline));
|
|
if (!ctl)
|
|
goto error;
|
|
|
|
/* info. */
|
|
status = rte_swx_ctl_pipeline_info_get(p, &ctl->info);
|
|
if (status)
|
|
goto error;
|
|
|
|
/* numa_node. */
|
|
status = rte_swx_ctl_pipeline_numa_node_get(p, &ctl->numa_node);
|
|
if (status)
|
|
goto error;
|
|
|
|
/* p. */
|
|
ctl->p = p;
|
|
|
|
/* actions. */
|
|
ctl->actions = calloc(ctl->info.n_actions, sizeof(struct action));
|
|
if (!ctl->actions)
|
|
goto error;
|
|
|
|
for (i = 0; i < ctl->info.n_actions; i++) {
|
|
struct action *a = &ctl->actions[i];
|
|
uint32_t j;
|
|
|
|
/* info. */
|
|
status = rte_swx_ctl_action_info_get(p, i, &a->info);
|
|
if (status)
|
|
goto error;
|
|
|
|
/* args. */
|
|
a->args = calloc(a->info.n_args,
|
|
sizeof(struct rte_swx_ctl_action_arg_info));
|
|
if (!a->args)
|
|
goto error;
|
|
|
|
for (j = 0; j < a->info.n_args; j++) {
|
|
status = rte_swx_ctl_action_arg_info_get(p,
|
|
i,
|
|
j,
|
|
&a->args[j]);
|
|
if (status)
|
|
goto error;
|
|
}
|
|
|
|
/* data_size. */
|
|
for (j = 0; j < a->info.n_args; j++) {
|
|
struct rte_swx_ctl_action_arg_info *info = &a->args[j];
|
|
|
|
a->data_size += info->n_bits;
|
|
}
|
|
|
|
a->data_size = (a->data_size + 7) / 8;
|
|
}
|
|
|
|
/* tables. */
|
|
ctl->tables = calloc(ctl->info.n_tables, sizeof(struct table));
|
|
if (!ctl->tables)
|
|
goto error;
|
|
|
|
for (i = 0; i < ctl->info.n_tables; i++) {
|
|
struct table *t = &ctl->tables[i];
|
|
|
|
TAILQ_INIT(&t->entries);
|
|
TAILQ_INIT(&t->pending_add);
|
|
TAILQ_INIT(&t->pending_modify0);
|
|
TAILQ_INIT(&t->pending_modify1);
|
|
TAILQ_INIT(&t->pending_delete);
|
|
}
|
|
|
|
for (i = 0; i < ctl->info.n_tables; i++) {
|
|
struct table *t = &ctl->tables[i];
|
|
uint32_t j;
|
|
|
|
/* info. */
|
|
status = rte_swx_ctl_table_info_get(p, i, &t->info);
|
|
if (status)
|
|
goto error;
|
|
|
|
/* mf. */
|
|
t->mf = calloc(t->info.n_match_fields,
|
|
sizeof(struct rte_swx_ctl_table_match_field_info));
|
|
if (!t->mf)
|
|
goto error;
|
|
|
|
for (j = 0; j < t->info.n_match_fields; j++) {
|
|
status = rte_swx_ctl_table_match_field_info_get(p,
|
|
i,
|
|
j,
|
|
&t->mf[j]);
|
|
if (status)
|
|
goto error;
|
|
}
|
|
|
|
/* actions. */
|
|
t->actions = calloc(t->info.n_actions,
|
|
sizeof(struct rte_swx_ctl_table_action_info));
|
|
if (!t->actions)
|
|
goto error;
|
|
|
|
for (j = 0; j < t->info.n_actions; j++) {
|
|
status = rte_swx_ctl_table_action_info_get(p,
|
|
i,
|
|
j,
|
|
&t->actions[j]);
|
|
if (status ||
|
|
t->actions[j].action_id >= ctl->info.n_actions)
|
|
goto error;
|
|
}
|
|
|
|
/* ops, is_stub. */
|
|
status = rte_swx_ctl_table_ops_get(p, i, &t->ops, &t->is_stub);
|
|
if (status)
|
|
goto error;
|
|
|
|
if ((t->is_stub && t->info.n_match_fields) ||
|
|
(!t->is_stub && !t->info.n_match_fields))
|
|
goto error;
|
|
|
|
/* params. */
|
|
status = table_params_get(ctl, i);
|
|
if (status)
|
|
goto error;
|
|
}
|
|
|
|
/* ts. */
|
|
status = rte_swx_pipeline_table_state_get(p, &ctl->ts);
|
|
if (status)
|
|
goto error;
|
|
|
|
/* ts_next. */
|
|
status = table_state_create(ctl);
|
|
if (status)
|
|
goto error;
|
|
|
|
return ctl;
|
|
|
|
error:
|
|
rte_swx_ctl_pipeline_free(ctl);
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_table_entry_add(struct rte_swx_ctl_pipeline *ctl,
|
|
const char *table_name,
|
|
struct rte_swx_table_entry *entry)
|
|
{
|
|
struct table *table;
|
|
struct rte_swx_table_entry *new_entry, *existing_entry;
|
|
uint32_t table_id;
|
|
|
|
CHECK(ctl, EINVAL);
|
|
CHECK(table_name && table_name[0], EINVAL);
|
|
|
|
table = table_find(ctl, table_name);
|
|
CHECK(table, EINVAL);
|
|
table_id = table - ctl->tables;
|
|
|
|
new_entry = table_entry_duplicate(ctl, table_id, entry, 1, 1);
|
|
CHECK(new_entry, ENOMEM);
|
|
|
|
/* The new entry is found in the table->entries list:
|
|
* - Add the new entry to the table->pending_modify1 list;
|
|
* - Move the existing entry from the table->entries list to the
|
|
* table->pending_modify0 list.
|
|
*/
|
|
existing_entry = table_entries_find(table, entry);
|
|
if (existing_entry) {
|
|
TAILQ_INSERT_TAIL(&table->pending_modify1,
|
|
new_entry,
|
|
node);
|
|
|
|
TAILQ_REMOVE(&table->entries,
|
|
existing_entry,
|
|
node);
|
|
|
|
TAILQ_INSERT_TAIL(&table->pending_modify0,
|
|
existing_entry,
|
|
node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The new entry is found in the table->pending_add list:
|
|
* - Replace the entry in the table->pending_add list with the new entry
|
|
* (and free the replaced entry).
|
|
*/
|
|
existing_entry = table_pending_add_find(table, entry);
|
|
if (existing_entry) {
|
|
TAILQ_INSERT_AFTER(&table->pending_add,
|
|
existing_entry,
|
|
new_entry,
|
|
node);
|
|
|
|
TAILQ_REMOVE(&table->pending_add,
|
|
existing_entry,
|
|
node);
|
|
|
|
table_entry_free(existing_entry);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The new entry is found in the table->pending_modify1 list:
|
|
* - Replace the entry in the table->pending_modify1 list with the new
|
|
* entry (and free the replaced entry).
|
|
*/
|
|
existing_entry = table_pending_modify1_find(table, entry);
|
|
if (existing_entry) {
|
|
TAILQ_INSERT_AFTER(&table->pending_modify1,
|
|
existing_entry,
|
|
new_entry,
|
|
node);
|
|
|
|
TAILQ_REMOVE(&table->pending_modify1,
|
|
existing_entry,
|
|
node);
|
|
|
|
table_entry_free(existing_entry);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The new entry is found in the table->pending_delete list:
|
|
* - Add the new entry to the table->pending_modify1 list;
|
|
* - Move the existing entry from the table->pending_delete list to the
|
|
* table->pending_modify0 list.
|
|
*/
|
|
existing_entry = table_pending_delete_find(table, entry);
|
|
if (existing_entry) {
|
|
TAILQ_INSERT_TAIL(&table->pending_modify1,
|
|
new_entry,
|
|
node);
|
|
|
|
TAILQ_REMOVE(&table->pending_delete,
|
|
existing_entry,
|
|
node);
|
|
|
|
TAILQ_INSERT_TAIL(&table->pending_modify0,
|
|
existing_entry,
|
|
node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The new entry is not found in any of the above lists:
|
|
* - Add the new entry to the table->pending_add list.
|
|
*/
|
|
TAILQ_INSERT_TAIL(&table->pending_add, new_entry, node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_table_entry_delete(struct rte_swx_ctl_pipeline *ctl,
|
|
const char *table_name,
|
|
struct rte_swx_table_entry *entry)
|
|
{
|
|
struct table *table;
|
|
struct rte_swx_table_entry *existing_entry;
|
|
uint32_t table_id;
|
|
|
|
CHECK(ctl, EINVAL);
|
|
|
|
CHECK(table_name && table_name[0], EINVAL);
|
|
table = table_find(ctl, table_name);
|
|
CHECK(table, EINVAL);
|
|
table_id = table - ctl->tables;
|
|
|
|
CHECK(entry, EINVAL);
|
|
CHECK(!table_entry_check(ctl, table_id, entry, 1, 0), EINVAL);
|
|
|
|
/* The entry is found in the table->entries list:
|
|
* - Move the existing entry from the table->entries list to to the
|
|
* table->pending_delete list.
|
|
*/
|
|
existing_entry = table_entries_find(table, entry);
|
|
if (existing_entry) {
|
|
TAILQ_REMOVE(&table->entries,
|
|
existing_entry,
|
|
node);
|
|
|
|
TAILQ_INSERT_TAIL(&table->pending_delete,
|
|
existing_entry,
|
|
node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The entry is found in the table->pending_add list:
|
|
* - Remove the entry from the table->pending_add list and free it.
|
|
*/
|
|
existing_entry = table_pending_add_find(table, entry);
|
|
if (existing_entry) {
|
|
TAILQ_REMOVE(&table->pending_add,
|
|
existing_entry,
|
|
node);
|
|
|
|
table_entry_free(existing_entry);
|
|
}
|
|
|
|
/* The entry is found in the table->pending_modify1 list:
|
|
* - Free the entry in the table->pending_modify1 list;
|
|
* - Move the existing entry from the table->pending_modify0 list to the
|
|
* table->pending_delete list.
|
|
*/
|
|
existing_entry = table_pending_modify1_find(table, entry);
|
|
if (existing_entry) {
|
|
struct rte_swx_table_entry *real_existing_entry;
|
|
|
|
TAILQ_REMOVE(&table->pending_modify1,
|
|
existing_entry,
|
|
node);
|
|
|
|
table_entry_free(existing_entry);
|
|
|
|
real_existing_entry = table_pending_modify0_find(table, entry);
|
|
CHECK(real_existing_entry, EINVAL); /* Coverity. */
|
|
|
|
TAILQ_REMOVE(&table->pending_modify0,
|
|
real_existing_entry,
|
|
node);
|
|
|
|
TAILQ_INSERT_TAIL(&table->pending_delete,
|
|
real_existing_entry,
|
|
node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* The entry is found in the table->pending_delete list:
|
|
* - Do nothing: the existing entry is already in the
|
|
* table->pending_delete list, i.e. already marked for delete, so
|
|
* simply keep it there as it is.
|
|
*/
|
|
|
|
/* The entry is not found in any of the above lists:
|
|
* - Do nothing: no existing entry to delete.
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_table_default_entry_add(struct rte_swx_ctl_pipeline *ctl,
|
|
const char *table_name,
|
|
struct rte_swx_table_entry *entry)
|
|
{
|
|
struct table *table;
|
|
struct rte_swx_table_entry *new_entry;
|
|
uint32_t table_id;
|
|
|
|
CHECK(ctl, EINVAL);
|
|
|
|
CHECK(table_name && table_name[0], EINVAL);
|
|
table = table_find(ctl, table_name);
|
|
CHECK(table, EINVAL);
|
|
table_id = table - ctl->tables;
|
|
CHECK(!table->info.default_action_is_const, EINVAL);
|
|
|
|
new_entry = table_entry_duplicate(ctl, table_id, entry, 0, 1);
|
|
CHECK(new_entry, ENOMEM);
|
|
|
|
table_pending_default_free(table);
|
|
|
|
table->pending_default = new_entry;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
table_rollfwd0(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;
|
|
|
|
/* Reset counters. */
|
|
table->n_add = 0;
|
|
table->n_modify = 0;
|
|
table->n_delete = 0;
|
|
|
|
/* Add pending rules. */
|
|
TAILQ_FOREACH(entry, &table->pending_add, node) {
|
|
int 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++;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
static void
|
|
table_rollfwd1(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 action *a;
|
|
uint8_t *action_data;
|
|
uint64_t action_id;
|
|
|
|
/* Copy the pending default entry. */
|
|
if (!table->pending_default)
|
|
return;
|
|
|
|
action_id = table->pending_default->action_id;
|
|
action_data = table->pending_default->action_data;
|
|
a = &ctl->actions[action_id];
|
|
|
|
memcpy(ts_next->default_action_data,
|
|
action_data,
|
|
a->data_size);
|
|
|
|
ts_next->default_action_id = action_id;
|
|
}
|
|
|
|
static void
|
|
table_rollfwd2(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
|
|
{
|
|
struct table *table = &ctl->tables[table_id];
|
|
|
|
/* Move all the pending add entries to the table, as they are now part
|
|
* of the table.
|
|
*/
|
|
table_pending_add_admit(table);
|
|
|
|
/* Move all the pending modify1 entries to table, are they are now part
|
|
* of the table. Free up all the pending modify0 entries, as they are no
|
|
* longer part of the table.
|
|
*/
|
|
table_pending_modify1_admit(table);
|
|
table_pending_modify0_free(table);
|
|
|
|
/* Free up all the pending delete entries, as they are no longer part of
|
|
* the table.
|
|
*/
|
|
table_pending_delete_free(table);
|
|
|
|
/* Free up the pending default entry, as it is now part of the table. */
|
|
table_pending_default_free(table);
|
|
}
|
|
|
|
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;
|
|
|
|
table->ops.add(ts_next->obj, entry);
|
|
table->n_delete--;
|
|
}
|
|
|
|
/* 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--;
|
|
}
|
|
|
|
/* 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--;
|
|
}
|
|
}
|
|
|
|
static void
|
|
table_abort(struct rte_swx_ctl_pipeline *ctl, uint32_t table_id)
|
|
{
|
|
struct table *table = &ctl->tables[table_id];
|
|
|
|
/* Free up all the pending add entries, as none of them is part of the
|
|
* table.
|
|
*/
|
|
table_pending_add_free(table);
|
|
|
|
/* Free up all the pending modify1 entries, as none of them made it to
|
|
* the table. Add back all the pending modify0 entries, as none of them
|
|
* was deleted from the table.
|
|
*/
|
|
table_pending_modify1_free(table);
|
|
table_pending_modify0_admit(table);
|
|
|
|
/* Add back all the pending delete entries, as none of them was deleted
|
|
* from the table.
|
|
*/
|
|
table_pending_delete_admit(table);
|
|
|
|
/* Free up the pending default entry, as it is no longer going to be
|
|
* added to the table.
|
|
*/
|
|
table_pending_default_free(table);
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_commit(struct rte_swx_ctl_pipeline *ctl, int abort_on_fail)
|
|
{
|
|
struct rte_swx_table_state *ts;
|
|
int status = 0;
|
|
uint32_t i;
|
|
|
|
CHECK(ctl, EINVAL);
|
|
|
|
/* Operate the changes on the current ts_next before it becomes the new
|
|
* ts.
|
|
*/
|
|
for (i = 0; i < ctl->info.n_tables; i++) {
|
|
status = table_rollfwd0(ctl, i);
|
|
if (status)
|
|
goto rollback;
|
|
}
|
|
|
|
for (i = 0; i < ctl->info.n_tables; i++)
|
|
table_rollfwd1(ctl, i);
|
|
|
|
/* Swap the table state for the data plane. The current ts and ts_next
|
|
* become the new ts_next and ts, respectively.
|
|
*/
|
|
rte_swx_pipeline_table_state_set(ctl->p, ctl->ts_next);
|
|
usleep(100);
|
|
ts = ctl->ts;
|
|
ctl->ts = ctl->ts_next;
|
|
ctl->ts_next = ts;
|
|
|
|
/* 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_rollfwd1(ctl, i);
|
|
table_rollfwd2(ctl, i);
|
|
}
|
|
|
|
return 0;
|
|
|
|
rollback:
|
|
for (i = 0; i < ctl->info.n_tables; i++) {
|
|
table_rollback(ctl, i);
|
|
if (abort_on_fail)
|
|
table_abort(ctl, i);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
rte_swx_ctl_pipeline_abort(struct rte_swx_ctl_pipeline *ctl)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (!ctl)
|
|
return;
|
|
|
|
for (i = 0; i < ctl->info.n_tables; i++)
|
|
table_abort(ctl, i);
|
|
}
|
|
|
|
#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)
|
|
{
|
|
char *tokens[RTE_SWX_CTL_ENTRY_TOKENS_MAX];
|
|
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;
|
|
|
|
/* Check input arguments. */
|
|
if (!ctl)
|
|
goto error;
|
|
|
|
if (!table_name || !table_name[0])
|
|
goto error;
|
|
|
|
table = table_find(ctl, table_name);
|
|
if (!table)
|
|
goto error;
|
|
|
|
if (!string || !string[0])
|
|
goto error;
|
|
|
|
/* Memory allocation. */
|
|
s0 = strdup(string);
|
|
if (!s0)
|
|
goto error;
|
|
|
|
entry = table_entry_alloc(table);
|
|
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)
|
|
break;
|
|
|
|
if (n_tokens >= RTE_SWX_CTL_ENTRY_TOKENS_MAX)
|
|
goto error;
|
|
|
|
tokens[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)
|
|
goto error;
|
|
|
|
if (n_tokens != 3 + table->info.n_match_fields +
|
|
action->info.n_args * 2)
|
|
goto error;
|
|
|
|
/*
|
|
* Match.
|
|
*/
|
|
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;
|
|
|
|
val = strtoull(mf_val, &mf_val, 0);
|
|
if (mf_val[0])
|
|
goto error;
|
|
|
|
/* Endianness conversion. */
|
|
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],
|
|
(uint8_t *)&val,
|
|
mf->n_bits / 8);
|
|
|
|
/* TBD Set entry->key_mask for wildcard and LPM tables. */
|
|
}
|
|
|
|
/*
|
|
* Action.
|
|
*/
|
|
/* 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;
|
|
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];
|
|
|
|
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] != ')'))
|
|
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)
|
|
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;
|
|
}
|
|
|
|
free(s0);
|
|
return entry;
|
|
|
|
error:
|
|
table_entry_free(entry);
|
|
free(s0);
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_table_fprintf(FILE *f,
|
|
struct rte_swx_ctl_pipeline *ctl,
|
|
const char *table_name)
|
|
{
|
|
struct table *table;
|
|
struct rte_swx_table_entry *entry;
|
|
uint32_t n_entries = 0, i;
|
|
|
|
if (!f || !ctl || !table_name || !table_name[0])
|
|
return -EINVAL;
|
|
|
|
table = table_find(ctl, table_name);
|
|
if (!table)
|
|
return -EINVAL;
|
|
|
|
/* Table. */
|
|
fprintf(f, "# Table %s: key size %u bytes, key offset %u, key mask [",
|
|
table->info.name,
|
|
table->params.key_size,
|
|
table->params.key_offset);
|
|
|
|
for (i = 0; i < table->params.key_size; i++)
|
|
fprintf(f, "%02x", table->params.key_mask0[i]);
|
|
|
|
fprintf(f, "], action data size %u bytes\n",
|
|
table->params.action_data_size);
|
|
|
|
/* 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");
|
|
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");
|
|
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");
|
|
n_entries++;
|
|
}
|
|
|
|
fprintf(f, "# Table %s currently has %u entries.\n",
|
|
table_name,
|
|
n_entries);
|
|
return 0;
|
|
}
|