From patchwork Tue Feb 7 16:41:45 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Cristian Dumitrescu X-Patchwork-Id: 123361 X-Patchwork-Delegate: thomas@monjalon.net Return-Path: X-Original-To: patchwork@inbox.dpdk.org Delivered-To: patchwork@inbox.dpdk.org Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 7CD5541C30; Tue, 7 Feb 2023 17:41:50 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 6059142D0D; Tue, 7 Feb 2023 17:41:50 +0100 (CET) Received: from mga18.intel.com (mga18.intel.com [134.134.136.126]) by mails.dpdk.org (Postfix) with ESMTP id 387DC40151 for ; Tue, 7 Feb 2023 17:41:48 +0100 (CET) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1675788108; x=1707324108; h=from:to:cc:subject:date:message-id:in-reply-to: references:mime-version:content-transfer-encoding; bh=q0Cvhk1hfFDCCByAazgqVLiVcmQg0tfp3nIMuILnITE=; b=dCzHmRSlgPt8rFaf5ybaOTfyHgjiEgL4EILwG5KXWWzFbZdRXWT0+c6s EQGQa/VhmE9HPKZR6iMonJiKUsblAA6YzToAJvbr8wUa5i8gckAUVmKoh L8IFU29XONM83KIg/ohAoGtRSKwiwDydQeBNHeKVWl6+EJWVvMCZh47mK 7383ytAV9WMieAviCLaFlPqfGzgf7U4WqW4c71pTLlNBWuF1z6VEhWoOt eXT3RElh57FkqluUXRQb/F9VrSRm8OHWm6XpSovU1l1xg84aeTun9xEly vgVdWBBWkuciDVyt4KdlrxdJHiiqQi8G7/k4pjVCgHdLGzrIsx6CoVXdz A==; X-IronPort-AV: E=McAfee;i="6500,9779,10614"; a="313202474" X-IronPort-AV: E=Sophos;i="5.97,278,1669104000"; d="scan'208";a="313202474" Received: from orsmga003.jf.intel.com ([10.7.209.27]) by orsmga106.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 07 Feb 2023 08:41:47 -0800 X-ExtLoop1: 1 X-IronPort-AV: E=McAfee;i="6500,9779,10614"; a="616876088" X-IronPort-AV: E=Sophos;i="5.97,278,1669104000"; d="scan'208";a="616876088" Received: from silpixa00400573.ir.intel.com (HELO silpixa00400573.ger.corp.intel.com) ([10.237.222.53]) by orsmga003.jf.intel.com with ESMTP; 07 Feb 2023 08:41:46 -0800 From: Cristian Dumitrescu To: dev@dpdk.org Cc: Kamalakannan R Subject: [PATCH V4] pipeline: add RSS support Date: Tue, 7 Feb 2023 16:41:45 +0000 Message-Id: <20230207164145.652805-1-cristian.dumitrescu@intel.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230203193732.436973-1-cristian.dumitrescu@intel.com> References: <20230203193732.436973-1-cristian.dumitrescu@intel.com> MIME-Version: 1.0 X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org 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 Signed-off-by: Kamalakannan R --- 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 +++++ examples/pipeline/examples/rss.cli | 42 +++ examples/pipeline/examples/rss.spec | 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 rss key ...\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 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, diff --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 diff --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; };