get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 132146,
    "url": "http://patches.dpdk.org/api/patches/132146/?format=api",
    "web_url": "http://patches.dpdk.org/project/dpdk/patch/20230928153605.759397-1-getelson@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": "<20230928153605.759397-1-getelson@nvidia.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230928153605.759397-1-getelson@nvidia.com",
    "date": "2023-09-28T15:36:05",
    "name": "[v3] testpmd: add hairpin-map parameter",
    "commit_ref": null,
    "pull_url": null,
    "state": "under-review",
    "archived": false,
    "hash": "9fe7050e7053c8d38fabe510ce7788994da1d013",
    "submitter": {
        "id": 1882,
        "url": "http://patches.dpdk.org/api/people/1882/?format=api",
        "name": "Gregory Etelson",
        "email": "getelson@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/20230928153605.759397-1-getelson@nvidia.com/mbox/",
    "series": [
        {
            "id": 29685,
            "url": "http://patches.dpdk.org/api/series/29685/?format=api",
            "web_url": "http://patches.dpdk.org/project/dpdk/list/?series=29685",
            "date": "2023-09-28T15:36:05",
            "name": "[v3] testpmd: add hairpin-map parameter",
            "version": 3,
            "mbox": "http://patches.dpdk.org/series/29685/mbox/"
        }
    ],
    "comments": "http://patches.dpdk.org/api/patches/132146/comments/",
    "check": "warning",
    "checks": "http://patches.dpdk.org/api/patches/132146/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 016FF42662;\n\tThu, 28 Sep 2023 17:36:47 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id C5AB3402D4;\n\tThu, 28 Sep 2023 17:36:46 +0200 (CEST)",
            "from NAM10-BN7-obe.outbound.protection.outlook.com\n (mail-bn7nam10on2070.outbound.protection.outlook.com [40.107.92.70])\n by mails.dpdk.org (Postfix) with ESMTP id 33C4D4021E\n for <dev@dpdk.org>; Thu, 28 Sep 2023 17:36:45 +0200 (CEST)",
            "from SJ2PR07CA0006.namprd07.prod.outlook.com (2603:10b6:a03:505::18)\n by DS0PR12MB8320.namprd12.prod.outlook.com (2603:10b6:8:f8::14) with\n Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6838.24; Thu, 28 Sep\n 2023 15:36:42 +0000",
            "from CO1PEPF000042A7.namprd03.prod.outlook.com\n (2603:10b6:a03:505:cafe::1) by SJ2PR07CA0006.outlook.office365.com\n (2603:10b6:a03:505::18) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6838.25 via Frontend\n Transport; Thu, 28 Sep 2023 15:36:41 +0000",
            "from mail.nvidia.com (216.228.117.161) by\n CO1PEPF000042A7.mail.protection.outlook.com (10.167.243.36) with Microsoft\n SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n 15.20.6838.14 via Frontend Transport; Thu, 28 Sep 2023 15:36:41 +0000",
            "from rnnvmail202.nvidia.com (10.129.68.7) by mail.nvidia.com\n (10.129.200.67) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.41; Thu, 28 Sep\n 2023 08:36:22 -0700",
            "from nvidia.com (10.126.230.35) by rnnvmail202.nvidia.com\n (10.129.68.7) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.986.41; Thu, 28 Sep\n 2023 08:36:19 -0700"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;\n b=IOp9YVP/eEZtfxjMC0aSEACot4pWZ8tNkKgYR5vqDf/rNtWPCcTPFr/bZpPigslSC1Js0bJoBde97IA+o9JSG2dDZKrw1AdErxXdFSpFv78J88lxGNoLVlqIc7u4jqU+8LReVBZeGiKFrBLDKHb4Vdj2RTgiyK/1Meqh0ufC59A3srBJBolbSgS2zmkWzqQo0Y8e9xsgD+sVtj5vQZdtLaHcMXfJX3EyX6wmfKwKiGHcvVv5wZdF5M+ZToj4cg5VOGarK+hGdJiU748ibJcYzYOXlKHfTX13q0gCoQIQTa9kZ+nMeifucP9qO6yZuNuqGnx+MhsIY7T6f+H2GNZFmQ==",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector9901;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n bh=JrURb4EpQyvZyOQzL+4rmky3ctPWKB+qsDCVRdfkPAY=;\n b=NrB0DjV1g+3ZWLwltht/fuGcjfW/WA6Iw1iHzK7eLLh4oS6YgbJSQc1iYdF+FR+bA2p+qyuVAMc6KNsBTKbAO2cNbTf/sDXbHR3WZowMcjLFJQxAb301iQ00knGCQN5sZXCo+HuJpOm3y8cGzVSNM5wBH+Uej1NnyedWEU0fZOhkX8kIFIsTwv8eohX7DI7DyfQkPlMCtwN13qVYyxzecbeqzbDsSNFXQqdE+u1ZrSVGVyPVXwekEhyRzWv6tT798IXrwIos91QZs5nlbY6JDUyKkuEK3LFaTbdGcQTOfIMEZ30jJeccqwJqoqClBXcJECtXa8dnSujupfPU4/F12g==",
        "ARC-Authentication-Results": "i=1; mx.microsoft.com 1; spf=pass (sender ip is\n 216.228.117.161) smtp.rcpttodomain=dpdk.org smtp.mailfrom=nvidia.com;\n dmarc=pass (p=reject sp=reject pct=100) action=none header.from=nvidia.com;\n dkim=none (message not signed); arc=none",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com;\n s=selector2;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=JrURb4EpQyvZyOQzL+4rmky3ctPWKB+qsDCVRdfkPAY=;\n b=Zo9GmCWYhpifpIZNbjgf8fI3YPD+3Fy866f4mxuUpm8zoU41TLH2OWGM5v6AtZjLF4L9U0ssV47pJCvZ0herj0XR6pJt64l4QRIky2GAkrg6/q2Dondl0K9cnMIaPiXwvijM35oNES5e5geBFFb5G+KdxMI/xY2oB6ZSv6kqxnByETmCsXFvYHBy8wOc1kt1eHcYboFS8zV1DI5YkUHl8998gwp2AikeW/6atWz+exJtd5d9jLqt8OkciLthSbI4CBAUwza3sy85rDsBkbOnld0zX71Oqnu50a2UU9rwKsBNzMJO8YsqGrpd/FQ/GUa3VhD98BAH+k80rk3lKf2i0w==",
        "X-MS-Exchange-Authentication-Results": "spf=pass (sender IP is 216.228.117.161)\n smtp.mailfrom=nvidia.com;\n dkim=none (message not signed)\n header.d=none;dmarc=pass action=none header.from=nvidia.com;",
        "Received-SPF": "Pass (protection.outlook.com: domain of nvidia.com designates\n 216.228.117.161 as permitted sender) receiver=protection.outlook.com;\n client-ip=216.228.117.161; helo=mail.nvidia.com; pr=C",
        "From": "Gregory Etelson <getelson@nvidia.com>",
        "To": "<dev@dpdk.org>",
        "CC": "<getelson@nvidia.com>, =?utf-8?b?wqA=?= <mkashani@nvidia.com>,\n <thomas@monjalon.net>, Aman Singh <aman.deep.singh@intel.com>,\n Yuying Zhang <yuying.zhang@intel.com>",
        "Subject": "[PATCH v3] testpmd: add hairpin-map parameter",
        "Date": "Thu, 28 Sep 2023 18:36:05 +0300",
        "Message-ID": "<20230928153605.759397-1-getelson@nvidia.com>",
        "X-Mailer": "git-send-email 2.39.2",
        "In-Reply-To": "<20230919101006.19936-1-getelson@nvidia.com>",
        "References": "<20230919101006.19936-1-getelson@nvidia.com>",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain",
        "X-Originating-IP": "[10.126.230.35]",
        "X-ClientProxiedBy": "rnnvmail201.nvidia.com (10.129.68.8) To\n rnnvmail202.nvidia.com (10.129.68.7)",
        "X-EOPAttributedMessage": "0",
        "X-MS-PublicTrafficType": "Email",
        "X-MS-TrafficTypeDiagnostic": "CO1PEPF000042A7:EE_|DS0PR12MB8320:EE_",
        "X-MS-Office365-Filtering-Correlation-Id": "2598c9bc-d518-41cc-4ae5-08dbc038b666",
        "X-LD-Processed": "43083d15-7273-40c1-b7db-39efd9ccc17a,ExtAddr",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "BCL:0;",
        "X-Microsoft-Antispam-Message-Info": "\n a1mpsvD3xWBekoJ/kSbXWPLglH44HfR+Ut9h6FFcpd0bCPGWwCxFwbEuYMJ3Qnr91ZBQOp9a6CpgQRTq89pdisPzWiTREbeRfxqf0izxYRAMezZ+jqEVo/AJZI5krqqwYYWma5assiO0LPYYxXbHQReMeSAHbnHFOrBxopiWgEkmFTx6kB2OuCyAb7Tk82+2z05tkUUKLO0lz3vNQg8n1xIeWy/e2x2OOUvDBuUdR/brW9E9gxdw0/7B3r4NXwoza4JWV0ebrevvPz4/YFrYh/Stalhz8L9BYdqJPSxmRTDJ/rVawDwWMtIrqwBscfkkuu2Fh0cPpOT3D2niYIBzZIELTHnXkgdYGPObHoNZjBDyyYBQoXCrtH9gm3nEe2h8f4cvwnfMYafX9ptIsbZMHDOxMiD6OSTPBWT6OZVqVSEuRRb3J+nNwPNH4aqyHk/7HyU2lYjKDXNMeDsQx2WshqIDsm8pYBGZIR/ouGYjHyUzLewTR95yGJlVNdBzD1xNybj6ys7KRnBSxBewNC4d2p91hnEQj+NRNPK+lK45er2SlNfXduw1cIeogBB2C2qm1UGJG4sRLstqH2ASR/zBHgK2tqmCJUqxepMgp0ChnTo5l6SkXDa+s44fGO65uprrfWYPT/NWqeYO6swPRWB6FGL/geJbbx2uVue2qja2lm/PEmp//T0Ca9m1flUqEQ8/KAFlGY1pyxQpNPvAobsCcCw+AYh0BiGNxHrhAIDo+S95U7UL7F5wHZKarQsdhxtQ6to0KO3he6WaMgSWtZktng==",
        "X-Forefront-Antispam-Report": "CIP:216.228.117.161; CTRY:US; LANG:en; SCL:1;\n SRV:;\n IPV:NLI; SFV:NSPM; H:mail.nvidia.com; PTR:dc6edge2.nvidia.com; CAT:NONE;\n SFS:(13230031)(4636009)(396003)(39860400002)(346002)(136003)(376002)(230922051799003)(186009)(1800799009)(451199024)(82310400011)(64100799003)(40470700004)(36840700001)(46966006)(7636003)(55016003)(356005)(82740400003)(40480700001)(7696005)(26005)(6286002)(16526019)(40460700003)(2616005)(1076003)(336012)(426003)(8936002)(8676002)(5660300002)(4326008)(86362001)(30864003)(2906002)(36756003)(316002)(41300700001)(6916009)(70586007)(54906003)(70206006)(6666004)(478600001)(47076005)(36860700001)(83380400001)(309714004);\n DIR:OUT; SFP:1101;",
        "X-OriginatorOrg": "Nvidia.com",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "28 Sep 2023 15:36:41.2509 (UTC)",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 2598c9bc-d518-41cc-4ae5-08dbc038b666",
        "X-MS-Exchange-CrossTenant-Id": "43083d15-7273-40c1-b7db-39efd9ccc17a",
        "X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp": "\n TenantId=43083d15-7273-40c1-b7db-39efd9ccc17a; Ip=[216.228.117.161];\n Helo=[mail.nvidia.com]",
        "X-MS-Exchange-CrossTenant-AuthSource": "\n CO1PEPF000042A7.namprd03.prod.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Anonymous",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "HybridOnPrem",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "DS0PR12MB8320",
        "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": "Hairpin offloads packet forwarding between ports.\nPacket is expected on Rx port <rp>, Rx queue <rq> and is delivered\nto Tx port <tp> Tx queue <tq>.\n\nTestpmd implements a static hairpin configuration scheme.\nThe scheme implicitly matches next valid port for given <rp> or <tp>.\nThat approach can be used in a single or double port setups only.\n\nThe new parameter allows explicit selection of Rx and Tx ports and\nqueues in hairpin configuration.\nThe new `hairpin-map` parameter is provided with 5 parameters,\nseparated by `:`\n\n`--hairpin-map=Rx port id:Rx queue:Tx port id:Tx queue:queues number`\n\nTestpmd operator can provide several `hairpin-map` parameters for\ndifferent hairpin maps.\nExample:\n\ndpdk-testpmd <EAL params> -- \\\n  <testpmd params> \\\n  --rxq=2 --txq=2 --hairpinq=2 --hairpin-mode=0x12 \\\n  --hairpin-map=0:2:1:2:1 \\ # [1]\n  --hairpin-map=0:3:2:2:3   # [2]\n\nHairpin map [1] binds Rx port 0, queue 2 with Tx port 1, queue 2.\nHairpin map [2] binds\n  Rx port 0, queue 3 with Tx port 2, queue 2,\n  Rx port 0, queue 4 with Tx port 2, queue 3,\n  Rx port 0, queue 5 with Tx port 2, queue 4.\n\nThe new `hairpin-map` parameter is optional.\nIf omitted, testpmd will create \"default\" hairpin maps.\n\nSigned-off-by: Gregory Etelson <getelson@nvidia.com>\n---\nv2: Fix Windows Server 2019 compilation failure.\nv3: Update the patch comment.\n    Fix typo.\n---\n app/test-pmd/parameters.c             |  63 ++++++++\n app/test-pmd/testpmd.c                | 212 ++++++++++++++++++--------\n app/test-pmd/testpmd.h                |  18 +++\n doc/guides/testpmd_app_ug/run_app.rst |   3 +\n 4 files changed, 230 insertions(+), 66 deletions(-)",
    "diff": "diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c\nindex a9ca58339d..c6bdfdf06f 100644\n--- a/app/test-pmd/parameters.c\n+++ b/app/test-pmd/parameters.c\n@@ -206,6 +206,12 @@ usage(char* progname)\n \tprintf(\"  --hairpin-mode=0xXX: bitmask set the hairpin port mode.\\n\"\n \t       \"    0x10 - explicit Tx rule, 0x02 - hairpin ports paired\\n\"\n \t       \"    0x01 - hairpin ports loop, 0x00 - hairpin port self\\n\");\n+\tprintf(\"  --hairpin-map=rxpi:rxq:txpi:txq:n: hairpin map.\\n\"\n+\t       \"    rxpi - Rx port index.\\n\"\n+\t       \"    rxq  - Rx queue.\\n\"\n+\t       \"    txpi - Tx port index.\\n\"\n+\t       \"    txq  - Tx queue.\\n\"\n+\t       \"    n    - hairpin queues number.\\n\");\n }\n \n #ifdef RTE_LIB_CMDLINE\n@@ -588,6 +594,55 @@ parse_link_speed(int n)\n \treturn speed;\n }\n \n+static __rte_always_inline\n+char *parse_hairpin_map_entry(char *input, char **next)\n+{\n+\tchar *tail = strchr(input, ':');\n+\n+\tif (!tail)\n+\t\treturn NULL;\n+\ttail[0] = '\\0';\n+\t*next = tail + 1;\n+\treturn input;\n+}\n+\n+static int\n+parse_hairpin_map(const char *hpmap)\n+{\n+\t/*\n+\t * Testpmd hairpin map format:\n+\t * <Rx port id:First Rx queue:Tx port id:First Tx queue:queues number>\n+\t */\n+\tchar *head, *next = (char *)(uintptr_t)hpmap;\n+\tstruct hairpin_map *map = calloc(1, sizeof(*map));\n+\n+\tif (!map)\n+\t\treturn -ENOMEM;\n+\n+\thead = parse_hairpin_map_entry(next, &next);\n+\tif (!head)\n+\t\tgoto err;\n+\tmap->rx_port = atoi(head);\n+\thead = parse_hairpin_map_entry(next, &next);\n+\tif (!head)\n+\t\tgoto err;\n+\tmap->rxq_head = atoi(head);\n+\thead = parse_hairpin_map_entry(next, &next);\n+\tif (!head)\n+\t\tgoto err;\n+\tmap->tx_port = atoi(head);\n+\thead = parse_hairpin_map_entry(next, &next);\n+\tif (!head)\n+\t\tgoto err;\n+\tmap->txq_head = atoi(head);\n+\tmap->qnum = atoi(next);\n+\thairpin_add_multiport_map(map);\n+\treturn 0;\n+err:\n+\tfree(map);\n+\treturn -EINVAL;\n+}\n+\n void\n launch_args_parse(int argc, char** argv)\n {\n@@ -663,6 +718,7 @@ launch_args_parse(int argc, char** argv)\n \t\t{ \"txd\",\t\t\t1, 0, 0 },\n \t\t{ \"hairpinq\",\t\t\t1, 0, 0 },\n \t\t{ \"hairpin-mode\",\t\t1, 0, 0 },\n+\t\t{ \"hairpin-map\",                1, 0, 0 },\n \t\t{ \"burst\",\t\t\t1, 0, 0 },\n \t\t{ \"flowgen-clones\",\t\t1, 0, 0 },\n \t\t{ \"flowgen-flows\",\t\t1, 0, 0 },\n@@ -1111,6 +1167,13 @@ launch_args_parse(int argc, char** argv)\n \t\t\t\telse\n \t\t\t\t\thairpin_mode = (uint32_t)n;\n \t\t\t}\n+\t\t\tif (!strcmp(lgopts[opt_idx].name, \"hairpin-map\")) {\n+\t\t\t\thairpin_multiport_mode = true;\n+\t\t\t\tret = parse_hairpin_map(optarg);\n+\t\t\t\tif (ret)\n+\t\t\t\t\trte_exit(EXIT_FAILURE, \"invalid hairpin map\\n\");\n+\n+\t\t\t}\n \t\t\tif (!strcmp(lgopts[opt_idx].name, \"burst\")) {\n \t\t\t\tn = atoi(optarg);\n \t\t\t\tif (n == 0) {\ndiff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c\nindex 938ca035d4..e01be907f1 100644\n--- a/app/test-pmd/testpmd.c\n+++ b/app/test-pmd/testpmd.c\n@@ -434,6 +434,16 @@ uint8_t clear_ptypes = true;\n /* Hairpin ports configuration mode. */\n uint32_t hairpin_mode;\n \n+bool hairpin_multiport_mode = false;\n+\n+static LIST_HEAD(, hairpin_map) hairpin_map_head = LIST_HEAD_INITIALIZER();\n+\n+void\n+hairpin_add_multiport_map(struct hairpin_map *map)\n+{\n+\tLIST_INSERT_HEAD(&hairpin_map_head, map, entry);\n+}\n+\n /* Pretty printing of ethdev events */\n static const char * const eth_event_desc[] = {\n \t[RTE_ETH_EVENT_UNKNOWN] = \"unknown\",\n@@ -2677,28 +2687,107 @@ port_is_started(portid_t port_id)\n #define HAIRPIN_MODE_TX_LOCKED_MEMORY RTE_BIT32(16)\n #define HAIRPIN_MODE_TX_RTE_MEMORY RTE_BIT32(17)\n \n-\n-/* Configure the Rx and Tx hairpin queues for the selected port. */\n static int\n-setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)\n+port_config_hairpin_rxq(portid_t pi, uint16_t peer_tx_port,\n+\t\t\tqueueid_t rxq_head, queueid_t txq_head,\n+\t\t\tuint16_t qcount, uint32_t manual_bind)\n {\n-\tqueueid_t qi;\n+\tint diag;\n+\tqueueid_t i, qi;\n+\tuint32_t tx_explicit = !!(hairpin_mode & 0x10);\n+\tuint32_t force_mem = !!(hairpin_mode & HAIRPIN_MODE_RX_FORCE_MEMORY);\n+\tuint32_t locked_mem = !!(hairpin_mode & HAIRPIN_MODE_RX_LOCKED_MEMORY);\n+\tuint32_t rte_mem = !!(hairpin_mode & HAIRPIN_MODE_RX_RTE_MEMORY);\n+\tstruct rte_port *port = &ports[pi];\n \tstruct rte_eth_hairpin_conf hairpin_conf = {\n \t\t.peer_count = 1,\n \t};\n-\tint i;\n+\n+\tfor (qi = rxq_head, i = 0; qi < rxq_head + qcount; qi++) {\n+\t\thairpin_conf.peers[0].port = peer_tx_port;\n+\t\thairpin_conf.peers[0].queue = i + txq_head;\n+\t\thairpin_conf.manual_bind = manual_bind;\n+\t\thairpin_conf.tx_explicit = tx_explicit;\n+\t\thairpin_conf.force_memory = force_mem;\n+\t\thairpin_conf.use_locked_device_memory = locked_mem;\n+\t\thairpin_conf.use_rte_memory = rte_mem;\n+\t\tdiag = rte_eth_rx_hairpin_queue_setup\n+\t\t\t(pi, qi, nb_rxd, &hairpin_conf);\n+\t\ti++;\n+\t\tif (diag == 0)\n+\t\t\tcontinue;\n+\n+\t\t/* Fail to setup rx queue, return */\n+\t\tif (port->port_status == RTE_PORT_HANDLING)\n+\t\t\tport->port_status = RTE_PORT_STOPPED;\n+\t\telse\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Port %d can not be set back to stopped\\n\", pi);\n+\t\tfprintf(stderr,\n+\t\t\t\"Port %d failed to configure hairpin on rxq %u.\\n\"\n+\t\t\t\"Peer port: %u peer txq: %u\\n\",\n+\t\t\tpi, qi, peer_tx_port, i);\n+\t\t/* try to reconfigure queues next time */\n+\t\tport->need_reconfig_queues = 1;\n+\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+port_config_hairpin_txq(portid_t pi, uint16_t peer_rx_port,\n+\t\t\tqueueid_t rxq_head, queueid_t txq_head,\n+\t\t\tuint16_t qcount, uint32_t manual_bind)\n+{\n \tint diag;\n+\tqueueid_t i, qi;\n+\tuint32_t tx_explicit = !!(hairpin_mode & 0x10);\n+\tuint32_t force_mem = !!(hairpin_mode & HAIRPIN_MODE_TX_FORCE_MEMORY);\n+\tuint32_t locked_mem = !!(hairpin_mode & HAIRPIN_MODE_TX_LOCKED_MEMORY);\n+\tuint32_t rte_mem = !!(hairpin_mode & HAIRPIN_MODE_TX_RTE_MEMORY);\n \tstruct rte_port *port = &ports[pi];\n+\tstruct rte_eth_hairpin_conf hairpin_conf = {\n+\t\t.peer_count = 1,\n+\t};\n+\n+\tfor (qi = txq_head, i = 0; qi < txq_head + qcount; qi++) {\n+\t\thairpin_conf.peers[0].port = peer_rx_port;\n+\t\thairpin_conf.peers[0].queue = i + rxq_head;\n+\t\thairpin_conf.manual_bind = manual_bind;\n+\t\thairpin_conf.tx_explicit = tx_explicit;\n+\t\thairpin_conf.force_memory = force_mem;\n+\t\thairpin_conf.use_locked_device_memory = locked_mem;\n+\t\thairpin_conf.use_rte_memory = rte_mem;\n+\t\tdiag = rte_eth_tx_hairpin_queue_setup\n+\t\t\t(pi, qi, nb_txd, &hairpin_conf);\n+\t\ti++;\n+\t\tif (diag == 0)\n+\t\t\tcontinue;\n+\n+\t\t/* Fail to setup rx queue, return */\n+\t\tif (port->port_status == RTE_PORT_HANDLING)\n+\t\t\tport->port_status = RTE_PORT_STOPPED;\n+\t\telse\n+\t\t\tfprintf(stderr,\n+\t\t\t\t\"Port %d can not be set back to stopped\\n\", pi);\n+\t\tfprintf(stderr,\n+\t\t\t\"Port %d failed to configure hairpin on txq %u.\\n\"\n+\t\t\t\"Peer port: %u peer rxq: %u\\n\",\n+\t\t\tpi, qi, peer_rx_port, i);\n+\t\t/* try to reconfigure queues next time */\n+\t\tport->need_reconfig_queues = 1;\n+\t\treturn -1;\n+\t}\n+\treturn 0;\n+}\n+\n+static int\n+setup_legacy_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)\n+{\n+\tint diag;\n \tuint16_t peer_rx_port = pi;\n \tuint16_t peer_tx_port = pi;\n \tuint32_t manual = 1;\n-\tuint32_t tx_exp = hairpin_mode & 0x10;\n-\tuint32_t rx_force_memory = hairpin_mode & HAIRPIN_MODE_RX_FORCE_MEMORY;\n-\tuint32_t rx_locked_memory = hairpin_mode & HAIRPIN_MODE_RX_LOCKED_MEMORY;\n-\tuint32_t rx_rte_memory = hairpin_mode & HAIRPIN_MODE_RX_RTE_MEMORY;\n-\tuint32_t tx_force_memory = hairpin_mode & HAIRPIN_MODE_TX_FORCE_MEMORY;\n-\tuint32_t tx_locked_memory = hairpin_mode & HAIRPIN_MODE_TX_LOCKED_MEMORY;\n-\tuint32_t tx_rte_memory = hairpin_mode & HAIRPIN_MODE_TX_RTE_MEMORY;\n \n \tif (!(hairpin_mode & 0xf)) {\n \t\tpeer_rx_port = pi;\n@@ -2706,10 +2795,10 @@ setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)\n \t\tmanual = 0;\n \t} else if (hairpin_mode & 0x1) {\n \t\tpeer_tx_port = rte_eth_find_next_owned_by(pi + 1,\n-\t\t\t\t\t\t       RTE_ETH_DEV_NO_OWNER);\n+\t\t\t\t\t\t\t  RTE_ETH_DEV_NO_OWNER);\n \t\tif (peer_tx_port >= RTE_MAX_ETHPORTS)\n \t\t\tpeer_tx_port = rte_eth_find_next_owned_by(0,\n-\t\t\t\t\t\tRTE_ETH_DEV_NO_OWNER);\n+\t\t\t\t\t\t\t\t  RTE_ETH_DEV_NO_OWNER);\n \t\tif (p_pi != RTE_MAX_ETHPORTS) {\n \t\t\tpeer_rx_port = p_pi;\n \t\t} else {\n@@ -2725,69 +2814,60 @@ setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)\n \t\t\tpeer_rx_port = p_pi;\n \t\t} else {\n \t\t\tpeer_rx_port = rte_eth_find_next_owned_by(pi + 1,\n-\t\t\t\t\t\tRTE_ETH_DEV_NO_OWNER);\n+\t\t\t\t\t\t\t\t  RTE_ETH_DEV_NO_OWNER);\n \t\t\tif (peer_rx_port >= RTE_MAX_ETHPORTS)\n \t\t\t\tpeer_rx_port = pi;\n \t\t}\n \t\tpeer_tx_port = peer_rx_port;\n \t\tmanual = 1;\n \t}\n+\tdiag = port_config_hairpin_txq(pi, peer_rx_port, nb_rxq, nb_txq,\n+\t\t\t\t       nb_hairpinq, manual);\n+\tif (diag)\n+\t\treturn diag;\n+\tdiag = port_config_hairpin_rxq(pi, peer_tx_port, nb_rxq, nb_txq,\n+\t\t\t\t       nb_hairpinq, manual);\n+\tif (diag)\n+\t\treturn diag;\n+\treturn 0;\n+}\n \n-\tfor (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) {\n-\t\thairpin_conf.peers[0].port = peer_rx_port;\n-\t\thairpin_conf.peers[0].queue = i + nb_rxq;\n-\t\thairpin_conf.manual_bind = !!manual;\n-\t\thairpin_conf.tx_explicit = !!tx_exp;\n-\t\thairpin_conf.force_memory = !!tx_force_memory;\n-\t\thairpin_conf.use_locked_device_memory = !!tx_locked_memory;\n-\t\thairpin_conf.use_rte_memory = !!tx_rte_memory;\n-\t\tdiag = rte_eth_tx_hairpin_queue_setup\n-\t\t\t(pi, qi, nb_txd, &hairpin_conf);\n-\t\ti++;\n-\t\tif (diag == 0)\n-\t\t\tcontinue;\n-\n-\t\t/* Fail to setup rx queue, return */\n-\t\tif (port->port_status == RTE_PORT_HANDLING)\n-\t\t\tport->port_status = RTE_PORT_STOPPED;\n-\t\telse\n-\t\t\tfprintf(stderr,\n-\t\t\t\t\"Port %d can not be set back to stopped\\n\", pi);\n-\t\tfprintf(stderr, \"Fail to configure port %d hairpin queues\\n\",\n-\t\t\tpi);\n-\t\t/* try to reconfigure queues next time */\n-\t\tport->need_reconfig_queues = 1;\n-\t\treturn -1;\n-\t}\n-\tfor (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) {\n-\t\thairpin_conf.peers[0].port = peer_tx_port;\n-\t\thairpin_conf.peers[0].queue = i + nb_txq;\n-\t\thairpin_conf.manual_bind = !!manual;\n-\t\thairpin_conf.tx_explicit = !!tx_exp;\n-\t\thairpin_conf.force_memory = !!rx_force_memory;\n-\t\thairpin_conf.use_locked_device_memory = !!rx_locked_memory;\n-\t\thairpin_conf.use_rte_memory = !!rx_rte_memory;\n-\t\tdiag = rte_eth_rx_hairpin_queue_setup\n-\t\t\t(pi, qi, nb_rxd, &hairpin_conf);\n-\t\ti++;\n-\t\tif (diag == 0)\n-\t\t\tcontinue;\n-\n-\t\t/* Fail to setup rx queue, return */\n-\t\tif (port->port_status == RTE_PORT_HANDLING)\n-\t\t\tport->port_status = RTE_PORT_STOPPED;\n-\t\telse\n-\t\t\tfprintf(stderr,\n-\t\t\t\t\"Port %d can not be set back to stopped\\n\", pi);\n-\t\tfprintf(stderr, \"Fail to configure port %d hairpin queues\\n\",\n-\t\t\tpi);\n-\t\t/* try to reconfigure queues next time */\n-\t\tport->need_reconfig_queues = 1;\n-\t\treturn -1;\n+static int\n+setup_mapped_harpin_queues(portid_t pi)\n+{\n+\tint ret = 0;\n+\tstruct hairpin_map *map;\n+\n+\tLIST_FOREACH(map, &hairpin_map_head, entry) {\n+\t\tif (map->rx_port == pi) {\n+\t\t\tret = port_config_hairpin_rxq(pi, map->tx_port,\n+\t\t\t\t\t\t      map->rxq_head,\n+\t\t\t\t\t\t      map->txq_head,\n+\t\t\t\t\t\t      map->qnum, true);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t\tif (map->tx_port == pi) {\n+\t\t\tret = port_config_hairpin_txq(pi, map->rx_port,\n+\t\t\t\t\t\t      map->rxq_head,\n+\t\t\t\t\t\t      map->txq_head,\n+\t\t\t\t\t\t      map->qnum, true);\n+\t\t\tif (ret)\n+\t\t\t\treturn ret;\n+\t\t}\n \t}\n \treturn 0;\n }\n \n+/* Configure the Rx and Tx hairpin queues for the selected port. */\n+static int\n+setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi)\n+{\n+\treturn !hairpin_multiport_mode ?\n+\t       setup_legacy_hairpin_queues(pi, p_pi, cnt_pi) :\n+\t       setup_mapped_harpin_queues(pi);\n+}\n+\n /* Configure the Rx with optional split. */\n int\n rx_queue_setup(uint16_t port_id, uint16_t rx_queue_id,\ndiff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h\nindex f1df6a8faf..208e8e9514 100644\n--- a/app/test-pmd/testpmd.h\n+++ b/app/test-pmd/testpmd.h\n@@ -125,6 +125,24 @@ enum noisy_fwd_mode {\n \tNOISY_FWD_MODE_MAX,\n };\n \n+struct hairpin_map {\n+\tLIST_ENTRY(hairpin_map) entry; /**< List entry. */\n+\tportid_t rx_port; /**< Hairpin Rx port ID. */\n+\tportid_t tx_port; /**< Hairpin Tx port ID. */\n+\tuint16_t rxq_head; /**< Hairpin Rx queue head. */\n+\tuint16_t txq_head; /**< Hairpin Tx queue head. */\n+\tuint16_t qnum; /**< Hairpin queues number. */\n+};\n+\n+/**\n+ * Command line arguments parser sets `hairpin_multiport_mode` to True\n+ * if explicit hairpin map configuration mode was used.\n+ */\n+extern bool hairpin_multiport_mode;\n+\n+/** Hairpin maps list. */\n+extern void hairpin_add_multiport_map(struct hairpin_map *map);\n+\n /**\n  * The data structure associated with RX and TX packet burst statistics\n  * that are recorded for each forwarding stream.\ndiff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/guides/testpmd_app_ug/run_app.rst\nindex 6e9c552e76..a202c98b4c 100644\n--- a/doc/guides/testpmd_app_ug/run_app.rst\n+++ b/doc/guides/testpmd_app_ug/run_app.rst\n@@ -566,6 +566,9 @@ The command line options are:\n \n     The default value is 0. Hairpin will use single port mode and implicit Tx flow mode.\n \n+*   ``--hairpin-map=Rx port id:Rx queue:Tx port id:Tx queue:queues number``\n+\n+    Set explicit hairpin configuration.\n \n Testpmd Multi-Process Command-line Options\n ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n",
    "prefixes": [
        "v3"
    ]
}