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 352BEA0548; Tue, 11 Oct 2022 18:48:31 +0200 (CEST) Received: from [217.70.189.124] (localhost [127.0.0.1]) by mails.dpdk.org (Postfix) with ESMTP id 230EF40F19; Tue, 11 Oct 2022 18:48:30 +0200 (CEST) Received: from NAM10-MW2-obe.outbound.protection.outlook.com (mail-mw2nam10on2070.outbound.protection.outlook.com [40.107.94.70]) by mails.dpdk.org (Postfix) with ESMTP id 5C84F40E50 for ; Tue, 11 Oct 2022 18:48:28 +0200 (CEST) ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=I5vIEUbi3kQZ+XhaYYf3g7oQH1nSZqjCLT3IyakrgnjJu9v2NsPA+BXfdVfxwzw+SDAF1pu1Mv8p74QivkYfX9j8/TkZHaXmSITtApRvt1L/VbHrbZIu+RkpxixGJYkTf1EAsyXXOcVN4SkzJfV4zBsW2azomUYLWxEInzXJur5hpUJwqu/unJzUsRy1kXvBQL+O3tOqO9gRbAva580gkAuwRJBzuWTPZEZr2aKRCOHF0w5d/XcZ3sdSlQlhbB0AYs5h2u+vnL/vB2aKH1mnDe47FgLukjo5oiNcW9GvAqZLDSKeS2nsxlBa+oT4zmj98PErSiFT1hQOgQiR8lFvJg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; 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=cJ/aa1PZRKFxjaKGgZ3JRnk+SAdwOHnHfXxJCyH17wI=; b=ahrj1hnkS6aklAoEDujpdDPOH5x9jqz+ZGZ4gfUdpVH1DpZl5yBgdAA4fJ1/rCO5JBYvejoobFTExKVbivATmabOjjCYEEN2AUik0ii44+IAjoAAg21wK4xQOFTeIuw6TGz+bErWXKFVWPp8bKevbDKQIaDd2bjMF5PxKYkgi93wf8kLRMpdrPxaKXdyJ9XWGdOujRduCfCWE8U5BEx+xeyPVJ/cjQk9mw5gVp6kXq4VmdKpmIGHjlaV1TUPWe4hVmSAhgdBqQNF4E4DbF8en+swYRtlPmuw7DSvFLrFvyXahX8UvU3VPbvnx4AyrCKzPS6RYCwKr2lsD3wats21yg== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=amd.com; dmarc=pass action=none header.from=amd.com; dkim=pass header.d=amd.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=amd.com; s=selector1; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=cJ/aa1PZRKFxjaKGgZ3JRnk+SAdwOHnHfXxJCyH17wI=; b=dj5zc5tFlCIQxvSHcbQESy2c36NSWoBygrZ8c9/ipNBPB1zstHJsN7i4UmhJ6zGuMI9LWuQM1ALzbs6yIL0YrZHafvFNZrnkuC9+bATx4kL/9iH0RlzFaLmc7FqO/D/iKaLnYAhmCsdpuEkH2hOw8PYzgWTFaH6TtTA+TdyaizE= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=amd.com; Received: from DM6PR12MB4297.namprd12.prod.outlook.com (2603:10b6:5:211::20) by DM4PR12MB6255.namprd12.prod.outlook.com (2603:10b6:8:a4::18) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.5709.15; Tue, 11 Oct 2022 16:48:25 +0000 Received: from DM6PR12MB4297.namprd12.prod.outlook.com ([fe80::b9fd:e732:4585:6b25]) by DM6PR12MB4297.namprd12.prod.outlook.com ([fe80::b9fd:e732:4585:6b25%7]) with mapi id 15.20.5676.036; Tue, 11 Oct 2022 16:48:25 +0000 Message-ID: <669dfda0-383a-7515-dd2d-47692da86e1f@amd.com> Date: Tue, 11 Oct 2022 17:48:20 +0100 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Thunderbird/102.3.2 Subject: Re: [RFC] net: add experimental UDP encapsulation PMD Content-Language: en-US To: Stephen Hemminger , dev@dpdk.org References: <20221011001016.173447-1-stephen@networkplumber.org> From: Ferruh Yigit In-Reply-To: <20221011001016.173447-1-stephen@networkplumber.org> Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 7bit X-ClientProxiedBy: LO4P123CA0158.GBRP123.PROD.OUTLOOK.COM (2603:10a6:600:188::19) To DM6PR12MB4297.namprd12.prod.outlook.com (2603:10b6:5:211::20) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-TrafficTypeDiagnostic: DM6PR12MB4297:EE_|DM4PR12MB6255:EE_ X-MS-Office365-Filtering-Correlation-Id: b55464b0-391f-4fad-7c99-08daaba86a7c X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: Q+52lPoxQhP/mQKwf0NkVbT/0I/+1z/bFee5LWTGZc9oRF+7hBSCQKk0WRP0wYifLA8Rui7WEND2lVkAI94Pxnykt+dJdRRGqj2nH5Yk58Or1kGVAHPVqCI3PntfNMjnkSTkX++k1A8wtyISh9ErmvHmvoeyMHMP77VEGody0RGRe+elGhDS+85A5WLu7t72y51bPF5BsiPpRpAWgbvFSCsdjpgtLhwvjdEYnZ1SEgNwbPzKIh7hbZu+jKxJTfYUG6to2a5eNJcfx2hxtPUSf5a4y3F5sb1HSFLTyifqbq8dVO2XFllnACI7tdoTjenTGVZGN5A0KBxheqKcRbdvVd6lh9STuaKcwH3gYmowsxY92EFBTt86vful6tNDCijeXgqDv/+IsJPXZof+xC8lGarJDCHPR4UtZwvEg0RJSNOAS8lTFJIQspkozdHKnXNPmrK7q+nZbLkcW/WITkuiWPLHzGG0bK8qc0SEHVWxSoOgvHKu+4X69gXsEmjFKe8t+GibmlZru67eq1+QB6jQMG41UBB+hSkWjTeK310QBBXB4MXMZGwZDpnXTp0yASXdTxnBpgTufenQDAKfcFwPDEUVCwR/WcjxAMp2ADCjWNZCQoVMU4uNscwh77QyLiXVMadf0QcibuFkK3Mj3VWIc5XZBtjPYIeQ91ZmaKHFYlNzejvgMdwx/f7PZOEDtLDQlFPFr85+Cqeh3/uvAFQaWmR04Clbj0tqvgW2yiRWGG5Hj/ahGgoHrgqR4mG4+/KpWO57UsEd6KgHgMjR+47myA== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:DM6PR12MB4297.namprd12.prod.outlook.com; PTR:; CAT:NONE; SFS:(13230022)(4636009)(376002)(396003)(346002)(39860400002)(136003)(366004)(451199015)(36756003)(86362001)(8676002)(316002)(38100700002)(66476007)(83380400001)(31696002)(66946007)(66556008)(45080400002)(44832011)(26005)(41300700001)(30864003)(2616005)(6666004)(5660300002)(2906002)(186003)(478600001)(8936002)(6486002)(6512007)(31686004)(53546011)(6506007)(45980500001)(43740500002); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?utf-8?B?Y2lZNXNKbk10Q1FSc213ZHRMaU8wcXVyaDlGTytIbUQxRW9scDlQbFVtbmpu?= =?utf-8?B?UUNaM1pWcWdQMzZNQ2dtQnJ5b205VzB4NElqSTdHVkdSWVEwWmFKVjhZcG5X?= =?utf-8?B?Yk5tanFzNzUycnNCTElNd2IzN2pmN2dnMldSbHpwclpodUd4QVJZYmhpZkVR?= =?utf-8?B?UUtWU2ZDSHFMTTY0UFhhMFVLZ3JJK3pSdE9kbU5oRyt4OFFnaHY3RjJwaEpD?= =?utf-8?B?Mko3SldJcTNoMjdMaUozbHhPWHcxZ2k2aHVUZ05tNVpKODJOWEg5aEJucHhu?= =?utf-8?B?eXY5d0VnVGxFMjM5aG14TzVWYjJneE1qVzFyRjExbXphWmdGUVpFNHlWMVA4?= =?utf-8?B?cUs1QmVDSEFKR0daT3MweXNidDh6U3RqQ3ZxdktDMTBNUWUyYmhtNjBuUmRP?= =?utf-8?B?TkZkS3BqTWVZNUJRK3pDNU03b280c2FxcE0wRjdlZWxQN1hWTWx5Q1JhUith?= =?utf-8?B?dEZWK0NERHBxMjVyUFI0RW1nYXRsaXoweTZEcTdqN1hvVlVxZ1lTYUlPOGdx?= =?utf-8?B?SlNGMVRmaU1NbEt1Zmo2MWs2REhtUTdNbVhXSkFPcFIwMHJGWjc1aXo0VVkr?= =?utf-8?B?M3dYS1I1RUw4Sk5PcjNzdUhRU0dtb2ZQRTNUckx3UXlmKzNkdjRqdHdPa1BZ?= =?utf-8?B?eHZweTB2Zkd4MjlteHpka1lla0ZEaTd2TW83clpaUjBic2x1T0EyV3NybnpB?= =?utf-8?B?NXdFMmVkOExKM0hSVjlNK3NuVTE3STVHUklIdlovS09lajNMenFmWDVRS1Ir?= =?utf-8?B?MUxyZUNEbmovbnEvcyt4Zk5SRVBmaU9UbHRUNkN5b0JmeTRNN2dMS1hpV3NR?= =?utf-8?B?Yi9XMkxCU3RIbU1uLzFCUXFoVFh6bS9KZ2c5SUJxbVVHaUVxTGRsNlV3Tlpz?= =?utf-8?B?NlNpV1oralpiTUxhWFJVU3hCbXhRV3B5WlBnVkttd3dLTm05WVpTeTVKSWV0?= =?utf-8?B?KzRNbExUbFd6b3VSQXNLeGRWa0dabGRhYjkxOHRYUTlMV3VRSXNXc2JCRjl6?= =?utf-8?B?MEpGUnBOOHZ5V0IwNEt5YU9RZzYrc2o3UmhGOUVRSFZIZW4zai9pa1VkK0E2?= =?utf-8?B?U3NiMitySkJBR1laSU9QL1p6NHBzWmRrS0d6Vi9kSzZ4NlpWVTFjNHhEQzMy?= =?utf-8?B?WGFIQzJoYXNnRnNtZkt1czFHdGZYVVdzQlRNWkZOQW9TSjlic3lGZ0lZd3V6?= =?utf-8?B?SGtVQUsxWHZPUUszZ0dqM0dDeTJIb1R3My9pdUFZcngxa1VreUtrNEkyVkVu?= =?utf-8?B?b2dOU0xJM2F1OUgxbEE3clVDRUhTUzhSc1l3bFE3VnhFTTZlWVJrc0tHazhj?= =?utf-8?B?VU50czI2dDVmZEl5QXdvdE1oRkYycTNKK08rTXdOREZyN1hVUWwzbmZuMGdV?= =?utf-8?B?Y2hkQjJQN1pqU1NzejR5NFNJUnNVV1BNYjY5VHEvcDZmSkNaZmgxOXpERDNQ?= =?utf-8?B?Uyt4dnhiOEc4d3lydThNMWNnNHNIYUduS3NSVjE5SWFwOW9wWUMzZFZYTXVB?= =?utf-8?B?bTU5dVNYb1NVZUtXRDZyZXJqMHdnYjlpYTdwTWkwdzhFY2lOLy9OUlpwK0pv?= =?utf-8?B?dnhDaHo2cjlZeml5L1pmZTRFZG90dG1lMFpDTjhBMElPdFh4VDRPZFNXN1p3?= =?utf-8?B?RkhaWHVkVS9XQjhIRCsraVN5R3AzSmdVOXRDNkVtblZEM0tqQVp2aTgxUWlI?= =?utf-8?B?UHR5ZXRsTnJNdndGVTJNN3RXdFE4dEY1ZEZxNmJyRkE2Nm1BVW1MK285d1py?= =?utf-8?B?YjQrbzREZm1WN0JTUHlwNG1reCsxV01MQTBIa1c4NnJOTmJXMFhibzJBQXRM?= =?utf-8?B?MzZkZzFZL2hLeXk4SDI4SEp5dFZVNlgvaHJONWxYZm5xMlc5bkgyMldIWWlY?= =?utf-8?B?dEdvSVhpWEpja241UjkvUzF0UytWaGdKRnRURTM5aTdHVkNwd3lOVXVMV1Iz?= =?utf-8?B?MDJWRXd4RmZDMGxOODlwcEE5V0Z2VHR1aGpKTW9GR2VCeC9UNWsxYzdWbnNH?= =?utf-8?B?NHVwU1JyTW5jb2VoU01zUDBDWnd1MGF0TkNzSXBPVHpwbTR4ZGdjVm5NMEZK?= =?utf-8?B?cHYxMnVvWE1QMXF3cHBKQTFYWTRhYVJpL3dzMVQ2VGpFVE9QaVl2dW9lV1R6?= =?utf-8?Q?uEYCBakDagL07EKvb1RjKbP48?= X-OriginatorOrg: amd.com X-MS-Exchange-CrossTenant-Network-Message-Id: b55464b0-391f-4fad-7c99-08daaba86a7c X-MS-Exchange-CrossTenant-AuthSource: DM6PR12MB4297.namprd12.prod.outlook.com X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 11 Oct 2022 16:48:25.7620 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 3dd8961f-e488-4e60-8e11-a82d994e183d X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: wcNwlIaAgyK5/U7kBO99AuyBFlX/m53R38wuWa6/chV/waDhgPwSdKCCtQbo/sEv X-MS-Exchange-Transport-CrossTenantHeadersStamped: DM4PR12MB6255 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 On 10/11/2022 1:10 AM, Stephen Hemminger wrote: > This is a new PMD which can be useful to test a DPDK application > from another test program. The PMD binds to a connected UDP socket > and expects to receive and send raw Ethernet packets over that > socket. > > This is especially useful for testing envirionments where you s/envirionments/environments/ > can't/don't want to give the test driver program route permission. > root permission? > Signed-off-by: Stephen Hemminger > --- > This is 1st draft port of some test infrastructure to get > feedback and comments from community. > > Later version will include an example and unit tests. > > doc/guides/nics/features/udp.ini | 10 + > doc/guides/nics/udp.rst | 30 ++ need to update 'doc/guides/nics/index.rst' and add new file. > drivers/net/meson.build | 1 + > drivers/net/udp/meson.build | 11 + > drivers/net/udp/rte_eth_udp.c | 728 +++++++++++++++++++++++++++++++ > drivers/net/udp/version.map | 3 + > 6 files changed, 783 insertions(+) > create mode 100644 doc/guides/nics/features/udp.ini > create mode 100644 doc/guides/nics/udp.rst > create mode 100644 drivers/net/udp/meson.build > create mode 100644 drivers/net/udp/rte_eth_udp.c > create mode 100644 drivers/net/udp/version.map > > diff --git a/doc/guides/nics/features/udp.ini b/doc/guides/nics/features/udp.ini > new file mode 100644 > index 000000000000..dfc39204dacf > --- /dev/null > +++ b/doc/guides/nics/features/udp.ini > @@ -0,0 +1,10 @@ > +; > +; Supported features of the 'udp' network poll mode driver. > +; > +; Refer to default.ini for the full list of available PMD features. > +; > +[Features] > +Basic stats = Y > +Stats per queue = Y > +Multiprocess aware = Y > +Scattered Rx = P > diff --git a/doc/guides/nics/udp.rst b/doc/guides/nics/udp.rst > new file mode 100644 > index 000000000000..7b86f5e273e9 > --- /dev/null > +++ b/doc/guides/nics/udp.rst > @@ -0,0 +1,30 @@ > +.. SPDX-License-Identifier: BSD-3-Clause > + Copyright(c) 2018 Intel Corporation. > + Is copyright owner Intel? > +UDP Poll Mode Driver > +==================== > + > +UDP Poll Mode Driver (PMD) is a basic virtual driver useful for testing. > +It provides a simple bare encapsulation of Ethernet frames in a UDP > +socket. This is useful since the test driver application can > +easily open a local UDP socket and interact with a DPDK application. > +This can even be done inside a VM or container in automated test setup. > + > + > +Driver Configuration > +-------------------- > + > +The driver is a virtual device configured with the --vdev option. > +The device name must start with the net_udp prefix follwed by numbers followed > +or letters The name is unique for each device. Each device can have or letters. The ... > +multiple stream options and multiple devices can be used. > +Multiple device definitions can be arranged using multiple --vdev. > + > +Both local and remote address must be specified. Both IPv4 and IPv6 > +are supported examples: > + > +.. code-block:: console > + > + .//app/dpdk-testpmd -l 0-3 -n 4 \ > + --vdev 'net_udp0,local=127.0.0.1:9000,remote=192.0.2.1:9000', > + --vdev 'net_udp1,local=[:0],9000,remote=[2001:DB8::1]:9000' maybe good to document ipv6 should start with '['. (this is according to the below code) > diff --git a/drivers/net/meson.build b/drivers/net/meson.build > index 35bfa78dee66..36f2d9ed9b96 100644 > --- a/drivers/net/meson.build > +++ b/drivers/net/meson.build > @@ -56,6 +56,7 @@ drivers = [ > 'tap', > 'thunderx', > 'txgbe', > + 'udp', > 'vdev_netvsc', > 'vhost', > 'virtio', > diff --git a/drivers/net/udp/meson.build b/drivers/net/udp/meson.build > new file mode 100644 > index 000000000000..e7bfd843f4b2 > --- /dev/null > +++ b/drivers/net/udp/meson.build > @@ -0,0 +1,11 @@ > +# SPDX-License-Identifier: BSD-3-Clause > + > +if is_windows > + build = false > + reason = 'not supported on Windows' > + subdir_done() > +endif > + > +sources = files('rte_eth_udp.c') > + > +pmd_supports_disable_iova_as_pa = true > diff --git a/drivers/net/udp/rte_eth_udp.c b/drivers/net/udp/rte_eth_udp.c > new file mode 100644 > index 000000000000..8ce65721b3ec > --- /dev/null > +++ b/drivers/net/udp/rte_eth_udp.c > @@ -0,0 +1,728 @@ > +/* SPDX-License-Identifier: BSD-3-Clause > + * > + * Copyright (c) 2022 Microsoft Corp. > + * All rights reserved. > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +/* Which strings are valid kvargs for this driver */ > +#define ETH_UDP_LOCAL_ARG "local" > +#define ETH_UDP_REMOTE_ARG "remote" > + > +static int eth_udp_logtype; > +#define PMD_LOG(level, fmt, args...) \ > + rte_log(RTE_LOG_ ## level, eth_udp_logtype, \ > + "%s(): " fmt "\n", __func__, ##args) > + > +struct pmd_internals; > + > +struct udp_queue { > + struct rte_mempool *mb_pool; > + struct pmd_internals *internals; > + uint16_t queue_id; > + int sock_fd; > + > + uint64_t pkts; > + uint64_t bytes; > + uint64_t nobufs; > +}; > + > +struct pmd_internals { > + uint16_t port_id; > + int sock_fd; > + > + struct sockaddr_storage remote; > + struct sockaddr_storage local; > + struct rte_ether_addr eth_addr; > + > + struct udp_queue rx_queues[RTE_MAX_QUEUES_PER_PORT]; > + struct udp_queue tx_queues[RTE_MAX_QUEUES_PER_PORT]; > +}; > + > +static struct rte_eth_link pmd_link = { > + .link_speed = RTE_ETH_SPEED_NUM_10G, > + .link_duplex = RTE_ETH_LINK_FULL_DUPLEX, > + .link_status = RTE_ETH_LINK_DOWN, > + .link_autoneg = RTE_ETH_LINK_FIXED, > +}; > + > +static int > +parse_ipv6_address(const char *value, struct sockaddr_in6 *sin6) > +{ > + char *str = strdupa(value); > + char *endp; > + > + ++str; /* skip leading '[' */ > + endp = strchr(str, ']'); > + if (endp == NULL) { > + PMD_LOG(ERR, "missing closing ]"); > + return -EINVAL; > + } > + > + *endp++ = '\0'; > + sin6->sin6_family = AF_INET6; > + > + if (inet_pton(AF_INET6, ++str, &sin6->sin6_addr) != 1) { > + PMD_LOG(ERR, "invalid ipv6 address '%s'", str); > + return -EINVAL; > + } > + > + /* Handle [ff80::1]:999 as address and port */ > + if (*endp == ':') { > + sin6->sin6_port = htons(strtoul(endp + 1, NULL, 0)); > + } else if (*endp != '\0') { > + PMD_LOG(ERR, "incorrect ipv6 port syntax"); > + return -EINVAL; > + } > + return 0; > +} > + > +static int > +parse_ipv4_address(const char *value, struct sockaddr_in *sin) > +{ > + char *str = strdupa(value); > + char *endp; > + > + endp = strchr(str, ':'); > + if (endp) > + *endp++ = '\0'; > + > + memset(sin, 0, sizeof(*sin)); > + sin->sin_family = AF_INET; > + > + if (inet_pton(AF_INET, str, &sin->sin_addr) != 1) { > + PMD_LOG(ERR, "invalid ipv4 address '%s'", str); > + return -EINVAL; > + } > + > + if (endp != NULL) > + sin->sin_port = htons(strtoul(endp, NULL, 0)); > + > + return 0; > +} > + > +/* Addresses are given on Kvargs as: kvargs > + * 127.0.0.1:9000 > + * [::1]:9000 > + */ > +static int > +get_address_arg(const char *key, const char *value, void *sarg) > +{ > + if (value == NULL) > + return -EINVAL; > + > + PMD_LOG(DEBUG, "%s='%s'", key, value); > + > + if (*value == '[') > + return parse_ipv6_address(value, sarg); > + else > + return parse_ipv4_address(value, sarg); > +} > + > +/* Helper function to determine how many mbufs are needed per packet */ > +static uint16_t > +eth_mbuf_per_pkt(uint16_t port_id, > + struct rte_mempool *mb_pool) > +{ > + const struct rte_eth_dev *dev = &rte_eth_devices[port_id]; Not good to access to global device array, 'rte_eth_devices[]'. Instead, what do you think to have 'eth_dev->data' in "struct udp_queue" and pass it to this function? It is an option to have 'eth_dev' reference in "struct udp_queue" but that reference differs for primary and secondary process, that is why 'eth_dev->data' is safer. > + uint16_t buf_size = rte_pktmbuf_data_room_size(mb_pool); > + > + return (dev->data->mtu + buf_size - 1) / buf_size; > +} > + > + > +/* > + * Receive packets from socket into mbufs. > + * > + * In order to handle multiple packets at a time and scattered receive > + * this allocates the worst case number of buffers. > + * > + * If out of memory, or socket gives error returns 0. > + */ > +static uint16_t > +eth_udp_rx(void *queue, struct rte_mbuf **pkts, uint16_t nb_pkts) > +{ > + struct udp_queue *udp_q = queue; > + uint16_t port_id = udp_q->internals->port_id; > + struct rte_mempool *mpool = udp_q->mb_pool; > + unsigned int segs_per_pkt = eth_mbuf_per_pkt(port_id, mpool); > + unsigned int num_segs = nb_pkts * segs_per_pkt; > + struct rte_mbuf *bufs[num_segs]; > + struct iovec iovecs[num_segs]; > + struct mmsghdr msgs[nb_pkts]; > + unsigned int seg_idx = 0, nb_iovs = 0; > + uint64_t num_rx_bytes = 0; > + int ret; > + > + /* Allocate worst case number of buffers to be used. */ > + if (rte_pktmbuf_alloc_bulk(mpool, bufs, num_segs) != 0) { > + PMD_LOG(ERR, "alloc mbuf failed"); > + ++udp_q->nobufs; > + return 0; > + } > + > + /* Initialize the multi-packet headers and link the mbufs per packet */ > + memset(msgs, 0, sizeof(msgs)); > + for (uint16_t i = 0; i < nb_pkts; i++) { > + msgs[i].msg_hdr.msg_iov = &iovecs[nb_iovs]; > + msgs[i].msg_hdr.msg_iovlen = segs_per_pkt; > + > + for (unsigned int n = 0; n < segs_per_pkt; n++, nb_iovs++) { > + struct rte_mbuf *mb = bufs[nb_iovs]; > + > + iovecs[nb_iovs].iov_base = rte_pktmbuf_mtod(mb, void *); > + iovecs[nb_iovs].iov_len = rte_pktmbuf_tailroom(mb); > + } > + } > + assert(nb_iovs == num_segs); > + > + ret = recvmmsg(udp_q->sock_fd, msgs, nb_pkts, 0, NULL); > + if (ret < 0) { > + if (!(errno == EWOULDBLOCK || errno == EINTR)) > + PMD_LOG(ERR, "recv failed: %s", strerror(errno)); > + > + rte_pktmbuf_free_bulk(bufs, num_segs); > + return 0; > + } > + PMD_LOG(DEBUG, "recvmmsg returned %d", ret); > + It can be better to use 'RTE_LOG_DP' in datapath. > + /* Adjust mbuf length and segments based on result. */ > + for (int i = 0; i < ret; i++) { > + struct rte_mbuf **top = &pkts[i]; > + struct rte_mbuf *m0, *mb; > + unsigned int unfilled; > + size_t len; > + > + /* Number of bytes in this packet */ > + len = msgs[i].msg_len; > + num_rx_bytes += len; > + > + m0 = mb = bufs[seg_idx]; > + m0->pkt_len = len; > + m0->port = port_id; > + m0->nb_segs = 0; > + > + while (len > 0) { > + mb->data_len = RTE_MIN(len, > + rte_pktmbuf_tailroom(mb)); > + len -= mb->data_len; > + *top = mb; > + top = &mb->next; > + > + ++m0->nb_segs; > + mb = bufs[++seg_idx]; > + > + } > + *top = NULL; > + > + /* Drop rest of chain */ > + unfilled = segs_per_pkt - m0->nb_segs; > + if (unfilled > 0) { > + rte_pktmbuf_free_bulk(bufs + seg_idx, unfilled); > + seg_idx += unfilled; > + } > + } > + > + udp_q->pkts += ret; > + udp_q->bytes += num_rx_bytes; > + > + /* Free any unused buffers */ > + if (seg_idx < num_segs) > + rte_pktmbuf_free_bulk(bufs + seg_idx, num_segs - seg_idx); > + > + return ret; > +} > + > +/* > + * Send mbufs over UDP socket. > + */ > +static uint16_t > +eth_udp_tx(void *queue, struct rte_mbuf **bufs, uint16_t nb_bufs) > +{ > + struct udp_queue *udp_q = queue; > + struct iovec iovecs[nb_bufs * RTE_MBUF_MAX_NB_SEGS]; > + struct mmsghdr msgs[nb_bufs]; > + unsigned int iov_iter; > + int ret; > + > + memset(msgs, 0, sizeof(msgs)); > + iov_iter = 0; > + for (uint16_t i = 0; i < nb_bufs; i++) { > + struct rte_mbuf *mb = bufs[i]; > + unsigned int nsegs = mb->nb_segs; > + > + msgs[i].msg_hdr.msg_iov = &iovecs[iov_iter]; > + msgs[i].msg_hdr.msg_iovlen = nsegs; > + > + for (unsigned int n = 0; n < nsegs; n++) { > + iovecs[iov_iter].iov_base = rte_pktmbuf_mtod(mb, void *); > + iovecs[iov_iter].iov_len = rte_pktmbuf_tailroom(mb); > + iov_iter++; > + mb = mb->next; > + } > + assert(mb == NULL); > + } > + > + ret = sendmmsg(udp_q->sock_fd, msgs, nb_bufs, 0); > + if (ret < 0) { > + if (!(errno == EWOULDBLOCK || errno == EINTR)) > + PMD_LOG(ERR, "sendmmsg failed: %s", strerror(errno)); > + ret = 0; > + } else { > + uint64_t num_tx_bytes = 0; > + > + for (int i = 0; i < ret; i++) > + num_tx_bytes += msgs[i].msg_len; > + > + udp_q->pkts += ret; > + udp_q->bytes += num_tx_bytes; > + } > + > + if (ret < nb_bufs) > + rte_pktmbuf_free_bulk(bufs + ret, nb_bufs - ret); > + > + return ret; > +} > + > +static int > +eth_dev_configure(struct rte_eth_dev *dev __rte_unused) > +{ > + return 0; > +} > + > +static int > +eth_dev_start(struct rte_eth_dev *dev) > +{ > + dev->data->dev_link.link_status = RTE_ETH_LINK_UP; > + return 0; > +} > + > +static int > +eth_dev_stop(struct rte_eth_dev *dev) > +{ > + struct pmd_internals *internal = dev->data->dev_private; > + unsigned int i; > + > + > + for (i = 0; i < dev->data->nb_tx_queues; i++) > + internal->tx_queues[i].sock_fd = -1; > + > + for (i = 0; i < dev->data->nb_rx_queues; i++) { > + struct udp_queue *rxq = &internal->rx_queues[i]; > + > + close(rxq->sock_fd); User can do stop/start. If socket closed here, will it work with next start? Should the socket be closed in 'close()' dev_ops? > + rxq->sock_fd = -1; > + } > + > + dev->data->dev_link.link_status = RTE_ETH_LINK_DOWN; > + return 0; > +} > + > +static int create_socket(struct pmd_internals *internals) > +{ > + socklen_t addrlen; > + int family, sock_fd, on = 1; > + > + family = internals->local.ss_family; > + sock_fd = socket(family, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); > + if (sock_fd < 0) { > + PMD_LOG(ERR, "socket(): failed %s", strerror(errno)); > + return -1; > + } > + > + if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)) < 0) { > + PMD_LOG(ERR, "setsockopt(SO_REUSEPORT): failed %s", strerror(errno)); > + goto fail; > + } > + > + if (family == AF_INET6) > + addrlen = sizeof(struct sockaddr_in6); > + else > + addrlen = sizeof(struct sockaddr_in); > + > + > + /* if address family is not set, then local address not specified */ > + if (bind(sock_fd, (struct sockaddr *)&internals->local, addrlen) < 0) { > + PMD_LOG(ERR, "bind: failed %s", strerror(errno)); > + goto fail; > + } > + > + if (connect(sock_fd, (struct sockaddr *)&internals->remote, addrlen) < 0) { > + PMD_LOG(ERR, "connect: failed %s", strerror(errno)); > + goto fail; > + } > + > + /* Get actual local family to reuse same address */ > + addrlen = sizeof(internals->local); > + if (getsockname(sock_fd, (struct sockaddr *)&internals->local, &addrlen) < 0) { > + PMD_LOG(ERR, "getsockname failed %s", strerror(errno)); > + goto fail; > + } > + > + addrlen = sizeof(internals->remote); > + if (getpeername(sock_fd, (struct sockaddr *)&internals->remote, &addrlen) < 0) { > + PMD_LOG(ERR, "getsockname failed %s", strerror(errno)); > + goto fail; > + } > + > + return sock_fd; > + > +fail: > + close(sock_fd); > + return -1; > +} > + > +static int > +eth_rx_queue_setup(struct rte_eth_dev *dev, uint16_t rx_queue_id, > + uint16_t nb_rx_desc __rte_unused, > + unsigned int socket_id __rte_unused, > + const struct rte_eth_rxconf *rx_conf __rte_unused, > + struct rte_mempool *mb_pool) > +{ > + struct pmd_internals *internals = dev->data->dev_private; > + struct udp_queue *rx_q = &internals->rx_queues[rx_queue_id]; > + > + dev->data->rx_queues[rx_queue_id] = rx_q; > + rx_q->internals = internals; > + rx_q->queue_id = rx_queue_id; > + rx_q->mb_pool = mb_pool; > + > + if (rx_queue_id == 0) > + rx_q->sock_fd = internals->sock_fd; > + else > + rx_q->sock_fd = create_socket(internals); ah, I guess 'SO_REUSEPORT' requirement is because connecting to same port per Rx queue. > + > + return (rx_q->sock_fd < 0) ? -1 : 0; > +} > + > +static void > +eth_rx_queue_release(struct rte_eth_dev *dev, uint16_t rx_queue_id) > +{ > + struct pmd_internals *internals = dev->data->dev_private; > + struct udp_queue *rx_q = &internals->rx_queues[rx_queue_id]; > + > + if (rx_q->queue_id > 0) > + close(rx_q->sock_fd); > + > + rx_q->sock_fd = -1; > +} > + > +static int > +eth_tx_queue_setup(struct rte_eth_dev *dev, uint16_t tx_queue_id, > + uint16_t nb_tx_desc __rte_unused, > + unsigned int socket_id __rte_unused, > + const struct rte_eth_txconf *tx_conf __rte_unused) > +{ > + struct pmd_internals *internals = dev->data->dev_private; > + struct udp_queue *tx_q = &internals->tx_queues[tx_queue_id]; > + > + dev->data->tx_queues[tx_queue_id] = tx_q; > + tx_q->queue_id = tx_queue_id; > + tx_q->internals = internals; > + tx_q->sock_fd = internals->sock_fd; > + > + return 0; > +} > + > +static void > +eth_tx_queue_release(struct rte_eth_dev *dev, uint16_t tx_queue_id) > +{ > + struct pmd_internals *internals = dev->data->dev_private; > + struct udp_queue *tx_q = &internals->tx_queues[tx_queue_id]; > + > + tx_q->sock_fd = -1; > +} > + > +static int > +eth_mtu_set(struct rte_eth_dev *dev __rte_unused, uint16_t mtu __rte_unused) > +{ > + return 0; > +} > + > +static int > +eth_dev_info(struct rte_eth_dev *dev __rte_unused, > + struct rte_eth_dev_info *dev_info) > +{ > + dev_info->max_mac_addrs = 1; > + dev_info->max_rx_pktlen = UINT16_MAX; > + dev_info->max_rx_queues = RTE_MAX_QUEUES_PER_PORT; > + dev_info->max_tx_queues = RTE_MAX_QUEUES_PER_PORT; > + dev_info->min_rx_bufsize = 0; > + dev_info->tx_offload_capa = RTE_ETH_TX_OFFLOAD_MULTI_SEGS; > + dev_info->rx_offload_capa = RTE_ETH_RX_OFFLOAD_SCATTER; > + > + return 0; > +} > + > +static int > +eth_link_update(struct rte_eth_dev *dev __rte_unused, > + int wait_to_complete __rte_unused) > +{ > + return 0; > +} > + > +static int > +eth_mac_address_set(__rte_unused struct rte_eth_dev *dev, > + __rte_unused struct rte_ether_addr *addr) > +{ > + return 0; > +} > + > +static int > +eth_stats_get(struct rte_eth_dev *dev, struct rte_eth_stats *stats) > +{ > + const struct pmd_internals *internal = dev->data->dev_private; > + unsigned int i, num_stats; > + > + num_stats = RTE_MIN((unsigned)RTE_ETHDEV_QUEUE_STAT_CNTRS, > + dev->data->nb_rx_queues); queue stats shouldn't be part of basic stats anymore, in that case no need to take RTE_ETHDEV_QUEUE_STAT_CNTRS into account. > + for (i = 0; i < num_stats; i++) { > + const struct udp_queue *q = &internal->rx_queues[i]; > + > + stats->q_ipackets[i] = q->pkts; > + stats->ipackets += q->pkts; > + stats->q_ibytes[i] += q->bytes; > + stats->ibytes += q->bytes; > + stats->rx_nombuf += q->nobufs; > + } > + > + num_stats = RTE_MIN((unsigned)RTE_ETHDEV_QUEUE_STAT_CNTRS, > + dev->data->nb_tx_queues); > + for (i = 0; i < num_stats; i++) { > + const struct udp_queue *q = &internal->tx_queues[i]; > + > + stats->q_opackets[i] = q->pkts; > + stats->opackets += q->pkts; > + stats->q_obytes[i] += q->bytes; > + stats->obytes += q->bytes; > + } > + > + return 0; > +} > + > +static int > +eth_stats_reset(struct rte_eth_dev *dev) > +{ > + struct pmd_internals *internal = dev->data->dev_private; > + unsigned int i; > + > + for (i = 0; i < RTE_DIM(internal->rx_queues); i++) { > + struct udp_queue *q = &internal->rx_queues[i]; > + > + q->pkts = 0; > + q->bytes = 0; > + q->nobufs = 0; > + } > + > + > + for (i = 0; i < RTE_DIM(internal->tx_queues); i++) { > + struct udp_queue *q = &internal->tx_queues[i]; > + > + q->pkts = 0; > + q->bytes = 0; > + q->nobufs = 0; > + } > + > + return 0; > +} > + > +static const struct eth_dev_ops ops = { > + .dev_start = eth_dev_start, > + .dev_stop = eth_dev_stop, > + .dev_configure = eth_dev_configure, > + .dev_infos_get = eth_dev_info, > + .rx_queue_setup = eth_rx_queue_setup, > + .tx_queue_setup = eth_tx_queue_setup, > + .rx_queue_release = eth_rx_queue_release, > + .tx_queue_release = eth_tx_queue_release, > + .mtu_set = eth_mtu_set, > + .link_update = eth_link_update, > + .mac_addr_set = eth_mac_address_set, > + .stats_get = eth_stats_get, > + .stats_reset = eth_stats_reset, > +}; > + > +static int > +parse_parameters(struct pmd_internals *internals, const char *params) > +{ > + static const char * const valid_args[] = { > + "local", "remote", NULL > + }; > + struct rte_kvargs *kvlist; > + int ret; > + > + if (params == NULL && params[0] == '\0') > + return 0; > + > + PMD_LOG(INFO, "parameters \"%s\"", params); > + kvlist = rte_kvargs_parse(params, valid_args); > + if (kvlist == NULL) > + return -1; > + > + ret = rte_kvargs_process(kvlist, ETH_UDP_LOCAL_ARG, > + &get_address_arg, &internals->local); > + if (ret < 0) > + goto out; > + > + ret = rte_kvargs_process(kvlist, ETH_UDP_REMOTE_ARG, > + &get_address_arg, &internals->remote); > + > +out: > + rte_kvargs_free(kvlist); > + return ret; > +} > + > +static int > +validate_parameters(struct pmd_internals *internals) > +{ > + int family = internals->remote.ss_family; > + > + if (family == AF_UNSPEC) { > + PMD_LOG(ERR, "remote address required"); > + return -EINVAL; > + } > + > + /* > + * if no local address is specified, > + * then use same port and and let kernel choose. > + */ > + if (internals->local.ss_family == AF_UNSPEC) { > + internals->local.ss_family = family; > + } else if (internals->local.ss_family != family) { > + PMD_LOG(ERR, "Local and remote address family differ"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int > +rte_pmd_udp_probe(struct rte_vdev_device *dev) > +{ > + struct pmd_internals *internals; > + struct rte_eth_dev *eth_dev; > + struct rte_eth_dev_data *data; > + const char *name; > + int ret; > + > + name = rte_vdev_device_name(dev); > + > + PMD_LOG(INFO, "Initializing pmd_udp for %s", name); > + > + if (rte_eal_process_type() == RTE_PROC_SECONDARY) { > + PMD_LOG(ERR, "Secondary not supported"); > + return -ENOTSUP; > + } > + > + eth_dev = rte_eth_vdev_allocate(dev, sizeof(*internals)); > + if (!eth_dev) > + return -ENOMEM; > + > + internals = eth_dev->data->dev_private; > + internals->port_id = eth_dev->data->port_id; > + > + ret = parse_parameters(internals, rte_vdev_device_args(dev)); > + if (ret < 0) > + goto fail; > + > + ret = validate_parameters(internals); > + if (ret < 0) > + goto fail; > + > + /* > + * Note: first socket is used for transmit and for > + * receive queue 0. > + */ > + internals->sock_fd = create_socket(internals); > + if (internals->sock_fd < 0) { > + ret = errno ? -errno : -EINVAL; > + goto fail; > + } > + > + rte_eth_random_addr(internals->eth_addr.addr_bytes); > + > + data = eth_dev->data; > + data->dev_link = pmd_link; > + data->mac_addrs = &internals->eth_addr; > + data->promiscuous = 1; > + data->all_multicast = 1; > + > + eth_dev->dev_ops = &ops; > + eth_dev->rx_pkt_burst = eth_udp_rx; > + eth_dev->tx_pkt_burst = eth_udp_tx; > + > + rte_eth_dev_probing_finish(eth_dev); > + > +fail: > + if (ret != 0) { > + eth_dev->data->mac_addrs = NULL; > + rte_eth_dev_release_port(eth_dev); > + } > + > + return ret; > +} > + > +static int > +rte_pmd_udp_remove(struct rte_vdev_device *dev) > +{ > + struct rte_eth_dev *eth_dev = NULL; > + const char *name; > + > + name = rte_vdev_device_name(dev); > + > + PMD_LOG(INFO, "Closing udp ethdev %s", name); > + > + /* find the ethdev entry */ > + eth_dev = rte_eth_dev_allocated(name); > + if (eth_dev == NULL) > + return -1; > + > + /* mac_addrs must not be freed alone because part of dev_private */ > + if (rte_eal_process_type() == RTE_PROC_PRIMARY) > + eth_dev->data->mac_addrs = NULL; > + > + rte_eth_dev_release_port(eth_dev); > + > + return 0; > +} > + > +static struct rte_vdev_driver pmd_udp_drv = { > + .probe = rte_pmd_udp_probe, > + .remove = rte_pmd_udp_remove, > +}; > + > +RTE_PMD_REGISTER_VDEV(net_udp, pmd_udp_drv); > +RTE_PMD_REGISTER_ALIAS(net_udp, eth_udp); alias is for old drivers, new ones shouldn't have it > +RTE_PMD_REGISTER_PARAM_STRING(net_udp, > + "local=" > + "remote="); > + > +RTE_INIT(eth_udp_init_log) > +{ > + eth_udp_logtype = rte_log_register("pmd.net.udp"); > + if (eth_udp_logtype >= 0) > + rte_log_set_level(eth_udp_logtype, RTE_LOG_NOTICE); > +} There is 'RTE_LOG_REGISTER_DEFAULT' macro to help above > diff --git a/drivers/net/udp/version.map b/drivers/net/udp/version.map > new file mode 100644 > index 000000000000..78c3585d7c6b > --- /dev/null > +++ b/drivers/net/udp/version.map > @@ -0,0 +1,3 @@ > +DPDK_23 { > + local: *; > +};