1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
// SPDX-License-Identifier: GPL-2.0
/*
* Self-check for the vm_util folio-order helpers, is_backed_by_folio() and
* is_range_backed_by_order(), which the khugepaged mTHP cases use to detect
* collapse results. For every anon THP order the kernel supports, fault
* memory in with only that order enabled and require the helpers to report
* exactly that order.
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <unistd.h>
#include "kselftest.h"
#include "vm_util.h"
#include <mm/hugepage_settings.h>
static int pagemap_fd;
static int kpageflags_fd;
static char *alloc_aligned(size_t size)
{
size_t len = size * 2;
char *p, *aligned;
p = mmap(NULL, len, PROT_READ | PROT_WRITE,
MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
if (p == MAP_FAILED)
ksft_exit_fail_perror("mmap()");
aligned = (char *)ALIGN((uintptr_t)p, size);
if (aligned != p)
munmap(p, aligned - p);
if (aligned + size != p + len)
munmap(aligned + size, p + len - aligned - size);
return aligned;
}
static void check_order(int order)
{
struct thp_settings settings = *thp_current_settings();
size_t size = psize() << order;
bool ok = true;
char *p;
int i;
for (i = 0; i < NR_ORDERS; i++)
settings.hugepages[i].enabled = THP_NEVER;
if (order)
settings.hugepages[order].enabled = THP_ALWAYS;
thp_push_settings(&settings);
p = alloc_aligned(size);
*p = 1;
if (!is_range_backed_by_order(p, size, order, pagemap_fd, kpageflags_fd)) {
ksft_print_msg("order %d not detected after fault\n", order);
ok = false;
}
/* A lower order must be rejected: the folio is larger */
if (order && is_range_backed_by_order(p, size, order - 1,
pagemap_fd, kpageflags_fd)) {
ksft_print_msg("order %d also reported as order %d\n",
order, order - 1);
ok = false;
}
/* A large folio must not pass as order 0 */
if (order && is_range_backed_by_order(p, size, 0,
pagemap_fd, kpageflags_fd)) {
ksft_print_msg("order %d also reported as order 0\n", order);
ok = false;
}
munmap(p, size);
thp_pop_settings();
ksft_test_result(ok, "order %d classified\n", order);
}
int main(void)
{
struct thp_settings settings;
unsigned long orders;
int order;
ksft_print_header();
if (!thp_available())
ksft_exit_skip("Transparent Hugepages not available\n");
pagemap_fd = open("/proc/self/pagemap", O_RDONLY);
if (pagemap_fd < 0)
ksft_exit_fail_perror("open(/proc/self/pagemap)");
kpageflags_fd = open("/proc/kpageflags", O_RDONLY);
if (kpageflags_fd < 0)
ksft_exit_skip("open(/proc/kpageflags) requires root\n");
orders = thp_supported_orders();
if (!orders)
ksft_exit_skip("No supported THP orders\n");
ksft_set_plan(__builtin_popcountl(orders) + 1);
thp_save_settings();
thp_read_settings(&settings);
/* Base of the settings stack; the bottom entry is never popped */
thp_push_settings(&settings);
check_order(0);
for (order = 1; order < NR_ORDERS; order++) {
if (!(orders & (1UL << order)))
continue;
check_order(order);
}
ksft_finished();
}
|