[PATCH 17/36] tuna: Add memory nodes and blocklist warnings to cpuset list
John Kacur <[email protected]> Fri, 10 Jul 2026 10:14:55 -0400
| Newsgroups | org.kernel.vger.linux-rt-users |
|---|---|
| Message-ID | <[email protected]> |
The cpuset list command now displays NUMA memory node assignments in verbose mode and warns if critical system processes are found in user-created cpusets. Changes: - Added MEMS column to verbose output showing cpuset.mems (memory nodes) - Added blocklist checking after listing cpusets - Warns if systemd, systemd-logind, or other critical processes are found in user-created cpusets (tuna*, rteval_*, etc.) - Skips system cgroups (*.mount, *.scope, *.slice) to avoid false warnings about systemd in init.scope The memory nodes display is especially useful on NUMA systems since we recently added NUMA-aware memory node assignment. The blocklist warnings help verify that the blocklist protection is working correctly and catch any cases where critical processes end up in custom cpusets despite protection. Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: John Kacur <[email protected]> --- tuna-cmd.py | 47 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/tuna-cmd.py b/tuna-cmd.py index 50e6e9f9d937..5c21d54bc2bf 100755 --- a/tuna-cmd.py +++ b/tuna-cmd.py @@ -882,7 +882,7 @@ def cpuset_list(pattern, verbose, skip_empty, recursive): Args: pattern: Glob pattern to filter cpusets (None for all) - verbose: If True, show detailed info (CPUs, tasks, partition) + verbose: If True, show detailed info (CPUs, memory nodes, tasks, partition) skip_empty: If True, skip cpusets with no CPUs assigned recursive: If True, search nested cpusets """ @@ -918,9 +918,9 @@ def cpuset_list(pattern, verbose, skip_empty, recursive): return if verbose: - # Verbose output: NAME, CPUS, TASKS, PARTITION - print(f"{'NAME':<20} {'CPUS':<15} {'TASKS':<7} {'PARTITION':<10}") - print("-" * 55) + # Verbose output: NAME, CPUS, MEMS, TASKS, PARTITION + print(f"{'NAME':<20} {'CPUS':<15} {'MEMS':<10} {'TASKS':<7} {'PARTITION':<10}") + print("-" * 65) for cs_name in cpusets: cs_path = os.path.join('/sys/fs/cgroup', cs_name) @@ -930,6 +930,10 @@ def cpuset_list(pattern, verbose, skip_empty, recursive): with open(os.path.join(cs_path, 'cpuset.cpus'), 'r', encoding='utf-8') as f: cpus = f.read().strip() or "(none)" + # Read memory nodes + with open(os.path.join(cs_path, 'cpuset.mems'), 'r', encoding='utf-8') as f: + mems = f.read().strip() or "(none)" + # Read tasks count with open(os.path.join(cs_path, 'cgroup.procs'), 'r', encoding='utf-8') as f: tasks = [line.strip() for line in f if line.strip()] @@ -943,7 +947,7 @@ def cpuset_list(pattern, verbose, skip_empty, recursive): else: partition = "n/a" - print(f"{cs_name:<20} {cpus:<15} {task_count:<7} {partition:<10}") + print(f"{cs_name:<20} {cpus:<15} {mems:<10} {task_count:<7} {partition:<10}") except (OSError, IOError) as e: print(f"{cs_name:<20} (error reading: {e})", file=sys.stderr) @@ -952,6 +956,39 @@ def cpuset_list(pattern, verbose, skip_empty, recursive): for cs_name in cpusets: print(cs_name) + # Check for blocklisted processes in cpusets + # Skip system cgroups (*.mount, *.scope, *.slice) - these are managed by systemd + warnings = [] + for cs_name in cpusets: + # Skip system cgroups - only check user-created cpusets + if cs_name.endswith(('.mount', '.scope', '.slice')): + continue + + cs_path = os.path.join('/sys/fs/cgroup', cs_name) + try: + with open(os.path.join(cs_path, 'cgroup.procs'), 'r', encoding='utf-8') as f: + pids = [line.strip() for line in f if line.strip()] + for pid_str in pids: + try: + pid = int(pid_str) + if cpuset.Cpuset._is_process_blocklisted(pid): + comm = cpuset.Cpuset._get_process_name(pid) + warnings.append(f"⚠️ WARNING: Blocklisted process PID {pid} ({comm}) found in cpuset '{cs_name}'") + except ValueError: + # Invalid PID, skip + pass + except (OSError, IOError): + # Can't read procs, skip + pass + + # Display warnings if any + if warnings: + print() + for warning in warnings: + print(warning, file=sys.stderr) + print("\nBlocklisted processes should not be in custom cpusets!", file=sys.stderr) + print("This may indicate a problem with blocklist protection.", file=sys.stderr) + def cpuset_destroy(name, pattern, force, skip_empty, recursive): """Handler for 'tuna cpuset destroy' command. -- 2.54.0