[PATCH 30/36] tuna: Add --cpuset filter option to show_threads command

John Kacur <[email protected]> Fri, 10 Jul 2026 10:15:08 -0400
Newsgroups org.kernel.vger.linux-rt-users
Message-ID <[email protected]>
Add a --cpuset option to the show_threads command to filter and display
only processes that belong to a specific cpuset. This makes it easy to
monitor which processes are running in a particular cpuset without
manually inspecting cgroup paths.

The filter works by parsing the cgroup path from /proc/[pid]/cgroup and
extracting the cpuset name. Processes are only displayed if they match
the requested cpuset name.

Usage:
  tuna show_threads --cpuset <name>    Show processes in cpuset
  tuna show_threads --cpuset ""        Show processes in root cgroup

The option can be combined with other filters:
  tuna show_threads --cpuset tuna0 -t <pattern>   Filter by cpuset and thread
  tuna show_threads --cpuset tuna0 -G             Show with cgroup info
  tuna show_threads --cpuset tuna0 -c 0-3         Filter by cpuset and CPUs

This completes the third integration feature for cpusets (Enhancement #5c),
providing a convenient way to verify which processes are in a cpuset after
using 'tuna move --cpuset' or 'tuna isolate --cpuset'.

Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: John Kacur <[email protected]>
---
 tuna-cmd.py | 25 +++++++++++++++++++++----
 1 file changed, 21 insertions(+), 4 deletions(-)

diff --git a/tuna-cmd.py b/tuna-cmd.py
index ce34aee8e909..c2208e268c97 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -247,6 +247,7 @@ def gen_parser():
     if have_inet_diag:
         show_threads.add_argument('-n', '--show_sockets', **MODS['show_sockets'])
     show_threads.add_argument('-G', '--cgroups', **MODS['cgroups'])
+    show_threads.add_argument('--cpuset', type=str, metavar='CPUSET-NAME', help='Show only threads in the specified cpuset')
     show_threads.add_argument('-z', '--spaced', **MODS['spaced'])
 
 
@@ -469,7 +470,7 @@ def ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes,
 
 def ps_show(ps, affect_children, thread_list, cpu_list,
             irq_list_numbers, show_uthreads, show_kthreads,
-            has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, compact, match_requested, irqs):
+            has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, compact, match_requested, irqs, cpuset_filter=None):
 
     ps_list = []
     for pid in list(ps.keys()):
@@ -502,6 +503,22 @@ def ps_show(ps, affect_children, thread_list, cpu_list,
             raise e
         if cpu_list and not set(cpu_list).intersection(set(affinity)):
             continue
+        # Filter by cpuset if requested
+        if cpuset_filter:
+            # Extract cpuset name from cgroup path
+            # Format: "0::/cpuset_name" or "0::/" for root
+            cgroup_path = ps[pid]["cgroups"]
+            if cgroup_path.startswith("0::"):
+                cgroup_path = cgroup_path[3:]  # Remove "0::" prefix
+            # Remove leading slash to get cpuset name
+            if cgroup_path.startswith("/"):
+                cgroup_path = cgroup_path[1:]
+            # Get the first component (cpuset name)
+            # For nested cpusets like "parent/child", this gets "parent"
+            cpuset_name = cgroup_path.split('/')[0] if cgroup_path else ""
+            # Check if process is in the requested cpuset
+            if cpuset_name != cpuset_filter:
+                continue
         if match_requested and thread_list and pid in thread_list:
             ps_list.append(pid)
         elif not match_requested:
@@ -540,7 +557,7 @@ def load_sockets():
 
 
 def do_ps(thread_list, cpu_list, irq_list, show_uthreads, show_kthreads,
-          affect_children, show_sockets, cgroups, compact, match_requested):
+          affect_children, show_sockets, cgroups, compact, match_requested, cpuset_filter=None):
     ps = procfs.pidstats()
     if affect_children:
         ps.reload_threads()
@@ -559,7 +576,7 @@ def do_ps(thread_list, cpu_list, irq_list, show_uthreads, show_kthreads,
             ps_show_header(has_ctxt_switch_info, cgroups)
         ps_show(ps, affect_children, thread_list,
                 cpu_list, irq_list, show_uthreads, show_kthreads,
-                has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, compact, match_requested, irqs)
+                has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups, compact, match_requested, irqs, cpuset_filter)
     except IOError:
         # 'tuna -P | head' for instance
         pass
@@ -1712,7 +1729,7 @@ def main():
 
         if has_content:
             do_ps(args.thread_list, args.cpu_list, args.irq_list, args.uthreads,
-                    args.kthreads, args.affect_children, args.show_sockets if "show_sockets" in args else None, args.cgroups, args.compact, match_requested)
+                    args.kthreads, args.affect_children, args.show_sockets if "show_sockets" in args else None, args.cgroups, args.compact, match_requested, args.cpuset if "cpuset" in args else None)
 
     elif args.command in ['show_irqs']:
         # Skip display if pattern matched nothing (warning already shown)
-- 
2.54.0