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