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