Once the neighbour table exceeds gc_thresh3, neigh_forced_gc() is called
on every allocation attempt with no rate limiting. In workloads with mostly
active/reachable entries, the GC walk traverses a large portion of the
neighbour table without reclaiming entries, holding tbl->lock for an
extended period. This causes severe lock contention and allocation
latencies exceeding 16ms under sustained neighbour creation.
Add a pre-lock check in neigh_forced_gc() to skip the GC run if one was
performed within the last second, avoiding repeated full table scans and
lock acquisitions on the hot allocation path.
Profiling of neigh_create() shows ~3 orders of magnitude latency
improvement with this change.
Link:https://lore.kernel.org/netdev/CALkUMdSCpx_ywYCx_ePLdm6yioO1nQWx7sSM=AEgsq0kywHxTw@mail.gmail.com/ (local)
Signed-off-by: Vimal Agrawal <redacted>
---
net/core/neighbour.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/net/core/neighbour.c b/net/core/neighbour.c
index 1349c0eedb64..078842db3c5f 100644
--- a/net/core/neighbour.c
+++ b/net/core/neighbour.c
@@ -260,6 +260,9 @@ static int neigh_forced_gc(struct neigh_table *tbl)
int shrunk = 0;
int loop = 0;
+ if (!time_after(jiffies, READ_ONCE(tbl->last_flush) + HZ))
+ return 0;
+
NEIGH_CACHE_STAT_INC(tbl, forced_gc_runs);
spin_lock_bh(&tbl->lock);
--
2.17.1