blob: f1571c43e91654f2e0d4d301e7f43d9199a14e38 [file] [log] [blame]
Jan Stancekdd4927f2012-10-15 09:54:51 +02001/*
2 * Copyright (C) 2012 Linux Test Project, Inc.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General Public
6 * License as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it would be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * Further, this software is distributed without any warranty that it
13 * is free of the rightful claim of any third person regarding
14 * infringement or the like. Any license provided herein, whether
15 * implied or otherwise, applies only to this software file. Patent
16 * licenses, if any, provided herein do not apply to combinations of
17 * this program with other software, or any other product whatsoever.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 * 02110-1301, USA.
23 */
24
25/*
26 * functional test for readahead() syscall
27 *
28 * This test is measuring effects of readahead syscall.
29 * It mmaps/reads a test file with and without prior call to readahead.
30 *
31 */
32#define _GNU_SOURCE
33#include <sys/types.h>
34#include <sys/syscall.h>
35#include <sys/mman.h>
36#include <sys/stat.h>
37#include <sys/types.h>
38#include <sys/time.h>
39#include <errno.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <stdint.h>
43#include <unistd.h>
44#include <fcntl.h>
45#include "config.h"
46#include "test.h"
Jan Stancekdd4927f2012-10-15 09:54:51 +020047#include "safe_macros.h"
48#include "linux_syscall_numbers.h"
49
50char *TCID = "readahead02";
Wanlong Gao354ebb42012-12-07 10:10:04 +080051int TST_TOTAL = 1;
Jan Stancekdd4927f2012-10-15 09:54:51 +020052
53#if defined(__NR_readahead)
54static const char testfile[] = "testfile";
55static const char drop_caches_fname[] = "/proc/sys/vm/drop_caches";
56static const char meminfo_fname[] = "/proc/meminfo";
Wanlong Gao354ebb42012-12-07 10:10:04 +080057static size_t testfile_size = 64 * 1024 * 1024;
Jan Stancekdd4927f2012-10-15 09:54:51 +020058static int opt_fsize;
59static char *opt_fsizestr;
60static int pagesize;
61
62option_t options[] = {
Wanlong Gao354ebb42012-12-07 10:10:04 +080063 {"s:", &opt_fsize, &opt_fsizestr},
64 {NULL, NULL, NULL}
Jan Stancekdd4927f2012-10-15 09:54:51 +020065};
66
67static void setup(void);
68static void cleanup(void);
69
70static void help(void)
71{
72 printf(" -s x testfile size (default 64MB)\n");
73}
74
75static int check_ret(long expected_ret)
76{
77 if (expected_ret == TEST_RETURN) {
78 tst_resm(TPASS, "expected ret success - "
Wanlong Gao354ebb42012-12-07 10:10:04 +080079 "returned value = %ld", TEST_RETURN);
Jan Stancekdd4927f2012-10-15 09:54:51 +020080 return 0;
81 }
82 tst_resm(TFAIL, "unexpected failure - "
Wanlong Gao354ebb42012-12-07 10:10:04 +080083 "returned value = %ld, expected: %ld",
84 TEST_RETURN, expected_ret);
Jan Stancekdd4927f2012-10-15 09:54:51 +020085 return 1;
86}
87
88static int has_file(const char *fname, int required)
89{
90 int ret;
91 struct stat buf;
92 ret = stat(fname, &buf);
93 if (ret == -1) {
94 if (errno == ENOENT)
95 if (required)
96 tst_brkm(TCONF, cleanup, "%s not available",
Wanlong Gao354ebb42012-12-07 10:10:04 +080097 fname);
Jan Stancekdd4927f2012-10-15 09:54:51 +020098 else
99 return 0;
100 else
Wanlong Gao354ebb42012-12-07 10:10:04 +0800101 tst_brkm(TBROK | TERRNO, cleanup, "stat %s", fname);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200102 }
103 return 1;
104}
105
106static void drop_caches(void)
107{
108 int ret;
109 FILE *f;
110
111 f = fopen(drop_caches_fname, "w");
112 if (f) {
113 ret = fprintf(f, "1");
114 fclose(f);
115 if (ret < 1)
116 tst_brkm(TBROK, cleanup, "Failed to drop caches");
117 } else {
118 tst_brkm(TBROK, cleanup, "Failed to open drop_caches");
119 }
120}
121
Jan Stancek02ee88c2014-04-22 10:37:19 +0200122static unsigned long parse_entry(const char *fname, const char *entry)
Jan Stancekdd4927f2012-10-15 09:54:51 +0200123{
124 FILE *f;
125 long value = -1;
126 int ret;
127 char *line = NULL;
128 size_t linelen;
129
130 f = fopen(fname, "r");
131 if (f) {
132 do {
133 ret = getline(&line, &linelen, f);
134 if (sscanf(line, entry, &value) == 1)
135 break;
136 } while (ret != -1);
137 fclose(f);
138 }
139 return value;
140}
141
Jan Stancek02ee88c2014-04-22 10:37:19 +0200142static unsigned long get_bytes_read(void)
Jan Stancekdd4927f2012-10-15 09:54:51 +0200143{
144 char fname[128];
Jan Stancek02ee88c2014-04-22 10:37:19 +0200145 char entry[] = "read_bytes: %lu";
Jan Stancekdd4927f2012-10-15 09:54:51 +0200146 sprintf(fname, "/proc/%u/io", getpid());
147 return parse_entry(fname, entry);
148}
149
Jan Stancek02ee88c2014-04-22 10:37:19 +0200150static unsigned long get_cached_size(void)
Jan Stancekdd4927f2012-10-15 09:54:51 +0200151{
Jan Stancek02ee88c2014-04-22 10:37:19 +0200152 char entry[] = "Cached: %lu";
Jan Stancekdd4927f2012-10-15 09:54:51 +0200153 return parse_entry(meminfo_fname, entry);
154}
155
156static void create_testfile(void)
157{
158 FILE *f;
159 char *tmp;
Jan Stancek02ee88c2014-04-22 10:37:19 +0200160 size_t i;
Jan Stancekdd4927f2012-10-15 09:54:51 +0200161
162 tst_resm(TINFO, "creating test file of size: %ld", testfile_size);
163 tmp = SAFE_MALLOC(cleanup, pagesize);
164
165 /* round to page size */
166 testfile_size = testfile_size & ~((long)pagesize - 1);
167
168 f = fopen(testfile, "w");
169 if (!f) {
170 free(tmp);
Wanlong Gao354ebb42012-12-07 10:10:04 +0800171 tst_brkm(TBROK | TERRNO, cleanup, "Failed to create %s",
172 testfile);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200173 }
174
175 for (i = 0; i < testfile_size; i += pagesize)
176 if (fwrite(tmp, pagesize, 1, f) < 1) {
177 free(tmp);
178 tst_brkm(TBROK, cleanup, "Failed to create %s",
Wanlong Gao354ebb42012-12-07 10:10:04 +0800179 testfile);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200180 }
181 fflush(f);
182 fsync(fileno(f));
183 fclose(f);
184 free(tmp);
185}
186
187/* read_testfile - mmap testfile and read every page.
188 * This functions measures how many I/O and time it takes to fully
189 * read contents of test file.
190 *
191 * @do_readahead: call readahead prior to reading file content?
192 * @fname: name of file to test
193 * @fsize: how many bytes to read/mmap
194 * @read_bytes: returns difference of bytes read, parsed from /proc/<pid>/io
195 * @usec: returns how many microsecond it took to go over fsize bytes
196 * @cached: returns cached kB from /proc/meminfo
197 */
198static void read_testfile(int do_readahead, const char *fname, size_t fsize,
Jan Stancek02ee88c2014-04-22 10:37:19 +0200199 unsigned long *read_bytes, long *usec,
200 unsigned long *cached)
Jan Stancekdd4927f2012-10-15 09:54:51 +0200201{
Jan Stancek02ee88c2014-04-22 10:37:19 +0200202 int fd;
203 size_t i;
Jan Stancekdd4927f2012-10-15 09:54:51 +0200204 long read_bytes_start;
205 unsigned char *p, tmp;
206 unsigned long time_start_usec, time_end_usec;
207 off_t offset;
208 struct timeval now;
209
210 fd = open(fname, O_RDONLY);
211 if (fd < 0)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800212 tst_brkm(TBROK | TERRNO, cleanup, "Failed to open %s", fname);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200213
214 if (do_readahead) {
Jan Stancek09bea282014-04-22 10:37:20 +0200215 /* read ahead in chunks, 2MB is maximum since 3.15-rc1 */
216 for (i = 0; i < fsize; i += 2*1024*1024) {
217 TEST(ltp_syscall(__NR_readahead, fd,
218 (off64_t) i, 2*1024*1024));
219 if (TEST_RETURN != 0)
220 break;
221 }
Jan Stancekdd4927f2012-10-15 09:54:51 +0200222 check_ret(0);
223 *cached = get_cached_size();
224
225 /* offset of file shouldn't change after readahead */
226 offset = lseek(fd, 0, SEEK_CUR);
Wanlong Gao354ebb42012-12-07 10:10:04 +0800227 if (offset == (off_t) - 1)
228 tst_brkm(TBROK | TERRNO, cleanup, "lseek failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200229 if (offset == 0)
230 tst_resm(TPASS, "offset is still at 0 as expected");
231 else
232 tst_resm(TFAIL, "offset has changed to: %lu", offset);
233 }
234
235 if (gettimeofday(&now, NULL) == -1)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800236 tst_brkm(TBROK | TERRNO, cleanup, "gettimeofday failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200237 time_start_usec = now.tv_sec * 1000000 + now.tv_usec;
238 read_bytes_start = get_bytes_read();
239
Wanlong Gao354ebb42012-12-07 10:10:04 +0800240 p = mmap(NULL, fsize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200241 if (p == MAP_FAILED)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800242 tst_brkm(TBROK | TERRNO, cleanup, "mmap failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200243
244 /* for old kernels, where MAP_POPULATE doesn't work, touch each page */
245 tmp = 0;
246 for (i = 0; i < fsize; i += pagesize)
247 tmp = tmp ^ p[i];
248 /* prevent gcc from optimizing out loop above */
249 if (tmp != 0)
250 tst_brkm(TBROK, NULL, "This line should not be reached");
251
252 if (!do_readahead)
253 *cached = get_cached_size();
254
255 if (munmap(p, fsize) == -1)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800256 tst_brkm(TBROK | TERRNO, cleanup, "munmap failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200257
258 *read_bytes = get_bytes_read() - read_bytes_start;
259 if (gettimeofday(&now, NULL) == -1)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800260 tst_brkm(TBROK | TERRNO, cleanup, "gettimeofday failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200261 time_end_usec = now.tv_sec * 1000000 + now.tv_usec;
262 *usec = time_end_usec - time_start_usec;
263
264 if (close(fd) == -1)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800265 tst_brkm(TBROK | TERRNO, cleanup, "close failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200266}
267
268static void test_readahead(void)
269{
Jan Stancek02ee88c2014-04-22 10:37:19 +0200270 unsigned long read_bytes, read_bytes_ra;
Jan Stancekdd4927f2012-10-15 09:54:51 +0200271 long usec, usec_ra;
Jan Stancek02ee88c2014-04-22 10:37:19 +0200272 unsigned long cached_max, cached_low, cached, cached_ra;
Jan Stancekdd4927f2012-10-15 09:54:51 +0200273 char proc_io_fname[128];
274 sprintf(proc_io_fname, "/proc/%u/io", getpid());
275
276 /* find out how much can cache hold if we read whole file */
Wanlong Gao354ebb42012-12-07 10:10:04 +0800277 read_testfile(0, testfile, testfile_size, &read_bytes, &usec, &cached);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200278 cached_max = get_cached_size();
279 sync();
280 drop_caches();
281 cached_low = get_cached_size();
282 cached_max = cached_max - cached_low;
283
284 tst_resm(TINFO, "read_testfile(0)");
Wanlong Gao354ebb42012-12-07 10:10:04 +0800285 read_testfile(0, testfile, testfile_size, &read_bytes, &usec, &cached);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200286 cached = cached - cached_low;
287
288 sync();
289 drop_caches();
290 cached_low = get_cached_size();
291 tst_resm(TINFO, "read_testfile(1)");
292 read_testfile(1, testfile, testfile_size, &read_bytes_ra,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800293 &usec_ra, &cached_ra);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200294 cached_ra = cached_ra - cached_low;
295
296 tst_resm(TINFO, "read_testfile(0) took: %ld usec", usec);
297 tst_resm(TINFO, "read_testfile(1) took: %ld usec", usec_ra);
298 if (has_file(proc_io_fname, 0)) {
Wanlong Gao354ebb42012-12-07 10:10:04 +0800299 tst_resm(TINFO, "read_testfile(0) read: %ld bytes", read_bytes);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200300 tst_resm(TINFO, "read_testfile(1) read: %ld bytes",
Wanlong Gao354ebb42012-12-07 10:10:04 +0800301 read_bytes_ra);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200302 /* actual number of read bytes depends on total RAM */
303 if (read_bytes_ra < read_bytes)
304 tst_resm(TPASS, "readahead saved some I/O");
305 else
306 tst_resm(TFAIL, "readahead failed to save any I/O");
307 } else {
308 tst_resm(TCONF, "Your system doesn't have /proc/pid/io,"
Wanlong Gao354ebb42012-12-07 10:10:04 +0800309 " unable to determine read bytes during test");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200310 }
311
312 tst_resm(TINFO, "cache can hold at least: %ld kB", cached_max);
313 tst_resm(TINFO, "read_testfile(0) used cache: %ld kB", cached);
314 tst_resm(TINFO, "read_testfile(1) used cache: %ld kB", cached_ra);
315
Wanlong Gao354ebb42012-12-07 10:10:04 +0800316 if (cached_max * 1024 >= testfile_size) {
Jan Stancekdd4927f2012-10-15 09:54:51 +0200317 /*
318 * if cache can hold ~testfile_size then cache increase
319 * for readahead should be at least testfile_size/2
320 */
Wanlong Gao354ebb42012-12-07 10:10:04 +0800321 if (cached_ra * 1024 > testfile_size / 2)
Jan Stancekdd4927f2012-10-15 09:54:51 +0200322 tst_resm(TPASS, "using cache as expected");
323 else
324 tst_resm(TWARN, "using less cache than expected");
325 } else {
326 tst_resm(TCONF, "Page cache on your system is too small "
Wanlong Gao354ebb42012-12-07 10:10:04 +0800327 "to hold whole testfile.");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200328 }
329}
330
331int main(int argc, char *argv[])
332{
Cyril Hrubis0b9589f2014-05-27 17:40:33 +0200333 const char *msg;
Jan Stancekdd4927f2012-10-15 09:54:51 +0200334 int lc;
335
336 msg = parse_opts(argc, argv, options, help);
337 if (msg != NULL)
338 tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
339
340 if (opt_fsize)
341 testfile_size = atoi(opt_fsizestr);
342
343 setup();
344 for (lc = 0; TEST_LOOPING(lc); lc++) {
Caspar Zhangd59a6592013-03-07 14:59:12 +0800345 tst_count = 0;
Jan Stancekdd4927f2012-10-15 09:54:51 +0200346 test_readahead();
347 }
348 cleanup();
349 tst_exit();
350}
351
352static void setup(void)
353{
354 tst_require_root(NULL);
355 tst_tmpdir();
356 TEST_PAUSE;
357
358 has_file(drop_caches_fname, 1);
359 has_file(meminfo_fname, 1);
360
361 /* check if readahead is supported */
Jan Stancek359980f2013-02-15 10:16:05 +0100362 ltp_syscall(__NR_readahead, 0, 0, 0);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200363
364 pagesize = getpagesize();
365 create_testfile();
366}
367
368static void cleanup(void)
369{
Jan Stancekdd4927f2012-10-15 09:54:51 +0200370 unlink(testfile);
371 tst_rmdir();
372}
373
374#else /* __NR_readahead */
375int main(void)
376{
377 tst_brkm(TCONF, NULL, "System doesn't support __NR_readahead");
378}
379#endif