The pflow_output SB_port_binding handler triggers a full
recompute when the type column is updated on a port binding.
However, for newly created port bindings, the OVSDB IDL
marks all non-default columns as "updated", even though no
actual update occurred.  This caused every new port binding
with a non-default type (e.g., remote, patch, localnet,
router) to unnecessarily trigger a full pflow_output
recompute, severely impacting ovn-controller performance
at scale.

This is particularly problematic in deployments that use
remote LSPs, such as ovn-kubernetes with L2 UDNs, where
frequent creation of remote port bindings leads to
continuous full recomputes and high CPU usage.

Guard the type-update check with sbrec_port_binding_is_new()
and sbrec_port_binding_is_deleted() so that only genuine
type changes on existing port bindings trigger a recompute.
This matches the pattern already used in binding.c for the
tunnel_key column.

Also fix a typo in the test name ("path" -> "patch").

Fixes: 73a10345a29c ("controller: Update physical flows for peer port when the 
patch port is removed.")
Reported-at: https://redhat.atlassian.net/browse/FDP-3819
Reported-by: Patryk Diak <[email protected]>
Assisted-by: Claude Opus 4.6, Claude Code
Signed-off-by: Dumitru Ceara <[email protected]>
---
 controller/ovn-controller.c |  6 ++++--
 tests/ovn-controller.at     | 14 +++++++++++++-
 tests/ovn-performance.at    |  7 +++++++
 3 files changed, 24 insertions(+), 3 deletions(-)

diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 3024399f36..b26ccfbd96 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -5081,12 +5081,14 @@ pflow_output_sb_port_binding_handler(struct engine_node 
*node,
      */
     const struct sbrec_port_binding *pb;
     SBREC_PORT_BINDING_TABLE_FOR_EACH_TRACKED (pb, p_ctx.port_binding_table) {
+        bool removed = sbrec_port_binding_is_deleted(pb);
+
         /* Trigger a full recompute if type column is updated. */
-        if (sbrec_port_binding_is_updated(pb, SBREC_PORT_BINDING_COL_TYPE)) {
+        if (!removed && !sbrec_port_binding_is_new(pb) &&
+            sbrec_port_binding_is_updated(pb, SBREC_PORT_BINDING_COL_TYPE)) {
             destroy_physical_ctx(&p_ctx);
             return EN_UNHANDLED;
         }
-        bool removed = sbrec_port_binding_is_deleted(pb);
         if (!physical_handle_flows_for_lport(pb, removed, &p_ctx,
                                              &pfo->flow_table)) {
             destroy_physical_ctx(&p_ctx);
diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
index b1814dfb74..f9f64d691c 100644
--- a/tests/ovn-controller.at
+++ b/tests/ovn-controller.at
@@ -962,7 +962,7 @@ AT_CLEANUP
 ])
 
 OVN_FOR_EACH_NORTHD([
-AT_SETUP([ovn-controller - ovn IP check path ports])
+AT_SETUP([ovn-controller - ovn IP check patch ports])
 AT_KEYWORDS([ovn-ip-patch-ports])
 
 ovn_start
@@ -1003,6 +1003,18 @@ check as hv1 ovn-appctl -t ovn-controller 
inc-engine/clear-stats
 check ovn-nbctl --wait=hv lsp-set-type ls0-rp localnet
 check_controller_engine_stats hv1 pflow_output recompute nocompute
 
+# Check that adding a new port with a non-default type does not trigger
+# a pflow_output recompute.
+check as hv1 ovn-appctl -t ovn-controller inc-engine/clear-stats
+check ovn-nbctl --wait=hv lsp-add ls0 lsp-remote -- lsp-set-type lsp-remote 
remote
+check_controller_engine_stats hv1 pflow_output norecompute compute
+
+# Check that deleting a port with a non-default type does not trigger
+# a pflow_output recompute.
+check as hv1 ovn-appctl -t ovn-controller inc-engine/clear-stats
+check ovn-nbctl --wait=hv lsp-del lsp-remote
+check_controller_engine_stats hv1 pflow_output norecompute compute
+
 OVN_CLEANUP([hv1])
 AT_CLEANUP
 ])
diff --git a/tests/ovn-performance.at b/tests/ovn-performance.at
index 2bccbb06dd..114917832b 100644
--- a/tests/ovn-performance.at
+++ b/tests/ovn-performance.at
@@ -386,6 +386,13 @@ for i in 1 2; do
     done
 done
 
+# Check that adding a new port with a non-default type does not trigger
+# a physical_run.
+OVN_CONTROLLER_EXPECT_NO_HIT(
+    [hv1 hv2], [physical_run],
+    [ovn-nbctl --wait=hv lsp-add ls1 lsp-remote -- lsp-set-type lsp-remote 
remote]
+)
+
 for i in 1 2; do
     j=$((i%2 + 1))
     as=as$i
-- 
2.54.0

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to