workqueue: Add tools/workqueue/wq_dump.py which prints out workqueue configuration
[linux-block.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
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']
81
82WQ_UNBOUND = prog['WQ_UNBOUND']
83WQ_ORDERED = prog['__WQ_ORDERED']
84WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM']
85
86WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA']
87WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM']
88
89print('Affinity Scopes')
90print('===============')
91
92print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}')
93
94def print_pod_type(pt):
95 print(f' nr_pods {pt.nr_pods.value_()}')
96
97 print(' pod_cpus', end='')
98 for pod in range(pt.nr_pods):
99 print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='')
100 print('')
101
102 print(' pod_node', end='')
103 for pod in range(pt.nr_pods):
104 print(f' [{pod}]={pt.pod_node[pod].value_()}', end='')
105 print('')
106
107 print(f' cpu_pod ', end='')
108 for cpu in for_each_possible_cpu(prog):
109 print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='')
110 print('')
111
112print('')
113print('NUMA')
114print_pod_type(wq_pod_types[WQ_AFFN_NUMA])
115print('')
116print('SYSTEM')
117print_pod_type(wq_pod_types[WQ_AFFN_SYSTEM])
118
119print('')
120print('Worker Pools')
121print('============')
122
123max_pool_id_len = 0
124max_ref_len = 0
125for pi, pool in idr_for_each(worker_pool_idr):
126 pool = drgn.Object(prog, 'struct worker_pool', address=pool)
127 max_pool_id_len = max(max_pool_id_len, len(f'{pi}'))
128 max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}'))
129
130for pi, pool in idr_for_each(worker_pool_idr):
131 pool = drgn.Object(prog, 'struct worker_pool', address=pool)
132 print(f'pool[{pi:0{max_pool_id_len}}] ref={pool.refcnt.value_():{max_ref_len}} nice={pool.attrs.nice.value_():3} ', end='')
133 print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='')
134 if pool.cpu >= 0:
135 print(f'cpu={pool.cpu.value_():3}', end='')
136 else:
137 print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='')
138 print('')
139
140print('')
141print('Workqueue CPU -> pool')
142print('=====================')
143
144print('[ workqueue \ CPU ', end='')
145for cpu in for_each_possible_cpu(prog):
146 print(f' {cpu:{max_pool_id_len}}', end='')
147print(' dfl]')
148
149for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
150 print(f'{wq.name.string_().decode()[-24:]:24}', end='')
151 if wq.flags & WQ_UNBOUND:
152 if wq.flags & WQ_ORDERED:
153 print(' ordered', end='')
154 else:
155 print(' unbound', end='')
156 else:
157 print(' percpu ', end='')
158
159 for cpu in for_each_possible_cpu(prog):
160 pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_()
161 field_len = max(len(str(cpu)), max_pool_id_len)
162 print(f' {pool_id:{field_len}}', end='')
163
164 if wq.flags & WQ_UNBOUND:
165 print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='')
166 print('')