1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (c) 2004 Daniel McNeil <daniel@osdl.org>
4 * 2004 Open Source Development Lab
5 *
6 * Copyright (c) 2004 Marty Ridgeway <mridge@us.ibm.com>
7 *
8 * Copyright (c) 2011 Cyril Hrubis <chrubis@suse.cz>
9 * Copyright (C) 2021 SUSE LLC Andrea Cervesato <andrea.cervesato@suse.com>
10 */
11
12 /*\
13 * [Description]
14 *
15 * Create a sparse file and write zeroes to it using libaio while other
16 * processes are doing buffered reads and check if the buffer reads always see
17 * zero.
18 */
19
20 #define _GNU_SOURCE
21
22 #include "tst_test.h"
23
24 #ifdef HAVE_LIBAIO
25 #include <stdlib.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28 #include <libaio.h>
29 #include "common.h"
30
31 static volatile int *run_child;
32
33 static char *str_numchildren;
34 static char *str_writesize;
35 static char *str_filesize;
36 static char *str_numaio;
37
38 static int numchildren = 16;
39 static long long writesize = 1024;
40 static long long filesize = 100 * 1024 * 1024;
41 static long long alignment;
42 static int numaio = 16;
43
check_event(struct io_event event)44 static void check_event(struct io_event event)
45 {
46 struct iocb *iocbp;
47
48 iocbp = (struct iocb *)event.obj;
49 if (event.res2 != 0 || event.res != iocbp->u.c.nbytes) {
50 tst_brk(TBROK, "AIO write offset %lld expected %ld got %ld",
51 iocbp->u.c.offset, iocbp->u.c.nbytes, event.res);
52 }
53 }
54
aiodio_sparse(char * filename,long long align,long long ws,long long fs,int naio)55 static void aiodio_sparse(char *filename, long long align, long long ws,
56 long long fs, int naio)
57 {
58 int fd;
59 int i, w;
60 struct iocb **iocbs;
61 struct iocb *iocb;
62 off_t offset;
63 io_context_t myctx;
64 struct io_event event;
65 int aio_inflight;
66
67 fd = SAFE_OPEN(filename, O_DIRECT | O_WRONLY | O_CREAT, 0666);
68 SAFE_FTRUNCATE(fd, fs);
69
70 memset(&myctx, 0, sizeof(myctx));
71 io_queue_init(naio, &myctx);
72
73 iocbs = SAFE_MALLOC(sizeof(struct iocb *) * naio);
74 iocb = SAFE_MALLOC(sizeof(struct iocb) * naio);
75
76 for (i = 0; i < naio; i++)
77 iocbs[i] = iocb + i;
78
79 offset = 0;
80 for (i = 0; i < naio; i++) {
81 void *bufptr;
82
83 bufptr = SAFE_MEMALIGN(align, ws);
84 memset(bufptr, 0, ws);
85 io_prep_pwrite(iocbs[i], fd, bufptr, ws, offset);
86 offset += ws;
87 }
88
89 w = io_submit(myctx, naio, iocbs);
90 if (w < 0)
91 tst_brk(TBROK, "io_submit: %s", tst_strerrno(-w));
92
93 aio_inflight = naio;
94
95 while (offset < fs) {
96 int n;
97 struct iocb *iocbp;
98
99 n = io_getevents(myctx, 1, 1, &event, 0);
100
101 if (-n == EINTR)
102 continue;
103
104 if (n != 1)
105 tst_brk(TBROK, "io_getevents: %s", tst_strerrno(-n));
106
107 aio_inflight--;
108
109 check_event(event);
110
111 /* start next write */
112 iocbp = (struct iocb *)event.obj;
113
114 io_prep_pwrite(iocbp, fd, iocbp->u.c.buf, ws, offset);
115 offset += ws;
116 w = io_submit(myctx, 1, &iocbp);
117 if (w < 0)
118 tst_brk(TBROK, "io_submit: %s", tst_strerrno(-w));
119
120 aio_inflight++;
121 }
122
123 while (aio_inflight > 0) {
124 int n;
125
126 n = io_getevents(myctx, 1, 1, &event, 0);
127
128 if (-n == EINTR)
129 continue;
130
131 if (n != 1)
132 tst_brk(TBROK, "io_getevents failed");
133
134 aio_inflight--;
135
136 check_event(event);
137 }
138
139 free(iocb);
140 free(iocbs);
141 }
142
setup(void)143 static void setup(void)
144 {
145 struct stat sb;
146
147 if (tst_parse_int(str_numchildren, &numchildren, 1, INT_MAX)) {
148 tst_brk(TBROK, "Invalid number of children '%s'",
149 str_numchildren);
150 }
151
152 if (tst_parse_filesize(str_writesize, &writesize, 1, LLONG_MAX))
153 tst_brk(TBROK, "Invalid write blocks size '%s'", str_writesize);
154
155 if (tst_parse_filesize(str_filesize, &filesize, 1, LLONG_MAX))
156 tst_brk(TBROK, "Invalid file size '%s'", str_filesize);
157
158 if (tst_parse_int(str_numaio, &numaio, 1, INT_MAX)) {
159 tst_brk(TBROK, "Invalid number of AIO control blocks '%s'",
160 str_numaio);
161 }
162
163 if ((numaio * writesize) > filesize) {
164 numaio = filesize / writesize;
165 tst_res(TINFO,
166 "Numbers of AIO have been reduced to %d so we fit filesize",
167 numaio);
168 }
169
170 SAFE_STAT(".", &sb);
171 alignment = sb.st_blksize;
172
173 run_child = SAFE_MMAP(NULL, sizeof(int), PROT_READ | PROT_WRITE,
174 MAP_SHARED | MAP_ANONYMOUS, -1, 0);
175
176 tst_res(TINFO, "Dirtying free blocks");
177 dirty_freeblocks(filesize);
178 }
179
cleanup(void)180 static void cleanup(void)
181 {
182 if (run_child) {
183 *run_child = 0;
184 SAFE_MUNMAP((void *)run_child, sizeof(int));
185 }
186 }
187
run(void)188 static void run(void)
189 {
190 char *filename = "file.bin";
191 int i, pid;
192
193 *run_child = 1;
194
195 for (i = 0; i < numchildren; i++) {
196 if (!SAFE_FORK()) {
197 io_read(filename, filesize, run_child);
198 return;
199 }
200 }
201
202 pid = SAFE_FORK();
203 if (!pid) {
204 aiodio_sparse(filename, alignment, writesize, filesize, numaio);
205 return;
206 }
207
208 tst_res(TINFO, "Child %i creates a sparse file", pid);
209
210 for (;;) {
211 if (SAFE_WAITPID(pid, NULL, WNOHANG))
212 break;
213
214 sleep(1);
215
216 if (!tst_remaining_runtime()) {
217 tst_res(TINFO, "Test out of runtime, exiting");
218 kill(pid, SIGKILL);
219 SAFE_WAITPID(pid, NULL, 0);
220 break;
221 }
222 }
223
224 *run_child = 0;
225
226 if (!tst_validate_children(numchildren))
227 tst_res(TPASS, "All bytes read were zeroed");
228 }
229
230 static struct tst_test test = {
231 .test_all = run,
232 .setup = setup,
233 .cleanup = cleanup,
234 .needs_tmpdir = 1,
235 .forks_child = 1,
236 .options = (struct tst_option[]) {
237 {"n:", &str_numchildren, "Number of threads (default 16)"},
238 {"w:", &str_writesize, "Size of writing blocks (default 1K)"},
239 {"s:", &str_filesize, "Size of file (default 100M)"},
240 {"o:", &str_numaio, "Number of AIO control blocks (default 16)"},
241 {},
242 },
243 .skip_filesystems = (const char *[]) {
244 "tmpfs",
245 NULL
246 },
247 .max_runtime = 1800,
248 };
249 #else
250 TST_TEST_TCONF("test requires libaio and its development packages");
251 #endif
252