mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	- Factor out wq_type_str() - Improve formatting so that it adapts to actual field widths. - Drop duplicate information from "Workqueue -> rescuer" section. If anything, we should add more rescuer-specific info - e.g. the number of work items rescued. Signed-off-by: Tejun Heo <tj@kernel.org> Cc: Juri Lelli <juri.lelli@redhat.com>
		
			
				
	
	
		
			207 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			207 lines
		
	
	
	
		
			6.7 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#!/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.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_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_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']
 | 
						|
 | 
						|
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}}] 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='')
 | 
						|
    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('')
 |