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