@@ -41,6 +41,12 @@ struct rte_swx_ctl_pipeline_info {
/** Number of input ports. */
uint32_t n_ports_out;
+ /** Number of packet mirroring slots. */
+ uint32_t n_mirroring_slots;
+
+ /** Number of packet mirroring sessions. */
+ uint32_t n_mirroring_sessions;
+
/** Number of actions. */
uint32_t n_actions;
@@ -655,6 +661,36 @@ rte_swx_ctl_pipeline_learner_stats_read(struct rte_swx_pipeline *p,
const char *learner_name,
struct rte_swx_learner_stats *stats);
+/*
+ * Packet mirroring API.
+ */
+
+/** Packet mirroring session parameters. */
+struct rte_swx_pipeline_mirroring_session_params {
+ /** Output port ID. */
+ uint32_t port_id;
+};
+
+/**
+ * Packet mirroring session set
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] session_id
+ * Packet mirroring session ID.
+ * @param[in] params
+ * Packet mirroring session parameters.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument;
+ * -EEXIST: Pipeline was already built successfully.
+ */
+__rte_experimental
+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);
+
/*
* Table Update API.
*/
@@ -556,6 +556,81 @@ port_out_free(struct rte_swx_pipeline *p)
}
}
+/*
+ * 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;
+
+ if (!p->n_mirroring_slots || !p->n_mirroring_sessions)
+ return 0;
+
+ 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.
*/
@@ -1655,6 +1730,56 @@ instr_drop_exec(struct rte_swx_pipeline *p)
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);
+}
+
/*
* extract.
*/
@@ -5655,6 +5780,14 @@ instr_translate(struct rte_swx_pipeline *p,
instr,
data);
+ if (!strcmp(tokens[tpos], "mirror"))
+ return instr_mirror_translate(p,
+ action,
+ &tokens[tpos],
+ n_tokens - tpos,
+ instr,
+ data);
+
if (!strcmp(tokens[tpos], "extract"))
return instr_hdr_extract_translate(p,
action,
@@ -6679,6 +6812,7 @@ static instr_exec_t instruction_table[] = {
[INSTR_TX] = instr_tx_exec,
[INSTR_TX_I] = instr_tx_i_exec,
[INSTR_DROP] = instr_drop_exec,
+ [INSTR_MIRROR] = instr_mirror_exec,
[INSTR_HDR_EXTRACT] = instr_hdr_extract_exec,
[INSTR_HDR_EXTRACT2] = instr_hdr_extract2_exec,
@@ -9028,6 +9162,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)
header_free(p);
extern_func_free(p);
extern_obj_free(p);
+ mirroring_free(p);
port_out_free(p);
port_in_free(p);
struct_free(p);
@@ -9236,6 +9371,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
if (status)
goto error;
+ status = mirroring_build(p);
+ if (status)
+ goto error;
+
status = struct_build(p);
if (status)
goto error;
@@ -9307,6 +9446,7 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
header_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);
@@ -9358,6 +9498,8 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
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;
@@ -10045,6 +10187,25 @@ rte_swx_ctl_meter_stats_read(struct rte_swx_pipeline *p,
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;
+
+ return 0;
+}
+
/*
* Pipeline compilation.
*/
@@ -10057,6 +10218,7 @@ instr_type_to_name(struct instruction *instr)
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_HDR_EXTRACT: return "INSTR_HDR_EXTRACT";
case INSTR_HDR_EXTRACT2: return "INSTR_HDR_EXTRACT2";
@@ -10359,6 +10521,34 @@ instr_io_export(struct instruction *instr, FILE *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_hdr_validate_export(struct instruction *instr, FILE *f)
{
@@ -10926,6 +11116,7 @@ static instruction_export_t export_table[] = {
[INSTR_TX] = instr_io_export,
[INSTR_TX_I] = instr_io_export,
[INSTR_DROP] = instr_io_export,
+ [INSTR_MIRROR] = instr_mirror_export,
[INSTR_HDR_EXTRACT] = instr_io_export,
[INSTR_HDR_EXTRACT2] = instr_io_export,
@@ -11144,6 +11335,7 @@ instr_type_to_func(struct instruction *instr)
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_HDR_EXTRACT: return "__instr_hdr_extract_exec";
case INSTR_HDR_EXTRACT2: return "__instr_hdr_extract2_exec";
@@ -159,6 +159,36 @@ rte_swx_pipeline_port_out_config(struct rte_swx_pipeline *p,
uint32_t port_id,
const char *port_type_name,
void *args);
+/*
+ * Packet mirroring
+ */
+
+/** Packet mirroring parameters. */
+struct rte_swx_pipeline_mirroring_params {
+ /** Number of packet mirroring slots. */
+ uint32_t n_slots;
+
+ /** Maximum number of packet mirroring sessions. */
+ uint32_t n_sessions;
+};
+
+/**
+ * Packet mirroring configure
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] params
+ * Packet mirroring parameters.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument;
+ * -ENOMEM: Not enough memory;
+ * -EEXIST: Pipeline was already built successfully.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_mirroring_config(struct rte_swx_pipeline *p,
+ struct rte_swx_pipeline_mirroring_params *params);
/*
* Extern objects and functions
@@ -109,6 +109,13 @@ struct port_out_runtime {
void *obj;
};
+/*
+ * Packet mirroring.
+ */
+struct mirroring_session {
+ uint32_t port_id;
+};
+
/*
* Extern object.
*/
@@ -228,6 +235,13 @@ enum instruction_type {
INSTR_TX_I, /* port_out = I */
INSTR_DROP,
+ /*
+ * mirror slot_id session_id
+ * slot_id = MEFT
+ * session_id = MEFT
+ */
+ INSTR_MIRROR,
+
/* extract h.header */
INSTR_HDR_EXTRACT,
INSTR_HDR_EXTRACT2,
@@ -671,6 +685,7 @@ struct instruction {
enum instruction_type type;
union {
struct instr_io io;
+ struct instr_dst_src mirror;
struct instr_hdr_validity valid;
struct instr_dst_src mov;
struct instr_regarray regarray;
@@ -903,6 +918,8 @@ struct thread {
/* Packet. */
struct rte_swx_pkt pkt;
uint8_t *ptr;
+ uint32_t *mirroring_slots;
+ uint64_t mirroring_slots_mask;
/* Structures. */
uint8_t **structs;
@@ -1400,6 +1417,7 @@ struct rte_swx_pipeline {
struct port_in_runtime *in;
struct port_out_runtime *out;
+ struct mirroring_session *mirroring_sessions;
struct instruction **action_instructions;
action_func_t *action_funcs;
struct rte_swx_table_state *table_state;
@@ -1417,6 +1435,8 @@ struct rte_swx_pipeline {
uint32_t n_structs;
uint32_t n_ports_in;
uint32_t n_ports_out;
+ uint32_t n_mirroring_slots;
+ uint32_t n_mirroring_sessions;
uint32_t n_extern_objs;
uint32_t n_extern_funcs;
uint32_t n_actions;
@@ -1512,6 +1532,8 @@ __instr_rx_exec(struct rte_swx_pipeline *p, struct thread *t, const struct instr
pkt_received ? "1 pkt" : "0 pkts",
p->port_id);
+ t->mirroring_slots_mask = 0;
+
/* Headers. */
t->valid_headers = 0;
t->n_headers_out = 0;
@@ -1597,6 +1619,29 @@ emit_handler(struct thread *t)
}
}
+static inline void
+mirroring_handler(struct rte_swx_pipeline *p, struct thread *t, struct rte_swx_pkt *pkt)
+{
+ uint64_t slots_mask = t->mirroring_slots_mask, slot_mask;
+ uint32_t slot_id;
+
+ for (slot_id = 0, slot_mask = 1LLU ; slots_mask; slot_id++, slot_mask <<= 1)
+ if (slot_mask & slots_mask) {
+ struct port_out_runtime *port;
+ struct mirroring_session *session;
+ uint32_t port_id, session_id;
+
+ session_id = t->mirroring_slots[slot_id];
+ session = &p->mirroring_sessions[session_id];
+
+ port_id = session->port_id;
+ port = &p->out[port_id];
+ port->pkt_clone_tx(port->obj, pkt);
+
+ slots_mask &= ~slot_mask;
+ }
+}
+
static inline void
__instr_tx_exec(struct rte_swx_pipeline *p, struct thread *t, const struct instruction *ip)
{
@@ -1612,6 +1657,7 @@ __instr_tx_exec(struct rte_swx_pipeline *p, struct thread *t, const struct instr
emit_handler(t);
/* Packet. */
+ mirroring_handler(p, t, pkt);
port->pkt_tx(port->obj, pkt);
}
@@ -1630,6 +1676,7 @@ __instr_tx_i_exec(struct rte_swx_pipeline *p, struct thread *t, const struct ins
emit_handler(t);
/* Packet. */
+ mirroring_handler(p, t, pkt);
port->pkt_tx(port->obj, pkt);
}
@@ -1649,9 +1696,30 @@ __instr_drop_exec(struct rte_swx_pipeline *p,
emit_handler(t);
/* Packet. */
+ mirroring_handler(p, t, pkt);
port->pkt_tx(port->obj, pkt);
}
+static inline void
+__instr_mirror_exec(struct rte_swx_pipeline *p,
+ struct thread *t,
+ const struct instruction *ip)
+{
+ uint64_t slot_id = instr_operand_hbo(t, &ip->mirror.dst);
+ uint64_t session_id = instr_operand_hbo(t, &ip->mirror.src);
+
+ slot_id &= p->n_mirroring_slots - 1;
+ session_id &= p->n_mirroring_sessions - 1;
+
+ TRACE("[Thread %2u]: mirror pkt (slot = %u, session = %u)\n",
+ p->thread_id,
+ (uint32_t)slot_id,
+ (uint32_t)session_id);
+
+ t->mirroring_slots[slot_id] = session_id;
+ t->mirroring_slots_mask |= 1LLU << slot_id;
+}
+
/*
* extract.
*/
@@ -67,6 +67,7 @@ EXPERIMENTAL {
rte_swx_ctl_pipeline_create;
rte_swx_ctl_pipeline_free;
rte_swx_ctl_pipeline_info_get;
+ rte_swx_ctl_pipeline_mirroring_session_set;
rte_swx_ctl_pipeline_numa_node_get;
rte_swx_ctl_pipeline_port_in_stats_read;
rte_swx_ctl_pipeline_port_out_stats_read;
@@ -90,6 +91,7 @@ EXPERIMENTAL {
rte_swx_pipeline_flush;
rte_swx_pipeline_free;
rte_swx_pipeline_instructions_config;
+ rte_swx_pipeline_mirroring_config;
rte_swx_pipeline_packet_header_register;
rte_swx_pipeline_packet_metadata_register;
rte_swx_pipeline_port_in_config;