get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 123052,
    "url": "https://patches.dpdk.org/api/patches/123052/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20230203193732.436973-1-cristian.dumitrescu@intel.com/",
    "project": {
        "id": 1,
        "url": "https://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": "<20230203193732.436973-1-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230203193732.436973-1-cristian.dumitrescu@intel.com",
    "date": "2023-02-03T19:37:32",
    "name": "pipeline: add RSS support",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "30b3f484c6e821e2989b2213b89f51986365c7b0",
    "submitter": {
        "id": 19,
        "url": "https://patches.dpdk.org/api/people/19/?format=api",
        "name": "Cristian Dumitrescu",
        "email": "cristian.dumitrescu@intel.com"
    },
    "delegate": {
        "id": 1,
        "url": "https://patches.dpdk.org/api/users/1/?format=api",
        "username": "tmonjalo",
        "first_name": "Thomas",
        "last_name": "Monjalon",
        "email": "thomas@monjalon.net"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20230203193732.436973-1-cristian.dumitrescu@intel.com/mbox/",
    "series": [
        {
            "id": 26792,
            "url": "https://patches.dpdk.org/api/series/26792/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=26792",
            "date": "2023-02-03T19:37:32",
            "name": "pipeline: add RSS support",
            "version": 1,
            "mbox": "https://patches.dpdk.org/series/26792/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/123052/comments/",
    "check": "warning",
    "checks": "https://patches.dpdk.org/api/patches/123052/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 2AC0C41BC0;\n\tFri,  3 Feb 2023 20:37:38 +0100 (CET)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id E8F1B40EDF;\n\tFri,  3 Feb 2023 20:37:37 +0100 (CET)",
            "from mga11.intel.com (mga11.intel.com [192.55.52.93])\n by mails.dpdk.org (Postfix) with ESMTP id 4660140EDF\n for <dev@dpdk.org>; Fri,  3 Feb 2023 20:37:36 +0100 (CET)",
            "from orsmga007.jf.intel.com ([10.7.209.58])\n by fmsmga102.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 03 Feb 2023 11:37:35 -0800",
            "from silpixa00400573.ir.intel.com (HELO\n silpixa00400573.ger.corp.intel.com) ([10.237.222.53])\n by orsmga007.jf.intel.com with ESMTP; 03 Feb 2023 11:37:33 -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=1675453056; x=1706989056;\n h=from:to:cc:subject:date:message-id:mime-version:\n content-transfer-encoding;\n bh=0Se5LDAe0IdBQHBZRy/cvkUleauyxkbaMGPz218C9Go=;\n b=Q64JsED8eqeqbIxydEsemX/fRBoYaTXXtpNMGJi0s1TssR4XqPJeNeak\n fqkDi2c4lZGO/SoFWboPMfctHrdHKRgiufG6XxeLfF6RGXf1OMpJtTR7P\n rG07k/47gfKQ9d8T7/XQ2pWfFTF4l89o2IxaRFW03Y8xqLCvKTDfjh8rC\n VIkL/FqJWx/Whn87NySovXCwYW6iELFAQ9v1YbbeUWTrU9jCgrHa9sd2J\n fvB1pEsITHYW2UJL/PIwC0CEfZdcJdPgSXaNKkqITPttHbBn4dWcu+Lng\n V0kv79OjD3YEmAvjiXCfLPoxNWw+4AEnYXpR/n46XQBq2tDQg8rZBSZCz Q==;",
        "X-IronPort-AV": [
            "E=McAfee;i=\"6500,9779,10610\"; a=\"326532690\"",
            "E=Sophos;i=\"5.97,271,1669104000\"; d=\"scan'208\";a=\"326532690\"",
            "E=McAfee;i=\"6500,9779,10610\"; a=\"659197694\"",
            "E=Sophos;i=\"5.97,271,1669104000\"; d=\"scan'208\";a=\"659197694\""
        ],
        "X-ExtLoop1": "1",
        "From": "Cristian Dumitrescu <cristian.dumitrescu@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "Kamalakannan R <kamalakannan.r@intel.com>",
        "Subject": "[PATCH] pipeline: add RSS support",
        "Date": "Fri,  3 Feb 2023 19:37:32 +0000",
        "Message-Id": "<20230203193732.436973-1-cristian.dumitrescu@intel.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "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---\n lib/pipeline/rte_swx_ctl.h               |  92 +++++++\n lib/pipeline/rte_swx_pipeline.c          | 310 +++++++++++++++++++++++\n lib/pipeline/rte_swx_pipeline.h          |  22 ++\n lib/pipeline/rte_swx_pipeline_internal.h |  92 +++++++\n lib/pipeline/version.map                 |   5 +\n 5 files changed, 521 insertions(+)",
    "diff": "diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h\nindex 2eb51b2c76..57fbe67dfa 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_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_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_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_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_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_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..0c477f2811 100644\n--- a/lib/pipeline/rte_swx_pipeline.c\n+++ b/lib/pipeline/rte_swx_pipeline.c\n@@ -1255,6 +1255,100 @@ 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 uint32_t rss_runtime_default[] = {1, 0};\n+\n+static int\n+rss_build(struct rte_swx_pipeline *p)\n+{\n+\tstruct rss *rss;\n+\n+\t/* Memory allocation. */\n+\tp->rss_runtime = calloc(p->n_rss, sizeof(struct rss_runtime *));\n+\tCHECK(p->rss_runtime, ENOMEM);\n+\n+\t/* RSS. */\n+\tTAILQ_FOREACH(rss, &p->rss, node)\n+\t\tp->rss_runtime[rss->id] = (struct rss_runtime *)rss_runtime_default;\n+\n+\treturn 0;\n+}\n+\n+static void\n+rss_build_free(struct rte_swx_pipeline *p)\n+{\n+\tfree(p->rss_runtime);\n+\tp->rss_runtime = NULL;\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 +3096,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 +6548,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 +7530,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 +10161,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 +10310,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 +10425,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 +10484,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 +10549,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 +11574,117 @@ 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 * 4;\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+\tuint32_t *key32 = (uint32_t *)key, i;\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+\tfor (i = 0; i < r->key_size; i++)\n+\t\tkey32[i] = rte_be_to_cpu_32(r->key[i]);\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+\tuint32_t *key32 = (uint32_t *)key, i;\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) && rte_is_power_of_2(key_size), EINVAL);\n+\tCHECK(key, EINVAL);\n+\tkey_size /= 4;\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+\tfor (i = 0; i < key_size; i++)\n+\t\tr_new->key[i] = rte_cpu_to_be_32(key32[i]);\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 +11858,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 +12309,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 +12898,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 +13126,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..7f5cce7bc4 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 32-bit increments. */\n+\tuint32_t key[0]; /* key as array of 32-bit increments, each in NBO format. */\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,54 @@ __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(uint32_t *key, uint32_t key_size, uint32_t *data, uint32_t data_size)\n+{\n+\tuint32_t hash_val = 0, key_mask = key_size - 1, 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_mask] << (31 - pos);\n+\t\t\tkey1 = key[(i + 1) & key_mask] >> (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, (uint32_t *)&src_ptr[src_offset], n_src_bytes >> 2);\n+\tMETADATA_WRITE(t, dst_offset, n_dst_bits, result);\n+}\n+\n /*\n  * mov.\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": []
}