1 /* 2 * 3 * Copyright (c) International Business Machines Corp., 2002 4 * Copyright (c) Cyril Hrubis chrubis (at) 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 ftest5, 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 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 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 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 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 */ 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 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 533 void cleanup(void) 534 { 535 536 tst_rmdir(); 537 } 538