1212from typing import Union
1313
1414from drgn import cast
15+ from drgn import IntegerLike
1516from drgn import NULL
1617from drgn import Object
1718from drgn import Program
2223from drgn .helpers .linux .list import list_empty
2324from drgn .helpers .linux .list import list_for_each_entry
2425from drgn .helpers .linux .percpu import per_cpu
26+ from drgn .helpers .linux .percpu import per_cpu_ptr
2527from drgn .helpers .linux .pid import find_task
2628
2729
4648 "show_one_worker_pool" ,
4749 "is_task_a_worker" ,
4850 "find_worker_executing_work" ,
51+ "workqueue_get_pwq" ,
4952)
5053
5154
5255_PF_WQ_WORKER = 0x00000020
5356
5457
58+ def _work_offq_pool_none (prog : Program ) -> IntegerLike :
59+ # Linux kernel commit afa4bb778e48 ("workqueue: clean up WORK_* constant
60+ # types, clarify masking") (in v6.4) changed WORK_OFFQ_POOL_NONE from
61+ # constants of type enum to constants of type unsigned long.
62+ try :
63+ val = prog ["WORK_OFFQ_POOL_NONE" ].value_ ()
64+ except KeyError :
65+ val = (
66+ Object (prog , "unsigned long" , 1 ).value_ ()
67+ << prog ["WORK_OFFQ_POOL_BITS" ]
68+ ) - 1
69+ return val
70+
71+
72+ def _work_struct_wq_data_mask (prog : Program ) -> IntegerLike :
73+ # Linux kernel commit afa4bb778e48 ("workqueue: clean up WORK_* constant
74+ # types, clarify masking") (in v6.4) changed WORK_STRUCT_WQ_DATA_MASK from
75+ # constants of type enum to constants of type unsigned long.
76+ try :
77+ val = prog ["WORK_STRUCT_WQ_DATA_MASK" ].value_ ()
78+ except KeyError :
79+ val = ~ (
80+ (Object (prog , "unsigned long" , 1 ) << prog ["WORK_STRUCT_FLAG_BITS" ])
81+ - 1
82+ )
83+ return val
84+
85+
5586def _print_work (work : Object ) -> None :
5687 prog = work .prog_
5788 try :
@@ -63,6 +94,28 @@ def _print_work(work: Object) -> None:
6394 )
6495
6596
97+ def workqueue_get_pwq (workqueue : Object , cpu : int ) -> Object :
98+ """
99+ Find pool_workqueue of a bound workqueue for a given CPU.
100+
101+ :param workqueue: ``struct workqueue_struct *``
102+ :return: ``struct pool_workqueue *``.
103+ """
104+ # At first Linux kernel commit ee1ceef72754 ("workqueue: Rename wq->cpu_pwqs to
105+ # wq->cpu_pwq") (in v6.6) renamed cpu_pwqs to cpu_pwq and then Linux kernel commit
106+ # 687a9aa56f81("workqueue: Make per-cpu pool_workqueues allocated and released
107+ # like unbound ones") (in v6.6) changed cpu_pwq to double pointer.
108+ # As both of the changes were made in v6.6, there are no kernel versions
109+ # with wq->cpu_pwq as a pointer. Still I have mentioned both the changes so that
110+ # we can track both name change and type change of this member.
111+ try :
112+ pwq = per_cpu_ptr (workqueue .cpu_pwqs , cpu )
113+ except AttributeError :
114+ pwq = per_cpu_ptr (workqueue .cpu_pwq , cpu )[0 ]
115+
116+ return pwq
117+
118+
66119def for_each_workqueue (prog : Program ) -> Iterator [Object ]:
67120 """
68121 Iterate over all workqueues in the system.
@@ -243,7 +296,7 @@ def get_work_pwq(work: Object) -> Object:
243296 if data & prog ["WORK_STRUCT_PWQ" ].value_ ():
244297 return cast (
245298 "struct pool_workqueue *" ,
246- data & _wq_data_mask (prog ),
299+ data & _work_struct_wq_data_mask (prog ),
247300 )
248301 else :
249302 return NULL (work .prog_ , "struct pool_workqueue *" )
@@ -262,12 +315,12 @@ def get_work_pool(work: Object) -> Object:
262315 data = cast ("unsigned long" , work .data .counter .read_ ())
263316
264317 if data & prog ["WORK_STRUCT_PWQ" ].value_ ():
265- pwq = data & _wq_data_mask (prog )
318+ pwq = data & _work_struct_wq_data_mask (prog )
266319 pool = Object (prog , "struct pool_workqueue" , address = pwq ).pool
267320 else :
268321 pool_id = data >> prog ["WORK_OFFQ_POOL_SHIFT" ].value_ ()
269322
270- if pool_id == prog [ "WORK_OFFQ_POOL_NONE" ]. value_ ( ):
323+ if pool_id == _work_offq_pool_none ( prog ):
271324 return NULL (work .prog_ , "struct worker_pool *" )
272325
273326 pool = idr_find (prog ["worker_pool_idr" ].address_of_ (), pool_id )
0 commit comments