Hi Gavin,

Thanks for the patch!

It would be great if you could elaborate more on why copying the
external_ids to the patch port is needed.

I'm asking because your code is now changing ovn-controller to also
monitor SB.Port_Binding.external_ids.  That means that every time a port
binding's external_ids get updated (i.e., every time the
NB.Logical_Switch_Port.external_ids are updated) we end up doing quite a
lot of compute work in ovn-controller.

That is (probably not complete) the following I-P handlers are called:
- pflow_output_sb_port_binding_handler()

- lflow_output_sb_port_binding_handler()

- runtime_data_sb_port_binding_handler() -> which marks en_runtime_data
as updated and in turn triggers the following I-P nodes to be computed:
  a. en_pflow_output (pflow_output_runtime_data_handler())
  b. en_lflow_output (lflow_output_runtime_data_handler())
  c. en_ct_zones (ct_zones_runtime_data_handler())
  d. en_mac_cache (mac_cache_runtime_data_handler())
  e. en_garp_rarp (garp_rarp_runtime_data_handler() - this will actually
fail to incrementally process changes for switches with localnet ports)
  f. en_lb_data (lb_data_runtime_data_handler())

- garp_rarp_sb_port_binding_handler() - this will actually fail to
incrementally process changes for switches with localnet ports)

So, I guess, based on how often the NB.Logical_Switch_Port external_ids
change we might see some signficant performance implications on the
ovn-controller side.

Please see some more comments inline.

On 6/18/25 5:57 PM, Gavin McKee via dev wrote:
> Signed-off-by: Gavin McKee <gavmcke...@googlemail.com>
> ---
>  controller/ovn-controller.8.xml |  5 +++
>  controller/ovn-controller.c     |  1 -
>  controller/patch.c              | 44 +++++++++++++++++---
>  tests/ovn-controller.at         | 72 +++++++++++++++++++++++++++++++++
>  4 files changed, 115 insertions(+), 7 deletions(-)
> 
> diff --git a/controller/ovn-controller.8.xml b/controller/ovn-controller.8.xml
> index 5007f5f80..422eb2cf7 100644
> --- a/controller/ovn-controller.8.xml
> +++ b/controller/ovn-controller.8.xml
> @@ -554,6 +554,11 @@
>            bridge, with the same <code>external_ids:ovn-localnet-port</code>
>            value.
>          </p>
> +        <p>
> +          When a <code>localnet</code> port has additional values in its
> +          <code>external_ids</code> column, these key-value pairs are copied 
> to
> +          the corresponding patch ports.
> +        </p>
>        </dd>
>  
>        <dt>
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 7940091da..8c8d47fe3 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -5857,7 +5857,6 @@ main(int argc, char *argv[])
>  
>      ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_sb_global_col_external_ids);
>      ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_logical_flow_col_external_ids);
> -    ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_port_binding_col_external_ids);
>      ovsdb_idl_omit(ovnsb_idl_loop.idl, &sbrec_ssl_col_external_ids);
>      ovsdb_idl_omit(ovnsb_idl_loop.idl,
>                     &sbrec_gateway_chassis_col_external_ids);
> diff --git a/controller/patch.c b/controller/patch.c
> index 4fed6e375..5ba1466e0 100644
> --- a/controller/patch.c
> +++ b/controller/patch.c
> @@ -73,18 +73,38 @@ match_patch_port(const struct ovsrec_port *port, const 
> char *peer)
>   * 'dst_name' in bridge 'dst'.  Initializes the patch port's 
> external-ids:'key'
>   * to 'key'.
>   *
> - * If such a patch port already exists, removes it from 'existing_ports'. */
> + * If such a patch port already exists, removes it from 'existing_ports'.  
> Any
> + * key-value pairs in 'extra_ids' are merged into the port's external IDs.  
> If
> + * the port already exists, its external IDs are updated accordingly.
> + */
>  static void
>  create_patch_port(struct ovsdb_idl_txn *ovs_idl_txn,
>                    const char *key, const char *value,
>                    const struct ovsrec_bridge *src, const char *src_name,
>                    const struct ovsrec_bridge *dst, const char *dst_name,
> -                  struct shash *existing_ports)
> +                  struct shash *existing_ports,
> +                  const struct smap *extra_ids)
>  {
>      for (size_t i = 0; i < src->n_ports; i++) {
>          if (match_patch_port(src->ports[i], dst_name)) {
>              /* Patch port already exists on 'src'. */
> -            shash_find_and_delete(existing_ports, src->ports[i]->name);
> +            const struct ovsrec_port *port = src->ports[i];
> +            shash_find_and_delete(existing_ports, port->name);
> +            if (extra_ids) {
> +                const struct smap *ids = &port->external_ids;
> +                struct smap merged = SMAP_INITIALIZER(&merged);
> +                smap_clone(&merged, ids);

We can avoid the local variable and just use "&port->external_ids" here.

> +                struct smap_node *node;
> +                SMAP_FOR_EACH (node, extra_ids) {
> +                    const char *cur = smap_get(&merged, node->key);
> +                    if (!cur || strcmp(cur, node->value)) {
> +                        ovsrec_port_update_external_ids_setkey(port,
> +                                                              node->key,
> +                                                              node->value);
> +                    }
> +                }

It might be faster to just merge &port->external_ids and extra_ids and
unconditionally call ovsrec_port_update_external_ids() and let the IDL
layer take care of the diffs.

Also, "merged" is a confusing name because it doesn't really contain any
merged sets of values.

> +                smap_destroy(&merged);
> +            }
>              return;
>          }
>      }
> @@ -94,9 +114,16 @@ create_patch_port(struct ovsdb_idl_txn *ovs_idl_txn,
>              src_name, src->name, dst->name);
>  
>      const struct smap if_options = SMAP_CONST1(&if_options, "peer", 
> dst_name);
> -    const struct smap port_ids = SMAP_CONST1(&port_ids, key, value);
> +
> +    struct smap port_ids = SMAP_INITIALIZER(&port_ids);
> +    if (extra_ids) {
> +        smap_clone(&port_ids, extra_ids);
> +    }
> +    smap_replace(&port_ids, key, value);
> +
>      ovsport_create(ovs_idl_txn, src, src_name, "patch", &port_ids, NULL,
>                     &if_options, 0);
> +    smap_destroy(&port_ids);
>  }
>  
>  static void
> @@ -223,12 +250,17 @@ add_bridge_mappings_by_type(struct ovsdb_idl_txn 
> *ovs_idl_txn,
>          sset_find_and_delete(&missed_bridges, msg_key);
>          free(msg_key);
>  
> +        const struct smap *extra_ids =
> +            !strcmp(pb_type, "localnet") ? &binding->external_ids : NULL;
> +

Why only for localnet?  But I guess that information would be part of
the answer to the question I had on the commit message (about the actual
use case).

>          char *name1 = patch_port_name(br_int->name, binding->logical_port);
>          char *name2 = patch_port_name(binding->logical_port, br_int->name);
>          create_patch_port(ovs_idl_txn, patch_port_id, binding->logical_port,
> -                          br_int, name1, br_ln, name2, existing_ports);
> +                          br_int, name1, br_ln, name2, existing_ports,
> +                          extra_ids);
>          create_patch_port(ovs_idl_txn, patch_port_id, binding->logical_port,
> -                          br_ln, name2, br_int, name1, existing_ports);
> +                          br_ln, name2, br_int, name1, existing_ports,
> +                          extra_ids);
>          free(name1);
>          free(name2);
>      }
> diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
> index ae7eb6f31..6ae97e87d 100644
> --- a/tests/ovn-controller.at
> +++ b/tests/ovn-controller.at
> @@ -139,6 +139,7 @@ check_patches \
>      'br-int  patch-br-int-to-localnet2 patch-localnet2-to-br-int' \
>      'br-eth0 patch-localnet2-to-br-int patch-br-int-to-localnet2'
>  
> +

Nit: unrelated change.

>  # Add logical patch ports to connect new logical datapath.
>  #
>  # OVN no longer uses OVS patch ports to implement logical patch ports, so
> @@ -174,6 +175,77 @@ OVS_APP_EXIT_AND_WAIT([ovsdb-server])
>  AT_CLEANUP
>  ])
>  
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([Localnet port external ids])
> +AT_KEYWORDS([lb])
> +ovn_start
> +
> +check ovn-nbctl ls-add sw0
> +check ovn-nbctl lsp-add sw0 sw0-port1
> +check ovn-nbctl lsp-set-addresses sw0-port1 "50:54:00:00:00:01 10.0.0.3 
> 1000::3"
> +check ovn-nbctl lsp-add sw0 sw0-port2
> +check ovn-nbctl lsp-set-addresses sw0-port2 "50:54:00:00:00:02 10.0.0.4 
> 1000::4"
> +
> +check ovn-nbctl lr-add lr0
> +check ovn-nbctl lrp-add lr0 lr0-sw0 00:00:00:00:ff:01 10.0.0.1/24 1000::1/64
> +check ovn-nbctl lsp-add sw0 sw0-lr0
> +check ovn-nbctl lsp-set-type sw0-lr0 router
> +check ovn-nbctl lsp-set-addresses sw0-lr0 router
> +check ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
> +
> +check ovn-nbctl ls-add public
> +check ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24 
> 2000::1/64
> +check ovn-nbctl lsp-add public public-lr0
> +check ovn-nbctl lsp-set-type public-lr0 router
> +check ovn-nbctl lsp-set-addresses public-lr0 router
> +check ovn-nbctl lsp-set-options public-lr0 router-port=lr0-public
> +
> +# localnet port
> +check ovn-nbctl lsp-add public ln-public
> +check ovn-nbctl lsp-set-type ln-public localnet
> +check ovn-nbctl lsp-set-addresses ln-public unknown
> +check ovn-nbctl lsp-set-options ln-public network_name=phys
> +
> +check ovn-nbctl lrp-set-gateway-chassis lr0-public hv1 20
> +
> +net_add n1
> +
> +sim_add hv1
> +as hv1
> +ovs-vsctl add-br br-phys
> +ovn_attach n1 br-phys 192.168.0.1
> +ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
> +ovs-vsctl -- add-port br-int hv1-vif1 -- \
> +    set interface hv1-vif1 external-ids:iface-id=sw0-port1 \
> +    options:tx_pcap=hv1/vif1-tx.pcap \
> +    options:rxq_pcap=hv1/vif1-rx.pcap \
> +    ofport-request=1
> +
> +check ovn-nbctl --wait=hv set logical_switch_port ln-public 
> external_ids:foo=bar
> +wait_column "foo=bar" Port_Binding external_ids logical_port=ln-public
> +
> +AT_CHECK([as hv1 ovs-vsctl get port patch-br-int-to-ln-public 
> external_ids:foo], [0], [dnl
> +bar
> +])
> +
> +AT_CHECK([as hv1 ovs-vsctl get port patch-ln-public-to-br-int 
> external_ids:foo], [0], [dnl
> +bar
> +])
> +
> +check ovn-nbctl --wait=hv set logical_switch_port ln-public 
> external_ids:foo=foobar
> +wait_column "foo=foobar" Port_Binding external_ids logical_port=ln-public
> +
> +AT_CHECK([as hv1 ovs-vsctl get port patch-br-int-to-ln-public 
> external_ids:foo], [0], [dnl
> +foobar
> +])
> +
> +AT_CHECK([as hv1 ovs-vsctl get port patch-ln-public-to-br-int 
> external_ids:foo], [0], [dnl
> +foobar
> +])
> +
> +AT_CLEANUP
> +])
> +
>  # Checks that ovn-controller populates datapath-type and iface-types
>  # correctly in the Chassis other_config column.
>  OVN_FOR_EACH_NORTHD([

Regards,
Dumitru


_______________________________________________
dev mailing list
d...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to