get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 80722,
    "url": "http://patches.dpdk.org/api/patches/80722/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20201014114015.17197-3-andreyv@nvidia.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": "<20201014114015.17197-3-andreyv@nvidia.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20201014114015.17197-3-andreyv@nvidia.com",
    "date": "2020-10-14T11:40:15",
    "name": "[v8,2/2] app/testpmd: support shared action",
    "commit_ref": null,
    "pull_url": null,
    "state": "accepted",
    "archived": true,
    "hash": "970eec68c712997d6a51805249122a1bdca77c33",
    "submitter": {
        "id": 1969,
        "url": "http://patches.dpdk.org/api/people/1969/?format=api",
        "name": "Andrey Vesnovaty",
        "email": "andreyv@nvidia.com"
    },
    "delegate": {
        "id": 319,
        "url": "http://patches.dpdk.org/api/users/319/?format=api",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@amd.com"
    },
    "mbox": "http://patches.dpdk.org/project/dpdk/patch/20201014114015.17197-3-andreyv@nvidia.com/mbox/",
    "series": [
        {
            "id": 12955,
            "url": "http://patches.dpdk.org/api/series/12955/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=12955",
            "date": "2020-10-14T11:40:13",
            "name": "RTE flow shared action",
            "version": 8,
            "mbox": "http://patches.dpdk.org/series/12955/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/80722/comments/",
    "check": "fail",
    "checks": "http://patches.dpdk.org/api/patches/80722/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 dpdk.org (dpdk.org [92.243.14.124])\n\tby inbox.dpdk.org (Postfix) with ESMTP id 7EFA8A04B7;\n\tWed, 14 Oct 2020 13:41:04 +0200 (CEST)",
            "from [92.243.14.124] (localhost [127.0.0.1])\n\tby dpdk.org (Postfix) with ESMTP id 79F451DE21;\n\tWed, 14 Oct 2020 13:40:32 +0200 (CEST)",
            "from mellanox.co.il (mail-il-dmz.mellanox.com [193.47.165.129])\n by dpdk.org (Postfix) with ESMTP id EB40D1DE1C\n for <dev@dpdk.org>; Wed, 14 Oct 2020 13:40:30 +0200 (CEST)",
            "from Internal Mail-Server by MTLPINE1 (envelope-from\n andreyv@nvidia.com) with SMTP; 14 Oct 2020 14:40:26 +0300",
            "from nvidia.com (r-arch-host11.mtr.labs.mlnx [10.213.43.60])\n by labmailer.mlnx (8.13.8/8.13.8) with ESMTP id 09EBeKD8000998;\n Wed, 14 Oct 2020 14:40:26 +0300"
        ],
        "From": "Andrey Vesnovaty <andreyv@nvidia.com>",
        "To": "dev@dpdk.org",
        "Cc": "jer@marvell.com, jerinjacobk@gmail.com, thomas@monjalon.net,\n ferruh.yigit@intel.com, stephen@networkplumber.org,\n bruce.richardson@intel.com, orika@nvidia.com, viacheslavo@nvidia.com,\n andrey.vesnovaty@gmail.com, mdr@ashroe.eu, nhorman@tuxdriver.com,\n ajit.khaparde@broadcom.com, samik.gupta@broadcom.com,\n andrew.rybchenko@oktetlabs.ru, Wenzhuo Lu <wenzhuo.lu@intel.com>,\n Beilei Xing <beilei.xing@intel.com>,\n Bernard Iremonger <bernard.iremonger@intel.com>",
        "Date": "Wed, 14 Oct 2020 14:40:15 +0300",
        "Message-Id": "<20201014114015.17197-3-andreyv@nvidia.com>",
        "X-Mailer": "git-send-email 2.26.2",
        "In-Reply-To": "<20201014114015.17197-1-andreyv@nvidia.com>",
        "References": "<20200702120511.16315-1-andreyv@mellanox.com>\n <20201014114015.17197-1-andreyv@nvidia.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Subject": "[dpdk-dev] [PATCH v8 2/2] app/testpmd: support shared action",
        "X-BeenThere": "dev@dpdk.org",
        "X-Mailman-Version": "2.1.15",
        "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",
        "Sender": "\"dev\" <dev-bounces@dpdk.org>"
    },
    "content": "This patch adds shared action support to testpmd CLI.\n\nAll shared actions created via testpmd CLI assigned ID for further\nreference in other CLI commands. Shared action ID supplied as CLI\nargument or assigned by testpmd is similar to flow ID & limited to\nscope of testpdm CLI.\n\nCreate shared action syntax:\nflow shared_action {port_id} create [action_id {shared_action_id}]\n\t[ingress] [egress] action {action} / end\n\nCreate shared action examples:\n\tflow shared_action 0 create action_id 100 \\\n\t\tingress action rss queues 1 2 end / end\n\tThis creates shared rss action with id 100 on port 0.\n\n\tflow shared_action 0 create action_id \\\n\t\tingress action rss queues 0 1 end / end\n\tThis creates shared rss action with id assigned by testpmd\n\ton port 0.\n\nUpdate shared action syntax:\nflow shared_action {port_id} update {shared_action_id}\n\taction {action} / end\n\nUpdate shared action example:\n\tflow shared_action 0 update 100 \\\n\t\taction rss queues 0 3 end / end\n\tThis updates shared rss action having id 100 on port 0\n\twith rss to queues 0 3 (in create example rss queues were\n\t1 & 2).\n\nDestroy shared action syntax:\nflow shared_action {port_id} destroy action_id {shared_action_id} [...]\n\nDestroy shared action example:\n\tflow shared_action 0 destroy action_id 100 action_id 101\n\tThis destroys shared actions having id 100 & 101\n\nQuery shared action syntax:\nflow shared_action {port} query {shared_action_id}\n\nQuery shared action example:\n\tflow shared_action 0 query 100\n\tThis queries shared actions having id 100\n\nUse shared action as flow action syntax:\nflow create {port_id} ... / end actions [action / [...]]\n\tshared {action_id} / [action / [...]] end\n\nUse shared action as flow action example:\n\tflow create 0 ingress pattern ... / end \\\n\t\tactions shared 100 / end\n\tThis creates flow rule where rss action is shared rss action\n\thaving id 100.\n\nAll shared action CLIs report status of the command.\nShared action query CLI output depends on action type.\n\nSigned-off-by: Andrey Vesnovaty <andreyv@nvidia.com>\nAcked-by: Ori Kam <orika@nvidia.com>\nAcked-by: Ajit Khaparde <ajit.khaparde@broadcom.com>\n---\n app/test-pmd/cmdline.c                      |  18 ++\n app/test-pmd/cmdline_flow.c                 | 294 +++++++++++++++++++-\n app/test-pmd/config.c                       | 216 ++++++++++++++\n app/test-pmd/testpmd.h                      |  20 ++\n doc/guides/testpmd_app_ug/testpmd_funcs.rst | 112 ++++++++\n 5 files changed, 659 insertions(+), 1 deletion(-)",
    "diff": "diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c\nindex 273fb1af62..078f0630c2 100644\n--- a/app/test-pmd/cmdline.c\n+++ b/app/test-pmd/cmdline.c\n@@ -1152,6 +1152,24 @@ static void cmd_help_long_parsed(void *parsed_result,\n \t\t\t\"    List and destroy aged flows\"\n \t\t\t\" flow rules\\n\\n\"\n \n+\t\t\t\"flow shared_action {port_id} create\"\n+\t\t\t\" [action_id {shared_action_id}]\"\n+\t\t\t\" [ingress] [egress]\"\n+\t\t\t\" action {action} / end\\n\"\n+\t\t\t\"    Create shared action.\\n\\n\"\n+\n+\t\t\t\"flow shared_action {port_id} update\"\n+\t\t\t\" {shared_action_id} action {action} / end\\n\"\n+\t\t\t\"    Update shared action.\\n\\n\"\n+\n+\t\t\t\"flow shared_action {port_id} destroy\"\n+\t\t\t\" action_id {shared_action_id} [...]\\n\"\n+\t\t\t\"    Destroy specific shared actions.\\n\\n\"\n+\n+\t\t\t\"flow shared_action {port_id} query\"\n+\t\t\t\" {shared_action_id}\\n\"\n+\t\t\t\"    Query an existing shared action.\\n\\n\"\n+\n \t\t\t\"set vxlan ip-version (ipv4|ipv6) vni (vni) udp-src\"\n \t\t\t\" (udp-src) udp-dst (udp-dst) ip-src (ip-src) ip-dst\"\n \t\t\t\" (ip-dst) eth-src (eth-src) eth-dst (eth-dst)\\n\"\ndiff --git a/app/test-pmd/cmdline_flow.c b/app/test-pmd/cmdline_flow.c\nindex 761ac5ab96..30b1c20825 100644\n--- a/app/test-pmd/cmdline_flow.c\n+++ b/app/test-pmd/cmdline_flow.c\n@@ -50,6 +50,7 @@ enum index {\n \tPORT_ID,\n \tGROUP_ID,\n \tPRIORITY_LEVEL,\n+\tSHARED_ACTION_ID,\n \n \t/* Top-level command. */\n \tSET,\n@@ -61,6 +62,7 @@ enum index {\n \t/* Top-level command. */\n \tFLOW,\n \t/* Sub-level commands. */\n+\tSHARED_ACTION,\n \tVALIDATE,\n \tCREATE,\n \tDESTROY,\n@@ -90,6 +92,21 @@ enum index {\n \tEGRESS,\n \tTRANSFER,\n \n+\t/* Shared action arguments */\n+\tSHARED_ACTION_CREATE,\n+\tSHARED_ACTION_UPDATE,\n+\tSHARED_ACTION_DESTROY,\n+\tSHARED_ACTION_QUERY,\n+\n+\t/* Shared action create arguments */\n+\tSHARED_ACTION_CREATE_ID,\n+\tSHARED_ACTION_INGRESS,\n+\tSHARED_ACTION_EGRESS,\n+\tSHARED_ACTION_SPEC,\n+\n+\t/* Shared action destroy arguments */\n+\tSHARED_ACTION_DESTROY_ID,\n+\n \t/* Validate/create pattern. */\n \tPATTERN,\n \tITEM_PARAM_IS,\n@@ -361,6 +378,8 @@ enum index {\n \tACTION_SET_IPV6_DSCP_VALUE,\n \tACTION_AGE,\n \tACTION_AGE_TIMEOUT,\n+\tACTION_SHARED,\n+\tSHARED_ACTION_ID2PTR,\n };\n \n /** Maximum size for pattern in struct rte_flow_item_raw. */\n@@ -654,6 +673,13 @@ struct buffer {\n \tenum index command; /**< Flow command. */\n \tportid_t port; /**< Affected port ID. */\n \tunion {\n+\t\tstruct {\n+\t\t\tuint32_t *action_id;\n+\t\t\tuint32_t action_id_n;\n+\t\t} sa_destroy; /**< Shared action destroy arguments. */\n+\t\tstruct {\n+\t\t\tuint32_t action_id;\n+\t\t} sa; /* Shared action query arguments */\n \t\tstruct {\n \t\t\tstruct rte_flow_attr attr;\n \t\t\tstruct rte_flow_item *pattern;\n@@ -710,6 +736,22 @@ struct parse_action_priv {\n \t\t.size = s, \\\n \t})\n \n+static const enum index next_sa_create_attr[] = {\n+\tSHARED_ACTION_CREATE_ID,\n+\tSHARED_ACTION_INGRESS,\n+\tSHARED_ACTION_EGRESS,\n+\tSHARED_ACTION_SPEC,\n+\tZERO,\n+};\n+\n+static const enum index next_sa_subcmd[] = {\n+\tSHARED_ACTION_CREATE,\n+\tSHARED_ACTION_UPDATE,\n+\tSHARED_ACTION_DESTROY,\n+\tSHARED_ACTION_QUERY,\n+\tZERO,\n+};\n+\n static const enum index next_vc_attr[] = {\n \tGROUP,\n \tPRIORITY,\n@@ -744,6 +786,12 @@ static const enum index next_aged_attr[] = {\n \tZERO,\n };\n \n+static const enum index next_sa_destroy_attr[] = {\n+\tSHARED_ACTION_DESTROY_ID,\n+\tEND,\n+\tZERO,\n+};\n+\n static const enum index item_param[] = {\n \tITEM_PARAM_IS,\n \tITEM_PARAM_SPEC,\n@@ -1194,6 +1242,7 @@ static const enum index next_action[] = {\n \tACTION_SET_IPV4_DSCP,\n \tACTION_SET_IPV6_DSCP,\n \tACTION_AGE,\n+\tACTION_SHARED,\n \tZERO,\n };\n \n@@ -1551,6 +1600,15 @@ static int parse_ipv6_addr(struct context *, const struct token *,\n static int parse_port(struct context *, const struct token *,\n \t\t      const char *, unsigned int,\n \t\t      void *, unsigned int);\n+static int parse_sa(struct context *, const struct token *,\n+\t\t    const char *, unsigned int,\n+\t\t    void *, unsigned int);\n+static int parse_sa_destroy(struct context *ctx, const struct token *token,\n+\t\t\t    const char *str, unsigned int len,\n+\t\t\t    void *buf, unsigned int size);\n+static int parse_sa_id2ptr(struct context *ctx, const struct token *token,\n+\t\t\t   const char *str, unsigned int len, void *buf,\n+\t\t\t   unsigned int size);\n static int comp_none(struct context *, const struct token *,\n \t\t     unsigned int, char *, unsigned int);\n static int comp_boolean(struct context *, const struct token *,\n@@ -1689,13 +1747,21 @@ static const struct token token_list[] = {\n \t\t.call = parse_int,\n \t\t.comp = comp_none,\n \t},\n+\t[SHARED_ACTION_ID] = {\n+\t\t.name = \"{shared_action_id}\",\n+\t\t.type = \"SHARED_ACTION_ID\",\n+\t\t.help = \"shared action id\",\n+\t\t.call = parse_int,\n+\t\t.comp = comp_none,\n+\t},\n \t/* Top-level command. */\n \t[FLOW] = {\n \t\t.name = \"flow\",\n \t\t.type = \"{command} {port_id} [{arg} [...]]\",\n \t\t.help = \"manage ingress/egress flow rules\",\n \t\t.next = NEXT(NEXT_ENTRY\n-\t\t\t     (VALIDATE,\n+\t\t\t     (SHARED_ACTION,\n+\t\t\t      VALIDATE,\n \t\t\t      CREATE,\n \t\t\t      DESTROY,\n \t\t\t      FLUSH,\n@@ -1706,7 +1772,44 @@ static const struct token token_list[] = {\n \t\t\t      ISOLATE)),\n \t\t.call = parse_init,\n \t},\n+\t/* Top-level command. */\n+\t[SHARED_ACTION] = {\n+\t\t.name = \"shared_action\",\n+\t\t.type = \"{command} {port_id} [{arg} [...]]\",\n+\t\t.help = \"manage shared actions\",\n+\t\t.next = NEXT(next_sa_subcmd, NEXT_ENTRY(PORT_ID)),\n+\t\t.args = ARGS(ARGS_ENTRY(struct buffer, port)),\n+\t\t.call = parse_sa,\n+\t},\n \t/* Sub-level commands. */\n+\t[SHARED_ACTION_CREATE] = {\n+\t\t.name = \"create\",\n+\t\t.help = \"create shared action\",\n+\t\t.next = NEXT(next_sa_create_attr),\n+\t\t.call = parse_sa,\n+\t},\n+\t[SHARED_ACTION_UPDATE] = {\n+\t\t.name = \"update\",\n+\t\t.help = \"update shared action\",\n+\t\t.next = NEXT(NEXT_ENTRY(SHARED_ACTION_SPEC),\n+\t\t\t     NEXT_ENTRY(SHARED_ACTION_ID)),\n+\t\t.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),\n+\t\t.call = parse_sa,\n+\t},\n+\t[SHARED_ACTION_DESTROY] = {\n+\t\t.name = \"destroy\",\n+\t\t.help = \"destroy shared action\",\n+\t\t.next = NEXT(NEXT_ENTRY(SHARED_ACTION_DESTROY_ID)),\n+\t\t.args = ARGS(ARGS_ENTRY(struct buffer, port)),\n+\t\t.call = parse_sa_destroy,\n+\t},\n+\t[SHARED_ACTION_QUERY] = {\n+\t\t.name = \"query\",\n+\t\t.help = \"query shared action\",\n+\t\t.next = NEXT(NEXT_ENTRY(END), NEXT_ENTRY(SHARED_ACTION_ID)),\n+\t\t.args = ARGS(ARGS_ENTRY(struct buffer, args.sa.action_id)),\n+\t\t.call = parse_sa,\n+\t},\n \t[VALIDATE] = {\n \t\t.name = \"validate\",\n \t\t.help = \"check whether a flow rule can be created\",\n@@ -3860,6 +3963,57 @@ static const struct token token_list[] = {\n \t\t.next = NEXT(action_age, NEXT_ENTRY(UNSIGNED)),\n \t\t.call = parse_vc_conf,\n \t},\n+\t/* Shared action destroy arguments. */\n+\t[SHARED_ACTION_DESTROY_ID] = {\n+\t\t.name = \"action_id\",\n+\t\t.help = \"specify a shared action id to destroy\",\n+\t\t.next = NEXT(next_sa_destroy_attr,\n+\t\t\t     NEXT_ENTRY(SHARED_ACTION_ID)),\n+\t\t.args = ARGS(ARGS_ENTRY_PTR(struct buffer,\n+\t\t\t\t\t    args.sa_destroy.action_id)),\n+\t\t.call = parse_sa_destroy,\n+\t},\n+\t/* Shared action create arguments. */\n+\t[SHARED_ACTION_CREATE_ID] = {\n+\t\t.name = \"action_id\",\n+\t\t.help = \"specify a shared action id to create\",\n+\t\t.next = NEXT(next_sa_create_attr,\n+\t\t\t     NEXT_ENTRY(SHARED_ACTION_ID)),\n+\t\t.args = ARGS(ARGS_ENTRY(struct buffer, args.vc.attr.group)),\n+\t},\n+\t[ACTION_SHARED] = {\n+\t\t.name = \"shared\",\n+\t\t.help = \"apply shared action by id\",\n+\t\t.priv = PRIV_ACTION(SHARED, 0),\n+\t\t.next = NEXT(NEXT_ENTRY(SHARED_ACTION_ID2PTR)),\n+\t\t.args = ARGS(ARGS_ENTRY_ARB(0, sizeof(uint32_t))),\n+\t\t.call = parse_vc,\n+\t},\n+\t[SHARED_ACTION_ID2PTR] = {\n+\t\t.name = \"{action_id}\",\n+\t\t.type = \"SHARED_ACTION_ID\",\n+\t\t.help = \"shared action id\",\n+\t\t.next = NEXT(NEXT_ENTRY(ACTION_NEXT)),\n+\t\t.call = parse_sa_id2ptr,\n+\t\t.comp = comp_none,\n+\t},\n+\t[SHARED_ACTION_INGRESS] = {\n+\t\t.name = \"ingress\",\n+\t\t.help = \"affect rule to ingress\",\n+\t\t.next = NEXT(next_sa_create_attr),\n+\t\t.call = parse_sa,\n+\t},\n+\t[SHARED_ACTION_EGRESS] = {\n+\t\t.name = \"egress\",\n+\t\t.help = \"affect rule to egress\",\n+\t\t.next = NEXT(next_sa_create_attr),\n+\t\t.call = parse_sa,\n+\t},\n+\t[SHARED_ACTION_SPEC] = {\n+\t\t.name = \"action\",\n+\t\t.help = \"specify action to share\",\n+\t\t.next = NEXT(next_action),\n+\t},\n };\n \n /** Remove and return last entry from argument stack. */\n@@ -4044,6 +4198,97 @@ parse_init(struct context *ctx, const struct token *token,\n \treturn len;\n }\n \n+/** Parse tokens for shared action commands. */\n+static int\n+parse_sa(struct context *ctx, const struct token *token,\n+\t const char *str, unsigned int len,\n+\t void *buf, unsigned int size)\n+{\n+\tstruct buffer *out = buf;\n+\n+\t/* Token name must match. */\n+\tif (parse_default(ctx, token, str, len, NULL, 0) < 0)\n+\t\treturn -1;\n+\t/* Nothing else to do if there is no buffer. */\n+\tif (!out)\n+\t\treturn len;\n+\tif (!out->command) {\n+\t\tif (ctx->curr != SHARED_ACTION)\n+\t\t\treturn -1;\n+\t\tif (sizeof(*out) > size)\n+\t\t\treturn -1;\n+\t\tout->command = ctx->curr;\n+\t\tctx->objdata = 0;\n+\t\tctx->object = out;\n+\t\tctx->objmask = NULL;\n+\t\tout->args.vc.data = (uint8_t *)out + size;\n+\t\treturn len;\n+\t}\n+\tswitch (ctx->curr) {\n+\tcase SHARED_ACTION_CREATE:\n+\tcase SHARED_ACTION_UPDATE:\n+\t\tout->args.vc.actions =\n+\t\t\t(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),\n+\t\t\t\t\t       sizeof(double));\n+\t\tout->args.vc.attr.group = UINT32_MAX;\n+\t\t/* fallthrough */\n+\tcase SHARED_ACTION_QUERY:\n+\t\tout->command = ctx->curr;\n+\t\tctx->objdata = 0;\n+\t\tctx->object = out;\n+\t\tctx->objmask = NULL;\n+\t\treturn len;\n+\tcase SHARED_ACTION_EGRESS:\n+\t\tout->args.vc.attr.egress = 1;\n+\t\treturn len;\n+\tcase SHARED_ACTION_INGRESS:\n+\t\tout->args.vc.attr.ingress = 1;\n+\t\treturn len;\n+\tdefault:\n+\t\treturn -1;\n+\t}\n+}\n+\n+\n+/** Parse tokens for shared action destroy command. */\n+static int\n+parse_sa_destroy(struct context *ctx, const struct token *token,\n+\t\t const char *str, unsigned int len,\n+\t\t void *buf, unsigned int size)\n+{\n+\tstruct buffer *out = buf;\n+\tuint32_t *action_id;\n+\n+\t/* Token name must match. */\n+\tif (parse_default(ctx, token, str, len, NULL, 0) < 0)\n+\t\treturn -1;\n+\t/* Nothing else to do if there is no buffer. */\n+\tif (!out)\n+\t\treturn len;\n+\tif (!out->command || out->command == SHARED_ACTION) {\n+\t\tif (ctx->curr != SHARED_ACTION_DESTROY)\n+\t\t\treturn -1;\n+\t\tif (sizeof(*out) > size)\n+\t\t\treturn -1;\n+\t\tout->command = ctx->curr;\n+\t\tctx->objdata = 0;\n+\t\tctx->object = out;\n+\t\tctx->objmask = NULL;\n+\t\tout->args.sa_destroy.action_id =\n+\t\t\t(void *)RTE_ALIGN_CEIL((uintptr_t)(out + 1),\n+\t\t\t\t\t       sizeof(double));\n+\t\treturn len;\n+\t}\n+\taction_id = out->args.sa_destroy.action_id\n+\t\t    + out->args.sa_destroy.action_id_n++;\n+\tif ((uint8_t *)action_id > (uint8_t *)out + size)\n+\t\treturn -1;\n+\tctx->objdata = 0;\n+\tctx->object = action_id;\n+\tctx->objmask = NULL;\n+\treturn len;\n+}\n+\n /** Parse tokens for validate/create commands. */\n static int\n parse_vc(struct context *ctx, const struct token *token,\n@@ -6111,6 +6356,32 @@ parse_port(struct context *ctx, const struct token *token,\n \treturn ret;\n }\n \n+static int\n+parse_sa_id2ptr(struct context *ctx, const struct token *token,\n+\t\tconst char *str, unsigned int len,\n+\t\tvoid *buf, unsigned int size)\n+{\n+\tstruct rte_flow_action *action = ctx->object;\n+\tuint32_t id;\n+\tint ret;\n+\n+\t(void)buf;\n+\t(void)size;\n+\tctx->objdata = 0;\n+\tctx->object = &id;\n+\tctx->objmask = NULL;\n+\tret = parse_int(ctx, token, str, len, ctx->object, sizeof(id));\n+\tctx->object = action;\n+\tif (ret != (int)len)\n+\t\treturn ret;\n+\t/* set shared action */\n+\tif (action) {\n+\t\taction->conf = port_shared_action_get_by_id(ctx->port, id);\n+\t\tret = (action->conf) ? ret : -1;\n+\t}\n+\treturn ret;\n+}\n+\n /** Parse set command, initialize output buffer for subsequent tokens. */\n static int\n parse_set_raw_encap_decap(struct context *ctx, const struct token *token,\n@@ -6560,6 +6831,27 @@ static void\n cmd_flow_parsed(const struct buffer *in)\n {\n \tswitch (in->command) {\n+\tcase SHARED_ACTION_CREATE:\n+\t\tport_shared_action_create(\n+\t\t\t\tin->port, in->args.vc.attr.group,\n+\t\t\t\t&((const struct rte_flow_shared_action_conf) {\n+\t\t\t\t\t.ingress = in->args.vc.attr.ingress,\n+\t\t\t\t\t.egress = in->args.vc.attr.egress,\n+\t\t\t\t}),\n+\t\t\t\tin->args.vc.actions);\n+\t\tbreak;\n+\tcase SHARED_ACTION_DESTROY:\n+\t\tport_shared_action_destroy(in->port,\n+\t\t\t\t\t   in->args.sa_destroy.action_id_n,\n+\t\t\t\t\t   in->args.sa_destroy.action_id);\n+\t\tbreak;\n+\tcase SHARED_ACTION_UPDATE:\n+\t\tport_shared_action_update(in->port, in->args.vc.attr.group,\n+\t\t\t\t\t  in->args.vc.actions);\n+\t\tbreak;\n+\tcase SHARED_ACTION_QUERY:\n+\t\tport_shared_action_query(in->port, in->args.sa.action_id);\n+\t\tbreak;\n \tcase VALIDATE:\n \t\tport_flow_validate(in->port, &in->args.vc.attr,\n \t\t\t\t   in->args.vc.pattern, in->args.vc.actions);\ndiff --git a/app/test-pmd/config.c b/app/test-pmd/config.c\nindex d4be6948b2..8eef82c3c4 100644\n--- a/app/test-pmd/config.c\n+++ b/app/test-pmd/config.c\n@@ -1645,6 +1645,222 @@ rss_config_display(struct rte_flow_action_rss *rss_conf)\n \t}\n }\n \n+static struct port_shared_action *\n+action_get_by_id(portid_t port_id, uint32_t id)\n+{\n+\tstruct rte_port *port;\n+\tstruct port_shared_action **ppsa;\n+\tstruct port_shared_action *psa = NULL;\n+\n+\tif (port_id_is_invalid(port_id, ENABLED_WARN) ||\n+\t    port_id == (portid_t)RTE_PORT_ALL)\n+\t\treturn NULL;\n+\tport = &ports[port_id];\n+\tppsa = &port->actions_list;\n+\twhile (*ppsa) {\n+\t\tif ((*ppsa)->id == id) {\n+\t\t\tpsa = *ppsa;\n+\t\t\tbreak;\n+\t\t}\n+\t\tppsa = &(*ppsa)->next;\n+\t}\n+\tif (!psa)\n+\t\tprintf(\"Failed to find shared action #%u on port %u\\n\",\n+\t\t       id, port_id);\n+\treturn psa;\n+}\n+\n+static int\n+action_alloc(portid_t port_id, uint32_t id,\n+\t     struct port_shared_action **action)\n+{\n+\tstruct rte_port *port;\n+\tstruct port_shared_action **ppsa;\n+\tstruct port_shared_action *psa = NULL;\n+\n+\t*action = NULL;\n+\tif (port_id_is_invalid(port_id, ENABLED_WARN) ||\n+\t    port_id == (portid_t)RTE_PORT_ALL)\n+\t\treturn -EINVAL;\n+\tport = &ports[port_id];\n+\tif (id == UINT32_MAX) {\n+\t\t/* taking first available ID */\n+\t\tif (port->actions_list) {\n+\t\t\tif (port->actions_list->id == UINT32_MAX - 1) {\n+\t\t\t\tprintf(\"Highest shared action ID is already\"\n+\t\t\t\t\" assigned, delete it first\\n\");\n+\t\t\t\treturn -ENOMEM;\n+\t\t\t}\n+\t\t\tid = port->actions_list->id + 1;\n+\t\t} else {\n+\t\t\tid = 0;\n+\t\t}\n+\t}\n+\tpsa = calloc(1, sizeof(*psa));\n+\tif (!psa) {\n+\t\tprintf(\"Allocation of port %u shared action failed\\n\",\n+\t\t       port_id);\n+\t\treturn -ENOMEM;\n+\t}\n+\tppsa = &port->actions_list;\n+\twhile (*ppsa && (*ppsa)->id > id)\n+\t\tppsa = &(*ppsa)->next;\n+\tif (*ppsa && (*ppsa)->id == id) {\n+\t\tprintf(\"Shared action #%u is already assigned,\"\n+\t\t\t\" delete it first\\n\", id);\n+\t\tfree(psa);\n+\t\treturn -EINVAL;\n+\t}\n+\tpsa->next = *ppsa;\n+\tpsa->id = id;\n+\t*ppsa = psa;\n+\t*action = psa;\n+\treturn 0;\n+}\n+\n+/** Create shared action */\n+int\n+port_shared_action_create(portid_t port_id, uint32_t id,\n+\t\t\t  const struct rte_flow_shared_action_conf *conf,\n+\t\t\t  const struct rte_flow_action *action)\n+{\n+\tstruct port_shared_action *psa;\n+\tint ret;\n+\tstruct rte_flow_error error;\n+\n+\tret = action_alloc(port_id, id, &psa);\n+\tif (ret)\n+\t\treturn ret;\n+\t/* Poisoning to make sure PMDs update it in case of error. */\n+\tmemset(&error, 0x22, sizeof(error));\n+\tpsa->action = rte_flow_shared_action_create(port_id, conf, action,\n+\t\t\t\t\t\t    &error);\n+\tif (!psa->action) {\n+\t\tuint32_t destroy_id = psa->id;\n+\t\tport_shared_action_destroy(port_id, 1, &destroy_id);\n+\t\treturn port_flow_complain(&error);\n+\t}\n+\tpsa->type = action->type;\n+\tprintf(\"Shared action #%u created\\n\", psa->id);\n+\treturn 0;\n+}\n+\n+/** Destroy shared action */\n+int\n+port_shared_action_destroy(portid_t port_id,\n+\t\t\t   uint32_t n,\n+\t\t\t   const uint32_t *actions)\n+{\n+\tstruct rte_port *port;\n+\tstruct port_shared_action **tmp;\n+\tuint32_t c = 0;\n+\tint ret = 0;\n+\n+\tif (port_id_is_invalid(port_id, ENABLED_WARN) ||\n+\t    port_id == (portid_t)RTE_PORT_ALL)\n+\t\treturn -EINVAL;\n+\tport = &ports[port_id];\n+\ttmp = &port->actions_list;\n+\twhile (*tmp) {\n+\t\tuint32_t i;\n+\n+\t\tfor (i = 0; i != n; ++i) {\n+\t\t\tstruct rte_flow_error error;\n+\t\t\tstruct port_shared_action *psa = *tmp;\n+\n+\t\t\tif (actions[i] != psa->id)\n+\t\t\t\tcontinue;\n+\t\t\t/*\n+\t\t\t * Poisoning to make sure PMDs update it in case\n+\t\t\t * of error.\n+\t\t\t */\n+\t\t\tmemset(&error, 0x33, sizeof(error));\n+\n+\t\t\tif (psa->action && rte_flow_shared_action_destroy(\n+\t\t\t\t\tport_id, psa->action, &error)) {\n+\t\t\t\tret = port_flow_complain(&error);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\t*tmp = psa->next;\n+\t\t\tfree(psa);\n+\t\t\tprintf(\"Shared action #%u destroyed\\n\", psa->id);\n+\t\t\tbreak;\n+\t\t}\n+\t\tif (i == n)\n+\t\t\ttmp = &(*tmp)->next;\n+\t\t++c;\n+\t}\n+\treturn ret;\n+}\n+\n+\n+/** Get shared action by port + id */\n+struct rte_flow_shared_action *\n+port_shared_action_get_by_id(portid_t port_id, uint32_t id)\n+{\n+\n+\tstruct port_shared_action *psa = action_get_by_id(port_id, id);\n+\n+\treturn (psa) ? psa->action : NULL;\n+}\n+\n+/** Update shared action */\n+int\n+port_shared_action_update(portid_t port_id, uint32_t id,\n+\t\t\t  const struct rte_flow_action *action)\n+{\n+\tstruct rte_flow_error error;\n+\tstruct rte_flow_shared_action *shared_action;\n+\n+\tshared_action = port_shared_action_get_by_id(port_id, id);\n+\tif (!shared_action)\n+\t\treturn -EINVAL;\n+\tif (rte_flow_shared_action_update(port_id, shared_action, action,\n+\t\t\t\t\t  &error)) {\n+\t\treturn port_flow_complain(&error);\n+\t}\n+\tprintf(\"Shared action #%u updated\\n\", id);\n+\treturn 0;\n+}\n+\n+int\n+port_shared_action_query(portid_t port_id, uint32_t id)\n+{\n+\tstruct rte_flow_error error;\n+\tstruct port_shared_action *psa;\n+\tuint64_t default_data;\n+\tvoid *data = NULL;\n+\tint ret = 0;\n+\n+\tpsa = action_get_by_id(port_id, id);\n+\tif (!psa)\n+\t\treturn -EINVAL;\n+\tswitch (psa->type) {\n+\tcase RTE_FLOW_ACTION_TYPE_RSS:\n+\t\tdata = &default_data;\n+\t\tbreak;\n+\tdefault:\n+\t\tprintf(\"Shared action %u (type: %d) on port %u doesn't support\"\n+\t\t       \" query\\n\", id, psa->type, port_id);\n+\t\treturn -1;\n+\t}\n+\tif (rte_flow_shared_action_query(port_id, psa->action, data, &error))\n+\t\tret = port_flow_complain(&error);\n+\tswitch (psa->type) {\n+\tcase RTE_FLOW_ACTION_TYPE_RSS:\n+\t\tif (!ret)\n+\t\t\tprintf(\"Shared RSS action:\\n\\trefs:%u\\n\",\n+\t\t\t       *((uint32_t *)data));\n+\t\tdata = NULL;\n+\t\tbreak;\n+\tdefault:\n+\t\tprintf(\"Shared action %u (type: %d) on port %u doesn't support\"\n+\t\t       \" query\\n\", id, psa->type, port_id);\n+\t\tret = -1;\n+\t}\n+\treturn ret;\n+}\n+\n /** Validate flow rule. */\n int\n port_flow_validate(portid_t port_id,\ndiff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h\nindex 9a29d7add0..a4dfd4fc23 100644\n--- a/app/test-pmd/testpmd.h\n+++ b/app/test-pmd/testpmd.h\n@@ -142,6 +142,14 @@ struct port_flow {\n \tuint8_t data[]; /**< Storage for flow rule description */\n };\n \n+/* Descriptor for shared action */\n+struct port_shared_action {\n+\tstruct port_shared_action *next; /**< Next flow in list. */\n+\tuint32_t id; /**< Shared action ID. */\n+\tenum rte_flow_action_type type; /**< Action type. */\n+\tstruct rte_flow_shared_action *action;\t/**< Shared action handle. */\n+};\n+\n /**\n  * The data structure associated with each port.\n  */\n@@ -172,6 +180,8 @@ struct rte_port {\n \tuint32_t                mc_addr_nb; /**< nb. of addr. in mc_addr_pool */\n \tuint8_t                 slave_flag; /**< bonding slave port */\n \tstruct port_flow        *flow_list; /**< Associated flows. */\n+\tstruct port_shared_action *actions_list;\n+\t/**< Associated shared actions. */\n \tconst struct rte_eth_rxtx_callback *rx_dump_cb[RTE_MAX_QUEUES_PER_PORT+1];\n \tconst struct rte_eth_rxtx_callback *tx_dump_cb[RTE_MAX_QUEUES_PER_PORT+1];\n \t/**< metadata value to insert in Tx packets. */\n@@ -749,6 +759,15 @@ void port_reg_bit_field_set(portid_t port_id, uint32_t reg_off,\n \t\t\t    uint8_t bit1_pos, uint8_t bit2_pos, uint32_t value);\n void port_reg_display(portid_t port_id, uint32_t reg_off);\n void port_reg_set(portid_t port_id, uint32_t reg_off, uint32_t value);\n+int port_shared_action_create(portid_t port_id, uint32_t id,\n+\t\t\t      const struct rte_flow_shared_action_conf *conf,\n+\t\t\t      const struct rte_flow_action *action);\n+int port_shared_action_destroy(portid_t port_id,\n+\t\t\t       uint32_t n, const uint32_t *action);\n+struct rte_flow_shared_action *port_shared_action_get_by_id(portid_t port_id,\n+\t\t\t\t\t\t\t    uint32_t id);\n+int port_shared_action_update(portid_t port_id, uint32_t id,\n+\t\t\t      const struct rte_flow_action *action);\n int port_flow_validate(portid_t port_id,\n \t\t       const struct rte_flow_attr *attr,\n \t\t       const struct rte_flow_item *pattern,\n@@ -757,6 +776,7 @@ int port_flow_create(portid_t port_id,\n \t\t     const struct rte_flow_attr *attr,\n \t\t     const struct rte_flow_item *pattern,\n \t\t     const struct rte_flow_action *actions);\n+int port_shared_action_query(portid_t port_id, uint32_t id);\n void update_age_action_context(const struct rte_flow_action *actions,\n \t\t     struct port_flow *pf);\n int port_flow_destroy(portid_t port_id, uint32_t n, const uint32_t *rule);\ndiff --git a/doc/guides/testpmd_app_ug/testpmd_funcs.rst b/doc/guides/testpmd_app_ug/testpmd_funcs.rst\nindex 795c739add..43c0ea0599 100644\n--- a/doc/guides/testpmd_app_ug/testpmd_funcs.rst\n+++ b/doc/guides/testpmd_app_ug/testpmd_funcs.rst\n@@ -4404,6 +4404,11 @@ This section lists supported actions and their attributes, if any.\n \n   - ``dscp_value {unsigned}``: The new DSCP value to be set\n \n+- ``shared``: Use shared action created via\n+  ``flow shared_action {port_id} create``\n+\n+  - ``shared_action_id {unsigned}``: Shared action ID to use\n+\n Destroying flow rules\n ~~~~~~~~~~~~~~~~~~~~~\n \n@@ -4693,6 +4698,113 @@ If attach ``destroy`` parameter, the command will destroy all the list aged flow\n    testpmd> flow aged 0\n    Port 0 total aged flows: 0\n \n+Creating shared actions\n+~~~~~~~~~~~~~~~~~~~~~~~\n+``flow shared_action {port_id} create`` creates shared action with optional\n+shared action ID. It is bound to ``rte_flow_shared_action_create()``::\n+\n+   flow shared_action {port_id} create [action_id {shared_action_id}]\n+      [ingress] [egress] action {action} / end\n+\n+If successful, it will show::\n+\n+   Shared action #[...] created\n+\n+Otherwise, it will complain either that shared action already exists or that\n+some error occurred::\n+\n+   Shared action #[...] is already assigned, delete it first\n+\n+::\n+\n+   Caught error type [...] ([...]): [...]\n+\n+Create shared rss action with id 100 to queues 1 and 2 on port 0::\n+\n+   testpmd> flow shared_action 0 create action_id 100 \\\n+      ingress action rss queues 1 2 end / end\n+\n+Create shared rss action with id assigned by testpmd to queues 1 and 2 on\n+port 0::\n+\n+\ttestpmd> flow shared_action 0 create action_id \\\n+\t\tingress action rss queues 0 1 end / end\n+\n+Updating shared actions\n+~~~~~~~~~~~~~~~~~~~~~~~\n+``flow shared_action {port_id} update`` updates configuration of the shared\n+action from its shared action ID (as returned by\n+``flow shared_action {port_id} create``). It is bound to\n+``rte_flow_shared_action_update()``::\n+\n+   flow shared_action {port_id} update {shared_action_id}\n+      action {action} / end\n+\n+If successful, it will show::\n+\n+   Shared action #[...] updated\n+\n+Otherwise, it will complain either that shared action not found or that some\n+error occurred::\n+\n+   Failed to find shared action #[...] on port [...]\n+\n+::\n+\n+   Caught error type [...] ([...]): [...]\n+\n+Update shared rss action having id 100 on port 0 with rss to queues 0 and 3\n+(in create example above rss queues were 1 and 2)::\n+\n+   testpmd> flow shared_action 0 update 100 action rss queues 0 3 end / end\n+\n+Destroying shared actions\n+~~~~~~~~~~~~~~~~~~~~~~~~~\n+``flow shared_action {port_id} update`` destroys one or more shared actions\n+from their shared action IDs (as returned by\n+``flow shared_action {port_id} create``). It is bound to\n+``rte_flow_shared_action_destroy()``::\n+\n+   flow shared_action {port_id} destroy action_id {shared_action_id} [...]\n+\n+If successful, it will show::\n+\n+   Shared action #[...] destroyed\n+\n+It does not report anything for shared action IDs that do not exist.\n+The usual error message is shown when a shared action cannot be destroyed::\n+\n+   Caught error type [...] ([...]): [...]\n+\n+Destroy shared actions having id 100 & 101::\n+\n+   testpmd> flow shared_action 0 destroy action_id 100 action_id 101\n+\n+Query shared actions\n+~~~~~~~~~~~~~~~~~~~~\n+``flow shared_action {port_id} query`` queries the shared action from its\n+shared action ID (as returned by ``flow shared_action {port_id} create``).\n+It is bound to ``rte_flow_shared_action_query()``::\n+\n+  flow shared_action {port_id} query {shared_action_id}\n+\n+Currently only rss shared action supported. If successful, it will show::\n+\n+   Shared RSS action:\n+      refs:[...]\n+\n+Otherwise, it will complain either that shared action not found or that some\n+error occurred::\n+\n+   Failed to find shared action #[...] on port [...]\n+\n+::\n+\n+   Caught error type [...] ([...]): [...]\n+\n+Query shared action having id 100::\n+\n+   testpmd> flow shared_action 0 query 100\n \n Sample QinQ flow rules\n ~~~~~~~~~~~~~~~~~~~~~~\n",
    "prefixes": [
        "v8",
        "2/2"
    ]
}