get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/123297/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 123297,
    "url": "http://patches.dpdk.org/api/patches/123297/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20230207152942.549097-1-cristian.dumitrescu@intel.com/",
    "project": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/projects/1/?format=api",
        "name": "DPDK",
        "link_name": "dpdk",
        "list_id": "dev.dpdk.org",
        "list_email": "dev@dpdk.org",
        "web_url": "http://core.dpdk.org",
        "scm_url": "git://dpdk.org/dpdk",
        "webscm_url": "http://git.dpdk.org/dpdk",
        "list_archive_url": "https://inbox.dpdk.org/dev",
        "list_archive_url_format": "https://inbox.dpdk.org/dev/{}",
        "commit_url_format": ""
    },
    "msgid": "<20230207152942.549097-1-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230207152942.549097-1-cristian.dumitrescu@intel.com",
    "date": "2023-02-07T15:29:42",
    "name": "[V2] pipeline: add RSS support",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "f0a8805885e47a881aebf5ccfd93aa21755cc644",
    "submitter": {
        "id": 19,
        "url": "http://patches.dpdk.org/api/people/19/?format=api",
        "name": "Cristian Dumitrescu",
        "email": "cristian.dumitrescu@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "http://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20230207152942.549097-1-cristian.dumitrescu@intel.com/mbox/",
    "series": [
        {
            "id": 26856,
            "url": "http://patches.dpdk.org/api/series/26856/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=26856",
            "date": "2023-02-07T15:29:42",
            "name": "[V2] pipeline: add RSS support",
            "version": 2,
            "mbox": "http://patches.dpdk.org/series/26856/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/123297/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/123297/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<dev-bounces@dpdk.org>",
        "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])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 408E441C30;\n\tTue,  7 Feb 2023 16:30:01 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 2051F40A84;\n\tTue,  7 Feb 2023 16:30:01 +0100 (CET)",
            "from mga09.intel.com (mga09.intel.com [134.134.136.24])\n by mails.dpdk.org (Postfix) with ESMTP id 0CCDC4021F\n for <dev@dpdk.org>; Tue,  7 Feb 2023 16:29:58 +0100 (CET)",
            "from orsmga001.jf.intel.com ([10.7.209.18])\n by orsmga102.jf.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 07 Feb 2023 07:29:57 -0800",
            "from silpixa00400573.ir.intel.com (HELO\n silpixa00400573.ger.corp.intel.com) ([10.237.222.53])\n by orsmga001.jf.intel.com with ESMTP; 07 Feb 2023 07:29:43 -0800"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple;\n d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n t=1675783799; x=1707319799;\n h=from:to:cc:subject:date:message-id:in-reply-to:\n references:mime-version:content-transfer-encoding;\n bh=7kni43uL/0POvu9wQVdN01JYQ8m3hCRa9tMDgdeF9rk=;\n b=iaBaH6B0HQw0jo6PfaSWL9wEIrsInQIcZvmw3AF+WXoAmX7IR8OWC1vd\n efhCAv6bN36VNxSP3yTiodLCI66uW3M5VMbBf0XYDqhJk1wT4eH0kSqAT\n JErp/OpKOiSap1q3wn9phga3FYrDn0XHqsczZ8+IuDxrwa4ScpkGOrJlk\n 8jR8WlUmekQ/OXASEmSAgmOOll0a2JLcdodbQ7EnEP40uj/6w7iZD63zx\n psEOV1Z43EI4JAqsfMWnsp1Mih99l+9lpGT/4o5Otxd5ZOOzJ12+jv8Jh\n tFOEotS3txHWn7OLJ1A8BGR4EyQ9zTEFfa86IIvwezJZpy/YFei5uNLTK Q==;",
        "X-IronPort-AV": [
            "E=McAfee;i=\"6500,9779,10614\"; a=\"330826403\"",
            "E=Sophos;i=\"5.97,278,1669104000\"; d=\"scan'208\";a=\"330826403\"",
            "E=McAfee;i=\"6500,9779,10614\"; a=\"699287546\"",
            "E=Sophos;i=\"5.97,278,1669104000\"; d=\"scan'208\";a=\"699287546\""
        ],
        "X-ExtLoop1": "1",
        "From": "Cristian Dumitrescu <cristian.dumitrescu@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "Kamalakannan R <kamalakannan.r@intel.com>",
        "Subject": "[PATCH V2] pipeline: add RSS support",
        "Date": "Tue,  7 Feb 2023 15:29:42 +0000",
        "Message-Id": "<20230207152942.549097-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",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.29",
        "Precedence": "list",
        "List-Id": "DPDK patches and discussions <dev.dpdk.org>",
        "List-Unsubscribe": "<https://mails.dpdk.org/options/dev>,\n <mailto:dev-request@dpdk.org?subject=unsubscribe>",
        "List-Archive": "<http://mails.dpdk.org/archives/dev/>",
        "List-Post": "<mailto:dev@dpdk.org>",
        "List-Help": "<mailto:dev-request@dpdk.org?subject=help>",
        "List-Subscribe": "<https://mails.dpdk.org/listinfo/dev>,\n <mailto:dev-request@dpdk.org?subject=subscribe>",
        "Errors-To": "dev-bounces@dpdk.org"
    },
    "content": "Add pipeline support for the Receive Side Scaling (RSS) hashing. While\nthe pipeline already supports the stateless hashing schemes, the RSS\nscheme uses a key configured by the control plane and preserved\nbetween successive RSS hash invocations.\n\nSigned-off-by: Cristian Dumitrescu <cristian.dumitrescu@intel.com>\nSigned-off-by: Kamalakannan R <kamalakannan.r@intel.com>\n---\nChange log:\n\nV2:\n-added CLI command and enxample file for the examples/pipeline\napplication\n\n examples/pipeline/cli.c                  |  73 +++++\n examples/pipeline/examples/rss.cli       |  42 +++\n examples/pipeline/examples/rss.spec      |  84 ++++++\n lib/pipeline/rte_swx_ctl.h               |  92 +++++++\n lib/pipeline/rte_swx_pipeline.c          | 334 +++++++++++++++++++++++\n lib/pipeline/rte_swx_pipeline.h          |  22 ++\n lib/pipeline/rte_swx_pipeline_internal.h |  96 +++++++\n lib/pipeline/rte_swx_pipeline_spec.c     | 107 ++++++++\n lib/pipeline/rte_swx_pipeline_spec.h     |  11 +\n lib/pipeline/version.map                 |   5 +\n 10 files changed, 866 insertions(+)\n create mode 100644 examples/pipeline/examples/rss.cli\n create mode 100644 examples/pipeline/examples/rss.spec",
    "diff": "diff --git a/examples/pipeline/cli.c b/examples/pipeline/cli.c\nindex 87f9c0370d..2ae6cc579f 100644\n--- a/examples/pipeline/cli.c\n+++ b/examples/pipeline/cli.c\n@@ -2622,6 +2622,67 @@ cmd_pipeline_meter_stats(char **tokens,\n \treturn;\n }\n \n+static const char cmd_pipeline_rss_help[] =\n+\"pipeline <pipeline_name> rss <rss_obj_name> key <key_byte0> ...\\n\";\n+\n+static void\n+cmd_pipeline_rss(char **tokens,\n+\tuint32_t n_tokens,\n+\tchar *out,\n+\tsize_t out_size,\n+\tvoid *obj __rte_unused)\n+{\n+\tuint8_t rss_key[CMD_MAX_TOKENS];\n+\tstruct rte_swx_pipeline *p;\n+\tconst char *rss_obj_name;\n+\tuint32_t rss_key_size, i;\n+\tint status;\n+\n+\tif (n_tokens < 6) {\n+\t\tsnprintf(out, out_size, MSG_ARG_MISMATCH, tokens[0]);\n+\t\treturn;\n+\t}\n+\n+\tp = rte_swx_pipeline_find(tokens[1]);\n+\tif (!p) {\n+\t\tsnprintf(out, out_size, MSG_ARG_INVALID, \"pipeline_name\");\n+\t\treturn;\n+\t}\n+\n+\tif (strcmp(tokens[2], \"rss\")) {\n+\t\tsnprintf(out, out_size, MSG_ARG_NOT_FOUND, \"rss\");\n+\t\treturn;\n+\t}\n+\n+\trss_obj_name = tokens[3];\n+\n+\tif (strcmp(tokens[4], \"key\")) {\n+\t\tsnprintf(out, out_size, MSG_ARG_NOT_FOUND, \"key\");\n+\t\treturn;\n+\t}\n+\n+\ttokens += 5;\n+\tn_tokens -= 5;\n+\trss_key_size = n_tokens;\n+\n+\tfor (i = 0; i < rss_key_size; i++) {\n+\t\tuint32_t key_byte;\n+\n+\t\tif (parser_read_uint32(&key_byte, tokens[i]) || (key_byte >= UINT8_MAX)) {\n+\t\t\tsnprintf(out, out_size, MSG_ARG_INVALID, \"key byte\");\n+\t\t\treturn;\n+\t\t}\n+\n+\t\trss_key[i] = (uint8_t)key_byte;\n+\t}\n+\n+\tstatus = rte_swx_ctl_pipeline_rss_key_write(p, rss_obj_name, rss_key_size, rss_key);\n+\tif (status) {\n+\t\tsnprintf(out, out_size, \"Command failed.\\n\");\n+\t\treturn;\n+\t}\n+}\n+\n static const char cmd_pipeline_stats_help[] =\n \"pipeline <pipeline_name> stats\\n\";\n \n@@ -3422,6 +3483,7 @@ cmd_help(char **tokens,\n \t\t\t\"\\tpipeline meter reset\\n\"\n \t\t\t\"\\tpipeline meter set\\n\"\n \t\t\t\"\\tpipeline meter stats\\n\"\n+\t\t\t\"\\tpipeline rss\\n\"\n \t\t\t\"\\tpipeline stats\\n\"\n \t\t\t\"\\tpipeline mirror session\\n\"\n \t\t\t\"\\tpipeline enable\\n\"\n@@ -3641,6 +3703,12 @@ cmd_help(char **tokens,\n \t\treturn;\n \t}\n \n+\tif (!strcmp(tokens[0], \"pipeline\") &&\n+\t\t(n_tokens == 2) && !strcmp(tokens[1], \"rss\")) {\n+\t\tsnprintf(out, out_size, \"\\n%s\\n\", cmd_pipeline_rss_help);\n+\t\treturn;\n+\t}\n+\n \tif ((strcmp(tokens[0], \"pipeline\") == 0) &&\n \t\t(n_tokens == 2) && (strcmp(tokens[1], \"stats\") == 0)) {\n \t\tsnprintf(out, out_size, \"\\n%s\\n\", cmd_pipeline_stats_help);\n@@ -3915,6 +3983,11 @@ cli_process(char *in, char *out, size_t out_size, void *obj)\n \t\t\treturn;\n \t\t}\n \n+\t\tif (n_tokens >= 3 && !strcmp(tokens[2], \"rss\")) {\n+\t\t\tcmd_pipeline_rss(tokens, n_tokens, out, out_size, obj);\n+\t\t\treturn;\n+\t\t}\n+\n \t\tif ((n_tokens >= 3) &&\n \t\t\t(strcmp(tokens[2], \"stats\") == 0)) {\n \t\t\tcmd_pipeline_stats(tokens, n_tokens, out, out_size,\ndiff --git a/examples/pipeline/examples/rss.cli b/examples/pipeline/examples/rss.cli\nnew file mode 100644\nindex 0000000000..350c49681c\n--- /dev/null\n+++ b/examples/pipeline/examples/rss.cli\n@@ -0,0 +1,42 @@\n+; SPDX-License-Identifier: BSD-3-Clause\n+; Copyright(c) 2023 Intel Corporation\n+\n+# Example command line:\n+#\t./build/examples/dpdk-pipeline -l0-1 -- -s ./examples/pipeline/examples/rss.cli\n+#\n+# Once the application has started, the command to get the CLI prompt is:\n+#\ttelnet 0.0.0.0 8086\n+\n+;\n+; Pipeline code generation & shared object library build.\n+;\n+pipeline codegen ./examples/pipeline/examples/rss.spec /tmp/rss.c\n+pipeline libbuild /tmp/rss.c /tmp/rss.so\n+\n+;\n+; List of DPDK devices.\n+;\n+; Note: Customize the parameters below to match your setup.\n+;\n+mempool MEMPOOL0 meta 0 pkt 2176 pool 32K cache 256 numa 0\n+ethdev 0000:18:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on\n+ethdev 0000:18:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on\n+ethdev 0000:3b:00.0 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on\n+ethdev 0000:3b:00.1 rxq 1 128 MEMPOOL0 txq 1 512 promiscuous on\n+\n+;\n+; List of pipelines.\n+;\n+pipeline PIPELINE0 build lib /tmp/rss.so io ./examples/pipeline/examples/ethdev.io numa 0\n+\n+;\n+; Initial set of table entries.\n+;\n+; The table entries can later be updated at run-time through the CLI commands.\n+;\n+pipeline PIPELINE0 rss rss0 key 0 0 0 0\n+\n+;\n+; Pipelines-to-threads mapping.\n+;\n+pipeline PIPELINE0 enable thread 1\ndiff --git a/examples/pipeline/examples/rss.spec b/examples/pipeline/examples/rss.spec\nnew file mode 100644\nindex 0000000000..486528c303\n--- /dev/null\n+++ b/examples/pipeline/examples/rss.spec\n@@ -0,0 +1,84 @@\n+; SPDX-License-Identifier: BSD-3-Clause\n+; Copyright(c) 2023 Intel Corporation\n+\n+; This simple example illustrates how to compute an RSS hash signature over an n-tuple set of fields\n+; read from the packet headers and/or the packet meta-data by using the \"rss\" instruction. In this\n+; specific example, the n-tuple is the (IPv4 source address, IPv4 destination address) 2-tuple.\n+\n+//\n+// Headers\n+//\n+struct ethernet_h {\n+\tbit<48> dst_addr\n+\tbit<48> src_addr\n+\tbit<16> ethertype\n+}\n+\n+struct ipv4_h {\n+\tbit<8> ver_ihl\n+\tbit<8> diffserv\n+\tbit<16> total_len\n+\tbit<16> identification\n+\tbit<16> flags_offset\n+\tbit<8> ttl\n+\tbit<8> protocol\n+\tbit<16> hdr_checksum\n+\tbit<32> src_addr\n+\tbit<32> dst_addr\n+}\n+\n+header ethernet instanceof ethernet_h\n+header ipv4 instanceof ipv4_h\n+\n+//\n+// Meta-data.\n+//\n+struct metadata_t {\n+\tbit<32> port\n+\tbit<32> hash\n+}\n+\n+metadata instanceof metadata_t\n+\n+//\n+// RSS.\n+//\n+rss rss0\n+\n+//\n+// Pipeline.\n+//\n+apply {\n+\t//\n+\t// RX and parse.\n+\t//\n+\trx m.port\n+\textract h.ethernet\n+\textract h.ipv4\n+\n+\t//\n+\t// Compute the RSS hash over the n-tuple.\n+\t//\n+\t// Details:\n+\t//    a) RSS object name: rss0;\n+\t//    b) Destination (i.e. hash result): m.hash;\n+\t//    c) Source (i.e. n-tuple to be hashed): The 2-tuple formed by the header fields\n+\t//       (h.ipv4.src_addr, h.ipv4.dst_addr). Only the first and the last n-tuple fields are\n+\t//       specified in the RSS instruction, but all the fields in between are part of the\n+\t//       n-tuple to be hashed.\n+\t//\n+\trss rss0 m.hash h.ipv4.src_addr h.ipv4.dst_addr\n+\n+\t//\n+\t// Use the computed hash to create a uniform distribution of pkts across the 4 output ports.\n+\t//\n+\tand m.hash 3\n+\tmov m.port m.hash\n+\n+\t//\n+\t// De-parse and TX.\n+\t//\n+\temit h.ethernet\n+\temit h.ipv4\n+\ttx m.port\n+}\ndiff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h\nindex 2eb51b2c76..6ef2551ab5 100644\n--- a/lib/pipeline/rte_swx_ctl.h\n+++ b/lib/pipeline/rte_swx_ctl.h\n@@ -67,6 +67,9 @@ struct rte_swx_ctl_pipeline_info {\n \n \t/** Number of meter arrays. */\n \tuint32_t n_metarrays;\n+\n+\t/** Number of RSS objects. */\n+\tuint32_t n_rss;\n };\n \n /**\n@@ -1521,6 +1524,95 @@ rte_swx_ctl_meter_stats_read_with_key(struct rte_swx_pipeline *p,\n \t\t\t\t      uint8_t *table_key,\n \t\t\t\t      struct rte_swx_ctl_meter_stats *stats);\n \n+/*\n+ * RSS Query and Configuration API.\n+ */\n+\n+/** RSS object info. */\n+struct rte_swx_ctl_rss_info {\n+\t/** RSS object name. */\n+\tchar name[RTE_SWX_CTL_NAME_SIZE];\n+};\n+\n+/**\n+ * RSS object info get\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] rss_obj_id\n+ *   RSS object ID (0 .. *n_rss* - 1).\n+ * @param[out] rss\n+ *   RSS object info.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument.\n+ */\n+__rte_experimental\n+int\n+rte_swx_ctl_rss_info_get(struct rte_swx_pipeline *p,\n+\t\t\t uint32_t rss_obj_id,\n+\t\t\t struct rte_swx_ctl_rss_info *rss);\n+\n+/**\n+ * RSS object key size read\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] rss_obj_name\n+ *   RSS object name.\n+ * @param[out] key_size\n+ *   RSS key size in bytes.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument.\n+ */\n+__rte_experimental\n+int\n+rte_swx_ctl_pipeline_rss_key_size_read(struct rte_swx_pipeline *p,\n+\t\t\t\t       const char *rss_obj_name,\n+\t\t\t\t       uint32_t *key_size);\n+\n+/**\n+ * RSS object key read\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] rss_obj_name\n+ *   RSS object name.\n+ * @param[out] key\n+ *   RSS key. Must be pre-allocated by the caller to store *key_size* bytes.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument.\n+ */\n+__rte_experimental\n+int\n+rte_swx_ctl_pipeline_rss_key_read(struct rte_swx_pipeline *p,\n+\t\t\t\t  const char *rss_obj_name,\n+\t\t\t\t  uint8_t *key);\n+\n+/**\n+ * RSS object key write\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] rss_obj_name\n+ *   RSS object name.\n+ * @param[in] key_size\n+ *   RSS key size in bytes. Must be at least 4 and a power of 2.\n+ * @param[in] key\n+ *   RSS key.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument.\n+ */\n+__rte_experimental\n+int\n+rte_swx_ctl_pipeline_rss_key_write(struct rte_swx_pipeline *p,\n+\t\t\t\t   const char *rss_obj_name,\n+\t\t\t\t   uint32_t key_size,\n+\t\t\t\t   uint8_t *key);\n+\n /**\n  * Pipeline control free\n  *\ndiff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c\nindex 0e631dea2b..53d97f0072 100644\n--- a/lib/pipeline/rte_swx_pipeline.c\n+++ b/lib/pipeline/rte_swx_pipeline.c\n@@ -1255,6 +1255,129 @@ hash_func_free(struct rte_swx_pipeline *p)\n \t}\n }\n \n+/*\n+ * RSS.\n+ */\n+static struct rss *\n+rss_find(struct rte_swx_pipeline *p, const char *name)\n+{\n+\tstruct rss *elem;\n+\n+\tTAILQ_FOREACH(elem, &p->rss, node)\n+\t\tif (strcmp(elem->name, name) == 0)\n+\t\t\treturn elem;\n+\n+\treturn NULL;\n+}\n+\n+static struct rss *\n+rss_find_by_id(struct rte_swx_pipeline *p, uint32_t rss_obj_id)\n+{\n+\tstruct rss *elem;\n+\n+\tTAILQ_FOREACH(elem, &p->rss, node)\n+\t\tif (elem->id == rss_obj_id)\n+\t\t\treturn elem;\n+\n+\treturn NULL;\n+}\n+\n+int\n+rte_swx_pipeline_rss_config(struct rte_swx_pipeline *p, const char *name)\n+{\n+\tstruct rss *r;\n+\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK_NAME(name, EINVAL);\n+\tCHECK(!rss_find(p, name), EEXIST);\n+\n+\t/* Memory allocation. */\n+\tr = calloc(1, sizeof(struct rss));\n+\tCHECK(r, ENOMEM);\n+\n+\t/* Node initialization. */\n+\tstrcpy(r->name, name);\n+\tr->id = p->n_rss;\n+\n+\t/* Node add to tailq. */\n+\tTAILQ_INSERT_TAIL(&p->rss, r, node);\n+\tp->n_rss++;\n+\n+\treturn 0;\n+}\n+\n+static void\n+rss_build_free(struct rte_swx_pipeline *p)\n+{\n+\tuint32_t i;\n+\n+\tif (!p->rss_runtime)\n+\t\treturn;\n+\n+\tfor (i = 0; i < p->n_rss; i++)\n+\t\tfree(p->rss_runtime[i]);\n+\n+\tfree(p->rss_runtime);\n+\tp->rss_runtime = NULL;\n+}\n+\n+static const struct {\n+\tuint32_t key_size;\n+\tuint8_t key[4];\n+} rss_runtime_default = {\n+\t.key_size = 4,\n+\t.key = {0, 0, 0, 0},\n+};\n+\n+static int\n+rss_build(struct rte_swx_pipeline *p)\n+{\n+\tuint32_t i;\n+\tint status = 0;\n+\n+\t/* Memory allocation. */\n+\tp->rss_runtime = calloc(p->n_rss, sizeof(struct rss_runtime *));\n+\tif (!p->rss_runtime) {\n+\t\tstatus = -ENOMEM;\n+\t\tgoto error;\n+\t}\n+\n+\t/* RSS. */\n+\tfor (i = 0; i < p->n_rss; i++) {\n+\t\tp->rss_runtime[i] = malloc(sizeof(rss_runtime_default));\n+\t\tif (!p->rss_runtime[i]) {\n+\t\t\tstatus = -ENOMEM;\n+\t\t\tgoto error;\n+\t\t}\n+\n+\t\tmemcpy(p->rss_runtime[i], &rss_runtime_default, sizeof(rss_runtime_default));\n+\t}\n+\n+\treturn 0;\n+\n+error:\n+\trss_build_free(p);\n+\treturn status;\n+}\n+\n+static void\n+rss_free(struct rte_swx_pipeline *p)\n+{\n+\trss_build_free(p);\n+\n+\tfor ( ; ; ) {\n+\t\tstruct rss *elem;\n+\n+\t\telem = TAILQ_FIRST(&p->rss);\n+\t\tif (!elem)\n+\t\t\tbreak;\n+\n+\t\tTAILQ_REMOVE(&p->rss, elem, node);\n+\t\tfree(elem);\n+\t}\n+}\n+\n /*\n  * Header.\n  */\n@@ -3002,6 +3125,63 @@ instr_hash_func_exec(struct rte_swx_pipeline *p)\n \tthread_ip_inc(p);\n }\n \n+/*\n+ * rss.\n+ */\n+static int\n+instr_rss_translate(struct rte_swx_pipeline *p,\n+\t\t    struct action *action,\n+\t\t    char **tokens,\n+\t\t    int n_tokens,\n+\t\t    struct instruction *instr,\n+\t\t    struct instruction_data *data __rte_unused)\n+{\n+\tstruct rss *rss;\n+\tstruct field *dst, *src_first, *src_last;\n+\tuint32_t src_struct_id_first = 0, src_struct_id_last = 0;\n+\n+\tCHECK(n_tokens == 5, EINVAL);\n+\n+\trss = rss_find(p, tokens[1]);\n+\tCHECK(rss, EINVAL);\n+\n+\tdst = metadata_field_parse(p, tokens[2]);\n+\tCHECK(dst, EINVAL);\n+\tCHECK(dst->n_bits <= 64, EINVAL);\n+\n+\tsrc_first = struct_field_parse(p, action, tokens[3], &src_struct_id_first);\n+\tCHECK(src_first, EINVAL);\n+\n+\tsrc_last = struct_field_parse(p, action, tokens[4], &src_struct_id_last);\n+\tCHECK(src_last, EINVAL);\n+\tCHECK(!src_last->var_size, EINVAL);\n+\tCHECK(src_struct_id_first == src_struct_id_last, EINVAL);\n+\n+\tinstr->type = INSTR_RSS;\n+\tinstr->rss.rss_obj_id = (uint8_t)rss->id;\n+\tinstr->rss.dst.offset = (uint8_t)dst->offset / 8;\n+\tinstr->rss.dst.n_bits = (uint8_t)dst->n_bits;\n+\tinstr->rss.src.struct_id = (uint8_t)src_struct_id_first;\n+\tinstr->rss.src.offset = (uint16_t)src_first->offset / 8;\n+\tinstr->rss.src.n_bytes = (uint16_t)((src_last->offset + src_last->n_bits -\n+\t\tsrc_first->offset) / 8);\n+\n+\treturn 0;\n+}\n+\n+static inline void\n+instr_rss_exec(struct rte_swx_pipeline *p)\n+{\n+\tstruct thread *t = &p->threads[p->thread_id];\n+\tstruct instruction *ip = t->ip;\n+\n+\t/* Extern function execute. */\n+\t__instr_rss_exec(p, t, ip);\n+\n+\t/* Thread. */\n+\tthread_ip_inc(p);\n+}\n+\n /*\n  * mov.\n  */\n@@ -6397,6 +6577,14 @@ instr_translate(struct rte_swx_pipeline *p,\n \t\t\t\t\t    instr,\n \t\t\t\t\t    data);\n \n+\tif (!strcmp(tokens[tpos], \"rss\"))\n+\t\treturn instr_rss_translate(p,\n+\t\t\t\t\t   action,\n+\t\t\t\t\t   &tokens[tpos],\n+\t\t\t\t\t   n_tokens - tpos,\n+\t\t\t\t\t   instr,\n+\t\t\t\t\t   data);\n+\n \tif (!strcmp(tokens[tpos], \"jmp\"))\n \t\treturn instr_jmp_translate(p,\n \t\t\t\t\t   action,\n@@ -7371,6 +7559,7 @@ static instr_exec_t instruction_table[] = {\n \t[INSTR_EXTERN_OBJ] = instr_extern_obj_exec,\n \t[INSTR_EXTERN_FUNC] = instr_extern_func_exec,\n \t[INSTR_HASH_FUNC] = instr_hash_func_exec,\n+\t[INSTR_RSS] = instr_rss_exec,\n \n \t[INSTR_JMP] = instr_jmp_exec,\n \t[INSTR_JMP_VALID] = instr_jmp_valid_exec,\n@@ -10001,6 +10190,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)\n \tinstruction_table_free(p);\n \tmetadata_free(p);\n \theader_free(p);\n+\trss_free(p);\n \thash_func_free(p);\n \textern_func_free(p);\n \textern_obj_free(p);\n@@ -10149,6 +10339,7 @@ rte_swx_pipeline_config(struct rte_swx_pipeline **p, const char *name, int numa_\n \tTAILQ_INIT(&pipeline->extern_objs);\n \tTAILQ_INIT(&pipeline->extern_funcs);\n \tTAILQ_INIT(&pipeline->hash_funcs);\n+\tTAILQ_INIT(&pipeline->rss);\n \tTAILQ_INIT(&pipeline->headers);\n \tTAILQ_INIT(&pipeline->actions);\n \tTAILQ_INIT(&pipeline->table_types);\n@@ -10263,6 +10454,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)\n \tif (status)\n \t\tgoto error;\n \n+\tstatus = rss_build(p);\n+\tif (status)\n+\t\tgoto error;\n+\n \tstatus = header_build(p);\n \tif (status)\n \t\tgoto error;\n@@ -10318,6 +10513,7 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)\n \tinstruction_table_build_free(p);\n \tmetadata_build_free(p);\n \theader_build_free(p);\n+\trss_build_free(p);\n \thash_func_build_free(p);\n \textern_func_build_free(p);\n \textern_obj_build_free(p);\n@@ -10382,6 +10578,7 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,\n \tpipeline->n_learners = p->n_learners;\n \tpipeline->n_regarrays = p->n_regarrays;\n \tpipeline->n_metarrays = p->n_metarrays;\n+\tpipeline->n_rss = p->n_rss;\n \n \treturn 0;\n }\n@@ -11406,6 +11603,112 @@ rte_swx_ctl_meter_stats_read_with_key(struct rte_swx_pipeline *p,\n \treturn rte_swx_ctl_meter_stats_read(p, metarray_name, entry_id, stats);\n }\n \n+int\n+rte_swx_ctl_rss_info_get(struct rte_swx_pipeline *p,\n+\t\t\t uint32_t rss_obj_id,\n+\t\t\t struct rte_swx_ctl_rss_info *info)\n+{\n+\tstruct rss *rss;\n+\n+\t/* Check the input arguments. */\n+\tif (!p || !info)\n+\t\treturn -EINVAL;\n+\n+\trss = rss_find_by_id(p, rss_obj_id);\n+\tif (!rss)\n+\t\treturn -EINVAL;\n+\n+\t/* Read from the internal data structures. */\n+\tstrcpy(info->name, rss->name);\n+\treturn 0;\n+}\n+\n+int\n+rte_swx_ctl_pipeline_rss_key_size_read(struct rte_swx_pipeline *p,\n+\t\t\t\t       const char *rss_name,\n+\t\t\t\t       uint32_t *key_size)\n+{\n+\tstruct rss *rss;\n+\tstruct rss_runtime *r;\n+\n+\t/* Check the input arguments. */\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK_NAME(rss_name, EINVAL);\n+\trss = rss_find(p, rss_name);\n+\tCHECK(rss, EINVAL);\n+\tr = p->rss_runtime[rss->id];\n+\n+\tCHECK(key_size, EINVAL);\n+\n+\t/* Read from the internal data structures. */\n+\t*key_size = r->key_size;\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_swx_ctl_pipeline_rss_key_read(struct rte_swx_pipeline *p,\n+\t\t\t\t  const char *rss_name,\n+\t\t\t\t  uint8_t *key)\n+{\n+\tstruct rss *rss;\n+\tstruct rss_runtime *r;\n+\n+\t/* Check the input arguments. */\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK_NAME(rss_name, EINVAL);\n+\trss = rss_find(p, rss_name);\n+\tCHECK(rss, EINVAL);\n+\tr = p->rss_runtime[rss->id];\n+\n+\tCHECK(key, EINVAL);\n+\n+\t/* Read from the internal data structures. */\n+\tmemcpy(key, r->key, r->key_size);\n+\n+\treturn 0;\n+}\n+\n+int\n+rte_swx_ctl_pipeline_rss_key_write(struct rte_swx_pipeline *p,\n+\t\t\t\t   const char *rss_name,\n+\t\t\t\t   uint32_t key_size,\n+\t\t\t\t   uint8_t *key)\n+{\n+\tstruct rss *rss;\n+\tstruct rss_runtime *r, *r_new;\n+\n+\t/* Check the input arguments. */\n+\tCHECK(p, EINVAL);\n+\n+\tCHECK_NAME(rss_name, EINVAL);\n+\trss = rss_find(p, rss_name);\n+\tCHECK(rss, EINVAL);\n+\tr = p->rss_runtime[rss->id];\n+\n+\tCHECK(key_size >= 4, EINVAL);\n+\tCHECK(key, EINVAL);\n+\n+\t/* Allocate new RSS run-time entry. */\n+\tr_new = malloc(sizeof(struct rss_runtime) + key_size * sizeof(uint32_t));\n+\tif (!r_new)\n+\t\treturn -ENOMEM;\n+\n+\t/* Fill in the new RSS run-time entry. */\n+\tr_new->key_size = key_size;\n+\tmemcpy(r_new->key, key, key_size);\n+\n+\t/* Commit the RSS run-time change atomically. */\n+\tp->rss_runtime[rss->id] = r_new;\n+\n+\t/* Free the old RSS run-time entry. */\n+\tfree(r);\n+\n+\treturn 0;\n+}\n+\n /*\n  * Pipeline compilation.\n  */\n@@ -11579,6 +11882,7 @@ instr_type_to_name(struct instruction *instr)\n \tcase INSTR_EXTERN_OBJ: return \"INSTR_EXTERN_OBJ\";\n \tcase INSTR_EXTERN_FUNC: return \"INSTR_EXTERN_FUNC\";\n \tcase INSTR_HASH_FUNC: return \"INSTR_HASH_FUNC\";\n+\tcase INSTR_RSS: return \"INSTR_RSS\";\n \n \tcase INSTR_JMP: return \"INSTR_JMP\";\n \tcase INSTR_JMP_VALID: return \"INSTR_JMP_VALID\";\n@@ -12029,6 +12333,34 @@ instr_hash_export(struct instruction *instr, FILE *f)\n \t\tinstr->hash_func.src.n_bytes);\n }\n \n+static void\n+instr_rss_export(struct instruction *instr, FILE *f)\n+{\n+\tfprintf(f,\n+\t\t\"\\t{\\n\"\n+\t\t\"\\t\\t.type = %s,\\n\"\n+\t\t\"\\t\\t.rss = {\\n\"\n+\t\t\"\\t\\t\\t.rss_obj_id = %u,\\n\"\n+\t\t\"\\t\\t\\t.dst = {\\n\"\n+\t\t\"\\t\\t\\t\\t.offset = %u,\\n\"\n+\t\t\"\\t\\t\\t\\t.n_bits = %u,\\n\"\n+\t\t\"\\t\\t\\t},\\n\"\n+\t\t\"\\t\\t\\t.src = {\\n\"\n+\t\t\"\\t\\t\\t\\t.struct_id = %u,\\n\"\n+\t\t\"\\t\\t\\t\\t.offset = %u,\\n\"\n+\t\t\"\\t\\t\\t\\t.n_bytes = %u,\\n\"\n+\t\t\"\\t\\t\\t},\\n\"\n+\t\t\"\\t\\t},\\n\"\n+\t\t\"\\t},\\n\",\n+\t\tinstr_type_to_name(instr),\n+\t\tinstr->rss.rss_obj_id,\n+\t\tinstr->rss.dst.offset,\n+\t\tinstr->rss.dst.n_bits,\n+\t\tinstr->rss.src.struct_id,\n+\t\tinstr->rss.src.offset,\n+\t\tinstr->rss.src.n_bytes);\n+}\n+\n static void\n instr_reg_export(struct instruction *instr __rte_unused, FILE *f __rte_unused)\n {\n@@ -12590,6 +12922,7 @@ static instruction_export_t export_table[] = {\n \t[INSTR_EXTERN_OBJ] = instr_extern_export,\n \t[INSTR_EXTERN_FUNC] = instr_extern_export,\n \t[INSTR_HASH_FUNC] = instr_hash_export,\n+\t[INSTR_RSS] = instr_rss_export,\n \n \t[INSTR_JMP] = instr_jmp_export,\n \t[INSTR_JMP_VALID] = instr_jmp_export,\n@@ -12817,6 +13150,7 @@ instr_type_to_func(struct instruction *instr)\n \tcase INSTR_EXTERN_OBJ: return NULL;\n \tcase INSTR_EXTERN_FUNC: return NULL;\n \tcase INSTR_HASH_FUNC: return \"__instr_hash_func_exec\";\n+\tcase INSTR_RSS: return \"__instr_rss_exec\";\n \n \tcase INSTR_JMP: return NULL;\n \tcase INSTR_JMP_VALID: return NULL;\ndiff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h\nindex 2c9cc6ee44..25df042d3b 100644\n--- a/lib/pipeline/rte_swx_pipeline.h\n+++ b/lib/pipeline/rte_swx_pipeline.h\n@@ -352,6 +352,28 @@ rte_swx_pipeline_hash_func_register(struct rte_swx_pipeline *p,\n \t\t\t\t    const char *name,\n \t\t\t\t    rte_swx_hash_func_t func);\n \n+/*\n+ * RSS.\n+ */\n+\n+/**\n+ * Pipeline Receive Side Scaling (RSS) object configure\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] name\n+ *   Name for the new RSS object.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument;\n+ *   -ENOMEM: Not enough space/cannot allocate memory;\n+ *   -EEXIST: RSS object with this name already exists.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_rss_config(struct rte_swx_pipeline *p,\n+\t\t\t    const char *name);\n+\n /*\n  * Packet headers and meta-data\n  */\ndiff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h\nindex 335506039b..0c29362d31 100644\n--- a/lib/pipeline/rte_swx_pipeline_internal.h\n+++ b/lib/pipeline/rte_swx_pipeline_internal.h\n@@ -199,6 +199,22 @@ struct hash_func_runtime {\n \trte_swx_hash_func_t func;\n };\n \n+/*\n+ * RSS.\n+ */\n+struct rss {\n+\tTAILQ_ENTRY(rss) node;\n+\tchar name[RTE_SWX_NAME_SIZE];\n+\tuint32_t id;\n+};\n+\n+TAILQ_HEAD(rss_tailq, rss);\n+\n+struct rss_runtime {\n+\tuint32_t key_size; /* key size in bytes. */\n+\tuint8_t key[0]; /* key. */\n+};\n+\n /*\n  * Header.\n  */\n@@ -524,6 +540,15 @@ enum instruction_type {\n \t */\n \tINSTR_HASH_FUNC,\n \n+\t/* rss RSS_OBJ_NAME dst src_first src_last\n+\t * Compute the RSS hash value over range of struct fields.\n+\t * dst = M\n+\t * src_first = HMEFT\n+\t * src_last = HMEFT\n+\t * src_first and src_last must be fields within the same struct\n+\t */\n+\tINSTR_RSS,\n+\n \t/* jmp LABEL\n \t * Unconditional jump\n \t */\n@@ -677,6 +702,21 @@ struct instr_hash_func {\n \t} src;\n };\n \n+struct instr_rss {\n+\tuint8_t rss_obj_id;\n+\n+\tstruct {\n+\t\tuint8_t offset;\n+\t\tuint8_t n_bits;\n+\t} dst;\n+\n+\tstruct {\n+\t\tuint8_t struct_id;\n+\t\tuint16_t offset;\n+\t\tuint16_t n_bytes;\n+\t} src;\n+};\n+\n struct instr_dst_src {\n \tstruct instr_operand dst;\n \tunion {\n@@ -763,6 +803,7 @@ struct instruction {\n \t\tstruct instr_extern_obj ext_obj;\n \t\tstruct instr_extern_func ext_func;\n \t\tstruct instr_hash_func hash_func;\n+\t\tstruct instr_rss rss;\n \t\tstruct instr_jmp jmp;\n \t};\n };\n@@ -1480,6 +1521,7 @@ struct rte_swx_pipeline {\n \tstruct extern_obj_tailq extern_objs;\n \tstruct extern_func_tailq extern_funcs;\n \tstruct hash_func_tailq hash_funcs;\n+\tstruct rss_tailq rss;\n \tstruct header_tailq headers;\n \tstruct struct_type *metadata_st;\n \tuint32_t metadata_struct_id;\n@@ -1502,6 +1544,7 @@ struct rte_swx_pipeline {\n \tstruct selector_statistics *selector_stats;\n \tstruct learner_statistics *learner_stats;\n \tstruct hash_func_runtime *hash_func_runtime;\n+\tstruct rss_runtime **rss_runtime;\n \tstruct regarray_runtime *regarray_runtime;\n \tstruct metarray_runtime *metarray_runtime;\n \tstruct instruction *instructions;\n@@ -1518,6 +1561,7 @@ struct rte_swx_pipeline {\n \tuint32_t n_extern_objs;\n \tuint32_t n_extern_funcs;\n \tuint32_t n_hash_funcs;\n+\tuint32_t n_rss;\n \tuint32_t n_actions;\n \tuint32_t n_tables;\n \tuint32_t n_selectors;\n@@ -2467,6 +2511,58 @@ __instr_hash_func_exec(struct rte_swx_pipeline *p,\n \tMETADATA_WRITE(t, dst_offset, n_dst_bits, result);\n }\n \n+/*\n+ * rss.\n+ */\n+static inline uint32_t\n+rss_func(void *rss_key, uint32_t rss_key_size, void *input_data, uint32_t input_data_size)\n+{\n+\tuint32_t *key = (uint32_t *)rss_key;\n+\tuint32_t *data = (uint32_t *)input_data;\n+\tuint32_t key_size = rss_key_size >> 2;\n+\tuint32_t data_size = input_data_size >> 2;\n+\tuint32_t hash_val = 0, i;\n+\n+\tfor (i = 0; i < data_size; i++) {\n+\t\tuint32_t d;\n+\n+\t\tfor (d = data[i]; d; d &= (d - 1)) {\n+\t\t\tuint32_t key0, key1, pos;\n+\n+\t\t\tpos = rte_bsf32(d);\n+\t\t\tkey0 = key[i % key_size] << (31 - pos);\n+\t\t\tkey1 = key[(i + 1) % key_size] >> (pos + 1);\n+\t\t\thash_val ^= key0 | key1;\n+\t\t}\n+\t}\n+\n+\treturn hash_val;\n+}\n+\n+static inline void\n+__instr_rss_exec(struct rte_swx_pipeline *p,\n+\t\t struct thread *t,\n+\t\t const struct instruction *ip)\n+{\n+\tuint32_t rss_obj_id = ip->rss.rss_obj_id;\n+\tuint32_t dst_offset = ip->rss.dst.offset;\n+\tuint32_t n_dst_bits = ip->rss.dst.n_bits;\n+\tuint32_t src_struct_id = ip->rss.src.struct_id;\n+\tuint32_t src_offset = ip->rss.src.offset;\n+\tuint32_t n_src_bytes = ip->rss.src.n_bytes;\n+\n+\tstruct rss_runtime *r = p->rss_runtime[rss_obj_id];\n+\tuint8_t *src_ptr = t->structs[src_struct_id];\n+\tuint32_t result;\n+\n+\tTRACE(\"[Thread %2u] rss %u\\n\",\n+\t      p->thread_id,\n+\t      rss_obj_id);\n+\n+\tresult = rss_func(r->key, r->key_size, &src_ptr[src_offset], n_src_bytes);\n+\tMETADATA_WRITE(t, dst_offset, n_dst_bits, result);\n+}\n+\n /*\n  * mov.\n  */\ndiff --git a/lib/pipeline/rte_swx_pipeline_spec.c b/lib/pipeline/rte_swx_pipeline_spec.c\nindex 9116f38ed2..4c2a8ca191 100644\n--- a/lib/pipeline/rte_swx_pipeline_spec.c\n+++ b/lib/pipeline/rte_swx_pipeline_spec.c\n@@ -2049,6 +2049,52 @@ metarray_statement_parse(struct metarray_spec *s,\n \treturn 0;\n }\n \n+/*\n+ *\n+ * rss\n+ *\n+ */\n+\n+static void\n+rss_spec_free(struct rss_spec *s)\n+{\n+        if (!s)\n+                return;\n+\n+        free(s->name);\n+        s->name = NULL;\n+}\n+\n+static int\n+rss_statement_parse(struct rss_spec *s,\n+\t\t\t char **tokens,\n+\t\t\t uint32_t n_tokens,\n+\t\t\t uint32_t n_lines,\n+\t\t\t uint32_t *err_line,\n+\t\t\t const char **err_msg)\n+{\n+\t/* Check format. */\n+\tif ((n_tokens != 2)) {\n+\t\tif (err_line)\n+\t\t\t*err_line = n_lines;\n+\t\tif (err_msg)\n+\t\t\t*err_msg = \"Invalid rss statement.\";\n+\t\treturn -EINVAL;\n+\t}\n+\n+\t/* spec. */\n+\ts->name = strdup(tokens[1]);\n+\tif (!s->name) {\n+\t\tif (err_line)\n+\t\t\t*err_line = n_lines;\n+\t\tif (err_msg)\n+\t\t\t*err_msg = \"Memory allocation failed.\";\n+\t\treturn -ENOMEM;\n+\t}\n+\n+\treturn 0;\n+}\n+\n /*\n  * apply.\n  *\n@@ -2243,6 +2289,17 @@ pipeline_spec_codegen(FILE *f,\n \n \tfprintf(f, \"};\\n\\n\");\n \n+\t/* rss. */\n+\tfprintf(f, \"static struct rss_spec rss[] = {\\n\");\n+\n+\tfor (i = 0; i < s->n_rss; i++) {\n+\t\tstruct rss_spec *rss_spec = &s->rss[i];\n+\t\tfprintf(f, \"\\t[%d] = {\\n\", i);\n+\t\tfprintf(f, \"\\t\\t.name = \\\"%s\\\",\\n\", rss_spec->name);\n+\t\tfprintf(f, \"\\t},\\n\");\n+\t}\n+\tfprintf(f, \"};\\n\\n\");\n+\n \t/* struct. */\n \tfor (i = 0; i < s->n_structs; i++) {\n \t\tstruct struct_spec *struct_spec = &s->structs[i];\n@@ -2794,6 +2851,7 @@ pipeline_spec_codegen(FILE *f,\n \tfprintf(f, \"\\t.learners = learners,\\n\");\n \tfprintf(f, \"\\t.regarrays = regarrays,\\n\");\n \tfprintf(f, \"\\t.metarrays = metarrays,\\n\");\n+\tfprintf(f, \"\\t.rss = rss,\\n\");\n \tfprintf(f, \"\\t.apply = apply,\\n\");\n \tfprintf(f, \"\\t.n_extobjs = sizeof(extobjs) / sizeof(extobjs[0]),\\n\");\n \tfprintf(f, \"\\t.n_structs = sizeof(structs) / sizeof(structs[0]),\\n\");\n@@ -2805,6 +2863,7 @@ pipeline_spec_codegen(FILE *f,\n \tfprintf(f, \"\\t.n_learners = sizeof(learners) / sizeof(learners[0]),\\n\");\n \tfprintf(f, \"\\t.n_regarrays = sizeof(regarrays) / sizeof(regarrays[0]),\\n\");\n \tfprintf(f, \"\\t.n_metarrays = sizeof(metarrays) / sizeof(metarrays[0]),\\n\");\n+\tfprintf(f, \"\\t.n_rss = sizeof(rss) / sizeof(rss[0]),\\n\");\n \tfprintf(f, \"\\t.n_apply = sizeof(apply) / sizeof(apply[0]),\\n\");\n \tfprintf(f, \"};\\n\");\n }\n@@ -2824,6 +2883,7 @@ pipeline_spec_parse(FILE *spec,\n \tstruct learner_spec learner_spec = {0};\n \tstruct regarray_spec regarray_spec = {0};\n \tstruct metarray_spec metarray_spec = {0};\n+\tstruct rss_spec rss_spec = {0};\n \tstruct apply_spec apply_spec = {0};\n \tstruct pipeline_spec *s = NULL;\n \tuint32_t n_lines = 0;\n@@ -3372,6 +3432,40 @@ pipeline_spec_parse(FILE *spec,\n \t\t\tcontinue;\n \t\t}\n \n+\t\t/* rss object configuration */\n+\t\tif (!strcmp(tokens[0], \"rss\")) {\n+\t\t\tstruct rss_spec *new_rss;\n+\n+\t\t\tstatus = rss_statement_parse(&rss_spec,\n+\t\t\t\t\t\t     tokens,\n+\t\t\t\t\t\t     n_tokens,\n+\t\t\t\t\t\t     n_lines,\n+\t\t\t\t\t\t     err_line,\n+\t\t\t\t\t\t     err_msg);\n+\t\t\tif (status)\n+\t\t\t\tgoto error;\n+\n+\t\t\tnew_rss = realloc(s->rss,\n+\t\t\t\t(s->n_rss + 1) * sizeof(struct rss_spec));\n+\t\t\tif (!new_rss) {\n+\t\t\t\tif (err_line)\n+\t\t\t\t\t*err_line = n_lines;\n+\t\t\t\tif (err_msg)\n+\t\t\t\t\t*err_msg = \"Memory allocation failed.\";\n+\t\t\t\tstatus = -ENOMEM;\n+\t\t\t\tgoto error;\n+\t\t\t}\n+\n+\t\t\ts->rss = new_rss;\n+\t\t\tmemcpy(&s->rss[s->n_rss],\n+\t\t\t       &rss_spec,\n+\t\t\t       sizeof(struct rss_spec));\n+\t\t\ts->n_rss++;\n+\t\t\tmemset(&rss_spec, 0, sizeof(struct rss_spec));\n+\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\t/* apply. */\n \t\tif (!strcmp(tokens[0], \"apply\")) {\n \t\t\tstatus = apply_statement_parse(&block_mask,\n@@ -3418,6 +3512,7 @@ pipeline_spec_parse(FILE *spec,\n \tlearner_spec_free(&learner_spec);\n \tregarray_spec_free(&regarray_spec);\n \tmetarray_spec_free(&metarray_spec);\n+\trss_spec_free(&rss_spec);\n \tapply_spec_free(&apply_spec);\n \tpipeline_spec_free(s);\n \n@@ -3583,6 +3678,18 @@ pipeline_spec_configure(struct rte_swx_pipeline *p,\n \t\t}\n \t}\n \n+\t/* rss. */\n+\tfor (i = 0; i < s->n_rss; i++) {\n+\t\tstruct rss_spec *rss_spec = &s->rss[i];\n+\n+\t\tstatus = rte_swx_pipeline_rss_config(p, rss_spec->name);\n+\t\tif (status) {\n+\t\t\tif (err_msg)\n+\t\t\t\t*err_msg = \"rss object configuration error.\";\n+\t\t\treturn status;\n+\t\t}\n+\t}\n+\n \t/* apply. */\n \tfor (i = 0; i < s->n_apply; i++) {\n \t\tstruct apply_spec *apply_spec = &s->apply[i];\ndiff --git a/lib/pipeline/rte_swx_pipeline_spec.h b/lib/pipeline/rte_swx_pipeline_spec.h\nindex 123e175f8b..dc2ff6fa08 100644\n--- a/lib/pipeline/rte_swx_pipeline_spec.h\n+++ b/lib/pipeline/rte_swx_pipeline_spec.h\n@@ -171,6 +171,15 @@ struct metarray_spec {\n \tuint32_t size;\n };\n \n+/*\n+ * rss.\n+ *\n+ * rss NAME\n+ */\n+struct rss_spec {\n+        char *name;\n+};\n+\n /*\n  * apply.\n  *\n@@ -198,6 +207,7 @@ struct pipeline_spec {\n \tstruct learner_spec *learners;\n \tstruct regarray_spec *regarrays;\n \tstruct metarray_spec *metarrays;\n+\tstruct rss_spec *rss;\n \tstruct apply_spec *apply;\n \n \tuint32_t n_extobjs;\n@@ -210,6 +220,7 @@ struct pipeline_spec {\n \tuint32_t n_learners;\n \tuint32_t n_regarrays;\n \tuint32_t n_metarrays;\n+\tuint32_t n_rss;\n \tuint32_t n_apply;\n };\n \ndiff --git a/lib/pipeline/version.map b/lib/pipeline/version.map\nindex 58e34459c3..6f45c73428 100644\n--- a/lib/pipeline/version.map\n+++ b/lib/pipeline/version.map\n@@ -157,6 +157,10 @@ EXPERIMENTAL {\n \trte_swx_pipeline_find;\n \n \t#added in 23.03\n+\trte_swx_ctl_rss_info_get;\n+\trte_swx_ctl_pipeline_rss_key_size_read;\n+\trte_swx_ctl_pipeline_rss_key_read;\n+\trte_swx_ctl_pipeline_rss_key_write;\n \trte_swx_ipsec_create;\n \trte_swx_ipsec_find;\n \trte_swx_ipsec_free;\n@@ -164,4 +168,5 @@ EXPERIMENTAL {\n \trte_swx_ipsec_sa_add;\n \trte_swx_ipsec_sa_delete;\n \trte_swx_ipsec_sa_read;\n+\trte_swx_pipeline_rss_config;\n };\n",
    "prefixes": [
        "V2"
    ]
}