1 /*
2 *
3 * Copyright (c) International Business Machines Corp., 2002
4 * Copyright (c) Cyril Hrubis chrubis@suse.cz 2009
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
14 * the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 /*
22 * NAME
23 * ftest08.c -- test single file io (tsfio.c by rbk) (ported from SPIE,
24 * section2/filesuite/ftest10.c, by Airong Zhang)
25 *
26 * this is the same as ftest4, except that it uses lseek64
27 *
28 * CALLS
29 * fsync, sync, lseek64, read, write
30 *
31 *
32 * ALGORITHM
33 * Several child processes doing random seeks, read/write
34 * operations on the same file.
35 *
36 *
37 * RESTRICTIONS
38 * Runs a long time with default args - can take others on input
39 * line. Use with "term mode".
40 *
41 */
42
43 #define _XOPEN_SOURCE 500
44 #define _LARGEFILE64_SOURCE 1
45 #include <stdio.h>
46 #include <sys/types.h>
47 #include <sys/param.h>
48 #include <sys/wait.h>
49 #include <sys/file.h>
50 #include <fcntl.h>
51 #include <sys/stat.h>
52 #include <sys/uio.h>
53 #include <errno.h>
54 #include <signal.h>
55 #include <unistd.h>
56 #include <inttypes.h>
57 #include "test.h"
58 #include "safe_macros.h"
59 #include "libftest.h"
60
61 char *TCID = "ftest08";
62 int TST_TOTAL = 1;
63
64 #define PASSED 1
65 #define FAILED 0
66
67 #define MAXCHILD 25
68 #define K_1 1024
69 #define K_2 2048
70 #define K_4 4096
71 #define MAXIOVCNT 16
72
73 static void init(void);
74 static void runtest(void);
75 static void dotest(int, int, int);
76 static void domisc(int, int);
77 static void term(int sig);
78 static void cleanup(void);
79
80 static int csize; /* chunk size */
81 static int iterations; /* # total iterations */
82 static off64_t max_size; /* max file size */
83 static int misc_intvl; /* for doing misc things; 0 ==> no */
84 static int nchild; /* number of child processes */
85 static int parent_pid;
86 static int pidlist[MAXCHILD];
87
88 static char filename[MAXPATHLEN];
89 static int local_flag;
90
main(int ac,char * av[])91 int main(int ac, char *av[])
92 {
93 int lc;
94
95 tst_parse_opts(ac, av, NULL, NULL);
96
97 for (lc = 0; TEST_LOOPING(lc); lc++) {
98
99 local_flag = PASSED;
100 init();
101 runtest();
102
103 if (local_flag == PASSED)
104 tst_resm(TPASS, "Test passed.");
105 else
106 tst_resm(TFAIL, "Test failed.");
107 }
108
109 cleanup();
110 tst_exit();
111 }
112
init(void)113 static void init(void)
114 {
115 int fd;
116 char wdbuf[MAXPATHLEN];
117
118 parent_pid = getpid();
119 tst_tmpdir();
120
121 /*
122 * Make a filename for the test.
123 */
124 if (!filename[0])
125 sprintf(filename, "%s/ftest08.%d", getcwd(wdbuf, MAXPATHLEN),
126 getpid());
127
128 fd = SAFE_OPEN(NULL, filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
129
130 close(fd);
131
132 /*
133 * Default values for run conditions.
134 */
135 iterations = 10;
136 nchild = 5;
137 csize = K_2; /* should run with 1, 2, and 4 K sizes */
138 max_size = K_1 * K_1;
139 misc_intvl = 10;
140
141 if (sigset(SIGTERM, term) == SIG_ERR) {
142 tst_brkm(TBROK | TERRNO, NULL, "first sigset failed");
143 }
144
145 }
146
runtest(void)147 static void runtest(void)
148 {
149 int child, count, fd, i, nwait, status;
150
151 nwait = 0;
152
153 for (i = 0; i < nchild; i++) {
154
155 if ((child = fork()) == 0) {
156 fd = open(filename, O_RDWR);
157 if (fd < 0) {
158 tst_brkm(TFAIL,
159 NULL,
160 "\tTest[%d]: error %d openning %s.",
161 i,
162 errno, filename);
163 }
164 dotest(nchild, i, fd);
165 close(fd);
166 tst_exit();
167 }
168
169 if (child < 0) {
170 tst_brkm(TBROK | TERRNO, NULL, "fork failed");
171 } else {
172 pidlist[i] = child;
173 nwait++;
174 }
175 }
176
177 /*
178 * Wait for children to finish.
179 */
180 count = 0;
181 while ((child = wait(&status)) != -1 || errno == EINTR) {
182 if (child > 0) {
183 //tst_resm(TINFO, "\tTest{%d} exited status = 0x%x", child, status);
184 if (status) {
185 tst_resm(TFAIL,
186 "\tExpected 0 exit status - failed.");
187 local_flag = FAILED;
188 }
189 ++count;
190 }
191 }
192
193 /*
194 * Should have collected all children.
195 */
196 if (count != nwait) {
197 tst_resm(TFAIL, "\tWrong # children waited on, count = %d",
198 count);
199 local_flag = FAILED;
200 }
201
202 unlink(filename);
203 sync();
204 }
205
206 /*
207 * dotest()
208 * Children execute this.
209 *
210 * Randomly read/mod/write chunks with known pattern and check.
211 * When fill sectors, iterate.
212 */
213 #define NMISC 2
214 enum m_type { m_fsync, m_sync };
215 char *m_str[] = { "fsync", "sync" };
216
217 int misc_cnt[NMISC]; /* counts # of each kind of misc */
218 int misc_flag;
219 int nchunks;
220
221 #define CHUNK(i) ((((off64_t)i) * testers + me) * csize)
222 #define NEXTMISC ((rand() % misc_intvl) + 5)
223
dotest(int testers,int me,int fd)224 static void dotest(int testers, int me, int fd)
225 {
226 char *bits;
227 char val, val0;
228 int count, collide, chunk, whenmisc, xfr, i;
229
230 /* Stuff for the readv call */
231 struct iovec r_iovec[MAXIOVCNT];
232 int r_ioveclen;
233
234 /* Stuff for the writev call */
235 struct iovec val0_iovec[MAXIOVCNT];
236 struct iovec val_iovec[MAXIOVCNT];
237 int w_ioveclen;
238 struct stat stat;
239
240 nchunks = max_size / (testers * csize);
241 whenmisc = 0;
242
243 if ((bits = malloc((nchunks + 7) / 8)) == NULL) {
244 tst_brkm(TBROK, NULL, "\tmalloc failed(bits)");
245 }
246
247 /* Allocate memory for the iovec buffers and init the iovec arrays */
248 r_ioveclen = w_ioveclen = csize / MAXIOVCNT;
249
250 /* Please note that the above statement implies that csize
251 * be evenly divisible by MAXIOVCNT.
252 */
253 for (i = 0; i < MAXIOVCNT; i++) {
254 if ((r_iovec[i].iov_base = malloc(r_ioveclen)) == NULL) {
255 tst_brkm(TBROK, NULL, "\tmalloc failed(iov_base)");
256 }
257 r_iovec[i].iov_len = r_ioveclen;
258
259 /* Allocate unused memory areas between all the buffers to
260 * make things more diffult for the OS.
261 */
262 if (malloc((i + 1) * 8) == NULL) {
263 tst_brkm(TBROK, NULL, "\tmalloc failed((i+1)*8)");
264 }
265
266 if ((val0_iovec[i].iov_base = malloc(w_ioveclen)) == NULL) {
267 tst_brkm(TBROK, NULL, "\tmalloc failed(val0_iovec)");
268 }
269
270 val0_iovec[i].iov_len = w_ioveclen;
271
272 if (malloc((i + 1) * 8) == NULL) {
273 tst_brkm(TBROK, NULL, "\tmalloc failed((i+1)*8)");
274 }
275
276 if ((val_iovec[i].iov_base = malloc(w_ioveclen)) == NULL) {
277 tst_brkm(TBROK, NULL, "\tmalloc failed(iov_base)");
278 }
279 val_iovec[i].iov_len = w_ioveclen;
280
281 if (malloc((i + 1) * 8) == NULL) {
282 tst_brkm(TBROK, NULL, "\tmalloc failed(((i+1)*8)");
283 }
284 }
285
286 /*
287 * No init sectors; file-sys makes 0 to start.
288 */
289 val = (64 / testers) * me + 1;
290 val0 = 0;
291
292 /*
293 * For each iteration:
294 * zap bits array
295 * loop:
296 * pick random chunk, read it.
297 * if corresponding bit off {
298 * verify == 0. (sparse file)
299 * ++count;
300 * } else
301 * verify == val.
302 * write "val" on it.
303 * repeat until count = nchunks.
304 * ++val.
305 */
306 srand(getpid());
307
308 if (misc_intvl)
309 whenmisc = NEXTMISC;
310
311 while (iterations-- > 0) {
312 for (i = 0; i < NMISC; i++)
313 misc_cnt[i] = 0;
314 memset(bits, 0, (nchunks + 7) / 8);
315 /* Have to fill the val0 and val iov buffers in a different manner
316 */
317 for (i = 0; i < MAXIOVCNT; i++) {
318 memset(val0_iovec[i].iov_base, val0,
319 val0_iovec[i].iov_len);
320 memset(val_iovec[i].iov_base, val,
321 val_iovec[i].iov_len);
322
323 }
324
325 count = 0;
326 collide = 0;
327
328 while (count < nchunks) {
329 chunk = rand() % nchunks;
330 /*
331 * Read it.
332 */
333 if (lseek64(fd, CHUNK(chunk), 0) < 0) {
334 tst_brkm(TFAIL,
335 NULL, "\tTest[%d]: lseek64(0) fail at %"
336 PRIx64 "x, errno = %d.", me,
337 CHUNK(chunk), errno);
338 }
339 if ((xfr = readv(fd, &r_iovec[0], MAXIOVCNT)) < 0) {
340 tst_brkm(TFAIL,
341 NULL, "\tTest[%d]: readv fail at %" PRIx64
342 "x, errno = %d.", me, CHUNK(chunk),
343 errno);
344 }
345 /*
346 * If chunk beyond EOF just write on it.
347 * Else if bit off, haven't seen it yet.
348 * Else, have. Verify values.
349 */
350 if (xfr == 0) {
351 bits[chunk / 8] |= (1 << (chunk % 8));
352 } else if ((bits[chunk / 8] & (1 << (chunk % 8))) == 0) {
353 if (xfr != csize) {
354 tst_brkm(TFAIL,
355 NULL,
356 "\tTest[%d]: xfr=%d != %d, zero read.",
357 me, xfr, csize);
358 }
359 for (i = 0; i < MAXIOVCNT; i++) {
360 if (memcmp
361 (r_iovec[i].iov_base,
362 val0_iovec[i].iov_base,
363 r_iovec[i].iov_len)) {
364 tst_resm(TFAIL,
365 "\tTest[%d] bad verify @ 0x%"
366 PRIx64
367 " for val %d count %d xfr %d.",
368 me, CHUNK(chunk), val0,
369 count, xfr);
370 fstat(fd, &stat);
371 tst_resm(TINFO,
372 "\tStat: size=%llx, ino=%x",
373 stat.st_size, (unsigned)stat.st_ino);
374 ft_dumpiov(&r_iovec[i]);
375 ft_dumpbits(bits,
376 (nchunks + 7) / 8);
377 tst_exit();
378 }
379 }
380 bits[chunk / 8] |= (1 << (chunk % 8));
381 ++count;
382 } else {
383 if (xfr != csize) {
384 tst_brkm(TFAIL,
385 NULL,
386 "\tTest[%d]: xfr=%d != %d, val read.",
387 me, xfr, csize);
388 }
389 ++collide;
390 for (i = 0; i < MAXIOVCNT; i++) {
391 if (memcmp
392 (r_iovec[i].iov_base,
393 val_iovec[i].iov_base,
394 r_iovec[i].iov_len)) {
395 tst_resm(TFAIL,
396 "\tTest[%d] bad verify @ 0x%"
397 PRIx64
398 " for val %d count %d xfr %d.",
399 me, CHUNK(chunk), val,
400 count, xfr);
401 fstat(fd, &stat);
402 tst_resm(TINFO,
403 "\tStat: size=%llx, ino=%x",
404 stat.st_size, (unsigned)stat.st_ino);
405 ft_dumpiov(&r_iovec[i]);
406 ft_dumpbits(bits,
407 (nchunks + 7) / 8);
408 tst_exit();
409 }
410 }
411 }
412 /*
413 * Write it.
414 */
415 if (lseek64(fd, -xfr, 1) < 0) {
416 tst_brkm(TFAIL,
417 NULL, "\tTest[%d]: lseek64(1) fail at %"
418 PRIx64 ", errno = %d.", me,
419 CHUNK(chunk), errno);
420 }
421 if ((xfr =
422 writev(fd, &val_iovec[0], MAXIOVCNT)) < csize) {
423 if (errno == ENOSPC) {
424 tst_resm(TFAIL,
425 "\tTest[%d]: no space, exiting.",
426 me);
427 fsync(fd);
428 tst_exit();
429 }
430 tst_brkm(TFAIL,
431 NULL, "\tTest[%d]: writev fail at %" PRIx64
432 "x xfr %d, errno = %d.", me,
433 CHUNK(chunk), xfr, errno);
434 }
435 /*
436 * If hit "misc" interval, do it.
437 */
438 if (misc_intvl && --whenmisc <= 0) {
439 domisc(me, fd);
440 whenmisc = NEXTMISC;
441 }
442 if (count + collide > 2 * nchunks)
443 break;
444 }
445
446 /*
447 * End of iteration, maybe before doing all chunks.
448 */
449
450 if (count < nchunks) {
451 //tst_resm(TINFO, "\tTest{%d} val %d stopping @ %d, collide = {%d}.",
452 // me, val, count, collide);
453 for (i = 0; i < nchunks; i++) {
454 if ((bits[i / 8] & (1 << (i % 8))) == 0) {
455 if (lseek64(fd, CHUNK(i), 0) <
456 (off64_t) 0) {
457 tst_brkm(TFAIL,
458 NULL, "\tTest[%d]: lseek64 fail at %"
459 PRIx64
460 "x, errno = %d.", me,
461 CHUNK(i), errno);
462 }
463 if (writev(fd, &val_iovec[0], MAXIOVCNT)
464 != csize) {
465 tst_brkm(TFAIL,
466 NULL, "\tTest[%d]: writev fail at %"
467 PRIx64
468 "x, errno = %d.", me,
469 CHUNK(i), errno);
470 }
471 }
472 }
473 }
474
475 fsync(fd);
476 ++misc_cnt[m_fsync];
477 //tst_resm(TINFO, "\tTest[%d] val %d done, count = %d, collide = %d.",
478 // me, val, count, collide);
479 //for (i = 0; i < NMISC; i++)
480 // tst_resm(TINFO, "\t\tTest[%d]: %d %s's.", me, misc_cnt[i], m_str[i]);
481 val0 = val++;
482 }
483 }
484
485 /*
486 * domisc()
487 * Inject misc syscalls into the thing.
488 */
domisc(int me,int fd)489 static void domisc(int me, int fd)
490 {
491 enum m_type type;
492
493 if (misc_flag) {
494 type = m_fsync;
495 misc_flag = 0;
496 } else {
497 type = m_sync;;
498 misc_flag = 1;
499 }
500
501 switch (type) {
502 case m_fsync:
503 if (fsync(fd) < 0) {
504 tst_brkm(TFAIL, NULL, "\tTest[%d]: fsync error %d.",
505 me,
506 errno);
507 }
508 break;
509 case m_sync:
510 sync();
511 break;
512 }
513
514 ++misc_cnt[type];
515 }
516
term(int sig LTP_ATTRIBUTE_UNUSED)517 static void term(int sig LTP_ATTRIBUTE_UNUSED)
518 {
519 int i;
520
521 tst_resm(TINFO, "\tterm -[%d]- got sig term.", getpid());
522
523 if (parent_pid == getpid()) {
524 for (i = 0; i < nchild; i++)
525 if (pidlist[i])
526 kill(pidlist[i], SIGTERM);
527 return;
528 }
529
530 tst_exit();
531 }
532
cleanup(void)533 void cleanup(void)
534 {
535
536 tst_rmdir();
537 }
538