mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-29 17:22:07 +00:00
235 lines
6.2 KiB
C
235 lines
6.2 KiB
C
|
// 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_MTHP);
|
||
|
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;
|
||
|
}
|