From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mails.dpdk.org (mails.dpdk.org [217.70.189.124]) by inbox.dpdk.org (Postfix) with ESMTP id 88C7C4599D; Fri, 20 Sep 2024 15:48:58 +0200 (CEST) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 513B94067E; Fri, 20 Sep 2024 15:48:58 +0200 (CEST) Received: from mgamail.intel.com (mgamail.intel.com [192.198.163.15]) by mails.dpdk.org (Postfix) with ESMTP id 9D99D40669 for ; Fri, 20 Sep 2024 15:48:55 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=intel.com; i=@intel.com; q=dns/txt; s=Intel; t=1726840136; x=1758376136; h=from:to:cc:subject:date:message-id:references: in-reply-to:content-transfer-encoding:mime-version; bh=Z02gH8GZbBD+qswm0+T0Vm1JlcEYR51IpCfor/NvgL0=; b=KtJj9YXJqJLiGwLo74rK9erh+U6k5Zxp2DtCEF01E5L3vcSN5TqbzXAB pugyFlgRh7tF3DV2cUTOslrsB23ErHdpcwzN2AsABBcOM6vBz6nUL8K9D nWw1j+hbMPmUi/q0fEBt3TzWCxipZ2099IcpIKtvY67xjYPbeZdZJWv2L bkIhRpDiKXagXENL8wbNX6gmA57YzupPBWC/c8JJ0LcxC1DwrAoAVJztf wVLSBd5yxBzQ59AB4Psodl36PDlVinKSzxMr4AWSPUukt3bb9HHA5NpjI G7EVklqNocPbbsUxHUGDk/NIXvEgyRd48UYWAq7Ue6zc3LsbWcRF8ngyP Q==; X-CSE-ConnectionGUID: ZGFWV/07RBuh3msSyp1ugg== X-CSE-MsgGUID: Vs27g72YQiGWHtt/GvJlug== X-IronPort-AV: E=McAfee;i="6700,10204,11200"; a="25993673" X-IronPort-AV: E=Sophos;i="6.10,244,1719903600"; d="scan'208";a="25993673" Received: from orviesa001.jf.intel.com ([10.64.159.141]) by fmvoesa109.fm.intel.com with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 20 Sep 2024 06:48:55 -0700 X-CSE-ConnectionGUID: rkF++2XtRnq3KjuXcJ95eg== X-CSE-MsgGUID: jMlxnBcXQaqH/sfw0JC0rw== X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="6.10,244,1719903600"; d="scan'208";a="107760258" Received: from orsmsx602.amr.corp.intel.com ([10.22.229.15]) by orviesa001.jf.intel.com with ESMTP/TLS/AES256-GCM-SHA384; 20 Sep 2024 06:48:55 -0700 Received: from orsmsx610.amr.corp.intel.com (10.22.229.23) by ORSMSX602.amr.corp.intel.com (10.22.229.15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39; Fri, 20 Sep 2024 06:48:54 -0700 Received: from ORSEDG601.ED.cps.intel.com (10.7.248.6) by orsmsx610.amr.corp.intel.com (10.22.229.23) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2507.39 via Frontend Transport; Fri, 20 Sep 2024 06:48:54 -0700 Received: from NAM12-MW2-obe.outbound.protection.outlook.com (104.47.66.41) by edgegateway.intel.com (134.134.137.102) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.1.2507.39; Fri, 20 Sep 2024 06:48:53 -0700 ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=ZGVV70HBEWBolkdv7aaXwtyXy//5T7Crn77BHrm+p/f9/lp/nvYTnYn37S8lQO75QzMu23yu9PRZ9MoYo+aoaDPnmpvFJZ2yFLiLBmmYSGc6k0B5rNVdeZPCgFK5A9lmmJiOr9McrJNbFdHtPDj2QECHcJsLGapIZI4M8Ysm33qnT0w0Zw1Hue79l/oX5OMFT73owl/c6APUzibliHH6gIc/U6svqm1ToJyCo0j5gusLAM3cB9Kwd3Owg2cngabkRrRFcAwxxqqoGJnk/5bYy1rJVqHRMUwNRm/CGRuU+0u+dN3W0phuQaYqNO9dhzfJAwiIX6O9Ob+0DVYlML9N5A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector10001; 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; bh=s3STmH39ZXoocjB69uqhG4yR2u4asnPsK0d2+NknACI=; b=AznSyGYZRvZELXAcFqEOv5YOPCHjrWikN/an7FUIaNHHEWQtqz3D8vIqnAuJrDYkkZ4O6gPbywjYf2Z9vM7s9mdkouj3D7KWPkCr3xt+7fn2QL+fx1VgMtYipWptNyRIyok9O7ZIsVtVfOCfbNIPeuPUsBHkKOHcBxtj8UlB4eSg0GDv84S5U3oyf7TFO7E0eIKosp5pnI92i5m2L5xkQDgWyqZUVckJtCqyUQvAg36F6NUElLRd7x3xzBnhYHfOdQZyO4KrzxsfS38qVGKlmKTKekSGXMFbnh7UXdz8Ddm8HdyWNu4J+68CZtdPsvaePSRP/lnHyxmzDS/TCPhI7Q== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=intel.com; dmarc=pass action=none header.from=intel.com; dkim=pass header.d=intel.com; arc=none Received: from IA1PR11MB6074.namprd11.prod.outlook.com (2603:10b6:208:3d6::14) by DM6PR11MB4594.namprd11.prod.outlook.com (2603:10b6:5:2a0::12) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.7982.23; Fri, 20 Sep 2024 13:48:51 +0000 Received: from IA1PR11MB6074.namprd11.prod.outlook.com ([fe80::37f8:8ecb:2c75:3092]) by IA1PR11MB6074.namprd11.prod.outlook.com ([fe80::37f8:8ecb:2c75:3092%4]) with mapi id 15.20.7982.018; Fri, 20 Sep 2024 13:48:51 +0000 From: "Singh, Aman Deep" To: Gregory Etelson , "dev@dpdk.org" CC: "thomas@monjalon.net" , "yuying.zhang@intel.com" , Dariusz Sosnowski Subject: RE: [PATCH v4 1/1] testpmd: add hairpin-map parameter Thread-Topic: [PATCH v4 1/1] testpmd: add hairpin-map parameter Thread-Index: AQHbBAjszXBOFXp0b0W00BOhezW93rJgu/Bw Date: Fri, 20 Sep 2024 13:48:51 +0000 Message-ID: References: <20230928153605.759397-1-getelson@nvidia.com> <20240911050833.202698-1-getelson@nvidia.com> In-Reply-To: <20240911050833.202698-1-getelson@nvidia.com> Accept-Language: en-US Content-Language: en-US X-MS-Has-Attach: X-MS-TNEF-Correlator: authentication-results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=intel.com; x-ms-publictraffictype: Email x-ms-traffictypediagnostic: IA1PR11MB6074:EE_|DM6PR11MB4594:EE_ x-ms-office365-filtering-correlation-id: 31515c92-4611-4387-cb8d-08dcd97af5ed x-ms-exchange-senderadcheck: 1 x-ms-exchange-antispam-relay: 0 x-microsoft-antispam: BCL:0; ARA:13230040|376014|366016|1800799024|38070700018; x-microsoft-antispam-message-info: =?us-ascii?Q?j7lrr0j9YGMNnB6iSV4ohV3vaqG+Qs44WPntVEAyn3CbYEDdKYJ+ySbH+jKG?= =?us-ascii?Q?DdyXa0U+8wDT90JM/r/wJ8ff7CNWZ0gPCM5jRT3zQbEPr61IJnU+bKtpL0AW?= =?us-ascii?Q?6uXVkzZl6CkpOChGk0ehpDNyU+nv7+C70KYHLpY8gGYRs+MwZeumtQrVl/cD?= =?us-ascii?Q?1r9j6Tr012qCOMSn7uvwYjKH+tqUzZnZYpU9ko2xCMDFIHr9L/9ccH99UWCp?= =?us-ascii?Q?pLCo2XVA5PQF5gou1VSDorP5IGDMcVRhpzzB6Hv8OuBpQsszUWASLZcGErLy?= =?us-ascii?Q?ys3jYlc8dnJCzTgW9Nry0hVF0RSL7dB3POzZ8odXSC3nJMcdwP5vf6xQXGVR?= =?us-ascii?Q?8nXNjtFgLsnumdlHhubZp2wZTbOyWCYvkxCPbUjkIH7nkkygnFjoCJ9ZtGHg?= =?us-ascii?Q?Sujnn69ZfvnAiz9Zhp4M4iDR4vU45S5k2rg1kHwXoVFz6s3O91kBd1UkLW+r?= =?us-ascii?Q?wat0Ivr7IGpIxm2e135uEdjXrgfqAN3cVknfjQu1FArvVw1o9+v9S8aR3cIR?= =?us-ascii?Q?5hxigaaf+A9gUPjh+L+eduxg+gc+VMlG8lyrZF05nnd7vtzrp25ZyQrpyPRq?= =?us-ascii?Q?VpUofjsrI/vJdsA56HDTEHIfJQMPNSBbmOe9uds67W6qxadgEwKW/Q+S9Z6U?= =?us-ascii?Q?xzbl9iBw6zRR2n2X4goQoEu0QJaoG1ncMM3TY3cBNqgYaAwleoVj2+gpHI0u?= =?us-ascii?Q?iYSMhUKJKf2ynZ0ahuwzy3blsVNhw8wHvn66NaT2UaPMnp3LAKMXh4CP2Muw?= =?us-ascii?Q?qZVUc7gsHomyQ8NAdMcX0BGnexUa0Xnh9BugoxJSUw3QPgiCPv85aM9xfTO+?= =?us-ascii?Q?CZnzQTN1o0scobnaRJIgClB+Tci2F2KWxJrjQF4YN/1mqIyvXLWOGqNTEZ1f?= =?us-ascii?Q?/XoyX2h3lQ46Q/rmYvybf6x9uI1Nd3KlDCpjcIgq0zHImNcy7Df4aw5Eus5R?= =?us-ascii?Q?6TdB57mlakHO22MoEzh6sniHjulHZ9lmmfipxuxXA8hEdLLbjPaHABjIXvND?= =?us-ascii?Q?rGu2RbOw+4gkzuJrvJwhC0Swxj+HHCkQlJfF57FqQa9UIJvQ8Sr3MgDCkh5J?= =?us-ascii?Q?5falojXjLVp8n5dLc2dsNwSxKyJ5jW/Vg2oZHx1oJj0zZAuTmW2I8adaXRoe?= =?us-ascii?Q?hSvxTCRwb0zbb3deSNvIIR4VvYAdbaM+WNg04tN+GyaknCadnUh/SBugXluD?= =?us-ascii?Q?pJcfy8bw84w5hj8vQOQOnKyFsg9UylrUUGpEejHGeRqE0dcYEavGFxzUZZ3Z?= =?us-ascii?Q?0T3LYMdiLfs4AsDrxCIHF8BJzojU2AVhYWcCoSf5HTIaqFg3P009zbom0ncC?= =?us-ascii?Q?mUdG32yJAauJvNl4LQIC3VxI4fJs8LtL9eLGnlrp+Pd5zw=3D=3D?= x-forefront-antispam-report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:IA1PR11MB6074.namprd11.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230040)(376014)(366016)(1800799024)(38070700018); DIR:OUT; SFP:1101; x-ms-exchange-antispam-messagedata-chunkcount: 1 x-ms-exchange-antispam-messagedata-0: =?us-ascii?Q?wcEu0a8yhV59FF8ujUtl+N7OlQVB8RhpUXnmtLxMk4Yms4mi9DkoHFIy9MNx?= =?us-ascii?Q?N9jOanaatOOAodqlIYXChg4/94p3iFOHqkFWrH2BBUHKCobrvcI1tIiorrUm?= =?us-ascii?Q?N3lNmBfr7Q1T+S2tUzxQoRgvUi+EKpf+Bq6sEukBGxPWN2DRd+uB+nV7cqgB?= =?us-ascii?Q?mfSNNwSGTPF0OvGFrqY1P4KOpRqQmj2j6QI1M3bfPrWVKC8fWmkuwlCpUlPE?= =?us-ascii?Q?Fx/c6W6mjfh43P2y8/fG9sD3Qmjpcnyx9EB9fJljyT+nTheBp3GE3D/17PBp?= =?us-ascii?Q?BrWmL8irkn8gy3HjoN/qkjOq4+6QQeeicd6/cOdhzEGprI5MV1Ejh3Zx2BoN?= =?us-ascii?Q?2qc32PIWHlLt50w1T5CHrU6wXHu8gkuGVvuMe3dcnvmFjJ3yD90BgW+BpBz7?= =?us-ascii?Q?HPCwyIqcGQQj26681laH+HWFykmofJDvNOrsdFAtynIDGmWfmyTw8PrQSORv?= =?us-ascii?Q?6PINeZ6luZ0g719QwOE93aAU+uJcrNbDm9+166rCtThO7Dl8fzLU/DbpFG6L?= =?us-ascii?Q?IxKirgvu4zuB3tjs9/YtRdSkDPF/R/3rPp9kk53+hwk4bdyvZnFs6BXfvDVB?= =?us-ascii?Q?TognX1hszn+p8rtfJ2w71j7+/mfo1owB7JXB/D1Hdf2T/TlMzF/f5s4qq59C?= =?us-ascii?Q?bM8QaXEEwiJVRShcUBQGdSwbvfXKJUSxd4kr4Y3NfU+WI3muNO9Q5fRM74Bg?= =?us-ascii?Q?LitW3uwSi6tZOkLCp5i+Tpjb7q9KYUSuXpCajJBY5TEJY5h0t1gOIg960lx0?= =?us-ascii?Q?3YHBA9tYRRqKfRgpHxu/5AhEWhtUT3R6Qstoh2P6qczTJgKw5Ve1SE7prQ9V?= =?us-ascii?Q?7E3BTRBLWDHBu+Jtwsef9AeQCHpLF7++erwR3mIbyOFSlADy/y72bxufPjF5?= =?us-ascii?Q?BsVDO+g0HIglmOoPoikoOa7eMODCqkLeFsIhyYwPU3AMQS6b0n+mRXhuCOTi?= =?us-ascii?Q?uKgbiAXooVtQ8ptFX1d9GEx5HCZqpbErypuIx4c/PiB7NDqq3SlRqmbMzaM2?= =?us-ascii?Q?fo8+ZEYoMLvHBJWhse3xMW0FQ9feHcaxOOXwWHEkrTr3awl9Bf0CQwG2nTyt?= =?us-ascii?Q?653ss/DBWxzj+n64sPSWqDnJZP0H0MdJ2wSNRZAKhXw7UJXwkZ8Gp3IeBS02?= =?us-ascii?Q?Yz+NqUd4VzBAKzVCOsNm1kXwVyNiqLHniGcJV+toZ1E0U55TuQIyBLSl3Q8O?= =?us-ascii?Q?ooqebEEC9/4KYK2iHRYI3W6dWzmE+55Ov2QGPHVtjvEV2AJPyaCeISfmI6My?= =?us-ascii?Q?uSfk8/wzLMZzHLXALE+UY3YC/9qnz4Wd0P5581tkoa9qNPJrCn5hlmVg8RYP?= =?us-ascii?Q?1IiO3e7iLSpzF9NA8WkQPM22e95fOTClfCF3biSArgzm0/wcz8+ufgNOk4Pb?= =?us-ascii?Q?sVoOIUHkfkKhGNls1OYe/0Vj2G8gVEF6P1WEr7Rn+V2rkevpuIWRqnvQOR2f?= =?us-ascii?Q?tIRLpfNbe/gH31JICwh22QNl6Tk/imSZuG7uncpPUNqEEtI5H5OMZXS36C9d?= =?us-ascii?Q?+NRoqRuQja7zbYu6pXFgUA1WR+xMXYdQsHsZQIHAoZmWAl+bU5y4a0k02vbz?= =?us-ascii?Q?ULr5F82HSbu0NLXxCxfiKe2m1hjVfhb5vdL5/t58h6Y1AYwYYmnS8FD0dv+9?= =?us-ascii?Q?lw=3D=3D?= Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-AuthSource: IA1PR11MB6074.namprd11.prod.outlook.com X-MS-Exchange-CrossTenant-Network-Message-Id: 31515c92-4611-4387-cb8d-08dcd97af5ed X-MS-Exchange-CrossTenant-originalarrivaltime: 20 Sep 2024 13:48:51.4951 (UTC) X-MS-Exchange-CrossTenant-fromentityheader: Hosted X-MS-Exchange-CrossTenant-id: 46c98d88-e344-4ed4-8496-4ed7712e255d X-MS-Exchange-CrossTenant-mailboxtype: HOSTED X-MS-Exchange-CrossTenant-userprincipalname: CXLABVoguDfbSSp46JoozOXV2b6kpNLNmNLgLDAKabq5AIU2aXhMiTFM1X01ziN8xfWbHOGb+tA6Ez1Tp5oED7lCK9t0B/ai8e6x2/ZCB10= X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM6PR11MB4594 X-OriginatorOrg: intel.com X-BeenThere: dev@dpdk.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: DPDK patches and discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: dev-bounces@dpdk.org Hi Gregory, Can you please resolve the compilation issues seen with this patch. Hairpin offloads packet forwarding between ports. Packet is expected on Rx port , Rx queue and is forwarded to Tx po= rt Tx queue . Testpmd implements a static hairpin configuration scheme. The new parameter allows explicit selection of Rx and Tx ports and queues i= n hairpin configuration. The new `hairpin-map` parameter is provided with 5 parameters, separated by= `:` `--hairpin-map=3DRx port id:Rx queue:Tx port id:Tx queue:queues number` Can we rephrase the parameter "queues number " to something like "total que= ue pairs" Testpmd operator can provide several `hairpin-map` parameters for different= hairpin maps. Example: dpdk-testpmd -- \ \ --rxq=3D2 --txq=3D2 --hairpinq=3D2 --hairpin-mode=3D0x12 \ --hairpin-map=3D0:2:1:2:1 \ # [1] --hairpin-map=3D0:3:2:2:3 # [2] Hairpin map [1] binds Rx port 0, queue 2 with Tx port 1, queue 2. Hairpin map [2] binds Rx port 0, queue 3 with Tx port 2, queue 2, Rx port 0, queue 4 with Tx port 2, queue 3, Rx port 0, queue 5 with Tx port 2, queue 4. The new `hairpin-map` parameter is optional. If omitted, testpmd will create "default" hairpin maps. Signed-off-by: Gregory Etelson Acked-by: Dariusz Sosnowski --- app/test-pmd/hairpin.c | 385 ++++++++++++++++++++++++++ app/test-pmd/meson.build | 1 + app/test-pmd/parameters.c | 10 + app/test-pmd/testpmd.c | 217 +-------------- app/test-pmd/testpmd.h | 14 + doc/guides/testpmd_app_ug/run_app.rst | 3 + 6 files changed, 416 insertions(+), 214 deletions(-) create mode 100644 a= pp/test-pmd/hairpin.c diff --git a/app/test-pmd/hairpin.c b/app/test-pmd/hairpin.c new file mode = 100644 index 0000000000..de54e8bac8 --- /dev/null +++ b/app/test-pmd/hairpin.c @@ -0,0 +1,385 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2022 NVIDIA Corporation & Affiliates */ + +#include +#include +#include +#include + +#include + +#include "testpmd.h" + +/* Hairpin ports configuration mode. */ uint32_t hairpin_mode; + +bool hairpin_multiport_mode =3D false; + +queueid_t nb_hairpinq; /**< Number of hairpin queues per port. */ + +static LIST_HEAD(, hairpin_map) hairpin_map_head =3D=20 +LIST_HEAD_INITIALIZER(); + +struct hairpin_map { + LIST_ENTRY(hairpin_map) entry; /**< List entry. */ + portid_t rx_port; /**< Hairpin Rx port ID. */ + portid_t tx_port; /**< Hairpin Tx port ID. */ + uint16_t rxq_head; /**< Hairpin Rx queue head. */ + uint16_t txq_head; /**< Hairpin Tx queue head. */ + uint16_t qnum; /**< Hairpin queues number. */ }; + +void +hairpin_add_multiport_map(struct hairpin_map *map) { + LIST_INSERT_HEAD(&hairpin_map_head, map, entry); } + +/* + * Get the allowed maximum number of hairpin queues. + * *pid return the port id which has minimal value of + * max_hairpin_queues in all ports. + */ +queueid_t +get_allowed_max_nb_hairpinq(portid_t *pid) { + queueid_t allowed_max_hairpinq =3D RTE_MAX_QUEUES_PER_PORT; + portid_t pi; + struct rte_eth_hairpin_cap cap; + + RTE_ETH_FOREACH_DEV(pi) { + if (rte_eth_dev_hairpin_capability_get(pi, &cap) !=3D 0) { + *pid =3D pi; + return 0; + } + if (cap.max_nb_queues < allowed_max_hairpinq) { + allowed_max_hairpinq =3D cap.max_nb_queues; + *pid =3D pi; + } + } + return allowed_max_hairpinq; +} + +/* + * Check input hairpin is valid or not. + * If input hairpin is not greater than any of maximum number + * of hairpin queues of all ports, it is valid. + * if valid, return 0, else return -1 + */ +int +check_nb_hairpinq(queueid_t hairpinq) +{ + queueid_t allowed_max_hairpinq; + portid_t pid =3D 0; + + allowed_max_hairpinq =3D get_allowed_max_nb_hairpinq(&pid); + if (hairpinq > allowed_max_hairpinq) { + fprintf(stderr, + "Fail: input hairpin (%u) can't be greater than max_hairpin_queues (%u)= of port %u\n", + hairpinq, allowed_max_hairpinq, pid); + return -1; + } + return 0; +} + +#define HAIRPIN_MODE_RX_FORCE_MEMORY RTE_BIT32(8) #define=20 +HAIRPIN_MODE_TX_FORCE_MEMORY RTE_BIT32(9) + +#define HAIRPIN_MODE_RX_LOCKED_MEMORY RTE_BIT32(12) #define=20 +HAIRPIN_MODE_RX_RTE_MEMORY RTE_BIT32(13) + +#define HAIRPIN_MODE_TX_LOCKED_MEMORY RTE_BIT32(16) #define=20 +HAIRPIN_MODE_TX_RTE_MEMORY RTE_BIT32(17) + +static int +port_config_hairpin_rxq(portid_t pi, uint16_t peer_tx_port, + queueid_t rxq_head, queueid_t txq_head, + uint16_t qcount, uint32_t manual_bind) { + int diag; + queueid_t i, qi; + uint32_t tx_explicit =3D !!(hairpin_mode & 0x10); + uint32_t force_mem =3D !!(hairpin_mode & HAIRPIN_MODE_RX_FORCE_MEMORY); + uint32_t locked_mem =3D !!(hairpin_mode & HAIRPIN_MODE_RX_LOCKED_MEMORY); + uint32_t rte_mem =3D !!(hairpin_mode & HAIRPIN_MODE_RX_RTE_MEMORY); + struct rte_port *port =3D &ports[pi]; + struct rte_eth_hairpin_conf hairpin_conf =3D { + .peer_count =3D 1, + }; + + for (qi =3D rxq_head, i =3D 0; qi < rxq_head + qcount; qi++) { + hairpin_conf.peers[0].port =3D peer_tx_port; + hairpin_conf.peers[0].queue =3D i + txq_head; + hairpin_conf.manual_bind =3D manual_bind; + hairpin_conf.tx_explicit =3D tx_explicit; + hairpin_conf.force_memory =3D force_mem; + hairpin_conf.use_locked_device_memory =3D locked_mem; + hairpin_conf.use_rte_memory =3D rte_mem; + diag =3D rte_eth_rx_hairpin_queue_setup + (pi, qi, nb_rxd, &hairpin_conf); + i++; + if (diag =3D=3D 0) + continue; + + /* Fail to setup rx queue, return */ + if (port->port_status =3D=3D RTE_PORT_HANDLING) + port->port_status =3D RTE_PORT_STOPPED; + else + fprintf(stderr, + "Port %d can not be set back to stopped\n", pi); + fprintf(stderr, + "Port %d failed to configure hairpin on rxq %u.\n" + "Peer port: %u peer txq: %u\n", + pi, qi, peer_tx_port, i); + /* try to reconfigure queues next time */ + port->need_reconfig_queues =3D 1; + return -1; + } + return 0; +} + +static int +port_config_hairpin_txq(portid_t pi, uint16_t peer_rx_port, + queueid_t rxq_head, queueid_t txq_head, + uint16_t qcount, uint32_t manual_bind) { + int diag; + queueid_t i, qi; + uint32_t tx_explicit =3D !!(hairpin_mode & 0x10); + uint32_t force_mem =3D !!(hairpin_mode & HAIRPIN_MODE_TX_FORCE_MEMORY); + uint32_t locked_mem =3D !!(hairpin_mode & HAIRPIN_MODE_TX_LOCKED_MEMORY); + uint32_t rte_mem =3D !!(hairpin_mode & HAIRPIN_MODE_TX_RTE_MEMORY); + struct rte_port *port =3D &ports[pi]; + struct rte_eth_hairpin_conf hairpin_conf =3D { + .peer_count =3D 1, + }; + + for (qi =3D txq_head, i =3D 0; qi < txq_head + qcount; qi++) { + hairpin_conf.peers[0].port =3D peer_rx_port; + hairpin_conf.peers[0].queue =3D i + rxq_head; + hairpin_conf.manual_bind =3D manual_bind; + hairpin_conf.tx_explicit =3D tx_explicit; + hairpin_conf.force_memory =3D force_mem; + hairpin_conf.use_locked_device_memory =3D locked_mem; + hairpin_conf.use_rte_memory =3D rte_mem; + diag =3D rte_eth_tx_hairpin_queue_setup + (pi, qi, nb_txd, &hairpin_conf); + i++; + if (diag =3D=3D 0) + continue; + + /* Fail to setup rx queue, return */ + if (port->port_status =3D=3D RTE_PORT_HANDLING) + port->port_status =3D RTE_PORT_STOPPED; + else + fprintf(stderr, + "Port %d can not be set back to stopped\n", pi); + fprintf(stderr, + "Port %d failed to configure hairpin on txq %u.\n" + "Peer port: %u peer rxq: %u\n", + pi, qi, peer_rx_port, i); + /* try to reconfigure queues next time */ + port->need_reconfig_queues =3D 1; + return -1; + } + return 0; +} + +static int +setup_legacy_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t=20 +cnt_pi) { + int diag; + uint16_t peer_rx_port =3D pi; + uint16_t peer_tx_port =3D pi; + uint32_t manual =3D 1; + + if (!(hairpin_mode & 0xf)) { + peer_rx_port =3D pi; + peer_tx_port =3D pi; + manual =3D 0; + } else if (hairpin_mode & 0x1) { + peer_tx_port =3D rte_eth_find_next_owned_by(pi + 1, + RTE_ETH_DEV_NO_OWNER); + if (peer_tx_port >=3D RTE_MAX_ETHPORTS) + peer_tx_port =3D rte_eth_find_next_owned_by(0, + RTE_ETH_DEV_NO_OWNER); + if (p_pi !=3D RTE_MAX_ETHPORTS) { + peer_rx_port =3D p_pi; + } else { + uint16_t next_pi; + + /* Last port will be the peer RX port of the first. */ + RTE_ETH_FOREACH_DEV(next_pi) + peer_rx_port =3D next_pi; + } + manual =3D 1; + } else if (hairpin_mode & 0x2) { + if (cnt_pi & 0x1) { + peer_rx_port =3D p_pi; + } else { + peer_rx_port =3D rte_eth_find_next_owned_by(pi + 1, + RTE_ETH_DEV_NO_OWNER); + if (peer_rx_port >=3D RTE_MAX_ETHPORTS) + peer_rx_port =3D pi; + } + peer_tx_port =3D peer_rx_port; + manual =3D 1; + } + diag =3D port_config_hairpin_txq(pi, peer_rx_port, nb_rxq, nb_txq, + nb_hairpinq, manual); + if (diag) + return diag; + diag =3D port_config_hairpin_rxq(pi, peer_tx_port, nb_rxq, nb_txq, + nb_hairpinq, manual); + if (diag) + return diag; + return 0; +} + +static int +setup_mapped_harpin_queues(portid_t pi) { + int ret =3D 0; + struct hairpin_map *map; + + LIST_FOREACH(map, &hairpin_map_head, entry) { + if (map->rx_port =3D=3D pi) { + ret =3D port_config_hairpin_rxq(pi, map->tx_port, + map->rxq_head, + map->txq_head, + map->qnum, true); + if (ret) + return ret; + } + if (map->tx_port =3D=3D pi) { + ret =3D port_config_hairpin_txq(pi, map->rx_port, + map->rxq_head, + map->txq_head, + map->qnum, true); + if (ret) + return ret; + } + } + return 0; +} + +/* Configure the Rx and Tx hairpin queues for the selected port. */ int=20 +setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi) { + return !hairpin_multiport_mode ? + setup_legacy_hairpin_queues(pi, p_pi, cnt_pi) : + setup_mapped_harpin_queues(pi); } + +int +hairpin_bind(uint16_t cfg_pi, portid_t *pl, portid_t *peer_pl) { + uint16_t i; + portid_t pi; + int peer_pi; + int diag; + int j; + + /* bind all started hairpin ports */ + for (i =3D 0; i < cfg_pi; i++) { + pi =3D pl[i]; + /* bind current Tx to all peer Rx */ + peer_pi =3D rte_eth_hairpin_get_peer_ports(pi, peer_pl, + RTE_MAX_ETHPORTS, 1); + if (peer_pi < 0) + return peer_pi; + for (j =3D 0; j < peer_pi; j++) { + if (!port_is_started(peer_pl[j])) + continue; + diag =3D rte_eth_hairpin_bind(pi, peer_pl[j]); + if (diag < 0) { + fprintf(stderr, + "Error during binding hairpin Tx port %u to %u: %s\n", + pi, peer_pl[j], + rte_strerror(-diag)); + return -1; + } + } + /* bind all peer Tx to current Rx */ + peer_pi =3D rte_eth_hairpin_get_peer_ports(pi, peer_pl, + RTE_MAX_ETHPORTS, 0); + if (peer_pi < 0) + return peer_pi; + for (j =3D 0; j < peer_pi; j++) { + if (!port_is_started(peer_pl[j])) + continue; + diag =3D rte_eth_hairpin_bind(peer_pl[j], pi); + if (diag < 0) { + fprintf(stderr, + "Error during binding hairpin Tx port %u to %u: %s\n", + peer_pl[j], pi, + rte_strerror(-diag)); + return -1; + } + } + } + return 0; +} + +void +hairpin_map_usage(void) +{ + printf(" --hairpin-map=3Drxpi:rxq:txpi:txq:n: hairpin map.\n" + " rxpi - Rx port index.\n" + " rxq - Rx queue.\n" + " txpi - Tx port index.\n" + " txq - Tx queue.\n" + " n - hairpin queues number.\n"); +} + +static char +*parse_hairpin_map_entry(char *input, char **next) { + char *tail =3D strchr(input, ':'); + + if (!tail) + return NULL; + tail[0] =3D '\0'; + *next =3D tail + 1; + return input; +} + +int +parse_hairpin_map(const char *hpmap) +{ + /* + * Testpmd hairpin map format: + * + */ + char *head, *next =3D (char *)(uintptr_t)hpmap; + struct hairpin_map *map =3D calloc(1, sizeof(*map)); Can use rte_calloc() here. + + if (!map) + return -ENOMEM; + + head =3D parse_hairpin_map_entry(next, &next); + if (!head) + goto err; + map->rx_port =3D atoi(head); + head =3D parse_hairpin_map_entry(next, &next); + if (!head) + goto err; + map->rxq_head =3D atoi(head); + head =3D parse_hairpin_map_entry(next, &next); + if (!head) + goto err; + map->tx_port =3D atoi(head); + head =3D parse_hairpin_map_entry(next, &next); + if (!head) + goto err; + map->txq_head =3D atoi(head); + map->qnum =3D atoi(next); + hairpin_add_multiport_map(map); + return 0; +err: + free(map); Can use rte_free() + return -EINVAL; +} + + diff --git a/app/test-pmd/meson.build b/app/test-pmd/meson.build index 719f= 875be0..f1c36529b4 100644 --- a/app/test-pmd/meson.build +++ b/app/test-pmd/meson.build @@ -15,6 +15,7 @@ sources =3D files( 'config.c', 'csumonly.c', 'flowgen.c', + 'hairpin.c', 'icmpecho.c', 'ieee1588fwd.c', 'iofwd.c', diff --git a/app/test-pmd/parameters.c b/app/test-pmd/parameters.c index 22= 364e09ab..7b31b94542 100644 --- a/app/test-pmd/parameters.c +++ b/app/test-pmd/parameters.c @@ -143,6 +143,8 @@ enum { TESTPMD_OPT_HAIRPINQ_NUM, #define TESTPMD_OPT_HAIRPIN_MODE "hairpin-mode" TESTPMD_OPT_HAIRPIN_MODE_NUM, +#define TESTPMD_OPT_HAIRPIN_MAP "hairpin-map" + TESTPMD_OPT_HAIRPIN_MAP_NUM, #define TESTPMD_OPT_BURST "burst" TESTPMD_OPT_BURST_NUM, #define TESTPMD_OPT_FLOWGEN_CLONES "flowgen-clones" @@ -317,6 +319,7 @@ static const struct option long_options[] =3D { REQUIRED_ARG(TESTPMD_OPT_TXD), REQUIRED_ARG(TESTPMD_OPT_HAIRPINQ), REQUIRED_ARG(TESTPMD_OPT_HAIRPIN_MODE), + REQUIRED_ARG(TESTPMD_OPT_HAIRPIN_MAP), REQUIRED_ARG(TESTPMD_OPT_BURST), REQUIRED_ARG(TESTPMD_OPT_FLOWGEN_CLONES), REQUIRED_ARG(TESTPMD_OPT_FLOWGEN_FLOWS), @@ -542,6 +545,7 @@ usage(char* progname) printf(" --hairpin-mode=3D0xXX: bitmask set the hairpin port mode.\n" " 0x10 - explicit Tx rule, 0x02 - hairpin ports paired\n" " 0x01 - hairpin ports loop, 0x00 - hairpin port self\n"); + hairpin_map_usage(); } =20 static int @@ -1317,6 +1321,12 @@ launch_args_parse(int argc, char** argv) hairpin_mode =3D (uint32_t)n; break; } + case TESTPMD_OPT_HAIRPIN_MAP_NUM: + hairpin_multiport_mode =3D true; + ret =3D parse_hairpin_map(optarg); + if (ret) + rte_exit(EXIT_FAILURE, "invalid hairpin map\n"); + break; case TESTPMD_OPT_BURST_NUM: n =3D atoi(optarg); if (n =3D=3D 0) { diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index b1401136= e4..f487769578 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -284,7 +284,6 @@ uint8_t dcb_config =3D 0; /* * Configurable number of RX/TX queues. */ -queueid_t nb_hairpinq; /**< Number of hairpin queues per port. */ queueid= _t nb_rxq =3D 1; /**< Number of RX queues per port. */ queueid_t nb_txq = =3D 1; /**< Number of TX queues per port. */ =20 @@ -431,9 +430,6 @@ bool setup_on_probe_event =3D true; /* Clear ptypes on port initialization. */ uint8_t clear_ptypes =3D true; =20 -/* Hairpin ports configuration mode. */ -uint32_t hairpin_mode; - /* Pretty printing of ethdev events */ static const char * const eth_event_desc[] =3D { [RTE_ETH_EVENT_UNKNOWN] =3D "unknown", @@ -1555,54 +1551,6 @@ check_nb_txd(queueid_t txd) return 0; } =20 - -/* - * Get the allowed maximum number of hairpin queues. - * *pid return the port id which has minimal value of - * max_hairpin_queues in all ports. - */ -queueid_t -get_allowed_max_nb_hairpinq(portid_t *pid) -{ - queueid_t allowed_max_hairpinq =3D RTE_MAX_QUEUES_PER_PORT; - portid_t pi; - struct rte_eth_hairpin_cap cap; - - RTE_ETH_FOREACH_DEV(pi) { - if (rte_eth_dev_hairpin_capability_get(pi, &cap) !=3D 0) { - *pid =3D pi; - return 0; - } - if (cap.max_nb_queues < allowed_max_hairpinq) { - allowed_max_hairpinq =3D cap.max_nb_queues; - *pid =3D pi; - } - } - return allowed_max_hairpinq; -} - -/* - * Check input hairpin is valid or not. - * If input hairpin is not greater than any of maximum number - * of hairpin queues of all ports, it is valid. - * if valid, return 0, else return -1 - */ -int -check_nb_hairpinq(queueid_t hairpinq) -{ - queueid_t allowed_max_hairpinq; - portid_t pid =3D 0; - - allowed_max_hairpinq =3D get_allowed_max_nb_hairpinq(&pid); - if (hairpinq > allowed_max_hairpinq) { - fprintf(stderr, - "Fail: input hairpin (%u) can't be greater than max_hairpin_queues (%u)= of port %u\n", - hairpinq, allowed_max_hairpinq, pid); - return -1; - } - return 0; -} - static int get_eth_overhead(struct rte_eth_dev_info *dev_info) { @@ -2684,126 +2632,= 6 @@ port_is_started(portid_t port_id) return 1; } =20 -#define HAIRPIN_MODE_RX_FORCE_MEMORY RTE_BIT32(8) -#define HAIRPIN_MODE_TX= _FORCE_MEMORY RTE_BIT32(9) - -#define HAIRPIN_MODE_RX_LOCKED_MEMORY RTE_BIT32(12) -#define HAIRPIN_MODE_= RX_RTE_MEMORY RTE_BIT32(13) - -#define HAIRPIN_MODE_TX_LOCKED_MEMORY RTE_BIT32(16) -#define HAIRPIN_MODE_= TX_RTE_MEMORY RTE_BIT32(17) - - -/* Configure the Rx and Tx hairpin queues for the selected port. */ -stati= c int -setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi) -{ - queueid_t qi; - struct rte_eth_hairpin_conf hairpin_conf =3D { - .peer_count =3D 1, - }; - int i; - int diag; - struct rte_port *port =3D &ports[pi]; - uint16_t peer_rx_port =3D pi; - uint16_t peer_tx_port =3D pi; - uint32_t manual =3D 1; - uint32_t tx_exp =3D hairpin_mode & 0x10; - uint32_t rx_force_memory =3D hairpin_mode & HAIRPIN_MODE_RX_FORCE_MEMORY; - uint32_t rx_locked_memory =3D hairpin_mode & HAIRPIN_MODE_RX_LOCKED_MEMOR= Y; - uint32_t rx_rte_memory =3D hairpin_mode & HAIRPIN_MODE_RX_RTE_MEMORY; - uint32_t tx_force_memory =3D hairpin_mode & HAIRPIN_MODE_TX_FORCE_MEMORY; - uint32_t tx_locked_memory =3D hairpin_mode & HAIRPIN_MODE_TX_LOCKED_MEMOR= Y; - uint32_t tx_rte_memory =3D hairpin_mode & HAIRPIN_MODE_TX_RTE_MEMORY; - - if (!(hairpin_mode & 0xf)) { - peer_rx_port =3D pi; - peer_tx_port =3D pi; - manual =3D 0; - } else if (hairpin_mode & 0x1) { - peer_tx_port =3D rte_eth_find_next_owned_by(pi + 1, - RTE_ETH_DEV_NO_OWNER); - if (peer_tx_port >=3D RTE_MAX_ETHPORTS) - peer_tx_port =3D rte_eth_find_next_owned_by(0, - RTE_ETH_DEV_NO_OWNER); - if (p_pi !=3D RTE_MAX_ETHPORTS) { - peer_rx_port =3D p_pi; - } else { - uint16_t next_pi; - - /* Last port will be the peer RX port of the first. */ - RTE_ETH_FOREACH_DEV(next_pi) - peer_rx_port =3D next_pi; - } - manual =3D 1; - } else if (hairpin_mode & 0x2) { - if (cnt_pi & 0x1) { - peer_rx_port =3D p_pi; - } else { - peer_rx_port =3D rte_eth_find_next_owned_by(pi + 1, - RTE_ETH_DEV_NO_OWNER); - if (peer_rx_port >=3D RTE_MAX_ETHPORTS) - peer_rx_port =3D pi; - } - peer_tx_port =3D peer_rx_port; - manual =3D 1; - } - - for (qi =3D nb_txq, i =3D 0; qi < nb_hairpinq + nb_txq; qi++) { - hairpin_conf.peers[0].port =3D peer_rx_port; - hairpin_conf.peers[0].queue =3D i + nb_rxq; - hairpin_conf.manual_bind =3D !!manual; - hairpin_conf.tx_explicit =3D !!tx_exp; - hairpin_conf.force_memory =3D !!tx_force_memory; - hairpin_conf.use_locked_device_memory =3D !!tx_locked_memory; - hairpin_conf.use_rte_memory =3D !!tx_rte_memory; - diag =3D rte_eth_tx_hairpin_queue_setup - (pi, qi, nb_txd, &hairpin_conf); - i++; - if (diag =3D=3D 0) - continue; - - /* Fail to setup rx queue, return */ - if (port->port_status =3D=3D RTE_PORT_HANDLING) - port->port_status =3D RTE_PORT_STOPPED; - else - fprintf(stderr, - "Port %d can not be set back to stopped\n", pi); - fprintf(stderr, "Fail to configure port %d hairpin queues\n", - pi); - /* try to reconfigure queues next time */ - port->need_reconfig_queues =3D 1; - return -1; - } - for (qi =3D nb_rxq, i =3D 0; qi < nb_hairpinq + nb_rxq; qi++) { - hairpin_conf.peers[0].port =3D peer_tx_port; - hairpin_conf.peers[0].queue =3D i + nb_txq; - hairpin_conf.manual_bind =3D !!manual; - hairpin_conf.tx_explicit =3D !!tx_exp; - hairpin_conf.force_memory =3D !!rx_force_memory; - hairpin_conf.use_locked_device_memory =3D !!rx_locked_memory; - hairpin_conf.use_rte_memory =3D !!rx_rte_memory; - diag =3D rte_eth_rx_hairpin_queue_setup - (pi, qi, nb_rxd, &hairpin_conf); - i++; - if (diag =3D=3D 0) - continue; - - /* Fail to setup rx queue, return */ - if (port->port_status =3D=3D RTE_PORT_HANDLING) - port->port_status =3D RTE_PORT_STOPPED; - else - fprintf(stderr, - "Port %d can not be set back to stopped\n", pi); - fprintf(stderr, "Fail to configure port %d hairpin queues\n", - pi); - /* try to reconfigure queues next time */ - port->need_reconfig_queues =3D 1; - return -1; - } - return 0; -} - /* Configure the Rx with optional split. */ int rx_queue_setup(uint16_t = port_id, uint16_t rx_queue_id, @@ -3043,7 +2871,6 @@ start_port(portid_t pi= d) portid_t peer_pl[RTE_MAX_ETHPORTS]; uint16_t cnt_pi =3D 0; uint16_t cfg_pi =3D 0; - int peer_pi; queueid_t qi; struct rte_port *port; struct rte_eth_hairpin_cap cap; @@ -3304,47 +3131,9 @@ start_port(portid_t pid) fprintf(stderr, "Please stop the ports first\n"); =20 if (hairpin_mode & 0xf) { - uint16_t i; - int j; - - /* bind all started hairpin ports */ - for (i =3D 0; i < cfg_pi; i++) { - pi =3D pl[i]; - /* bind current Tx to all peer Rx */ - peer_pi =3D rte_eth_hairpin_get_peer_ports(pi, peer_pl, - RTE_MAX_ETHPORTS, 1); - if (peer_pi < 0) - return peer_pi; - for (j =3D 0; j < peer_pi; j++) { - if (!port_is_started(peer_pl[j])) - continue; - diag =3D rte_eth_hairpin_bind(pi, peer_pl[j]); - if (diag < 0) { - fprintf(stderr, - "Error during binding hairpin Tx port %u to %u: %s\n", - pi, peer_pl[j], - rte_strerror(-diag)); - return -1; - } - } - /* bind all peer Tx to current Rx */ - peer_pi =3D rte_eth_hairpin_get_peer_ports(pi, peer_pl, - RTE_MAX_ETHPORTS, 0); - if (peer_pi < 0) - return peer_pi; - for (j =3D 0; j < peer_pi; j++) { - if (!port_is_started(peer_pl[j])) - continue; - diag =3D rte_eth_hairpin_bind(peer_pl[j], pi); - if (diag < 0) { - fprintf(stderr, - "Error during binding hairpin Tx port %u to %u: %s\n", - peer_pl[j], pi, - rte_strerror(-diag)); - return -1; - } - } - } + diag =3D hairpin_bind(cfg_pi, pl, peer_pl); + if (diag < 0) + return -1; } =20 fill_xstats_display_info_for_port(pid); diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 9facd7f2= 81..659c5b4fdc 100644 --- a/app/test-pmd/testpmd.h +++ b/app/test-pmd/testpmd.h @@ -126,6 +126,16 @@ enum noisy_fwd_mode { NOISY_FWD_MODE_MAX, }; =20 +/** + * Command line arguments parser sets `hairpin_multiport_mode` to True + * if explicit hairpin map configuration mode was used. + */ +extern bool hairpin_multiport_mode; + +/** Hairpin maps list. */ +struct hairpin_map; +extern void hairpin_add_multiport_map(struct hairpin_map *map); + /** * The data structure associated with RX and TX packet burst statistics * that are recorded for each forwarding stream. @@ -1252,6 +1262,10 @@ extern int flow_parse(const char *src, void *result,= unsigned int size, struct rte_flow_attr **attr, struct rte_flow_item **pattern, struct rte_flow_action **actions); +int setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi);=20 +int hairpin_bind(uint16_t cfg_pi, portid_t *pl, portid_t *peer_pl);=20 +void hairpin_map_usage(void); int parse_hairpin_map(const char *hpmap); =20 uint64_t str_to_rsstypes(const char *str); const char *rsstypes_to_str(ui= nt64_t rss_type); diff --git a/doc/guides/testpmd_app_ug/run_app.rst b/doc/= guides/testpmd_app_ug/run_app.rst index 1a9b812a7f..48717707a7 100644 --- a/doc/guides/testpmd_app_ug/run_app.rst +++ b/doc/guides/testpmd_app_ug/run_app.rst @@ -571,6 +571,9 @@ The command line options are: =20 The default value is 0. Hairpin will use single port mode and implicit= Tx flow mode. =20 +* ``--hairpin-map=3DRx port id:Rx queue:Tx port id:Tx queue:queues numbe= r`` + + Set explicit hairpin configuration. =20 Testpmd Multi-Process Command-line Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~= ~~~~~~~~~~~~ -- 2.43.0