xref: /openbmc/linux/tools/workqueue/wq_dump.py (revision 7f7dc377a3b2bbaa8cf8941587c228eab4bd82ec)
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('')
167