Re: [PATCH 2/2] tools/workqueue/wq_dump.py: Add busy worker inspection and BH pool states
From: Tejun Heo
Date: Mon Aug 31 2026 - 17:19:40 EST
Hello, Aaron.
On Mon, Aug 31, 2026 at 02:15:54PM -0400, Aaron Tomlin wrote:
> + if pool.flags & POOL_BH_DRAINING:
> + print(' draining', end='')
> + if pool.flags & POOL_DISASSOCIATED:
> + print(' disassociated', end='')
Note that POOL_DISASSOCIATED is set by init_worker_pool() and cleared only
for per-cpu non-BH pools, so BH and unbound pools carry it for their whole
lifetime. This would print " disassociated" on every one of their lines.
It's only meaningful on a per-cpu non-BH pool whose CPU is offline.
> + 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
process_one_work() hashes the worker before setting current_func and
current_pwq, and clears them right after unhashing, all under pool->lock. A
worker caught in either window reads back NULL, so worker.current_pwq.wq
raises and prog.symbol(0) can raise errors and kill the rest of the dump.
Work items can finish at a very high rate, so this wouldn't be difficult to
hit on a busy machine. Maybe catch the exceptions and retry the worker?
> + 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'
worker->current_start was added in v7.0 by e8e14ac7cfe4 ("workqueue: Show
in-flight work item duration in stall diagnostics"). The 'jiffies' in prog
test doesn't protect the member access, so on an older kernel or vmcore the
first busy worker aborts the dump, the same failure mode the previous patch
fixes for wq->attrs. Please handle it the same way.
Also, the values are plain Python integers, so on a 32bit kernel the
subtraction goes negative when jiffies wraps (the first wrap is five minutes
after boot due to INITIAL_JIFFIES) and the max() turns the duration into
"for 0s". Mask the difference to the target's word size instead, e.g.:
mask = (1 << (prog['jiffies'].type_.size * 8)) - 1
dur_s = ((jiffies - worker.current_start.value_()) & mask) // hz
Thanks.
--
tejun