get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 131619,
    "url": "https://patches.dpdk.org/api/patches/131619/?format=api",
    "web_url": "https://patches.dpdk.org/project/dpdk/patch/20230919101006.19936-1-getelson@nvidia.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": "<20230919101006.19936-1-getelson@nvidia.com>",
    "list_archive_url": "https://inbox.dpdk.org/dev/20230919101006.19936-1-getelson@nvidia.com",
    "date": "2023-09-19T10:10:06",
    "name": "testpmd: add hairpin-map parameter",
    "commit_ref": null,
    "pull_url": null,
    "state": "superseded",
    "archived": true,
    "hash": "935ad17766b559e6f7da3a199a2d66e4467aa071",
    "submitter": {
        "id": 1882,
        "url": "https://patches.dpdk.org/api/people/1882/?format=api",
        "name": "Gregory Etelson",
        "email": "getelson@nvidia.com"
    },
    "delegate": {
        "id": 319,
        "url": "https://patches.dpdk.org/api/users/319/?format=api",
        "username": "fyigit",
        "first_name": "Ferruh",
        "last_name": "Yigit",
        "email": "ferruh.yigit@amd.com"
    },
    "mbox": "https://patches.dpdk.org/project/dpdk/patch/20230919101006.19936-1-getelson@nvidia.com/mbox/",
    "series": [
        {
            "id": 29548,
            "url": "https://patches.dpdk.org/api/series/29548/?format=api",
            "web_url": "https://patches.dpdk.org/project/dpdk/list/?series=29548",
            "date": "2023-09-19T10:10:06",
            "name": "testpmd: add hairpin-map parameter",
            "version": 1,
            "mbox": "https://patches.dpdk.org/series/29548/mbox/"
        }
    ],
    "comments": "https://patches.dpdk.org/api/patches/131619/comments/",
    "check": "fail",
    "checks": "https://patches.dpdk.org/api/patches/131619/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 6F80342601;\n\tTue, 19 Sep 2023 12:10:49 +0200 (CEST)",
            "from mails.dpdk.org (localhost [127.0.0.1])\n\tby mails.dpdk.org (Postfix) with ESMTP id 01A50406B8;\n\tTue, 19 Sep 2023 12:10:49 +0200 (CEST)",
            "from NAM11-DM6-obe.outbound.protection.outlook.com\n (mail-dm6nam11on2064.outbound.protection.outlook.com [40.107.223.64])\n by mails.dpdk.org (Postfix) with ESMTP id F357E406A2\n for <dev@dpdk.org>; Tue, 19 Sep 2023 12:10:46 +0200 (CEST)",
            "from SJ2PR07CA0012.namprd07.prod.outlook.com (2603:10b6:a03:505::13)\n by CY8PR12MB8412.namprd12.prod.outlook.com (2603:10b6:930:6f::11)\n with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6792.27; Tue, 19 Sep\n 2023 10:10:38 +0000",
            "from CO1PEPF000044F3.namprd05.prod.outlook.com\n (2603:10b6:a03:505:cafe::bc) by SJ2PR07CA0012.outlook.office365.com\n (2603:10b6:a03:505::13) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.6792.28 via Frontend\n Transport; Tue, 19 Sep 2023 10:10:37 +0000",
            "from mail.nvidia.com (216.228.117.161) by\n CO1PEPF000044F3.mail.protection.outlook.com (10.167.241.73) with Microsoft\n SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n 15.20.6792.19 via Frontend Transport; Tue, 19 Sep 2023 10:10:37 +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; Tue, 19 Sep\n 2023 03:10:25 -0700",
            "from nvidia.com (10.126.231.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; Tue, 19 Sep\n 2023 03:10:23 -0700"
        ],
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none;\n b=e7FuzdBao6RwiOa4HdN520S2MiXGt+4335+ZuLgZPjQbVarFS2qhc9lwzn+Mz03inlTO9fW2BSC1ZNkXMH3M+Y1NsyBEcw7/6CSq7szmITDJJwAdREDIwO38KHdWIgCIWVBCh2HD3Dr4buKNnxMC5H2wBp7ekUJcb9iUPqBB+pf2WyWbREEZaWTyxmcJ7dsBb4rJGBUK5RqaINd4R/zY09S5rbUJrq5B2SLqIyCDktx9ysOymqYlxlwVjbm7KScvShG2gqF35ULpX3UFA6WiJj3+wtoKIRkwCS2+Q2/75pM0UnKbDMoavWBNAGVTou4RAcmp+aOcp4S42bYsgEn5Jg==",
        "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=QypT/rE7X1IvLCaUUIPANcJAlunZW69zNDUTfbEDDMo=;\n b=QBE804e8idHJEIlxH7r+jVrytt2feYpNF5BFepf3JVLbaGZ2G+L8DYVLA9latCiRehNWY/Yy7+XNQoasPgQYxVMPn7Dy1X0NV93+yEFiMsfS7akiMLnR/0GMmVVGErkqBG7SzTJcv9YLuVdbl2fZdvnCAS9E9xzsqUiksEWcA6b/gNA3BT+TQTl1qlXtla2RM6JUltfr0vbHdw9Z6tR3R2pp6f7FdonrM6HcyEF//cChRhAuIGbG/o4OwHI+cBy1tuVyVbHON3cfXSHm1c8TZbHwt/KdAdablbkc1Tk3zzENAj9NF9lTvri12WYkNIaDp+QYG6Q2My/q2PpeSEDXfQ==",
        "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=QypT/rE7X1IvLCaUUIPANcJAlunZW69zNDUTfbEDDMo=;\n b=X8X2FX2hOAAngzfFhyFirZzTDBcNQdpflyidkhy7Dt95JE6Wis9UPy75ccfzTPEQ0UlfwvzerAI0tm4jBWipnBDx4GV4yTeTWVEPziy65ZTH3p0bkyLf2CUF7zCgT76t8/qGnwALEJhssMLkNLBeNkixhHmJDgCCcgN1dr9z6lrmiwxkJZWD79OB1pw3l51vwKcHyUQGuMDYy+X81dKMn6SuR+xsxdDrai8eoEmC9zCCgx6Mc25o1vlXYgVReKvAsuYN7rvfXVgA71Ye5HiqBg2SC1FsCbp3Y2DtValu3rKXWGxn48IRakfQl5W7E3yzfJyvIBTuQW6TmNaG6+rEwQ==",
        "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 \"Aman Singh\" <aman.deep.singh@intel.com>,\n Yuying Zhang <yuying.zhang@intel.com>",
        "Subject": "[PATCH] testpmd: add hairpin-map parameter",
        "Date": "Tue, 19 Sep 2023 13:10:06 +0300",
        "Message-ID": "<20230919101006.19936-1-getelson@nvidia.com>",
        "X-Mailer": "git-send-email 2.39.2",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "Content-Type": "text/plain",
        "X-Originating-IP": "[10.126.231.35]",
        "X-ClientProxiedBy": "rnnvmail202.nvidia.com (10.129.68.7) To\n rnnvmail202.nvidia.com (10.129.68.7)",
        "X-EOPAttributedMessage": "0",
        "X-MS-PublicTrafficType": "Email",
        "X-MS-TrafficTypeDiagnostic": "CO1PEPF000044F3:EE_|CY8PR12MB8412:EE_",
        "X-MS-Office365-Filtering-Correlation-Id": "585cfd47-a7fb-4453-002b-08dbb8f8abaf",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "BCL:0;",
        "X-Microsoft-Antispam-Message-Info": "\n zXeQlIKtj8Kl1TVwZL1HdzmEKheJiW2RlmUWnj9an9AHloUyzIw8BH1DLEbM+6YoklSkDXRABeHYA5hJB54qjq8nQwNc/oKBPt9eY2WERSSlxwez+Y02hnZFiJ9hLNlnEnWVUR5XzZZES5qyU6gpI7hvLZ2aYHakAe7satvYfD0LBBPNVcwCrGLV9HYZlq7UsIjUVmWYax16qcfJJxy7mrg5VIJEpNaTNaV5kg5IIhVnFVf8xEE5876x5jWzUGKiDs24/FRxcziNDUfFBuUkrCLNVeoxk1gAUYfHlT/vvrJ2UkEqHYODA+cuHRPJv87JSV6JvqtJEtiHSd1PdK0AGDAmbnljVF+/6prUbfxBzJMn5+B5W1BaUhA5qJxG+cuAAhwbVHwFcJE4FDOkzPehTItOTOWfk34e6Bun+3r3Q5zFZTpSj2Cb1XoTNfrJZmJVfQTSdxMIYnZmEeGKqJn5B4BM+HSWMgnwLxfWkLSo9F6kDXxKzhb1hr6tac3Eb9owq1wba+rNbnsl2EVW74Z3urwsTbj9J23zIR8vQJ0bBwmnYzMTwzC543oCTiZyYRb2/dhzgwF+8thyOqB3MVqiva+V7TYvWh4GbjHxpaDHXijNrexGlp3OUgVVSv/uw9Pn60bnSlcb8z7iRT8eXmmeB+F+jmvOLvaY4HmLdgqzn052YlHbtvDlWZAgL9MM/oZbZm2SkzaETbCE+YbXHALNjnfKcQvWdF/F86nM5crlX44CYzQAB9Ltpx89InrYOfPozGlgMhCVv9B9fYRDs2oO2Q==",
        "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)(376002)(39860400002)(136003)(346002)(451199024)(186009)(82310400011)(1800799009)(46966006)(40470700004)(36840700001)(30864003)(478600001)(41300700001)(55016003)(83380400001)(5660300002)(40480700001)(6666004)(2906002)(70206006)(316002)(70586007)(6916009)(54906003)(8676002)(4326008)(8936002)(7696005)(1076003)(40460700003)(26005)(2616005)(16526019)(6286002)(426003)(336012)(36860700001)(47076005)(36756003)(7636003)(356005)(86362001)(82740400003)(309714004);\n DIR:OUT; SFP:1101;",
        "X-OriginatorOrg": "Nvidia.com",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "19 Sep 2023 10:10:37.3892 (UTC)",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 585cfd47-a7fb-4453-002b-08dbb8f8abaf",
        "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 CO1PEPF000044F3.namprd05.prod.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Anonymous",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "HybridOnPrem",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "CY8PR12MB8412",
        "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": "Testpmd hairpin implementation always sets the next valid port to\ncomplete hairpin binding. That limits hairpin configuration options.\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---\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..675de81861 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(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 = 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..2ba1727c51 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_queus(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_queus(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": []
}