get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2218457,
    "url": "http://patchwork.ozlabs.org/api/patches/2218457/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/openvswitch/patch/20260401091318.2671624-9-elibr@nvidia.com/",
    "project": {
        "id": 47,
        "url": "http://patchwork.ozlabs.org/api/projects/47/?format=api",
        "name": "Open vSwitch",
        "link_name": "openvswitch",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "git@github.com:openvswitch/ovs.git",
        "webscm_url": "https://github.com/openvswitch/ovs",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20260401091318.2671624-9-elibr@nvidia.com>",
    "list_archive_url": null,
    "date": "2026-04-01T09:13:15",
    "name": "[ovs-dev,v3,08/11] netdev-dpdk: Refactor common functions for reuse by netdev-doca.",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "449e81c66079d83a2791c77ac95c95d4c147a6b7",
    "submitter": {
        "id": 79848,
        "url": "http://patchwork.ozlabs.org/api/people/79848/?format=api",
        "name": "Eli Britstein",
        "email": "elibr@nvidia.com"
    },
    "delegate": {
        "id": 75123,
        "url": "http://patchwork.ozlabs.org/api/users/75123/?format=api",
        "username": "echaudron",
        "first_name": "Eelco",
        "last_name": "Chaudron",
        "email": "echaudro@redhat.com"
    },
    "mbox": "http://patchwork.ozlabs.org/project/openvswitch/patch/20260401091318.2671624-9-elibr@nvidia.com/mbox/",
    "series": [
        {
            "id": 498297,
            "url": "http://patchwork.ozlabs.org/api/series/498297/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/openvswitch/list/?series=498297",
            "date": "2026-04-01T09:13:07",
            "name": "netdev-doca",
            "version": 3,
            "mbox": "http://patchwork.ozlabs.org/series/498297/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2218457/comments/",
    "check": "success",
    "checks": "http://patchwork.ozlabs.org/api/patches/2218457/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<ovs-dev-bounces@openvswitch.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "dev@openvswitch.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@legolas.ozlabs.org",
            "ovs-dev@lists.linuxfoundation.org"
        ],
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256\n header.s=selector2 header.b=QNAcx5d5;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=140.211.166.138; helo=smtp1.osuosl.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=patchwork.ozlabs.org)",
            "smtp1.osuosl.org;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key,\n unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256\n header.s=selector2 header.b=QNAcx5d5",
            "smtp2.osuosl.org;\n dmarc=pass (p=reject dis=none) header.from=nvidia.com",
            "smtp2.osuosl.org; dkim=pass (2048-bit key,\n unprotected) header.d=Nvidia.com header.i=@Nvidia.com header.a=rsa-sha256\n header.s=selector2 header.b=QNAcx5d5"
        ],
        "Received": [
            "from smtp1.osuosl.org (smtp1.osuosl.org [140.211.166.138])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4flzrH5WN7z1yGH\n\tfor <incoming@patchwork.ozlabs.org>; Wed, 01 Apr 2026 20:16:31 +1100 (AEDT)",
            "from localhost (localhost [127.0.0.1])\n\tby smtp1.osuosl.org (Postfix) with ESMTP id DC31E81FAF;\n\tWed,  1 Apr 2026 09:16:28 +0000 (UTC)",
            "from smtp1.osuosl.org ([127.0.0.1])\n by localhost (smtp1.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id tdXSPAxPum24; Wed,  1 Apr 2026 09:16:25 +0000 (UTC)",
            "from lists.linuxfoundation.org (lf-lists.osuosl.org\n [IPv6:2605:bc80:3010:104::8cd3:938])\n\tby smtp1.osuosl.org (Postfix) with ESMTPS id 8AEAD821B7;\n\tWed,  1 Apr 2026 09:16:25 +0000 (UTC)",
            "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id 43741C003D;\n\tWed,  1 Apr 2026 09:16:25 +0000 (UTC)",
            "from smtp2.osuosl.org (smtp2.osuosl.org [IPv6:2605:bc80:3010::133])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 036BEC003D\n for <dev@openvswitch.org>; Wed,  1 Apr 2026 09:16:23 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n by smtp2.osuosl.org (Postfix) with ESMTP id 837704088E\n for <dev@openvswitch.org>; Wed,  1 Apr 2026 09:15:26 +0000 (UTC)",
            "from smtp2.osuosl.org ([127.0.0.1])\n by localhost (smtp2.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id BiPa-qdss1lX for <dev@openvswitch.org>;\n Wed,  1 Apr 2026 09:15:21 +0000 (UTC)",
            "from PH8PR06CU001.outbound.protection.outlook.com\n (mail-westus3azlp170120001.outbound.protection.outlook.com\n [IPv6:2a01:111:f403:c107::1])\n by smtp2.osuosl.org (Postfix) with ESMTPS id 9A3994090D\n for <dev@openvswitch.org>; Wed,  1 Apr 2026 09:15:20 +0000 (UTC)",
            "from PH7PR02CA0003.namprd02.prod.outlook.com (2603:10b6:510:33d::31)\n by DM6PR12MB4402.namprd12.prod.outlook.com (2603:10b6:5:2a5::18) with\n Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.9769.16; Wed, 1 Apr\n 2026 09:15:07 +0000",
            "from SA2PEPF000015C8.namprd03.prod.outlook.com\n (2603:10b6:510:33d:cafe::e8) by PH7PR02CA0003.outlook.office365.com\n (2603:10b6:510:33d::31) with Microsoft SMTP Server (version=TLS1_3,\n cipher=TLS_AES_256_GCM_SHA384) id 15.20.9745.30 via Frontend Transport; Wed,\n 1 Apr 2026 09:14:52 +0000",
            "from mail.nvidia.com (216.228.117.161) by\n SA2PEPF000015C8.mail.protection.outlook.com (10.167.241.198) with Microsoft\n SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id\n 15.20.9769.17 via Frontend Transport; Wed, 1 Apr 2026 09:15:07 +0000",
            "from rnnvmail201.nvidia.com (10.129.68.8) by mail.nvidia.com\n (10.129.200.67) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Wed, 1 Apr\n 2026 02:14:49 -0700",
            "from nvidia.com (10.126.231.35) by rnnvmail201.nvidia.com\n (10.129.68.8) with Microsoft SMTP Server (version=TLS1_2,\n cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.2.2562.20; Wed, 1 Apr\n 2026 02:14:46 -0700"
        ],
        "X-Virus-Scanned": [
            "amavis at osuosl.org",
            "amavis at osuosl.org"
        ],
        "X-Comment": "SPF check N/A for local connections -\n client-ip=2605:bc80:3010:104::8cd3:938; helo=lists.linuxfoundation.org;\n envelope-from=ovs-dev-bounces@openvswitch.org; receiver=<UNKNOWN> ",
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 smtp1.osuosl.org 8AEAD821B7",
            "OpenDKIM Filter v2.11.0 smtp2.osuosl.org 9A3994090D"
        ],
        "Received-SPF": [
            "Pass (mailfrom) identity=mailfrom;\n client-ip=2a01:111:f403:c107::1;\n helo=ph8pr06cu001.outbound.protection.outlook.com;\n envelope-from=elibr@nvidia.com; receiver=<UNKNOWN>",
            "Pass (protection.outlook.com: domain of nvidia.com designates\n 216.228.117.161 as permitted sender) receiver=protection.outlook.com;\n client-ip=216.228.117.161; helo=mail.nvidia.com; pr=C"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 smtp2.osuosl.org 9A3994090D",
        "ARC-Seal": "i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;\n b=dm6gegaf+nwhGozpWgyHbhlOD50z3xJ3bMxjkorGdO26Wytx0WR4uw9VsKvK8pECCBJ+jEuVp6U8sjdGe69lJYAES5xdGTcxZJW6CgofALygMFf/tN4w7bF5beqO6z51vUbH4bZL8Q880JjErMENtD1Q0zE+va+nBLpMcL30XHIHDmBKB8w7VhG1RGmYUgUOpc8S3oaW0l38wC+gZymFfbo+drxVqVbqjs+sJ2QE5Y+KZRSl9XBoVTy8CyLKObwm3VwTTdYs5VH8J7XCWBnztu7YYNj8uFI5ggTyHsuTdXFNzGvDLgCZtAhrcf997MjKzmld7LFtAAsNAQLMozie6Q==",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;\n s=arcselector10001;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;\n bh=PUG5x4PHuGJiplWjsEIfr6jDi5vlD5HRom7yZ6uoFN8=;\n b=dh4AQ6gN6PtMIlPRQPqxpYpI9S5OVYln8T/HpcrSUoSnuJrYO61gPVXD/YgosgDmP65navRXGMrUZdC14/9x/zpBxeojbWvr4pMMsisWLFEIwRS+bvyfdUQFAqPAErcn+wBmb4H2LdQfeWAoiyJd8x07XBTa80losI+vIsdKRA07qqKt/ERXRu53FKbrPgpuNVrs/sCq7Ngl5UT33IVV9pA1OxtAqyurwHpO91zJFXy9Bi+To/9cbeTlGecc1jok8Bai+E7SsIAa2He1vKzZdW8Nf9fHyNPQti1uvyKhhqAvuCRiM6ai+blChidzETG8tIoxI9Grjog4DPk7FNDw3Q==",
        "ARC-Authentication-Results": "i=1; mx.microsoft.com 1; spf=pass (sender ip is\n 216.228.117.161) smtp.rcpttodomain=openvswitch.org smtp.mailfrom=nvidia.com;\n dmarc=pass (p=reject sp=reject pct=100) action=none header.from=nvidia.com;\n dkim=none (message not signed); arc=none (0)",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=Nvidia.com;\n s=selector2;\n h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;\n bh=PUG5x4PHuGJiplWjsEIfr6jDi5vlD5HRom7yZ6uoFN8=;\n b=QNAcx5d5OdMOc5ik0PF/q3BCNMsOx1sdvdp4JUyxqHlG7Q8s7J+YD288bBWILdwWhXjK50ILFc2SSDeAxSWq3sLrraOE103XEc2k1km5OmlONFWzz2omgb9UiI4mELPEr1LA7J4eQFdvstygvxjrT/zVIHGePcgm7wUwqOAUW3o/2vMeWPEBmPvKYIsFzq/n2Db8XUhBt6csX+Ey2TAHIGOoAPsqKHOQn1r4XV5XZZIMfL9bOf7hfvj4EuTSXY1/IzfeH6X6BiCWjWiARGlDldLZz5P232XOefhKcGShWmHBf4k3v8JrvG71EOVzxIHk+jIMZbPtVwpQhjkfXO0S5w==",
        "X-MS-Exchange-Authentication-Results": "spf=pass (sender IP is 216.228.117.161)\n smtp.mailfrom=nvidia.com;\n dkim=none (message not signed)\n header.d=none;dmarc=pass action=none header.from=nvidia.com;",
        "From": "Eli Britstein <elibr@nvidia.com>",
        "To": "<dev@openvswitch.org>",
        "Date": "Wed, 1 Apr 2026 12:13:15 +0300",
        "Message-ID": "<20260401091318.2671624-9-elibr@nvidia.com>",
        "X-Mailer": "git-send-email 2.34.1",
        "In-Reply-To": "<20260401091318.2671624-1-elibr@nvidia.com>",
        "References": "<20260401091318.2671624-1-elibr@nvidia.com>",
        "MIME-Version": "1.0",
        "X-Originating-IP": "[10.126.231.35]",
        "X-ClientProxiedBy": "rnnvmail203.nvidia.com (10.129.68.9) To\n rnnvmail201.nvidia.com (10.129.68.8)",
        "X-EOPAttributedMessage": "0",
        "X-MS-PublicTrafficType": "Email",
        "X-MS-TrafficTypeDiagnostic": "SA2PEPF000015C8:EE_|DM6PR12MB4402:EE_",
        "X-MS-Office365-Filtering-Correlation-Id": "7616278f-2192-4484-27a3-08de8fcf2acc",
        "X-MS-Exchange-SenderADCheck": "1",
        "X-MS-Exchange-AntiSpam-Relay": "0",
        "X-Microsoft-Antispam": "BCL:0;\n ARA:13230040|82310400026|1800799024|376014|36860700016|18002099003|22082099003|56012099003;",
        "X-Microsoft-Antispam-Message-Info": "\n Q6d80iXL/Iin6YGOR5OU+ma2cbNjlnmuD+SCJ1FYO4kgPND8aBBi0YOnzu2slOiPp7z8oNbZPr7i06AVfqf4qPfHVzx7OtQnT29UcDIE78t17E+GqrtwMPR57BPkMl/gdDq4quGuRle/jJ9AsbTdhWKz7ZpA9ThOG3lAVHcT915JfMuhuAYM0iPfEVoSw1sfCGbr5vD2eZgM9R8kcMmIedTfMe2ECjrzU3+W8PQIKAXXZd8wqco0lPSvj8jnRj+JreoyrlUPyUZkgZSd1i5YrDoYGUoVZ5Hn5ye2y3iE4vPj9cPTYQ0vYJLj+eF87dOcSk8kw9jClrLQ9bbp+C/6ZGzZpuLSsr5z59bb966CGg/GtnwMJp52WEm471qy3OrZWX1D4sD+2CruRzu0uz5QeddGWX8GGVAtMUUqebrwioQi2u6mY/+7qBucpoah9M1DaPUzOSzQONWxeN9WtGXsTirtABzhyHs4hmZHhmZz8MOlizuwYagMkEroa3UpylvZxQd/WAo9t/LANL7RGTuYltZsQBWBE9wecj3Mn1RzdsCnJjQUq18UKwAP4l1IQfxjb4MVzSulWVca4mEYHtqQUqShec/kipK9F8kJxhprAyQ+P/g+C9dX5yNo4QITRzgBjoEQeUtJs0D+bpw5fUPxIgwsk8djjcMIXouw7pRgITCj1mLim5C+nHjUsdlhp7SPLJznukOqO5L+gBFJF9L/iol83PqNM4WSVmrDckbf41yiIcdHwBEMnbWnhGoH0Wjwad6f7kyxLwhbNFbB4OcVCA==",
        "X-Forefront-Antispam-Report": "CIP:216.228.117.161; CTRY:US; LANG:en; SCL:1;\n SRV:;\n IPV:NLI; SFV:NSPM; H:mail.nvidia.com; PTR:dc6edge2.nvidia.com; CAT:NONE;\n SFS:(13230040)(82310400026)(1800799024)(376014)(36860700016)(18002099003)(22082099003)(56012099003);\n DIR:OUT; SFP:1101;",
        "X-MS-Exchange-AntiSpam-MessageData-ChunkCount": "1",
        "X-MS-Exchange-AntiSpam-MessageData-0": "\n v5av7yBdrqelU2+bFTjxGR6eDB6pppcEssb3N3frETLhYAZZLWRtDOxjEvq3Nm5pYUtc9FX0LyU1nt3wCNbMQllDpq8BcHus94JP7NFsI/Q6H6fFgIFaapkPDoPGm17BiYngWfLC4StofPJsMIAMfy2RI2XKUxcmA5SXcikvzzoS4ZWX64A5F7aDvDZlfz2CMjVsAmUDHOgIwAfw0ZfDFFsJnZsjRnC14koWa2oQMTghy0VXrBwSovRB4xrpXBaugBEmWjsqGS2X+yJyKX1meq3KofLJRhUFeWZNlGDgjgLSyly1x9dyGsR9WE8/++MvpjmxXIoixUrjLN82VODxEma06h/m6+hQGEIKekXoGOyv2UGANQJAqV5pTtzg7NnNQVThkfO3s6rNW9BuS5N0kd6kn8Y/GhJqrtcIAnj+q3sZodaVRXLmA8NYIx1y30A0",
        "X-OriginatorOrg": "Nvidia.com",
        "X-MS-Exchange-CrossTenant-OriginalArrivalTime": "01 Apr 2026 09:15:07.0726 (UTC)",
        "X-MS-Exchange-CrossTenant-Network-Message-Id": "\n 7616278f-2192-4484-27a3-08de8fcf2acc",
        "X-MS-Exchange-CrossTenant-Id": "43083d15-7273-40c1-b7db-39efd9ccc17a",
        "X-MS-Exchange-CrossTenant-OriginalAttributedTenantConnectingIp": "\n TenantId=43083d15-7273-40c1-b7db-39efd9ccc17a; Ip=[216.228.117.161];\n Helo=[mail.nvidia.com]",
        "X-MS-Exchange-CrossTenant-AuthSource": "\n SA2PEPF000015C8.namprd03.prod.outlook.com",
        "X-MS-Exchange-CrossTenant-AuthAs": "Anonymous",
        "X-MS-Exchange-CrossTenant-FromEntityHeader": "HybridOnPrem",
        "X-MS-Exchange-Transport-CrossTenantHeadersStamped": "DM6PR12MB4402",
        "Subject": "[ovs-dev] [PATCH v3 08/11] netdev-dpdk: Refactor common functions\n for reuse by netdev-doca.",
        "X-BeenThere": "ovs-dev@openvswitch.org",
        "X-Mailman-Version": "2.1.30",
        "Precedence": "list",
        "List-Id": "<ovs-dev.openvswitch.org>",
        "List-Unsubscribe": "<https://mail.openvswitch.org/mailman/options/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=unsubscribe>",
        "List-Archive": "<http://mail.openvswitch.org/pipermail/ovs-dev/>",
        "List-Post": "<mailto:ovs-dev@openvswitch.org>",
        "List-Help": "<mailto:ovs-dev-request@openvswitch.org?subject=help>",
        "List-Subscribe": "<https://mail.openvswitch.org/mailman/listinfo/ovs-dev>,\n <mailto:ovs-dev-request@openvswitch.org?subject=subscribe>",
        "Cc": "Eli Britstein <elibr@nvidia.com>, Ilya Maximets <i.maximets@ovn.org>,\n David Marchand <david.marchand@redhat.com>, Maor Dickman <maord@nvidia.com>",
        "Content-Type": "text/plain; charset=\"us-ascii\"",
        "Content-Transfer-Encoding": "7bit",
        "Errors-To": "ovs-dev-bounces@openvswitch.org",
        "Sender": "\"dev\" <ovs-dev-bounces@openvswitch.org>"
    },
    "content": "Refactor common functions from netdev-dpdk to be declared in\nnetdev-dpdk-private to be reused by netdev-doca.\n\nSigned-off-by: Eli Britstein <elibr@nvidia.com>\n---\n lib/netdev-dpdk-private.h | 108 ++++++\n lib/netdev-dpdk.c         | 692 +++++++++++++++++++++-----------------\n 2 files changed, 492 insertions(+), 308 deletions(-)",
    "diff": "diff --git a/lib/netdev-dpdk-private.h b/lib/netdev-dpdk-private.h\nindex 083ddacb3..1b33c27a4 100644\n--- a/lib/netdev-dpdk-private.h\n+++ b/lib/netdev-dpdk-private.h\n@@ -64,6 +64,16 @@ extern const struct rte_eth_conf port_conf;\n typedef uint16_t dpdk_port_t;\n #define DPDK_PORT_ID_FMT \"%\"PRIu16\n \n+struct dp_packet;\n+struct dp_packet_batch;\n+struct eth_addr;\n+struct netdev;\n+struct netdev_stats;\n+struct rte_eth_xstat;\n+struct rte_eth_xstat_name;\n+struct smap;\n+enum netdev_features;\n+\n /* Enums. */\n \n enum dpdk_hw_ol_features {\n@@ -84,6 +94,11 @@ enum dpdk_hw_ol_features {\n \n /* Structs. */\n \n+struct netdev_dpdk_watchdog_params {\n+    struct ovs_mutex *mutex;\n+    struct ovs_list *list;\n+};\n+\n #ifndef NETDEV_DPDK_TX_Q_TYPE\n #error \"NETDEV_DPDK_TX_Q_TYPE must be defined before\"  \\\n        \"including netdev-dpdk-private.h\"\n@@ -104,6 +119,12 @@ struct netdev_rxq_dpdk {\n     dpdk_port_t port_id;\n };\n \n+static inline struct netdev_rxq_dpdk *\n+netdev_rxq_dpdk_cast(const struct netdev_rxq *rxq)\n+{\n+    return CONTAINER_OF(rxq, struct netdev_rxq_dpdk, up);\n+}\n+\n struct netdev_dpdk_common {\n     PADDED_MEMBERS_CACHELINE_MARKER(CACHE_LINE_SIZE, cacheline0,\n         uint16_t port_id;\n@@ -179,4 +200,91 @@ dpdk_dev_is_started(struct netdev_dpdk_common *common)\n     return started;\n }\n \n+/* Common functions shared between netdev-dpdk and netdev-doca. */\n+\n+/* Type-independent helpers. */\n+struct rte_mempool *netdev_dpdk_mp_create_pool(const char *pool_name,\n+                                               uint32_t n_mbufs,\n+                                               uint32_t mbuf_size,\n+                                               int socket_id);\n+uint32_t netdev_dpdk_buf_size(int mtu);\n+size_t netdev_dpdk_copy_batch_to_mbuf(struct netdev_dpdk_common *common,\n+                                      struct dp_packet_batch *batch);\n+const char *netdev_dpdk_link_speed_to_str__(uint32_t link_speed);\n+void netdev_dpdk_mbuf_dump(const char *prefix, const char *message,\n+                           const struct rte_mbuf *mbuf);\n+\n+/* Functions operating on struct netdev_dpdk_common. */\n+void netdev_dpdk_detect_hw_ol_features(struct netdev_dpdk_common *common,\n+                                       const struct rte_eth_dev_info *info);\n+void netdev_dpdk_build_port_conf(struct netdev_dpdk_common *common,\n+                                 const struct rte_eth_dev_info *info,\n+                                 struct rte_eth_conf *conf);\n+void netdev_dpdk_check_link_status(struct netdev_dpdk_common *common);\n+\n+void *netdev_dpdk_watchdog(void *params);\n+\n+void netdev_dpdk_update_netdev_flags(struct netdev_dpdk_common *common);\n+void netdev_dpdk_clear_xstats(struct netdev_dpdk_common *common);\n+void netdev_dpdk_configure_xstats(struct netdev_dpdk_common *common);\n+void netdev_dpdk_set_rxq_config(struct netdev_dpdk_common *common,\n+                                const struct smap *args);\n+int netdev_dpdk_prep_hwol_batch(struct netdev_dpdk_common *common,\n+                                struct rte_mbuf **pkts, int pkt_cnt);\n+int netdev_dpdk_filter_packet_len(struct netdev_dpdk_common *common,\n+                                  struct rte_mbuf **pkts, int pkt_cnt);\n+int netdev_dpdk_eth_tx_burst(struct netdev_dpdk_common *common,\n+                             dpdk_port_t port_id, int qid,\n+                             struct rte_mbuf **pkts, int cnt);\n+void netdev_dpdk_get_config_common(struct netdev_dpdk_common *common,\n+                                   struct smap *args);\n+struct netdev_dpdk_common *\n+netdev_dpdk_lookup_by_port_id__(dpdk_port_t port_id,\n+                                struct ovs_list *list);\n+dpdk_port_t netdev_dpdk_get_port_by_devargs(const char *devargs);\n+\n+/* Rxq ops shared between dpdk and doca. */\n+struct netdev_rxq *netdev_dpdk_rxq_alloc(void);\n+int netdev_dpdk_rxq_construct(struct netdev_rxq *rxq);\n+void netdev_dpdk_rxq_destruct(struct netdev_rxq *rxq);\n+void netdev_dpdk_rxq_dealloc(struct netdev_rxq *rxq);\n+\n+/* Netdev provider ops usable by both dpdk and doca. */\n+\n+int netdev_dpdk_get_numa_id(const struct netdev *netdev);\n+int netdev_dpdk_set_tx_multiq(struct netdev *netdev, unsigned int n_txq);\n+int netdev_dpdk_set_etheraddr__(struct netdev_dpdk_common *common,\n+                                const struct eth_addr mac);\n+int netdev_dpdk_update_flags(struct netdev *netdev,\n+                             enum netdev_flags off, enum netdev_flags on,\n+                             enum netdev_flags *old_flagsp);\n+int netdev_dpdk_update_flags__(struct netdev_dpdk_common *common,\n+                               enum netdev_flags off, enum netdev_flags on,\n+                               enum netdev_flags *old_flagsp);\n+int netdev_dpdk_set_etheraddr(struct netdev *netdev,\n+                              const struct eth_addr mac);\n+int netdev_dpdk_get_etheraddr(const struct netdev *netdev,\n+                              struct eth_addr *mac);\n+int netdev_dpdk_get_mtu(const struct netdev *netdev, int *mtup);\n+int netdev_dpdk_get_ifindex(const struct netdev *netdev);\n+int netdev_dpdk_get_carrier(const struct netdev *netdev, bool *carrier);\n+long long int netdev_dpdk_get_carrier_resets(const struct netdev *netdev);\n+int netdev_dpdk_set_miimon(struct netdev *netdev, long long int interval);\n+int netdev_dpdk_get_speed(const struct netdev *netdev, uint32_t *current,\n+                          uint32_t *max);\n+int netdev_dpdk_get_features(const struct netdev *netdev,\n+                             enum netdev_features *current,\n+                             enum netdev_features *advertised,\n+                             enum netdev_features *supported,\n+                             enum netdev_features *peer);\n+void netdev_dpdk_convert_xstats(struct netdev_stats *stats,\n+                                const struct rte_eth_xstat *xstats,\n+                                const struct rte_eth_xstat_name *names,\n+                                const unsigned int size);\n+int netdev_dpdk_get_stats(const struct netdev *netdev,\n+                          struct netdev_stats *stats);\n+int netdev_dpdk_get_status__(const struct netdev *netdev,\n+                             struct ovs_mutex *dev_mutex,\n+                             struct smap *args);\n+\n #endif /* NETDEV_DPDK_PRIVATE_H */\ndiff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c\nindex 2562eb4b4..dbf988de4 100644\n--- a/lib/netdev-dpdk.c\n+++ b/lib/netdev-dpdk.c\n@@ -452,17 +452,11 @@ static void netdev_dpdk_vhost_destruct(struct netdev *netdev);\n \n static int netdev_dpdk_get_sw_custom_stats(const struct netdev *,\n                                            struct netdev_custom_stats *);\n-static void netdev_dpdk_configure_xstats(struct netdev_dpdk_common *common);\n-static void netdev_dpdk_clear_xstats(struct netdev_dpdk_common *common);\n-\n int netdev_dpdk_get_vid(const struct netdev_dpdk *dev);\n \n struct ingress_policer *\n netdev_dpdk_get_ingress_policer(const struct netdev_dpdk *dev);\n \n-static void netdev_dpdk_mbuf_dump(const char *prefix, const char *message,\n-                                  const struct rte_mbuf *);\n-\n static bool\n is_dpdk_class(const struct netdev_class *class)\n {\n@@ -479,8 +473,8 @@ is_dpdk_class(const struct netdev_class *class)\n  * behaviour, which reduces performance. To prevent this, use a buffer size\n  * that is closest to 'mtu', but which satisfies the aforementioned criteria.\n  */\n-static uint32_t\n-dpdk_buf_size(int mtu)\n+uint32_t\n+netdev_dpdk_buf_size(int mtu)\n {\n     return ROUND_UP(MTU_TO_MAX_FRAME_LEN(mtu), NETDEV_DPDK_MBUF_ALIGN)\n             + RTE_PKTMBUF_HEADROOM;\n@@ -630,6 +624,49 @@ dpdk_calculate_mbufs(struct netdev_dpdk *dev, int mtu)\n     return n_mbufs;\n }\n \n+struct rte_mempool *\n+netdev_dpdk_mp_create_pool(const char *pool_name, uint32_t n_mbufs,\n+                    uint32_t mbuf_size, int socket_id)\n+{\n+    uint32_t mbuf_priv_data_len;\n+    uint32_t aligned_mbuf_size;\n+    struct rte_mempool *mp;\n+    uint32_t pkt_size;\n+\n+    /* The size of the mbuf's private area (i.e. area that holds OvS'\n+     * dp_packet data)*/\n+    mbuf_priv_data_len = sizeof(struct dp_packet) -\n+                             sizeof(struct rte_mbuf);\n+    /* The size of the entire dp_packet. */\n+    pkt_size = sizeof(struct dp_packet) + mbuf_size;\n+    /* mbuf size, rounded up to cacheline size. */\n+    aligned_mbuf_size = ROUND_UP(pkt_size, RTE_CACHE_LINE_SIZE);\n+    /* If there is a size discrepancy, add padding to mbuf_priv_data_len.\n+     * This maintains mbuf size cache alignment, while also honoring RX\n+     * buffer alignment in the data portion of the mbuf. If this adjustment\n+     * is not made, there is a possiblity later on that for an element of\n+     * the mempool, buf, buf->data_len < (buf->buf_len - buf->data_off).\n+     * This is problematic in the case of multi-segment mbufs, particularly\n+     * when an mbuf segment needs to be resized (when [push|popp]ing a VLAN\n+     * header, for example.\n+     */\n+    mbuf_priv_data_len += (aligned_mbuf_size - pkt_size);\n+\n+    mp = rte_pktmbuf_pool_create(pool_name, n_mbufs, MP_CACHE_SZ,\n+                                 mbuf_priv_data_len, mbuf_size,\n+                                 socket_id);\n+\n+    if (mp) {\n+        /* rte_pktmbuf_pool_create has done some initialization of the\n+         * rte_mbuf part of each dp_packet, while ovs_rte_pktmbuf_init\n+         * initializes some OVS specific fields of dp_packet.\n+         */\n+        rte_mempool_obj_iter(mp, ovs_rte_pktmbuf_init, NULL);\n+    }\n+\n+    return mp;\n+}\n+\n static struct dpdk_mp *\n dpdk_mp_create(struct netdev_dpdk *dev, int mtu)\n {\n@@ -638,9 +675,6 @@ dpdk_mp_create(struct netdev_dpdk *dev, int mtu)\n     int socket_id = dev->common.requested_socket_id;\n     uint32_t n_mbufs = 0;\n     uint32_t mbuf_size = 0;\n-    uint32_t aligned_mbuf_size = 0;\n-    uint32_t mbuf_priv_data_len = 0;\n-    uint32_t pkt_size = 0;\n     uint32_t hash = hash_string(netdev_name, 0);\n     struct dpdk_mp *dmp = NULL;\n     int ret;\n@@ -659,13 +693,6 @@ dpdk_mp_create(struct netdev_dpdk *dev, int mtu)\n     n_mbufs = dpdk_calculate_mbufs(dev, mtu);\n \n     do {\n-        /* Full DPDK memory pool name must be unique and cannot be\n-         * longer than RTE_MEMPOOL_NAMESIZE. Note that for the shared\n-         * mempool case this can result in one device using a mempool\n-         * which references a different device in it's name. However as\n-         * mempool names are hashed, the device name will not be readable\n-         * so this is not an issue for tasks such as debugging.\n-         */\n         ret = snprintf(mp_name, RTE_MEMPOOL_NAMESIZE,\n                        \"ovs%08x%02d%05d%07u\",\n                         hash, socket_id, mtu, n_mbufs);\n@@ -684,38 +711,12 @@ dpdk_mp_create(struct netdev_dpdk *dev, int mtu)\n                   dev->common.requested_n_rxq, dev->common.requested_n_txq,\n                   RTE_CACHE_LINE_SIZE);\n \n-        /* The size of the mbuf's private area (i.e. area that holds OvS'\n-         * dp_packet data)*/\n-        mbuf_priv_data_len = sizeof(struct dp_packet) -\n-                                 sizeof(struct rte_mbuf);\n-        /* The size of the entire dp_packet. */\n-        pkt_size = sizeof(struct dp_packet) + mbuf_size;\n-        /* mbuf size, rounded up to cacheline size. */\n-        aligned_mbuf_size = ROUND_UP(pkt_size, RTE_CACHE_LINE_SIZE);\n-        /* If there is a size discrepancy, add padding to mbuf_priv_data_len.\n-         * This maintains mbuf size cache alignment, while also honoring RX\n-         * buffer alignment in the data portion of the mbuf. If this adjustment\n-         * is not made, there is a possiblity later on that for an element of\n-         * the mempool, buf, buf->data_len < (buf->buf_len - buf->data_off).\n-         * This is problematic in the case of multi-segment mbufs, particularly\n-         * when an mbuf segment needs to be resized (when [push|popp]ing a VLAN\n-         * header, for example.\n-         */\n-        mbuf_priv_data_len += (aligned_mbuf_size - pkt_size);\n-\n-        dmp->mp = rte_pktmbuf_pool_create(mp_name, n_mbufs, MP_CACHE_SZ,\n-                                          mbuf_priv_data_len,\n-                                          mbuf_size,\n-                                          socket_id);\n+        dmp->mp = netdev_dpdk_mp_create_pool(mp_name, n_mbufs, mbuf_size,\n+                                      socket_id);\n \n         if (dmp->mp) {\n             VLOG_DBG(\"Allocated \\\"%s\\\" mempool with %u mbufs\",\n                      mp_name, n_mbufs);\n-            /* rte_pktmbuf_pool_create has done some initialization of the\n-             * rte_mbuf part of each dp_packet, while ovs_rte_pktmbuf_init\n-             * initializes some OVS specific fields of dp_packet.\n-             */\n-            rte_mempool_obj_iter(dmp->mp, ovs_rte_pktmbuf_init, NULL);\n             return dmp;\n         } else if (rte_errno == EEXIST) {\n             /* A mempool with the same name already exists.  We just\n@@ -821,7 +822,7 @@ static int\n netdev_dpdk_mempool_configure(struct netdev_dpdk *dev)\n     OVS_REQUIRES(dev->common.mutex)\n {\n-    uint32_t buf_size = dpdk_buf_size(dev->common.requested_mtu);\n+    uint32_t buf_size = netdev_dpdk_buf_size(dev->common.requested_mtu);\n     struct dpdk_mp *dmp;\n     int ret = 0;\n \n@@ -866,8 +867,8 @@ netdev_dpdk_mempool_configure(struct netdev_dpdk *dev)\n     return ret;\n }\n \n-static void\n-check_link_status(struct netdev_dpdk_common *common)\n+void\n+netdev_dpdk_check_link_status(struct netdev_dpdk_common *common)\n {\n     struct rte_eth_link link;\n \n@@ -902,21 +903,24 @@ check_link_status(struct netdev_dpdk_common *common)\n     }\n }\n \n-static void *\n-dpdk_watchdog(void *dummy OVS_UNUSED)\n+void *\n+netdev_dpdk_watchdog(void *args_)\n {\n+    struct netdev_dpdk_watchdog_params *params = args_;\n     struct netdev_dpdk_common *common;\n \n+    ovs_assert(params);\n+\n     pthread_detach(pthread_self());\n \n     for (;;) {\n-        ovs_mutex_lock(&dpdk_mutex);\n-        LIST_FOR_EACH (common, list_node, &dpdk_list) {\n+        ovs_mutex_lock(params->mutex);\n+        LIST_FOR_EACH (common, list_node, params->list) {\n             ovs_mutex_lock(&common->mutex);\n-            check_link_status(common);\n+            netdev_dpdk_check_link_status(common);\n             ovs_mutex_unlock(&common->mutex);\n         }\n-        ovs_mutex_unlock(&dpdk_mutex);\n+        ovs_mutex_unlock(params->mutex);\n         xsleep(DPDK_PORT_WATCHDOG_INTERVAL);\n     }\n \n@@ -936,7 +940,7 @@ netdev_dpdk_update_netdev_flag(struct netdev_dpdk_common *common,\n     }\n }\n \n-static void\n+void\n netdev_dpdk_update_netdev_flags(struct netdev_dpdk_common *common)\n     OVS_REQUIRES(common->mutex)\n {\n@@ -962,85 +966,192 @@ netdev_dpdk_update_netdev_flags(struct netdev_dpdk_common *common)\n                                    NETDEV_TX_OFFLOAD_OUTER_UDP_CKSUM);\n }\n \n-static int\n-dpdk_eth_dev_port_config(struct netdev_dpdk_common *common,\n-                         const struct rte_eth_dev_info *info,\n-                         int n_rxq, int n_txq)\n+void\n+netdev_dpdk_detect_hw_ol_features(struct netdev_dpdk_common *common,\n+                           const struct rte_eth_dev_info *info)\n+    OVS_REQUIRES(common->mutex)\n {\n-    struct rte_eth_conf conf = port_conf;\n-    uint16_t conf_mtu;\n-    int diag = 0;\n-    int i;\n+    uint32_t rx_chksm_offload_capa = RTE_ETH_RX_OFFLOAD_UDP_CKSUM |\n+                                     RTE_ETH_RX_OFFLOAD_TCP_CKSUM |\n+                                     RTE_ETH_RX_OFFLOAD_IPV4_CKSUM;\n+\n+    if (strstr(info->driver_name, \"vf\") != NULL) {\n+        VLOG_INFO(\"Virtual function detected, HW_CRC_STRIP will be enabled\");\n+        common->hw_ol_features |= NETDEV_RX_HW_CRC_STRIP;\n+    } else {\n+        common->hw_ol_features &= ~NETDEV_RX_HW_CRC_STRIP;\n+    }\n \n+    if ((info->rx_offload_capa & rx_chksm_offload_capa) !=\n+            rx_chksm_offload_capa) {\n+        VLOG_WARN(\"Rx checksum offload is not supported on port \"\n+                  DPDK_PORT_ID_FMT, common->port_id);\n+        common->hw_ol_features &= ~NETDEV_RX_CHECKSUM_OFFLOAD;\n+    } else {\n+        common->hw_ol_features |= NETDEV_RX_CHECKSUM_OFFLOAD;\n+    }\n+\n+    if (info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_SCATTER) {\n+        common->hw_ol_features |= NETDEV_RX_HW_SCATTER;\n+    } else {\n+        common->hw_ol_features &= ~NETDEV_RX_HW_SCATTER;\n+    }\n+\n+    if (info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) {\n+        common->hw_ol_features |= NETDEV_TX_IPV4_CKSUM_OFFLOAD;\n+    } else {\n+        common->hw_ol_features &= ~NETDEV_TX_IPV4_CKSUM_OFFLOAD;\n+    }\n+\n+    if (info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_TCP_CKSUM) {\n+        common->hw_ol_features |= NETDEV_TX_TCP_CKSUM_OFFLOAD;\n+    } else {\n+        common->hw_ol_features &= ~NETDEV_TX_TCP_CKSUM_OFFLOAD;\n+    }\n+\n+    if (info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_UDP_CKSUM) {\n+        common->hw_ol_features |= NETDEV_TX_UDP_CKSUM_OFFLOAD;\n+    } else {\n+        common->hw_ol_features &= ~NETDEV_TX_UDP_CKSUM_OFFLOAD;\n+    }\n+\n+    if (info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_SCTP_CKSUM) {\n+        common->hw_ol_features |= NETDEV_TX_SCTP_CKSUM_OFFLOAD;\n+    } else {\n+        common->hw_ol_features &= ~NETDEV_TX_SCTP_CKSUM_OFFLOAD;\n+    }\n+\n+    if (info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM) {\n+        common->hw_ol_features |= NETDEV_TX_OUTER_IP_CKSUM_OFFLOAD;\n+    } else {\n+        common->hw_ol_features &= ~NETDEV_TX_OUTER_IP_CKSUM_OFFLOAD;\n+    }\n+\n+    if (info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM) {\n+        common->hw_ol_features |= NETDEV_TX_OUTER_UDP_CKSUM_OFFLOAD;\n+    } else {\n+        common->hw_ol_features &= ~NETDEV_TX_OUTER_UDP_CKSUM_OFFLOAD;\n+    }\n+\n+    common->hw_ol_features &= ~NETDEV_TX_TSO_OFFLOAD;\n+    if (userspace_tso_enabled()) {\n+        if (info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_TCP_TSO) {\n+            common->hw_ol_features |= NETDEV_TX_TSO_OFFLOAD;\n+        } else {\n+            VLOG_WARN(\"%s: Tx TSO offload is not supported.\",\n+                      netdev_get_name(&common->up));\n+        }\n+\n+        if (info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO) {\n+            common->hw_ol_features |= NETDEV_TX_VXLAN_TNL_TSO_OFFLOAD;\n+        } else {\n+            VLOG_WARN(\"%s: Tx Vxlan tunnel TSO offload is not supported.\",\n+                      netdev_get_name(&common->up));\n+        }\n+\n+        if (info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO) {\n+            common->hw_ol_features |= NETDEV_TX_GENEVE_TNL_TSO_OFFLOAD;\n+        } else {\n+            VLOG_WARN(\"%s: Tx Geneve tunnel TSO offload is not supported.\",\n+                      netdev_get_name(&common->up));\n+        }\n+\n+        if (info->tx_offload_capa & RTE_ETH_TX_OFFLOAD_GRE_TNL_TSO) {\n+            common->hw_ol_features |= NETDEV_TX_GRE_TNL_TSO_OFFLOAD;\n+        } else {\n+            VLOG_WARN(\"%s: Tx GRE tunnel TSO offload is not supported.\",\n+                      netdev_get_name(&common->up));\n+        }\n+    }\n+}\n+\n+void\n+netdev_dpdk_build_port_conf(struct netdev_dpdk_common *common,\n+                         const struct rte_eth_dev_info *info,\n+                         struct rte_eth_conf *conf)\n+{\n     /* As of DPDK 17.11.1 a few PMDs require to explicitly enable\n      * scatter to support jumbo RX.\n      * Setting scatter for the device is done after checking for\n      * scatter support in the device capabilites. */\n     if (common->mtu > RTE_ETHER_MTU) {\n         if (common->hw_ol_features & NETDEV_RX_HW_SCATTER) {\n-            conf.rxmode.offloads |= RTE_ETH_RX_OFFLOAD_SCATTER;\n+            conf->rxmode.offloads |= RTE_ETH_RX_OFFLOAD_SCATTER;\n         }\n     }\n \n-    conf.intr_conf.lsc = common->lsc_interrupt_mode;\n+    conf->intr_conf.lsc = common->lsc_interrupt_mode;\n \n     if (common->hw_ol_features & NETDEV_RX_CHECKSUM_OFFLOAD) {\n-        conf.rxmode.offloads |= RTE_ETH_RX_OFFLOAD_CHECKSUM;\n+        conf->rxmode.offloads |= RTE_ETH_RX_OFFLOAD_CHECKSUM;\n     }\n \n     if (!(common->hw_ol_features & NETDEV_RX_HW_CRC_STRIP)\n         && info->rx_offload_capa & RTE_ETH_RX_OFFLOAD_KEEP_CRC) {\n-        conf.rxmode.offloads |= RTE_ETH_RX_OFFLOAD_KEEP_CRC;\n+        conf->rxmode.offloads |= RTE_ETH_RX_OFFLOAD_KEEP_CRC;\n     }\n \n     if (common->hw_ol_features & NETDEV_TX_IPV4_CKSUM_OFFLOAD) {\n-        conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_IPV4_CKSUM;\n+        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_IPV4_CKSUM;\n     }\n \n     if (common->hw_ol_features & NETDEV_TX_TCP_CKSUM_OFFLOAD) {\n-        conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_TCP_CKSUM;\n+        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_TCP_CKSUM;\n     }\n \n     if (common->hw_ol_features & NETDEV_TX_UDP_CKSUM_OFFLOAD) {\n-        conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_UDP_CKSUM;\n+        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_UDP_CKSUM;\n     }\n \n     if (common->hw_ol_features & NETDEV_TX_SCTP_CKSUM_OFFLOAD) {\n-        conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_SCTP_CKSUM;\n+        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_SCTP_CKSUM;\n     }\n \n     if (common->hw_ol_features & NETDEV_TX_TSO_OFFLOAD) {\n-        conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_TCP_TSO;\n+        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_TCP_TSO;\n     }\n \n     if (common->hw_ol_features & NETDEV_TX_VXLAN_TNL_TSO_OFFLOAD) {\n-        conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO;\n+        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO;\n     }\n \n     if (common->hw_ol_features & NETDEV_TX_GENEVE_TNL_TSO_OFFLOAD) {\n-        conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO;\n+        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO;\n     }\n \n     if (common->hw_ol_features & NETDEV_TX_GRE_TNL_TSO_OFFLOAD) {\n-        conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_GRE_TNL_TSO;\n+        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_GRE_TNL_TSO;\n     }\n \n     if (common->hw_ol_features & NETDEV_TX_OUTER_IP_CKSUM_OFFLOAD) {\n-        conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM;\n+        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM;\n     }\n \n     if (common->hw_ol_features & NETDEV_TX_OUTER_UDP_CKSUM_OFFLOAD) {\n-        conf.txmode.offloads |= RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM;\n+        conf->txmode.offloads |= RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM;\n     }\n \n     /* Limit configured rss hash functions to only those supported\n      * by the eth device. */\n-    conf.rx_adv_conf.rss_conf.rss_hf &= info->flow_type_rss_offloads;\n-    if (conf.rx_adv_conf.rss_conf.rss_hf == 0) {\n-        conf.rxmode.mq_mode = RTE_ETH_MQ_RX_NONE;\n+    conf->rx_adv_conf.rss_conf.rss_hf &= info->flow_type_rss_offloads;\n+    if (conf->rx_adv_conf.rss_conf.rss_hf == 0) {\n+        conf->rxmode.mq_mode = RTE_ETH_MQ_RX_NONE;\n     } else {\n-        conf.rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;\n+        conf->rxmode.mq_mode = RTE_ETH_MQ_RX_RSS;\n     }\n+}\n+\n+static int\n+dpdk_eth_dev_port_config(struct netdev_dpdk_common *common,\n+                         const struct rte_eth_dev_info *info,\n+                         int n_rxq, int n_txq)\n+{\n+    struct rte_eth_conf conf = port_conf;\n+    uint16_t conf_mtu;\n+    int diag = 0;\n+    int i;\n+\n+    netdev_dpdk_build_port_conf(common, info, &conf);\n \n     /* A device may report more queues than it makes available (this has\n      * been observed for Intel xl710, which reserves some of them for\n@@ -1179,9 +1290,6 @@ dpdk_eth_dev_init(struct netdev_dpdk *dev)\n     struct rte_ether_addr eth_addr;\n     int diag;\n     int n_rxq, n_txq;\n-    uint32_t rx_chksm_offload_capa = RTE_ETH_RX_OFFLOAD_UDP_CKSUM |\n-                                     RTE_ETH_RX_OFFLOAD_TCP_CKSUM |\n-                                     RTE_ETH_RX_OFFLOAD_IPV4_CKSUM;\n \n     if (dpif_offload_enabled()) {\n         /*\n@@ -1204,95 +1312,7 @@ dpdk_eth_dev_init(struct netdev_dpdk *dev)\n \n     dev->common.is_representor = !!(*info.dev_flags & RTE_ETH_DEV_REPRESENTOR);\n \n-    if (strstr(info.driver_name, \"vf\") != NULL) {\n-        VLOG_INFO(\"Virtual function detected, HW_CRC_STRIP will be enabled\");\n-        dev->common.hw_ol_features |= NETDEV_RX_HW_CRC_STRIP;\n-    } else {\n-        dev->common.hw_ol_features &= ~NETDEV_RX_HW_CRC_STRIP;\n-    }\n-\n-    if ((info.rx_offload_capa & rx_chksm_offload_capa) !=\n-            rx_chksm_offload_capa) {\n-        VLOG_WARN(\"Rx checksum offload is not supported on port \"\n-                  DPDK_PORT_ID_FMT, dev->common.port_id);\n-        dev->common.hw_ol_features &= ~NETDEV_RX_CHECKSUM_OFFLOAD;\n-    } else {\n-        dev->common.hw_ol_features |= NETDEV_RX_CHECKSUM_OFFLOAD;\n-    }\n-\n-    if (info.rx_offload_capa & RTE_ETH_RX_OFFLOAD_SCATTER) {\n-        dev->common.hw_ol_features |= NETDEV_RX_HW_SCATTER;\n-    } else {\n-        /* Do not warn on lack of scatter support */\n-        dev->common.hw_ol_features &= ~NETDEV_RX_HW_SCATTER;\n-    }\n-\n-    if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_IPV4_CKSUM) {\n-        dev->common.hw_ol_features |= NETDEV_TX_IPV4_CKSUM_OFFLOAD;\n-    } else {\n-        dev->common.hw_ol_features &= ~NETDEV_TX_IPV4_CKSUM_OFFLOAD;\n-    }\n-\n-    if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_TCP_CKSUM) {\n-        dev->common.hw_ol_features |= NETDEV_TX_TCP_CKSUM_OFFLOAD;\n-    } else {\n-        dev->common.hw_ol_features &= ~NETDEV_TX_TCP_CKSUM_OFFLOAD;\n-    }\n-\n-    if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_UDP_CKSUM) {\n-        dev->common.hw_ol_features |= NETDEV_TX_UDP_CKSUM_OFFLOAD;\n-    } else {\n-        dev->common.hw_ol_features &= ~NETDEV_TX_UDP_CKSUM_OFFLOAD;\n-    }\n-\n-    if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_SCTP_CKSUM) {\n-        dev->common.hw_ol_features |= NETDEV_TX_SCTP_CKSUM_OFFLOAD;\n-    } else {\n-        dev->common.hw_ol_features &= ~NETDEV_TX_SCTP_CKSUM_OFFLOAD;\n-    }\n-\n-    if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_OUTER_IPV4_CKSUM) {\n-        dev->common.hw_ol_features |= NETDEV_TX_OUTER_IP_CKSUM_OFFLOAD;\n-    } else {\n-        dev->common.hw_ol_features &= ~NETDEV_TX_OUTER_IP_CKSUM_OFFLOAD;\n-    }\n-\n-    if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_OUTER_UDP_CKSUM) {\n-        dev->common.hw_ol_features |= NETDEV_TX_OUTER_UDP_CKSUM_OFFLOAD;\n-    } else {\n-        dev->common.hw_ol_features &= ~NETDEV_TX_OUTER_UDP_CKSUM_OFFLOAD;\n-    }\n-\n-    dev->common.hw_ol_features &= ~NETDEV_TX_TSO_OFFLOAD;\n-    if (userspace_tso_enabled()) {\n-        if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_TCP_TSO) {\n-            dev->common.hw_ol_features |= NETDEV_TX_TSO_OFFLOAD;\n-        } else {\n-            VLOG_WARN(\"%s: Tx TSO offload is not supported.\",\n-                      netdev_get_name(&dev->common.up));\n-        }\n-\n-        if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_VXLAN_TNL_TSO) {\n-            dev->common.hw_ol_features |= NETDEV_TX_VXLAN_TNL_TSO_OFFLOAD;\n-        } else {\n-            VLOG_WARN(\"%s: Tx Vxlan tunnel TSO offload is not supported.\",\n-                      netdev_get_name(&dev->common.up));\n-        }\n-\n-        if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_GENEVE_TNL_TSO) {\n-            dev->common.hw_ol_features |= NETDEV_TX_GENEVE_TNL_TSO_OFFLOAD;\n-        } else {\n-            VLOG_WARN(\"%s: Tx Geneve tunnel TSO offload is not supported.\",\n-                      netdev_get_name(&dev->common.up));\n-        }\n-\n-        if (info.tx_offload_capa & RTE_ETH_TX_OFFLOAD_GRE_TNL_TSO) {\n-            dev->common.hw_ol_features |= NETDEV_TX_GRE_TNL_TSO_OFFLOAD;\n-        } else {\n-            VLOG_WARN(\"%s: Tx GRE tunnel TSO offload is not supported.\",\n-                      netdev_get_name(&dev->common.up));\n-        }\n-    }\n+    netdev_dpdk_detect_hw_ol_features(&dev->common, &info);\n \n     n_rxq = MIN(info.max_rx_queues, dev->common.up.n_rxq);\n     n_txq = MIN(info.max_tx_queues, dev->common.up.n_txq);\n@@ -1742,7 +1762,7 @@ netdev_dpdk_dealloc(struct netdev *netdev)\n     rte_free(dev);\n }\n \n-static void\n+void\n netdev_dpdk_clear_xstats(struct netdev_dpdk_common *common)\n     OVS_REQUIRES(common->mutex)\n {\n@@ -1774,7 +1794,7 @@ is_queue_stat(const char *s)\n              ovs_scan(s + 1, \"x_q%\"SCNu16\"_bytes\", &tmp));\n }\n \n-static void\n+void\n netdev_dpdk_configure_xstats(struct netdev_dpdk_common *common)\n     OVS_REQUIRES(common->mutex)\n {\n@@ -1842,46 +1862,54 @@ out:\n     free(rte_xstats_names);\n }\n \n-static int\n-netdev_dpdk_get_config(const struct netdev *netdev, struct smap *args)\n+void\n+netdev_dpdk_get_config_common(struct netdev_dpdk_common *common,\n+                              struct smap *args)\n+    OVS_REQUIRES(common->mutex)\n {\n-    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);\n-\n-    ovs_mutex_lock(&dev->common.mutex);\n-\n-    if (dev->common.devargs && dev->common.devargs[0]) {\n-        smap_add_format(args, \"dpdk-devargs\", \"%s\", dev->common.devargs);\n+    if (common->devargs && common->devargs[0]) {\n+        smap_add_format(args, \"dpdk-devargs\", \"%s\", common->devargs);\n     }\n \n-    smap_add_format(args, \"n_rxq\", \"%d\", dev->common.user_n_rxq);\n+    smap_add_format(args, \"n_rxq\", \"%d\", common->user_n_rxq);\n \n-    if (dev->common.fc_conf.mode == RTE_ETH_FC_TX_PAUSE ||\n-        dev->common.fc_conf.mode == RTE_ETH_FC_FULL) {\n+    if (common->fc_conf.mode == RTE_ETH_FC_TX_PAUSE ||\n+        common->fc_conf.mode == RTE_ETH_FC_FULL) {\n         smap_add(args, \"rx-flow-ctrl\", \"true\");\n     }\n \n-    if (dev->common.fc_conf.mode == RTE_ETH_FC_RX_PAUSE ||\n-        dev->common.fc_conf.mode == RTE_ETH_FC_FULL) {\n+    if (common->fc_conf.mode == RTE_ETH_FC_RX_PAUSE ||\n+        common->fc_conf.mode == RTE_ETH_FC_FULL) {\n         smap_add(args, \"tx-flow-ctrl\", \"true\");\n     }\n \n-    if (dev->common.fc_conf.autoneg) {\n+    if (common->fc_conf.autoneg) {\n         smap_add(args, \"flow-ctrl-autoneg\", \"true\");\n     }\n \n-    smap_add_format(args, \"n_rxq_desc\", \"%d\", dev->common.rxq_size);\n-    smap_add_format(args, \"n_txq_desc\", \"%d\", dev->common.txq_size);\n-\n-    if (dev->rx_steer_flags == DPDK_RX_STEER_LACP) {\n-        smap_add(args, \"rx-steering\", \"rss+lacp\");\n-    }\n+    smap_add_format(args, \"n_rxq_desc\", \"%d\", common->rxq_size);\n+    smap_add_format(args, \"n_txq_desc\", \"%d\", common->txq_size);\n \n     smap_add(args, \"dpdk-lsc-interrupt\",\n-             dev->common.lsc_interrupt_mode ? \"true\" : \"false\");\n+             common->lsc_interrupt_mode ? \"true\" : \"false\");\n \n-    if (dev->common.is_representor) {\n+    if (common->is_representor) {\n         smap_add_format(args, \"dpdk-vf-mac\", ETH_ADDR_FMT,\n-                        ETH_ADDR_ARGS(dev->common.requested_hwaddr));\n+                        ETH_ADDR_ARGS(common->requested_hwaddr));\n+    }\n+}\n+\n+static int\n+netdev_dpdk_get_config(const struct netdev *netdev, struct smap *args)\n+{\n+    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);\n+\n+    ovs_mutex_lock(&dev->common.mutex);\n+\n+    netdev_dpdk_get_config_common(&dev->common, args);\n+\n+    if (dev->rx_steer_flags == DPDK_RX_STEER_LACP) {\n+        smap_add(args, \"rx-steering\", \"rss+lacp\");\n     }\n \n     ovs_mutex_unlock(&dev->common.mutex);\n@@ -1889,21 +1917,30 @@ netdev_dpdk_get_config(const struct netdev *netdev, struct smap *args)\n     return 0;\n }\n \n-static struct netdev_dpdk *\n-netdev_dpdk_lookup_by_port_id(dpdk_port_t port_id)\n-    OVS_REQUIRES(dpdk_mutex)\n+struct netdev_dpdk_common *\n+netdev_dpdk_lookup_by_port_id__(dpdk_port_t port_id, struct ovs_list *list)\n {\n-    struct netdev_dpdk *dev;\n+    struct netdev_dpdk_common *common;\n \n-    LIST_FOR_EACH (dev, common.list_node, &dpdk_list) {\n-        if (dev->common.port_id == port_id) {\n-            return dev;\n+    LIST_FOR_EACH (common, list_node, list) {\n+        if (common->port_id == port_id) {\n+            return common;\n         }\n     }\n \n     return NULL;\n }\n \n+static struct netdev_dpdk *\n+netdev_dpdk_lookup_by_port_id(dpdk_port_t port_id)\n+    OVS_REQUIRES(dpdk_mutex)\n+{\n+    struct netdev_dpdk_common *common;\n+\n+    common = netdev_dpdk_lookup_by_port_id__(port_id, &dpdk_list);\n+    return common ? CONTAINER_OF(common, struct netdev_dpdk, common) : NULL;\n+}\n+\n static dpdk_port_t\n netdev_dpdk_get_port_by_mac(const char *mac_str)\n {\n@@ -1929,7 +1966,7 @@ netdev_dpdk_get_port_by_mac(const char *mac_str)\n }\n \n /* Return the first DPDK port id matching the devargs pattern. */\n-static dpdk_port_t netdev_dpdk_get_port_by_devargs(const char *devargs)\n+dpdk_port_t netdev_dpdk_get_port_by_devargs(const char *devargs)\n     OVS_REQUIRES(dpdk_mutex)\n {\n     dpdk_port_t port_id;\n@@ -2058,8 +2095,8 @@ dpdk_eth_event_callback(dpdk_port_t port_id, enum rte_eth_event_type type,\n     return 0;\n }\n \n-static void\n-dpdk_set_rxq_config(struct netdev_dpdk_common *common,\n+void\n+netdev_dpdk_set_rxq_config(struct netdev_dpdk_common *common,\n                     const struct smap *args)\n     OVS_REQUIRES(common->mutex)\n {\n@@ -2182,7 +2219,7 @@ netdev_dpdk_set_config(struct netdev *netdev, const struct smap *args,\n \n     dpdk_set_rx_steer_config(netdev, dev, args, errp);\n \n-    dpdk_set_rxq_config(&dev->common, args);\n+    netdev_dpdk_set_rxq_config(&dev->common, args);\n \n     new_devargs = smap_get(args, \"dpdk-devargs\");\n \n@@ -2402,7 +2439,7 @@ netdev_dpdk_vhost_client_set_config(struct netdev *netdev,\n     return 0;\n }\n \n-static int\n+int\n netdev_dpdk_get_numa_id(const struct netdev *netdev)\n {\n     struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n@@ -2411,7 +2448,7 @@ netdev_dpdk_get_numa_id(const struct netdev *netdev)\n }\n \n /* Sets the number of tx queues for the dpdk interface. */\n-static int\n+int\n netdev_dpdk_set_tx_multiq(struct netdev *netdev, unsigned int n_txq)\n {\n     struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n@@ -2430,7 +2467,7 @@ out:\n     return 0;\n }\n \n-static struct netdev_rxq *\n+struct netdev_rxq *\n netdev_dpdk_rxq_alloc(void)\n {\n     struct netdev_rxq_dpdk *rx = dpdk_rte_mzalloc(sizeof *rx);\n@@ -2442,31 +2479,25 @@ netdev_dpdk_rxq_alloc(void)\n     return NULL;\n }\n \n-static struct netdev_rxq_dpdk *\n-netdev_rxq_dpdk_cast(const struct netdev_rxq *rxq)\n-{\n-    return CONTAINER_OF(rxq, struct netdev_rxq_dpdk, up);\n-}\n-\n-static int\n+int\n netdev_dpdk_rxq_construct(struct netdev_rxq *rxq)\n {\n     struct netdev_rxq_dpdk *rx = netdev_rxq_dpdk_cast(rxq);\n-    struct netdev_dpdk *dev = netdev_dpdk_cast(rxq->netdev);\n+    struct netdev_dpdk_common *common = netdev_dpdk_common_cast(rxq->netdev);\n \n-    ovs_mutex_lock(&dev->common.mutex);\n-    rx->port_id = dev->common.port_id;\n-    ovs_mutex_unlock(&dev->common.mutex);\n+    ovs_mutex_lock(&common->mutex);\n+    rx->port_id = common->port_id;\n+    ovs_mutex_unlock(&common->mutex);\n \n     return 0;\n }\n \n-static void\n+void\n netdev_dpdk_rxq_destruct(struct netdev_rxq *rxq OVS_UNUSED)\n {\n }\n \n-static void\n+void\n netdev_dpdk_rxq_dealloc(struct netdev_rxq *rxq)\n {\n     struct netdev_rxq_dpdk *rx = netdev_rxq_dpdk_cast(rxq);\n@@ -2638,7 +2669,7 @@ netdev_dpdk_prep_hwol_packet(struct netdev_dpdk_common *common,\n \n /* Prepare a batch for HWOL.\n  * Return the number of good packets in the batch. */\n-static int\n+int\n netdev_dpdk_prep_hwol_batch(struct netdev_dpdk_common *common,\n                             struct rte_mbuf **pkts, int pkt_cnt)\n {\n@@ -2663,7 +2694,7 @@ netdev_dpdk_prep_hwol_batch(struct netdev_dpdk_common *common,\n     return cnt;\n }\n \n-static void\n+void\n netdev_dpdk_mbuf_dump(const char *prefix, const char *message,\n                       const struct rte_mbuf *mbuf)\n {\n@@ -2696,27 +2727,32 @@ netdev_dpdk_mbuf_dump(const char *prefix, const char *message,\n  * 'pkts', even in case of failure.\n  *\n  * Returns the number of packets that weren't transmitted. */\n-static inline int\n-netdev_dpdk_eth_tx_burst(struct netdev_dpdk *dev, int qid,\n+int\n+netdev_dpdk_eth_tx_burst(struct netdev_dpdk_common *common,\n+                         dpdk_port_t port_id, int qid,\n                          struct rte_mbuf **pkts, int cnt)\n {\n     uint32_t nb_tx = 0;\n     uint16_t nb_tx_prep = cnt;\n \n-    nb_tx_prep = rte_eth_tx_prepare(dev->common.port_id, qid, pkts, cnt);\n+    if (OVS_UNLIKELY(!dpdk_dev_is_started(common))) {\n+        goto out;\n+    }\n+\n+    nb_tx_prep = rte_eth_tx_prepare(port_id, qid, pkts, cnt);\n     if (nb_tx_prep != cnt) {\n         VLOG_WARN_RL(&rl, \"%s: Output batch contains invalid packets. \"\n                      \"Only %u/%u are valid: %s\",\n-                     netdev_get_name(&dev->common.up),\n+                     netdev_get_name(&common->up),\n                      nb_tx_prep, cnt, rte_strerror(rte_errno));\n-        netdev_dpdk_mbuf_dump(netdev_get_name(&dev->common.up),\n+        netdev_dpdk_mbuf_dump(netdev_get_name(&common->up),\n                               \"First invalid packet\", pkts[nb_tx_prep]);\n     }\n \n     while (nb_tx != nb_tx_prep) {\n         uint32_t ret;\n \n-        ret = rte_eth_tx_burst(dev->common.port_id, qid, pkts + nb_tx,\n+        ret = rte_eth_tx_burst(port_id, qid, pkts + nb_tx,\n                                nb_tx_prep - nb_tx);\n         if (!ret) {\n             break;\n@@ -2725,6 +2761,7 @@ netdev_dpdk_eth_tx_burst(struct netdev_dpdk *dev, int qid,\n         nb_tx += ret;\n     }\n \n+out:\n     if (OVS_UNLIKELY(nb_tx != cnt)) {\n         /* Free buffers, which we couldn't transmit. */\n         rte_pktmbuf_free_bulk(&pkts[nb_tx], cnt - nb_tx);\n@@ -2926,9 +2963,9 @@ netdev_dpdk_qos_run(struct netdev_dpdk *dev, struct rte_mbuf **pkts,\n     return cnt;\n }\n \n-static int\n-netdev_dpdk_filter_packet_len(struct netdev_dpdk *dev, struct rte_mbuf **pkts,\n-                              int pkt_cnt)\n+int\n+netdev_dpdk_filter_packet_len(struct netdev_dpdk_common *common,\n+                              struct rte_mbuf **pkts, int pkt_cnt)\n {\n     int i = 0;\n     int cnt = 0;\n@@ -2938,12 +2975,12 @@ netdev_dpdk_filter_packet_len(struct netdev_dpdk *dev, struct rte_mbuf **pkts,\n      * during the offloading preparation for performance reasons. */\n     for (i = 0; i < pkt_cnt; i++) {\n         pkt = pkts[i];\n-        if (OVS_UNLIKELY((pkt->pkt_len > dev->common.max_packet_len)\n+        if (OVS_UNLIKELY((pkt->pkt_len > common->max_packet_len)\n             && !pkt->tso_segsz)) {\n             VLOG_WARN_RL(&rl, \"%s: Too big size %\" PRIu32 \" \"\n                          \"max_packet_len %d\",\n-                         dev->common.up.name, pkt->pkt_len,\n-                         dev->common.max_packet_len);\n+                         common->up.name, pkt->pkt_len,\n+                         common->max_packet_len);\n             rte_pktmbuf_free(pkt);\n             continue;\n         }\n@@ -3111,8 +3148,8 @@ dpdk_copy_dp_packet_to_mbuf(struct rte_mempool *mp, struct dp_packet *pkt_orig)\n  * DPDK memory.\n  *\n  * Returns the number of good packets in the batch. */\n-static size_t\n-dpdk_copy_batch_to_mbuf(struct netdev_dpdk_common *common,\n+size_t\n+netdev_dpdk_copy_batch_to_mbuf(struct netdev_dpdk_common *common,\n                         struct dp_packet_batch *batch)\n {\n     size_t i, size = dp_packet_batch_size(batch);\n@@ -3157,13 +3194,13 @@ netdev_dpdk_common_send(struct netdev *netdev, struct dp_packet_batch *batch,\n \n     /* Copy dp-packets to mbufs. */\n     if (OVS_UNLIKELY(need_copy)) {\n-        cnt = dpdk_copy_batch_to_mbuf(&dev->common, batch);\n+        cnt = netdev_dpdk_copy_batch_to_mbuf(&dev->common, batch);\n         stats->tx_failure_drops += pkt_cnt - cnt;\n         pkt_cnt = cnt;\n     }\n \n     /* Drop oversized packets. */\n-    cnt = netdev_dpdk_filter_packet_len(dev, pkts, pkt_cnt);\n+    cnt = netdev_dpdk_filter_packet_len(&dev->common, pkts, pkt_cnt);\n     stats->tx_mtu_exceeded_drops += pkt_cnt - cnt;\n     pkt_cnt = cnt;\n \n@@ -3290,7 +3327,8 @@ netdev_dpdk_eth_send(struct netdev *netdev, int qid,\n \n     cnt = netdev_dpdk_common_send(netdev, batch, &stats);\n \n-    dropped = netdev_dpdk_eth_tx_burst(dev, qid, pkts, cnt);\n+    dropped = netdev_dpdk_eth_tx_burst(&dev->common, dev->common.port_id,\n+                                      qid, pkts, cnt);\n     stats.tx_failure_drops += dropped;\n     dropped += batch_cnt - cnt;\n     if (OVS_UNLIKELY(dropped)) {\n@@ -3312,14 +3350,14 @@ netdev_dpdk_eth_send(struct netdev *netdev, int qid,\n     return 0;\n }\n \n-static int\n-netdev_dpdk_set_etheraddr__(struct netdev_dpdk *dev, const struct eth_addr mac)\n-    OVS_REQUIRES(dev->common.mutex)\n+int\n+netdev_dpdk_set_etheraddr__(struct netdev_dpdk_common *common,\n+                            const struct eth_addr mac)\n+    OVS_REQUIRES(common->mutex)\n {\n-    struct netdev_dpdk_common *common = &dev->common;\n     int err = 0;\n \n-    if (dev->type == DPDK_DEV_ETH) {\n+    if (common->port_id != DPDK_ETH_PORT_ID_INVALID) {\n         struct rte_ether_addr ea;\n \n         memcpy(ea.addr_bytes, mac.ea, ETH_ADDR_LEN);\n@@ -3336,25 +3374,25 @@ netdev_dpdk_set_etheraddr__(struct netdev_dpdk *dev, const struct eth_addr mac)\n     return err;\n }\n \n-static int\n+int\n netdev_dpdk_set_etheraddr(struct netdev *netdev, const struct eth_addr mac)\n {\n-    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);\n+    struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n     int err = 0;\n \n-    ovs_mutex_lock(&dev->common.mutex);\n-    if (!eth_addr_equals(dev->common.hwaddr, mac)) {\n-        err = netdev_dpdk_set_etheraddr__(dev, mac);\n+    ovs_mutex_lock(&common->mutex);\n+    if (!eth_addr_equals(common->hwaddr, mac)) {\n+        err = netdev_dpdk_set_etheraddr__(common, mac);\n         if (!err) {\n             netdev_change_seq_changed(netdev);\n         }\n     }\n-    ovs_mutex_unlock(&dev->common.mutex);\n+    ovs_mutex_unlock(&common->mutex);\n \n     return err;\n }\n \n-static int\n+int\n netdev_dpdk_get_etheraddr(const struct netdev *netdev, struct eth_addr *mac)\n {\n     struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n@@ -3366,7 +3404,7 @@ netdev_dpdk_get_etheraddr(const struct netdev *netdev, struct eth_addr *mac)\n     return 0;\n }\n \n-static int\n+int\n netdev_dpdk_get_mtu(const struct netdev *netdev, int *mtup)\n {\n     struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n@@ -3711,7 +3749,7 @@ out:\n     return 0;\n }\n \n-static void\n+void\n netdev_dpdk_convert_xstats(struct netdev_stats *stats,\n                            const struct rte_eth_xstat *xstats,\n                            const struct rte_eth_xstat_name *names,\n@@ -3754,10 +3792,10 @@ netdev_dpdk_convert_xstats(struct netdev_stats *stats,\n #undef DPDK_XSTATS\n }\n \n-static int\n+int\n netdev_dpdk_get_carrier(const struct netdev *netdev, bool *carrier);\n \n-static int\n+int\n netdev_dpdk_get_stats(const struct netdev *netdev, struct netdev_stats *stats)\n {\n     struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n@@ -3767,6 +3805,12 @@ netdev_dpdk_get_stats(const struct netdev *netdev, struct netdev_stats *stats)\n     netdev_dpdk_get_carrier(netdev, &gg);\n     ovs_mutex_lock(&common->mutex);\n \n+    if (!dpdk_dev_is_started(common)) {\n+        memset(stats, 0, sizeof *stats);\n+        ovs_mutex_unlock(&common->mutex);\n+        return 0;\n+    }\n+\n     struct rte_eth_xstat *rte_xstats = NULL;\n     struct rte_eth_xstat_name *rte_xstats_names = NULL;\n     int rte_xstats_len, rte_xstats_new_len, rte_xstats_ret;\n@@ -3789,7 +3833,7 @@ netdev_dpdk_get_stats(const struct netdev *netdev, struct netdev_stats *stats)\n     rte_xstats_names = xcalloc(rte_xstats_len, sizeof *rte_xstats_names);\n     rte_xstats = xcalloc(rte_xstats_len, sizeof *rte_xstats);\n \n-    /* Retreive xstats names */\n+    /* Retrieve 'xstats' names. */\n     rte_xstats_new_len = rte_eth_xstats_get_names(common->port_id,\n                                                   rte_xstats_names,\n                                                   rte_xstats_len);\n@@ -3798,7 +3842,7 @@ netdev_dpdk_get_stats(const struct netdev *netdev, struct netdev_stats *stats)\n                   common->port_id);\n         goto out;\n     }\n-    /* Retreive xstats values */\n+    /* Retrieve 'xstats' values. */\n     memset(rte_xstats, 0xff, sizeof *rte_xstats * rte_xstats_len);\n     rte_xstats_ret = rte_eth_xstats_get(common->port_id, rte_xstats,\n                                         rte_xstats_len);\n@@ -3937,7 +3981,7 @@ netdev_dpdk_get_sw_custom_stats(const struct netdev *netdev,\n     return 0;\n }\n \n-static int\n+int\n netdev_dpdk_get_features(const struct netdev *netdev,\n                          enum netdev_features *current,\n                          enum netdev_features *advertised,\n@@ -4002,7 +4046,7 @@ netdev_dpdk_get_features(const struct netdev *netdev,\n     return 0;\n }\n \n-static int\n+int\n netdev_dpdk_get_speed(const struct netdev *netdev, uint32_t *current,\n                       uint32_t *max)\n {\n@@ -4013,7 +4057,12 @@ netdev_dpdk_get_speed(const struct netdev *netdev, uint32_t *current,\n \n     ovs_mutex_lock(&common->mutex);\n     link = common->link;\n-    diag = rte_eth_dev_info_get(common->port_id, &dev_info);\n+    if (dpdk_dev_is_started(common)) {\n+        diag = rte_eth_dev_info_get(common->port_id, &dev_info);\n+    } else {\n+        memset(&dev_info, 0, sizeof dev_info);\n+        diag = -ENODEV;\n+    }\n     ovs_mutex_unlock(&common->mutex);\n \n     *current = link.link_speed != RTE_ETH_SPEED_NUM_UNKNOWN\n@@ -4158,7 +4207,7 @@ netdev_dpdk_set_policing(struct netdev* netdev, uint32_t policer_rate,\n     return 0;\n }\n \n-static int\n+int\n netdev_dpdk_get_ifindex(const struct netdev *netdev)\n {\n     struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n@@ -4173,13 +4222,13 @@ netdev_dpdk_get_ifindex(const struct netdev *netdev)\n     return ifindex;\n }\n \n-static int\n+int\n netdev_dpdk_get_carrier(const struct netdev *netdev, bool *carrier)\n {\n     struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n \n     ovs_mutex_lock(&common->mutex);\n-    check_link_status(common);\n+    netdev_dpdk_check_link_status(common);\n     *carrier = common->link.link_status;\n \n     ovs_mutex_unlock(&common->mutex);\n@@ -4205,7 +4254,7 @@ netdev_dpdk_vhost_get_carrier(const struct netdev *netdev, bool *carrier)\n     return 0;\n }\n \n-static long long int\n+long long int\n netdev_dpdk_get_carrier_resets(const struct netdev *netdev)\n {\n     struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n@@ -4218,21 +4267,19 @@ netdev_dpdk_get_carrier_resets(const struct netdev *netdev)\n     return carrier_resets;\n }\n \n-static int\n+int\n netdev_dpdk_set_miimon(struct netdev *netdev OVS_UNUSED,\n                        long long int interval OVS_UNUSED)\n {\n     return EOPNOTSUPP;\n }\n \n-static int\n-netdev_dpdk_update_flags__(struct netdev_dpdk *dev,\n+int\n+netdev_dpdk_update_flags__(struct netdev_dpdk_common *common,\n                            enum netdev_flags off, enum netdev_flags on,\n                            enum netdev_flags *old_flagsp)\n-    OVS_REQUIRES(dev->common.mutex)\n+    OVS_REQUIRES(common->mutex)\n {\n-    struct netdev_dpdk_common *common = &dev->common;\n-\n     if ((off | on) & ~(NETDEV_UP | NETDEV_PROMISC)) {\n         return EINVAL;\n     }\n@@ -4245,9 +4292,8 @@ netdev_dpdk_update_flags__(struct netdev_dpdk *dev,\n         return 0;\n     }\n \n-    if (dev->type == DPDK_DEV_ETH) {\n-\n-        if ((dev->common.flags ^ *old_flagsp) & NETDEV_UP) {\n+    if (common->port_id != DPDK_ETH_PORT_ID_INVALID) {\n+        if ((common->flags ^ *old_flagsp) & NETDEV_UP) {\n             int err;\n \n             if (common->flags & NETDEV_UP) {\n@@ -4272,6 +4318,8 @@ netdev_dpdk_update_flags__(struct netdev_dpdk *dev,\n \n         netdev_change_seq_changed(&common->up);\n     } else {\n+        struct netdev_dpdk *dev = netdev_dpdk_cast(&common->up);\n+\n         /* If DPDK_DEV_VHOST device's NETDEV_UP flag was changed and vhost is\n          * running then change netdev's change_seq to trigger link state\n          * update. */\n@@ -4293,17 +4341,17 @@ netdev_dpdk_update_flags__(struct netdev_dpdk *dev,\n     return 0;\n }\n \n-static int\n+int\n netdev_dpdk_update_flags(struct netdev *netdev,\n                          enum netdev_flags off, enum netdev_flags on,\n                          enum netdev_flags *old_flagsp)\n {\n-    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);\n+    struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n     int error;\n \n-    ovs_mutex_lock(&dev->common.mutex);\n-    error = netdev_dpdk_update_flags__(dev, off, on, old_flagsp);\n-    ovs_mutex_unlock(&dev->common.mutex);\n+    ovs_mutex_lock(&common->mutex);\n+    error = netdev_dpdk_update_flags__(common, off, on, old_flagsp);\n+    ovs_mutex_unlock(&common->mutex);\n \n     return error;\n }\n@@ -4378,7 +4426,7 @@ netdev_dpdk_vhost_user_get_status(const struct netdev *netdev,\n  * Convert a given uint32_t link speed defined in DPDK to a string\n  * equivalent.\n  */\n-static const char *\n+const char *\n netdev_dpdk_link_speed_to_str__(uint32_t link_speed)\n {\n     switch (link_speed) {\n@@ -4398,31 +4446,28 @@ netdev_dpdk_link_speed_to_str__(uint32_t link_speed)\n     }\n }\n \n-static int\n-netdev_dpdk_get_status(const struct netdev *netdev, struct smap *args)\n+int\n+netdev_dpdk_get_status__(const struct netdev *netdev,\n+                         struct ovs_mutex *dev_mutex,\n+                         struct smap *args)\n {\n     struct netdev_dpdk_common *common = netdev_dpdk_common_cast(netdev);\n-    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);\n     struct rte_eth_dev_info dev_info;\n-    size_t rx_steer_flows_num;\n-    uint64_t rx_steer_flags;\n     uint32_t link_speed;\n-    int n_rxq;\n     int diag;\n \n     if (!rte_eth_dev_is_valid_port(common->port_id)) {\n         return ENODEV;\n     }\n \n-    ovs_mutex_lock(&dpdk_mutex);\n+    ovs_assert(dev_mutex);\n+\n+    ovs_mutex_lock(dev_mutex);\n     ovs_mutex_lock(&common->mutex);\n     diag = rte_eth_dev_info_get(common->port_id, &dev_info);\n     link_speed = common->link.link_speed;\n-    rx_steer_flags = dev->rx_steer_flags;\n-    rx_steer_flows_num = dev->rx_steer_flows_num;\n-    n_rxq = netdev->n_rxq;\n     ovs_mutex_unlock(&common->mutex);\n-    ovs_mutex_unlock(&dpdk_mutex);\n+    ovs_mutex_unlock(dev_mutex);\n \n     smap_add_format(args, \"port_no\", DPDK_PORT_ID_FMT, common->port_id);\n     smap_add_format(args, \"numa_id\", \"%d\",\n@@ -4477,6 +4522,29 @@ netdev_dpdk_get_status(const struct netdev *netdev, struct smap *args)\n                         ETH_ADDR_ARGS(common->hwaddr));\n     }\n \n+    return 0;\n+}\n+\n+static int\n+netdev_dpdk_get_status(const struct netdev *netdev, struct smap *args)\n+{\n+    struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);\n+    size_t rx_steer_flows_num;\n+    uint64_t rx_steer_flags;\n+    int n_rxq;\n+    int ret;\n+\n+    ret = netdev_dpdk_get_status__(netdev, &dpdk_mutex, args);\n+    if (ret) {\n+        return ret;\n+    }\n+\n+    ovs_mutex_lock(&dev->common.mutex);\n+    rx_steer_flags = dev->rx_steer_flags;\n+    rx_steer_flows_num = dev->rx_steer_flows_num;\n+    n_rxq = netdev->n_rxq;\n+    ovs_mutex_unlock(&dev->common.mutex);\n+\n     if (rx_steer_flags && !rx_steer_flows_num) {\n         smap_add(args, \"rx-steering\", \"unsupported\");\n     } else if (rx_steer_flags == DPDK_RX_STEER_LACP) {\n@@ -4499,15 +4567,16 @@ netdev_dpdk_get_status(const struct netdev *netdev, struct smap *args)\n }\n \n static void\n-netdev_dpdk_set_admin_state__(struct netdev_dpdk *dev, bool admin_state)\n-    OVS_REQUIRES(dev->common.mutex)\n+netdev_dpdk_set_admin_state__(struct netdev_dpdk_common *common,\n+                              bool admin_state)\n+    OVS_REQUIRES(common->mutex)\n {\n     enum netdev_flags old_flags;\n \n     if (admin_state) {\n-        netdev_dpdk_update_flags__(dev, 0, NETDEV_UP, &old_flags);\n+        netdev_dpdk_update_flags__(common, 0, NETDEV_UP, &old_flags);\n     } else {\n-        netdev_dpdk_update_flags__(dev, NETDEV_UP, 0, &old_flags);\n+        netdev_dpdk_update_flags__(common, NETDEV_UP, 0, &old_flags);\n     }\n }\n \n@@ -4530,11 +4599,12 @@ netdev_dpdk_set_admin_state(struct unixctl_conn *conn, int argc,\n         struct netdev *netdev = netdev_from_name(argv[1]);\n \n         if (netdev && is_dpdk_class(netdev->netdev_class)) {\n-            struct netdev_dpdk *dev = netdev_dpdk_cast(netdev);\n+            struct netdev_dpdk_common *common =\n+                netdev_dpdk_common_cast(netdev);\n \n-            ovs_mutex_lock(&dev->common.mutex);\n-            netdev_dpdk_set_admin_state__(dev, up);\n-            ovs_mutex_unlock(&dev->common.mutex);\n+            ovs_mutex_lock(&common->mutex);\n+            netdev_dpdk_set_admin_state__(common, up);\n+            ovs_mutex_unlock(&common->mutex);\n \n             netdev_close(netdev);\n         } else {\n@@ -4548,7 +4618,7 @@ netdev_dpdk_set_admin_state(struct unixctl_conn *conn, int argc,\n         ovs_mutex_lock(&dpdk_mutex);\n         LIST_FOR_EACH (dev, common.list_node, &dpdk_list) {\n             ovs_mutex_lock(&dev->common.mutex);\n-            netdev_dpdk_set_admin_state__(dev, up);\n+            netdev_dpdk_set_admin_state__(&dev->common, up);\n             ovs_mutex_unlock(&dev->common.mutex);\n         }\n         ovs_mutex_unlock(&dpdk_mutex);\n@@ -5144,9 +5214,14 @@ netdev_dpdk_class_init(void)\n     /* This function can be called for different classes.  The initialization\n      * needs to be done only once */\n     if (ovsthread_once_start(&once)) {\n+        static struct netdev_dpdk_watchdog_params watchdog_params = {\n+            .mutex = &dpdk_mutex,\n+            .list = &dpdk_list,\n+        };\n         int ret;\n \n-        ovs_thread_create(\"dpdk_watchdog\", dpdk_watchdog, NULL);\n+        ovs_thread_create(\"dpdk_watchdog\", netdev_dpdk_watchdog,\n+                          &watchdog_params);\n         unixctl_command_register(\"netdev-dpdk/set-admin-state\",\n                                  \"[netdev] up|down\", 1, 2,\n                                  netdev_dpdk_set_admin_state, NULL);\n@@ -6158,7 +6233,8 @@ retry:\n     dev->common.tx_q = NULL;\n \n     if (!eth_addr_equals(dev->common.hwaddr, dev->common.requested_hwaddr)) {\n-        err = netdev_dpdk_set_etheraddr__(dev, dev->common.requested_hwaddr);\n+        err = netdev_dpdk_set_etheraddr__(&dev->common,\n+                                          dev->common.requested_hwaddr);\n         if (err) {\n             goto out;\n         }\n@@ -6676,7 +6752,7 @@ parse_user_mempools_list(const struct smap *ovs_other_config)\n \n         user_mempools = xrealloc(user_mempools, (n_user_mempools + 1) *\n                                  sizeof(struct user_mempool_config));\n-        adj_mtu = FRAME_LEN_TO_MTU(dpdk_buf_size(mtu));\n+        adj_mtu = FRAME_LEN_TO_MTU(netdev_dpdk_buf_size(mtu));\n         user_mempools[n_user_mempools].adj_mtu = adj_mtu;\n         user_mempools[n_user_mempools].socket_id = socket_id;\n         n_user_mempools++;\n",
    "prefixes": [
        "ovs-dev",
        "v3",
        "08/11"
    ]
}