diff mbox series

[ovs-dev,1/2] controller: incrementally create ipv6 prefix delegation port_binding list

Message ID 2f9670104e1c94fe73048b1849200e9943a4f230.1625680335.git.lorenzo.bianconi@redhat.com
State Superseded
Headers show
Series incrementally process ras-ipv6 pd router ports | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test fail github build: failed
ovsrobot/github-robot-_ovn-kubernetes success github build: passed

Commit Message

Lorenzo Bianconi July 7, 2021, 5:54 p.m. UTC
Incrementally manage local_active_ports_ipv6_pd map for interfaces
where IPv6 prefix-delegation has been enabled. This patch allows to
avoid looping over all local interfaces to check if prefix-delegation
is running on the current port binding.

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 controller/binding.c        |  35 ++++++++++++
 controller/binding.h        |   1 +
 controller/ovn-controller.c |  25 ++++++++-
 controller/ovn-controller.h |   8 +++
 controller/pinctrl.c        | 109 ++++++++++++++++++------------------
 controller/pinctrl.h        |   3 +-
 6 files changed, 124 insertions(+), 57 deletions(-)

Comments

Mark Michelson July 8, 2021, 7:09 p.m. UTC | #1
On 7/7/21 1:54 PM, Lorenzo Bianconi wrote:
> Incrementally manage local_active_ports_ipv6_pd map for interfaces
> where IPv6 prefix-delegation has been enabled. This patch allows to
> avoid looping over all local interfaces to check if prefix-delegation
> is running on the current port binding.
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> ---
>   controller/binding.c        |  35 ++++++++++++
>   controller/binding.h        |   1 +
>   controller/ovn-controller.c |  25 ++++++++-
>   controller/ovn-controller.h |   8 +++
>   controller/pinctrl.c        | 109 ++++++++++++++++++------------------
>   controller/pinctrl.h        |   3 +-
>   6 files changed, 124 insertions(+), 57 deletions(-)
> 
> diff --git a/controller/binding.c b/controller/binding.c
> index 594babc98..2cec7ea84 100644
> --- a/controller/binding.c
> +++ b/controller/binding.c
> @@ -574,6 +574,33 @@ remove_related_lport(const struct sbrec_port_binding *pb,
>       }
>   }
>   
> +static void
> +update_active_pb_ras_pd(const struct sbrec_port_binding *pb,
> +                        struct hmap *local_datapaths,
> +                        struct hmap *map, const char *conf)
> +{
> +    const char *ras_pd_conf = smap_get(&pb->options, conf);
> +    if (!ras_pd_conf) {
> +        return;
> +    }
> +
> +    struct pb_active_ra_pd *ras_pd =
> +        get_pb_active_ras_pd(map, pb->logical_port);
> +    if (ras_pd && !strcmp(ras_pd_conf, "false")) {
> +        hmap_remove(map, &ras_pd->hmap_node);
> +        return;
> +    }

Let's say that a port binding has set "ipv6_prefix_delegation" to 
"true". This function runs, and so the ras_pd is allocated and added to 
the map.

Now something changes and the operator removes the 
ipv6_prefix_delegation from the port_binding.

Now I would suspect that when this function runs, the ras_pd should be 
found and removed from the map. However, since the option was removed, 
the function will return early, and the ras_pd will remain in the map.

Is this behavior intentional? I would suspect that if the option is 
removed, then ras_pd should be removed from the map.

> +    if (!ras_pd && !strcmp(ras_pd_conf, "true")) {
> +        ras_pd = xzalloc(sizeof *ras_pd);
> +        ras_pd->pb = pb;
> +        hmap_insert(map, &ras_pd->hmap_node, hash_string(pb->logical_port, 0));
> +    }
> +    if (ras_pd) {
> +        ras_pd->ld = get_local_datapath(local_datapaths,
> +                                        pb->datapath->tunnel_key);
> +    }
> +}
> +
>   /* Corresponds to each Port_Binding.type. */
>   enum en_lport_type {
>       LP_UNKNOWN,
> @@ -1645,6 +1672,10 @@ binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out)
>       const struct sbrec_port_binding *pb;
>       SBREC_PORT_BINDING_TABLE_FOR_EACH (pb,
>                                          b_ctx_in->port_binding_table) {
> +        update_active_pb_ras_pd(pb, b_ctx_out->local_datapaths,
> +                                b_ctx_out->local_active_ports_ipv6_pd,
> +                                "ipv6_prefix_delegation");
> +
>           enum en_lport_type lport_type = get_lport_type(pb);
>   
>           switch (lport_type) {
> @@ -2482,6 +2513,10 @@ delete_done:
>               continue;
>           }
>   
> +        update_active_pb_ras_pd(pb, b_ctx_out->local_datapaths,
> +                                b_ctx_out->local_active_ports_ipv6_pd,
> +                                "ipv6_prefix_delegation");
> +
>           enum en_lport_type lport_type = get_lport_type(pb);
>   
>           struct binding_lport *b_lport =
> diff --git a/controller/binding.h b/controller/binding.h
> index a08011ae2..5a6f46a14 100644
> --- a/controller/binding.h
> +++ b/controller/binding.h
> @@ -72,6 +72,7 @@ void related_lports_destroy(struct related_lports *);
>   
>   struct binding_ctx_out {
>       struct hmap *local_datapaths;
> +    struct hmap *local_active_ports_ipv6_pd;

Since the hmap nodes are keyed on a string, would it make more sense to 
make this struct shash instead of struct hmap? It would eliminate the 
need for get_pb_active_ras_pd() since it could be replaced with a call 
to shash_find_data() instead.

>       struct local_binding_data *lbinding_data;
>   
>       /* sset of (potential) local lports. */
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 9050380f3..a42b96ddb 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -133,6 +133,20 @@ get_local_datapath(const struct hmap *local_datapaths, uint32_t tunnel_key)
>               : NULL);
>   }
>   
> +struct pb_active_ra_pd *
> +get_pb_active_ras_pd(const struct hmap *map, const char *name)
> +{
> +    uint32_t key = hash_string(name, 0);
> +    struct hmap_node *node;
> +
> +    node = hmap_first_with_hash(map, key);
> +    if (node) {
> +        return CONTAINER_OF(node, struct pb_active_ra_pd, hmap_node);
> +    }
> +
> +    return NULL;
> +}
> +
>   uint32_t
>   get_tunnel_type(const char *name)
>   {
> @@ -1028,6 +1042,8 @@ struct ed_type_runtime_data {
>       bool tracked;
>       bool local_lports_changed;
>       struct hmap tracked_dp_bindings;
> +
> +    struct hmap local_active_ports_ipv6_pd;

I think this could also be replaced with a struct shash.

>   };
>   
>   /* struct ed_type_runtime_data has the below members for tracking the
> @@ -1115,6 +1131,7 @@ en_runtime_data_init(struct engine_node *node OVS_UNUSED,
>       sset_init(&data->egress_ifaces);
>       smap_init(&data->local_iface_ids);
>       local_binding_data_init(&data->lbinding_data);
> +    hmap_init(&data->local_active_ports_ipv6_pd);
>   
>       /* Init the tracked data. */
>       hmap_init(&data->tracked_dp_bindings);
> @@ -1140,6 +1157,7 @@ en_runtime_data_cleanup(void *data)
>           free(cur_node);
>       }
>       hmap_destroy(&rt_data->local_datapaths);
> +    hmap_destroy(&rt_data->local_active_ports_ipv6_pd);
>       local_binding_data_destroy(&rt_data->lbinding_data);
>   }
>   
> @@ -1218,6 +1236,8 @@ init_binding_ctx(struct engine_node *node,
>       b_ctx_in->ovs_table = ovs_table;
>   
>       b_ctx_out->local_datapaths = &rt_data->local_datapaths;
> +    b_ctx_out->local_active_ports_ipv6_pd =
> +        &rt_data->local_active_ports_ipv6_pd;
>       b_ctx_out->local_lports = &rt_data->local_lports;
>       b_ctx_out->local_lports_changed = false;
>       b_ctx_out->related_lports = &rt_data->related_lports;
> @@ -1235,6 +1255,7 @@ en_runtime_data_run(struct engine_node *node, void *data)
>   {
>       struct ed_type_runtime_data *rt_data = data;
>       struct hmap *local_datapaths = &rt_data->local_datapaths;
> +    struct hmap *local_active_ipv6_pd = &rt_data->local_active_ports_ipv6_pd;
>       struct sset *local_lports = &rt_data->local_lports;
>       struct sset *active_tunnels = &rt_data->active_tunnels;
>   
> @@ -1250,6 +1271,7 @@ en_runtime_data_run(struct engine_node *node, void *data)
>               free(cur_node);
>           }
>           hmap_clear(local_datapaths);
> +        hmap_clear(local_active_ipv6_pd);
>           local_binding_data_destroy(&rt_data->lbinding_data);
>           sset_destroy(local_lports);
>           related_lports_destroy(&rt_data->related_lports);
> @@ -3263,7 +3285,8 @@ main(int argc, char *argv[])
>                                       sbrec_bfd_table_get(ovnsb_idl_loop.idl),
>                                       br_int, chassis,
>                                       &runtime_data->local_datapaths,
> -                                    &runtime_data->active_tunnels);
> +                                    &runtime_data->active_tunnels,
> +                                    &runtime_data->local_active_ports_ipv6_pd);
>                           /* Updating monitor conditions if runtime data or
>                            * logical datapath goups changed. */
>                           if (engine_node_changed(&en_runtime_data)
> diff --git a/controller/ovn-controller.h b/controller/ovn-controller.h
> index 5d9466880..cb054109e 100644
> --- a/controller/ovn-controller.h
> +++ b/controller/ovn-controller.h
> @@ -71,6 +71,8 @@ struct local_datapath {
>   
>   struct local_datapath *get_local_datapath(const struct hmap *,
>                                             uint32_t tunnel_key);
> +struct pb_active_ra_pd *
> +get_pb_active_ras_pd(const struct hmap *map, const char *name);
>   
>   const struct ovsrec_bridge *get_bridge(const struct ovsrec_bridge_table *,
>                                          const char *br_name);
> @@ -87,4 +89,10 @@ enum chassis_tunnel_type {
>   
>   uint32_t get_tunnel_type(const char *name);
>   
> +struct pb_active_ra_pd {
> +    const struct sbrec_port_binding *pb;
> +    const struct local_datapath *ld;
> +    struct hmap_node hmap_node;
> +};

I think this structure should be renamed. The name of the structure 
implies port bindings with active router advertisements and/or prefix 
delegation. However, the structure itself is simply allowing looking up 
a (port binding, local datapath) pair using a hash. This could be used 
for much more than just IPv6 router advertisements and prefix 
delegation. The current usage of this structure is only for those 
purposes, but that shouldn't stop others from using such a structure if 
desired.

> +
>   #endif /* controller/ovn-controller.h */
> diff --git a/controller/pinctrl.c b/controller/pinctrl.c
> index 78ecfed84..1a74af872 100644
> --- a/controller/pinctrl.c
> +++ b/controller/pinctrl.c
> @@ -1249,80 +1249,77 @@ fill_ipv6_prefix_state(struct ovsdb_idl_txn *ovnsb_idl_txn,
>   static void
>   prepare_ipv6_prefixd(struct ovsdb_idl_txn *ovnsb_idl_txn,
>                        struct ovsdb_idl_index *sbrec_port_binding_by_name,
> -                     const struct hmap *local_datapaths,
> +                     const struct hmap *local_active_ports_ipv6_pd,
>                        const struct sbrec_chassis *chassis,
>                        const struct sset *active_tunnels)
>       OVS_REQUIRES(pinctrl_mutex)
>   {
> -    const struct local_datapath *ld;
> +    const struct pb_active_ra_pd *pb_ipv6;
>       bool changed = false;
>   
> -    HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
> -        if (datapath_is_switch(ld->datapath)) {
> -            /* logical switch */
> +    HMAP_FOR_EACH (pb_ipv6, hmap_node, local_active_ports_ipv6_pd) {
> +        const struct sbrec_port_binding *pb = pb_ipv6->pb;
> +        int j;
> +
> +        if (!smap_get_bool(&pb->options, "ipv6_prefix_delegation",
> +                           false)) {
>               continue;
>           }
>   
> -        for (size_t i = 0; i < ld->n_peer_ports; i++) {
> -            const struct sbrec_port_binding *pb = ld->peer_ports[i].local;
> -            int j;
> +        if (!pb_ipv6->ld) {
> +            continue;
> +        }
>   
> -            if (!smap_get_bool(&pb->options, "ipv6_prefix_delegation",
> -                               false)) {
> -                continue;
> -            }
> +        const char *peer_s = smap_get(&pb->options, "peer");
> +        if (!peer_s) {
> +            continue;
> +        }
>   
> -            const char *peer_s = smap_get(&pb->options, "peer");
> -            if (!peer_s) {
> -                continue;
> -            }
> +        const struct sbrec_port_binding *peer
> +            = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s);
> +        if (!peer) {
> +            continue;
> +        }
>   
> -            const struct sbrec_port_binding *peer
> -                = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s);
> -            if (!peer) {
> -                continue;
> -            }
> +        char *redirect_name = xasprintf("cr-%s", pb->logical_port);
> +        bool resident = lport_is_chassis_resident(
> +                sbrec_port_binding_by_name, chassis, active_tunnels,
> +                redirect_name);
> +        free(redirect_name);
> +        if (!resident && strcmp(pb->type, "l3gateway")) {
> +            continue;
> +        }
>   
> -            char *redirect_name = xasprintf("cr-%s", pb->logical_port);
> -            bool resident = lport_is_chassis_resident(
> -                    sbrec_port_binding_by_name, chassis, active_tunnels,
> -                    redirect_name);
> -            free(redirect_name);
> -            if (!resident && strcmp(pb->type, "l3gateway")) {
> +        struct in6_addr ip6_addr;
> +        struct eth_addr ea = eth_addr_zero;
> +        for (j = 0; j < pb->n_mac; j++) {
> +            struct lport_addresses laddrs;
> +
> +            if (!extract_lsp_addresses(pb->mac[j], &laddrs)) {
>                   continue;
>               }
>   
> -            struct in6_addr ip6_addr;
> -            struct eth_addr ea = eth_addr_zero;
> -            for (j = 0; j < pb->n_mac; j++) {
> -                struct lport_addresses laddrs;
> -
> -                if (!extract_lsp_addresses(pb->mac[j], &laddrs)) {
> -                    continue;
> -                }
> -
> -                ea = laddrs.ea;
> -                if (laddrs.n_ipv6_addrs > 0) {
> -                    ip6_addr = laddrs.ipv6_addrs[0].addr;
> -                    destroy_lport_addresses(&laddrs);
> -                    break;
> -                }
> +            ea = laddrs.ea;
> +            if (laddrs.n_ipv6_addrs > 0) {
> +                ip6_addr = laddrs.ipv6_addrs[0].addr;
>                   destroy_lport_addresses(&laddrs);
> +                break;
>               }
> +            destroy_lport_addresses(&laddrs);
> +        }
>   
> -            if (eth_addr_is_zero(ea)) {
> -                continue;
> -            }
> -
> -            if (j == pb->n_mac) {
> -                in6_generate_lla(ea, &ip6_addr);
> -            }
> +        if (eth_addr_is_zero(ea)) {
> +            continue;
> +        }
>   
> -            changed |= fill_ipv6_prefix_state(ovnsb_idl_txn, ld,
> -                                              ea, ip6_addr,
> -                                              peer->tunnel_key,
> -                                              peer->datapath->tunnel_key);
> +        if (j == pb->n_mac) {
> +            in6_generate_lla(ea, &ip6_addr);
>           }
> +
> +        changed |= fill_ipv6_prefix_state(ovnsb_idl_txn, pb_ipv6->ld,
> +                                          ea, ip6_addr,
> +                                          peer->tunnel_key,
> +                                          peer->datapath->tunnel_key);
>       }
>   
>       struct shash_node *iter, *next;
> @@ -3411,7 +3408,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>               const struct ovsrec_bridge *br_int,
>               const struct sbrec_chassis *chassis,
>               const struct hmap *local_datapaths,
> -            const struct sset *active_tunnels)
> +            const struct sset *active_tunnels,
> +            const struct hmap *local_active_ports_ipv6_pd)
>   {
>       ovs_mutex_lock(&pinctrl_mutex);
>       pinctrl_set_br_int_name_(br_int->name);
> @@ -3426,7 +3424,8 @@ pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>                              local_datapaths, active_tunnels);
>       prepare_ipv6_ras(local_datapaths);
>       prepare_ipv6_prefixd(ovnsb_idl_txn, sbrec_port_binding_by_name,
> -                         local_datapaths, chassis, active_tunnels);
> +                         local_active_ports_ipv6_pd, chassis,
> +                         active_tunnels);
>       sync_dns_cache(dns_table);
>       controller_event_run(ovnsb_idl_txn, ce_table, chassis);
>       ip_mcast_sync(ovnsb_idl_txn, chassis, local_datapaths,
> diff --git a/controller/pinctrl.h b/controller/pinctrl.h
> index cc0a51984..9d98ee72e 100644
> --- a/controller/pinctrl.h
> +++ b/controller/pinctrl.h
> @@ -49,7 +49,8 @@ void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
>                    const struct sbrec_bfd_table *,
>                    const struct ovsrec_bridge *, const struct sbrec_chassis *,
>                    const struct hmap *local_datapaths,
> -                 const struct sset *active_tunnels);
> +                 const struct sset *active_tunnels,
> +                 const struct hmap *local_active_ports_ipv6_pd);
>   void pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn);
>   void pinctrl_destroy(void);
>   void pinctrl_set_br_int_name(char *br_int_name);
>
diff mbox series

Patch

diff --git a/controller/binding.c b/controller/binding.c
index 594babc98..2cec7ea84 100644
--- a/controller/binding.c
+++ b/controller/binding.c
@@ -574,6 +574,33 @@  remove_related_lport(const struct sbrec_port_binding *pb,
     }
 }
 
+static void
+update_active_pb_ras_pd(const struct sbrec_port_binding *pb,
+                        struct hmap *local_datapaths,
+                        struct hmap *map, const char *conf)
+{
+    const char *ras_pd_conf = smap_get(&pb->options, conf);
+    if (!ras_pd_conf) {
+        return;
+    }
+
+    struct pb_active_ra_pd *ras_pd =
+        get_pb_active_ras_pd(map, pb->logical_port);
+    if (ras_pd && !strcmp(ras_pd_conf, "false")) {
+        hmap_remove(map, &ras_pd->hmap_node);
+        return;
+    }
+    if (!ras_pd && !strcmp(ras_pd_conf, "true")) {
+        ras_pd = xzalloc(sizeof *ras_pd);
+        ras_pd->pb = pb;
+        hmap_insert(map, &ras_pd->hmap_node, hash_string(pb->logical_port, 0));
+    }
+    if (ras_pd) {
+        ras_pd->ld = get_local_datapath(local_datapaths,
+                                        pb->datapath->tunnel_key);
+    }
+}
+
 /* Corresponds to each Port_Binding.type. */
 enum en_lport_type {
     LP_UNKNOWN,
@@ -1645,6 +1672,10 @@  binding_run(struct binding_ctx_in *b_ctx_in, struct binding_ctx_out *b_ctx_out)
     const struct sbrec_port_binding *pb;
     SBREC_PORT_BINDING_TABLE_FOR_EACH (pb,
                                        b_ctx_in->port_binding_table) {
+        update_active_pb_ras_pd(pb, b_ctx_out->local_datapaths,
+                                b_ctx_out->local_active_ports_ipv6_pd,
+                                "ipv6_prefix_delegation");
+
         enum en_lport_type lport_type = get_lport_type(pb);
 
         switch (lport_type) {
@@ -2482,6 +2513,10 @@  delete_done:
             continue;
         }
 
+        update_active_pb_ras_pd(pb, b_ctx_out->local_datapaths,
+                                b_ctx_out->local_active_ports_ipv6_pd,
+                                "ipv6_prefix_delegation");
+
         enum en_lport_type lport_type = get_lport_type(pb);
 
         struct binding_lport *b_lport =
diff --git a/controller/binding.h b/controller/binding.h
index a08011ae2..5a6f46a14 100644
--- a/controller/binding.h
+++ b/controller/binding.h
@@ -72,6 +72,7 @@  void related_lports_destroy(struct related_lports *);
 
 struct binding_ctx_out {
     struct hmap *local_datapaths;
+    struct hmap *local_active_ports_ipv6_pd;
     struct local_binding_data *lbinding_data;
 
     /* sset of (potential) local lports. */
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 9050380f3..a42b96ddb 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -133,6 +133,20 @@  get_local_datapath(const struct hmap *local_datapaths, uint32_t tunnel_key)
             : NULL);
 }
 
+struct pb_active_ra_pd *
+get_pb_active_ras_pd(const struct hmap *map, const char *name)
+{
+    uint32_t key = hash_string(name, 0);
+    struct hmap_node *node;
+
+    node = hmap_first_with_hash(map, key);
+    if (node) {
+        return CONTAINER_OF(node, struct pb_active_ra_pd, hmap_node);
+    }
+
+    return NULL;
+}
+
 uint32_t
 get_tunnel_type(const char *name)
 {
@@ -1028,6 +1042,8 @@  struct ed_type_runtime_data {
     bool tracked;
     bool local_lports_changed;
     struct hmap tracked_dp_bindings;
+
+    struct hmap local_active_ports_ipv6_pd;
 };
 
 /* struct ed_type_runtime_data has the below members for tracking the
@@ -1115,6 +1131,7 @@  en_runtime_data_init(struct engine_node *node OVS_UNUSED,
     sset_init(&data->egress_ifaces);
     smap_init(&data->local_iface_ids);
     local_binding_data_init(&data->lbinding_data);
+    hmap_init(&data->local_active_ports_ipv6_pd);
 
     /* Init the tracked data. */
     hmap_init(&data->tracked_dp_bindings);
@@ -1140,6 +1157,7 @@  en_runtime_data_cleanup(void *data)
         free(cur_node);
     }
     hmap_destroy(&rt_data->local_datapaths);
+    hmap_destroy(&rt_data->local_active_ports_ipv6_pd);
     local_binding_data_destroy(&rt_data->lbinding_data);
 }
 
@@ -1218,6 +1236,8 @@  init_binding_ctx(struct engine_node *node,
     b_ctx_in->ovs_table = ovs_table;
 
     b_ctx_out->local_datapaths = &rt_data->local_datapaths;
+    b_ctx_out->local_active_ports_ipv6_pd =
+        &rt_data->local_active_ports_ipv6_pd;
     b_ctx_out->local_lports = &rt_data->local_lports;
     b_ctx_out->local_lports_changed = false;
     b_ctx_out->related_lports = &rt_data->related_lports;
@@ -1235,6 +1255,7 @@  en_runtime_data_run(struct engine_node *node, void *data)
 {
     struct ed_type_runtime_data *rt_data = data;
     struct hmap *local_datapaths = &rt_data->local_datapaths;
+    struct hmap *local_active_ipv6_pd = &rt_data->local_active_ports_ipv6_pd;
     struct sset *local_lports = &rt_data->local_lports;
     struct sset *active_tunnels = &rt_data->active_tunnels;
 
@@ -1250,6 +1271,7 @@  en_runtime_data_run(struct engine_node *node, void *data)
             free(cur_node);
         }
         hmap_clear(local_datapaths);
+        hmap_clear(local_active_ipv6_pd);
         local_binding_data_destroy(&rt_data->lbinding_data);
         sset_destroy(local_lports);
         related_lports_destroy(&rt_data->related_lports);
@@ -3263,7 +3285,8 @@  main(int argc, char *argv[])
                                     sbrec_bfd_table_get(ovnsb_idl_loop.idl),
                                     br_int, chassis,
                                     &runtime_data->local_datapaths,
-                                    &runtime_data->active_tunnels);
+                                    &runtime_data->active_tunnels,
+                                    &runtime_data->local_active_ports_ipv6_pd);
                         /* Updating monitor conditions if runtime data or
                          * logical datapath goups changed. */
                         if (engine_node_changed(&en_runtime_data)
diff --git a/controller/ovn-controller.h b/controller/ovn-controller.h
index 5d9466880..cb054109e 100644
--- a/controller/ovn-controller.h
+++ b/controller/ovn-controller.h
@@ -71,6 +71,8 @@  struct local_datapath {
 
 struct local_datapath *get_local_datapath(const struct hmap *,
                                           uint32_t tunnel_key);
+struct pb_active_ra_pd *
+get_pb_active_ras_pd(const struct hmap *map, const char *name);
 
 const struct ovsrec_bridge *get_bridge(const struct ovsrec_bridge_table *,
                                        const char *br_name);
@@ -87,4 +89,10 @@  enum chassis_tunnel_type {
 
 uint32_t get_tunnel_type(const char *name);
 
+struct pb_active_ra_pd {
+    const struct sbrec_port_binding *pb;
+    const struct local_datapath *ld;
+    struct hmap_node hmap_node;
+};
+
 #endif /* controller/ovn-controller.h */
diff --git a/controller/pinctrl.c b/controller/pinctrl.c
index 78ecfed84..1a74af872 100644
--- a/controller/pinctrl.c
+++ b/controller/pinctrl.c
@@ -1249,80 +1249,77 @@  fill_ipv6_prefix_state(struct ovsdb_idl_txn *ovnsb_idl_txn,
 static void
 prepare_ipv6_prefixd(struct ovsdb_idl_txn *ovnsb_idl_txn,
                      struct ovsdb_idl_index *sbrec_port_binding_by_name,
-                     const struct hmap *local_datapaths,
+                     const struct hmap *local_active_ports_ipv6_pd,
                      const struct sbrec_chassis *chassis,
                      const struct sset *active_tunnels)
     OVS_REQUIRES(pinctrl_mutex)
 {
-    const struct local_datapath *ld;
+    const struct pb_active_ra_pd *pb_ipv6;
     bool changed = false;
 
-    HMAP_FOR_EACH (ld, hmap_node, local_datapaths) {
-        if (datapath_is_switch(ld->datapath)) {
-            /* logical switch */
+    HMAP_FOR_EACH (pb_ipv6, hmap_node, local_active_ports_ipv6_pd) {
+        const struct sbrec_port_binding *pb = pb_ipv6->pb;
+        int j;
+
+        if (!smap_get_bool(&pb->options, "ipv6_prefix_delegation",
+                           false)) {
             continue;
         }
 
-        for (size_t i = 0; i < ld->n_peer_ports; i++) {
-            const struct sbrec_port_binding *pb = ld->peer_ports[i].local;
-            int j;
+        if (!pb_ipv6->ld) {
+            continue;
+        }
 
-            if (!smap_get_bool(&pb->options, "ipv6_prefix_delegation",
-                               false)) {
-                continue;
-            }
+        const char *peer_s = smap_get(&pb->options, "peer");
+        if (!peer_s) {
+            continue;
+        }
 
-            const char *peer_s = smap_get(&pb->options, "peer");
-            if (!peer_s) {
-                continue;
-            }
+        const struct sbrec_port_binding *peer
+            = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s);
+        if (!peer) {
+            continue;
+        }
 
-            const struct sbrec_port_binding *peer
-                = lport_lookup_by_name(sbrec_port_binding_by_name, peer_s);
-            if (!peer) {
-                continue;
-            }
+        char *redirect_name = xasprintf("cr-%s", pb->logical_port);
+        bool resident = lport_is_chassis_resident(
+                sbrec_port_binding_by_name, chassis, active_tunnels,
+                redirect_name);
+        free(redirect_name);
+        if (!resident && strcmp(pb->type, "l3gateway")) {
+            continue;
+        }
 
-            char *redirect_name = xasprintf("cr-%s", pb->logical_port);
-            bool resident = lport_is_chassis_resident(
-                    sbrec_port_binding_by_name, chassis, active_tunnels,
-                    redirect_name);
-            free(redirect_name);
-            if (!resident && strcmp(pb->type, "l3gateway")) {
+        struct in6_addr ip6_addr;
+        struct eth_addr ea = eth_addr_zero;
+        for (j = 0; j < pb->n_mac; j++) {
+            struct lport_addresses laddrs;
+
+            if (!extract_lsp_addresses(pb->mac[j], &laddrs)) {
                 continue;
             }
 
-            struct in6_addr ip6_addr;
-            struct eth_addr ea = eth_addr_zero;
-            for (j = 0; j < pb->n_mac; j++) {
-                struct lport_addresses laddrs;
-
-                if (!extract_lsp_addresses(pb->mac[j], &laddrs)) {
-                    continue;
-                }
-
-                ea = laddrs.ea;
-                if (laddrs.n_ipv6_addrs > 0) {
-                    ip6_addr = laddrs.ipv6_addrs[0].addr;
-                    destroy_lport_addresses(&laddrs);
-                    break;
-                }
+            ea = laddrs.ea;
+            if (laddrs.n_ipv6_addrs > 0) {
+                ip6_addr = laddrs.ipv6_addrs[0].addr;
                 destroy_lport_addresses(&laddrs);
+                break;
             }
+            destroy_lport_addresses(&laddrs);
+        }
 
-            if (eth_addr_is_zero(ea)) {
-                continue;
-            }
-
-            if (j == pb->n_mac) {
-                in6_generate_lla(ea, &ip6_addr);
-            }
+        if (eth_addr_is_zero(ea)) {
+            continue;
+        }
 
-            changed |= fill_ipv6_prefix_state(ovnsb_idl_txn, ld,
-                                              ea, ip6_addr,
-                                              peer->tunnel_key,
-                                              peer->datapath->tunnel_key);
+        if (j == pb->n_mac) {
+            in6_generate_lla(ea, &ip6_addr);
         }
+
+        changed |= fill_ipv6_prefix_state(ovnsb_idl_txn, pb_ipv6->ld,
+                                          ea, ip6_addr,
+                                          peer->tunnel_key,
+                                          peer->datapath->tunnel_key);
     }
 
     struct shash_node *iter, *next;
@@ -3411,7 +3408,8 @@  pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
             const struct ovsrec_bridge *br_int,
             const struct sbrec_chassis *chassis,
             const struct hmap *local_datapaths,
-            const struct sset *active_tunnels)
+            const struct sset *active_tunnels,
+            const struct hmap *local_active_ports_ipv6_pd)
 {
     ovs_mutex_lock(&pinctrl_mutex);
     pinctrl_set_br_int_name_(br_int->name);
@@ -3426,7 +3424,8 @@  pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                            local_datapaths, active_tunnels);
     prepare_ipv6_ras(local_datapaths);
     prepare_ipv6_prefixd(ovnsb_idl_txn, sbrec_port_binding_by_name,
-                         local_datapaths, chassis, active_tunnels);
+                         local_active_ports_ipv6_pd, chassis,
+                         active_tunnels);
     sync_dns_cache(dns_table);
     controller_event_run(ovnsb_idl_txn, ce_table, chassis);
     ip_mcast_sync(ovnsb_idl_txn, chassis, local_datapaths,
diff --git a/controller/pinctrl.h b/controller/pinctrl.h
index cc0a51984..9d98ee72e 100644
--- a/controller/pinctrl.h
+++ b/controller/pinctrl.h
@@ -49,7 +49,8 @@  void pinctrl_run(struct ovsdb_idl_txn *ovnsb_idl_txn,
                  const struct sbrec_bfd_table *,
                  const struct ovsrec_bridge *, const struct sbrec_chassis *,
                  const struct hmap *local_datapaths,
-                 const struct sset *active_tunnels);
+                 const struct sset *active_tunnels,
+                 const struct hmap *local_active_ports_ipv6_pd);
 void pinctrl_wait(struct ovsdb_idl_txn *ovnsb_idl_txn);
 void pinctrl_destroy(void);
 void pinctrl_set_br_int_name(char *br_int_name);