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 2538E45C11; Wed, 30 Oct 2024 08:38:15 +0100 (CET) Received: from mails.dpdk.org (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id ECA46402EE; Wed, 30 Oct 2024 08:38:14 +0100 (CET) Received: from NAM02-DM3-obe.outbound.protection.outlook.com (mail-dm3nam02on2072.outbound.protection.outlook.com [40.107.95.72]) by mails.dpdk.org (Postfix) with ESMTP id 7FD164025C for ; Wed, 30 Oct 2024 08:38:13 +0100 (CET) ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none; b=LwbZHf5rdYIIh0864ysmy9ZQMsg5Qiy75o5/ltPZL4rW0Fa2Xcd0+7Xx9Q4zIyiAXZGAKEyrQ6DnB/hjDhzaCT0FMnKMpwsu2UcMHqlV+eg2ofhR2yvKzIGqrWiyo9vzQYdbFY40K/awKgIjx+hSkmNKm9iKiHH+Hy2OyXUM8GLVDPHSGYzyEazg57RVRpSsdwHx4C+2unvbXSNgSIdC4wcUqxryUSpLk5Fbb7rwN3r1PlpLGn/uvpH77uEq6IFBwRXh3fRMApM1kK3qW8/Vuo6GfUAXHlfNECGY/3AnqZtrFWi5T/TZERQJFruitdHtxW0aLVJhaSH1oyn/nTtYig== 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=9t2qQJU4bMDO1eEo+9BX8aCQR4bWPhEa9eeFkCUtCaY=; b=Jt+cymgPNdJub7AbVDZKJxNqgNQjIZN+u2rt4JWcYuLGkh70cbut/DNjWzKXw2mjfOg45pg0fXNK5z2rvAJAndTWf+5cBrlHcgbk4r7glCi9nIsMSPfvunc6Mzxx198Lp+HNxwHueX2VDS8WfFY6XFHDO9Y6tUc4n9dK6bCm3UOK62juQNZEAsYI7NqorJMjihmQnHIYfCz9InTGWGc/ktgL2yJ1ZEq7XnkqypQXS+Ln1TPvMs0LOIxJ90QJ3E5PUWi2sZ8h1O7yWVqMequq+XKm7IeQ3UZTxtBUAHQS9JZ5kcFa0rO0xDLgUDIPspIcvOFibUFGlnVknQ9kjs9rag== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass (sender ip is 216.228.117.160) smtp.rcpttodomain=amd.com smtp.mailfrom=nvidia.com; dmarc=pass (p=reject sp=reject pct=100) action=none header.from=nvidia.com; dkim=none (message not signed); arc=none (0) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com; s=selector2; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=9t2qQJU4bMDO1eEo+9BX8aCQR4bWPhEa9eeFkCUtCaY=; b=FpMA2K4kyghttZgz+6Fwdt4uLW9N4pNhUkTiMuVoWUfKQ0R42ZuP310bguO8nNavcqN1xZQdaSbg5kuIly0ydMLEG/RTWHmOwXtcz4ag5MreYQPjlemTWXmTdffLQw94CBLEJTcfrXQ/vdf3SvgK4lYb1vZWhoV3wtpIWldTe2qhSVeTwKMKsX3pygSydmDl4kAq7mfgE/L73KgvHqUBTmnJOyicuQwpUeQ6/M6TYXIZlS1kWtnBvdF/HRLNp10T5YTbDVbttm5z6EQwcskoe54SJZNT+WTQ9A2Y3TNHqAt1RSqcatlulZ+7+OyMYWypYqakMRQsXfUhvMmRkoWapA== Received: from CH0PR03CA0063.namprd03.prod.outlook.com (2603:10b6:610:cc::8) by SA1PR12MB7222.namprd12.prod.outlook.com (2603:10b6:806:2bf::15) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8114.20; Wed, 30 Oct 2024 07:38:05 +0000 Received: from CH1PEPF0000A348.namprd04.prod.outlook.com (2603:10b6:610:cc:cafe::1b) by CH0PR03CA0063.outlook.office365.com (2603:10b6:610:cc::8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8114.17 via Frontend Transport; Wed, 30 Oct 2024 07:38:05 +0000 X-MS-Exchange-Authentication-Results: spf=pass (sender IP is 216.228.117.160) smtp.mailfrom=nvidia.com; dkim=none (message not signed) header.d=none;dmarc=pass action=none header.from=nvidia.com; Received-SPF: Pass (protection.outlook.com: domain of nvidia.com designates 216.228.117.160 as permitted sender) receiver=protection.outlook.com; client-ip=216.228.117.160; helo=mail.nvidia.com; pr=C Received: from mail.nvidia.com (216.228.117.160) by CH1PEPF0000A348.mail.protection.outlook.com (10.167.244.4) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8114.16 via Frontend Transport; Wed, 30 Oct 2024 07:38:05 +0000 Received: from rnnvmail201.nvidia.com (10.129.68.8) by mail.nvidia.com (10.129.200.66) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.4; Wed, 30 Oct 2024 00:37:50 -0700 Received: from nvidia.com (10.126.231.35) by rnnvmail201.nvidia.com (10.129.68.8) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.1544.4; Wed, 30 Oct 2024 00:37:46 -0700 From: Gregory Etelson To: , , CC: , , , , , , Subject: [PATCH v6] testpmd: add hairpin map parameter Date: Wed, 30 Oct 2024 09:37:24 +0200 Message-ID: <20241030073724.103112-1-getelson@nvidia.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240922065053.996568-1-getelson@nvidia.com> References: <20240922065053.996568-1-getelson@nvidia.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit Content-Type: text/plain X-Originating-IP: [10.126.231.35] X-ClientProxiedBy: rnnvmail203.nvidia.com (10.129.68.9) To rnnvmail201.nvidia.com (10.129.68.8) X-EOPAttributedMessage: 0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: CH1PEPF0000A348:EE_|SA1PR12MB7222:EE_ X-MS-Office365-Filtering-Correlation-Id: abc1451b-93e8-4cee-59b8-08dcf8b5cadc 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; ARA:13230040|36860700013|376014|82310400026|1800799024; X-Microsoft-Antispam-Message-Info: =?us-ascii?Q?SQsmq+qGlyH+r6ew8AyauV0lYXASDX2oqvYzI2tyCtfcbAJCv5Mz77IZVY8j?= =?us-ascii?Q?2VmxLLgREbLUZQRssV1abDvenNk0WrGgk3HfxuxmCeEz3VRZKr18d5RQZ8f8?= =?us-ascii?Q?3wqXVf4AKGX5IMVzR7C/hOv+Q0kNT3EOv0cAilVGLthiv+k3xAb7CBRTbcLA?= =?us-ascii?Q?uMGFaBZemho0K4bF66C7jcBy6X+prR4e26DKpoD3Sgi3fXwD5H7NGgatFK6P?= =?us-ascii?Q?grYKbodyaq1bw+rlmJKCx8gwbG37a6oHlQJJgDyGDLxPJhs6dYpsC4vXAmb7?= =?us-ascii?Q?XluX1t9/sLJrXZ6G/E7kjDSBqXPu2nxSDsxjgtFA2tBf0RKnhaZbgTmn/MAx?= =?us-ascii?Q?qcLrGymeNwW0eafACJ/JtItECic7SJ6RA1isZusFilW588y469+I6Akizn+Q?= =?us-ascii?Q?q8tjyUbUO4Z3V7Xj4D2ueeHlNKFMGgu23fzXEcSIWNSfEW0iJj0M68HuZfUd?= =?us-ascii?Q?NvE+puyTJqB1zeew58foznNf2jnZ+Qnd4Z5DteNV6dEfcWj07XUOxoXqGMCZ?= =?us-ascii?Q?OKp8eTEVbdBoRrSH/HPbiKa+AD4xC7/hMuu+SiQVHzmuPSh8hh1h3hRosNN9?= =?us-ascii?Q?XS7Id3xblI4+Jk4Tnt7tjW5aw+YJ+yiy8C7rvEM4PA0r2DcUD5wLi3uGfRwg?= =?us-ascii?Q?chzyQQ8+LWY4zKxSsbJlMLVkdLcmbglmQ6ZjH4oeaFB4A9H1SfkhXPvodBBN?= =?us-ascii?Q?w0TiGWZPuziWY6xgHk29fy6uZegJjY3skBx9wMU5HUFcGXwlDabJIeopFFdm?= =?us-ascii?Q?kMLbRSYOu1BADqnDFQEIS63XDU7ix9NG/esITJ2Tga8lMNRUGRPTYWMs5X7T?= =?us-ascii?Q?M64nGj4K3Ci7UAfZR5A8Btp/6TKHA3RXVvvLfztPlOkf8i4STMwRJqreCfCI?= =?us-ascii?Q?TkNRyt0uEZkk0SfrKTLuqK2F+gezk3+wVaC3ssWBmex4jqj6mQp1z2dqnQDY?= =?us-ascii?Q?/YiPPvSlwD+yhtepqefw6RzZt7fZAVStMBLBz/6DV+s71TsSfQTty0lUW/OQ?= =?us-ascii?Q?3ZiJeOXzhRZBkPjDQ2VGoBSm0DdqVvh1FGTGYSpsfIKYsxgI+bVm3za6o2sx?= =?us-ascii?Q?xM6jHbBgmxzBCiaLXDWS+1DzxPHgzQ8oHcxSmiwajDMwxJbsTtyP3/yFTn9P?= =?us-ascii?Q?/eFnS7Bku1jUds3KLm7Hevw7DCTqG5jCsYtQrYfSBYH50oM1tCBxpOOoEAbl?= =?us-ascii?Q?QeheDbBwi3JZCdoDKXlZZG/9uaI0uwIMUC8R/0gqSJlXiZ9l1tCpn4Dswtto?= =?us-ascii?Q?vxB/eFMUtHIjonqHZDmmaW029LeH49dUNqpidlzBti7Zcg07agzcgvZvHpyh?= =?us-ascii?Q?b3PrJYrCvqF9rz0ciyl/VC7Fg/+fVLNyjx5rw7n9BoTHTdNh6hB6gulnPDbf?= =?us-ascii?Q?oq3DYLwF3LUq0RLupa72tTvzM238otf4fjOYIlkxOKfDt/H5fQ=3D=3D?= X-Forefront-Antispam-Report: CIP:216.228.117.160; CTRY:US; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:mail.nvidia.com; PTR:dc6edge1.nvidia.com; CAT:NONE; SFS:(13230040)(36860700013)(376014)(82310400026)(1800799024); DIR:OUT; SFP:1101; X-OriginatorOrg: Nvidia.com X-MS-Exchange-CrossTenant-OriginalArrivalTime: 30 Oct 2024 07:38:05.4256 (UTC) X-MS-Exchange-CrossTenant-Network-Message-Id: abc1451b-93e8-4cee-59b8-08dcf8b5cadc X-MS-Exchange-CrossTenant-Id: 43083d15-7273-40c1-b7db-39efd9ccc17a X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp: TenantId=43083d15-7273-40c1-b7db-39efd9ccc17a; Ip=[216.228.117.160]; Helo=[mail.nvidia.com] X-MS-Exchange-CrossTenant-AuthSource: CH1PEPF0000A348.namprd04.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Anonymous X-MS-Exchange-CrossTenant-FromEntityHeader: HybridOnPrem X-MS-Exchange-Transport-CrossTenantHeadersStamped: SA1PR12MB7222 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 Hairpin offloads packet forwarding between ports. Packet is expected on Rx port , Rx queue and is forwarded to Tx port Tx queue . Testpmd implements a static hairpin configuration scheme. The new parameter allows explicit selection of Rx and Tx ports and queues in hairpin configuration. The new `hairpin-map` parameter is provided with 5 parameters, separated by `:` `--hairpin-map=Rx port id:Rx queue:Tx port id:Tx queue:queues number` Testpmd operator can provide several `hairpin-map` parameters for different hairpin maps. Example: dpdk-testpmd -- \ \ --rxq=2 --txq=2 --hairpinq=2 --hairpin-mode=0x12 \ --hairpin-map=0:2:1:2:1 \ # [1] --hairpin-map=0: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 | 360 ++++++++++++++++++++++++++ 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, 391 insertions(+), 214 deletions(-) create mode 100644 app/test-pmd/hairpin.c diff --git a/app/test-pmd/hairpin.c b/app/test-pmd/hairpin.c new file mode 100644 index 0000000000..6d42fdc088 --- /dev/null +++ b/app/test-pmd/hairpin.c @@ -0,0 +1,360 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2022 NVIDIA Corporation & Affiliates + */ + +#include +#include +#include +#include +#include + +#include + +#include "testpmd.h" + +/* Hairpin ports configuration mode. */ +uint32_t hairpin_mode; + +bool hairpin_multiport_mode = false; + +queueid_t nb_hairpinq; /**< Number of hairpin queues per port. */ + +static LIST_HEAD(, hairpin_map) hairpin_map_head = 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 = 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) != 0) { + *pid = pi; + return 0; + } + if (cap.max_nb_queues < allowed_max_hairpinq) { + allowed_max_hairpinq = cap.max_nb_queues; + *pid = 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 = 0; + + allowed_max_hairpinq = 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 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) + +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 = !!(hairpin_mode & 0x10); + uint32_t force_mem = !!(hairpin_mode & HAIRPIN_MODE_RX_FORCE_MEMORY); + uint32_t locked_mem = !!(hairpin_mode & HAIRPIN_MODE_RX_LOCKED_MEMORY); + uint32_t rte_mem = !!(hairpin_mode & HAIRPIN_MODE_RX_RTE_MEMORY); + struct rte_port *port = &ports[pi]; + struct rte_eth_hairpin_conf hairpin_conf = { + .peer_count = 1, + }; + + for (qi = rxq_head, i = 0; qi < rxq_head + qcount; qi++) { + hairpin_conf.peers[0].port = peer_tx_port; + hairpin_conf.peers[0].queue = i + txq_head; + hairpin_conf.manual_bind = manual_bind; + hairpin_conf.tx_explicit = tx_explicit; + hairpin_conf.force_memory = force_mem; + hairpin_conf.use_locked_device_memory = locked_mem; + hairpin_conf.use_rte_memory = rte_mem; + diag = rte_eth_rx_hairpin_queue_setup + (pi, qi, nb_rxd, &hairpin_conf); + i++; + if (diag == 0) + continue; + + /* Fail to setup rx queue, return */ + if (port->port_status == RTE_PORT_HANDLING) + port->port_status = RTE_PORT_STOPPED; + else + fprintf(stderr, + "Port %d can not be set back to stopped\n", pi); + fprintf(stderr, + "Port %u 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 = 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 = !!(hairpin_mode & 0x10); + uint32_t force_mem = !!(hairpin_mode & HAIRPIN_MODE_TX_FORCE_MEMORY); + uint32_t locked_mem = !!(hairpin_mode & HAIRPIN_MODE_TX_LOCKED_MEMORY); + uint32_t rte_mem = !!(hairpin_mode & HAIRPIN_MODE_TX_RTE_MEMORY); + struct rte_port *port = &ports[pi]; + struct rte_eth_hairpin_conf hairpin_conf = { + .peer_count = 1, + }; + + for (qi = txq_head, i = 0; qi < txq_head + qcount; qi++) { + hairpin_conf.peers[0].port = peer_rx_port; + hairpin_conf.peers[0].queue = i + rxq_head; + hairpin_conf.manual_bind = manual_bind; + hairpin_conf.tx_explicit = tx_explicit; + hairpin_conf.force_memory = force_mem; + hairpin_conf.use_locked_device_memory = locked_mem; + hairpin_conf.use_rte_memory = rte_mem; + diag = rte_eth_tx_hairpin_queue_setup + (pi, qi, nb_txd, &hairpin_conf); + i++; + if (diag == 0) + continue; + + /* Fail to setup rx queue, return */ + if (port->port_status == RTE_PORT_HANDLING) + port->port_status = 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 = 1; + return -1; + } + return 0; +} + +static int +setup_legacy_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi) +{ + int diag; + uint16_t peer_rx_port = pi; + uint16_t peer_tx_port = pi; + uint32_t manual = 1; + + if (!(hairpin_mode & 0xf)) { + peer_rx_port = pi; + peer_tx_port = pi; + manual = 0; + } else if (hairpin_mode & 0x1) { + peer_tx_port = rte_eth_find_next_owned_by(pi + 1, + RTE_ETH_DEV_NO_OWNER); + if (peer_tx_port >= RTE_MAX_ETHPORTS) + peer_tx_port = rte_eth_find_next_owned_by(0, + RTE_ETH_DEV_NO_OWNER); + if (p_pi != RTE_MAX_ETHPORTS) { + peer_rx_port = 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 = next_pi; + } + manual = 1; + } else if (hairpin_mode & 0x2) { + if (cnt_pi & 0x1) { + peer_rx_port = p_pi; + } else { + peer_rx_port = rte_eth_find_next_owned_by(pi + 1, + RTE_ETH_DEV_NO_OWNER); + if (peer_rx_port >= RTE_MAX_ETHPORTS) + peer_rx_port = pi; + } + peer_tx_port = peer_rx_port; + manual = 1; + } + diag = port_config_hairpin_txq(pi, peer_rx_port, nb_rxq, nb_txq, + nb_hairpinq, manual); + if (diag) + return diag; + diag = 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 = 0; + struct hairpin_map *map; + + LIST_FOREACH(map, &hairpin_map_head, entry) { + if (map->rx_port == pi) { + ret = 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 == pi) { + ret = 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 +setup_hairpin_queues(portid_t pi, portid_t p_pi, uint16_t cnt_pi) +{ + if (hairpin_multiport_mode) + return setup_mapped_harpin_queues(pi); + + return setup_legacy_hairpin_queues(pi, p_pi, cnt_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 = 0; i < cfg_pi; i++) { + pi = pl[i]; + /* bind current Tx to all peer Rx */ + peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl, + RTE_MAX_ETHPORTS, 1); + if (peer_pi < 0) + return peer_pi; + for (j = 0; j < peer_pi; j++) { + if (!port_is_started(peer_pl[j])) + continue; + diag = 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 = rte_eth_hairpin_get_peer_ports(pi, peer_pl, + RTE_MAX_ETHPORTS, 0); + if (peer_pi < 0) + return peer_pi; + for (j = 0; j < peer_pi; j++) { + if (!port_is_started(peer_pl[j])) + continue; + diag = 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=rxpi: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"); +} + +int +parse_hairpin_map(const char *hpmap) +{ + /* + * Testpmd hairpin map format: + * + */ + int ret; + struct hairpin_map *map = calloc(1, sizeof(*map)); + + if (!map) + return -ENOMEM; + + ret = sscanf(hpmap, "%hu:%hu:%hu:%hu:%hu", + &map->rx_port, &map->rxq_head, + &map->tx_port, &map->txq_head, &map->qnum); + if (ret != 5) { + free(map); + return -EINVAL; + } + hairpin_add_multiport_map(map); + return 0; +} diff --git a/app/test-pmd/meson.build b/app/test-pmd/meson.build index 719f875be0..f1c36529b4 100644 --- a/app/test-pmd/meson.build +++ b/app/test-pmd/meson.build @@ -15,6 +15,7 @@ sources = 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 22364e09ab..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[] = { 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=0xXX: 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(); } static int @@ -1317,6 +1321,12 @@ launch_args_parse(int argc, char** argv) hairpin_mode = (uint32_t)n; break; } + case TESTPMD_OPT_HAIRPIN_MAP_NUM: + hairpin_multiport_mode = true; + ret = parse_hairpin_map(optarg); + if (ret) + rte_exit(EXIT_FAILURE, "invalid hairpin map\n"); + break; case TESTPMD_OPT_BURST_NUM: n = atoi(optarg); if (n == 0) { diff --git a/app/test-pmd/testpmd.c b/app/test-pmd/testpmd.c index b1401136e4..f487769578 100644 --- a/app/test-pmd/testpmd.c +++ b/app/test-pmd/testpmd.c @@ -284,7 +284,6 @@ uint8_t dcb_config = 0; /* * Configurable number of RX/TX queues. */ -queueid_t nb_hairpinq; /**< Number of hairpin queues per port. */ queueid_t nb_rxq = 1; /**< Number of RX queues per port. */ queueid_t nb_txq = 1; /**< Number of TX queues per port. */ @@ -431,9 +430,6 @@ bool setup_on_probe_event = true; /* Clear ptypes on port initialization. */ uint8_t clear_ptypes = true; -/* Hairpin ports configuration mode. */ -uint32_t hairpin_mode; - /* Pretty printing of ethdev events */ static const char * const eth_event_desc[] = { [RTE_ETH_EVENT_UNKNOWN] = "unknown", @@ -1555,54 +1551,6 @@ check_nb_txd(queueid_t txd) return 0; } - -/* - * 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 = 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) != 0) { - *pid = pi; - return 0; - } - if (cap.max_nb_queues < allowed_max_hairpinq) { - allowed_max_hairpinq = cap.max_nb_queues; - *pid = 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 = 0; - - allowed_max_hairpinq = 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; } -#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. */ -static 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 = { - .peer_count = 1, - }; - int i; - int diag; - struct rte_port *port = &ports[pi]; - uint16_t peer_rx_port = pi; - uint16_t peer_tx_port = pi; - uint32_t manual = 1; - uint32_t tx_exp = hairpin_mode & 0x10; - uint32_t rx_force_memory = hairpin_mode & HAIRPIN_MODE_RX_FORCE_MEMORY; - uint32_t rx_locked_memory = hairpin_mode & HAIRPIN_MODE_RX_LOCKED_MEMORY; - uint32_t rx_rte_memory = hairpin_mode & HAIRPIN_MODE_RX_RTE_MEMORY; - uint32_t tx_force_memory = hairpin_mode & HAIRPIN_MODE_TX_FORCE_MEMORY; - uint32_t tx_locked_memory = hairpin_mode & HAIRPIN_MODE_TX_LOCKED_MEMORY; - uint32_t tx_rte_memory = hairpin_mode & HAIRPIN_MODE_TX_RTE_MEMORY; - - if (!(hairpin_mode & 0xf)) { - peer_rx_port = pi; - peer_tx_port = pi; - manual = 0; - } else if (hairpin_mode & 0x1) { - peer_tx_port = rte_eth_find_next_owned_by(pi + 1, - RTE_ETH_DEV_NO_OWNER); - if (peer_tx_port >= RTE_MAX_ETHPORTS) - peer_tx_port = rte_eth_find_next_owned_by(0, - RTE_ETH_DEV_NO_OWNER); - if (p_pi != RTE_MAX_ETHPORTS) { - peer_rx_port = 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 = next_pi; - } - manual = 1; - } else if (hairpin_mode & 0x2) { - if (cnt_pi & 0x1) { - peer_rx_port = p_pi; - } else { - peer_rx_port = rte_eth_find_next_owned_by(pi + 1, - RTE_ETH_DEV_NO_OWNER); - if (peer_rx_port >= RTE_MAX_ETHPORTS) - peer_rx_port = pi; - } - peer_tx_port = peer_rx_port; - manual = 1; - } - - for (qi = nb_txq, i = 0; qi < nb_hairpinq + nb_txq; qi++) { - hairpin_conf.peers[0].port = peer_rx_port; - hairpin_conf.peers[0].queue = i + nb_rxq; - hairpin_conf.manual_bind = !!manual; - hairpin_conf.tx_explicit = !!tx_exp; - hairpin_conf.force_memory = !!tx_force_memory; - hairpin_conf.use_locked_device_memory = !!tx_locked_memory; - hairpin_conf.use_rte_memory = !!tx_rte_memory; - diag = rte_eth_tx_hairpin_queue_setup - (pi, qi, nb_txd, &hairpin_conf); - i++; - if (diag == 0) - continue; - - /* Fail to setup rx queue, return */ - if (port->port_status == RTE_PORT_HANDLING) - port->port_status = 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 = 1; - return -1; - } - for (qi = nb_rxq, i = 0; qi < nb_hairpinq + nb_rxq; qi++) { - hairpin_conf.peers[0].port = peer_tx_port; - hairpin_conf.peers[0].queue = i + nb_txq; - hairpin_conf.manual_bind = !!manual; - hairpin_conf.tx_explicit = !!tx_exp; - hairpin_conf.force_memory = !!rx_force_memory; - hairpin_conf.use_locked_device_memory = !!rx_locked_memory; - hairpin_conf.use_rte_memory = !!rx_rte_memory; - diag = rte_eth_rx_hairpin_queue_setup - (pi, qi, nb_rxd, &hairpin_conf); - i++; - if (diag == 0) - continue; - - /* Fail to setup rx queue, return */ - if (port->port_status == RTE_PORT_HANDLING) - port->port_status = 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 = 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 pid) portid_t peer_pl[RTE_MAX_ETHPORTS]; uint16_t cnt_pi = 0; uint16_t cfg_pi = 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"); if (hairpin_mode & 0xf) { - uint16_t i; - int j; - - /* bind all started hairpin ports */ - for (i = 0; i < cfg_pi; i++) { - pi = pl[i]; - /* bind current Tx to all peer Rx */ - peer_pi = rte_eth_hairpin_get_peer_ports(pi, peer_pl, - RTE_MAX_ETHPORTS, 1); - if (peer_pi < 0) - return peer_pi; - for (j = 0; j < peer_pi; j++) { - if (!port_is_started(peer_pl[j])) - continue; - diag = 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 = rte_eth_hairpin_get_peer_ports(pi, peer_pl, - RTE_MAX_ETHPORTS, 0); - if (peer_pi < 0) - return peer_pi; - for (j = 0; j < peer_pi; j++) { - if (!port_is_started(peer_pl[j])) - continue; - diag = 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 = hairpin_bind(cfg_pi, pl, peer_pl); + if (diag < 0) + return -1; } fill_xstats_display_info_for_port(pid); diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h index 131ea53f84..314482e69c 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, }; +/** + * 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. @@ -1255,6 +1265,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); +int hairpin_bind(uint16_t cfg_pi, portid_t *pl, portid_t *peer_pl); +void hairpin_map_usage(void); +int parse_hairpin_map(const char *hpmap); uint64_t str_to_rsstypes(const char *str); const char *rsstypes_to_str(uint64_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: The default value is 0. Hairpin will use single port mode and implicit Tx flow mode. +* ``--hairpin-map=Rx port id:Rx queue:Tx port id:Tx queue:queues number`` + + Set explicit hairpin configuration. Testpmd Multi-Process Command-line Options ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -- 2.43.0