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[,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 drgn
50from drgn.helpers.linux.list import list_for_each_entry,list_empty
51from drgn.helpers.linux.percpu import per_cpu_ptr
52from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu
53from drgn.helpers.linux.idr import idr_for_each
54
55import argparse
56parser = argparse.ArgumentParser(description=desc,
57                                 formatter_class=argparse.RawTextHelpFormatter)
58args = parser.parse_args()
59
60def err(s):
61    print(s, file=sys.stderr, flush=True)
62    sys.exit(1)
63
64def 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
78worker_pool_idr         = prog['worker_pool_idr']
79workqueues              = prog['workqueues']
80wq_unbound_cpumask      = prog['wq_unbound_cpumask']
81wq_pod_types            = prog['wq_pod_types']
82wq_affn_dfl             = prog['wq_affn_dfl']
83wq_affn_names           = prog['wq_affn_names']
84
85WQ_UNBOUND              = prog['WQ_UNBOUND']
86WQ_ORDERED              = prog['__WQ_ORDERED']
87WQ_MEM_RECLAIM          = prog['WQ_MEM_RECLAIM']
88
89WQ_AFFN_CPU             = prog['WQ_AFFN_CPU']
90WQ_AFFN_SMT             = prog['WQ_AFFN_SMT']
91WQ_AFFN_CACHE           = prog['WQ_AFFN_CACHE']
92WQ_AFFN_NUMA            = prog['WQ_AFFN_NUMA']
93WQ_AFFN_SYSTEM          = prog['WQ_AFFN_SYSTEM']
94
95print('Affinity Scopes')
96print('===============')
97
98print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}')
99
100def 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
118for 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])
122
123print('')
124print('Worker Pools')
125print('============')
126
127max_pool_id_len = 0
128max_ref_len = 0
129for 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
134for 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='')
142        print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='')
143        if pool.attrs.affn_strict:
144            print(' strict', end='')
145    print('')
146
147print('')
148print('Workqueue CPU -> pool')
149print('=====================')
150
151print('[    workqueue     \     type   CPU', end='')
152for cpu in for_each_possible_cpu(prog):
153    print(f' {cpu:{max_pool_id_len}}', end='')
154print(' dfl]')
155
156for 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:
160            print(' ordered   ', end='')
161        else:
162            print(' unbound', end='')
163            if wq.unbound_attrs.affn_strict:
164                print(',S ', end='')
165            else:
166                print('   ', end='')
167    else:
168        print(' percpu    ', end='')
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('')
178