get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 109148,
    "url": "https://patches.dpdk.org/api/patches/109148/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20220405123002.40671-2-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": "<20220405123002.40671-2-cristian.dumitrescu@intel.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20220405123002.40671-2-cristian.dumitrescu@intel.com",
    "date": "2022-04-05T12:30:01",
    "name": "[V2,2/3] pipeline: support packet mirroring",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "7f4b2e08c0123911b59d34e1de334a9ac31e4958",
    "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/20220405123002.40671-2-cristian.dumitrescu@intel.com/mbox/",
    "series": [
        {
            "id": 22351,
            "url": "https://patches.dpdk.org/api/series/22351/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=22351",
            "date": "2022-04-05T12:30:00",
            "name": "[V2,1/3] port: support packet mirroring",
            "version": 2,
            "mbox": "https://patches.dpdk.org/series/22351/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/109148/comments/",
    "check": "success",
    "checks": "https://patches.dpdk.org/api/patches/109148/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 97850A0509;\n\tTue,  5 Apr 2022 14:30:17 +0200 (CEST)",
            "from [217.70.189.124] (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id BAAA642847;\n\tTue,  5 Apr 2022 14:30:15 +0200 (CEST)",
            "from mga04.intel.com (mga04.intel.com [192.55.52.120])\n by mails.dpdk.org (Postfix) with ESMTP id 455FE40DF6\n for <dev@dpdk.org>; Tue,  5 Apr 2022 14:30:06 +0200 (CEST)",
            "from orsmga002.jf.intel.com ([10.7.209.21])\n by fmsmga104.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384;\n 05 Apr 2022 05:30:06 -0700",
            "from silpixa00400573.ir.intel.com (HELO\n silpixa00400573.ger.corp.intel.com) ([10.237.223.107])\n by orsmga002.jf.intel.com with ESMTP; 05 Apr 2022 05:30:04 -0700"
        ],
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple;\n d=intel.com; i=@intel.com; q=dns/txt; s=Intel;\n t=1649161807; x=1680697807;\n h=from:to:cc:subject:date:message-id:in-reply-to: references;\n bh=+aDZ8RNYE6Mzqm8SABiim3xH2l/q2/7GJU1fT6hvU0A=;\n b=VAJaPILGj1V1u9msR7uKG/SgqFps7WzH2+BHUYdcAb5EzFBy9rifjK0U\n ZNZOHcWE/N7vgZ50moJ1dbfDqqborAVtYNw2mp4f81H1YIvAib6IRhkCp\n h5PVnG9YUIGrOoO5n+W7g9MGrXSGYFtv87raF2UJCo5CPWKhi1/uE62K4\n NoNlu6T4JE00eKvIkuDix6B/ndbO33yFw/+i1RIj3L9Kc8wD5hgJ7KuKE\n ioLAVY2qonM3yp4YUwRuSQJu9NlrC4idPfIXbERRcSIz0x7QRtA9CNCem\n w7+cN2p7xHRWFGr4Cg61jyEy1/zVIFnB4dA34Ud2cY+g4J7ztpAqTdF+9 Q==;",
        "X-IronPort-AV": [
            "E=McAfee;i=\"6200,9189,10307\"; a=\"259567325\"",
            "E=Sophos;i=\"5.90,236,1643702400\"; d=\"scan'208\";a=\"259567325\"",
            "E=Sophos;i=\"5.90,236,1643702400\"; d=\"scan'208\";a=\"523441147\""
        ],
        "X-ExtLoop1": "1",
        "From": "Cristian Dumitrescu <cristian.dumitrescu@intel.com>",
        "To": "dev@dpdk.org",
        "Cc": "Kamalakannan R <kamalakannan.r@intel.com>",
        "Subject": "[PATCH V2 2/3] pipeline: support packet mirroring",
        "Date": "Tue,  5 Apr 2022 13:30:01 +0100",
        "Message-Id": "<20220405123002.40671-2-cristian.dumitrescu@intel.com>",
        "X-Mailer": "git-send-email 2.17.1",
        "In-Reply-To": "<20220405123002.40671-1-cristian.dumitrescu@intel.com>",
        "References": "<20220304180623.74893-1-cristian.dumitrescu@intel.com>\n <20220405123002.40671-1-cristian.dumitrescu@intel.com>",
        "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": "The packet mirroring is configured through slots and sessions, with\nthe number of slots and sessions set at init.\n\nThe new \"mirror\" instruction assigns one of the existing sessions to a\nspecific slot, which results in scheduling a mirror operation for the\ncurrent packet to be executed later at the time the packet is either\ntransmitted or dropped.\n\nSeveral copies of the same input packet can be mirrored to different\noutput ports by using multiple slots.\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               |  42 +++++\n lib/pipeline/rte_swx_pipeline.c          | 198 +++++++++++++++++++++++\n lib/pipeline/rte_swx_pipeline.h          |  30 ++++\n lib/pipeline/rte_swx_pipeline_internal.h |  76 +++++++++\n lib/pipeline/version.map                 |   2 +\n 5 files changed, 348 insertions(+)",
    "diff": "diff --git a/lib/pipeline/rte_swx_ctl.h b/lib/pipeline/rte_swx_ctl.h\nindex ed752ad5eb..204026dc0e 100644\n--- a/lib/pipeline/rte_swx_ctl.h\n+++ b/lib/pipeline/rte_swx_ctl.h\n@@ -41,6 +41,12 @@ struct rte_swx_ctl_pipeline_info {\n \t/** Number of input ports. */\n \tuint32_t n_ports_out;\n \n+\t/** Number of packet mirroring slots. */\n+\tuint32_t n_mirroring_slots;\n+\n+\t/** Number of packet mirroring sessions. */\n+\tuint32_t n_mirroring_sessions;\n+\n \t/** Number of actions. */\n \tuint32_t n_actions;\n \n@@ -655,6 +661,42 @@ rte_swx_ctl_pipeline_learner_stats_read(struct rte_swx_pipeline *p,\n \t\t\t\t      const char *learner_name,\n \t\t\t\t      struct rte_swx_learner_stats *stats);\n \n+/*\n+ * Packet mirroring API.\n+ */\n+\n+/** Packet mirroring session parameters. */\n+struct rte_swx_pipeline_mirroring_session_params {\n+\t/** Output port ID. */\n+\tuint32_t port_id;\n+\n+\t/** Fast clone flag. */\n+\tint fast_clone;\n+\n+\t/** Truncation packet length (in bytes). Zero means no truncation. */\n+\tuint32_t truncation_length;\n+};\n+\n+/**\n+ * Packet mirroring session set\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] session_id\n+ *   Packet mirroring session ID.\n+ * @param[in] params\n+ *   Packet mirroring session parameters.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument;\n+ *   -EEXIST: Pipeline was already built successfully.\n+ */\n+__rte_experimental\n+int\n+rte_swx_ctl_pipeline_mirroring_session_set(struct rte_swx_pipeline *p,\n+\tuint32_t session_id,\n+\tstruct rte_swx_pipeline_mirroring_session_params *params);\n+\n /*\n  * Table Update API.\n  */\ndiff --git a/lib/pipeline/rte_swx_pipeline.c b/lib/pipeline/rte_swx_pipeline.c\nindex 8c4670e111..062fbcfc89 100644\n--- a/lib/pipeline/rte_swx_pipeline.c\n+++ b/lib/pipeline/rte_swx_pipeline.c\n@@ -423,6 +423,8 @@ rte_swx_pipeline_port_out_type_register(struct rte_swx_pipeline *p,\n \tCHECK(ops->create, EINVAL);\n \tCHECK(ops->free, EINVAL);\n \tCHECK(ops->pkt_tx, EINVAL);\n+\tCHECK(ops->pkt_fast_clone_tx, EINVAL);\n+\tCHECK(ops->pkt_clone_tx, EINVAL);\n \tCHECK(ops->stats_read, EINVAL);\n \n \tCHECK(!port_out_type_find(p, name), EEXIST);\n@@ -509,6 +511,8 @@ port_out_build(struct rte_swx_pipeline *p)\n \t\tstruct port_out_runtime *out = &p->out[port->id];\n \n \t\tout->pkt_tx = port->type->ops.pkt_tx;\n+\t\tout->pkt_fast_clone_tx = port->type->ops.pkt_fast_clone_tx;\n+\t\tout->pkt_clone_tx = port->type->ops.pkt_clone_tx;\n \t\tout->flush = port->type->ops.flush;\n \t\tout->obj = port->obj;\n \t}\n@@ -554,6 +558,81 @@ port_out_free(struct rte_swx_pipeline *p)\n \t}\n }\n \n+/*\n+ * Packet mirroring.\n+ */\n+int\n+rte_swx_pipeline_mirroring_config(struct rte_swx_pipeline *p,\n+\t\t\t\t  struct rte_swx_pipeline_mirroring_params *params)\n+{\n+\tCHECK(p, EINVAL);\n+\tCHECK(params, EINVAL);\n+\tCHECK(params->n_slots, EINVAL);\n+\tCHECK(params->n_sessions, EINVAL);\n+\tCHECK(!p->build_done, EEXIST);\n+\n+\tp->n_mirroring_slots = rte_align32pow2(params->n_slots);\n+\tif (p->n_mirroring_slots > 64)\n+\t\tp->n_mirroring_slots = 64;\n+\n+\tp->n_mirroring_sessions = rte_align32pow2(params->n_sessions);\n+\n+\treturn 0;\n+}\n+\n+static void\n+mirroring_build_free(struct rte_swx_pipeline *p)\n+{\n+\tuint32_t i;\n+\n+\tfor (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {\n+\t\tstruct thread *t = &p->threads[i];\n+\n+\t\t/* mirroring_slots. */\n+\t\tfree(t->mirroring_slots);\n+\t\tt->mirroring_slots = NULL;\n+\t}\n+\n+\t/* mirroring_sessions. */\n+\tfree(p->mirroring_sessions);\n+\tp->mirroring_sessions = NULL;\n+}\n+\n+static void\n+mirroring_free(struct rte_swx_pipeline *p)\n+{\n+\tmirroring_build_free(p);\n+}\n+\n+static int\n+mirroring_build(struct rte_swx_pipeline *p)\n+{\n+\tuint32_t i;\n+\n+\tif (!p->n_mirroring_slots || !p->n_mirroring_sessions)\n+\t\treturn 0;\n+\n+\tfor (i = 0; i < RTE_SWX_PIPELINE_THREADS_MAX; i++) {\n+\t\tstruct thread *t = &p->threads[i];\n+\n+\t\t/* mirroring_slots. */\n+\t\tt->mirroring_slots = calloc(p->n_mirroring_slots, sizeof(uint32_t));\n+\t\tif (!t->mirroring_slots)\n+\t\t\tgoto error;\n+\t}\n+\n+\t/* mirroring_sessions. */\n+\tp->mirroring_sessions = calloc(p->n_mirroring_sessions, sizeof(struct mirroring_session));\n+\tif (!p->mirroring_sessions)\n+\t\tgoto error;\n+\n+\treturn 0;\n+\n+error:\n+\tmirroring_build_free(p);\n+\treturn -ENOMEM;\n+}\n+\n /*\n  * Extern object.\n  */\n@@ -1653,6 +1732,56 @@ instr_drop_exec(struct rte_swx_pipeline *p)\n \tinstr_rx_exec(p);\n }\n \n+/*\n+ * mirror.\n+ */\n+static int\n+instr_mirror_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+\tchar *dst = tokens[1], *src = tokens[2];\n+\tstruct field *fdst, *fsrc;\n+\tuint32_t dst_struct_id = 0, src_struct_id = 0;\n+\n+\tCHECK(n_tokens == 3, EINVAL);\n+\n+\tfdst = struct_field_parse(p, action, dst, &dst_struct_id);\n+\tCHECK(fdst, EINVAL);\n+\tCHECK(dst[0] != 'h', EINVAL);\n+\tCHECK(!fdst->var_size, EINVAL);\n+\n+\tfsrc = struct_field_parse(p, action, src, &src_struct_id);\n+\tCHECK(fsrc, EINVAL);\n+\tCHECK(src[0] != 'h', EINVAL);\n+\tCHECK(!fsrc->var_size, EINVAL);\n+\n+\tinstr->type = INSTR_MIRROR;\n+\tinstr->mirror.dst.struct_id = (uint8_t)dst_struct_id;\n+\tinstr->mirror.dst.n_bits = fdst->n_bits;\n+\tinstr->mirror.dst.offset = fdst->offset / 8;\n+\tinstr->mirror.src.struct_id = (uint8_t)src_struct_id;\n+\tinstr->mirror.src.n_bits = fsrc->n_bits;\n+\tinstr->mirror.src.offset = fsrc->offset / 8;\n+\n+\treturn 0;\n+}\n+\n+static inline void\n+instr_mirror_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__instr_mirror_exec(p, t, ip);\n+\n+\t/* Thread. */\n+\tthread_ip_inc(p);\n+}\n+\n /*\n  * extract.\n  */\n@@ -5653,6 +5782,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], \"mirror\"))\n+\t\treturn instr_mirror_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], \"extract\"))\n \t\treturn instr_hdr_extract_translate(p,\n \t\t\t\t\t\t   action,\n@@ -6677,6 +6814,7 @@ static instr_exec_t instruction_table[] = {\n \t[INSTR_TX] = instr_tx_exec,\n \t[INSTR_TX_I] = instr_tx_i_exec,\n \t[INSTR_DROP] = instr_drop_exec,\n+\t[INSTR_MIRROR] = instr_mirror_exec,\n \n \t[INSTR_HDR_EXTRACT] = instr_hdr_extract_exec,\n \t[INSTR_HDR_EXTRACT2] = instr_hdr_extract2_exec,\n@@ -9026,6 +9164,7 @@ rte_swx_pipeline_free(struct rte_swx_pipeline *p)\n \theader_free(p);\n \textern_func_free(p);\n \textern_obj_free(p);\n+\tmirroring_free(p);\n \tport_out_free(p);\n \tport_in_free(p);\n \tstruct_free(p);\n@@ -9234,6 +9373,10 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)\n \tif (status)\n \t\tgoto error;\n \n+\tstatus = mirroring_build(p);\n+\tif (status)\n+\t\tgoto error;\n+\n \tstatus = struct_build(p);\n \tif (status)\n \t\tgoto error;\n@@ -9305,6 +9448,7 @@ rte_swx_pipeline_build(struct rte_swx_pipeline *p)\n \theader_build_free(p);\n \textern_func_build_free(p);\n \textern_obj_build_free(p);\n+\tmirroring_build_free(p);\n \tport_out_build_free(p);\n \tport_in_build_free(p);\n \tstruct_build_free(p);\n@@ -9356,6 +9500,8 @@ rte_swx_ctl_pipeline_info_get(struct rte_swx_pipeline *p,\n \n \tpipeline->n_ports_in = p->n_ports_in;\n \tpipeline->n_ports_out = p->n_ports_out;\n+\tpipeline->n_mirroring_slots = p->n_mirroring_slots;\n+\tpipeline->n_mirroring_sessions = p->n_mirroring_sessions;\n \tpipeline->n_actions = n_actions;\n \tpipeline->n_tables = n_tables;\n \tpipeline->n_selectors = p->n_selectors;\n@@ -10043,6 +10189,27 @@ rte_swx_ctl_meter_stats_read(struct rte_swx_pipeline *p,\n \treturn 0;\n }\n \n+int\n+rte_swx_ctl_pipeline_mirroring_session_set(struct rte_swx_pipeline *p,\n+\t\t\t\t\t   uint32_t session_id,\n+\t\t\t\t\t   struct rte_swx_pipeline_mirroring_session_params *params)\n+{\n+\tstruct mirroring_session *s;\n+\n+\tCHECK(p, EINVAL);\n+\tCHECK(p->build_done, EEXIST);\n+\tCHECK(session_id < p->n_mirroring_sessions, EINVAL);\n+\tCHECK(params, EINVAL);\n+\tCHECK(params->port_id < p->n_ports_out, EINVAL);\n+\n+\ts = &p->mirroring_sessions[session_id];\n+\ts->port_id = params->port_id;\n+\ts->fast_clone = params->fast_clone;\n+\ts->truncation_length = params->truncation_length ? params->truncation_length : UINT32_MAX;\n+\n+\treturn 0;\n+}\n+\n /*\n  * Pipeline compilation.\n  */\n@@ -10055,6 +10222,7 @@ instr_type_to_name(struct instruction *instr)\n \tcase INSTR_TX: return \"INSTR_TX\";\n \tcase INSTR_TX_I: return \"INSTR_TX_I\";\n \tcase INSTR_DROP: return \"INSTR_DROP\";\n+\tcase INSTR_MIRROR: return \"INSTR_MIRROR\";\n \n \tcase INSTR_HDR_EXTRACT: return \"INSTR_HDR_EXTRACT\";\n \tcase INSTR_HDR_EXTRACT2: return \"INSTR_HDR_EXTRACT2\";\n@@ -10357,6 +10525,34 @@ instr_io_export(struct instruction *instr, FILE *f)\n \t\t\"\\t},\\n\");\n }\n \n+static void\n+instr_mirror_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.mirror = {\\n\"\n+\t\t\"\\t\\t\\t.dst = {\\n\"\n+\t\t\"\\t\\t\\t\\t.struct_id = %u,\\n\"\n+\t\t\"\\t\\t\\t\\t.n_bits = %u,\\n\"\n+\t\t\"\\t\\t\\t\\t.offset = %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.n_bits = %u,\\n\"\n+\t\t\"\\t\\t\\t\\t.offset = %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->mirror.dst.struct_id,\n+\t\tinstr->mirror.dst.n_bits,\n+\t\tinstr->mirror.dst.offset,\n+\t\tinstr->mirror.src.struct_id,\n+\t\tinstr->mirror.src.n_bits,\n+\t\tinstr->mirror.src.offset);\n+}\n+\n static void\n instr_hdr_validate_export(struct instruction *instr, FILE *f)\n {\n@@ -10924,6 +11120,7 @@ static instruction_export_t export_table[] = {\n \t[INSTR_TX] = instr_io_export,\n \t[INSTR_TX_I] = instr_io_export,\n \t[INSTR_DROP] = instr_io_export,\n+\t[INSTR_MIRROR] = instr_mirror_export,\n \n \t[INSTR_HDR_EXTRACT] = instr_io_export,\n \t[INSTR_HDR_EXTRACT2] = instr_io_export,\n@@ -11142,6 +11339,7 @@ instr_type_to_func(struct instruction *instr)\n \tcase INSTR_TX: return \"__instr_tx_exec\";\n \tcase INSTR_TX_I: return \"__instr_tx_i_exec\";\n \tcase INSTR_DROP: return \"__instr_drop_exec\";\n+\tcase INSTR_MIRROR: return \"__instr_mirror_exec\";\n \n \tcase INSTR_HDR_EXTRACT: return \"__instr_hdr_extract_exec\";\n \tcase INSTR_HDR_EXTRACT2: return \"__instr_hdr_extract2_exec\";\ndiff --git a/lib/pipeline/rte_swx_pipeline.h b/lib/pipeline/rte_swx_pipeline.h\nindex 430e458335..1cfd1c542f 100644\n--- a/lib/pipeline/rte_swx_pipeline.h\n+++ b/lib/pipeline/rte_swx_pipeline.h\n@@ -159,6 +159,36 @@ rte_swx_pipeline_port_out_config(struct rte_swx_pipeline *p,\n \t\t\t\t uint32_t port_id,\n \t\t\t\t const char *port_type_name,\n \t\t\t\t void *args);\n+/*\n+ * Packet mirroring\n+ */\n+\n+/** Packet mirroring parameters. */\n+struct rte_swx_pipeline_mirroring_params {\n+\t/** Number of packet mirroring slots. */\n+\tuint32_t n_slots;\n+\n+\t/** Maximum number of packet mirroring sessions. */\n+\tuint32_t n_sessions;\n+};\n+\n+/**\n+ * Packet mirroring configure\n+ *\n+ * @param[in] p\n+ *   Pipeline handle.\n+ * @param[in] params\n+ *   Packet mirroring parameters.\n+ * @return\n+ *   0 on success or the following error codes otherwise:\n+ *   -EINVAL: Invalid argument;\n+ *   -ENOMEM: Not enough memory;\n+ *   -EEXIST: Pipeline was already built successfully.\n+ */\n+__rte_experimental\n+int\n+rte_swx_pipeline_mirroring_config(struct rte_swx_pipeline *p,\n+\t\t\t\t  struct rte_swx_pipeline_mirroring_params *params);\n \n /*\n  * Extern objects and functions\ndiff --git a/lib/pipeline/rte_swx_pipeline_internal.h b/lib/pipeline/rte_swx_pipeline_internal.h\nindex da3e88bfa8..808a0cbdbb 100644\n--- a/lib/pipeline/rte_swx_pipeline_internal.h\n+++ b/lib/pipeline/rte_swx_pipeline_internal.h\n@@ -104,10 +104,21 @@ TAILQ_HEAD(port_out_tailq, port_out);\n \n struct port_out_runtime {\n \trte_swx_port_out_pkt_tx_t pkt_tx;\n+\trte_swx_port_out_pkt_fast_clone_tx_t pkt_fast_clone_tx;\n+\trte_swx_port_out_pkt_clone_tx_t pkt_clone_tx;\n \trte_swx_port_out_flush_t flush;\n \tvoid *obj;\n };\n \n+/*\n+ * Packet mirroring.\n+ */\n+struct mirroring_session {\n+\tuint32_t port_id;\n+\tint fast_clone;\n+\tuint32_t truncation_length;\n+};\n+\n /*\n  * Extern object.\n  */\n@@ -227,6 +238,13 @@ enum instruction_type {\n \tINSTR_TX_I, /* port_out = I */\n \tINSTR_DROP,\n \n+\t/*\n+\t * mirror slot_id session_id\n+\t * slot_id = MEFT\n+\t * session_id = MEFT\n+\t */\n+\tINSTR_MIRROR,\n+\n \t/* extract h.header */\n \tINSTR_HDR_EXTRACT,\n \tINSTR_HDR_EXTRACT2,\n@@ -670,6 +688,7 @@ struct instruction {\n \tenum instruction_type type;\n \tunion {\n \t\tstruct instr_io io;\n+\t\tstruct instr_dst_src mirror;\n \t\tstruct instr_hdr_validity valid;\n \t\tstruct instr_dst_src mov;\n \t\tstruct instr_regarray regarray;\n@@ -902,6 +921,8 @@ struct thread {\n \t/* Packet. */\n \tstruct rte_swx_pkt pkt;\n \tuint8_t *ptr;\n+\tuint32_t *mirroring_slots;\n+\tuint64_t mirroring_slots_mask;\n \n \t/* Structures. */\n \tuint8_t **structs;\n@@ -1399,6 +1420,7 @@ struct rte_swx_pipeline {\n \n \tstruct port_in_runtime *in;\n \tstruct port_out_runtime *out;\n+\tstruct mirroring_session *mirroring_sessions;\n \tstruct instruction **action_instructions;\n \taction_func_t *action_funcs;\n \tstruct rte_swx_table_state *table_state;\n@@ -1416,6 +1438,8 @@ struct rte_swx_pipeline {\n \tuint32_t n_structs;\n \tuint32_t n_ports_in;\n \tuint32_t n_ports_out;\n+\tuint32_t n_mirroring_slots;\n+\tuint32_t n_mirroring_sessions;\n \tuint32_t n_extern_objs;\n \tuint32_t n_extern_funcs;\n \tuint32_t n_actions;\n@@ -1511,6 +1535,8 @@ __instr_rx_exec(struct rte_swx_pipeline *p, struct thread *t, const struct instr\n \t      pkt_received ? \"1 pkt\" : \"0 pkts\",\n \t      p->port_id);\n \n+\tt->mirroring_slots_mask = 0;\n+\n \t/* Headers. */\n \tt->valid_headers = 0;\n \tt->n_headers_out = 0;\n@@ -1596,6 +1622,33 @@ emit_handler(struct thread *t)\n \t}\n }\n \n+static inline void\n+mirroring_handler(struct rte_swx_pipeline *p, struct thread *t, struct rte_swx_pkt *pkt)\n+{\n+\tuint64_t slots_mask = t->mirroring_slots_mask, slot_mask;\n+\tuint32_t slot_id;\n+\n+\tfor (slot_id = 0, slot_mask = 1LLU ; slots_mask; slot_id++, slot_mask <<= 1)\n+\t\tif (slot_mask & slots_mask) {\n+\t\t\tstruct port_out_runtime *port;\n+\t\t\tstruct mirroring_session *session;\n+\t\t\tuint32_t port_id, session_id;\n+\n+\t\t\tsession_id = t->mirroring_slots[slot_id];\n+\t\t\tsession = &p->mirroring_sessions[session_id];\n+\n+\t\t\tport_id = session->port_id;\n+\t\t\tport = &p->out[port_id];\n+\n+\t\t\tif (session->fast_clone)\n+\t\t\t\tport->pkt_fast_clone_tx(port->obj, pkt);\n+\t\t\telse\n+\t\t\t\tport->pkt_clone_tx(port->obj, pkt, session->truncation_length);\n+\n+\t\t\tslots_mask &= ~slot_mask;\n+\t\t}\n+}\n+\n static inline void\n __instr_tx_exec(struct rte_swx_pipeline *p, struct thread *t, const struct instruction *ip)\n {\n@@ -1611,6 +1664,7 @@ __instr_tx_exec(struct rte_swx_pipeline *p, struct thread *t, const struct instr\n \temit_handler(t);\n \n \t/* Packet. */\n+\tmirroring_handler(p, t, pkt);\n \tport->pkt_tx(port->obj, pkt);\n }\n \n@@ -1629,6 +1683,7 @@ __instr_tx_i_exec(struct rte_swx_pipeline *p, struct thread *t, const struct ins\n \temit_handler(t);\n \n \t/* Packet. */\n+\tmirroring_handler(p, t, pkt);\n \tport->pkt_tx(port->obj, pkt);\n }\n \n@@ -1648,9 +1703,30 @@ __instr_drop_exec(struct rte_swx_pipeline *p,\n \temit_handler(t);\n \n \t/* Packet. */\n+\tmirroring_handler(p, t, pkt);\n \tport->pkt_tx(port->obj, pkt);\n }\n \n+static inline void\n+__instr_mirror_exec(struct rte_swx_pipeline *p,\n+\t\t    struct thread *t,\n+\t\t    const struct instruction *ip)\n+{\n+\tuint64_t slot_id = instr_operand_hbo(t, &ip->mirror.dst);\n+\tuint64_t session_id = instr_operand_hbo(t, &ip->mirror.src);\n+\n+\tslot_id &= p->n_mirroring_slots - 1;\n+\tsession_id &= p->n_mirroring_sessions - 1;\n+\n+\tTRACE(\"[Thread %2u]: mirror pkt (slot = %u, session = %u)\\n\",\n+\t      p->thread_id,\n+\t      (uint32_t)slot_id,\n+\t      (uint32_t)session_id);\n+\n+\tt->mirroring_slots[slot_id] = session_id;\n+\tt->mirroring_slots_mask |= 1LLU << slot_id;\n+}\n+\n /*\n  * extract.\n  */\ndiff --git a/lib/pipeline/version.map b/lib/pipeline/version.map\nindex 8bc90e7cd7..44332aad26 100644\n--- a/lib/pipeline/version.map\n+++ b/lib/pipeline/version.map\n@@ -67,6 +67,7 @@ EXPERIMENTAL {\n \trte_swx_ctl_pipeline_create;\n \trte_swx_ctl_pipeline_free;\n \trte_swx_ctl_pipeline_info_get;\n+\trte_swx_ctl_pipeline_mirroring_session_set;\n \trte_swx_ctl_pipeline_numa_node_get;\n \trte_swx_ctl_pipeline_port_in_stats_read;\n \trte_swx_ctl_pipeline_port_out_stats_read;\n@@ -90,6 +91,7 @@ EXPERIMENTAL {\n \trte_swx_pipeline_flush;\n \trte_swx_pipeline_free;\n \trte_swx_pipeline_instructions_config;\n+\trte_swx_pipeline_mirroring_config;\n \trte_swx_pipeline_packet_header_register;\n \trte_swx_pipeline_packet_metadata_register;\n \trte_swx_pipeline_port_in_config;\n",
    "prefixes": [
        "V2",
        "2/3"
    ]
}