xref: /linux-6.15/tools/workqueue/wq_dump.py (revision 4cb1ef64)
17f7dc377STejun Heo#!/usr/bin/env drgn
27f7dc377STejun Heo#
37f7dc377STejun Heo# Copyright (C) 2023 Tejun Heo <[email protected]>
47f7dc377STejun Heo# Copyright (C) 2023 Meta Platforms, Inc. and affiliates.
57f7dc377STejun Heo
67f7dc377STejun Heodesc = """
77f7dc377STejun HeoThis is a drgn script to show the current workqueue configuration. For more
87f7dc377STejun Heoinfo on drgn, visit https://github.com/osandov/drgn.
97f7dc377STejun Heo
107f7dc377STejun HeoAffinity Scopes
117f7dc377STejun Heo===============
127f7dc377STejun Heo
137f7dc377STejun HeoShows the CPUs that can be used for unbound workqueues and how they will be
147f7dc377STejun Heogrouped by each available affinity type. For each type:
157f7dc377STejun Heo
167f7dc377STejun Heo  nr_pods   number of CPU pods in the affinity type
177f7dc377STejun Heo  pod_cpus  CPUs in each pod
187f7dc377STejun Heo  pod_node  NUMA node for memory allocation for each pod
197f7dc377STejun Heo  cpu_pod   pod that each CPU is associated to
207f7dc377STejun Heo
217f7dc377STejun HeoWorker Pools
227f7dc377STejun Heo============
237f7dc377STejun Heo
247f7dc377STejun HeoLists all worker pools indexed by their ID. For each pool:
257f7dc377STejun Heo
267f7dc377STejun Heo  ref       number of pool_workqueue's associated with this pool
277f7dc377STejun Heo  nice      nice value of the worker threads in the pool
287f7dc377STejun Heo  idle      number of idle workers
297f7dc377STejun Heo  workers   number of all workers
307f7dc377STejun Heo  cpu       CPU the pool is associated with (per-cpu pool)
317f7dc377STejun Heo  cpus      CPUs the workers in the pool can run on (unbound pool)
327f7dc377STejun Heo
337f7dc377STejun HeoWorkqueue CPU -> pool
347f7dc377STejun Heo=====================
357f7dc377STejun Heo
367f7dc377STejun HeoLists all workqueues along with their type and worker pool association. For
377f7dc377STejun Heoeach workqueue:
387f7dc377STejun Heo
398639ecebSTejun Heo  NAME TYPE[,FLAGS] POOL_ID...
407f7dc377STejun Heo
417f7dc377STejun Heo  NAME      name of the workqueue
427f7dc377STejun Heo  TYPE      percpu, unbound or ordered
438639ecebSTejun Heo  FLAGS     S: strict affinity scope
447f7dc377STejun Heo  POOL_ID   worker pool ID associated with each possible CPU
457f7dc377STejun Heo"""
467f7dc377STejun Heo
477f7dc377STejun Heoimport sys
487f7dc377STejun Heo
497f7dc377STejun Heoimport drgn
507f7dc377STejun Heofrom drgn.helpers.linux.list import list_for_each_entry,list_empty
517f7dc377STejun Heofrom drgn.helpers.linux.percpu import per_cpu_ptr
527f7dc377STejun Heofrom drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu
5307daa99bSTejun Heofrom drgn.helpers.linux.nodemask import for_each_node
547f7dc377STejun Heofrom drgn.helpers.linux.idr import idr_for_each
557f7dc377STejun Heo
567f7dc377STejun Heoimport argparse
577f7dc377STejun Heoparser = argparse.ArgumentParser(description=desc,
587f7dc377STejun Heo                                 formatter_class=argparse.RawTextHelpFormatter)
597f7dc377STejun Heoargs = parser.parse_args()
607f7dc377STejun Heo
617f7dc377STejun Heodef err(s):
627f7dc377STejun Heo    print(s, file=sys.stderr, flush=True)
637f7dc377STejun Heo    sys.exit(1)
647f7dc377STejun Heo
657f7dc377STejun Heodef cpumask_str(cpumask):
667f7dc377STejun Heo    output = ""
677f7dc377STejun Heo    base = 0
687f7dc377STejun Heo    v = 0
697f7dc377STejun Heo    for cpu in for_each_cpu(cpumask[0]):
707f7dc377STejun Heo        while cpu - base >= 32:
717f7dc377STejun Heo            output += f'{hex(v)} '
727f7dc377STejun Heo            base += 32
737f7dc377STejun Heo            v = 0
747f7dc377STejun Heo        v |= 1 << (cpu - base)
757f7dc377STejun Heo    if v > 0:
767f7dc377STejun Heo        output += f'{v:08x}'
777f7dc377STejun Heo    return output.strip()
787f7dc377STejun Heo
79a6b48c83STejun Heowq_type_len = 9
80a6b48c83STejun Heo
81a6b48c83STejun Heodef wq_type_str(wq):
82*4cb1ef64STejun Heo    if wq.flags & WQ_BH:
83*4cb1ef64STejun Heo        return f'{"bh":{wq_type_len}}'
84*4cb1ef64STejun Heo    elif wq.flags & WQ_UNBOUND:
85a6b48c83STejun Heo        if wq.flags & WQ_ORDERED:
86a6b48c83STejun Heo            return f'{"ordered":{wq_type_len}}'
87a6b48c83STejun Heo        else:
88a6b48c83STejun Heo            if wq.unbound_attrs.affn_strict:
89a6b48c83STejun Heo                return f'{"unbound,S":{wq_type_len}}'
90a6b48c83STejun Heo            else:
91a6b48c83STejun Heo                return f'{"unbound":{wq_type_len}}'
92a6b48c83STejun Heo    else:
93a6b48c83STejun Heo        return f'{"percpu":{wq_type_len}}'
94a6b48c83STejun Heo
957f7dc377STejun Heoworker_pool_idr         = prog['worker_pool_idr']
967f7dc377STejun Heoworkqueues              = prog['workqueues']
977f7dc377STejun Heowq_unbound_cpumask      = prog['wq_unbound_cpumask']
987f7dc377STejun Heowq_pod_types            = prog['wq_pod_types']
9963c5484eSTejun Heowq_affn_dfl             = prog['wq_affn_dfl']
10063c5484eSTejun Heowq_affn_names           = prog['wq_affn_names']
1017f7dc377STejun Heo
102*4cb1ef64STejun HeoWQ_BH                   = prog['WQ_BH']
1037f7dc377STejun HeoWQ_UNBOUND              = prog['WQ_UNBOUND']
1047f7dc377STejun HeoWQ_ORDERED              = prog['__WQ_ORDERED']
1057f7dc377STejun HeoWQ_MEM_RECLAIM          = prog['WQ_MEM_RECLAIM']
1067f7dc377STejun Heo
10763c5484eSTejun HeoWQ_AFFN_CPU             = prog['WQ_AFFN_CPU']
10863c5484eSTejun HeoWQ_AFFN_SMT             = prog['WQ_AFFN_SMT']
10963c5484eSTejun HeoWQ_AFFN_CACHE           = prog['WQ_AFFN_CACHE']
1107f7dc377STejun HeoWQ_AFFN_NUMA            = prog['WQ_AFFN_NUMA']
1117f7dc377STejun HeoWQ_AFFN_SYSTEM          = prog['WQ_AFFN_SYSTEM']
1127f7dc377STejun Heo
113*4cb1ef64STejun HeoPOOL_BH                 = prog['POOL_BH']
114*4cb1ef64STejun Heo
115a6b48c83STejun HeoWQ_NAME_LEN             = prog['WQ_NAME_LEN'].value_()
116a6b48c83STejun Heocpumask_str_len         = len(cpumask_str(wq_unbound_cpumask))
117a6b48c83STejun Heo
1187f7dc377STejun Heoprint('Affinity Scopes')
1197f7dc377STejun Heoprint('===============')
1207f7dc377STejun Heo
1217f7dc377STejun Heoprint(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}')
1227f7dc377STejun Heo
1237f7dc377STejun Heodef print_pod_type(pt):
1247f7dc377STejun Heo    print(f'  nr_pods  {pt.nr_pods.value_()}')
1257f7dc377STejun Heo
1267f7dc377STejun Heo    print('  pod_cpus', end='')
1277f7dc377STejun Heo    for pod in range(pt.nr_pods):
1287f7dc377STejun Heo        print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='')
1297f7dc377STejun Heo    print('')
1307f7dc377STejun Heo
1317f7dc377STejun Heo    print('  pod_node', end='')
1327f7dc377STejun Heo    for pod in range(pt.nr_pods):
1337f7dc377STejun Heo        print(f' [{pod}]={pt.pod_node[pod].value_()}', end='')
1347f7dc377STejun Heo    print('')
1357f7dc377STejun Heo
1367f7dc377STejun Heo    print(f'  cpu_pod ', end='')
1377f7dc377STejun Heo    for cpu in for_each_possible_cpu(prog):
1387f7dc377STejun Heo        print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='')
1397f7dc377STejun Heo    print('')
1407f7dc377STejun Heo
14163c5484eSTejun Heofor affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]:
1427f7dc377STejun Heo    print('')
14363c5484eSTejun Heo    print(f'{wq_affn_names[affn].string_().decode().upper()}{" (default)" if affn == wq_affn_dfl else ""}')
14463c5484eSTejun Heo    print_pod_type(wq_pod_types[affn])
1457f7dc377STejun Heo
1467f7dc377STejun Heoprint('')
1477f7dc377STejun Heoprint('Worker Pools')
1487f7dc377STejun Heoprint('============')
1497f7dc377STejun Heo
1507f7dc377STejun Heomax_pool_id_len = 0
1517f7dc377STejun Heomax_ref_len = 0
1527f7dc377STejun Heofor pi, pool in idr_for_each(worker_pool_idr):
1537f7dc377STejun Heo    pool = drgn.Object(prog, 'struct worker_pool', address=pool)
1547f7dc377STejun Heo    max_pool_id_len = max(max_pool_id_len, len(f'{pi}'))
1557f7dc377STejun Heo    max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}'))
1567f7dc377STejun Heo
1577f7dc377STejun Heofor pi, pool in idr_for_each(worker_pool_idr):
1587f7dc377STejun Heo    pool = drgn.Object(prog, 'struct worker_pool', address=pool)
159*4cb1ef64STejun Heo    print(f'pool[{pi:0{max_pool_id_len}}] flags=0x{pool.flags.value_():02x} ref={pool.refcnt.value_():{max_ref_len}} nice={pool.attrs.nice.value_():3} ', end='')
1607f7dc377STejun Heo    print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='')
1617f7dc377STejun Heo    if pool.cpu >= 0:
1627f7dc377STejun Heo        print(f'cpu={pool.cpu.value_():3}', end='')
163*4cb1ef64STejun Heo        if pool.flags & POOL_BH:
164*4cb1ef64STejun Heo            print(' bh', end='')
1657f7dc377STejun Heo    else:
1667f7dc377STejun Heo        print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='')
1678639ecebSTejun Heo        print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='')
1688639ecebSTejun Heo        if pool.attrs.affn_strict:
1698639ecebSTejun Heo            print(' strict', end='')
1707f7dc377STejun Heo    print('')
1717f7dc377STejun Heo
1727f7dc377STejun Heoprint('')
1737f7dc377STejun Heoprint('Workqueue CPU -> pool')
1747f7dc377STejun Heoprint('=====================')
1757f7dc377STejun Heo
176a6b48c83STejun Heoprint(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"type   CPU":{wq_type_len}}', end='')
1777f7dc377STejun Heofor cpu in for_each_possible_cpu(prog):
1787f7dc377STejun Heo    print(f' {cpu:{max_pool_id_len}}', end='')
1797f7dc377STejun Heoprint(' dfl]')
1807f7dc377STejun Heo
1817f7dc377STejun Heofor wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
182a6b48c83STejun Heo    print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} {wq_type_str(wq):10}', end='')
1837f7dc377STejun Heo
1847f7dc377STejun Heo    for cpu in for_each_possible_cpu(prog):
1857f7dc377STejun Heo        pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_()
1867f7dc377STejun Heo        field_len = max(len(str(cpu)), max_pool_id_len)
1877f7dc377STejun Heo        print(f' {pool_id:{field_len}}', end='')
1887f7dc377STejun Heo
1897f7dc377STejun Heo    if wq.flags & WQ_UNBOUND:
1907f7dc377STejun Heo        print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='')
1917f7dc377STejun Heo    print('')
192ab5e5b99SJuri Lelli
193ab5e5b99SJuri Lelliprint('')
194ab5e5b99SJuri Lelliprint('Workqueue -> rescuer')
195a6b48c83STejun Heoprint('====================')
196a6b48c83STejun Heo
197a6b48c83STejun Heoucpus_len = max(cpumask_str_len, len("unbound_cpus"))
198a6b48c83STejun Heorcpus_len = max(cpumask_str_len, len("rescuer_cpus"))
199a6b48c83STejun Heo
200a6b48c83STejun Heoprint(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"unbound_cpus":{ucpus_len}}    pid {"rescuer_cpus":{rcpus_len}} ]')
201ab5e5b99SJuri Lelli
202ab5e5b99SJuri Lellifor wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
203a6b48c83STejun Heo    if not (wq.flags & WQ_MEM_RECLAIM):
204a6b48c83STejun Heo        continue
205ab5e5b99SJuri Lelli
206a6b48c83STejun Heo    print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='')
207a6b48c83STejun Heo    if wq.unbound_attrs.value_() != 0:
208a6b48c83STejun Heo        print(f' {cpumask_str(wq.unbound_attrs.cpumask):{ucpus_len}}', end='')
209a6b48c83STejun Heo    else:
210a6b48c83STejun Heo        print(f' {"":{ucpus_len}}', end='')
211a6b48c83STejun Heo
212a6b48c83STejun Heo    print(f' {wq.rescuer.task.pid.value_():6}', end='')
213a6b48c83STejun Heo    print(f' {cpumask_str(wq.rescuer.task.cpus_ptr):{rcpus_len}}', end='')
214ab5e5b99SJuri Lelli    print('')
21507daa99bSTejun Heo
21607daa99bSTejun Heoprint('')
21707daa99bSTejun Heoprint('Unbound workqueue -> node_nr/max_active')
21807daa99bSTejun Heoprint('=======================================')
21907daa99bSTejun Heo
22007daa99bSTejun Heoif 'node_to_cpumask_map' in prog:
22107daa99bSTejun Heo    __cpu_online_mask = prog['__cpu_online_mask']
22207daa99bSTejun Heo    node_to_cpumask_map = prog['node_to_cpumask_map']
22307daa99bSTejun Heo    nr_node_ids = prog['nr_node_ids'].value_()
22407daa99bSTejun Heo
22507daa99bSTejun Heo    print(f'online_cpus={cpumask_str(__cpu_online_mask.address_of_())}')
22607daa99bSTejun Heo    for node in for_each_node():
22707daa99bSTejun Heo        print(f'NODE[{node:02}]={cpumask_str(node_to_cpumask_map[node])}')
22807daa99bSTejun Heo    print('')
22907daa99bSTejun Heo
23007daa99bSTejun Heo    print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ min max', end='')
23107daa99bSTejun Heo    first = True
23207daa99bSTejun Heo    for node in for_each_node():
23307daa99bSTejun Heo        if first:
23407daa99bSTejun Heo            print(f'  NODE {node}', end='')
23507daa99bSTejun Heo            first = False
23607daa99bSTejun Heo        else:
23707daa99bSTejun Heo            print(f' {node:7}', end='')
23807daa99bSTejun Heo    print(f' {"dfl":>7} ]')
23907daa99bSTejun Heo    print('')
24007daa99bSTejun Heo
24107daa99bSTejun Heo    for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
24207daa99bSTejun Heo        if not (wq.flags & WQ_UNBOUND):
24307daa99bSTejun Heo            continue
24407daa99bSTejun Heo
24507daa99bSTejun Heo        print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} ', end='')
24607daa99bSTejun Heo        print(f'{wq.min_active.value_():3} {wq.max_active.value_():3}', end='')
24707daa99bSTejun Heo        for node in for_each_node():
24807daa99bSTejun Heo            nna = wq.node_nr_active[node]
24907daa99bSTejun Heo            print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}', end='')
25007daa99bSTejun Heo        nna = wq.node_nr_active[nr_node_ids]
25107daa99bSTejun Heo        print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}')
25207daa99bSTejun Heoelse:
25307daa99bSTejun Heo    printf(f'node_to_cpumask_map not present, is NUMA enabled?')
254