blob: 5ce2a334c55fdefa932921d7b2c0cf8c5b5a4065 [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"
47#include "usctest.h"
48#include "safe_macros.h"
49#include "linux_syscall_numbers.h"
50
51char *TCID = "readahead02";
Wanlong Gao354ebb42012-12-07 10:10:04 +080052int TST_TOTAL = 1;
Jan Stancekdd4927f2012-10-15 09:54:51 +020053
54#if defined(__NR_readahead)
55static const char testfile[] = "testfile";
56static const char drop_caches_fname[] = "/proc/sys/vm/drop_caches";
57static const char meminfo_fname[] = "/proc/meminfo";
Wanlong Gao354ebb42012-12-07 10:10:04 +080058static size_t testfile_size = 64 * 1024 * 1024;
Jan Stancekdd4927f2012-10-15 09:54:51 +020059static int opt_fsize;
60static char *opt_fsizestr;
61static int pagesize;
62
63option_t options[] = {
Wanlong Gao354ebb42012-12-07 10:10:04 +080064 {"s:", &opt_fsize, &opt_fsizestr},
65 {NULL, NULL, NULL}
Jan Stancekdd4927f2012-10-15 09:54:51 +020066};
67
68static void setup(void);
69static void cleanup(void);
70
71static void help(void)
72{
73 printf(" -s x testfile size (default 64MB)\n");
74}
75
76static int check_ret(long expected_ret)
77{
78 if (expected_ret == TEST_RETURN) {
79 tst_resm(TPASS, "expected ret success - "
Wanlong Gao354ebb42012-12-07 10:10:04 +080080 "returned value = %ld", TEST_RETURN);
Jan Stancekdd4927f2012-10-15 09:54:51 +020081 return 0;
82 }
83 tst_resm(TFAIL, "unexpected failure - "
Wanlong Gao354ebb42012-12-07 10:10:04 +080084 "returned value = %ld, expected: %ld",
85 TEST_RETURN, expected_ret);
Jan Stancekdd4927f2012-10-15 09:54:51 +020086 return 1;
87}
88
89static int has_file(const char *fname, int required)
90{
91 int ret;
92 struct stat buf;
93 ret = stat(fname, &buf);
94 if (ret == -1) {
95 if (errno == ENOENT)
96 if (required)
97 tst_brkm(TCONF, cleanup, "%s not available",
Wanlong Gao354ebb42012-12-07 10:10:04 +080098 fname);
Jan Stancekdd4927f2012-10-15 09:54:51 +020099 else
100 return 0;
101 else
Wanlong Gao354ebb42012-12-07 10:10:04 +0800102 tst_brkm(TBROK | TERRNO, cleanup, "stat %s", fname);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200103 }
104 return 1;
105}
106
107static void drop_caches(void)
108{
109 int ret;
110 FILE *f;
111
112 f = fopen(drop_caches_fname, "w");
113 if (f) {
114 ret = fprintf(f, "1");
115 fclose(f);
116 if (ret < 1)
117 tst_brkm(TBROK, cleanup, "Failed to drop caches");
118 } else {
119 tst_brkm(TBROK, cleanup, "Failed to open drop_caches");
120 }
121}
122
123static long parse_entry(const char *fname, const char *entry)
124{
125 FILE *f;
126 long value = -1;
127 int ret;
128 char *line = NULL;
129 size_t linelen;
130
131 f = fopen(fname, "r");
132 if (f) {
133 do {
134 ret = getline(&line, &linelen, f);
135 if (sscanf(line, entry, &value) == 1)
136 break;
137 } while (ret != -1);
138 fclose(f);
139 }
140 return value;
141}
142
143static long get_bytes_read(void)
144{
145 char fname[128];
146 char entry[] = "read_bytes: %ld";
147 sprintf(fname, "/proc/%u/io", getpid());
148 return parse_entry(fname, entry);
149}
150
151static long get_cached_size(void)
152{
153 char entry[] = "Cached: %ld";
154 return parse_entry(meminfo_fname, entry);
155}
156
157static void create_testfile(void)
158{
159 FILE *f;
160 char *tmp;
161 int i;
162
163 tst_resm(TINFO, "creating test file of size: %ld", testfile_size);
164 tmp = SAFE_MALLOC(cleanup, pagesize);
165
166 /* round to page size */
167 testfile_size = testfile_size & ~((long)pagesize - 1);
168
169 f = fopen(testfile, "w");
170 if (!f) {
171 free(tmp);
Wanlong Gao354ebb42012-12-07 10:10:04 +0800172 tst_brkm(TBROK | TERRNO, cleanup, "Failed to create %s",
173 testfile);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200174 }
175
176 for (i = 0; i < testfile_size; i += pagesize)
177 if (fwrite(tmp, pagesize, 1, f) < 1) {
178 free(tmp);
179 tst_brkm(TBROK, cleanup, "Failed to create %s",
Wanlong Gao354ebb42012-12-07 10:10:04 +0800180 testfile);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200181 }
182 fflush(f);
183 fsync(fileno(f));
184 fclose(f);
185 free(tmp);
186}
187
188/* read_testfile - mmap testfile and read every page.
189 * This functions measures how many I/O and time it takes to fully
190 * read contents of test file.
191 *
192 * @do_readahead: call readahead prior to reading file content?
193 * @fname: name of file to test
194 * @fsize: how many bytes to read/mmap
195 * @read_bytes: returns difference of bytes read, parsed from /proc/<pid>/io
196 * @usec: returns how many microsecond it took to go over fsize bytes
197 * @cached: returns cached kB from /proc/meminfo
198 */
199static void read_testfile(int do_readahead, const char *fname, size_t fsize,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800200 long *read_bytes, long *usec, long *cached)
Jan Stancekdd4927f2012-10-15 09:54:51 +0200201{
202 int fd, i;
203 long read_bytes_start;
204 unsigned char *p, tmp;
205 unsigned long time_start_usec, time_end_usec;
206 off_t offset;
207 struct timeval now;
208
209 fd = open(fname, O_RDONLY);
210 if (fd < 0)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800211 tst_brkm(TBROK | TERRNO, cleanup, "Failed to open %s", fname);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200212
213 if (do_readahead) {
Jan Stancek359980f2013-02-15 10:16:05 +0100214 TEST(ltp_syscall(__NR_readahead, fd, (off64_t) 0,
215 (size_t) fsize));
Jan Stancekdd4927f2012-10-15 09:54:51 +0200216 check_ret(0);
217 *cached = get_cached_size();
218
219 /* offset of file shouldn't change after readahead */
220 offset = lseek(fd, 0, SEEK_CUR);
Wanlong Gao354ebb42012-12-07 10:10:04 +0800221 if (offset == (off_t) - 1)
222 tst_brkm(TBROK | TERRNO, cleanup, "lseek failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200223 if (offset == 0)
224 tst_resm(TPASS, "offset is still at 0 as expected");
225 else
226 tst_resm(TFAIL, "offset has changed to: %lu", offset);
227 }
228
229 if (gettimeofday(&now, NULL) == -1)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800230 tst_brkm(TBROK | TERRNO, cleanup, "gettimeofday failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200231 time_start_usec = now.tv_sec * 1000000 + now.tv_usec;
232 read_bytes_start = get_bytes_read();
233
Wanlong Gao354ebb42012-12-07 10:10:04 +0800234 p = mmap(NULL, fsize, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200235 if (p == MAP_FAILED)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800236 tst_brkm(TBROK | TERRNO, cleanup, "mmap failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200237
238 /* for old kernels, where MAP_POPULATE doesn't work, touch each page */
239 tmp = 0;
240 for (i = 0; i < fsize; i += pagesize)
241 tmp = tmp ^ p[i];
242 /* prevent gcc from optimizing out loop above */
243 if (tmp != 0)
244 tst_brkm(TBROK, NULL, "This line should not be reached");
245
246 if (!do_readahead)
247 *cached = get_cached_size();
248
249 if (munmap(p, fsize) == -1)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800250 tst_brkm(TBROK | TERRNO, cleanup, "munmap failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200251
252 *read_bytes = get_bytes_read() - read_bytes_start;
253 if (gettimeofday(&now, NULL) == -1)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800254 tst_brkm(TBROK | TERRNO, cleanup, "gettimeofday failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200255 time_end_usec = now.tv_sec * 1000000 + now.tv_usec;
256 *usec = time_end_usec - time_start_usec;
257
258 if (close(fd) == -1)
Wanlong Gao354ebb42012-12-07 10:10:04 +0800259 tst_brkm(TBROK | TERRNO, cleanup, "close failed");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200260}
261
262static void test_readahead(void)
263{
264 long read_bytes, read_bytes_ra;
265 long usec, usec_ra;
266 long cached_max, cached_low, cached, cached_ra;
267 char proc_io_fname[128];
268 sprintf(proc_io_fname, "/proc/%u/io", getpid());
269
270 /* find out how much can cache hold if we read whole file */
Wanlong Gao354ebb42012-12-07 10:10:04 +0800271 read_testfile(0, testfile, testfile_size, &read_bytes, &usec, &cached);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200272 cached_max = get_cached_size();
273 sync();
274 drop_caches();
275 cached_low = get_cached_size();
276 cached_max = cached_max - cached_low;
277
278 tst_resm(TINFO, "read_testfile(0)");
Wanlong Gao354ebb42012-12-07 10:10:04 +0800279 read_testfile(0, testfile, testfile_size, &read_bytes, &usec, &cached);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200280 cached = cached - cached_low;
281
282 sync();
283 drop_caches();
284 cached_low = get_cached_size();
285 tst_resm(TINFO, "read_testfile(1)");
286 read_testfile(1, testfile, testfile_size, &read_bytes_ra,
Wanlong Gao354ebb42012-12-07 10:10:04 +0800287 &usec_ra, &cached_ra);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200288 cached_ra = cached_ra - cached_low;
289
290 tst_resm(TINFO, "read_testfile(0) took: %ld usec", usec);
291 tst_resm(TINFO, "read_testfile(1) took: %ld usec", usec_ra);
292 if (has_file(proc_io_fname, 0)) {
Wanlong Gao354ebb42012-12-07 10:10:04 +0800293 tst_resm(TINFO, "read_testfile(0) read: %ld bytes", read_bytes);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200294 tst_resm(TINFO, "read_testfile(1) read: %ld bytes",
Wanlong Gao354ebb42012-12-07 10:10:04 +0800295 read_bytes_ra);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200296 /* actual number of read bytes depends on total RAM */
297 if (read_bytes_ra < read_bytes)
298 tst_resm(TPASS, "readahead saved some I/O");
299 else
300 tst_resm(TFAIL, "readahead failed to save any I/O");
301 } else {
302 tst_resm(TCONF, "Your system doesn't have /proc/pid/io,"
Wanlong Gao354ebb42012-12-07 10:10:04 +0800303 " unable to determine read bytes during test");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200304 }
305
306 tst_resm(TINFO, "cache can hold at least: %ld kB", cached_max);
307 tst_resm(TINFO, "read_testfile(0) used cache: %ld kB", cached);
308 tst_resm(TINFO, "read_testfile(1) used cache: %ld kB", cached_ra);
309
Wanlong Gao354ebb42012-12-07 10:10:04 +0800310 if (cached_max * 1024 >= testfile_size) {
Jan Stancekdd4927f2012-10-15 09:54:51 +0200311 /*
312 * if cache can hold ~testfile_size then cache increase
313 * for readahead should be at least testfile_size/2
314 */
Wanlong Gao354ebb42012-12-07 10:10:04 +0800315 if (cached_ra * 1024 > testfile_size / 2)
Jan Stancekdd4927f2012-10-15 09:54:51 +0200316 tst_resm(TPASS, "using cache as expected");
317 else
318 tst_resm(TWARN, "using less cache than expected");
319 } else {
320 tst_resm(TCONF, "Page cache on your system is too small "
Wanlong Gao354ebb42012-12-07 10:10:04 +0800321 "to hold whole testfile.");
Jan Stancekdd4927f2012-10-15 09:54:51 +0200322 }
323}
324
325int main(int argc, char *argv[])
326{
327 char *msg;
328 int lc;
329
330 msg = parse_opts(argc, argv, options, help);
331 if (msg != NULL)
332 tst_brkm(TBROK, NULL, "OPTION PARSING ERROR - %s", msg);
333
334 if (opt_fsize)
335 testfile_size = atoi(opt_fsizestr);
336
337 setup();
338 for (lc = 0; TEST_LOOPING(lc); lc++) {
339 Tst_count = 0;
340 test_readahead();
341 }
342 cleanup();
343 tst_exit();
344}
345
346static void setup(void)
347{
348 tst_require_root(NULL);
349 tst_tmpdir();
350 TEST_PAUSE;
351
352 has_file(drop_caches_fname, 1);
353 has_file(meminfo_fname, 1);
354
355 /* check if readahead is supported */
Jan Stancek359980f2013-02-15 10:16:05 +0100356 ltp_syscall(__NR_readahead, 0, 0, 0);
Jan Stancekdd4927f2012-10-15 09:54:51 +0200357
358 pagesize = getpagesize();
359 create_testfile();
360}
361
362static void cleanup(void)
363{
364 TEST_CLEANUP;
365 unlink(testfile);
366 tst_rmdir();
367}
368
369#else /* __NR_readahead */
370int main(void)
371{
372 tst_brkm(TCONF, NULL, "System doesn't support __NR_readahead");
373}
374#endif