xref: /openbmc/linux/tools/workqueue/wq_dump.py (revision 63c5484e74952f60f5810256bd69814d167b8d22)
1#!/usr/bin/env drgn
2#
3# Copyright (C) 2023 Tejun Heo <tj@kernel.org>
4# Copyright (C) 2023 Meta Platforms, Inc. and affiliates.
5
6desc = """
7This is a drgn script to show the current workqueue configuration. For more
8info on drgn, visit https://github.com/osandov/drgn.
9
10Affinity Scopes
11===============
12
13Shows the CPUs that can be used for unbound workqueues and how they will be
14grouped by each available affinity type. For each type:
15
16  nr_pods   number of CPU pods in the affinity type
17  pod_cpus  CPUs in each pod
18  pod_node  NUMA node for memory allocation for each pod
19  cpu_pod   pod that each CPU is associated to
20
21Worker Pools
22============
23
24Lists all worker pools indexed by their ID. For each pool:
25
26  ref       number of pool_workqueue's associated with this pool
27  nice      nice value of the worker threads in the pool
28  idle      number of idle workers
29  workers   number of all workers
30  cpu       CPU the pool is associated with (per-cpu pool)
31  cpus      CPUs the workers in the pool can run on (unbound pool)
32
33Workqueue CPU -> pool
34=====================
35
36Lists all workqueues along with their type and worker pool association. For
37each workqueue:
38
39  NAME TYPE POOL_ID...
40
41  NAME      name of the workqueue
42  TYPE      percpu, unbound or ordered
43  POOL_ID   worker pool ID associated with each possible CPU
44"""
45
46import sys
47
48import drgn
49from drgn.helpers.linux.list import list_for_each_entry,list_empty
50from drgn.helpers.linux.percpu import per_cpu_ptr
51from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu
52from drgn.helpers.linux.idr import idr_for_each
53
54import argparse
55parser = argparse.ArgumentParser(description=desc,
56                                 formatter_class=argparse.RawTextHelpFormatter)
57args = parser.parse_args()
58
59def err(s):
60    print(s, file=sys.stderr, flush=True)
61    sys.exit(1)
62
63def cpumask_str(cpumask):
64    output = ""
65    base = 0
66    v = 0
67    for cpu in for_each_cpu(cpumask[0]):
68        while cpu - base >= 32:
69            output += f'{hex(v)} '
70            base += 32
71            v = 0
72        v |= 1 << (cpu - base)
73    if v > 0:
74        output += f'{v:08x}'
75    return output.strip()
76
77worker_pool_idr         = prog['worker_pool_idr']
78workqueues              = prog['workqueues']
79wq_unbound_cpumask      = prog['wq_unbound_cpumask']
80wq_pod_types            = prog['wq_pod_types']
81wq_affn_dfl             = prog['wq_affn_dfl']
82wq_affn_names           = prog['wq_affn_names']
83
84WQ_UNBOUND              = prog['WQ_UNBOUND']
85WQ_ORDERED              = prog['__WQ_ORDERED']
86WQ_MEM_RECLAIM          = prog['WQ_MEM_RECLAIM']
87
88WQ_AFFN_CPU             = prog['WQ_AFFN_CPU']
89WQ_AFFN_SMT             = prog['WQ_AFFN_SMT']
90WQ_AFFN_CACHE           = prog['WQ_AFFN_CACHE']
91WQ_AFFN_NUMA            = prog['WQ_AFFN_NUMA']
92WQ_AFFN_SYSTEM          = prog['WQ_AFFN_SYSTEM']
93
94print('Affinity Scopes')
95print('===============')
96
97print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}')
98
99def print_pod_type(pt):
100    print(f'  nr_pods  {pt.nr_pods.value_()}')
101
102    print('  pod_cpus', end='')
103    for pod in range(pt.nr_pods):
104        print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='')
105    print('')
106
107    print('  pod_node', end='')
108    for pod in range(pt.nr_pods):
109        print(f' [{pod}]={pt.pod_node[pod].value_()}', end='')
110    print('')
111
112    print(f'  cpu_pod ', end='')
113    for cpu in for_each_possible_cpu(prog):
114        print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='')
115    print('')
116
117for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]:
118    print('')
119    print(f'{wq_affn_names[affn].string_().decode().upper()}{" (default)" if affn == wq_affn_dfl else ""}')
120    print_pod_type(wq_pod_types[affn])
121
122print('')
123print('Worker Pools')
124print('============')
125
126max_pool_id_len = 0
127max_ref_len = 0
128for pi, pool in idr_for_each(worker_pool_idr):
129    pool = drgn.Object(prog, 'struct worker_pool', address=pool)
130    max_pool_id_len = max(max_pool_id_len, len(f'{pi}'))
131    max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}'))
132
133for pi, pool in idr_for_each(worker_pool_idr):
134    pool = drgn.Object(prog, 'struct worker_pool', address=pool)
135    print(f'pool[{pi:0{max_pool_id_len}}] ref={pool.refcnt.value_():{max_ref_len}} nice={pool.attrs.nice.value_():3} ', end='')
136    print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='')
137    if pool.cpu >= 0:
138        print(f'cpu={pool.cpu.value_():3}', end='')
139    else:
140        print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='')
141    print('')
142
143print('')
144print('Workqueue CPU -> pool')
145print('=====================')
146
147print('[    workqueue \ CPU            ', end='')
148for cpu in for_each_possible_cpu(prog):
149    print(f' {cpu:{max_pool_id_len}}', end='')
150print(' dfl]')
151
152for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
153    print(f'{wq.name.string_().decode()[-24:]:24}', end='')
154    if wq.flags & WQ_UNBOUND:
155        if wq.flags & WQ_ORDERED:
156            print(' ordered', end='')
157        else:
158            print(' unbound', end='')
159    else:
160        print(' percpu ', end='')
161
162    for cpu in for_each_possible_cpu(prog):
163        pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_()
164        field_len = max(len(str(cpu)), max_pool_id_len)
165        print(f' {pool_id:{field_len}}', end='')
166
167    if wq.flags & WQ_UNBOUND:
168        print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='')
169    print('')
170