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.nodemask import for_each_node 54from drgn.helpers.linux.idr import idr_for_each 55 56import argparse 57parser = argparse.ArgumentParser(description=desc, 58 formatter_class=argparse.RawTextHelpFormatter) 59args = parser.parse_args() 60 61def err(s): 62 print(s, file=sys.stderr, flush=True) 63 sys.exit(1) 64 65def cpumask_str(cpumask): 66 output = "" 67 base = 0 68 v = 0 69 for cpu in for_each_cpu(cpumask[0]): 70 while cpu - base >= 32: 71 output += f'{hex(v)} ' 72 base += 32 73 v = 0 74 v |= 1 << (cpu - base) 75 if v > 0: 76 output += f'{v:08x}' 77 return output.strip() 78 79wq_type_len = 9 80 81def wq_type_str(wq): 82 if wq.flags & WQ_BH: 83 return f'{"bh":{wq_type_len}}' 84 elif wq.flags & WQ_UNBOUND: 85 if wq.flags & WQ_ORDERED: 86 return f'{"ordered":{wq_type_len}}' 87 else: 88 if wq.unbound_attrs.affn_strict: 89 return f'{"unbound,S":{wq_type_len}}' 90 else: 91 return f'{"unbound":{wq_type_len}}' 92 else: 93 return f'{"percpu":{wq_type_len}}' 94 95worker_pool_idr = prog['worker_pool_idr'] 96workqueues = prog['workqueues'] 97wq_unbound_cpumask = prog['wq_unbound_cpumask'] 98wq_pod_types = prog['wq_pod_types'] 99wq_affn_dfl = prog['wq_affn_dfl'] 100wq_affn_names = prog['wq_affn_names'] 101 102WQ_BH = prog['WQ_BH'] 103WQ_UNBOUND = prog['WQ_UNBOUND'] 104WQ_ORDERED = prog['__WQ_ORDERED'] 105WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM'] 106 107WQ_AFFN_CPU = prog['WQ_AFFN_CPU'] 108WQ_AFFN_SMT = prog['WQ_AFFN_SMT'] 109WQ_AFFN_CACHE = prog['WQ_AFFN_CACHE'] 110WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA'] 111WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM'] 112 113POOL_BH = prog['POOL_BH'] 114 115WQ_NAME_LEN = prog['WQ_NAME_LEN'].value_() 116cpumask_str_len = len(cpumask_str(wq_unbound_cpumask)) 117 118print('Affinity Scopes') 119print('===============') 120 121print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}') 122 123def print_pod_type(pt): 124 print(f' nr_pods {pt.nr_pods.value_()}') 125 126 print(' pod_cpus', end='') 127 for pod in range(pt.nr_pods): 128 print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='') 129 print('') 130 131 print(' pod_node', end='') 132 for pod in range(pt.nr_pods): 133 print(f' [{pod}]={pt.pod_node[pod].value_()}', end='') 134 print('') 135 136 print(f' cpu_pod ', end='') 137 for cpu in for_each_possible_cpu(prog): 138 print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='') 139 print('') 140 141for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]: 142 print('') 143 print(f'{wq_affn_names[affn].string_().decode().upper()}{" (default)" if affn == wq_affn_dfl else ""}') 144 print_pod_type(wq_pod_types[affn]) 145 146print('') 147print('Worker Pools') 148print('============') 149 150max_pool_id_len = 0 151max_ref_len = 0 152for pi, pool in idr_for_each(worker_pool_idr): 153 pool = drgn.Object(prog, 'struct worker_pool', address=pool) 154 max_pool_id_len = max(max_pool_id_len, len(f'{pi}')) 155 max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}')) 156 157for pi, pool in idr_for_each(worker_pool_idr): 158 pool = drgn.Object(prog, 'struct worker_pool', address=pool) 159 print(f'pool[{pi:0{max_pool_id_len}}] flags=0x{pool.flags.value_():02x} ref={pool.refcnt.value_():{max_ref_len}} nice={pool.attrs.nice.value_():3} ', end='') 160 print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='') 161 if pool.cpu >= 0: 162 print(f'cpu={pool.cpu.value_():3}', end='') 163 if pool.flags & POOL_BH: 164 print(' bh', end='') 165 else: 166 print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='') 167 print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='') 168 if pool.attrs.affn_strict: 169 print(' strict', end='') 170 print('') 171 172print('') 173print('Workqueue CPU -> pool') 174print('=====================') 175 176print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"type CPU":{wq_type_len}}', end='') 177for cpu in for_each_possible_cpu(prog): 178 print(f' {cpu:{max_pool_id_len}}', end='') 179print(' dfl]') 180 181for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'): 182 print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} {wq_type_str(wq):10}', end='') 183 184 for cpu in for_each_possible_cpu(prog): 185 pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_() 186 field_len = max(len(str(cpu)), max_pool_id_len) 187 print(f' {pool_id:{field_len}}', end='') 188 189 if wq.flags & WQ_UNBOUND: 190 print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='') 191 print('') 192 193print('') 194print('Workqueue -> rescuer') 195print('====================') 196 197ucpus_len = max(cpumask_str_len, len("unbound_cpus")) 198rcpus_len = max(cpumask_str_len, len("rescuer_cpus")) 199 200print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"unbound_cpus":{ucpus_len}} pid {"rescuer_cpus":{rcpus_len}} ]') 201 202for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'): 203 if not (wq.flags & WQ_MEM_RECLAIM): 204 continue 205 206 print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='') 207 if wq.unbound_attrs.value_() != 0: 208 print(f' {cpumask_str(wq.unbound_attrs.cpumask):{ucpus_len}}', end='') 209 else: 210 print(f' {"":{ucpus_len}}', end='') 211 212 print(f' {wq.rescuer.task.pid.value_():6}', end='') 213 print(f' {cpumask_str(wq.rescuer.task.cpus_ptr):{rcpus_len}}', end='') 214 print('') 215 216print('') 217print('Unbound workqueue -> node_nr/max_active') 218print('=======================================') 219 220if 'node_to_cpumask_map' in prog: 221 __cpu_online_mask = prog['__cpu_online_mask'] 222 node_to_cpumask_map = prog['node_to_cpumask_map'] 223 nr_node_ids = prog['nr_node_ids'].value_() 224 225 print(f'online_cpus={cpumask_str(__cpu_online_mask.address_of_())}') 226 for node in for_each_node(): 227 print(f'NODE[{node:02}]={cpumask_str(node_to_cpumask_map[node])}') 228 print('') 229 230 print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ min max', end='') 231 first = True 232 for node in for_each_node(): 233 if first: 234 print(f' NODE {node}', end='') 235 first = False 236 else: 237 print(f' {node:7}', end='') 238 print(f' {"dfl":>7} ]') 239 print('') 240 241 for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'): 242 if not (wq.flags & WQ_UNBOUND): 243 continue 244 245 print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} ', end='') 246 print(f'{wq.min_active.value_():3} {wq.max_active.value_():3}', end='') 247 for node in for_each_node(): 248 nna = wq.node_nr_active[node] 249 print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}', end='') 250 nna = wq.node_nr_active[nr_node_ids] 251 print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}') 252else: 253 printf(f'node_to_cpumask_map not present, is NUMA enabled?') 254