| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- // SPDX-License-Identifier: GPL-2.0-or-later
- /*
- * thp_swap_allocator_test
- *
- * The purpose of this test program is helping check if THP swpout
- * can correctly get swap slots to swap out as a whole instead of
- * being split. It randomly releases swap entries through madvise
- * DONTNEED and swapin/out on two memory areas: a memory area for
- * 64KB THP and the other area for small folios. The second memory
- * can be enabled by "-s".
- * Before running the program, we need to setup a zRAM or similar
- * swap device by:
- * echo lzo > /sys/block/zram0/comp_algorithm
- * echo 64M > /sys/block/zram0/disksize
- * echo never > /sys/kernel/mm/transparent_hugepage/hugepages-2048kB/enabled
- * echo always > /sys/kernel/mm/transparent_hugepage/hugepages-64kB/enabled
- * mkswap /dev/zram0
- * swapon /dev/zram0
- * The expected result should be 0% anon swpout fallback ratio w/ or
- * w/o "-s".
- *
- * Author(s): Barry Song <v-songbaohua@oppo.com>
- */
- #define _GNU_SOURCE
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <linux/mman.h>
- #include <sys/mman.h>
- #include <errno.h>
- #include <time.h>
- #define MEMSIZE_MTHP (60 * 1024 * 1024)
- #define MEMSIZE_SMALLFOLIO (4 * 1024 * 1024)
- #define ALIGNMENT_MTHP (64 * 1024)
- #define ALIGNMENT_SMALLFOLIO (4 * 1024)
- #define TOTAL_DONTNEED_MTHP (16 * 1024 * 1024)
- #define TOTAL_DONTNEED_SMALLFOLIO (1 * 1024 * 1024)
- #define MTHP_FOLIO_SIZE (64 * 1024)
- #define SWPOUT_PATH \
- "/sys/kernel/mm/transparent_hugepage/hugepages-64kB/stats/swpout"
- #define SWPOUT_FALLBACK_PATH \
- "/sys/kernel/mm/transparent_hugepage/hugepages-64kB/stats/swpout_fallback"
- static void *aligned_alloc_mem(size_t size, size_t alignment)
- {
- void *mem = NULL;
- if (posix_memalign(&mem, alignment, size) != 0) {
- perror("posix_memalign");
- return NULL;
- }
- return mem;
- }
- /*
- * This emulates the behavior of native libc and Java heap,
- * as well as process exit and munmap. It helps generate mTHP
- * and ensures that iterations can proceed with mTHP, as we
- * currently don't support large folios swap-in.
- */
- static void random_madvise_dontneed(void *mem, size_t mem_size,
- size_t align_size, size_t total_dontneed_size)
- {
- size_t num_pages = total_dontneed_size / align_size;
- size_t i;
- size_t offset;
- void *addr;
- for (i = 0; i < num_pages; ++i) {
- offset = (rand() % (mem_size / align_size)) * align_size;
- addr = (char *)mem + offset;
- if (madvise(addr, align_size, MADV_DONTNEED) != 0)
- perror("madvise dontneed");
- memset(addr, 0x11, align_size);
- }
- }
- static void random_swapin(void *mem, size_t mem_size,
- size_t align_size, size_t total_swapin_size)
- {
- size_t num_pages = total_swapin_size / align_size;
- size_t i;
- size_t offset;
- void *addr;
- for (i = 0; i < num_pages; ++i) {
- offset = (rand() % (mem_size / align_size)) * align_size;
- addr = (char *)mem + offset;
- memset(addr, 0x11, align_size);
- }
- }
- static unsigned long read_stat(const char *path)
- {
- FILE *file;
- unsigned long value;
- file = fopen(path, "r");
- if (!file) {
- perror("fopen");
- return 0;
- }
- if (fscanf(file, "%lu", &value) != 1) {
- perror("fscanf");
- fclose(file);
- return 0;
- }
- fclose(file);
- return value;
- }
- int main(int argc, char *argv[])
- {
- int use_small_folio = 0, aligned_swapin = 0;
- void *mem1 = NULL, *mem2 = NULL;
- int i;
- for (i = 1; i < argc; ++i) {
- if (strcmp(argv[i], "-s") == 0)
- use_small_folio = 1;
- else if (strcmp(argv[i], "-a") == 0)
- aligned_swapin = 1;
- }
- mem1 = aligned_alloc_mem(MEMSIZE_MTHP, ALIGNMENT_MTHP);
- if (mem1 == NULL) {
- fprintf(stderr, "Failed to allocate large folios memory\n");
- return EXIT_FAILURE;
- }
- if (madvise(mem1, MEMSIZE_MTHP, MADV_HUGEPAGE) != 0) {
- perror("madvise hugepage for mem1");
- free(mem1);
- return EXIT_FAILURE;
- }
- if (use_small_folio) {
- mem2 = aligned_alloc_mem(MEMSIZE_SMALLFOLIO, ALIGNMENT_SMALLFOLIO);
- if (mem2 == NULL) {
- fprintf(stderr, "Failed to allocate small folios memory\n");
- free(mem1);
- return EXIT_FAILURE;
- }
- if (madvise(mem2, MEMSIZE_SMALLFOLIO, MADV_NOHUGEPAGE) != 0) {
- perror("madvise nohugepage for mem2");
- free(mem1);
- free(mem2);
- return EXIT_FAILURE;
- }
- }
- /* warm-up phase to occupy the swapfile */
- memset(mem1, 0x11, MEMSIZE_MTHP);
- madvise(mem1, MEMSIZE_MTHP, MADV_PAGEOUT);
- if (use_small_folio) {
- memset(mem2, 0x11, MEMSIZE_SMALLFOLIO);
- madvise(mem2, MEMSIZE_SMALLFOLIO, MADV_PAGEOUT);
- }
- /* iterations with newly created mTHP, swap-in, and swap-out */
- for (i = 0; i < 100; ++i) {
- unsigned long initial_swpout;
- unsigned long initial_swpout_fallback;
- unsigned long final_swpout;
- unsigned long final_swpout_fallback;
- unsigned long swpout_inc;
- unsigned long swpout_fallback_inc;
- double fallback_percentage;
- initial_swpout = read_stat(SWPOUT_PATH);
- initial_swpout_fallback = read_stat(SWPOUT_FALLBACK_PATH);
- /*
- * The following setup creates a 1:1 ratio of mTHP to small folios
- * since large folio swap-in isn't supported yet. Once we support
- * mTHP swap-in, we'll likely need to reduce MEMSIZE_MTHP and
- * increase MEMSIZE_SMALLFOLIO to maintain the ratio.
- */
- random_swapin(mem1, MEMSIZE_MTHP,
- aligned_swapin ? ALIGNMENT_MTHP : ALIGNMENT_SMALLFOLIO,
- TOTAL_DONTNEED_MTHP);
- random_madvise_dontneed(mem1, MEMSIZE_MTHP, ALIGNMENT_MTHP,
- TOTAL_DONTNEED_MTHP);
- if (use_small_folio) {
- random_swapin(mem2, MEMSIZE_SMALLFOLIO,
- ALIGNMENT_SMALLFOLIO,
- TOTAL_DONTNEED_SMALLFOLIO);
- }
- if (madvise(mem1, MEMSIZE_MTHP, MADV_PAGEOUT) != 0) {
- perror("madvise pageout for mem1");
- free(mem1);
- if (mem2 != NULL)
- free(mem2);
- return EXIT_FAILURE;
- }
- if (use_small_folio) {
- if (madvise(mem2, MEMSIZE_SMALLFOLIO, MADV_PAGEOUT) != 0) {
- perror("madvise pageout for mem2");
- free(mem1);
- free(mem2);
- return EXIT_FAILURE;
- }
- }
- final_swpout = read_stat(SWPOUT_PATH);
- final_swpout_fallback = read_stat(SWPOUT_FALLBACK_PATH);
- swpout_inc = final_swpout - initial_swpout;
- swpout_fallback_inc = final_swpout_fallback - initial_swpout_fallback;
- fallback_percentage = (double)swpout_fallback_inc /
- (swpout_fallback_inc + swpout_inc) * 100;
- printf("Iteration %d: swpout inc: %lu, swpout fallback inc: %lu, Fallback percentage: %.2f%%\n",
- i + 1, swpout_inc, swpout_fallback_inc, fallback_percentage);
- }
- free(mem1);
- if (mem2 != NULL)
- free(mem2);
- return EXIT_SUCCESS;
- }
|