1#!/bin/sh 2 3# 4# Copyright (c) 2008 Peter Holm <pho@FreeBSD.org> 5# All rights reserved. 6# 7# Redistribution and use in source and binary forms, with or without 8# modification, are permitted provided that the following conditions 9# are met: 10# 1. Redistributions of source code must retain the above copyright 11# notice, this list of conditions and the following disclaimer. 12# 2. Redistributions in binary form must reproduce the above copyright 13# notice, this list of conditions and the following disclaimer in the 14# documentation and/or other materials provided with the distribution. 15# 16# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26# SUCH DAMAGE. 27# 28 29# Test scenario by kris@freebsd.org 30 31# Test problems with "umount -f and fsx. Results in a "KDB: enter: watchdog timeout" 32 33# http://people.freebsd.org/~pho/stress/log/kostik745.txt 34# Fixed by r275743 35 36[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1 37 38. ../default.cfg 39 40sed '1,/^EOF/d' < $0 > /tmp/fsx.c 41mycc -o /tmp/fsx -O2 /tmp/fsx.c || exit 1 42rm -f /tmp/fsx.c 43 44D=$diskimage 45dd if=/dev/zero of=$D bs=1m count=1k status=none || exit 1 46 47mount | grep "$mntpoint" | grep md${mdstart}$part > /dev/null && umount $mntpoint 48mdconfig -l | grep md$mdstart > /dev/null && mdconfig -d -u $mdstart 49 50mdconfig -a -t vnode -f $D -u $mdstart 51bsdlabel -w md$mdstart auto 52newfs md${mdstart}$part > /dev/null 2>&1 53mount /dev/md${mdstart}$part $mntpoint 54sleep 5 55for i in `jot 100`; do 56 /tmp/fsx -S $i -q $mntpoint/xxx$i > /dev/null & 57done 58sleep 30 59umount -f $mntpoint & 60for i in `jot 30`; do 61 sleep 30 62 pgrep -q fsx || break 63done 64if pgrep -q fsx; then 65 echo FAIL 66 ps -l $! 67 pkill fsx 68fi 69sleep 5 70wait 71mdconfig -d -u $mdstart 72rm -f $D /tmp/fsx 73exit 0 74EOF 75/* 76 * Copyright (c) 1998-2001 Apple Computer, Inc. All rights reserved. 77 * 78 * @APPLE_LICENSE_HEADER_START@ 79 * 80 * The contents of this file constitute Original Code as defined in and 81 * are subject to the Apple Public Source License Version 2.0 (the 82 * "License"). You may not use this file except in compliance with the 83 * License. Please obtain a copy of the License at 84 * http://www.opensource.apple.com/apsl/ and read it before using this file. 85 * 86 * This Original Code and all software distributed under the License are 87 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 88 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 89 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 90 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 91 * License for the specific language governing rights and limitations 92 * under the License. 93 * 94 * @APPLE_LICENSE_HEADER_END@ 95 * 96 * File: fsx.c 97 * Author: Avadis Tevanian, Jr. 98 * 99 * File system exerciser. 100 * 101 * Rewrite and enhancements 1998-2001 Conrad Minshall -- conrad@mac.com 102 * 103 * Various features from Joe Sokol, Pat Dirks, and Clark Warner. 104 * 105 * Small changes to work under Linux -- davej@suse.de 106 * 107 * Sundry porting patches from Guy Harris 12/2001 108 * 109 * Checks for mmap last-page zero fill. 110 * 111 * Updated license to APSL 2.0, 2004/7/27 - Jordan Hubbard 112 * 113 */ 114 115#include <sys/types.h> 116#include <sys/stat.h> 117#ifdef _UWIN 118# include <sys/param.h> 119# include <limits.h> 120# include <time.h> 121# include <strings.h> 122#endif 123#include <fcntl.h> 124#include <sys/mman.h> 125#ifndef MAP_FILE 126# define MAP_FILE 0 127#endif 128#include <limits.h> 129#include <signal.h> 130#include <stdio.h> 131#include <stdlib.h> 132#include <string.h> 133#include <unistd.h> 134#include <stdarg.h> 135#include <errno.h> 136 137#define NUMPRINTCOLUMNS 32 /* # columns of data to print on each line */ 138 139/* 140 * A log entry is an operation and a bunch of arguments. 141 */ 142 143struct log_entry { 144 int operation; 145 int args[3]; 146}; 147 148#define LOGSIZE 1000 149 150struct log_entry oplog[LOGSIZE]; /* the log */ 151int logptr = 0; /* current position in log */ 152int logcount = 0; /* total ops */ 153 154/* 155 * Define operations 156 */ 157 158#define OP_READ 1 159#define OP_WRITE 2 160#define OP_TRUNCATE 3 161#define OP_CLOSEOPEN 4 162#define OP_MAPREAD 5 163#define OP_MAPWRITE 6 164#define OP_SKIPPED 7 165#define OP_INVALIDATE 8 166 167int page_size; 168int page_mask; 169 170char *original_buf; /* a pointer to the original data */ 171char *good_buf; /* a pointer to the correct data */ 172char *temp_buf; /* a pointer to the current data */ 173char *fname; /* name of our test file */ 174int fd; /* fd for our test file */ 175 176off_t file_size = 0; 177off_t biggest = 0; 178char state[256]; 179unsigned long testcalls = 0; /* calls to function "test" */ 180 181unsigned long simulatedopcount = 0; /* -b flag */ 182int closeprob = 0; /* -c flag */ 183int invlprob = 0; /* -i flag */ 184int debug = 0; /* -d flag */ 185unsigned long debugstart = 0; /* -D flag */ 186unsigned long maxfilelen = 256 * 1024; /* -l flag */ 187int sizechecks = 1; /* -n flag disables them */ 188int maxoplen = 64 * 1024; /* -o flag */ 189int quiet = 0; /* -q flag */ 190unsigned long progressinterval = 0; /* -p flag */ 191int readbdy = 1; /* -r flag */ 192int style = 0; /* -s flag */ 193int truncbdy = 1; /* -t flag */ 194int writebdy = 1; /* -w flag */ 195long monitorstart = -1; /* -m flag */ 196long monitorend = -1; /* -m flag */ 197int lite = 0; /* -L flag */ 198long numops = -1; /* -N flag */ 199int randomoplen = 1; /* -O flag disables it */ 200int seed = 1; /* -S flag */ 201int mapped_writes = 1; /* -W flag disables */ 202int mapped_reads = 1; /* -R flag disables it */ 203int mapped_msync = 1; /* -U flag disables */ 204int fsxgoodfd = 0; 205FILE * fsxlogf = NULL; 206int badoff = -1; 207int closeopen = 0; 208int invl = 0; 209 210void 211vwarnc(code, fmt, ap) 212 int code; 213 const char *fmt; 214 va_list ap; 215{ 216 fprintf(stderr, "fsx: "); 217 if (fmt != NULL) { 218 vfprintf(stderr, fmt, ap); 219 fprintf(stderr, ": "); 220 } 221 fprintf(stderr, "%s\n", strerror(code)); 222} 223 224void 225warn(const char * fmt, ...) 226{ 227 va_list ap; 228 va_start(ap, fmt); 229 vwarnc(errno, fmt, ap); 230 va_end(ap); 231} 232 233void 234prt(char *fmt, ...) 235{ 236 va_list args; 237 238 va_start(args, fmt); 239 vfprintf(stdout, fmt, args); 240 va_end(args); 241 242 if (fsxlogf) { 243 va_start(args, fmt); 244 vfprintf(fsxlogf, fmt, args); 245 va_end(args); 246 } 247} 248 249void 250prterr(char *prefix) 251{ 252 prt("%s%s%s\n", prefix, prefix ? ": " : "", strerror(errno)); 253} 254 255void 256do_log4(int operation, int arg0, int arg1, int arg2) 257{ 258 struct log_entry *le; 259 260 le = &oplog[logptr]; 261 le->operation = operation; 262 le->args[0] = arg0; 263 le->args[1] = arg1; 264 le->args[2] = arg2; 265 logptr++; 266 logcount++; 267 if (logptr >= LOGSIZE) 268 logptr = 0; 269} 270 271void 272log4(int operation, int arg0, int arg1, int arg2) 273{ 274 do_log4(operation, arg0, arg1, arg2); 275 if (closeopen) 276 do_log4(OP_CLOSEOPEN, 0, 0, 0); 277 if (invl) 278 do_log4(OP_INVALIDATE, 0, 0, 0); 279} 280 281void 282logdump(void) 283{ 284 struct log_entry *lp; 285 int i, count, down, opnum; 286 287 prt("LOG DUMP (%d total operations):\n", logcount); 288 if (logcount < LOGSIZE) { 289 i = 0; 290 count = logcount; 291 } else { 292 i = logptr; 293 count = LOGSIZE; 294 } 295 296 opnum = i + 1 + (logcount/LOGSIZE)*LOGSIZE; 297 for ( ; count > 0; count--) { 298 lp = &oplog[i]; 299 300 if (lp->operation == OP_CLOSEOPEN || 301 lp->operation == OP_INVALIDATE) { 302 switch (lp->operation) { 303 case OP_CLOSEOPEN: 304 prt("\t\tCLOSE/OPEN\n"); 305 break; 306 case OP_INVALIDATE: 307 prt("\t\tMS_INVALIDATE\n"); 308 break; 309 } 310 i++; 311 if (i == LOGSIZE) 312 i = 0; 313 continue; 314 } 315 316 prt("%d(%d mod 256): ", opnum, opnum%256); 317 switch (lp->operation) { 318 case OP_MAPREAD: 319 prt("MAPREAD\t0x%x thru 0x%x\t(0x%x bytes)", 320 lp->args[0], lp->args[0] + lp->args[1] - 1, 321 lp->args[1]); 322 if (badoff >= lp->args[0] && badoff < 323 lp->args[0] + lp->args[1]) 324 prt("\t***RRRR***"); 325 break; 326 case OP_MAPWRITE: 327 prt("MAPWRITE 0x%x thru 0x%x\t(0x%x bytes)", 328 lp->args[0], lp->args[0] + lp->args[1] - 1, 329 lp->args[1]); 330 if (badoff >= lp->args[0] && badoff < 331 lp->args[0] + lp->args[1]) 332 prt("\t******WWWW"); 333 break; 334 case OP_READ: 335 prt("READ\t0x%x thru 0x%x\t(0x%x bytes)", 336 lp->args[0], lp->args[0] + lp->args[1] - 1, 337 lp->args[1]); 338 if (badoff >= lp->args[0] && 339 badoff < lp->args[0] + lp->args[1]) 340 prt("\t***RRRR***"); 341 break; 342 case OP_WRITE: 343 prt("WRITE\t0x%x thru 0x%x\t(0x%x bytes)", 344 lp->args[0], lp->args[0] + lp->args[1] - 1, 345 lp->args[1]); 346 if (lp->args[0] > lp->args[2]) 347 prt(" HOLE"); 348 else if (lp->args[0] + lp->args[1] > lp->args[2]) 349 prt(" EXTEND"); 350 if ((badoff >= lp->args[0] || badoff >=lp->args[2]) && 351 badoff < lp->args[0] + lp->args[1]) 352 prt("\t***WWWW"); 353 break; 354 case OP_TRUNCATE: 355 down = lp->args[0] < lp->args[1]; 356 prt("TRUNCATE %s\tfrom 0x%x to 0x%x", 357 down ? "DOWN" : "UP", lp->args[1], lp->args[0]); 358 if (badoff >= lp->args[!down] && 359 badoff < lp->args[!!down]) 360 prt("\t******WWWW"); 361 break; 362 case OP_SKIPPED: 363 prt("SKIPPED (no operation)"); 364 break; 365 default: 366 prt("BOGUS LOG ENTRY (operation code = %d)!", 367 lp->operation); 368 } 369 prt("\n"); 370 opnum++; 371 i++; 372 if (i == LOGSIZE) 373 i = 0; 374 } 375} 376 377void 378save_buffer(char *buffer, off_t bufferlength, int fd) 379{ 380 off_t ret; 381 ssize_t byteswritten; 382 383 if (fd <= 0 || bufferlength == 0) 384 return; 385 386 if (bufferlength > SSIZE_MAX) { 387 prt("fsx flaw: overflow in save_buffer\n"); 388 exit(67); 389 } 390 if (lite) { 391 off_t size_by_seek = lseek(fd, (off_t)0, SEEK_END); 392 if (size_by_seek == (off_t)-1) 393 prterr("save_buffer: lseek eof"); 394 else if (bufferlength > size_by_seek) { 395 warn("save_buffer: .fsxgood file too short... will save 0x%llx bytes instead of 0x%llx\n", (unsigned long long)size_by_seek, 396 (unsigned long long)bufferlength); 397 bufferlength = size_by_seek; 398 } 399 } 400 401 ret = lseek(fd, (off_t)0, SEEK_SET); 402 if (ret == (off_t)-1) 403 prterr("save_buffer: lseek 0"); 404 405 byteswritten = write(fd, buffer, (size_t)bufferlength); 406 if (byteswritten != bufferlength) { 407 if (byteswritten == -1) 408 prterr("save_buffer write"); 409 else 410 warn("save_buffer: short write, 0x%x bytes instead of 0x%llx\n", 411 (unsigned)byteswritten, 412 (unsigned long long)bufferlength); 413 } 414} 415 416void 417report_failure(int status) 418{ 419 logdump(); 420 421 if (fsxgoodfd) { 422 if (good_buf) { 423 save_buffer(good_buf, file_size, fsxgoodfd); 424 prt("Correct content saved for comparison\n"); 425 prt("(maybe hexdump \"%s\" vs \"%s.fsxgood\")\n", 426 fname, fname); 427 } 428 close(fsxgoodfd); 429 } 430 exit(status); 431} 432 433#define short_at(cp) ((unsigned short)((*((unsigned char *)(cp)) << 8) | \ 434 *(((unsigned char *)(cp)) + 1))) 435 436void 437check_buffers(unsigned offset, unsigned size) 438{ 439 unsigned char c, t; 440 unsigned i = 0; 441 unsigned n = 0; 442 unsigned op = 0; 443 unsigned bad = 0; 444 445 if (memcmp(good_buf + offset, temp_buf, size) != 0) { 446 prt("READ BAD DATA: offset = 0x%x, size = 0x%x\n", 447 offset, size); 448 prt("OFFSET\tGOOD\tBAD\tRANGE\n"); 449 while (size > 0) { 450 c = good_buf[offset]; 451 t = temp_buf[i]; 452 if (c != t) { 453 if (n == 0) { 454 bad = short_at(&temp_buf[i]); 455 prt("0x%5x\t0x%04x\t0x%04x", offset, 456 short_at(&good_buf[offset]), bad); 457 op = temp_buf[offset & 1 ? i+1 : i]; 458 } 459 n++; 460 badoff = offset; 461 } 462 offset++; 463 i++; 464 size--; 465 } 466 if (n) { 467 prt("\t0x%5x\n", n); 468 if (bad) 469 prt("operation# (mod 256) for the bad data may be %u\n", ((unsigned)op & 0xff)); 470 else 471 prt("operation# (mod 256) for the bad data unknown, check HOLE and EXTEND ops\n"); 472 } else 473 prt("????????????????\n"); 474 report_failure(110); 475 } 476} 477 478void 479check_size(void) 480{ 481 struct stat statbuf; 482 off_t size_by_seek; 483 484 if (fstat(fd, &statbuf)) { 485 prterr("check_size: fstat"); 486 statbuf.st_size = -1; 487 } 488 size_by_seek = lseek(fd, (off_t)0, SEEK_END); 489 if (file_size != statbuf.st_size || file_size != size_by_seek) { 490 prt("Size error: expected 0x%llx stat 0x%llx seek 0x%llx\n", 491 (unsigned long long)file_size, 492 (unsigned long long)statbuf.st_size, 493 (unsigned long long)size_by_seek); 494 report_failure(120); 495 } 496} 497 498void 499check_trunc_hack(void) 500{ 501 struct stat statbuf; 502 503 ftruncate(fd, (off_t)0); 504 ftruncate(fd, (off_t)100000); 505 fstat(fd, &statbuf); 506 if (statbuf.st_size != (off_t)100000) { 507 prt("no extend on truncate! not posix!\n"); 508 exit(130); 509 } 510 ftruncate(fd, (off_t)0); 511} 512 513void 514doread(unsigned offset, unsigned size) 515{ 516 off_t ret; 517 unsigned iret; 518 519 offset -= offset % readbdy; 520 if (size == 0) { 521 if (!quiet && testcalls > simulatedopcount) 522 prt("skipping zero size read\n"); 523 log4(OP_SKIPPED, OP_READ, offset, size); 524 return; 525 } 526 if (size + offset > file_size) { 527 if (!quiet && testcalls > simulatedopcount) 528 prt("skipping seek/read past end of file\n"); 529 log4(OP_SKIPPED, OP_READ, offset, size); 530 return; 531 } 532 533 log4(OP_READ, offset, size, 0); 534 535 if (testcalls <= simulatedopcount) 536 return; 537 538 if (!quiet && ((progressinterval && 539 testcalls % progressinterval == 0) || 540 (debug && 541 (monitorstart == -1 || 542 (offset + size > monitorstart && 543 (monitorend == -1 || offset <= monitorend)))))) 544 prt("%lu read\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, 545 offset, offset + size - 1, size); 546 ret = lseek(fd, (off_t)offset, SEEK_SET); 547 if (ret == (off_t)-1) { 548 prterr("doread: lseek"); 549 report_failure(140); 550 } 551 iret = read(fd, temp_buf, size); 552 if (iret != size) { 553 if (iret == -1) 554 prterr("doread: read"); 555 else 556 prt("short read: 0x%x bytes instead of 0x%x\n", 557 iret, size); 558 report_failure(141); 559 } 560 check_buffers(offset, size); 561} 562 563void 564check_eofpage(char *s, unsigned offset, char *p, int size) 565{ 566 uintptr_t last_page, should_be_zero; 567 568 if (offset + size <= (file_size & ~page_mask)) 569 return; 570 /* 571 * we landed in the last page of the file 572 * test to make sure the VM system provided 0's 573 * beyond the true end of the file mapping 574 * (as required by mmap def in 1996 posix 1003.1) 575 */ 576 last_page = ((uintptr_t)p + (offset & page_mask) + size) & ~page_mask; 577 578 for (should_be_zero = last_page + (file_size & page_mask); 579 should_be_zero < last_page + page_size; 580 should_be_zero++) 581 if (*(char *)should_be_zero) { 582 prt("Mapped %s: non-zero data past EOF (0x%llx) page offset 0x%x is 0x%04x\n", 583 s, file_size - 1, should_be_zero & page_mask, 584 short_at(should_be_zero)); 585 report_failure(205); 586 } 587} 588 589void 590domapread(unsigned offset, unsigned size) 591{ 592 unsigned pg_offset; 593 unsigned map_size; 594 char *p; 595 596 offset -= offset % readbdy; 597 if (size == 0) { 598 if (!quiet && testcalls > simulatedopcount) 599 prt("skipping zero size read\n"); 600 log4(OP_SKIPPED, OP_MAPREAD, offset, size); 601 return; 602 } 603 if (size + offset > file_size) { 604 if (!quiet && testcalls > simulatedopcount) 605 prt("skipping seek/read past end of file\n"); 606 log4(OP_SKIPPED, OP_MAPREAD, offset, size); 607 return; 608 } 609 610 log4(OP_MAPREAD, offset, size, 0); 611 612 if (testcalls <= simulatedopcount) 613 return; 614 615 if (!quiet && ((progressinterval && 616 testcalls % progressinterval == 0) || 617 (debug && 618 (monitorstart == -1 || 619 (offset + size > monitorstart && 620 (monitorend == -1 || offset <= monitorend)))))) 621 prt("%lu mapread\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, 622 offset, offset + size - 1, size); 623 624 pg_offset = offset & page_mask; 625 map_size = pg_offset + size; 626 627 if ((p = (char *)mmap(0, map_size, PROT_READ, MAP_FILE | MAP_SHARED, fd, 628 (off_t)(offset - pg_offset))) == (char *)-1) { 629 prterr("domapread: mmap"); 630 report_failure(190); 631 } 632 memcpy(temp_buf, p + pg_offset, size); 633 634 check_eofpage("Read", offset, p, size); 635 636 if (munmap(p, map_size) != 0) { 637 prterr("domapread: munmap"); 638 report_failure(191); 639 } 640 641 check_buffers(offset, size); 642} 643 644void 645gendata(char *original_buf, char *good_buf, unsigned offset, unsigned size) 646{ 647 while (size--) { 648 good_buf[offset] = testcalls % 256; 649 if (offset % 2) 650 good_buf[offset] += original_buf[offset]; 651 offset++; 652 } 653} 654 655void 656dowrite(unsigned offset, unsigned size) 657{ 658 off_t ret; 659 unsigned iret; 660 661 offset -= offset % writebdy; 662 if (size == 0) { 663 if (!quiet && testcalls > simulatedopcount) 664 prt("skipping zero size write\n"); 665 log4(OP_SKIPPED, OP_WRITE, offset, size); 666 return; 667 } 668 669 log4(OP_WRITE, offset, size, file_size); 670 671 gendata(original_buf, good_buf, offset, size); 672 if (file_size < offset + size) { 673 if (file_size < offset) 674 memset(good_buf + file_size, '\0', offset - file_size); 675 file_size = offset + size; 676 if (lite) { 677 warn("Lite file size bug in fsx!"); 678 report_failure(149); 679 } 680 } 681 682 if (testcalls <= simulatedopcount) 683 return; 684 685 if (!quiet && ((progressinterval && 686 testcalls % progressinterval == 0) || 687 (debug && 688 (monitorstart == -1 || 689 (offset + size > monitorstart && 690 (monitorend == -1 || offset <= monitorend)))))) 691 prt("%lu write\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, 692 offset, offset + size - 1, size); 693 ret = lseek(fd, (off_t)offset, SEEK_SET); 694 if (ret == (off_t)-1) { 695 prterr("dowrite: lseek"); 696 report_failure(150); 697 } 698 iret = write(fd, good_buf + offset, size); 699 if (iret != size) { 700 if (iret == -1) 701 prterr("dowrite: write"); 702 else 703 prt("short write: 0x%x bytes instead of 0x%x\n", 704 iret, size); 705 report_failure(151); 706 } 707} 708 709void 710domapwrite(unsigned offset, unsigned size) 711{ 712 unsigned pg_offset; 713 unsigned map_size; 714 off_t cur_filesize; 715 char *p; 716 717 offset -= offset % writebdy; 718 if (size == 0) { 719 if (!quiet && testcalls > simulatedopcount) 720 prt("skipping zero size write\n"); 721 log4(OP_SKIPPED, OP_MAPWRITE, offset, size); 722 return; 723 } 724 cur_filesize = file_size; 725 726 log4(OP_MAPWRITE, offset, size, 0); 727 728 gendata(original_buf, good_buf, offset, size); 729 if (file_size < offset + size) { 730 if (file_size < offset) 731 memset(good_buf + file_size, '\0', offset - file_size); 732 file_size = offset + size; 733 if (lite) { 734 warn("Lite file size bug in fsx!"); 735 report_failure(200); 736 } 737 } 738 739 if (testcalls <= simulatedopcount) 740 return; 741 742 if (!quiet && ((progressinterval && 743 testcalls % progressinterval == 0) || 744 (debug && 745 (monitorstart == -1 || 746 (offset + size > monitorstart && 747 (monitorend == -1 || offset <= monitorend)))))) 748 prt("%lu mapwrite\t0x%x thru\t0x%x\t(0x%x bytes)\n", testcalls, 749 offset, offset + size - 1, size); 750 751 if (file_size > cur_filesize) { 752 if (ftruncate(fd, file_size) == -1) { 753 prterr("domapwrite: ftruncate"); 754 exit(201); 755 } 756 } 757 pg_offset = offset & page_mask; 758 map_size = pg_offset + size; 759 760 if ((p = (char *)mmap(0, map_size, PROT_READ | PROT_WRITE, 761 MAP_FILE | MAP_SHARED, fd, 762 (off_t)(offset - pg_offset))) == MAP_FAILED) { 763 prterr("domapwrite: mmap"); 764 report_failure(202); 765 } 766 memcpy(p + pg_offset, good_buf + offset, size); 767 if (mapped_msync && msync(p, map_size, MS_SYNC) != 0) { 768 prterr("domapwrite: msync"); 769 report_failure(203); 770 } 771 772 check_eofpage("Write", offset, p, size); 773 774 if (munmap(p, map_size) != 0) { 775 prterr("domapwrite: munmap"); 776 report_failure(204); 777 } 778} 779 780void 781dotruncate(unsigned size) 782{ 783 int oldsize = file_size; 784 785 size -= size % truncbdy; 786 if (size > biggest) { 787 biggest = size; 788 if (!quiet && testcalls > simulatedopcount) 789 prt("truncating to largest ever: 0x%x\n", size); 790 } 791 792 log4(OP_TRUNCATE, size, (unsigned)file_size, 0); 793 794 if (size > file_size) 795 memset(good_buf + file_size, '\0', size - file_size); 796 file_size = size; 797 798 if (testcalls <= simulatedopcount) 799 return; 800 801 if ((progressinterval && testcalls % progressinterval == 0) || 802 (debug && (monitorstart == -1 || monitorend == -1 || 803 size <= monitorend))) 804 prt("%lu trunc\tfrom 0x%x to 0x%x\n", testcalls, oldsize, size); 805 if (ftruncate(fd, (off_t)size) == -1) { 806 prt("ftruncate1: %x\n", size); 807 prterr("dotruncate: ftruncate"); 808 report_failure(160); 809 } 810} 811 812void 813writefileimage() 814{ 815 ssize_t iret; 816 817 if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) { 818 prterr("writefileimage: lseek"); 819 report_failure(171); 820 } 821 iret = write(fd, good_buf, file_size); 822 if ((off_t)iret != file_size) { 823 if (iret == -1) 824 prterr("writefileimage: write"); 825 else 826 prt("short write: 0x%x bytes instead of 0x%llx\n", 827 iret, (unsigned long long)file_size); 828 report_failure(172); 829 } 830 if (lite ? 0 : ftruncate(fd, file_size) == -1) { 831 prt("ftruncate2: %llx\n", (unsigned long long)file_size); 832 prterr("writefileimage: ftruncate"); 833 report_failure(173); 834 } 835} 836 837void 838docloseopen(void) 839{ 840 if (testcalls <= simulatedopcount) 841 return; 842 843 if (debug) 844 prt("%lu close/open\n", testcalls); 845 if (close(fd)) { 846 prterr("docloseopen: close"); 847 report_failure(180); 848 } 849 fd = open(fname, O_RDWR, 0); 850 if (fd < 0) { 851 prterr("docloseopen: open"); 852 report_failure(181); 853 } 854} 855 856void 857doinvl(void) 858{ 859 char *p; 860 861 if (file_size == 0) 862 return; 863 if (testcalls <= simulatedopcount) 864 return; 865 if (debug) 866 prt("%lu msync(MS_INVALIDATE)\n", testcalls); 867 868 if ((p = (char *)mmap(0, file_size, PROT_READ | PROT_WRITE, 869 MAP_FILE | MAP_SHARED, fd, 0)) == MAP_FAILED) { 870 prterr("doinvl: mmap"); 871 report_failure(205); 872 } 873 874 if (msync(p, 0, MS_SYNC | MS_INVALIDATE) != 0) { 875 prterr("doinvl: msync"); 876 report_failure(206); 877 } 878 879 if (munmap(p, file_size) != 0) { 880 prterr("doinvl: munmap"); 881 report_failure(207); 882 } 883} 884 885void 886test(void) 887{ 888 unsigned long offset; 889 unsigned long size = maxoplen; 890 unsigned long rv = random(); 891 unsigned long op = rv % (3 + !lite + mapped_writes); 892 893 /* turn off the map read if necessary */ 894 895 if (op == 2 && !mapped_reads) 896 op = 0; 897 898 if (simulatedopcount > 0 && testcalls == simulatedopcount) 899 writefileimage(); 900 901 testcalls++; 902 903 if (closeprob) 904 closeopen = (rv >> 3) < (1 << 28) / closeprob; 905 if (invlprob) 906 invl = (rv >> 3) < (1 << 28) / invlprob; 907 908 if (debugstart > 0 && testcalls >= debugstart) 909 debug = 1; 910 911 if (!quiet && testcalls < simulatedopcount && testcalls % 100000 == 0) 912 prt("%lu...\n", testcalls); 913 914 /* 915 * READ: op = 0 916 * WRITE: op = 1 917 * MAPREAD: op = 2 918 * TRUNCATE: op = 3 919 * MAPWRITE: op = 3 or 4 920 */ 921 if (lite ? 0 : op == 3 && style == 0) /* vanilla truncate? */ 922 dotruncate(random() % maxfilelen); 923 else { 924 if (randomoplen) 925 size = random() % (maxoplen+1); 926 if (lite ? 0 : op == 3) 927 dotruncate(size); 928 else { 929 offset = random(); 930 if (op == 1 || op == (lite ? 3 : 4)) { 931 offset %= maxfilelen; 932 if (offset + size > maxfilelen) 933 size = maxfilelen - offset; 934 if (op != 1) 935 domapwrite(offset, size); 936 else 937 dowrite(offset, size); 938 } else { 939 if (file_size) 940 offset %= file_size; 941 else 942 offset = 0; 943 if (offset + size > file_size) 944 size = file_size - offset; 945 if (op != 0) 946 domapread(offset, size); 947 else 948 doread(offset, size); 949 } 950 } 951 } 952 if (sizechecks && testcalls > simulatedopcount) 953 check_size(); 954 if (invl) 955 doinvl(); 956 if (closeopen) 957 docloseopen(); 958} 959 960void 961cleanup(sig) 962 int sig; 963{ 964 if (sig) 965 prt("signal %d\n", sig); 966 prt("testcalls = %lu\n", testcalls); 967 exit(sig); 968} 969 970void 971usage(void) 972{ 973 fprintf(stdout, "usage: %s", 974 "fsx [-dnqLOW] [-b opnum] [-c Prob] [-l flen] [-m start:end] [-o oplen] [-p progressinterval] [-r readbdy] [-s style] [-t truncbdy] [-w writebdy] [-D startingop] [-N numops] [-P dirpath] [-S seed] fname\n\ 975 -b opnum: beginning operation number (default 1)\n\ 976 -c P: 1 in P chance of file close+open at each op (default infinity)\n\ 977 -d: debug output for all operations\n\ 978 -i P: 1 in P chance of calling msync(MS_INVALIDATE) (default infinity)\n\ 979 -l flen: the upper bound on file size (default 262144)\n\ 980 -m startop:endop: monitor (print debug output) specified byte range (default 0:infinity)\n\ 981 -n: no verifications of file size\n\ 982 -o oplen: the upper bound on operation size (default 65536)\n\ 983 -p progressinterval: debug output at specified operation interval\n\ 984 -q: quieter operation\n\ 985 -r readbdy: 4096 would make reads page aligned (default 1)\n\ 986 -s style: 1 gives smaller truncates (default 0)\n\ 987 -t truncbdy: 4096 would make truncates page aligned (default 1)\n\ 988 -w writebdy: 4096 would make writes page aligned (default 1)\n\ 989 -D startingop: debug output starting at specified operation\n\ 990 -L: fsxLite - no file creations & no file size changes\n\ 991 -N numops: total # operations to do (default infinity)\n\ 992 -O: use oplen (see -o flag) for every op (default random)\n\ 993 -P dirpath: save .fsxlog and .fsxgood files in dirpath (default ./)\n\ 994 -S seed: for random # generator (default 1) 0 gets timestamp\n\ 995 -W: mapped write operations DISabled\n\ 996 -R: mapped read operations DISabled)\n\ 997 -U: msync after mapped write operations DISabled\n\ 998 fname: this filename is REQUIRED (no default)\n"); 999 exit(90); 1000} 1001 1002int 1003getnum(char *s, char **e) 1004{ 1005 int ret = -1; 1006 1007 *e = (char *) 0; 1008 ret = strtol(s, e, 0); 1009 if (*e) 1010 switch (**e) { 1011 case 'b': 1012 case 'B': 1013 ret *= 512; 1014 *e = *e + 1; 1015 break; 1016 case 'k': 1017 case 'K': 1018 ret *= 1024; 1019 *e = *e + 1; 1020 break; 1021 case 'm': 1022 case 'M': 1023 ret *= 1024*1024; 1024 *e = *e + 1; 1025 break; 1026 case 'w': 1027 case 'W': 1028 ret *= 4; 1029 *e = *e + 1; 1030 break; 1031 } 1032 return (ret); 1033} 1034 1035int 1036main(int argc, char **argv) 1037{ 1038 int i, ch; 1039 char *endp; 1040 char goodfile[1024]; 1041 char logfile[1024]; 1042 1043 goodfile[0] = 0; 1044 logfile[0] = 0; 1045 1046 page_size = getpagesize(); 1047 page_mask = page_size - 1; 1048 1049 setvbuf(stdout, (char *)0, _IOLBF, 0); /* line buffered stdout */ 1050 1051 while ((ch = getopt(argc, argv, 1052 "b:c:di:l:m:no:p:qr:s:t:w:D:LN:OP:RS:UW")) != -1) 1053 switch (ch) { 1054 case 'b': 1055 simulatedopcount = getnum(optarg, &endp); 1056 if (!quiet) 1057 fprintf(stdout, "Will begin at operation %ld\n", 1058 simulatedopcount); 1059 if (simulatedopcount == 0) 1060 usage(); 1061 simulatedopcount -= 1; 1062 break; 1063 case 'c': 1064 closeprob = getnum(optarg, &endp); 1065 if (!quiet) 1066 fprintf(stdout, 1067 "Chance of close/open is 1 in %d\n", 1068 closeprob); 1069 if (closeprob <= 0) 1070 usage(); 1071 break; 1072 case 'd': 1073 debug = 1; 1074 break; 1075 case 'i': 1076 invlprob = getnum(optarg, &endp); 1077 if (!quiet) 1078 fprintf(stdout, 1079 "Chance of MS_INVALIDATE is 1 in %d\n", 1080 invlprob); 1081 if (invlprob <= 0) 1082 usage(); 1083 break; 1084 case 'l': 1085 maxfilelen = getnum(optarg, &endp); 1086 if (maxfilelen <= 0) 1087 usage(); 1088 break; 1089 case 'm': 1090 monitorstart = getnum(optarg, &endp); 1091 if (monitorstart < 0) 1092 usage(); 1093 if (!endp || *endp++ != ':') 1094 usage(); 1095 monitorend = getnum(endp, &endp); 1096 if (monitorend < 0) 1097 usage(); 1098 if (monitorend == 0) 1099 monitorend = -1; /* aka infinity */ 1100 debug = 1; 1101 case 'n': 1102 sizechecks = 0; 1103 break; 1104 case 'o': 1105 maxoplen = getnum(optarg, &endp); 1106 if (maxoplen <= 0) 1107 usage(); 1108 break; 1109 case 'p': 1110 progressinterval = getnum(optarg, &endp); 1111// if (progressinterval < 0) 1112// usage(); 1113 break; 1114 case 'q': 1115 quiet = 1; 1116 break; 1117 case 'r': 1118 readbdy = getnum(optarg, &endp); 1119 if (readbdy <= 0) 1120 usage(); 1121 break; 1122 case 's': 1123 style = getnum(optarg, &endp); 1124 if (style < 0 || style > 1) 1125 usage(); 1126 break; 1127 case 't': 1128 truncbdy = getnum(optarg, &endp); 1129 if (truncbdy <= 0) 1130 usage(); 1131 break; 1132 case 'w': 1133 writebdy = getnum(optarg, &endp); 1134 if (writebdy <= 0) 1135 usage(); 1136 break; 1137 case 'D': 1138 debugstart = getnum(optarg, &endp); 1139 if (debugstart < 1) 1140 usage(); 1141 break; 1142 case 'L': 1143 lite = 1; 1144 break; 1145 case 'N': 1146 numops = getnum(optarg, &endp); 1147 if (numops < 0) 1148 usage(); 1149 break; 1150 case 'O': 1151 randomoplen = 0; 1152 break; 1153 case 'P': 1154 strncpy(goodfile, optarg, sizeof(goodfile)); 1155 strcat(goodfile, "/"); 1156 strncpy(logfile, optarg, sizeof(logfile)); 1157 strcat(logfile, "/"); 1158 break; 1159 case 'R': 1160 mapped_reads = 0; 1161 break; 1162 case 'S': 1163 seed = getnum(optarg, &endp); 1164 if (seed == 0) 1165 seed = time(0) % 10000; 1166 if (!quiet) 1167 fprintf(stdout, "Seed set to %d\n", seed); 1168 if (seed < 0) 1169 usage(); 1170 break; 1171 case 'W': 1172 mapped_writes = 0; 1173 if (!quiet) 1174 fprintf(stdout, "mapped writes DISABLED\n"); 1175 break; 1176 case 'U': 1177 mapped_msync = 0; 1178 if (!quiet) 1179 fprintf(stdout, "mapped msync DISABLED\n"); 1180 break; 1181 1182 default: 1183 usage(); 1184 /* NOTREACHED */ 1185 } 1186 argc -= optind; 1187 argv += optind; 1188 if (argc != 1) 1189 usage(); 1190 fname = argv[0]; 1191 1192 signal(SIGHUP, cleanup); 1193 signal(SIGINT, cleanup); 1194 signal(SIGPIPE, cleanup); 1195 signal(SIGALRM, cleanup); 1196 signal(SIGTERM, cleanup); 1197 signal(SIGXCPU, cleanup); 1198 signal(SIGXFSZ, cleanup); 1199 signal(SIGVTALRM, cleanup); 1200 signal(SIGUSR1, cleanup); 1201 signal(SIGUSR2, cleanup); 1202 1203 initstate(seed, state, 256); 1204 setstate(state); 1205 fd = open(fname, O_RDWR|(lite ? 0 : O_CREAT|O_TRUNC), 0666); 1206 if (fd < 0) { 1207 prterr(fname); 1208 exit(91); 1209 } 1210 strncat(goodfile, fname, 256); 1211 strcat (goodfile, ".fsxgood"); 1212 fsxgoodfd = open(goodfile, O_RDWR|O_CREAT|O_TRUNC, 0666); 1213 if (fsxgoodfd < 0) { 1214 prterr(goodfile); 1215 exit(92); 1216 } 1217 strncat(logfile, fname, 256); 1218 strcat (logfile, ".fsxlog"); 1219 fsxlogf = fopen(logfile, "w"); 1220 if (fsxlogf == NULL) { 1221 prterr(logfile); 1222 exit(93); 1223 } 1224 if (lite) { 1225 off_t ret; 1226 file_size = maxfilelen = lseek(fd, (off_t)0, SEEK_END); 1227 if (file_size == (off_t)-1) { 1228 prterr(fname); 1229 warn("main: lseek eof"); 1230 exit(94); 1231 } 1232 ret = lseek(fd, (off_t)0, SEEK_SET); 1233 if (ret == (off_t)-1) { 1234 prterr(fname); 1235 warn("main: lseek 0"); 1236 exit(95); 1237 } 1238 } 1239 original_buf = (char *) malloc(maxfilelen); 1240 for (i = 0; i < maxfilelen; i++) 1241 original_buf[i] = random() % 256; 1242 good_buf = (char *) malloc(maxfilelen); 1243 memset(good_buf, '\0', maxfilelen); 1244 temp_buf = (char *) malloc(maxoplen); 1245 memset(temp_buf, '\0', maxoplen); 1246 if (lite) { /* zero entire existing file */ 1247 ssize_t written; 1248 1249 written = write(fd, good_buf, (size_t)maxfilelen); 1250 if (written != maxfilelen) { 1251 if (written == -1) { 1252 prterr(fname); 1253 warn("main: error on write"); 1254 } else 1255 warn("main: short write, 0x%x bytes instead of 0x%x\n", 1256 (unsigned)written, maxfilelen); 1257 exit(98); 1258 } 1259 } else 1260 check_trunc_hack(); 1261 1262 while (numops == -1 || numops--) 1263 test(); 1264 1265 if (close(fd)) { 1266 prterr("close"); 1267 report_failure(99); 1268 } 1269 prt("All operations completed A-OK!\n"); 1270 1271 exit(0); 1272 return 0; 1273} 1274