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 memset(&start, 0, sizeof(struct io_stats));
59 SAFE_CG_READ(tst_cg, "io.stat", buf, BUFSIZ - 1);
60 line = strtok_r(buf, "\n", &buf_ptr);
61 while (line) {
62 const int convs = read_io_stats(line, &start);
63
64 if (convs < 2)
65 continue;
66
67 tst_res(TINFO, "Found %u:%u in io.stat", dev_major, dev_minor);
68
69 if (start.mjr == dev_major || start.mnr == dev_minor)
70 break;
71
72 line = strtok_r(NULL, "\n", &buf_ptr);
73 }
74
75 SAFE_CG_PRINTF(tst_cg, "cgroup.procs", "%d", getpid());
76
77 fd = SAFE_OPEN("/dev/urandom", O_RDONLY, 0600);
78 SAFE_READ(1, fd, buf, pgsz);
79 SAFE_CLOSE(fd);
80
81 fd = SAFE_OPEN("mnt/dat", O_WRONLY | O_CREAT, 0600);
82
83 for (i = 0; i < 4; i++) {
84 SAFE_WRITE(SAFE_WRITE_ALL, fd, buf, pgsz);
85 SAFE_FSYNC(fd);
86 TST_EXP_PASS_SILENT(posix_fadvise(fd, pgsz * i, pgsz, POSIX_FADV_DONTNEED));
87 }
88
89 SAFE_CLOSE(fd);
90 fd = SAFE_OPEN("mnt/dat", O_RDONLY, 0600);
91
92 for (i = 0; i < 4; i++)
93 SAFE_READ(1, fd, buf, pgsz);
94
95 tst_res(TPASS, "Did some IO in the IO controller");
96
97 SAFE_CG_READ(tst_cg, "io.stat", buf, BUFSIZ - 1);
98 line = strtok_r(buf, "\n", &buf_ptr);
99 while (line) {
100 struct io_stats end;
101 const int convs = read_io_stats(line, &end);
102
103 if (convs < 8)
104 break;
105
106 if (end.mjr != dev_major || end.mnr != dev_minor) {
107 line = strtok_r(NULL, "\n", &buf_ptr);
108 continue;
109 }
110
111 tst_res(TPASS, "Found %u:%u in io.stat", dev_major, dev_minor);
112 TST_EXP_EXPR(end.rbytes > start.rbytes,
113 "(rbytes=%lu) > (st_rbytes=%lu)",
114 end.rbytes, start.rbytes);
115 TST_EXP_EXPR(end.wbytes > start.wbytes,
116 "(wbytes=%lu) > (st_wbytes=%lu)",
117 end.wbytes, start.wbytes);
118 TST_EXP_EXPR(end.rios > start.rios,
119 "(rios=%lu) > (st_rios=%lu)",
120 end.rios, start.rios);
121 TST_EXP_EXPR(end.wios > start.wios,
122 "(wios=%lu) > (st_wios=%lu)",
123 end.wios, start.wios);
124
125 goto out;
126 }
127
128 tst_res(TINFO, "io.stat:\n%s", buf);
129 tst_res(TFAIL, "Did not find %u:%u in io.stat", dev_major, dev_minor);
130 out:
131 free(buf);
132 SAFE_CLOSE(fd);
133 SAFE_UNLINK("mnt/dat");
134 }
135
setup(void)136 static void setup(void)
137 {
138 char buf[PATH_MAX] = { 0 };
139 char *path = SAFE_GETCWD(buf, PATH_MAX - sizeof("mnt") - 1);
140 struct stat st;
141
142 strcpy(path + strlen(path), "/mnt");
143
144 tst_stat_mount_dev(path, &st);
145 dev_major = major(st.st_rdev);
146 dev_minor = minor(st.st_rdev);
147 }
148
149 static struct tst_test test = {
150 .test_all = run,
151 .setup = setup,
152 .mntpoint = "mnt",
153 .mount_device = 1,
154 .all_filesystems = 1,
155 .skip_filesystems = (const char *const[]){ "ntfs", "tmpfs", NULL },
156 .needs_cgroup_ver = TST_CG_V2,
157 .needs_cgroup_ctrls = (const char *const[]){ "io", NULL },
158 };
159