projects/linux·blob uaccess-cleanups · refs

255 lines · 8.4 KiB

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