get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

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

{
    "id": 2095640,
    "url": "http://patchwork.ozlabs.org/api/patches/2095640/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/ovn/patch/20250609173539.1636916-13-mmichels@redhat.com/",
    "project": {
        "id": 68,
        "url": "http://patchwork.ozlabs.org/api/projects/68/?format=api",
        "name": "Open Virtual Network development",
        "link_name": "ovn",
        "list_id": "ovs-dev.openvswitch.org",
        "list_email": "ovs-dev@openvswitch.org",
        "web_url": "http://openvswitch.org/",
        "scm_url": "",
        "webscm_url": "",
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20250609173539.1636916-13-mmichels@redhat.com>",
    "list_archive_url": null,
    "date": "2025-06-09T17:35:31",
    "name": "[ovs-dev,12/14] datapaths: Add a hashvec of datapath bindings.",
    "commit_ref": null,
    "pull_url": null,
    "state": "deferred",
    "archived": false,
    "hash": "4385cb23e0fbe9706344d34b0afe8a1f0fa263f9",
    "submitter": {
        "id": 71978,
        "url": "http://patchwork.ozlabs.org/api/people/71978/?format=api",
        "name": "Mark Michelson",
        "email": "mmichels@redhat.com"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/ovn/patch/20250609173539.1636916-13-mmichels@redhat.com/mbox/",
    "series": [
        {
            "id": 460139,
            "url": "http://patchwork.ozlabs.org/api/series/460139/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/ovn/list/?series=460139",
            "date": "2025-06-09T17:35:19",
            "name": "Logical Flow Sync Refactor.",
            "version": 1,
            "mbox": "http://patchwork.ozlabs.org/series/460139/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2095640/comments/",
    "check": "success",
    "checks": "http://patchwork.ozlabs.org/api/patches/2095640/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\" (1024-bit key;\n unprotected) header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=XMU0G92d;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=openvswitch.org\n (client-ip=2605:bc80:3010::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\" (1024-bit key)\n header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=XMU0G92d",
            "smtp4.osuosl.org; dmarc=pass (p=quarantine dis=none)\n header.from=redhat.com",
            "smtp4.osuosl.org;\n dkim=fail reason=\"signature verification failed\" (1024-bit key)\n header.d=redhat.com header.i=@redhat.com header.a=rsa-sha256\n header.s=mimecast20190719 header.b=XMU0G92d"
        ],
        "Received": [
            "from smtp1.osuosl.org (smtp1.osuosl.org [IPv6:2605:bc80:3010::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 4bGJxf3Wq8z1yZ1\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 10 Jun 2025 03:36:22 +1000 (AEST)",
            "from localhost (localhost [127.0.0.1])\n\tby smtp1.osuosl.org (Postfix) with ESMTP id B200282166;\n\tMon,  9 Jun 2025 17:36:34 +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 0n98UWhheIh4; Mon,  9 Jun 2025 17:36:31 +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 B6A8C820AB;\n\tMon,  9 Jun 2025 17:36:25 +0000 (UTC)",
            "from lf-lists.osuosl.org (localhost [127.0.0.1])\n\tby lists.linuxfoundation.org (Postfix) with ESMTP id A05FDC0AC5;\n\tMon,  9 Jun 2025 17:36:25 +0000 (UTC)",
            "from smtp4.osuosl.org (smtp4.osuosl.org [IPv6:2605:bc80:3010::137])\n by lists.linuxfoundation.org (Postfix) with ESMTP id 14D26C0AC6\n for <dev@openvswitch.org>; Mon,  9 Jun 2025 17:36:24 +0000 (UTC)",
            "from localhost (localhost [127.0.0.1])\n by smtp4.osuosl.org (Postfix) with ESMTP id 1E79041D52\n for <dev@openvswitch.org>; Mon,  9 Jun 2025 17:36:04 +0000 (UTC)",
            "from smtp4.osuosl.org ([127.0.0.1])\n by localhost (smtp4.osuosl.org [127.0.0.1]) (amavis, port 10024) with ESMTP\n id cnwEb0wEw11s for <dev@openvswitch.org>;\n Mon,  9 Jun 2025 17:35:59 +0000 (UTC)",
            "from us-smtp-delivery-124.mimecast.com\n (us-smtp-delivery-124.mimecast.com [170.10.133.124])\n by smtp4.osuosl.org (Postfix) with ESMTPS id F333041C9A\n for <dev@openvswitch.org>; Mon,  9 Jun 2025 17:35:53 +0000 (UTC)",
            "from mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com\n (ec2-54-186-198-63.us-west-2.compute.amazonaws.com [54.186.198.63]) by\n relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3,\n cipher=TLS_AES_256_GCM_SHA384) id us-mta-494-mCuDoJR7MtmqqepKtIDXrA-1; Mon,\n 09 Jun 2025 13:35:51 -0400",
            "from mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com\n (mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com [10.30.177.111])\n (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest\n SHA256)\n (No client certificate requested)\n by mx-prod-mc-01.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTPS\n id 5B0FB195609D\n for <dev@openvswitch.org>; Mon,  9 Jun 2025 17:35:50 +0000 (UTC)",
            "from localhost.localdomain.com (unknown [10.22.58.15])\n by mx-prod-int-08.mail-002.prod.us-west-2.aws.redhat.com (Postfix) with ESMTP\n id DD23718003FC\n for <dev@openvswitch.org>; Mon,  9 Jun 2025 17:35:49 +0000 (UTC)"
        ],
        "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 B6A8C820AB",
            "OpenDKIM Filter v2.11.0 smtp4.osuosl.org F333041C9A"
        ],
        "Received-SPF": "Pass (mailfrom) identity=mailfrom; client-ip=170.10.133.124;\n helo=us-smtp-delivery-124.mimecast.com; envelope-from=mmichels@redhat.com;\n receiver=<UNKNOWN>",
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 smtp4.osuosl.org F333041C9A",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com;\n s=mimecast20190719; t=1749490553;\n h=from:from:reply-to:subject:subject:date:date:message-id:message-id:\n to:to:cc:mime-version:mime-version:content-type:content-type:\n content-transfer-encoding:content-transfer-encoding:\n in-reply-to:in-reply-to:references:references;\n bh=hKVzcaaeZ7tpU2Mzg+abPc2csFwaqLgSQFAX9509zgk=;\n b=XMU0G92dYmjImwaoWWYl0qpT/P69TAOo5DkPuz3ORA0fTYmHfvAyFUO4TOeV5E5dNVl3oC\n c6LigAUlFRGGZ1nzygJ/wznQxfbAN7F1SWV8oc7YoERLgfA5fEW0+jbukMKpjjlHThbqsr\n tEBGGSfpNJnjSQqcEP1r3uGUqITkTFE=",
        "X-MC-Unique": "mCuDoJR7MtmqqepKtIDXrA-1",
        "X-Mimecast-MFC-AGG-ID": "mCuDoJR7MtmqqepKtIDXrA_1749490550",
        "To": "dev@openvswitch.org",
        "Date": "Mon,  9 Jun 2025 13:35:31 -0400",
        "Message-ID": "<20250609173539.1636916-13-mmichels@redhat.com>",
        "In-Reply-To": "<20250609173539.1636916-1-mmichels@redhat.com>",
        "References": "<20250609173539.1636916-1-mmichels@redhat.com>",
        "MIME-Version": "1.0",
        "X-Scanned-By": "MIMEDefang 3.4.1 on 10.30.177.111",
        "X-Mimecast-Spam-Score": "0",
        "X-Mimecast-MFC-PROC-ID": "ePqYcn7Q17EGryhY5NGk5C2i5JXA0ybTpFMiWPWJA30_1749490550",
        "X-Mimecast-Originator": "redhat.com",
        "Subject": "[ovs-dev] [PATCH ovn 12/14] datapaths: Add a hashvec of datapath\n bindings.",
        "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>",
        "From": "Mark Michelson via dev <ovs-dev@openvswitch.org>",
        "Reply-To": "Mark Michelson <mmichels@redhat.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": "The ovn_datapaths structure maintains an array of datapaths, allowing\nfor bitmaps to be used to indicate the relevant datapaths associated\nwith an entity, such as a logical flow.\n\nLogical flow syncing is attempting to rid itself of dependencies on\novn_datapaths, but the logical flow creation hinges on the datapath\nindices.\n\nThis commit addresses the issue by introducing a type-agnostic\novn_datapath_binding_hashvec structure that can be used to store\nsouthbound Datapath_Bindings in both a hashmap and an array. This allows\nfor us to correlate a southbound Datapath_Binding with a specific index\ninto its corresponding vector. The ovn_datapaths array is created based\non this vector, meaning that an ovn_datapath's index corresponds to the\nsame southbound Datapath_Binding's ovn_datapath_binding index.\n\nIn order to maintain consistency of indices, this commit also changes\nsynced datapath bindings to be a vector instead of a list, and sorts the\nvector with each run. This ensures that synced datapaths will contain\nthe same indices as long as new datapaths are not added or removed from\nthe database. If datapaths are added or removed, then northd has to\nrecompute, meaning that the ovn_datapaths will recreate their arrays and\nindices.\n\nSigned-off-by: Mark Michelson <mmichels@redhat.com>\n---\n northd/datapath_sync.c                       | 52 ++++++++++++++++\n northd/datapath_sync.h                       | 33 ++++++++--\n northd/en-datapath-logical-router.c          | 10 ++-\n northd/en-datapath-logical-router.h          |  7 ++-\n northd/en-datapath-logical-switch.c          | 10 ++-\n northd/en-datapath-logical-switch.h          |  5 +-\n northd/en-datapath-sync.c                    | 36 ++++++++---\n northd/en-lflow.c                            | 28 ++++-----\n northd/en-northd.c                           |  5 +-\n northd/en-port-binding-chassisredirect.c     |  8 +--\n northd/en-port-binding-logical-router-port.c |  2 +-\n northd/en-port-binding-logical-switch-port.c |  2 +-\n northd/en-port-binding-mirror.c              |  4 +-\n northd/northd.c                              | 64 +++++++++-----------\n northd/northd.h                              |  2 +-\n 15 files changed, 180 insertions(+), 88 deletions(-)",
    "diff": "diff --git a/northd/datapath_sync.c b/northd/datapath_sync.c\nindex 78e8cd8a1..94b953fe9 100644\n--- a/northd/datapath_sync.c\n+++ b/northd/datapath_sync.c\n@@ -15,7 +15,9 @@\n \n #include <config.h>\n \n+#include \"ovn-sb-idl.h\"\n #include \"datapath_sync.h\"\n+#include \"uuid.h\"\n \n static const char *ovn_datapath_strings [] = {\n     [DP_SWITCH] = \"logical-switch\",\n@@ -84,3 +86,53 @@ ovn_unsynced_datapath_map_destroy(struct ovn_unsynced_datapath_map *map)\n     }\n     hmap_destroy(&map->dps);\n }\n+\n+void\n+ovn_datapath_binding_hashvec_init(struct ovn_datapath_binding_hashvec *hashvec)\n+{\n+    hmap_init(&hashvec->bindings_map);\n+    hashvec->bindings_vec =\n+        VECTOR_EMPTY_INITIALIZER(struct ovn_datapath_binding *);\n+}\n+\n+void\n+ovn_datapath_binding_hashvec_destroy(\n+    struct ovn_datapath_binding_hashvec *hashvec)\n+{\n+    struct ovn_datapath_binding *binding;\n+    HMAP_FOR_EACH_POP (binding, hmap_node, &hashvec->bindings_map) {\n+        free(binding);\n+    }\n+    hmap_destroy(&hashvec->bindings_map);\n+    vector_destroy(&hashvec->bindings_vec);\n+}\n+\n+const struct ovn_datapath_binding *\n+ovn_datapath_binding_hashvec_add(\n+    struct ovn_datapath_binding_hashvec *hashvec,\n+    const struct sbrec_datapath_binding *sb)\n+{\n+    struct ovn_datapath_binding *binding = xmalloc(sizeof *binding);\n+    binding->sb = sb;\n+    binding->index = vector_len(&hashvec->bindings_vec);\n+    hmap_insert(&hashvec->bindings_map, &binding->hmap_node,\n+                uuid_hash(&sb->header_.uuid));\n+    vector_push(&hashvec->bindings_vec, &binding);\n+    return binding;\n+}\n+\n+const struct ovn_datapath_binding *\n+ovn_datapath_binding_find(const struct ovn_datapath_binding_hashvec *hashvec,\n+                          const struct sbrec_datapath_binding *sb)\n+{\n+    const struct ovn_datapath_binding *binding;\n+    size_t hash = uuid_hash(&sb->header_.uuid);\n+    HMAP_FOR_EACH_WITH_HASH (binding, hmap_node, hash,\n+                             &hashvec->bindings_map) {\n+        if (uuid_equals(&binding->sb->header_.uuid, &sb->header_.uuid)) {\n+            return binding;\n+        }\n+    }\n+\n+    return NULL;\n+}\ndiff --git a/northd/datapath_sync.h b/northd/datapath_sync.h\nindex efe4bb3e8..0feefae33 100644\n--- a/northd/datapath_sync.h\n+++ b/northd/datapath_sync.h\n@@ -17,7 +17,7 @@\n #define DATAPATH_SYNC_H 1\n \n #include \"openvswitch/hmap.h\"\n-#include \"openvswitch/list.h\"\n+#include \"vec.h\"\n #include \"smap.h\"\n \n /* Datapath syncing API. This file consists of utility functions\n@@ -30,7 +30,8 @@\n  * 2. The en_datapath_sync node takes all of the maps in as input and\n  * syncs them with southbound datapath bindings. This includes allocating\n  * tunnel keys across all datapath types. The output of this node is\n- * ovn_synced_datapaths, which contains a list of all synced datapaths.\n+ * ovn_synced_datapaths, which contains a sorted vector of all synced\n+ * datapaths.\n  * 3. A northbound type-aware node then takes the ovn_synced_datapaths,\n  * and decodes the generic synced datapaths back into a type-specific\n  * version (e.g. ovn_synced_logical_router). Later nodes can then consume\n@@ -66,13 +67,12 @@ struct ovn_unsynced_datapath_map {\n };\n \n struct ovn_synced_datapath {\n-    struct ovs_list list_node;\n     const struct ovsdb_idl_row *nb_row;\n     const struct sbrec_datapath_binding *sb_dp;\n };\n \n struct ovn_synced_datapaths {\n-    struct ovs_list synced_dps;\n+    struct vector synced_dps;\n };\n \n struct ovn_unsynced_datapath *ovn_unsynced_datapath_alloc(\n@@ -84,4 +84,29 @@ void ovn_unsynced_datapath_map_init(struct ovn_unsynced_datapath_map *map,\n                                     enum ovn_datapath_type dp_type);\n void ovn_unsynced_datapath_map_destroy(struct ovn_unsynced_datapath_map *map);\n \n+struct ovn_datapath_binding {\n+    struct hmap_node hmap_node;\n+    const struct sbrec_datapath_binding *sb;\n+    size_t index;\n+};\n+\n+struct ovn_datapath_binding_hashvec {\n+    struct hmap bindings_map;\n+    struct vector bindings_vec;\n+};\n+\n+void ovn_datapath_binding_hashvec_init(\n+    struct ovn_datapath_binding_hashvec *hashvec);\n+\n+void ovn_datapath_binding_hashvec_destroy(\n+    struct ovn_datapath_binding_hashvec *hashvec);\n+\n+const struct ovn_datapath_binding *\n+ovn_datapath_binding_hashvec_add(\n+    struct ovn_datapath_binding_hashvec *hashvec,\n+    const struct sbrec_datapath_binding *sb);\n+\n+const struct ovn_datapath_binding *\n+ovn_datapath_binding_find(const struct ovn_datapath_binding_hashvec *hashvec,\n+                          const struct sbrec_datapath_binding *sb);\n #endif /* DATAPATH_SYNC_H */\ndiff --git a/northd/en-datapath-logical-router.c b/northd/en-datapath-logical-router.c\nindex b8ec7073e..9d6e145ab 100644\n--- a/northd/en-datapath-logical-router.c\n+++ b/northd/en-datapath-logical-router.c\n@@ -117,6 +117,7 @@ synced_logical_router_map_init(\n     struct ovn_synced_logical_router_map *router_map)\n {\n     hmap_init(&router_map->synced_routers);\n+    ovn_datapath_binding_hashvec_init(&router_map->datapaths);\n }\n \n static void\n@@ -128,6 +129,7 @@ synced_logical_router_map_destroy(\n         free(lr);\n     }\n     hmap_destroy(&router_map->synced_routers);\n+    ovn_datapath_binding_hashvec_destroy(&router_map->datapaths);\n }\n \n void *\n@@ -152,14 +154,15 @@ en_datapath_synced_logical_router_run(struct engine_node *node , void *data)\n     synced_logical_router_map_init(router_map);\n \n     struct ovn_synced_datapath *sdp;\n-    LIST_FOR_EACH (sdp, list_node, &dps->synced_dps) {\n+    VECTOR_FOR_EACH (&dps->synced_dps, sdp) {\n         if (sdp->nb_row->table->class_ != &nbrec_table_logical_router) {\n             continue;\n         }\n         struct ovn_synced_logical_router *lr = xmalloc(sizeof *lr);\n         lr->nb = CONTAINER_OF(sdp->nb_row, struct nbrec_logical_router,\n                               header_);\n-        lr->sb = sdp->sb_dp;\n+        lr->binding = ovn_datapath_binding_hashvec_add(&router_map->datapaths,\n+                                                       sdp->sb_dp);\n         hmap_insert(&router_map->synced_routers, &lr->hmap_node,\n                     uuid_hash(&lr->nb->header_.uuid));\n     }\n@@ -179,7 +182,8 @@ ovn_synced_logical_router_find(const struct ovn_synced_logical_router_map *map,\n {\n     uint32_t hash = uuid_hash(nb_uuid);\n     const struct ovn_synced_logical_router *router;\n-    HMAP_FOR_EACH_WITH_HASH (router, hmap_node, hash, &map->synced_routers) {\n+    HMAP_FOR_EACH_WITH_HASH (router, hmap_node, hash,\n+                             &map->synced_routers) {\n         if (uuid_equals(&router->nb->header_.uuid, nb_uuid)) {\n             return router;\n         }\ndiff --git a/northd/en-datapath-logical-router.h b/northd/en-datapath-logical-router.h\nindex eb0923f9c..66d4f08db 100644\n--- a/northd/en-datapath-logical-router.h\n+++ b/northd/en-datapath-logical-router.h\n@@ -19,6 +19,7 @@\n \n #include \"lib/inc-proc-eng.h\"\n #include \"openvswitch/hmap.h\"\n+#include \"datapath_sync.h\"\n \n \n void *en_datapath_logical_router_init(struct engine_node *,\n@@ -31,11 +32,13 @@ void en_datapath_logical_router_cleanup(void *data);\n struct ovn_synced_logical_router {\n     struct hmap_node hmap_node;\n     const struct nbrec_logical_router *nb;\n-    const struct sbrec_datapath_binding *sb;\n+    const struct ovn_datapath_binding *binding;\n };\n \n struct ovn_synced_logical_router_map {\n+    /* Contains struct ovn_synced_logical_router */\n     struct hmap synced_routers;\n+    struct ovn_datapath_binding_hashvec datapaths;\n };\n \n void *en_datapath_synced_logical_router_init(struct engine_node *,\n@@ -49,6 +52,6 @@ void en_datapath_synced_logical_router_cleanup(void *data);\n struct uuid;\n const struct ovn_synced_logical_router *\n ovn_synced_logical_router_find(const struct ovn_synced_logical_router_map *map,\n-                               const struct uuid *nb_uuid);\n+                              const struct uuid *nb_uuid);\n \n #endif /* EN_DATAPATH_LOGICAL_ROUTER_H */\ndiff --git a/northd/en-datapath-logical-switch.c b/northd/en-datapath-logical-switch.c\nindex 6dd2adf3c..767f3c9ec 100644\n--- a/northd/en-datapath-logical-switch.c\n+++ b/northd/en-datapath-logical-switch.c\n@@ -115,6 +115,7 @@ synced_logical_switch_map_init(\n     struct ovn_synced_logical_switch_map *switch_map)\n {\n     hmap_init(&switch_map->synced_switches);\n+    ovn_datapath_binding_hashvec_init(&switch_map->datapaths);\n }\n \n static void\n@@ -126,6 +127,7 @@ synced_logical_switch_map_destroy(\n         free(ls);\n     }\n     hmap_destroy(&switch_map->synced_switches);\n+    ovn_datapath_binding_hashvec_destroy(&switch_map->datapaths);\n }\n \n void *\n@@ -150,14 +152,15 @@ en_datapath_synced_logical_switch_run(struct engine_node *node , void *data)\n     synced_logical_switch_map_init(switch_map);\n \n     struct ovn_synced_datapath *sdp;\n-    LIST_FOR_EACH (sdp, list_node, &dps->synced_dps) {\n+    VECTOR_FOR_EACH (&dps->synced_dps, sdp) {\n         if (sdp->nb_row->table->class_ != &nbrec_table_logical_switch) {\n             continue;\n         }\n         struct ovn_synced_logical_switch *lsw = xmalloc(sizeof *lsw);\n         lsw->nb = CONTAINER_OF(sdp->nb_row, struct nbrec_logical_switch,\n                                header_);\n-        lsw->sb = sdp->sb_dp;\n+        lsw->binding = ovn_datapath_binding_hashvec_add(&switch_map->datapaths,\n+                                                        sdp->sb_dp);\n         hmap_insert(&switch_map->synced_switches, &lsw->hmap_node,\n                     uuid_hash(&lsw->nb->header_.uuid));\n     }\n@@ -177,7 +180,8 @@ ovn_synced_logical_switch_find(const struct ovn_synced_logical_switch_map *map,\n {\n     uint32_t hash = uuid_hash(nb_uuid);\n     const struct ovn_synced_logical_switch *ls;\n-    HMAP_FOR_EACH_WITH_HASH (ls, hmap_node, hash, &map->synced_switches) {\n+    HMAP_FOR_EACH_WITH_HASH (ls, hmap_node, hash,\n+                             &map->synced_switches) {\n         if (uuid_equals(&ls->nb->header_.uuid, nb_uuid)) {\n             return ls;\n         }\ndiff --git a/northd/en-datapath-logical-switch.h b/northd/en-datapath-logical-switch.h\nindex 3f00b9996..7023218a3 100644\n--- a/northd/en-datapath-logical-switch.h\n+++ b/northd/en-datapath-logical-switch.h\n@@ -19,6 +19,7 @@\n \n #include \"lib/inc-proc-eng.h\"\n #include \"openvswitch/hmap.h\"\n+#include \"datapath_sync.h\"\n \n \n void *en_datapath_logical_switch_init(struct engine_node *,\n@@ -31,11 +32,13 @@ void en_datapath_logical_switch_cleanup(void *data);\n struct ovn_synced_logical_switch {\n     struct hmap_node hmap_node;\n     const struct nbrec_logical_switch *nb;\n-    const struct sbrec_datapath_binding *sb;\n+    const struct ovn_datapath_binding *binding;\n };\n \n struct ovn_synced_logical_switch_map {\n+    /* Contains struct ovn_synced_logical_switch */\n     struct hmap synced_switches;\n+    struct ovn_datapath_binding_hashvec datapaths;\n };\n \n void *en_datapath_synced_logical_switch_init(struct engine_node *,\ndiff --git a/northd/en-datapath-sync.c b/northd/en-datapath-sync.c\nindex 09a19abf8..3ed016850 100644\n--- a/northd/en-datapath-sync.c\n+++ b/northd/en-datapath-sync.c\n@@ -33,7 +33,8 @@ en_datapath_sync_init(struct engine_node *node OVS_UNUSED,\n {\n     struct ovn_synced_datapaths *synced_datapaths\n         = xmalloc(sizeof *synced_datapaths);\n-    ovs_list_init(&synced_datapaths->synced_dps);\n+    synced_datapaths->synced_dps =\n+        VECTOR_EMPTY_INITIALIZER(struct ovn_synced_datapath *);\n \n     return synced_datapaths;\n }\n@@ -97,10 +98,11 @@ static void\n reset_synced_datapaths(struct ovn_synced_datapaths *synced_datapaths)\n {\n     struct ovn_synced_datapath *sdp;\n-    LIST_FOR_EACH_POP (sdp, list_node, &synced_datapaths->synced_dps) {\n+    VECTOR_FOR_EACH (&synced_datapaths->synced_dps, sdp) {\n         free(sdp);\n     }\n-    ovs_list_init(&synced_datapaths->synced_dps);\n+    /* vector_destroy both destroys and inits the vector */\n+    vector_destroy(&synced_datapaths->synced_dps);\n }\n \n static void\n@@ -186,9 +188,8 @@ assign_requested_tunnel_keys(struct vector *candidate_sdps,\n         }\n         sbrec_datapath_binding_set_tunnel_key(candidate->sdp->sb_dp,\n                                               candidate->requested_tunnel_key);\n-        ovs_list_push_back(&synced_datapaths->synced_dps,\n-                           &candidate->sdp->list_node);\n         candidate->tunnel_key_assigned = true;\n+        vector_push(&synced_datapaths->synced_dps, &candidate->sdp);\n     }\n }\n \n@@ -207,9 +208,8 @@ assign_existing_tunnel_keys(struct vector *candidate_sdps,\n          * reuse it.\n          */\n         if (ovn_add_tnlid(dp_tnlids, candidate->existing_tunnel_key)) {\n-            ovs_list_push_back(&synced_datapaths->synced_dps,\n-                               &candidate->sdp->list_node);\n             candidate->tunnel_key_assigned = true;\n+            vector_push(&synced_datapaths->synced_dps, &candidate->sdp);\n         }\n     }\n }\n@@ -234,9 +234,8 @@ allocate_tunnel_keys(struct vector *candidate_sdps,\n         }\n         sbrec_datapath_binding_set_tunnel_key(candidate->sdp->sb_dp,\n                                               tunnel_key);\n-        ovs_list_push_back(&synced_datapaths->synced_dps,\n-                           &candidate->sdp->list_node);\n         candidate->tunnel_key_assigned = true;\n+        vector_push(&synced_datapaths->synced_dps, &candidate->sdp);\n     }\n }\n \n@@ -253,6 +252,17 @@ delete_unassigned_candidates(struct vector *candidate_sdps)\n     }\n }\n \n+static int\n+synced_dp_cmp(const void *a_, const void *b_)\n+{\n+    const struct ovn_synced_datapath *const *a__ = a_;\n+    const struct ovn_synced_datapath *const *b__ = b_;\n+    const struct ovn_synced_datapath *a = *a__;\n+    const struct ovn_synced_datapath *b = *b__;\n+\n+    return uuid_compare_3way(&a->nb_row->uuid, &b->nb_row->uuid);\n+}\n+\n enum engine_node_state\n en_datapath_sync_run(struct engine_node *node , void *data)\n {\n@@ -303,6 +313,11 @@ en_datapath_sync_run(struct engine_node *node , void *data)\n \n     ovn_destroy_tnlids(&dp_tnlids);\n \n+    /* Sort the synced_datapaths. This ensures the order is consistent between\n+     * runs when no new datapaths have been inserted or deleted.\n+     */\n+    vector_qsort(&synced_datapaths->synced_dps, synced_dp_cmp);\n+\n     return EN_UPDATED;\n }\n \n@@ -311,7 +326,8 @@ void en_datapath_sync_cleanup(void *data)\n     struct ovn_synced_datapaths *synced_datapaths = data;\n     struct ovn_synced_datapath *sdp;\n \n-    LIST_FOR_EACH_POP (sdp, list_node, &synced_datapaths->synced_dps) {\n+    VECTOR_FOR_EACH (&synced_datapaths->synced_dps, sdp) {\n         free(sdp);\n     }\n+    vector_destroy(&synced_datapaths->synced_dps);\n }\ndiff --git a/northd/en-lflow.c b/northd/en-lflow.c\nindex 07d7c7882..e9363a2e4 100644\n--- a/northd/en-lflow.c\n+++ b/northd/en-lflow.c\n@@ -31,6 +31,7 @@\n #include \"lflow-mgr.h\"\n #include \"en-datapath-logical-switch.h\"\n #include \"en-datapath-logical-router.h\"\n+#include \"datapath_sync.h\"\n \n #include \"lib/inc-proc-eng.h\"\n #include \"northd.h\"\n@@ -314,25 +315,20 @@ datapath_is_valid(const struct sbrec_datapath_binding *dp,\n     if (dp_type == DP_MAX) {\n         return false;\n     }\n-    struct uuid nb_dp_key;\n-    if (!smap_get_uuid(&dp->external_ids, \"nb_uuid\", &nb_dp_key)) {\n+    const struct ovn_datapath_binding_hashvec *hashvec;\n+    switch (dp_type) {\n+    case DP_SWITCH:\n+        hashvec = &synced_lses->datapaths;\n+        break;\n+    case DP_ROUTER:\n+        hashvec = &synced_lrs->datapaths;\n+        break;\n+    case DP_MAX:\n+    default:\n         return false;\n     }\n-    if (dp_type == DP_SWITCH) {\n-        if (ovn_synced_logical_switch_find(synced_lses, &nb_dp_key)) {\n-            return true;\n-        } else {\n-            return false;\n-        }\n-    } else if (dp_type == DP_ROUTER) {\n-        if (ovn_synced_logical_router_find(synced_lrs, &nb_dp_key)) {\n-            return true;\n-        } else {\n-            return false;\n-        }\n-    }\n \n-    return false;\n+    return ovn_datapath_binding_find(hashvec, dp) != NULL;\n }\n \n static void\ndiff --git a/northd/en-northd.c b/northd/en-northd.c\nindex 15840e361..3a805106c 100644\n--- a/northd/en-northd.c\n+++ b/northd/en-northd.c\n@@ -146,9 +146,8 @@ en_northd_run(struct engine_node *node, void *data)\n     struct northd_input input_data;\n \n     northd_destroy(data);\n-    northd_init(data);\n-\n     northd_get_input_data(node, &input_data);\n+    northd_init(data, &input_data);\n \n     COVERAGE_INC(northd_run);\n     stopwatch_start(OVNNB_DB_RUN_STOPWATCH_NAME, time_msec());\n@@ -433,7 +432,7 @@ void\n {\n     struct northd_data *data = xzalloc(sizeof *data);\n \n-    northd_init(data);\n+    northd_init(data, NULL);\n \n     return data;\n }\ndiff --git a/northd/en-port-binding-chassisredirect.c b/northd/en-port-binding-chassisredirect.c\nindex 05e17472b..474d6170a 100644\n--- a/northd/en-port-binding-chassisredirect.c\n+++ b/northd/en-port-binding-chassisredirect.c\n@@ -197,8 +197,8 @@ en_port_binding_chassisredirect_port_run(struct engine_node *node, void *data)\n             struct chassisredirect_port *crp =\n                 chassisredirect_router_port_alloc(nbrp);\n             upb = ovn_unpaired_port_binding_alloc(0, crp->name,\n-                                                  PB_CHASSISREDIRECT_PORT,\n-                                                  crp, rdgps->lr->sb);\n+                                                  PB_CHASSISREDIRECT_PORT, crp,\n+                                                  rdgps->lr->binding->sb);\n             shash_add(&map->ports, crp->name, upb);\n         }\n     }\n@@ -235,8 +235,8 @@ en_port_binding_chassisredirect_port_run(struct engine_node *node, void *data)\n             struct chassisredirect_port *crp =\n                 chassisredirect_switch_port_alloc(nbsp);\n             upb = ovn_unpaired_port_binding_alloc(0, crp->name,\n-                                                  PB_CHASSISREDIRECT_PORT,\n-                                                  crp, localnets->ls->sb);\n+                                                  PB_CHASSISREDIRECT_PORT, crp,\n+                                                  localnets->ls->binding->sb);\n             shash_add(&map->ports, crp->name, upb);\n         }\n     }\ndiff --git a/northd/en-port-binding-logical-router-port.c b/northd/en-port-binding-logical-router-port.c\nindex d47ba2e26..dfa78b9b2 100644\n--- a/northd/en-port-binding-logical-router-port.c\n+++ b/northd/en-port-binding-logical-router-port.c\n@@ -97,7 +97,7 @@ en_port_binding_logical_router_port_run(struct engine_node *node, void *data)\n                                                   nbrp->name,\n                                                   PB_ROUTER_PORT,\n                                                   cookie,\n-                                                  paired_lr->sb);\n+                                                  paired_lr->binding->sb);\n             smap_clone(&upb->external_ids, &nbrp->external_ids);\n             if (!shash_add_once(&map->ports, nbrp->name, upb)) {\n                 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);\ndiff --git a/northd/en-port-binding-logical-switch-port.c b/northd/en-port-binding-logical-switch-port.c\nindex eca1fe85d..ec8e5aa42 100644\n--- a/northd/en-port-binding-logical-switch-port.c\n+++ b/northd/en-port-binding-logical-switch-port.c\n@@ -117,7 +117,7 @@ en_port_binding_logical_switch_port_run(struct engine_node *node, void *data)\n                                                   nbsp->name,\n                                                   PB_SWITCH_PORT,\n                                                   cookie,\n-                                                  paired_ls->sb);\n+                                                  paired_ls->binding->sb);\n             smap_clone(&upb->external_ids, &nbsp->external_ids);\n             const char *name = smap_get(&nbsp->external_ids,\n                                         \"neutron:port_name\");\ndiff --git a/northd/en-port-binding-mirror.c b/northd/en-port-binding-mirror.c\nindex f79d56259..a4c782d4b 100644\n--- a/northd/en-port-binding-mirror.c\n+++ b/northd/en-port-binding-mirror.c\n@@ -107,13 +107,13 @@ en_port_binding_mirror_run(struct engine_node *node, void *data)\n                 if (!sset_find(&all_switch_ports, nb_mirror->sink)) {\n                     continue;\n                 }\n-                struct mirror_port *mp = mirror_port_alloc(ls->sb,\n+                struct mirror_port *mp = mirror_port_alloc(ls->binding->sb,\n                                                            nb_mirror->sink,\n                                                            nbsp);\n                 struct ovn_unpaired_port_binding *upb;\n                 upb = ovn_unpaired_port_binding_alloc(0, mp->name,\n                                                       PB_MIRROR_PORT, mp,\n-                                                      ls->sb);\n+                                                      ls->binding->sb);\n                 shash_add(&map->ports, mp->name, upb);\n             }\n         }\ndiff --git a/northd/northd.c b/northd/northd.c\nindex 39323aa5f..096c5f09c 100644\n--- a/northd/northd.c\n+++ b/northd/northd.c\n@@ -505,17 +505,26 @@ struct lrouter_group {\n static void init_mcast_info_for_datapath(struct ovn_datapath *od);\n \n static struct ovn_datapath *\n-ovn_datapath_create(struct hmap *datapaths, const struct uuid *key,\n+ovn_datapath_create(struct ovn_datapaths *datapaths, const struct uuid *key,\n                     const struct nbrec_logical_switch *nbs,\n                     const struct nbrec_logical_router *nbr,\n-                    const struct sbrec_datapath_binding *sb)\n+                    const struct sbrec_datapath_binding *sb,\n+                    size_t index)\n {\n     struct ovn_datapath *od = xzalloc(sizeof *od);\n     od->key = *key;\n     od->sb = sb;\n     od->nbs = nbs;\n     od->nbr = nbr;\n-    hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key));\n+    hmap_insert(&datapaths->datapaths, &od->key_node, uuid_hash(&od->key));\n+    /* Datapath indices correspond with the southbound datapath vector for\n+     * the particular datapath type. This ensures that whether objects derive\n+     * their own indices/bitmaps from the datapaths array or the sb_vec,\n+     * the indices are consistent.\n+     */\n+    datapaths->array[index] = od;\n+    od->index = index;\n+    od->datapaths = datapaths;\n     od->lr_group = NULL;\n     hmap_init(&od->ports);\n     sset_init(&od->router_ips);\n@@ -829,25 +838,6 @@ parse_dynamic_routing_redistribute(\n     return out;\n }\n \n-static void\n-ods_build_array_index(struct ovn_datapaths *datapaths)\n-{\n-    /* Assign unique sequential indexes to all datapaths.  These are not\n-     * visible outside of the northd loop, so, unlike the tunnel keys, it\n-     * doesn't matter if they are different on every iteration. */\n-    size_t index = 0;\n-\n-    datapaths->array = xrealloc(datapaths->array,\n-                            ods_size(datapaths) * sizeof *datapaths->array);\n-\n-    struct ovn_datapath *od;\n-    HMAP_FOR_EACH (od, key_node, &datapaths->datapaths) {\n-        od->index = index;\n-        datapaths->array[index++] = od;\n-        od->datapaths = datapaths;\n-    }\n-}\n-\n /* Initializes 'ls_datapaths' to contain a \"struct ovn_datapath\" for every\n  * logical switch, and initializes 'lr_datapaths' to contain a\n  * \"struct ovn_datapath\" for every logical router.\n@@ -861,9 +851,10 @@ build_datapaths(const struct ovn_synced_logical_switch_map *ls_map,\n     struct ovn_synced_logical_switch *ls;\n     HMAP_FOR_EACH (ls, hmap_node, &ls_map->synced_switches) {\n         struct ovn_datapath *od =\n-            ovn_datapath_create(&ls_datapaths->datapaths,\n+            ovn_datapath_create(ls_datapaths,\n                                 &ls->nb->header_.uuid,\n-                                ls->nb, NULL, ls->sb);\n+                                ls->nb, NULL, ls->binding->sb,\n+                                ls->binding->index);\n         init_ipam_info_for_datapath(od);\n         if (smap_get_bool(&od->nbs->other_config,\n                           \"enable-stateless-acl-with-lb\",\n@@ -875,9 +866,10 @@ build_datapaths(const struct ovn_synced_logical_switch_map *ls_map,\n     struct ovn_synced_logical_router *lr;\n     HMAP_FOR_EACH (lr, hmap_node, &lr_map->synced_routers) {\n         struct ovn_datapath *od =\n-            ovn_datapath_create(&lr_datapaths->datapaths,\n+            ovn_datapath_create(lr_datapaths,\n                                 &lr->nb->header_.uuid,\n-                                NULL, lr->nb, lr->sb);\n+                                NULL, lr->nb, lr->binding->sb,\n+                                lr->binding->index);\n         if (smap_get(&od->nbr->options, \"chassis\")) {\n             od->is_gw_router = true;\n         }\n@@ -887,9 +879,6 @@ build_datapaths(const struct ovn_synced_logical_switch_map *ls_map,\n             parse_dynamic_routing_redistribute(&od->nbr->options, DRRM_NONE,\n                                                od->nbr->name);\n     }\n-\n-    ods_build_array_index(ls_datapaths);\n-    ods_build_array_index(lr_datapaths);\n }\n \n static bool lsp_can_be_inc_processed(const struct nbrec_logical_switch_port *);\n@@ -2053,7 +2042,7 @@ join_logical_ports(\n     struct shash_node *node;\n     SHASH_FOR_EACH (node, &paired_lrps->paired_router_ports) {\n         struct ovn_paired_logical_router_port *slrp = node->data;\n-        od = ovn_datapath_from_sbrec_(lr_datapaths, slrp->router->sb);\n+        od = ovn_datapath_from_sbrec_(lr_datapaths, slrp->router->binding->sb);\n         if (!od) {\n             /* This can happen if the router is not enabled */\n             continue;\n@@ -2072,8 +2061,7 @@ join_logical_ports(\n \n     SHASH_FOR_EACH (node, &paired_lsps->paired_switch_ports) {\n         struct ovn_paired_logical_switch_port *slsp = node->data;\n-        od = ovn_datapath_from_sbrec_(ls_datapaths, slsp->sw->sb);\n-\n+        od = ovn_datapath_from_sbrec_(ls_datapaths, slsp->sw->binding->sb);\n         ovs_assert(od);\n \n         join_logical_ports_lsp(ls_ports, od, slsp->nb, slsp->sb,\n@@ -18462,10 +18450,10 @@ build_static_mac_binding_table(\n }\n \f\n static void\n-ovn_datapaths_init(struct ovn_datapaths *datapaths)\n+ovn_datapaths_init(struct ovn_datapaths *datapaths, size_t n_datapaths)\n {\n     hmap_init(&datapaths->datapaths);\n-    datapaths->array = NULL;\n+    datapaths->array = xcalloc(n_datapaths, sizeof *datapaths->array);\n }\n \n static void\n@@ -18518,10 +18506,12 @@ destroy_datapaths_and_ports(struct ovn_datapaths *ls_datapaths,\n }\n \n void\n-northd_init(struct northd_data *data)\n+northd_init(struct northd_data *data, const struct northd_input *ni)\n {\n-    ovn_datapaths_init(&data->ls_datapaths);\n-    ovn_datapaths_init(&data->lr_datapaths);\n+    ovn_datapaths_init(&data->ls_datapaths,\n+        ni ? vector_len(&ni->synced_lses->datapaths.bindings_vec) : 0);\n+    ovn_datapaths_init(&data->lr_datapaths,\n+        ni ? vector_len(&ni->synced_lrs->datapaths.bindings_vec) : 0);\n     hmap_init(&data->ls_ports);\n     hmap_init(&data->lr_ports);\n     hmap_init(&data->lb_datapaths_map);\ndiff --git a/northd/northd.h b/northd/northd.h\nindex 23c2fad49..74ef05892 100644\n--- a/northd/northd.h\n+++ b/northd/northd.h\n@@ -834,7 +834,7 @@ bool northd_handle_lr_changes(const struct northd_input *,\n                               struct northd_data *);\n void destroy_northd_data_tracked_changes(struct northd_data *);\n void northd_destroy(struct northd_data *data);\n-void northd_init(struct northd_data *data);\n+void northd_init(struct northd_data *data, const struct northd_input *);\n void northd_indices_create(struct northd_data *data,\n                            struct ovsdb_idl *ovnsb_idl);\n \n",
    "prefixes": [
        "ovs-dev",
        "12/14"
    ]
}