wq_dump.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  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. desc = """
  6. This is a drgn script to show the current workqueue configuration. For more
  7. info on drgn, visit https://github.com/osandov/drgn.
  8. Affinity Scopes
  9. ===============
  10. Shows the CPUs that can be used for unbound workqueues and how they will be
  11. grouped by each available affinity type. For each type:
  12. nr_pods number of CPU pods in the affinity type
  13. pod_cpus CPUs in each pod
  14. pod_node NUMA node for memory allocation for each pod
  15. cpu_pod pod that each CPU is associated to
  16. Worker Pools
  17. ============
  18. Lists all worker pools indexed by their ID. For each pool:
  19. ref number of pool_workqueue's associated with this pool
  20. nice nice value of the worker threads in the pool
  21. idle number of idle workers
  22. workers number of all workers
  23. cpu CPU the pool is associated with (per-cpu pool)
  24. cpus CPUs the workers in the pool can run on (unbound pool)
  25. Workqueue CPU -> pool
  26. =====================
  27. Lists all workqueues along with their type and worker pool association. For
  28. each workqueue:
  29. NAME TYPE[,FLAGS] POOL_ID...
  30. NAME name of the workqueue
  31. TYPE percpu, unbound or ordered
  32. FLAGS S: strict affinity scope
  33. POOL_ID worker pool ID associated with each possible CPU
  34. """
  35. import sys
  36. import drgn
  37. from drgn.helpers.linux.list import list_for_each_entry,list_empty
  38. from drgn.helpers.linux.percpu import per_cpu_ptr
  39. from drgn.helpers.linux.cpumask import for_each_cpu,for_each_possible_cpu
  40. from drgn.helpers.linux.nodemask import for_each_node
  41. from drgn.helpers.linux.idr import idr_for_each
  42. import argparse
  43. parser = argparse.ArgumentParser(description=desc,
  44. formatter_class=argparse.RawTextHelpFormatter)
  45. args = parser.parse_args()
  46. def err(s):
  47. print(s, file=sys.stderr, flush=True)
  48. sys.exit(1)
  49. def cpumask_str(cpumask):
  50. output = ""
  51. base = 0
  52. v = 0
  53. for cpu in for_each_cpu(cpumask[0]):
  54. while cpu - base >= 32:
  55. output += f'{hex(v)} '
  56. base += 32
  57. v = 0
  58. v |= 1 << (cpu - base)
  59. if v > 0:
  60. output += f'{v:08x}'
  61. return output.strip()
  62. wq_type_len = 9
  63. def wq_type_str(wq):
  64. if wq.flags & WQ_BH:
  65. return f'{"bh":{wq_type_len}}'
  66. elif wq.flags & WQ_UNBOUND:
  67. if wq.flags & WQ_ORDERED:
  68. return f'{"ordered":{wq_type_len}}'
  69. else:
  70. if wq.unbound_attrs.affn_strict:
  71. return f'{"unbound,S":{wq_type_len}}'
  72. else:
  73. return f'{"unbound":{wq_type_len}}'
  74. else:
  75. return f'{"percpu":{wq_type_len}}'
  76. worker_pool_idr = prog['worker_pool_idr']
  77. workqueues = prog['workqueues']
  78. wq_unbound_cpumask = prog['wq_unbound_cpumask']
  79. wq_pod_types = prog['wq_pod_types']
  80. wq_affn_dfl = prog['wq_affn_dfl']
  81. wq_affn_names = prog['wq_affn_names']
  82. WQ_BH = prog['WQ_BH']
  83. WQ_UNBOUND = prog['WQ_UNBOUND']
  84. WQ_ORDERED = prog['__WQ_ORDERED']
  85. WQ_MEM_RECLAIM = prog['WQ_MEM_RECLAIM']
  86. WQ_AFFN_CPU = prog['WQ_AFFN_CPU']
  87. WQ_AFFN_SMT = prog['WQ_AFFN_SMT']
  88. WQ_AFFN_CACHE = prog['WQ_AFFN_CACHE']
  89. WQ_AFFN_NUMA = prog['WQ_AFFN_NUMA']
  90. WQ_AFFN_SYSTEM = prog['WQ_AFFN_SYSTEM']
  91. POOL_BH = prog['POOL_BH']
  92. WQ_NAME_LEN = prog['WQ_NAME_LEN'].value_()
  93. cpumask_str_len = len(cpumask_str(wq_unbound_cpumask))
  94. print('Affinity Scopes')
  95. print('===============')
  96. print(f'wq_unbound_cpumask={cpumask_str(wq_unbound_cpumask)}')
  97. def print_pod_type(pt):
  98. print(f' nr_pods {pt.nr_pods.value_()}')
  99. print(' pod_cpus', end='')
  100. for pod in range(pt.nr_pods):
  101. print(f' [{pod}]={cpumask_str(pt.pod_cpus[pod])}', end='')
  102. print('')
  103. print(' pod_node', end='')
  104. for pod in range(pt.nr_pods):
  105. print(f' [{pod}]={pt.pod_node[pod].value_()}', end='')
  106. print('')
  107. print(f' cpu_pod ', end='')
  108. for cpu in for_each_possible_cpu(prog):
  109. print(f' [{cpu}]={pt.cpu_pod[cpu].value_()}', end='')
  110. print('')
  111. for affn in [WQ_AFFN_CPU, WQ_AFFN_SMT, WQ_AFFN_CACHE, WQ_AFFN_NUMA, WQ_AFFN_SYSTEM]:
  112. print('')
  113. print(f'{wq_affn_names[affn].string_().decode().upper()}{" (default)" if affn == wq_affn_dfl else ""}')
  114. print_pod_type(wq_pod_types[affn])
  115. print('')
  116. print('Worker Pools')
  117. print('============')
  118. max_pool_id_len = 0
  119. max_ref_len = 0
  120. for pi, pool in idr_for_each(worker_pool_idr):
  121. pool = drgn.Object(prog, 'struct worker_pool', address=pool)
  122. max_pool_id_len = max(max_pool_id_len, len(f'{pi}'))
  123. max_ref_len = max(max_ref_len, len(f'{pool.refcnt.value_()}'))
  124. for pi, pool in idr_for_each(worker_pool_idr):
  125. pool = drgn.Object(prog, 'struct worker_pool', address=pool)
  126. 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='')
  127. print(f'idle/workers={pool.nr_idle.value_():3}/{pool.nr_workers.value_():3} ', end='')
  128. if pool.cpu >= 0:
  129. print(f'cpu={pool.cpu.value_():3}', end='')
  130. if pool.flags & POOL_BH:
  131. print(' bh', end='')
  132. else:
  133. print(f'cpus={cpumask_str(pool.attrs.cpumask)}', end='')
  134. print(f' pod_cpus={cpumask_str(pool.attrs.__pod_cpumask)}', end='')
  135. if pool.attrs.affn_strict:
  136. print(' strict', end='')
  137. print('')
  138. print('')
  139. print('Workqueue CPU -> pool')
  140. print('=====================')
  141. print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"type CPU":{wq_type_len}}', end='')
  142. for cpu in for_each_possible_cpu(prog):
  143. print(f' {cpu:{max_pool_id_len}}', end='')
  144. print(' dfl]')
  145. for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
  146. print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} {wq_type_str(wq):10}', end='')
  147. for cpu in for_each_possible_cpu(prog):
  148. pool_id = per_cpu_ptr(wq.cpu_pwq, cpu)[0].pool.id.value_()
  149. field_len = max(len(str(cpu)), max_pool_id_len)
  150. print(f' {pool_id:{field_len}}', end='')
  151. if wq.flags & WQ_UNBOUND:
  152. print(f' {wq.dfl_pwq.pool.id.value_():{max_pool_id_len}}', end='')
  153. print('')
  154. print('')
  155. print('Workqueue -> rescuer')
  156. print('====================')
  157. ucpus_len = max(cpumask_str_len, len("unbound_cpus"))
  158. rcpus_len = max(cpumask_str_len, len("rescuer_cpus"))
  159. print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ {"unbound_cpus":{ucpus_len}} pid {"rescuer_cpus":{rcpus_len}} ]')
  160. for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
  161. if not (wq.flags & WQ_MEM_RECLAIM):
  162. continue
  163. print(f'{wq.name.string_().decode():{WQ_NAME_LEN}}', end='')
  164. if wq.unbound_attrs.value_() != 0:
  165. print(f' {cpumask_str(wq.unbound_attrs.cpumask):{ucpus_len}}', end='')
  166. else:
  167. print(f' {"":{ucpus_len}}', end='')
  168. print(f' {wq.rescuer.task.pid.value_():6}', end='')
  169. print(f' {cpumask_str(wq.rescuer.task.cpus_ptr):{rcpus_len}}', end='')
  170. print('')
  171. print('')
  172. print('Unbound workqueue -> node_nr/max_active')
  173. print('=======================================')
  174. if 'node_to_cpumask_map' in prog:
  175. __cpu_online_mask = prog['__cpu_online_mask']
  176. node_to_cpumask_map = prog['node_to_cpumask_map']
  177. nr_node_ids = prog['nr_node_ids'].value_()
  178. print(f'online_cpus={cpumask_str(__cpu_online_mask.address_of_())}')
  179. for node in for_each_node():
  180. print(f'NODE[{node:02}]={cpumask_str(node_to_cpumask_map[node])}')
  181. print('')
  182. print(f'[{"workqueue":^{WQ_NAME_LEN-2}}\\ min max', end='')
  183. first = True
  184. for node in for_each_node():
  185. if first:
  186. print(f' NODE {node}', end='')
  187. first = False
  188. else:
  189. print(f' {node:7}', end='')
  190. print(f' {"dfl":>7} ]')
  191. print('')
  192. for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
  193. if not (wq.flags & WQ_UNBOUND):
  194. continue
  195. print(f'{wq.name.string_().decode():{WQ_NAME_LEN}} ', end='')
  196. print(f'{wq.min_active.value_():3} {wq.max_active.value_():3}', end='')
  197. for node in for_each_node():
  198. nna = wq.node_nr_active[node]
  199. print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}', end='')
  200. nna = wq.node_nr_active[nr_node_ids]
  201. print(f' {nna.nr.counter.value_():3}/{nna.max.value_():3}')
  202. else:
  203. printf(f'node_to_cpumask_map not present, is NUMA enabled?')