Commit 33016631dc for frr

commit 33016631dc885d7041822bb448d900dc3aa2a520
Author: Vic Lan <vic.lan@pica8.com>
Date:   Sat Sep 12 08:46:46 2026 +0800

    zebra: avoid duplicate dependency refs on NHG keep-around reuse

    An installed or queued NHG whose refcount reaches zero is retained with
    a KEEP_AROUND reference until its timer expires. Its dependencies remain
    referenced while the parent is retained.

    When the same NHG is reused before timer expiry, `zebra_nhg_increment_ref()`
    cancels the timer and reuses the retained parent reference. However, it
    wrongly incremented every dependency, even though no additional parent
    reference was acquired.

    Just avoid propagating dependency references when cancelling a KEEP_AROUND
    timer.

    Signed-off-by: Vic Lan <vic.lan@pica8.com>

diff --git a/zebra/zebra_nhg.c b/zebra/zebra_nhg.c
index 52b43bbe5b..da62d5947c 100644
--- a/zebra/zebra_nhg.c
+++ b/zebra/zebra_nhg.c
@@ -2005,15 +2005,16 @@ void zebra_nhg_decrement_ref(struct nhg_hash_entry *nhe)

 void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe)
 {
-	if (IS_ZEBRA_DEBUG_NHG_DETAIL)
-		zlog_debug("%s: nhe %p (%pNG) %d => %d", __func__, nhe, nhe,
-			   nhe->refcnt, nhe->refcnt + 1);
-
-	nhe->refcnt++;
-
+	/*
+	 * A KEEP_AROUND reference already retains the NHE and its dependencies.
+	 * Reusing that reference must not increment the dependencies again.
+	 */
 	if (event_is_scheduled(nhe->timer)) {
+		if (IS_ZEBRA_DEBUG_NHG_DETAIL)
+			zlog_debug("%s: nhe %p (%pNG) %d => %d (cancel keep-around)", __func__,
+				   nhe, nhe, nhe->refcnt, nhe->refcnt);
+
 		event_cancel(&nhe->timer);
-		nhe->refcnt--;
 		UNSET_FLAG(nhe->flags, NEXTHOP_GROUP_KEEP_AROUND);
 		/*
 		 * This group is being resurrected from KEEP_AROUND.
@@ -2028,6 +2029,12 @@ void zebra_nhg_increment_ref(struct nhg_hash_entry *nhe)
 		return;
 	}

+	if (IS_ZEBRA_DEBUG_NHG_DETAIL)
+		zlog_debug("%s: nhe %p (%pNG) %d => %d", __func__, nhe, nhe, nhe->refcnt,
+			   nhe->refcnt + 1);
+
+	nhe->refcnt++;
+
 	if (!zebra_nhg_depends_is_empty(nhe))
 		nhg_connected_tree_increment_ref(&nhe->nhg_depends);
 }