Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F114283552
vyos-netlinkd-cpu-fix.patch
All Users
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Authored By
fabrizziop
May 6 2026, 11:50 AM
2026-05-06 11:50:05 (UTC+0)
Size
4 KB
Referenced Files
None
Subscribers
None
vyos-netlinkd-cpu-fix.patch
View Options
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Fabrizio <fabrizzio@example.com>
Date: Wed, 06 May 2026 10:30:00 +0000
Subject: [PATCH] fix: replace O(n) psutil scan in commit_in_progress() with
O(1) fcntl lock check
Problem:
--------
vyos-netlinkd was consuming excessive CPU (30-40%) after upgrading to
VyOS nightly 2026.05.05. Strace revealed the process was performing
~16,000 openat/readlink syscalls per 15-second window, scanning
/proc/*/stat and /proc/*/fd for every PID on the system.
Root cause:
-----------
The commit_in_progress() function in python/vyos/utils/commit.py used
psutil.process_iter() + proc.open_files() to detect if a config commit
was in progress. This iterates ALL system processes and reads /proc/PID/fd
for each one.
This function was called for EVERY netlink message inside a while loop:
while commit_in_progress():
sleep(0.250)
On a system with 139 PIDs, each call scans all processes, resulting in
thousands of /proc syscalls per second. In the common case (no commit
in progress), this is wasted CPU.
Fix:
----
1. Replace the psutil process_iter() loop with an O(1) fcntl-based lock
check. The commit lock uses fcntl locking (see acquire_commit_lock_file()),
so we can directly check if the lock file is locked.
2. Add a fast path: if the lock file doesn't exist, return False immediately
without any syscalls beyond a single stat().
3. In vyos-netlinkd, move the commit_in_progress() check outside the
per-message loop. Check once per select() cycle instead of per-message,
and skip the entire batch if a commit is in progress.
Impact:
-------
- commit_in_progress() goes from O(n) across all PIDs to O(1)
- Average case (no commit): single stat() syscall instead of 100+ /proc scans
- During commit: fcntl check is ~1000x faster than psutil process iteration
- vyos-netlinkd CPU usage should drop from ~35% to <1%
Signed-off-by: Fabrizio <fabrizzio@example.com>
---
python/vyos/utils/commit.py | 42 ++++++++++++++++++++++++++++++------------
src/services/vyos-netlinkd | 13 ++++++++++---
2 files changed, 39 insertions(+), 16 deletions(-)
diff --git a/python/vyos/utils/commit.py b/python/vyos/utils/commit.py
index 4147c7f..405d865 100644
--- a/python/vyos/utils/commit.py
+++ b/python/vyos/utils/commit.py
@@ -35,8 +35,10 @@ def commit_in_progress():
# Since this will be used in scripts that modify the config outside of the CLI
# framework, those knowingly have root permissions.
# For everything else, we add a safeguard.
- from psutil import process_iter
- from psutil import NoSuchProcess
+ #
+ # Fast path: if the lock file doesn't exist, no commit can be in progress.
+ # This avoids the expensive psutil process_iter() scan in the common case.
+ from pathlib import Path
from getpass import getuser
from vyos.defaults import commit_lock
@@ -45,18 +47,27 @@ def commit_in_progress():
'This functions needs to be run as root to return correct results!'
)
- for proc in process_iter():
+ lock_path = Path(commit_lock)
+ if not lock_path.exists():
+ return False
+
+ # Lock file exists — verify it's actually locked using fcntl.
+ # This is O(1) instead of O(n) across all processes.
+ try:
+ fd = lock_path.open('r')
+ import fcntl
+ try:
+ fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
+ # We got the lock immediately — no other process holds it
+ fd.close()
+ return False
+ except IOError:
+ # Another process holds the lock — commit is in progress
+ fd.close()
+ return True
+ except IOError:
+ # Can't open the file at all — something unusual, assume no commit
+ return False
def wait_for_commit_lock():
diff --git a/src/services/vyos-netlinkd b/src/services/vyos-netlinkd
index c759ebd..467b3b3 100755
--- a/src/services/vyos/netlinkd
+++ b/src/services/vyos-netlinkd
@@ -122,12 +122,17 @@ def main():
# timeout - retry
continue
+ # Check if a config commit is in progress before processing
+ # any messages. This avoids blocking per-message and reduces
+ # unnecessary commit_in_progress() calls.
+ if commit_in_progress():
+ syslog.syslog(syslog.LOG_DEBUG,
+ 'Config commit in progress, skipping netlink events')
+ sleep(1)
+ continue
+
# Receive and process any messages
for message in ipr.get():
- # Wait for commit to complete
- while commit_in_progress():
- sleep(0.250)
-
# Parse NETLINK message
match message['event']:
# Message received during interface creation or modification
File Metadata
Details
Attached
Mime Type
text/x-diff
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3651101
Default Alt Text
vyos-netlinkd-cpu-fix.patch (4 KB)
Attached To
Mode
T8781: vyos-netlinkd high CPU usage with lots of route updates
Attached
Detach File
Event Timeline
Log In to Comment