1111
1212from typing import Iterator , Optional , Union
1313
14- from drgn import NULL , Object , Program , cast
14+ from drgn import NULL , IntegerLike , Object , Program , cast
1515from drgn .helpers .common .format import escape_ascii_string
1616from drgn .helpers .linux .idr import idr_find , idr_for_each
1717from drgn .helpers .linux .list import (
1818 hlist_for_each_entry ,
1919 list_empty ,
2020 list_for_each_entry ,
2121)
22- from drgn .helpers .linux .percpu import per_cpu
22+ from drgn .helpers .linux .percpu import per_cpu , per_cpu_ptr
2323from drgn .helpers .linux .pid import find_task
2424
2525__all__ = (
4343 "show_one_worker_pool" ,
4444 "is_task_a_worker" ,
4545 "find_worker_executing_work" ,
46+ "workqueue_get_pwq" ,
4647)
4748
4849
4950_PF_WQ_WORKER = 0x00000020
5051
5152
53+ def _work_offq_pool_none (prog : Program ) -> IntegerLike :
54+ # Linux kernel commit afa4bb778e48 ("workqueue: clean up WORK_* constant
55+ # types, clarify masking") (in v6.4) changed WORK_OFFQ_POOL_NONE from
56+ # constants of type enum to constants of type unsigned long.
57+ try :
58+ val = prog ["WORK_OFFQ_POOL_NONE" ].value_ ()
59+ except KeyError :
60+ val = (
61+ Object (prog , "unsigned long" , 1 ).value_ () << prog ["WORK_OFFQ_POOL_BITS" ]
62+ ) - 1
63+ return val
64+
65+
66+ def _work_struct_wq_data_mask (prog : Program ) -> IntegerLike :
67+ # Linux kernel commit afa4bb778e48 ("workqueue: clean up WORK_* constant
68+ # types, clarify masking") (in v6.4) changed WORK_STRUCT_WQ_DATA_MASK from
69+ # constants of type enum to constants of type unsigned long.
70+ try :
71+ val = prog ["WORK_STRUCT_WQ_DATA_MASK" ].value_ ()
72+ except KeyError :
73+ val = ~ ((Object (prog , "unsigned long" , 1 ) << prog ["WORK_STRUCT_FLAG_BITS" ]) - 1 )
74+ return val
75+
76+
5277def _print_work (work : Object ) -> None :
5378 prog = work .prog_
5479 print (
5580 f" ({ work .type_ .type_name ()} )0x{ work .value_ ():x} : func: { prog .symbol (work .func .value_ ()).name } "
5681 )
5782
5883
84+ def workqueue_get_pwq (workqueue : Object , cpu : int ) -> Object :
85+ """
86+ Find pool_workqueue of a bound workqueue for a given CPU.
87+
88+ :param workqueue: ``struct workqueue_struct *``
89+ :return: ``struct pool_workqueue *``.
90+ """
91+ # At first Linux kernel commit ee1ceef72754 ("workqueue: Rename wq->cpu_pwqs to
92+ # wq->cpu_pwq") (in v6.6) renamed cpu_pwqs to cpu_pwq and then Linux kernel commit
93+ # 687a9aa56f81("workqueue: Make per-cpu pool_workqueues allocated and released
94+ # like unbound ones") (in v6.6) changed cpu_pwq to double pointer.
95+ # As both of the changes were made in v6.6, there are no kernel versions
96+ # with wq->cpu_pwq as a pointer. Still I have mentioned both the changes so that
97+ # we can track both name change and type change of this member.
98+ try :
99+ pwq = per_cpu_ptr (workqueue .cpu_pwqs , cpu )
100+ except AttributeError :
101+ prog = workqueue .prog_
102+ pwq = Object (
103+ prog ,
104+ "struct pool_workqueue" ,
105+ address = prog .read_word (per_cpu_ptr (workqueue .cpu_pwq , cpu )),
106+ ).address_of_ ()
107+
108+ return pwq
109+
110+
59111def for_each_workqueue (prog : Program ) -> Iterator [Object ]:
60112 """
61113 Iterate over all workqueues in the system.
@@ -210,9 +262,7 @@ def get_work_pwq(work: Object) -> Object:
210262 prog = work .prog_
211263 data = cast ("unsigned long" , work .data .counter .read_ ())
212264 if data & prog ["WORK_STRUCT_PWQ" ].value_ ():
213- return cast (
214- "struct pool_workqueue *" , data & prog ["WORK_STRUCT_WQ_DATA_MASK" ].value_ ()
215- )
265+ return cast ("struct pool_workqueue *" , data & _work_struct_wq_data_mask (prog ))
216266 else :
217267 return NULL (work .prog_ , "struct pool_workqueue *" )
218268
@@ -230,12 +280,12 @@ def get_work_pool(work: Object) -> Object:
230280 data = cast ("unsigned long" , work .data .counter .read_ ())
231281
232282 if data & prog ["WORK_STRUCT_PWQ" ].value_ ():
233- pwq = data & prog [ "WORK_STRUCT_WQ_DATA_MASK" ]. value_ ( )
283+ pwq = data & _work_struct_wq_data_mask ( prog )
234284 pool = Object (prog , "struct pool_workqueue" , address = pwq ).pool
235285 else :
236286 pool_id = data >> prog ["WORK_OFFQ_POOL_SHIFT" ].value_ ()
237287
238- if pool_id == prog [ "WORK_OFFQ_POOL_NONE" ]. value_ ( ):
288+ if pool_id == _work_offq_pool_none ( prog ):
239289 return NULL (work .prog_ , "struct worker_pool *" )
240290
241291 pool = idr_find (prog ["worker_pool_idr" ].address_of_ (), pool_id )
0 commit comments