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'] 81wq_affn_dfl = prog['wq_affn_dfl'] 82wq_affn_names = prog['wq_affn_names'] 83 84WQ_UNBOUND = prog['WQ_UNBOUND'] 85WQ_ORDERED = prog['__WQ_ORDERED'] 86WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM'] 87 88WQ_AFFN_CPU = prog['WQ_AFFN_CPU'] 89WQ_AFFN_SMT = prog['WQ_AFFN_SMT'] 90WQ_AFFN_CACHE = prog['WQ_AFFN_CACHE'] 91WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA'] 92WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM'] 93 94print('Affinity Scopes') 95print('===============') 96 97print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}') 98 99def print_pod_type(pt): 100 print(f' nr_pods {pt.nr_pods.value_()}') 101 102 print(' pod_cpus', end='') 103 for pod in range(pt.nr_pods): 104 print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='') 105 print('') 106 107 print(' pod_node', end='') 108 for pod in range(pt.nr_pods): 109 print(f' [{pod}]={pt.pod_node[pod].value_()}', end='') 110 print('') 111 112 print(f' cpu_pod ', end='') 113 for cpu in for_each_possible_cpu(prog): 114 print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='') 115 print('') 116 117for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]: 118 print('') 119 print(f'{wq_affn_names[affn].string_().decode().upper()}{" (default)" if affn == wq_affn_dfl else ""}') 120 print_pod_type(wq_pod_types[affn]) 121 122print('') 123print('Worker Pools') 124print('============') 125 126max_pool_id_len = 0 127max_ref_len = 0 128for pi, pool in idr_for_each(worker_pool_idr): 129 pool = drgn.Object(prog, 'struct worker_pool', address=pool) 130 max_pool_id_len = max(max_pool_id_len, len(f'{pi}')) 131 max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}')) 132 133for pi, pool in idr_for_each(worker_pool_idr): 134 pool = drgn.Object(prog, 'struct worker_pool', address=pool) 135 print(f'pool[{pi:0{max_pool_id_len}}] ref={pool.refcnt.value_():{max_ref_len}} nice={pool.attrs.nice.value_():3} ', end='') 136 print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='') 137 if pool.cpu >= 0: 138 print(f'cpu={pool.cpu.value_():3}', end='') 139 else: 140 print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='') 141 print('') 142 143print('') 144print('Workqueue CPU -> pool') 145print('=====================') 146 147print('[ workqueue \ CPU ', end='') 148for cpu in for_each_possible_cpu(prog): 149 print(f' {cpu:{max_pool_id_len}}', end='') 150print(' dfl]') 151 152for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'): 153 print(f'{wq.name.string_().decode()[-24:]:24}', end='') 154 if wq.flags & WQ_UNBOUND: 155 if wq.flags & WQ_ORDERED: 156 print(' ordered', end='') 157 else: 158 print(' unbound', end='') 159 else: 160 print(' percpu ', end='') 161 162 for cpu in for_each_possible_cpu(prog): 163 pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_() 164 field_len = max(len(str(cpu)), max_pool_id_len) 165 print(f' {pool_id:{field_len}}', end='') 166 167 if wq.flags & WQ_UNBOUND: 168 print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='') 169 print('') 170