1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * thp_swap_allocator_test
5 * The purpose of this test program is helping check if THP swpout
6 * can correctly get swap slots to swap out as a whole instead of
7 * being split. It randomly releases swap entries through madvise
8 * DONTNEED and swapin/out on two memory areas: a memory area for
9 * 64KB THP and the other area for small folios. The second memory
10 * can be enabled by "-s".
11 * Before running the program, we need to setup a zRAM or similar
13 * echo lzo > /sys/block/zram0/comp_algorithm
14 * echo 64M > /sys/block/zram0/disksize
15 * echo never > /sys/kernel/mm/transparent_hugepage/hugepages-2048kB/enabled
16 * echo always > /sys/kernel/mm/transparent_hugepage/hugepages-64kB/enabled
19 * The expected result should be 0% anon swpout fallback ratio w/ or
22 * Author(s): Barry Song <v-songbaohua@oppo.com>
30 #include <linux/mman.h>
35 #define MEMSIZE_MTHP (60 * 1024 * 1024)
36 #define MEMSIZE_SMALLFOLIO (4 * 1024 * 1024)
37 #define ALIGNMENT_MTHP (64 * 1024)
38 #define ALIGNMENT_SMALLFOLIO (4 * 1024)
39 #define TOTAL_DONTNEED_MTHP (16 * 1024 * 1024)
40 #define TOTAL_DONTNEED_SMALLFOLIO (1 * 1024 * 1024)
41 #define MTHP_FOLIO_SIZE (64 * 1024)
44 "/sys/kernel/mm/transparent_hugepage/hugepages-64kB/stats/swpout"
45 #define SWPOUT_FALLBACK_PATH \
46 "/sys/kernel/mm/transparent_hugepage/hugepages-64kB/stats/swpout_fallback"
48 static void *aligned_alloc_mem(size_t size
, size_t alignment
)
52 if (posix_memalign(&mem
, alignment
, size
) != 0) {
53 perror("posix_memalign");
60 * This emulates the behavior of native libc and Java heap,
61 * as well as process exit and munmap. It helps generate mTHP
62 * and ensures that iterations can proceed with mTHP, as we
63 * currently don't support large folios swap-in.
65 static void random_madvise_dontneed(void *mem
, size_t mem_size
,
66 size_t align_size
, size_t total_dontneed_size
)
68 size_t num_pages
= total_dontneed_size
/ align_size
;
73 for (i
= 0; i
< num_pages
; ++i
) {
74 offset
= (rand() % (mem_size
/ align_size
)) * align_size
;
75 addr
= (char *)mem
+ offset
;
76 if (madvise(addr
, align_size
, MADV_DONTNEED
) != 0)
77 perror("madvise dontneed");
79 memset(addr
, 0x11, align_size
);
83 static void random_swapin(void *mem
, size_t mem_size
,
84 size_t align_size
, size_t total_swapin_size
)
86 size_t num_pages
= total_swapin_size
/ align_size
;
91 for (i
= 0; i
< num_pages
; ++i
) {
92 offset
= (rand() % (mem_size
/ align_size
)) * align_size
;
93 addr
= (char *)mem
+ offset
;
94 memset(addr
, 0x11, align_size
);
98 static unsigned long read_stat(const char *path
)
103 file
= fopen(path
, "r");
109 if (fscanf(file
, "%lu", &value
) != 1) {
119 int main(int argc
, char *argv
[])
121 int use_small_folio
= 0, aligned_swapin
= 0;
122 void *mem1
= NULL
, *mem2
= NULL
;
125 for (i
= 1; i
< argc
; ++i
) {
126 if (strcmp(argv
[i
], "-s") == 0)
128 else if (strcmp(argv
[i
], "-a") == 0)
132 mem1
= aligned_alloc_mem(MEMSIZE_MTHP
, ALIGNMENT_MTHP
);
134 fprintf(stderr
, "Failed to allocate large folios memory\n");
138 if (madvise(mem1
, MEMSIZE_MTHP
, MADV_HUGEPAGE
) != 0) {
139 perror("madvise hugepage for mem1");
144 if (use_small_folio
) {
145 mem2
= aligned_alloc_mem(MEMSIZE_SMALLFOLIO
, ALIGNMENT_MTHP
);
147 fprintf(stderr
, "Failed to allocate small folios memory\n");
152 if (madvise(mem2
, MEMSIZE_SMALLFOLIO
, MADV_NOHUGEPAGE
) != 0) {
153 perror("madvise nohugepage for mem2");
160 /* warm-up phase to occupy the swapfile */
161 memset(mem1
, 0x11, MEMSIZE_MTHP
);
162 madvise(mem1
, MEMSIZE_MTHP
, MADV_PAGEOUT
);
163 if (use_small_folio
) {
164 memset(mem2
, 0x11, MEMSIZE_SMALLFOLIO
);
165 madvise(mem2
, MEMSIZE_SMALLFOLIO
, MADV_PAGEOUT
);
168 /* iterations with newly created mTHP, swap-in, and swap-out */
169 for (i
= 0; i
< 100; ++i
) {
170 unsigned long initial_swpout
;
171 unsigned long initial_swpout_fallback
;
172 unsigned long final_swpout
;
173 unsigned long final_swpout_fallback
;
174 unsigned long swpout_inc
;
175 unsigned long swpout_fallback_inc
;
176 double fallback_percentage
;
178 initial_swpout
= read_stat(SWPOUT_PATH
);
179 initial_swpout_fallback
= read_stat(SWPOUT_FALLBACK_PATH
);
182 * The following setup creates a 1:1 ratio of mTHP to small folios
183 * since large folio swap-in isn't supported yet. Once we support
184 * mTHP swap-in, we'll likely need to reduce MEMSIZE_MTHP and
185 * increase MEMSIZE_SMALLFOLIO to maintain the ratio.
187 random_swapin(mem1
, MEMSIZE_MTHP
,
188 aligned_swapin
? ALIGNMENT_MTHP
: ALIGNMENT_SMALLFOLIO
,
189 TOTAL_DONTNEED_MTHP
);
190 random_madvise_dontneed(mem1
, MEMSIZE_MTHP
, ALIGNMENT_MTHP
,
191 TOTAL_DONTNEED_MTHP
);
193 if (use_small_folio
) {
194 random_swapin(mem2
, MEMSIZE_SMALLFOLIO
,
195 ALIGNMENT_SMALLFOLIO
,
196 TOTAL_DONTNEED_SMALLFOLIO
);
199 if (madvise(mem1
, MEMSIZE_MTHP
, MADV_PAGEOUT
) != 0) {
200 perror("madvise pageout for mem1");
207 if (use_small_folio
) {
208 if (madvise(mem2
, MEMSIZE_SMALLFOLIO
, MADV_PAGEOUT
) != 0) {
209 perror("madvise pageout for mem2");
216 final_swpout
= read_stat(SWPOUT_PATH
);
217 final_swpout_fallback
= read_stat(SWPOUT_FALLBACK_PATH
);
219 swpout_inc
= final_swpout
- initial_swpout
;
220 swpout_fallback_inc
= final_swpout_fallback
- initial_swpout_fallback
;
222 fallback_percentage
= (double)swpout_fallback_inc
/
223 (swpout_fallback_inc
+ swpout_inc
) * 100;
225 printf("Iteration %d: swpout inc: %lu, swpout fallback inc: %lu, Fallback percentage: %.2f%%\n",
226 i
+ 1, swpout_inc
, swpout_fallback_inc
, fallback_percentage
);