Skip to content

Commit 093cd45

Browse files
laoarKernel Patches Daemon
authored andcommitted
selftests/bpf: add a simple BPF based THP policy
This test case implements a basic THP policy that sets THPeligible to 0 for a specific task. I selected THPeligible for verification because its straightforward nature makes it ideal for validating the BPF THP policy functionality. Below configs must be enabled for this test: CONFIG_BPF_MM=y CONFIG_BPF_THP=y CONFIG_TRANSPARENT_HUGEPAGE=y Signed-off-by: Yafang Shao <[email protected]>
1 parent cf0f715 commit 093cd45

File tree

4 files changed

+274
-0
lines changed

4 files changed

+274
-0
lines changed

MAINTAINERS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16524,6 +16524,8 @@ F: mm/huge_memory.c
1652416524
F: mm/huge_memory_bpf.c
1652516525
F: mm/khugepaged.c
1652616526
F: mm/mm_slot.h
16527+
F: tools/testing/selftests/bpf/prog_tests/thp_adjust.c
16528+
F: tools/testing/selftests/bpf/progs/test_thp_adjust*
1652716529
F: tools/testing/selftests/mm/khugepaged.c
1652816530
F: tools/testing/selftests/mm/split_huge_page_test.c
1652916531
F: tools/testing/selftests/mm/transhuge-stress.c

tools/testing/selftests/bpf/config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ CONFIG_BPF_JIT=y
77
CONFIG_BPF_KPROBE_OVERRIDE=y
88
CONFIG_BPF_LIRC_MODE2=y
99
CONFIG_BPF_LSM=y
10+
CONFIG_BPF_MM=y
1011
CONFIG_BPF_STREAM_PARSER=y
1112
CONFIG_BPF_SYSCALL=y
13+
CONFIG_BPF_THP=y
1214
# CONFIG_BPF_UNPRIV_DEFAULT_OFF is not set
1315
CONFIG_CGROUP_BPF=y
1416
CONFIG_CRYPTO_HMAC=y
@@ -115,6 +117,7 @@ CONFIG_SECURITY=y
115117
CONFIG_SECURITYFS=y
116118
CONFIG_SYN_COOKIES=y
117119
CONFIG_TEST_BPF=m
120+
CONFIG_TRANSPARENT_HUGEPAGE=y
118121
CONFIG_UDMABUF=y
119122
CONFIG_USERFAULTFD=y
120123
CONFIG_VSOCKETS=y
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
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 (16 * 1024 * 1024) /* 16MB */
8+
#define THP_ENABLED_FILE "/sys/kernel/mm/transparent_hugepage/enabled"
9+
#define PMD_SIZE_FILE "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size"
10+
11+
static struct test_thp_adjust *skel;
12+
static char old_mode[32];
13+
static long pagesize;
14+
15+
static 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_FILE, 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+
static int thp_mode_set(const char *desired_mode)
49+
{
50+
int fd, err;
51+
52+
fd = open(THP_ENABLED_FILE, 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+
static int thp_mode_reset(void)
62+
{
63+
int fd, err;
64+
65+
fd = open(THP_ENABLED_FILE, 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+
static char *thp_alloc(void)
75+
{
76+
char *addr;
77+
int err, i;
78+
79+
addr = mmap(NULL, LEN, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
80+
if (addr == MAP_FAILED)
81+
return NULL;
82+
83+
err = madvise(addr, LEN, MADV_HUGEPAGE);
84+
if (err == -1)
85+
goto unmap;
86+
87+
/* Accessing a single byte within a page is sufficient to trigger a page fault. */
88+
for (i = 0; i < LEN; i += pagesize)
89+
addr[i] = 1;
90+
return addr;
91+
92+
unmap:
93+
munmap(addr, LEN);
94+
return NULL;
95+
}
96+
97+
static void thp_free(char *ptr)
98+
{
99+
munmap(ptr, LEN);
100+
}
101+
102+
static int get_pmd_order(void)
103+
{
104+
ssize_t bytes_read, size;
105+
int fd, order, ret = -1;
106+
char buf[64], *endptr;
107+
108+
fd = open(PMD_SIZE_FILE, O_RDONLY);
109+
if (fd < 0)
110+
return -1;
111+
112+
bytes_read = read(fd, buf, sizeof(buf) - 1);
113+
if (bytes_read <= 0)
114+
goto close_fd;
115+
116+
/* Remove potential newline character */
117+
if (buf[bytes_read - 1] == '\n')
118+
buf[bytes_read - 1] = '\0';
119+
120+
size = strtoul(buf, &endptr, 10);
121+
if (endptr == buf || *endptr != '\0')
122+
goto close_fd;
123+
if (size % pagesize != 0)
124+
goto close_fd;
125+
ret = size / pagesize;
126+
if ((ret & (ret - 1)) == 0) {
127+
order = 0;
128+
while (ret > 1) {
129+
ret >>= 1;
130+
order++;
131+
}
132+
ret = order;
133+
}
134+
135+
close_fd:
136+
close(fd);
137+
return ret;
138+
}
139+
140+
static int get_thp_eligible(pid_t pid, unsigned long addr)
141+
{
142+
int this_vma = 0, eligible = -1;
143+
unsigned long start, end;
144+
char smaps_path[64];
145+
FILE *smaps_file;
146+
char line[4096];
147+
148+
snprintf(smaps_path, sizeof(smaps_path), "/proc/%d/smaps", pid);
149+
smaps_file = fopen(smaps_path, "r");
150+
if (!smaps_file)
151+
return -1;
152+
153+
while (fgets(line, sizeof(line), smaps_file)) {
154+
if (sscanf(line, "%lx-%lx", &start, &end) == 2) {
155+
/* addr is monotonic */
156+
if (addr < start)
157+
break;
158+
this_vma = (addr >= start && addr < end) ? 1 : 0;
159+
continue;
160+
}
161+
162+
if (!this_vma)
163+
continue;
164+
165+
if (strstr(line, "THPeligible:")) {
166+
sscanf(line, "THPeligible: %d", &eligible);
167+
break;
168+
}
169+
}
170+
171+
fclose(smaps_file);
172+
return eligible;
173+
}
174+
175+
static void subtest_thp_eligible(void)
176+
{
177+
struct bpf_link *ops_link;
178+
int elighble;
179+
char *ptr;
180+
181+
ops_link = bpf_map__attach_struct_ops(skel->maps.thp_eligible_ops);
182+
if (!ASSERT_OK_PTR(ops_link, "attach struct_ops"))
183+
return;
184+
185+
ptr = thp_alloc();
186+
if (!ASSERT_OK_PTR(ptr, "THP alloc"))
187+
goto detach;
188+
189+
elighble = get_thp_eligible(getpid(), (unsigned long)ptr);
190+
ASSERT_EQ(elighble, 0, "THPeligible");
191+
192+
thp_free(ptr);
193+
detach:
194+
bpf_link__destroy(ops_link);
195+
}
196+
197+
static int thp_adjust_setup(void)
198+
{
199+
int err = -1, pmd_order;
200+
201+
pagesize = sysconf(_SC_PAGESIZE);
202+
pmd_order = get_pmd_order();
203+
if (!ASSERT_NEQ(pmd_order, -1, "get_pmd_order"))
204+
return -1;
205+
206+
if (!ASSERT_NEQ(thp_mode_save(), -1, "THP mode save"))
207+
return -1;
208+
if (!ASSERT_GE(thp_mode_set("madvise"), 0, "THP mode set"))
209+
return -1;
210+
211+
skel = test_thp_adjust__open();
212+
if (!ASSERT_OK_PTR(skel, "open"))
213+
goto thp_reset;
214+
215+
skel->bss->pmd_order = pmd_order;
216+
skel->struct_ops.thp_eligible_ops->pid = getpid();
217+
218+
err = test_thp_adjust__load(skel);
219+
if (!ASSERT_OK(err, "load"))
220+
goto destroy;
221+
return 0;
222+
223+
destroy:
224+
test_thp_adjust__destroy(skel);
225+
thp_reset:
226+
ASSERT_GE(thp_mode_reset(), 0, "THP mode reset");
227+
return err;
228+
}
229+
230+
static void thp_adjust_destroy(void)
231+
{
232+
test_thp_adjust__destroy(skel);
233+
ASSERT_GE(thp_mode_reset(), 0, "THP mode reset");
234+
}
235+
236+
void test_thp_adjust(void)
237+
{
238+
if (thp_adjust_setup() == -1)
239+
return;
240+
241+
if (test__start_subtest("thp_eligible"))
242+
subtest_thp_eligible();
243+
244+
thp_adjust_destroy();
245+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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+
int pmd_order;
10+
11+
SEC("struct_ops/thp_get_order")
12+
int BPF_PROG(thp_not_eligible, struct vm_area_struct *vma, enum tva_type type,
13+
unsigned long orders)
14+
{
15+
/* THPeligible in /proc/pid/smaps is 0 */
16+
if (type == TVA_SMAPS)
17+
return 0;
18+
return pmd_order;
19+
}
20+
21+
SEC(".struct_ops.link")
22+
struct bpf_thp_ops thp_eligible_ops = {
23+
.thp_get_order = (void *)thp_not_eligible,
24+
};

0 commit comments

Comments
 (0)