xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/readahead/readahead02.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2012 Linux Test Project, Inc.
4  */
5 
6 /*
7  * functional test for readahead() syscall
8  *
9  * This test is measuring effects of readahead syscall.
10  * It mmaps/reads a test file with and without prior call to readahead.
11  *
12  * The overlay part of the test is regression for:
13  *  b833a3660394
14  *  ("ovl: add ovl_fadvise()")
15  * Introduced by:
16  *  5b910bd615ba
17  *  ("ovl: fix GPF in swapfile_activate of file from overlayfs over xfs")
18  */
19 #define _GNU_SOURCE
20 #include <sys/types.h>
21 #include <sys/syscall.h>
22 #include <sys/mman.h>
23 #include <sys/mount.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <stdint.h>
30 #include <unistd.h>
31 #include <fcntl.h>
32 #include "config.h"
33 #include "tst_test.h"
34 #include "tst_timer.h"
35 #include "lapi/syscalls.h"
36 
37 static char testfile[PATH_MAX] = "testfile";
38 #define DROP_CACHES_FNAME "/proc/sys/vm/drop_caches"
39 #define MEMINFO_FNAME "/proc/meminfo"
40 #define PROC_IO_FNAME "/proc/self/io"
41 #define DEFAULT_FILESIZE (64 * 1024 * 1024)
42 
43 static size_t testfile_size = DEFAULT_FILESIZE;
44 static char *opt_fsizestr;
45 static int pagesize;
46 static unsigned long cached_max;
47 static int ovl_mounted;
48 static int readahead_length  = 4096;
49 static char sys_bdi_ra_path[PATH_MAX];
50 static int orig_bdi_limit;
51 
52 static const char mntpoint[] = OVL_BASE_MNTPOINT;
53 
libc_readahead(int fd,off_t offset,size_t len)54 static int libc_readahead(int fd, off_t offset, size_t len)
55 {
56 	return readahead(fd, offset, len);
57 }
58 
fadvise_willneed(int fd,off_t offset,size_t len)59 static int fadvise_willneed(int fd, off_t offset, size_t len)
60 {
61 	/* Should have the same effect as readahead() syscall */
62 	errno = posix_fadvise(fd, offset, len, POSIX_FADV_WILLNEED);
63 	/* posix_fadvise returns error number (not in errno) */
64 	return errno ? -1 : 0;
65 }
66 
67 static struct tcase {
68 	const char *tname;
69 	int use_overlay;
70 	int use_fadvise;
71 	/* Use either readahead() syscall or POSIX_FADV_WILLNEED */
72 	int (*readahead)(int fd, off_t offset, size_t len);
73 } tcases[] = {
74 	{ "readahead on file", 0, 0, libc_readahead },
75 	{ "readahead on overlayfs file", 1, 0, libc_readahead },
76 	{ "POSIX_FADV_WILLNEED on file", 0, 1, fadvise_willneed },
77 	{ "POSIX_FADV_WILLNEED on overlayfs file", 1, 1, fadvise_willneed },
78 };
79 
80 static int readahead_supported = 1;
81 static int fadvise_supported = 1;
82 
has_file(const char * fname,int required)83 static int has_file(const char *fname, int required)
84 {
85 	struct stat buf;
86 
87 	if (stat(fname, &buf) == -1) {
88 		if (errno != ENOENT)
89 			tst_brk(TBROK | TERRNO, "stat %s", fname);
90 		if (required)
91 			tst_brk(TCONF, "%s not available", fname);
92 		return 0;
93 	}
94 	return 1;
95 }
96 
drop_caches(void)97 static void drop_caches(void)
98 {
99 	SAFE_FILE_PRINTF(DROP_CACHES_FNAME, "1");
100 }
101 
get_bytes_read(void)102 static unsigned long get_bytes_read(void)
103 {
104 	unsigned long ret;
105 
106 	SAFE_FILE_LINES_SCANF(PROC_IO_FNAME, "read_bytes: %lu", &ret);
107 
108 	return ret;
109 }
110 
get_cached_size(void)111 static unsigned long get_cached_size(void)
112 {
113 	unsigned long ret;
114 
115 	SAFE_FILE_LINES_SCANF(MEMINFO_FNAME, "Cached: %lu", &ret);
116 
117 	return ret;
118 }
119 
create_testfile(int use_overlay)120 static void create_testfile(int use_overlay)
121 {
122 	int fd;
123 	char *tmp;
124 	size_t i;
125 
126 	sprintf(testfile, "%s/testfile",
127 		use_overlay ? OVL_MNT : OVL_BASE_MNTPOINT);
128 	tst_res(TINFO, "creating test file of size: %zu", testfile_size);
129 	tmp = SAFE_MALLOC(pagesize);
130 
131 	/* round to page size */
132 	testfile_size = testfile_size & ~((long)pagesize - 1);
133 
134 	fd = SAFE_CREAT(testfile, 0644);
135 	for (i = 0; i < testfile_size; i += pagesize)
136 		SAFE_WRITE(SAFE_WRITE_ALL, fd, tmp, pagesize);
137 	SAFE_FSYNC(fd);
138 	SAFE_CLOSE(fd);
139 	free(tmp);
140 }
141 
142 /* read_testfile - mmap testfile and read every page.
143  * This functions measures how many I/O and time it takes to fully
144  * read contents of test file.
145  *
146  * @do_readahead: call readahead prior to reading file content?
147  * @fname: name of file to test
148  * @fsize: how many bytes to read/mmap
149  * @read_bytes: returns difference of bytes read, parsed from /proc/<pid>/io
150  * @usec: returns how many microsecond it took to go over fsize bytes
151  * @cached: returns cached kB from /proc/meminfo
152  */
read_testfile(struct tcase * tc,int do_readahead,const char * fname,size_t fsize,unsigned long * read_bytes,long long * usec,unsigned long * cached)153 static int read_testfile(struct tcase *tc, int do_readahead,
154 			 const char *fname, size_t fsize,
155 			 unsigned long *read_bytes, long long *usec,
156 			 unsigned long *cached)
157 {
158 	int fd;
159 	size_t i = 0;
160 	long read_bytes_start;
161 	unsigned char *p, tmp;
162 	off_t offset = 0;
163 
164 	fd = SAFE_OPEN(fname, O_RDONLY);
165 
166 	if (do_readahead) {
167 		do {
168 			TEST(tc->readahead(fd, offset, fsize - offset));
169 			if (TST_RET != 0) {
170 				SAFE_CLOSE(fd);
171 				return TST_ERR;
172 			}
173 
174 			i++;
175 			offset += readahead_length;
176 		} while ((size_t)offset < fsize);
177 		tst_res(TINFO, "readahead calls made: %zu", i);
178 		*cached = get_cached_size();
179 
180 		/* offset of file shouldn't change after readahead */
181 		offset = SAFE_LSEEK(fd, 0, SEEK_CUR);
182 		if (offset == 0)
183 			tst_res(TPASS, "offset is still at 0 as expected");
184 		else
185 			tst_res(TFAIL, "offset has changed to: %lu", offset);
186 	}
187 
188 	tst_timer_start(CLOCK_MONOTONIC);
189 	read_bytes_start = get_bytes_read();
190 
191 	p = SAFE_MMAP(NULL, fsize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0);
192 
193 	/* for old kernels, where MAP_POPULATE doesn't work, touch each page */
194 	tmp = 0;
195 	for (i = 0; i < fsize; i += pagesize)
196 		tmp = tmp ^ p[i];
197 	/* prevent gcc from optimizing out loop above */
198 	if (tmp != 0)
199 		tst_brk(TBROK, "This line should not be reached");
200 
201 	if (!do_readahead)
202 		*cached = get_cached_size();
203 
204 	SAFE_MUNMAP(p, fsize);
205 
206 	*read_bytes = get_bytes_read() - read_bytes_start;
207 
208 	tst_timer_stop();
209 	*usec = tst_timer_elapsed_us();
210 
211 	SAFE_CLOSE(fd);
212 	return 0;
213 }
214 
test_readahead(unsigned int n)215 static void test_readahead(unsigned int n)
216 {
217 	unsigned long read_bytes, read_bytes_ra;
218 	long long usec, usec_ra;
219 	unsigned long cached_high, cached_low, cached, cached_ra;
220 	int ret;
221 	struct tcase *tc = &tcases[n];
222 
223 	tst_res(TINFO, "Test #%d: %s", n, tc->tname);
224 
225 	if (tc->use_overlay && !ovl_mounted) {
226 		tst_res(TCONF, "overlayfs is not configured in this kernel");
227 		return;
228 	}
229 
230 	create_testfile(tc->use_overlay);
231 
232 	/* find out how much can cache hold if we read whole file */
233 	read_testfile(tc, 0, testfile, testfile_size, &read_bytes, &usec,
234 		      &cached);
235 	cached_high = get_cached_size();
236 	sync();
237 	drop_caches();
238 	cached_low = get_cached_size();
239 	cached_max = MAX(cached_max, cached_high - cached_low);
240 
241 	tst_res(TINFO, "read_testfile(0)");
242 	read_testfile(tc, 0, testfile, testfile_size, &read_bytes, &usec,
243 		      &cached);
244 	if (cached > cached_low)
245 		cached = cached - cached_low;
246 	else
247 		cached = 0;
248 
249 	sync();
250 	drop_caches();
251 	cached_low = get_cached_size();
252 	tst_res(TINFO, "read_testfile(1)");
253 	ret = read_testfile(tc, 1, testfile, testfile_size, &read_bytes_ra,
254 			    &usec_ra, &cached_ra);
255 
256 	if (ret == EINVAL) {
257 		if (tc->use_fadvise &&
258 		    (!tc->use_overlay || !fadvise_supported)) {
259 			fadvise_supported = 0;
260 			tst_res(TCONF, "CONFIG_ADVISE_SYSCALLS not configured "
261 				"in kernel?");
262 			return;
263 		}
264 
265 		if (!tc->use_overlay || !readahead_supported) {
266 			readahead_supported = 0;
267 			tst_res(TCONF, "readahead not supported on %s",
268 				tst_device->fs_type);
269 			return;
270 		}
271 	}
272 
273 	if (ret) {
274 		tst_res(TFAIL | TTERRNO, "%s failed on %s",
275 			tc->use_fadvise ? "fadvise" : "readahead",
276 			tc->use_overlay ? "overlayfs" :
277 			tst_device->fs_type);
278 		return;
279 	}
280 
281 	if (cached_ra > cached_low)
282 		cached_ra = cached_ra - cached_low;
283 	else
284 		cached_ra = 0;
285 
286 	tst_res(TINFO, "read_testfile(0) took: %lli usec", usec);
287 	tst_res(TINFO, "read_testfile(1) took: %lli usec", usec_ra);
288 	if (has_file(PROC_IO_FNAME, 0)) {
289 		tst_res(TINFO, "read_testfile(0) read: %ld bytes", read_bytes);
290 		tst_res(TINFO, "read_testfile(1) read: %ld bytes",
291 			read_bytes_ra);
292 		/* actual number of read bytes depends on total RAM */
293 		if (read_bytes_ra < read_bytes)
294 			tst_res(TPASS, "readahead saved some I/O");
295 		else
296 			tst_res(TFAIL, "readahead failed to save any I/O");
297 	} else {
298 		tst_res(TCONF, "Your system doesn't have /proc/self/io,"
299 			" unable to determine read bytes during test");
300 	}
301 
302 	tst_res(TINFO, "cache can hold at least: %ld kB", cached_max);
303 	tst_res(TINFO, "read_testfile(0) used cache: %ld kB", cached);
304 	tst_res(TINFO, "read_testfile(1) used cache: %ld kB", cached_ra);
305 
306 	if (cached_max * 1024 >= testfile_size) {
307 		/*
308 		 * if cache can hold ~testfile_size then cache increase
309 		 * for readahead should be at least testfile_size/2
310 		 */
311 		if (cached_ra * 1024 > testfile_size / 2)
312 			tst_res(TPASS, "using cache as expected");
313 		else if (!cached_ra)
314 			tst_res(TFAIL, "readahead failed to use any cache");
315 		else
316 			tst_res(TWARN, "using less cache than expected");
317 	} else {
318 		tst_res(TCONF, "Page cache on your system is too small "
319 			"to hold whole testfile.");
320 	}
321 
322 	/*
323 	 * The time consuming of readahead quite depending on the platform IO
324 	 * speed, sometime test timeout when the default max_runtime is used up.
325 	 *
326 	 *  readahead02.c:221: TINFO: Test #2: POSIX_FADV_WILLNEED on file
327 	 *  readahead02.c:285: TINFO: read_testfile(0) took: 26317623 usec
328 	 *  readahead02.c:286: TINFO: read_testfile(1) took: 26101484 usec
329 	 *
330 	 * Here raise the maximum runtime dynamically.
331 	 */
332 	if ((tc+1)->readahead)
333 		tst_set_max_runtime(test.max_runtime + (usec + usec_ra) / 1000000);
334 }
335 
336 
337 /*
338  * We try raising bdi readahead limit as much as we can. We write
339  * and read back "read_ahead_kb" sysfs value, starting with filesize.
340  * If that fails, we try again with lower value.
341  * readahead_length used in the test is then set to MIN(bdi limit, 2M),
342  * to respect kernels prior to commit 600e19afc5f8a6c.
343  */
setup_readahead_length(void)344 static void setup_readahead_length(void)
345 {
346 	struct stat sbuf;
347 	char tmp[PATH_MAX], *backing_dev;
348 	int ra_new_limit, ra_limit;
349 
350 	/* Find out backing device name */
351 	SAFE_LSTAT(tst_device->dev, &sbuf);
352 	if (S_ISLNK(sbuf.st_mode))
353 		SAFE_READLINK(tst_device->dev, tmp, PATH_MAX);
354 	else
355 		strcpy(tmp, tst_device->dev);
356 
357 	backing_dev = basename(tmp);
358 	sprintf(sys_bdi_ra_path, "/sys/class/block/%s/bdi/read_ahead_kb",
359 		backing_dev);
360 	if (access(sys_bdi_ra_path, F_OK))
361 		return;
362 
363 	SAFE_FILE_SCANF(sys_bdi_ra_path, "%d", &orig_bdi_limit);
364 
365 	/* raise bdi limit as much as kernel allows */
366 	ra_new_limit = testfile_size / 1024;
367 	while (ra_new_limit > pagesize / 1024) {
368 		SAFE_FILE_PRINTF(sys_bdi_ra_path, "%d", ra_new_limit);
369 		SAFE_FILE_SCANF(sys_bdi_ra_path, "%d", &ra_limit);
370 
371 		if (ra_limit == ra_new_limit) {
372 			readahead_length = MIN(ra_new_limit * 1024,
373 				2 * 1024 * 1024);
374 			break;
375 		}
376 		ra_new_limit = ra_new_limit / 2;
377 	}
378 }
379 
setup(void)380 static void setup(void)
381 {
382 	if (opt_fsizestr) {
383 		testfile_size = SAFE_STRTOL(opt_fsizestr, 1, INT_MAX);
384 		tst_set_max_runtime(1 + testfile_size / (DEFAULT_FILESIZE/32));
385 	}
386 
387 	if (access(PROC_IO_FNAME, F_OK))
388 		tst_brk(TCONF, "Requires " PROC_IO_FNAME);
389 
390 	has_file(DROP_CACHES_FNAME, 1);
391 	has_file(MEMINFO_FNAME, 1);
392 
393 	/* check if readahead is supported */
394 	tst_syscall(__NR_readahead, 0, 0, 0);
395 
396 	pagesize = getpagesize();
397 
398 	setup_readahead_length();
399 	tst_res(TINFO, "readahead length: %d", readahead_length);
400 
401 	ovl_mounted = TST_MOUNT_OVERLAY();
402 }
403 
cleanup(void)404 static void cleanup(void)
405 {
406 	if (ovl_mounted)
407 		SAFE_UMOUNT(OVL_MNT);
408 
409 	if (orig_bdi_limit)
410 		SAFE_FILE_PRINTF(sys_bdi_ra_path, "%d", orig_bdi_limit);
411 }
412 
413 static struct tst_test test = {
414 	.needs_root = 1,
415 	.mount_device = 1,
416 	.mntpoint = mntpoint,
417 	.setup = setup,
418 	.cleanup = cleanup,
419 	.options = (struct tst_option[]) {
420 		{"s:", &opt_fsizestr, "Testfile size (default 64MB)"},
421 		{}
422 	},
423 	.test = test_readahead,
424 	.tcnt = ARRAY_SIZE(tcases),
425 	.max_runtime = 30,
426 	.tags = (const struct tst_tag[]) {
427 		{"linux-git", "b833a3660394"},
428 		{"linux-git", "5b910bd615ba"},
429 		{}
430 	}
431 };
432