* [PATCH V2] pipeline: add RSS support
2023-02-03 19:37 [PATCH] pipeline: add RSS support Cristian Dumitrescu
@ 2023-02-07 15:29 ` Cristian Dumitrescu
2023-02-07 15:43 ` [PATCH V3] " Cristian Dumitrescu
2023-02-07 16:41 ` [PATCH V4] " Cristian Dumitrescu
2 siblings, 0 replies; 5+ messages in thread
From: Cristian Dumitrescu @ 2023-02-07 15:29 UTC (permalink / raw)
To: dev; +Cc: Kamalakannan R
Add pipeline support for the Receive Side Scaling (RSS) hashing. While
the pipeline already supports the stateless hashing schemes, the RSS
scheme uses a key configured by the control plane and preserved
between successive RSS hash invocations.
Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
Change log:
V2:
-added CLI command and enxample file for the examples/pipeline
application
examples/pipeline/cli.c | 73 +++++
| 42 +++
| 84 ++++++
lib/pipeline/rte_swx_ctl.h | 92 +++++++
lib/pipeline/rte_swx_pipeline.c | 334 +++++++++++++++++++++++
lib/pipeline/rte_swx_pipeline.h | 22 ++
lib/pipeline/rte_swx_pipeline_internal.h | 96 +++++++
lib/pipeline/rte_swx_pipeline_spec.c | 107 ++++++++
lib/pipeline/rte_swx_pipeline_spec.h | 11 +
lib/pipeline/version.map | 5 +
10 files changed, 866 insertions(+)
create mode 100644 examples/pipeline/examples/rss.cli
create mode 100644 examples/pipeline/examples/rss.spec
diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 87f9c0370d..2ae6cc579f 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -2622,6 +2622,67 @@ cmd_pipeline_meter_stats(char **tokens,
return;
}
+static const char cmd_pipeline_rss_help[] =
+"pipeline <pipeline_name> rss <rss_obj_name> key <key_byte0> ...\n";
+
+static void
+cmd_pipeline_rss(char **tokens,
+ uint32_t n_tokens,
+ char *out,
+ size_t out_size,
+ void *obj __rte_unused)
+{
+ uint8_t rss_key[CMD_MAX_TOKENS];
+ struct rte_swx_pipeline *p;
+ const char *rss_obj_name;
+ uint32_t rss_key_size, i;
+ int status;
+
+ if (n_tokens < 6) {
+ snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+ return;
+ }
+
+ p = rte_swx_pipeline_find(tokens[1]);
+ if (!p) {
+ snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+ return;
+ }
+
+ if (strcmp(tokens[2], "rss")) {
+ snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
+ return;
+ }
+
+ rss_obj_name = tokens[3];
+
+ if (strcmp(tokens[4], "key")) {
+ snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
+ return;
+ }
+
+ tokens += 5;
+ n_tokens -= 5;
+ rss_key_size = n_tokens;
+
+ for (i = 0; i < rss_key_size; i++) {
+ uint32_t key_byte;
+
+ if (parser_read_uint32(&key_byte, tokens[i]) || (key_byte >= UINT8_MAX)) {
+ snprintf(out, out_size, MSG_ARG_INVALID, "key byte");
+ return;
+ }
+
+ rss_key[i] = (uint8_t)key_byte;
+ }
+
+ status = rte_swx_ctl_pipeline_rss_key_write(p, rss_obj_name, rss_key_size, rss_key);
+ if (status) {
+ snprintf(out, out_size, "Command failed.\n");
+ return;
+ }
+}
+
static const char cmd_pipeline_stats_help[] =
"pipeline <pipeline_name> stats\n";
@@ -3422,6 +3483,7 @@ cmd_help(char **tokens,
"\tpipeline meter reset\n"
"\tpipeline meter set\n"
"\tpipeline meter stats\n"
+ "\tpipeline rss\n"
"\tpipeline stats\n"
"\tpipeline mirror session\n"
"\tpipeline enable\n"
@@ -3641,6 +3703,12 @@ cmd_help(char **tokens,
return;
}
+ if (!strcmp(tokens[0], "pipeline") &&
+ (n_tokens == 2) && !strcmp(tokens[1], "rss")) {
+ snprintf(out, out_size, "\n%s\n", cmd_pipeline_rss_help);
+ return;
+ }
+
if ((strcmp(tokens[0], "pipeline") == 0) &&
(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
@@ -3915,6 +3983,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
return;
}
+ if (n_tokens >= 3 && !strcmp(tokens[2], "rss")) {
+ cmd_pipeline_rss(tokens, n_tokens, out, out_size, obj);
+ return;
+ }
+
if ((n_tokens >= 3) &&
(strcmp(tokens[2], "stats") == 0)) {
cmd_pipeline_stats(tokens, n_tokens, out, out_size,
--git a/examples/pipeline/examples/rss.cli b/examples/pipeline/examples/rss.cli
new file mode 100644
index 0000000000..350c49681c
--- /dev/null
+++ b/examples/pipeline/examples/rss.cli
@@ -0,0 +1,42 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2023 Intel Corporation
+
+# Example command line:
+# ./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/rss.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+# telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/rss.spec /tmp/rss.c
+pipeline libbuild /tmp/rss.c /tmp/rss.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/rss.so io ./examples/pipeline/examples/ethdev.io numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
+pipeline PIPELINE0 rss rss0 key 0 0 0 0
+
+;
+; Pipelines-to-threads mapping.
+;
+pipeline PIPELINE0 enable thread 1
--git a/examples/pipeline/examples/rss.spec b/examples/pipeline/examples/rss.spec
new file mode 100644
index 0000000000..486528c303
--- /dev/null
+++ b/examples/pipeline/examples/rss.spec
@@ -0,0 +1,84 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2023 Intel Corporation
+
+; This simple example illustrates how to compute an RSS hash signature over an n-tuple set of fields
+; read from the packet headers and/or the packet meta-data by using the "rss" instruction. In this
+; specific example, the n-tuple is the (IPv4 source address, IPv4 destination address) 2-tuple.
+
+//
+// Headers
+//
+struct ethernet_h {
+ bit<48> dst_addr
+ bit<48> src_addr
+ bit<16> ethertype
+}
+
+struct ipv4_h {
+ bit<8> ver_ihl
+ bit<8> diffserv
+ bit<16> total_len
+ bit<16> identification
+ bit<16> flags_offset
+ bit<8> ttl
+ bit<8> protocol
+ bit<16> hdr_checksum
+ bit<32> src_addr
+ bit<32> dst_addr
+}
+
+header ethernet instanceof ethernet_h
+header ipv4 instanceof ipv4_h
+
+//
+// Meta-data.
+//
+struct metadata_t {
+ bit<32> port
+ bit<32> hash
+}
+
+metadata instanceof metadata_t
+
+//
+// RSS.
+//
+rss rss0
+
+//
+// Pipeline.
+//
+apply {
+ //
+ // RX and parse.
+ //
+ rx m.port
+ extract h.ethernet
+ extract h.ipv4
+
+ //
+ // Compute the RSS hash over the n-tuple.
+ //
+ // Details:
+ // a) RSS object name: rss0;
+ // b) Destination (i.e. hash result): m.hash;
+ // c) Source (i.e. n-tuple to be hashed): The 2-tuple formed by the header fields
+ // (h.ipv4.src_addr, h.ipv4.dst_addr). Only the first and the last n-tuple fields are
+ // specified in the RSS instruction, but all the fields in between are part of the
+ // n-tuple to be hashed.
+ //
+ rss rss0 m.hash h.ipv4.src_addr h.ipv4.dst_addr
+
+ //
+ // Use the computed hash to create a uniform distribution of pkts across the 4 output ports.
+ //
+ and m.hash 3
+ mov m.port m.hash
+
+ //
+ // De-parse and TX.
+ //
+ emit h.ethernet
+ emit h.ipv4
+ tx m.port
+}
diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h
index 2eb51b2c76..6ef2551ab5 100644
--- a/lib/pipeline/rte_swx_ctl.h
+++ b/lib/pipeline/rte_swx_ctl.h
@@ -67,6 +67,9 @@ struct rte_swx_ctl_pipeline_info {
/** Number of meter arrays. */
uint32_t n_metarrays;
+
+ /** Number of RSS objects. */
+ uint32_t n_rss;
};
/**
@@ -1521,6 +1524,95 @@ rte_swx_ctl_meter_stats_read_with_key(struct rte_swx_pipeline *p,
uint8_t *table_key,
struct rte_swx_ctl_meter_stats *stats);
+/*
+ * RSS Query and Configuration API.
+ */
+
+/** RSS object info. */
+struct rte_swx_ctl_rss_info {
+ /** RSS object name. */
+ char name[RTE_SWX_CTL_NAME_SIZE];
+};
+
+/**
+ * RSS object info get
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_id
+ * RSS object ID (0 .. *n_rss* - 1).
+ * @param[out] rss
+ * RSS object info.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_rss_info_get(struct rte_swx_pipeline *p,
+ uint32_t rss_obj_id,
+ struct rte_swx_ctl_rss_info *rss);
+
+/**
+ * RSS object key size read
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_name
+ * RSS object name.
+ * @param[out] key_size
+ * RSS key size in bytes.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_rss_key_size_read(struct rte_swx_pipeline *p,
+ const char *rss_obj_name,
+ uint32_t *key_size);
+
+/**
+ * RSS object key read
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_name
+ * RSS object name.
+ * @param[out] key
+ * RSS key. Must be pre-allocated by the caller to store *key_size* bytes.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_rss_key_read(struct rte_swx_pipeline *p,
+ const char *rss_obj_name,
+ uint8_t *key);
+
+/**
+ * RSS object key write
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_name
+ * RSS object name.
+ * @param[in] key_size
+ * RSS key size in bytes. Must be at least 4 and a power of 2.
+ * @param[in] key
+ * RSS key.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_rss_key_write(struct rte_swx_pipeline *p,
+ const char *rss_obj_name,
+ uint32_t key_size,
+ uint8_t *key);
+
/**
* Pipeline control free
*
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 0e631dea2b..53d97f0072 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -1255,6 +1255,129 @@ hash_func_free(struct rte_swx_pipeline *p)
}
}
+/*
+ * RSS.
+ */
+static struct rss *
+rss_find(struct rte_swx_pipeline *p, const char *name)
+{
+ struct rss *elem;
+
+ TAILQ_FOREACH(elem, &p->rss, node)
+ if (strcmp(elem->name, name) == 0)
+ return elem;
+
+ return NULL;
+}
+
+static struct rss *
+rss_find_by_id(struct rte_swx_pipeline *p, uint32_t rss_obj_id)
+{
+ struct rss *elem;
+
+ TAILQ_FOREACH(elem, &p->rss, node)
+ if (elem->id == rss_obj_id)
+ return elem;
+
+ return NULL;
+}
+
+int
+rte_swx_pipeline_rss_config(struct rte_swx_pipeline *p, const char *name)
+{
+ struct rss *r;
+
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(name, EINVAL);
+ CHECK(!rss_find(p, name), EEXIST);
+
+ /* Memory allocation. */
+ r = calloc(1, sizeof(struct rss));
+ CHECK(r, ENOMEM);
+
+ /* Node initialization. */
+ strcpy(r->name, name);
+ r->id = p->n_rss;
+
+ /* Node add to tailq. */
+ TAILQ_INSERT_TAIL(&p->rss, r, node);
+ p->n_rss++;
+
+ return 0;
+}
+
+static void
+rss_build_free(struct rte_swx_pipeline *p)
+{
+ uint32_t i;
+
+ if (!p->rss_runtime)
+ return;
+
+ for (i = 0; i < p->n_rss; i++)
+ free(p->rss_runtime[i]);
+
+ free(p->rss_runtime);
+ p->rss_runtime = NULL;
+}
+
+static const struct {
+ uint32_t key_size;
+ uint8_t key[4];
+} rss_runtime_default = {
+ .key_size = 4,
+ .key = {0, 0, 0, 0},
+};
+
+static int
+rss_build(struct rte_swx_pipeline *p)
+{
+ uint32_t i;
+ int status = 0;
+
+ /* Memory allocation. */
+ p->rss_runtime = calloc(p->n_rss, sizeof(struct rss_runtime *));
+ if (!p->rss_runtime) {
+ status = -ENOMEM;
+ goto error;
+ }
+
+ /* RSS. */
+ for (i = 0; i < p->n_rss; i++) {
+ p->rss_runtime[i] = malloc(sizeof(rss_runtime_default));
+ if (!p->rss_runtime[i]) {
+ status = -ENOMEM;
+ goto error;
+ }
+
+ memcpy(p->rss_runtime[i], &rss_runtime_default, sizeof(rss_runtime_default));
+ }
+
+ return 0;
+
+error:
+ rss_build_free(p);
+ return status;
+}
+
+static void
+rss_free(struct rte_swx_pipeline *p)
+{
+ rss_build_free(p);
+
+ for ( ; ; ) {
+ struct rss *elem;
+
+ elem = TAILQ_FIRST(&p->rss);
+ if (!elem)
+ break;
+
+ TAILQ_REMOVE(&p->rss, elem, node);
+ free(elem);
+ }
+}
+
/*
* Header.
*/
@@ -3002,6 +3125,63 @@ instr_hash_func_exec(struct rte_swx_pipeline *p)
thread_ip_inc(p);
}
+/*
+ * rss.
+ */
+static int
+instr_rss_translate(struct rte_swx_pipeline *p,
+ struct action *action,
+ char **tokens,
+ int n_tokens,
+ struct instruction *instr,
+ struct instruction_data *data __rte_unused)
+{
+ struct rss *rss;
+ struct field *dst, *src_first, *src_last;
+ uint32_t src_struct_id_first = 0, src_struct_id_last = 0;
+
+ CHECK(n_tokens == 5, EINVAL);
+
+ rss = rss_find(p, tokens[1]);
+ CHECK(rss, EINVAL);
+
+ dst = metadata_field_parse(p, tokens[2]);
+ CHECK(dst, EINVAL);
+ CHECK(dst->n_bits <= 64, 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_last->var_size, EINVAL);
+ CHECK(src_struct_id_first == src_struct_id_last, EINVAL);
+
+ instr->type = INSTR_RSS;
+ instr->rss.rss_obj_id = (uint8_t)rss->id;
+ instr->rss.dst.offset = (uint8_t)dst->offset / 8;
+ instr->rss.dst.n_bits = (uint8_t)dst->n_bits;
+ instr->rss.src.struct_id = (uint8_t)src_struct_id_first;
+ instr->rss.src.offset = (uint16_t)src_first->offset / 8;
+ instr->rss.src.n_bytes = (uint16_t)((src_last->offset + src_last->n_bits -
+ src_first->offset) / 8);
+
+ return 0;
+}
+
+static inline void
+instr_rss_exec(struct rte_swx_pipeline *p)
+{
+ struct thread *t = &p->threads[p->thread_id];
+ struct instruction *ip = t->ip;
+
+ /* Extern function execute. */
+ __instr_rss_exec(p, t, ip);
+
+ /* Thread. */
+ thread_ip_inc(p);
+}
+
/*
* mov.
*/
@@ -6397,6 +6577,14 @@ instr_translate(struct rte_swx_pipeline *p,
instr,
data);
+ if (!strcmp(tokens[tpos], "rss"))
+ return instr_rss_translate(p,
+ action,
+ &tokens[tpos],
+ n_tokens - tpos,
+ instr,
+ data);
+
if (!strcmp(tokens[tpos], "jmp"))
return instr_jmp_translate(p,
action,
@@ -7371,6 +7559,7 @@ static instr_exec_t instruction_table[] = {
[INSTR_EXTERN_OBJ] = instr_extern_obj_exec,
[INSTR_EXTERN_FUNC] = instr_extern_func_exec,
[INSTR_HASH_FUNC] = instr_hash_func_exec,
+ [INSTR_RSS] = instr_rss_exec,
[INSTR_JMP] = instr_jmp_exec,
[INSTR_JMP_VALID] = instr_jmp_valid_exec,
@@ -10001,6 +10190,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)
instruction_table_free(p);
metadata_free(p);
header_free(p);
+ rss_free(p);
hash_func_free(p);
extern_func_free(p);
extern_obj_free(p);
@@ -10149,6 +10339,7 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, const char *name, int numa_
TAILQ_INIT(&pipeline->extern_objs);
TAILQ_INIT(&pipeline->extern_funcs);
TAILQ_INIT(&pipeline->hash_funcs);
+ TAILQ_INIT(&pipeline->rss);
TAILQ_INIT(&pipeline->headers);
TAILQ_INIT(&pipeline->actions);
TAILQ_INIT(&pipeline->table_types);
@@ -10263,6 +10454,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
if (status)
goto error;
+ status = rss_build(p);
+ if (status)
+ goto error;
+
status = header_build(p);
if (status)
goto error;
@@ -10318,6 +10513,7 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
instruction_table_build_free(p);
metadata_build_free(p);
header_build_free(p);
+ rss_build_free(p);
hash_func_build_free(p);
extern_func_build_free(p);
extern_obj_build_free(p);
@@ -10382,6 +10578,7 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
pipeline->n_learners = p->n_learners;
pipeline->n_regarrays = p->n_regarrays;
pipeline->n_metarrays = p->n_metarrays;
+ pipeline->n_rss = p->n_rss;
return 0;
}
@@ -11406,6 +11603,112 @@ rte_swx_ctl_meter_stats_read_with_key(struct rte_swx_pipeline *p,
return rte_swx_ctl_meter_stats_read(p, metarray_name, entry_id, stats);
}
+int
+rte_swx_ctl_rss_info_get(struct rte_swx_pipeline *p,
+ uint32_t rss_obj_id,
+ struct rte_swx_ctl_rss_info *info)
+{
+ struct rss *rss;
+
+ /* Check the input arguments. */
+ if (!p || !info)
+ return -EINVAL;
+
+ rss = rss_find_by_id(p, rss_obj_id);
+ if (!rss)
+ return -EINVAL;
+
+ /* Read from the internal data structures. */
+ strcpy(info->name, rss->name);
+ return 0;
+}
+
+int
+rte_swx_ctl_pipeline_rss_key_size_read(struct rte_swx_pipeline *p,
+ const char *rss_name,
+ uint32_t *key_size)
+{
+ struct rss *rss;
+ struct rss_runtime *r;
+
+ /* Check the input arguments. */
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(rss_name, EINVAL);
+ rss = rss_find(p, rss_name);
+ CHECK(rss, EINVAL);
+ r = p->rss_runtime[rss->id];
+
+ CHECK(key_size, EINVAL);
+
+ /* Read from the internal data structures. */
+ *key_size = r->key_size;
+
+ return 0;
+}
+
+int
+rte_swx_ctl_pipeline_rss_key_read(struct rte_swx_pipeline *p,
+ const char *rss_name,
+ uint8_t *key)
+{
+ struct rss *rss;
+ struct rss_runtime *r;
+
+ /* Check the input arguments. */
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(rss_name, EINVAL);
+ rss = rss_find(p, rss_name);
+ CHECK(rss, EINVAL);
+ r = p->rss_runtime[rss->id];
+
+ CHECK(key, EINVAL);
+
+ /* Read from the internal data structures. */
+ memcpy(key, r->key, r->key_size);
+
+ return 0;
+}
+
+int
+rte_swx_ctl_pipeline_rss_key_write(struct rte_swx_pipeline *p,
+ const char *rss_name,
+ uint32_t key_size,
+ uint8_t *key)
+{
+ struct rss *rss;
+ struct rss_runtime *r, *r_new;
+
+ /* Check the input arguments. */
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(rss_name, EINVAL);
+ rss = rss_find(p, rss_name);
+ CHECK(rss, EINVAL);
+ r = p->rss_runtime[rss->id];
+
+ CHECK(key_size >= 4, EINVAL);
+ CHECK(key, EINVAL);
+
+ /* Allocate new RSS run-time entry. */
+ r_new = malloc(sizeof(struct rss_runtime) + key_size * sizeof(uint32_t));
+ if (!r_new)
+ return -ENOMEM;
+
+ /* Fill in the new RSS run-time entry. */
+ r_new->key_size = key_size;
+ memcpy(r_new->key, key, key_size);
+
+ /* Commit the RSS run-time change atomically. */
+ p->rss_runtime[rss->id] = r_new;
+
+ /* Free the old RSS run-time entry. */
+ free(r);
+
+ return 0;
+}
+
/*
* Pipeline compilation.
*/
@@ -11579,6 +11882,7 @@ instr_type_to_name(struct instruction *instr)
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_RSS: return "INSTR_RSS";
case INSTR_JMP: return "INSTR_JMP";
case INSTR_JMP_VALID: return "INSTR_JMP_VALID";
@@ -12029,6 +12333,34 @@ instr_hash_export(struct instruction *instr, FILE *f)
instr->hash_func.src.n_bytes);
}
+static void
+instr_rss_export(struct instruction *instr, FILE *f)
+{
+ fprintf(f,
+ "\t{\n"
+ "\t\t.type = %s,\n"
+ "\t\t.rss = {\n"
+ "\t\t\t.rss_obj_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->rss.rss_obj_id,
+ instr->rss.dst.offset,
+ instr->rss.dst.n_bits,
+ instr->rss.src.struct_id,
+ instr->rss.src.offset,
+ instr->rss.src.n_bytes);
+}
+
static void
instr_reg_export(struct instruction *instr __rte_unused, FILE *f __rte_unused)
{
@@ -12590,6 +12922,7 @@ static instruction_export_t export_table[] = {
[INSTR_EXTERN_OBJ] = instr_extern_export,
[INSTR_EXTERN_FUNC] = instr_extern_export,
[INSTR_HASH_FUNC] = instr_hash_export,
+ [INSTR_RSS] = instr_rss_export,
[INSTR_JMP] = instr_jmp_export,
[INSTR_JMP_VALID] = instr_jmp_export,
@@ -12817,6 +13150,7 @@ instr_type_to_func(struct instruction *instr)
case INSTR_EXTERN_OBJ: return NULL;
case INSTR_EXTERN_FUNC: return NULL;
case INSTR_HASH_FUNC: return "__instr_hash_func_exec";
+ case INSTR_RSS: return "__instr_rss_exec";
case INSTR_JMP: return NULL;
case INSTR_JMP_VALID: return NULL;
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index 2c9cc6ee44..25df042d3b 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -352,6 +352,28 @@ rte_swx_pipeline_hash_func_register(struct rte_swx_pipeline *p,
const char *name,
rte_swx_hash_func_t func);
+/*
+ * RSS.
+ */
+
+/**
+ * Pipeline Receive Side Scaling (RSS) object configure
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] name
+ * Name for the new RSS object.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument;
+ * -ENOMEM: Not enough space/cannot allocate memory;
+ * -EEXIST: RSS object with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_rss_config(struct rte_swx_pipeline *p,
+ const char *name);
+
/*
* Packet headers and meta-data
*/
diff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h
index 335506039b..0c29362d31 100644
--- a/lib/pipeline/rte_swx_pipeline_internal.h
+++ b/lib/pipeline/rte_swx_pipeline_internal.h
@@ -199,6 +199,22 @@ struct hash_func_runtime {
rte_swx_hash_func_t func;
};
+/*
+ * RSS.
+ */
+struct rss {
+ TAILQ_ENTRY(rss) node;
+ char name[RTE_SWX_NAME_SIZE];
+ uint32_t id;
+};
+
+TAILQ_HEAD(rss_tailq, rss);
+
+struct rss_runtime {
+ uint32_t key_size; /* key size in bytes. */
+ uint8_t key[0]; /* key. */
+};
+
/*
* Header.
*/
@@ -524,6 +540,15 @@ enum instruction_type {
*/
INSTR_HASH_FUNC,
+ /* rss RSS_OBJ_NAME dst src_first src_last
+ * Compute the RSS hash value over range of struct fields.
+ * dst = M
+ * src_first = HMEFT
+ * src_last = HMEFT
+ * src_first and src_last must be fields within the same struct
+ */
+ INSTR_RSS,
+
/* jmp LABEL
* Unconditional jump
*/
@@ -677,6 +702,21 @@ struct instr_hash_func {
} src;
};
+struct instr_rss {
+ uint8_t rss_obj_id;
+
+ struct {
+ uint8_t offset;
+ uint8_t n_bits;
+ } dst;
+
+ struct {
+ uint8_t struct_id;
+ uint16_t offset;
+ uint16_t n_bytes;
+ } src;
+};
+
struct instr_dst_src {
struct instr_operand dst;
union {
@@ -763,6 +803,7 @@ struct instruction {
struct instr_extern_obj ext_obj;
struct instr_extern_func ext_func;
struct instr_hash_func hash_func;
+ struct instr_rss rss;
struct instr_jmp jmp;
};
};
@@ -1480,6 +1521,7 @@ struct rte_swx_pipeline {
struct extern_obj_tailq extern_objs;
struct extern_func_tailq extern_funcs;
struct hash_func_tailq hash_funcs;
+ struct rss_tailq rss;
struct header_tailq headers;
struct struct_type *metadata_st;
uint32_t metadata_struct_id;
@@ -1502,6 +1544,7 @@ struct rte_swx_pipeline {
struct selector_statistics *selector_stats;
struct learner_statistics *learner_stats;
struct hash_func_runtime *hash_func_runtime;
+ struct rss_runtime **rss_runtime;
struct regarray_runtime *regarray_runtime;
struct metarray_runtime *metarray_runtime;
struct instruction *instructions;
@@ -1518,6 +1561,7 @@ struct rte_swx_pipeline {
uint32_t n_extern_objs;
uint32_t n_extern_funcs;
uint32_t n_hash_funcs;
+ uint32_t n_rss;
uint32_t n_actions;
uint32_t n_tables;
uint32_t n_selectors;
@@ -2467,6 +2511,58 @@ __instr_hash_func_exec(struct rte_swx_pipeline *p,
METADATA_WRITE(t, dst_offset, n_dst_bits, result);
}
+/*
+ * rss.
+ */
+static inline uint32_t
+rss_func(void *rss_key, uint32_t rss_key_size, void *input_data, uint32_t input_data_size)
+{
+ uint32_t *key = (uint32_t *)rss_key;
+ uint32_t *data = (uint32_t *)input_data;
+ uint32_t key_size = rss_key_size >> 2;
+ uint32_t data_size = input_data_size >> 2;
+ uint32_t hash_val = 0, i;
+
+ for (i = 0; i < data_size; i++) {
+ uint32_t d;
+
+ for (d = data[i]; d; d &= (d - 1)) {
+ uint32_t key0, key1, pos;
+
+ pos = rte_bsf32(d);
+ key0 = key[i % key_size] << (31 - pos);
+ key1 = key[(i + 1) % key_size] >> (pos + 1);
+ hash_val ^= key0 | key1;
+ }
+ }
+
+ return hash_val;
+}
+
+static inline void
+__instr_rss_exec(struct rte_swx_pipeline *p,
+ struct thread *t,
+ const struct instruction *ip)
+{
+ uint32_t rss_obj_id = ip->rss.rss_obj_id;
+ uint32_t dst_offset = ip->rss.dst.offset;
+ uint32_t n_dst_bits = ip->rss.dst.n_bits;
+ uint32_t src_struct_id = ip->rss.src.struct_id;
+ uint32_t src_offset = ip->rss.src.offset;
+ uint32_t n_src_bytes = ip->rss.src.n_bytes;
+
+ struct rss_runtime *r = p->rss_runtime[rss_obj_id];
+ uint8_t *src_ptr = t->structs[src_struct_id];
+ uint32_t result;
+
+ TRACE("[Thread %2u] rss %u\n",
+ p->thread_id,
+ rss_obj_id);
+
+ result = rss_func(r->key, r->key_size, &src_ptr[src_offset], n_src_bytes);
+ METADATA_WRITE(t, dst_offset, n_dst_bits, result);
+}
+
/*
* mov.
*/
diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 9116f38ed2..4c2a8ca191 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2049,6 +2049,52 @@ metarray_statement_parse(struct metarray_spec *s,
return 0;
}
+/*
+ *
+ * rss
+ *
+ */
+
+static void
+rss_spec_free(struct rss_spec *s)
+{
+ if (!s)
+ return;
+
+ free(s->name);
+ s->name = NULL;
+}
+
+static int
+rss_statement_parse(struct rss_spec *s,
+ char **tokens,
+ uint32_t n_tokens,
+ uint32_t n_lines,
+ uint32_t *err_line,
+ const char **err_msg)
+{
+ /* Check format. */
+ if ((n_tokens != 2)) {
+ if (err_line)
+ *err_line = n_lines;
+ if (err_msg)
+ *err_msg = "Invalid rss statement.";
+ return -EINVAL;
+ }
+
+ /* spec. */
+ s->name = strdup(tokens[1]);
+ if (!s->name) {
+ if (err_line)
+ *err_line = n_lines;
+ if (err_msg)
+ *err_msg = "Memory allocation failed.";
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
/*
* apply.
*
@@ -2243,6 +2289,17 @@ pipeline_spec_codegen(FILE *f,
fprintf(f, "};\n\n");
+ /* rss. */
+ fprintf(f, "static struct rss_spec rss[] = {\n");
+
+ for (i = 0; i < s->n_rss; i++) {
+ struct rss_spec *rss_spec = &s->rss[i];
+ fprintf(f, "\t[%d] = {\n", i);
+ fprintf(f, "\t\t.name = \"%s\",\n", rss_spec->name);
+ fprintf(f, "\t},\n");
+ }
+ fprintf(f, "};\n\n");
+
/* struct. */
for (i = 0; i < s->n_structs; i++) {
struct struct_spec *struct_spec = &s->structs[i];
@@ -2794,6 +2851,7 @@ pipeline_spec_codegen(FILE *f,
fprintf(f, "\t.learners = learners,\n");
fprintf(f, "\t.regarrays = regarrays,\n");
fprintf(f, "\t.metarrays = metarrays,\n");
+ fprintf(f, "\t.rss = rss,\n");
fprintf(f, "\t.apply = apply,\n");
fprintf(f, "\t.n_extobjs = sizeof(extobjs) / sizeof(extobjs[0]),\n");
fprintf(f, "\t.n_structs = sizeof(structs) / sizeof(structs[0]),\n");
@@ -2805,6 +2863,7 @@ pipeline_spec_codegen(FILE *f,
fprintf(f, "\t.n_learners = sizeof(learners) / sizeof(learners[0]),\n");
fprintf(f, "\t.n_regarrays = sizeof(regarrays) / sizeof(regarrays[0]),\n");
fprintf(f, "\t.n_metarrays = sizeof(metarrays) / sizeof(metarrays[0]),\n");
+ fprintf(f, "\t.n_rss = sizeof(rss) / sizeof(rss[0]),\n");
fprintf(f, "\t.n_apply = sizeof(apply) / sizeof(apply[0]),\n");
fprintf(f, "};\n");
}
@@ -2824,6 +2883,7 @@ pipeline_spec_parse(FILE *spec,
struct learner_spec learner_spec = {0};
struct regarray_spec regarray_spec = {0};
struct metarray_spec metarray_spec = {0};
+ struct rss_spec rss_spec = {0};
struct apply_spec apply_spec = {0};
struct pipeline_spec *s = NULL;
uint32_t n_lines = 0;
@@ -3372,6 +3432,40 @@ pipeline_spec_parse(FILE *spec,
continue;
}
+ /* rss object configuration */
+ if (!strcmp(tokens[0], "rss")) {
+ struct rss_spec *new_rss;
+
+ status = rss_statement_parse(&rss_spec,
+ tokens,
+ n_tokens,
+ n_lines,
+ err_line,
+ err_msg);
+ if (status)
+ goto error;
+
+ new_rss = realloc(s->rss,
+ (s->n_rss + 1) * sizeof(struct rss_spec));
+ if (!new_rss) {
+ if (err_line)
+ *err_line = n_lines;
+ if (err_msg)
+ *err_msg = "Memory allocation failed.";
+ status = -ENOMEM;
+ goto error;
+ }
+
+ s->rss = new_rss;
+ memcpy(&s->rss[s->n_rss],
+ &rss_spec,
+ sizeof(struct rss_spec));
+ s->n_rss++;
+ memset(&rss_spec, 0, sizeof(struct rss_spec));
+
+ continue;
+ }
+
/* apply. */
if (!strcmp(tokens[0], "apply")) {
status = apply_statement_parse(&block_mask,
@@ -3418,6 +3512,7 @@ pipeline_spec_parse(FILE *spec,
learner_spec_free(&learner_spec);
regarray_spec_free(®array_spec);
metarray_spec_free(&metarray_spec);
+ rss_spec_free(&rss_spec);
apply_spec_free(&apply_spec);
pipeline_spec_free(s);
@@ -3583,6 +3678,18 @@ pipeline_spec_configure(struct rte_swx_pipeline *p,
}
}
+ /* rss. */
+ for (i = 0; i < s->n_rss; i++) {
+ struct rss_spec *rss_spec = &s->rss[i];
+
+ status = rte_swx_pipeline_rss_config(p, rss_spec->name);
+ if (status) {
+ if (err_msg)
+ *err_msg = "rss object configuration error.";
+ return status;
+ }
+ }
+
/* apply. */
for (i = 0; i < s->n_apply; i++) {
struct apply_spec *apply_spec = &s->apply[i];
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 123e175f8b..dc2ff6fa08 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -171,6 +171,15 @@ struct metarray_spec {
uint32_t size;
};
+/*
+ * rss.
+ *
+ * rss NAME
+ */
+struct rss_spec {
+ char *name;
+};
+
/*
* apply.
*
@@ -198,6 +207,7 @@ struct pipeline_spec {
struct learner_spec *learners;
struct regarray_spec *regarrays;
struct metarray_spec *metarrays;
+ struct rss_spec *rss;
struct apply_spec *apply;
uint32_t n_extobjs;
@@ -210,6 +220,7 @@ struct pipeline_spec {
uint32_t n_learners;
uint32_t n_regarrays;
uint32_t n_metarrays;
+ uint32_t n_rss;
uint32_t n_apply;
};
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 58e34459c3..6f45c73428 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -157,6 +157,10 @@ EXPERIMENTAL {
rte_swx_pipeline_find;
#added in 23.03
+ rte_swx_ctl_rss_info_get;
+ rte_swx_ctl_pipeline_rss_key_size_read;
+ rte_swx_ctl_pipeline_rss_key_read;
+ rte_swx_ctl_pipeline_rss_key_write;
rte_swx_ipsec_create;
rte_swx_ipsec_find;
rte_swx_ipsec_free;
@@ -164,4 +168,5 @@ EXPERIMENTAL {
rte_swx_ipsec_sa_add;
rte_swx_ipsec_sa_delete;
rte_swx_ipsec_sa_read;
+ rte_swx_pipeline_rss_config;
};
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH V3] pipeline: add RSS support
2023-02-03 19:37 [PATCH] pipeline: add RSS support Cristian Dumitrescu
2023-02-07 15:29 ` [PATCH V2] " Cristian Dumitrescu
@ 2023-02-07 15:43 ` Cristian Dumitrescu
2023-02-07 16:41 ` [PATCH V4] " Cristian Dumitrescu
2 siblings, 0 replies; 5+ messages in thread
From: Cristian Dumitrescu @ 2023-02-07 15:43 UTC (permalink / raw)
To: dev; +Cc: Kamalakannan R
Add pipeline support for the Receive Side Scaling (RSS) hashing. While
the pipeline already supports the stateless hashing schemes, the RSS
scheme uses a key configured by the control plane and preserved
between successive RSS hash invocations.
Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
Change log:
V3:
-fixed minor style issues
V2:
-added CLI command and enxample file for the examples/pipeline
application
examples/pipeline/cli.c | 73 +++++
| 42 +++
| 84 ++++++
lib/pipeline/rte_swx_ctl.h | 92 +++++++
lib/pipeline/rte_swx_pipeline.c | 334 +++++++++++++++++++++++
lib/pipeline/rte_swx_pipeline.h | 22 ++
lib/pipeline/rte_swx_pipeline_internal.h | 96 +++++++
lib/pipeline/rte_swx_pipeline_spec.c | 107 ++++++++
lib/pipeline/rte_swx_pipeline_spec.h | 11 +
lib/pipeline/version.map | 5 +
10 files changed, 866 insertions(+)
create mode 100644 examples/pipeline/examples/rss.cli
create mode 100644 examples/pipeline/examples/rss.spec
diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 87f9c0370d..2ae6cc579f 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -2622,6 +2622,67 @@ cmd_pipeline_meter_stats(char **tokens,
return;
}
+static const char cmd_pipeline_rss_help[] =
+"pipeline <pipeline_name> rss <rss_obj_name> key <key_byte0> ...\n";
+
+static void
+cmd_pipeline_rss(char **tokens,
+ uint32_t n_tokens,
+ char *out,
+ size_t out_size,
+ void *obj __rte_unused)
+{
+ uint8_t rss_key[CMD_MAX_TOKENS];
+ struct rte_swx_pipeline *p;
+ const char *rss_obj_name;
+ uint32_t rss_key_size, i;
+ int status;
+
+ if (n_tokens < 6) {
+ snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+ return;
+ }
+
+ p = rte_swx_pipeline_find(tokens[1]);
+ if (!p) {
+ snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+ return;
+ }
+
+ if (strcmp(tokens[2], "rss")) {
+ snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
+ return;
+ }
+
+ rss_obj_name = tokens[3];
+
+ if (strcmp(tokens[4], "key")) {
+ snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
+ return;
+ }
+
+ tokens += 5;
+ n_tokens -= 5;
+ rss_key_size = n_tokens;
+
+ for (i = 0; i < rss_key_size; i++) {
+ uint32_t key_byte;
+
+ if (parser_read_uint32(&key_byte, tokens[i]) || (key_byte >= UINT8_MAX)) {
+ snprintf(out, out_size, MSG_ARG_INVALID, "key byte");
+ return;
+ }
+
+ rss_key[i] = (uint8_t)key_byte;
+ }
+
+ status = rte_swx_ctl_pipeline_rss_key_write(p, rss_obj_name, rss_key_size, rss_key);
+ if (status) {
+ snprintf(out, out_size, "Command failed.\n");
+ return;
+ }
+}
+
static const char cmd_pipeline_stats_help[] =
"pipeline <pipeline_name> stats\n";
@@ -3422,6 +3483,7 @@ cmd_help(char **tokens,
"\tpipeline meter reset\n"
"\tpipeline meter set\n"
"\tpipeline meter stats\n"
+ "\tpipeline rss\n"
"\tpipeline stats\n"
"\tpipeline mirror session\n"
"\tpipeline enable\n"
@@ -3641,6 +3703,12 @@ cmd_help(char **tokens,
return;
}
+ if (!strcmp(tokens[0], "pipeline") &&
+ (n_tokens == 2) && !strcmp(tokens[1], "rss")) {
+ snprintf(out, out_size, "\n%s\n", cmd_pipeline_rss_help);
+ return;
+ }
+
if ((strcmp(tokens[0], "pipeline") == 0) &&
(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
@@ -3915,6 +3983,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
return;
}
+ if (n_tokens >= 3 && !strcmp(tokens[2], "rss")) {
+ cmd_pipeline_rss(tokens, n_tokens, out, out_size, obj);
+ return;
+ }
+
if ((n_tokens >= 3) &&
(strcmp(tokens[2], "stats") == 0)) {
cmd_pipeline_stats(tokens, n_tokens, out, out_size,
--git a/examples/pipeline/examples/rss.cli b/examples/pipeline/examples/rss.cli
new file mode 100644
index 0000000000..350c49681c
--- /dev/null
+++ b/examples/pipeline/examples/rss.cli
@@ -0,0 +1,42 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2023 Intel Corporation
+
+# Example command line:
+# ./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/rss.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+# telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/rss.spec /tmp/rss.c
+pipeline libbuild /tmp/rss.c /tmp/rss.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/rss.so io ./examples/pipeline/examples/ethdev.io numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
+pipeline PIPELINE0 rss rss0 key 0 0 0 0
+
+;
+; Pipelines-to-threads mapping.
+;
+pipeline PIPELINE0 enable thread 1
--git a/examples/pipeline/examples/rss.spec b/examples/pipeline/examples/rss.spec
new file mode 100644
index 0000000000..486528c303
--- /dev/null
+++ b/examples/pipeline/examples/rss.spec
@@ -0,0 +1,84 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2023 Intel Corporation
+
+; This simple example illustrates how to compute an RSS hash signature over an n-tuple set of fields
+; read from the packet headers and/or the packet meta-data by using the "rss" instruction. In this
+; specific example, the n-tuple is the (IPv4 source address, IPv4 destination address) 2-tuple.
+
+//
+// Headers
+//
+struct ethernet_h {
+ bit<48> dst_addr
+ bit<48> src_addr
+ bit<16> ethertype
+}
+
+struct ipv4_h {
+ bit<8> ver_ihl
+ bit<8> diffserv
+ bit<16> total_len
+ bit<16> identification
+ bit<16> flags_offset
+ bit<8> ttl
+ bit<8> protocol
+ bit<16> hdr_checksum
+ bit<32> src_addr
+ bit<32> dst_addr
+}
+
+header ethernet instanceof ethernet_h
+header ipv4 instanceof ipv4_h
+
+//
+// Meta-data.
+//
+struct metadata_t {
+ bit<32> port
+ bit<32> hash
+}
+
+metadata instanceof metadata_t
+
+//
+// RSS.
+//
+rss rss0
+
+//
+// Pipeline.
+//
+apply {
+ //
+ // RX and parse.
+ //
+ rx m.port
+ extract h.ethernet
+ extract h.ipv4
+
+ //
+ // Compute the RSS hash over the n-tuple.
+ //
+ // Details:
+ // a) RSS object name: rss0;
+ // b) Destination (i.e. hash result): m.hash;
+ // c) Source (i.e. n-tuple to be hashed): The 2-tuple formed by the header fields
+ // (h.ipv4.src_addr, h.ipv4.dst_addr). Only the first and the last n-tuple fields are
+ // specified in the RSS instruction, but all the fields in between are part of the
+ // n-tuple to be hashed.
+ //
+ rss rss0 m.hash h.ipv4.src_addr h.ipv4.dst_addr
+
+ //
+ // Use the computed hash to create a uniform distribution of pkts across the 4 output ports.
+ //
+ and m.hash 3
+ mov m.port m.hash
+
+ //
+ // De-parse and TX.
+ //
+ emit h.ethernet
+ emit h.ipv4
+ tx m.port
+}
diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h
index 2eb51b2c76..6ef2551ab5 100644
--- a/lib/pipeline/rte_swx_ctl.h
+++ b/lib/pipeline/rte_swx_ctl.h
@@ -67,6 +67,9 @@ struct rte_swx_ctl_pipeline_info {
/** Number of meter arrays. */
uint32_t n_metarrays;
+
+ /** Number of RSS objects. */
+ uint32_t n_rss;
};
/**
@@ -1521,6 +1524,95 @@ rte_swx_ctl_meter_stats_read_with_key(struct rte_swx_pipeline *p,
uint8_t *table_key,
struct rte_swx_ctl_meter_stats *stats);
+/*
+ * RSS Query and Configuration API.
+ */
+
+/** RSS object info. */
+struct rte_swx_ctl_rss_info {
+ /** RSS object name. */
+ char name[RTE_SWX_CTL_NAME_SIZE];
+};
+
+/**
+ * RSS object info get
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_id
+ * RSS object ID (0 .. *n_rss* - 1).
+ * @param[out] rss
+ * RSS object info.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_rss_info_get(struct rte_swx_pipeline *p,
+ uint32_t rss_obj_id,
+ struct rte_swx_ctl_rss_info *rss);
+
+/**
+ * RSS object key size read
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_name
+ * RSS object name.
+ * @param[out] key_size
+ * RSS key size in bytes.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_rss_key_size_read(struct rte_swx_pipeline *p,
+ const char *rss_obj_name,
+ uint32_t *key_size);
+
+/**
+ * RSS object key read
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_name
+ * RSS object name.
+ * @param[out] key
+ * RSS key. Must be pre-allocated by the caller to store *key_size* bytes.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_rss_key_read(struct rte_swx_pipeline *p,
+ const char *rss_obj_name,
+ uint8_t *key);
+
+/**
+ * RSS object key write
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_name
+ * RSS object name.
+ * @param[in] key_size
+ * RSS key size in bytes. Must be at least 4 and a power of 2.
+ * @param[in] key
+ * RSS key.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_rss_key_write(struct rte_swx_pipeline *p,
+ const char *rss_obj_name,
+ uint32_t key_size,
+ uint8_t *key);
+
/**
* Pipeline control free
*
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 0e631dea2b..53d97f0072 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -1255,6 +1255,129 @@ hash_func_free(struct rte_swx_pipeline *p)
}
}
+/*
+ * RSS.
+ */
+static struct rss *
+rss_find(struct rte_swx_pipeline *p, const char *name)
+{
+ struct rss *elem;
+
+ TAILQ_FOREACH(elem, &p->rss, node)
+ if (strcmp(elem->name, name) == 0)
+ return elem;
+
+ return NULL;
+}
+
+static struct rss *
+rss_find_by_id(struct rte_swx_pipeline *p, uint32_t rss_obj_id)
+{
+ struct rss *elem;
+
+ TAILQ_FOREACH(elem, &p->rss, node)
+ if (elem->id == rss_obj_id)
+ return elem;
+
+ return NULL;
+}
+
+int
+rte_swx_pipeline_rss_config(struct rte_swx_pipeline *p, const char *name)
+{
+ struct rss *r;
+
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(name, EINVAL);
+ CHECK(!rss_find(p, name), EEXIST);
+
+ /* Memory allocation. */
+ r = calloc(1, sizeof(struct rss));
+ CHECK(r, ENOMEM);
+
+ /* Node initialization. */
+ strcpy(r->name, name);
+ r->id = p->n_rss;
+
+ /* Node add to tailq. */
+ TAILQ_INSERT_TAIL(&p->rss, r, node);
+ p->n_rss++;
+
+ return 0;
+}
+
+static void
+rss_build_free(struct rte_swx_pipeline *p)
+{
+ uint32_t i;
+
+ if (!p->rss_runtime)
+ return;
+
+ for (i = 0; i < p->n_rss; i++)
+ free(p->rss_runtime[i]);
+
+ free(p->rss_runtime);
+ p->rss_runtime = NULL;
+}
+
+static const struct {
+ uint32_t key_size;
+ uint8_t key[4];
+} rss_runtime_default = {
+ .key_size = 4,
+ .key = {0, 0, 0, 0},
+};
+
+static int
+rss_build(struct rte_swx_pipeline *p)
+{
+ uint32_t i;
+ int status = 0;
+
+ /* Memory allocation. */
+ p->rss_runtime = calloc(p->n_rss, sizeof(struct rss_runtime *));
+ if (!p->rss_runtime) {
+ status = -ENOMEM;
+ goto error;
+ }
+
+ /* RSS. */
+ for (i = 0; i < p->n_rss; i++) {
+ p->rss_runtime[i] = malloc(sizeof(rss_runtime_default));
+ if (!p->rss_runtime[i]) {
+ status = -ENOMEM;
+ goto error;
+ }
+
+ memcpy(p->rss_runtime[i], &rss_runtime_default, sizeof(rss_runtime_default));
+ }
+
+ return 0;
+
+error:
+ rss_build_free(p);
+ return status;
+}
+
+static void
+rss_free(struct rte_swx_pipeline *p)
+{
+ rss_build_free(p);
+
+ for ( ; ; ) {
+ struct rss *elem;
+
+ elem = TAILQ_FIRST(&p->rss);
+ if (!elem)
+ break;
+
+ TAILQ_REMOVE(&p->rss, elem, node);
+ free(elem);
+ }
+}
+
/*
* Header.
*/
@@ -3002,6 +3125,63 @@ instr_hash_func_exec(struct rte_swx_pipeline *p)
thread_ip_inc(p);
}
+/*
+ * rss.
+ */
+static int
+instr_rss_translate(struct rte_swx_pipeline *p,
+ struct action *action,
+ char **tokens,
+ int n_tokens,
+ struct instruction *instr,
+ struct instruction_data *data __rte_unused)
+{
+ struct rss *rss;
+ struct field *dst, *src_first, *src_last;
+ uint32_t src_struct_id_first = 0, src_struct_id_last = 0;
+
+ CHECK(n_tokens == 5, EINVAL);
+
+ rss = rss_find(p, tokens[1]);
+ CHECK(rss, EINVAL);
+
+ dst = metadata_field_parse(p, tokens[2]);
+ CHECK(dst, EINVAL);
+ CHECK(dst->n_bits <= 64, 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_last->var_size, EINVAL);
+ CHECK(src_struct_id_first == src_struct_id_last, EINVAL);
+
+ instr->type = INSTR_RSS;
+ instr->rss.rss_obj_id = (uint8_t)rss->id;
+ instr->rss.dst.offset = (uint8_t)dst->offset / 8;
+ instr->rss.dst.n_bits = (uint8_t)dst->n_bits;
+ instr->rss.src.struct_id = (uint8_t)src_struct_id_first;
+ instr->rss.src.offset = (uint16_t)src_first->offset / 8;
+ instr->rss.src.n_bytes = (uint16_t)((src_last->offset + src_last->n_bits -
+ src_first->offset) / 8);
+
+ return 0;
+}
+
+static inline void
+instr_rss_exec(struct rte_swx_pipeline *p)
+{
+ struct thread *t = &p->threads[p->thread_id];
+ struct instruction *ip = t->ip;
+
+ /* Extern function execute. */
+ __instr_rss_exec(p, t, ip);
+
+ /* Thread. */
+ thread_ip_inc(p);
+}
+
/*
* mov.
*/
@@ -6397,6 +6577,14 @@ instr_translate(struct rte_swx_pipeline *p,
instr,
data);
+ if (!strcmp(tokens[tpos], "rss"))
+ return instr_rss_translate(p,
+ action,
+ &tokens[tpos],
+ n_tokens - tpos,
+ instr,
+ data);
+
if (!strcmp(tokens[tpos], "jmp"))
return instr_jmp_translate(p,
action,
@@ -7371,6 +7559,7 @@ static instr_exec_t instruction_table[] = {
[INSTR_EXTERN_OBJ] = instr_extern_obj_exec,
[INSTR_EXTERN_FUNC] = instr_extern_func_exec,
[INSTR_HASH_FUNC] = instr_hash_func_exec,
+ [INSTR_RSS] = instr_rss_exec,
[INSTR_JMP] = instr_jmp_exec,
[INSTR_JMP_VALID] = instr_jmp_valid_exec,
@@ -10001,6 +10190,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)
instruction_table_free(p);
metadata_free(p);
header_free(p);
+ rss_free(p);
hash_func_free(p);
extern_func_free(p);
extern_obj_free(p);
@@ -10149,6 +10339,7 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, const char *name, int numa_
TAILQ_INIT(&pipeline->extern_objs);
TAILQ_INIT(&pipeline->extern_funcs);
TAILQ_INIT(&pipeline->hash_funcs);
+ TAILQ_INIT(&pipeline->rss);
TAILQ_INIT(&pipeline->headers);
TAILQ_INIT(&pipeline->actions);
TAILQ_INIT(&pipeline->table_types);
@@ -10263,6 +10454,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
if (status)
goto error;
+ status = rss_build(p);
+ if (status)
+ goto error;
+
status = header_build(p);
if (status)
goto error;
@@ -10318,6 +10513,7 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
instruction_table_build_free(p);
metadata_build_free(p);
header_build_free(p);
+ rss_build_free(p);
hash_func_build_free(p);
extern_func_build_free(p);
extern_obj_build_free(p);
@@ -10382,6 +10578,7 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
pipeline->n_learners = p->n_learners;
pipeline->n_regarrays = p->n_regarrays;
pipeline->n_metarrays = p->n_metarrays;
+ pipeline->n_rss = p->n_rss;
return 0;
}
@@ -11406,6 +11603,112 @@ rte_swx_ctl_meter_stats_read_with_key(struct rte_swx_pipeline *p,
return rte_swx_ctl_meter_stats_read(p, metarray_name, entry_id, stats);
}
+int
+rte_swx_ctl_rss_info_get(struct rte_swx_pipeline *p,
+ uint32_t rss_obj_id,
+ struct rte_swx_ctl_rss_info *info)
+{
+ struct rss *rss;
+
+ /* Check the input arguments. */
+ if (!p || !info)
+ return -EINVAL;
+
+ rss = rss_find_by_id(p, rss_obj_id);
+ if (!rss)
+ return -EINVAL;
+
+ /* Read from the internal data structures. */
+ strcpy(info->name, rss->name);
+ return 0;
+}
+
+int
+rte_swx_ctl_pipeline_rss_key_size_read(struct rte_swx_pipeline *p,
+ const char *rss_name,
+ uint32_t *key_size)
+{
+ struct rss *rss;
+ struct rss_runtime *r;
+
+ /* Check the input arguments. */
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(rss_name, EINVAL);
+ rss = rss_find(p, rss_name);
+ CHECK(rss, EINVAL);
+ r = p->rss_runtime[rss->id];
+
+ CHECK(key_size, EINVAL);
+
+ /* Read from the internal data structures. */
+ *key_size = r->key_size;
+
+ return 0;
+}
+
+int
+rte_swx_ctl_pipeline_rss_key_read(struct rte_swx_pipeline *p,
+ const char *rss_name,
+ uint8_t *key)
+{
+ struct rss *rss;
+ struct rss_runtime *r;
+
+ /* Check the input arguments. */
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(rss_name, EINVAL);
+ rss = rss_find(p, rss_name);
+ CHECK(rss, EINVAL);
+ r = p->rss_runtime[rss->id];
+
+ CHECK(key, EINVAL);
+
+ /* Read from the internal data structures. */
+ memcpy(key, r->key, r->key_size);
+
+ return 0;
+}
+
+int
+rte_swx_ctl_pipeline_rss_key_write(struct rte_swx_pipeline *p,
+ const char *rss_name,
+ uint32_t key_size,
+ uint8_t *key)
+{
+ struct rss *rss;
+ struct rss_runtime *r, *r_new;
+
+ /* Check the input arguments. */
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(rss_name, EINVAL);
+ rss = rss_find(p, rss_name);
+ CHECK(rss, EINVAL);
+ r = p->rss_runtime[rss->id];
+
+ CHECK(key_size >= 4, EINVAL);
+ CHECK(key, EINVAL);
+
+ /* Allocate new RSS run-time entry. */
+ r_new = malloc(sizeof(struct rss_runtime) + key_size * sizeof(uint32_t));
+ if (!r_new)
+ return -ENOMEM;
+
+ /* Fill in the new RSS run-time entry. */
+ r_new->key_size = key_size;
+ memcpy(r_new->key, key, key_size);
+
+ /* Commit the RSS run-time change atomically. */
+ p->rss_runtime[rss->id] = r_new;
+
+ /* Free the old RSS run-time entry. */
+ free(r);
+
+ return 0;
+}
+
/*
* Pipeline compilation.
*/
@@ -11579,6 +11882,7 @@ instr_type_to_name(struct instruction *instr)
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_RSS: return "INSTR_RSS";
case INSTR_JMP: return "INSTR_JMP";
case INSTR_JMP_VALID: return "INSTR_JMP_VALID";
@@ -12029,6 +12333,34 @@ instr_hash_export(struct instruction *instr, FILE *f)
instr->hash_func.src.n_bytes);
}
+static void
+instr_rss_export(struct instruction *instr, FILE *f)
+{
+ fprintf(f,
+ "\t{\n"
+ "\t\t.type = %s,\n"
+ "\t\t.rss = {\n"
+ "\t\t\t.rss_obj_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->rss.rss_obj_id,
+ instr->rss.dst.offset,
+ instr->rss.dst.n_bits,
+ instr->rss.src.struct_id,
+ instr->rss.src.offset,
+ instr->rss.src.n_bytes);
+}
+
static void
instr_reg_export(struct instruction *instr __rte_unused, FILE *f __rte_unused)
{
@@ -12590,6 +12922,7 @@ static instruction_export_t export_table[] = {
[INSTR_EXTERN_OBJ] = instr_extern_export,
[INSTR_EXTERN_FUNC] = instr_extern_export,
[INSTR_HASH_FUNC] = instr_hash_export,
+ [INSTR_RSS] = instr_rss_export,
[INSTR_JMP] = instr_jmp_export,
[INSTR_JMP_VALID] = instr_jmp_export,
@@ -12817,6 +13150,7 @@ instr_type_to_func(struct instruction *instr)
case INSTR_EXTERN_OBJ: return NULL;
case INSTR_EXTERN_FUNC: return NULL;
case INSTR_HASH_FUNC: return "__instr_hash_func_exec";
+ case INSTR_RSS: return "__instr_rss_exec";
case INSTR_JMP: return NULL;
case INSTR_JMP_VALID: return NULL;
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index 2c9cc6ee44..25df042d3b 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -352,6 +352,28 @@ rte_swx_pipeline_hash_func_register(struct rte_swx_pipeline *p,
const char *name,
rte_swx_hash_func_t func);
+/*
+ * RSS.
+ */
+
+/**
+ * Pipeline Receive Side Scaling (RSS) object configure
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] name
+ * Name for the new RSS object.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument;
+ * -ENOMEM: Not enough space/cannot allocate memory;
+ * -EEXIST: RSS object with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_rss_config(struct rte_swx_pipeline *p,
+ const char *name);
+
/*
* Packet headers and meta-data
*/
diff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h
index 335506039b..0c29362d31 100644
--- a/lib/pipeline/rte_swx_pipeline_internal.h
+++ b/lib/pipeline/rte_swx_pipeline_internal.h
@@ -199,6 +199,22 @@ struct hash_func_runtime {
rte_swx_hash_func_t func;
};
+/*
+ * RSS.
+ */
+struct rss {
+ TAILQ_ENTRY(rss) node;
+ char name[RTE_SWX_NAME_SIZE];
+ uint32_t id;
+};
+
+TAILQ_HEAD(rss_tailq, rss);
+
+struct rss_runtime {
+ uint32_t key_size; /* key size in bytes. */
+ uint8_t key[0]; /* key. */
+};
+
/*
* Header.
*/
@@ -524,6 +540,15 @@ enum instruction_type {
*/
INSTR_HASH_FUNC,
+ /* rss RSS_OBJ_NAME dst src_first src_last
+ * Compute the RSS hash value over range of struct fields.
+ * dst = M
+ * src_first = HMEFT
+ * src_last = HMEFT
+ * src_first and src_last must be fields within the same struct
+ */
+ INSTR_RSS,
+
/* jmp LABEL
* Unconditional jump
*/
@@ -677,6 +702,21 @@ struct instr_hash_func {
} src;
};
+struct instr_rss {
+ uint8_t rss_obj_id;
+
+ struct {
+ uint8_t offset;
+ uint8_t n_bits;
+ } dst;
+
+ struct {
+ uint8_t struct_id;
+ uint16_t offset;
+ uint16_t n_bytes;
+ } src;
+};
+
struct instr_dst_src {
struct instr_operand dst;
union {
@@ -763,6 +803,7 @@ struct instruction {
struct instr_extern_obj ext_obj;
struct instr_extern_func ext_func;
struct instr_hash_func hash_func;
+ struct instr_rss rss;
struct instr_jmp jmp;
};
};
@@ -1480,6 +1521,7 @@ struct rte_swx_pipeline {
struct extern_obj_tailq extern_objs;
struct extern_func_tailq extern_funcs;
struct hash_func_tailq hash_funcs;
+ struct rss_tailq rss;
struct header_tailq headers;
struct struct_type *metadata_st;
uint32_t metadata_struct_id;
@@ -1502,6 +1544,7 @@ struct rte_swx_pipeline {
struct selector_statistics *selector_stats;
struct learner_statistics *learner_stats;
struct hash_func_runtime *hash_func_runtime;
+ struct rss_runtime **rss_runtime;
struct regarray_runtime *regarray_runtime;
struct metarray_runtime *metarray_runtime;
struct instruction *instructions;
@@ -1518,6 +1561,7 @@ struct rte_swx_pipeline {
uint32_t n_extern_objs;
uint32_t n_extern_funcs;
uint32_t n_hash_funcs;
+ uint32_t n_rss;
uint32_t n_actions;
uint32_t n_tables;
uint32_t n_selectors;
@@ -2467,6 +2511,58 @@ __instr_hash_func_exec(struct rte_swx_pipeline *p,
METADATA_WRITE(t, dst_offset, n_dst_bits, result);
}
+/*
+ * rss.
+ */
+static inline uint32_t
+rss_func(void *rss_key, uint32_t rss_key_size, void *input_data, uint32_t input_data_size)
+{
+ uint32_t *key = (uint32_t *)rss_key;
+ uint32_t *data = (uint32_t *)input_data;
+ uint32_t key_size = rss_key_size >> 2;
+ uint32_t data_size = input_data_size >> 2;
+ uint32_t hash_val = 0, i;
+
+ for (i = 0; i < data_size; i++) {
+ uint32_t d;
+
+ for (d = data[i]; d; d &= (d - 1)) {
+ uint32_t key0, key1, pos;
+
+ pos = rte_bsf32(d);
+ key0 = key[i % key_size] << (31 - pos);
+ key1 = key[(i + 1) % key_size] >> (pos + 1);
+ hash_val ^= key0 | key1;
+ }
+ }
+
+ return hash_val;
+}
+
+static inline void
+__instr_rss_exec(struct rte_swx_pipeline *p,
+ struct thread *t,
+ const struct instruction *ip)
+{
+ uint32_t rss_obj_id = ip->rss.rss_obj_id;
+ uint32_t dst_offset = ip->rss.dst.offset;
+ uint32_t n_dst_bits = ip->rss.dst.n_bits;
+ uint32_t src_struct_id = ip->rss.src.struct_id;
+ uint32_t src_offset = ip->rss.src.offset;
+ uint32_t n_src_bytes = ip->rss.src.n_bytes;
+
+ struct rss_runtime *r = p->rss_runtime[rss_obj_id];
+ uint8_t *src_ptr = t->structs[src_struct_id];
+ uint32_t result;
+
+ TRACE("[Thread %2u] rss %u\n",
+ p->thread_id,
+ rss_obj_id);
+
+ result = rss_func(r->key, r->key_size, &src_ptr[src_offset], n_src_bytes);
+ METADATA_WRITE(t, dst_offset, n_dst_bits, result);
+}
+
/*
* mov.
*/
diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 9116f38ed2..a164275526 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2049,6 +2049,52 @@ metarray_statement_parse(struct metarray_spec *s,
return 0;
}
+/*
+ *
+ * rss
+ *
+ */
+
+static void
+rss_spec_free(struct rss_spec *s)
+{
+ if (!s)
+ return;
+
+ free(s->name);
+ s->name = NULL;
+}
+
+static int
+rss_statement_parse(struct rss_spec *s,
+ char **tokens,
+ uint32_t n_tokens,
+ uint32_t n_lines,
+ uint32_t *err_line,
+ const char **err_msg)
+{
+ /* Check format. */
+ if ((n_tokens != 2)) {
+ if (err_line)
+ *err_line = n_lines;
+ if (err_msg)
+ *err_msg = "Invalid rss statement.";
+ return -EINVAL;
+ }
+
+ /* spec. */
+ s->name = strdup(tokens[1]);
+ if (!s->name) {
+ if (err_line)
+ *err_line = n_lines;
+ if (err_msg)
+ *err_msg = "Memory allocation failed.";
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
/*
* apply.
*
@@ -2243,6 +2289,17 @@ pipeline_spec_codegen(FILE *f,
fprintf(f, "};\n\n");
+ /* rss. */
+ fprintf(f, "static struct rss_spec rss[] = {\n");
+
+ for (i = 0; i < s->n_rss; i++) {
+ struct rss_spec *rss_spec = &s->rss[i];
+ fprintf(f, "\t[%d] = {\n", i);
+ fprintf(f, "\t\t.name = \"%s\",\n", rss_spec->name);
+ fprintf(f, "\t},\n");
+ }
+ fprintf(f, "};\n\n");
+
/* struct. */
for (i = 0; i < s->n_structs; i++) {
struct struct_spec *struct_spec = &s->structs[i];
@@ -2794,6 +2851,7 @@ pipeline_spec_codegen(FILE *f,
fprintf(f, "\t.learners = learners,\n");
fprintf(f, "\t.regarrays = regarrays,\n");
fprintf(f, "\t.metarrays = metarrays,\n");
+ fprintf(f, "\t.rss = rss,\n");
fprintf(f, "\t.apply = apply,\n");
fprintf(f, "\t.n_extobjs = sizeof(extobjs) / sizeof(extobjs[0]),\n");
fprintf(f, "\t.n_structs = sizeof(structs) / sizeof(structs[0]),\n");
@@ -2805,6 +2863,7 @@ pipeline_spec_codegen(FILE *f,
fprintf(f, "\t.n_learners = sizeof(learners) / sizeof(learners[0]),\n");
fprintf(f, "\t.n_regarrays = sizeof(regarrays) / sizeof(regarrays[0]),\n");
fprintf(f, "\t.n_metarrays = sizeof(metarrays) / sizeof(metarrays[0]),\n");
+ fprintf(f, "\t.n_rss = sizeof(rss) / sizeof(rss[0]),\n");
fprintf(f, "\t.n_apply = sizeof(apply) / sizeof(apply[0]),\n");
fprintf(f, "};\n");
}
@@ -2824,6 +2883,7 @@ pipeline_spec_parse(FILE *spec,
struct learner_spec learner_spec = {0};
struct regarray_spec regarray_spec = {0};
struct metarray_spec metarray_spec = {0};
+ struct rss_spec rss_spec = {0};
struct apply_spec apply_spec = {0};
struct pipeline_spec *s = NULL;
uint32_t n_lines = 0;
@@ -3372,6 +3432,40 @@ pipeline_spec_parse(FILE *spec,
continue;
}
+ /* rss object configuration */
+ if (!strcmp(tokens[0], "rss")) {
+ struct rss_spec *new_rss;
+
+ status = rss_statement_parse(&rss_spec,
+ tokens,
+ n_tokens,
+ n_lines,
+ err_line,
+ err_msg);
+ if (status)
+ goto error;
+
+ new_rss = realloc(s->rss,
+ (s->n_rss + 1) * sizeof(struct rss_spec));
+ if (!new_rss) {
+ if (err_line)
+ *err_line = n_lines;
+ if (err_msg)
+ *err_msg = "Memory allocation failed.";
+ status = -ENOMEM;
+ goto error;
+ }
+
+ s->rss = new_rss;
+ memcpy(&s->rss[s->n_rss],
+ &rss_spec,
+ sizeof(struct rss_spec));
+ s->n_rss++;
+ memset(&rss_spec, 0, sizeof(struct rss_spec));
+
+ continue;
+ }
+
/* apply. */
if (!strcmp(tokens[0], "apply")) {
status = apply_statement_parse(&block_mask,
@@ -3418,6 +3512,7 @@ pipeline_spec_parse(FILE *spec,
learner_spec_free(&learner_spec);
regarray_spec_free(®array_spec);
metarray_spec_free(&metarray_spec);
+ rss_spec_free(&rss_spec);
apply_spec_free(&apply_spec);
pipeline_spec_free(s);
@@ -3583,6 +3678,18 @@ pipeline_spec_configure(struct rte_swx_pipeline *p,
}
}
+ /* rss. */
+ for (i = 0; i < s->n_rss; i++) {
+ struct rss_spec *rss_spec = &s->rss[i];
+
+ status = rte_swx_pipeline_rss_config(p, rss_spec->name);
+ if (status) {
+ if (err_msg)
+ *err_msg = "rss object configuration error.";
+ return status;
+ }
+ }
+
/* apply. */
for (i = 0; i < s->n_apply; i++) {
struct apply_spec *apply_spec = &s->apply[i];
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 123e175f8b..dd88c0bfab 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -171,6 +171,15 @@ struct metarray_spec {
uint32_t size;
};
+/*
+ * rss.
+ *
+ * rss NAME
+ */
+struct rss_spec {
+ char *name;
+};
+
/*
* apply.
*
@@ -198,6 +207,7 @@ struct pipeline_spec {
struct learner_spec *learners;
struct regarray_spec *regarrays;
struct metarray_spec *metarrays;
+ struct rss_spec *rss;
struct apply_spec *apply;
uint32_t n_extobjs;
@@ -210,6 +220,7 @@ struct pipeline_spec {
uint32_t n_learners;
uint32_t n_regarrays;
uint32_t n_metarrays;
+ uint32_t n_rss;
uint32_t n_apply;
};
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 58e34459c3..6f45c73428 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -157,6 +157,10 @@ EXPERIMENTAL {
rte_swx_pipeline_find;
#added in 23.03
+ rte_swx_ctl_rss_info_get;
+ rte_swx_ctl_pipeline_rss_key_size_read;
+ rte_swx_ctl_pipeline_rss_key_read;
+ rte_swx_ctl_pipeline_rss_key_write;
rte_swx_ipsec_create;
rte_swx_ipsec_find;
rte_swx_ipsec_free;
@@ -164,4 +168,5 @@ EXPERIMENTAL {
rte_swx_ipsec_sa_add;
rte_swx_ipsec_sa_delete;
rte_swx_ipsec_sa_read;
+ rte_swx_pipeline_rss_config;
};
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH V4] pipeline: add RSS support
2023-02-03 19:37 [PATCH] pipeline: add RSS support Cristian Dumitrescu
2023-02-07 15:29 ` [PATCH V2] " Cristian Dumitrescu
2023-02-07 15:43 ` [PATCH V3] " Cristian Dumitrescu
@ 2023-02-07 16:41 ` Cristian Dumitrescu
2023-02-19 22:14 ` Thomas Monjalon
2 siblings, 1 reply; 5+ messages in thread
From: Cristian Dumitrescu @ 2023-02-07 16:41 UTC (permalink / raw)
To: dev; +Cc: Kamalakannan R
Add pipeline support for the Receive Side Scaling (RSS) hashing. While
the pipeline already supports the stateless hashing schemes, the RSS
scheme uses a key configured by the control plane and preserved
between successive RSS hash invocations.
Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
---
Change log:
V4:
-rebased on the main latest
V3:
-fixed minor style issues
V2:
-added CLI command and enxample file for the examples/pipeline
application
examples/pipeline/cli.c | 73 +++++
| 42 +++
| 84 ++++++
lib/pipeline/rte_swx_ctl.h | 92 +++++++
lib/pipeline/rte_swx_pipeline.c | 334 +++++++++++++++++++++++
lib/pipeline/rte_swx_pipeline.h | 22 ++
lib/pipeline/rte_swx_pipeline_internal.h | 96 +++++++
lib/pipeline/rte_swx_pipeline_spec.c | 107 ++++++++
lib/pipeline/rte_swx_pipeline_spec.h | 11 +
lib/pipeline/version.map | 5 +
10 files changed, 866 insertions(+)
create mode 100644 examples/pipeline/examples/rss.cli
create mode 100644 examples/pipeline/examples/rss.spec
diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c
index 87f9c0370d..2ae6cc579f 100644
--- a/examples/pipeline/cli.c
+++ b/examples/pipeline/cli.c
@@ -2622,6 +2622,67 @@ cmd_pipeline_meter_stats(char **tokens,
return;
}
+static const char cmd_pipeline_rss_help[] =
+"pipeline <pipeline_name> rss <rss_obj_name> key <key_byte0> ...\n";
+
+static void
+cmd_pipeline_rss(char **tokens,
+ uint32_t n_tokens,
+ char *out,
+ size_t out_size,
+ void *obj __rte_unused)
+{
+ uint8_t rss_key[CMD_MAX_TOKENS];
+ struct rte_swx_pipeline *p;
+ const char *rss_obj_name;
+ uint32_t rss_key_size, i;
+ int status;
+
+ if (n_tokens < 6) {
+ snprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);
+ return;
+ }
+
+ p = rte_swx_pipeline_find(tokens[1]);
+ if (!p) {
+ snprintf(out, out_size, MSG_ARG_INVALID, "pipeline_name");
+ return;
+ }
+
+ if (strcmp(tokens[2], "rss")) {
+ snprintf(out, out_size, MSG_ARG_NOT_FOUND, "rss");
+ return;
+ }
+
+ rss_obj_name = tokens[3];
+
+ if (strcmp(tokens[4], "key")) {
+ snprintf(out, out_size, MSG_ARG_NOT_FOUND, "key");
+ return;
+ }
+
+ tokens += 5;
+ n_tokens -= 5;
+ rss_key_size = n_tokens;
+
+ for (i = 0; i < rss_key_size; i++) {
+ uint32_t key_byte;
+
+ if (parser_read_uint32(&key_byte, tokens[i]) || (key_byte >= UINT8_MAX)) {
+ snprintf(out, out_size, MSG_ARG_INVALID, "key byte");
+ return;
+ }
+
+ rss_key[i] = (uint8_t)key_byte;
+ }
+
+ status = rte_swx_ctl_pipeline_rss_key_write(p, rss_obj_name, rss_key_size, rss_key);
+ if (status) {
+ snprintf(out, out_size, "Command failed.\n");
+ return;
+ }
+}
+
static const char cmd_pipeline_stats_help[] =
"pipeline <pipeline_name> stats\n";
@@ -3422,6 +3483,7 @@ cmd_help(char **tokens,
"\tpipeline meter reset\n"
"\tpipeline meter set\n"
"\tpipeline meter stats\n"
+ "\tpipeline rss\n"
"\tpipeline stats\n"
"\tpipeline mirror session\n"
"\tpipeline enable\n"
@@ -3641,6 +3703,12 @@ cmd_help(char **tokens,
return;
}
+ if (!strcmp(tokens[0], "pipeline") &&
+ (n_tokens == 2) && !strcmp(tokens[1], "rss")) {
+ snprintf(out, out_size, "\n%s\n", cmd_pipeline_rss_help);
+ return;
+ }
+
if ((strcmp(tokens[0], "pipeline") == 0) &&
(n_tokens == 2) && (strcmp(tokens[1], "stats") == 0)) {
snprintf(out, out_size, "\n%s\n", cmd_pipeline_stats_help);
@@ -3915,6 +3983,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)
return;
}
+ if (n_tokens >= 3 && !strcmp(tokens[2], "rss")) {
+ cmd_pipeline_rss(tokens, n_tokens, out, out_size, obj);
+ return;
+ }
+
if ((n_tokens >= 3) &&
(strcmp(tokens[2], "stats") == 0)) {
cmd_pipeline_stats(tokens, n_tokens, out, out_size,
--git a/examples/pipeline/examples/rss.cli b/examples/pipeline/examples/rss.cli
new file mode 100644
index 0000000000..350c49681c
--- /dev/null
+++ b/examples/pipeline/examples/rss.cli
@@ -0,0 +1,42 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2023 Intel Corporation
+
+# Example command line:
+# ./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/rss.cli
+#
+# Once the application has started, the command to get the CLI prompt is:
+# telnet 0.0.0.0 8086
+
+;
+; Pipeline code generation & shared object library build.
+;
+pipeline codegen ./examples/pipeline/examples/rss.spec /tmp/rss.c
+pipeline libbuild /tmp/rss.c /tmp/rss.so
+
+;
+; List of DPDK devices.
+;
+; Note: Customize the parameters below to match your setup.
+;
+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0
+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on
+
+;
+; List of pipelines.
+;
+pipeline PIPELINE0 build lib /tmp/rss.so io ./examples/pipeline/examples/ethdev.io numa 0
+
+;
+; Initial set of table entries.
+;
+; The table entries can later be updated at run-time through the CLI commands.
+;
+pipeline PIPELINE0 rss rss0 key 0 0 0 0
+
+;
+; Pipelines-to-threads mapping.
+;
+pipeline PIPELINE0 enable thread 1
--git a/examples/pipeline/examples/rss.spec b/examples/pipeline/examples/rss.spec
new file mode 100644
index 0000000000..486528c303
--- /dev/null
+++ b/examples/pipeline/examples/rss.spec
@@ -0,0 +1,84 @@
+; SPDX-License-Identifier: BSD-3-Clause
+; Copyright(c) 2023 Intel Corporation
+
+; This simple example illustrates how to compute an RSS hash signature over an n-tuple set of fields
+; read from the packet headers and/or the packet meta-data by using the "rss" instruction. In this
+; specific example, the n-tuple is the (IPv4 source address, IPv4 destination address) 2-tuple.
+
+//
+// Headers
+//
+struct ethernet_h {
+ bit<48> dst_addr
+ bit<48> src_addr
+ bit<16> ethertype
+}
+
+struct ipv4_h {
+ bit<8> ver_ihl
+ bit<8> diffserv
+ bit<16> total_len
+ bit<16> identification
+ bit<16> flags_offset
+ bit<8> ttl
+ bit<8> protocol
+ bit<16> hdr_checksum
+ bit<32> src_addr
+ bit<32> dst_addr
+}
+
+header ethernet instanceof ethernet_h
+header ipv4 instanceof ipv4_h
+
+//
+// Meta-data.
+//
+struct metadata_t {
+ bit<32> port
+ bit<32> hash
+}
+
+metadata instanceof metadata_t
+
+//
+// RSS.
+//
+rss rss0
+
+//
+// Pipeline.
+//
+apply {
+ //
+ // RX and parse.
+ //
+ rx m.port
+ extract h.ethernet
+ extract h.ipv4
+
+ //
+ // Compute the RSS hash over the n-tuple.
+ //
+ // Details:
+ // a) RSS object name: rss0;
+ // b) Destination (i.e. hash result): m.hash;
+ // c) Source (i.e. n-tuple to be hashed): The 2-tuple formed by the header fields
+ // (h.ipv4.src_addr, h.ipv4.dst_addr). Only the first and the last n-tuple fields are
+ // specified in the RSS instruction, but all the fields in between are part of the
+ // n-tuple to be hashed.
+ //
+ rss rss0 m.hash h.ipv4.src_addr h.ipv4.dst_addr
+
+ //
+ // Use the computed hash to create a uniform distribution of pkts across the 4 output ports.
+ //
+ and m.hash 3
+ mov m.port m.hash
+
+ //
+ // De-parse and TX.
+ //
+ emit h.ethernet
+ emit h.ipv4
+ tx m.port
+}
diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h
index 2eb51b2c76..6ef2551ab5 100644
--- a/lib/pipeline/rte_swx_ctl.h
+++ b/lib/pipeline/rte_swx_ctl.h
@@ -67,6 +67,9 @@ struct rte_swx_ctl_pipeline_info {
/** Number of meter arrays. */
uint32_t n_metarrays;
+
+ /** Number of RSS objects. */
+ uint32_t n_rss;
};
/**
@@ -1521,6 +1524,95 @@ rte_swx_ctl_meter_stats_read_with_key(struct rte_swx_pipeline *p,
uint8_t *table_key,
struct rte_swx_ctl_meter_stats *stats);
+/*
+ * RSS Query and Configuration API.
+ */
+
+/** RSS object info. */
+struct rte_swx_ctl_rss_info {
+ /** RSS object name. */
+ char name[RTE_SWX_CTL_NAME_SIZE];
+};
+
+/**
+ * RSS object info get
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_id
+ * RSS object ID (0 .. *n_rss* - 1).
+ * @param[out] rss
+ * RSS object info.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_rss_info_get(struct rte_swx_pipeline *p,
+ uint32_t rss_obj_id,
+ struct rte_swx_ctl_rss_info *rss);
+
+/**
+ * RSS object key size read
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_name
+ * RSS object name.
+ * @param[out] key_size
+ * RSS key size in bytes.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_rss_key_size_read(struct rte_swx_pipeline *p,
+ const char *rss_obj_name,
+ uint32_t *key_size);
+
+/**
+ * RSS object key read
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_name
+ * RSS object name.
+ * @param[out] key
+ * RSS key. Must be pre-allocated by the caller to store *key_size* bytes.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_rss_key_read(struct rte_swx_pipeline *p,
+ const char *rss_obj_name,
+ uint8_t *key);
+
+/**
+ * RSS object key write
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] rss_obj_name
+ * RSS object name.
+ * @param[in] key_size
+ * RSS key size in bytes. Must be at least 4 and a power of 2.
+ * @param[in] key
+ * RSS key.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument.
+ */
+__rte_experimental
+int
+rte_swx_ctl_pipeline_rss_key_write(struct rte_swx_pipeline *p,
+ const char *rss_obj_name,
+ uint32_t key_size,
+ uint8_t *key);
+
/**
* Pipeline control free
*
diff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c
index 0e631dea2b..53d97f0072 100644
--- a/lib/pipeline/rte_swx_pipeline.c
+++ b/lib/pipeline/rte_swx_pipeline.c
@@ -1255,6 +1255,129 @@ hash_func_free(struct rte_swx_pipeline *p)
}
}
+/*
+ * RSS.
+ */
+static struct rss *
+rss_find(struct rte_swx_pipeline *p, const char *name)
+{
+ struct rss *elem;
+
+ TAILQ_FOREACH(elem, &p->rss, node)
+ if (strcmp(elem->name, name) == 0)
+ return elem;
+
+ return NULL;
+}
+
+static struct rss *
+rss_find_by_id(struct rte_swx_pipeline *p, uint32_t rss_obj_id)
+{
+ struct rss *elem;
+
+ TAILQ_FOREACH(elem, &p->rss, node)
+ if (elem->id == rss_obj_id)
+ return elem;
+
+ return NULL;
+}
+
+int
+rte_swx_pipeline_rss_config(struct rte_swx_pipeline *p, const char *name)
+{
+ struct rss *r;
+
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(name, EINVAL);
+ CHECK(!rss_find(p, name), EEXIST);
+
+ /* Memory allocation. */
+ r = calloc(1, sizeof(struct rss));
+ CHECK(r, ENOMEM);
+
+ /* Node initialization. */
+ strcpy(r->name, name);
+ r->id = p->n_rss;
+
+ /* Node add to tailq. */
+ TAILQ_INSERT_TAIL(&p->rss, r, node);
+ p->n_rss++;
+
+ return 0;
+}
+
+static void
+rss_build_free(struct rte_swx_pipeline *p)
+{
+ uint32_t i;
+
+ if (!p->rss_runtime)
+ return;
+
+ for (i = 0; i < p->n_rss; i++)
+ free(p->rss_runtime[i]);
+
+ free(p->rss_runtime);
+ p->rss_runtime = NULL;
+}
+
+static const struct {
+ uint32_t key_size;
+ uint8_t key[4];
+} rss_runtime_default = {
+ .key_size = 4,
+ .key = {0, 0, 0, 0},
+};
+
+static int
+rss_build(struct rte_swx_pipeline *p)
+{
+ uint32_t i;
+ int status = 0;
+
+ /* Memory allocation. */
+ p->rss_runtime = calloc(p->n_rss, sizeof(struct rss_runtime *));
+ if (!p->rss_runtime) {
+ status = -ENOMEM;
+ goto error;
+ }
+
+ /* RSS. */
+ for (i = 0; i < p->n_rss; i++) {
+ p->rss_runtime[i] = malloc(sizeof(rss_runtime_default));
+ if (!p->rss_runtime[i]) {
+ status = -ENOMEM;
+ goto error;
+ }
+
+ memcpy(p->rss_runtime[i], &rss_runtime_default, sizeof(rss_runtime_default));
+ }
+
+ return 0;
+
+error:
+ rss_build_free(p);
+ return status;
+}
+
+static void
+rss_free(struct rte_swx_pipeline *p)
+{
+ rss_build_free(p);
+
+ for ( ; ; ) {
+ struct rss *elem;
+
+ elem = TAILQ_FIRST(&p->rss);
+ if (!elem)
+ break;
+
+ TAILQ_REMOVE(&p->rss, elem, node);
+ free(elem);
+ }
+}
+
/*
* Header.
*/
@@ -3002,6 +3125,63 @@ instr_hash_func_exec(struct rte_swx_pipeline *p)
thread_ip_inc(p);
}
+/*
+ * rss.
+ */
+static int
+instr_rss_translate(struct rte_swx_pipeline *p,
+ struct action *action,
+ char **tokens,
+ int n_tokens,
+ struct instruction *instr,
+ struct instruction_data *data __rte_unused)
+{
+ struct rss *rss;
+ struct field *dst, *src_first, *src_last;
+ uint32_t src_struct_id_first = 0, src_struct_id_last = 0;
+
+ CHECK(n_tokens == 5, EINVAL);
+
+ rss = rss_find(p, tokens[1]);
+ CHECK(rss, EINVAL);
+
+ dst = metadata_field_parse(p, tokens[2]);
+ CHECK(dst, EINVAL);
+ CHECK(dst->n_bits <= 64, 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_last->var_size, EINVAL);
+ CHECK(src_struct_id_first == src_struct_id_last, EINVAL);
+
+ instr->type = INSTR_RSS;
+ instr->rss.rss_obj_id = (uint8_t)rss->id;
+ instr->rss.dst.offset = (uint8_t)dst->offset / 8;
+ instr->rss.dst.n_bits = (uint8_t)dst->n_bits;
+ instr->rss.src.struct_id = (uint8_t)src_struct_id_first;
+ instr->rss.src.offset = (uint16_t)src_first->offset / 8;
+ instr->rss.src.n_bytes = (uint16_t)((src_last->offset + src_last->n_bits -
+ src_first->offset) / 8);
+
+ return 0;
+}
+
+static inline void
+instr_rss_exec(struct rte_swx_pipeline *p)
+{
+ struct thread *t = &p->threads[p->thread_id];
+ struct instruction *ip = t->ip;
+
+ /* Extern function execute. */
+ __instr_rss_exec(p, t, ip);
+
+ /* Thread. */
+ thread_ip_inc(p);
+}
+
/*
* mov.
*/
@@ -6397,6 +6577,14 @@ instr_translate(struct rte_swx_pipeline *p,
instr,
data);
+ if (!strcmp(tokens[tpos], "rss"))
+ return instr_rss_translate(p,
+ action,
+ &tokens[tpos],
+ n_tokens - tpos,
+ instr,
+ data);
+
if (!strcmp(tokens[tpos], "jmp"))
return instr_jmp_translate(p,
action,
@@ -7371,6 +7559,7 @@ static instr_exec_t instruction_table[] = {
[INSTR_EXTERN_OBJ] = instr_extern_obj_exec,
[INSTR_EXTERN_FUNC] = instr_extern_func_exec,
[INSTR_HASH_FUNC] = instr_hash_func_exec,
+ [INSTR_RSS] = instr_rss_exec,
[INSTR_JMP] = instr_jmp_exec,
[INSTR_JMP_VALID] = instr_jmp_valid_exec,
@@ -10001,6 +10190,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)
instruction_table_free(p);
metadata_free(p);
header_free(p);
+ rss_free(p);
hash_func_free(p);
extern_func_free(p);
extern_obj_free(p);
@@ -10149,6 +10339,7 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, const char *name, int numa_
TAILQ_INIT(&pipeline->extern_objs);
TAILQ_INIT(&pipeline->extern_funcs);
TAILQ_INIT(&pipeline->hash_funcs);
+ TAILQ_INIT(&pipeline->rss);
TAILQ_INIT(&pipeline->headers);
TAILQ_INIT(&pipeline->actions);
TAILQ_INIT(&pipeline->table_types);
@@ -10263,6 +10454,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
if (status)
goto error;
+ status = rss_build(p);
+ if (status)
+ goto error;
+
status = header_build(p);
if (status)
goto error;
@@ -10318,6 +10513,7 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)
instruction_table_build_free(p);
metadata_build_free(p);
header_build_free(p);
+ rss_build_free(p);
hash_func_build_free(p);
extern_func_build_free(p);
extern_obj_build_free(p);
@@ -10382,6 +10578,7 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,
pipeline->n_learners = p->n_learners;
pipeline->n_regarrays = p->n_regarrays;
pipeline->n_metarrays = p->n_metarrays;
+ pipeline->n_rss = p->n_rss;
return 0;
}
@@ -11406,6 +11603,112 @@ rte_swx_ctl_meter_stats_read_with_key(struct rte_swx_pipeline *p,
return rte_swx_ctl_meter_stats_read(p, metarray_name, entry_id, stats);
}
+int
+rte_swx_ctl_rss_info_get(struct rte_swx_pipeline *p,
+ uint32_t rss_obj_id,
+ struct rte_swx_ctl_rss_info *info)
+{
+ struct rss *rss;
+
+ /* Check the input arguments. */
+ if (!p || !info)
+ return -EINVAL;
+
+ rss = rss_find_by_id(p, rss_obj_id);
+ if (!rss)
+ return -EINVAL;
+
+ /* Read from the internal data structures. */
+ strcpy(info->name, rss->name);
+ return 0;
+}
+
+int
+rte_swx_ctl_pipeline_rss_key_size_read(struct rte_swx_pipeline *p,
+ const char *rss_name,
+ uint32_t *key_size)
+{
+ struct rss *rss;
+ struct rss_runtime *r;
+
+ /* Check the input arguments. */
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(rss_name, EINVAL);
+ rss = rss_find(p, rss_name);
+ CHECK(rss, EINVAL);
+ r = p->rss_runtime[rss->id];
+
+ CHECK(key_size, EINVAL);
+
+ /* Read from the internal data structures. */
+ *key_size = r->key_size;
+
+ return 0;
+}
+
+int
+rte_swx_ctl_pipeline_rss_key_read(struct rte_swx_pipeline *p,
+ const char *rss_name,
+ uint8_t *key)
+{
+ struct rss *rss;
+ struct rss_runtime *r;
+
+ /* Check the input arguments. */
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(rss_name, EINVAL);
+ rss = rss_find(p, rss_name);
+ CHECK(rss, EINVAL);
+ r = p->rss_runtime[rss->id];
+
+ CHECK(key, EINVAL);
+
+ /* Read from the internal data structures. */
+ memcpy(key, r->key, r->key_size);
+
+ return 0;
+}
+
+int
+rte_swx_ctl_pipeline_rss_key_write(struct rte_swx_pipeline *p,
+ const char *rss_name,
+ uint32_t key_size,
+ uint8_t *key)
+{
+ struct rss *rss;
+ struct rss_runtime *r, *r_new;
+
+ /* Check the input arguments. */
+ CHECK(p, EINVAL);
+
+ CHECK_NAME(rss_name, EINVAL);
+ rss = rss_find(p, rss_name);
+ CHECK(rss, EINVAL);
+ r = p->rss_runtime[rss->id];
+
+ CHECK(key_size >= 4, EINVAL);
+ CHECK(key, EINVAL);
+
+ /* Allocate new RSS run-time entry. */
+ r_new = malloc(sizeof(struct rss_runtime) + key_size * sizeof(uint32_t));
+ if (!r_new)
+ return -ENOMEM;
+
+ /* Fill in the new RSS run-time entry. */
+ r_new->key_size = key_size;
+ memcpy(r_new->key, key, key_size);
+
+ /* Commit the RSS run-time change atomically. */
+ p->rss_runtime[rss->id] = r_new;
+
+ /* Free the old RSS run-time entry. */
+ free(r);
+
+ return 0;
+}
+
/*
* Pipeline compilation.
*/
@@ -11579,6 +11882,7 @@ instr_type_to_name(struct instruction *instr)
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_RSS: return "INSTR_RSS";
case INSTR_JMP: return "INSTR_JMP";
case INSTR_JMP_VALID: return "INSTR_JMP_VALID";
@@ -12029,6 +12333,34 @@ instr_hash_export(struct instruction *instr, FILE *f)
instr->hash_func.src.n_bytes);
}
+static void
+instr_rss_export(struct instruction *instr, FILE *f)
+{
+ fprintf(f,
+ "\t{\n"
+ "\t\t.type = %s,\n"
+ "\t\t.rss = {\n"
+ "\t\t\t.rss_obj_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->rss.rss_obj_id,
+ instr->rss.dst.offset,
+ instr->rss.dst.n_bits,
+ instr->rss.src.struct_id,
+ instr->rss.src.offset,
+ instr->rss.src.n_bytes);
+}
+
static void
instr_reg_export(struct instruction *instr __rte_unused, FILE *f __rte_unused)
{
@@ -12590,6 +12922,7 @@ static instruction_export_t export_table[] = {
[INSTR_EXTERN_OBJ] = instr_extern_export,
[INSTR_EXTERN_FUNC] = instr_extern_export,
[INSTR_HASH_FUNC] = instr_hash_export,
+ [INSTR_RSS] = instr_rss_export,
[INSTR_JMP] = instr_jmp_export,
[INSTR_JMP_VALID] = instr_jmp_export,
@@ -12817,6 +13150,7 @@ instr_type_to_func(struct instruction *instr)
case INSTR_EXTERN_OBJ: return NULL;
case INSTR_EXTERN_FUNC: return NULL;
case INSTR_HASH_FUNC: return "__instr_hash_func_exec";
+ case INSTR_RSS: return "__instr_rss_exec";
case INSTR_JMP: return NULL;
case INSTR_JMP_VALID: return NULL;
diff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h
index 2c9cc6ee44..25df042d3b 100644
--- a/lib/pipeline/rte_swx_pipeline.h
+++ b/lib/pipeline/rte_swx_pipeline.h
@@ -352,6 +352,28 @@ rte_swx_pipeline_hash_func_register(struct rte_swx_pipeline *p,
const char *name,
rte_swx_hash_func_t func);
+/*
+ * RSS.
+ */
+
+/**
+ * Pipeline Receive Side Scaling (RSS) object configure
+ *
+ * @param[in] p
+ * Pipeline handle.
+ * @param[in] name
+ * Name for the new RSS object.
+ * @return
+ * 0 on success or the following error codes otherwise:
+ * -EINVAL: Invalid argument;
+ * -ENOMEM: Not enough space/cannot allocate memory;
+ * -EEXIST: RSS object with this name already exists.
+ */
+__rte_experimental
+int
+rte_swx_pipeline_rss_config(struct rte_swx_pipeline *p,
+ const char *name);
+
/*
* Packet headers and meta-data
*/
diff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h
index 345b32502c..ac22d94a93 100644
--- a/lib/pipeline/rte_swx_pipeline_internal.h
+++ b/lib/pipeline/rte_swx_pipeline_internal.h
@@ -199,6 +199,22 @@ struct hash_func_runtime {
rte_swx_hash_func_t func;
};
+/*
+ * RSS.
+ */
+struct rss {
+ TAILQ_ENTRY(rss) node;
+ char name[RTE_SWX_NAME_SIZE];
+ uint32_t id;
+};
+
+TAILQ_HEAD(rss_tailq, rss);
+
+struct rss_runtime {
+ uint32_t key_size; /* key size in bytes. */
+ uint8_t key[0]; /* key. */
+};
+
/*
* Header.
*/
@@ -524,6 +540,15 @@ enum instruction_type {
*/
INSTR_HASH_FUNC,
+ /* rss RSS_OBJ_NAME dst src_first src_last
+ * Compute the RSS hash value over range of struct fields.
+ * dst = M
+ * src_first = HMEFT
+ * src_last = HMEFT
+ * src_first and src_last must be fields within the same struct
+ */
+ INSTR_RSS,
+
/* jmp LABEL
* Unconditional jump
*/
@@ -677,6 +702,21 @@ struct instr_hash_func {
} src;
};
+struct instr_rss {
+ uint8_t rss_obj_id;
+
+ struct {
+ uint8_t offset;
+ uint8_t n_bits;
+ } dst;
+
+ struct {
+ uint8_t struct_id;
+ uint16_t offset;
+ uint16_t n_bytes;
+ } src;
+};
+
struct instr_dst_src {
struct instr_operand dst;
union {
@@ -763,6 +803,7 @@ struct instruction {
struct instr_extern_obj ext_obj;
struct instr_extern_func ext_func;
struct instr_hash_func hash_func;
+ struct instr_rss rss;
struct instr_jmp jmp;
};
};
@@ -1480,6 +1521,7 @@ struct rte_swx_pipeline {
struct extern_obj_tailq extern_objs;
struct extern_func_tailq extern_funcs;
struct hash_func_tailq hash_funcs;
+ struct rss_tailq rss;
struct header_tailq headers;
struct struct_type *metadata_st;
uint32_t metadata_struct_id;
@@ -1502,6 +1544,7 @@ struct rte_swx_pipeline {
struct selector_statistics *selector_stats;
struct learner_statistics *learner_stats;
struct hash_func_runtime *hash_func_runtime;
+ struct rss_runtime **rss_runtime;
struct regarray_runtime *regarray_runtime;
struct metarray_runtime *metarray_runtime;
struct instruction *instructions;
@@ -1518,6 +1561,7 @@ struct rte_swx_pipeline {
uint32_t n_extern_objs;
uint32_t n_extern_funcs;
uint32_t n_hash_funcs;
+ uint32_t n_rss;
uint32_t n_actions;
uint32_t n_tables;
uint32_t n_selectors;
@@ -2467,6 +2511,58 @@ __instr_hash_func_exec(struct rte_swx_pipeline *p,
METADATA_WRITE(t, dst_offset, n_dst_bits, result);
}
+/*
+ * rss.
+ */
+static inline uint32_t
+rss_func(void *rss_key, uint32_t rss_key_size, void *input_data, uint32_t input_data_size)
+{
+ uint32_t *key = (uint32_t *)rss_key;
+ uint32_t *data = (uint32_t *)input_data;
+ uint32_t key_size = rss_key_size >> 2;
+ uint32_t data_size = input_data_size >> 2;
+ uint32_t hash_val = 0, i;
+
+ for (i = 0; i < data_size; i++) {
+ uint32_t d;
+
+ for (d = data[i]; d; d &= (d - 1)) {
+ uint32_t key0, key1, pos;
+
+ pos = rte_bsf32(d);
+ key0 = key[i % key_size] << (31 - pos);
+ key1 = key[(i + 1) % key_size] >> (pos + 1);
+ hash_val ^= key0 | key1;
+ }
+ }
+
+ return hash_val;
+}
+
+static inline void
+__instr_rss_exec(struct rte_swx_pipeline *p,
+ struct thread *t,
+ const struct instruction *ip)
+{
+ uint32_t rss_obj_id = ip->rss.rss_obj_id;
+ uint32_t dst_offset = ip->rss.dst.offset;
+ uint32_t n_dst_bits = ip->rss.dst.n_bits;
+ uint32_t src_struct_id = ip->rss.src.struct_id;
+ uint32_t src_offset = ip->rss.src.offset;
+ uint32_t n_src_bytes = ip->rss.src.n_bytes;
+
+ struct rss_runtime *r = p->rss_runtime[rss_obj_id];
+ uint8_t *src_ptr = t->structs[src_struct_id];
+ uint32_t result;
+
+ TRACE("[Thread %2u] rss %u\n",
+ p->thread_id,
+ rss_obj_id);
+
+ result = rss_func(r->key, r->key_size, &src_ptr[src_offset], n_src_bytes);
+ METADATA_WRITE(t, dst_offset, n_dst_bits, result);
+}
+
/*
* mov.
*/
diff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c
index 9116f38ed2..a164275526 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.c
+++ b/lib/pipeline/rte_swx_pipeline_spec.c
@@ -2049,6 +2049,52 @@ metarray_statement_parse(struct metarray_spec *s,
return 0;
}
+/*
+ *
+ * rss
+ *
+ */
+
+static void
+rss_spec_free(struct rss_spec *s)
+{
+ if (!s)
+ return;
+
+ free(s->name);
+ s->name = NULL;
+}
+
+static int
+rss_statement_parse(struct rss_spec *s,
+ char **tokens,
+ uint32_t n_tokens,
+ uint32_t n_lines,
+ uint32_t *err_line,
+ const char **err_msg)
+{
+ /* Check format. */
+ if ((n_tokens != 2)) {
+ if (err_line)
+ *err_line = n_lines;
+ if (err_msg)
+ *err_msg = "Invalid rss statement.";
+ return -EINVAL;
+ }
+
+ /* spec. */
+ s->name = strdup(tokens[1]);
+ if (!s->name) {
+ if (err_line)
+ *err_line = n_lines;
+ if (err_msg)
+ *err_msg = "Memory allocation failed.";
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
/*
* apply.
*
@@ -2243,6 +2289,17 @@ pipeline_spec_codegen(FILE *f,
fprintf(f, "};\n\n");
+ /* rss. */
+ fprintf(f, "static struct rss_spec rss[] = {\n");
+
+ for (i = 0; i < s->n_rss; i++) {
+ struct rss_spec *rss_spec = &s->rss[i];
+ fprintf(f, "\t[%d] = {\n", i);
+ fprintf(f, "\t\t.name = \"%s\",\n", rss_spec->name);
+ fprintf(f, "\t},\n");
+ }
+ fprintf(f, "};\n\n");
+
/* struct. */
for (i = 0; i < s->n_structs; i++) {
struct struct_spec *struct_spec = &s->structs[i];
@@ -2794,6 +2851,7 @@ pipeline_spec_codegen(FILE *f,
fprintf(f, "\t.learners = learners,\n");
fprintf(f, "\t.regarrays = regarrays,\n");
fprintf(f, "\t.metarrays = metarrays,\n");
+ fprintf(f, "\t.rss = rss,\n");
fprintf(f, "\t.apply = apply,\n");
fprintf(f, "\t.n_extobjs = sizeof(extobjs) / sizeof(extobjs[0]),\n");
fprintf(f, "\t.n_structs = sizeof(structs) / sizeof(structs[0]),\n");
@@ -2805,6 +2863,7 @@ pipeline_spec_codegen(FILE *f,
fprintf(f, "\t.n_learners = sizeof(learners) / sizeof(learners[0]),\n");
fprintf(f, "\t.n_regarrays = sizeof(regarrays) / sizeof(regarrays[0]),\n");
fprintf(f, "\t.n_metarrays = sizeof(metarrays) / sizeof(metarrays[0]),\n");
+ fprintf(f, "\t.n_rss = sizeof(rss) / sizeof(rss[0]),\n");
fprintf(f, "\t.n_apply = sizeof(apply) / sizeof(apply[0]),\n");
fprintf(f, "};\n");
}
@@ -2824,6 +2883,7 @@ pipeline_spec_parse(FILE *spec,
struct learner_spec learner_spec = {0};
struct regarray_spec regarray_spec = {0};
struct metarray_spec metarray_spec = {0};
+ struct rss_spec rss_spec = {0};
struct apply_spec apply_spec = {0};
struct pipeline_spec *s = NULL;
uint32_t n_lines = 0;
@@ -3372,6 +3432,40 @@ pipeline_spec_parse(FILE *spec,
continue;
}
+ /* rss object configuration */
+ if (!strcmp(tokens[0], "rss")) {
+ struct rss_spec *new_rss;
+
+ status = rss_statement_parse(&rss_spec,
+ tokens,
+ n_tokens,
+ n_lines,
+ err_line,
+ err_msg);
+ if (status)
+ goto error;
+
+ new_rss = realloc(s->rss,
+ (s->n_rss + 1) * sizeof(struct rss_spec));
+ if (!new_rss) {
+ if (err_line)
+ *err_line = n_lines;
+ if (err_msg)
+ *err_msg = "Memory allocation failed.";
+ status = -ENOMEM;
+ goto error;
+ }
+
+ s->rss = new_rss;
+ memcpy(&s->rss[s->n_rss],
+ &rss_spec,
+ sizeof(struct rss_spec));
+ s->n_rss++;
+ memset(&rss_spec, 0, sizeof(struct rss_spec));
+
+ continue;
+ }
+
/* apply. */
if (!strcmp(tokens[0], "apply")) {
status = apply_statement_parse(&block_mask,
@@ -3418,6 +3512,7 @@ pipeline_spec_parse(FILE *spec,
learner_spec_free(&learner_spec);
regarray_spec_free(®array_spec);
metarray_spec_free(&metarray_spec);
+ rss_spec_free(&rss_spec);
apply_spec_free(&apply_spec);
pipeline_spec_free(s);
@@ -3583,6 +3678,18 @@ pipeline_spec_configure(struct rte_swx_pipeline *p,
}
}
+ /* rss. */
+ for (i = 0; i < s->n_rss; i++) {
+ struct rss_spec *rss_spec = &s->rss[i];
+
+ status = rte_swx_pipeline_rss_config(p, rss_spec->name);
+ if (status) {
+ if (err_msg)
+ *err_msg = "rss object configuration error.";
+ return status;
+ }
+ }
+
/* apply. */
for (i = 0; i < s->n_apply; i++) {
struct apply_spec *apply_spec = &s->apply[i];
diff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h
index 123e175f8b..dd88c0bfab 100644
--- a/lib/pipeline/rte_swx_pipeline_spec.h
+++ b/lib/pipeline/rte_swx_pipeline_spec.h
@@ -171,6 +171,15 @@ struct metarray_spec {
uint32_t size;
};
+/*
+ * rss.
+ *
+ * rss NAME
+ */
+struct rss_spec {
+ char *name;
+};
+
/*
* apply.
*
@@ -198,6 +207,7 @@ struct pipeline_spec {
struct learner_spec *learners;
struct regarray_spec *regarrays;
struct metarray_spec *metarrays;
+ struct rss_spec *rss;
struct apply_spec *apply;
uint32_t n_extobjs;
@@ -210,6 +220,7 @@ struct pipeline_spec {
uint32_t n_learners;
uint32_t n_regarrays;
uint32_t n_metarrays;
+ uint32_t n_rss;
uint32_t n_apply;
};
diff --git a/lib/pipeline/version.map b/lib/pipeline/version.map
index 2b48c9f393..5e94104817 100644
--- a/lib/pipeline/version.map
+++ b/lib/pipeline/version.map
@@ -157,6 +157,10 @@ EXPERIMENTAL {
rte_swx_pipeline_find;
# added in 23.03
+ rte_swx_ctl_rss_info_get;
+ rte_swx_ctl_pipeline_rss_key_size_read;
+ rte_swx_ctl_pipeline_rss_key_read;
+ rte_swx_ctl_pipeline_rss_key_write;
rte_swx_ipsec_create;
rte_swx_ipsec_find;
rte_swx_ipsec_free;
@@ -164,4 +168,5 @@ EXPERIMENTAL {
rte_swx_ipsec_sa_add;
rte_swx_ipsec_sa_delete;
rte_swx_ipsec_sa_read;
+ rte_swx_pipeline_rss_config;
};
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH V4] pipeline: add RSS support
2023-02-07 16:41 ` [PATCH V4] " Cristian Dumitrescu
@ 2023-02-19 22:14 ` Thomas Monjalon
0 siblings, 0 replies; 5+ messages in thread
From: Thomas Monjalon @ 2023-02-19 22:14 UTC (permalink / raw)
To: Cristian Dumitrescu; +Cc: dev, Kamalakannan R
07/02/2023 17:41, Cristian Dumitrescu:
> Add pipeline support for the Receive Side Scaling (RSS) hashing. While
> the pipeline already supports the stateless hashing schemes, the RSS
> scheme uses a key configured by the control plane and preserved
> between successive RSS hash invocations.
>
> Signed-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>
> Signed-off-by: Kamalakannan R <kamalakannan.r@intel.com>
Applied, thanks.
^ permalink raw reply [flat|nested] 5+ messages in thread