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