• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (c) 2022 SUSE LLC <rpalethorpe@suse.com>
4  */
5 /*\
6  *
7  * [Description]
8  *
9  * Perform some I/O on a file and check if at least some of it is
10  * recorded by the I/O controller.
11  *
12  * The exact amount of I/O performed is dependent on the file system,
13  * page cache, scheduler and block driver. We call sync and drop the
14  * file's page cache to force reading and writing. We also write
15  * random data to try to prevent compression.
16  *
17  * The pagecache is a particular issue for reading. If the call to
18  * fadvise is ignored then the data may only be read from the
19  * cache. So that no I/O requests are made.
20  */
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/sysmacros.h>
25 
26 #include "tst_test.h"
27 
28 struct io_stats {
29 	unsigned int mjr;
30 	unsigned int mnr;
31 	unsigned long rbytes;
32 	unsigned long wbytes;
33 	unsigned long rios;
34 	unsigned long wios;
35 	unsigned long dbytes;
36 	unsigned long dios;
37 };
38 
39 static unsigned int dev_major, dev_minor;
40 
read_io_stats(const char * const line,struct io_stats * const stat)41 static int read_io_stats(const char *const line, struct io_stats *const stat)
42 {
43 	return sscanf(line,
44 		      "%u:%u rbytes=%lu wbytes=%lu rios=%lu wios=%lu dbytes=%lu dios=%lu",
45 		      &stat->mjr, &stat->mnr,
46 		      &stat->rbytes, &stat->wbytes, &stat->rios, &stat->wios,
47 		      &stat->dbytes, &stat->dios);
48 }
49 
run(void)50 static void run(void)
51 {
52 	int i, fd;
53 	char *line, *buf_ptr;
54 	const size_t pgsz = SAFE_SYSCONF(_SC_PAGESIZE);
55 	char *buf = SAFE_MALLOC(MAX((size_t)BUFSIZ, pgsz));
56 	struct io_stats start;
57 
58 	SAFE_CG_READ(tst_cg, "io.stat", buf, BUFSIZ - 1);
59 	line = strtok_r(buf, "\n", &buf_ptr);
60 	while (line) {
61 		const int convs = read_io_stats(line, &start);
62 
63 		if (convs < 2)
64 			continue;
65 
66 		tst_res(TINFO, "Found %u:%u in io.stat", dev_major, dev_minor);
67 
68 		if (start.mjr == dev_major || start.mnr == dev_minor)
69 			break;
70 
71 		line = strtok_r(NULL, "\n", &buf_ptr);
72 	}
73 
74 	SAFE_CG_PRINTF(tst_cg, "cgroup.procs", "%d", getpid());
75 
76 	fd = SAFE_OPEN("/dev/urandom", O_RDONLY, 0600);
77 	SAFE_READ(1, fd, buf, pgsz);
78 	SAFE_CLOSE(fd);
79 
80 	fd = SAFE_OPEN("mnt/dat", O_WRONLY | O_CREAT, 0600);
81 
82 	for (i = 0; i < 4; i++) {
83 		SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, pgsz);
84 		SAFE_FSYNC(fd);
85 		TST_EXP_PASS_SILENT(posix_fadvise(fd, pgsz * i, pgsz, POSIX_FADV_DONTNEED));
86 	}
87 
88 	SAFE_CLOSE(fd);
89 	fd = SAFE_OPEN("mnt/dat", O_RDONLY, 0600);
90 
91 	for (i = 0; i < 4; i++)
92 		SAFE_READ(1, fd, buf, pgsz);
93 
94 	tst_res(TPASS, "Did some IO in the IO controller");
95 
96 	SAFE_CG_READ(tst_cg, "io.stat", buf, BUFSIZ - 1);
97 	line = strtok_r(buf, "\n", &buf_ptr);
98 	while (line) {
99 		struct io_stats end;
100 		const int convs = read_io_stats(line, &end);
101 
102 		if (convs < 8)
103 			break;
104 
105 		if (end.mjr != dev_major || end.mnr != dev_minor) {
106 			line = strtok_r(NULL, "\n", &buf_ptr);
107 			continue;
108 		}
109 
110 		tst_res(TPASS, "Found %u:%u in io.stat", dev_major, dev_minor);
111 		TST_EXP_EXPR(end.rbytes > start.rbytes,
112 			     "(rbytes=%lu) > (st_rbytes=%lu)",
113 			     end.rbytes, start.rbytes);
114 		TST_EXP_EXPR(end.wbytes > start.wbytes,
115 			     "(wbytes=%lu) > (st_wbytes=%lu)",
116 			     end.wbytes, start.wbytes);
117 		TST_EXP_EXPR(end.rios > start.rios,
118 			     "(rios=%lu) > (st_rios=%lu)",
119 			     end.rios, start.rios);
120 		TST_EXP_EXPR(end.wios > start.wios,
121 			     "(wios=%lu) > (st_wios=%lu)",
122 			     end.wios, start.wios);
123 
124 		goto out;
125 	}
126 
127 	tst_res(TINFO, "io.stat:\n%s", buf);
128 	tst_res(TFAIL, "Did not find %u:%u in io.stat", dev_major, dev_minor);
129 out:
130 	free(buf);
131 	SAFE_CLOSE(fd);
132 	SAFE_UNLINK("mnt/dat");
133 }
134 
setup(void)135 static void setup(void)
136 {
137 	char buf[PATH_MAX] = { 0 };
138 	char *path = SAFE_GETCWD(buf, PATH_MAX - sizeof("mnt") - 1);
139 	struct stat st;
140 
141 	strcpy(path + strlen(path), "/mnt");
142 
143 	tst_stat_mount_dev(path, &st);
144 	dev_major = major(st.st_rdev);
145 	dev_minor = minor(st.st_rdev);
146 }
147 
148 static struct tst_test test = {
149 	.test_all = run,
150 	.setup = setup,
151 	.mntpoint = "mnt",
152 	.mount_device = 1,
153 	.all_filesystems = 1,
154 	.skip_filesystems = (const char *const[]){ "ntfs", "tmpfs", NULL },
155 	.needs_cgroup_ver = TST_CG_V2,
156 	.needs_cgroup_ctrls = (const char *const[]){ "io", NULL },
157 };
158