Skip to content

Commit 7a0033d

Browse files
laoarintel-lab-lkp
authored andcommitted
selftests/bpf: Add selftest for THP adjustment
This test case uses a BPF program to enforce the following THP allocation policy: - Current task will wakeup khugepaged to allocate THP The result is as follows, $ ./test_progs --name="thp_adjust" torvalds#437 thp_adjust:OK Summary: 1/0 PASSED, 0 SKIPPED, 0 FAILED CONFIG_TRANSPARENT_HUGEPAGE=y is required for this test. Signed-off-by: Yafang Shao <laoar.shao@gmail.com>
1 parent b4ff815 commit 7a0033d

File tree

3 files changed

+197
-0
lines changed

3 files changed

+197
-0
lines changed

tools/testing/selftests/bpf/config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,3 +118,4 @@ CONFIG_XDP_SOCKETS=y
118118
CONFIG_XFRM_INTERFACE=y
119119
CONFIG_TCP_CONG_DCTCP=y
120120
CONFIG_TCP_CONG_BBR=y
121+
CONFIG_TRANSPARENT_HUGEPAGE=y
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include <sys/mman.h>
4+
#include <test_progs.h>
5+
#include "test_thp_adjust.skel.h"
6+
7+
#define LEN (4 * 1024 * 1024) /* 4MB */
8+
#define THP_ENABLED_PATH "/sys/kernel/mm/transparent_hugepage/enabled"
9+
#define SMAPS_PATH "/proc/self/smaps"
10+
#define ANON_HUGE_PAGES "AnonHugePages:"
11+
12+
static char *thp_addr;
13+
static char old_mode[32];
14+
15+
int thp_mode_save(void)
16+
{
17+
const char *start, *end;
18+
char buf[128];
19+
int fd, err;
20+
size_t len;
21+
22+
fd = open(THP_ENABLED_PATH, O_RDONLY);
23+
if (fd == -1)
24+
return -1;
25+
26+
err = read(fd, buf, sizeof(buf) - 1);
27+
if (err == -1)
28+
goto close;
29+
30+
start = strchr(buf, '[');
31+
end = start ? strchr(start, ']') : NULL;
32+
if (!start || !end || end <= start) {
33+
err = -1;
34+
goto close;
35+
}
36+
37+
len = end - start - 1;
38+
if (len >= sizeof(old_mode))
39+
len = sizeof(old_mode) - 1;
40+
strncpy(old_mode, start + 1, len);
41+
old_mode[len] = '\0';
42+
43+
close:
44+
close(fd);
45+
return err;
46+
}
47+
48+
int thp_set(const char *desired_mode)
49+
{
50+
int fd, err;
51+
52+
fd = open(THP_ENABLED_PATH, O_RDWR);
53+
if (fd == -1)
54+
return -1;
55+
56+
err = write(fd, desired_mode, strlen(desired_mode));
57+
close(fd);
58+
return err;
59+
}
60+
61+
int thp_reset(void)
62+
{
63+
int fd, err;
64+
65+
fd = open(THP_ENABLED_PATH, O_WRONLY);
66+
if (fd == -1)
67+
return -1;
68+
69+
err = write(fd, old_mode, strlen(old_mode));
70+
close(fd);
71+
return err;
72+
}
73+
74+
int thp_alloc(void)
75+
{
76+
int err, i;
77+
78+
thp_addr = mmap(NULL, LEN, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
79+
if (thp_addr == MAP_FAILED)
80+
return -1;
81+
82+
for (i = 0; i < LEN; i += 4096)
83+
thp_addr[i] = 1;
84+
85+
err = madvise(thp_addr, LEN, MADV_HUGEPAGE);
86+
if (err == -1)
87+
goto unmap;
88+
return 0;
89+
90+
unmap:
91+
munmap(thp_addr, LEN);
92+
return -1;
93+
}
94+
95+
void thp_free(void)
96+
{
97+
if (!thp_addr)
98+
return;
99+
munmap(thp_addr, LEN);
100+
}
101+
102+
void test_thp_adjust(void)
103+
{
104+
struct bpf_link *fentry_link, *ops_link;
105+
struct test_thp_adjust *skel;
106+
int err, first_calls;
107+
108+
if (!ASSERT_NEQ(thp_mode_save(), -1, "THP mode save"))
109+
return;
110+
if (!ASSERT_GE(thp_set("madvise"), 0, "THP mode set"))
111+
return;
112+
113+
skel = test_thp_adjust__open();
114+
if (!ASSERT_OK_PTR(skel, "open"))
115+
goto thp_reset;
116+
117+
skel->bss->target_pid = getpid();
118+
119+
err = test_thp_adjust__load(skel);
120+
if (!ASSERT_OK(err, "load"))
121+
goto destroy;
122+
123+
fentry_link = bpf_program__attach_trace(skel->progs.thp_run);
124+
if (!ASSERT_OK_PTR(fentry_link, "attach fentry"))
125+
goto destroy;
126+
127+
ops_link = bpf_map__attach_struct_ops(skel->maps.thp);
128+
if (!ASSERT_OK_PTR(ops_link, "attach struct_ops"))
129+
goto destroy;
130+
131+
if (!ASSERT_NEQ(thp_alloc(), -1, "THP alloc"))
132+
goto destroy;
133+
134+
/* After attaching struct_ops, THP will be allocated. */
135+
if (!ASSERT_GT(skel->bss->khugepaged_enter, 0, "khugepaged enter"))
136+
goto thp_free;
137+
138+
first_calls = skel->bss->khugepaged_enter;
139+
140+
thp_free();
141+
142+
if (!ASSERT_GE(thp_set("never"), 0, "THP set"))
143+
goto destroy;
144+
145+
if (!ASSERT_NEQ(thp_alloc(), -1, "THP alloc"))
146+
goto destroy;
147+
148+
/* In "never" mode, THP won't be allocated even if the prog is attached. */
149+
if (!ASSERT_EQ(skel->bss->khugepaged_enter, first_calls, "khugepaged enter"))
150+
goto thp_free;
151+
152+
thp_free:
153+
thp_free();
154+
destroy:
155+
test_thp_adjust__destroy(skel);
156+
thp_reset:
157+
ASSERT_GE(thp_reset(), 0, "THP mode reset");
158+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
#include "vmlinux.h"
4+
#include <bpf/bpf_helpers.h>
5+
#include <bpf/bpf_tracing.h>
6+
7+
char _license[] SEC("license") = "GPL";
8+
9+
#define THP_ALLOC_KHUGEPAGED (1<<1)
10+
11+
int target_pid;
12+
int khugepaged_enter;
13+
14+
SEC("fentry/__khugepaged_enter")
15+
int BPF_PROG(thp_run, struct mm_struct *mm)
16+
{
17+
struct task_struct *current = bpf_get_current_task_btf();
18+
19+
if (current->mm == mm && current->pid == target_pid)
20+
khugepaged_enter++;
21+
return 0;
22+
}
23+
24+
SEC("struct_ops/allocator")
25+
int BPF_PROG(bpf_thp_allocator)
26+
{
27+
struct task_struct *current = bpf_get_current_task_btf();
28+
29+
/* Allocate THP for this task in khugepaged. */
30+
if (current->pid == target_pid)
31+
return THP_ALLOC_KHUGEPAGED;
32+
return 0;
33+
}
34+
35+
SEC(".struct_ops.link")
36+
struct bpf_thp_ops thp = {
37+
.allocator = (void *)bpf_thp_allocator,
38+
};

0 commit comments

Comments
 (0)