Commit | Line | Data |
---|---|---|
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 | ||
6 | desc = """ | |
7 | This is a drgn script to show the current workqueue configuration. For more | |
8 | info on drgn, visit https://github.com/osandov/drgn. | |
9 | ||
10 | Affinity Scopes | |
11 | =============== | |
12 | ||
13 | Shows the CPUs that can be used for unbound workqueues and how they will be | |
14 | grouped 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 | ||
21 | Worker Pools | |
22 | ============ | |
23 | ||
24 | Lists 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 | ||
33 | Workqueue CPU -> pool | |
34 | ===================== | |
35 | ||
36 | Lists all workqueues along with their type and worker pool association. For | |
37 | each 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 | ||
47 | import sys | |
48 | ||
49 | import drgn | |
50 | from drgn.helpers.linux.list import list_for_each_entry,list_empty | |
51 | from drgn.helpers.linux.percpu import per_cpu_ptr | |
52 | from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu | |
53 | from drgn.helpers.linux.idr import idr_for_each | |
54 | ||
55 | import argparse | |
56 | parser = argparse.ArgumentParser(description=desc, | |
57 | formatter_class=argparse.RawTextHelpFormatter) | |
58 | args = parser.parse_args() | |
59 | ||
60 | def err(s): | |
61 | print(s, file=sys.stderr, flush=True) | |
62 | sys.exit(1) | |
63 | ||
64 | def cpumask_str(cpumask): | |
65 | output = "" | |
66 | base = 0 | |
67 | v = 0 | |
68 | for cpu in for_each_cpu(cpumask[0]): | |
69 | while cpu - base >= 32: | |
70 | output += f'{hex(v)} ' | |
71 | base += 32 | |
72 | v = 0 | |
73 | v |= 1 << (cpu - base) | |
74 | if v > 0: | |
75 | output += f'{v:08x}' | |
76 | return output.strip() | |
77 | ||
78 | worker_pool_idr = prog['worker_pool_idr'] | |
79 | workqueues = prog['workqueues'] | |
80 | wq_unbound_cpumask = prog['wq_unbound_cpumask'] | |
81 | wq_pod_types = prog['wq_pod_types'] | |
63c5484e TH |
82 | wq_affn_dfl = prog['wq_affn_dfl'] |
83 | wq_affn_names = prog['wq_affn_names'] | |
7f7dc377 TH |
84 | |
85 | WQ_UNBOUND = prog['WQ_UNBOUND'] | |
86 | WQ_ORDERED = prog['__WQ_ORDERED'] | |
87 | WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM'] | |
88 | ||
63c5484e TH |
89 | WQ_AFFN_CPU = prog['WQ_AFFN_CPU'] |
90 | WQ_AFFN_SMT = prog['WQ_AFFN_SMT'] | |
91 | WQ_AFFN_CACHE = prog['WQ_AFFN_CACHE'] | |
7f7dc377 TH |
92 | WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA'] |
93 | WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM'] | |
94 | ||
95 | print('Affinity Scopes') | |
96 | print('===============') | |
97 | ||
98 | print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}') | |
99 | ||
100 | def print_pod_type(pt): | |
101 | print(f' nr_pods {pt.nr_pods.value_()}') | |
102 | ||
103 | print(' pod_cpus', end='') | |
104 | for pod in range(pt.nr_pods): | |
105 | print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='') | |
106 | print('') | |
107 | ||
108 | print(' pod_node', end='') | |
109 | for pod in range(pt.nr_pods): | |
110 | print(f' [{pod}]={pt.pod_node[pod].value_()}', end='') | |
111 | print('') | |
112 | ||
113 | print(f' cpu_pod ', end='') | |
114 | for cpu in for_each_possible_cpu(prog): | |
115 | print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='') | |
116 | print('') | |
117 | ||
63c5484e TH |
118 | for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]: |
119 | print('') | |
120 | print(f'{wq_affn_names[affn].string_().decode().upper()}{" (default)" if affn == wq_affn_dfl else ""}') | |
121 | print_pod_type(wq_pod_types[affn]) | |
7f7dc377 TH |
122 | |
123 | print('') | |
124 | print('Worker Pools') | |
125 | print('============') | |
126 | ||
127 | max_pool_id_len = 0 | |
128 | max_ref_len = 0 | |
129 | for pi, pool in idr_for_each(worker_pool_idr): | |
130 | pool = drgn.Object(prog, 'struct worker_pool', address=pool) | |
131 | max_pool_id_len = max(max_pool_id_len, len(f'{pi}')) | |
132 | max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}')) | |
133 | ||
134 | for pi, pool in idr_for_each(worker_pool_idr): | |
135 | pool = drgn.Object(prog, 'struct worker_pool', address=pool) | |
136 | print(f'pool[{pi:0{max_pool_id_len}}] ref={pool.refcnt.value_():{max_ref_len}} nice={pool.attrs.nice.value_():3} ', end='') | |
137 | print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='') | |
138 | if pool.cpu >= 0: | |
139 | print(f'cpu={pool.cpu.value_():3}', end='') | |
140 | else: | |
141 | print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='') | |
8639eceb TH |
142 | print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='') |
143 | if pool.attrs.affn_strict: | |
144 | print(' strict', end='') | |
7f7dc377 TH |
145 | print('') |
146 | ||
147 | print('') | |
148 | print('Workqueue CPU -> pool') | |
149 | print('=====================') | |
150 | ||
8639eceb | 151 | print('[ workqueue \ type CPU', end='') |
7f7dc377 TH |
152 | for cpu in for_each_possible_cpu(prog): |
153 | print(f' {cpu:{max_pool_id_len}}', end='') | |
154 | print(' dfl]') | |
155 | ||
156 | for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'): | |
157 | print(f'{wq.name.string_().decode()[-24:]:24}', end='') | |
158 | if wq.flags & WQ_UNBOUND: | |
159 | if wq.flags & WQ_ORDERED: | |
8639eceb | 160 | print(' ordered ', end='') |
7f7dc377 TH |
161 | else: |
162 | print(' unbound', end='') | |
8639eceb TH |
163 | if wq.unbound_attrs.affn_strict: |
164 | print(',S ', end='') | |
165 | else: | |
166 | print(' ', end='') | |
7f7dc377 | 167 | else: |
8639eceb | 168 | print(' percpu ', end='') |
7f7dc377 TH |
169 | |
170 | for cpu in for_each_possible_cpu(prog): | |
171 | pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_() | |
172 | field_len = max(len(str(cpu)), max_pool_id_len) | |
173 | print(f' {pool_id:{field_len}}', end='') | |
174 | ||
175 | if wq.flags & WQ_UNBOUND: | |
176 | print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='') | |
177 | print('') |