1 #!/usr/bin/env drgn 1 #!/usr/bin/env drgn 2 # 2 # 3 # Copyright (C) 2023 Tejun Heo <tj@kernel.org> 3 # Copyright (C) 2023 Tejun Heo <tj@kernel.org> 4 # Copyright (C) 2023 Meta Platforms, Inc. and 4 # Copyright (C) 2023 Meta Platforms, Inc. and affiliates. 5 5 6 desc = """ 6 desc = """ 7 This is a drgn script to show the current work 7 This is a drgn script to show the current workqueue configuration. For more 8 info on drgn, visit https://github.com/osandov 8 info on drgn, visit https://github.com/osandov/drgn. 9 9 10 Affinity Scopes 10 Affinity Scopes 11 =============== 11 =============== 12 12 13 Shows the CPUs that can be used for unbound wo 13 Shows the CPUs that can be used for unbound workqueues and how they will be 14 grouped by each available affinity type. For e 14 grouped by each available affinity type. For each type: 15 15 16 nr_pods number of CPU pods in the affinity 16 nr_pods number of CPU pods in the affinity type 17 pod_cpus CPUs in each pod 17 pod_cpus CPUs in each pod 18 pod_node NUMA node for memory allocation fo 18 pod_node NUMA node for memory allocation for each pod 19 cpu_pod pod that each CPU is associated to 19 cpu_pod pod that each CPU is associated to 20 20 21 Worker Pools 21 Worker Pools 22 ============ 22 ============ 23 23 24 Lists all worker pools indexed by their ID. Fo 24 Lists all worker pools indexed by their ID. For each pool: 25 25 26 ref number of pool_workqueue's associa 26 ref number of pool_workqueue's associated with this pool 27 nice nice value of the worker threads i 27 nice nice value of the worker threads in the pool 28 idle number of idle workers 28 idle number of idle workers 29 workers number of all workers 29 workers number of all workers 30 cpu CPU the pool is associated with (p 30 cpu CPU the pool is associated with (per-cpu pool) 31 cpus CPUs the workers in the pool can r 31 cpus CPUs the workers in the pool can run on (unbound pool) 32 32 33 Workqueue CPU -> pool 33 Workqueue CPU -> pool 34 ===================== 34 ===================== 35 35 36 Lists all workqueues along with their type and 36 Lists all workqueues along with their type and worker pool association. For 37 each workqueue: 37 each workqueue: 38 38 39 NAME TYPE[,FLAGS] POOL_ID... 39 NAME TYPE[,FLAGS] POOL_ID... 40 40 41 NAME name of the workqueue 41 NAME name of the workqueue 42 TYPE percpu, unbound or ordered 42 TYPE percpu, unbound or ordered 43 FLAGS S: strict affinity scope 43 FLAGS S: strict affinity scope 44 POOL_ID worker pool ID associated with eac 44 POOL_ID worker pool ID associated with each possible CPU 45 """ 45 """ 46 46 47 import sys 47 import sys 48 48 49 import drgn 49 import drgn 50 from drgn.helpers.linux.list import list_for_e 50 from drgn.helpers.linux.list import list_for_each_entry,list_empty 51 from drgn.helpers.linux.percpu import per_cpu_ 51 from drgn.helpers.linux.percpu import per_cpu_ptr 52 from drgn.helpers.linux.cpumask import for_eac 52 from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu 53 from drgn.helpers.linux.nodemask import for_ea 53 from drgn.helpers.linux.nodemask import for_each_node 54 from drgn.helpers.linux.idr import idr_for_eac 54 from drgn.helpers.linux.idr import idr_for_each 55 55 56 import argparse 56 import argparse 57 parser = argparse.ArgumentParser(description=d 57 parser = argparse.ArgumentParser(description=desc, 58 formatter_cla 58 formatter_class=argparse.RawTextHelpFormatter) 59 args = parser.parse_args() 59 args = parser.parse_args() 60 60 61 def err(s): 61 def err(s): 62 print(s, file=sys.stderr, flush=True) 62 print(s, file=sys.stderr, flush=True) 63 sys.exit(1) 63 sys.exit(1) 64 64 65 def cpumask_str(cpumask): 65 def cpumask_str(cpumask): 66 output = "" 66 output = "" 67 base = 0 67 base = 0 68 v = 0 68 v = 0 69 for cpu in for_each_cpu(cpumask[0]): 69 for cpu in for_each_cpu(cpumask[0]): 70 while cpu - base >= 32: 70 while cpu - base >= 32: 71 output += f'{hex(v)} ' 71 output += f'{hex(v)} ' 72 base += 32 72 base += 32 73 v = 0 73 v = 0 74 v |= 1 << (cpu - base) 74 v |= 1 << (cpu - base) 75 if v > 0: 75 if v > 0: 76 output += f'{v:08x}' 76 output += f'{v:08x}' 77 return output.strip() 77 return output.strip() 78 78 79 wq_type_len = 9 79 wq_type_len = 9 80 80 81 def wq_type_str(wq): 81 def wq_type_str(wq): 82 if wq.flags & WQ_BH: 82 if wq.flags & WQ_BH: 83 return f'{"bh":{wq_type_len}}' 83 return f'{"bh":{wq_type_len}}' 84 elif wq.flags & WQ_UNBOUND: 84 elif wq.flags & WQ_UNBOUND: 85 if wq.flags & WQ_ORDERED: 85 if wq.flags & WQ_ORDERED: 86 return f'{"ordered":{wq_type_len}} 86 return f'{"ordered":{wq_type_len}}' 87 else: 87 else: 88 if wq.unbound_attrs.affn_strict: 88 if wq.unbound_attrs.affn_strict: 89 return f'{"unbound,S":{wq_type 89 return f'{"unbound,S":{wq_type_len}}' 90 else: 90 else: 91 return f'{"unbound":{wq_type_l 91 return f'{"unbound":{wq_type_len}}' 92 else: 92 else: 93 return f'{"percpu":{wq_type_len}}' 93 return f'{"percpu":{wq_type_len}}' 94 94 95 worker_pool_idr = prog['worker_pool_id 95 worker_pool_idr = prog['worker_pool_idr'] 96 workqueues = prog['workqueues'] 96 workqueues = prog['workqueues'] 97 wq_unbound_cpumask = prog['wq_unbound_cpu 97 wq_unbound_cpumask = prog['wq_unbound_cpumask'] 98 wq_pod_types = prog['wq_pod_types'] 98 wq_pod_types = prog['wq_pod_types'] 99 wq_affn_dfl = prog['wq_affn_dfl'] 99 wq_affn_dfl = prog['wq_affn_dfl'] 100 wq_affn_names = prog['wq_affn_names' 100 wq_affn_names = prog['wq_affn_names'] 101 101 102 WQ_BH = prog['WQ_BH'] 102 WQ_BH = prog['WQ_BH'] 103 WQ_UNBOUND = prog['WQ_UNBOUND'] 103 WQ_UNBOUND = prog['WQ_UNBOUND'] 104 WQ_ORDERED = prog['__WQ_ORDERED'] 104 WQ_ORDERED = prog['__WQ_ORDERED'] 105 WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM 105 WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM'] 106 106 107 WQ_AFFN_CPU = prog['WQ_AFFN_CPU'] 107 WQ_AFFN_CPU = prog['WQ_AFFN_CPU'] 108 WQ_AFFN_SMT = prog['WQ_AFFN_SMT'] 108 WQ_AFFN_SMT = prog['WQ_AFFN_SMT'] 109 WQ_AFFN_CACHE = prog['WQ_AFFN_CACHE' 109 WQ_AFFN_CACHE = prog['WQ_AFFN_CACHE'] 110 WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA'] 110 WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA'] 111 WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM 111 WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM'] 112 112 113 POOL_BH = prog['POOL_BH'] 113 POOL_BH = prog['POOL_BH'] 114 114 115 WQ_NAME_LEN = prog['WQ_NAME_LEN']. 115 WQ_NAME_LEN = prog['WQ_NAME_LEN'].value_() 116 cpumask_str_len = len(cpumask_str(wq_u 116 cpumask_str_len = len(cpumask_str(wq_unbound_cpumask)) 117 117 118 print('Affinity Scopes') 118 print('Affinity Scopes') 119 print('===============') 119 print('===============') 120 120 121 print(f'wq_unbound_cpumask={cpumask_str(wq_unb 121 print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}') 122 122 123 def print_pod_type(pt): 123 def print_pod_type(pt): 124 print(f' nr_pods {pt.nr_pods.value_()}') 124 print(f' nr_pods {pt.nr_pods.value_()}') 125 125 126 print(' pod_cpus', end='') 126 print(' pod_cpus', end='') 127 for pod in range(pt.nr_pods): 127 for pod in range(pt.nr_pods): 128 print(f' [{pod}]={cpumask_str(pt.pod_c 128 print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='') 129 print('') 129 print('') 130 130 131 print(' pod_node', end='') 131 print(' pod_node', end='') 132 for pod in range(pt.nr_pods): 132 for pod in range(pt.nr_pods): 133 print(f' [{pod}]={pt.pod_node[pod].val 133 print(f' [{pod}]={pt.pod_node[pod].value_()}', end='') 134 print('') 134 print('') 135 135 136 print(f' cpu_pod ', end='') 136 print(f' cpu_pod ', end='') 137 for cpu in for_each_possible_cpu(prog): 137 for cpu in for_each_possible_cpu(prog): 138 print(f' [{cpu}]={pt.cpu_pod[cpu].valu 138 print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='') 139 print('') 139 print('') 140 140 141 for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN 141 for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]: 142 print('') 142 print('') 143 print(f'{wq_affn_names[affn].string_().dec 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]) 144 print_pod_type(wq_pod_types[affn]) 145 145 146 print('') 146 print('') 147 print('Worker Pools') 147 print('Worker Pools') 148 print('============') 148 print('============') 149 149 150 max_pool_id_len = 0 150 max_pool_id_len = 0 151 max_ref_len = 0 151 max_ref_len = 0 152 for pi, pool in idr_for_each(worker_pool_idr): 152 for pi, pool in idr_for_each(worker_pool_idr): 153 pool = drgn.Object(prog, 'struct worker_po 153 pool = drgn.Object(prog, 'struct worker_pool', address=pool) 154 max_pool_id_len = max(max_pool_id_len, len 154 max_pool_id_len = max(max_pool_id_len, len(f'{pi}')) 155 max_ref_len = max(max_ref_len, len(f'{pool 155 max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}')) 156 156 157 for pi, pool in idr_for_each(worker_pool_idr): 157 for pi, pool in idr_for_each(worker_pool_idr): 158 pool = drgn.Object(prog, 'struct worker_po 158 pool = drgn.Object(prog, 'struct worker_pool', address=pool) 159 print(f'pool[{pi:0{max_pool_id_len}}] flag 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_( 160 print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='') 161 if pool.cpu >= 0: 161 if pool.cpu >= 0: 162 print(f'cpu={pool.cpu.value_():3}', en 162 print(f'cpu={pool.cpu.value_():3}', end='') 163 if pool.flags & POOL_BH: 163 if pool.flags & POOL_BH: 164 print(' bh', end='') 164 print(' bh', end='') 165 else: 165 else: 166 print(f'cpus={cpumask_str(pool.attrs.c 166 print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='') 167 print(f' pod_cpus={cpumask_str(pool.at 167 print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='') 168 if pool.attrs.affn_strict: 168 if pool.attrs.affn_strict: 169 print(' strict', end='') 169 print(' strict', end='') 170 print('') 170 print('') 171 171 172 print('') 172 print('') 173 print('Workqueue CPU -> pool') 173 print('Workqueue CPU -> pool') 174 print('=====================') 174 print('=====================') 175 175 176 print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"ty 176 print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"type CPU":{wq_type_len}}', end='') 177 for cpu in for_each_possible_cpu(prog): 177 for cpu in for_each_possible_cpu(prog): 178 print(f' {cpu:{max_pool_id_len}}', end='') 178 print(f' {cpu:{max_pool_id_len}}', end='') 179 print(' dfl]') 179 print(' dfl]') 180 180 181 for wq in list_for_each_entry('struct workqueu 181 for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'): 182 print(f'{wq.name.string_().decode():{WQ_NA 182 print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} {wq_type_str(wq):10}', end='') 183 183 184 for cpu in for_each_possible_cpu(prog): 184 for cpu in for_each_possible_cpu(prog): 185 pool_id = per_cpu_ptr(wq.cpu_pwq, cpu) 185 pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_() 186 field_len = max(len(str(cpu)), max_poo 186 field_len = max(len(str(cpu)), max_pool_id_len) 187 print(f' {pool_id:{field_len}}', end=' 187 print(f' {pool_id:{field_len}}', end='') 188 188 189 if wq.flags & WQ_UNBOUND: 189 if wq.flags & WQ_UNBOUND: 190 print(f' {wq.dfl_pwq.pool.id.value_(): 190 print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='') 191 print('') 191 print('') 192 192 193 print('') 193 print('') 194 print('Workqueue -> rescuer') 194 print('Workqueue -> rescuer') 195 print('====================') 195 print('====================') 196 196 197 ucpus_len = max(cpumask_str_len, len("unbound_ 197 ucpus_len = max(cpumask_str_len, len("unbound_cpus")) 198 rcpus_len = max(cpumask_str_len, len("rescuer_ 198 rcpus_len = max(cpumask_str_len, len("rescuer_cpus")) 199 199 200 print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"un 200 print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"unbound_cpus":{ucpus_len}} pid {"rescuer_cpus":{rcpus_len}} ]') 201 201 202 for wq in list_for_each_entry('struct workqueu 202 for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'): 203 if not (wq.flags & WQ_MEM_RECLAIM): 203 if not (wq.flags & WQ_MEM_RECLAIM): 204 continue 204 continue 205 205 206 print(f'{wq.name.string_().decode():{WQ_NA 206 print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='') 207 if wq.unbound_attrs.value_() != 0: 207 if wq.unbound_attrs.value_() != 0: 208 print(f' {cpumask_str(wq.unbound_attrs 208 print(f' {cpumask_str(wq.unbound_attrs.cpumask):{ucpus_len}}', end='') 209 else: 209 else: 210 print(f' {"":{ucpus_len}}', end='') 210 print(f' {"":{ucpus_len}}', end='') 211 211 212 print(f' {wq.rescuer.task.pid.value_():6}' 212 print(f' {wq.rescuer.task.pid.value_():6}', end='') 213 print(f' {cpumask_str(wq.rescuer.task.cpus 213 print(f' {cpumask_str(wq.rescuer.task.cpus_ptr):{rcpus_len}}', end='') 214 print('') 214 print('') 215 215 216 print('') 216 print('') 217 print('Unbound workqueue -> node_nr/max_active 217 print('Unbound workqueue -> node_nr/max_active') 218 print('======================================= 218 print('=======================================') 219 219 220 if 'node_to_cpumask_map' in prog: 220 if 'node_to_cpumask_map' in prog: 221 __cpu_online_mask = prog['__cpu_online_mas 221 __cpu_online_mask = prog['__cpu_online_mask'] 222 node_to_cpumask_map = prog['node_to_cpumas 222 node_to_cpumask_map = prog['node_to_cpumask_map'] 223 nr_node_ids = prog['nr_node_ids'].value_() 223 nr_node_ids = prog['nr_node_ids'].value_() 224 224 225 print(f'online_cpus={cpumask_str(__cpu_onl 225 print(f'online_cpus={cpumask_str(__cpu_online_mask.address_of_())}') 226 for node in for_each_node(): 226 for node in for_each_node(): 227 print(f'NODE[{node:02}]={cpumask_str(n 227 print(f'NODE[{node:02}]={cpumask_str(node_to_cpumask_map[node])}') 228 print('') 228 print('') 229 229 230 print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ 230 print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ min max', end='') 231 first = True 231 first = True 232 for node in for_each_node(): 232 for node in for_each_node(): 233 if first: 233 if first: 234 print(f' NODE {node}', end='') 234 print(f' NODE {node}', end='') 235 first = False 235 first = False 236 else: 236 else: 237 print(f' {node:7}', end='') 237 print(f' {node:7}', end='') 238 print(f' {"dfl":>7} ]') 238 print(f' {"dfl":>7} ]') 239 print('') 239 print('') 240 240 241 for wq in list_for_each_entry('struct work 241 for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'): 242 if not (wq.flags & WQ_UNBOUND): 242 if not (wq.flags & WQ_UNBOUND): 243 continue 243 continue 244 244 245 print(f'{wq.name.string_().decode():{W 245 print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} ', end='') 246 print(f'{wq.min_active.value_():3} {wq 246 print(f'{wq.min_active.value_():3} {wq.max_active.value_():3}', end='') 247 for node in for_each_node(): 247 for node in for_each_node(): 248 nna = wq.node_nr_active[node] 248 nna = wq.node_nr_active[node] 249 print(f' {nna.nr.counter.value_(): 249 print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}', end='') 250 nna = wq.node_nr_active[nr_node_ids] 250 nna = wq.node_nr_active[nr_node_ids] 251 print(f' {nna.nr.counter.value_():3}/{ 251 print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}') 252 else: 252 else: 253 printf(f'node_to_cpumask_map not present, 253 printf(f'node_to_cpumask_map not present, is NUMA enabled?')
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.