Commit aa75408898 for frr

commit aa7540889880ec333e60badd209c2e1127d2f9e7
Author: Nick Bouliane <nbouliane@coreweave.com>
Date:   Tue Sep 29 12:42:21 2026 -0400

    bgpd: skip SR-TE nexthop walk when no colored nexthops exist

    bgp_nexthop_update() walks the entire nexthop cache table on every
    colorless nexthop update, looking for colored entries that share the
    same endpoint (a workaround for zebra's RNH subsystem not being
    color-aware). The walk runs even when the table contains no colored
    entries at all, in which case it cannot ever find anything.

    This makes convergence O(N^2) in the number of tracked nexthops:
    during a session reset with N remote EVPN VTEPs, zebra sends ~N
    nexthop updates and each one triggers a full walk of the N-entry
    table. Measured on an EVPN fabric across 10k/15k/25k/30k remote VTEPs,
    bgpd's route-learning CPU grows quadratically, reaching ~80
    CPU-seconds per restart at 30k VTEPs, with perf showing the cycles
    concentrated under zclient_nexthop_update()/typed_rb_next(). With this
    change those symbols disappear from the profile and learning cost
    returns to linear in the number of routes. Deployments that do not use
    SR-TE at all - most EVPN fabrics - currently pay the full cost of the
    workaround.

    The nexthop cache tree is keyed by (srte_color, ifindex, prefix), so
    colored entries always sort at the end of the tree. Check the last
    entry before walking: if it is colorless (or the tree is empty), no
    colored entry exists and the walk is skipped. The walk itself is
    unchanged when colored entries are present.

    Signed-off-by: Nick Bouliane <nbouliane@coreweave.com>

diff --git a/bgpd/bgp_nht.c b/bgpd/bgp_nht.c
index a7c24d77c2..7a988aa968 100644
--- a/bgpd/bgp_nht.c
+++ b/bgpd/bgp_nht.c
@@ -1056,10 +1056,22 @@ void bgp_nexthop_update(struct vrf *vrf, struct prefix *match,
 	 * make zebra's RNH subsystem aware of SR-TE colors (like bgpd is),
 	 * which should provide a better infrastructure to solve this issue in
 	 * a more efficient and elegant way.
+	 *
+	 * The walk below is only useful when colored entries exist. The
+	 * cache tree is keyed by (srte_color, ifindex, prefix), so colored
+	 * entries always sort at the end of the tree: if the last entry is
+	 * colorless, there is no colored entry at all and the walk can be
+	 * skipped. Without this check, every colorless NH update walks the
+	 * whole table for nothing, which makes convergence O(N^2) in the
+	 * number of tracked nexthops for deployments not using SR-TE.
 	 */
 	if (nhr->srte_color == 0) {
 		struct bgp_nexthop_cache *bnc_iter;

+		bnc_iter = bgp_nexthop_cache_last(&bgp->nexthop_cache_table[afi]);
+		if (!bnc_iter || bnc_iter->srte_color == 0)
+			return;
+
 		frr_each (bgp_nexthop_cache, &bgp->nexthop_cache_table[afi],
 			  bnc_iter) {
 			if (!prefix_same(match, &bnc_iter->prefix) ||