| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- #!/usr/bin/env drgn
- #
- # Copyright (C) 2023 Tejun Heo <tj@kernel.org>
- # Copyright (C) 2023 Meta Platforms, Inc. and affiliates.
- desc = """
- This is a drgn script to show the current workqueue configuration. For more
- info on drgn, visit https://github.com/osandov/drgn.
- Affinity Scopes
- ===============
- Shows the CPUs that can be used for unbound workqueues and how they will be
- grouped by each available affinity type. For each type:
- nr_pods number of CPU pods in the affinity type
- pod_cpus CPUs in each pod
- pod_node NUMA node for memory allocation for each pod
- cpu_pod pod that each CPU is associated to
- Worker Pools
- ============
- Lists all worker pools indexed by their ID. For each pool:
- ref number of pool_workqueue's associated with this pool
- nice nice value of the worker threads in the pool
- idle number of idle workers
- workers number of all workers
- cpu CPU the pool is associated with (per-cpu pool)
- cpus CPUs the workers in the pool can run on (unbound pool)
- Workqueue CPU -> pool
- =====================
- Lists all workqueues along with their type and worker pool association. For
- each workqueue:
- NAME TYPE[,FLAGS] POOL_ID...
- NAME name of the workqueue
- TYPE percpu, unbound or ordered
- FLAGS S: strict affinity scope
- POOL_ID worker pool ID associated with each possible CPU
- """
- import sys
- import drgn
- from drgn.helpers.linux.list import list_for_each_entry,list_empty
- from drgn.helpers.linux.percpu import per_cpu_ptr
- from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu
- from drgn.helpers.linux.nodemask import for_each_node
- from drgn.helpers.linux.idr import idr_for_each
- import argparse
- parser = argparse.ArgumentParser(description=desc,
- formatter_class=argparse.RawTextHelpFormatter)
- args = parser.parse_args()
- def err(s):
- print(s, file=sys.stderr, flush=True)
- sys.exit(1)
- def cpumask_str(cpumask):
- output = ""
- base = 0
- v = 0
- for cpu in for_each_cpu(cpumask[0]):
- while cpu - base >= 32:
- output += f'{hex(v)} '
- base += 32
- v = 0
- v |= 1 << (cpu - base)
- if v > 0:
- output += f'{v:08x}'
- return output.strip()
- wq_type_len = 9
- def wq_type_str(wq):
- if wq.flags & WQ_BH:
- return f'{"bh":{wq_type_len}}'
- elif wq.flags & WQ_UNBOUND:
- if wq.flags & WQ_ORDERED:
- return f'{"ordered":{wq_type_len}}'
- else:
- if wq.unbound_attrs.affn_strict:
- return f'{"unbound,S":{wq_type_len}}'
- else:
- return f'{"unbound":{wq_type_len}}'
- else:
- return f'{"percpu":{wq_type_len}}'
- worker_pool_idr = prog['worker_pool_idr']
- workqueues = prog['workqueues']
- wq_unbound_cpumask = prog['wq_unbound_cpumask']
- wq_pod_types = prog['wq_pod_types']
- wq_affn_dfl = prog['wq_affn_dfl']
- wq_affn_names = prog['wq_affn_names']
- WQ_BH = prog['WQ_BH']
- WQ_UNBOUND = prog['WQ_UNBOUND']
- WQ_ORDERED = prog['__WQ_ORDERED']
- WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM']
- WQ_AFFN_CPU = prog['WQ_AFFN_CPU']
- WQ_AFFN_SMT = prog['WQ_AFFN_SMT']
- WQ_AFFN_CACHE = prog['WQ_AFFN_CACHE']
- WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA']
- WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM']
- POOL_BH = prog['POOL_BH']
- WQ_NAME_LEN = prog['WQ_NAME_LEN'].value_()
- cpumask_str_len = len(cpumask_str(wq_unbound_cpumask))
- print('Affinity Scopes')
- print('===============')
- print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}')
- def print_pod_type(pt):
- print(f' nr_pods {pt.nr_pods.value_()}')
- print(' pod_cpus', end='')
- for pod in range(pt.nr_pods):
- print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='')
- print('')
- print(' pod_node', end='')
- for pod in range(pt.nr_pods):
- print(f' [{pod}]={pt.pod_node[pod].value_()}', end='')
- print('')
- print(f' cpu_pod ', end='')
- for cpu in for_each_possible_cpu(prog):
- print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='')
- print('')
- for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]:
- print('')
- print(f'{wq_affn_names[affn].string_().decode().upper()}{" (default)" if affn == wq_affn_dfl else ""}')
- print_pod_type(wq_pod_types[affn])
- print('')
- print('Worker Pools')
- print('============')
- max_pool_id_len = 0
- max_ref_len = 0
- for pi, pool in idr_for_each(worker_pool_idr):
- pool = drgn.Object(prog, 'struct worker_pool', address=pool)
- max_pool_id_len = max(max_pool_id_len, len(f'{pi}'))
- max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}'))
- for pi, pool in idr_for_each(worker_pool_idr):
- pool = drgn.Object(prog, 'struct worker_pool', address=pool)
- 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='')
- print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='')
- if pool.cpu >= 0:
- print(f'cpu={pool.cpu.value_():3}', end='')
- if pool.flags & POOL_BH:
- print(' bh', end='')
- else:
- print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='')
- print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='')
- if pool.attrs.affn_strict:
- print(' strict', end='')
- print('')
- print('')
- print('Workqueue CPU -> pool')
- print('=====================')
- print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"type CPU":{wq_type_len}}', end='')
- for cpu in for_each_possible_cpu(prog):
- print(f' {cpu:{max_pool_id_len}}', end='')
- print(' dfl]')
- for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
- print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} {wq_type_str(wq):10}', end='')
- for cpu in for_each_possible_cpu(prog):
- pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_()
- field_len = max(len(str(cpu)), max_pool_id_len)
- print(f' {pool_id:{field_len}}', end='')
- if wq.flags & WQ_UNBOUND:
- print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='')
- print('')
- print('')
- print('Workqueue -> rescuer')
- print('====================')
- ucpus_len = max(cpumask_str_len, len("unbound_cpus"))
- rcpus_len = max(cpumask_str_len, len("rescuer_cpus"))
- print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"unbound_cpus":{ucpus_len}} pid {"rescuer_cpus":{rcpus_len}} ]')
- for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
- if not (wq.flags & WQ_MEM_RECLAIM):
- continue
- print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='')
- if wq.unbound_attrs.value_() != 0:
- print(f' {cpumask_str(wq.unbound_attrs.cpumask):{ucpus_len}}', end='')
- else:
- print(f' {"":{ucpus_len}}', end='')
- print(f' {wq.rescuer.task.pid.value_():6}', end='')
- print(f' {cpumask_str(wq.rescuer.task.cpus_ptr):{rcpus_len}}', end='')
- print('')
- print('')
- print('Unbound workqueue -> node_nr/max_active')
- print('=======================================')
- if 'node_to_cpumask_map' in prog:
- __cpu_online_mask = prog['__cpu_online_mask']
- node_to_cpumask_map = prog['node_to_cpumask_map']
- nr_node_ids = prog['nr_node_ids'].value_()
- print(f'online_cpus={cpumask_str(__cpu_online_mask.address_of_())}')
- for node in for_each_node():
- print(f'NODE[{node:02}]={cpumask_str(node_to_cpumask_map[node])}')
- print('')
- print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ min max', end='')
- first = True
- for node in for_each_node():
- if first:
- print(f' NODE {node}', end='')
- first = False
- else:
- print(f' {node:7}', end='')
- print(f' {"dfl":>7} ]')
- print('')
- for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
- if not (wq.flags & WQ_UNBOUND):
- continue
- print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} ', end='')
- print(f'{wq.min_active.value_():3} {wq.max_active.value_():3}', end='')
- for node in for_each_node():
- nna = wq.node_nr_active[node]
- print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}', end='')
- nna = wq.node_nr_active[nr_node_ids]
- print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}')
- else:
- printf(f'node_to_cpumask_map not present, is NUMA enabled?')
|