Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.17.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * thp_swap_allocator_test
  4 *
  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
 12 * swap device by:
 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
 17 *  mkswap /dev/zram0
 18 *  swapon /dev/zram0
 19 * The expected result should be 0% anon swpout fallback ratio w/ or
 20 * w/o "-s".
 21 *
 22 * Author(s): Barry Song <v-songbaohua@oppo.com>
 23 */
 24
 25#define _GNU_SOURCE
 26#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 mTHP
 62 * and ensures that iterations can proceed with mTHP, as we
 63 * 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 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.
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}