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 | ||
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 | ||
46 | import sys | |
47 | ||
48 | import drgn | |
49 | from drgn.helpers.linux.list import list_for_each_entry,list_empty | |
50 | from drgn.helpers.linux.percpu import per_cpu_ptr | |
51 | from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu | |
52 | from drgn.helpers.linux.idr import idr_for_each | |
53 | ||
54 | import argparse | |
55 | parser = argparse.ArgumentParser(description=desc, | |
56 | formatter_class=argparse.RawTextHelpFormatter) | |
57 | args = parser.parse_args() | |
58 | ||
59 | def err(s): | |
60 | print(s, file=sys.stderr, flush=True) | |
61 | sys.exit(1) | |
62 | ||
63 | def 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 | ||
77 | worker_pool_idr = prog['worker_pool_idr'] | |
78 | workqueues = prog['workqueues'] | |
79 | wq_unbound_cpumask = prog['wq_unbound_cpumask'] | |
80 | wq_pod_types = prog['wq_pod_types'] | |
81 | ||
82 | WQ_UNBOUND = prog['WQ_UNBOUND'] | |
83 | WQ_ORDERED = prog['__WQ_ORDERED'] | |
84 | WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM'] | |
85 | ||
86 | WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA'] | |
87 | WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM'] | |
88 | ||
89 | print('Affinity Scopes') | |
90 | print('===============') | |
91 | ||
92 | print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}') | |
93 | ||
94 | def 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 | ||
112 | print('') | |
113 | print('NUMA') | |
114 | print_pod_type(wq_pod_types[WQ_AFFN_NUMA]) | |
115 | print('') | |
116 | print('SYSTEM') | |
117 | print_pod_type(wq_pod_types[WQ_AFFN_SYSTEM]) | |
118 | ||
119 | print('') | |
120 | print('Worker Pools') | |
121 | print('============') | |
122 | ||
123 | max_pool_id_len = 0 | |
124 | max_ref_len = 0 | |
125 | for 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 | ||
130 | for 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 | ||
140 | print('') | |
141 | print('Workqueue CPU -> pool') | |
142 | print('=====================') | |
143 | ||
144 | print('[ workqueue \ CPU ', end='') | |
145 | for cpu in for_each_possible_cpu(prog): | |
146 | print(f' {cpu:{max_pool_id_len}}', end='') | |
147 | print(' dfl]') | |
148 | ||
149 | for 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('') |