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