Thread (32 messages) flat view 32 messages, 4 authors, 20h ago
HOTtoday

Revision v3 of 3 in this series.

Revisions (3)
  1. v1 [diff vs current]
  2. v2 [diff vs current]
  3. v3 current

[PATCH v3 net-next 01/15] selftest: net: Deflake Periodic GC test in test_neigh.sh.

From: Kuniyuki Iwashima <kuniyu@google.com>
Date: 2026-08-11 02:24:52
Subsystem: kernel selftest framework, networking [general], the rest · Maintainers: Shuah Khan, Shuah Khan, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

test_neigh.sh has a test case to check if GC is running periodically.

It applies a new value to BASE_REACHABLE_TIME, waits for half of
the old value (+2s) to ensure the config is reflected, and waits for
BASE_REACHABLE_TIME to see if the next GC is triggered.

  orig_base_reachable=$(ip -j ntable show name "$tbl_name" | jq '.[] | \
                        select(has("thresh1")) | .["base_reachable"]')
  run_cmd "ip ntable change name $tbl_name thresh1 10 base_reachable 10000"
  run_cmd "sleep $(((orig_base_reachable / 1000) / 2 + 2))"

This is because neigh_periodic_work() schedules the next GC with
(BASE_REACHABLE_TIME / 2) seconds:

  queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,
                     NEIGH_VAR(&tbl->parms, BASE_REACHABLE_TIME) >> 1);

However, the very first run is scheduled with a longer period in
neigh_table_init(), which will be up to 45s:

  neigh_set_reach_time(&tbl->parms);
  queue_delayed_work(system_power_efficient_wq, &tbl->gc_work,
                     tbl->parms.reachable_time);

The initial value is randomly chosen, so if it is larger than 27s
(larger than two "sleep"s), the test fails.

The test fails more frequently when neigh_table is namespacified by
the later patch.

In addition, the long "sleep" makes the test more flaky on kernel with
CONFIG_NO_HZ_IDLE=y, which is enabled by defconfig.

The tbl->gc_work is initialised with INIT_DEFERRABLE_WORK() and queued
to system_power_efficient_wq.

Even when the next GC is supposed to be triggered 5s later, "sleep"
makes the CPU idle, delaying GC execution until often 20s later,
which is too late.

Let's take the initial tbl->parms.reachable_time into account and
poll periodic_gc_runs every 0.1s up to the proper timeout, not to
make CPU idle.

Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
---
It seems NIPA has CONFIG_HZ_PERIODIC=y and has no problem.
We may want to add it in net/config.
https://netdev-ctrl.bots.linux.dev/logs/vmksft/net-dbg/results/769301/config
---
 tools/testing/selftests/net/test_neigh.sh | 27 ++++++++++++++++++-----
 1 file changed, 21 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/net/test_neigh.sh b/tools/testing/selftests/net/test_neigh.sh
index 7c594bf6ead0..447423713266 100755
--- a/tools/testing/selftests/net/test_neigh.sh
+++ b/tools/testing/selftests/net/test_neigh.sh
@@ -63,6 +63,15 @@ exit_cleanup_all()
 	exit "${EXIT_STATUS}"
 }
 
+get_periodic_gc_runs()
+{
+	local ns=$1
+	local tbl_name=$2
+
+	ip -n "$ns" -j -s ntable show name "$tbl_name" | \
+		jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]'
+}
+
 ################################################################################
 # Tests
 
@@ -282,24 +291,30 @@ extern_valid_common()
 	local periodic_gc_runs_t0
 	local periodic_gc_runs_t1
 	local orig_base_reachable
+	local base_reachable=10000
 	local orig_gc_stale
+	local timeout
 
 	run_cmd "ip -n $ns1 neigh flush dev veth0"
 	orig_thresh1=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["thresh1"]')
 	orig_base_reachable=$(ip -j ntable show name "$tbl_name" | jq '.[] | select(has("thresh1")) | .["base_reachable"]')
-	run_cmd "ip ntable change name $tbl_name thresh1 10 base_reachable 10000"
+	run_cmd "ip ntable change name $tbl_name thresh1 10 base_reachable $base_reachable"
 	orig_gc_stale=$(ip -n "$ns1" -j ntable show name "$tbl_name" dev veth0 | jq '.[]["gc_stale"]')
 	run_cmd "ip -n $ns1 ntable change name $tbl_name dev veth0 gc_stale 1000"
 	run_cmd "ip -n $ns1 neigh add $ip_addr lladdr $mac nud stale dev veth0 extern_valid"
 	run_cmd "ip -n $ns1 neigh add ${subnet}3 lladdr $mac nud stale dev veth0"
-	# Wait orig_base_reachable/2 for the new interval to take effect.
-	run_cmd "sleep $(((orig_base_reachable / 1000) / 2 + 2))"
+	# Wait up to neigh_set_reach_time(BASE_REACHABLE_TIME) (~45s) + 5s
+	# for the new interval to take effect, see neigh_table_init().
+	timeout=$(((orig_base_reachable / 1000) * 3 / 2 + 5))
+	slowwait_for_counter "$timeout" 1 get_periodic_gc_runs "$ns1" "$tbl_name" >/dev/null
 	for i in {1..20}; do
 		run_cmd "ip -n $ns1 neigh add ${subnet}$((i + 4)) nud none dev veth0"
 	done
-	periodic_gc_runs_t0=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]')
-	run_cmd "sleep 10"
-	periodic_gc_runs_t1=$(ip -j -s ntable show name "$tbl_name" | jq '.[] | select(has("periodic_gc_runs")) | .["periodic_gc_runs"]')
+	periodic_gc_runs_t0=$(get_periodic_gc_runs "$ns1" "$tbl_name")
+	# Wait up to BASE_REACHABLE_TIME / 2 + 5s, see neigh_periodic_work().
+	timeout=$(((base_reachable / 1000) / 2 + 5))
+	slowwait_for_counter "$timeout" 1 get_periodic_gc_runs "$ns1" "$tbl_name" >/dev/null
+	periodic_gc_runs_t1=$(get_periodic_gc_runs "$ns1" "$tbl_name")
 	[[ $periodic_gc_runs_t1 -ne $periodic_gc_runs_t0 ]]
 	check_err $? "Periodic garbage collection did not run"
 	run_cmd "ip -n $ns1 neigh get $ip_addr dev veth0 | grep \"extern_valid\""
-- 
2.55.0.691.gc56d675ccc-goog
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help