Skip to content

Commit 2c85424

Browse files
imran-knbrenns10
authored andcommitted
workqueue: Fix errors seen for newer (v6.4 and later) kernels.
LSE-373. Signed-off-by: Imran Khan <[email protected]>
1 parent 66a8b3b commit 2c85424

File tree

2 files changed

+61
-7
lines changed

2 files changed

+61
-7
lines changed

drgn_tools/workqueue.py

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from typing import Union
1313

1414
from drgn import cast
15+
from drgn import IntegerLike
1516
from drgn import NULL
1617
from drgn import Object
1718
from drgn import Program
@@ -22,6 +23,7 @@
2223
from drgn.helpers.linux.list import list_empty
2324
from drgn.helpers.linux.list import list_for_each_entry
2425
from drgn.helpers.linux.percpu import per_cpu
26+
from drgn.helpers.linux.percpu import per_cpu_ptr
2527
from drgn.helpers.linux.pid import find_task
2628

2729

@@ -46,12 +48,41 @@
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+
5586
def _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+
66119
def 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)

tests/test_workqueue.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,12 @@ def test_for_each_cpu_worker_pool(prog: drgn.Program) -> None:
7878
def test_for_each_pwq(prog: drgn.Program) -> None:
7979
workq = prog["system_wq"]
8080
pwqs = [pwq.value_() for pwq in wq.for_each_pwq(workq)]
81-
cpu_pwqs = [
82-
per_cpu_ptr(workq.cpu_pwqs, cpu).value_()
81+
cpu_pwqs_attr = "cpu_pwqs" if hasattr(workq, "cpu_pwqs") else "cpu_pwq"
82+
cpu_pwqs_list = [
83+
per_cpu_ptr(getattr(workq, cpu_pwqs_attr), cpu).value_()
8384
for cpu in for_each_online_cpu(prog)
8485
]
85-
assert pwqs.sort() == cpu_pwqs.sort()
86+
assert pwqs.sort() == cpu_pwqs_list.sort()
8687

8788

8889
def test_for_each_pending_work_on_cpu(prog: drgn.Program) -> None:
@@ -97,7 +98,7 @@ def test_for_each_pending_work_in_pool(prog: drgn.Program) -> None:
9798

9899

99100
def test_for_each_pending_work_of_pwq(prog: drgn.Program) -> None:
100-
cpu_pwqs_0 = per_cpu_ptr(prog["system_wq"].cpu_pwqs, 0)
101+
cpu_pwqs_0 = wq.workqueue_get_pwq(prog["system_wq"], 0)
101102
for work in wq.for_each_pending_work_of_pwq(cpu_pwqs_0):
102103
pass
103104

0 commit comments

Comments
 (0)