From: Aaron Tomlin <atomlin@atomlin.com>
To: tj@kernel.org
Cc: leitao@debian.org, atomlin@atomlin.com, linux-kernel@vger.kernel.org
Subject: [PATCH 2/2] tools/workqueue/wq_dump.py: Add busy worker inspection and BH pool states
Date: Mon, 31 Aug 2026 14:15:54 -0400 [thread overview]
Message-ID: <20260831181554.117795-3-atomlin@atomlin.com> (raw)
In-Reply-To: <20260831181554.117795-1-atomlin@atomlin.com>
Currently, wq_dump.py displays static affinity scopes and pool topology,
offering no visibility into in-flight work items or transient pool states.
Enhance wq_dump.py with live busy worker inspection (i.e., option
-b|--busy) to dump active worker tasks, callback functions, workqueues,
descriptions, and elapsed durations.
Signed-off-by: Aaron Tomlin <atomlin@atomlin.com>
---
tools/workqueue/wq_dump.py | 50 +++++++++++++++++++++++++++++++++++---
1 file changed, 47 insertions(+), 3 deletions(-)
diff --git a/tools/workqueue/wq_dump.py b/tools/workqueue/wq_dump.py
index 9313ebe0c525..1659bba6c0dd 100644
--- a/tools/workqueue/wq_dump.py
+++ b/tools/workqueue/wq_dump.py
@@ -29,6 +29,10 @@ Lists all worker pools indexed by their ID. For each pool:
workers number of all workers
cpu CPU the pool is associated with (per-cpu pool)
cpus CPUs the workers in the pool can run on (unbound pool)
+ flags pool flags (bh, draining, disassociated)
+
+ If -b|--busy is specified, lists all busy workers currently executing
+ work items, their task PID/comm, workqueue, callback function, and duration.
Workqueue CPU -> pool
=====================
@@ -49,12 +53,14 @@ import sys
import argparse
parser = argparse.ArgumentParser(description=desc,
formatter_class=argparse.RawTextHelpFormatter)
+parser.add_argument('-b', '--busy', action='store_true',
+ help='Show busy workers currently executing work items')
args = parser.parse_args()
import drgn
-from drgn.helpers.linux.list import list_for_each_entry,list_empty
+from drgn.helpers.linux.list import list_for_each_entry, list_empty, hlist_for_each_entry
from drgn.helpers.linux.percpu import per_cpu_ptr
-from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu
+from drgn.helpers.linux.cpumask import for_each_cpu, for_each_possible_cpu
from drgn.helpers.linux.nodemask import for_each_node
from drgn.helpers.linux.idr import idr_for_each
@@ -62,6 +68,10 @@ def err(s):
print(s, file=sys.stderr, flush=True)
sys.exit(1)
+def get_hz():
+ cs = prog['clocksource_jiffies']
+ return round(1000000000 / (cs.mult.value_() >> cs.shift.value_()))
+
def cpumask_str(cpumask):
output = ""
base = 0
@@ -118,9 +128,13 @@ WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA']
WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM']
POOL_BH = prog['POOL_BH']
+POOL_BH_DRAINING = prog['POOL_BH_DRAINING']
+POOL_DISASSOCIATED = prog['POOL_DISASSOCIATED']
+HIGHPRI_NICE_LEVEL = prog['HIGHPRI_NICE_LEVEL']
WQ_NAME_LEN = prog['WQ_NAME_LEN'].value_()
cpumask_str_len = len(cpumask_str(wq_unbound_cpumask))
+hz = get_hz()
print('Affinity Scopes')
print('===============')
@@ -168,14 +182,44 @@ for pi, pool in idr_for_each(worker_pool_idr):
if pool.cpu >= 0:
print(f'cpu={pool.cpu.value_():3}', end='')
if pool.flags & POOL_BH:
- print(' bh', end='')
+ bh_type = 'bh-hi' if pool.attrs.nice == HIGHPRI_NICE_LEVEL else 'bh'
+ print(f' {bh_type}', end='')
+ if pool.flags & POOL_BH_DRAINING:
+ print(' draining', end='')
+ if pool.flags & POOL_DISASSOCIATED:
+ print(' disassociated', end='')
else:
print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='')
print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='')
if pool.attrs.affn_strict:
print(' strict', end='')
+ if pool.flags & POOL_DISASSOCIATED:
+ print(' disassociated', end='')
print('')
+ if args.busy:
+ for bkt in pool.busy_hash:
+ for worker in hlist_for_each_entry('struct worker', bkt.address_of_(), 'hentry'):
+ wq_name = worker.current_pwq.wq.name.string_().decode()
+ fn_name = prog.symbol(worker.current_func.value_()).name
+
+ dur_str = ''
+ if 'jiffies' in prog and worker.current_start.value_():
+ jiffies = prog['jiffies'].value_()
+ dur_s = max(0, (jiffies - worker.current_start.value_()) // hz)
+ dur_str = f' for {dur_s}s'
+
+ if pool.flags & POOL_BH:
+ w_id = 'bh' if pool.attrs.nice != HIGHPRI_NICE_LEVEL else 'bh-hi'
+ elif worker.task.value_():
+ w_id = f'PID {worker.task.pid.value_():<6} ({worker.task.comm.string_().decode()})'
+ else:
+ w_id = f'worker[{worker.id.value_()}]'
+
+ desc = worker.desc.string_().decode()
+ desc_str = f' desc="{desc}"' if desc and desc != wq_name else ''
+ print(f' busy: {w_id}: {wq_name}:{fn_name}{dur_str}{desc_str}')
+
print('')
print('Workqueue CPU -> pool')
print('=====================')
--
2.55.0
next prev parent reply other threads:[~2026-08-31 18:16 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-08-31 18:15 [PATCH 0/2] tools/workqueue/wq_dump.py: Backward compatibility and live worker inspection Aaron Tomlin
2026-08-31 18:15 ` [PATCH 1/2] tools/workqueue/wq_dump.py: Support backward compatibility for wq->attrs rename Aaron Tomlin
2026-08-31 20:38 ` Tejun Heo
2026-08-31 18:15 ` Aaron Tomlin [this message]
2026-08-31 20:38 ` [PATCH 2/2] tools/workqueue/wq_dump.py: Add busy worker inspection and BH pool states Tejun Heo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260831181554.117795-3-atomlin@atomlin.com \
--to=atomlin@atomlin.com \
--cc=leitao@debian.org \
--cc=linux-kernel@vger.kernel.org \
--cc=tj@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.