0a00384a76
Fix segmentation fault due to null pointer dereferencing inside the
"mirror" instruction when number of mirroring slots is set to 0. This
was taking place when the "mirror" instruction was used without the
mirror feature being properly configured, i.e. the API function
rte_swx_pipeline_mirroring_config was not called at initialization.
Fixes: dac0ecd909
("pipeline: support packet mirroring")
Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
13490 lines
302 KiB
C
13490 lines
302 KiB
C
/* SPDX-License-Identifier: BSD-3-Clause
|
|
* Copyright(c) 2020 Intel Corporation
|
|
*/
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
#include <dlfcn.h>
|
|
|
|
#include <rte_jhash.h>
|
|
#include <rte_hash_crc.h>
|
|
|
|
#include <rte_swx_port_ethdev.h>
|
|
#include <rte_swx_port_fd.h>
|
|
#include <rte_swx_port_ring.h>
|
|
#include "rte_swx_port_source_sink.h"
|
|
|
|
#include <rte_swx_table_em.h>
|
|
#include <rte_swx_table_wm.h>
|
|
|
|
#include "rte_swx_pipeline_internal.h"
|
|
|
|
#define CHECK(condition, err_code) \
|
|
do { \
|
|
if (!(condition)) \
|
|
return -(err_code); \
|
|
} while (0)
|
|
|
|
#define CHECK_NAME(name, err_code) \
|
|
CHECK((name) && \
|
|
(name)[0] && \
|
|
(strnlen((name), RTE_SWX_NAME_SIZE) < RTE_SWX_NAME_SIZE), \
|
|
err_code)
|
|
|
|
#define CHECK_INSTRUCTION(instr, err_code) \
|
|
CHECK((instr) && \
|
|
(instr)[0] && \
|
|
(strnlen((instr), RTE_SWX_INSTRUCTION_SIZE) < \
|
|
RTE_SWX_INSTRUCTION_SIZE), \
|
|
err_code)
|
|
|
|
/*
|
|
* Environment.
|
|
*/
|
|
#ifndef RTE_SWX_PIPELINE_HUGE_PAGES_DISABLE
|
|
|
|
#include <rte_malloc.h>
|
|
|
|
static void *
|
|
env_malloc(size_t size, size_t alignment, int numa_node)
|
|
{
|
|
return rte_zmalloc_socket(NULL, size, alignment, numa_node);
|
|
}
|
|
|
|
static void
|
|
env_free(void *start, size_t size __rte_unused)
|
|
{
|
|
rte_free(start);
|
|
}
|
|
|
|
#else
|
|
|
|
#include <numa.h>
|
|
|
|
static void *
|
|
env_malloc(size_t size, size_t alignment __rte_unused, int numa_node)
|
|
{
|
|
void *start;
|
|
|
|
if (numa_available() == -1)
|
|
return NULL;
|
|
|
|
start = numa_alloc_onnode(size, numa_node);
|
|
if (!start)
|
|
return NULL;
|
|
|
|
memset(start, 0, size);
|
|
return start;
|
|
}
|
|
|
|
static void
|
|
env_free(void *start, size_t size)
|
|
{
|
|
if (numa_available() == -1)
|
|
return;
|
|
|
|
numa_free(start, size);
|
|
}
|
|
|
|
#endif
|
|
|
|
/*
|
|
* Struct.
|
|
*/
|
|
static struct struct_type *
|
|
struct_type_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct struct_type *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->struct_types, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct field *
|
|
struct_type_field_find(struct struct_type *st, const char *name)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < st->n_fields; i++) {
|
|
struct field *f = &st->fields[i];
|
|
|
|
if (strcmp(f->name, name) == 0)
|
|
return f;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_struct_type_register(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct rte_swx_field_params *fields,
|
|
uint32_t n_fields,
|
|
int last_field_has_variable_size)
|
|
{
|
|
struct struct_type *st;
|
|
uint32_t i;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(fields, EINVAL);
|
|
CHECK(n_fields, EINVAL);
|
|
|
|
for (i = 0; i < n_fields; i++) {
|
|
struct rte_swx_field_params *f = &fields[i];
|
|
int var_size = ((i == n_fields - 1) && last_field_has_variable_size) ? 1 : 0;
|
|
uint32_t j;
|
|
|
|
CHECK_NAME(f->name, EINVAL);
|
|
CHECK(f->n_bits, EINVAL);
|
|
CHECK((f->n_bits <= 64) || var_size, EINVAL);
|
|
CHECK((f->n_bits & 7) == 0, EINVAL);
|
|
|
|
for (j = 0; j < i; j++) {
|
|
struct rte_swx_field_params *f_prev = &fields[j];
|
|
|
|
CHECK(strcmp(f->name, f_prev->name), EINVAL);
|
|
}
|
|
}
|
|
|
|
CHECK(!struct_type_find(p, name), EEXIST);
|
|
|
|
/* Node allocation. */
|
|
st = calloc(1, sizeof(struct struct_type));
|
|
CHECK(st, ENOMEM);
|
|
|
|
st->fields = calloc(n_fields, sizeof(struct field));
|
|
if (!st->fields) {
|
|
free(st);
|
|
CHECK(0, ENOMEM);
|
|
}
|
|
|
|
/* Node initialization. */
|
|
strcpy(st->name, name);
|
|
for (i = 0; i < n_fields; i++) {
|
|
struct field *dst = &st->fields[i];
|
|
struct rte_swx_field_params *src = &fields[i];
|
|
int var_size = ((i == n_fields - 1) && last_field_has_variable_size) ? 1 : 0;
|
|
|
|
strcpy(dst->name, src->name);
|
|
dst->n_bits = src->n_bits;
|
|
dst->offset = st->n_bits;
|
|
dst->var_size = var_size;
|
|
|
|
st->n_bits += src->n_bits;
|
|
st->n_bits_min += var_size ? 0 : src->n_bits;
|
|
}
|
|
st->n_fields = n_fields;
|
|
st->var_size = last_field_has_variable_size;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->struct_types, st, node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
struct_build(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
|
|
t->structs = calloc(p->n_structs, sizeof(uint8_t *));
|
|
CHECK(t->structs, ENOMEM);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
struct_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
|
|
free(t->structs);
|
|
t->structs = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
struct_free(struct rte_swx_pipeline *p)
|
|
{
|
|
struct_build_free(p);
|
|
|
|
/* Struct types. */
|
|
for ( ; ; ) {
|
|
struct struct_type *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->struct_types);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->struct_types, elem, node);
|
|
free(elem->fields);
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Input port.
|
|
*/
|
|
static struct port_in_type *
|
|
port_in_type_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct port_in_type *elem;
|
|
|
|
if (!name)
|
|
return NULL;
|
|
|
|
TAILQ_FOREACH(elem, &p->port_in_types, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_port_in_type_register(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct rte_swx_port_in_ops *ops)
|
|
{
|
|
struct port_in_type *elem;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(ops, EINVAL);
|
|
CHECK(ops->create, EINVAL);
|
|
CHECK(ops->free, EINVAL);
|
|
CHECK(ops->pkt_rx, EINVAL);
|
|
CHECK(ops->stats_read, EINVAL);
|
|
|
|
CHECK(!port_in_type_find(p, name), EEXIST);
|
|
|
|
/* Node allocation. */
|
|
elem = calloc(1, sizeof(struct port_in_type));
|
|
CHECK(elem, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(elem->name, name);
|
|
memcpy(&elem->ops, ops, sizeof(*ops));
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->port_in_types, elem, node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct port_in *
|
|
port_in_find(struct rte_swx_pipeline *p, uint32_t port_id)
|
|
{
|
|
struct port_in *port;
|
|
|
|
TAILQ_FOREACH(port, &p->ports_in, node)
|
|
if (port->id == port_id)
|
|
return port;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_port_in_config(struct rte_swx_pipeline *p,
|
|
uint32_t port_id,
|
|
const char *port_type_name,
|
|
void *args)
|
|
{
|
|
struct port_in_type *type = NULL;
|
|
struct port_in *port = NULL;
|
|
void *obj = NULL;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK(!port_in_find(p, port_id), EINVAL);
|
|
|
|
CHECK_NAME(port_type_name, EINVAL);
|
|
type = port_in_type_find(p, port_type_name);
|
|
CHECK(type, EINVAL);
|
|
|
|
obj = type->ops.create(args);
|
|
CHECK(obj, ENODEV);
|
|
|
|
/* Node allocation. */
|
|
port = calloc(1, sizeof(struct port_in));
|
|
CHECK(port, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
port->type = type;
|
|
port->obj = obj;
|
|
port->id = port_id;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->ports_in, port, node);
|
|
if (p->n_ports_in < port_id + 1)
|
|
p->n_ports_in = port_id + 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
port_in_build(struct rte_swx_pipeline *p)
|
|
{
|
|
struct port_in *port;
|
|
uint32_t i;
|
|
|
|
CHECK(p->n_ports_in, EINVAL);
|
|
CHECK(rte_is_power_of_2(p->n_ports_in), EINVAL);
|
|
|
|
for (i = 0; i < p->n_ports_in; i++)
|
|
CHECK(port_in_find(p, i), EINVAL);
|
|
|
|
p->in = calloc(p->n_ports_in, sizeof(struct port_in_runtime));
|
|
CHECK(p->in, ENOMEM);
|
|
|
|
TAILQ_FOREACH(port, &p->ports_in, node) {
|
|
struct port_in_runtime *in = &p->in[port->id];
|
|
|
|
in->pkt_rx = port->type->ops.pkt_rx;
|
|
in->obj = port->obj;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
port_in_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
free(p->in);
|
|
p->in = NULL;
|
|
}
|
|
|
|
static void
|
|
port_in_free(struct rte_swx_pipeline *p)
|
|
{
|
|
port_in_build_free(p);
|
|
|
|
/* Input ports. */
|
|
for ( ; ; ) {
|
|
struct port_in *port;
|
|
|
|
port = TAILQ_FIRST(&p->ports_in);
|
|
if (!port)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->ports_in, port, node);
|
|
port->type->ops.free(port->obj);
|
|
free(port);
|
|
}
|
|
|
|
/* Input port types. */
|
|
for ( ; ; ) {
|
|
struct port_in_type *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->port_in_types);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->port_in_types, elem, node);
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Output port.
|
|
*/
|
|
static struct port_out_type *
|
|
port_out_type_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct port_out_type *elem;
|
|
|
|
if (!name)
|
|
return NULL;
|
|
|
|
TAILQ_FOREACH(elem, &p->port_out_types, node)
|
|
if (!strcmp(elem->name, name))
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_port_out_type_register(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct rte_swx_port_out_ops *ops)
|
|
{
|
|
struct port_out_type *elem;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(ops, EINVAL);
|
|
CHECK(ops->create, EINVAL);
|
|
CHECK(ops->free, EINVAL);
|
|
CHECK(ops->pkt_tx, EINVAL);
|
|
CHECK(ops->pkt_fast_clone_tx, EINVAL);
|
|
CHECK(ops->pkt_clone_tx, EINVAL);
|
|
CHECK(ops->stats_read, EINVAL);
|
|
|
|
CHECK(!port_out_type_find(p, name), EEXIST);
|
|
|
|
/* Node allocation. */
|
|
elem = calloc(1, sizeof(struct port_out_type));
|
|
CHECK(elem, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(elem->name, name);
|
|
memcpy(&elem->ops, ops, sizeof(*ops));
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->port_out_types, elem, node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct port_out *
|
|
port_out_find(struct rte_swx_pipeline *p, uint32_t port_id)
|
|
{
|
|
struct port_out *port;
|
|
|
|
TAILQ_FOREACH(port, &p->ports_out, node)
|
|
if (port->id == port_id)
|
|
return port;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_port_out_config(struct rte_swx_pipeline *p,
|
|
uint32_t port_id,
|
|
const char *port_type_name,
|
|
void *args)
|
|
{
|
|
struct port_out_type *type = NULL;
|
|
struct port_out *port = NULL;
|
|
void *obj = NULL;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK(!port_out_find(p, port_id), EINVAL);
|
|
|
|
CHECK_NAME(port_type_name, EINVAL);
|
|
type = port_out_type_find(p, port_type_name);
|
|
CHECK(type, EINVAL);
|
|
|
|
obj = type->ops.create(args);
|
|
CHECK(obj, ENODEV);
|
|
|
|
/* Node allocation. */
|
|
port = calloc(1, sizeof(struct port_out));
|
|
CHECK(port, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
port->type = type;
|
|
port->obj = obj;
|
|
port->id = port_id;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->ports_out, port, node);
|
|
if (p->n_ports_out < port_id + 1)
|
|
p->n_ports_out = port_id + 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
port_out_build(struct rte_swx_pipeline *p)
|
|
{
|
|
struct port_out *port;
|
|
uint32_t i;
|
|
|
|
CHECK(p->n_ports_out, EINVAL);
|
|
|
|
for (i = 0; i < p->n_ports_out; i++)
|
|
CHECK(port_out_find(p, i), EINVAL);
|
|
|
|
p->out = calloc(p->n_ports_out, sizeof(struct port_out_runtime));
|
|
CHECK(p->out, ENOMEM);
|
|
|
|
TAILQ_FOREACH(port, &p->ports_out, node) {
|
|
struct port_out_runtime *out = &p->out[port->id];
|
|
|
|
out->pkt_tx = port->type->ops.pkt_tx;
|
|
out->pkt_fast_clone_tx = port->type->ops.pkt_fast_clone_tx;
|
|
out->pkt_clone_tx = port->type->ops.pkt_clone_tx;
|
|
out->flush = port->type->ops.flush;
|
|
out->obj = port->obj;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
port_out_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
free(p->out);
|
|
p->out = NULL;
|
|
}
|
|
|
|
static void
|
|
port_out_free(struct rte_swx_pipeline *p)
|
|
{
|
|
port_out_build_free(p);
|
|
|
|
/* Output ports. */
|
|
for ( ; ; ) {
|
|
struct port_out *port;
|
|
|
|
port = TAILQ_FIRST(&p->ports_out);
|
|
if (!port)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->ports_out, port, node);
|
|
port->type->ops.free(port->obj);
|
|
free(port);
|
|
}
|
|
|
|
/* Output port types. */
|
|
for ( ; ; ) {
|
|
struct port_out_type *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->port_out_types);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->port_out_types, elem, node);
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Packet mirroring.
|
|
*/
|
|
int
|
|
rte_swx_pipeline_mirroring_config(struct rte_swx_pipeline *p,
|
|
struct rte_swx_pipeline_mirroring_params *params)
|
|
{
|
|
CHECK(p, EINVAL);
|
|
CHECK(params, EINVAL);
|
|
CHECK(params->n_slots, EINVAL);
|
|
CHECK(params->n_sessions, EINVAL);
|
|
CHECK(!p->build_done, EEXIST);
|
|
|
|
p->n_mirroring_slots = rte_align32pow2(params->n_slots);
|
|
if (p->n_mirroring_slots > 64)
|
|
p->n_mirroring_slots = 64;
|
|
|
|
p->n_mirroring_sessions = rte_align32pow2(params->n_sessions);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
mirroring_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
|
|
/* mirroring_slots. */
|
|
free(t->mirroring_slots);
|
|
t->mirroring_slots = NULL;
|
|
}
|
|
|
|
/* mirroring_sessions. */
|
|
free(p->mirroring_sessions);
|
|
p->mirroring_sessions = NULL;
|
|
}
|
|
|
|
static void
|
|
mirroring_free(struct rte_swx_pipeline *p)
|
|
{
|
|
mirroring_build_free(p);
|
|
}
|
|
|
|
static int
|
|
mirroring_build(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
|
|
/* mirroring_slots. */
|
|
t->mirroring_slots = calloc(p->n_mirroring_slots, sizeof(uint32_t));
|
|
if (!t->mirroring_slots)
|
|
goto error;
|
|
}
|
|
|
|
/* mirroring_sessions. */
|
|
p->mirroring_sessions = calloc(p->n_mirroring_sessions, sizeof(struct mirroring_session));
|
|
if (!p->mirroring_sessions)
|
|
goto error;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
mirroring_build_free(p);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/*
|
|
* Extern object.
|
|
*/
|
|
static struct extern_type *
|
|
extern_type_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct extern_type *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->extern_types, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct extern_type_member_func *
|
|
extern_type_member_func_find(struct extern_type *type, const char *name)
|
|
{
|
|
struct extern_type_member_func *elem;
|
|
|
|
TAILQ_FOREACH(elem, &type->funcs, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct extern_obj *
|
|
extern_obj_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct extern_obj *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->extern_objs, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct extern_type_member_func *
|
|
extern_obj_member_func_parse(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct extern_obj **obj)
|
|
{
|
|
struct extern_obj *object;
|
|
struct extern_type_member_func *func;
|
|
char *object_name, *func_name;
|
|
|
|
if (name[0] != 'e' || name[1] != '.')
|
|
return NULL;
|
|
|
|
object_name = strdup(&name[2]);
|
|
if (!object_name)
|
|
return NULL;
|
|
|
|
func_name = strchr(object_name, '.');
|
|
if (!func_name) {
|
|
free(object_name);
|
|
return NULL;
|
|
}
|
|
|
|
*func_name = 0;
|
|
func_name++;
|
|
|
|
object = extern_obj_find(p, object_name);
|
|
if (!object) {
|
|
free(object_name);
|
|
return NULL;
|
|
}
|
|
|
|
func = extern_type_member_func_find(object->type, func_name);
|
|
if (!func) {
|
|
free(object_name);
|
|
return NULL;
|
|
}
|
|
|
|
if (obj)
|
|
*obj = object;
|
|
|
|
free(object_name);
|
|
return func;
|
|
}
|
|
|
|
static struct field *
|
|
extern_obj_mailbox_field_parse(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct extern_obj **object)
|
|
{
|
|
struct extern_obj *obj;
|
|
struct field *f;
|
|
char *obj_name, *field_name;
|
|
|
|
if ((name[0] != 'e') || (name[1] != '.'))
|
|
return NULL;
|
|
|
|
obj_name = strdup(&name[2]);
|
|
if (!obj_name)
|
|
return NULL;
|
|
|
|
field_name = strchr(obj_name, '.');
|
|
if (!field_name) {
|
|
free(obj_name);
|
|
return NULL;
|
|
}
|
|
|
|
*field_name = 0;
|
|
field_name++;
|
|
|
|
obj = extern_obj_find(p, obj_name);
|
|
if (!obj) {
|
|
free(obj_name);
|
|
return NULL;
|
|
}
|
|
|
|
f = struct_type_field_find(obj->type->mailbox_struct_type, field_name);
|
|
if (!f) {
|
|
free(obj_name);
|
|
return NULL;
|
|
}
|
|
|
|
if (object)
|
|
*object = obj;
|
|
|
|
free(obj_name);
|
|
return f;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_extern_type_register(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
const char *mailbox_struct_type_name,
|
|
rte_swx_extern_type_constructor_t constructor,
|
|
rte_swx_extern_type_destructor_t destructor)
|
|
{
|
|
struct extern_type *elem;
|
|
struct struct_type *mailbox_struct_type;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!extern_type_find(p, name), EEXIST);
|
|
|
|
CHECK_NAME(mailbox_struct_type_name, EINVAL);
|
|
mailbox_struct_type = struct_type_find(p, mailbox_struct_type_name);
|
|
CHECK(mailbox_struct_type, EINVAL);
|
|
CHECK(!mailbox_struct_type->var_size, EINVAL);
|
|
|
|
CHECK(constructor, EINVAL);
|
|
CHECK(destructor, EINVAL);
|
|
|
|
/* Node allocation. */
|
|
elem = calloc(1, sizeof(struct extern_type));
|
|
CHECK(elem, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(elem->name, name);
|
|
elem->mailbox_struct_type = mailbox_struct_type;
|
|
elem->constructor = constructor;
|
|
elem->destructor = destructor;
|
|
TAILQ_INIT(&elem->funcs);
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->extern_types, elem, node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_extern_type_member_func_register(struct rte_swx_pipeline *p,
|
|
const char *extern_type_name,
|
|
const char *name,
|
|
rte_swx_extern_type_member_func_t member_func)
|
|
{
|
|
struct extern_type *type;
|
|
struct extern_type_member_func *type_member;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(extern_type_name, EINVAL);
|
|
type = extern_type_find(p, extern_type_name);
|
|
CHECK(type, EINVAL);
|
|
CHECK(type->n_funcs < RTE_SWX_EXTERN_TYPE_MEMBER_FUNCS_MAX, ENOSPC);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!extern_type_member_func_find(type, name), EEXIST);
|
|
|
|
CHECK(member_func, EINVAL);
|
|
|
|
/* Node allocation. */
|
|
type_member = calloc(1, sizeof(struct extern_type_member_func));
|
|
CHECK(type_member, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(type_member->name, name);
|
|
type_member->func = member_func;
|
|
type_member->id = type->n_funcs;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&type->funcs, type_member, node);
|
|
type->n_funcs++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_extern_object_config(struct rte_swx_pipeline *p,
|
|
const char *extern_type_name,
|
|
const char *name,
|
|
const char *args)
|
|
{
|
|
struct extern_type *type;
|
|
struct extern_obj *obj;
|
|
void *obj_handle;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(extern_type_name, EINVAL);
|
|
type = extern_type_find(p, extern_type_name);
|
|
CHECK(type, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!extern_obj_find(p, name), EEXIST);
|
|
|
|
/* Node allocation. */
|
|
obj = calloc(1, sizeof(struct extern_obj));
|
|
CHECK(obj, ENOMEM);
|
|
|
|
/* Object construction. */
|
|
obj_handle = type->constructor(args);
|
|
if (!obj_handle) {
|
|
free(obj);
|
|
CHECK(0, ENODEV);
|
|
}
|
|
|
|
/* Node initialization. */
|
|
strcpy(obj->name, name);
|
|
obj->type = type;
|
|
obj->obj = obj_handle;
|
|
obj->struct_id = p->n_structs;
|
|
obj->id = p->n_extern_objs;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->extern_objs, obj, node);
|
|
p->n_extern_objs++;
|
|
p->n_structs++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
extern_obj_build(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
struct extern_obj *obj;
|
|
|
|
t->extern_objs = calloc(p->n_extern_objs,
|
|
sizeof(struct extern_obj_runtime));
|
|
CHECK(t->extern_objs, ENOMEM);
|
|
|
|
TAILQ_FOREACH(obj, &p->extern_objs, node) {
|
|
struct extern_obj_runtime *r =
|
|
&t->extern_objs[obj->id];
|
|
struct extern_type_member_func *func;
|
|
uint32_t mailbox_size =
|
|
obj->type->mailbox_struct_type->n_bits / 8;
|
|
|
|
r->obj = obj->obj;
|
|
|
|
r->mailbox = calloc(1, mailbox_size);
|
|
CHECK(r->mailbox, ENOMEM);
|
|
|
|
TAILQ_FOREACH(func, &obj->type->funcs, node)
|
|
r->funcs[func->id] = func->func;
|
|
|
|
t->structs[obj->struct_id] = r->mailbox;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
extern_obj_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
uint32_t j;
|
|
|
|
if (!t->extern_objs)
|
|
continue;
|
|
|
|
for (j = 0; j < p->n_extern_objs; j++) {
|
|
struct extern_obj_runtime *r = &t->extern_objs[j];
|
|
|
|
free(r->mailbox);
|
|
}
|
|
|
|
free(t->extern_objs);
|
|
t->extern_objs = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
extern_obj_free(struct rte_swx_pipeline *p)
|
|
{
|
|
extern_obj_build_free(p);
|
|
|
|
/* Extern objects. */
|
|
for ( ; ; ) {
|
|
struct extern_obj *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->extern_objs);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->extern_objs, elem, node);
|
|
if (elem->obj)
|
|
elem->type->destructor(elem->obj);
|
|
free(elem);
|
|
}
|
|
|
|
/* Extern types. */
|
|
for ( ; ; ) {
|
|
struct extern_type *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->extern_types);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->extern_types, elem, node);
|
|
|
|
for ( ; ; ) {
|
|
struct extern_type_member_func *func;
|
|
|
|
func = TAILQ_FIRST(&elem->funcs);
|
|
if (!func)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&elem->funcs, func, node);
|
|
free(func);
|
|
}
|
|
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Extern function.
|
|
*/
|
|
static struct extern_func *
|
|
extern_func_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct extern_func *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->extern_funcs, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct extern_func *
|
|
extern_func_parse(struct rte_swx_pipeline *p,
|
|
const char *name)
|
|
{
|
|
if (name[0] != 'f' || name[1] != '.')
|
|
return NULL;
|
|
|
|
return extern_func_find(p, &name[2]);
|
|
}
|
|
|
|
static struct field *
|
|
extern_func_mailbox_field_parse(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct extern_func **function)
|
|
{
|
|
struct extern_func *func;
|
|
struct field *f;
|
|
char *func_name, *field_name;
|
|
|
|
if ((name[0] != 'f') || (name[1] != '.'))
|
|
return NULL;
|
|
|
|
func_name = strdup(&name[2]);
|
|
if (!func_name)
|
|
return NULL;
|
|
|
|
field_name = strchr(func_name, '.');
|
|
if (!field_name) {
|
|
free(func_name);
|
|
return NULL;
|
|
}
|
|
|
|
*field_name = 0;
|
|
field_name++;
|
|
|
|
func = extern_func_find(p, func_name);
|
|
if (!func) {
|
|
free(func_name);
|
|
return NULL;
|
|
}
|
|
|
|
f = struct_type_field_find(func->mailbox_struct_type, field_name);
|
|
if (!f) {
|
|
free(func_name);
|
|
return NULL;
|
|
}
|
|
|
|
if (function)
|
|
*function = func;
|
|
|
|
free(func_name);
|
|
return f;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_extern_func_register(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
const char *mailbox_struct_type_name,
|
|
rte_swx_extern_func_t func)
|
|
{
|
|
struct extern_func *f;
|
|
struct struct_type *mailbox_struct_type;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!extern_func_find(p, name), EEXIST);
|
|
|
|
CHECK_NAME(mailbox_struct_type_name, EINVAL);
|
|
mailbox_struct_type = struct_type_find(p, mailbox_struct_type_name);
|
|
CHECK(mailbox_struct_type, EINVAL);
|
|
CHECK(!mailbox_struct_type->var_size, EINVAL);
|
|
|
|
CHECK(func, EINVAL);
|
|
|
|
/* Node allocation. */
|
|
f = calloc(1, sizeof(struct extern_func));
|
|
CHECK(func, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(f->name, name);
|
|
f->mailbox_struct_type = mailbox_struct_type;
|
|
f->func = func;
|
|
f->struct_id = p->n_structs;
|
|
f->id = p->n_extern_funcs;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->extern_funcs, f, node);
|
|
p->n_extern_funcs++;
|
|
p->n_structs++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
extern_func_build(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
struct extern_func *func;
|
|
|
|
/* Memory allocation. */
|
|
t->extern_funcs = calloc(p->n_extern_funcs,
|
|
sizeof(struct extern_func_runtime));
|
|
CHECK(t->extern_funcs, ENOMEM);
|
|
|
|
/* Extern function. */
|
|
TAILQ_FOREACH(func, &p->extern_funcs, node) {
|
|
struct extern_func_runtime *r =
|
|
&t->extern_funcs[func->id];
|
|
uint32_t mailbox_size =
|
|
func->mailbox_struct_type->n_bits / 8;
|
|
|
|
r->func = func->func;
|
|
|
|
r->mailbox = calloc(1, mailbox_size);
|
|
CHECK(r->mailbox, ENOMEM);
|
|
|
|
t->structs[func->struct_id] = r->mailbox;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
extern_func_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
uint32_t j;
|
|
|
|
if (!t->extern_funcs)
|
|
continue;
|
|
|
|
for (j = 0; j < p->n_extern_funcs; j++) {
|
|
struct extern_func_runtime *r = &t->extern_funcs[j];
|
|
|
|
free(r->mailbox);
|
|
}
|
|
|
|
free(t->extern_funcs);
|
|
t->extern_funcs = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
extern_func_free(struct rte_swx_pipeline *p)
|
|
{
|
|
extern_func_build_free(p);
|
|
|
|
for ( ; ; ) {
|
|
struct extern_func *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->extern_funcs);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->extern_funcs, elem, node);
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Hash function.
|
|
*/
|
|
static struct hash_func *
|
|
hash_func_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct hash_func *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->hash_funcs, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_hash_func_register(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
rte_swx_hash_func_t func)
|
|
{
|
|
struct hash_func *f;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!hash_func_find(p, name), EEXIST);
|
|
|
|
CHECK(func, EINVAL);
|
|
|
|
/* Node allocation. */
|
|
f = calloc(1, sizeof(struct hash_func));
|
|
CHECK(func, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(f->name, name);
|
|
f->func = func;
|
|
f->id = p->n_hash_funcs;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->hash_funcs, f, node);
|
|
p->n_hash_funcs++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
hash_func_build(struct rte_swx_pipeline *p)
|
|
{
|
|
struct hash_func *func;
|
|
|
|
/* Memory allocation. */
|
|
p->hash_func_runtime = calloc(p->n_hash_funcs, sizeof(struct hash_func_runtime));
|
|
CHECK(p->hash_func_runtime, ENOMEM);
|
|
|
|
/* Hash function. */
|
|
TAILQ_FOREACH(func, &p->hash_funcs, node) {
|
|
struct hash_func_runtime *r = &p->hash_func_runtime[func->id];
|
|
|
|
r->func = func->func;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
hash_func_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
free(p->hash_func_runtime);
|
|
p->hash_func_runtime = NULL;
|
|
}
|
|
|
|
static void
|
|
hash_func_free(struct rte_swx_pipeline *p)
|
|
{
|
|
hash_func_build_free(p);
|
|
|
|
for ( ; ; ) {
|
|
struct hash_func *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->hash_funcs);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->hash_funcs, elem, node);
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Header.
|
|
*/
|
|
static struct header *
|
|
header_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct header *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->headers, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct header *
|
|
header_find_by_struct_id(struct rte_swx_pipeline *p, uint32_t struct_id)
|
|
{
|
|
struct header *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->headers, node)
|
|
if (elem->struct_id == struct_id)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct header *
|
|
header_parse(struct rte_swx_pipeline *p,
|
|
const char *name)
|
|
{
|
|
if (name[0] != 'h' || name[1] != '.')
|
|
return NULL;
|
|
|
|
return header_find(p, &name[2]);
|
|
}
|
|
|
|
static struct field *
|
|
header_field_parse(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct header **header)
|
|
{
|
|
struct header *h;
|
|
struct field *f;
|
|
char *header_name, *field_name;
|
|
|
|
if ((name[0] != 'h') || (name[1] != '.'))
|
|
return NULL;
|
|
|
|
header_name = strdup(&name[2]);
|
|
if (!header_name)
|
|
return NULL;
|
|
|
|
field_name = strchr(header_name, '.');
|
|
if (!field_name) {
|
|
free(header_name);
|
|
return NULL;
|
|
}
|
|
|
|
*field_name = 0;
|
|
field_name++;
|
|
|
|
h = header_find(p, header_name);
|
|
if (!h) {
|
|
free(header_name);
|
|
return NULL;
|
|
}
|
|
|
|
f = struct_type_field_find(h->st, field_name);
|
|
if (!f) {
|
|
free(header_name);
|
|
return NULL;
|
|
}
|
|
|
|
if (header)
|
|
*header = h;
|
|
|
|
free(header_name);
|
|
return f;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_packet_header_register(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
const char *struct_type_name)
|
|
{
|
|
struct struct_type *st;
|
|
struct header *h;
|
|
size_t n_headers_max;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK_NAME(struct_type_name, EINVAL);
|
|
|
|
CHECK(!header_find(p, name), EEXIST);
|
|
|
|
st = struct_type_find(p, struct_type_name);
|
|
CHECK(st, EINVAL);
|
|
|
|
n_headers_max = RTE_SIZEOF_FIELD(struct thread, valid_headers) * 8;
|
|
CHECK(p->n_headers < n_headers_max, ENOSPC);
|
|
|
|
/* Node allocation. */
|
|
h = calloc(1, sizeof(struct header));
|
|
CHECK(h, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(h->name, name);
|
|
h->st = st;
|
|
h->struct_id = p->n_structs;
|
|
h->id = p->n_headers;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->headers, h, node);
|
|
p->n_headers++;
|
|
p->n_structs++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
header_build(struct rte_swx_pipeline *p)
|
|
{
|
|
struct header *h;
|
|
uint32_t n_bytes = 0, i;
|
|
|
|
TAILQ_FOREACH(h, &p->headers, node) {
|
|
n_bytes += h->st->n_bits / 8;
|
|
}
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
uint32_t offset = 0;
|
|
|
|
t->headers = calloc(p->n_headers,
|
|
sizeof(struct header_runtime));
|
|
CHECK(t->headers, ENOMEM);
|
|
|
|
t->headers_out = calloc(p->n_headers,
|
|
sizeof(struct header_out_runtime));
|
|
CHECK(t->headers_out, ENOMEM);
|
|
|
|
t->header_storage = calloc(1, n_bytes);
|
|
CHECK(t->header_storage, ENOMEM);
|
|
|
|
t->header_out_storage = calloc(1, n_bytes);
|
|
CHECK(t->header_out_storage, ENOMEM);
|
|
|
|
TAILQ_FOREACH(h, &p->headers, node) {
|
|
uint8_t *header_storage;
|
|
uint32_t n_bytes = h->st->n_bits / 8;
|
|
|
|
header_storage = &t->header_storage[offset];
|
|
offset += n_bytes;
|
|
|
|
t->headers[h->id].ptr0 = header_storage;
|
|
t->headers[h->id].n_bytes = n_bytes;
|
|
|
|
t->structs[h->struct_id] = header_storage;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
header_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
|
|
free(t->headers_out);
|
|
t->headers_out = NULL;
|
|
|
|
free(t->headers);
|
|
t->headers = NULL;
|
|
|
|
free(t->header_out_storage);
|
|
t->header_out_storage = NULL;
|
|
|
|
free(t->header_storage);
|
|
t->header_storage = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
header_free(struct rte_swx_pipeline *p)
|
|
{
|
|
header_build_free(p);
|
|
|
|
for ( ; ; ) {
|
|
struct header *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->headers);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->headers, elem, node);
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Meta-data.
|
|
*/
|
|
static struct field *
|
|
metadata_field_parse(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
if (!p->metadata_st)
|
|
return NULL;
|
|
|
|
if (name[0] != 'm' || name[1] != '.')
|
|
return NULL;
|
|
|
|
return struct_type_field_find(p->metadata_st, &name[2]);
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_packet_metadata_register(struct rte_swx_pipeline *p,
|
|
const char *struct_type_name)
|
|
{
|
|
struct struct_type *st = NULL;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(struct_type_name, EINVAL);
|
|
st = struct_type_find(p, struct_type_name);
|
|
CHECK(st, EINVAL);
|
|
CHECK(!st->var_size, EINVAL);
|
|
CHECK(!p->metadata_st, EINVAL);
|
|
|
|
p->metadata_st = st;
|
|
p->metadata_struct_id = p->n_structs;
|
|
|
|
p->n_structs++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
metadata_build(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t n_bytes = p->metadata_st->n_bits / 8;
|
|
uint32_t i;
|
|
|
|
/* Thread-level initialization. */
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
uint8_t *metadata;
|
|
|
|
metadata = calloc(1, n_bytes);
|
|
CHECK(metadata, ENOMEM);
|
|
|
|
t->metadata = metadata;
|
|
t->structs[p->metadata_struct_id] = metadata;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
metadata_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
|
|
free(t->metadata);
|
|
t->metadata = NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
metadata_free(struct rte_swx_pipeline *p)
|
|
{
|
|
metadata_build_free(p);
|
|
}
|
|
|
|
/*
|
|
* Instruction.
|
|
*/
|
|
static int
|
|
instruction_is_tx(enum instruction_type type)
|
|
{
|
|
switch (type) {
|
|
case INSTR_TX:
|
|
case INSTR_TX_I:
|
|
case INSTR_DROP:
|
|
return 1;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
instruction_does_tx(struct instruction *instr)
|
|
{
|
|
switch (instr->type) {
|
|
case INSTR_TX:
|
|
case INSTR_TX_I:
|
|
case INSTR_DROP:
|
|
case INSTR_HDR_EMIT_TX:
|
|
case INSTR_HDR_EMIT2_TX:
|
|
case INSTR_HDR_EMIT3_TX:
|
|
case INSTR_HDR_EMIT4_TX:
|
|
case INSTR_HDR_EMIT5_TX:
|
|
case INSTR_HDR_EMIT6_TX:
|
|
case INSTR_HDR_EMIT7_TX:
|
|
case INSTR_HDR_EMIT8_TX:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
instruction_is_jmp(struct instruction *instr)
|
|
{
|
|
switch (instr->type) {
|
|
case INSTR_JMP:
|
|
case INSTR_JMP_VALID:
|
|
case INSTR_JMP_INVALID:
|
|
case INSTR_JMP_HIT:
|
|
case INSTR_JMP_MISS:
|
|
case INSTR_JMP_ACTION_HIT:
|
|
case INSTR_JMP_ACTION_MISS:
|
|
case INSTR_JMP_EQ:
|
|
case INSTR_JMP_EQ_MH:
|
|
case INSTR_JMP_EQ_HM:
|
|
case INSTR_JMP_EQ_HH:
|
|
case INSTR_JMP_EQ_I:
|
|
case INSTR_JMP_NEQ:
|
|
case INSTR_JMP_NEQ_MH:
|
|
case INSTR_JMP_NEQ_HM:
|
|
case INSTR_JMP_NEQ_HH:
|
|
case INSTR_JMP_NEQ_I:
|
|
case INSTR_JMP_LT:
|
|
case INSTR_JMP_LT_MH:
|
|
case INSTR_JMP_LT_HM:
|
|
case INSTR_JMP_LT_HH:
|
|
case INSTR_JMP_LT_MI:
|
|
case INSTR_JMP_LT_HI:
|
|
case INSTR_JMP_GT:
|
|
case INSTR_JMP_GT_MH:
|
|
case INSTR_JMP_GT_HM:
|
|
case INSTR_JMP_GT_HH:
|
|
case INSTR_JMP_GT_MI:
|
|
case INSTR_JMP_GT_HI:
|
|
return 1;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static int
|
|
instruction_does_thread_yield(struct instruction *instr)
|
|
{
|
|
switch (instr->type) {
|
|
case INSTR_RX:
|
|
case INSTR_TABLE:
|
|
case INSTR_TABLE_AF:
|
|
case INSTR_SELECTOR:
|
|
case INSTR_LEARNER:
|
|
case INSTR_LEARNER_AF:
|
|
case INSTR_EXTERN_OBJ:
|
|
case INSTR_EXTERN_FUNC:
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static struct field *
|
|
action_field_parse(struct action *action, const char *name);
|
|
|
|
static struct field *
|
|
struct_field_parse(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
const char *name,
|
|
uint32_t *struct_id)
|
|
{
|
|
struct field *f;
|
|
|
|
switch (name[0]) {
|
|
case 'h':
|
|
{
|
|
struct header *header;
|
|
|
|
f = header_field_parse(p, name, &header);
|
|
if (!f)
|
|
return NULL;
|
|
|
|
*struct_id = header->struct_id;
|
|
return f;
|
|
}
|
|
|
|
case 'm':
|
|
{
|
|
f = metadata_field_parse(p, name);
|
|
if (!f)
|
|
return NULL;
|
|
|
|
*struct_id = p->metadata_struct_id;
|
|
return f;
|
|
}
|
|
|
|
case 't':
|
|
{
|
|
if (!action)
|
|
return NULL;
|
|
|
|
f = action_field_parse(action, name);
|
|
if (!f)
|
|
return NULL;
|
|
|
|
*struct_id = 0;
|
|
return f;
|
|
}
|
|
|
|
case 'e':
|
|
{
|
|
struct extern_obj *obj;
|
|
|
|
f = extern_obj_mailbox_field_parse(p, name, &obj);
|
|
if (!f)
|
|
return NULL;
|
|
|
|
*struct_id = obj->struct_id;
|
|
return f;
|
|
}
|
|
|
|
case 'f':
|
|
{
|
|
struct extern_func *func;
|
|
|
|
f = extern_func_mailbox_field_parse(p, name, &func);
|
|
if (!f)
|
|
return NULL;
|
|
|
|
*struct_id = func->struct_id;
|
|
return f;
|
|
}
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* rx.
|
|
*/
|
|
static int
|
|
instr_rx_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct field *f;
|
|
|
|
CHECK(!action, EINVAL);
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
f = metadata_field_parse(p, tokens[1]);
|
|
CHECK(f, EINVAL);
|
|
|
|
instr->type = INSTR_RX;
|
|
instr->io.io.offset = f->offset / 8;
|
|
instr->io.io.n_bits = f->n_bits;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* tx.
|
|
*/
|
|
static int
|
|
instr_tx_translate(struct rte_swx_pipeline *p,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *port = tokens[1];
|
|
struct field *f;
|
|
uint32_t port_val;
|
|
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
f = metadata_field_parse(p, port);
|
|
if (f) {
|
|
instr->type = INSTR_TX;
|
|
instr->io.io.offset = f->offset / 8;
|
|
instr->io.io.n_bits = f->n_bits;
|
|
return 0;
|
|
}
|
|
|
|
/* TX_I. */
|
|
port_val = strtoul(port, &port, 0);
|
|
CHECK(!port[0], EINVAL);
|
|
|
|
instr->type = INSTR_TX_I;
|
|
instr->io.io.val = port_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_drop_translate(struct rte_swx_pipeline *p __rte_unused,
|
|
struct action *action __rte_unused,
|
|
char **tokens __rte_unused,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
CHECK(n_tokens == 1, EINVAL);
|
|
|
|
/* DROP. */
|
|
instr->type = INSTR_DROP;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_tx_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_tx_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_tx_i_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_tx_i_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_drop_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_drop_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
/*
|
|
* mirror.
|
|
*/
|
|
static int
|
|
instr_mirror_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct field *fdst, *fsrc;
|
|
uint32_t dst_struct_id = 0, src_struct_id = 0;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = struct_field_parse(p, action, dst, &dst_struct_id);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(dst[0] != 'h', EINVAL);
|
|
CHECK(!fdst->var_size, EINVAL);
|
|
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
CHECK(fsrc, EINVAL);
|
|
CHECK(src[0] != 'h', EINVAL);
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_MIRROR;
|
|
instr->mirror.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->mirror.dst.n_bits = fdst->n_bits;
|
|
instr->mirror.dst.offset = fdst->offset / 8;
|
|
instr->mirror.src.struct_id = (uint8_t)src_struct_id;
|
|
instr->mirror.src.n_bits = fsrc->n_bits;
|
|
instr->mirror.src.offset = fsrc->offset / 8;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_mirror_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_mirror_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* recirculate.
|
|
*/
|
|
static int
|
|
instr_recirculate_translate(struct rte_swx_pipeline *p __rte_unused,
|
|
struct action *action __rte_unused,
|
|
char **tokens __rte_unused,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
CHECK(n_tokens == 1, EINVAL);
|
|
|
|
instr->type = INSTR_RECIRCULATE;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_recircid_translate(struct rte_swx_pipeline *p,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct field *f;
|
|
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
f = metadata_field_parse(p, tokens[1]);
|
|
CHECK(f, EINVAL);
|
|
|
|
instr->type = INSTR_RECIRCID;
|
|
instr->io.io.offset = f->offset / 8;
|
|
instr->io.io.n_bits = f->n_bits;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_recirculate_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_recirculate_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_recircid_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_recircid_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* extract.
|
|
*/
|
|
static int
|
|
instr_hdr_extract_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct header *h;
|
|
|
|
CHECK(!action, EINVAL);
|
|
CHECK((n_tokens == 2) || (n_tokens == 3), EINVAL);
|
|
|
|
h = header_parse(p, tokens[1]);
|
|
CHECK(h, EINVAL);
|
|
|
|
if (n_tokens == 2) {
|
|
CHECK(!h->st->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_HDR_EXTRACT;
|
|
instr->io.hdr.header_id[0] = h->id;
|
|
instr->io.hdr.struct_id[0] = h->struct_id;
|
|
instr->io.hdr.n_bytes[0] = h->st->n_bits / 8;
|
|
} else {
|
|
struct field *mf;
|
|
|
|
CHECK(h->st->var_size, EINVAL);
|
|
|
|
mf = metadata_field_parse(p, tokens[2]);
|
|
CHECK(mf, EINVAL);
|
|
CHECK(!mf->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_HDR_EXTRACT_M;
|
|
instr->io.io.offset = mf->offset / 8;
|
|
instr->io.io.n_bits = mf->n_bits;
|
|
instr->io.hdr.header_id[0] = h->id;
|
|
instr->io.hdr.struct_id[0] = h->struct_id;
|
|
instr->io.hdr.n_bytes[0] = h->st->n_bits_min / 8;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_hdr_lookahead_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct header *h;
|
|
|
|
CHECK(!action, EINVAL);
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
h = header_parse(p, tokens[1]);
|
|
CHECK(h, EINVAL);
|
|
CHECK(!h->st->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_HDR_LOOKAHEAD;
|
|
instr->io.hdr.header_id[0] = h->id;
|
|
instr->io.hdr.struct_id[0] = h->struct_id;
|
|
instr->io.hdr.n_bytes[0] = 0; /* Unused. */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_extract_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_extract_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_extract2_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_extract2_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_extract3_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_extract3_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_extract4_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_extract4_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_extract5_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_extract5_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_extract6_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_extract6_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_extract7_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_extract7_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_extract8_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_extract8_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_extract_m_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_extract_m_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_lookahead_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_lookahead_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* emit.
|
|
*/
|
|
static int
|
|
instr_hdr_emit_translate(struct rte_swx_pipeline *p,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct header *h;
|
|
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
h = header_parse(p, tokens[1]);
|
|
CHECK(h, EINVAL);
|
|
|
|
instr->type = INSTR_HDR_EMIT;
|
|
instr->io.hdr.header_id[0] = h->id;
|
|
instr->io.hdr.struct_id[0] = h->struct_id;
|
|
instr->io.hdr.n_bytes[0] = h->st->n_bits / 8;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_emit_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_emit_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_emit_tx_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_emit_tx_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_emit2_tx_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_emit2_tx_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_emit3_tx_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_emit3_tx_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_emit4_tx_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_emit4_tx_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_emit5_tx_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_emit5_tx_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_emit6_tx_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_emit6_tx_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_emit7_tx_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_emit7_tx_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_emit8_tx_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_emit8_tx_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_reset(p, t);
|
|
instr_rx_exec(p);
|
|
}
|
|
|
|
/*
|
|
* validate.
|
|
*/
|
|
static int
|
|
instr_hdr_validate_translate(struct rte_swx_pipeline *p,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct header *h;
|
|
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
h = header_parse(p, tokens[1]);
|
|
CHECK(h, EINVAL);
|
|
|
|
instr->type = INSTR_HDR_VALIDATE;
|
|
instr->valid.header_id = h->id;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_validate_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_validate_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* invalidate.
|
|
*/
|
|
static int
|
|
instr_hdr_invalidate_translate(struct rte_swx_pipeline *p,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct header *h;
|
|
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
h = header_parse(p, tokens[1]);
|
|
CHECK(h, EINVAL);
|
|
|
|
instr->type = INSTR_HDR_INVALIDATE;
|
|
instr->valid.header_id = h->id;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_hdr_invalidate_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_hdr_invalidate_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* table.
|
|
*/
|
|
static struct table *
|
|
table_find(struct rte_swx_pipeline *p, const char *name);
|
|
|
|
static struct selector *
|
|
selector_find(struct rte_swx_pipeline *p, const char *name);
|
|
|
|
static struct learner *
|
|
learner_find(struct rte_swx_pipeline *p, const char *name);
|
|
|
|
static int
|
|
instr_table_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct table *t;
|
|
struct selector *s;
|
|
struct learner *l;
|
|
|
|
CHECK(!action, EINVAL);
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
t = table_find(p, tokens[1]);
|
|
if (t) {
|
|
instr->type = INSTR_TABLE;
|
|
instr->table.table_id = t->id;
|
|
return 0;
|
|
}
|
|
|
|
s = selector_find(p, tokens[1]);
|
|
if (s) {
|
|
instr->type = INSTR_SELECTOR;
|
|
instr->table.table_id = s->id;
|
|
return 0;
|
|
}
|
|
|
|
l = learner_find(p, tokens[1]);
|
|
if (l) {
|
|
instr->type = INSTR_LEARNER;
|
|
instr->table.table_id = l->id;
|
|
return 0;
|
|
}
|
|
|
|
CHECK(0, EINVAL);
|
|
}
|
|
|
|
static inline void
|
|
instr_table_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
uint32_t table_id = ip->table.table_id;
|
|
struct rte_swx_table_state *ts = &t->table_state[table_id];
|
|
struct table_runtime *table = &t->tables[table_id];
|
|
struct table_statistics *stats = &p->table_stats[table_id];
|
|
uint64_t action_id, n_pkts_hit, n_pkts_action;
|
|
uint8_t *action_data;
|
|
int done, hit;
|
|
|
|
/* Table. */
|
|
done = table->func(ts->obj,
|
|
table->mailbox,
|
|
table->key,
|
|
&action_id,
|
|
&action_data,
|
|
&hit);
|
|
if (!done) {
|
|
/* Thread. */
|
|
TRACE("[Thread %2u] table %u (not finalized)\n",
|
|
p->thread_id,
|
|
table_id);
|
|
|
|
thread_yield(p);
|
|
return;
|
|
}
|
|
|
|
action_id = hit ? action_id : ts->default_action_id;
|
|
action_data = hit ? action_data : ts->default_action_data;
|
|
n_pkts_hit = stats->n_pkts_hit[hit];
|
|
n_pkts_action = stats->n_pkts_action[action_id];
|
|
|
|
TRACE("[Thread %2u] table %u (%s, action %u)\n",
|
|
p->thread_id,
|
|
table_id,
|
|
hit ? "hit" : "miss",
|
|
(uint32_t)action_id);
|
|
|
|
t->action_id = action_id;
|
|
t->structs[0] = action_data;
|
|
t->hit = hit;
|
|
stats->n_pkts_hit[hit] = n_pkts_hit + 1;
|
|
stats->n_pkts_action[action_id] = n_pkts_action + 1;
|
|
|
|
/* Thread. */
|
|
thread_ip_action_call(p, t, action_id);
|
|
}
|
|
|
|
static inline void
|
|
instr_table_af_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
uint32_t table_id = ip->table.table_id;
|
|
struct rte_swx_table_state *ts = &t->table_state[table_id];
|
|
struct table_runtime *table = &t->tables[table_id];
|
|
struct table_statistics *stats = &p->table_stats[table_id];
|
|
uint64_t action_id, n_pkts_hit, n_pkts_action;
|
|
uint8_t *action_data;
|
|
action_func_t action_func;
|
|
int done, hit;
|
|
|
|
/* Table. */
|
|
done = table->func(ts->obj,
|
|
table->mailbox,
|
|
table->key,
|
|
&action_id,
|
|
&action_data,
|
|
&hit);
|
|
if (!done) {
|
|
/* Thread. */
|
|
TRACE("[Thread %2u] table %u (not finalized)\n",
|
|
p->thread_id,
|
|
table_id);
|
|
|
|
thread_yield(p);
|
|
return;
|
|
}
|
|
|
|
action_id = hit ? action_id : ts->default_action_id;
|
|
action_data = hit ? action_data : ts->default_action_data;
|
|
action_func = p->action_funcs[action_id];
|
|
n_pkts_hit = stats->n_pkts_hit[hit];
|
|
n_pkts_action = stats->n_pkts_action[action_id];
|
|
|
|
TRACE("[Thread %2u] table %u (%s, action %u)\n",
|
|
p->thread_id,
|
|
table_id,
|
|
hit ? "hit" : "miss",
|
|
(uint32_t)action_id);
|
|
|
|
t->action_id = action_id;
|
|
t->structs[0] = action_data;
|
|
t->hit = hit;
|
|
stats->n_pkts_hit[hit] = n_pkts_hit + 1;
|
|
stats->n_pkts_action[action_id] = n_pkts_action + 1;
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
|
|
/* Action. */
|
|
action_func(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_selector_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
uint32_t selector_id = ip->table.table_id;
|
|
struct rte_swx_table_state *ts = &t->table_state[p->n_tables + selector_id];
|
|
struct selector_runtime *selector = &t->selectors[selector_id];
|
|
struct selector_statistics *stats = &p->selector_stats[selector_id];
|
|
uint64_t n_pkts = stats->n_pkts;
|
|
int done;
|
|
|
|
/* Table. */
|
|
done = rte_swx_table_selector_select(ts->obj,
|
|
selector->mailbox,
|
|
selector->group_id_buffer,
|
|
selector->selector_buffer,
|
|
selector->member_id_buffer);
|
|
if (!done) {
|
|
/* Thread. */
|
|
TRACE("[Thread %2u] selector %u (not finalized)\n",
|
|
p->thread_id,
|
|
selector_id);
|
|
|
|
thread_yield(p);
|
|
return;
|
|
}
|
|
|
|
|
|
TRACE("[Thread %2u] selector %u\n",
|
|
p->thread_id,
|
|
selector_id);
|
|
|
|
stats->n_pkts = n_pkts + 1;
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_learner_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
uint32_t learner_id = ip->table.table_id;
|
|
struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
|
|
p->n_selectors + learner_id];
|
|
struct learner_runtime *l = &t->learners[learner_id];
|
|
struct learner_statistics *stats = &p->learner_stats[learner_id];
|
|
uint64_t action_id, n_pkts_hit, n_pkts_action, time;
|
|
uint8_t *action_data;
|
|
int done, hit;
|
|
|
|
/* Table. */
|
|
time = rte_get_tsc_cycles();
|
|
|
|
done = rte_swx_table_learner_lookup(ts->obj,
|
|
l->mailbox,
|
|
time,
|
|
l->key,
|
|
&action_id,
|
|
&action_data,
|
|
&hit);
|
|
if (!done) {
|
|
/* Thread. */
|
|
TRACE("[Thread %2u] learner %u (not finalized)\n",
|
|
p->thread_id,
|
|
learner_id);
|
|
|
|
thread_yield(p);
|
|
return;
|
|
}
|
|
|
|
action_id = hit ? action_id : ts->default_action_id;
|
|
action_data = hit ? action_data : ts->default_action_data;
|
|
n_pkts_hit = stats->n_pkts_hit[hit];
|
|
n_pkts_action = stats->n_pkts_action[action_id];
|
|
|
|
TRACE("[Thread %2u] learner %u (%s, action %u)\n",
|
|
p->thread_id,
|
|
learner_id,
|
|
hit ? "hit" : "miss",
|
|
(uint32_t)action_id);
|
|
|
|
t->action_id = action_id;
|
|
t->structs[0] = action_data;
|
|
t->hit = hit;
|
|
t->learner_id = learner_id;
|
|
t->time = time;
|
|
stats->n_pkts_hit[hit] = n_pkts_hit + 1;
|
|
stats->n_pkts_action[action_id] = n_pkts_action + 1;
|
|
|
|
/* Thread. */
|
|
thread_ip_action_call(p, t, action_id);
|
|
}
|
|
|
|
static inline void
|
|
instr_learner_af_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
uint32_t learner_id = ip->table.table_id;
|
|
struct rte_swx_table_state *ts = &t->table_state[p->n_tables +
|
|
p->n_selectors + learner_id];
|
|
struct learner_runtime *l = &t->learners[learner_id];
|
|
struct learner_statistics *stats = &p->learner_stats[learner_id];
|
|
uint64_t action_id, n_pkts_hit, n_pkts_action, time;
|
|
uint8_t *action_data;
|
|
action_func_t action_func;
|
|
int done, hit;
|
|
|
|
/* Table. */
|
|
time = rte_get_tsc_cycles();
|
|
|
|
done = rte_swx_table_learner_lookup(ts->obj,
|
|
l->mailbox,
|
|
time,
|
|
l->key,
|
|
&action_id,
|
|
&action_data,
|
|
&hit);
|
|
if (!done) {
|
|
/* Thread. */
|
|
TRACE("[Thread %2u] learner %u (not finalized)\n",
|
|
p->thread_id,
|
|
learner_id);
|
|
|
|
thread_yield(p);
|
|
return;
|
|
}
|
|
|
|
action_id = hit ? action_id : ts->default_action_id;
|
|
action_data = hit ? action_data : ts->default_action_data;
|
|
action_func = p->action_funcs[action_id];
|
|
n_pkts_hit = stats->n_pkts_hit[hit];
|
|
n_pkts_action = stats->n_pkts_action[action_id];
|
|
|
|
TRACE("[Thread %2u] learner %u (%s, action %u)\n",
|
|
p->thread_id,
|
|
learner_id,
|
|
hit ? "hit" : "miss",
|
|
(uint32_t)action_id);
|
|
|
|
t->action_id = action_id;
|
|
t->structs[0] = action_data;
|
|
t->hit = hit;
|
|
t->learner_id = learner_id;
|
|
t->time = time;
|
|
stats->n_pkts_hit[hit] = n_pkts_hit + 1;
|
|
stats->n_pkts_action[action_id] = n_pkts_action + 1;
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
|
|
/* Action */
|
|
action_func(p);
|
|
}
|
|
|
|
/*
|
|
* learn.
|
|
*/
|
|
static struct action *
|
|
action_find(struct rte_swx_pipeline *p, const char *name);
|
|
|
|
static int
|
|
action_has_nbo_args(struct action *a);
|
|
|
|
static int
|
|
learner_action_args_check(struct rte_swx_pipeline *p, struct action *a, const char *mf_name);
|
|
|
|
static int
|
|
instr_learn_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct action *a;
|
|
struct field *mf_first_arg = NULL, *mf_timeout_id = NULL;
|
|
const char *mf_first_arg_name, *mf_timeout_id_name;
|
|
|
|
CHECK(action, EINVAL);
|
|
CHECK((n_tokens == 3) || (n_tokens == 4), EINVAL);
|
|
|
|
/* Action. */
|
|
a = action_find(p, tokens[1]);
|
|
CHECK(a, EINVAL);
|
|
CHECK(!action_has_nbo_args(a), EINVAL);
|
|
|
|
/* Action first argument. */
|
|
mf_first_arg_name = (n_tokens == 4) ? tokens[2] : NULL;
|
|
CHECK(!learner_action_args_check(p, a, mf_first_arg_name), EINVAL);
|
|
|
|
if (mf_first_arg_name) {
|
|
mf_first_arg = metadata_field_parse(p, mf_first_arg_name);
|
|
CHECK(mf_first_arg, EINVAL);
|
|
}
|
|
|
|
/* Timeout ID. */
|
|
mf_timeout_id_name = (n_tokens == 4) ? tokens[3] : tokens[2];
|
|
CHECK_NAME(mf_timeout_id_name, EINVAL);
|
|
mf_timeout_id = metadata_field_parse(p, mf_timeout_id_name);
|
|
CHECK(mf_timeout_id, EINVAL);
|
|
|
|
/* Instruction. */
|
|
instr->type = INSTR_LEARNER_LEARN;
|
|
instr->learn.action_id = a->id;
|
|
instr->learn.mf_first_arg_offset = mf_first_arg ? (mf_first_arg->offset / 8) : 0;
|
|
instr->learn.mf_timeout_id_offset = mf_timeout_id->offset / 8;
|
|
instr->learn.mf_timeout_id_n_bits = mf_timeout_id->n_bits;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_learn_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_learn_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* rearm.
|
|
*/
|
|
static int
|
|
instr_rearm_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct field *mf_timeout_id;
|
|
const char *mf_timeout_id_name;
|
|
|
|
CHECK(action, EINVAL);
|
|
CHECK((n_tokens == 1) || (n_tokens == 2), EINVAL);
|
|
|
|
/* INSTR_LEARNER_REARM. */
|
|
if (n_tokens == 1) {
|
|
instr->type = INSTR_LEARNER_REARM;
|
|
return 0;
|
|
}
|
|
|
|
/* INSTR_LEARNER_REARM_NEW. */
|
|
mf_timeout_id_name = tokens[1];
|
|
CHECK_NAME(mf_timeout_id_name, EINVAL);
|
|
mf_timeout_id = metadata_field_parse(p, mf_timeout_id_name);
|
|
CHECK(mf_timeout_id, EINVAL);
|
|
|
|
instr->type = INSTR_LEARNER_REARM_NEW;
|
|
instr->learn.mf_timeout_id_offset = mf_timeout_id->offset / 8;
|
|
instr->learn.mf_timeout_id_n_bits = mf_timeout_id->n_bits;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_rearm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_rearm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_rearm_new_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_rearm_new_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* forget.
|
|
*/
|
|
static int
|
|
instr_forget_translate(struct rte_swx_pipeline *p __rte_unused,
|
|
struct action *action,
|
|
char **tokens __rte_unused,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
CHECK(action, EINVAL);
|
|
CHECK(n_tokens == 1, EINVAL);
|
|
|
|
instr->type = INSTR_LEARNER_FORGET;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_forget_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_forget_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* extern.
|
|
*/
|
|
static int
|
|
instr_extern_translate(struct rte_swx_pipeline *p,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *token = tokens[1];
|
|
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
if (token[0] == 'e') {
|
|
struct extern_obj *obj;
|
|
struct extern_type_member_func *func;
|
|
|
|
func = extern_obj_member_func_parse(p, token, &obj);
|
|
CHECK(func, EINVAL);
|
|
|
|
instr->type = INSTR_EXTERN_OBJ;
|
|
instr->ext_obj.ext_obj_id = obj->id;
|
|
instr->ext_obj.func_id = func->id;
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (token[0] == 'f') {
|
|
struct extern_func *func;
|
|
|
|
func = extern_func_parse(p, token);
|
|
CHECK(func, EINVAL);
|
|
|
|
instr->type = INSTR_EXTERN_FUNC;
|
|
instr->ext_func.ext_func_id = func->id;
|
|
|
|
return 0;
|
|
}
|
|
|
|
CHECK(0, EINVAL);
|
|
}
|
|
|
|
static inline void
|
|
instr_extern_obj_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
uint32_t done;
|
|
|
|
/* Extern object member function execute. */
|
|
done = __instr_extern_obj_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc_cond(t, done);
|
|
thread_yield_cond(p, done ^ 1);
|
|
}
|
|
|
|
static inline void
|
|
instr_extern_func_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
uint32_t done;
|
|
|
|
/* Extern function execute. */
|
|
done = __instr_extern_func_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc_cond(t, done);
|
|
thread_yield_cond(p, done ^ 1);
|
|
}
|
|
|
|
/*
|
|
* hash.
|
|
*/
|
|
static int
|
|
instr_hash_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
struct hash_func *func;
|
|
struct field *dst, *src_first, *src_last;
|
|
uint32_t src_struct_id_first = 0, src_struct_id_last = 0;
|
|
|
|
CHECK(n_tokens == 5, EINVAL);
|
|
|
|
func = hash_func_find(p, tokens[1]);
|
|
CHECK(func, EINVAL);
|
|
|
|
dst = metadata_field_parse(p, tokens[2]);
|
|
CHECK(dst, EINVAL);
|
|
|
|
src_first = struct_field_parse(p, action, tokens[3], &src_struct_id_first);
|
|
CHECK(src_first, EINVAL);
|
|
|
|
src_last = struct_field_parse(p, action, tokens[4], &src_struct_id_last);
|
|
CHECK(src_last, EINVAL);
|
|
CHECK(src_struct_id_first == src_struct_id_last, EINVAL);
|
|
|
|
instr->type = INSTR_HASH_FUNC;
|
|
instr->hash_func.hash_func_id = (uint8_t)func->id;
|
|
instr->hash_func.dst.offset = (uint8_t)dst->offset / 8;
|
|
instr->hash_func.dst.n_bits = (uint8_t)dst->n_bits;
|
|
instr->hash_func.src.struct_id = (uint8_t)src_struct_id_first;
|
|
instr->hash_func.src.offset = (uint16_t)src_first->offset / 8;
|
|
instr->hash_func.src.n_bytes = (uint16_t)((src_last->offset + src_last->n_bits -
|
|
src_first->offset) / 8);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_hash_func_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Extern function execute. */
|
|
__instr_hash_func_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* mov.
|
|
*/
|
|
static int
|
|
instr_mov_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct field *fdst, *fsrc;
|
|
uint64_t src_val;
|
|
uint32_t dst_struct_id = 0, src_struct_id = 0;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size, EINVAL);
|
|
|
|
/* MOV, MOV_MH, MOV_HM or MOV_HH. */
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
if (fsrc) {
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_MOV;
|
|
if (dst[0] != 'h' && src[0] == 'h')
|
|
instr->type = INSTR_MOV_MH;
|
|
if (dst[0] == 'h' && src[0] != 'h')
|
|
instr->type = INSTR_MOV_HM;
|
|
if (dst[0] == 'h' && src[0] == 'h')
|
|
instr->type = INSTR_MOV_HH;
|
|
|
|
instr->mov.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->mov.dst.n_bits = fdst->n_bits;
|
|
instr->mov.dst.offset = fdst->offset / 8;
|
|
instr->mov.src.struct_id = (uint8_t)src_struct_id;
|
|
instr->mov.src.n_bits = fsrc->n_bits;
|
|
instr->mov.src.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* MOV_I. */
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
if (dst[0] == 'h')
|
|
src_val = hton64(src_val) >> (64 - fdst->n_bits);
|
|
|
|
instr->type = INSTR_MOV_I;
|
|
instr->mov.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->mov.dst.n_bits = fdst->n_bits;
|
|
instr->mov.dst.offset = fdst->offset / 8;
|
|
instr->mov.src_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_mov_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_mov_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_mov_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_mov_mh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_mov_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_mov_hm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_mov_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_mov_hh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_mov_i_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_mov_i_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* dma.
|
|
*/
|
|
static inline void
|
|
instr_dma_ht_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_dma_ht_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_dma_ht2_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_dma_ht2_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_dma_ht3_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_dma_ht3_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_dma_ht4_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_dma_ht4_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_dma_ht5_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_dma_ht5_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_dma_ht6_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_dma_ht6_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_dma_ht7_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_dma_ht7_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_dma_ht8_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
__instr_dma_ht8_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* alu.
|
|
*/
|
|
static int
|
|
instr_alu_add_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct field *fdst, *fsrc;
|
|
uint64_t src_val;
|
|
uint32_t dst_struct_id = 0, src_struct_id = 0;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size, EINVAL);
|
|
|
|
/* ADD, ADD_HM, ADD_MH, ADD_HH. */
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
if (fsrc) {
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_ALU_ADD;
|
|
if (dst[0] == 'h' && src[0] != 'h')
|
|
instr->type = INSTR_ALU_ADD_HM;
|
|
if (dst[0] != 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_ADD_MH;
|
|
if (dst[0] == 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_ADD_HH;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src.struct_id = (uint8_t)src_struct_id;
|
|
instr->alu.src.n_bits = fsrc->n_bits;
|
|
instr->alu.src.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* ADD_MI, ADD_HI. */
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
instr->type = INSTR_ALU_ADD_MI;
|
|
if (dst[0] == 'h')
|
|
instr->type = INSTR_ALU_ADD_HI;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_alu_sub_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct field *fdst, *fsrc;
|
|
uint64_t src_val;
|
|
uint32_t dst_struct_id = 0, src_struct_id = 0;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size, EINVAL);
|
|
|
|
/* SUB, SUB_HM, SUB_MH, SUB_HH. */
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
if (fsrc) {
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_ALU_SUB;
|
|
if (dst[0] == 'h' && src[0] != 'h')
|
|
instr->type = INSTR_ALU_SUB_HM;
|
|
if (dst[0] != 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_SUB_MH;
|
|
if (dst[0] == 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_SUB_HH;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src.struct_id = (uint8_t)src_struct_id;
|
|
instr->alu.src.n_bits = fsrc->n_bits;
|
|
instr->alu.src.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* SUB_MI, SUB_HI. */
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
instr->type = INSTR_ALU_SUB_MI;
|
|
if (dst[0] == 'h')
|
|
instr->type = INSTR_ALU_SUB_HI;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_alu_ckadd_translate(struct rte_swx_pipeline *p,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct header *hdst, *hsrc;
|
|
struct field *fdst, *fsrc;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = header_field_parse(p, dst, &hdst);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size && (fdst->n_bits == 16), EINVAL);
|
|
|
|
/* CKADD_FIELD. */
|
|
fsrc = header_field_parse(p, src, &hsrc);
|
|
if (fsrc) {
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_ALU_CKADD_FIELD;
|
|
instr->alu.dst.struct_id = (uint8_t)hdst->struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src.struct_id = (uint8_t)hsrc->struct_id;
|
|
instr->alu.src.n_bits = fsrc->n_bits;
|
|
instr->alu.src.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* CKADD_STRUCT, CKADD_STRUCT20. */
|
|
hsrc = header_parse(p, src);
|
|
CHECK(hsrc, EINVAL);
|
|
|
|
instr->type = INSTR_ALU_CKADD_STRUCT;
|
|
if (!hsrc->st->var_size && ((hsrc->st->n_bits / 8) == 20))
|
|
instr->type = INSTR_ALU_CKADD_STRUCT20;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)hdst->struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src.struct_id = (uint8_t)hsrc->struct_id;
|
|
instr->alu.src.n_bits = (uint8_t)hsrc->id; /* The src header ID is stored here. */
|
|
instr->alu.src.offset = 0; /* Unused. */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_alu_cksub_translate(struct rte_swx_pipeline *p,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct header *hdst, *hsrc;
|
|
struct field *fdst, *fsrc;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = header_field_parse(p, dst, &hdst);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size && (fdst->n_bits == 16), EINVAL);
|
|
|
|
fsrc = header_field_parse(p, src, &hsrc);
|
|
CHECK(fsrc, EINVAL);
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_ALU_CKSUB_FIELD;
|
|
instr->alu.dst.struct_id = (uint8_t)hdst->struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src.struct_id = (uint8_t)hsrc->struct_id;
|
|
instr->alu.src.n_bits = fsrc->n_bits;
|
|
instr->alu.src.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_alu_shl_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct field *fdst, *fsrc;
|
|
uint64_t src_val;
|
|
uint32_t dst_struct_id = 0, src_struct_id = 0;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size, EINVAL);
|
|
|
|
/* SHL, SHL_HM, SHL_MH, SHL_HH. */
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
if (fsrc) {
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_ALU_SHL;
|
|
if (dst[0] == 'h' && src[0] != 'h')
|
|
instr->type = INSTR_ALU_SHL_HM;
|
|
if (dst[0] != 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_SHL_MH;
|
|
if (dst[0] == 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_SHL_HH;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src.struct_id = (uint8_t)src_struct_id;
|
|
instr->alu.src.n_bits = fsrc->n_bits;
|
|
instr->alu.src.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* SHL_MI, SHL_HI. */
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
instr->type = INSTR_ALU_SHL_MI;
|
|
if (dst[0] == 'h')
|
|
instr->type = INSTR_ALU_SHL_HI;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_alu_shr_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct field *fdst, *fsrc;
|
|
uint64_t src_val;
|
|
uint32_t dst_struct_id = 0, src_struct_id = 0;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size, EINVAL);
|
|
|
|
/* SHR, SHR_HM, SHR_MH, SHR_HH. */
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
if (fsrc) {
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_ALU_SHR;
|
|
if (dst[0] == 'h' && src[0] != 'h')
|
|
instr->type = INSTR_ALU_SHR_HM;
|
|
if (dst[0] != 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_SHR_MH;
|
|
if (dst[0] == 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_SHR_HH;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src.struct_id = (uint8_t)src_struct_id;
|
|
instr->alu.src.n_bits = fsrc->n_bits;
|
|
instr->alu.src.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* SHR_MI, SHR_HI. */
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
instr->type = INSTR_ALU_SHR_MI;
|
|
if (dst[0] == 'h')
|
|
instr->type = INSTR_ALU_SHR_HI;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_alu_and_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct field *fdst, *fsrc;
|
|
uint64_t src_val;
|
|
uint32_t dst_struct_id = 0, src_struct_id = 0;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size, EINVAL);
|
|
|
|
/* AND, AND_MH, AND_HM, AND_HH. */
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
if (fsrc) {
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_ALU_AND;
|
|
if (dst[0] != 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_AND_MH;
|
|
if (dst[0] == 'h' && src[0] != 'h')
|
|
instr->type = INSTR_ALU_AND_HM;
|
|
if (dst[0] == 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_AND_HH;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src.struct_id = (uint8_t)src_struct_id;
|
|
instr->alu.src.n_bits = fsrc->n_bits;
|
|
instr->alu.src.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* AND_I. */
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
if (dst[0] == 'h')
|
|
src_val = hton64(src_val) >> (64 - fdst->n_bits);
|
|
|
|
instr->type = INSTR_ALU_AND_I;
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_alu_or_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct field *fdst, *fsrc;
|
|
uint64_t src_val;
|
|
uint32_t dst_struct_id = 0, src_struct_id = 0;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size, EINVAL);
|
|
|
|
/* OR, OR_MH, OR_HM, OR_HH. */
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
if (fsrc) {
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_ALU_OR;
|
|
if (dst[0] != 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_OR_MH;
|
|
if (dst[0] == 'h' && src[0] != 'h')
|
|
instr->type = INSTR_ALU_OR_HM;
|
|
if (dst[0] == 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_OR_HH;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src.struct_id = (uint8_t)src_struct_id;
|
|
instr->alu.src.n_bits = fsrc->n_bits;
|
|
instr->alu.src.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* OR_I. */
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
if (dst[0] == 'h')
|
|
src_val = hton64(src_val) >> (64 - fdst->n_bits);
|
|
|
|
instr->type = INSTR_ALU_OR_I;
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_alu_xor_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *src = tokens[2];
|
|
struct field *fdst, *fsrc;
|
|
uint64_t src_val;
|
|
uint32_t dst_struct_id = 0, src_struct_id = 0;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size, EINVAL);
|
|
|
|
/* XOR, XOR_MH, XOR_HM, XOR_HH. */
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
if (fsrc) {
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_ALU_XOR;
|
|
if (dst[0] != 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_XOR_MH;
|
|
if (dst[0] == 'h' && src[0] != 'h')
|
|
instr->type = INSTR_ALU_XOR_HM;
|
|
if (dst[0] == 'h' && src[0] == 'h')
|
|
instr->type = INSTR_ALU_XOR_HH;
|
|
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src.struct_id = (uint8_t)src_struct_id;
|
|
instr->alu.src.n_bits = fsrc->n_bits;
|
|
instr->alu.src.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* XOR_I. */
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
if (dst[0] == 'h')
|
|
src_val = hton64(src_val) >> (64 - fdst->n_bits);
|
|
|
|
instr->type = INSTR_ALU_XOR_I;
|
|
instr->alu.dst.struct_id = (uint8_t)dst_struct_id;
|
|
instr->alu.dst.n_bits = fdst->n_bits;
|
|
instr->alu.dst.offset = fdst->offset / 8;
|
|
instr->alu.src_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_add_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs */
|
|
__instr_alu_add_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_add_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_add_mh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_add_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_add_hm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_add_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_add_hh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_add_mi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_add_mi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_add_hi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_add_hi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_sub_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_sub_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_sub_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_sub_mh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_sub_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_sub_hm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_sub_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_sub_hh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_sub_mi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_sub_mi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_sub_hi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_sub_hi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shl_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shl_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shl_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shl_mh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shl_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shl_hm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shl_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shl_hh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shl_mi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shl_mi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shl_hi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shl_hi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shr_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shr_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shr_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shr_mh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shr_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shr_hm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shr_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shr_hh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shr_mi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shr_mi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_shr_hi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_shr_hi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_and_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_and_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_and_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_and_mh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_and_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_and_hm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_and_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_and_hh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_and_i_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_and_i_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_or_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_or_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_or_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_or_mh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_or_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_or_hm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_or_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_or_hh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_or_i_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_or_i_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_xor_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_xor_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_xor_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_xor_mh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_xor_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_xor_hm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_xor_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_xor_hh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_xor_i_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_xor_i_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_ckadd_field_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_ckadd_field_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_cksub_field_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_cksub_field_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_ckadd_struct20_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_ckadd_struct20_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_alu_ckadd_struct_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_alu_ckadd_struct_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* Register array.
|
|
*/
|
|
static struct regarray *
|
|
regarray_find(struct rte_swx_pipeline *p, const char *name);
|
|
|
|
static int
|
|
instr_regprefetch_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *regarray = tokens[1], *idx = tokens[2];
|
|
struct regarray *r;
|
|
struct field *fidx;
|
|
uint32_t idx_struct_id, idx_val;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
r = regarray_find(p, regarray);
|
|
CHECK(r, EINVAL);
|
|
|
|
/* REGPREFETCH_RH, REGPREFETCH_RM. */
|
|
fidx = struct_field_parse(p, action, idx, &idx_struct_id);
|
|
if (fidx) {
|
|
CHECK(!fidx->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_REGPREFETCH_RM;
|
|
if (idx[0] == 'h')
|
|
instr->type = INSTR_REGPREFETCH_RH;
|
|
|
|
instr->regarray.regarray_id = r->id;
|
|
instr->regarray.idx.struct_id = (uint8_t)idx_struct_id;
|
|
instr->regarray.idx.n_bits = fidx->n_bits;
|
|
instr->regarray.idx.offset = fidx->offset / 8;
|
|
instr->regarray.dstsrc_val = 0; /* Unused. */
|
|
return 0;
|
|
}
|
|
|
|
/* REGPREFETCH_RI. */
|
|
idx_val = strtoul(idx, &idx, 0);
|
|
CHECK(!idx[0], EINVAL);
|
|
|
|
instr->type = INSTR_REGPREFETCH_RI;
|
|
instr->regarray.regarray_id = r->id;
|
|
instr->regarray.idx_val = idx_val;
|
|
instr->regarray.dstsrc_val = 0; /* Unused. */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_regrd_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *dst = tokens[1], *regarray = tokens[2], *idx = tokens[3];
|
|
struct regarray *r;
|
|
struct field *fdst, *fidx;
|
|
uint32_t dst_struct_id, idx_struct_id, idx_val;
|
|
|
|
CHECK(n_tokens == 4, EINVAL);
|
|
|
|
r = regarray_find(p, regarray);
|
|
CHECK(r, EINVAL);
|
|
|
|
fdst = struct_field_parse(p, NULL, dst, &dst_struct_id);
|
|
CHECK(fdst, EINVAL);
|
|
CHECK(!fdst->var_size, EINVAL);
|
|
|
|
/* REGRD_HRH, REGRD_HRM, REGRD_MRH, REGRD_MRM. */
|
|
fidx = struct_field_parse(p, action, idx, &idx_struct_id);
|
|
if (fidx) {
|
|
CHECK(!fidx->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_REGRD_MRM;
|
|
if (dst[0] == 'h' && idx[0] != 'h')
|
|
instr->type = INSTR_REGRD_HRM;
|
|
if (dst[0] != 'h' && idx[0] == 'h')
|
|
instr->type = INSTR_REGRD_MRH;
|
|
if (dst[0] == 'h' && idx[0] == 'h')
|
|
instr->type = INSTR_REGRD_HRH;
|
|
|
|
instr->regarray.regarray_id = r->id;
|
|
instr->regarray.idx.struct_id = (uint8_t)idx_struct_id;
|
|
instr->regarray.idx.n_bits = fidx->n_bits;
|
|
instr->regarray.idx.offset = fidx->offset / 8;
|
|
instr->regarray.dstsrc.struct_id = (uint8_t)dst_struct_id;
|
|
instr->regarray.dstsrc.n_bits = fdst->n_bits;
|
|
instr->regarray.dstsrc.offset = fdst->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* REGRD_MRI, REGRD_HRI. */
|
|
idx_val = strtoul(idx, &idx, 0);
|
|
CHECK(!idx[0], EINVAL);
|
|
|
|
instr->type = INSTR_REGRD_MRI;
|
|
if (dst[0] == 'h')
|
|
instr->type = INSTR_REGRD_HRI;
|
|
|
|
instr->regarray.regarray_id = r->id;
|
|
instr->regarray.idx_val = idx_val;
|
|
instr->regarray.dstsrc.struct_id = (uint8_t)dst_struct_id;
|
|
instr->regarray.dstsrc.n_bits = fdst->n_bits;
|
|
instr->regarray.dstsrc.offset = fdst->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_regwr_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *regarray = tokens[1], *idx = tokens[2], *src = tokens[3];
|
|
struct regarray *r;
|
|
struct field *fidx, *fsrc;
|
|
uint64_t src_val;
|
|
uint32_t idx_struct_id, idx_val, src_struct_id;
|
|
|
|
CHECK(n_tokens == 4, EINVAL);
|
|
|
|
r = regarray_find(p, regarray);
|
|
CHECK(r, EINVAL);
|
|
|
|
/* REGWR_RHH, REGWR_RHM, REGWR_RMH, REGWR_RMM. */
|
|
fidx = struct_field_parse(p, action, idx, &idx_struct_id);
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
if (fidx && fsrc) {
|
|
CHECK(!fidx->var_size, EINVAL);
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_REGWR_RMM;
|
|
if (idx[0] == 'h' && src[0] != 'h')
|
|
instr->type = INSTR_REGWR_RHM;
|
|
if (idx[0] != 'h' && src[0] == 'h')
|
|
instr->type = INSTR_REGWR_RMH;
|
|
if (idx[0] == 'h' && src[0] == 'h')
|
|
instr->type = INSTR_REGWR_RHH;
|
|
|
|
instr->regarray.regarray_id = r->id;
|
|
instr->regarray.idx.struct_id = (uint8_t)idx_struct_id;
|
|
instr->regarray.idx.n_bits = fidx->n_bits;
|
|
instr->regarray.idx.offset = fidx->offset / 8;
|
|
instr->regarray.dstsrc.struct_id = (uint8_t)src_struct_id;
|
|
instr->regarray.dstsrc.n_bits = fsrc->n_bits;
|
|
instr->regarray.dstsrc.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* REGWR_RHI, REGWR_RMI. */
|
|
if (fidx && !fsrc) {
|
|
CHECK(!fidx->var_size, EINVAL);
|
|
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
instr->type = INSTR_REGWR_RMI;
|
|
if (idx[0] == 'h')
|
|
instr->type = INSTR_REGWR_RHI;
|
|
|
|
instr->regarray.regarray_id = r->id;
|
|
instr->regarray.idx.struct_id = (uint8_t)idx_struct_id;
|
|
instr->regarray.idx.n_bits = fidx->n_bits;
|
|
instr->regarray.idx.offset = fidx->offset / 8;
|
|
instr->regarray.dstsrc_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
/* REGWR_RIH, REGWR_RIM. */
|
|
if (!fidx && fsrc) {
|
|
idx_val = strtoul(idx, &idx, 0);
|
|
CHECK(!idx[0], EINVAL);
|
|
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_REGWR_RIM;
|
|
if (src[0] == 'h')
|
|
instr->type = INSTR_REGWR_RIH;
|
|
|
|
instr->regarray.regarray_id = r->id;
|
|
instr->regarray.idx_val = idx_val;
|
|
instr->regarray.dstsrc.struct_id = (uint8_t)src_struct_id;
|
|
instr->regarray.dstsrc.n_bits = fsrc->n_bits;
|
|
instr->regarray.dstsrc.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* REGWR_RII. */
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
idx_val = strtoul(idx, &idx, 0);
|
|
CHECK(!idx[0], EINVAL);
|
|
|
|
instr->type = INSTR_REGWR_RII;
|
|
instr->regarray.idx_val = idx_val;
|
|
instr->regarray.dstsrc_val = src_val;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_regadd_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *regarray = tokens[1], *idx = tokens[2], *src = tokens[3];
|
|
struct regarray *r;
|
|
struct field *fidx, *fsrc;
|
|
uint64_t src_val;
|
|
uint32_t idx_struct_id, idx_val, src_struct_id;
|
|
|
|
CHECK(n_tokens == 4, EINVAL);
|
|
|
|
r = regarray_find(p, regarray);
|
|
CHECK(r, EINVAL);
|
|
|
|
/* REGADD_RHH, REGADD_RHM, REGADD_RMH, REGADD_RMM. */
|
|
fidx = struct_field_parse(p, action, idx, &idx_struct_id);
|
|
fsrc = struct_field_parse(p, action, src, &src_struct_id);
|
|
if (fidx && fsrc) {
|
|
CHECK(!fidx->var_size, EINVAL);
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_REGADD_RMM;
|
|
if (idx[0] == 'h' && src[0] != 'h')
|
|
instr->type = INSTR_REGADD_RHM;
|
|
if (idx[0] != 'h' && src[0] == 'h')
|
|
instr->type = INSTR_REGADD_RMH;
|
|
if (idx[0] == 'h' && src[0] == 'h')
|
|
instr->type = INSTR_REGADD_RHH;
|
|
|
|
instr->regarray.regarray_id = r->id;
|
|
instr->regarray.idx.struct_id = (uint8_t)idx_struct_id;
|
|
instr->regarray.idx.n_bits = fidx->n_bits;
|
|
instr->regarray.idx.offset = fidx->offset / 8;
|
|
instr->regarray.dstsrc.struct_id = (uint8_t)src_struct_id;
|
|
instr->regarray.dstsrc.n_bits = fsrc->n_bits;
|
|
instr->regarray.dstsrc.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* REGADD_RHI, REGADD_RMI. */
|
|
if (fidx && !fsrc) {
|
|
CHECK(!fidx->var_size, EINVAL);
|
|
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
instr->type = INSTR_REGADD_RMI;
|
|
if (idx[0] == 'h')
|
|
instr->type = INSTR_REGADD_RHI;
|
|
|
|
instr->regarray.regarray_id = r->id;
|
|
instr->regarray.idx.struct_id = (uint8_t)idx_struct_id;
|
|
instr->regarray.idx.n_bits = fidx->n_bits;
|
|
instr->regarray.idx.offset = fidx->offset / 8;
|
|
instr->regarray.dstsrc_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
/* REGADD_RIH, REGADD_RIM. */
|
|
if (!fidx && fsrc) {
|
|
idx_val = strtoul(idx, &idx, 0);
|
|
CHECK(!idx[0], EINVAL);
|
|
|
|
CHECK(!fsrc->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_REGADD_RIM;
|
|
if (src[0] == 'h')
|
|
instr->type = INSTR_REGADD_RIH;
|
|
|
|
instr->regarray.regarray_id = r->id;
|
|
instr->regarray.idx_val = idx_val;
|
|
instr->regarray.dstsrc.struct_id = (uint8_t)src_struct_id;
|
|
instr->regarray.dstsrc.n_bits = fsrc->n_bits;
|
|
instr->regarray.dstsrc.offset = fsrc->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* REGADD_RII. */
|
|
src_val = strtoull(src, &src, 0);
|
|
CHECK(!src[0], EINVAL);
|
|
|
|
idx_val = strtoul(idx, &idx, 0);
|
|
CHECK(!idx[0], EINVAL);
|
|
|
|
instr->type = INSTR_REGADD_RII;
|
|
instr->regarray.idx_val = idx_val;
|
|
instr->regarray.dstsrc_val = src_val;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_regprefetch_rh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regprefetch_rh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regprefetch_rm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regprefetch_rm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regprefetch_ri_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regprefetch_ri_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regrd_hrh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regrd_hrh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regrd_hrm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regrd_hrm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regrd_mrh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regrd_mrh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regrd_mrm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regrd_mrm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regrd_hri_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regrd_hri_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regrd_mri_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regrd_mri_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regwr_rhh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regwr_rhh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regwr_rhm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regwr_rhm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regwr_rmh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regwr_rmh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regwr_rmm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regwr_rmm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regwr_rhi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regwr_rhi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regwr_rmi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regwr_rmi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regwr_rih_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regwr_rih_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regwr_rim_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regwr_rim_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regwr_rii_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regwr_rii_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regadd_rhh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regadd_rhh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regadd_rhm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regadd_rhm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regadd_rmh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regadd_rmh_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regadd_rmm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regadd_rmm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regadd_rhi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regadd_rhi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regadd_rmi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regadd_rmi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regadd_rih_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regadd_rih_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regadd_rim_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regadd_rim_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_regadd_rii_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_regadd_rii_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* metarray.
|
|
*/
|
|
static struct metarray *
|
|
metarray_find(struct rte_swx_pipeline *p, const char *name);
|
|
|
|
static int
|
|
instr_metprefetch_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *metarray = tokens[1], *idx = tokens[2];
|
|
struct metarray *m;
|
|
struct field *fidx;
|
|
uint32_t idx_struct_id, idx_val;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
m = metarray_find(p, metarray);
|
|
CHECK(m, EINVAL);
|
|
|
|
/* METPREFETCH_H, METPREFETCH_M. */
|
|
fidx = struct_field_parse(p, action, idx, &idx_struct_id);
|
|
if (fidx) {
|
|
CHECK(!fidx->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_METPREFETCH_M;
|
|
if (idx[0] == 'h')
|
|
instr->type = INSTR_METPREFETCH_H;
|
|
|
|
instr->meter.metarray_id = m->id;
|
|
instr->meter.idx.struct_id = (uint8_t)idx_struct_id;
|
|
instr->meter.idx.n_bits = fidx->n_bits;
|
|
instr->meter.idx.offset = fidx->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* METPREFETCH_I. */
|
|
idx_val = strtoul(idx, &idx, 0);
|
|
CHECK(!idx[0], EINVAL);
|
|
|
|
instr->type = INSTR_METPREFETCH_I;
|
|
instr->meter.metarray_id = m->id;
|
|
instr->meter.idx_val = idx_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_meter_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
char *metarray = tokens[1], *idx = tokens[2], *length = tokens[3];
|
|
char *color_in = tokens[4], *color_out = tokens[5];
|
|
struct metarray *m;
|
|
struct field *fidx, *flength, *fcin, *fcout;
|
|
uint32_t idx_struct_id, length_struct_id;
|
|
uint32_t color_in_struct_id, color_out_struct_id;
|
|
|
|
CHECK(n_tokens == 6, EINVAL);
|
|
|
|
m = metarray_find(p, metarray);
|
|
CHECK(m, EINVAL);
|
|
|
|
fidx = struct_field_parse(p, action, idx, &idx_struct_id);
|
|
|
|
flength = struct_field_parse(p, action, length, &length_struct_id);
|
|
CHECK(flength, EINVAL);
|
|
CHECK(!flength->var_size, EINVAL);
|
|
|
|
fcin = struct_field_parse(p, action, color_in, &color_in_struct_id);
|
|
|
|
fcout = struct_field_parse(p, NULL, color_out, &color_out_struct_id);
|
|
CHECK(fcout, EINVAL);
|
|
CHECK(!fcout->var_size, EINVAL);
|
|
|
|
/* index = HMEFT, length = HMEFT, color_in = MEFT, color_out = MEF. */
|
|
if (fidx && fcin) {
|
|
CHECK(!fidx->var_size, EINVAL);
|
|
CHECK(!fcin->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_METER_MMM;
|
|
if (idx[0] == 'h' && length[0] == 'h')
|
|
instr->type = INSTR_METER_HHM;
|
|
if (idx[0] == 'h' && length[0] != 'h')
|
|
instr->type = INSTR_METER_HMM;
|
|
if (idx[0] != 'h' && length[0] == 'h')
|
|
instr->type = INSTR_METER_MHM;
|
|
|
|
instr->meter.metarray_id = m->id;
|
|
|
|
instr->meter.idx.struct_id = (uint8_t)idx_struct_id;
|
|
instr->meter.idx.n_bits = fidx->n_bits;
|
|
instr->meter.idx.offset = fidx->offset / 8;
|
|
|
|
instr->meter.length.struct_id = (uint8_t)length_struct_id;
|
|
instr->meter.length.n_bits = flength->n_bits;
|
|
instr->meter.length.offset = flength->offset / 8;
|
|
|
|
instr->meter.color_in.struct_id = (uint8_t)color_in_struct_id;
|
|
instr->meter.color_in.n_bits = fcin->n_bits;
|
|
instr->meter.color_in.offset = fcin->offset / 8;
|
|
|
|
instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
|
|
instr->meter.color_out.n_bits = fcout->n_bits;
|
|
instr->meter.color_out.offset = fcout->offset / 8;
|
|
}
|
|
|
|
/* index = HMEFT, length = HMEFT, color_in = I, color_out = MEF. */
|
|
if (fidx && !fcin) {
|
|
uint32_t color_in_val;
|
|
|
|
CHECK(!fidx->var_size, EINVAL);
|
|
|
|
color_in_val = strtoul(color_in, &color_in, 0);
|
|
CHECK(!color_in[0], EINVAL);
|
|
|
|
instr->type = INSTR_METER_MMI;
|
|
if (idx[0] == 'h' && length[0] == 'h')
|
|
instr->type = INSTR_METER_HHI;
|
|
if (idx[0] == 'h' && length[0] != 'h')
|
|
instr->type = INSTR_METER_HMI;
|
|
if (idx[0] != 'h' && length[0] == 'h')
|
|
instr->type = INSTR_METER_MHI;
|
|
|
|
instr->meter.metarray_id = m->id;
|
|
|
|
instr->meter.idx.struct_id = (uint8_t)idx_struct_id;
|
|
instr->meter.idx.n_bits = fidx->n_bits;
|
|
instr->meter.idx.offset = fidx->offset / 8;
|
|
|
|
instr->meter.length.struct_id = (uint8_t)length_struct_id;
|
|
instr->meter.length.n_bits = flength->n_bits;
|
|
instr->meter.length.offset = flength->offset / 8;
|
|
|
|
instr->meter.color_in_val = color_in_val;
|
|
|
|
instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
|
|
instr->meter.color_out.n_bits = fcout->n_bits;
|
|
instr->meter.color_out.offset = fcout->offset / 8;
|
|
}
|
|
|
|
/* index = I, length = HMEFT, color_in = MEFT, color_out = MEF. */
|
|
if (!fidx && fcin) {
|
|
uint32_t idx_val;
|
|
|
|
idx_val = strtoul(idx, &idx, 0);
|
|
CHECK(!idx[0], EINVAL);
|
|
|
|
CHECK(!fcin->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_METER_IMM;
|
|
if (length[0] == 'h')
|
|
instr->type = INSTR_METER_IHM;
|
|
|
|
instr->meter.metarray_id = m->id;
|
|
|
|
instr->meter.idx_val = idx_val;
|
|
|
|
instr->meter.length.struct_id = (uint8_t)length_struct_id;
|
|
instr->meter.length.n_bits = flength->n_bits;
|
|
instr->meter.length.offset = flength->offset / 8;
|
|
|
|
instr->meter.color_in.struct_id = (uint8_t)color_in_struct_id;
|
|
instr->meter.color_in.n_bits = fcin->n_bits;
|
|
instr->meter.color_in.offset = fcin->offset / 8;
|
|
|
|
instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
|
|
instr->meter.color_out.n_bits = fcout->n_bits;
|
|
instr->meter.color_out.offset = fcout->offset / 8;
|
|
}
|
|
|
|
/* index = I, length = HMEFT, color_in = I, color_out = MEF. */
|
|
if (!fidx && !fcin) {
|
|
uint32_t idx_val, color_in_val;
|
|
|
|
idx_val = strtoul(idx, &idx, 0);
|
|
CHECK(!idx[0], EINVAL);
|
|
|
|
color_in_val = strtoul(color_in, &color_in, 0);
|
|
CHECK(!color_in[0], EINVAL);
|
|
|
|
instr->type = INSTR_METER_IMI;
|
|
if (length[0] == 'h')
|
|
instr->type = INSTR_METER_IHI;
|
|
|
|
instr->meter.metarray_id = m->id;
|
|
|
|
instr->meter.idx_val = idx_val;
|
|
|
|
instr->meter.length.struct_id = (uint8_t)length_struct_id;
|
|
instr->meter.length.n_bits = flength->n_bits;
|
|
instr->meter.length.offset = flength->offset / 8;
|
|
|
|
instr->meter.color_in_val = color_in_val;
|
|
|
|
instr->meter.color_out.struct_id = (uint8_t)color_out_struct_id;
|
|
instr->meter.color_out.n_bits = fcout->n_bits;
|
|
instr->meter.color_out.offset = fcout->offset / 8;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_metprefetch_h_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_metprefetch_h_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_metprefetch_m_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_metprefetch_m_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_metprefetch_i_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_metprefetch_i_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_hhm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_hhm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_hhi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_hhi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_hmm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_hmm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_hmi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_hmi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_mhm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_mhm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_mhi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_mhi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_mmm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_mmm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_mmi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_mmi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_ihm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_ihm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_ihi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_ihi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_imm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_imm_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_meter_imi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
/* Structs. */
|
|
__instr_meter_imi_exec(p, t, ip);
|
|
|
|
/* Thread. */
|
|
thread_ip_inc(p);
|
|
}
|
|
|
|
/*
|
|
* jmp.
|
|
*/
|
|
static int
|
|
instr_jmp_translate(struct rte_swx_pipeline *p __rte_unused,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
instr->type = INSTR_JMP;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_valid_translate(struct rte_swx_pipeline *p,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
struct header *h;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
h = header_parse(p, tokens[2]);
|
|
CHECK(h, EINVAL);
|
|
|
|
instr->type = INSTR_JMP_VALID;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
instr->jmp.header_id = h->id;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_invalid_translate(struct rte_swx_pipeline *p,
|
|
struct action *action __rte_unused,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
struct header *h;
|
|
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
h = header_parse(p, tokens[2]);
|
|
CHECK(h, EINVAL);
|
|
|
|
instr->type = INSTR_JMP_INVALID;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
instr->jmp.header_id = h->id;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_hit_translate(struct rte_swx_pipeline *p __rte_unused,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
CHECK(!action, EINVAL);
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
instr->type = INSTR_JMP_HIT;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_miss_translate(struct rte_swx_pipeline *p __rte_unused,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
CHECK(!action, EINVAL);
|
|
CHECK(n_tokens == 2, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
instr->type = INSTR_JMP_MISS;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_action_hit_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
struct action *a;
|
|
|
|
CHECK(!action, EINVAL);
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
a = action_find(p, tokens[2]);
|
|
CHECK(a, EINVAL);
|
|
|
|
instr->type = INSTR_JMP_ACTION_HIT;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
instr->jmp.action_id = a->id;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_action_miss_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
struct action *a;
|
|
|
|
CHECK(!action, EINVAL);
|
|
CHECK(n_tokens == 3, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
a = action_find(p, tokens[2]);
|
|
CHECK(a, EINVAL);
|
|
|
|
instr->type = INSTR_JMP_ACTION_MISS;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
instr->jmp.action_id = a->id;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_eq_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
char *a = tokens[2], *b = tokens[3];
|
|
struct field *fa, *fb;
|
|
uint64_t b_val;
|
|
uint32_t a_struct_id, b_struct_id;
|
|
|
|
CHECK(n_tokens == 4, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
fa = struct_field_parse(p, action, a, &a_struct_id);
|
|
CHECK(fa, EINVAL);
|
|
CHECK(!fa->var_size, EINVAL);
|
|
|
|
/* JMP_EQ, JMP_EQ_MH, JMP_EQ_HM, JMP_EQ_HH. */
|
|
fb = struct_field_parse(p, action, b, &b_struct_id);
|
|
if (fb) {
|
|
CHECK(!fb->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_JMP_EQ;
|
|
if (a[0] != 'h' && b[0] == 'h')
|
|
instr->type = INSTR_JMP_EQ_MH;
|
|
if (a[0] == 'h' && b[0] != 'h')
|
|
instr->type = INSTR_JMP_EQ_HM;
|
|
if (a[0] == 'h' && b[0] == 'h')
|
|
instr->type = INSTR_JMP_EQ_HH;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
|
|
instr->jmp.a.struct_id = (uint8_t)a_struct_id;
|
|
instr->jmp.a.n_bits = fa->n_bits;
|
|
instr->jmp.a.offset = fa->offset / 8;
|
|
instr->jmp.b.struct_id = (uint8_t)b_struct_id;
|
|
instr->jmp.b.n_bits = fb->n_bits;
|
|
instr->jmp.b.offset = fb->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* JMP_EQ_I. */
|
|
b_val = strtoull(b, &b, 0);
|
|
CHECK(!b[0], EINVAL);
|
|
|
|
if (a[0] == 'h')
|
|
b_val = hton64(b_val) >> (64 - fa->n_bits);
|
|
|
|
instr->type = INSTR_JMP_EQ_I;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
instr->jmp.a.struct_id = (uint8_t)a_struct_id;
|
|
instr->jmp.a.n_bits = fa->n_bits;
|
|
instr->jmp.a.offset = fa->offset / 8;
|
|
instr->jmp.b_val = b_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_neq_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
char *a = tokens[2], *b = tokens[3];
|
|
struct field *fa, *fb;
|
|
uint64_t b_val;
|
|
uint32_t a_struct_id, b_struct_id;
|
|
|
|
CHECK(n_tokens == 4, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
fa = struct_field_parse(p, action, a, &a_struct_id);
|
|
CHECK(fa, EINVAL);
|
|
CHECK(!fa->var_size, EINVAL);
|
|
|
|
/* JMP_NEQ, JMP_NEQ_MH, JMP_NEQ_HM, JMP_NEQ_HH. */
|
|
fb = struct_field_parse(p, action, b, &b_struct_id);
|
|
if (fb) {
|
|
CHECK(!fb->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_JMP_NEQ;
|
|
if (a[0] != 'h' && b[0] == 'h')
|
|
instr->type = INSTR_JMP_NEQ_MH;
|
|
if (a[0] == 'h' && b[0] != 'h')
|
|
instr->type = INSTR_JMP_NEQ_HM;
|
|
if (a[0] == 'h' && b[0] == 'h')
|
|
instr->type = INSTR_JMP_NEQ_HH;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
|
|
instr->jmp.a.struct_id = (uint8_t)a_struct_id;
|
|
instr->jmp.a.n_bits = fa->n_bits;
|
|
instr->jmp.a.offset = fa->offset / 8;
|
|
instr->jmp.b.struct_id = (uint8_t)b_struct_id;
|
|
instr->jmp.b.n_bits = fb->n_bits;
|
|
instr->jmp.b.offset = fb->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* JMP_NEQ_I. */
|
|
b_val = strtoull(b, &b, 0);
|
|
CHECK(!b[0], EINVAL);
|
|
|
|
if (a[0] == 'h')
|
|
b_val = hton64(b_val) >> (64 - fa->n_bits);
|
|
|
|
instr->type = INSTR_JMP_NEQ_I;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
instr->jmp.a.struct_id = (uint8_t)a_struct_id;
|
|
instr->jmp.a.n_bits = fa->n_bits;
|
|
instr->jmp.a.offset = fa->offset / 8;
|
|
instr->jmp.b_val = b_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_lt_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
char *a = tokens[2], *b = tokens[3];
|
|
struct field *fa, *fb;
|
|
uint64_t b_val;
|
|
uint32_t a_struct_id, b_struct_id;
|
|
|
|
CHECK(n_tokens == 4, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
fa = struct_field_parse(p, action, a, &a_struct_id);
|
|
CHECK(fa, EINVAL);
|
|
CHECK(!fa->var_size, EINVAL);
|
|
|
|
/* JMP_LT, JMP_LT_MH, JMP_LT_HM, JMP_LT_HH. */
|
|
fb = struct_field_parse(p, action, b, &b_struct_id);
|
|
if (fb) {
|
|
CHECK(!fb->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_JMP_LT;
|
|
if (a[0] == 'h' && b[0] != 'h')
|
|
instr->type = INSTR_JMP_LT_HM;
|
|
if (a[0] != 'h' && b[0] == 'h')
|
|
instr->type = INSTR_JMP_LT_MH;
|
|
if (a[0] == 'h' && b[0] == 'h')
|
|
instr->type = INSTR_JMP_LT_HH;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
|
|
instr->jmp.a.struct_id = (uint8_t)a_struct_id;
|
|
instr->jmp.a.n_bits = fa->n_bits;
|
|
instr->jmp.a.offset = fa->offset / 8;
|
|
instr->jmp.b.struct_id = (uint8_t)b_struct_id;
|
|
instr->jmp.b.n_bits = fb->n_bits;
|
|
instr->jmp.b.offset = fb->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* JMP_LT_MI, JMP_LT_HI. */
|
|
b_val = strtoull(b, &b, 0);
|
|
CHECK(!b[0], EINVAL);
|
|
|
|
instr->type = INSTR_JMP_LT_MI;
|
|
if (a[0] == 'h')
|
|
instr->type = INSTR_JMP_LT_HI;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
|
|
instr->jmp.a.struct_id = (uint8_t)a_struct_id;
|
|
instr->jmp.a.n_bits = fa->n_bits;
|
|
instr->jmp.a.offset = fa->offset / 8;
|
|
instr->jmp.b_val = b_val;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_gt_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char **tokens,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
char *a = tokens[2], *b = tokens[3];
|
|
struct field *fa, *fb;
|
|
uint64_t b_val;
|
|
uint32_t a_struct_id, b_struct_id;
|
|
|
|
CHECK(n_tokens == 4, EINVAL);
|
|
|
|
strcpy(data->jmp_label, tokens[1]);
|
|
|
|
fa = struct_field_parse(p, action, a, &a_struct_id);
|
|
CHECK(fa, EINVAL);
|
|
CHECK(!fa->var_size, EINVAL);
|
|
|
|
/* JMP_GT, JMP_GT_MH, JMP_GT_HM, JMP_GT_HH. */
|
|
fb = struct_field_parse(p, action, b, &b_struct_id);
|
|
if (fb) {
|
|
CHECK(!fb->var_size, EINVAL);
|
|
|
|
instr->type = INSTR_JMP_GT;
|
|
if (a[0] == 'h' && b[0] != 'h')
|
|
instr->type = INSTR_JMP_GT_HM;
|
|
if (a[0] != 'h' && b[0] == 'h')
|
|
instr->type = INSTR_JMP_GT_MH;
|
|
if (a[0] == 'h' && b[0] == 'h')
|
|
instr->type = INSTR_JMP_GT_HH;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
|
|
instr->jmp.a.struct_id = (uint8_t)a_struct_id;
|
|
instr->jmp.a.n_bits = fa->n_bits;
|
|
instr->jmp.a.offset = fa->offset / 8;
|
|
instr->jmp.b.struct_id = (uint8_t)b_struct_id;
|
|
instr->jmp.b.n_bits = fb->n_bits;
|
|
instr->jmp.b.offset = fb->offset / 8;
|
|
return 0;
|
|
}
|
|
|
|
/* JMP_GT_MI, JMP_GT_HI. */
|
|
b_val = strtoull(b, &b, 0);
|
|
CHECK(!b[0], EINVAL);
|
|
|
|
instr->type = INSTR_JMP_GT_MI;
|
|
if (a[0] == 'h')
|
|
instr->type = INSTR_JMP_GT_HI;
|
|
instr->jmp.ip = NULL; /* Resolved later. */
|
|
|
|
instr->jmp.a.struct_id = (uint8_t)a_struct_id;
|
|
instr->jmp.a.n_bits = fa->n_bits;
|
|
instr->jmp.a.offset = fa->offset / 8;
|
|
instr->jmp.b_val = b_val;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmp\n", p->thread_id);
|
|
|
|
thread_ip_set(t, ip->jmp.ip);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_valid_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
uint32_t header_id = ip->jmp.header_id;
|
|
|
|
TRACE("[Thread %2u] jmpv\n", p->thread_id);
|
|
|
|
t->ip = HEADER_VALID(t, header_id) ? ip->jmp.ip : (t->ip + 1);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_invalid_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
uint32_t header_id = ip->jmp.header_id;
|
|
|
|
TRACE("[Thread %2u] jmpnv\n", p->thread_id);
|
|
|
|
t->ip = HEADER_VALID(t, header_id) ? (t->ip + 1) : ip->jmp.ip;
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_hit_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
struct instruction *ip_next[] = {t->ip + 1, ip->jmp.ip};
|
|
|
|
TRACE("[Thread %2u] jmph\n", p->thread_id);
|
|
|
|
t->ip = ip_next[t->hit];
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_miss_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
struct instruction *ip_next[] = {ip->jmp.ip, t->ip + 1};
|
|
|
|
TRACE("[Thread %2u] jmpnh\n", p->thread_id);
|
|
|
|
t->ip = ip_next[t->hit];
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_action_hit_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpa\n", p->thread_id);
|
|
|
|
t->ip = (ip->jmp.action_id == t->action_id) ? ip->jmp.ip : (t->ip + 1);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_action_miss_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpna\n", p->thread_id);
|
|
|
|
t->ip = (ip->jmp.action_id == t->action_id) ? (t->ip + 1) : ip->jmp.ip;
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_eq_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpeq\n", p->thread_id);
|
|
|
|
JMP_CMP(t, ip, ==);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_eq_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpeq (mh)\n", p->thread_id);
|
|
|
|
JMP_CMP_MH(t, ip, ==);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_eq_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpeq (hm)\n", p->thread_id);
|
|
|
|
JMP_CMP_HM(t, ip, ==);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_eq_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpeq (hh)\n", p->thread_id);
|
|
|
|
JMP_CMP_HH_FAST(t, ip, ==);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_eq_i_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpeq (i)\n", p->thread_id);
|
|
|
|
JMP_CMP_I(t, ip, ==);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_neq_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpneq\n", p->thread_id);
|
|
|
|
JMP_CMP(t, ip, !=);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_neq_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpneq (mh)\n", p->thread_id);
|
|
|
|
JMP_CMP_MH(t, ip, !=);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_neq_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpneq (hm)\n", p->thread_id);
|
|
|
|
JMP_CMP_HM(t, ip, !=);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_neq_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpneq (hh)\n", p->thread_id);
|
|
|
|
JMP_CMP_HH_FAST(t, ip, !=);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_neq_i_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpneq (i)\n", p->thread_id);
|
|
|
|
JMP_CMP_I(t, ip, !=);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_lt_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmplt\n", p->thread_id);
|
|
|
|
JMP_CMP(t, ip, <);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_lt_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmplt (mh)\n", p->thread_id);
|
|
|
|
JMP_CMP_MH(t, ip, <);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_lt_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmplt (hm)\n", p->thread_id);
|
|
|
|
JMP_CMP_HM(t, ip, <);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_lt_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmplt (hh)\n", p->thread_id);
|
|
|
|
JMP_CMP_HH(t, ip, <);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_lt_mi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmplt (mi)\n", p->thread_id);
|
|
|
|
JMP_CMP_MI(t, ip, <);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_lt_hi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmplt (hi)\n", p->thread_id);
|
|
|
|
JMP_CMP_HI(t, ip, <);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_gt_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpgt\n", p->thread_id);
|
|
|
|
JMP_CMP(t, ip, >);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_gt_mh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpgt (mh)\n", p->thread_id);
|
|
|
|
JMP_CMP_MH(t, ip, >);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_gt_hm_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpgt (hm)\n", p->thread_id);
|
|
|
|
JMP_CMP_HM(t, ip, >);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_gt_hh_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpgt (hh)\n", p->thread_id);
|
|
|
|
JMP_CMP_HH(t, ip, >);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_gt_mi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpgt (mi)\n", p->thread_id);
|
|
|
|
JMP_CMP_MI(t, ip, >);
|
|
}
|
|
|
|
static inline void
|
|
instr_jmp_gt_hi_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
|
|
TRACE("[Thread %2u] jmpgt (hi)\n", p->thread_id);
|
|
|
|
JMP_CMP_HI(t, ip, >);
|
|
}
|
|
|
|
/*
|
|
* return.
|
|
*/
|
|
static int
|
|
instr_return_translate(struct rte_swx_pipeline *p __rte_unused,
|
|
struct action *action,
|
|
char **tokens __rte_unused,
|
|
int n_tokens,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused)
|
|
{
|
|
CHECK(action, EINVAL);
|
|
CHECK(n_tokens == 1, EINVAL);
|
|
|
|
instr->type = INSTR_RETURN;
|
|
return 0;
|
|
}
|
|
|
|
static inline void
|
|
instr_return_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
|
|
TRACE("[Thread %2u] return\n", p->thread_id);
|
|
|
|
t->ip = t->ret;
|
|
}
|
|
|
|
static int
|
|
instr_translate(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
char *string,
|
|
struct instruction *instr,
|
|
struct instruction_data *data)
|
|
{
|
|
char *tokens[RTE_SWX_INSTRUCTION_TOKENS_MAX];
|
|
int n_tokens = 0, tpos = 0;
|
|
|
|
/* Parse the instruction string into tokens. */
|
|
for ( ; ; ) {
|
|
char *token;
|
|
|
|
token = strtok_r(string, " \t\v", &string);
|
|
if (!token)
|
|
break;
|
|
|
|
CHECK(n_tokens < RTE_SWX_INSTRUCTION_TOKENS_MAX, EINVAL);
|
|
CHECK_NAME(token, EINVAL);
|
|
|
|
tokens[n_tokens] = token;
|
|
n_tokens++;
|
|
}
|
|
|
|
CHECK(n_tokens, EINVAL);
|
|
|
|
/* Handle the optional instruction label. */
|
|
if ((n_tokens >= 2) && !strcmp(tokens[1], ":")) {
|
|
strcpy(data->label, tokens[0]);
|
|
|
|
tpos += 2;
|
|
CHECK(n_tokens - tpos, EINVAL);
|
|
}
|
|
|
|
/* Identify the instruction type. */
|
|
if (!strcmp(tokens[tpos], "rx"))
|
|
return instr_rx_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "tx"))
|
|
return instr_tx_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "drop"))
|
|
return instr_drop_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "mirror"))
|
|
return instr_mirror_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "recirculate"))
|
|
return instr_recirculate_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "recircid"))
|
|
return instr_recircid_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "extract"))
|
|
return instr_hdr_extract_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "lookahead"))
|
|
return instr_hdr_lookahead_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "emit"))
|
|
return instr_hdr_emit_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "validate"))
|
|
return instr_hdr_validate_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "invalidate"))
|
|
return instr_hdr_invalidate_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "mov"))
|
|
return instr_mov_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "add"))
|
|
return instr_alu_add_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "sub"))
|
|
return instr_alu_sub_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "ckadd"))
|
|
return instr_alu_ckadd_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "cksub"))
|
|
return instr_alu_cksub_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "and"))
|
|
return instr_alu_and_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "or"))
|
|
return instr_alu_or_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "xor"))
|
|
return instr_alu_xor_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "shl"))
|
|
return instr_alu_shl_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "shr"))
|
|
return instr_alu_shr_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "regprefetch"))
|
|
return instr_regprefetch_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "regrd"))
|
|
return instr_regrd_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "regwr"))
|
|
return instr_regwr_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "regadd"))
|
|
return instr_regadd_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "metprefetch"))
|
|
return instr_metprefetch_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "meter"))
|
|
return instr_meter_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "table"))
|
|
return instr_table_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "learn"))
|
|
return instr_learn_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
if (!strcmp(tokens[tpos], "rearm"))
|
|
return instr_rearm_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "forget"))
|
|
return instr_forget_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "extern"))
|
|
return instr_extern_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "hash"))
|
|
return instr_hash_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmp"))
|
|
return instr_jmp_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmpv"))
|
|
return instr_jmp_valid_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmpnv"))
|
|
return instr_jmp_invalid_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmph"))
|
|
return instr_jmp_hit_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmpnh"))
|
|
return instr_jmp_miss_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmpa"))
|
|
return instr_jmp_action_hit_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmpna"))
|
|
return instr_jmp_action_miss_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmpeq"))
|
|
return instr_jmp_eq_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmpneq"))
|
|
return instr_jmp_neq_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmplt"))
|
|
return instr_jmp_lt_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "jmpgt"))
|
|
return instr_jmp_gt_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
if (!strcmp(tokens[tpos], "return"))
|
|
return instr_return_translate(p,
|
|
action,
|
|
&tokens[tpos],
|
|
n_tokens - tpos,
|
|
instr,
|
|
data);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static struct instruction_data *
|
|
label_find(struct instruction_data *data, uint32_t n, const char *label)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < n; i++)
|
|
if (!strcmp(label, data[i].label))
|
|
return &data[i];
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static uint32_t
|
|
label_is_used(struct instruction_data *data, uint32_t n, const char *label)
|
|
{
|
|
uint32_t count = 0, i;
|
|
|
|
if (!label[0])
|
|
return 0;
|
|
|
|
for (i = 0; i < n; i++)
|
|
if (!strcmp(label, data[i].jmp_label))
|
|
count++;
|
|
|
|
return count;
|
|
}
|
|
|
|
static int
|
|
instr_label_check(struct instruction_data *instruction_data,
|
|
uint32_t n_instructions)
|
|
{
|
|
uint32_t i;
|
|
|
|
/* Check that all instruction labels are unique. */
|
|
for (i = 0; i < n_instructions; i++) {
|
|
struct instruction_data *data = &instruction_data[i];
|
|
char *label = data->label;
|
|
uint32_t j;
|
|
|
|
if (!label[0])
|
|
continue;
|
|
|
|
for (j = i + 1; j < n_instructions; j++)
|
|
CHECK(strcmp(label, instruction_data[j].label), EINVAL);
|
|
}
|
|
|
|
/* Check that no jump instruction (either conditional or not) can jump to itself (loop). */
|
|
for (i = 0; i < n_instructions; i++) {
|
|
struct instruction_data *data = &instruction_data[i];
|
|
char *label = data->label;
|
|
char *jmp_label = data->jmp_label;
|
|
|
|
/* Continue if this instruction does not have a label or it is not a jump. */
|
|
if (!label[0] || !jmp_label[0])
|
|
continue;
|
|
|
|
CHECK(strcmp(label, jmp_label), EINVAL);
|
|
}
|
|
|
|
/* Get users for each instruction label. */
|
|
for (i = 0; i < n_instructions; i++) {
|
|
struct instruction_data *data = &instruction_data[i];
|
|
char *label = data->label;
|
|
|
|
data->n_users = label_is_used(instruction_data,
|
|
n_instructions,
|
|
label);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_jmp_resolve(struct instruction *instructions,
|
|
struct instruction_data *instruction_data,
|
|
uint32_t n_instructions)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < n_instructions; i++) {
|
|
struct instruction *instr = &instructions[i];
|
|
struct instruction_data *data = &instruction_data[i];
|
|
struct instruction_data *found;
|
|
|
|
if (!instruction_is_jmp(instr))
|
|
continue;
|
|
|
|
found = label_find(instruction_data,
|
|
n_instructions,
|
|
data->jmp_label);
|
|
CHECK(found, EINVAL);
|
|
|
|
instr->jmp.ip = &instructions[found - instruction_data];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
instr_verify(struct rte_swx_pipeline *p __rte_unused,
|
|
struct action *a,
|
|
struct instruction *instr,
|
|
struct instruction_data *data __rte_unused,
|
|
uint32_t n_instructions)
|
|
{
|
|
if (!a) {
|
|
enum instruction_type type;
|
|
uint32_t i;
|
|
|
|
/* Check that the first instruction is rx. */
|
|
CHECK(instr[0].type == INSTR_RX, EINVAL);
|
|
|
|
/* Check that there is at least one tx instruction. */
|
|
for (i = 0; i < n_instructions; i++) {
|
|
type = instr[i].type;
|
|
|
|
if (instruction_is_tx(type))
|
|
break;
|
|
}
|
|
CHECK(i < n_instructions, EINVAL);
|
|
|
|
/* Check that the last instruction is either tx or unconditional
|
|
* jump.
|
|
*/
|
|
type = instr[n_instructions - 1].type;
|
|
CHECK(instruction_is_tx(type) || (type == INSTR_JMP), EINVAL);
|
|
}
|
|
|
|
if (a) {
|
|
enum instruction_type type;
|
|
uint32_t i;
|
|
|
|
/* Check that there is at least one return or tx instruction. */
|
|
for (i = 0; i < n_instructions; i++) {
|
|
type = instr[i].type;
|
|
|
|
if ((type == INSTR_RETURN) || instruction_is_tx(type))
|
|
break;
|
|
}
|
|
CHECK(i < n_instructions, EINVAL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t
|
|
instr_compact(struct instruction *instructions,
|
|
struct instruction_data *instruction_data,
|
|
uint32_t n_instructions)
|
|
{
|
|
uint32_t i, pos = 0;
|
|
|
|
/* Eliminate the invalid instructions that have been optimized out. */
|
|
for (i = 0; i < n_instructions; i++) {
|
|
struct instruction *instr = &instructions[i];
|
|
struct instruction_data *data = &instruction_data[i];
|
|
|
|
if (data->invalid)
|
|
continue;
|
|
|
|
if (i != pos) {
|
|
memcpy(&instructions[pos], instr, sizeof(*instr));
|
|
memcpy(&instruction_data[pos], data, sizeof(*data));
|
|
}
|
|
|
|
pos++;
|
|
}
|
|
|
|
return pos;
|
|
}
|
|
|
|
static int
|
|
instr_pattern_extract_many_search(struct instruction *instr,
|
|
struct instruction_data *data,
|
|
uint32_t n_instr,
|
|
uint32_t *n_pattern_instr)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < n_instr; i++) {
|
|
if (data[i].invalid)
|
|
break;
|
|
|
|
if (instr[i].type != INSTR_HDR_EXTRACT)
|
|
break;
|
|
|
|
if (i == RTE_DIM(instr->io.hdr.header_id))
|
|
break;
|
|
|
|
if (i && data[i].n_users)
|
|
break;
|
|
}
|
|
|
|
if (i < 2)
|
|
return 0;
|
|
|
|
*n_pattern_instr = i;
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
instr_pattern_extract_many_replace(struct instruction *instr,
|
|
struct instruction_data *data,
|
|
uint32_t n_instr)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 1; i < n_instr; i++) {
|
|
instr[0].type++;
|
|
instr[0].io.hdr.header_id[i] = instr[i].io.hdr.header_id[0];
|
|
instr[0].io.hdr.struct_id[i] = instr[i].io.hdr.struct_id[0];
|
|
instr[0].io.hdr.n_bytes[i] = instr[i].io.hdr.n_bytes[0];
|
|
|
|
data[i].invalid = 1;
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
instr_pattern_extract_many_optimize(struct instruction *instructions,
|
|
struct instruction_data *instruction_data,
|
|
uint32_t n_instructions)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < n_instructions; ) {
|
|
struct instruction *instr = &instructions[i];
|
|
struct instruction_data *data = &instruction_data[i];
|
|
uint32_t n_instr = 0;
|
|
int detected;
|
|
|
|
/* Extract many. */
|
|
detected = instr_pattern_extract_many_search(instr,
|
|
data,
|
|
n_instructions - i,
|
|
&n_instr);
|
|
if (detected) {
|
|
instr_pattern_extract_many_replace(instr,
|
|
data,
|
|
n_instr);
|
|
i += n_instr;
|
|
continue;
|
|
}
|
|
|
|
/* No pattern starting at the current instruction. */
|
|
i++;
|
|
}
|
|
|
|
/* Eliminate the invalid instructions that have been optimized out. */
|
|
n_instructions = instr_compact(instructions,
|
|
instruction_data,
|
|
n_instructions);
|
|
|
|
return n_instructions;
|
|
}
|
|
|
|
static int
|
|
instr_pattern_emit_many_tx_search(struct instruction *instr,
|
|
struct instruction_data *data,
|
|
uint32_t n_instr,
|
|
uint32_t *n_pattern_instr)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < n_instr; i++) {
|
|
if (data[i].invalid)
|
|
break;
|
|
|
|
if (instr[i].type != INSTR_HDR_EMIT)
|
|
break;
|
|
|
|
if (i == RTE_DIM(instr->io.hdr.header_id))
|
|
break;
|
|
|
|
if (i && data[i].n_users)
|
|
break;
|
|
}
|
|
|
|
if (!i)
|
|
return 0;
|
|
|
|
if (instr[i].type != INSTR_TX)
|
|
return 0;
|
|
|
|
if (data[i].n_users)
|
|
return 0;
|
|
|
|
i++;
|
|
|
|
*n_pattern_instr = i;
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
instr_pattern_emit_many_tx_replace(struct instruction *instr,
|
|
struct instruction_data *data,
|
|
uint32_t n_instr)
|
|
{
|
|
uint32_t i;
|
|
|
|
/* Any emit instruction in addition to the first one. */
|
|
for (i = 1; i < n_instr - 1; i++) {
|
|
instr[0].type++;
|
|
instr[0].io.hdr.header_id[i] = instr[i].io.hdr.header_id[0];
|
|
instr[0].io.hdr.struct_id[i] = instr[i].io.hdr.struct_id[0];
|
|
instr[0].io.hdr.n_bytes[i] = instr[i].io.hdr.n_bytes[0];
|
|
|
|
data[i].invalid = 1;
|
|
}
|
|
|
|
/* The TX instruction is the last one in the pattern. */
|
|
instr[0].type++;
|
|
instr[0].io.io.offset = instr[i].io.io.offset;
|
|
instr[0].io.io.n_bits = instr[i].io.io.n_bits;
|
|
data[i].invalid = 1;
|
|
}
|
|
|
|
static uint32_t
|
|
instr_pattern_emit_many_tx_optimize(struct instruction *instructions,
|
|
struct instruction_data *instruction_data,
|
|
uint32_t n_instructions)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < n_instructions; ) {
|
|
struct instruction *instr = &instructions[i];
|
|
struct instruction_data *data = &instruction_data[i];
|
|
uint32_t n_instr = 0;
|
|
int detected;
|
|
|
|
/* Emit many + TX. */
|
|
detected = instr_pattern_emit_many_tx_search(instr,
|
|
data,
|
|
n_instructions - i,
|
|
&n_instr);
|
|
if (detected) {
|
|
instr_pattern_emit_many_tx_replace(instr,
|
|
data,
|
|
n_instr);
|
|
i += n_instr;
|
|
continue;
|
|
}
|
|
|
|
/* No pattern starting at the current instruction. */
|
|
i++;
|
|
}
|
|
|
|
/* Eliminate the invalid instructions that have been optimized out. */
|
|
n_instructions = instr_compact(instructions,
|
|
instruction_data,
|
|
n_instructions);
|
|
|
|
return n_instructions;
|
|
}
|
|
|
|
static uint32_t
|
|
action_arg_src_mov_count(struct action *a,
|
|
uint32_t arg_id,
|
|
struct instruction *instructions,
|
|
struct instruction_data *instruction_data,
|
|
uint32_t n_instructions);
|
|
|
|
static int
|
|
instr_pattern_mov_all_validate_search(struct rte_swx_pipeline *p,
|
|
struct action *a,
|
|
struct instruction *instr,
|
|
struct instruction_data *data,
|
|
uint32_t n_instr,
|
|
struct instruction *instructions,
|
|
struct instruction_data *instruction_data,
|
|
uint32_t n_instructions,
|
|
uint32_t *n_pattern_instr)
|
|
{
|
|
struct header *h;
|
|
uint32_t src_field_id, i, j;
|
|
|
|
/* Prerequisites. */
|
|
if (!a || !a->st)
|
|
return 0;
|
|
|
|
/* First instruction: MOV_HM. */
|
|
if (data[0].invalid || (instr[0].type != INSTR_MOV_HM))
|
|
return 0;
|
|
|
|
h = header_find_by_struct_id(p, instr[0].mov.dst.struct_id);
|
|
if (!h || h->st->var_size)
|
|
return 0;
|
|
|
|
for (src_field_id = 0; src_field_id < a->st->n_fields; src_field_id++)
|
|
if (instr[0].mov.src.offset == a->st->fields[src_field_id].offset / 8)
|
|
break;
|
|
|
|
if (src_field_id == a->st->n_fields)
|
|
return 0;
|
|
|
|
if (instr[0].mov.dst.offset ||
|
|
(instr[0].mov.dst.n_bits != h->st->fields[0].n_bits) ||
|
|
instr[0].mov.src.struct_id ||
|
|
(instr[0].mov.src.n_bits != a->st->fields[src_field_id].n_bits) ||
|
|
(instr[0].mov.dst.n_bits != instr[0].mov.src.n_bits))
|
|
return 0;
|
|
|
|
if ((n_instr < h->st->n_fields + 1) ||
|
|
(a->st->n_fields < src_field_id + h->st->n_fields + 1))
|
|
return 0;
|
|
|
|
/* Subsequent instructions: MOV_HM. */
|
|
for (i = 1; i < h->st->n_fields; i++)
|
|
if (data[i].invalid ||
|
|
data[i].n_users ||
|
|
(instr[i].type != INSTR_MOV_HM) ||
|
|
(instr[i].mov.dst.struct_id != h->struct_id) ||
|
|
(instr[i].mov.dst.offset != h->st->fields[i].offset / 8) ||
|
|
(instr[i].mov.dst.n_bits != h->st->fields[i].n_bits) ||
|
|
instr[i].mov.src.struct_id ||
|
|
(instr[i].mov.src.offset != a->st->fields[src_field_id + i].offset / 8) ||
|
|
(instr[i].mov.src.n_bits != a->st->fields[src_field_id + i].n_bits) ||
|
|
(instr[i].mov.dst.n_bits != instr[i].mov.src.n_bits))
|
|
return 0;
|
|
|
|
/* Last instruction: HDR_VALIDATE. */
|
|
if ((instr[i].type != INSTR_HDR_VALIDATE) ||
|
|
(instr[i].valid.header_id != h->id))
|
|
return 0;
|
|
|
|
/* Check that none of the action args that are used as source for this
|
|
* DMA transfer are not used as source in any other mov instruction.
|
|
*/
|
|
for (j = src_field_id; j < src_field_id + h->st->n_fields; j++) {
|
|
uint32_t n_users;
|
|
|
|
n_users = action_arg_src_mov_count(a,
|
|
j,
|
|
instructions,
|
|
instruction_data,
|
|
n_instructions);
|
|
if (n_users > 1)
|
|
return 0;
|
|
}
|
|
|
|
*n_pattern_instr = 1 + i;
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
instr_pattern_mov_all_validate_replace(struct rte_swx_pipeline *p,
|
|
struct action *a,
|
|
struct instruction *instr,
|
|
struct instruction_data *data,
|
|
uint32_t n_instr)
|
|
{
|
|
struct header *h;
|
|
uint32_t src_field_id, src_offset, i;
|
|
|
|
/* Read from the instructions before they are modified. */
|
|
h = header_find_by_struct_id(p, instr[0].mov.dst.struct_id);
|
|
if (!h)
|
|
return;
|
|
|
|
for (src_field_id = 0; src_field_id < a->st->n_fields; src_field_id++)
|
|
if (instr[0].mov.src.offset == a->st->fields[src_field_id].offset / 8)
|
|
break;
|
|
|
|
if (src_field_id == a->st->n_fields)
|
|
return;
|
|
|
|
src_offset = instr[0].mov.src.offset;
|
|
|
|
/* Modify the instructions. */
|
|
instr[0].type = INSTR_DMA_HT;
|
|
instr[0].dma.dst.header_id[0] = h->id;
|
|
instr[0].dma.dst.struct_id[0] = h->struct_id;
|
|
instr[0].dma.src.offset[0] = (uint8_t)src_offset;
|
|
instr[0].dma.n_bytes[0] = h->st->n_bits / 8;
|
|
|
|
for (i = 1; i < n_instr; i++)
|
|
data[i].invalid = 1;
|
|
|
|
/* Update the endianness of the action arguments to header endianness. */
|
|
for (i = 0; i < h->st->n_fields; i++)
|
|
a->args_endianness[src_field_id + i] = 1;
|
|
}
|
|
|
|
static uint32_t
|
|
instr_pattern_mov_all_validate_optimize(struct rte_swx_pipeline *p,
|
|
struct action *a,
|
|
struct instruction *instructions,
|
|
struct instruction_data *instruction_data,
|
|
uint32_t n_instructions)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (!a || !a->st)
|
|
return n_instructions;
|
|
|
|
for (i = 0; i < n_instructions; ) {
|
|
struct instruction *instr = &instructions[i];
|
|
struct instruction_data *data = &instruction_data[i];
|
|
uint32_t n_instr = 0;
|
|
int detected;
|
|
|
|
/* Mov all + validate. */
|
|
detected = instr_pattern_mov_all_validate_search(p,
|
|
a,
|
|
instr,
|
|
data,
|
|
n_instructions - i,
|
|
instructions,
|
|
instruction_data,
|
|
n_instructions,
|
|
&n_instr);
|
|
if (detected) {
|
|
instr_pattern_mov_all_validate_replace(p, a, instr, data, n_instr);
|
|
i += n_instr;
|
|
continue;
|
|
}
|
|
|
|
/* No pattern starting at the current instruction. */
|
|
i++;
|
|
}
|
|
|
|
/* Eliminate the invalid instructions that have been optimized out. */
|
|
n_instructions = instr_compact(instructions,
|
|
instruction_data,
|
|
n_instructions);
|
|
|
|
return n_instructions;
|
|
}
|
|
|
|
static int
|
|
instr_pattern_dma_many_search(struct instruction *instr,
|
|
struct instruction_data *data,
|
|
uint32_t n_instr,
|
|
uint32_t *n_pattern_instr)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < n_instr; i++) {
|
|
if (data[i].invalid)
|
|
break;
|
|
|
|
if (instr[i].type != INSTR_DMA_HT)
|
|
break;
|
|
|
|
if (i == RTE_DIM(instr->dma.dst.header_id))
|
|
break;
|
|
|
|
if (i && data[i].n_users)
|
|
break;
|
|
}
|
|
|
|
if (i < 2)
|
|
return 0;
|
|
|
|
*n_pattern_instr = i;
|
|
return 1;
|
|
}
|
|
|
|
static void
|
|
instr_pattern_dma_many_replace(struct instruction *instr,
|
|
struct instruction_data *data,
|
|
uint32_t n_instr)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 1; i < n_instr; i++) {
|
|
instr[0].type++;
|
|
instr[0].dma.dst.header_id[i] = instr[i].dma.dst.header_id[0];
|
|
instr[0].dma.dst.struct_id[i] = instr[i].dma.dst.struct_id[0];
|
|
instr[0].dma.src.offset[i] = instr[i].dma.src.offset[0];
|
|
instr[0].dma.n_bytes[i] = instr[i].dma.n_bytes[0];
|
|
|
|
data[i].invalid = 1;
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
instr_pattern_dma_many_optimize(struct instruction *instructions,
|
|
struct instruction_data *instruction_data,
|
|
uint32_t n_instructions)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < n_instructions; ) {
|
|
struct instruction *instr = &instructions[i];
|
|
struct instruction_data *data = &instruction_data[i];
|
|
uint32_t n_instr = 0;
|
|
int detected;
|
|
|
|
/* DMA many. */
|
|
detected = instr_pattern_dma_many_search(instr,
|
|
data,
|
|
n_instructions - i,
|
|
&n_instr);
|
|
if (detected) {
|
|
instr_pattern_dma_many_replace(instr, data, n_instr);
|
|
i += n_instr;
|
|
continue;
|
|
}
|
|
|
|
/* No pattern starting at the current instruction. */
|
|
i++;
|
|
}
|
|
|
|
/* Eliminate the invalid instructions that have been optimized out. */
|
|
n_instructions = instr_compact(instructions,
|
|
instruction_data,
|
|
n_instructions);
|
|
|
|
return n_instructions;
|
|
}
|
|
|
|
static uint32_t
|
|
instr_optimize(struct rte_swx_pipeline *p,
|
|
struct action *a,
|
|
struct instruction *instructions,
|
|
struct instruction_data *instruction_data,
|
|
uint32_t n_instructions)
|
|
{
|
|
/* Extract many. */
|
|
n_instructions = instr_pattern_extract_many_optimize(instructions,
|
|
instruction_data,
|
|
n_instructions);
|
|
|
|
/* Emit many + TX. */
|
|
n_instructions = instr_pattern_emit_many_tx_optimize(instructions,
|
|
instruction_data,
|
|
n_instructions);
|
|
|
|
/* Mov all + validate. */
|
|
n_instructions = instr_pattern_mov_all_validate_optimize(p,
|
|
a,
|
|
instructions,
|
|
instruction_data,
|
|
n_instructions);
|
|
|
|
/* DMA many. */
|
|
n_instructions = instr_pattern_dma_many_optimize(instructions,
|
|
instruction_data,
|
|
n_instructions);
|
|
|
|
return n_instructions;
|
|
}
|
|
|
|
static int
|
|
instruction_config(struct rte_swx_pipeline *p,
|
|
struct action *a,
|
|
const char **instructions,
|
|
uint32_t n_instructions)
|
|
{
|
|
struct instruction *instr = NULL;
|
|
struct instruction_data *data = NULL;
|
|
int err = 0;
|
|
uint32_t i;
|
|
|
|
CHECK(n_instructions, EINVAL);
|
|
CHECK(instructions, EINVAL);
|
|
for (i = 0; i < n_instructions; i++)
|
|
CHECK_INSTRUCTION(instructions[i], EINVAL);
|
|
|
|
/* Memory allocation. */
|
|
instr = calloc(n_instructions, sizeof(struct instruction));
|
|
if (!instr) {
|
|
err = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
data = calloc(n_instructions, sizeof(struct instruction_data));
|
|
if (!data) {
|
|
err = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
for (i = 0; i < n_instructions; i++) {
|
|
char *string = strdup(instructions[i]);
|
|
if (!string) {
|
|
err = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
err = instr_translate(p, a, string, &instr[i], &data[i]);
|
|
if (err) {
|
|
free(string);
|
|
goto error;
|
|
}
|
|
|
|
free(string);
|
|
}
|
|
|
|
err = instr_label_check(data, n_instructions);
|
|
if (err)
|
|
goto error;
|
|
|
|
err = instr_verify(p, a, instr, data, n_instructions);
|
|
if (err)
|
|
goto error;
|
|
|
|
n_instructions = instr_optimize(p, a, instr, data, n_instructions);
|
|
|
|
err = instr_jmp_resolve(instr, data, n_instructions);
|
|
if (err)
|
|
goto error;
|
|
|
|
if (a) {
|
|
a->instructions = instr;
|
|
a->instruction_data = data;
|
|
a->n_instructions = n_instructions;
|
|
} else {
|
|
p->instructions = instr;
|
|
p->instruction_data = data;
|
|
p->n_instructions = n_instructions;
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
free(data);
|
|
free(instr);
|
|
return err;
|
|
}
|
|
|
|
static instr_exec_t instruction_table[] = {
|
|
[INSTR_RX] = instr_rx_exec,
|
|
[INSTR_TX] = instr_tx_exec,
|
|
[INSTR_TX_I] = instr_tx_i_exec,
|
|
[INSTR_DROP] = instr_drop_exec,
|
|
[INSTR_MIRROR] = instr_mirror_exec,
|
|
[INSTR_RECIRCULATE] = instr_recirculate_exec,
|
|
[INSTR_RECIRCID] = instr_recircid_exec,
|
|
|
|
[INSTR_HDR_EXTRACT] = instr_hdr_extract_exec,
|
|
[INSTR_HDR_EXTRACT2] = instr_hdr_extract2_exec,
|
|
[INSTR_HDR_EXTRACT3] = instr_hdr_extract3_exec,
|
|
[INSTR_HDR_EXTRACT4] = instr_hdr_extract4_exec,
|
|
[INSTR_HDR_EXTRACT5] = instr_hdr_extract5_exec,
|
|
[INSTR_HDR_EXTRACT6] = instr_hdr_extract6_exec,
|
|
[INSTR_HDR_EXTRACT7] = instr_hdr_extract7_exec,
|
|
[INSTR_HDR_EXTRACT8] = instr_hdr_extract8_exec,
|
|
[INSTR_HDR_EXTRACT_M] = instr_hdr_extract_m_exec,
|
|
[INSTR_HDR_LOOKAHEAD] = instr_hdr_lookahead_exec,
|
|
|
|
[INSTR_HDR_EMIT] = instr_hdr_emit_exec,
|
|
[INSTR_HDR_EMIT_TX] = instr_hdr_emit_tx_exec,
|
|
[INSTR_HDR_EMIT2_TX] = instr_hdr_emit2_tx_exec,
|
|
[INSTR_HDR_EMIT3_TX] = instr_hdr_emit3_tx_exec,
|
|
[INSTR_HDR_EMIT4_TX] = instr_hdr_emit4_tx_exec,
|
|
[INSTR_HDR_EMIT5_TX] = instr_hdr_emit5_tx_exec,
|
|
[INSTR_HDR_EMIT6_TX] = instr_hdr_emit6_tx_exec,
|
|
[INSTR_HDR_EMIT7_TX] = instr_hdr_emit7_tx_exec,
|
|
[INSTR_HDR_EMIT8_TX] = instr_hdr_emit8_tx_exec,
|
|
|
|
[INSTR_HDR_VALIDATE] = instr_hdr_validate_exec,
|
|
[INSTR_HDR_INVALIDATE] = instr_hdr_invalidate_exec,
|
|
|
|
[INSTR_MOV] = instr_mov_exec,
|
|
[INSTR_MOV_MH] = instr_mov_mh_exec,
|
|
[INSTR_MOV_HM] = instr_mov_hm_exec,
|
|
[INSTR_MOV_HH] = instr_mov_hh_exec,
|
|
[INSTR_MOV_I] = instr_mov_i_exec,
|
|
|
|
[INSTR_DMA_HT] = instr_dma_ht_exec,
|
|
[INSTR_DMA_HT2] = instr_dma_ht2_exec,
|
|
[INSTR_DMA_HT3] = instr_dma_ht3_exec,
|
|
[INSTR_DMA_HT4] = instr_dma_ht4_exec,
|
|
[INSTR_DMA_HT5] = instr_dma_ht5_exec,
|
|
[INSTR_DMA_HT6] = instr_dma_ht6_exec,
|
|
[INSTR_DMA_HT7] = instr_dma_ht7_exec,
|
|
[INSTR_DMA_HT8] = instr_dma_ht8_exec,
|
|
|
|
[INSTR_ALU_ADD] = instr_alu_add_exec,
|
|
[INSTR_ALU_ADD_MH] = instr_alu_add_mh_exec,
|
|
[INSTR_ALU_ADD_HM] = instr_alu_add_hm_exec,
|
|
[INSTR_ALU_ADD_HH] = instr_alu_add_hh_exec,
|
|
[INSTR_ALU_ADD_MI] = instr_alu_add_mi_exec,
|
|
[INSTR_ALU_ADD_HI] = instr_alu_add_hi_exec,
|
|
|
|
[INSTR_ALU_SUB] = instr_alu_sub_exec,
|
|
[INSTR_ALU_SUB_MH] = instr_alu_sub_mh_exec,
|
|
[INSTR_ALU_SUB_HM] = instr_alu_sub_hm_exec,
|
|
[INSTR_ALU_SUB_HH] = instr_alu_sub_hh_exec,
|
|
[INSTR_ALU_SUB_MI] = instr_alu_sub_mi_exec,
|
|
[INSTR_ALU_SUB_HI] = instr_alu_sub_hi_exec,
|
|
|
|
[INSTR_ALU_CKADD_FIELD] = instr_alu_ckadd_field_exec,
|
|
[INSTR_ALU_CKADD_STRUCT] = instr_alu_ckadd_struct_exec,
|
|
[INSTR_ALU_CKADD_STRUCT20] = instr_alu_ckadd_struct20_exec,
|
|
[INSTR_ALU_CKSUB_FIELD] = instr_alu_cksub_field_exec,
|
|
|
|
[INSTR_ALU_AND] = instr_alu_and_exec,
|
|
[INSTR_ALU_AND_MH] = instr_alu_and_mh_exec,
|
|
[INSTR_ALU_AND_HM] = instr_alu_and_hm_exec,
|
|
[INSTR_ALU_AND_HH] = instr_alu_and_hh_exec,
|
|
[INSTR_ALU_AND_I] = instr_alu_and_i_exec,
|
|
|
|
[INSTR_ALU_OR] = instr_alu_or_exec,
|
|
[INSTR_ALU_OR_MH] = instr_alu_or_mh_exec,
|
|
[INSTR_ALU_OR_HM] = instr_alu_or_hm_exec,
|
|
[INSTR_ALU_OR_HH] = instr_alu_or_hh_exec,
|
|
[INSTR_ALU_OR_I] = instr_alu_or_i_exec,
|
|
|
|
[INSTR_ALU_XOR] = instr_alu_xor_exec,
|
|
[INSTR_ALU_XOR_MH] = instr_alu_xor_mh_exec,
|
|
[INSTR_ALU_XOR_HM] = instr_alu_xor_hm_exec,
|
|
[INSTR_ALU_XOR_HH] = instr_alu_xor_hh_exec,
|
|
[INSTR_ALU_XOR_I] = instr_alu_xor_i_exec,
|
|
|
|
[INSTR_ALU_SHL] = instr_alu_shl_exec,
|
|
[INSTR_ALU_SHL_MH] = instr_alu_shl_mh_exec,
|
|
[INSTR_ALU_SHL_HM] = instr_alu_shl_hm_exec,
|
|
[INSTR_ALU_SHL_HH] = instr_alu_shl_hh_exec,
|
|
[INSTR_ALU_SHL_MI] = instr_alu_shl_mi_exec,
|
|
[INSTR_ALU_SHL_HI] = instr_alu_shl_hi_exec,
|
|
|
|
[INSTR_ALU_SHR] = instr_alu_shr_exec,
|
|
[INSTR_ALU_SHR_MH] = instr_alu_shr_mh_exec,
|
|
[INSTR_ALU_SHR_HM] = instr_alu_shr_hm_exec,
|
|
[INSTR_ALU_SHR_HH] = instr_alu_shr_hh_exec,
|
|
[INSTR_ALU_SHR_MI] = instr_alu_shr_mi_exec,
|
|
[INSTR_ALU_SHR_HI] = instr_alu_shr_hi_exec,
|
|
|
|
[INSTR_REGPREFETCH_RH] = instr_regprefetch_rh_exec,
|
|
[INSTR_REGPREFETCH_RM] = instr_regprefetch_rm_exec,
|
|
[INSTR_REGPREFETCH_RI] = instr_regprefetch_ri_exec,
|
|
|
|
[INSTR_REGRD_HRH] = instr_regrd_hrh_exec,
|
|
[INSTR_REGRD_HRM] = instr_regrd_hrm_exec,
|
|
[INSTR_REGRD_MRH] = instr_regrd_mrh_exec,
|
|
[INSTR_REGRD_MRM] = instr_regrd_mrm_exec,
|
|
[INSTR_REGRD_HRI] = instr_regrd_hri_exec,
|
|
[INSTR_REGRD_MRI] = instr_regrd_mri_exec,
|
|
|
|
[INSTR_REGWR_RHH] = instr_regwr_rhh_exec,
|
|
[INSTR_REGWR_RHM] = instr_regwr_rhm_exec,
|
|
[INSTR_REGWR_RMH] = instr_regwr_rmh_exec,
|
|
[INSTR_REGWR_RMM] = instr_regwr_rmm_exec,
|
|
[INSTR_REGWR_RHI] = instr_regwr_rhi_exec,
|
|
[INSTR_REGWR_RMI] = instr_regwr_rmi_exec,
|
|
[INSTR_REGWR_RIH] = instr_regwr_rih_exec,
|
|
[INSTR_REGWR_RIM] = instr_regwr_rim_exec,
|
|
[INSTR_REGWR_RII] = instr_regwr_rii_exec,
|
|
|
|
[INSTR_REGADD_RHH] = instr_regadd_rhh_exec,
|
|
[INSTR_REGADD_RHM] = instr_regadd_rhm_exec,
|
|
[INSTR_REGADD_RMH] = instr_regadd_rmh_exec,
|
|
[INSTR_REGADD_RMM] = instr_regadd_rmm_exec,
|
|
[INSTR_REGADD_RHI] = instr_regadd_rhi_exec,
|
|
[INSTR_REGADD_RMI] = instr_regadd_rmi_exec,
|
|
[INSTR_REGADD_RIH] = instr_regadd_rih_exec,
|
|
[INSTR_REGADD_RIM] = instr_regadd_rim_exec,
|
|
[INSTR_REGADD_RII] = instr_regadd_rii_exec,
|
|
|
|
[INSTR_METPREFETCH_H] = instr_metprefetch_h_exec,
|
|
[INSTR_METPREFETCH_M] = instr_metprefetch_m_exec,
|
|
[INSTR_METPREFETCH_I] = instr_metprefetch_i_exec,
|
|
|
|
[INSTR_METER_HHM] = instr_meter_hhm_exec,
|
|
[INSTR_METER_HHI] = instr_meter_hhi_exec,
|
|
[INSTR_METER_HMM] = instr_meter_hmm_exec,
|
|
[INSTR_METER_HMI] = instr_meter_hmi_exec,
|
|
[INSTR_METER_MHM] = instr_meter_mhm_exec,
|
|
[INSTR_METER_MHI] = instr_meter_mhi_exec,
|
|
[INSTR_METER_MMM] = instr_meter_mmm_exec,
|
|
[INSTR_METER_MMI] = instr_meter_mmi_exec,
|
|
[INSTR_METER_IHM] = instr_meter_ihm_exec,
|
|
[INSTR_METER_IHI] = instr_meter_ihi_exec,
|
|
[INSTR_METER_IMM] = instr_meter_imm_exec,
|
|
[INSTR_METER_IMI] = instr_meter_imi_exec,
|
|
|
|
[INSTR_TABLE] = instr_table_exec,
|
|
[INSTR_TABLE_AF] = instr_table_af_exec,
|
|
[INSTR_SELECTOR] = instr_selector_exec,
|
|
[INSTR_LEARNER] = instr_learner_exec,
|
|
[INSTR_LEARNER_AF] = instr_learner_af_exec,
|
|
[INSTR_LEARNER_LEARN] = instr_learn_exec,
|
|
[INSTR_LEARNER_REARM] = instr_rearm_exec,
|
|
[INSTR_LEARNER_REARM_NEW] = instr_rearm_new_exec,
|
|
[INSTR_LEARNER_FORGET] = instr_forget_exec,
|
|
[INSTR_EXTERN_OBJ] = instr_extern_obj_exec,
|
|
[INSTR_EXTERN_FUNC] = instr_extern_func_exec,
|
|
[INSTR_HASH_FUNC] = instr_hash_func_exec,
|
|
|
|
[INSTR_JMP] = instr_jmp_exec,
|
|
[INSTR_JMP_VALID] = instr_jmp_valid_exec,
|
|
[INSTR_JMP_INVALID] = instr_jmp_invalid_exec,
|
|
[INSTR_JMP_HIT] = instr_jmp_hit_exec,
|
|
[INSTR_JMP_MISS] = instr_jmp_miss_exec,
|
|
[INSTR_JMP_ACTION_HIT] = instr_jmp_action_hit_exec,
|
|
[INSTR_JMP_ACTION_MISS] = instr_jmp_action_miss_exec,
|
|
|
|
[INSTR_JMP_EQ] = instr_jmp_eq_exec,
|
|
[INSTR_JMP_EQ_MH] = instr_jmp_eq_mh_exec,
|
|
[INSTR_JMP_EQ_HM] = instr_jmp_eq_hm_exec,
|
|
[INSTR_JMP_EQ_HH] = instr_jmp_eq_hh_exec,
|
|
[INSTR_JMP_EQ_I] = instr_jmp_eq_i_exec,
|
|
|
|
[INSTR_JMP_NEQ] = instr_jmp_neq_exec,
|
|
[INSTR_JMP_NEQ_MH] = instr_jmp_neq_mh_exec,
|
|
[INSTR_JMP_NEQ_HM] = instr_jmp_neq_hm_exec,
|
|
[INSTR_JMP_NEQ_HH] = instr_jmp_neq_hh_exec,
|
|
[INSTR_JMP_NEQ_I] = instr_jmp_neq_i_exec,
|
|
|
|
[INSTR_JMP_LT] = instr_jmp_lt_exec,
|
|
[INSTR_JMP_LT_MH] = instr_jmp_lt_mh_exec,
|
|
[INSTR_JMP_LT_HM] = instr_jmp_lt_hm_exec,
|
|
[INSTR_JMP_LT_HH] = instr_jmp_lt_hh_exec,
|
|
[INSTR_JMP_LT_MI] = instr_jmp_lt_mi_exec,
|
|
[INSTR_JMP_LT_HI] = instr_jmp_lt_hi_exec,
|
|
|
|
[INSTR_JMP_GT] = instr_jmp_gt_exec,
|
|
[INSTR_JMP_GT_MH] = instr_jmp_gt_mh_exec,
|
|
[INSTR_JMP_GT_HM] = instr_jmp_gt_hm_exec,
|
|
[INSTR_JMP_GT_HH] = instr_jmp_gt_hh_exec,
|
|
[INSTR_JMP_GT_MI] = instr_jmp_gt_mi_exec,
|
|
[INSTR_JMP_GT_HI] = instr_jmp_gt_hi_exec,
|
|
|
|
[INSTR_RETURN] = instr_return_exec,
|
|
};
|
|
|
|
static int
|
|
instruction_table_build(struct rte_swx_pipeline *p)
|
|
{
|
|
p->instruction_table = calloc(RTE_SWX_PIPELINE_INSTRUCTION_TABLE_SIZE_MAX,
|
|
sizeof(struct instr_exec_t *));
|
|
if (!p->instruction_table)
|
|
return -EINVAL;
|
|
|
|
memcpy(p->instruction_table, instruction_table, sizeof(instruction_table));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
instruction_table_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
if (!p->instruction_table)
|
|
return;
|
|
|
|
free(p->instruction_table);
|
|
p->instruction_table = NULL;
|
|
}
|
|
|
|
static void
|
|
instruction_table_free(struct rte_swx_pipeline *p)
|
|
{
|
|
instruction_table_build_free(p);
|
|
}
|
|
|
|
static inline void
|
|
instr_exec(struct rte_swx_pipeline *p)
|
|
{
|
|
struct thread *t = &p->threads[p->thread_id];
|
|
struct instruction *ip = t->ip;
|
|
instr_exec_t instr = p->instruction_table[ip->type];
|
|
|
|
instr(p);
|
|
}
|
|
|
|
/*
|
|
* Action.
|
|
*/
|
|
static struct action *
|
|
action_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct action *elem;
|
|
|
|
if (!name)
|
|
return NULL;
|
|
|
|
TAILQ_FOREACH(elem, &p->actions, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct action *
|
|
action_find_by_id(struct rte_swx_pipeline *p, uint32_t id)
|
|
{
|
|
struct action *action = NULL;
|
|
|
|
TAILQ_FOREACH(action, &p->actions, node)
|
|
if (action->id == id)
|
|
return action;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct field *
|
|
action_field_find(struct action *a, const char *name)
|
|
{
|
|
return a->st ? struct_type_field_find(a->st, name) : NULL;
|
|
}
|
|
|
|
static struct field *
|
|
action_field_parse(struct action *action, const char *name)
|
|
{
|
|
if (name[0] != 't' || name[1] != '.')
|
|
return NULL;
|
|
|
|
return action_field_find(action, &name[2]);
|
|
}
|
|
|
|
static int
|
|
action_has_nbo_args(struct action *a)
|
|
{
|
|
uint32_t i;
|
|
|
|
/* Return if the action does not have any args. */
|
|
if (!a->st)
|
|
return 0; /* FALSE */
|
|
|
|
for (i = 0; i < a->st->n_fields; i++)
|
|
if (a->args_endianness[i])
|
|
return 1; /* TRUE */
|
|
|
|
return 0; /* FALSE */
|
|
}
|
|
|
|
static int
|
|
action_does_learning(struct action *a)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < a->n_instructions; i++)
|
|
switch (a->instructions[i].type) {
|
|
case INSTR_LEARNER_LEARN:
|
|
return 1; /* TRUE */
|
|
|
|
case INSTR_LEARNER_FORGET:
|
|
return 1; /* TRUE */
|
|
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
return 0; /* FALSE */
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_action_config(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
const char *args_struct_type_name,
|
|
const char **instructions,
|
|
uint32_t n_instructions)
|
|
{
|
|
struct struct_type *args_struct_type = NULL;
|
|
struct action *a = NULL;
|
|
int status = 0;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!action_find(p, name), EEXIST);
|
|
|
|
if (args_struct_type_name) {
|
|
CHECK_NAME(args_struct_type_name, EINVAL);
|
|
args_struct_type = struct_type_find(p, args_struct_type_name);
|
|
CHECK(args_struct_type, EINVAL);
|
|
CHECK(!args_struct_type->var_size, EINVAL);
|
|
}
|
|
|
|
/* Node allocation. */
|
|
a = calloc(1, sizeof(struct action));
|
|
if (!a) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
if (args_struct_type) {
|
|
a->args_endianness = calloc(args_struct_type->n_fields, sizeof(int));
|
|
if (!a->args_endianness) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* Node initialization. */
|
|
strcpy(a->name, name);
|
|
a->st = args_struct_type;
|
|
a->id = p->n_actions;
|
|
|
|
/* Instruction translation. */
|
|
status = instruction_config(p, a, instructions, n_instructions);
|
|
if (status)
|
|
goto error;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->actions, a, node);
|
|
p->n_actions++;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
if (!a)
|
|
return status;
|
|
|
|
free(a->args_endianness);
|
|
free(a->instructions);
|
|
free(a->instruction_data);
|
|
free(a);
|
|
|
|
return status;
|
|
}
|
|
|
|
static int
|
|
action_build(struct rte_swx_pipeline *p)
|
|
{
|
|
struct action *action;
|
|
|
|
/* p->action_instructions. */
|
|
p->action_instructions = calloc(p->n_actions, sizeof(struct instruction *));
|
|
CHECK(p->action_instructions, ENOMEM);
|
|
|
|
TAILQ_FOREACH(action, &p->actions, node)
|
|
p->action_instructions[action->id] = action->instructions;
|
|
|
|
/* p->action_funcs. */
|
|
p->action_funcs = calloc(p->n_actions, sizeof(action_func_t));
|
|
CHECK(p->action_funcs, ENOMEM);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
action_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
free(p->action_funcs);
|
|
p->action_funcs = NULL;
|
|
|
|
free(p->action_instructions);
|
|
p->action_instructions = NULL;
|
|
}
|
|
|
|
static void
|
|
action_free(struct rte_swx_pipeline *p)
|
|
{
|
|
action_build_free(p);
|
|
|
|
for ( ; ; ) {
|
|
struct action *action;
|
|
|
|
action = TAILQ_FIRST(&p->actions);
|
|
if (!action)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->actions, action, node);
|
|
free(action->args_endianness);
|
|
free(action->instructions);
|
|
free(action->instruction_data);
|
|
free(action);
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
action_arg_src_mov_count(struct action *a,
|
|
uint32_t arg_id,
|
|
struct instruction *instructions,
|
|
struct instruction_data *instruction_data,
|
|
uint32_t n_instructions)
|
|
{
|
|
uint32_t offset, n_users = 0, i;
|
|
|
|
if (!a->st ||
|
|
(arg_id >= a->st->n_fields) ||
|
|
!instructions ||
|
|
!instruction_data ||
|
|
!n_instructions)
|
|
return 0;
|
|
|
|
offset = a->st->fields[arg_id].offset / 8;
|
|
|
|
for (i = 0; i < n_instructions; i++) {
|
|
struct instruction *instr = &instructions[i];
|
|
struct instruction_data *data = &instruction_data[i];
|
|
|
|
if (data->invalid ||
|
|
((instr->type != INSTR_MOV) && (instr->type != INSTR_MOV_HM)) ||
|
|
instr->mov.src.struct_id ||
|
|
(instr->mov.src.offset != offset))
|
|
continue;
|
|
|
|
n_users++;
|
|
}
|
|
|
|
return n_users;
|
|
}
|
|
|
|
#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
|
|
|
|
#define ACTION_ARGS_TOKENS_MAX 256
|
|
|
|
static int
|
|
action_args_parse(struct action *a, const char *args, uint8_t *data)
|
|
{
|
|
char *tokens[ACTION_ARGS_TOKENS_MAX], *s0 = NULL, *s;
|
|
uint32_t n_tokens = 0, offset = 0, i;
|
|
int status = 0;
|
|
|
|
/* Checks. */
|
|
if (!a->st || !args || !args[0]) {
|
|
status = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
/* Memory allocation. */
|
|
s0 = strdup(args);
|
|
if (!s0) {
|
|
status = -ENOMEM;
|
|
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_DIM(tokens)) {
|
|
status = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
tokens[n_tokens] = token;
|
|
n_tokens++;
|
|
}
|
|
|
|
/* More checks. */
|
|
if (n_tokens != a->st->n_fields * 2) {
|
|
status = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
/* Process the action arguments. */
|
|
for (i = 0; i < a->st->n_fields; i++) {
|
|
struct field *f = &a->st->fields[i];
|
|
char *arg_name = tokens[i * 2];
|
|
char *arg_val = tokens[i * 2 + 1];
|
|
uint64_t val;
|
|
|
|
if (strcmp(arg_name, f->name)) {
|
|
status = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
val = strtoull(arg_val, &arg_val, 0);
|
|
if (arg_val[0]) {
|
|
status = -EINVAL;
|
|
goto error;
|
|
}
|
|
|
|
/* Endianness conversion. */
|
|
if (a->args_endianness[i])
|
|
val = field_hton(val, f->n_bits);
|
|
|
|
/* Copy to entry. */
|
|
memcpy(&data[offset], (uint8_t *)&val, f->n_bits / 8);
|
|
offset += f->n_bits / 8;
|
|
}
|
|
|
|
error:
|
|
free(s0);
|
|
return status;
|
|
}
|
|
|
|
/*
|
|
* Table.
|
|
*/
|
|
static struct table_type *
|
|
table_type_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct table_type *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->table_types, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct table_type *
|
|
table_type_resolve(struct rte_swx_pipeline *p,
|
|
const char *recommended_type_name,
|
|
enum rte_swx_table_match_type match_type)
|
|
{
|
|
struct table_type *elem;
|
|
|
|
/* Only consider the recommended type if the match type is correct. */
|
|
if (recommended_type_name)
|
|
TAILQ_FOREACH(elem, &p->table_types, node)
|
|
if (!strcmp(elem->name, recommended_type_name) &&
|
|
(elem->match_type == match_type))
|
|
return elem;
|
|
|
|
/* Ignore the recommended type and get the first element with this match
|
|
* type.
|
|
*/
|
|
TAILQ_FOREACH(elem, &p->table_types, node)
|
|
if (elem->match_type == match_type)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct table *
|
|
table_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct table *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->tables, node)
|
|
if (strcmp(elem->name, name) == 0)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct table *
|
|
table_find_by_id(struct rte_swx_pipeline *p, uint32_t id)
|
|
{
|
|
struct table *table = NULL;
|
|
|
|
TAILQ_FOREACH(table, &p->tables, node)
|
|
if (table->id == id)
|
|
return table;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_table_type_register(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
enum rte_swx_table_match_type match_type,
|
|
struct rte_swx_table_ops *ops)
|
|
{
|
|
struct table_type *elem;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!table_type_find(p, name), EEXIST);
|
|
|
|
CHECK(ops, EINVAL);
|
|
CHECK(ops->create, EINVAL);
|
|
CHECK(ops->lkp, EINVAL);
|
|
CHECK(ops->free, EINVAL);
|
|
|
|
/* Node allocation. */
|
|
elem = calloc(1, sizeof(struct table_type));
|
|
CHECK(elem, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(elem->name, name);
|
|
elem->match_type = match_type;
|
|
memcpy(&elem->ops, ops, sizeof(*ops));
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->table_types, elem, node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
table_match_type_resolve(struct rte_swx_match_field_params *fields,
|
|
uint32_t n_fields,
|
|
enum rte_swx_table_match_type *match_type)
|
|
{
|
|
uint32_t n_fields_em = 0, n_fields_lpm = 0, i;
|
|
|
|
for (i = 0; i < n_fields; i++) {
|
|
struct rte_swx_match_field_params *f = &fields[i];
|
|
|
|
if (f->match_type == RTE_SWX_TABLE_MATCH_EXACT)
|
|
n_fields_em++;
|
|
|
|
if (f->match_type == RTE_SWX_TABLE_MATCH_LPM)
|
|
n_fields_lpm++;
|
|
}
|
|
|
|
if ((n_fields_lpm > 1) ||
|
|
(n_fields_lpm && (n_fields_em != n_fields - 1)))
|
|
return -EINVAL;
|
|
|
|
*match_type = (n_fields_em == n_fields) ?
|
|
RTE_SWX_TABLE_MATCH_EXACT :
|
|
RTE_SWX_TABLE_MATCH_WILDCARD;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
table_match_fields_check(struct rte_swx_pipeline *p,
|
|
struct rte_swx_pipeline_table_params *params,
|
|
struct header **header)
|
|
{
|
|
struct header *h0 = NULL;
|
|
struct field *hf, *mf;
|
|
uint32_t *offset = NULL, i;
|
|
int status = 0;
|
|
|
|
/* Return if no match fields. */
|
|
if (!params->n_fields) {
|
|
if (params->fields) {
|
|
status = -EINVAL;
|
|
goto end;
|
|
}
|
|
|
|
if (header)
|
|
*header = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Memory allocation. */
|
|
offset = calloc(params->n_fields, sizeof(uint32_t));
|
|
if (!offset) {
|
|
status = -ENOMEM;
|
|
goto end;
|
|
}
|
|
|
|
/* Check that all the match fields belong to either the same header or
|
|
* to the meta-data.
|
|
*/
|
|
hf = header_field_parse(p, params->fields[0].name, &h0);
|
|
mf = metadata_field_parse(p, params->fields[0].name);
|
|
if ((!hf && !mf) || (hf && hf->var_size)) {
|
|
status = -EINVAL;
|
|
goto end;
|
|
}
|
|
|
|
offset[0] = h0 ? hf->offset : mf->offset;
|
|
|
|
for (i = 1; i < params->n_fields; i++)
|
|
if (h0) {
|
|
struct header *h;
|
|
|
|
hf = header_field_parse(p, params->fields[i].name, &h);
|
|
if (!hf || (h->id != h0->id) || hf->var_size) {
|
|
status = -EINVAL;
|
|
goto end;
|
|
}
|
|
|
|
offset[i] = hf->offset;
|
|
} else {
|
|
mf = metadata_field_parse(p, params->fields[i].name);
|
|
if (!mf) {
|
|
status = -EINVAL;
|
|
goto end;
|
|
}
|
|
|
|
offset[i] = mf->offset;
|
|
}
|
|
|
|
/* Check that there are no duplicated match fields. */
|
|
for (i = 0; i < params->n_fields; i++) {
|
|
uint32_t j;
|
|
|
|
for (j = 0; j < i; j++)
|
|
if (offset[j] == offset[i]) {
|
|
status = -EINVAL;
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
/* Return. */
|
|
if (header)
|
|
*header = h0;
|
|
|
|
end:
|
|
free(offset);
|
|
return status;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_table_config(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct rte_swx_pipeline_table_params *params,
|
|
const char *recommended_table_type_name,
|
|
const char *args,
|
|
uint32_t size)
|
|
{
|
|
struct table_type *type;
|
|
struct table *t = NULL;
|
|
struct action *default_action;
|
|
struct header *header = NULL;
|
|
uint32_t action_data_size_max = 0, i;
|
|
int status = 0;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!table_find(p, name), EEXIST);
|
|
CHECK(!selector_find(p, name), EEXIST);
|
|
CHECK(!learner_find(p, name), EEXIST);
|
|
|
|
CHECK(params, EINVAL);
|
|
|
|
/* Match checks. */
|
|
status = table_match_fields_check(p, params, &header);
|
|
if (status)
|
|
return status;
|
|
|
|
/* Action checks. */
|
|
CHECK(params->n_actions, EINVAL);
|
|
CHECK(params->action_names, EINVAL);
|
|
for (i = 0; i < params->n_actions; i++) {
|
|
const char *action_name = params->action_names[i];
|
|
struct action *a;
|
|
uint32_t action_data_size;
|
|
int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
|
|
|
|
CHECK_NAME(action_name, EINVAL);
|
|
|
|
a = action_find(p, action_name);
|
|
CHECK(a, EINVAL);
|
|
CHECK(!action_does_learning(a), EINVAL);
|
|
|
|
action_data_size = a->st ? a->st->n_bits / 8 : 0;
|
|
if (action_data_size > action_data_size_max)
|
|
action_data_size_max = action_data_size;
|
|
|
|
if (params->action_is_for_table_entries)
|
|
action_is_for_table_entries = params->action_is_for_table_entries[i];
|
|
if (params->action_is_for_default_entry)
|
|
action_is_for_default_entry = params->action_is_for_default_entry[i];
|
|
CHECK(action_is_for_table_entries || action_is_for_default_entry, EINVAL);
|
|
}
|
|
|
|
CHECK_NAME(params->default_action_name, EINVAL);
|
|
for (i = 0; i < p->n_actions; i++)
|
|
if (!strcmp(params->action_names[i],
|
|
params->default_action_name))
|
|
break;
|
|
CHECK(i < params->n_actions, EINVAL);
|
|
CHECK(!params->action_is_for_default_entry || params->action_is_for_default_entry[i],
|
|
EINVAL);
|
|
|
|
default_action = action_find(p, params->default_action_name);
|
|
CHECK((default_action->st && params->default_action_args) || !params->default_action_args,
|
|
EINVAL);
|
|
|
|
/* Table type checks. */
|
|
if (recommended_table_type_name)
|
|
CHECK_NAME(recommended_table_type_name, EINVAL);
|
|
|
|
if (params->n_fields) {
|
|
enum rte_swx_table_match_type match_type;
|
|
|
|
status = table_match_type_resolve(params->fields, params->n_fields, &match_type);
|
|
if (status)
|
|
return status;
|
|
|
|
type = table_type_resolve(p, recommended_table_type_name, match_type);
|
|
CHECK(type, EINVAL);
|
|
} else {
|
|
type = NULL;
|
|
}
|
|
|
|
/* Memory allocation. */
|
|
t = calloc(1, sizeof(struct table));
|
|
if (!t) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
t->fields = calloc(params->n_fields, sizeof(struct match_field));
|
|
if (!t->fields) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
t->actions = calloc(params->n_actions, sizeof(struct action *));
|
|
if (!t->actions) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
if (action_data_size_max) {
|
|
t->default_action_data = calloc(1, action_data_size_max);
|
|
if (!t->default_action_data) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
t->action_is_for_table_entries = calloc(params->n_actions, sizeof(int));
|
|
if (!t->action_is_for_table_entries) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
t->action_is_for_default_entry = calloc(params->n_actions, sizeof(int));
|
|
if (!t->action_is_for_default_entry) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
/* Node initialization. */
|
|
strcpy(t->name, name);
|
|
if (args && args[0])
|
|
strcpy(t->args, args);
|
|
t->type = type;
|
|
|
|
for (i = 0; i < params->n_fields; i++) {
|
|
struct rte_swx_match_field_params *field = ¶ms->fields[i];
|
|
struct match_field *f = &t->fields[i];
|
|
|
|
f->match_type = field->match_type;
|
|
f->field = header ?
|
|
header_field_parse(p, field->name, NULL) :
|
|
metadata_field_parse(p, field->name);
|
|
}
|
|
t->n_fields = params->n_fields;
|
|
t->header = header;
|
|
|
|
for (i = 0; i < params->n_actions; i++) {
|
|
int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
|
|
|
|
if (params->action_is_for_table_entries)
|
|
action_is_for_table_entries = params->action_is_for_table_entries[i];
|
|
if (params->action_is_for_default_entry)
|
|
action_is_for_default_entry = params->action_is_for_default_entry[i];
|
|
|
|
t->actions[i] = action_find(p, params->action_names[i]);
|
|
t->action_is_for_table_entries[i] = action_is_for_table_entries;
|
|
t->action_is_for_default_entry[i] = action_is_for_default_entry;
|
|
}
|
|
t->default_action = default_action;
|
|
if (default_action->st) {
|
|
status = action_args_parse(default_action,
|
|
params->default_action_args,
|
|
t->default_action_data);
|
|
if (status)
|
|
goto error;
|
|
}
|
|
|
|
t->n_actions = params->n_actions;
|
|
t->default_action_is_const = params->default_action_is_const;
|
|
t->action_data_size_max = action_data_size_max;
|
|
|
|
t->size = size;
|
|
t->id = p->n_tables;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->tables, t, node);
|
|
p->n_tables++;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
if (!t)
|
|
return status;
|
|
|
|
free(t->action_is_for_default_entry);
|
|
free(t->action_is_for_table_entries);
|
|
free(t->default_action_data);
|
|
free(t->actions);
|
|
free(t->fields);
|
|
free(t);
|
|
|
|
return status;
|
|
}
|
|
|
|
static struct rte_swx_table_params *
|
|
table_params_get(struct table *table)
|
|
{
|
|
struct rte_swx_table_params *params;
|
|
struct field *first, *last;
|
|
uint8_t *key_mask;
|
|
uint32_t key_size, key_offset, action_data_size, i;
|
|
|
|
/* Memory allocation. */
|
|
params = calloc(1, sizeof(struct rte_swx_table_params));
|
|
if (!params)
|
|
return NULL;
|
|
|
|
/* Find first (smallest offset) and last (biggest offset) match fields. */
|
|
first = table->fields[0].field;
|
|
last = table->fields[0].field;
|
|
|
|
for (i = 0; i < table->n_fields; i++) {
|
|
struct field *f = table->fields[i].field;
|
|
|
|
if (f->offset < first->offset)
|
|
first = f;
|
|
|
|
if (f->offset > last->offset)
|
|
last = f;
|
|
}
|
|
|
|
/* Key offset and size. */
|
|
key_offset = first->offset / 8;
|
|
key_size = (last->offset + last->n_bits - first->offset) / 8;
|
|
|
|
/* Memory allocation. */
|
|
key_mask = calloc(1, key_size);
|
|
if (!key_mask) {
|
|
free(params);
|
|
return NULL;
|
|
}
|
|
|
|
/* Key mask. */
|
|
for (i = 0; i < table->n_fields; i++) {
|
|
struct field *f = table->fields[i].field;
|
|
uint32_t start = (f->offset - first->offset) / 8;
|
|
size_t size = f->n_bits / 8;
|
|
|
|
memset(&key_mask[start], 0xFF, size);
|
|
}
|
|
|
|
/* Action data size. */
|
|
action_data_size = 0;
|
|
for (i = 0; i < table->n_actions; i++) {
|
|
struct action *action = table->actions[i];
|
|
uint32_t ads = action->st ? action->st->n_bits / 8 : 0;
|
|
|
|
if (ads > action_data_size)
|
|
action_data_size = ads;
|
|
}
|
|
|
|
/* Fill in. */
|
|
params->match_type = table->type->match_type;
|
|
params->key_size = key_size;
|
|
params->key_offset = key_offset;
|
|
params->key_mask0 = key_mask;
|
|
params->action_data_size = action_data_size;
|
|
params->n_keys_max = table->size;
|
|
|
|
return params;
|
|
}
|
|
|
|
static void
|
|
table_params_free(struct rte_swx_table_params *params)
|
|
{
|
|
if (!params)
|
|
return;
|
|
|
|
free(params->key_mask0);
|
|
free(params);
|
|
}
|
|
|
|
static int
|
|
table_stub_lkp(void *table __rte_unused,
|
|
void *mailbox __rte_unused,
|
|
uint8_t **key __rte_unused,
|
|
uint64_t *action_id __rte_unused,
|
|
uint8_t **action_data __rte_unused,
|
|
int *hit)
|
|
{
|
|
*hit = 0;
|
|
return 1; /* DONE. */
|
|
}
|
|
|
|
static int
|
|
table_build(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
/* Per pipeline: table statistics. */
|
|
p->table_stats = calloc(p->n_tables, sizeof(struct table_statistics));
|
|
CHECK(p->table_stats, ENOMEM);
|
|
|
|
for (i = 0; i < p->n_tables; i++) {
|
|
p->table_stats[i].n_pkts_action = calloc(p->n_actions, sizeof(uint64_t));
|
|
CHECK(p->table_stats[i].n_pkts_action, ENOMEM);
|
|
}
|
|
|
|
/* Per thread: table runt-time. */
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
struct table *table;
|
|
|
|
t->tables = calloc(p->n_tables, sizeof(struct table_runtime));
|
|
CHECK(t->tables, ENOMEM);
|
|
|
|
TAILQ_FOREACH(table, &p->tables, node) {
|
|
struct table_runtime *r = &t->tables[table->id];
|
|
|
|
if (table->type) {
|
|
uint64_t size;
|
|
|
|
size = table->type->ops.mailbox_size_get();
|
|
|
|
/* r->func. */
|
|
r->func = table->type->ops.lkp;
|
|
|
|
/* r->mailbox. */
|
|
if (size) {
|
|
r->mailbox = calloc(1, size);
|
|
CHECK(r->mailbox, ENOMEM);
|
|
}
|
|
|
|
/* r->key. */
|
|
r->key = table->header ?
|
|
&t->structs[table->header->struct_id] :
|
|
&t->structs[p->metadata_struct_id];
|
|
} else {
|
|
r->func = table_stub_lkp;
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
table_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
uint32_t j;
|
|
|
|
if (!t->tables)
|
|
continue;
|
|
|
|
for (j = 0; j < p->n_tables; j++) {
|
|
struct table_runtime *r = &t->tables[j];
|
|
|
|
free(r->mailbox);
|
|
}
|
|
|
|
free(t->tables);
|
|
t->tables = NULL;
|
|
}
|
|
|
|
if (p->table_stats) {
|
|
for (i = 0; i < p->n_tables; i++)
|
|
free(p->table_stats[i].n_pkts_action);
|
|
|
|
free(p->table_stats);
|
|
}
|
|
}
|
|
|
|
static void
|
|
table_free(struct rte_swx_pipeline *p)
|
|
{
|
|
table_build_free(p);
|
|
|
|
/* Tables. */
|
|
for ( ; ; ) {
|
|
struct table *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->tables);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->tables, elem, node);
|
|
free(elem->fields);
|
|
free(elem->actions);
|
|
free(elem->default_action_data);
|
|
free(elem);
|
|
}
|
|
|
|
/* Table types. */
|
|
for ( ; ; ) {
|
|
struct table_type *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->table_types);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->table_types, elem, node);
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Selector.
|
|
*/
|
|
static struct selector *
|
|
selector_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct selector *s;
|
|
|
|
TAILQ_FOREACH(s, &p->selectors, node)
|
|
if (strcmp(s->name, name) == 0)
|
|
return s;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct selector *
|
|
selector_find_by_id(struct rte_swx_pipeline *p, uint32_t id)
|
|
{
|
|
struct selector *s = NULL;
|
|
|
|
TAILQ_FOREACH(s, &p->selectors, node)
|
|
if (s->id == id)
|
|
return s;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
selector_fields_check(struct rte_swx_pipeline *p,
|
|
struct rte_swx_pipeline_selector_params *params,
|
|
struct header **header)
|
|
{
|
|
struct header *h0 = NULL;
|
|
struct field *hf, *mf;
|
|
uint32_t i;
|
|
|
|
/* Return if no selector fields. */
|
|
if (!params->n_selector_fields || !params->selector_field_names)
|
|
return -EINVAL;
|
|
|
|
/* Check that all the selector fields either belong to the same header
|
|
* or are all meta-data fields.
|
|
*/
|
|
hf = header_field_parse(p, params->selector_field_names[0], &h0);
|
|
mf = metadata_field_parse(p, params->selector_field_names[0]);
|
|
if (!hf && !mf)
|
|
return -EINVAL;
|
|
|
|
for (i = 1; i < params->n_selector_fields; i++)
|
|
if (h0) {
|
|
struct header *h;
|
|
|
|
hf = header_field_parse(p, params->selector_field_names[i], &h);
|
|
if (!hf || (h->id != h0->id))
|
|
return -EINVAL;
|
|
} else {
|
|
mf = metadata_field_parse(p, params->selector_field_names[i]);
|
|
if (!mf)
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check that there are no duplicated match fields. */
|
|
for (i = 0; i < params->n_selector_fields; i++) {
|
|
const char *field_name = params->selector_field_names[i];
|
|
uint32_t j;
|
|
|
|
for (j = i + 1; j < params->n_selector_fields; j++)
|
|
if (!strcmp(params->selector_field_names[j], field_name))
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Return. */
|
|
if (header)
|
|
*header = h0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_selector_config(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct rte_swx_pipeline_selector_params *params)
|
|
{
|
|
struct selector *s;
|
|
struct header *selector_header = NULL;
|
|
struct field *group_id_field, *member_id_field;
|
|
uint32_t i;
|
|
int status = 0;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!table_find(p, name), EEXIST);
|
|
CHECK(!selector_find(p, name), EEXIST);
|
|
CHECK(!learner_find(p, name), EEXIST);
|
|
|
|
CHECK(params, EINVAL);
|
|
|
|
CHECK_NAME(params->group_id_field_name, EINVAL);
|
|
group_id_field = metadata_field_parse(p, params->group_id_field_name);
|
|
CHECK(group_id_field, EINVAL);
|
|
|
|
for (i = 0; i < params->n_selector_fields; i++) {
|
|
const char *field_name = params->selector_field_names[i];
|
|
|
|
CHECK_NAME(field_name, EINVAL);
|
|
}
|
|
status = selector_fields_check(p, params, &selector_header);
|
|
if (status)
|
|
return status;
|
|
|
|
CHECK_NAME(params->member_id_field_name, EINVAL);
|
|
member_id_field = metadata_field_parse(p, params->member_id_field_name);
|
|
CHECK(member_id_field, EINVAL);
|
|
|
|
CHECK(params->n_groups_max, EINVAL);
|
|
|
|
CHECK(params->n_members_per_group_max, EINVAL);
|
|
|
|
/* Memory allocation. */
|
|
s = calloc(1, sizeof(struct selector));
|
|
if (!s) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
s->selector_fields = calloc(params->n_selector_fields, sizeof(struct field *));
|
|
if (!s->selector_fields) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
/* Node initialization. */
|
|
strcpy(s->name, name);
|
|
|
|
s->group_id_field = group_id_field;
|
|
|
|
for (i = 0; i < params->n_selector_fields; i++) {
|
|
const char *field_name = params->selector_field_names[i];
|
|
|
|
s->selector_fields[i] = selector_header ?
|
|
header_field_parse(p, field_name, NULL) :
|
|
metadata_field_parse(p, field_name);
|
|
}
|
|
|
|
s->n_selector_fields = params->n_selector_fields;
|
|
|
|
s->selector_header = selector_header;
|
|
|
|
s->member_id_field = member_id_field;
|
|
|
|
s->n_groups_max = params->n_groups_max;
|
|
|
|
s->n_members_per_group_max = params->n_members_per_group_max;
|
|
|
|
s->id = p->n_selectors;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->selectors, s, node);
|
|
p->n_selectors++;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
if (!s)
|
|
return status;
|
|
|
|
free(s->selector_fields);
|
|
|
|
free(s);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
selector_params_free(struct rte_swx_table_selector_params *params)
|
|
{
|
|
if (!params)
|
|
return;
|
|
|
|
free(params->selector_mask);
|
|
|
|
free(params);
|
|
}
|
|
|
|
static struct rte_swx_table_selector_params *
|
|
selector_table_params_get(struct selector *s)
|
|
{
|
|
struct rte_swx_table_selector_params *params = NULL;
|
|
struct field *first, *last;
|
|
uint32_t i;
|
|
|
|
/* Memory allocation. */
|
|
params = calloc(1, sizeof(struct rte_swx_table_selector_params));
|
|
if (!params)
|
|
goto error;
|
|
|
|
/* Group ID. */
|
|
params->group_id_offset = s->group_id_field->offset / 8;
|
|
|
|
/* Find first (smallest offset) and last (biggest offset) selector fields. */
|
|
first = s->selector_fields[0];
|
|
last = s->selector_fields[0];
|
|
|
|
for (i = 0; i < s->n_selector_fields; i++) {
|
|
struct field *f = s->selector_fields[i];
|
|
|
|
if (f->offset < first->offset)
|
|
first = f;
|
|
|
|
if (f->offset > last->offset)
|
|
last = f;
|
|
}
|
|
|
|
/* Selector offset and size. */
|
|
params->selector_offset = first->offset / 8;
|
|
params->selector_size = (last->offset + last->n_bits - first->offset) / 8;
|
|
|
|
/* Memory allocation. */
|
|
params->selector_mask = calloc(1, params->selector_size);
|
|
if (!params->selector_mask)
|
|
goto error;
|
|
|
|
/* Selector mask. */
|
|
for (i = 0; i < s->n_selector_fields; i++) {
|
|
struct field *f = s->selector_fields[i];
|
|
uint32_t start = (f->offset - first->offset) / 8;
|
|
size_t size = f->n_bits / 8;
|
|
|
|
memset(¶ms->selector_mask[start], 0xFF, size);
|
|
}
|
|
|
|
/* Member ID. */
|
|
params->member_id_offset = s->member_id_field->offset / 8;
|
|
|
|
/* Maximum number of groups. */
|
|
params->n_groups_max = s->n_groups_max;
|
|
|
|
/* Maximum number of members per group. */
|
|
params->n_members_per_group_max = s->n_members_per_group_max;
|
|
|
|
return params;
|
|
|
|
error:
|
|
selector_params_free(params);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
selector_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
uint32_t j;
|
|
|
|
if (!t->selectors)
|
|
continue;
|
|
|
|
for (j = 0; j < p->n_selectors; j++) {
|
|
struct selector_runtime *r = &t->selectors[j];
|
|
|
|
free(r->mailbox);
|
|
}
|
|
|
|
free(t->selectors);
|
|
t->selectors = NULL;
|
|
}
|
|
|
|
free(p->selector_stats);
|
|
p->selector_stats = NULL;
|
|
}
|
|
|
|
static int
|
|
selector_build(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
int status = 0;
|
|
|
|
/* Per pipeline: selector statistics. */
|
|
p->selector_stats = calloc(p->n_selectors, sizeof(struct selector_statistics));
|
|
if (!p->selector_stats) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
/* Per thread: selector run-time. */
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
struct selector *s;
|
|
|
|
t->selectors = calloc(p->n_selectors, sizeof(struct selector_runtime));
|
|
if (!t->selectors) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
TAILQ_FOREACH(s, &p->selectors, node) {
|
|
struct selector_runtime *r = &t->selectors[s->id];
|
|
uint64_t size;
|
|
|
|
/* r->mailbox. */
|
|
size = rte_swx_table_selector_mailbox_size_get();
|
|
if (size) {
|
|
r->mailbox = calloc(1, size);
|
|
if (!r->mailbox) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* r->group_id_buffer. */
|
|
r->group_id_buffer = &t->structs[p->metadata_struct_id];
|
|
|
|
/* r->selector_buffer. */
|
|
r->selector_buffer = s->selector_header ?
|
|
&t->structs[s->selector_header->struct_id] :
|
|
&t->structs[p->metadata_struct_id];
|
|
|
|
/* r->member_id_buffer. */
|
|
r->member_id_buffer = &t->structs[p->metadata_struct_id];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
selector_build_free(p);
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
selector_free(struct rte_swx_pipeline *p)
|
|
{
|
|
selector_build_free(p);
|
|
|
|
/* Selector tables. */
|
|
for ( ; ; ) {
|
|
struct selector *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->selectors);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->selectors, elem, node);
|
|
free(elem->selector_fields);
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Learner table.
|
|
*/
|
|
static struct learner *
|
|
learner_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct learner *l;
|
|
|
|
TAILQ_FOREACH(l, &p->learners, node)
|
|
if (!strcmp(l->name, name))
|
|
return l;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct learner *
|
|
learner_find_by_id(struct rte_swx_pipeline *p, uint32_t id)
|
|
{
|
|
struct learner *l = NULL;
|
|
|
|
TAILQ_FOREACH(l, &p->learners, node)
|
|
if (l->id == id)
|
|
return l;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static int
|
|
learner_match_fields_check(struct rte_swx_pipeline *p,
|
|
struct rte_swx_pipeline_learner_params *params,
|
|
struct header **header)
|
|
{
|
|
struct header *h0 = NULL;
|
|
struct field *hf, *mf;
|
|
uint32_t i;
|
|
|
|
/* Return if no match fields. */
|
|
if (!params->n_fields || !params->field_names)
|
|
return -EINVAL;
|
|
|
|
/* Check that all the match fields either belong to the same header
|
|
* or are all meta-data fields.
|
|
*/
|
|
hf = header_field_parse(p, params->field_names[0], &h0);
|
|
mf = metadata_field_parse(p, params->field_names[0]);
|
|
if (!hf && !mf)
|
|
return -EINVAL;
|
|
|
|
for (i = 1; i < params->n_fields; i++)
|
|
if (h0) {
|
|
struct header *h;
|
|
|
|
hf = header_field_parse(p, params->field_names[i], &h);
|
|
if (!hf || (h->id != h0->id))
|
|
return -EINVAL;
|
|
} else {
|
|
mf = metadata_field_parse(p, params->field_names[i]);
|
|
if (!mf)
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check that there are no duplicated match fields. */
|
|
for (i = 0; i < params->n_fields; i++) {
|
|
const char *field_name = params->field_names[i];
|
|
uint32_t j;
|
|
|
|
for (j = i + 1; j < params->n_fields; j++)
|
|
if (!strcmp(params->field_names[j], field_name))
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Return. */
|
|
if (header)
|
|
*header = h0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
learner_action_args_check(struct rte_swx_pipeline *p, struct action *a, const char *mf_name)
|
|
{
|
|
struct struct_type *mst = p->metadata_st, *ast = a->st;
|
|
struct field *mf, *af;
|
|
uint32_t mf_pos, i;
|
|
|
|
if (!ast) {
|
|
if (mf_name)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Check that mf_name is the name of a valid meta-data field. */
|
|
CHECK_NAME(mf_name, EINVAL);
|
|
mf = metadata_field_parse(p, mf_name);
|
|
CHECK(mf, EINVAL);
|
|
|
|
/* Check that there are enough meta-data fields, starting with the mf_name field, to cover
|
|
* all the action arguments.
|
|
*/
|
|
mf_pos = mf - mst->fields;
|
|
CHECK(mst->n_fields - mf_pos >= ast->n_fields, EINVAL);
|
|
|
|
/* Check that the size of each of the identified meta-data fields matches exactly the size
|
|
* of the corresponding action argument.
|
|
*/
|
|
for (i = 0; i < ast->n_fields; i++) {
|
|
mf = &mst->fields[mf_pos + i];
|
|
af = &ast->fields[i];
|
|
|
|
CHECK(mf->n_bits == af->n_bits, EINVAL);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
learner_action_learning_check(struct rte_swx_pipeline *p,
|
|
struct action *action,
|
|
const char **action_names,
|
|
uint32_t n_actions)
|
|
{
|
|
uint32_t i;
|
|
|
|
/* For each "learn" instruction of the current action, check that the learned action (i.e.
|
|
* the action passed as argument to the "learn" instruction) is also enabled for the
|
|
* current learner table.
|
|
*/
|
|
for (i = 0; i < action->n_instructions; i++) {
|
|
struct instruction *instr = &action->instructions[i];
|
|
uint32_t found = 0, j;
|
|
|
|
if (instr->type != INSTR_LEARNER_LEARN)
|
|
continue;
|
|
|
|
for (j = 0; j < n_actions; j++) {
|
|
struct action *a;
|
|
|
|
a = action_find(p, action_names[j]);
|
|
if (!a)
|
|
return -EINVAL;
|
|
|
|
if (a->id == instr->learn.action_id)
|
|
found = 1;
|
|
}
|
|
|
|
if (!found)
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_learner_config(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct rte_swx_pipeline_learner_params *params,
|
|
uint32_t size,
|
|
uint32_t *timeout,
|
|
uint32_t n_timeouts)
|
|
{
|
|
struct learner *l = NULL;
|
|
struct action *default_action;
|
|
struct header *header = NULL;
|
|
uint32_t action_data_size_max = 0, i;
|
|
int status = 0;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!table_find(p, name), EEXIST);
|
|
CHECK(!selector_find(p, name), EEXIST);
|
|
CHECK(!learner_find(p, name), EEXIST);
|
|
|
|
CHECK(params, EINVAL);
|
|
|
|
/* Match checks. */
|
|
status = learner_match_fields_check(p, params, &header);
|
|
if (status)
|
|
return status;
|
|
|
|
/* Action checks. */
|
|
CHECK(params->n_actions, EINVAL);
|
|
CHECK(params->action_names, EINVAL);
|
|
for (i = 0; i < params->n_actions; i++) {
|
|
const char *action_name = params->action_names[i];
|
|
struct action *a;
|
|
uint32_t action_data_size;
|
|
int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
|
|
|
|
CHECK_NAME(action_name, EINVAL);
|
|
|
|
a = action_find(p, action_name);
|
|
CHECK(a, EINVAL);
|
|
|
|
status = learner_action_learning_check(p,
|
|
a,
|
|
params->action_names,
|
|
params->n_actions);
|
|
if (status)
|
|
return status;
|
|
|
|
action_data_size = a->st ? a->st->n_bits / 8 : 0;
|
|
if (action_data_size > action_data_size_max)
|
|
action_data_size_max = action_data_size;
|
|
|
|
if (params->action_is_for_table_entries)
|
|
action_is_for_table_entries = params->action_is_for_table_entries[i];
|
|
if (params->action_is_for_default_entry)
|
|
action_is_for_default_entry = params->action_is_for_default_entry[i];
|
|
CHECK(action_is_for_table_entries || action_is_for_default_entry, EINVAL);
|
|
}
|
|
|
|
CHECK_NAME(params->default_action_name, EINVAL);
|
|
for (i = 0; i < p->n_actions; i++)
|
|
if (!strcmp(params->action_names[i],
|
|
params->default_action_name))
|
|
break;
|
|
CHECK(i < params->n_actions, EINVAL);
|
|
CHECK(!params->action_is_for_default_entry || params->action_is_for_default_entry[i],
|
|
EINVAL);
|
|
|
|
default_action = action_find(p, params->default_action_name);
|
|
CHECK((default_action->st && params->default_action_args) || !params->default_action_args,
|
|
EINVAL);
|
|
|
|
/* Any other checks. */
|
|
CHECK(size, EINVAL);
|
|
CHECK(timeout, EINVAL);
|
|
CHECK(n_timeouts && (n_timeouts < RTE_SWX_TABLE_LEARNER_N_KEY_TIMEOUTS_MAX), EINVAL);
|
|
|
|
/* Memory allocation. */
|
|
l = calloc(1, sizeof(struct learner));
|
|
if (!l) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
l->fields = calloc(params->n_fields, sizeof(struct field *));
|
|
if (!l->fields) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
l->actions = calloc(params->n_actions, sizeof(struct action *));
|
|
if (!l->actions) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
if (action_data_size_max) {
|
|
l->default_action_data = calloc(1, action_data_size_max);
|
|
if (!l->default_action_data) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
l->action_is_for_table_entries = calloc(params->n_actions, sizeof(int));
|
|
if (!l->action_is_for_table_entries) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
l->action_is_for_default_entry = calloc(params->n_actions, sizeof(int));
|
|
if (!l->action_is_for_default_entry) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
/* Node initialization. */
|
|
strcpy(l->name, name);
|
|
|
|
for (i = 0; i < params->n_fields; i++) {
|
|
const char *field_name = params->field_names[i];
|
|
|
|
l->fields[i] = header ?
|
|
header_field_parse(p, field_name, NULL) :
|
|
metadata_field_parse(p, field_name);
|
|
}
|
|
|
|
l->n_fields = params->n_fields;
|
|
|
|
l->header = header;
|
|
|
|
for (i = 0; i < params->n_actions; i++) {
|
|
int action_is_for_table_entries = 1, action_is_for_default_entry = 1;
|
|
|
|
if (params->action_is_for_table_entries)
|
|
action_is_for_table_entries = params->action_is_for_table_entries[i];
|
|
if (params->action_is_for_default_entry)
|
|
action_is_for_default_entry = params->action_is_for_default_entry[i];
|
|
|
|
l->actions[i] = action_find(p, params->action_names[i]);
|
|
l->action_is_for_table_entries[i] = action_is_for_table_entries;
|
|
l->action_is_for_default_entry[i] = action_is_for_default_entry;
|
|
}
|
|
|
|
l->default_action = default_action;
|
|
|
|
if (default_action->st) {
|
|
status = action_args_parse(default_action,
|
|
params->default_action_args,
|
|
l->default_action_data);
|
|
if (status)
|
|
goto error;
|
|
}
|
|
|
|
l->n_actions = params->n_actions;
|
|
|
|
l->default_action_is_const = params->default_action_is_const;
|
|
|
|
l->action_data_size_max = action_data_size_max;
|
|
|
|
l->size = size;
|
|
|
|
for (i = 0; i < n_timeouts; i++)
|
|
l->timeout[i] = timeout[i];
|
|
|
|
l->n_timeouts = n_timeouts;
|
|
|
|
l->id = p->n_learners;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->learners, l, node);
|
|
p->n_learners++;
|
|
|
|
return 0;
|
|
|
|
error:
|
|
if (!l)
|
|
return status;
|
|
|
|
free(l->action_is_for_default_entry);
|
|
free(l->action_is_for_table_entries);
|
|
free(l->default_action_data);
|
|
free(l->actions);
|
|
free(l->fields);
|
|
free(l);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
learner_params_free(struct rte_swx_table_learner_params *params)
|
|
{
|
|
if (!params)
|
|
return;
|
|
|
|
free(params->key_mask0);
|
|
|
|
free(params->key_timeout);
|
|
|
|
free(params);
|
|
}
|
|
|
|
static struct rte_swx_table_learner_params *
|
|
learner_params_get(struct learner *l)
|
|
{
|
|
struct rte_swx_table_learner_params *params = NULL;
|
|
struct field *first, *last;
|
|
uint32_t i;
|
|
|
|
/* Memory allocation. */
|
|
params = calloc(1, sizeof(struct rte_swx_table_learner_params));
|
|
if (!params)
|
|
goto error;
|
|
|
|
/* Find first (smallest offset) and last (biggest offset) match fields. */
|
|
first = l->fields[0];
|
|
last = l->fields[0];
|
|
|
|
for (i = 0; i < l->n_fields; i++) {
|
|
struct field *f = l->fields[i];
|
|
|
|
if (f->offset < first->offset)
|
|
first = f;
|
|
|
|
if (f->offset > last->offset)
|
|
last = f;
|
|
}
|
|
|
|
/* Key offset and size. */
|
|
params->key_offset = first->offset / 8;
|
|
params->key_size = (last->offset + last->n_bits - first->offset) / 8;
|
|
|
|
/* Memory allocation. */
|
|
params->key_mask0 = calloc(1, params->key_size);
|
|
if (!params->key_mask0)
|
|
goto error;
|
|
|
|
/* Key mask. */
|
|
for (i = 0; i < l->n_fields; i++) {
|
|
struct field *f = l->fields[i];
|
|
uint32_t start = (f->offset - first->offset) / 8;
|
|
size_t size = f->n_bits / 8;
|
|
|
|
memset(¶ms->key_mask0[start], 0xFF, size);
|
|
}
|
|
|
|
/* Action data size. */
|
|
params->action_data_size = l->action_data_size_max;
|
|
|
|
/* Maximum number of keys. */
|
|
params->n_keys_max = l->size;
|
|
|
|
/* Memory allocation. */
|
|
params->key_timeout = calloc(l->n_timeouts, sizeof(uint32_t));
|
|
if (!params->key_timeout)
|
|
goto error;
|
|
|
|
/* Timeout. */
|
|
for (i = 0; i < l->n_timeouts; i++)
|
|
params->key_timeout[i] = l->timeout[i];
|
|
|
|
params->n_key_timeouts = l->n_timeouts;
|
|
|
|
return params;
|
|
|
|
error:
|
|
learner_params_free(params);
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
learner_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
uint32_t j;
|
|
|
|
if (!t->learners)
|
|
continue;
|
|
|
|
for (j = 0; j < p->n_learners; j++) {
|
|
struct learner_runtime *r = &t->learners[j];
|
|
|
|
free(r->mailbox);
|
|
}
|
|
|
|
free(t->learners);
|
|
t->learners = NULL;
|
|
}
|
|
|
|
if (p->learner_stats) {
|
|
for (i = 0; i < p->n_learners; i++)
|
|
free(p->learner_stats[i].n_pkts_action);
|
|
|
|
free(p->learner_stats);
|
|
}
|
|
}
|
|
|
|
static int
|
|
learner_build(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
int status = 0;
|
|
|
|
/* Per pipeline: learner statistics. */
|
|
p->learner_stats = calloc(p->n_learners, sizeof(struct learner_statistics));
|
|
CHECK(p->learner_stats, ENOMEM);
|
|
|
|
for (i = 0; i < p->n_learners; i++) {
|
|
p->learner_stats[i].n_pkts_action = calloc(p->n_actions, sizeof(uint64_t));
|
|
CHECK(p->learner_stats[i].n_pkts_action, ENOMEM);
|
|
}
|
|
|
|
/* Per thread: learner run-time. */
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
struct learner *l;
|
|
|
|
t->learners = calloc(p->n_learners, sizeof(struct learner_runtime));
|
|
if (!t->learners) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
TAILQ_FOREACH(l, &p->learners, node) {
|
|
struct learner_runtime *r = &t->learners[l->id];
|
|
uint64_t size;
|
|
|
|
/* r->mailbox. */
|
|
size = rte_swx_table_learner_mailbox_size_get();
|
|
if (size) {
|
|
r->mailbox = calloc(1, size);
|
|
if (!r->mailbox) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
/* r->key. */
|
|
r->key = l->header ?
|
|
&t->structs[l->header->struct_id] :
|
|
&t->structs[p->metadata_struct_id];
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
error:
|
|
learner_build_free(p);
|
|
return status;
|
|
}
|
|
|
|
static void
|
|
learner_free(struct rte_swx_pipeline *p)
|
|
{
|
|
learner_build_free(p);
|
|
|
|
/* Learner tables. */
|
|
for ( ; ; ) {
|
|
struct learner *l;
|
|
|
|
l = TAILQ_FIRST(&p->learners);
|
|
if (!l)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->learners, l, node);
|
|
free(l->fields);
|
|
free(l->actions);
|
|
free(l->default_action_data);
|
|
free(l);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Table state.
|
|
*/
|
|
static int
|
|
table_state_build(struct rte_swx_pipeline *p)
|
|
{
|
|
struct table *table;
|
|
struct selector *s;
|
|
struct learner *l;
|
|
|
|
p->table_state = calloc(p->n_tables + p->n_selectors + p->n_learners,
|
|
sizeof(struct rte_swx_table_state));
|
|
CHECK(p->table_state, ENOMEM);
|
|
|
|
TAILQ_FOREACH(table, &p->tables, node) {
|
|
struct rte_swx_table_state *ts = &p->table_state[table->id];
|
|
|
|
if (table->type) {
|
|
struct rte_swx_table_params *params;
|
|
|
|
/* ts->obj. */
|
|
params = table_params_get(table);
|
|
CHECK(params, ENOMEM);
|
|
|
|
ts->obj = table->type->ops.create(params,
|
|
NULL,
|
|
table->args,
|
|
p->numa_node);
|
|
|
|
table_params_free(params);
|
|
CHECK(ts->obj, ENODEV);
|
|
}
|
|
|
|
/* ts->default_action_data. */
|
|
if (table->action_data_size_max) {
|
|
ts->default_action_data =
|
|
malloc(table->action_data_size_max);
|
|
CHECK(ts->default_action_data, ENOMEM);
|
|
|
|
memcpy(ts->default_action_data,
|
|
table->default_action_data,
|
|
table->action_data_size_max);
|
|
}
|
|
|
|
/* ts->default_action_id. */
|
|
ts->default_action_id = table->default_action->id;
|
|
}
|
|
|
|
TAILQ_FOREACH(s, &p->selectors, node) {
|
|
struct rte_swx_table_state *ts = &p->table_state[p->n_tables + s->id];
|
|
struct rte_swx_table_selector_params *params;
|
|
|
|
/* ts->obj. */
|
|
params = selector_table_params_get(s);
|
|
CHECK(params, ENOMEM);
|
|
|
|
ts->obj = rte_swx_table_selector_create(params, NULL, p->numa_node);
|
|
|
|
selector_params_free(params);
|
|
CHECK(ts->obj, ENODEV);
|
|
}
|
|
|
|
TAILQ_FOREACH(l, &p->learners, node) {
|
|
struct rte_swx_table_state *ts = &p->table_state[p->n_tables +
|
|
p->n_selectors + l->id];
|
|
struct rte_swx_table_learner_params *params;
|
|
|
|
/* ts->obj. */
|
|
params = learner_params_get(l);
|
|
CHECK(params, ENOMEM);
|
|
|
|
ts->obj = rte_swx_table_learner_create(params, p->numa_node);
|
|
learner_params_free(params);
|
|
CHECK(ts->obj, ENODEV);
|
|
|
|
/* ts->default_action_data. */
|
|
if (l->action_data_size_max) {
|
|
ts->default_action_data = malloc(l->action_data_size_max);
|
|
CHECK(ts->default_action_data, ENOMEM);
|
|
|
|
memcpy(ts->default_action_data,
|
|
l->default_action_data,
|
|
l->action_data_size_max);
|
|
}
|
|
|
|
/* ts->default_action_id. */
|
|
ts->default_action_id = l->default_action->id;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
table_state_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (!p->table_state)
|
|
return;
|
|
|
|
for (i = 0; i < p->n_tables; i++) {
|
|
struct rte_swx_table_state *ts = &p->table_state[i];
|
|
struct table *table = table_find_by_id(p, i);
|
|
|
|
/* ts->obj. */
|
|
if (table->type && ts->obj)
|
|
table->type->ops.free(ts->obj);
|
|
|
|
/* ts->default_action_data. */
|
|
free(ts->default_action_data);
|
|
}
|
|
|
|
for (i = 0; i < p->n_selectors; i++) {
|
|
struct rte_swx_table_state *ts = &p->table_state[p->n_tables + i];
|
|
|
|
/* ts->obj. */
|
|
if (ts->obj)
|
|
rte_swx_table_selector_free(ts->obj);
|
|
}
|
|
|
|
for (i = 0; i < p->n_learners; i++) {
|
|
struct rte_swx_table_state *ts = &p->table_state[p->n_tables + p->n_selectors + i];
|
|
|
|
/* ts->obj. */
|
|
if (ts->obj)
|
|
rte_swx_table_learner_free(ts->obj);
|
|
|
|
/* ts->default_action_data. */
|
|
free(ts->default_action_data);
|
|
}
|
|
|
|
free(p->table_state);
|
|
p->table_state = NULL;
|
|
}
|
|
|
|
static void
|
|
table_state_free(struct rte_swx_pipeline *p)
|
|
{
|
|
table_state_build_free(p);
|
|
}
|
|
|
|
/*
|
|
* Register array.
|
|
*/
|
|
static struct regarray *
|
|
regarray_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct regarray *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->regarrays, node)
|
|
if (!strcmp(elem->name, name))
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct regarray *
|
|
regarray_find_by_id(struct rte_swx_pipeline *p, uint32_t id)
|
|
{
|
|
struct regarray *elem = NULL;
|
|
|
|
TAILQ_FOREACH(elem, &p->regarrays, node)
|
|
if (elem->id == id)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_regarray_config(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
uint32_t size,
|
|
uint64_t init_val)
|
|
{
|
|
struct regarray *r;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!regarray_find(p, name), EEXIST);
|
|
|
|
CHECK(size, EINVAL);
|
|
size = rte_align32pow2(size);
|
|
|
|
/* Memory allocation. */
|
|
r = calloc(1, sizeof(struct regarray));
|
|
CHECK(r, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(r->name, name);
|
|
r->init_val = init_val;
|
|
r->size = size;
|
|
r->id = p->n_regarrays;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->regarrays, r, node);
|
|
p->n_regarrays++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
regarray_build(struct rte_swx_pipeline *p)
|
|
{
|
|
struct regarray *regarray;
|
|
|
|
if (!p->n_regarrays)
|
|
return 0;
|
|
|
|
p->regarray_runtime = calloc(p->n_regarrays, sizeof(struct regarray_runtime));
|
|
CHECK(p->regarray_runtime, ENOMEM);
|
|
|
|
TAILQ_FOREACH(regarray, &p->regarrays, node) {
|
|
struct regarray_runtime *r = &p->regarray_runtime[regarray->id];
|
|
uint32_t i;
|
|
|
|
r->regarray = env_malloc(regarray->size * sizeof(uint64_t),
|
|
RTE_CACHE_LINE_SIZE,
|
|
p->numa_node);
|
|
CHECK(r->regarray, ENOMEM);
|
|
|
|
if (regarray->init_val)
|
|
for (i = 0; i < regarray->size; i++)
|
|
r->regarray[i] = regarray->init_val;
|
|
|
|
r->size_mask = regarray->size - 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
regarray_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (!p->regarray_runtime)
|
|
return;
|
|
|
|
for (i = 0; i < p->n_regarrays; i++) {
|
|
struct regarray *regarray = regarray_find_by_id(p, i);
|
|
struct regarray_runtime *r = &p->regarray_runtime[i];
|
|
|
|
env_free(r->regarray, regarray->size * sizeof(uint64_t));
|
|
}
|
|
|
|
free(p->regarray_runtime);
|
|
p->regarray_runtime = NULL;
|
|
}
|
|
|
|
static void
|
|
regarray_free(struct rte_swx_pipeline *p)
|
|
{
|
|
regarray_build_free(p);
|
|
|
|
for ( ; ; ) {
|
|
struct regarray *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->regarrays);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->regarrays, elem, node);
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Meter array.
|
|
*/
|
|
static struct meter_profile *
|
|
meter_profile_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct meter_profile *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->meter_profiles, node)
|
|
if (!strcmp(elem->name, name))
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct metarray *
|
|
metarray_find(struct rte_swx_pipeline *p, const char *name)
|
|
{
|
|
struct metarray *elem;
|
|
|
|
TAILQ_FOREACH(elem, &p->metarrays, node)
|
|
if (!strcmp(elem->name, name))
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct metarray *
|
|
metarray_find_by_id(struct rte_swx_pipeline *p, uint32_t id)
|
|
{
|
|
struct metarray *elem = NULL;
|
|
|
|
TAILQ_FOREACH(elem, &p->metarrays, node)
|
|
if (elem->id == id)
|
|
return elem;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_metarray_config(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
uint32_t size)
|
|
{
|
|
struct metarray *m;
|
|
|
|
CHECK(p, EINVAL);
|
|
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(!metarray_find(p, name), EEXIST);
|
|
|
|
CHECK(size, EINVAL);
|
|
size = rte_align32pow2(size);
|
|
|
|
/* Memory allocation. */
|
|
m = calloc(1, sizeof(struct metarray));
|
|
CHECK(m, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(m->name, name);
|
|
m->size = size;
|
|
m->id = p->n_metarrays;
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->metarrays, m, node);
|
|
p->n_metarrays++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct meter_profile meter_profile_default = {
|
|
.node = {0},
|
|
.name = "",
|
|
.params = {0},
|
|
|
|
.profile = {
|
|
.cbs = 10000,
|
|
.pbs = 10000,
|
|
.cir_period = 1,
|
|
.cir_bytes_per_period = 1,
|
|
.pir_period = 1,
|
|
.pir_bytes_per_period = 1,
|
|
},
|
|
|
|
.n_users = 0,
|
|
};
|
|
|
|
static void
|
|
meter_init(struct meter *m)
|
|
{
|
|
memset(m, 0, sizeof(struct meter));
|
|
rte_meter_trtcm_config(&m->m, &meter_profile_default.profile);
|
|
m->profile = &meter_profile_default;
|
|
m->color_mask = RTE_COLOR_GREEN;
|
|
|
|
meter_profile_default.n_users++;
|
|
}
|
|
|
|
static int
|
|
metarray_build(struct rte_swx_pipeline *p)
|
|
{
|
|
struct metarray *m;
|
|
|
|
if (!p->n_metarrays)
|
|
return 0;
|
|
|
|
p->metarray_runtime = calloc(p->n_metarrays, sizeof(struct metarray_runtime));
|
|
CHECK(p->metarray_runtime, ENOMEM);
|
|
|
|
TAILQ_FOREACH(m, &p->metarrays, node) {
|
|
struct metarray_runtime *r = &p->metarray_runtime[m->id];
|
|
uint32_t i;
|
|
|
|
r->metarray = env_malloc(m->size * sizeof(struct meter),
|
|
RTE_CACHE_LINE_SIZE,
|
|
p->numa_node);
|
|
CHECK(r->metarray, ENOMEM);
|
|
|
|
for (i = 0; i < m->size; i++)
|
|
meter_init(&r->metarray[i]);
|
|
|
|
r->size_mask = m->size - 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
metarray_build_free(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
if (!p->metarray_runtime)
|
|
return;
|
|
|
|
for (i = 0; i < p->n_metarrays; i++) {
|
|
struct metarray *m = metarray_find_by_id(p, i);
|
|
struct metarray_runtime *r = &p->metarray_runtime[i];
|
|
|
|
env_free(r->metarray, m->size * sizeof(struct meter));
|
|
}
|
|
|
|
free(p->metarray_runtime);
|
|
p->metarray_runtime = NULL;
|
|
}
|
|
|
|
static void
|
|
metarray_free(struct rte_swx_pipeline *p)
|
|
{
|
|
metarray_build_free(p);
|
|
|
|
/* Meter arrays. */
|
|
for ( ; ; ) {
|
|
struct metarray *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->metarrays);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->metarrays, elem, node);
|
|
free(elem);
|
|
}
|
|
|
|
/* Meter profiles. */
|
|
for ( ; ; ) {
|
|
struct meter_profile *elem;
|
|
|
|
elem = TAILQ_FIRST(&p->meter_profiles);
|
|
if (!elem)
|
|
break;
|
|
|
|
TAILQ_REMOVE(&p->meter_profiles, elem, node);
|
|
free(elem);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Pipeline.
|
|
*/
|
|
void
|
|
rte_swx_pipeline_free(struct rte_swx_pipeline *p)
|
|
{
|
|
void *lib;
|
|
|
|
if (!p)
|
|
return;
|
|
|
|
lib = p->lib;
|
|
|
|
free(p->instruction_data);
|
|
free(p->instructions);
|
|
|
|
metarray_free(p);
|
|
regarray_free(p);
|
|
table_state_free(p);
|
|
learner_free(p);
|
|
selector_free(p);
|
|
table_free(p);
|
|
action_free(p);
|
|
instruction_table_free(p);
|
|
metadata_free(p);
|
|
header_free(p);
|
|
hash_func_free(p);
|
|
extern_func_free(p);
|
|
extern_obj_free(p);
|
|
mirroring_free(p);
|
|
port_out_free(p);
|
|
port_in_free(p);
|
|
struct_free(p);
|
|
|
|
free(p);
|
|
|
|
if (lib)
|
|
dlclose(lib);
|
|
}
|
|
|
|
static int
|
|
port_in_types_register(struct rte_swx_pipeline *p)
|
|
{
|
|
int status;
|
|
|
|
status = rte_swx_pipeline_port_in_type_register(p,
|
|
"ethdev",
|
|
&rte_swx_port_ethdev_reader_ops);
|
|
if (status)
|
|
return status;
|
|
|
|
status = rte_swx_pipeline_port_in_type_register(p,
|
|
"ring",
|
|
&rte_swx_port_ring_reader_ops);
|
|
if (status)
|
|
return status;
|
|
|
|
#ifdef RTE_PORT_PCAP
|
|
status = rte_swx_pipeline_port_in_type_register(p,
|
|
"source",
|
|
&rte_swx_port_source_ops);
|
|
if (status)
|
|
return status;
|
|
#endif
|
|
|
|
status = rte_swx_pipeline_port_in_type_register(p,
|
|
"fd",
|
|
&rte_swx_port_fd_reader_ops);
|
|
if (status)
|
|
return status;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
port_out_types_register(struct rte_swx_pipeline *p)
|
|
{
|
|
int status;
|
|
|
|
status = rte_swx_pipeline_port_out_type_register(p,
|
|
"ethdev",
|
|
&rte_swx_port_ethdev_writer_ops);
|
|
if (status)
|
|
return status;
|
|
|
|
status = rte_swx_pipeline_port_out_type_register(p,
|
|
"ring",
|
|
&rte_swx_port_ring_writer_ops);
|
|
if (status)
|
|
return status;
|
|
|
|
status = rte_swx_pipeline_port_out_type_register(p,
|
|
"sink",
|
|
&rte_swx_port_sink_ops);
|
|
if (status)
|
|
return status;
|
|
|
|
status = rte_swx_pipeline_port_out_type_register(p,
|
|
"fd",
|
|
&rte_swx_port_fd_writer_ops);
|
|
if (status)
|
|
return status;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
table_types_register(struct rte_swx_pipeline *p)
|
|
{
|
|
int status;
|
|
|
|
status = rte_swx_pipeline_table_type_register(p,
|
|
"exact",
|
|
RTE_SWX_TABLE_MATCH_EXACT,
|
|
&rte_swx_table_exact_match_ops);
|
|
if (status)
|
|
return status;
|
|
|
|
status = rte_swx_pipeline_table_type_register(p,
|
|
"wildcard",
|
|
RTE_SWX_TABLE_MATCH_WILDCARD,
|
|
&rte_swx_table_wildcard_match_ops);
|
|
if (status)
|
|
return status;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
hash_funcs_register(struct rte_swx_pipeline *p)
|
|
{
|
|
int status;
|
|
|
|
status = rte_swx_pipeline_hash_func_register(p, "jhash", rte_jhash);
|
|
if (status)
|
|
return status;
|
|
|
|
status = rte_swx_pipeline_hash_func_register(p, "crc32", rte_hash_crc);
|
|
if (status)
|
|
return status;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_config(struct rte_swx_pipeline **p, int numa_node)
|
|
{
|
|
struct rte_swx_pipeline *pipeline = NULL;
|
|
int status = 0;
|
|
|
|
/* Check input parameters. */
|
|
CHECK(p, EINVAL);
|
|
|
|
/* Memory allocation. */
|
|
pipeline = calloc(1, sizeof(struct rte_swx_pipeline));
|
|
if (!pipeline) {
|
|
status = -ENOMEM;
|
|
goto error;
|
|
}
|
|
|
|
/* Initialization. */
|
|
TAILQ_INIT(&pipeline->struct_types);
|
|
TAILQ_INIT(&pipeline->port_in_types);
|
|
TAILQ_INIT(&pipeline->ports_in);
|
|
TAILQ_INIT(&pipeline->port_out_types);
|
|
TAILQ_INIT(&pipeline->ports_out);
|
|
TAILQ_INIT(&pipeline->extern_types);
|
|
TAILQ_INIT(&pipeline->extern_objs);
|
|
TAILQ_INIT(&pipeline->extern_funcs);
|
|
TAILQ_INIT(&pipeline->hash_funcs);
|
|
TAILQ_INIT(&pipeline->headers);
|
|
TAILQ_INIT(&pipeline->actions);
|
|
TAILQ_INIT(&pipeline->table_types);
|
|
TAILQ_INIT(&pipeline->tables);
|
|
TAILQ_INIT(&pipeline->selectors);
|
|
TAILQ_INIT(&pipeline->learners);
|
|
TAILQ_INIT(&pipeline->regarrays);
|
|
TAILQ_INIT(&pipeline->meter_profiles);
|
|
TAILQ_INIT(&pipeline->metarrays);
|
|
|
|
pipeline->n_structs = 1; /* Struct 0 is reserved for action_data. */
|
|
pipeline->n_mirroring_slots = RTE_SWX_PACKET_MIRRORING_SLOTS_DEFAULT;
|
|
pipeline->n_mirroring_sessions = RTE_SWX_PACKET_MIRRORING_SESSIONS_DEFAULT;
|
|
pipeline->numa_node = numa_node;
|
|
|
|
status = port_in_types_register(pipeline);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = port_out_types_register(pipeline);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = table_types_register(pipeline);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = hash_funcs_register(pipeline);
|
|
if (status)
|
|
goto error;
|
|
|
|
*p = pipeline;
|
|
return 0;
|
|
|
|
error:
|
|
rte_swx_pipeline_free(pipeline);
|
|
return status;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_instructions_config(struct rte_swx_pipeline *p,
|
|
const char **instructions,
|
|
uint32_t n_instructions)
|
|
{
|
|
int err;
|
|
uint32_t i;
|
|
|
|
err = instruction_config(p, NULL, instructions, n_instructions);
|
|
if (err)
|
|
return err;
|
|
|
|
/* Thread instruction pointer reset. */
|
|
for (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {
|
|
struct thread *t = &p->threads[i];
|
|
|
|
thread_ip_reset(p, t);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
pipeline_compile(struct rte_swx_pipeline *p);
|
|
|
|
int
|
|
rte_swx_pipeline_build(struct rte_swx_pipeline *p)
|
|
{
|
|
struct rte_swx_port_sink_params drop_port_params = {
|
|
.file_name = NULL,
|
|
};
|
|
int status;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK(p->build_done == 0, EEXIST);
|
|
|
|
status = port_in_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
/* Drop port. */
|
|
status = rte_swx_pipeline_port_out_config(p,
|
|
p->n_ports_out,
|
|
"sink",
|
|
&drop_port_params);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = port_out_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = mirroring_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = struct_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = extern_obj_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = extern_func_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = hash_func_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = header_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = metadata_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = instruction_table_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = action_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = table_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = selector_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = learner_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = table_state_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = regarray_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
status = metarray_build(p);
|
|
if (status)
|
|
goto error;
|
|
|
|
p->build_done = 1;
|
|
|
|
pipeline_compile(p);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
metarray_build_free(p);
|
|
regarray_build_free(p);
|
|
table_state_build_free(p);
|
|
learner_build_free(p);
|
|
selector_build_free(p);
|
|
table_build_free(p);
|
|
action_build_free(p);
|
|
instruction_table_build_free(p);
|
|
metadata_build_free(p);
|
|
header_build_free(p);
|
|
hash_func_build_free(p);
|
|
extern_func_build_free(p);
|
|
extern_obj_build_free(p);
|
|
mirroring_build_free(p);
|
|
port_out_build_free(p);
|
|
port_in_build_free(p);
|
|
struct_build_free(p);
|
|
|
|
return status;
|
|
}
|
|
|
|
void
|
|
rte_swx_pipeline_run(struct rte_swx_pipeline *p, uint32_t n_instructions)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < n_instructions; i++)
|
|
instr_exec(p);
|
|
}
|
|
|
|
void
|
|
rte_swx_pipeline_flush(struct rte_swx_pipeline *p)
|
|
{
|
|
uint32_t i;
|
|
|
|
for (i = 0; i < p->n_ports_out; i++) {
|
|
struct port_out_runtime *port = &p->out[i];
|
|
|
|
if (port->flush)
|
|
port->flush(port->obj);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Control.
|
|
*/
|
|
int
|
|
rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
|
|
struct rte_swx_ctl_pipeline_info *pipeline)
|
|
{
|
|
struct action *action;
|
|
struct table *table;
|
|
uint32_t n_actions = 0, n_tables = 0;
|
|
|
|
if (!p || !pipeline)
|
|
return -EINVAL;
|
|
|
|
TAILQ_FOREACH(action, &p->actions, node)
|
|
n_actions++;
|
|
|
|
TAILQ_FOREACH(table, &p->tables, node)
|
|
n_tables++;
|
|
|
|
pipeline->n_ports_in = p->n_ports_in;
|
|
pipeline->n_ports_out = p->n_ports_out;
|
|
pipeline->n_mirroring_slots = p->n_mirroring_slots;
|
|
pipeline->n_mirroring_sessions = p->n_mirroring_sessions;
|
|
pipeline->n_actions = n_actions;
|
|
pipeline->n_tables = n_tables;
|
|
pipeline->n_selectors = p->n_selectors;
|
|
pipeline->n_learners = p->n_learners;
|
|
pipeline->n_regarrays = p->n_regarrays;
|
|
pipeline->n_metarrays = p->n_metarrays;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_numa_node_get(struct rte_swx_pipeline *p, int *numa_node)
|
|
{
|
|
if (!p || !numa_node)
|
|
return -EINVAL;
|
|
|
|
*numa_node = p->numa_node;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_action_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t action_id,
|
|
struct rte_swx_ctl_action_info *action)
|
|
{
|
|
struct action *a = NULL;
|
|
|
|
if (!p || (action_id >= p->n_actions) || !action)
|
|
return -EINVAL;
|
|
|
|
a = action_find_by_id(p, action_id);
|
|
if (!a)
|
|
return -EINVAL;
|
|
|
|
strcpy(action->name, a->name);
|
|
action->n_args = a->st ? a->st->n_fields : 0;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_action_arg_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t action_id,
|
|
uint32_t action_arg_id,
|
|
struct rte_swx_ctl_action_arg_info *action_arg)
|
|
{
|
|
struct action *a = NULL;
|
|
struct field *arg = NULL;
|
|
|
|
if (!p || (action_id >= p->n_actions) || !action_arg)
|
|
return -EINVAL;
|
|
|
|
a = action_find_by_id(p, action_id);
|
|
if (!a || !a->st || (action_arg_id >= a->st->n_fields))
|
|
return -EINVAL;
|
|
|
|
arg = &a->st->fields[action_arg_id];
|
|
strcpy(action_arg->name, arg->name);
|
|
action_arg->n_bits = arg->n_bits;
|
|
action_arg->is_network_byte_order = a->args_endianness[action_arg_id];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_table_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t table_id,
|
|
struct rte_swx_ctl_table_info *table)
|
|
{
|
|
struct table *t = NULL;
|
|
|
|
if (!p || !table)
|
|
return -EINVAL;
|
|
|
|
t = table_find_by_id(p, table_id);
|
|
if (!t)
|
|
return -EINVAL;
|
|
|
|
strcpy(table->name, t->name);
|
|
strcpy(table->args, t->args);
|
|
table->n_match_fields = t->n_fields;
|
|
table->n_actions = t->n_actions;
|
|
table->default_action_is_const = t->default_action_is_const;
|
|
table->size = t->size;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_table_match_field_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t table_id,
|
|
uint32_t match_field_id,
|
|
struct rte_swx_ctl_table_match_field_info *match_field)
|
|
{
|
|
struct table *t;
|
|
struct match_field *f;
|
|
|
|
if (!p || (table_id >= p->n_tables) || !match_field)
|
|
return -EINVAL;
|
|
|
|
t = table_find_by_id(p, table_id);
|
|
if (!t || (match_field_id >= t->n_fields))
|
|
return -EINVAL;
|
|
|
|
f = &t->fields[match_field_id];
|
|
match_field->match_type = f->match_type;
|
|
match_field->is_header = t->header ? 1 : 0;
|
|
match_field->n_bits = f->field->n_bits;
|
|
match_field->offset = f->field->offset;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_table_action_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t table_id,
|
|
uint32_t table_action_id,
|
|
struct rte_swx_ctl_table_action_info *table_action)
|
|
{
|
|
struct table *t;
|
|
|
|
if (!p || (table_id >= p->n_tables) || !table_action)
|
|
return -EINVAL;
|
|
|
|
t = table_find_by_id(p, table_id);
|
|
if (!t || (table_action_id >= t->n_actions))
|
|
return -EINVAL;
|
|
|
|
table_action->action_id = t->actions[table_action_id]->id;
|
|
|
|
table_action->action_is_for_table_entries = t->action_is_for_table_entries[table_action_id];
|
|
table_action->action_is_for_default_entry = t->action_is_for_default_entry[table_action_id];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_table_ops_get(struct rte_swx_pipeline *p,
|
|
uint32_t table_id,
|
|
struct rte_swx_table_ops *table_ops,
|
|
int *is_stub)
|
|
{
|
|
struct table *t;
|
|
|
|
if (!p || (table_id >= p->n_tables))
|
|
return -EINVAL;
|
|
|
|
t = table_find_by_id(p, table_id);
|
|
if (!t)
|
|
return -EINVAL;
|
|
|
|
if (t->type) {
|
|
if (table_ops)
|
|
memcpy(table_ops, &t->type->ops, sizeof(*table_ops));
|
|
*is_stub = 0;
|
|
} else {
|
|
*is_stub = 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_selector_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t selector_id,
|
|
struct rte_swx_ctl_selector_info *selector)
|
|
{
|
|
struct selector *s = NULL;
|
|
|
|
if (!p || !selector)
|
|
return -EINVAL;
|
|
|
|
s = selector_find_by_id(p, selector_id);
|
|
if (!s)
|
|
return -EINVAL;
|
|
|
|
strcpy(selector->name, s->name);
|
|
|
|
selector->n_selector_fields = s->n_selector_fields;
|
|
selector->n_groups_max = s->n_groups_max;
|
|
selector->n_members_per_group_max = s->n_members_per_group_max;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_selector_group_id_field_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t selector_id,
|
|
struct rte_swx_ctl_table_match_field_info *field)
|
|
{
|
|
struct selector *s;
|
|
|
|
if (!p || (selector_id >= p->n_selectors) || !field)
|
|
return -EINVAL;
|
|
|
|
s = selector_find_by_id(p, selector_id);
|
|
if (!s)
|
|
return -EINVAL;
|
|
|
|
field->match_type = RTE_SWX_TABLE_MATCH_EXACT;
|
|
field->is_header = 0;
|
|
field->n_bits = s->group_id_field->n_bits;
|
|
field->offset = s->group_id_field->offset;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_selector_field_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t selector_id,
|
|
uint32_t selector_field_id,
|
|
struct rte_swx_ctl_table_match_field_info *field)
|
|
{
|
|
struct selector *s;
|
|
struct field *f;
|
|
|
|
if (!p || (selector_id >= p->n_selectors) || !field)
|
|
return -EINVAL;
|
|
|
|
s = selector_find_by_id(p, selector_id);
|
|
if (!s || (selector_field_id >= s->n_selector_fields))
|
|
return -EINVAL;
|
|
|
|
f = s->selector_fields[selector_field_id];
|
|
field->match_type = RTE_SWX_TABLE_MATCH_EXACT;
|
|
field->is_header = s->selector_header ? 1 : 0;
|
|
field->n_bits = f->n_bits;
|
|
field->offset = f->offset;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_selector_member_id_field_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t selector_id,
|
|
struct rte_swx_ctl_table_match_field_info *field)
|
|
{
|
|
struct selector *s;
|
|
|
|
if (!p || (selector_id >= p->n_selectors) || !field)
|
|
return -EINVAL;
|
|
|
|
s = selector_find_by_id(p, selector_id);
|
|
if (!s)
|
|
return -EINVAL;
|
|
|
|
field->match_type = RTE_SWX_TABLE_MATCH_EXACT;
|
|
field->is_header = 0;
|
|
field->n_bits = s->member_id_field->n_bits;
|
|
field->offset = s->member_id_field->offset;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_learner_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t learner_id,
|
|
struct rte_swx_ctl_learner_info *learner)
|
|
{
|
|
struct learner *l = NULL;
|
|
|
|
if (!p || !learner)
|
|
return -EINVAL;
|
|
|
|
l = learner_find_by_id(p, learner_id);
|
|
if (!l)
|
|
return -EINVAL;
|
|
|
|
strcpy(learner->name, l->name);
|
|
|
|
learner->n_match_fields = l->n_fields;
|
|
learner->n_actions = l->n_actions;
|
|
learner->default_action_is_const = l->default_action_is_const;
|
|
learner->size = l->size;
|
|
learner->n_key_timeouts = l->n_timeouts;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_learner_match_field_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t learner_id,
|
|
uint32_t match_field_id,
|
|
struct rte_swx_ctl_table_match_field_info *match_field)
|
|
{
|
|
struct learner *l;
|
|
struct field *f;
|
|
|
|
if (!p || (learner_id >= p->n_learners) || !match_field)
|
|
return -EINVAL;
|
|
|
|
l = learner_find_by_id(p, learner_id);
|
|
if (!l || (match_field_id >= l->n_fields))
|
|
return -EINVAL;
|
|
|
|
f = l->fields[match_field_id];
|
|
match_field->match_type = RTE_SWX_TABLE_MATCH_EXACT;
|
|
match_field->is_header = l->header ? 1 : 0;
|
|
match_field->n_bits = f->n_bits;
|
|
match_field->offset = f->offset;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_learner_action_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t learner_id,
|
|
uint32_t learner_action_id,
|
|
struct rte_swx_ctl_table_action_info *learner_action)
|
|
{
|
|
struct learner *l;
|
|
|
|
if (!p || (learner_id >= p->n_learners) || !learner_action)
|
|
return -EINVAL;
|
|
|
|
l = learner_find_by_id(p, learner_id);
|
|
if (!l || (learner_action_id >= l->n_actions))
|
|
return -EINVAL;
|
|
|
|
learner_action->action_id = l->actions[learner_action_id]->id;
|
|
|
|
learner_action->action_is_for_table_entries =
|
|
l->action_is_for_table_entries[learner_action_id];
|
|
|
|
learner_action->action_is_for_default_entry =
|
|
l->action_is_for_default_entry[learner_action_id];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_learner_timeout_get(struct rte_swx_pipeline *p,
|
|
uint32_t learner_id,
|
|
uint32_t timeout_id,
|
|
uint32_t *timeout)
|
|
{
|
|
struct learner *l;
|
|
|
|
if (!p || (learner_id >= p->n_learners) || !timeout)
|
|
return -EINVAL;
|
|
|
|
l = learner_find_by_id(p, learner_id);
|
|
if (!l || (timeout_id >= l->n_timeouts))
|
|
return -EINVAL;
|
|
|
|
*timeout = l->timeout[timeout_id];
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_learner_timeout_set(struct rte_swx_pipeline *p,
|
|
uint32_t learner_id,
|
|
uint32_t timeout_id,
|
|
uint32_t timeout)
|
|
{
|
|
struct learner *l;
|
|
struct rte_swx_table_state *ts;
|
|
int status;
|
|
|
|
if (!p || (learner_id >= p->n_learners) || !timeout)
|
|
return -EINVAL;
|
|
|
|
l = learner_find_by_id(p, learner_id);
|
|
if (!l || (timeout_id >= l->n_timeouts))
|
|
return -EINVAL;
|
|
|
|
if (!p->build_done)
|
|
return -EINVAL;
|
|
|
|
ts = &p->table_state[p->n_tables + p->n_selectors + l->id];
|
|
|
|
status = rte_swx_table_learner_timeout_update(ts->obj, timeout_id, timeout);
|
|
if (status)
|
|
return -EINVAL;
|
|
|
|
l->timeout[timeout_id] = timeout;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_table_state_get(struct rte_swx_pipeline *p,
|
|
struct rte_swx_table_state **table_state)
|
|
{
|
|
if (!p || !table_state || !p->build_done)
|
|
return -EINVAL;
|
|
|
|
*table_state = p->table_state;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_pipeline_table_state_set(struct rte_swx_pipeline *p,
|
|
struct rte_swx_table_state *table_state)
|
|
{
|
|
if (!p || !table_state || !p->build_done)
|
|
return -EINVAL;
|
|
|
|
p->table_state = table_state;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_port_in_stats_read(struct rte_swx_pipeline *p,
|
|
uint32_t port_id,
|
|
struct rte_swx_port_in_stats *stats)
|
|
{
|
|
struct port_in *port;
|
|
|
|
if (!p || !stats)
|
|
return -EINVAL;
|
|
|
|
port = port_in_find(p, port_id);
|
|
if (!port)
|
|
return -EINVAL;
|
|
|
|
port->type->ops.stats_read(port->obj, stats);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_port_out_stats_read(struct rte_swx_pipeline *p,
|
|
uint32_t port_id,
|
|
struct rte_swx_port_out_stats *stats)
|
|
{
|
|
struct port_out *port;
|
|
|
|
if (!p || !stats)
|
|
return -EINVAL;
|
|
|
|
port = port_out_find(p, port_id);
|
|
if (!port)
|
|
return -EINVAL;
|
|
|
|
port->type->ops.stats_read(port->obj, stats);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_table_stats_read(struct rte_swx_pipeline *p,
|
|
const char *table_name,
|
|
struct rte_swx_table_stats *stats)
|
|
{
|
|
struct table *table;
|
|
struct table_statistics *table_stats;
|
|
|
|
if (!p || !table_name || !table_name[0] || !stats || !stats->n_pkts_action)
|
|
return -EINVAL;
|
|
|
|
table = table_find(p, table_name);
|
|
if (!table)
|
|
return -EINVAL;
|
|
|
|
table_stats = &p->table_stats[table->id];
|
|
|
|
memcpy(stats->n_pkts_action,
|
|
table_stats->n_pkts_action,
|
|
p->n_actions * sizeof(uint64_t));
|
|
|
|
stats->n_pkts_hit = table_stats->n_pkts_hit[1];
|
|
stats->n_pkts_miss = table_stats->n_pkts_hit[0];
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_selector_stats_read(struct rte_swx_pipeline *p,
|
|
const char *selector_name,
|
|
struct rte_swx_pipeline_selector_stats *stats)
|
|
{
|
|
struct selector *s;
|
|
|
|
if (!p || !selector_name || !selector_name[0] || !stats)
|
|
return -EINVAL;
|
|
|
|
s = selector_find(p, selector_name);
|
|
if (!s)
|
|
return -EINVAL;
|
|
|
|
stats->n_pkts = p->selector_stats[s->id].n_pkts;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_learner_stats_read(struct rte_swx_pipeline *p,
|
|
const char *learner_name,
|
|
struct rte_swx_learner_stats *stats)
|
|
{
|
|
struct learner *l;
|
|
struct learner_statistics *learner_stats;
|
|
|
|
if (!p || !learner_name || !learner_name[0] || !stats || !stats->n_pkts_action)
|
|
return -EINVAL;
|
|
|
|
l = learner_find(p, learner_name);
|
|
if (!l)
|
|
return -EINVAL;
|
|
|
|
learner_stats = &p->learner_stats[l->id];
|
|
|
|
memcpy(stats->n_pkts_action,
|
|
learner_stats->n_pkts_action,
|
|
p->n_actions * sizeof(uint64_t));
|
|
|
|
stats->n_pkts_hit = learner_stats->n_pkts_hit[1];
|
|
stats->n_pkts_miss = learner_stats->n_pkts_hit[0];
|
|
|
|
stats->n_pkts_learn_ok = learner_stats->n_pkts_learn[0];
|
|
stats->n_pkts_learn_err = learner_stats->n_pkts_learn[1];
|
|
|
|
stats->n_pkts_rearm = learner_stats->n_pkts_rearm;
|
|
stats->n_pkts_forget = learner_stats->n_pkts_forget;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_regarray_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t regarray_id,
|
|
struct rte_swx_ctl_regarray_info *regarray)
|
|
{
|
|
struct regarray *r;
|
|
|
|
if (!p || !regarray)
|
|
return -EINVAL;
|
|
|
|
r = regarray_find_by_id(p, regarray_id);
|
|
if (!r)
|
|
return -EINVAL;
|
|
|
|
strcpy(regarray->name, r->name);
|
|
regarray->size = r->size;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_regarray_read(struct rte_swx_pipeline *p,
|
|
const char *regarray_name,
|
|
uint32_t regarray_index,
|
|
uint64_t *value)
|
|
{
|
|
struct regarray *regarray;
|
|
struct regarray_runtime *r;
|
|
|
|
if (!p || !regarray_name || !value)
|
|
return -EINVAL;
|
|
|
|
regarray = regarray_find(p, regarray_name);
|
|
if (!regarray || (regarray_index >= regarray->size))
|
|
return -EINVAL;
|
|
|
|
r = &p->regarray_runtime[regarray->id];
|
|
*value = r->regarray[regarray_index];
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_regarray_write(struct rte_swx_pipeline *p,
|
|
const char *regarray_name,
|
|
uint32_t regarray_index,
|
|
uint64_t value)
|
|
{
|
|
struct regarray *regarray;
|
|
struct regarray_runtime *r;
|
|
|
|
if (!p || !regarray_name)
|
|
return -EINVAL;
|
|
|
|
regarray = regarray_find(p, regarray_name);
|
|
if (!regarray || (regarray_index >= regarray->size))
|
|
return -EINVAL;
|
|
|
|
r = &p->regarray_runtime[regarray->id];
|
|
r->regarray[regarray_index] = value;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_metarray_info_get(struct rte_swx_pipeline *p,
|
|
uint32_t metarray_id,
|
|
struct rte_swx_ctl_metarray_info *metarray)
|
|
{
|
|
struct metarray *m;
|
|
|
|
if (!p || !metarray)
|
|
return -EINVAL;
|
|
|
|
m = metarray_find_by_id(p, metarray_id);
|
|
if (!m)
|
|
return -EINVAL;
|
|
|
|
strcpy(metarray->name, m->name);
|
|
metarray->size = m->size;
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_meter_profile_add(struct rte_swx_pipeline *p,
|
|
const char *name,
|
|
struct rte_meter_trtcm_params *params)
|
|
{
|
|
struct meter_profile *mp;
|
|
int status;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK_NAME(name, EINVAL);
|
|
CHECK(params, EINVAL);
|
|
CHECK(!meter_profile_find(p, name), EEXIST);
|
|
|
|
/* Node allocation. */
|
|
mp = calloc(1, sizeof(struct meter_profile));
|
|
CHECK(mp, ENOMEM);
|
|
|
|
/* Node initialization. */
|
|
strcpy(mp->name, name);
|
|
memcpy(&mp->params, params, sizeof(struct rte_meter_trtcm_params));
|
|
status = rte_meter_trtcm_profile_config(&mp->profile, params);
|
|
if (status) {
|
|
free(mp);
|
|
CHECK(0, EINVAL);
|
|
}
|
|
|
|
/* Node add to tailq. */
|
|
TAILQ_INSERT_TAIL(&p->meter_profiles, mp, node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_meter_profile_delete(struct rte_swx_pipeline *p,
|
|
const char *name)
|
|
{
|
|
struct meter_profile *mp;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK_NAME(name, EINVAL);
|
|
|
|
mp = meter_profile_find(p, name);
|
|
CHECK(mp, EINVAL);
|
|
CHECK(!mp->n_users, EBUSY);
|
|
|
|
/* Remove node from tailq. */
|
|
TAILQ_REMOVE(&p->meter_profiles, mp, node);
|
|
free(mp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_meter_reset(struct rte_swx_pipeline *p,
|
|
const char *metarray_name,
|
|
uint32_t metarray_index)
|
|
{
|
|
struct meter_profile *mp_old;
|
|
struct metarray *metarray;
|
|
struct metarray_runtime *metarray_runtime;
|
|
struct meter *m;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK_NAME(metarray_name, EINVAL);
|
|
|
|
metarray = metarray_find(p, metarray_name);
|
|
CHECK(metarray, EINVAL);
|
|
CHECK(metarray_index < metarray->size, EINVAL);
|
|
|
|
metarray_runtime = &p->metarray_runtime[metarray->id];
|
|
m = &metarray_runtime->metarray[metarray_index];
|
|
mp_old = m->profile;
|
|
|
|
meter_init(m);
|
|
|
|
mp_old->n_users--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_meter_set(struct rte_swx_pipeline *p,
|
|
const char *metarray_name,
|
|
uint32_t metarray_index,
|
|
const char *profile_name)
|
|
{
|
|
struct meter_profile *mp, *mp_old;
|
|
struct metarray *metarray;
|
|
struct metarray_runtime *metarray_runtime;
|
|
struct meter *m;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK_NAME(metarray_name, EINVAL);
|
|
|
|
metarray = metarray_find(p, metarray_name);
|
|
CHECK(metarray, EINVAL);
|
|
CHECK(metarray_index < metarray->size, EINVAL);
|
|
|
|
mp = meter_profile_find(p, profile_name);
|
|
CHECK(mp, EINVAL);
|
|
|
|
metarray_runtime = &p->metarray_runtime[metarray->id];
|
|
m = &metarray_runtime->metarray[metarray_index];
|
|
mp_old = m->profile;
|
|
|
|
memset(m, 0, sizeof(struct meter));
|
|
rte_meter_trtcm_config(&m->m, &mp->profile);
|
|
m->profile = mp;
|
|
m->color_mask = RTE_COLORS;
|
|
|
|
mp->n_users++;
|
|
mp_old->n_users--;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_meter_stats_read(struct rte_swx_pipeline *p,
|
|
const char *metarray_name,
|
|
uint32_t metarray_index,
|
|
struct rte_swx_ctl_meter_stats *stats)
|
|
{
|
|
struct metarray *metarray;
|
|
struct metarray_runtime *metarray_runtime;
|
|
struct meter *m;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK_NAME(metarray_name, EINVAL);
|
|
|
|
metarray = metarray_find(p, metarray_name);
|
|
CHECK(metarray, EINVAL);
|
|
CHECK(metarray_index < metarray->size, EINVAL);
|
|
|
|
CHECK(stats, EINVAL);
|
|
|
|
metarray_runtime = &p->metarray_runtime[metarray->id];
|
|
m = &metarray_runtime->metarray[metarray_index];
|
|
|
|
memcpy(stats->n_pkts, m->n_pkts, sizeof(m->n_pkts));
|
|
memcpy(stats->n_bytes, m->n_bytes, sizeof(m->n_bytes));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
rte_swx_ctl_pipeline_mirroring_session_set(struct rte_swx_pipeline *p,
|
|
uint32_t session_id,
|
|
struct rte_swx_pipeline_mirroring_session_params *params)
|
|
{
|
|
struct mirroring_session *s;
|
|
|
|
CHECK(p, EINVAL);
|
|
CHECK(p->build_done, EEXIST);
|
|
CHECK(session_id < p->n_mirroring_sessions, EINVAL);
|
|
CHECK(params, EINVAL);
|
|
CHECK(params->port_id < p->n_ports_out, EINVAL);
|
|
|
|
s = &p->mirroring_sessions[session_id];
|
|
s->port_id = params->port_id;
|
|
s->fast_clone = params->fast_clone;
|
|
s->truncation_length = params->truncation_length ? params->truncation_length : UINT32_MAX;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Pipeline compilation.
|
|
*/
|
|
static const char *
|
|
instr_type_to_name(struct instruction *instr)
|
|
{
|
|
switch (instr->type) {
|
|
case INSTR_RX: return "INSTR_RX";
|
|
|
|
case INSTR_TX: return "INSTR_TX";
|
|
case INSTR_TX_I: return "INSTR_TX_I";
|
|
case INSTR_DROP: return "INSTR_DROP";
|
|
case INSTR_MIRROR: return "INSTR_MIRROR";
|
|
case INSTR_RECIRCULATE: return "INSTR_RECIRCULATE";
|
|
case INSTR_RECIRCID: return "INSTR_RECIRCID";
|
|
|
|
case INSTR_HDR_EXTRACT: return "INSTR_HDR_EXTRACT";
|
|
case INSTR_HDR_EXTRACT2: return "INSTR_HDR_EXTRACT2";
|
|
case INSTR_HDR_EXTRACT3: return "INSTR_HDR_EXTRACT3";
|
|
case INSTR_HDR_EXTRACT4: return "INSTR_HDR_EXTRACT4";
|
|
case INSTR_HDR_EXTRACT5: return "INSTR_HDR_EXTRACT5";
|
|
case INSTR_HDR_EXTRACT6: return "INSTR_HDR_EXTRACT6";
|
|
case INSTR_HDR_EXTRACT7: return "INSTR_HDR_EXTRACT7";
|
|
case INSTR_HDR_EXTRACT8: return "INSTR_HDR_EXTRACT8";
|
|
|
|
case INSTR_HDR_EXTRACT_M: return "INSTR_HDR_EXTRACT_M";
|
|
|
|
case INSTR_HDR_LOOKAHEAD: return "INSTR_HDR_LOOKAHEAD";
|
|
|
|
case INSTR_HDR_EMIT: return "INSTR_HDR_EMIT";
|
|
case INSTR_HDR_EMIT_TX: return "INSTR_HDR_EMIT_TX";
|
|
case INSTR_HDR_EMIT2_TX: return "INSTR_HDR_EMIT2_TX";
|
|
case INSTR_HDR_EMIT3_TX: return "INSTR_HDR_EMIT3_TX";
|
|
case INSTR_HDR_EMIT4_TX: return "INSTR_HDR_EMIT4_TX";
|
|
case INSTR_HDR_EMIT5_TX: return "INSTR_HDR_EMIT5_TX";
|
|
case INSTR_HDR_EMIT6_TX: return "INSTR_HDR_EMIT6_TX";
|
|
case INSTR_HDR_EMIT7_TX: return "INSTR_HDR_EMIT7_TX";
|
|
case INSTR_HDR_EMIT8_TX: return "INSTR_HDR_EMIT8_TX";
|
|
|
|
case INSTR_HDR_VALIDATE: return "INSTR_HDR_VALIDATE";
|
|
case INSTR_HDR_INVALIDATE: return "INSTR_HDR_INVALIDATE";
|
|
|
|
case INSTR_MOV: return "INSTR_MOV";
|
|
case INSTR_MOV_MH: return "INSTR_MOV_MH";
|
|
case INSTR_MOV_HM: return "INSTR_MOV_HM";
|
|
case INSTR_MOV_HH: return "INSTR_MOV_HH";
|
|
case INSTR_MOV_I: return "INSTR_MOV_I";
|
|
|
|
case INSTR_DMA_HT: return "INSTR_DMA_HT";
|
|
case INSTR_DMA_HT2: return "INSTR_DMA_HT2";
|
|
case INSTR_DMA_HT3: return "INSTR_DMA_HT3";
|
|
case INSTR_DMA_HT4: return "INSTR_DMA_HT4";
|
|
case INSTR_DMA_HT5: return "INSTR_DMA_HT5";
|
|
case INSTR_DMA_HT6: return "INSTR_DMA_HT6";
|
|
case INSTR_DMA_HT7: return "INSTR_DMA_HT7";
|
|
case INSTR_DMA_HT8: return "INSTR_DMA_HT8";
|
|
|
|
case INSTR_ALU_ADD: return "INSTR_ALU_ADD";
|
|
case INSTR_ALU_ADD_MH: return "INSTR_ALU_ADD_MH";
|
|
case INSTR_ALU_ADD_HM: return "INSTR_ALU_ADD_HM";
|
|
case INSTR_ALU_ADD_HH: return "INSTR_ALU_ADD_HH";
|
|
case INSTR_ALU_ADD_MI: return "INSTR_ALU_ADD_MI";
|
|
case INSTR_ALU_ADD_HI: return "INSTR_ALU_ADD_HI";
|
|
|
|
case INSTR_ALU_SUB: return "INSTR_ALU_SUB";
|
|
case INSTR_ALU_SUB_MH: return "INSTR_ALU_SUB_MH";
|
|
case INSTR_ALU_SUB_HM: return "INSTR_ALU_SUB_HM";
|
|
case INSTR_ALU_SUB_HH: return "INSTR_ALU_SUB_HH";
|
|
case INSTR_ALU_SUB_MI: return "INSTR_ALU_SUB_MI";
|
|
case INSTR_ALU_SUB_HI: return "INSTR_ALU_SUB_HI";
|
|
|
|
case INSTR_ALU_CKADD_FIELD: return "INSTR_ALU_CKADD_FIELD";
|
|
case INSTR_ALU_CKADD_STRUCT20: return "INSTR_ALU_CKADD_STRUCT20";
|
|
case INSTR_ALU_CKADD_STRUCT: return "INSTR_ALU_CKADD_STRUCT";
|
|
case INSTR_ALU_CKSUB_FIELD: return "INSTR_ALU_CKSUB_FIELD";
|
|
|
|
case INSTR_ALU_AND: return "INSTR_ALU_AND";
|
|
case INSTR_ALU_AND_MH: return "INSTR_ALU_AND_MH";
|
|
case INSTR_ALU_AND_HM: return "INSTR_ALU_AND_HM";
|
|
case INSTR_ALU_AND_HH: return "INSTR_ALU_AND_HH";
|
|
case INSTR_ALU_AND_I: return "INSTR_ALU_AND_I";
|
|
|
|
case INSTR_ALU_OR: return "INSTR_ALU_OR";
|
|
case INSTR_ALU_OR_MH: return "INSTR_ALU_OR_MH";
|
|
case INSTR_ALU_OR_HM: return "INSTR_ALU_OR_HM";
|
|
case INSTR_ALU_OR_HH: return "INSTR_ALU_OR_HH";
|
|
case INSTR_ALU_OR_I: return "INSTR_ALU_OR_I";
|
|
|
|
case INSTR_ALU_XOR: return "INSTR_ALU_XOR";
|
|
case INSTR_ALU_XOR_MH: return "INSTR_ALU_XOR_MH";
|
|
case INSTR_ALU_XOR_HM: return "INSTR_ALU_XOR_HM";
|
|
case INSTR_ALU_XOR_HH: return "INSTR_ALU_XOR_HH";
|
|
case INSTR_ALU_XOR_I: return "INSTR_ALU_XOR_I";
|
|
|
|
case INSTR_ALU_SHL: return "INSTR_ALU_SHL";
|
|
case INSTR_ALU_SHL_MH: return "INSTR_ALU_SHL_MH";
|
|
case INSTR_ALU_SHL_HM: return "INSTR_ALU_SHL_HM";
|
|
case INSTR_ALU_SHL_HH: return "INSTR_ALU_SHL_HH";
|
|
case INSTR_ALU_SHL_MI: return "INSTR_ALU_SHL_MI";
|
|
case INSTR_ALU_SHL_HI: return "INSTR_ALU_SHL_HI";
|
|
|
|
case INSTR_ALU_SHR: return "INSTR_ALU_SHR";
|
|
case INSTR_ALU_SHR_MH: return "INSTR_ALU_SHR_MH";
|
|
case INSTR_ALU_SHR_HM: return "INSTR_ALU_SHR_HM";
|
|
case INSTR_ALU_SHR_HH: return "INSTR_ALU_SHR_HH";
|
|
case INSTR_ALU_SHR_MI: return "INSTR_ALU_SHR_MI";
|
|
case INSTR_ALU_SHR_HI: return "INSTR_ALU_SHR_HI";
|
|
|
|
case INSTR_REGPREFETCH_RH: return "INSTR_REGPREFETCH_RH";
|
|
case INSTR_REGPREFETCH_RM: return "INSTR_REGPREFETCH_RM";
|
|
case INSTR_REGPREFETCH_RI: return "INSTR_REGPREFETCH_RI";
|
|
|
|
case INSTR_REGRD_HRH: return "INSTR_REGRD_HRH";
|
|
case INSTR_REGRD_HRM: return "INSTR_REGRD_HRM";
|
|
case INSTR_REGRD_HRI: return "INSTR_REGRD_HRI";
|
|
case INSTR_REGRD_MRH: return "INSTR_REGRD_MRH";
|
|
case INSTR_REGRD_MRM: return "INSTR_REGRD_MRM";
|
|
case INSTR_REGRD_MRI: return "INSTR_REGRD_MRI";
|
|
|
|
case INSTR_REGWR_RHH: return "INSTR_REGWR_RHH";
|
|
case INSTR_REGWR_RHM: return "INSTR_REGWR_RHM";
|
|
case INSTR_REGWR_RHI: return "INSTR_REGWR_RHI";
|
|
case INSTR_REGWR_RMH: return "INSTR_REGWR_RMH";
|
|
case INSTR_REGWR_RMM: return "INSTR_REGWR_RMM";
|
|
case INSTR_REGWR_RMI: return "INSTR_REGWR_RMI";
|
|
case INSTR_REGWR_RIH: return "INSTR_REGWR_RIH";
|
|
case INSTR_REGWR_RIM: return "INSTR_REGWR_RIM";
|
|
case INSTR_REGWR_RII: return "INSTR_REGWR_RII";
|
|
|
|
case INSTR_REGADD_RHH: return "INSTR_REGADD_RHH";
|
|
case INSTR_REGADD_RHM: return "INSTR_REGADD_RHM";
|
|
case INSTR_REGADD_RHI: return "INSTR_REGADD_RHI";
|
|
case INSTR_REGADD_RMH: return "INSTR_REGADD_RMH";
|
|
case INSTR_REGADD_RMM: return "INSTR_REGADD_RMM";
|
|
case INSTR_REGADD_RMI: return "INSTR_REGADD_RMI";
|
|
case INSTR_REGADD_RIH: return "INSTR_REGADD_RIH";
|
|
case INSTR_REGADD_RIM: return "INSTR_REGADD_RIM";
|
|
case INSTR_REGADD_RII: return "INSTR_REGADD_RII";
|
|
|
|
case INSTR_METPREFETCH_H: return "INSTR_METPREFETCH_H";
|
|
case INSTR_METPREFETCH_M: return "INSTR_METPREFETCH_M";
|
|
case INSTR_METPREFETCH_I: return "INSTR_METPREFETCH_I";
|
|
|
|
case INSTR_METER_HHM: return "INSTR_METER_HHM";
|
|
case INSTR_METER_HHI: return "INSTR_METER_HHI";
|
|
case INSTR_METER_HMM: return "INSTR_METER_HMM";
|
|
case INSTR_METER_HMI: return "INSTR_METER_HMI";
|
|
case INSTR_METER_MHM: return "INSTR_METER_MHM";
|
|
case INSTR_METER_MHI: return "INSTR_METER_MHI";
|
|
case INSTR_METER_MMM: return "INSTR_METER_MMM";
|
|
case INSTR_METER_MMI: return "INSTR_METER_MMI";
|
|
case INSTR_METER_IHM: return "INSTR_METER_IHM";
|
|
case INSTR_METER_IHI: return "INSTR_METER_IHI";
|
|
case INSTR_METER_IMM: return "INSTR_METER_IMM";
|
|
case INSTR_METER_IMI: return "INSTR_METER_IMI";
|
|
|
|
case INSTR_TABLE: return "INSTR_TABLE";
|
|
case INSTR_TABLE_AF: return "INSTR_TABLE_AF";
|
|
case INSTR_SELECTOR: return "INSTR_SELECTOR";
|
|
case INSTR_LEARNER: return "INSTR_LEARNER";
|
|
case INSTR_LEARNER_AF: return "INSTR_LEARNER_AF";
|
|
|
|
case INSTR_LEARNER_LEARN: return "INSTR_LEARNER_LEARN";
|
|
case INSTR_LEARNER_REARM: return "INSTR_LEARNER_REARM";
|
|
case INSTR_LEARNER_REARM_NEW: return "INSTR_LEARNER_REARM_NEW";
|
|
case INSTR_LEARNER_FORGET: return "INSTR_LEARNER_FORGET";
|
|
|
|
case INSTR_EXTERN_OBJ: return "INSTR_EXTERN_OBJ";
|
|
case INSTR_EXTERN_FUNC: return "INSTR_EXTERN_FUNC";
|
|
case INSTR_HASH_FUNC: return "INSTR_HASH_FUNC";
|
|
|
|
case INSTR_JMP: return "INSTR_JMP";
|
|
case INSTR_JMP_VALID: return "INSTR_JMP_VALID";
|
|
case INSTR_JMP_INVALID: return "INSTR_JMP_INVALID";
|
|
case INSTR_JMP_HIT: return "INSTR_JMP_HIT";
|
|
case INSTR_JMP_MISS: return "INSTR_JMP_MISS";
|
|
case INSTR_JMP_ACTION_HIT: return "INSTR_JMP_ACTION_HIT";
|
|
case INSTR_JMP_ACTION_MISS: return "INSTR_JMP_ACTION_MISS";
|
|
case INSTR_JMP_EQ: return "INSTR_JMP_EQ";
|
|
case INSTR_JMP_EQ_MH: return "INSTR_JMP_EQ_MH";
|
|
case INSTR_JMP_EQ_HM: return "INSTR_JMP_EQ_HM";
|
|
case INSTR_JMP_EQ_HH: return "INSTR_JMP_EQ_HH";
|
|
case INSTR_JMP_EQ_I: return "INSTR_JMP_EQ_I";
|
|
case INSTR_JMP_NEQ: return "INSTR_JMP_NEQ";
|
|
case INSTR_JMP_NEQ_MH: return "INSTR_JMP_NEQ_MH";
|
|
case INSTR_JMP_NEQ_HM: return "INSTR_JMP_NEQ_HM";
|
|
case INSTR_JMP_NEQ_HH: return "INSTR_JMP_NEQ_HH";
|
|
case INSTR_JMP_NEQ_I: return "INSTR_JMP_NEQ_I";
|
|
case INSTR_JMP_LT: return "INSTR_JMP_LT";
|
|
case INSTR_JMP_LT_MH: return "INSTR_JMP_LT_MH";
|
|
case INSTR_JMP_LT_HM: return "INSTR_JMP_LT_HM";
|
|
case INSTR_JMP_LT_HH: return "INSTR_JMP_LT_HH";
|
|
case INSTR_JMP_LT_MI: return "INSTR_JMP_LT_MI";
|
|
case INSTR_JMP_LT_HI: return "INSTR_JMP_LT_HI";
|
|
case INSTR_JMP_GT: return "INSTR_JMP_GT";
|
|
case INSTR_JMP_GT_MH: return "INSTR_JMP_GT_MH";
|
|
case INSTR_JMP_GT_HM: return "INSTR_JMP_GT_HM";
|
|
case INSTR_JMP_GT_HH: return "INSTR_JMP_GT_HH";
|
|
case INSTR_JMP_GT_MI: return "INSTR_JMP_GT_MI";
|
|
case INSTR_JMP_GT_HI: return "INSTR_JMP_GT_HI";
|
|
|
|
case INSTR_RETURN: return "INSTR_RETURN";
|
|
|
|
default: return "INSTR_UNKNOWN";
|
|
}
|
|
}
|
|
|
|
typedef void
|
|
(*instruction_export_t)(struct instruction *, FILE *);
|
|
|
|
static void
|
|
instr_io_export(struct instruction *instr, FILE *f)
|
|
{
|
|
uint32_t n_io = 0, n_io_imm = 0, n_hdrs = 0, i;
|
|
|
|
/* n_io, n_io_imm, n_hdrs. */
|
|
if (instr->type == INSTR_RX ||
|
|
instr->type == INSTR_TX ||
|
|
instr->type == INSTR_HDR_EXTRACT_M ||
|
|
(instr->type >= INSTR_HDR_EMIT_TX && instr->type <= INSTR_HDR_EMIT8_TX))
|
|
n_io = 1;
|
|
|
|
if (instr->type == INSTR_TX_I)
|
|
n_io_imm = 1;
|
|
|
|
if (instr->type >= INSTR_HDR_EXTRACT && instr->type <= INSTR_HDR_EXTRACT8)
|
|
n_hdrs = 1 + (instr->type - INSTR_HDR_EXTRACT);
|
|
|
|
if (instr->type == INSTR_HDR_EXTRACT_M ||
|
|
instr->type == INSTR_HDR_LOOKAHEAD ||
|
|
instr->type == INSTR_HDR_EMIT)
|
|
n_hdrs = 1;
|
|
|
|
if (instr->type >= INSTR_HDR_EMIT_TX && instr->type <= INSTR_HDR_EMIT8_TX)
|
|
n_hdrs = 1 + (instr->type - INSTR_HDR_EMIT_TX);
|
|
|
|
/* instr. */
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n",
|
|
instr_type_to_name(instr));
|
|
|
|
/* instr.io. */
|
|
if (n_io || n_io_imm || n_hdrs)
|
|
fprintf(f,
|
|
"\t\t.io = {\n");
|
|
|
|
/* instr.io.io. */
|
|
if (n_io)
|
|
fprintf(f,
|
|
"\t\t\t.io = {\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t},\n",
|
|
instr->io.io.offset,
|
|
instr->io.io.n_bits);
|
|
|
|
if (n_io_imm)
|
|
fprintf(f,
|
|
"\t\t\t.io = {\n"
|
|
"\t\t\t\t.val = %u,\n"
|
|
"\t\t\t},\n",
|
|
instr->io.io.val);
|
|
|
|
/* instr.io.hdr. */
|
|
if (n_hdrs) {
|
|
fprintf(f,
|
|
"\t\t.hdr = {\n");
|
|
|
|
/* instr.io.hdr.header_id. */
|
|
fprintf(f,
|
|
"\t\t\t.header_id = {");
|
|
|
|
for (i = 0; i < n_hdrs; i++)
|
|
fprintf(f,
|
|
"%u, ",
|
|
instr->io.hdr.header_id[i]);
|
|
|
|
fprintf(f,
|
|
"},\n");
|
|
|
|
/* instr.io.hdr.struct_id. */
|
|
fprintf(f,
|
|
"\t\t\t.struct_id = {");
|
|
|
|
for (i = 0; i < n_hdrs; i++)
|
|
fprintf(f,
|
|
"%u, ",
|
|
instr->io.hdr.struct_id[i]);
|
|
|
|
fprintf(f,
|
|
"},\n");
|
|
|
|
/* instr.io.hdr.n_bytes. */
|
|
fprintf(f,
|
|
"\t\t\t.n_bytes = {");
|
|
|
|
for (i = 0; i < n_hdrs; i++)
|
|
fprintf(f,
|
|
"%u, ",
|
|
instr->io.hdr.n_bytes[i]);
|
|
|
|
fprintf(f,
|
|
"},\n");
|
|
|
|
/* instr.io.hdr - closing curly brace. */
|
|
fprintf(f,
|
|
"\t\t\t}\n,");
|
|
}
|
|
|
|
/* instr.io - closing curly brace. */
|
|
if (n_io || n_io_imm || n_hdrs)
|
|
fprintf(f,
|
|
"\t\t},\n");
|
|
|
|
/* instr - closing curly brace. */
|
|
fprintf(f,
|
|
"\t},\n");
|
|
}
|
|
|
|
static void
|
|
instr_mirror_export(struct instruction *instr, FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.mirror = {\n"
|
|
"\t\t\t.dst = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t}\n,"
|
|
"\t\t\t.src = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t}\n,"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->mirror.dst.struct_id,
|
|
instr->mirror.dst.n_bits,
|
|
instr->mirror.dst.offset,
|
|
instr->mirror.src.struct_id,
|
|
instr->mirror.src.n_bits,
|
|
instr->mirror.src.offset);
|
|
}
|
|
|
|
static void
|
|
instr_recirculate_export(struct instruction *instr, FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr));
|
|
}
|
|
|
|
static void
|
|
instr_recircid_export(struct instruction *instr, FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.io = {\n"
|
|
"\t\t\t.offset = %u,\n"
|
|
"\t\t\t.n_bits = %u,\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->io.io.offset,
|
|
instr->io.io.n_bits);
|
|
}
|
|
|
|
static void
|
|
instr_hdr_validate_export(struct instruction *instr, FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.valid = {\n"
|
|
"\t\t\t.header_id = %u,\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->valid.header_id);
|
|
}
|
|
|
|
static void
|
|
instr_mov_export(struct instruction *instr, FILE *f)
|
|
{
|
|
if (instr->type != INSTR_MOV_I)
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.mov = {\n"
|
|
"\t\t\t.dst = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t},\n"
|
|
"\t\t\t.src = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t},\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->mov.dst.struct_id,
|
|
instr->mov.dst.n_bits,
|
|
instr->mov.dst.offset,
|
|
instr->mov.src.struct_id,
|
|
instr->mov.src.n_bits,
|
|
instr->mov.src.offset);
|
|
else
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.mov = {\n"
|
|
"\t\t\t.dst = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t}\n,"
|
|
"\t\t\t.src_val = %" PRIu64 ",\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->mov.dst.struct_id,
|
|
instr->mov.dst.n_bits,
|
|
instr->mov.dst.offset,
|
|
instr->mov.src_val);
|
|
}
|
|
|
|
static void
|
|
instr_dma_ht_export(struct instruction *instr, FILE *f)
|
|
{
|
|
uint32_t n_dma = 0, i;
|
|
|
|
/* n_dma. */
|
|
n_dma = 1 + (instr->type - INSTR_DMA_HT);
|
|
|
|
/* instr. */
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n",
|
|
instr_type_to_name(instr));
|
|
|
|
/* instr.dma. */
|
|
fprintf(f,
|
|
"\t\t.dma = {\n");
|
|
|
|
/* instr.dma.dst. */
|
|
fprintf(f,
|
|
"\t\t\t.dst = {\n");
|
|
|
|
/* instr.dma.dst.header_id. */
|
|
fprintf(f,
|
|
"\t\t\t\t.header_id = {");
|
|
|
|
for (i = 0; i < n_dma; i++)
|
|
fprintf(f,
|
|
"%u, ",
|
|
instr->dma.dst.header_id[i]);
|
|
|
|
fprintf(f,
|
|
"},\n");
|
|
|
|
/* instr.dma.dst.struct_id. */
|
|
fprintf(f,
|
|
"\t\t\t\t.struct_id = {");
|
|
|
|
for (i = 0; i < n_dma; i++)
|
|
fprintf(f,
|
|
"%u, ",
|
|
instr->dma.dst.struct_id[i]);
|
|
|
|
fprintf(f,
|
|
"},\n");
|
|
|
|
/* instr.dma.dst - closing curly brace. */
|
|
fprintf(f,
|
|
"\t\t\t},\n");
|
|
|
|
/* instr.dma.src. */
|
|
fprintf(f,
|
|
"\t\t\t.src = {\n");
|
|
|
|
/* instr.dma.src.offset. */
|
|
fprintf(f,
|
|
"\t\t\t\t.offset = {");
|
|
|
|
for (i = 0; i < n_dma; i++)
|
|
fprintf(f,
|
|
"%u, ",
|
|
instr->dma.src.offset[i]);
|
|
|
|
fprintf(f,
|
|
"},\n");
|
|
|
|
/* instr.dma.src - closing curly brace. */
|
|
fprintf(f,
|
|
"\t\t\t},\n");
|
|
|
|
/* instr.dma.n_bytes. */
|
|
fprintf(f,
|
|
"\t\t\t.n_bytes = {");
|
|
|
|
for (i = 0; i < n_dma; i++)
|
|
fprintf(f,
|
|
"%u, ",
|
|
instr->dma.n_bytes[i]);
|
|
|
|
fprintf(f,
|
|
"},\n");
|
|
|
|
/* instr.dma - closing curly brace. */
|
|
fprintf(f,
|
|
"\t\t},\n");
|
|
|
|
/* instr - closing curly brace. */
|
|
fprintf(f,
|
|
"\t},\n");
|
|
}
|
|
|
|
static void
|
|
instr_alu_export(struct instruction *instr, FILE *f)
|
|
{
|
|
int imm = 0;
|
|
|
|
if (instr->type == INSTR_ALU_ADD_MI ||
|
|
instr->type == INSTR_ALU_ADD_HI ||
|
|
instr->type == INSTR_ALU_SUB_MI ||
|
|
instr->type == INSTR_ALU_SUB_HI ||
|
|
instr->type == INSTR_ALU_SHL_MI ||
|
|
instr->type == INSTR_ALU_SHL_HI ||
|
|
instr->type == INSTR_ALU_SHR_MI ||
|
|
instr->type == INSTR_ALU_SHR_HI ||
|
|
instr->type == INSTR_ALU_AND_I ||
|
|
instr->type == INSTR_ALU_OR_I ||
|
|
instr->type == INSTR_ALU_XOR_I)
|
|
imm = 1;
|
|
|
|
if (!imm)
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.alu = {\n"
|
|
"\t\t\t.dst = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t},\n"
|
|
"\t\t\t.src = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t},\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->alu.dst.struct_id,
|
|
instr->alu.dst.n_bits,
|
|
instr->alu.dst.offset,
|
|
instr->alu.src.struct_id,
|
|
instr->alu.src.n_bits,
|
|
instr->alu.src.offset);
|
|
else
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.alu = {\n"
|
|
"\t\t\t.dst = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t}\n,"
|
|
"\t\t\t.src_val = %" PRIu64 ",\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->alu.dst.struct_id,
|
|
instr->alu.dst.n_bits,
|
|
instr->alu.dst.offset,
|
|
instr->alu.src_val);
|
|
}
|
|
|
|
static void
|
|
instr_hash_export(struct instruction *instr, FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.hash_func = {\n"
|
|
"\t\t\t.hash_func_id = %u,\n"
|
|
"\t\t\t.dst = {\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t},\n"
|
|
"\t\t\t.src = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t\t.n_bytes = %u,\n"
|
|
"\t\t\t},\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->hash_func.hash_func_id,
|
|
instr->hash_func.dst.offset,
|
|
instr->hash_func.dst.n_bits,
|
|
instr->hash_func.src.struct_id,
|
|
instr->hash_func.src.offset,
|
|
instr->hash_func.src.n_bytes);
|
|
}
|
|
|
|
static void
|
|
instr_reg_export(struct instruction *instr __rte_unused, FILE *f __rte_unused)
|
|
{
|
|
int prefetch = 0, idx_imm = 0, src_imm = 0;
|
|
|
|
if (instr->type == INSTR_REGPREFETCH_RH ||
|
|
instr->type == INSTR_REGPREFETCH_RM ||
|
|
instr->type == INSTR_REGPREFETCH_RI)
|
|
prefetch = 1;
|
|
|
|
/* index is the 3rd operand for the regrd instruction and the 2nd
|
|
* operand for the regwr and regadd instructions.
|
|
*/
|
|
if (instr->type == INSTR_REGPREFETCH_RI ||
|
|
instr->type == INSTR_REGRD_HRI ||
|
|
instr->type == INSTR_REGRD_MRI ||
|
|
instr->type == INSTR_REGWR_RIH ||
|
|
instr->type == INSTR_REGWR_RIM ||
|
|
instr->type == INSTR_REGWR_RII ||
|
|
instr->type == INSTR_REGADD_RIH ||
|
|
instr->type == INSTR_REGADD_RIM ||
|
|
instr->type == INSTR_REGADD_RII)
|
|
idx_imm = 1;
|
|
|
|
/* src is the 3rd operand for the regwr and regadd instructions. */
|
|
if (instr->type == INSTR_REGWR_RHI ||
|
|
instr->type == INSTR_REGWR_RMI ||
|
|
instr->type == INSTR_REGWR_RII ||
|
|
instr->type == INSTR_REGADD_RHI ||
|
|
instr->type == INSTR_REGADD_RMI ||
|
|
instr->type == INSTR_REGADD_RII)
|
|
src_imm = 1;
|
|
|
|
/* instr.regarray.regarray_id. */
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.regarray = {\n"
|
|
"\t\t\t.regarray_id = %u,\n",
|
|
instr_type_to_name(instr),
|
|
instr->regarray.regarray_id);
|
|
|
|
/* instr.regarray.idx / instr.regarray.idx_val. */
|
|
if (!idx_imm)
|
|
fprintf(f,
|
|
"\t\t\t\t.idx = {\n"
|
|
"\t\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t\t},\n",
|
|
instr->regarray.idx.struct_id,
|
|
instr->regarray.idx.n_bits,
|
|
instr->regarray.idx.offset);
|
|
else
|
|
fprintf(f,
|
|
"\t\t\t\t.idx_val = %u,\n",
|
|
instr->regarray.idx_val);
|
|
|
|
/* instr.regarray.dstsrc / instr.regarray.dstsrc_val. */
|
|
if (!prefetch) {
|
|
if (!src_imm)
|
|
fprintf(f,
|
|
"\t\t\t\t.dstsrc = {\n"
|
|
"\t\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t\t},\n",
|
|
instr->regarray.dstsrc.struct_id,
|
|
instr->regarray.dstsrc.n_bits,
|
|
instr->regarray.dstsrc.offset);
|
|
else
|
|
fprintf(f,
|
|
"\t\t\t\t.dstsrc_val = %" PRIu64 ",\n",
|
|
instr->regarray.dstsrc_val);
|
|
}
|
|
|
|
/* instr.regarray and instr - closing curly braces. */
|
|
fprintf(f,
|
|
"\t\t},\n"
|
|
"\t},\n");
|
|
}
|
|
|
|
static void
|
|
instr_meter_export(struct instruction *instr __rte_unused, FILE *f __rte_unused)
|
|
{
|
|
int prefetch = 0, idx_imm = 0, color_in_imm = 0;
|
|
|
|
if (instr->type == INSTR_METPREFETCH_H ||
|
|
instr->type == INSTR_METPREFETCH_M ||
|
|
instr->type == INSTR_METPREFETCH_I)
|
|
prefetch = 1;
|
|
|
|
/* idx_imm. */
|
|
if (instr->type == INSTR_METPREFETCH_I ||
|
|
instr->type == INSTR_METER_IHM ||
|
|
instr->type == INSTR_METER_IHI ||
|
|
instr->type == INSTR_METER_IMM ||
|
|
instr->type == INSTR_METER_IMI)
|
|
idx_imm = 1;
|
|
|
|
/* color_in_imm. */
|
|
if (instr->type == INSTR_METER_HHI ||
|
|
instr->type == INSTR_METER_HMI ||
|
|
instr->type == INSTR_METER_MHI ||
|
|
instr->type == INSTR_METER_MMI ||
|
|
instr->type == INSTR_METER_IHI ||
|
|
instr->type == INSTR_METER_IMI)
|
|
color_in_imm = 1;
|
|
|
|
/* instr.meter.metarray_id. */
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.meter = {\n"
|
|
"\t\t\t.metarray_id = %u,\n",
|
|
instr_type_to_name(instr),
|
|
instr->meter.metarray_id);
|
|
|
|
/* instr.meter.idx / instr.meter.idx_val. */
|
|
if (!idx_imm)
|
|
fprintf(f,
|
|
"\t\t\t.idx = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t},\n",
|
|
instr->meter.idx.struct_id,
|
|
instr->meter.idx.n_bits,
|
|
instr->meter.idx.offset);
|
|
else
|
|
fprintf(f,
|
|
"\t\t\t.idx_val = %u,\n",
|
|
instr->meter.idx_val);
|
|
|
|
if (!prefetch) {
|
|
/* instr.meter.length. */
|
|
fprintf(f,
|
|
"\t\t\t.length = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t},\n",
|
|
instr->meter.length.struct_id,
|
|
instr->meter.length.n_bits,
|
|
instr->meter.length.offset);
|
|
|
|
/* instr.meter.color_in / instr.meter.color_in_val. */
|
|
if (!color_in_imm)
|
|
fprintf(f,
|
|
"\t\t\t.color_in = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t},\n",
|
|
instr->meter.color_in.struct_id,
|
|
instr->meter.color_in.n_bits,
|
|
instr->meter.color_in.offset);
|
|
else
|
|
fprintf(f,
|
|
"\t\t\t.color_in_val = %u,\n",
|
|
(uint32_t)instr->meter.color_in_val);
|
|
|
|
/* instr.meter.color_out. */
|
|
fprintf(f,
|
|
"\t\t\t.color_out = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t},\n",
|
|
instr->meter.color_out.struct_id,
|
|
instr->meter.color_out.n_bits,
|
|
instr->meter.color_out.offset);
|
|
}
|
|
|
|
/* instr.meter and instr - closing curly braces. */
|
|
fprintf(f,
|
|
"\t\t},\n"
|
|
"\t},\n");
|
|
}
|
|
|
|
static void
|
|
instr_table_export(struct instruction *instr,
|
|
FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.table = {\n"
|
|
"\t\t\t.table_id = %u,\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->table.table_id);
|
|
}
|
|
|
|
static void
|
|
instr_learn_export(struct instruction *instr, FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.learn = {\n"
|
|
"\t\t\t.action_id = %u,\n"
|
|
"\t\t\t.mf_first_arg_offset = %u,\n"
|
|
"\t\t\t.mf_timeout_id_offset = %u,\n"
|
|
"\t\t\t.mf_timeout_id_n_bits = %u,\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->learn.action_id,
|
|
instr->learn.mf_first_arg_offset,
|
|
instr->learn.mf_timeout_id_offset,
|
|
instr->learn.mf_timeout_id_n_bits);
|
|
}
|
|
|
|
static void
|
|
instr_rearm_export(struct instruction *instr, FILE *f)
|
|
{
|
|
if (instr->type == INSTR_LEARNER_REARM)
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr));
|
|
else
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.learn = {\n"
|
|
"\t\t\t.mf_timeout_id_offset = %u,\n"
|
|
"\t\t\t.mf_timeout_id_n_bits = %u,\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->learn.mf_timeout_id_offset,
|
|
instr->learn.mf_timeout_id_n_bits);
|
|
}
|
|
|
|
static void
|
|
instr_forget_export(struct instruction *instr, FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr));
|
|
}
|
|
|
|
static void
|
|
instr_extern_export(struct instruction *instr, FILE *f)
|
|
{
|
|
if (instr->type == INSTR_EXTERN_OBJ)
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.ext_obj = {\n"
|
|
"\t\t\t.ext_obj_id = %u,\n"
|
|
"\t\t\t.func_id = %u,\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->ext_obj.ext_obj_id,
|
|
instr->ext_obj.func_id);
|
|
else
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.ext_func = {\n"
|
|
"\t\t\t.ext_func_id = %u,\n"
|
|
"\t\t},\n"
|
|
"\t},\n",
|
|
instr_type_to_name(instr),
|
|
instr->ext_func.ext_func_id);
|
|
}
|
|
|
|
static void
|
|
instr_jmp_export(struct instruction *instr, FILE *f __rte_unused)
|
|
{
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n"
|
|
"\t\t.jmp = {\n"
|
|
"\t\t\t.ip = NULL,\n",
|
|
instr_type_to_name(instr));
|
|
|
|
switch (instr->type) {
|
|
case INSTR_JMP_VALID:
|
|
case INSTR_JMP_INVALID:
|
|
fprintf(f,
|
|
"\t\t\t.header_id = %u,\n",
|
|
instr->jmp.header_id);
|
|
break;
|
|
|
|
case INSTR_JMP_ACTION_HIT:
|
|
case INSTR_JMP_ACTION_MISS:
|
|
fprintf(f,
|
|
"\t\t\t.action_id = %u,\n",
|
|
instr->jmp.action_id);
|
|
break;
|
|
|
|
case INSTR_JMP_EQ:
|
|
case INSTR_JMP_EQ_MH:
|
|
case INSTR_JMP_EQ_HM:
|
|
case INSTR_JMP_EQ_HH:
|
|
case INSTR_JMP_NEQ:
|
|
case INSTR_JMP_NEQ_MH:
|
|
case INSTR_JMP_NEQ_HM:
|
|
case INSTR_JMP_NEQ_HH:
|
|
case INSTR_JMP_LT:
|
|
case INSTR_JMP_LT_MH:
|
|
case INSTR_JMP_LT_HM:
|
|
case INSTR_JMP_LT_HH:
|
|
case INSTR_JMP_GT:
|
|
case INSTR_JMP_GT_MH:
|
|
case INSTR_JMP_GT_HM:
|
|
case INSTR_JMP_GT_HH:
|
|
fprintf(f,
|
|
"\t\t\t.a = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t},\n"
|
|
"\t\t\t.b = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t},\n",
|
|
instr->jmp.a.struct_id,
|
|
instr->jmp.a.n_bits,
|
|
instr->jmp.a.offset,
|
|
instr->jmp.b.struct_id,
|
|
instr->jmp.b.n_bits,
|
|
instr->jmp.b.offset);
|
|
break;
|
|
|
|
case INSTR_JMP_EQ_I:
|
|
case INSTR_JMP_NEQ_I:
|
|
case INSTR_JMP_LT_MI:
|
|
case INSTR_JMP_LT_HI:
|
|
case INSTR_JMP_GT_MI:
|
|
case INSTR_JMP_GT_HI:
|
|
fprintf(f,
|
|
"\t\t\t.a = {\n"
|
|
"\t\t\t\t.struct_id = %u,\n"
|
|
"\t\t\t\t.n_bits = %u,\n"
|
|
"\t\t\t\t.offset = %u,\n"
|
|
"\t\t\t}\n,"
|
|
"\t\t\t.b_val = %" PRIu64 ",\n",
|
|
instr->jmp.a.struct_id,
|
|
instr->jmp.a.n_bits,
|
|
instr->jmp.a.offset,
|
|
instr->jmp.b_val);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
fprintf(f,
|
|
"\t\t},\n"
|
|
"\t},\n");
|
|
}
|
|
|
|
static void
|
|
instr_return_export(struct instruction *instr,
|
|
FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"\t{\n"
|
|
"\t\t.type = %s,\n",
|
|
instr_type_to_name(instr));
|
|
|
|
fprintf(f,
|
|
"\t},\n");
|
|
}
|
|
|
|
static instruction_export_t export_table[] = {
|
|
[INSTR_RX] = instr_io_export,
|
|
|
|
[INSTR_TX] = instr_io_export,
|
|
[INSTR_TX_I] = instr_io_export,
|
|
[INSTR_DROP] = instr_io_export,
|
|
[INSTR_MIRROR] = instr_mirror_export,
|
|
[INSTR_RECIRCULATE] = instr_recirculate_export,
|
|
[INSTR_RECIRCID] = instr_recircid_export,
|
|
|
|
[INSTR_HDR_EXTRACT] = instr_io_export,
|
|
[INSTR_HDR_EXTRACT2] = instr_io_export,
|
|
[INSTR_HDR_EXTRACT3] = instr_io_export,
|
|
[INSTR_HDR_EXTRACT4] = instr_io_export,
|
|
[INSTR_HDR_EXTRACT5] = instr_io_export,
|
|
[INSTR_HDR_EXTRACT6] = instr_io_export,
|
|
[INSTR_HDR_EXTRACT7] = instr_io_export,
|
|
[INSTR_HDR_EXTRACT8] = instr_io_export,
|
|
|
|
[INSTR_HDR_EXTRACT_M] = instr_io_export,
|
|
|
|
[INSTR_HDR_LOOKAHEAD] = instr_io_export,
|
|
|
|
[INSTR_HDR_EMIT] = instr_io_export,
|
|
[INSTR_HDR_EMIT_TX] = instr_io_export,
|
|
[INSTR_HDR_EMIT2_TX] = instr_io_export,
|
|
[INSTR_HDR_EMIT3_TX] = instr_io_export,
|
|
[INSTR_HDR_EMIT4_TX] = instr_io_export,
|
|
[INSTR_HDR_EMIT5_TX] = instr_io_export,
|
|
[INSTR_HDR_EMIT6_TX] = instr_io_export,
|
|
[INSTR_HDR_EMIT7_TX] = instr_io_export,
|
|
[INSTR_HDR_EMIT8_TX] = instr_io_export,
|
|
|
|
[INSTR_HDR_VALIDATE] = instr_hdr_validate_export,
|
|
[INSTR_HDR_INVALIDATE] = instr_hdr_validate_export,
|
|
|
|
[INSTR_MOV] = instr_mov_export,
|
|
[INSTR_MOV_MH] = instr_mov_export,
|
|
[INSTR_MOV_HM] = instr_mov_export,
|
|
[INSTR_MOV_HH] = instr_mov_export,
|
|
[INSTR_MOV_I] = instr_mov_export,
|
|
|
|
[INSTR_DMA_HT] = instr_dma_ht_export,
|
|
[INSTR_DMA_HT2] = instr_dma_ht_export,
|
|
[INSTR_DMA_HT3] = instr_dma_ht_export,
|
|
[INSTR_DMA_HT4] = instr_dma_ht_export,
|
|
[INSTR_DMA_HT5] = instr_dma_ht_export,
|
|
[INSTR_DMA_HT6] = instr_dma_ht_export,
|
|
[INSTR_DMA_HT7] = instr_dma_ht_export,
|
|
[INSTR_DMA_HT8] = instr_dma_ht_export,
|
|
|
|
[INSTR_ALU_ADD] = instr_alu_export,
|
|
[INSTR_ALU_ADD_MH] = instr_alu_export,
|
|
[INSTR_ALU_ADD_HM] = instr_alu_export,
|
|
[INSTR_ALU_ADD_HH] = instr_alu_export,
|
|
[INSTR_ALU_ADD_MI] = instr_alu_export,
|
|
[INSTR_ALU_ADD_HI] = instr_alu_export,
|
|
|
|
[INSTR_ALU_SUB] = instr_alu_export,
|
|
[INSTR_ALU_SUB_MH] = instr_alu_export,
|
|
[INSTR_ALU_SUB_HM] = instr_alu_export,
|
|
[INSTR_ALU_SUB_HH] = instr_alu_export,
|
|
[INSTR_ALU_SUB_MI] = instr_alu_export,
|
|
[INSTR_ALU_SUB_HI] = instr_alu_export,
|
|
|
|
[INSTR_ALU_CKADD_FIELD] = instr_alu_export,
|
|
[INSTR_ALU_CKADD_STRUCT] = instr_alu_export,
|
|
[INSTR_ALU_CKADD_STRUCT20] = instr_alu_export,
|
|
[INSTR_ALU_CKSUB_FIELD] = instr_alu_export,
|
|
|
|
[INSTR_ALU_AND] = instr_alu_export,
|
|
[INSTR_ALU_AND_MH] = instr_alu_export,
|
|
[INSTR_ALU_AND_HM] = instr_alu_export,
|
|
[INSTR_ALU_AND_HH] = instr_alu_export,
|
|
[INSTR_ALU_AND_I] = instr_alu_export,
|
|
|
|
[INSTR_ALU_OR] = instr_alu_export,
|
|
[INSTR_ALU_OR_MH] = instr_alu_export,
|
|
[INSTR_ALU_OR_HM] = instr_alu_export,
|
|
[INSTR_ALU_OR_HH] = instr_alu_export,
|
|
[INSTR_ALU_OR_I] = instr_alu_export,
|
|
|
|
[INSTR_ALU_XOR] = instr_alu_export,
|
|
[INSTR_ALU_XOR_MH] = instr_alu_export,
|
|
[INSTR_ALU_XOR_HM] = instr_alu_export,
|
|
[INSTR_ALU_XOR_HH] = instr_alu_export,
|
|
[INSTR_ALU_XOR_I] = instr_alu_export,
|
|
|
|
[INSTR_ALU_SHL] = instr_alu_export,
|
|
[INSTR_ALU_SHL_MH] = instr_alu_export,
|
|
[INSTR_ALU_SHL_HM] = instr_alu_export,
|
|
[INSTR_ALU_SHL_HH] = instr_alu_export,
|
|
[INSTR_ALU_SHL_MI] = instr_alu_export,
|
|
[INSTR_ALU_SHL_HI] = instr_alu_export,
|
|
|
|
[INSTR_ALU_SHR] = instr_alu_export,
|
|
[INSTR_ALU_SHR_MH] = instr_alu_export,
|
|
[INSTR_ALU_SHR_HM] = instr_alu_export,
|
|
[INSTR_ALU_SHR_HH] = instr_alu_export,
|
|
[INSTR_ALU_SHR_MI] = instr_alu_export,
|
|
[INSTR_ALU_SHR_HI] = instr_alu_export,
|
|
|
|
[INSTR_REGPREFETCH_RH] = instr_reg_export,
|
|
[INSTR_REGPREFETCH_RM] = instr_reg_export,
|
|
[INSTR_REGPREFETCH_RI] = instr_reg_export,
|
|
|
|
[INSTR_REGRD_HRH] = instr_reg_export,
|
|
[INSTR_REGRD_HRM] = instr_reg_export,
|
|
[INSTR_REGRD_MRH] = instr_reg_export,
|
|
[INSTR_REGRD_MRM] = instr_reg_export,
|
|
[INSTR_REGRD_HRI] = instr_reg_export,
|
|
[INSTR_REGRD_MRI] = instr_reg_export,
|
|
|
|
[INSTR_REGWR_RHH] = instr_reg_export,
|
|
[INSTR_REGWR_RHM] = instr_reg_export,
|
|
[INSTR_REGWR_RMH] = instr_reg_export,
|
|
[INSTR_REGWR_RMM] = instr_reg_export,
|
|
[INSTR_REGWR_RHI] = instr_reg_export,
|
|
[INSTR_REGWR_RMI] = instr_reg_export,
|
|
[INSTR_REGWR_RIH] = instr_reg_export,
|
|
[INSTR_REGWR_RIM] = instr_reg_export,
|
|
[INSTR_REGWR_RII] = instr_reg_export,
|
|
|
|
[INSTR_REGADD_RHH] = instr_reg_export,
|
|
[INSTR_REGADD_RHM] = instr_reg_export,
|
|
[INSTR_REGADD_RMH] = instr_reg_export,
|
|
[INSTR_REGADD_RMM] = instr_reg_export,
|
|
[INSTR_REGADD_RHI] = instr_reg_export,
|
|
[INSTR_REGADD_RMI] = instr_reg_export,
|
|
[INSTR_REGADD_RIH] = instr_reg_export,
|
|
[INSTR_REGADD_RIM] = instr_reg_export,
|
|
[INSTR_REGADD_RII] = instr_reg_export,
|
|
|
|
[INSTR_METPREFETCH_H] = instr_meter_export,
|
|
[INSTR_METPREFETCH_M] = instr_meter_export,
|
|
[INSTR_METPREFETCH_I] = instr_meter_export,
|
|
|
|
[INSTR_METER_HHM] = instr_meter_export,
|
|
[INSTR_METER_HHI] = instr_meter_export,
|
|
[INSTR_METER_HMM] = instr_meter_export,
|
|
[INSTR_METER_HMI] = instr_meter_export,
|
|
[INSTR_METER_MHM] = instr_meter_export,
|
|
[INSTR_METER_MHI] = instr_meter_export,
|
|
[INSTR_METER_MMM] = instr_meter_export,
|
|
[INSTR_METER_MMI] = instr_meter_export,
|
|
[INSTR_METER_IHM] = instr_meter_export,
|
|
[INSTR_METER_IHI] = instr_meter_export,
|
|
[INSTR_METER_IMM] = instr_meter_export,
|
|
[INSTR_METER_IMI] = instr_meter_export,
|
|
|
|
[INSTR_TABLE] = instr_table_export,
|
|
[INSTR_TABLE_AF] = instr_table_export,
|
|
[INSTR_SELECTOR] = instr_table_export,
|
|
[INSTR_LEARNER] = instr_table_export,
|
|
[INSTR_LEARNER_AF] = instr_table_export,
|
|
|
|
[INSTR_LEARNER_LEARN] = instr_learn_export,
|
|
[INSTR_LEARNER_REARM] = instr_rearm_export,
|
|
[INSTR_LEARNER_REARM_NEW] = instr_rearm_export,
|
|
[INSTR_LEARNER_FORGET] = instr_forget_export,
|
|
|
|
[INSTR_EXTERN_OBJ] = instr_extern_export,
|
|
[INSTR_EXTERN_FUNC] = instr_extern_export,
|
|
[INSTR_HASH_FUNC] = instr_hash_export,
|
|
|
|
[INSTR_JMP] = instr_jmp_export,
|
|
[INSTR_JMP_VALID] = instr_jmp_export,
|
|
[INSTR_JMP_INVALID] = instr_jmp_export,
|
|
[INSTR_JMP_HIT] = instr_jmp_export,
|
|
[INSTR_JMP_MISS] = instr_jmp_export,
|
|
[INSTR_JMP_ACTION_HIT] = instr_jmp_export,
|
|
[INSTR_JMP_ACTION_MISS] = instr_jmp_export,
|
|
|
|
[INSTR_JMP_EQ] = instr_jmp_export,
|
|
[INSTR_JMP_EQ_MH] = instr_jmp_export,
|
|
[INSTR_JMP_EQ_HM] = instr_jmp_export,
|
|
[INSTR_JMP_EQ_HH] = instr_jmp_export,
|
|
[INSTR_JMP_EQ_I] = instr_jmp_export,
|
|
|
|
[INSTR_JMP_NEQ] = instr_jmp_export,
|
|
[INSTR_JMP_NEQ_MH] = instr_jmp_export,
|
|
[INSTR_JMP_NEQ_HM] = instr_jmp_export,
|
|
[INSTR_JMP_NEQ_HH] = instr_jmp_export,
|
|
[INSTR_JMP_NEQ_I] = instr_jmp_export,
|
|
|
|
[INSTR_JMP_LT] = instr_jmp_export,
|
|
[INSTR_JMP_LT_MH] = instr_jmp_export,
|
|
[INSTR_JMP_LT_HM] = instr_jmp_export,
|
|
[INSTR_JMP_LT_HH] = instr_jmp_export,
|
|
[INSTR_JMP_LT_MI] = instr_jmp_export,
|
|
[INSTR_JMP_LT_HI] = instr_jmp_export,
|
|
|
|
[INSTR_JMP_GT] = instr_jmp_export,
|
|
[INSTR_JMP_GT_MH] = instr_jmp_export,
|
|
[INSTR_JMP_GT_HM] = instr_jmp_export,
|
|
[INSTR_JMP_GT_HH] = instr_jmp_export,
|
|
[INSTR_JMP_GT_MI] = instr_jmp_export,
|
|
[INSTR_JMP_GT_HI] = instr_jmp_export,
|
|
|
|
[INSTR_RETURN] = instr_return_export,
|
|
};
|
|
|
|
static void
|
|
action_data_codegen(struct action *a, FILE *f)
|
|
{
|
|
uint32_t i;
|
|
|
|
fprintf(f,
|
|
"static const struct instruction action_%s_instructions[] = {\n",
|
|
a->name);
|
|
|
|
for (i = 0; i < a->n_instructions; i++) {
|
|
struct instruction *instr = &a->instructions[i];
|
|
instruction_export_t func = export_table[instr->type];
|
|
|
|
func(instr, f);
|
|
}
|
|
|
|
fprintf(f, "};\n");
|
|
}
|
|
|
|
static const char *
|
|
instr_type_to_func(struct instruction *instr)
|
|
{
|
|
switch (instr->type) {
|
|
case INSTR_RX: return NULL;
|
|
|
|
case INSTR_TX: return "__instr_tx_exec";
|
|
case INSTR_TX_I: return "__instr_tx_i_exec";
|
|
case INSTR_DROP: return "__instr_drop_exec";
|
|
case INSTR_MIRROR: return "__instr_mirror_exec";
|
|
case INSTR_RECIRCULATE: return "__instr_recirculate_exec";
|
|
case INSTR_RECIRCID: return "__instr_recircid_exec";
|
|
|
|
case INSTR_HDR_EXTRACT: return "__instr_hdr_extract_exec";
|
|
case INSTR_HDR_EXTRACT2: return "__instr_hdr_extract2_exec";
|
|
case INSTR_HDR_EXTRACT3: return "__instr_hdr_extract3_exec";
|
|
case INSTR_HDR_EXTRACT4: return "__instr_hdr_extract4_exec";
|
|
case INSTR_HDR_EXTRACT5: return "__instr_hdr_extract5_exec";
|
|
case INSTR_HDR_EXTRACT6: return "__instr_hdr_extract6_exec";
|
|
case INSTR_HDR_EXTRACT7: return "__instr_hdr_extract7_exec";
|
|
case INSTR_HDR_EXTRACT8: return "__instr_hdr_extract8_exec";
|
|
|
|
case INSTR_HDR_EXTRACT_M: return "__instr_hdr_extract_m_exec";
|
|
|
|
case INSTR_HDR_LOOKAHEAD: return "__instr_hdr_lookahead_exec";
|
|
|
|
case INSTR_HDR_EMIT: return "__instr_hdr_emit_exec";
|
|
case INSTR_HDR_EMIT_TX: return "__instr_hdr_emit_tx_exec";
|
|
case INSTR_HDR_EMIT2_TX: return "__instr_hdr_emit2_tx_exec";
|
|
case INSTR_HDR_EMIT3_TX: return "__instr_hdr_emit3_tx_exec";
|
|
case INSTR_HDR_EMIT4_TX: return "__instr_hdr_emit4_tx_exec";
|
|
case INSTR_HDR_EMIT5_TX: return "__instr_hdr_emit5_tx_exec";
|
|
case INSTR_HDR_EMIT6_TX: return "__instr_hdr_emit6_tx_exec";
|
|
case INSTR_HDR_EMIT7_TX: return "__instr_hdr_emit7_tx_exec";
|
|
case INSTR_HDR_EMIT8_TX: return "__instr_hdr_emit8_tx_exec";
|
|
|
|
case INSTR_HDR_VALIDATE: return "__instr_hdr_validate_exec";
|
|
case INSTR_HDR_INVALIDATE: return "__instr_hdr_invalidate_exec";
|
|
|
|
case INSTR_MOV: return "__instr_mov_exec";
|
|
case INSTR_MOV_MH: return "__instr_mov_mh_exec";
|
|
case INSTR_MOV_HM: return "__instr_mov_hm_exec";
|
|
case INSTR_MOV_HH: return "__instr_mov_hh_exec";
|
|
case INSTR_MOV_I: return "__instr_mov_i_exec";
|
|
|
|
case INSTR_DMA_HT: return "__instr_dma_ht_exec";
|
|
case INSTR_DMA_HT2: return "__instr_dma_ht2_exec";
|
|
case INSTR_DMA_HT3: return "__instr_dma_ht3_exec";
|
|
case INSTR_DMA_HT4: return "__instr_dma_ht4_exec";
|
|
case INSTR_DMA_HT5: return "__instr_dma_ht5_exec";
|
|
case INSTR_DMA_HT6: return "__instr_dma_ht6_exec";
|
|
case INSTR_DMA_HT7: return "__instr_dma_ht7_exec";
|
|
case INSTR_DMA_HT8: return "__instr_dma_ht8_exec";
|
|
|
|
case INSTR_ALU_ADD: return "__instr_alu_add_exec";
|
|
case INSTR_ALU_ADD_MH: return "__instr_alu_add_mh_exec";
|
|
case INSTR_ALU_ADD_HM: return "__instr_alu_add_hm_exec";
|
|
case INSTR_ALU_ADD_HH: return "__instr_alu_add_hh_exec";
|
|
case INSTR_ALU_ADD_MI: return "__instr_alu_add_mi_exec";
|
|
case INSTR_ALU_ADD_HI: return "__instr_alu_add_hi_exec";
|
|
|
|
case INSTR_ALU_SUB: return "__instr_alu_sub_exec";
|
|
case INSTR_ALU_SUB_MH: return "__instr_alu_sub_mh_exec";
|
|
case INSTR_ALU_SUB_HM: return "__instr_alu_sub_hm_exec";
|
|
case INSTR_ALU_SUB_HH: return "__instr_alu_sub_hh_exec";
|
|
case INSTR_ALU_SUB_MI: return "__instr_alu_sub_mi_exec";
|
|
case INSTR_ALU_SUB_HI: return "__instr_alu_sub_hi_exec";
|
|
|
|
case INSTR_ALU_CKADD_FIELD: return "__instr_alu_ckadd_field_exec";
|
|
case INSTR_ALU_CKADD_STRUCT20: return "__instr_alu_ckadd_struct20_exec";
|
|
case INSTR_ALU_CKADD_STRUCT: return "__instr_alu_ckadd_struct_exec";
|
|
case INSTR_ALU_CKSUB_FIELD: return "__instr_alu_cksub_field_exec";
|
|
|
|
case INSTR_ALU_AND: return "__instr_alu_and_exec";
|
|
case INSTR_ALU_AND_MH: return "__instr_alu_and_mh_exec";
|
|
case INSTR_ALU_AND_HM: return "__instr_alu_and_hm_exec";
|
|
case INSTR_ALU_AND_HH: return "__instr_alu_and_hh_exec";
|
|
case INSTR_ALU_AND_I: return "__instr_alu_and_i_exec";
|
|
|
|
case INSTR_ALU_OR: return "__instr_alu_or_exec";
|
|
case INSTR_ALU_OR_MH: return "__instr_alu_or_mh_exec";
|
|
case INSTR_ALU_OR_HM: return "__instr_alu_or_hm_exec";
|
|
case INSTR_ALU_OR_HH: return "__instr_alu_or_hh_exec";
|
|
case INSTR_ALU_OR_I: return "__instr_alu_or_i_exec";
|
|
|
|
case INSTR_ALU_XOR: return "__instr_alu_xor_exec";
|
|
case INSTR_ALU_XOR_MH: return "__instr_alu_xor_mh_exec";
|
|
case INSTR_ALU_XOR_HM: return "__instr_alu_xor_hm_exec";
|
|
case INSTR_ALU_XOR_HH: return "__instr_alu_xor_hh_exec";
|
|
case INSTR_ALU_XOR_I: return "__instr_alu_xor_i_exec";
|
|
|
|
case INSTR_ALU_SHL: return "__instr_alu_shl_exec";
|
|
case INSTR_ALU_SHL_MH: return "__instr_alu_shl_mh_exec";
|
|
case INSTR_ALU_SHL_HM: return "__instr_alu_shl_hm_exec";
|
|
case INSTR_ALU_SHL_HH: return "__instr_alu_shl_hh_exec";
|
|
case INSTR_ALU_SHL_MI: return "__instr_alu_shl_mi_exec";
|
|
case INSTR_ALU_SHL_HI: return "__instr_alu_shl_hi_exec";
|
|
|
|
case INSTR_ALU_SHR: return "__instr_alu_shr_exec";
|
|
case INSTR_ALU_SHR_MH: return "__instr_alu_shr_mh_exec";
|
|
case INSTR_ALU_SHR_HM: return "__instr_alu_shr_hm_exec";
|
|
case INSTR_ALU_SHR_HH: return "__instr_alu_shr_hh_exec";
|
|
case INSTR_ALU_SHR_MI: return "__instr_alu_shr_mi_exec";
|
|
case INSTR_ALU_SHR_HI: return "__instr_alu_shr_hi_exec";
|
|
|
|
case INSTR_REGPREFETCH_RH: return "__instr_regprefetch_rh_exec";
|
|
case INSTR_REGPREFETCH_RM: return "__instr_regprefetch_rm_exec";
|
|
case INSTR_REGPREFETCH_RI: return "__instr_regprefetch_ri_exec";
|
|
|
|
case INSTR_REGRD_HRH: return "__instr_regrd_hrh_exec";
|
|
case INSTR_REGRD_HRM: return "__instr_regrd_hrm_exec";
|
|
case INSTR_REGRD_HRI: return "__instr_regrd_hri_exec";
|
|
case INSTR_REGRD_MRH: return "__instr_regrd_mrh_exec";
|
|
case INSTR_REGRD_MRM: return "__instr_regrd_mrm_exec";
|
|
case INSTR_REGRD_MRI: return "__instr_regrd_mri_exec";
|
|
|
|
case INSTR_REGWR_RHH: return "__instr_regwr_rhh_exec";
|
|
case INSTR_REGWR_RHM: return "__instr_regwr_rhm_exec";
|
|
case INSTR_REGWR_RHI: return "__instr_regwr_rhi_exec";
|
|
case INSTR_REGWR_RMH: return "__instr_regwr_rmh_exec";
|
|
case INSTR_REGWR_RMM: return "__instr_regwr_rmm_exec";
|
|
case INSTR_REGWR_RMI: return "__instr_regwr_rmi_exec";
|
|
case INSTR_REGWR_RIH: return "__instr_regwr_rih_exec";
|
|
case INSTR_REGWR_RIM: return "__instr_regwr_rim_exec";
|
|
case INSTR_REGWR_RII: return "__instr_regwr_rii_exec";
|
|
|
|
case INSTR_REGADD_RHH: return "__instr_regadd_rhh_exec";
|
|
case INSTR_REGADD_RHM: return "__instr_regadd_rhm_exec";
|
|
case INSTR_REGADD_RHI: return "__instr_regadd_rhi_exec";
|
|
case INSTR_REGADD_RMH: return "__instr_regadd_rmh_exec";
|
|
case INSTR_REGADD_RMM: return "__instr_regadd_rmm_exec";
|
|
case INSTR_REGADD_RMI: return "__instr_regadd_rmi_exec";
|
|
case INSTR_REGADD_RIH: return "__instr_regadd_rih_exec";
|
|
case INSTR_REGADD_RIM: return "__instr_regadd_rim_exec";
|
|
case INSTR_REGADD_RII: return "__instr_regadd_rii_exec";
|
|
|
|
case INSTR_METPREFETCH_H: return "__instr_metprefetch_h_exec";
|
|
case INSTR_METPREFETCH_M: return "__instr_metprefetch_m_exec";
|
|
case INSTR_METPREFETCH_I: return "__instr_metprefetch_i_exec";
|
|
|
|
case INSTR_METER_HHM: return "__instr_meter_hhm_exec";
|
|
case INSTR_METER_HHI: return "__instr_meter_hhi_exec";
|
|
case INSTR_METER_HMM: return "__instr_meter_hmm_exec";
|
|
case INSTR_METER_HMI: return "__instr_meter_hmi_exec";
|
|
case INSTR_METER_MHM: return "__instr_meter_mhm_exec";
|
|
case INSTR_METER_MHI: return "__instr_meter_mhi_exec";
|
|
case INSTR_METER_MMM: return "__instr_meter_mmm_exec";
|
|
case INSTR_METER_MMI: return "__instr_meter_mmi_exec";
|
|
case INSTR_METER_IHM: return "__instr_meter_ihm_exec";
|
|
case INSTR_METER_IHI: return "__instr_meter_ihi_exec";
|
|
case INSTR_METER_IMM: return "__instr_meter_imm_exec";
|
|
case INSTR_METER_IMI: return "__instr_meter_imi_exec";
|
|
|
|
case INSTR_TABLE: return NULL;
|
|
case INSTR_TABLE_AF: return NULL;
|
|
case INSTR_SELECTOR: return NULL;
|
|
case INSTR_LEARNER: return NULL;
|
|
case INSTR_LEARNER_AF: return NULL;
|
|
|
|
case INSTR_LEARNER_LEARN: return "__instr_learn_exec";
|
|
case INSTR_LEARNER_REARM: return "__instr_rearm_exec";
|
|
case INSTR_LEARNER_REARM_NEW: return "__instr_rearm_new_exec";
|
|
case INSTR_LEARNER_FORGET: return "__instr_forget_exec";
|
|
|
|
case INSTR_EXTERN_OBJ: return NULL;
|
|
case INSTR_EXTERN_FUNC: return NULL;
|
|
case INSTR_HASH_FUNC: return "__instr_hash_func_exec";
|
|
|
|
case INSTR_JMP: return NULL;
|
|
case INSTR_JMP_VALID: return NULL;
|
|
case INSTR_JMP_INVALID: return NULL;
|
|
case INSTR_JMP_HIT: return NULL;
|
|
case INSTR_JMP_MISS: return NULL;
|
|
case INSTR_JMP_ACTION_HIT: return NULL;
|
|
case INSTR_JMP_ACTION_MISS: return NULL;
|
|
case INSTR_JMP_EQ: return NULL;
|
|
case INSTR_JMP_EQ_MH: return NULL;
|
|
case INSTR_JMP_EQ_HM: return NULL;
|
|
case INSTR_JMP_EQ_HH: return NULL;
|
|
case INSTR_JMP_EQ_I: return NULL;
|
|
case INSTR_JMP_NEQ: return NULL;
|
|
case INSTR_JMP_NEQ_MH: return NULL;
|
|
case INSTR_JMP_NEQ_HM: return NULL;
|
|
case INSTR_JMP_NEQ_HH: return NULL;
|
|
case INSTR_JMP_NEQ_I: return NULL;
|
|
case INSTR_JMP_LT: return NULL;
|
|
case INSTR_JMP_LT_MH: return NULL;
|
|
case INSTR_JMP_LT_HM: return NULL;
|
|
case INSTR_JMP_LT_HH: return NULL;
|
|
case INSTR_JMP_LT_MI: return NULL;
|
|
case INSTR_JMP_LT_HI: return NULL;
|
|
case INSTR_JMP_GT: return NULL;
|
|
case INSTR_JMP_GT_MH: return NULL;
|
|
case INSTR_JMP_GT_HM: return NULL;
|
|
case INSTR_JMP_GT_HH: return NULL;
|
|
case INSTR_JMP_GT_MI: return NULL;
|
|
case INSTR_JMP_GT_HI: return NULL;
|
|
|
|
case INSTR_RETURN: return NULL;
|
|
|
|
default: return NULL;
|
|
}
|
|
}
|
|
|
|
static void
|
|
action_instr_does_tx_codegen(struct action *a,
|
|
uint32_t instr_pos,
|
|
struct instruction *instr,
|
|
FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"%s(p, t, &action_%s_instructions[%u]);\n"
|
|
"\tthread_ip_reset(p, t);\n"
|
|
"\tinstr_rx_exec(p);\n"
|
|
"\treturn;\n",
|
|
instr_type_to_func(instr),
|
|
a->name,
|
|
instr_pos);
|
|
}
|
|
|
|
static void
|
|
action_instr_extern_obj_codegen(struct action *a,
|
|
uint32_t instr_pos,
|
|
FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"while (!__instr_extern_obj_exec(p, t, &action_%s_instructions[%u]));\n",
|
|
a->name,
|
|
instr_pos);
|
|
}
|
|
|
|
static void
|
|
action_instr_extern_func_codegen(struct action *a,
|
|
uint32_t instr_pos,
|
|
FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"while (!__instr_extern_func_exec(p, t, &action_%s_instructions[%u]));\n",
|
|
a->name,
|
|
instr_pos);
|
|
}
|
|
|
|
static void
|
|
action_instr_jmp_codegen(struct action *a,
|
|
uint32_t instr_pos,
|
|
struct instruction *instr,
|
|
struct instruction_data *data,
|
|
FILE *f)
|
|
{
|
|
switch (instr->type) {
|
|
case INSTR_JMP:
|
|
fprintf(f,
|
|
"goto %s;\n",
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_VALID:
|
|
fprintf(f,
|
|
"if (HEADER_VALID(t, action_%s_instructions[%u].jmp.header_id))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_INVALID:
|
|
fprintf(f,
|
|
"if (!HEADER_VALID(t, action_%s_instructions[%u].jmp.header_id))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_HIT:
|
|
fprintf(f,
|
|
"if (t->hit)\n"
|
|
"\t\tgoto %s;\n",
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_MISS:
|
|
fprintf(f,
|
|
"if (!t->hit)\n"
|
|
"\t\tgoto %s;\n",
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_ACTION_HIT:
|
|
fprintf(f,
|
|
"if (t->action_id == action_%s_instructions[%u].jmp.action_id)\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_ACTION_MISS:
|
|
fprintf(f,
|
|
"if (t->action_id != action_%s_instructions[%u].jmp.action_id)\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_EQ:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) == "
|
|
"instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_EQ_MH:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) == "
|
|
"instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_EQ_HM:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) == "
|
|
"instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_EQ_HH:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) == "
|
|
"instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_EQ_I:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) == "
|
|
"action_%s_instructions[%u].jmp.b_val)\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_NEQ:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) != "
|
|
"instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_NEQ_MH:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) != "
|
|
"instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_NEQ_HM:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) != "
|
|
"instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_NEQ_HH:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) != "
|
|
"instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_NEQ_I:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) != "
|
|
"action_%s_instructions[%u].jmp.b_val)\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_LT:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) < "
|
|
"instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_LT_MH:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) < "
|
|
"instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_LT_HM:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) < "
|
|
"instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_LT_HH:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) < "
|
|
"instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_LT_MI:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) < "
|
|
"action_%s_instructions[%u].jmp.b_val)\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_LT_HI:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) < "
|
|
"action_%s_instructions[%u].jmp.b_val)\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_GT:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) > "
|
|
"instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_GT_MH:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) > "
|
|
"instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_GT_HM:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) > "
|
|
"instr_operand_hbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_GT_HH:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) > "
|
|
"instr_operand_nbo(t, &action_%s_instructions[%u].jmp.b))\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_GT_MI:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &action_%s_instructions[%u].jmp.a) > "
|
|
"action_%s_instructions[%u].jmp.b_val)\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
case INSTR_JMP_GT_HI:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &action_%s_instructions[%u].jmp.a) > "
|
|
"action_%s_instructions[%u].jmp.b_val)\n"
|
|
"\t\tgoto %s;\n",
|
|
a->name,
|
|
instr_pos,
|
|
a->name,
|
|
instr_pos,
|
|
data->jmp_label);
|
|
return;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void
|
|
action_instr_return_codegen(FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"return;\n");
|
|
}
|
|
|
|
static void
|
|
action_instr_codegen(struct action *a, FILE *f)
|
|
{
|
|
uint32_t i;
|
|
|
|
fprintf(f,
|
|
"void\n"
|
|
"action_%s_run(struct rte_swx_pipeline *p)\n"
|
|
"{\n"
|
|
"\tstruct thread *t = &p->threads[p->thread_id];\n"
|
|
"\n",
|
|
a->name);
|
|
|
|
for (i = 0; i < a->n_instructions; i++) {
|
|
struct instruction *instr = &a->instructions[i];
|
|
struct instruction_data *data = &a->instruction_data[i];
|
|
|
|
/* Label, if present. */
|
|
if (data->label[0])
|
|
fprintf(f, "\n%s : ", data->label);
|
|
else
|
|
fprintf(f, "\n\t");
|
|
|
|
/* TX instruction type. */
|
|
if (instruction_does_tx(instr)) {
|
|
action_instr_does_tx_codegen(a, i, instr, f);
|
|
continue;
|
|
}
|
|
|
|
/* Extern object/function instruction type. */
|
|
if (instr->type == INSTR_EXTERN_OBJ) {
|
|
action_instr_extern_obj_codegen(a, i, f);
|
|
continue;
|
|
}
|
|
|
|
if (instr->type == INSTR_EXTERN_FUNC) {
|
|
action_instr_extern_func_codegen(a, i, f);
|
|
continue;
|
|
}
|
|
|
|
/* Jump instruction type. */
|
|
if (instruction_is_jmp(instr)) {
|
|
action_instr_jmp_codegen(a, i, instr, data, f);
|
|
continue;
|
|
}
|
|
|
|
/* Return instruction type. */
|
|
if (instr->type == INSTR_RETURN) {
|
|
action_instr_return_codegen(f);
|
|
continue;
|
|
}
|
|
|
|
/* Any other instruction type. */
|
|
fprintf(f,
|
|
"%s(p, t, &action_%s_instructions[%u]);\n",
|
|
instr_type_to_func(instr),
|
|
a->name,
|
|
i);
|
|
}
|
|
|
|
fprintf(f, "}\n\n");
|
|
}
|
|
|
|
struct instruction_group {
|
|
TAILQ_ENTRY(instruction_group) node;
|
|
|
|
uint32_t group_id;
|
|
|
|
uint32_t first_instr_id;
|
|
|
|
uint32_t last_instr_id;
|
|
|
|
instr_exec_t func;
|
|
};
|
|
|
|
TAILQ_HEAD(instruction_group_list, instruction_group);
|
|
|
|
static struct instruction_group *
|
|
instruction_group_list_group_find(struct instruction_group_list *igl, uint32_t instruction_id)
|
|
{
|
|
struct instruction_group *g;
|
|
|
|
TAILQ_FOREACH(g, igl, node)
|
|
if ((g->first_instr_id <= instruction_id) && (instruction_id <= g->last_instr_id))
|
|
return g;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
instruction_group_list_free(struct instruction_group_list *igl)
|
|
{
|
|
if (!igl)
|
|
return;
|
|
|
|
for ( ; ; ) {
|
|
struct instruction_group *g;
|
|
|
|
g = TAILQ_FIRST(igl);
|
|
if (!g)
|
|
break;
|
|
|
|
TAILQ_REMOVE(igl, g, node);
|
|
free(g);
|
|
}
|
|
|
|
free(igl);
|
|
}
|
|
|
|
static struct instruction_group_list *
|
|
instruction_group_list_create(struct rte_swx_pipeline *p)
|
|
{
|
|
struct instruction_group_list *igl = NULL;
|
|
struct instruction_group *g = NULL;
|
|
uint32_t n_groups = 0, i;
|
|
|
|
if (!p || !p->instructions || !p->instruction_data || !p->n_instructions)
|
|
goto error;
|
|
|
|
/* List init. */
|
|
igl = calloc(1, sizeof(struct instruction_group_list));
|
|
if (!igl)
|
|
goto error;
|
|
|
|
TAILQ_INIT(igl);
|
|
|
|
/* Allocate the first group. */
|
|
g = calloc(1, sizeof(struct instruction_group));
|
|
if (!g)
|
|
goto error;
|
|
|
|
/* Iteration 1: Separate the instructions into groups based on the thread yield
|
|
* instructions. Do not worry about the jump instructions at this point.
|
|
*/
|
|
for (i = 0; i < p->n_instructions; i++) {
|
|
struct instruction *instr = &p->instructions[i];
|
|
|
|
/* Check for thread yield instructions. */
|
|
if (!instruction_does_thread_yield(instr))
|
|
continue;
|
|
|
|
/* If the current group contains at least one instruction, then finalize it (with
|
|
* the previous instruction), add it to the list and allocate a new group (that
|
|
* starts with the current instruction).
|
|
*/
|
|
if (i - g->first_instr_id) {
|
|
/* Finalize the group. */
|
|
g->last_instr_id = i - 1;
|
|
|
|
/* Add the group to the list. Advance the number of groups. */
|
|
TAILQ_INSERT_TAIL(igl, g, node);
|
|
n_groups++;
|
|
|
|
/* Allocate a new group. */
|
|
g = calloc(1, sizeof(struct instruction_group));
|
|
if (!g)
|
|
goto error;
|
|
|
|
/* Initialize the new group. */
|
|
g->group_id = n_groups;
|
|
g->first_instr_id = i;
|
|
}
|
|
|
|
/* Finalize the current group (with the current instruction, therefore this group
|
|
* contains just the current thread yield instruction), add it to the list and
|
|
* allocate a new group (that starts with the next instruction).
|
|
*/
|
|
|
|
/* Finalize the group. */
|
|
g->last_instr_id = i;
|
|
|
|
/* Add the group to the list. Advance the number of groups. */
|
|
TAILQ_INSERT_TAIL(igl, g, node);
|
|
n_groups++;
|
|
|
|
/* Allocate a new group. */
|
|
g = calloc(1, sizeof(struct instruction_group));
|
|
if (!g)
|
|
goto error;
|
|
|
|
/* Initialize the new group. */
|
|
g->group_id = n_groups;
|
|
g->first_instr_id = i + 1;
|
|
}
|
|
|
|
/* Handle the last group. */
|
|
if (i - g->first_instr_id) {
|
|
/* Finalize the group. */
|
|
g->last_instr_id = i - 1;
|
|
|
|
/* Add the group to the list. Advance the number of groups. */
|
|
TAILQ_INSERT_TAIL(igl, g, node);
|
|
n_groups++;
|
|
} else
|
|
free(g);
|
|
|
|
g = NULL;
|
|
|
|
/* Iteration 2: Handle jumps. If the current group contains an instruction which represents
|
|
* the destination of a jump instruction located in a different group ("far jump"), then the
|
|
* current group has to be split, so that the instruction representing the far jump
|
|
* destination is at the start of its group.
|
|
*/
|
|
for ( ; ; ) {
|
|
int is_modified = 0;
|
|
|
|
for (i = 0; i < p->n_instructions; i++) {
|
|
struct instruction_data *data = &p->instruction_data[i];
|
|
struct instruction_group *g;
|
|
uint32_t j;
|
|
|
|
/* Continue when the current instruction is not a jump destination. */
|
|
if (!data->n_users)
|
|
continue;
|
|
|
|
g = instruction_group_list_group_find(igl, i);
|
|
if (!g)
|
|
goto error;
|
|
|
|
/* Find out all the jump instructions with this destination. */
|
|
for (j = 0; j < p->n_instructions; j++) {
|
|
struct instruction *jmp_instr = &p->instructions[j];
|
|
struct instruction_data *jmp_data = &p->instruction_data[j];
|
|
struct instruction_group *jmp_g, *new_g;
|
|
|
|
/* Continue when not a jump instruction. Even when jump instruction,
|
|
* continue when the jump destination is not this instruction.
|
|
*/
|
|
if (!instruction_is_jmp(jmp_instr) ||
|
|
strcmp(jmp_data->jmp_label, data->label))
|
|
continue;
|
|
|
|
jmp_g = instruction_group_list_group_find(igl, j);
|
|
if (!jmp_g)
|
|
goto error;
|
|
|
|
/* Continue when both the jump instruction and the jump destination
|
|
* instruction are in the same group. Even when in different groups,
|
|
* still continue if the jump destination instruction is already the
|
|
* first instruction of its group.
|
|
*/
|
|
if ((jmp_g->group_id == g->group_id) || (g->first_instr_id == i))
|
|
continue;
|
|
|
|
/* Split the group of the current jump destination instruction to
|
|
* make this instruction the first instruction of a new group.
|
|
*/
|
|
new_g = calloc(1, sizeof(struct instruction_group));
|
|
if (!new_g)
|
|
goto error;
|
|
|
|
new_g->group_id = n_groups;
|
|
new_g->first_instr_id = i;
|
|
new_g->last_instr_id = g->last_instr_id;
|
|
|
|
g->last_instr_id = i - 1;
|
|
|
|
TAILQ_INSERT_AFTER(igl, g, new_g, node);
|
|
n_groups++;
|
|
is_modified = 1;
|
|
|
|
/* The decision to split this group (to make the current instruction
|
|
* the first instruction of a new group) is already taken and fully
|
|
* implemented, so no need to search for more reasons to do it.
|
|
*/
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Re-evaluate everything, as at least one group got split, so some jumps that were
|
|
* previously considered local (i.e. the jump destination is in the same group as
|
|
* the jump instruction) can now be "far jumps" (i.e. the jump destination is in a
|
|
* different group than the jump instruction). Wost case scenario: each instruction
|
|
* that is a jump destination ends up as the first instruction of its group.
|
|
*/
|
|
if (!is_modified)
|
|
break;
|
|
}
|
|
|
|
/* Re-assign the group IDs to be in incremental order. */
|
|
i = 0;
|
|
TAILQ_FOREACH(g, igl, node) {
|
|
g->group_id = i;
|
|
|
|
i++;
|
|
}
|
|
|
|
return igl;
|
|
|
|
error:
|
|
instruction_group_list_free(igl);
|
|
|
|
free(g);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void
|
|
pipeline_instr_does_tx_codegen(struct rte_swx_pipeline *p __rte_unused,
|
|
uint32_t instr_pos,
|
|
struct instruction *instr,
|
|
FILE *f)
|
|
{
|
|
fprintf(f,
|
|
"%s(p, t, &pipeline_instructions[%u]);\n"
|
|
"\tthread_ip_reset(p, t);\n"
|
|
"\tinstr_rx_exec(p);\n"
|
|
"\treturn;\n",
|
|
instr_type_to_func(instr),
|
|
instr_pos);
|
|
}
|
|
|
|
static int
|
|
pipeline_instr_jmp_codegen(struct rte_swx_pipeline *p,
|
|
struct instruction_group_list *igl,
|
|
uint32_t jmp_instr_id,
|
|
struct instruction *jmp_instr,
|
|
struct instruction_data *jmp_data,
|
|
FILE *f)
|
|
{
|
|
struct instruction_group *jmp_g, *g;
|
|
struct instruction_data *data;
|
|
uint32_t instr_id;
|
|
|
|
switch (jmp_instr->type) {
|
|
case INSTR_JMP:
|
|
break;
|
|
|
|
case INSTR_JMP_VALID:
|
|
fprintf(f,
|
|
"if (HEADER_VALID(t, pipeline_instructions[%u].jmp.header_id))",
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_INVALID:
|
|
fprintf(f,
|
|
"if (!HEADER_VALID(t, pipeline_instructions[%u].jmp.header_id))",
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_HIT:
|
|
fprintf(f,
|
|
"if (t->hit)\n");
|
|
break;
|
|
|
|
case INSTR_JMP_MISS:
|
|
fprintf(f,
|
|
"if (!t->hit)\n");
|
|
break;
|
|
|
|
case INSTR_JMP_ACTION_HIT:
|
|
fprintf(f,
|
|
"if (t->action_id == pipeline_instructions[%u].jmp.action_id)",
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_ACTION_MISS:
|
|
fprintf(f,
|
|
"if (t->action_id != pipeline_instructions[%u].jmp.action_id)",
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_EQ:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) == "
|
|
"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_EQ_MH:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) == "
|
|
"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_EQ_HM:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) == "
|
|
"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_EQ_HH:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) == "
|
|
"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_EQ_I:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) == "
|
|
"pipeline_instructions[%u].jmp.b_val)",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_NEQ:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) != "
|
|
"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_NEQ_MH:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) != "
|
|
"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_NEQ_HM:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) != "
|
|
"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_NEQ_HH:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) != "
|
|
"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_NEQ_I:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) != "
|
|
"pipeline_instructions[%u].jmp.b_val)",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_LT:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) < "
|
|
"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_LT_MH:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) < "
|
|
"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_LT_HM:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) < "
|
|
"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_LT_HH:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) < "
|
|
"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_LT_MI:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) < "
|
|
"pipeline_instructions[%u].jmp.b_val)",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_LT_HI:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) < "
|
|
"pipeline_instructions[%u].jmp.b_val)",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_GT:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) > "
|
|
"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_GT_MH:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) > "
|
|
"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_GT_HM:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) > "
|
|
"instr_operand_hbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_GT_HH:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) > "
|
|
"instr_operand_nbo(t, &pipeline_instructions[%u].jmp.b))",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_GT_MI:
|
|
fprintf(f,
|
|
"if (instr_operand_hbo(t, &pipeline_instructions[%u].jmp.a) > "
|
|
"pipeline_instructions[%u].jmp.b_val)",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
case INSTR_JMP_GT_HI:
|
|
fprintf(f,
|
|
"if (instr_operand_nbo(t, &pipeline_instructions[%u].jmp.a) > "
|
|
"pipeline_instructions[%u].jmp.b_val)",
|
|
jmp_instr_id,
|
|
jmp_instr_id);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Find the instruction group of the jump instruction. */
|
|
jmp_g = instruction_group_list_group_find(igl, jmp_instr_id);
|
|
if (!jmp_g)
|
|
return -EINVAL;
|
|
|
|
/* Find the instruction group of the jump destination instruction. */
|
|
data = label_find(p->instruction_data, p->n_instructions, jmp_data->jmp_label);
|
|
if (!data)
|
|
return -EINVAL;
|
|
|
|
instr_id = data - p->instruction_data;
|
|
|
|
g = instruction_group_list_group_find(igl, instr_id);
|
|
if (!g)
|
|
return -EINVAL;
|
|
|
|
/* Code generation for "near" jump (same instruction group) or "far" jump (different
|
|
* instruction group).
|
|
*/
|
|
if (g->group_id == jmp_g->group_id)
|
|
fprintf(f,
|
|
"\n\t\tgoto %s;\n",
|
|
jmp_data->jmp_label);
|
|
else
|
|
fprintf(f,
|
|
" {\n"
|
|
"\t\tthread_ip_set(t, &p->instructions[%u]);\n"
|
|
"\t\treturn;\n"
|
|
"\t}\n\n",
|
|
g->group_id);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
instruction_group_list_codegen(struct instruction_group_list *igl,
|
|
struct rte_swx_pipeline *p,
|
|
FILE *f)
|
|
{
|
|
struct instruction_group *g;
|
|
uint32_t i;
|
|
int is_required = 0;
|
|
|
|
/* Check if code generation is required. */
|
|
TAILQ_FOREACH(g, igl, node)
|
|
if (g->first_instr_id < g->last_instr_id)
|
|
is_required = 1;
|
|
|
|
if (!is_required)
|
|
return;
|
|
|
|
/* Generate the code for the pipeline instruction array. */
|
|
fprintf(f,
|
|
"static const struct instruction pipeline_instructions[] = {\n");
|
|
|
|
for (i = 0; i < p->n_instructions; i++) {
|
|
struct instruction *instr = &p->instructions[i];
|
|
instruction_export_t func = export_table[instr->type];
|
|
|
|
func(instr, f);
|
|
}
|
|
|
|
fprintf(f, "};\n\n");
|
|
|
|
/* Generate the code for the pipeline functions: one function for each instruction group
|
|
* that contains more than one instruction.
|
|
*/
|
|
TAILQ_FOREACH(g, igl, node) {
|
|
struct instruction *last_instr;
|
|
uint32_t j;
|
|
|
|
/* Skip if group contains a single instruction. */
|
|
if (g->last_instr_id == g->first_instr_id)
|
|
continue;
|
|
|
|
/* Generate new pipeline function. */
|
|
fprintf(f,
|
|
"void\n"
|
|
"pipeline_func_%u(struct rte_swx_pipeline *p)\n"
|
|
"{\n"
|
|
"\tstruct thread *t = &p->threads[p->thread_id];\n"
|
|
"\n",
|
|
g->group_id);
|
|
|
|
/* Generate the code for each pipeline instruction. */
|
|
for (j = g->first_instr_id; j <= g->last_instr_id; j++) {
|
|
struct instruction *instr = &p->instructions[j];
|
|
struct instruction_data *data = &p->instruction_data[j];
|
|
|
|
/* Label, if present. */
|
|
if (data->label[0])
|
|
fprintf(f, "\n%s : ", data->label);
|
|
else
|
|
fprintf(f, "\n\t");
|
|
|
|
/* TX instruction type. */
|
|
if (instruction_does_tx(instr)) {
|
|
pipeline_instr_does_tx_codegen(p, j, instr, f);
|
|
continue;
|
|
}
|
|
|
|
/* Jump instruction type. */
|
|
if (instruction_is_jmp(instr)) {
|
|
pipeline_instr_jmp_codegen(p, igl, j, instr, data, f);
|
|
continue;
|
|
}
|
|
|
|
/* Any other instruction type. */
|
|
fprintf(f,
|
|
"%s(p, t, &pipeline_instructions[%u]);\n",
|
|
instr_type_to_func(instr),
|
|
j);
|
|
}
|
|
|
|
/* Finalize the generated pipeline function. For some instructions such as TX,
|
|
* emit-many-and-TX and unconditional jump, the next instruction has been already
|
|
* decided unconditionally and the instruction pointer of the current thread set
|
|
* accordingly; for all the other instructions, the instruction pointer must be
|
|
* incremented now.
|
|
*/
|
|
last_instr = &p->instructions[g->last_instr_id];
|
|
|
|
if (!instruction_does_tx(last_instr) && (last_instr->type != INSTR_JMP))
|
|
fprintf(f,
|
|
"thread_ip_inc(p);\n");
|
|
|
|
fprintf(f,
|
|
"}\n"
|
|
"\n");
|
|
}
|
|
}
|
|
|
|
static uint32_t
|
|
instruction_group_list_custom_instructions_count(struct instruction_group_list *igl)
|
|
{
|
|
struct instruction_group *g;
|
|
uint32_t n_custom_instr = 0;
|
|
|
|
/* Groups with a single instruction: no function is generated for this group, the group
|
|
* keeps its current instruction. Groups with more than two instructions: one function and
|
|
* the associated custom instruction get generated for each such group.
|
|
*/
|
|
TAILQ_FOREACH(g, igl, node) {
|
|
if (g->first_instr_id == g->last_instr_id)
|
|
continue;
|
|
|
|
n_custom_instr++;
|
|
}
|
|
|
|
return n_custom_instr;
|
|
}
|
|
|
|
static int
|
|
pipeline_codegen(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
|
|
{
|
|
struct action *a;
|
|
FILE *f = NULL;
|
|
|
|
/* Create the .c file. */
|
|
f = fopen("/tmp/pipeline.c", "w");
|
|
if (!f)
|
|
return -EIO;
|
|
|
|
/* Include the .h file. */
|
|
fprintf(f, "#include \"rte_swx_pipeline_internal.h\"\n");
|
|
|
|
/* Add the code for each action. */
|
|
TAILQ_FOREACH(a, &p->actions, node) {
|
|
fprintf(f, "/**\n * Action %s\n */\n\n", a->name);
|
|
|
|
action_data_codegen(a, f);
|
|
|
|
fprintf(f, "\n");
|
|
|
|
action_instr_codegen(a, f);
|
|
|
|
fprintf(f, "\n");
|
|
}
|
|
|
|
/* Add the pipeline code. */
|
|
instruction_group_list_codegen(igl, p, f);
|
|
|
|
/* Close the .c file. */
|
|
fclose(f);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifndef RTE_SWX_PIPELINE_CMD_MAX_SIZE
|
|
#define RTE_SWX_PIPELINE_CMD_MAX_SIZE 4096
|
|
#endif
|
|
|
|
static int
|
|
pipeline_libload(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
|
|
{
|
|
struct action *a;
|
|
struct instruction_group *g;
|
|
char *dir_in, *buffer = NULL;
|
|
const char *dir_out;
|
|
int status = 0;
|
|
|
|
/* Get the environment variables. */
|
|
dir_in = getenv("RTE_INSTALL_DIR");
|
|
if (!dir_in) {
|
|
status = -EINVAL;
|
|
goto free;
|
|
}
|
|
|
|
dir_out = "/tmp";
|
|
|
|
/* Memory allocation for the command buffer. */
|
|
buffer = malloc(RTE_SWX_PIPELINE_CMD_MAX_SIZE);
|
|
if (!buffer) {
|
|
status = -ENOMEM;
|
|
goto free;
|
|
}
|
|
|
|
snprintf(buffer,
|
|
RTE_SWX_PIPELINE_CMD_MAX_SIZE,
|
|
"gcc -c -O3 -fpic -Wno-deprecated-declarations -o %s/pipeline.o %s/pipeline.c "
|
|
"-I %s/lib/pipeline "
|
|
"-I %s/lib/eal/include "
|
|
"-I %s/lib/eal/x86/include "
|
|
"-I %s/lib/eal/include/generic "
|
|
"-I %s/lib/meter "
|
|
"-I %s/lib/port "
|
|
"-I %s/lib/table "
|
|
"-I %s/lib/pipeline "
|
|
"-I %s/config "
|
|
"-I %s/build "
|
|
"-I %s/lib/eal/linux/include "
|
|
">%s/pipeline.log 2>&1 "
|
|
"&& "
|
|
"gcc -shared %s/pipeline.o -o %s/libpipeline.so "
|
|
">>%s/pipeline.log 2>&1",
|
|
dir_out,
|
|
dir_out,
|
|
dir_in,
|
|
dir_in,
|
|
dir_in,
|
|
dir_in,
|
|
dir_in,
|
|
dir_in,
|
|
dir_in,
|
|
dir_in,
|
|
dir_in,
|
|
dir_in,
|
|
dir_in,
|
|
dir_out,
|
|
dir_out,
|
|
dir_out,
|
|
dir_out);
|
|
|
|
/* Build the shared object library. */
|
|
status = system(buffer);
|
|
if (status)
|
|
goto free;
|
|
|
|
/* Open library. */
|
|
snprintf(buffer,
|
|
RTE_SWX_PIPELINE_CMD_MAX_SIZE,
|
|
"%s/libpipeline.so",
|
|
dir_out);
|
|
|
|
p->lib = dlopen(buffer, RTLD_LAZY);
|
|
if (!p->lib) {
|
|
status = -EIO;
|
|
goto free;
|
|
}
|
|
|
|
/* Get the action function symbols. */
|
|
TAILQ_FOREACH(a, &p->actions, node) {
|
|
snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "action_%s_run", a->name);
|
|
|
|
p->action_funcs[a->id] = dlsym(p->lib, buffer);
|
|
if (!p->action_funcs[a->id]) {
|
|
status = -EINVAL;
|
|
goto free;
|
|
}
|
|
}
|
|
|
|
/* Get the pipeline function symbols. */
|
|
TAILQ_FOREACH(g, igl, node) {
|
|
if (g->first_instr_id == g->last_instr_id)
|
|
continue;
|
|
|
|
snprintf(buffer, RTE_SWX_PIPELINE_CMD_MAX_SIZE, "pipeline_func_%u", g->group_id);
|
|
|
|
g->func = dlsym(p->lib, buffer);
|
|
if (!g->func) {
|
|
status = -EINVAL;
|
|
goto free;
|
|
}
|
|
}
|
|
|
|
free:
|
|
if (status && p->lib) {
|
|
dlclose(p->lib);
|
|
p->lib = NULL;
|
|
}
|
|
|
|
free(buffer);
|
|
|
|
return status;
|
|
}
|
|
|
|
static int
|
|
pipeline_adjust_check(struct rte_swx_pipeline *p __rte_unused,
|
|
struct instruction_group_list *igl)
|
|
{
|
|
uint32_t n_custom_instr = instruction_group_list_custom_instructions_count(igl);
|
|
|
|
/* Check that enough space is available within the pipeline instruction table to store all
|
|
* the custom instructions.
|
|
*/
|
|
if (INSTR_CUSTOM_0 + n_custom_instr > RTE_SWX_PIPELINE_INSTRUCTION_TABLE_SIZE_MAX)
|
|
return -ENOSPC;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
pipeline_adjust(struct rte_swx_pipeline *p, struct instruction_group_list *igl)
|
|
{
|
|
struct instruction_group *g;
|
|
uint32_t i;
|
|
|
|
/* Pipeline table instructions. */
|
|
for (i = 0; i < p->n_instructions; i++) {
|
|
struct instruction *instr = &p->instructions[i];
|
|
|
|
if (instr->type == INSTR_TABLE)
|
|
instr->type = INSTR_TABLE_AF;
|
|
|
|
if (instr->type == INSTR_LEARNER)
|
|
instr->type = INSTR_LEARNER_AF;
|
|
}
|
|
|
|
/* Pipeline custom instructions. */
|
|
i = 0;
|
|
TAILQ_FOREACH(g, igl, node) {
|
|
struct instruction *instr = &p->instructions[g->first_instr_id];
|
|
uint32_t j;
|
|
|
|
if (g->first_instr_id == g->last_instr_id)
|
|
continue;
|
|
|
|
/* Install a new custom instruction. */
|
|
p->instruction_table[INSTR_CUSTOM_0 + i] = g->func;
|
|
|
|
/* First instruction of the group: change its type to the new custom instruction. */
|
|
instr->type = INSTR_CUSTOM_0 + i;
|
|
|
|
/* All the subsequent instructions of the group: invalidate. */
|
|
for (j = g->first_instr_id + 1; j <= g->last_instr_id; j++) {
|
|
struct instruction_data *data = &p->instruction_data[j];
|
|
|
|
data->invalid = 1;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
/* Remove the invalidated instructions. */
|
|
p->n_instructions = instr_compact(p->instructions, p->instruction_data, p->n_instructions);
|
|
|
|
/* Resolve the jump destination for any "standalone" jump instructions (i.e. those jump
|
|
* instructions that are the only instruction within their group, so they were left
|
|
* unmodified).
|
|
*/
|
|
instr_jmp_resolve(p->instructions, p->instruction_data, p->n_instructions);
|
|
}
|
|
|
|
static int
|
|
pipeline_compile(struct rte_swx_pipeline *p)
|
|
{
|
|
struct instruction_group_list *igl = NULL;
|
|
int status = 0;
|
|
|
|
igl = instruction_group_list_create(p);
|
|
if (!igl) {
|
|
status = -ENOMEM;
|
|
goto free;
|
|
}
|
|
|
|
/* Code generation. */
|
|
status = pipeline_codegen(p, igl);
|
|
if (status)
|
|
goto free;
|
|
|
|
/* Build and load the shared object library. */
|
|
status = pipeline_libload(p, igl);
|
|
if (status)
|
|
goto free;
|
|
|
|
/* Adjust instructions. */
|
|
status = pipeline_adjust_check(p, igl);
|
|
if (status)
|
|
goto free;
|
|
|
|
pipeline_adjust(p, igl);
|
|
|
|
free:
|
|
instruction_group_list_free(igl);
|
|
|
|
return status;
|
|
}
|