]> git.ipfire.org Git - thirdparty/linux.git/blame - tools/workqueue/wq_dump.py
Merge tag 'pwm/for-6.9-rc5-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git...
[thirdparty/linux.git] / tools / workqueue / wq_dump.py
CommitLineData
7f7dc377
TH
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
8639eceb 39 NAME TYPE[,FLAGS] POOL_ID...
7f7dc377
TH
40
41 NAME name of the workqueue
42 TYPE percpu, unbound or ordered
8639eceb 43 FLAGS S: strict affinity scope
7f7dc377
TH
44 POOL_ID worker pool ID associated with each possible CPU
45"""
46
47import sys
48
49import drgn
50from drgn.helpers.linux.list import list_for_each_entry,list_empty
51from drgn.helpers.linux.percpu import per_cpu_ptr
52from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu
07daa99b 53from drgn.helpers.linux.nodemask import for_each_node
7f7dc377
TH
54from drgn.helpers.linux.idr import idr_for_each
55
56import argparse
57parser = argparse.ArgumentParser(description=desc,
58 formatter_class=argparse.RawTextHelpFormatter)
59args = parser.parse_args()
60
61def err(s):
62 print(s, file=sys.stderr, flush=True)
63 sys.exit(1)
64
65def cpumask_str(cpumask):
66 output = ""
67 base = 0
68 v = 0
69 for cpu in for_each_cpu(cpumask[0]):
70 while cpu - base >= 32:
71 output += f'{hex(v)} '
72 base += 32
73 v = 0
74 v |= 1 << (cpu - base)
75 if v > 0:
76 output += f'{v:08x}'
77 return output.strip()
78
a6b48c83
TH
79wq_type_len = 9
80
81def wq_type_str(wq):
4cb1ef64
TH
82 if wq.flags & WQ_BH:
83 return f'{"bh":{wq_type_len}}'
84 elif wq.flags & WQ_UNBOUND:
a6b48c83
TH
85 if wq.flags & WQ_ORDERED:
86 return f'{"ordered":{wq_type_len}}'
87 else:
88 if wq.unbound_attrs.affn_strict:
89 return f'{"unbound,S":{wq_type_len}}'
90 else:
91 return f'{"unbound":{wq_type_len}}'
92 else:
93 return f'{"percpu":{wq_type_len}}'
94
7f7dc377
TH
95worker_pool_idr = prog['worker_pool_idr']
96workqueues = prog['workqueues']
97wq_unbound_cpumask = prog['wq_unbound_cpumask']
98wq_pod_types = prog['wq_pod_types']
63c5484e
TH
99wq_affn_dfl = prog['wq_affn_dfl']
100wq_affn_names = prog['wq_affn_names']
7f7dc377 101
4cb1ef64 102WQ_BH = prog['WQ_BH']
7f7dc377
TH
103WQ_UNBOUND = prog['WQ_UNBOUND']
104WQ_ORDERED = prog['__WQ_ORDERED']
105WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM']
106
63c5484e
TH
107WQ_AFFN_CPU = prog['WQ_AFFN_CPU']
108WQ_AFFN_SMT = prog['WQ_AFFN_SMT']
109WQ_AFFN_CACHE = prog['WQ_AFFN_CACHE']
7f7dc377
TH
110WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA']
111WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM']
112
4cb1ef64
TH
113POOL_BH = prog['POOL_BH']
114
a6b48c83 115WQ_NAME_LEN = prog['WQ_NAME_LEN'].value_()
a6b48c83
TH
116cpumask_str_len = len(cpumask_str(wq_unbound_cpumask))
117
7f7dc377
TH
118print('Affinity Scopes')
119print('===============')
120
121print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}')
122
123def print_pod_type(pt):
124 print(f' nr_pods {pt.nr_pods.value_()}')
125
126 print(' pod_cpus', end='')
127 for pod in range(pt.nr_pods):
128 print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='')
129 print('')
130
131 print(' pod_node', end='')
132 for pod in range(pt.nr_pods):
133 print(f' [{pod}]={pt.pod_node[pod].value_()}', end='')
134 print('')
135
136 print(f' cpu_pod ', end='')
137 for cpu in for_each_possible_cpu(prog):
138 print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='')
139 print('')
140
63c5484e
TH
141for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]:
142 print('')
143 print(f'{wq_affn_names[affn].string_().decode().upper()}{" (default)" if affn == wq_affn_dfl else ""}')
144 print_pod_type(wq_pod_types[affn])
7f7dc377
TH
145
146print('')
147print('Worker Pools')
148print('============')
149
150max_pool_id_len = 0
151max_ref_len = 0
152for pi, pool in idr_for_each(worker_pool_idr):
153 pool = drgn.Object(prog, 'struct worker_pool', address=pool)
154 max_pool_id_len = max(max_pool_id_len, len(f'{pi}'))
155 max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}'))
156
157for pi, pool in idr_for_each(worker_pool_idr):
158 pool = drgn.Object(prog, 'struct worker_pool', address=pool)
4cb1ef64 159 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='')
7f7dc377
TH
160 print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='')
161 if pool.cpu >= 0:
162 print(f'cpu={pool.cpu.value_():3}', end='')
4cb1ef64
TH
163 if pool.flags & POOL_BH:
164 print(' bh', end='')
7f7dc377
TH
165 else:
166 print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='')
8639eceb
TH
167 print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='')
168 if pool.attrs.affn_strict:
169 print(' strict', end='')
7f7dc377
TH
170 print('')
171
172print('')
173print('Workqueue CPU -> pool')
174print('=====================')
175
a6b48c83 176print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"type CPU":{wq_type_len}}', end='')
7f7dc377
TH
177for cpu in for_each_possible_cpu(prog):
178 print(f' {cpu:{max_pool_id_len}}', end='')
179print(' dfl]')
180
181for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
a6b48c83 182 print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} {wq_type_str(wq):10}', end='')
7f7dc377
TH
183
184 for cpu in for_each_possible_cpu(prog):
185 pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_()
186 field_len = max(len(str(cpu)), max_pool_id_len)
187 print(f' {pool_id:{field_len}}', end='')
188
189 if wq.flags & WQ_UNBOUND:
190 print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='')
191 print('')
ab5e5b99
JL
192
193print('')
194print('Workqueue -> rescuer')
a6b48c83
TH
195print('====================')
196
197ucpus_len = max(cpumask_str_len, len("unbound_cpus"))
198rcpus_len = max(cpumask_str_len, len("rescuer_cpus"))
199
200print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"unbound_cpus":{ucpus_len}} pid {"rescuer_cpus":{rcpus_len}} ]')
ab5e5b99
JL
201
202for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
a6b48c83
TH
203 if not (wq.flags & WQ_MEM_RECLAIM):
204 continue
205
206 print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='')
207 if wq.unbound_attrs.value_() != 0:
208 print(f' {cpumask_str(wq.unbound_attrs.cpumask):{ucpus_len}}', end='')
ab5e5b99 209 else:
a6b48c83 210 print(f' {"":{ucpus_len}}', end='')
ab5e5b99 211
a6b48c83
TH
212 print(f' {wq.rescuer.task.pid.value_():6}', end='')
213 print(f' {cpumask_str(wq.rescuer.task.cpus_ptr):{rcpus_len}}', end='')
ab5e5b99 214 print('')
07daa99b
TH
215
216print('')
217print('Unbound workqueue -> node_nr/max_active')
218print('=======================================')
219
220if 'node_to_cpumask_map' in prog:
221 __cpu_online_mask = prog['__cpu_online_mask']
222 node_to_cpumask_map = prog['node_to_cpumask_map']
223 nr_node_ids = prog['nr_node_ids'].value_()
224
225 print(f'online_cpus={cpumask_str(__cpu_online_mask.address_of_())}')
226 for node in for_each_node():
227 print(f'NODE[{node:02}]={cpumask_str(node_to_cpumask_map[node])}')
228 print('')
229
230 print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ min max', end='')
231 first = True
232 for node in for_each_node():
233 if first:
234 print(f' NODE {node}', end='')
235 first = False
236 else:
237 print(f' {node:7}', end='')
238 print(f' {"dfl":>7} ]')
239 print('')
240
241 for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
242 if not (wq.flags & WQ_UNBOUND):
243 continue
244
245 print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} ', end='')
246 print(f'{wq.min_active.value_():3} {wq.max_active.value_():3}', end='')
247 for node in for_each_node():
248 nna = wq.node_nr_active[node]
249 print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}', end='')
250 nna = wq.node_nr_active[nr_node_ids]
251 print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}')
252else:
253 printf(f'node_to_cpumask_map not present, is NUMA enabled?')