Skip to content

Commit 16c1fde

Browse files
committed
Added test cases for workqueue helpers
Signed-off-by: Imran Khan <[email protected]>
1 parent 3ec4bab commit 16c1fde

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
# Copyright (c) 2022, Oracle and/or its affiliates.
2+
# SPDX-License-Identifier: LGPL-2.1-or-later
3+
4+
from drgn.helpers.linux.cpumask import for_each_online_cpu
5+
from drgn.helpers.linux.percpu import per_cpu, per_cpu_ptr
6+
from drgn.helpers.linux.pid import for_each_task
7+
from drgn.helpers.linux.workqueue import (
8+
find_worker_executing_work,
9+
find_workqueue,
10+
for_each_cpu_worker_pool,
11+
for_each_pending_work,
12+
for_each_pending_work_in_pool,
13+
for_each_pending_work_of_pwq,
14+
for_each_pending_work_on_cpu,
15+
for_each_pool,
16+
for_each_pool_worker,
17+
for_each_pwq,
18+
for_each_worker,
19+
for_each_workqueue,
20+
get_work_pool,
21+
get_work_pwq,
22+
)
23+
from tests.linux_kernel import LinuxKernelTestCase, skip_unless_have_test_kmod
24+
25+
26+
def system_workqueue_names(prog):
27+
# Pick some global workqueues that will always exist
28+
return {
29+
prog["system_wq"].name.string_(),
30+
prog["system_highpri_wq"].name.string_(),
31+
prog["system_long_wq"].name.string_(),
32+
prog["system_unbound_wq"].name.string_(),
33+
prog["system_freezable_wq"].name.string_(),
34+
}
35+
36+
37+
class TestWorkqueue(LinuxKernelTestCase):
38+
@classmethod
39+
@skip_unless_have_test_kmod
40+
def setUpClass(cls):
41+
cls.test_wq = cls.prog["drgn_test_wq"]
42+
cls.test_works = [cls.prog["drgn_test_works"][i].address_ for i in range(5)]
43+
cls.test_work_0 = cls.prog["drgn_test_works"][0].address_of_()
44+
cls.test_blocker_work = cls.prog["drgn_test_blocker_work"].address_of_()
45+
cls.test_wq_sysfs = open(
46+
"/sys/module/drgn_test/parameters/workqueue_test_trigger", "w+"
47+
)
48+
cls.test_wq_sysfs.write("1")
49+
cls.test_wq_sysfs.flush()
50+
51+
@classmethod
52+
@skip_unless_have_test_kmod
53+
def tearDownClass(cls):
54+
cls.test_wq_sysfs.write("0")
55+
cls.test_wq_sysfs.close()
56+
57+
def test_for_each_workqueue(self):
58+
# The found workqueue names should be a superset of the test names.
59+
self.assertGreaterEqual(
60+
{wq.name.string_() for wq in for_each_workqueue(self.prog)},
61+
system_workqueue_names(self.prog),
62+
)
63+
64+
def test_for_each_pool(self):
65+
self.assertIn(
66+
per_cpu(self.prog["cpu_worker_pools"], 0)[0].address_of_(),
67+
for_each_pool(self.prog),
68+
)
69+
70+
def test_for_each_worker(self):
71+
kworkers = [
72+
task.value_()
73+
for task in for_each_task(self.prog)
74+
if task.comm.string_().decode().startswith("kworker")
75+
]
76+
self.assertEqual(
77+
[worker.task.value_() for worker in for_each_worker(self.prog)].sort(),
78+
kworkers.sort(),
79+
)
80+
81+
def test_for_each_pool_worker(self):
82+
test_pool = per_cpu(self.prog["cpu_worker_pools"], 0)[0].address_
83+
kworkers = [
84+
workers.value_()
85+
for workers in for_each_worker(self.prog)
86+
if workers.pool.value_() == test_pool
87+
]
88+
pool_kworkers = [
89+
workers.value_()
90+
for workers in for_each_pool_worker(
91+
per_cpu(self.prog["cpu_worker_pools"], 0)[0].address_of_()
92+
)
93+
]
94+
self.assertEqual(kworkers.sort(), pool_kworkers.sort())
95+
96+
def test_for_each_cpu_worker_pool(self):
97+
cpu0_worker_pools = [
98+
per_cpu(self.prog["cpu_worker_pools"], 0)[i].address_ for i in [0, 1]
99+
]
100+
worker_pools = [
101+
worker_pool.value_()
102+
for worker_pool in for_each_cpu_worker_pool(self.prog, 0)
103+
]
104+
self.assertEqual(worker_pools, cpu0_worker_pools)
105+
106+
def test_find_workqueue(self):
107+
workqueue_names = system_workqueue_names(self.prog)
108+
for name in workqueue_names:
109+
workqueue = find_workqueue(self.prog, name)
110+
self.assertEqual(name, workqueue.name.string_())
111+
112+
@skip_unless_have_test_kmod
113+
def test_for_each_pwq(self):
114+
# Since "drgn_test_wq" is a bound workqueue, list pwqs
115+
# should contain only per-cpu pwqs i.e cpu_pwqs
116+
pwqs = [pwq.value_() for pwq in for_each_pwq(self.test_wq)]
117+
cpu_pwqs = [
118+
per_cpu_ptr(self.test_wq.cpu_pwqs, cpu).value_()
119+
for cpu in for_each_online_cpu(self.prog)
120+
]
121+
self.assertEqual(pwqs.sort(), cpu_pwqs.sort())
122+
123+
@skip_unless_have_test_kmod
124+
def test_for_each_pending_work(self):
125+
all_works = [work.value_() for work in for_each_pending_work(self.prog)]
126+
self.assertGreaterEqual(all_works, self.test_works)
127+
128+
@skip_unless_have_test_kmod
129+
def test_for_each_pending_work_on_cpu(self):
130+
all_works = [
131+
work.value_() for work in for_each_pending_work_on_cpu(self.prog, 0)
132+
]
133+
self.assertGreaterEqual(all_works, self.test_works)
134+
135+
@skip_unless_have_test_kmod
136+
def test_for_each_pending_work_in_pool(self):
137+
pool = per_cpu(self.prog["cpu_worker_pools"], 0)[0].address_of_()
138+
all_works_in_pool = [
139+
work.value_() for work in for_each_pending_work_in_pool(pool)
140+
]
141+
self.assertGreaterEqual(all_works_in_pool, self.test_works)
142+
143+
@skip_unless_have_test_kmod
144+
def test_for_each_pending_work_of_pwq(self):
145+
cpu_pwqs_0 = per_cpu_ptr(self.test_wq.cpu_pwqs, 0)
146+
all_works_of_pwq = [
147+
work.value_() for work in for_each_pending_work_of_pwq(cpu_pwqs_0)
148+
]
149+
self.assertEqual(all_works_of_pwq, self.test_works)
150+
151+
@skip_unless_have_test_kmod
152+
def test_get_work_pwq(self):
153+
cpu_pwqs_0 = per_cpu_ptr(self.test_wq.cpu_pwqs, 0)
154+
cpu_pwqs_1 = per_cpu_ptr(self.test_wq.cpu_pwqs, 1)
155+
pwq = get_work_pwq(self.test_work_0)
156+
self.assertEqual(pwq, cpu_pwqs_0)
157+
self.assertNotEqual(pwq, cpu_pwqs_1)
158+
159+
@skip_unless_have_test_kmod
160+
def test_get_work_pool(self):
161+
pool = get_work_pool(self.test_work_0)
162+
pool_0 = per_cpu(self.prog["cpu_worker_pools"], 0)[0].address_of_()
163+
pool_1 = per_cpu(self.prog["cpu_worker_pools"], 1)[0].address_of_()
164+
self.assertEqual(pool, pool_0)
165+
self.assertNotEqual(pool, pool_1)
166+
167+
@skip_unless_have_test_kmod
168+
def test_find_worker_executing_work(self):
169+
worker = find_worker_executing_work(self.test_blocker_work)
170+
self.assertEqual(worker.current_work, self.test_blocker_work)

tests/linux_kernel/kmod/drgn_test.c

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
#include <linux/slab.h>
3434
#include <linux/vmalloc.h>
3535
#include <linux/wait.h>
36+
#include <linux/workqueue.h>
37+
#include <linux/delay.h>
3638
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
3739
#define HAVE_XARRAY 1
3840
#include <linux/xarray.h>
@@ -1070,6 +1072,90 @@ static void drgn_test_waitq_exit(void)
10701072
}
10711073
}
10721074

1075+
// workqeue
1076+
1077+
struct workqueue_struct *drgn_test_wq;
1078+
struct work_struct drgn_test_works[5];
1079+
struct work_struct drgn_test_blocker_work;
1080+
unsigned long drgn_test_skip_worker_loop = 1;
1081+
1082+
static int workqueue_set_test_trigger(const char *val,
1083+
const struct kernel_param *kp)
1084+
{
1085+
unsigned long trigger;
1086+
int ret, i;
1087+
1088+
ret = kstrtoul(val, 0, &trigger);
1089+
if (ret)
1090+
return ret;
1091+
1092+
if (trigger) {
1093+
ret = queue_work_on(0, drgn_test_wq, &drgn_test_blocker_work);
1094+
1095+
for (i = 0; i < 5; i++)
1096+
ret = queue_work_on(0, drgn_test_wq, &drgn_test_works[i]);
1097+
1098+
if (!ret)
1099+
drgn_test_skip_worker_loop = 0;
1100+
} else
1101+
drgn_test_skip_worker_loop = 0;
1102+
1103+
return 0;
1104+
}
1105+
1106+
static const struct kernel_param_ops workqueue_test_ops = {
1107+
.set = workqueue_set_test_trigger,
1108+
};
1109+
1110+
module_param_cb(workqueue_test_trigger, &workqueue_test_ops, &drgn_test_skip_worker_loop,
1111+
0644);
1112+
1113+
static void drgn_test_work_func(struct work_struct *data)
1114+
{
1115+
(void)data;
1116+
mdelay(5); //just busy loop for 5ms
1117+
return;
1118+
}
1119+
1120+
static void drgn_test_blocker_work_func(struct work_struct *data)
1121+
{
1122+
(void)data;
1123+
1124+
/*
1125+
* mdelay will keep busy looping so workqueue framework should
1126+
* not fork new workers for involved pool.
1127+
* module exit resets the loop variable and this in turn will complete
1128+
* the function. Subsequent destroy_workqueue from exit will orderly
1129+
* cleanup remaining works and destroy the test workqueue
1130+
*/
1131+
while(drgn_test_skip_worker_loop) {
1132+
mdelay(10);
1133+
cond_resched();
1134+
}
1135+
}
1136+
1137+
static int __init drgn_test_workqueue_init(void)
1138+
{
1139+
int i;
1140+
1141+
drgn_test_wq = alloc_workqueue("drgn_test_wq", 0, 0);
1142+
if (!drgn_test_wq)
1143+
return -ENOMEM;
1144+
1145+
INIT_WORK(&drgn_test_blocker_work, drgn_test_blocker_work_func);
1146+
1147+
for (i = 0; i < 5; i++)
1148+
INIT_WORK(&drgn_test_works[i], drgn_test_work_func);
1149+
1150+
return 0;
1151+
}
1152+
1153+
static void drgn_test_workqueue_exit(void)
1154+
{
1155+
drgn_test_skip_worker_loop = 0;
1156+
destroy_workqueue(drgn_test_wq);
1157+
}
1158+
10731159
// Dummy function symbol.
10741160
int drgn_test_function(int x)
10751161
{
@@ -1088,6 +1174,7 @@ static void drgn_test_exit(void)
10881174
drgn_test_xarray_exit();
10891175
drgn_test_waitq_exit();
10901176
drgn_test_idr_exit();
1177+
drgn_test_workqueue_exit();
10911178
}
10921179

10931180
static int __init drgn_test_init(void)
@@ -1126,6 +1213,9 @@ static int __init drgn_test_init(void)
11261213
if (ret)
11271214
goto out;
11281215
ret = drgn_test_idr_init();
1216+
if (ret)
1217+
goto out;
1218+
ret = drgn_test_workqueue_init();
11291219
out:
11301220
if (ret)
11311221
drgn_test_exit();

0 commit comments

Comments
 (0)