xref: /aosp_15_r20/external/ltp/testcases/kernel/syscalls/preadv2/preadv203.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2019 Cyril Hrubis <[email protected]>
4  */
5 
6 /*
7  * This is a basic functional test for RWF_NOWAIT flag, we are attempting to
8  * force preadv2() either to return a short read or EAGAIN with three
9  * concurently running threads:
10  *
11  *  nowait_reader: reads from a random offset from a random file with
12  *                 RWF_NOWAIT flag and expects to get EAGAIN and short
13  *                 read sooner or later
14  *
15  *  writer_thread: rewrites random file in order to keep the underlying device
16  *                 busy so that pages evicted from cache cannot be faulted
17  *                 immediately
18  *
19  *  cache_dropper: attempts to evict pages from a cache in order for reader to
20  *                 hit evicted page sooner or later
21  */
22 
23 /*
24  * If test fails with EOPNOTSUPP you have likely hit a glibc bug:
25  *
26  * https://sourceware.org/bugzilla/show_bug.cgi?id=23579
27  *
28  * Which can be worked around by calling preadv2() directly by syscall() such as:
29  *
30  * static ssize_t sys_preadv2(int fd, const struct iovec *iov, int iovcnt,
31  *                            off_t offset, int flags)
32  * {
33  *	return syscall(SYS_preadv2, fd, iov, iovcnt, offset, offset>>32, flags);
34  * }
35  *
36  */
37 
38 #define _GNU_SOURCE
39 #include <string.h>
40 #include <sys/uio.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <ctype.h>
44 #include <pthread.h>
45 
46 #include "tst_test.h"
47 #include "tst_safe_pthread.h"
48 #include "lapi/preadv2.h"
49 
50 #define CHUNK_SZ 4123
51 #define CHUNKS 60
52 #define MNTPOINT "mntpoint"
53 #define FILES 500
54 
55 static int fds[FILES];
56 
57 static volatile int stop;
58 
drop_caches(void)59 static void drop_caches(void)
60 {
61 	SAFE_FILE_PRINTF("/proc/sys/vm/drop_caches", "3");
62 }
63 
64 /*
65  * All files are divided in chunks each filled with the same bytes starting with
66  * '0' at offset 0 and with increasing value on each next chunk.
67  *
68  * 000....000111....111.......AAA......AAA...
69  * | chunk0 || chunk1 |  ...  |  chunk17 |
70  */
verify_short_read(struct iovec * iov,size_t iov_cnt,off_t off,size_t size)71 static int verify_short_read(struct iovec *iov, size_t iov_cnt,
72 		             off_t off, size_t size)
73 {
74 	unsigned int i;
75 	size_t j, checked = 0;
76 
77 	for (i = 0; i < iov_cnt; i++) {
78 		char *buf = iov[i].iov_base;
79 		for (j = 0; j < iov[i].iov_len; j++) {
80 			char exp_val = '0' + (off + checked)/CHUNK_SZ;
81 
82 			if (exp_val != buf[j]) {
83 				tst_res(TFAIL,
84 				        "Wrong value read pos %zu size %zu %c (%i) %c (%i)!",
85 				        checked, size, exp_val, exp_val,
86 					isprint(buf[j]) ? buf[j] : ' ', buf[j]);
87 				return 1;
88 			}
89 
90 			if (++checked >= size)
91 				return 0;
92 		}
93 	}
94 
95 	return 0;
96 }
97 
nowait_reader(void * unused LTP_ATTRIBUTE_UNUSED)98 static void *nowait_reader(void *unused LTP_ATTRIBUTE_UNUSED)
99 {
100 	char buf1[CHUNK_SZ/2];
101 	char buf2[CHUNK_SZ];
102 	unsigned int full_read_cnt = 0, eagain_cnt = 0;
103 	unsigned int short_read_cnt = 0, zero_read_cnt = 0;
104 
105 	struct iovec rd_iovec[] = {
106 		{buf1, sizeof(buf1)},
107 		{buf2, sizeof(buf2)},
108 	};
109 
110 	while (!stop) {
111 		if (eagain_cnt >= 100 && short_read_cnt >= 10)
112 			stop = 1;
113 
114 		/* Ensure short reads doesn't happen because of tripping on EOF */
115 		off_t off = random() % ((CHUNKS - 2) * CHUNK_SZ);
116 		int fd = fds[random() % FILES];
117 
118 		TEST(preadv2(fd, rd_iovec, 2, off, RWF_NOWAIT));
119 
120 		if (TST_RET < 0) {
121 			if (TST_ERR != EAGAIN)
122 				tst_brk(TBROK | TTERRNO, "preadv2() failed");
123 
124 			eagain_cnt++;
125 			continue;
126 		}
127 
128 
129 		if (TST_RET == 0) {
130 			zero_read_cnt++;
131 			continue;
132 		}
133 
134 		if (TST_RET != CHUNK_SZ + CHUNK_SZ/2) {
135 			verify_short_read(rd_iovec, 2, off, TST_RET);
136 			short_read_cnt++;
137 			continue;
138 		}
139 
140 		full_read_cnt++;
141 	}
142 
143 	tst_res(TINFO,
144 	        "Number of full_reads %u, short reads %u, zero len reads %u, EAGAIN(s) %u",
145 		full_read_cnt, short_read_cnt, zero_read_cnt, eagain_cnt);
146 
147 	return (void*)(long)eagain_cnt;
148 }
149 
writer_thread(void * unused)150 static void *writer_thread(void *unused)
151 {
152 	char buf[CHUNK_SZ];
153 	unsigned int j, write_cnt = 0;
154 
155 	struct iovec wr_iovec[] = {
156 		{buf, sizeof(buf)},
157 	};
158 
159 	while (!stop) {
160 		int fd = fds[random() % FILES];
161 
162 		for (j = 0; j < CHUNKS && !stop; j++) {
163 			memset(buf, '0' + j, sizeof(buf));
164 
165 			off_t off = CHUNK_SZ * j;
166 
167 			if (pwritev(fd, wr_iovec, 1, off) < 0) {
168 				if (errno == EBADF) {
169 					tst_res(TINFO | TERRNO, "FDs closed, exiting...");
170 					return unused;
171 				}
172 
173 				tst_brk(TBROK | TERRNO, "pwritev()");
174 			}
175 
176 			write_cnt++;
177 		}
178 	}
179 
180 	tst_res(TINFO, "Number of writes %u", write_cnt);
181 
182 	return unused;
183 }
184 
cache_dropper(void * unused)185 static void *cache_dropper(void *unused)
186 {
187 	unsigned int drop_cnt = 0;
188 
189 	while (!stop) {
190 		drop_caches();
191 		drop_cnt++;
192 	}
193 
194 	tst_res(TINFO, "Cache dropped %u times", drop_cnt);
195 
196 	return unused;
197 }
198 
verify_preadv2(void)199 static void verify_preadv2(void)
200 {
201 	pthread_t reader, dropper, writer;
202 	void *eagains;
203 
204 	stop = 0;
205 
206 	drop_caches();
207 
208 	SAFE_PTHREAD_CREATE(&dropper, NULL, cache_dropper, NULL);
209 	SAFE_PTHREAD_CREATE(&reader, NULL, nowait_reader, NULL);
210 	SAFE_PTHREAD_CREATE(&writer, NULL, writer_thread, NULL);
211 
212 	while (!stop && tst_remaining_runtime())
213 		usleep(100000);
214 
215 	stop = 1;
216 
217 	SAFE_PTHREAD_JOIN(reader, &eagains);
218 	SAFE_PTHREAD_JOIN(dropper, NULL);
219 	SAFE_PTHREAD_JOIN(writer, NULL);
220 
221 	if (eagains)
222 		tst_res(TPASS, "Got some EAGAIN");
223 	else
224 		tst_res(TFAIL, "Haven't got EAGAIN");
225 }
226 
check_preadv2_nowait(int fd)227 static void check_preadv2_nowait(int fd)
228 {
229 	char buf[1];
230 	struct iovec iovec[] = {
231 		{buf, sizeof(buf)},
232 	};
233 
234 	TEST(preadv2(fd, iovec, 1, 0, RWF_NOWAIT));
235 
236 	if (TST_ERR == EOPNOTSUPP)
237 		tst_brk(TCONF | TTERRNO, "preadv2()");
238 }
239 
setup(void)240 static void setup(void)
241 {
242 	char path[1024];
243 	char buf[CHUNK_SZ];
244 	unsigned int i;
245 	char j;
246 
247 	for (i = 0; i < FILES; i++) {
248 		snprintf(path, sizeof(path), MNTPOINT"/file_%i", i);
249 
250 		fds[i] = SAFE_OPEN(path, O_RDWR | O_CREAT, 0644);
251 
252 		if (i == 0)
253 			check_preadv2_nowait(fds[i]);
254 
255 		for (j = 0; j < CHUNKS; j++) {
256 			memset(buf, '0' + j, sizeof(buf));
257 			SAFE_WRITE(SAFE_WRITE_RETRY, fds[i], buf, sizeof(buf));
258 		}
259 	}
260 }
261 
do_cleanup(void)262 static void do_cleanup(void)
263 {
264 	unsigned int i;
265 
266 	for (i = 0; i < FILES; i++) {
267 		if (fds[i] > 0)
268 			SAFE_CLOSE(fds[i]);
269 	}
270 }
271 
272 TST_DECLARE_ONCE_FN(cleanup, do_cleanup);
273 
274 static struct tst_test test = {
275 	.setup = setup,
276 	.cleanup = cleanup,
277 	.test_all = verify_preadv2,
278 	.mntpoint = MNTPOINT,
279 	.mount_device = 1,
280 	.all_filesystems = 1,
281 	.max_runtime = 60,
282 	.needs_root = 1,
283 };
284