* [PATCH 0/6] User experience and code quality improvements
@ 2026-06-22 20:21 John Kacur
2026-06-22 20:21 ` [PATCH 1/6] tuna: Warn when user-specified patterns match zero threads or IRQs John Kacur
` (5 more replies)
0 siblings, 6 replies; 7+ messages in thread
From: John Kacur @ 2026-06-22 20:21 UTC (permalink / raw)
To: linux-rt-users
This patch series improves tuna's user experience and code quality through
several enhancements:
Patches 1-3 improve pattern matching feedback by warning users when their
thread or IRQ patterns match zero results, with an opt-in -w/--warn flag
to control this behavior.
Patch 4 enhances the what_is command with updated kernel thread descriptions
for modern kernels, including jbd2, kworker, irq/, and other commonly seen
threads.
Patch 5 adds comprehensive docstrings to all converter functions, improving
code documentation and maintainability.
Patch 6 adds centralized input validation for CPU numbers and RT priorities,
providing clear error messages instead of confusing system call failures.
John Kacur (6):
tuna: Warn when user-specified patterns match zero threads or IRQs
tuna: Suppress output when pattern matches zero results
tuna: Make warnings opt-in with -w/--warn flag
tuna: Enhance what_is command with new kernel thread descriptions and
usability improvements
tuna: Add comprehensive docstrings to converter functions
tuna: Add centralized input validation for CPU numbers and RT
priorities
tuna-cmd.py | 211 ++++++++++++++++++++++++++++++++++++++++++++++++---
tuna/help.py | 26 +++++--
tuna/tuna.py | 51 ++++++++++++-
3 files changed, 269 insertions(+), 19 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH 1/6] tuna: Warn when user-specified patterns match zero threads or IRQs
2026-06-22 20:21 [PATCH 0/6] User experience and code quality improvements John Kacur
@ 2026-06-22 20:21 ` John Kacur
2026-06-22 20:21 ` [PATCH 2/6] tuna: Suppress output when pattern matches zero results John Kacur
` (4 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: John Kacur @ 2026-06-22 20:21 UTC (permalink / raw)
To: linux-rt-users
When a user specifies a thread or IRQ pattern that matches nothing,
tuna previously showed only empty output with column headers. This left
users uncertain whether their pattern syntax was incorrect, the pattern
was valid but matched nothing, or something failed silently.
Add feedback when patterns match zero results by printing a warning to
stderr. This helps users debug their patterns and understand that tuna
processed the pattern but found no matches.
The warning appears for relevant commands (show_threads, show_irqs,
move, spread, priority, what_is) only when the user explicitly specified
a pattern (match_requested == True) and the resulting list is empty.
Example:
$ tuna show_threads -t 'foo*'
tuna: warning: thread pattern matched 0 threads
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: John Kacur <jkacur@redhat.com>
---
tuna-cmd.py | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/tuna-cmd.py b/tuna-cmd.py
index 293044920223..015bb7a54663 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -712,6 +712,15 @@ def main():
if 'nohz_full' in vars(args) and args.nohz_full:
args.cpu_list = nohz_full_to_cpu()
+ # Warn if user-specified patterns matched nothing
+ if match_requested:
+ if args.command in ['show_threads', 'move', 'm', 'spread', 'x', 'priority', 'p', 'what_is', 'W']:
+ if 'thread_list' in vars(args) and isinstance(args.thread_list, list) and not args.thread_list:
+ print("tuna: warning: thread pattern matched 0 threads", file=sys.stderr)
+ if args.command in ['show_irqs', 'move', 'm', 'spread', 'x']:
+ if 'irq_list' in vars(args) and isinstance(args.irq_list, list) and not args.irq_list:
+ print("tuna: warning: IRQ pattern matched 0 IRQs", file=sys.stderr)
+
# if args.command in ['apply', 'a']:
# apply_config(args.profilename)
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 2/6] tuna: Suppress output when pattern matches zero results
2026-06-22 20:21 [PATCH 0/6] User experience and code quality improvements John Kacur
2026-06-22 20:21 ` [PATCH 1/6] tuna: Warn when user-specified patterns match zero threads or IRQs John Kacur
@ 2026-06-22 20:21 ` John Kacur
2026-06-22 20:21 ` [PATCH 3/6] tuna: Make warnings opt-in with -w/--warn flag John Kacur
` (3 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: John Kacur @ 2026-06-22 20:21 UTC (permalink / raw)
To: linux-rt-users
When a user-specified pattern matches nothing, showing empty column
headers is redundant after already displaying the warning message.
Suppress the display output entirely for show_threads and show_irqs
commands when patterns match zero results. The warning message to
stderr provides sufficient feedback to the user.
Example:
$ tuna show_threads -t 'foo*'
tuna: warning: thread pattern matched 0 threads
(no headers or empty output displayed)
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: John Kacur <jkacur@redhat.com>
---
tuna-cmd.py | 26 +++++++++++++++++++++++---
1 file changed, 23 insertions(+), 3 deletions(-)
diff --git a/tuna-cmd.py b/tuna-cmd.py
index 015bb7a54663..d9d1eaa83757 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -746,11 +746,31 @@ def main():
list_config()
elif args.command in ['show_threads']:
- 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)
+ # Skip display if pattern matched nothing (warning already shown)
+ has_content = False
+ if match_requested:
+ if 'thread_list' in vars(args) and isinstance(args.thread_list, list) and args.thread_list:
+ has_content = True
+ if 'irq_list' in vars(args) and isinstance(args.irq_list, list) and args.irq_list:
+ has_content = True
+ else:
+ has_content = True # No filtering, will show content
+
+ 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)
elif args.command in ['show_irqs']:
- show_irqs(args.irq_list, args.cpu_list, match_requested)
+ # Skip display if pattern matched nothing (warning already shown)
+ has_content = False
+ if match_requested:
+ if 'irq_list' in vars(args) and isinstance(args.irq_list, list) and args.irq_list:
+ has_content = True
+ else:
+ has_content = True # No filtering, will show content
+
+ if has_content:
+ show_irqs(args.irq_list, args.cpu_list, match_requested)
elif args.command in ['move', 'm', 'spread', 'x']:
spread = args.command in ['spread', 'x']
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 3/6] tuna: Make warnings opt-in with -w/--warn flag
2026-06-22 20:21 [PATCH 0/6] User experience and code quality improvements John Kacur
2026-06-22 20:21 ` [PATCH 1/6] tuna: Warn when user-specified patterns match zero threads or IRQs John Kacur
2026-06-22 20:21 ` [PATCH 2/6] tuna: Suppress output when pattern matches zero results John Kacur
@ 2026-06-22 20:21 ` John Kacur
2026-06-22 20:21 ` [PATCH 4/6] tuna: Enhance what_is command with new kernel thread descriptions and usability improvements John Kacur
` (2 subsequent siblings)
5 siblings, 0 replies; 7+ messages in thread
From: John Kacur @ 2026-06-22 20:21 UTC (permalink / raw)
To: linux-rt-users
Change warnings from automatic to opt-in behavior. Users must now
specify -w or --warn to see warning messages when patterns match
nothing. This keeps output clean for shell scripts and command chaining.
Also adopt rteval-style [WARNING] prefix format instead of
"tuna: warning:" for consistency with other realtime tools.
Examples:
$ tuna show_threads -t 'foo*'
(silent - no output)
$ tuna -w show_threads -t 'foo*'
[WARNING] thread pattern matched 0 threads
This allows shell chaining without warning noise while still
providing feedback when users want it.
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: John Kacur <jkacur@redhat.com>
---
tuna-cmd.py | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/tuna-cmd.py b/tuna-cmd.py
index d9d1eaa83757..c4bd55fac3df 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -97,6 +97,7 @@ def gen_parser():
"logging": dict(dest='loglevel', metavar='LOG-LEVEL', type=get_loglevel, help="Log application details to file for given LOG-LEVEL"),
"debug" : dict(action='store_true', dest='debug', help='Print DEBUG level logging details to console'),
"version": dict(action='version', version='0.20', help="show version"),
+ "warn": dict(action='store_true', dest='warn', help='Warn when thread/IRQ patterns match nothing'),
"threads": dict(dest='thread_list', default='', metavar='THREAD-LIST', type=str, help="THREAD-LIST affected by commands"),
"irqs": dict(dest='irq_list', default='', metavar='IRQ-LIST', type=str, help="IRQ-LIST affect by commands"),
"cpus": dict(dest='cpu_list', default=[], metavar='CPU-LIST', type=tuna.cpustring_to_list, help='CPU-LIST affected by commands'),
@@ -124,6 +125,7 @@ def gen_parser():
parser.add_argument('-v', '--version', **MODS['version'])
parser.add_argument('-L', '--logging', **MODS['logging'])
parser.add_argument('-D', '--debug', **MODS['debug'])
+ parser.add_argument('-w', '--warn', **MODS['warn'])
subparser = parser.add_subparsers(dest='command')
@@ -712,14 +714,14 @@ def main():
if 'nohz_full' in vars(args) and args.nohz_full:
args.cpu_list = nohz_full_to_cpu()
- # Warn if user-specified patterns matched nothing
- if match_requested:
+ # Warn if user-specified patterns matched nothing (only if --warn)
+ if match_requested and args.warn:
if args.command in ['show_threads', 'move', 'm', 'spread', 'x', 'priority', 'p', 'what_is', 'W']:
if 'thread_list' in vars(args) and isinstance(args.thread_list, list) and not args.thread_list:
- print("tuna: warning: thread pattern matched 0 threads", file=sys.stderr)
+ print("[WARNING] thread pattern matched 0 threads", file=sys.stderr)
if args.command in ['show_irqs', 'move', 'm', 'spread', 'x']:
if 'irq_list' in vars(args) and isinstance(args.irq_list, list) and not args.irq_list:
- print("tuna: warning: IRQ pattern matched 0 IRQs", file=sys.stderr)
+ print("[WARNING] IRQ pattern matched 0 IRQs", file=sys.stderr)
# if args.command in ['apply', 'a']:
# apply_config(args.profilename)
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 4/6] tuna: Enhance what_is command with new kernel thread descriptions and usability improvements
2026-06-22 20:21 [PATCH 0/6] User experience and code quality improvements John Kacur
` (2 preceding siblings ...)
2026-06-22 20:21 ` [PATCH 3/6] tuna: Make warnings opt-in with -w/--warn flag John Kacur
@ 2026-06-22 20:21 ` John Kacur
2026-06-22 20:21 ` [PATCH 5/6] tuna: Add comprehensive docstrings to converter functions John Kacur
2026-06-22 20:21 ` [PATCH 6/6] tuna: Add centralized input validation for CPU numbers and RT priorities John Kacur
5 siblings, 0 replies; 7+ messages in thread
From: John Kacur @ 2026-06-22 20:21 UTC (permalink / raw)
To: linux-rt-users
Add comprehensive help descriptions for 14 modern kernel threads including
kworker, threaded IRQs, memory management daemons (kswapd, kcompactd,
khugepaged, ksmd), and system threads (kauditd, kdevtmpfs, oom_reaper,
watchdogd, cpuhp, rcu_preempt, rcu_sched). Also add jbd2 for ext4 journaling.
Update and correct 5 existing descriptions:
- ksoftirqd: Fix misleading "networking only" description to reflect it
handles all softirq types (timers, networking, block I/O, RCU, tasklets)
- watchdog: Fix incorrect sysctl references, correctly describe soft/hard
lockup detection
- events, kblockd: Mark as legacy, explain modern kworker replacement
- kjournald: Mark as ext3-specific legacy, reference jbd2 for ext4
Improve pattern matching:
- Handle NUMA-numbered threads (kswapd0 -> kswapd, kcompactd1 -> kcompactd)
by stripping trailing digits
- Fix kworker threads with multiple slashes (kworker/R-xfs-buf/nvme0n1p2)
by special-casing the kworker/ prefix
Fix bugs:
- Reduce double blank line between title and description to single line
- Fix user threads printing title twice by returning empty help instead
of duplicating the title
Enhance usability:
- Accept thread lists with flexible separators: "811, 950" (comma+space),
"811,950" (comma), "811 950" (space), all without requiring quotes
- Support multiple positional arguments (nargs='+') and join them
- Split input by both comma and whitespace using regex
- Add blank lines between thread descriptions for better readability
- Apply same flexible parsing to IRQ and socket lists for consistency
The what_is command is now significantly more useful for understanding
modern kernel threads, with better formatting and more intuitive input
handling.
Assisted-by: Claude:claude-sonnet-4-5
Signed-off-by: John Kacur <jkacur@redhat.com>
---
tuna-cmd.py | 29 +++++++++++++++++++++--------
tuna/help.py | 26 ++++++++++++++++++++------
tuna/tuna.py | 21 +++++++++++++++++++--
3 files changed, 60 insertions(+), 16 deletions(-)
diff --git a/tuna-cmd.py b/tuna-cmd.py
index c4bd55fac3df..6cb0a679cc60 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -86,7 +86,7 @@ def gen_parser():
POS = {
"cpu_list": dict(metavar='CPU-LIST', type=tuna.cpustring_to_list, help="CPU-LIST affected by commands"),
- "thread_list": dict(metavar='THREAD-LIST', type=str, help="THREAD-LIST affected by commands"),
+ "thread_list": dict(metavar='THREAD-LIST', nargs='+', help="THREAD-LIST affected by commands"),
"filename": dict(metavar='FILENAME', type=str, help="Save kthreads sched tunables to this file"),
"profilename": dict(type=str, help="Apply changes described in this file"),
"run_command": dict(metavar='COMMAND', type=str, help="fork a new process and run the \"COMMAND\""),
@@ -255,7 +255,7 @@ def thread_help(tid, ps):
pinfo = ps[tid]
cmdline = procfs.process_cmdline(pinfo)
help, title = tuna.kthread_help_plain_text(tid, cmdline)
- print(title, "\n\n")
+ print(title, "\n")
if help.isspace():
help = "No help description available."
print(help)
@@ -545,7 +545,9 @@ def threadstring_to_list(threadstr, ps=None):
thread_list = []
if not threadstr:
return thread_list, match_requested
- thread_strings = list(set(threadstr.split(',')))
+ # Split by both comma and whitespace to handle "811,950" and "811, 950" and "811 950"
+ import re
+ thread_strings = list(set(s.strip() for s in re.split(r'[,\s]+', threadstr) if s.strip()))
for s in thread_strings:
if s.isdigit():
thread_list.append(int(s))
@@ -563,7 +565,9 @@ def irqstring_to_list(irqstr):
irq_list = []
if not irqstr:
return irq_list, match_requested
- irq_strings = list(set(irqstr.split(',')))
+ # Split by both comma and whitespace
+ import re
+ irq_strings = list(set(s.strip() for s in re.split(r'[,\s]+', irqstr) if s.strip()))
for s in irq_strings:
if s.isdigit():
irq_list.append(int(s))
@@ -575,7 +579,9 @@ def irqstring_to_list(irqstr):
def socketstring_to_list(socketstr):
cpu_list = []
- socket_strings = list(set(socketstr.split(',')))
+ # Split by both comma and whitespace
+ import re
+ socket_strings = list(set(s.strip() for s in re.split(r'[,\s]+', socketstr) if s.strip()))
cpu_info = sysfs.cpus()
for s in socket_strings:
@@ -673,8 +679,12 @@ def main():
# Convert thread_list from string to list
if 'thread_list' in vars(args) and args.thread_list:
- if isinstance(args.thread_list, str):
- args.thread_list, match_requested = threadstring_to_list(args.thread_list, ps)
+ # Join multiple arguments into a single string (handles "811, 950" without quotes)
+ if isinstance(args.thread_list, list):
+ thread_str = ' '.join(args.thread_list)
+ else:
+ thread_str = args.thread_list
+ args.thread_list, match_requested = threadstring_to_list(thread_str, ps)
# Convert irq_list from string to list
if 'irq_list' in vars(args) and args.irq_list:
@@ -792,8 +802,11 @@ def main():
elif args.command in ['W', 'what_is']:
if not ps:
ps = procfs.pidstats()
- for tid in args.thread_list:
+ for i, tid in enumerate(args.thread_list):
thread_help(tid, ps)
+ # Add blank line between threads (but not after the last one)
+ if i < len(args.thread_list) - 1:
+ print()
elif args.command in ['g', 'gui']:
# Don't try to start the gui if no display is available
diff --git a/tuna/help.py b/tuna/help.py
index 1a9473a86875..458897e01fb8 100644
--- a/tuna/help.py
+++ b/tuna/help.py
@@ -14,23 +14,37 @@ KTHREAD_HELP = {
'sirq-rcu/':N_('Pushes the RCU grace period along (if possible) and will handle dereferenced RCU callbacks, such as freeing structures after a grace period. \n[One per CPU]'),
'khelper':N_('Used to call user mode helpers from the kernel, such as /sbin/bridge-stp, ocfs2_hb_ctl, pnpbios, poweroff, request-key, etc."'),
'group_balance':N_('Scheduler load balance monitoring.'),
-'kjournald':N_('Main thread function used to manage a filesystem logging device journal. This kernel thread is responsible for two things: <b>COMMIT</b>: Every so often we need to commit the current state of the filesystem to disk. The journal thread is responsible for writing all of the metadata buffers to disk. <b>CHECKPOINT</b>: We cannot reuse a used section of the log file until all of the data in that part of the log has been rewritten elsewhere on the disk. Flushing these old buffers to reclaim space in the log is known as checkpointing, and this thread is responsible for that job.'),
+'kjournald':N_('Legacy: Journal thread for ext3 filesystems. Responsible for two main tasks: <b>COMMIT</b>: Periodically commits filesystem state to disk by writing metadata buffers. <b>CHECKPOINT</b>: Flushes old buffers to reclaim log space (checkpointing). Modern systems using ext4 use jbd2/device-name threads instead (e.g., jbd2/sda1-8).'),
+'jbd2/':N_('Journal block device thread for ext4 filesystems (JBD2 = Journaling Block Device version 2). Handles commit and checkpoint operations for ext4 journaling. Named as jbd2/device-name (e.g., jbd2/sda1-8, jbd2/dm-0). Runs at normal priority but can be tuned for latency-sensitive workloads.'),
'lockd':N_('Locking arbiter for NFS on the system'),
'sirq-sched/':N_('Triggered when a rebalance of tasks is needed to CPU domains. This handles balancing of SCHED_OTHER tasks across CPUs. RT tasks balancing is done directly in schedule and wakeup paths. Runs at prio 1 because it needs to schedule above all SCHED_OTHER tasks. If the user has the same issue but doesn"t mind having latencies against other kernel threads that run here, then its fine. But it should definitely be documented that PRIO 1 has other threads on it at boot up. \n[One per CPU]'),
'sirq-high/':N_('This is from a poor attempt to prioritize tasklets. Some tasklets wanted to run before anything else. Thus there were two tasklet softirqs made. tasklet_vec and tasklet_hi_vec. A driver writer could put their "critical" tasklets into the tasklet_hi_vec and it would run before other softirqs. This never really worked as intended. \n[One per CPU]'),
-'kblockd/':N_('Workqueue used to process IO requests. Used by IO schedulers and block device drivers. \n[One per CPU]'),
+'kblockd/':N_('Legacy: In older kernels, dedicated per-CPU threads for the block I/O subsystem workqueue. In modern kernels, this work is handled by kworker threads. You may see "kblockd" in kworker names like "kworker/0:1H-kblockd", indicating block I/O work. Used by IO schedulers and block device drivers. \n[One per CPU - Legacy only]'),
'sirq-net-rx/':N_('When receiving a packet the device will place the packet on a queue with its hard interrupt (threaded in RT). The sirq-net-rx is responsible for finding out what to do with the packet. It may forward it to another box if the current box is used as a router, or it will find the task the packet is for. If that task is currently waiting for the packet, the softirq might hand it off to that task and the task will handle the rest of the processing of the packet. \n[One per CPU]'),
'krcupreemptd':N_('This should run at the lowest RT priority. With preemptible RCU, a loaded system may have tasks that hold RCU locks but have a high nice value. These tasks may be pushed off for seconds, and if the system is tight on memory, the RCU deferred freeing may not occur. The result can be drastic. The krcupreemptd is a daemon that runs just above SCHED_OTHER and wakes up once a second and performs a synchronize RCU. With RCU boosting, all those that hold RCU locks will inherit the priority of the krcupreemptd and wake up and release the RCU locks. This is only a concern for loaded systems and SCHED_OTHER tasks. If there is an issue of RT tasks starving out SCHED_OTHER tasks and causing problems with freeing memory, then the RT tasks are designed badly.'),
-'ksoftirqd/':N_('Activated when under heavy networking activity. Used to avoid monopolizing the CPUs doing just software interrupt processing. \n[One per CPU]'),
+'ksoftirqd/':N_('Handles softirq processing in process context. When softirqs (timers, networking, block I/O, RCU, tasklets, etc.) accumulate or take too long, this thread prevents them from monopolizing CPU time in interrupt context. Wakes up when softirq load is high or when in_interrupt() processing would exceed time limits. \n[One per CPU]'),
'sirq-timer/':N_('Basically the timer wheel. Things that add itself to the timer wheel timeouts will be handled by this softirq. Parts of the kernel that need timeouts will use this softirq (i.e. network timeouts). The resolution to these timeouts are defined by the HZ value. \n[One per CPU]'),
-'events/':N_('Global workqueue, used to schedule work to be done in process context. \n[One per CPU]'),
-'watchdog/':N_('Run briefly once per second to reset the softlockup timestamp. If this gets delayed for more than 60 seconds then a message will be printed. Use /proc/sys/kernel/hung_task_timeout_secs and /proc/sys/kernel/hung_task_check_count to control this behaviour. Setting /proc/sys/kernel/hung_task_timeout_secs to zero will disable this check. \n[One per CPU]'),
+'events/':N_('Legacy: In older kernels, these were dedicated per-CPU threads for the global "events" workqueue. In modern kernels (since ~2.6.36), workqueues are handled by kworker threads. You may still see "events" in kworker names like "kworker/0:1-events", indicating work from the events workqueue. \n[One per CPU - Legacy only]'),
+'watchdog/':N_('Per-CPU watchdog thread that detects soft lockups (CPU stuck in kernel mode with preemption disabled) and hard lockups (CPU stuck with interrupts disabled). Runs at highest priority (SCHED_FIFO 99) and touches a timestamp periodically. If the timestamp is not updated within the threshold period, a lockup is detected and logged. Can be configured via /proc/sys/kernel/watchdog_thresh. \n[One per CPU]'),
'sirq-net-tx/':N_('This is the network transmit queue. Most of the time the network packets will be handled by the task that is sending the packets out, and doing so at the priority of that task. But if the protocol window or the network device queue is full, then the packets will be pushed off to later. The sirq-net-tx softirq is responsible for sending out these packets. \n[One per CPU]'),
'sirq-block/':N_('Called after a completion to a block device is made. Looking further into this call, I only see a couple of users. The SCSI driver uses this as well as cciss. \n[One per CPU]'),
'sirq-tasklet/':N_('Catch all for those devices that couldn"t use softirqs directly and mostly made before work queues were around. The difference between a tasklet and a softirq is that the same tasklet can not run on two different CPUs at the same time. In this regard it acts like a "task" (hence the name "tasklet"). Various devices use tasklets. \n[One per CPU]'),
'usb-storage':N_('Per USB storage device virtual SCSI controller. Persistant across device insertion/removal, as is the SCSI node. This is done so that a device which is removed can be re-attached and be granted the same /dev node as before, creating persistance between connections of the target unit. Gets commands from the SCSI mid-layer and, after sanity checking several things, sends the command to the "protocol" handler. This handler is responsible for re-writing the command (if necessary) into a form which the device will accept. For example, ATAPI devices do not support 6-byte commands. Thus, they must be re-written into 10-byte variants.'),
'migration/':N_('High priority system thread that performs thread migration by bumping thread off CPU then pushing onto another runqueue. \n[One per CPU]'),
-'rpciod/':N_('Handles Sun RPC network messages (mainly for NFS) \n[One per CPU]')
+'rpciod/':N_('Handles Sun RPC network messages (mainly for NFS) \n[One per CPU]'),
+'irq/':N_('Threaded IRQ handler. Modern kernels can run interrupt handlers in kernel threads instead of hardirq context, allowing them to be preempted and scheduled like other tasks. This is especially important for PREEMPT_RT kernels. Named as irq/NUMBER-handler_name (e.g., irq/197-iwlwifi:default_queue). The thread priority and CPU affinity can be controlled to reduce latency. Use /proc/irq/NUMBER/smp_affinity to control CPU binding.'),
+'kworker/':N_('Kernel workqueue threads. These handle deferred work from various kernel subsystems. Work items are queued and processed asynchronously in process context. The naming format is kworker/CPU:ID-workqueue_name (e.g., kworker/0:1-events). High priority workqueues use "H" suffix. Modern kernels use "R-" prefix for rescue threads. \n[Multiple per CPU]'),
+'kswapd':N_('Virtual memory pageout daemon. Responsible for reclaiming memory pages when the system is low on free memory. It writes dirty pages to swap and reclaims clean pages. Wakes up periodically or when free memory falls below certain thresholds. On NUMA systems, there is one instance per memory node (kswapd0, kswapd1, etc.).'),
+'khugepaged':N_('Transparent Huge Pages (THP) daemon. Scans memory to find sequences of 4KB pages that can be collapsed into a single 2MB huge page to reduce TLB pressure and improve performance. Can be controlled via /sys/kernel/mm/transparent_hugepage/.'),
+'kcompactd':N_('Memory compaction daemon. Performs memory defragmentation to create larger contiguous blocks of free memory, which is essential for huge page allocations. Runs periodically and when memory fragmentation is detected. On NUMA systems, there is one instance per memory node (kcompactd0, kcompactd1, etc.).'),
+'ksmd':N_('Kernel Samepage Merging daemon. Scans memory to find identical pages and merges them to save memory. Primarily used in virtualized environments where multiple VMs may have identical pages. Can be controlled via /sys/kernel/mm/ksm/.'),
+'kdevtmpfs':N_('Device filesystem daemon. Manages the devtmpfs filesystem, automatically creating and removing device nodes in /dev as devices are added or removed from the system.'),
+'kauditd':N_('Kernel audit daemon. Handles audit records generated by the Linux audit subsystem, writing them to the audit log. Part of the security auditing framework.'),
+'oom_reaper':N_('Out-of-Memory reaper thread. Reclaims memory from processes that have been killed by the OOM killer. Runs asynchronously to quickly free memory without waiting for the dying process to exit completely.'),
+'watchdogd':N_('Hardware watchdog daemon. Periodically writes to the hardware watchdog device to prevent system resets. Different from the per-CPU softlockup watchdog threads (watchdog/).'),
+'cpuhp/':N_('CPU hotplug state machine threads. Manage the CPU hotplug process, handling the complex state transitions when CPUs are brought online or taken offline. \n[One per CPU]'),
+'rcu_preempt':N_('RCU (Read-Copy-Update) thread for preemptible RCU. Handles grace period detection and callback processing for the preemptible variant of RCU used in PREEMPT kernels.'),
+'rcu_sched':N_('RCU (Read-Copy-Update) scheduler thread. Handles grace period detection for non-preemptible RCU, ensuring readers have completed before allowing writers to free old data structures.')
}
PROC_SYS_HELP = {
diff --git a/tuna/tuna.py b/tuna/tuna.py
index 8e57cddd0c13..be3ca5017ef0 100755
--- a/tuna/tuna.py
+++ b/tuna/tuna.py
@@ -22,9 +22,26 @@ except NameError:
fntable = []
def kthread_help(key):
+ # Special case: kworker threads (can have multiple slashes in workqueue name)
+ if key.startswith('kworker/'):
+ return help.KTHREAD_HELP.get('kworker/', " ")
+
+ # Handle per-CPU threads with / suffix (e.g., ksoftirqd/0 -> ksoftirqd/)
if '/' in key:
key = key[:key.rfind('/')+1]
- return help.KTHREAD_HELP.get(key, " ")
+ return help.KTHREAD_HELP.get(key, " ")
+
+ # Try exact match first
+ result = help.KTHREAD_HELP.get(key)
+ if result and result != " ":
+ return result
+
+ # Strip trailing digits for NUMA threads (e.g., kswapd0 -> kswapd, kcompactd1 -> kcompactd)
+ base_key = re.sub(r'\d+$', '', key)
+ if base_key != key: # Had digits to strip
+ return help.KTHREAD_HELP.get(base_key, " ")
+
+ return " "
def proc_sys_help(key):
if not fntable:
@@ -50,7 +67,7 @@ def kthread_help_plain_text(pid, cmdline):
help = kthread_help(cmdline)
else:
title = _("User Thread %(pid)d (%(cmdline)s):") % params
- help = title
+ help = " " # No help available for user threads
return help, title
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 5/6] tuna: Add comprehensive docstrings to converter functions
2026-06-22 20:21 [PATCH 0/6] User experience and code quality improvements John Kacur
` (3 preceding siblings ...)
2026-06-22 20:21 ` [PATCH 4/6] tuna: Enhance what_is command with new kernel thread descriptions and usability improvements John Kacur
@ 2026-06-22 20:21 ` John Kacur
2026-06-22 20:21 ` [PATCH 6/6] tuna: Add centralized input validation for CPU numbers and RT priorities John Kacur
5 siblings, 0 replies; 7+ messages in thread
From: John Kacur @ 2026-06-22 20:21 UTC (permalink / raw)
To: linux-rt-users
Add detailed docstrings to all argparse converter functions to improve
code documentation and maintainability:
- threadstring_to_list(): Documents flexible input formats (PIDs,
patterns, mixed), parameters, and the match_requested return value
- irqstring_to_list(): Explains IRQ number and pattern matching
- socketstring_to_list(): Documents socket-to-CPU expansion and
validation behavior
- pick_op(): Explains operation prefix extraction (+, -, or None)
- get_loglevel(): Documents numeric (0-4) and string format conversion
- get_policy_and_rtprio(): Explains POLICY:RTPRIO parsing
Each docstring includes Args, Returns, and Notes sections with clear
examples of accepted input formats. This completes task #3 from the
argparse enhancements tracking document.
Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: John Kacur <jkacur@redhat.com>
---
tuna-cmd.py | 119 +++++++++++++++++++++++++++++++++++++++++++++++++++
tuna/tuna.py | 30 +++++++++++++
2 files changed, 149 insertions(+)
diff --git a/tuna-cmd.py b/tuna-cmd.py
index 6cb0a679cc60..f1605896c33c 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -28,6 +28,28 @@ import shutil
import tuna.cpupower as cpw
def get_loglevel(level):
+ """Convert a logging level string to a Python logging level constant.
+
+ Accepts either numeric (0-4) or string (DEBUG, INFO, WARNING, ERROR) formats.
+ Numeric values are multiplied by 10 to match Python logging constants.
+
+ Args:
+ level: Logging level as string. Can be:
+ - Numeric: "0" (NOTSET), "1" (DEBUG), "2" (INFO), "3" (WARNING), "4" (ERROR)
+ - String: "DEBUG", "INFO", "WARNING", "ERROR" (case-insensitive)
+
+ Returns:
+ Integer logging level constant compatible with Python's logging module.
+ - Numeric input: Returns level * 10 (e.g., "1" -> 10 for DEBUG)
+ - String input: Returns the uppercase string (e.g., "debug" -> "DEBUG")
+
+ Raises:
+ ValueError: If level is "CRITICAL" (not supported by tuna)
+
+ Note:
+ Python logging levels: 0=NOTSET, 10=DEBUG, 20=INFO, 30=WARNING, 40=ERROR, 50=CRITICAL
+ CRITICAL level is explicitly rejected as tuna does not support it.
+ """
if level.isdigit() and int(level) in range(0,5):
# logging built-in module levels:
# 0 - NOTSET
@@ -541,6 +563,31 @@ def do_list_op(op, current_list, op_list):
return list(set(op_list))
def threadstring_to_list(threadstr, ps=None):
+ """Convert a thread specifier string to a list of thread IDs.
+
+ Accepts flexible input formats with comma and/or space separators.
+ Thread specifiers can be numeric PIDs or name patterns with wildcards.
+
+ Args:
+ threadstr: String of thread specifiers. Can be:
+ - Numeric PIDs: "1", "1,2,3", "1, 2, 3", "1 2 3"
+ - Thread name patterns: "systemd*", "kworker/*"
+ - Mixed: "1,systemd*,100", "1 systemd* 100"
+ Patterns use shell-style wildcards (* and ?).
+ ps: Optional procfs.pidstats() object. If None, will be created
+ on demand when needed to resolve name patterns.
+
+ Returns:
+ Tuple of (thread_list, match_requested) where:
+ - thread_list: List of integer PIDs that match the specifiers
+ - match_requested: Boolean indicating if any filtering was requested
+ (True if threadstr is non-empty, False otherwise)
+
+ Note:
+ The match_requested flag is used by display functions to determine
+ whether to show only matched threads or all threads in the system.
+ Empty input returns ([], False) to indicate "show all threads".
+ """
match_requested = bool(threadstr)
thread_list = []
if not threadstr:
@@ -561,6 +608,30 @@ def threadstring_to_list(threadstr, ps=None):
return thread_list, match_requested
def irqstring_to_list(irqstr):
+ """Convert an IRQ specifier string to a list of IRQ numbers.
+
+ Accepts flexible input formats with comma and/or space separators.
+ IRQ specifiers can be numeric IRQ numbers or IRQ user patterns with wildcards.
+
+ Args:
+ irqstr: String of IRQ specifiers. Can be:
+ - Numeric IRQ numbers: "50", "50,51,52", "50, 51, 52", "50 51 52"
+ - IRQ user patterns: "eth0*", "nvme*"
+ - Mixed: "50,eth0*,100", "50 eth0* 100"
+ Patterns use shell-style wildcards (* and ?) matched against
+ IRQ user names from /proc/interrupts.
+
+ Returns:
+ Tuple of (irq_list, match_requested) where:
+ - irq_list: List of integer IRQ numbers that match the specifiers
+ - match_requested: Boolean indicating if any filtering was requested
+ (True if irqstr is non-empty, False otherwise)
+
+ Note:
+ The match_requested flag is used by display functions to determine
+ whether to show only matched IRQs or all IRQs in the system.
+ Empty input returns ([], False) to indicate "show all IRQs".
+ """
match_requested = bool(irqstr)
irq_list = []
if not irqstr:
@@ -578,6 +649,30 @@ def irqstring_to_list(irqstr):
return irq_list, match_requested
def socketstring_to_list(socketstr):
+ """Convert a CPU socket specifier string to a list of CPU numbers.
+
+ Accepts flexible input formats with comma and/or space separators.
+ Socket specifiers are socket IDs that get expanded to all CPUs in those sockets.
+
+ Args:
+ socketstr: String of socket specifiers. Can be:
+ - Single socket: "0"
+ - Multiple sockets: "0,1", "0, 1", "0 1"
+ Socket IDs must be valid socket numbers present in the system.
+
+ Returns:
+ List of integer CPU numbers belonging to the specified sockets.
+ CPUs are returned in the order they appear in sysfs.
+
+ Raises:
+ SystemExit: If any socket ID is invalid. Prints available sockets
+ and exits with code 2.
+
+ Note:
+ This function queries sysfs to discover CPU topology and validate
+ socket IDs. Invalid socket IDs cause immediate program termination
+ with an error message showing valid options.
+ """
cpu_list = []
# Split by both comma and whitespace
import re
@@ -593,6 +688,30 @@ def socketstring_to_list(socketstr):
return cpu_list
def pick_op(argument):
+ """Extract an operation prefix from an argument string.
+
+ Checks if the argument starts with a '+' or '-' operation prefix.
+ If present, returns the operation and the remaining string.
+
+ Args:
+ argument: String to check for operation prefix.
+
+ Returns:
+ Tuple of (operation, remaining_string) where:
+ - operation: '+' for addition, '-' for removal, None for replacement
+ - remaining_string: The argument with the operation prefix removed,
+ or the original argument if no prefix was found
+
+ Examples:
+ >>> pick_op("+1,2,3")
+ ('+', '1,2,3')
+ >>> pick_op("-0,1")
+ ('-', '0,1')
+ >>> pick_op("1,2,3")
+ (None, '1,2,3')
+ >>> pick_op("")
+ (None, '')
+ """
if argument == "":
return (None, argument)
if argument[0] in ('+', '-'):
diff --git a/tuna/tuna.py b/tuna/tuna.py
index be3ca5017ef0..1cb538575b17 100755
--- a/tuna/tuna.py
+++ b/tuna/tuna.py
@@ -530,6 +530,36 @@ def get_irq_affinity_text(irqs, irq):
return ""
def get_policy_and_rtprio(parm):
+ """Parse a scheduling policy and priority from a POLICY:RTPRIO string.
+
+ Accepts flexible input formats for specifying scheduler policy and real-time priority.
+ Can specify just a policy, just a priority, or both separated by a colon.
+
+ Args:
+ parm: String specifying policy and/or priority. Can be:
+ - Policy only: "FIFO", "RR", "OTHER", "BATCH", "IDLE"
+ - Priority only: "50" (assumes current policy)
+ - Both: "FIFO:50", "RR:99"
+ Policy names are case-insensitive.
+
+ Returns:
+ Tuple of (policy, rtprio) where:
+ - policy: Integer scheduling policy constant (e.g., SCHED_FIFO, SCHED_RR)
+ or None if only priority was specified
+ - rtprio: Integer real-time priority (1-99 for RT policies, 0 for others)
+ Defaults to 1 for RT policies if not specified
+
+ Raises:
+ ValueError: If the policy name is invalid or cannot be parsed
+
+ Examples:
+ >>> get_policy_and_rtprio("FIFO:50")
+ (SCHED_FIFO, 50)
+ >>> get_policy_and_rtprio("RR")
+ (SCHED_RR, 1)
+ >>> get_policy_and_rtprio("50")
+ (None, 50)
+ """
parms = parm.split(":")
rtprio = 0
policy = None
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH 6/6] tuna: Add centralized input validation for CPU numbers and RT priorities
2026-06-22 20:21 [PATCH 0/6] User experience and code quality improvements John Kacur
` (4 preceding siblings ...)
2026-06-22 20:21 ` [PATCH 5/6] tuna: Add comprehensive docstrings to converter functions John Kacur
@ 2026-06-22 20:21 ` John Kacur
5 siblings, 0 replies; 7+ messages in thread
From: John Kacur @ 2026-06-22 20:21 UTC (permalink / raw)
To: linux-rt-users
Add a validation block in main() that validates converted arguments before
command dispatch. This provides early detection of invalid inputs with
clear, user-friendly error messages.
CPU number validation:
- Validates that all CPU numbers in cpu_list are within valid range
(0 to nr_cpus-1)
- Applies to commands: move, spread, isolate, include, run, save,
show_threads, show_irqs, gui, cpu_power
- Error message shows invalid CPUs and system's valid range
RT priority validation:
- Validates that RT priorities are within valid range (1-99)
- Applies to commands: priority, run
- Validates both POLICY:RTPRIO format and priority-only format
- Only validates when a policy is specified or priority is non-zero
Old behavior with invalid inputs:
- Invalid CPU: Either silent failure (show_threads) or confusing
tracebacks (move, isolate)
- Invalid RT priority: Generic "Invalid argument" errno message
New behavior:
- Clear error message (e.g., "tuna: Invalid CPU numbers: [999].
System has 16 CPUs (0-15)")
- Fails fast before attempting operations
- No verbose usage output, just the concise error
Benefits:
- Better user experience with clear error messages
- Centralized validation logic (easier to maintain)
- Prevents confusing error messages from system calls
This completes task #6 from the argparse enhancements tracking document.
Assisted-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: John Kacur <jkacur@redhat.com>
---
tuna-cmd.py | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/tuna-cmd.py b/tuna-cmd.py
index f1605896c33c..b9a071414125 100755
--- a/tuna-cmd.py
+++ b/tuna-cmd.py
@@ -852,6 +852,32 @@ def main():
if 'irq_list' in vars(args) and isinstance(args.irq_list, list) and not args.irq_list:
print("[WARNING] IRQ pattern matched 0 IRQs", file=sys.stderr)
+ # Validate converted arguments
+ if args.command in ['move', 'm', 'spread', 'x', 'isolate', 'i', 'include', 'I', 'run', 'r', 'save', 's', 'show_threads', 'show_irqs', 'gui', 'g', 'cpu_power']:
+ if 'cpu_list' in vars(args) and args.cpu_list:
+ nr_cpus = utils.get_nr_cpus()
+ invalid_cpus = [c for c in args.cpu_list if c >= nr_cpus]
+ if invalid_cpus:
+ print(f"[ERROR] Invalid CPU numbers: {invalid_cpus}. System has {nr_cpus} CPUs (0-{nr_cpus-1})", file=sys.stderr)
+ sys.exit(2)
+
+ if args.command in ['priority', 'p', 'run', 'r']:
+ if 'priority' in vars(args) and args.priority:
+ policy, rtprio = args.priority
+ # Only validate rtprio if a policy was specified or if rtprio is non-zero
+ if policy is not None or rtprio != 0:
+ # RT policies (FIFO, RR) require priority 1-99
+ # SCHED_FIFO = 1, SCHED_RR = 2 in Linux
+ if policy in (1, 2) and rtprio is not None:
+ if rtprio < 1 or rtprio > 99:
+ print(f"[ERROR] Invalid RT priority: {rtprio}. RT priorities must be 1-99", file=sys.stderr)
+ sys.exit(2)
+ # If only priority specified (no policy), assume RT and validate
+ elif policy is None and rtprio != 0:
+ if rtprio < 1 or rtprio > 99:
+ print(f"[ERROR] Invalid RT priority: {rtprio}. RT priorities must be 1-99", file=sys.stderr)
+ sys.exit(2)
+
# if args.command in ['apply', 'a']:
# apply_config(args.profilename)
--
2.54.0
^ permalink raw reply related [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-06-22 20:21 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-22 20:21 [PATCH 0/6] User experience and code quality improvements John Kacur
2026-06-22 20:21 ` [PATCH 1/6] tuna: Warn when user-specified patterns match zero threads or IRQs John Kacur
2026-06-22 20:21 ` [PATCH 2/6] tuna: Suppress output when pattern matches zero results John Kacur
2026-06-22 20:21 ` [PATCH 3/6] tuna: Make warnings opt-in with -w/--warn flag John Kacur
2026-06-22 20:21 ` [PATCH 4/6] tuna: Enhance what_is command with new kernel thread descriptions and usability improvements John Kacur
2026-06-22 20:21 ` [PATCH 5/6] tuna: Add comprehensive docstrings to converter functions John Kacur
2026-06-22 20:21 ` [PATCH 6/6] tuna: Add centralized input validation for CPU numbers and RT priorities John Kacur
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).