235 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * thp_swap_allocator_test4 *5 * The purpose of this test program is helping check if THP swpout6 * can correctly get swap slots to swap out as a whole instead of7 * being split. It randomly releases swap entries through madvise8 * DONTNEED and swapin/out on two memory areas: a memory area for9 * 64KB THP and the other area for small folios. The second memory10 * can be enabled by "-s".11 * Before running the program, we need to setup a zRAM or similar12 * swap device by:13 * echo lzo > /sys/block/zram0/comp_algorithm14 * echo 64M > /sys/block/zram0/disksize15 * echo never > /sys/kernel/mm/transparent_hugepage/hugepages-2048kB/enabled16 * echo always > /sys/kernel/mm/transparent_hugepage/hugepages-64kB/enabled17 * mkswap /dev/zram018 * swapon /dev/zram019 * The expected result should be 0% anon swpout fallback ratio w/ or20 * w/o "-s".21 *22 * Author(s): Barry Song <v-songbaohua@oppo.com>23 */24 25#define _GNU_SOURCE26#include <stdio.h>27#include <stdlib.h>28#include <unistd.h>29#include <string.h>30#include <linux/mman.h>31#include <sys/mman.h>32#include <errno.h>33#include <time.h>34 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)42 43#define SWPOUT_PATH \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"47 48static void *aligned_alloc_mem(size_t size, size_t alignment)49{50 void *mem = NULL;51 52 if (posix_memalign(&mem, alignment, size) != 0) {53 perror("posix_memalign");54 return NULL;55 }56 return mem;57}58 59/*60 * This emulates the behavior of native libc and Java heap,61 * as well as process exit and munmap. It helps generate mTHP62 * and ensures that iterations can proceed with mTHP, as we63 * currently don't support large folios swap-in.64 */65static void random_madvise_dontneed(void *mem, size_t mem_size,66 size_t align_size, size_t total_dontneed_size)67{68 size_t num_pages = total_dontneed_size / align_size;69 size_t i;70 size_t offset;71 void *addr;72 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");78 79 memset(addr, 0x11, align_size);80 }81}82 83static void random_swapin(void *mem, size_t mem_size,84 size_t align_size, size_t total_swapin_size)85{86 size_t num_pages = total_swapin_size / align_size;87 size_t i;88 size_t offset;89 void *addr;90 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);95 }96}97 98static unsigned long read_stat(const char *path)99{100 FILE *file;101 unsigned long value;102 103 file = fopen(path, "r");104 if (!file) {105 perror("fopen");106 return 0;107 }108 109 if (fscanf(file, "%lu", &value) != 1) {110 perror("fscanf");111 fclose(file);112 return 0;113 }114 115 fclose(file);116 return value;117}118 119int main(int argc, char *argv[])120{121 int use_small_folio = 0, aligned_swapin = 0;122 void *mem1 = NULL, *mem2 = NULL;123 int i;124 125 for (i = 1; i < argc; ++i) {126 if (strcmp(argv[i], "-s") == 0)127 use_small_folio = 1;128 else if (strcmp(argv[i], "-a") == 0)129 aligned_swapin = 1;130 }131 132 mem1 = aligned_alloc_mem(MEMSIZE_MTHP, ALIGNMENT_MTHP);133 if (mem1 == NULL) {134 fprintf(stderr, "Failed to allocate large folios memory\n");135 return EXIT_FAILURE;136 }137 138 if (madvise(mem1, MEMSIZE_MTHP, MADV_HUGEPAGE) != 0) {139 perror("madvise hugepage for mem1");140 free(mem1);141 return EXIT_FAILURE;142 }143 144 if (use_small_folio) {145 mem2 = aligned_alloc_mem(MEMSIZE_SMALLFOLIO, ALIGNMENT_MTHP);146 if (mem2 == NULL) {147 fprintf(stderr, "Failed to allocate small folios memory\n");148 free(mem1);149 return EXIT_FAILURE;150 }151 152 if (madvise(mem2, MEMSIZE_SMALLFOLIO, MADV_NOHUGEPAGE) != 0) {153 perror("madvise nohugepage for mem2");154 free(mem1);155 free(mem2);156 return EXIT_FAILURE;157 }158 }159 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);166 }167 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;177 178 initial_swpout = read_stat(SWPOUT_PATH);179 initial_swpout_fallback = read_stat(SWPOUT_FALLBACK_PATH);180 181 /*182 * The following setup creates a 1:1 ratio of mTHP to small folios183 * since large folio swap-in isn't supported yet. Once we support184 * mTHP swap-in, we'll likely need to reduce MEMSIZE_MTHP and185 * increase MEMSIZE_SMALLFOLIO to maintain the ratio.186 */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);192 193 if (use_small_folio) {194 random_swapin(mem2, MEMSIZE_SMALLFOLIO,195 ALIGNMENT_SMALLFOLIO,196 TOTAL_DONTNEED_SMALLFOLIO);197 }198 199 if (madvise(mem1, MEMSIZE_MTHP, MADV_PAGEOUT) != 0) {200 perror("madvise pageout for mem1");201 free(mem1);202 if (mem2 != NULL)203 free(mem2);204 return EXIT_FAILURE;205 }206 207 if (use_small_folio) {208 if (madvise(mem2, MEMSIZE_SMALLFOLIO, MADV_PAGEOUT) != 0) {209 perror("madvise pageout for mem2");210 free(mem1);211 free(mem2);212 return EXIT_FAILURE;213 }214 }215 216 final_swpout = read_stat(SWPOUT_PATH);217 final_swpout_fallback = read_stat(SWPOUT_FALLBACK_PATH);218 219 swpout_inc = final_swpout - initial_swpout;220 swpout_fallback_inc = final_swpout_fallback - initial_swpout_fallback;221 222 fallback_percentage = (double)swpout_fallback_inc /223 (swpout_fallback_inc + swpout_inc) * 100;224 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);227 }228 229 free(mem1);230 if (mem2 != NULL)231 free(mem2);232 233 return EXIT_SUCCESS;234}235