Skip to content

Commit

Permalink
workqueue: Fix errors seen for newer (v6.4 and later) kernels.
Browse files Browse the repository at this point in the history
LSE-373.

Signed-off-by: Imran Khan <[email protected]>
  • Loading branch information
imran-kn authored and brenns10 committed Jun 2, 2024
1 parent 66a8b3b commit 2c85424
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 7 deletions.
59 changes: 56 additions & 3 deletions drgn_tools/workqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from typing import Union

from drgn import cast
from drgn import IntegerLike
from drgn import NULL
from drgn import Object
from drgn import Program
Expand All @@ -22,6 +23,7 @@
from drgn.helpers.linux.list import list_empty
from drgn.helpers.linux.list import list_for_each_entry
from drgn.helpers.linux.percpu import per_cpu
from drgn.helpers.linux.percpu import per_cpu_ptr
from drgn.helpers.linux.pid import find_task


Expand All @@ -46,12 +48,41 @@
"show_one_worker_pool",
"is_task_a_worker",
"find_worker_executing_work",
"workqueue_get_pwq",
)


_PF_WQ_WORKER = 0x00000020


def _work_offq_pool_none(prog: Program) -> IntegerLike:
# Linux kernel commit afa4bb778e48 ("workqueue: clean up WORK_* constant
# types, clarify masking") (in v6.4) changed WORK_OFFQ_POOL_NONE from
# constants of type enum to constants of type unsigned long.
try:
val = prog["WORK_OFFQ_POOL_NONE"].value_()
except KeyError:
val = (
Object(prog, "unsigned long", 1).value_()
<< prog["WORK_OFFQ_POOL_BITS"]
) - 1
return val


def _work_struct_wq_data_mask(prog: Program) -> IntegerLike:
# Linux kernel commit afa4bb778e48 ("workqueue: clean up WORK_* constant
# types, clarify masking") (in v6.4) changed WORK_STRUCT_WQ_DATA_MASK from
# constants of type enum to constants of type unsigned long.
try:
val = prog["WORK_STRUCT_WQ_DATA_MASK"].value_()
except KeyError:
val = ~(
(Object(prog, "unsigned long", 1) << prog["WORK_STRUCT_FLAG_BITS"])
- 1
)
return val


def _print_work(work: Object) -> None:
prog = work.prog_
try:
Expand All @@ -63,6 +94,28 @@ def _print_work(work: Object) -> None:
)


def workqueue_get_pwq(workqueue: Object, cpu: int) -> Object:
"""
Find pool_workqueue of a bound workqueue for a given CPU.
:param workqueue: ``struct workqueue_struct *``
:return: ``struct pool_workqueue *``.
"""
# At first Linux kernel commit ee1ceef72754 ("workqueue: Rename wq->cpu_pwqs to
# wq->cpu_pwq") (in v6.6) renamed cpu_pwqs to cpu_pwq and then Linux kernel commit
# 687a9aa56f81("workqueue: Make per-cpu pool_workqueues allocated and released
# like unbound ones") (in v6.6) changed cpu_pwq to double pointer.
# As both of the changes were made in v6.6, there are no kernel versions
# with wq->cpu_pwq as a pointer. Still I have mentioned both the changes so that
# we can track both name change and type change of this member.
try:
pwq = per_cpu_ptr(workqueue.cpu_pwqs, cpu)
except AttributeError:
pwq = per_cpu_ptr(workqueue.cpu_pwq, cpu)[0]

return pwq


def for_each_workqueue(prog: Program) -> Iterator[Object]:
"""
Iterate over all workqueues in the system.
Expand Down Expand Up @@ -243,7 +296,7 @@ def get_work_pwq(work: Object) -> Object:
if data & prog["WORK_STRUCT_PWQ"].value_():
return cast(
"struct pool_workqueue *",
data & _wq_data_mask(prog),
data & _work_struct_wq_data_mask(prog),
)
else:
return NULL(work.prog_, "struct pool_workqueue *")
Expand All @@ -262,12 +315,12 @@ def get_work_pool(work: Object) -> Object:
data = cast("unsigned long", work.data.counter.read_())

if data & prog["WORK_STRUCT_PWQ"].value_():
pwq = data & _wq_data_mask(prog)
pwq = data & _work_struct_wq_data_mask(prog)
pool = Object(prog, "struct pool_workqueue", address=pwq).pool
else:
pool_id = data >> prog["WORK_OFFQ_POOL_SHIFT"].value_()

if pool_id == prog["WORK_OFFQ_POOL_NONE"].value_():
if pool_id == _work_offq_pool_none(prog):
return NULL(work.prog_, "struct worker_pool *")

pool = idr_find(prog["worker_pool_idr"].address_of_(), pool_id)
Expand Down
9 changes: 5 additions & 4 deletions tests/test_workqueue.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,12 @@ def test_for_each_cpu_worker_pool(prog: drgn.Program) -> None:
def test_for_each_pwq(prog: drgn.Program) -> None:
workq = prog["system_wq"]
pwqs = [pwq.value_() for pwq in wq.for_each_pwq(workq)]
cpu_pwqs = [
per_cpu_ptr(workq.cpu_pwqs, cpu).value_()
cpu_pwqs_attr = "cpu_pwqs" if hasattr(workq, "cpu_pwqs") else "cpu_pwq"
cpu_pwqs_list = [
per_cpu_ptr(getattr(workq, cpu_pwqs_attr), cpu).value_()
for cpu in for_each_online_cpu(prog)
]
assert pwqs.sort() == cpu_pwqs.sort()
assert pwqs.sort() == cpu_pwqs_list.sort()


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


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

Expand Down

0 comments on commit 2c85424

Please sign in to comment.