From 332ee29d1ab7a8690ca8f6d06b0d116e3a4a4f0f Mon Sep 17 00:00:00 2001 From: Nathan Scott Date: Mon, 7 Jul 2003 06:36:46 +0000 Subject: [PATCH] xfstests updates - rework build to be like other xfs packages, revive some old fs test tools and reenable xfs extensions, move ltp code into a separate subdir to help keeping in sync with real ltp project (hopefully) --- 013 | 2 +- 022 | 2 +- 022.out | 2 +- 049 | 4 +- 065 | 3 +- 067 | 5 +- 068 | 2 +- 070 | 2 +- Makefile | 19 +- common.config | 5 +- common.dump | 45 +- configure.in | 273 +- group | 2 +- include/Makefile | 4 +- include/builddefs.in | 160 +- include/buildmacros | 181 ++ include/buildrules | 76 +- include/dataascii.h | 65 + include/databin.h | 44 + include/file_lock.h | 40 + include/forker.h | 63 + include/open_flags.h | 73 + include/pattern.h | 90 + include/str_to_bytes.h | 39 + include/test.h | 223 ++ include/tlibio.h | 148 ++ include/usctest.h | 319 +++ lib/Makefile | 54 + lib/dataascii.c | 218 ++ lib/databin.c | 298 +++ lib/datapid.c | 374 +++ lib/file_lock.c | 209 ++ lib/forker.c | 284 ++ lib/open_flags.c | 330 +++ lib/pattern.c | 168 ++ {src => lib}/random.c | 0 lib/random_range.c | 917 +++++++ lib/str_to_bytes.c | 208 ++ lib/string_to_tokens.c | 109 + lib/tlibio.c | 1992 ++++++++++++++ lib/write_log.c | 464 ++++ ltp/Makefile | 60 + ltp/doio.c | 5423 +++++++++++++++++++++++++++++++++++++++ ltp/doio.h | 198 ++ {src => ltp}/fsstress.c | 82 +- ltp/fsx.c | 1076 ++++++++ ltp/growfiles.c | 2819 ++++++++++++++++++++ ltp/iogen.c | 1973 ++++++++++++++ m4/Makefile | 1 + soak | 2 +- src/Makefile | 53 +- tools/srcdiff | 2 +- 52 files changed, 18677 insertions(+), 528 deletions(-) create mode 100644 include/buildmacros create mode 100644 include/dataascii.h create mode 100644 include/databin.h create mode 100644 include/file_lock.h create mode 100644 include/forker.h create mode 100644 include/open_flags.h create mode 100644 include/pattern.h create mode 100644 include/str_to_bytes.h create mode 100644 include/test.h create mode 100644 include/tlibio.h create mode 100644 include/usctest.h create mode 100644 lib/Makefile create mode 100644 lib/dataascii.c create mode 100644 lib/databin.c create mode 100644 lib/datapid.c create mode 100644 lib/file_lock.c create mode 100644 lib/forker.c create mode 100644 lib/open_flags.c create mode 100644 lib/pattern.c rename {src => lib}/random.c (100%) create mode 100644 lib/random_range.c create mode 100644 lib/str_to_bytes.c create mode 100644 lib/string_to_tokens.c create mode 100644 lib/tlibio.c create mode 100644 lib/write_log.c create mode 100644 ltp/Makefile create mode 100644 ltp/doio.c create mode 100644 ltp/doio.h rename {src => ltp}/fsstress.c (95%) create mode 100644 ltp/fsx.c create mode 100644 ltp/growfiles.c create mode 100644 ltp/iogen.c diff --git a/013 b/013 index 68951f8b..841b003e 100755 --- a/013 +++ b/013 @@ -85,7 +85,7 @@ _do_test() echo "fsstress.$_n : $_param" echo "-----------------------------------------------" # -v >$tmp.out - if ! $here/src/fsstress $_param $FSSTRESS_AVOID -n $_count -d $out >/dev/null 2>&1 + if ! $here/ltp/fsstress $_param $FSSTRESS_AVOID -n $_count -d $out >/dev/null 2>&1 then echo " fsstress (count=$_count) returned $? - see $seq.full" diff --git a/022 b/022 index 718f3850..c997b9f4 100755 --- a/022 +++ b/022 @@ -4,7 +4,7 @@ # Test out a level 0 dump/restore to a tape of a subdir # i.e. it is testing out drive_scsitape.c # -# Use src/fsstress to create a directory structure with a mix of files +# Use fsstress to create a directory structure with a mix of files # #----------------------------------------------------------------------- # Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved. diff --git a/022.out b/022.out index 91668f63..95e1639d 100644 --- a/022.out +++ b/022.out @@ -1,6 +1,6 @@ QA output created by 022 Put scsi tape driver into variable block size mode -Creating directory system to dump using src/fsstress. +Creating directory system to dump using fsstress. ----------------------------------------------- fsstress : -f link=10 -f creat=10 -f mkdir=10 -f truncate=5 -f symlink=10 diff --git a/049 b/049 index 72a3fd4c..a1ec2f67 100755 --- a/049 +++ b/049 @@ -106,7 +106,7 @@ mount -t xfs -o loop $SCRATCH_MNT/test.xfs $SCRATCH_MNT/test >> $seq.full 2>&1 \ || _fail "!!! failed to loop mount xfs" _log "stress" -src/fsstress -d $SCRATCH_MNT/test -n 1000 $FSSTRESS_AVOID >> $seq.full 2>&1 \ +ltp/fsstress -d $SCRATCH_MNT/test -n 1000 $FSSTRESS_AVOID >> $seq.full 2>&1 \ || _fail "!!! stress failed" _log "clean" @@ -126,7 +126,7 @@ mount -t ext2 -o loop $SCRATCH_MNT/test/test.ext2 $SCRATCH_MNT/test2 >> $seq.ful || _fail "!!! failed to loop mount xfs" _log "stress ext2 on xfs via loop" -src/fsstress -d $SCRATCH_MNT/test2 -n 1000 $FSSTRESS_AVOID >> $seq.full 2>&1 \ +ltp/fsstress -d $SCRATCH_MNT/test2 -n 1000 $FSSTRESS_AVOID >> $seq.full 2>&1 \ || _fail "!!! stress ext2 failed" _log "clean" diff --git a/065 b/065 index 5e2c6a8e..aaa0edfd 100755 --- a/065 +++ b/065 @@ -98,8 +98,7 @@ umount $SCRATCH_DEV # _wipe_fs -mkdir -p $dump_dir ||\ - _error "cannot mkdir \"$dump_dir\"" +mkdir -p $dump_dir || _fail "cannot mkdir \"$dump_dir\"" cd $dump_dir echo "Do the incremental dumps" diff --git a/067 b/067 index 31d1950d..8312ac43 100755 --- a/067 +++ b/067 @@ -59,9 +59,8 @@ _acl_requirements _require_scratch # set up fs for 1K inodes -export MKFS_OPTIONS="-i size=1024" -_scratch_mkfs_xfs >>$here/$seq.full || _error "mkfs failed" -_scratch_mount >>$here/$seq.full || _error "mount failed" +_scratch_mkfs_xfs -i size=1024 >>$here/$seq.full || _fail "mkfs failed" +_scratch_mount >>$here/$seq.full || _fail "mount failed" cd $SCRATCH_MNT # xfs_growfs -n $SCRATCH_MNT diff --git a/068 b/068 index 405304cf..fec586bf 100755 --- a/068 +++ b/068 @@ -181,7 +181,7 @@ then # -n 10 makes this take about 10 seconds, # This allows the loop to end shortly after $tmp/running # is deleted - $here/src/fsstress -d "$STRESS_DIR" -n 10 > /dev/null 2>&1 + $here/ltp/fsstress -d "$STRESS_DIR" -n 10 > /dev/null 2>&1 sync done diff --git a/070 b/070 index 75d3e638..f2eb6e7f 100755 --- a/070 +++ b/070 @@ -52,7 +52,7 @@ trap "rm -f $tmp.*; exit \$status" 0 1 2 3 15 # real QA test starts here -$here/src/fsstress \ +$here/ltp/fsstress \ -d $TEST_DIR/fsstress \ -f allocsp=0 \ -f freesp=0 \ diff --git a/Makefile b/Makefile index dea3f30d..b4065dd2 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as @@ -39,11 +39,11 @@ endif TESTS = $(shell sed -n -e '/^[0-9][0-9][0-9]*/s/ .*//p' group) CONFIGURE = configure include/builddefs -LSRCFILES = configure configure.in -LDIRT = *.bad *.new *.core *.full *.raw core a.out *.bak \ - check.log check.time config.* conftest* +LSRCFILES = configure configure.in aclocal.m4 README VERSION +LDIRT = config.log .dep config.status config.cache confdefs.h conftest* \ + check.log check.time -SUBDIRS = include src +SUBDIRS = include lib ltp src m4 default: $(CONFIGURE) new remake check $(TESTS) ifeq ($(HAVE_BUILDDEFS), no) @@ -58,14 +58,13 @@ else clean: # if configure hasn't run, nothing to clean endif -$(CONFIGURE): configure.in include/builddefs.in - rm -f config.cache +$(CONFIGURE): autoconf ./configure -install install-dev: default - $(SUBDIRS_MAKERULE) +aclocal.m4:: + aclocal --acdir=$(TOPDIR)/m4 --output=$@ realclean distclean: clean rm -f $(LDIRT) $(CONFIGURE) - rm -rf autom4te.cache + rm -rf autom4te.cache Logs diff --git a/common.config b/common.config index 3ba9a7ce..3202fff6 100644 --- a/common.config +++ b/common.config @@ -76,7 +76,7 @@ export MOUNT_OPTIONS=${MOUNT_OPTIONS:=-ologbufs=2} export CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"} export BENCH_PASSES=${BENCH_PASSES:=5} -export DEBUG=${DEBUG:=-DEXPERIMENTAL_LARGE_SECTORS} +#export DEBUG=${DEBUG:=...} # arbitrary CFLAGS really. export MALLOCLIB=${MALLOCLIB:=/usr/lib/libefence.a} export LOCAL_CONFIGURE_OPTIONS=${LOCAL_CONFIGURE_OPTIONS:=--enable-readline=yes} @@ -105,7 +105,8 @@ in flutz) MODULAR=0 EMAIL="nathans@larry" - TEST_DEV=/dev/sda5 + TEST_DEV=/dev/sda5 + TEST_LOGDEV=/dev/sda9 TEST_DIR=/xfsqa1 SCRATCH_DEV=/dev/sda6 SCRATCH_LOGDEV=/dev/sda7 diff --git a/common.dump b/common.dump index c24bec8d..eeb74cd4 100644 --- a/common.dump +++ b/common.dump @@ -228,20 +228,12 @@ _require_tape() _set_variable } -_error() -{ - echo "Error: $*" | tee -a $here/$seq.full - echo "(see $here/$seq.full for details)" - status=1 - exit -} - _wipe_fs() { _require_scratch - _scratch_mkfs_xfs >>$here/$seq.full || _error "mkfs failed" - _scratch_mount >>$here/$seq.full || _error "mount failed" + _scratch_mkfs_xfs >>$here/$seq.full || _fail "mkfs failed" + _scratch_mount >>$here/$seq.full || _fail "mount failed" } # @@ -286,20 +278,20 @@ _cleanup() _stable_fs() { _saveddir=`pwd`; cd / - umount $SCRATCH_MNT >>$here/$seq.full || _error "unmount failed" - _scratch_mount >>$here/$seq.full || _error "mount failed" + umount $SCRATCH_MNT >>$here/$seq.full || _fail "unmount failed" + _scratch_mount >>$here/$seq.full || _fail "mount failed" cd $_saveddir } # -# Run src/fsstress to create a mixture of +# Run fsstress to create a mixture of # files,dirs,links,symlinks # # Pinched from test 013. # _create_dumpdir_stress() { - echo "Creating directory system to dump using src/fsstress." + echo "Creating directory system to dump using fsstress." _wipe_fs @@ -315,7 +307,7 @@ _create_dumpdir_stress() echo "-----------------------------------------------" echo "fsstress : $_param" echo "-----------------------------------------------" - if ! $here/src/fsstress $_param -s 1 $FSSTRESS_AVOID -n $_count -d $dump_dir >$tmp.out 2>&1 + if ! $here/ltp/fsstress $_param -s 1 $FSSTRESS_AVOID -n $_count -d $dump_dir >$tmp.out 2>&1 then echo " fsstress (count=$_count) returned $? - see $here/$seq.full" @@ -452,8 +444,7 @@ _do_create_dumpdir_fill() { echo "Creating directory system to dump using src/fill." - mkdir -p $dump_dir ||\ - _error "cannot mkdir \"$dump_dir\"" + mkdir -p $dump_dir || _fail "cannot mkdir \"$dump_dir\"" cd $dump_dir $verbose && echo -n "Setup " @@ -524,8 +515,7 @@ _do_create_dumpdir_fill() _create_dumpdir_largefile() { _wipe_fs - mkdir -p $dump_dir ||\ - _error "cannot mkdir \"$dump_dir\"" + mkdir -p $dump_dir || _fail "cannot mkdir \"$dump_dir\"" _largesize=4294967297 _largefile=$dump_dir/largefile echo "dd a largefile at offset $_largesize" @@ -598,8 +588,7 @@ _do_create_dump_symlinks() { echo "Creating directory system of symlinks to dump." - mkdir -p $dump_dir ||\ - _error "cannot mkdir \"$dump_dir\"" + mkdir -p $dump_dir || _fail "cannot mkdir \"$dump_dir\"" cd $dump_dir $verbose && echo -n "Setup " @@ -721,8 +710,7 @@ _create_dumpdir_hardlinks() _wipe_fs echo "Creating directory system of hardlinks to incrementally dump." - mkdir -p $dump_dir ||\ - _error "cannot mkdir \"$dump_dir\"" + mkdir -p $dump_dir || _fail "cannot mkdir \"$dump_dir\"" cd $dump_dir _create_hardset $_numsets @@ -838,12 +826,12 @@ _parse_args() case $1 in -f) - [ -z "$2" ] && _error "missing argument for -f" + [ -z "$2" ] && _fail "missing argument for -f" dumptape=$2 shift ;; -L) - [ -z "$2" ] && _error "missing argument for -L" + [ -z "$2" ] && _fail "missing argument for -L" session_label=$2 shift ;; @@ -864,12 +852,12 @@ _parse_args() do_quota_check=false ;; -l) - [ -z "$2" ] && _error "missing argument for -l" + [ -z "$2" ] && _fail "missing argument for -l" dump_args="$dump_args -l$2" shift ;; *) - _error "invalid argument to common.dump function: $1" + _fail "invalid argument to common.dump function: $1" ;; esac shift @@ -958,8 +946,7 @@ _do_dump_multi_file() _prepare_restore_dir() { rm -rf $restore_dir - mkdir $restore_dir ||\ - _error "failed to mkdir $restore_dir" + mkdir $restore_dir || _fail "failed to mkdir $restore_dir" } diff --git a/configure.in b/configure.in index 19966c51..b545b03b 100644 --- a/configure.in +++ b/configure.in @@ -1,261 +1,22 @@ -dnl unpacking check - this file must exist -AC_INIT(src/fsstress.c) -pkg_name="xfstests" -AC_SUBST(pkg_name) +AC_INIT(src/xfsctl.c) +AC_PACKAGE_GLOBALS(xfstests) +AC_PACKAGE_UTILITIES(xfstests) -# -# Note: the following environment variables may be set to override the -# defaults (to change paths and/or executables, build parameters, etc): -# -# DEBUG OPTIMIZER MAKE CC LD TAR ZIP RPM AWK SED ECHO -# MALLOCLIB DISTRIBUTION PACKAGE_BUILDER PREFIX ROOT_PREFIX -# +AC_PACKAGE_NEED_UUID_H +AC_PACKAGE_NEED_UUIDCOMPARE -DEBUG=${DEBUG:-'-DDEBUG'} # -DNDEBUG -OPTIMIZER=${OPTIMIZER:-'-g'} # (-O1 enforced default) -MALLOCLIB=${MALLOCLIB:-''} # /usr/lib/libefence.a +AC_PACKAGE_NEED_XFS_LIBXFS_H +AC_PACKAGE_NEED_XFSCTL_MACRO +AC_PACKAGE_NEED_LIBXFSINIT_LIBXFS +AC_PACKAGE_NEED_XFS_HANDLE_H +AC_PACKAGE_NEED_ATTRLIST_LIBHANDLE -dnl Debug build? -debug_build="$DEBUG" -AC_SUBST(debug_build) +AC_PACKAGE_NEED_ATTR_XATTR_H +AC_PACKAGE_NEED_GETXATTR_LIBATTR +AC_PACKAGE_NEED_SYS_ACL_H +AC_PACKAGE_NEED_ACL_LIBACL_H +AC_PACKAGE_NEED_ACLINIT_LIBACL -dnl Optimization options? -opt_build="$OPTIMIZER" -AC_SUBST(opt_build) +AC_PACKAGE_WANT_LIBGDBM -dnl Alternate malloc library? -malloc_lib="$MALLOCLIB" -AC_SUBST(malloc_lib) - -dnl Set version -. ./VERSION - -pkg_version=${PKG_MAJOR}.${PKG_MINOR}.${PKG_REVISION} -pkg_release=$PKG_BUILD -AC_SUBST(pkg_version) -AC_SUBST(pkg_release) - -pkg_distribution="SGI ProPack" -test -z "$DISTRIBUTION" || pkg_distribution="$DISTRIBUTION" -AC_SUBST(pkg_distribution) - -pkg_builder=`id -u -n`@`hostname` -test -z "$PACKAGE_BUILDER" || pkg_builder="$PACKAGE_BUILDER" -AC_SUBST(pkg_builder) - -dnl check if user wants their own C compiler -test -z "$CC" && AC_PROG_CC -cc=$CC -AC_SUBST(cc) - -dnl check if users wants their own make -test -z "$MAKE" && AC_PATH_PROG(MAKE, make, /usr/bin/make) -make=$MAKE -AC_SUBST(make) - -dnl check if users wants their own linker -test -z "$LD" && AC_PATH_PROG(LD, ld, /usr/bin/ld) -ld=$LD -AC_SUBST(ld) - -dnl check if the tar program is available -test -z "$TAR" && AC_PATH_PROG(TAR, tar) -tar=$TAR -AC_SUBST(tar) - -dnl check if the gzip program is available -test -z "$ZIP" && AC_PATH_PROG(ZIP, gzip, /bin/gzip) -zip=$ZIP -AC_SUBST(zip) - -dnl check if the rpm program is available -test -z "$RPM" && AC_PATH_PROG(RPM, rpm, /bin/rpm) -rpm=$RPM -AC_SUBST(rpm) - -dnl .. and what version is rpm -rpm_version=0 -test -x $RPM && \ - rpm_version=`$RPM --version | awk '{print $NF}' | awk -F. '{print $1}'` -AC_SUBST(rpm_version) - -dnl check if the makedepend program is available -test -z "$MAKEDEPEND" && AC_PATH_PROG(MAKEDEPEND, makedepend, /bin/true) -makedepend=$MAKEDEPEND -AC_SUBST(makedepend) - -dnl check if symbolic links are supported -AC_PROG_LN_S - -dnl check if user wants their own awk, sed and echo -test -z "$AWK" && AC_PATH_PROG(AWK, awk, /bin/awk) -awk=$AWK -AC_SUBST(awk) -test -z "$SED" && AC_PATH_PROG(SED, sed, /bin/sed) -sed=$SED -AC_SUBST(sed) -test -z "$ECHO" && AC_PATH_PROG(ECHO, echo, /bin/echo) -echo=$ECHO -AC_SUBST(echo) - -CPPFLAGS="-I/usr/include/xfs" -AC_SUBST(CPPFLAGS) - -dnl Checks for UUID header and library. -AC_CHECK_HEADER(uuid/uuid.h,, [ - echo - echo 'FATAL ERROR: could not find a valid UUID header.' - echo 'Install either the e2fsprogs-devel (rpm) or the uuid-dev (deb) package.' - exit 1 -]) -AC_CHECK_LIB(uuid, uuid_generate,, [ - echo - echo 'FATAL ERROR: could not find a valid UUID library.' - echo 'Install either the e2fsprogs-devel (rpm) or the uuid-dev (deb) package.' - exit 1 -]) -libuuid="/usr/lib/libuuid.a" -AC_SUBST(libuuid) - -dnl Checks for base XFS headers and libraries. -AC_CHECK_HEADER(xfs/libxfs.h,, [ - echo - echo 'FATAL ERROR: could not find a valid XFS library header.' - echo 'Install either the xfsprogs-devel (rpm) or the xfslibs-dev (deb) package.' - echo 'Alternatively, run "make install-dev" from the xfsprogs source.' - exit 1 -]) -AC_CHECK_LIB(xfs, libxfs_init,, [ - echo - echo 'FATAL ERROR: could not find a valid XFS base library.' - echo 'Install either the xfsprogs-devel (rpm) or the xfslibs-dev (deb) package.' - echo 'Alternatively, run "make install-dev" from the xfsprogs source.' - exit 1 -]) -AC_CHECK_HEADER(xfs/handle.h,, [ - echo - echo 'FATAL ERROR: could not find a valid XFS handle header.' - echo 'Install either the xfsprogs-devel (rpm) or the xfslibs-dev (deb) package.' - echo 'Alternatively, run "make install-dev" from the xfsprogs source.' - exit 1 -]) -AC_CHECK_LIB(handle, path_to_handle,, [ - echo - echo 'FATAL ERROR: could not find a valid XFS handle library.' - echo 'Install either the xfsprogs-devel (rpm) or the xfslibs-dev (deb) package.' - echo 'Alternatively, run "make install-dev" from the xfsprogs source.' - exit 1 -]) -libxfs="-lxfs" -libhdl="-lhandle" -AC_SUBST(libxfs) -AC_SUBST(libhdl) - -dnl Checks for Extended Attributes header and library. -AC_CHECK_HEADER(attr/xattr.h,, [ - echo - echo 'FATAL ERROR: could not find a valid Extended Attributes header.' - echo 'Install either the attr-devel (rpm) or the attr-dev (deb) package.' - echo 'Alternatively, run "make install-dev" from the attr source.' - exit 1 -]) -AC_CHECK_LIB(attr, getxattr,, [ - echo - echo 'FATAL ERROR: could not find a valid Extended Attributes library.' - echo 'Install either the libattr (rpm) or the libattr1 (deb) package.' - echo 'Alternatively, run "make install-lib" from the attr source.' - exit 1 -]) -libattr="-lattr" -AC_SUBST(libattr) - -dnl Checks for Access Control List headers and library. -AC_CHECK_HEADER(sys/acl.h,, [ - echo - echo 'FATAL ERROR: could not find a valid Access Control List headers.' - echo 'Install either the acl-devel (rpm) or the acl (deb) package.' - echo 'Alternatively, run "make install-dev" from the acl source.' - exit 1 -]) -AC_CHECK_HEADER(acl/libacl.h,, [ - echo - echo 'FATAL ERROR: could not find a valid Access Control List headers.' - echo 'Install either the acl-devel (rpm) or the acl (deb) package.' - echo 'Alternatively, run "make install-dev" from the acl source.' - exit 1 -]) -AC_CHECK_LIB(acl, acl_init,, [ - echo - echo 'FATAL ERROR: could not find a valid Access Control List library.' - echo 'Install either the libacl (rpm) or the libacl1 (deb) package.' - echo 'Alternatively, run "make install-lib" from the acl source.' - exit 1 -]) -libacl="-lacl" -AC_SUBST(libacl) - -dnl Checks for GNU database manager header and library. -libgdbm="" -have_db=true -AC_CHECK_HEADER(gdbm/ndbm.h,, [ have_db=false ]) -if test $have_db = "true" -a -f /usr/lib/libgdbm.a; then - libgdbm="/usr/lib/libgdbm.a" -fi -AC_SUBST(libgdbm) -AC_SUBST(have_db) - -dnl alternate root and usr prefixes -test -z "$ROOT_PREFIX" && ROOT_PREFIX="" -root_prefix="$ROOT_PREFIX" -test -z "$PREFIX" && PREFIX="/usr" -prefix="$PREFIX" - -dnl man pages (source) -dnl also check if man page source is gzipped -dnl (usually on Debian, but not Redhat pre-7.0) -pkg_man_dir=${prefix}/share/man -have_zipped_manpages=false -for d in ${prefix}/share/man ${prefix}/man ; do - if test -f $d/man1/man.1.gz - then - pkg_man_dir=$d - have_zipped_manpages=true - break - fi -done -AC_SUBST(pkg_man_dir) -AC_SUBST(have_zipped_manpages) - -dnl binaries -pkg_bin_dir=${prefix}/sbin -AC_SUBST(pkg_bin_dir) - -dnl static libraries -pkg_lib_dir=${prefix}/lib -AC_SUBST(pkg_lib_dir) - -dnl runtime shared system libraries -pkg_slib_dir=${root_prefix}/lib -AC_SUBST(pkg_slib_dir) - -dnl system binaries -pkg_sbin_dir=${root_prefix}/sbin -AC_SUBST(pkg_sbin_dir) - -dnl include files -pkg_inc_dir=${prefix}/include -AC_SUBST(pkg_inc_dir) - -dnl doc directory -pkg_doc_dir=${prefix}/share/doc/${pkg_name} -AC_SUBST(pkg_doc_dir) - - -dnl -dnl output files -dnl - -AC_OUTPUT( \ -dnl Build definitions for use in Makefiles - include/builddefs \ -) +AC_OUTPUT(include/builddefs) diff --git a/group b/group index a7eea660..3b52f2df 100644 --- a/group +++ b/group @@ -130,4 +130,4 @@ ioctl nathans@sgi.com 070 attr auto 071 rw 072 rw -073 copy +#073 copy diff --git a/include/Makefile b/include/Makefile index d6ec04d9..10648e49 100644 --- a/include/Makefile +++ b/include/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as @@ -33,6 +33,8 @@ TOPDIR = .. include $(TOPDIR)/include/builddefs +HFILES = dataascii.h databin.h pattern.h \ + random_range.h string_to_tokens.h tlibio.h write_log.h LSRCFILES = builddefs.in buildrules default install install-dev: diff --git a/include/builddefs.in b/include/builddefs.in index 0befc549..f808ffd1 100644 --- a/include/builddefs.in +++ b/include/builddefs.in @@ -1,5 +1,5 @@ # -# Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved. +# Copyright (c) 2003 Silicon Graphics, Inc. All Rights Reserved. # # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as @@ -45,124 +45,52 @@ LIBATTR = @libattr@ LIBGDBM = @libgdbm@ LIBUUID = @libuuid@ LIBHANDLE = @libhdl@ -HAVE_DB = @have_db@ -CPPFLAGS = -I/usr/include/xfs -I/usr/include/attr - -BUILDRULES = $(TOPDIR)/include/buildrules - -# General package information -PKG_NAME = @pkg_name@ -PKG_RELEASE = @pkg_release@ -PKG_VERSION = @pkg_version@ -PKG_DISTRIBUTION = @pkg_distribution@ -PKG_BUILDER = @pkg_builder@ -PKG_BIN_DIR = @pkg_bin_dir@ -PKG_LIB_DIR = @pkg_lib_dir@ -PKG_SBIN_DIR = @pkg_sbin_dir@ -PKG_SLIB_DIR = @pkg_slib_dir@ -PKG_INC_DIR = @pkg_inc_dir@ -PKG_MAN_DIR = @pkg_man_dir@ -PKG_DOC_DIR = @pkg_doc_dir@ - -# LCFLAGS, LLDFLAGS, LLDLIBS, LSRCFILES and LDIRT may be specified in -# user Makefiles. Note: LSRCFILES is anything other than Makefile, $(CFILES) -# $(CXXFILES), or $(HFILES) and is used to construct the manifest list -# during the "dist" phase (packaging). - -CFLAGS += -O1 $(OPTIMIZER) $(DEBUG) -funsigned-char -Wall $(LCFLAGS) \ - -I$(TOPDIR)/include '-DVERSION="$(PKG_VERSION)"' -D_GNU_SOURCE \ - -D_FILE_OFFSET_BITS=64 - -LDFLAGS = $(LLDFLAGS) -LDLIBS = $(LLDLIBS) $(MALLOCLIB) - -MAKEOPTS = --no-print-directory -SRCFILES = Makefile $(HFILES) $(CFILES) $(LSRCFILES) $(LFILES) $(YFILES) -DIRT = $(LDIRT) dep dep.bak $(OBJECTS) $(CMDTARGET) $(LIBTARGET) \ - $(STATICLIBTARGET) *.[1-9].gz - -OBJECTS = $(ASFILES:.s=.o) \ - $(CFILES:.c=.o) \ - $(LFILES:.l=.o) \ - $(YFILES:%.y=%.tab.o) - -MAKE = @make@ -CC = @cc@ -LD = @ld@ -AWK = @awk@ -SED = @sed@ -INSTALL = $(TOPDIR)/install-sh -o root -g root -ECHO = @echo@ -LN_S = @LN_S@ - -CCF = $(CC) $(CFLAGS) -MAKEF = $(MAKE) $(MAKEOPTS) -CXXF = $(CXX) $(CXXFLAGS) -LDF = $(LD) $(LDFLAGS) -MAKEDEPEND = @makedepend@ - -ZIP = @zip@ -TAR = @tar@ -RPM = @rpm@ -RPM_VERSION = @rpm_version@ - -HAVE_ZIPPED_MANPAGES = @have_zipped_manpages@ - -SHELL = /bin/sh -IMAGES_DIR = $(TOPDIR)/all-images -DIST_DIR = $(TOPDIR)/dist - -SUBDIRS_MAKERULE = \ - @for d in $(SUBDIRS) ""; do \ - if test -d "$$d" -a ! -z "$$d"; then \ - $(ECHO) === $$d ===; \ - $(MAKEF) -C $$d $@ || exit $$?; \ - fi; \ - done - -MAN_MAKERULE = \ - @for f in *.[12345678] ""; do \ - if test ! -z "$$f"; then \ - $(ZIP) --best -c < $$f > $$f.gz; \ - fi; \ - done +LIBTEST = $(TOPDIR)/lib/libtest.la + +PKG_NAME = @pkg_name@ +PKG_USER = @pkg_user@ +PKG_GROUP = @pkg_group@ +PKG_RELEASE = @pkg_release@ +PKG_VERSION = @pkg_version@ +PKG_PLATFORM = @pkg_platform@ +PKG_DISTRIBUTION= @pkg_distribution@ + +CC = @cc@ +AWK = @awk@ +SED = @sed@ +TAR = @tar@ +ZIP = @zip@ +MAKE = @make@ +ECHO = @echo@ +SORT = @sort@ +LN_S = @LN_S@ +LIBTOOL = @LIBTOOL@ +MAKEDEPEND = @makedepend@ + +MSGFMT = @msgfmt@ +MSGMERGE = @msgmerge@ + +RPM = @rpm@ +RPMBUILD = @rpmbuild@ +RPM_VERSION = @rpm_version@ + +ENABLE_SHARED = @enable_shared@ +ENABLE_DBM = @enable_dbm@ + +ifeq ($(PKG_PLATFORM),linux) +PCFLAGS = -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 +endif +ifeq ($(PKG_PLATFORM),darwin) +PCFLAGS = -traditional-cpp +endif -INSTALL_MAN = \ - @for d in $(MAN_PAGES); do \ - first=true; \ - for m in `$(AWK) '/^\.SH NAME/ {ok=1; next} ok {print; exit}' $$d \ - | sed -e 's/,/ /g' -e 's/\\-.*//' -e 's/\\\f[0-9]//g' -e 's/ / /g;q'`; \ - do \ - [ -z "$$m" -o "$$m" = "\\" ] && continue; \ - t=$(MAN_DEST)/$$m.$(MAN_SECTION); \ - if $$first; then \ - if $(HAVE_ZIPPED_MANPAGES); then \ - $(ZIP) --best -c $$d > $$d.gz; _sfx=.gz; \ - fi; \ - u=$$m.$(MAN_SECTION)$$_sfx; \ - echo $(INSTALL) -m 644 $${d}$$_sfx $${t}$$_sfx; \ - $(INSTALL) -m 644 $${d}$$_sfx $${t}$$_sfx; \ - else \ - echo $(INSTALL) -S $$u $${t}$$_sfx; \ - $(INSTALL) -S $$u $${t}$$_sfx; \ - fi; \ - first=false; \ - done; \ - done +CFLAGS += -O1 $(OPTIMIZER) $(DEBUG) -funsigned-char -Wall -I$(TOPDIR)/include \ + -DVERSION=\"$(PKG_VERSION)\" -DIST_MAKERULE = \ - $(MAKEF) -C build dist +# Global, Platform, Local CFLAGS +CFLAGS += $(GCFLAGS) $(PCFLAGS) $(LCFLAGS) -SOURCE_MAKERULE = \ - @test -z "$$DIR" && DIR="."; \ - for f in $(SRCFILES) ""; do \ - if test ! -z "$$f"; then $(ECHO) $$DIR/$$f; fi;\ - done; \ - for d in `echo $(SUBDIRS)` ; do \ - if test -d "$$d" -a ! -z "$$d"; then \ - $(MAKEF) DIR=$$DIR/$$d -C $$d $@ || exit $$?; \ - fi; \ - done +include $(TOPDIR)/include/buildmacros endif diff --git a/include/buildmacros b/include/buildmacros new file mode 100644 index 00000000..bce094c2 --- /dev/null +++ b/include/buildmacros @@ -0,0 +1,181 @@ +# +# Copyright (c) 2002-2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write the Free Software Foundation, Inc., 59 +# Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, +# Mountain View, CA 94043, or: +# +# http://www.sgi.com +# +# For further information regarding this notice, see: +# +# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ +# + +BUILDRULES = $(TOPDIR)/include/buildrules + +# LCFLAGS, LLDFLAGS, LLDLIBS, LSRCFILES and LDIRT may be specified in +# user Makefiles. Note: LSRCFILES is anything other than Makefile, $(CFILES) +# $(CXXFILES), or $(HFILES) and is used to construct the manifest list +# during the "dist" phase (packaging). + +LDFLAGS = $(LLDFLAGS) +LDLIBS = $(LLDLIBS) $(PLDLIBS) $(MALLOCLIB) + +MAKEOPTS = --no-print-directory +SRCFILES = Makefile $(HFILES) $(CFILES) $(LSRCFILES) $(LFILES) $(YFILES) + +DEPDIRT = dep dep.bak +MANDIRT = *.[1-9].gz +PODIRT = *.tmpo *.mo +CDIRT = $(OBJECTS) $(LTOBJECTS) $(LTCOMMAND) $(LTLIBRARY) +DIRT = $(LDIRT) $(DEPDIRT) $(MANDIRT) $(PODIRT) $(CDIRT) + +OBJECTS = $(ASFILES:.s=.o) \ + $(CFILES:.c=.o) \ + $(LFILES:.l=.o) \ + $(YFILES:%.y=%.tab.o) + +INSTALL = $(TOPDIR)/install-sh -o $(PKG_USER) -g $(PKG_GROUP) + +SHELL = /bin/sh +IMAGES_DIR = $(TOPDIR)/all-images +DIST_DIR = $(TOPDIR)/dist + +CCF = $(CC) $(CFLAGS) $(CPPFLAGS) +MAKEF = $(MAKE) $(MAKEOPTS) +CXXF = $(CXX) $(CXXFLAGS) + +# For libtool. +LIBNAME = $(basename $(LTLIBRARY)) +LTOBJECTS = $(OBJECTS:.o=.lo) +LTVERSION = $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) + +LTLINK = $(LIBTOOL) --mode=link $(CC) +LTEXEC = $(LIBTOOL) --mode=execute +LTINSTALL = $(LIBTOOL) --mode=install $(INSTALL) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CCF) -D_REENTRANT -fno-strict-aliasing + +ifeq ($(ENABLE_SHARED),yes) +LTLDFLAGS += -rpath $(PKG_LIB_DIR) +LTLDFLAGS += -version-info $(LTVERSION) +endif + +ifeq ($(ENABLE_SHARED),yes) +INSTALL_LTLIB = \ + cd $(TOPDIR)/$(LIBNAME)/.libs; \ + ../$(INSTALL) -m 755 -d $(PKG_LIB_DIR); \ + ../$(INSTALL) -m 644 -T so_dot_version $(LIBNAME).lai $(PKG_LIB_DIR); \ + test "$(PKG_DISTRIBUTION)" = debian || \ + ../$(INSTALL) -T so_dot_current $(LIBNAME).lai $(PKG_LIB_DIR) +endif + +# Libtool thinks the static and shared libs should be in the same dir, so +# make the static lib appear in the place we chose as rpath (using the two +# symlinks below). +# Other things want the shared libs to appear in /usr/lib, else they'll +# link with the static libs there. So, another symlink to get the .so into +# /usr/lib. +ifeq ($(ENABLE_SHARED),yes) +INSTALL_LTLIB_DEV = \ + cd $(TOPDIR)/$(LIBNAME)/.libs; \ + ../$(INSTALL) -m 755 -d $(PKG_DEVLIB_DIR); \ + ../$(INSTALL) -m 644 -T old_lib $(LIBNAME).lai $(PKG_DEVLIB_DIR); \ + ../$(INSTALL) -m 644 $(LIBNAME).lai $(PKG_DEVLIB_DIR)/$(LIBNAME).la ; \ + ../$(INSTALL) -m 755 -d $(PKG_LIB_DIR); \ + ../$(INSTALL) -T so_base $(LIBNAME).lai $(PKG_LIB_DIR); \ + ../$(INSTALL) -S $(PKG_DEVLIB_DIR)/$(LIBNAME).a $(PKG_LIB_DIR)/$(LIBNAME).a; \ + ../$(INSTALL) -S $(PKG_DEVLIB_DIR)/$(LIBNAME).la $(PKG_LIB_DIR)/$(LIBNAME).la; \ + ../$(INSTALL) -S $(PKG_LIB_DIR)/$(LIBNAME).so $(PKG_DEVLIB_DIR)/$(LIBNAME).so +else +INSTALL_LTLIB_DEV = $(INSTALL_LTLIB_STATIC) +endif + +INSTALL_LTLIB_STATIC = \ + cd $(TOPDIR)/$(LIBNAME)/.libs; \ + ../$(INSTALL) -m 755 -d $(PKG_DEVLIB_DIR); \ + ../$(INSTALL) -m 644 -T old_lib $(LIBNAME).lai $(PKG_DEVLIB_DIR) + +INSTALL_MAN = \ + @for d in $(MAN_PAGES); do \ + first=true; \ + for m in `$(AWK) \ + '/^\.S[h|H] NAME/ {ok=1; next} ok {print; exit}' $$d \ + | $(SED) \ + -e 's/^\.Nm //' -e 's/,/ /g' -e 's/\\-.*//' \ + -e 's/\\\f[0-9]//g' -e 's/ / /g;q'`; \ + do \ + [ -z "$$m" -o "$$m" = "\\" ] && continue; \ + t=$(MAN_DEST)/$$m.$(MAN_SECTION); \ + if $$first; then \ + if $(HAVE_ZIPPED_MANPAGES); then \ + $(ZIP) -9 -c $$d > $$d.gz; _sfx=.gz; \ + fi; \ + u=$$m.$(MAN_SECTION)$$_sfx; \ + echo $(INSTALL) -m 644 $${d}$$_sfx $${t}$$_sfx;\ + $(INSTALL) -m 644 $${d}$$_sfx $${t}$$_sfx; \ + else \ + echo $(INSTALL) -S $$u $${t}$$_sfx; \ + $(INSTALL) -S $$u $${t}$$_sfx; \ + fi; \ + first=false; \ + done; \ + done + +ifeq ($(ENABLE_GETTEXT),yes) +INSTALL_LINGUAS = \ + @for l in $(LINGUAS) ""; do \ + if test -f "$$l.mo" ; then \ + ldir=$(PKG_LOCALE_DIR)/$$l/LC_MESSAGES; \ + $(INSTALL) -m 755 -d $$ldir; \ + $(INSTALL) -m 644 $$l.mo $$ldir/$(PKG_NAME).mo; \ + fi; \ + done +endif + +SUBDIRS_MAKERULE = \ + @for d in $(SUBDIRS) ""; do \ + if test -d "$$d" -a ! -z "$$d"; then \ + $(ECHO) === $$d ===; \ + $(MAKEF) -C $$d $@ || exit $$?; \ + fi; \ + done + +MAN_MAKERULE = \ + @for f in *.[12345678] ""; do \ + if test ! -z "$$f"; then \ + $(ZIP) --best -c < $$f > $$f.gz; \ + fi; \ + done + +DIST_MAKERULE = \ + $(MAKEF) -C build dist + +SOURCE_MAKERULE = \ + @test -z "$$DIR" && DIR="."; \ + for f in $(SRCFILES) ""; do \ + if test ! -z "$$f"; then $(ECHO) $$DIR/$$f; fi;\ + done; \ + for d in `echo $(SUBDIRS)` ; do \ + if test -d "$$d" -a ! -z "$$d"; then \ + $(MAKEF) DIR=$$DIR/$$d -C $$d $@ || exit $$?; \ + fi; \ + done diff --git a/include/buildrules b/include/buildrules index 9522042d..cecb4c94 100644 --- a/include/buildrules +++ b/include/buildrules @@ -1,10 +1,10 @@ # -# Copyright (c) 1999, 2001 Silicon Graphics, Inc. All Rights Reserved. -# +# Copyright (c) 1999, 2001-2003 Silicon Graphics, Inc. All Rights Reserved. +# # This program is free software; you can redistribute it and/or modify it # under the terms of version 2 of the GNU General Public License as published # by the Free Software Fondation. -# +# # This program is distributed in the hope that it would be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. Further, any license provided herein, @@ -15,7 +15,7 @@ # distributed without any warranty that the program is delivered free of the # rightful claim of any third person by way of infringement or the like. See # the GNU General Public License for more details. -# +# # You should have received a copy of the GNU General Public License along with # this program; if not, write the Free Software Foundation, Inc., 59 Temple # Place - Suite 330, Boston MA 02111-1307, USA. @@ -26,28 +26,9 @@ _BUILDRULES_INCLUDED_ = 1 include $(TOPDIR)/include/builddefs -# -# Standard targets -# -ifdef CMDTARGET -$(CMDTARGET) : $(SUBDIRS) $(OBJECTS) - $(CCF) -o $(CMDTARGET) $(LDFLAGS) $(OBJECTS) $(LDLIBS) -$(CMDTARGET).static : $(SUBDIRS) $(OBJECTS) - $(CCF) -static -o $(CMDTARGET).static $(LDFLAGS) $(OBJECTS) $(LDLIBS) -endif - -ifdef LIBTARGET -$(LIBTARGET) : $(SUBDIRS) $(OBJECTS) - $(CC) $(LDFLAGS) -fPIC -shared -Wl,-soname,$(LIBTARGET) -o $(LIBTARGET) $(OBJECTS) $(LDLIBS) -endif - -ifdef STATICLIBTARGET -$(STATICLIBTARGET) : $(SUBDIRS) $(OBJECTS) - $(AR) crf $(STATICLIBTARGET) $? -endif - clean clobber : $(SUBDIRS) rm -f $(DIRT) + @rm -fr .libs $(SUBDIRS_MAKERULE) # Never blow away subdirs @@ -57,10 +38,41 @@ $(SUBDIRS): $(SUBDIRS_MAKERULE) endif +# +# Standard targets +# + +ifdef LTCOMMAND +$(LTCOMMAND) : $(SUBDIRS) $(OBJECTS) $(LTDEPENDENCIES) + $(LTLINK) -o $@ $(LDFLAGS) $(OBJECTS) $(LDLIBS) +endif + +ifdef LTLIBRARY +$(LTLIBRARY) : $(SUBDIRS) $(LTOBJECTS) + $(LTLINK) $(LTLDFLAGS) -o $(LTLIBRARY) $(LTOBJECTS) $(LTLIBS) + +%.lo: %.c + $(LTCOMPILE) -c $< +endif + +ifdef LINGUAS +%.pot: $(XGETTEXTFILES) + xgettext --omit-header --language=C --keyword=_ -o $@ $(XGETTEXTFILES) + +%.po: $(PKG_NAME).pot + $(MSGMERGE) -o $@.tmpo $@ $< + @if ! diff $@.tmpo $@ >/dev/null; then \ + echo "$@ is out of date, see $@.tmpo"; \ + fi + +%.mo: %.po + $(MSGFMT) -o $@ $< +endif + source : $(SOURCE_MAKERULE) -endif +endif # _BUILDRULES_INCLUDED_ $(_FORCE): @@ -68,10 +80,16 @@ $(_FORCE): depend : $(CFILES) $(HFILES) $(SUBDIRS_MAKERULE) - touch dep - $(MAKEDEPEND) -fdep -- $(CFLAGS) -- $(CFILES) + touch .dep + $(MAKEDEPEND) -f - -- $(CFLAGS) -- $(CFILES) | \ + $(SED) -e 's,`pwd`,$(TOPDIR),g' \ + -e 's, */[^ ]*,,g' \ + -e '/^[^ ]*: *$$/d' \ + -e '/^#.*/d' -e '/^ *$$/d' \ + > .dep + test -s .dep || rm -f .dep # Include dep, but only if it exists -ifeq ($(shell test -f dep && echo dep), dep) -include dep +ifeq ($(shell test -f .dep && echo .dep), .dep) +include .dep endif diff --git a/include/dataascii.h b/include/dataascii.h new file mode 100644 index 00000000..cd75245e --- /dev/null +++ b/include/dataascii.h @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +#ifndef _DATAASCII_H_ +#define _DATAASCII_H_ + +/*********************************************************************** + * int dataasciigen(listofchars, buffer, size, offset) + * + * This function fills buffer with ascii characters. + * The ascii characters are obtained from listofchars or the CHARS array + * if listofchars is NULL. + * Each char is selected by an index. The index is the remainder + * of count divided by the array size. + * This method allows more than one process to write to a location + * in a file without corrupting it for another process' point of view. + * + * The return value will be the number of character written in buffer + * (size). + * + ***********************************************************************/ +int dataasciigen(char *, char *, int, int); + +/*********************************************************************** + * int dataasciichk(listofchars, buffer, size, count, errmsg) + * + * This function checks the contents of a buffer produced by + * dataasciigen. + * + * return values: + * >= 0 : error at character count + * < 0 : no error + ***********************************************************************/ + +int dataasciichk(char *, char *, int, int, char**); + +#endif diff --git a/include/databin.h b/include/databin.h new file mode 100644 index 00000000..b71fbc0c --- /dev/null +++ b/include/databin.h @@ -0,0 +1,44 @@ +#ifndef _DATABIN_H_ +#define _DATABIN_H_ + +/******************************************************************************* +* NAME +* databingen - fill a buffer with a data pattern +* +* SYNOPSIS +* (void) databingen(mode, buffer, bsize, offset) +* int mode; +* char *buffer; +* int bsize; +* int offset; +* +* DESCRIPTION +* datagen fills the buffer pointed to by 'buffer' with 'bsize' bytes +* of data of the form indicated by 'mode'. +* All modes (expect r -random) are file offset based. +* This allows more than process to do writing to the file without +* corrupting it if the same modes were used. +* They data modes to choose from, these are: +* +* 'a' - writes an alternating bit pattern (i.e. 0x5555555...) +* +* 'c' - writes a checkerboard pattern (i.e. 0xff00ff00ff00...) +* +* 'C' - writes counting pattern (i.e. 0 - 07, 0 - 07, ...); +* +* 'o' - writes all bits set (i.e. 0xffffffffffffff...) +* +* 'z' - writes all bits cleared (i.e. 0x000000000...); +* +* 'r' - writes random integers +* +* RETURN VALUE +* None +* +*******************************************************************************/ + +void databingen( int mode, unsigned char *buffer, int bsize, int offset ); + +void databinchedk( int mode, unsigned char *buffer, int bsize, int offset, char **errmsg); + +#endif diff --git a/include/file_lock.h b/include/file_lock.h new file mode 100644 index 00000000..8c9a9483 --- /dev/null +++ b/include/file_lock.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +#ifndef _FILE_LOCK_H_ +#define _FILE_LOCK_H_ + +extern char Fl_syscall_str[128]; + +int file_lock( int , int, char ** ); +int record_lock( int , int , int , int , char ** ); + +#endif /* _FILE_LOCK_H_ */ diff --git a/include/forker.h b/include/forker.h new file mode 100644 index 00000000..effd5d66 --- /dev/null +++ b/include/forker.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +#ifndef _FORKER_H_ +#define _FORKER_H_ + +#define FORKER_MAX_PIDS 4098 + +extern int Forker_pids[FORKER_MAX_PIDS]; /* holds pids of forked processes */ +extern int Forker_npids; /* number of entries in Forker_pids */ + +/* + * This function will fork and the parent will exit zero and + * the child will return. This will orphan the returning process + * putting it in the background. + */ +int background( char * ); + +/* + * Forker will fork ncopies-1 copies of self. + * + * arg 1: Number of copies of the process to be running after return. + * This value minus one is the number of forks performed. + * arg 2: mode: 0 - all children are first generation descendents. + * 1 - each subsequent child is a descendent of another + * descendent, resulting in only one direct descendent of the + * parent and each other child is a child of another child in + * relation to the parent. + * arg 3: prefix: string to preceed any error messages. A value of NULL + * results in no error messages on failure. + * returns: returns to parent the number of children forked. + */ +int forker( int , int , char * ); + +#endif /* _FORKER_H_ */ diff --git a/include/open_flags.h b/include/open_flags.h new file mode 100644 index 00000000..87fe6ff1 --- /dev/null +++ b/include/open_flags.h @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +#ifndef _OPEN_FLAGS_H_ +#define _OPEN_FLAGS_H_ + +/*********************************************************************** + * This function attempts to convert open flag bits into human readable + * symbols (i.e. O_TRUNC). If there are more than one symbol, + * the string will be placed as a separator between symbols. + * Commonly used separators would be a comma "," or pipe "|". + * If is one and not all bits can be converted to + * symbols, the "UNKNOWN" symbol will be added to return string. + * + * Return Value + * openflags2symbols will return the indentified symbols. + * If no symbols are recognized the return value will be a empty + * string or the "UNKNOWN" symbol. + * + * Limitations + * Currently (05/96) all known symbols are coded into openflags2symbols. + * If new open flags are added this code will have to updated + * to know about them or they will not be recognized. + * + * The Open_symbols must be large enough to hold all possible symbols + * for a given system. + * + ***********************************************************************/ +char *openflags2symbols( int, char *, int ); + +/*********************************************************************** + * This function will take a string of comma separated open flags symbols + * and translate them into an open flag bitmask. + * If any symbol is not valid, -1 is returned. On this error condition + * the badname pointer is updated if not NULL. badname will point + * to the beginning location of where the invalid symbol was found. + * string will be returned unchanged. + * + * A signal received while parsing string could cause the string to + * contain a NULL character in the middle of it. + * + ***********************************************************************/ +int parse_open_flags( char *, char ** ); + +#endif diff --git a/include/pattern.h b/include/pattern.h new file mode 100644 index 00000000..74f841ca --- /dev/null +++ b/include/pattern.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +#ifndef _PATTERN_H_ +#define _PATTERN_H_ + +/* + * pattern_check(buf, buflen, pat, patlen, patshift) + * + * Check a buffer of length buflen against repeated occurrances of + * a pattern whose length is patlen. Patshift can be used to rotate + * the pattern by patshift bytes to the left. + * + * Patshift may be greater than patlen, the pattern will be rotated by + * (patshift % patshift) bytes. + * + * pattern_check returns -1 if the buffer does not contain repeated + * occurrances of the indicated pattern (shifted by patshift). + * + * The algorithm used to check the buffer relies on the fact that buf is + * supposed to be repeated copies of pattern. The basic algorithm is + * to validate the first patlen bytes of buf against the pat argument + * passed in - then validate the next patlen bytes against the 1st patlen + * bytes - the next (2*patlen) bytes against the 1st (2*pathen) bytes, and + * so on. This algorithm only works when the assumption of a buffer full + * of repeated copies of a pattern holds, and gives MUCH better results + * then walking the buffer byte by byte. + * + * Performance wise, It appears to be about 5% slower than doing a straight + * memcmp of 2 buffers, but the big win is that it does not require a + * 2nd comparison buffer, only the pattern. + */ +int pattern_check( char * , int , char * , int , int ); + +/* + * pattern_fill(buf, buflen, pat, patlen, patshift) + * + * Fill a buffer of length buflen with repeated occurrances of + * a pattern whose length is patlen. Patshift can be used to rotate + * the pattern by patshift bytes to the left. + * + * Patshift may be greater than patlen, the pattern will be rotated by + * (patshift % patlen) bytes. + * + * If buflen is not a multiple of patlen, a partial pattern will be written + * in the last part of the buffer. This implies that a buffer which is + * shorter than the pattern length will receive only a partial pattern ... + * + * pattern_fill always returns 0 - no validation of arguments is done. + * + * The algorithm used to fill the buffer relies on the fact that buf is + * supposed to be repeated copies of pattern. The basic algorithm is + * to fill the first patlen bytes of buf with the pat argument + * passed in - then copy the next patlen bytes with the 1st patlen + * bytes - the next (2*patlen) bytes with the 1st (2*pathen) bytes, and + * so on. This algorithm only works when the assumption of a buffer full + * of repeated copies of a pattern holds, and gives MUCH better results + * then filling the buffer 1 byte at a time. + */ +int pattern_fill( char * , int , char * , int , int ); + +#endif diff --git a/include/str_to_bytes.h b/include/str_to_bytes.h new file mode 100644 index 00000000..100d37df --- /dev/null +++ b/include/str_to_bytes.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +#ifndef _STR_TO_BYTES_ +#define _STR_TO_BYTES_ + +int str_to_bytes ( char * ); +long str_to_lbytes ( char * ); +long long str_to_llbytes( char * ); + +#endif diff --git a/include/test.h b/include/test.h new file mode 100644 index 00000000..8a7646b4 --- /dev/null +++ b/include/test.h @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ + +/* $Id: test.h,v 1.1 2003/07/07 06:36:46 nathans Exp $ */ + +#ifndef __TEST_H__ +#define __TEST_H__ + +#include +#include +#include +#include +#include + +#define TPASS 0 /* Test passed flag */ +#define TFAIL 1 /* Test failed flag */ +#define TBROK 2 /* Test broken flag */ +#define TWARN 4 /* Test warning flag */ +#define TRETR 8 /* Test retire flag */ +#define TINFO 16 /* Test information flag */ +#define TCONF 32 /* Test not appropriate for configuration flag */ + +/* + * To determine if you are on a Umk or Unicos system, + * use sysconf(_SC_CRAY_SYSTEM). But since _SC_CRAY_SYSTEM + * is not defined until 90, it will be define here if not already + * defined. + * if ( sysconf(_SC_CRAY_SYSTEM) == 1 ) + * on UMK + * else # returned 0 or -1 + * on Unicos + * This is only being done on CRAY systems. + */ +#ifdef CRAY +#ifndef _SC_CRAY_SYSTEM +#define _SC_CRAY_SYSTEM 140 +#endif /* ! _SC_CRAY_SYSTEM */ +#endif /* CRAY */ + +/* + * Ensure that NUMSIGS is defined. + * It should be defined in signal.h or sys/signal.h on + * UNICOS/mk and IRIX systems. On UNICOS systems, + * it is not defined, thus it is being set to UNICOS's NSIG. + * Note: IRIX's NSIG (signals are 1-(NSIG-1)) + * is not same meaning as UNICOS/UMK's NSIG (signals 1-NSIG) + */ +#ifndef NUMSIGS +#define NUMSIGS NSIG +#endif + + +/* defines for unexpected signal setup routine (set_usig.c) */ +#define FORK 1 /* SIGCLD is to be ignored */ +#define NOFORK 0 /* SIGCLD is to be caught */ +#define DEF_HANDLER 0 /* tells set_usig() to use default signal handler */ + +/* + * The following defines are used to control tst_res and t_result reporting. + */ + +#define TOUTPUT "TOUTPUT" /* The name of the environment variable */ + /* that can be set to one of the following */ + /* strings to control tst_res output */ + /* If not set, TOUT_VERBOSE_S is assumed */ + +#define TOUT_VERBOSE_S "VERBOSE" /* All test cases reported */ +#define TOUT_CONDENSE_S "CONDENSE" /* ranges are used where identical messages*/ + /* occur for sequential test cases */ +#define TOUT_NOPASS_S "NOPASS" /* No pass test cases are reported */ +#define TOUT_DISCARD_S "DISCARD" /* No output is reported */ + +#define TST_NOBUF "TST_NOBUF" /* The name of the environment variable */ + /* that can be set to control whether or not */ + /* tst_res will buffer output into 4096 byte */ + /* blocks of output */ + /* If not set, buffer is done. If set, no */ + /* internal buffering will be done in tst_res */ + /* t_result does not have internal buffering */ + +/* + * The following defines are used to control tst_tmpdir, tst_wildcard and t_mkchdir + */ + +#define TDIRECTORY "TDIRECTORY" /* The name of the environment variable */ + /* that if is set, the value (directory) */ + /* is used by all tests as their working */ + /* directory. tst_rmdir and t_rmdir will */ + /* not attempt to clean up. */ + /* This environment variable should only */ + /* be set when doing system testing since */ + /* tests will collide and break and fail */ + /* because of setting it. */ + +#define TEMPDIR "/tmp" /* This is the default temporary directory. */ + /* The environment variable TMPDIR is */ + /* used prior to this valid by tempnam(3). */ + /* To control the base location of the */ + /* temporary directory, set the TMPDIR */ + /* environment variable to desired path */ + +/* + * The following contains support for error message passing. + * See test_error.c for details. + */ +#define TST_ERR_MESG_SIZE 1023 /* max size of error message */ +#define TST_ERR_FILE_SIZE 511 /* max size of module name used by compiler */ +#define TST_ERR_FUNC_SIZE 127 /* max size of func name */ + +typedef struct { + int te_line; /* line where last error was reported. Use */ + /* "__LINE__" and let compiler do the rest */ + int te_level; /* If set, will prevent current stored */ + /* error to not be overwritten */ + char te_func[TST_ERR_FUNC_SIZE+1]; /* name of function of last error */ + /* Name of function or NULL */ + char te_file[TST_ERR_FILE_SIZE+1]; /* module of last error. Use */ + /* "__FILE__" and let compiler do the rest */ + char te_mesg[TST_ERR_MESG_SIZE+1]; /* string of last error */ + +} _TST_ERROR; + +extern _TST_ERROR Tst_error; /* defined in test_error.c */ +#if __STDC__ +extern void tst_set_error(char *file, int line, char *func, char *fmt, ...); +#else +extern void tst_set_error(); +#endif +extern void tst_clear_error(); + + +/* + * The following define contains the name of an environmental variable + * that can be used to specify the number of iterations. + * It is supported in parse_opts.c and USC_setup.c. + */ +#define USC_ITERATION_ENV "USC_ITERATIONS" + +/* + * The following define contains the name of an environmental variable + * that can be used to specify to iteration until desired time + * in floating point seconds has gone by. + * Supported in USC_setup.c. + */ +#define USC_LOOP_WALLTIME "USC_LOOP_WALLTIME" + +/* + * The following define contains the name of an environmental variable + * that can be used to specify that no functional checks are wanted. + * It is supported in parse_opts.c and USC_setup.c. + */ +#define USC_NO_FUNC_CHECK "USC_NO_FUNC_CHECK" + +/* + * The following define contains the name of an environmental variable + * that can be used to specify the delay between each loop iteration. + * The value is in seconds (fractional numbers are allowed). + * It is supported in parse_opts.c. + */ +#define USC_LOOP_DELAY "USC_LOOP_DELAY" + +/* + * The following prototypes are needed to remove compile errors + * on IRIX systems when compiled with -n32 and -64. + */ +extern void tst_res(int ttype, char *fname, char *arg_fmt, ...); +extern void tst_resm(int ttype, char *arg_fmt, ...); +extern void tst_brk(int ttype, char *fname, void (*func)(), + char *arg_fmt, ...); +extern void tst_brkloop(int ttype, char *fname, void (*func)(), + char *arg_fmt, ...); +extern void tst_brkm(int ttype, void (*func)(), char *arg_fmt, ...); +extern void tst_brkloopm(int ttype, void (*func)(), char *arg_fmt, ...); + +extern int tst_environ(); +extern void tst_exit(); +extern void tst_flush(); + +/* prototypes for the t_res.c functions */ +extern void t_result(char *tcid, int tnum, int ttype, char *tmesg); +extern void tt_exit(); +extern int t_environ(); +extern void t_breakum(char *tcid, int total, int typ, char *msg, void (*fnc)()); + +extern void tst_sig(int fork_flag, void (*handler)(), void (*cleanup)()); +extern void tst_tmpdir(); +extern void tst_rmdir(); + +extern char * get_high_address(void); + +extern void get_kver(int*, int*, int*); +extern int tst_kvercmp(int, int, int); + +#endif /* end of __TEST_H__ */ diff --git a/include/tlibio.h b/include/tlibio.h new file mode 100644 index 00000000..ac0d5705 --- /dev/null +++ b/include/tlibio.h @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ + +#define LIO_IO_SYNC 00001 /* read/write */ +#define LIO_IO_ASYNC 00002 /* reada/writea/aio_write/aio_read */ +#define LIO_IO_SLISTIO 00004 /* single stride sync listio */ +#define LIO_IO_ALISTIO 00010 /* single stride async listio */ +#define LIO_IO_SYNCV 00020 /* single-buffer readv/writev */ +#define LIO_IO_SYNCP 00040 /* pread/pwrite */ + +#ifdef sgi +#define LIO_IO_ATYPES 00077 /* all io types */ +#define LIO_IO_TYPES 00061 /* all io types, non-async */ +#endif /* sgi */ +#ifdef linux +#define LIO_IO_TYPES 00021 /* all io types */ +#endif /* linux */ +#ifdef CRAY +#define LIO_IO_TYPES 00017 /* all io types */ +#endif /* CRAY */ + +#define LIO_WAIT_NONE 00010000 /* return asap -- use with care */ +#define LIO_WAIT_ACTIVE 00020000 /* spin looking at iosw fields, or EINPROGRESS */ +#define LIO_WAIT_RECALL 00040000 /* call recall(2)/aio_suspend(3) */ +#define LIO_WAIT_SIGPAUSE 00100000 /* call pause */ +#define LIO_WAIT_SIGACTIVE 00200000 /* spin waiting for signal */ +#ifdef sgi +#define LIO_WAIT_CBSUSPEND 00400000 /* aio_suspend waiting for callback */ +#define LIO_WAIT_SIGSUSPEND 01000000 /* aio_suspend waiting for signal */ +#define LIO_WAIT_ATYPES 01760000 /* all async wait types, except nowait */ +#define LIO_WAIT_TYPES 00020000 /* all sync wait types (sorta) */ +#endif /* sgi */ +#ifdef linux +#define LIO_WAIT_TYPES 00300000 /* all wait types, except nowait */ +#endif /* linux */ +#ifdef CRAY +#define LIO_WAIT_TYPES 00360000 /* all wait types, except nowait */ +#endif /* CRAY */ + +/* meta wait io */ +/* 00 000 0000 */ + +#ifdef sgi +/* all callback wait types */ +#define LIO_WAIT_CBTYPES (LIO_WAIT_CBSUSPEND) +/* all signal wait types */ +#define LIO_WAIT_SIGTYPES (LIO_WAIT_SIGPAUSE|LIO_WAIT_SIGACTIVE|LIO_WAIT_SIGSUSPEND) +/* all aio_{read,write} or lio_listio */ +#define LIO_IO_ASYNC_TYPES (LIO_IO_ASYNC|LIO_IO_SLISTIO|LIO_IO_ALISTIO) +#endif /* sgi */ +#ifdef linux +/* all signal wait types */ +#define LIO_WAIT_SIGTYPES (LIO_WAIT_SIGPAUSE) +#endif /* linux */ +#ifdef CRAY +/* all signal wait types */ +#define LIO_WAIT_SIGTYPES (LIO_WAIT_SIGPAUSE|LIO_WAIT_SIGACTIVE) +#endif /* CRAY */ + +/* + * This bit provides a way to randomly pick an io type and wait method. + * lio_read_buffer() and lio_write_buffer() functions will call + * lio_random_methods() with the given method. + */ +#define LIO_RANDOM 010000000 + +/* + * This bit provides a way for the programmer to use async i/o with + * signals and to use their own signal handler. By default, + * the signal will only be given to the system call if the wait + * method is LIO_WAIT_SIGPAUSE or LIO_WAIT_SIGACTIVE. + * Whenever these wait methods are used, libio signal handler + * will be used. + */ +#define LIO_USE_SIGNAL 020000000 + +/* + * prototypes/structures for functions in the libio.c module. See comments + * in that module, or man page entries for information on the individual + * functions. + */ + +int stride_bounds(int offset, int stride, int nstrides, + int bytes_per_stride, int *min_byte, int *max_byte); + +int lio_set_debug(int level); +int lio_parse_io_arg1(char *string); +void lio_help1(char *prefex); +int lio_parse_io_arg2(char *string, char **badtoken); +void lio_help2(char *prefex); +int lio_write_buffer(int fd, int method, char *buffer, int size, + int sig, char **errmsg, long wrd); + +int lio_read_buffer(int fd, int method, char *buffer, int size, + int sig, char **errmsg, long wrd); +int lio_random_methods(long mask); + +#if CRAY +#include +int lio_wait4asyncio(int method, int fd, struct iosw **statptr); +int lio_check_asyncio(char *io_type, int size, struct iosw *status); +#endif /* CRAY */ +#ifdef sgi +#include +int lio_wait4asyncio(int method, int fd, aiocb_t *aiocbp); +int lio_check_asyncio(char *io_type, int size, aiocb_t *aiocbp, int method); +#endif /* sgi */ + +/* + * Define the structure that contains the infomation that is used + * by the parsing and help functions. + */ +struct lio_info_type { + char *token; + int bits; + char *desc; +}; + + diff --git a/include/usctest.h b/include/usctest.h new file mode 100644 index 00000000..3655023f --- /dev/null +++ b/include/usctest.h @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ + +/* $Id: usctest.h,v 1.1 2003/07/07 06:36:46 nathans Exp $ */ + +/********************************************************** + * + * IRIX/Linux Feature Test and Evaluation - Silicon Graphics, Inc. + * + * FUNCTION NAME : usctest.h + * + * FUNCTION TITLE : System Call Test Macros + * + * SYNOPSIS: + * See DESCRIPTION below. + * + * AUTHOR : William Roske + * + * INITIAL RELEASE : UNICOS 7.0 + * + * DESCRIPTION + * TEST(SCALL) - calls a system call + * TEST_VOID(SCALL) - same as TEST() but for syscalls with no return value. + * TEST_CLEANUP - print the log of errno return counts if STD_ERRNO_LOG + * is set. + * TEST_PAUSEF(HAND) - Pause for SIGUSR1 if the pause flag is set. + * Use "hand" as the interrupt handling function + * TEST_PAUSE - Pause for SIGUSR1 if the pause flag is set. + * Use internal function to do nothing on signal and go on. + * TEST_LOOPING(COUNTER) - Conditional to check if test should + * loop. Evaluates to TRUE (1) or FALSE (0). + * TEST_ERROR_LOG(eno) - log that this errno was received, + * if STD_ERRNO_LOG is set. + * TEST_EXP_ENOS(array) - set the bits in TEST_VALID_ENO array at + * positions specified in integer "array" + * + * RETURN VALUE + * TEST(SCALL) - Global Variables set: + * int TEST_RETURN=return code from SCALL + * int TEST_ERRNO=value of errno at return from SCALL + * TEST_VOID(SCALL) - Global Variables set: + * int TEST_ERRNO=value of errno at return from SCALL + * TEST_CLEANUP - None. + * TEST_PAUSEF(HAND) - None. + * TEST_PAUSE - None. + * TEST_LOOPING(COUNTER) - True if COUNTER < STD_LOOP_COUNT or + * STD_INFINITE is set. + * TEST_ERROR_LOG(eno) - None + * TEST_EXP_ENOS(array) - None + * + * KNOWN BUGS + * If you use the TEST_PAUSE or TEST_LOOPING macros, you must + * link in parse_opts.o, which contains the code for those functions. + * + *#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#**/ + +#ifndef __USCTEST_H__ +#define __USCTEST_H__ 1 + +#ifndef _SC_CLK_TCK +#include +#endif + +#include + +/* + * Ensure that PATH_MAX is defined + */ +#ifndef PATH_MAX +#ifdef MAXPATHLEN +#define PATH_MAX MAXPATHLEN +#else +#define PATH_MAX 1024 +#endif +#endif + +#ifndef CRAY +#ifndef BSIZE +#define BSIZE BBSIZE +#endif +#endif + +/*********************************************************************** + * Define option_t structure type. + * Entries in this struct are used by the parse_opts routine + * to indicate valid options and return option arguments + ***********************************************************************/ +typedef struct { + char *option; /* Valid option string (one option only) like "a:" */ + int *flag; /* pointer to location to set true if option given */ + char **arg; /* pointer to location to place argument, if needed */ +} option_t; + +/*********************************************************************** + * The following globals are defined in parse_opts.c but must be + * externed here because they are used in the macros defined below. + ***********************************************************************/ +extern int STD_FUNCTIONAL_TEST, /* turned off by -f to not do functional test */ + STD_TIMING_ON, /* turned on by -t to print timing stats */ + STD_PAUSE, /* turned on by -p to pause before loop */ + STD_INFINITE, /* turned on by -i0 to loop forever */ + STD_LOOP_COUNT, /* changed by -in to set loop count to n */ + STD_ERRNO_LOG, /* turned on by -e to log errnos returned */ + STD_ERRNO_LIST[], /* counts of errnos returned. indexed by errno */ + STD_COPIES, + STD_argind; + +extern float STD_LOOP_DURATION, /* wall clock time to iterate */ + STD_LOOP_DELAY; /* delay time after each iteration */ + +#define USC_MAX_ERRNO 2000 + +/********************************************************************** + * Prototype for parse_opts routine + **********************************************************************/ +extern char *parse_opts(int ac, char **av, option_t *user_optarr, void (*uhf)()); + + +/* + * define a structure + */ +struct usc_errno_t { + int flag; +}; + +/*********************************************************************** + **** + **** + **** + **********************************************************************/ +#ifdef _USC_LIB_ + +extern int TEST_RETURN; +extern int TEST_ERRNO; + +#else +/*********************************************************************** + * Global array of bit masks to indicate errnos that are expected. + * Bits set by TEST_EXP_ENOS() macro and used by TEST_CLEANUP() macro. + ***********************************************************************/ +struct usc_errno_t TEST_VALID_ENO[USC_MAX_ERRNO]; + +/*********************************************************************** + * Globals for returning the return code and errno from the system call + * test macros. + ***********************************************************************/ +int TEST_RETURN; +int TEST_ERRNO; + +/*********************************************************************** + * temporary variables for determining max and min times in TEST macro + ***********************************************************************/ +long btime, etime, tmptime; + +#endif /* _USC_LIB_ */ + +/*********************************************************************** + * structure for timing accumulator and counters + ***********************************************************************/ +struct tblock { + long tb_max; + long tb_min; + long tb_total; + long tb_count; +}; + +/*********************************************************************** + * The following globals are externed here so that they are accessable + * in the macros that follow. + ***********************************************************************/ +extern struct tblock tblock; +extern void STD_go(); +extern int (*_TMP_FUNC)(void); +extern void STD_opts_help(); + + +/*********************************************************************** + * TEST: calls a system call + * + * parameters: + * SCALL = system call and parameters to execute + * + ***********************************************************************/ +#define TEST(SCALL) errno=0; TEST_RETURN = (unsigned) SCALL; TEST_ERRNO=errno; + +/*********************************************************************** + * TEST_VOID: calls a system call + * + * parameters: + * SCALL = system call and parameters to execute + * + * Note: This is IDENTICAL to the TEST() macro except that it is intended + * for use with syscalls returning no values (void syscall()). The + * Typecasting nothing (void) into an unsigned integer causes compilation + * errors. + * + ***********************************************************************/ +#define TEST_VOID(SCALL) errno=0; SCALL; TEST_ERRNO=errno; + +/*********************************************************************** + * TEST_CLEANUP: print system call timing stats and errno log entries + * to stdout if STD_TIMING_ON and STD_ERRNO_LOG are set, respectively. + * Do NOT print ANY information if no system calls logged. + * + * parameters: + * none + * + ***********************************************************************/ +#define TEST_CLEANUP \ +if ( STD_ERRNO_LOG ) { \ + for (tmptime=0; tmptime +#include +#include "dataascii.h" + +#define CHARS "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghjiklmnopqrstuvwxyz\n" +#define CHARS_SIZE sizeof(CHARS) + +#ifdef UNIT_TEST +#include /* malloc */ +#endif + +static char Errmsg[80]; + +int +dataasciigen(listofchars, buffer, bsize, offset) +char *listofchars; /* a null terminated list of characters */ +char *buffer; +int bsize; +int offset; +{ + int cnt; + int total; + int ind; /* index into CHARS array */ + char *chr; + int chars_size; + char *charlist; + + chr=buffer; + total=offset+bsize; + + if ( listofchars == NULL ) { + charlist=CHARS; + chars_size=CHARS_SIZE; + } + else { + charlist=listofchars; + chars_size=strlen(listofchars); + } + + for(cnt=offset; cnt +#include +#include /* memset */ +#include /* rand */ +#include "databin.h" + +#if UNIT_TEST +#include +#endif + +static char Errmsg[80]; + +void +databingen (mode, buffer, bsize, offset) +int mode; /* either a, c, r, o, z or C */ +unsigned char *buffer; /* buffer pointer */ +int bsize; /* size of buffer */ +int offset; /* offset into the file where buffer starts */ +{ +int ind; + + switch (mode) + { + default: + case 'a': /* alternating bit pattern */ + memset(buffer,0x55,bsize); + break; + + case 'c': /* checkerboard pattern */ + memset(buffer,0xf0,bsize); + break; + + case 'C': /* */ + for (ind=0;ind< bsize;ind++) { + buffer[ind] = ((offset+ind)%8 & 0177); + } + break; + + case 'o': + memset(buffer,0xff,bsize); + break; + + case 'z': + memset(buffer,0x0,bsize); + break; + + case 'r': /* random */ + for (ind=0;ind< bsize;ind++) { + buffer[ind] = (rand () & 0177) | 0100; + } + } +} + +/*********************************************************************** + * + * return values: + * >= 0 : error at byte offset into the file, offset+buffer[0-(bsize-1)] + * < 0 : no error + ***********************************************************************/ +int +databinchk(mode, buffer, bsize, offset, errmsg) +int mode; /* either a, c, r, z, o, or C */ +unsigned char *buffer; /* buffer pointer */ +int bsize; /* size of buffer */ +int offset; /* offset into the file where buffer starts */ +char **errmsg; +{ + int cnt; + unsigned char *chr; + int total; + long expbits; + long actbits; + + chr=buffer; + total=bsize; + + if ( errmsg != NULL ) { + *errmsg = Errmsg; + } + + switch (mode) + { + default: + case 'a': /* alternating bit pattern */ + expbits=0x55; + break; + + case 'c': /* checkerboard pattern */ + expbits=0xf0; + break; + + case 'C': /* counting pattern */ + for (cnt=0;cnt< bsize;cnt++) { + expbits = ((offset+cnt)%8 & 0177); + + if ( buffer[cnt] != expbits ) { + sprintf(Errmsg, + "data mismatch at offset %d, exp:%#lo, act:%#o", + offset+cnt, expbits, buffer[cnt]); + return offset+cnt; + } + } + sprintf(Errmsg, "all %d bytes match desired pattern", bsize); + return -1; + + case 'o': + expbits=0xff; + break; + + case 'z': + expbits=0; + break; + + case 'r': + return -1; /* no check can be done for random */ + } + + for (cnt=0; cnt< word-offset in file (same #) >< pid > + +1234567890123456789012345678901234567890123456789012345678901234 +________________________________________________________________ +< pid >< offset in file of this word >< pid > + + +8 bits to a bytes == character + NBPW 8 +************/ + +#include +#include +#ifdef UNIT_TEST +#include +#include +#endif + +static char Errmsg[80]; + +#define LOWER16BITS(X) (X & 0177777) +#define LOWER32BITS(X) (X & 0xffffffff) + +/*** +#define HIGHBITS(WRD, bits) ( (-1 << (64-bits)) & WRD) +#define LOWBITS(WRD, bits) ( (-1 >> (64-bits)) & WRD) +****/ + +#define NBPBYTE 8 /* number bits per byte */ + +#ifndef DEBUG +#define DEBUG 0 +#endif + +/*********************************************************************** + * + * + * 1 2 3 4 5 6 7 8 9 10 11 12 13 14 14 15 bytes + * 1234567890123456789012345678901234567890123456789012345678901234 bits + * ________________________________________________________________ 1 word + * < pid >< offset in file of this word >< pid > + * + * the words are put together where offset zero is the start. + * thus, offset 16 is the start of the second full word + * Thus, offset 8 is in middle of word 1 + ***********************************************************************/ +int +datapidgen(pid, buffer, bsize, offset) +int pid; +char *buffer; +int bsize; +int offset; +{ +#if CRAY + + int cnt; + int tmp; + char *chr; + long *wptr; + long word; + int woff; /* file offset for the word */ + int boff; /* buffer offset or index */ + int num_full_words; + + num_full_words = bsize/NBPW; + boff = 0; + + if ( cnt=(offset % NBPW) ) { /* partial word */ + + woff = offset - cnt; +#if DEBUG +printf("partial at beginning, cnt = %d, woff = %d\n", cnt, woff); +#endif + + word = ((LOWER16BITS(pid) << 48) | (LOWER32BITS(woff) << 16) | LOWER16BITS(pid)); + + chr = (char *)&word; + + for (tmp=0; tmp +#include +#include +#include +#include +#include +#include +#include +#include +#include /* memset, strerror */ +#include "file_lock.h" + + +#ifndef EFSEXCLWR +#define EFSEXCLWR 503 +#endif + +/* + * String containing the last system call. + * + */ +char Fl_syscall_str[128]; + +static char errmsg[256]; + +/*********************************************************************** + * + * Test interface to the fcntl system call. + * It will loop if the LOCK_NB flags is NOT set. + ***********************************************************************/ +int +file_lock(fd, flags, errormsg) +int fd; +int flags; +char **errormsg; +{ + register int cmd, ret; + struct flock flocks; + + memset(&flocks, 0, sizeof(struct flock)); + + if (flags&LOCK_NB) + cmd = F_SETLK; + else + cmd = F_SETLKW; + + flocks.l_whence = 0; + flocks.l_start = 0; + flocks.l_len = 0; + + if (flags&LOCK_UN) + flocks.l_type = F_UNLCK; + else if (flags&LOCK_EX) + flocks.l_type = F_WRLCK; + else if (flags&LOCK_SH) + flocks.l_type = F_RDLCK; + else { + errno = EINVAL; + if ( errormsg != NULL ) { + sprintf(errmsg, + "Programmer error, called file_lock with in valid flags\n"); + *errormsg = errmsg; + } + return -1; + } + + sprintf(Fl_syscall_str, + "fcntl(%d, %d, &flocks): type:%d whence:%d, start:%lld len:%lld\n", + fd, cmd, flocks.l_type, flocks.l_whence, + (long long)flocks.l_start, (long long)flocks.l_len); + + while (1) { + ret = fcntl(fd, cmd, &flocks); + + if ( ret < 0 ) { + if ( cmd == F_SETLK ) + switch (errno) { + /* these errors are okay */ + case EACCES: /* Permission denied */ + case EINTR: /* interrupted system call */ +#ifdef EFILESH + case EFILESH: /* file shared */ +#endif + case EFSEXCLWR: /* File is write protected */ + continue; /* retry getting lock */ + } + if ( errormsg != NULL ) { + sprintf(errmsg, "fcntl(%d, %d, &flocks): errno:%d %s\n", + fd, cmd, errno, strerror(errno)); + *errormsg = errmsg; + } + return -1; + } + break; + } + + return ret; + +} /* end of file_lock */ + +/*********************************************************************** + * + * Test interface to the fcntl system call. + * It will loop if the LOCK_NB flags is NOT set. + ***********************************************************************/ +int +record_lock(fd, flags, start, len, errormsg) +int fd; +int flags; +int start; +int len; +char **errormsg; +{ + register int cmd, ret; + struct flock flocks; + + memset(&flocks, 0, sizeof(struct flock)); + + if (flags&LOCK_NB) + cmd = F_SETLK; + else + cmd = F_SETLKW; + + flocks.l_whence = 0; + flocks.l_start = start; + flocks.l_len = len; + + if (flags&LOCK_UN) + flocks.l_type = F_UNLCK; + else if (flags&LOCK_EX) + flocks.l_type = F_WRLCK; + else if (flags&LOCK_SH) + flocks.l_type = F_RDLCK; + else { + errno = EINVAL; + if ( errormsg != NULL ) { + sprintf(errmsg, + "Programmer error, called record_lock with in valid flags\n"); + *errormsg = errmsg; + } + return -1; + } + + sprintf(Fl_syscall_str, + "fcntl(%d, %d, &flocks): type:%d whence:%d, start:%lld len:%lld\n", + fd, cmd, flocks.l_type, flocks.l_whence, + (long long)flocks.l_start, (long long)flocks.l_len); + + while (1) { + ret = fcntl(fd, cmd, &flocks); + + if ( ret < 0 ) { + if ( cmd == F_SETLK ) + switch (errno) { + /* these errors are okay */ + case EACCES: /* Permission denied */ + case EINTR: /* interrupted system call */ +#ifdef EFILESH + case EFILESH: /* file shared */ +#endif + case EFSEXCLWR: /* File is write protected */ + continue; /* retry getting lock */ + } + if ( errormsg != NULL ) { + sprintf(errmsg, "fcntl(%d, %d, &flocks): errno:%d %s\n", + fd, cmd, errno, strerror(errno)); + *errormsg = errmsg; + } + return -1; + } + break; + } + + return ret; + +} /* end of record_lock */ + + diff --git a/lib/forker.c b/lib/forker.c new file mode 100644 index 00000000..ca2c0332 --- /dev/null +++ b/lib/forker.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +/************************************************************** + * + * OS Testing - Silicon Graphics, Inc. + * + * FUNCTION NAME : forker + * background + * + * FUNCTION TITLE : fork desired number of copies of the current process + * fork a process and return control to caller + * + * SYNOPSIS: + * int forker(ncopies, mode, prefix) + * int ncopies; + * int mode; + * char *prefix; + * + * int background(prefix); + * char *prefix; + * + * extern int Forker_pids[]; + * extern int Forker_npids; + * + * AUTHOR : Richard Logan + * + * CO-PILOT(s) : Dean Roehrich + * + * INITIAL RELEASE : UNICOS 8.0 + * + * DESIGN DESCRIPTION + * The background function will do a fork of the current process. + * The parent process will then exit, thus orphaning the + * child process. Doing this will not nice the child process + * like executing a cmd in the background using "&" from the shell. + * If the fork fails and prefix is not NULL, a error message is printed + * to stderr and the process will exit with a value of errno. + * + * The forker function will fork minus one copies + * of the current process. There are two modes in how the forks + * will be done. Mode 0 (default) will have all new processes + * be childern of the parent process. Using Mode 1, + * the parent process will have one child and that child will + * fork the next process, if necessary, and on and on. + * The forker function will return the number of successful + * forks. This value will be different for the parent and each child. + * Using mode 0, the parent will get the total number of successful + * forks. Using mode 1, the newest child will get the total number + * of forks. The parent will get a return value of 1. + * + * The forker function also updates the global variables + * Forker_pids[] and Forker_npids. The Forker_pids array will + * be updated to contain the pid of each new process. The + * Forker_npids variable contains the number of entries + * in Forker_pids. Note, not all processes will have + * access to all pids via Forker_pids. If using mode 0, only the + * parent process and the last process will have all information. + * If using mode 1, only the last child process will have all information. + * + * If the prefix parameter is not NULL and the fork system call fails, + * a error message will be printed to stderr. The error message + * the be preceeded with prefix string. If prefix is NULL, + * no error message is printed. + * + * SPECIAL REQUIREMENTS + * None. + * + * UPDATE HISTORY + * This should contain the description, author, and date of any + * "interesting" modifications (i.e. info should helpful in + * maintaining/enhancing this module). + * username description + * ---------------------------------------------------------------- + * rrl This functions will first written during + * the SFS testing days, 1993. + * + * BUGS/LIMITATIONS + * The child pids are stored in the fixed array, Forker_pids. + * The array only has space for 4098 pids. Only the first + * 4098 pids will be stored in the array. + * + **************************************************************/ + +#include +#include +#include /* fork, getpid, sleep */ +#include +#include /* exit */ +#include "forker.h" + +int Forker_pids[FORKER_MAX_PIDS]; /* holds pids of forked processes */ +int Forker_npids=0; /* number of entries in Forker_pids */ + +/*********************************************************************** + * + * This function will fork and the parent will exit zero and + * the child will return. This will orphan the returning process + * putting it in the background. + * + * Return Value + * 0 : if fork did not fail + * !0 : if fork failed, the return value will be the errno. + ***********************************************************************/ +int +background(prefix) +char *prefix; +{ + switch (fork()) { + case -1: + if ( prefix != NULL ) + fprintf(stderr, "%s: In %s background(), fork() failed, errno:%d %s\n", + prefix, __FILE__, errno, strerror(errno)); + exit(errno); + + case 0: /* child process */ + break; + + default: + exit(0); + } + + return 0; + +} /* end of background */ + +/*********************************************************************** + * Forker will fork ncopies-1 copies of self. + * + ***********************************************************************/ +int +forker(ncopies, mode, prefix) +int ncopies; +int mode; /* 0 - all childern of parent, 1 - only 1 direct child */ +char *prefix; /* if ! NULL, an message will be printed to stderr */ + /* if fork fails. The prefix (program name) will */ + /* preceed the message */ +{ + int cnt; + int pid; + static int ind = 0; + + Forker_pids[ind]=0; + + for ( cnt=1; cnt < ncopies; cnt++ ) { + + switch ( mode ) { + case 1 : /* only 1 direct child */ + if ( (pid = fork()) == -1 ) { + if ( prefix != NULL ) + fprintf(stderr, "%s: %s,forker(): fork() failed, errno:%d %s\n", + prefix, __FILE__, errno, strerror(errno)); + return 0; + } + Forker_npids++; + + switch (pid ) { + case 0: /* child - continues the forking */ + + if ( Forker_npids < FORKER_MAX_PIDS ) + Forker_pids[Forker_npids-1]=getpid(); + break; + + default: /* parent - stop the forking */ + if ( Forker_npids < FORKER_MAX_PIDS ) + Forker_pids[Forker_npids-1]=pid; + return cnt-1; + } + + break; + + default : /* all new processes are childern of parent */ + if ( (pid = fork()) == -1 ) { + if ( prefix != NULL ) + fprintf(stderr, "%s: %s,forker(): fork() failed, errno:%d %s\n", + prefix, __FILE__, errno, strerror(errno)); + return cnt-1; + } + Forker_npids++; + + switch (pid ) { + case 0: /* child - stops the forking */ + if ( Forker_npids < FORKER_MAX_PIDS ) + Forker_pids[Forker_npids-1]=getpid(); + return cnt; + + default: /* parent - continues the forking */ + if ( Forker_npids < FORKER_MAX_PIDS ) + Forker_pids[Forker_npids-1]=pid; + break; + } + break; + } + } + + if ( Forker_npids < FORKER_MAX_PIDS ) + Forker_pids[Forker_npids]=0; + return cnt-1; + +} /* end of forker */ + + +#if UNIT_TEST + +/* + * The following is a unit test main for the background and forker + * functions. + */ + +int +main(argc, argv) +int argc; +char **argv; +{ + int ncopies=1; + int mode=0; + int ret; + int ind; + + if ( argc == 1 ) { + printf("Usage: %s ncopies [mode]\n", argv[0]); + exit(1); + } + + if ( sscanf(argv[1], "%i", &ncopies) != 1 ) { + printf("%s: ncopies argument must be integer\n", argv[0]); + exit(1); + } + + if ( argc == 3 ) + if ( sscanf(argv[2], "%i", &mode) != 1 ) { + printf("%s: mode argument must be integer\n", argv[0]); + exit(1); + } + + printf("Starting Pid = %d\n", getpid()); + ret=background(argv[0]); + printf("After background() ret:%d, pid = %d\n", ret, getpid()); + + ret=forker(ncopies, mode, argv[0]); + + printf("forker(%d, %d, %s) ret:%d, pid = %d, sleeping 30 seconds.\n", + ncopies, mode, argv[0], ret, getpid()); + + printf("%d My version of Forker_pids[], Forker_npids = %d\n", + getpid(), Forker_npids); + + for (ind=0; ind is not NULL, + * will updated to point that symbol in . + * Parse_open_flags will return -1 on this error. + * Otherwise parse_open_flags will return the open flag bitmask. + * If parse_open_flags returns, will left unchanged. + * + * The openflags2symbols function attempts to convert open flag + * bits into human readable symbols (i.e. O_TRUNC). If there + * are more than one symbol, the string will be placed as + * a separator between symbols. Commonly used separators would + * be a comma "," or pipe "|". If is one and not all + * bits can be converted to symbols, the "UNKNOWN" + * symbol will be added to return string. + * Openflags2symbols will return the indentified symbols. + * If no symbols are recognized the return value will be a empty + * string or the "UNKNOWN" symbol. + * + * SPECIAL REQUIREMENTS + * None. + * + * UPDATE HISTORY + * This should contain the description, author, and date of any + * "interesting" modifications (i.e. info should helpful in + * maintaining/enhancing this module). + * username description + * ---------------------------------------------------------------- + * rrl This code was first created during the beginning + * of the SFS testing days. I think that was in 1993. + * This code was updated in 05/96. + * (05/96) openflags2symbols was written. + * + * BUGS/LIMITATIONS + * Currently (05/96) all known symbols are coded into openflags2symbols. + * If new open flags are added this code will have to updated + * to know about them or they will not be recognized. + * + **************************************************************/ + +#include +#include +#include +#include +#include /* strcat */ +#include "open_flags.h" + +#define UNKNOWN_SYMBOL "UNKNOWN" + +static char Open_symbols[512]; /* space for openflags2symbols return value */ + +struct open_flag_t { + char *symbol; + int flag; +}; + +static struct open_flag_t Open_flags[] = { + { "O_RDONLY", O_RDONLY }, + { "O_WRONLY", O_WRONLY }, + { "O_RDWR", O_RDWR }, + { "O_SYNC", O_SYNC }, + { "O_CREAT", O_CREAT }, + { "O_TRUNC", O_TRUNC }, + { "O_EXCL", O_EXCL }, + { "O_APPEND", O_APPEND }, + { "O_NONBLOCK", O_NONBLOCK }, +#if O_NOCTTY + { "O_NOCTTY", O_NOCTTY }, +#endif +#if O_DSYNC + { "O_DSYNC", O_DSYNC }, +#endif +#if O_RSYNC + { "O_RSYNC", O_RSYNC }, +#endif +#if O_ASYNC + { "O_ASYNC", O_ASYNC }, +#endif +#if O_PTYIGN + { "O_PTYIGN", O_PTYIGN }, +#endif +#if O_NDELAY + { "O_NDELAY", O_NDELAY }, +#endif +#if O_RAW + { "O_RAW", O_RAW }, +#endif +#ifdef O_SSD + { "O_SSD", O_SSD }, +#endif +#if O_BIG + { "O_BIG", O_BIG }, +#endif +#if O_PLACE + { "O_PLACE", O_PLACE }, +#endif +#if O_RESTART + { "O_RESTART", O_RESTART }, +#endif +#if O_SFSXOP + { "O_SFSXOP", O_SFSXOP }, +#endif +#if O_SFS_DEFER_TM + { "O_SFS_DEFER_TM", O_SFS_DEFER_TM }, +#endif +#if O_WELLFORMED + { "O_WELLFORMED", O_WELLFORMED }, +#endif +#if O_LDRAW + { "O_LDRAW", O_LDRAW }, +#endif +#if O_T3D + { "O_T3D", O_T3D }, +#endif /* O_T3D */ +#if O_PARALLEL + { "O_PARALLEL", O_PARALLEL }, + { "O_FSA", O_PARALLEL|O_WELLFORMED|O_RAW }, /* short cut */ +#endif /* O_PARALLEL */ +#ifdef O_LARGEFILE + { "O_LARGEFILE", O_LARGEFILE }, +#endif +#ifdef O_DIRECT + { "O_DIRECT", O_DIRECT }, +#endif +#ifdef O_PRIV + { "O_PRIV", O_PRIV }, +#endif + +}; + +int +parse_open_flags(char *string, char **badname) +{ + int bits = 0; + char *name; + char *cc; + char savecc; + int found; + int ind; + + name=string; + cc=name; + + while ( 1 ) { + + for(; ((*cc != ',') && (*cc != '\0')); cc++); + savecc = *cc; + *cc = '\0'; + + found = 0; + + for(ind=0; ind < sizeof(Open_flags)/sizeof(struct open_flag_t); ind++) { + if ( strcmp(name, Open_flags[ind].symbol) == 0 ) { + bits |= Open_flags[ind].flag; + found=1; + break; + } + } + + *cc = savecc; /* restore string */ + + if ( found == 0 ) { /* invalid name */ + if ( badname != NULL ) + *badname = name; + return -1; + } + + if ( savecc == '\0' ) + break; + + name = ++cc; + + } /* end while */ + + return bits; + +} /* end of parse_open_flags */ + + +char * +openflags2symbols(int openflags, char *sep, int mode) +{ + int ind; + int size; + int bits = openflags; + int havesome=0; + + Open_symbols[0]='\0'; + + size=sizeof(Open_flags)/sizeof(struct open_flag_t); + + /* + * Deal with special case of O_RDONLY. If O_WRONLY nor O_RDWR + * bits are not set, assume O_RDONLY. + */ + + if ( (bits & (O_WRONLY | O_RDWR)) == 0 ) { + strcat(Open_symbols, "O_RDONLY"); + havesome=1; + } + + /* + * Loop through all but O_RDONLY elments of Open_flags + */ + for(ind=1; ind < size; ind++) { + + if ( (bits & Open_flags[ind].flag) == Open_flags[ind].flag ) { + if ( havesome ) + strcat(Open_symbols, sep); + + strcat(Open_symbols, Open_flags[ind].symbol); + havesome++; + + /* remove flag bits from bits */ + bits = bits & (~Open_flags[ind].flag); + } + } + + /* + * If not all bits were identified and mode was equal to 1, + * added UNKNOWN_SYMBOL to return string + */ + if ( bits && mode == 1 ) { /* not all bits were identified */ + if ( havesome ) + strcat(Open_symbols, sep); + strcat(Open_symbols, UNKNOWN_SYMBOL); + } + + return Open_symbols; + +} /* end of openflags2symbols */ + + +#ifdef UNIT_TEST + +/* + * The following code provides a UNIT test main for + * parse_open_flags and openflags2symbols functions. + */ + +int +main(argc, argv) +int argc; +char **argv; +{ + int bits; + int ret; + char *err; + + if (argc == 1 ) { + printf("Usage: %s openflagsbits\n\t%s symbols\n", argv[0], argv[0]); + exit(1); + } + + if ( sscanf(argv[1], "%i", &bits) == 1 ) { + printf("openflags2symbols(%#o, \",\", 1) returned %s\n", + bits, openflags2symbols(bits, ",", 1)); + + } else { + ret=parse_open_flags(argv[1], &err); + if ( ret == -1 ) + printf("parse_open_flags(%s, &err) returned -1, err = %s\n", + argv[0], err); + else + printf("parse_open_flags(%s, &err) returned %#o\n", argv[0], ret); + } + + exit(0); +} + +#endif /* end of UNIT_TEST */ diff --git a/lib/pattern.c b/lib/pattern.c new file mode 100644 index 00000000..7f4d5873 --- /dev/null +++ b/lib/pattern.c @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +#include +#include "pattern.h" + +/* + * The routines in this module are used to fill/check a data buffer + * with/against a known pattern. + */ + +int +pattern_check(buf, buflen, pat, patlen, patshift) +char *buf; +int buflen; +char *pat; +int patlen; +int patshift; +{ + int nb, ncmp, nleft; + char *cp; + + if (patlen) + patshift = patshift % patlen; + + cp = buf; + nleft = buflen; + + /* + * The following 2 blocks of code are to compare the first patlen + * bytes of buf. We need 2 checks if patshift is > 0 since we + * must check the last (patlen - patshift) bytes, and then the + * first (patshift) bytes. + */ + + nb = patlen - patshift; + if (nleft < nb) { + return (memcmp(cp, pat + patshift, nleft) ? -1 : 0); + } else { + if (memcmp(cp, pat + patshift, nb)) + return -1; + + nleft -= nb; + cp += nb; + } + + if (patshift > 0) { + nb = patshift; + if (nleft < nb) { + return (memcmp(cp, pat, nleft) ? -1 : 0); + } else { + if (memcmp(cp, pat, nb)) + return -1; + + nleft -= nb; + cp += nb; + } + } + + /* + * Now, verify the rest of the buffer using the algorithm described + * in the function header. + */ + + ncmp = cp - buf; + while (ncmp < buflen) { + nb = (ncmp < nleft) ? ncmp : nleft; + if (memcmp(buf, cp, nb)) + return -1; + + cp += nb; + ncmp += nb; + nleft -= nb; + } + + return 0; +} + +int +pattern_fill(buf, buflen, pat, patlen, patshift) +char *buf; +int buflen; +char *pat; +int patlen; +int patshift; +{ + int trans, ncopied, nleft; + char *cp; + + if (patlen) + patshift = patshift % patlen; + + cp = buf; + nleft = buflen; + + /* + * The following 2 blocks of code are to fill the first patlen + * bytes of buf. We need 2 sections if patshift is > 0 since we + * must first copy the last (patlen - patshift) bytes into buf[0]..., + * and then the first (patshift) bytes of pattern following them. + */ + + trans = patlen - patshift; + if (nleft < trans) { + memcpy(cp, pat + patshift, nleft); + return 0; + } else { + memcpy(cp, pat + patshift, trans); + nleft -= trans; + cp += trans; + } + + if (patshift > 0) { + trans = patshift; + if (nleft < trans) { + memcpy(cp, pat, nleft); + return 0; + } else { + memcpy(cp, pat, trans); + nleft -= trans; + cp += trans; + } + } + + /* + * Now, fill the rest of the buffer using the algorithm described + * in the function header comment. + */ + + ncopied = cp - buf; + while (ncopied < buflen) { + trans = (ncopied < nleft) ? ncopied : nleft; + memcpy(cp, buf, trans); + cp += trans; + ncopied += trans; + nleft -= trans; + } + + return(0); +} diff --git a/src/random.c b/lib/random.c similarity index 100% rename from src/random.c rename to lib/random.c diff --git a/lib/random_range.c b/lib/random_range.c new file mode 100644 index 00000000..124cd796 --- /dev/null +++ b/lib/random_range.c @@ -0,0 +1,917 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +#include +#include +#include +#include +#include "random_range.h" + +/* + * Internal format of the range array set up by parse_range() + */ + +struct range { + int min; + int max; + int mult; +}; + +/* + * parse_ranges() is a function to parse a comma-separated list of range + * tokens each having the following form: + * + * num + * or + * min:max[:mult] + * + * any of the values may be blank (ie. min::mult, :max, etc.) and default + * values for missing arguments may be supplied by the caller. + * + * The special first form is short hand for 'num:num'. + * + * After parsing the string, the ranges are put into an array of integers, + * which is malloc'd by the routine. The min, max, and mult entries of each + * range can be extracted from the array using the range_min(), range_max(), + * and range_mult() functions. + * + * It is the responsibility of the caller to free the space allocated by + * parse_ranges() - a single call to free() will free the space. + * + * str The string to parse - assumed to be a comma-separated + * list of tokens having the above format. + * defmin default value to plug in for min, if it is missing + * defmax default value to plug in for max, if it is missing + * defmult default value to plug in for mult, if missing + * parse_func A user-supplied function pointer, which parse_ranges() + * can call to parse the min, max, and mult strings. This + * allows for customized number formats. The function + * MUST have the following prototype: + * parse_func(char *str, int *val) + * The function should return -1 if str cannot be parsed + * into an integer, or >= 0 if it was successfully + * parsed. The resulting integer will be stored in + * *val. If parse_func is NULL, parse_ranges will parse + * the tokens in a manner consistent with the the sscanf + * %i format. + * range_ptr A user-supplied char **, which will be set to point + * at malloc'd space which holds the parsed range + * values. If range_ptr is NULL, parse_ranges() just + * parses the string. The data returned in range_ptr + * should not be processed directly - use the functions + * range_min(), range_max(), and range_mult() to access + * data for a given range. + * errptr user-supplied char ** which can be set to point to a + * static error string. If errptr is NULL, it is ignored. + * + * parse_range() returns -1 on error, or the number of ranges parsed. + */ + +static int str_to_int(); +static long long divider(long long, long long, long long, long long); + +int +parse_ranges(str, defmin, defmax, defmult, parse_func, rangeptr, errptr) +char *str; +int defmin; +int defmax; +int defmult; +int (*parse_func)(); +char **rangeptr; +char **errptr; +{ + int ncommas; + char *tmpstr, *cp, *tok, *n1str, *n2str, *multstr; + struct range *rp, *ranges; + static char errmsg[256]; + + if (errptr != NULL) { + *errptr = errmsg; + } + + for (ncommas = 0, cp = str; *cp != '\0'; cp++) { + if (*cp == ',') { + ncommas++; + } + } + + if (parse_func == NULL) { + parse_func = str_to_int; + } + + tmpstr = strdup(str); + ranges = (struct range *)malloc((ncommas+1) * sizeof(struct range)); + rp = ranges; + + tok = strtok(tmpstr, ","); + while (tok != NULL) { + n1str = tok; + n2str = NULL; + multstr = NULL; + + rp->min = defmin; + rp->max = defmax; + rp->mult = defmult; + + if ((cp = strchr(n1str, ':')) != NULL) { + *cp = '\0'; + n2str = cp+1; + + if ((cp = strchr(n2str, ':')) != NULL) { + *cp = '\0'; + multstr = cp+1; + } + } + + /* + * Parse the 'min' field - if it is zero length (:n2[:mult] + * format), retain the default value, otherwise, pass the + * string to the parse function. + */ + + if ((int)strlen(n1str) > 0) { + if ((*parse_func)(n1str, &rp->min) < 0) { + sprintf(errmsg, "error parsing string %s into an integer", n1str); + free(tmpstr); + free(ranges); + return -1; + } + } + + /* + * Process the 'max' field - if one was not present (n1 format) + * set max equal to min. If the field was present, but + * zero length (n1: format), retain the default. Otherwise + * pass the string to the parse function. + */ + + if (n2str == NULL) { + rp->max = rp->min; + } else if ((int)strlen(n2str) > 0) { + if ((*parse_func)(n2str, &rp->max) < 0) { + sprintf(errmsg, "error parsing string %s into an integer", n2str); + free(tmpstr); + free(ranges); + return -1; + } + } + + /* + * Process the 'mult' field - if one was not present + * (n1:n2 format), or the field was zero length (n1:n2: format) + * then set the mult field to defmult - otherwise pass then + * mult field to the parse function. + */ + + if (multstr != NULL && (int)strlen(multstr) > 0) { + if ((*parse_func)(multstr, &rp->mult) < 0) { + sprintf(errmsg, "error parsing string %s into an integer", multstr); + free(tmpstr); + free(ranges); + return -1; + } + } + + rp++; + tok = strtok(NULL, ","); + } + + free(tmpstr); + + if (rangeptr != NULL) { + *rangeptr = (char *)ranges; + } else { + free(ranges); /* just running in parse mode */ + } + + return (rp - ranges); +} + +/* + * The default integer-parsing function + */ + +static int +str_to_int(str, ip) +char *str; +int *ip; +{ + char c; + + if (sscanf(str, "%i%c", ip, &c) != 1) { + return -1; + } else { + return 0; + } +} + +/* + * Three simple functions to return the min, max, and mult values for a given + * range. It is assumed that rbuf is a range buffer set up by parse_ranges(), + * and that r is a valid range within that buffer. + */ + +int +range_min(rbuf, r) +char *rbuf; +int r; +{ + return ((struct range *)rbuf)[r].min; +} + +int +range_max(rbuf, r) +char *rbuf; +int r; +{ + return ((struct range *)rbuf)[r].max; +} + +int +range_mult(rbuf, r) +char *rbuf; +int r; +{ + return ((struct range *)rbuf)[r].mult; +} + +/***************************************************************************** + * random_range(int start, int end, int mult, char **errp) + * + * Returns a psuedo-random number which is >= 'start', <= 'end', and a multiple + * of 'mult'. Start and end may be any valid integer, but mult must be an + * integer > 0. errp is a char ** which will be set to point to a static + * error message buffer if it is not NULL, and an error occurs. + * + * The errp is the only way to check if the routine fails - currently the only + * failure conditions are: + * + * mult < 1 + * no numbers in the start-end range that are a multiple of 'mult' + * + * If random_range_fails, and errp is a valid pointer, it will point to an + * internal error buffer. If errp is a vaild pointer, and random_range + * is successful, errp will be set to NULL. + * + * Note - if mult is 1 (the most common case), there are error conditions + * possible, and errp need not be used. + * + * Note: Uses lrand48(), assuming that set_random_seed() uses srand48() when + * setting the seed. + *****************************************************************************/ + +long +random_range(min, max, mult, errp) +int min; +int max; +int mult; +char **errp; +{ + int r, nmults, orig_min, orig_max, orig_mult, tmp; + extern long lrand48(); + static char errbuf[128]; + + /* + * Sanity check + */ + + if (mult < 1) { + if (errp != NULL) { + sprintf(errbuf, "mult arg must be greater than 0"); + *errp = errbuf; + } + return -1; + } + + /* + * Save original parameter values for use in error message + */ + + orig_min = min; + orig_max = max; + orig_mult = mult; + + /* + * switch min/max if max < min + */ + + if (max < min) { + tmp = max; + max = min; + min = tmp; + } + + /* + * select the random number + */ + + if ((r = min % mult)) /* bump to the next higher 'mult' multiple */ + min += mult - r; + + if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */ + max -= r; + + if (min > max) { /* no 'mult' multiples between min & max */ + if (errp != NULL) { + sprintf(errbuf, "no numbers in the range %d:%d that are a multiple of %d", orig_min, orig_max, orig_mult); + *errp = errbuf; + } + return -1; + } + + if (errp != NULL) { + *errp = NULL; + } + + nmults = ((max - min) / mult) + 1; +#if CRAY + /* + * If max is less than 2gb, then the value can fit in 32 bits + * and the standard lrand48() routine can be used. + */ + if ( max <= (long)2147483647 ) { + return (long) (min + (((long)lrand48() % nmults) * mult)); + } else { + /* + * max is greater than 2gb - meeds more than 32 bits. + * Since lrand48 only will get a number up to 32bits. + */ + long randnum; + randnum=divider(min, max, 0, -1); + return (long) (min + ((randnum % nmults) * mult)); + } + +#else + return (min + ((lrand48() % nmults) * mult)); +#endif + +} + +/* + * Just like random_range, but all values are longs. + */ +long +random_rangel(min, max, mult, errp) +long min; +long max; +long mult; +char **errp; +{ + long r, nmults, orig_min, orig_max, orig_mult, tmp; + extern long lrand48(); + static char errbuf[128]; + + /* + * Sanity check + */ + + if (mult < 1) { + if (errp != NULL) { + sprintf(errbuf, "mult arg must be greater than 0"); + *errp = errbuf; + } + return -1; + } + + /* + * Save original parameter values for use in error message + */ + + orig_min = min; + orig_max = max; + orig_mult = mult; + + /* + * switch min/max if max < min + */ + + if (max < min) { + tmp = max; + max = min; + min = tmp; + } + + /* + * select the random number + */ + + if ((r = min % mult)) /* bump to the next higher 'mult' multiple */ + min += mult - r; + + if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */ + max -= r; + + if (min > max) { /* no 'mult' multiples between min & max */ + if (errp != NULL) { + sprintf(errbuf, + "no numbers in the range %ld:%ld that are a multiple of %ld", + orig_min, orig_max, orig_mult); + *errp = errbuf; + } + return -1; + } + + if (errp != NULL) { + *errp = NULL; + } + + nmults = ((max - min) / mult) + 1; +#if CRAY || (_MIPS_SZLONG == 64) + /* + * If max is less than 2gb, then the value can fit in 32 bits + * and the standard lrand48() routine can be used. + */ + if ( max <= (long)2147483647 ) { + return (long) (min + (((long)lrand48() % nmults) * mult)); + } else { + /* + * max is greater than 2gb - meeds more than 32 bits. + * Since lrand48 only will get a number up to 32bits. + */ + long randnum; + randnum=divider(min, max, 0, -1); + return (long) (min + ((randnum % nmults) * mult)); + } + +#else + return (min + ((lrand48() % nmults) * mult)); +#endif +} + +/* + * Attempts to be just like random_range, but everything is long long (64 bit) + */ +long long +random_rangell(min, max, mult, errp) +long long min; +long long max; +long long mult; +char **errp; +{ + long long r, nmults, orig_min, orig_max, orig_mult, tmp; + long long randnum; + extern long lrand48(); + static char errbuf[128]; + + /* + * Sanity check + */ + + if (mult < 1) { + if (errp != NULL) { + sprintf(errbuf, "mult arg must be greater than 0"); + *errp = errbuf; + } + return -1; + } + + /* + * Save original parameter values for use in error message + */ + + orig_min = min; + orig_max = max; + orig_mult = mult; + + /* + * switch min/max if max < min + */ + + if (max < min) { + tmp = max; + max = min; + min = tmp; + } + + /* + * select the random number + */ + + if ((r = min % mult)) /* bump to the next higher 'mult' multiple */ + min += mult - r; + + if ((r = max % mult)) /* reduce to the next lower 'mult' multiple */ + max -= r; + + if (min > max) { /* no 'mult' multiples between min & max */ + if (errp != NULL) { + sprintf(errbuf, + "no numbers in the range %lld:%lld that are a multiple of %lld", + orig_min, orig_max, orig_mult); + *errp = errbuf; + } + return -1; + } + + if (errp != NULL) { + *errp = NULL; + } + + nmults = ((max - min) / mult) + 1; + /* + * If max is less than 2gb, then the value can fit in 32 bits + * and the standard lrand48() routine can be used. + */ + if ( max <= (long)2147483647 ) { + return (long long) (min + (((long long)lrand48() % nmults) * mult)); + } else { + /* + * max is greater than 2gb - meeds more than 32 bits. + * Since lrand48 only will get a number up to 32bits. + */ + randnum=divider(min, max, 0, -1); + return (long long) (min + ((randnum % nmults) * mult)); + } + +} + +/* + * This functional will recusively call itself to return a random + * number min and max. It was designed to work the 64bit numbers + * even when compiled as 32 bit process. + * algorithm: to use the official lrand48() routine - limited to 32 bits. + * find the difference between min and max (max-min). + * if the difference is 2g or less, use the random number gotton from lrand48(). + * Determine the midway point between min and max. + * if the midway point is less than 2g from min or max, + * randomly add the random number gotton from lrand48() to + * either min or the midpoint. + * Otherwise, call outself with min and max being min and midway value or + * midway value and max. This will reduce the range in half. + */ +static long long +divider(long long min, long long max, long long cnt, long long rand) +{ + long long med, half, diff; + + /* + * prevent run away code. We are dividing by two each count. + * if we get to a count of more than 32, we should have gotten + * to 2gb. + */ + if ( cnt > 32 ) + return -1; + + /* + * Only get a random number the first time. + */ + if ( cnt == 0 || rand < -1 ) { + rand = (long long)lrand48(); /* 32 bit random number */ + } + + diff = max - min; + + if ( diff <= 2147483647 ) + return min + rand; + + half = diff/(long long)2; /* half the distance between min and max */ + med = min + half; /* med way point between min and max */ + +#if DEBUG +printf("divider: min=%lld, max=%lld, cnt=%lld, rand=%lld\n", min, max, cnt, rand); +printf(" diff = %lld, half = %lld, med = %lld\n", diff, half, med); +#endif + + if ( half <= 2147483647 ) { + /* + * If half is smaller than 2gb, we can use the random number + * to pick the number within the min to med or med to max + * if the cnt bit of rand is zero or one, respectively. + */ + if ( rand & (1<> 1; + nshift++; + } + + return 01L << (nshift-1); + +} + + +#if RANDOM_BIT_UNITTEST +/* + * The following is a unit test main function for random_bit(). + */ +main(argc, argv) +int argc; +char **argv; +{ + int ind; + int cnt, iter; + long mask, ret; + + printf("test for first and last bit set\n"); + mask=1L; + ret=random_bit(mask); + printf("random_bit(%#o) returned %#o\n", mask, ret); + + mask=1L<<(sizeof(long)*8-1); + ret=random_bit(mask); + printf("random_bit(%#o) returned %#o\n", mask, ret); + + if ( argc >= 3 ) { + iter=atoi(argv[1]); + for (ind=2; ind= 3 ) { + if ( sscanf(argv[2], "%i", &iter) != 1 ) { + printf("Usage: %s [func iterations] \n", argv[0]); + printf("argv[2] is not a number\n"); + exit(1); + } + } + + + /* + * random_rangel () + */ + if ( strcmp(argv[1], "random_rangel") == 0 ) { + ltmin=lmax; + part = lmax/PARTNUM; + for(ind=0; ind ltmax ) + ltmax = lret; + for(ind=0; ind valbound[PARTNUM-1] ) { + cntarr[PARTNUM-1]++; + } + } + for(ind=0; ind lltmax ) + lltmax = llret; + + for(ind=0; ind lvalbound[PARTNUM-1] ) { + cntarr[PARTNUM-1]++; + } + } + for(ind=0; ind itmax ) + itmax = lret; + + for(ind=0; ind valbound[PARTNUM-1] ) { + cntarr[PARTNUM-1]++; + } + } + for(ind=0; ind +#include +#include "str_to_bytes.h" + +/**************************************************************************** + * str_to_bytes(s) + * + * Computes the number of bytes described by string s. s is assumed to be + * a base 10 positive (ie. >= 0) number followed by an optional single + * character multiplier. The following multipliers are supported: + * + * char mult + * ----------------- + * b BSIZE or BBSIZE + * k 1024 bytes + * K 1024 * sizeof(long) + * m 2^20 (1048576) + * M 2^20 (1048576 * sizeof(long) + * g 2^30 (1073741824) + * G 2^30 (1073741824) * sizeof(long) + * + * for instance, "1k" and "1024" would both cause str_to_bytes to return 1024. + * + * Returns -1 if mult is an invalid character, or if the integer portion of + * s is not a positive integer. + * + ****************************************************************************/ + +#if CRAY +#define B_MULT BSIZE /* block size */ +#elif sgi +#define B_MULT BBSIZE /* block size */ +#elif linux +#define B_MULT DEV_BSIZE /* block size */ +#endif + + +#define K_MULT 1024 /* Kilo or 2^10 */ +#define M_MULT 1048576 /* Mega or 2^20 */ +#define G_MULT 1073741824 /* Giga or 2^30 */ +#define T_MULT 1099511627776 /* tera or 2^40 */ + +int +str_to_bytes(s) +char *s; +{ + char mult, junk; + int nconv; + float num; + + nconv = sscanf(s, "%f%c%c", &num, &mult, &junk); + if (nconv == 0 || nconv == 3 ) + return -1; + + if (nconv == 1) + return num; + + switch (mult) { + case 'b': + return (int)(num * (float)B_MULT); + case 'k': + return (int)(num * (float)K_MULT); + case 'K': + return (int)((num * (float)K_MULT) * sizeof(long)); + case 'm': + return (int)(num * (float)M_MULT); + case 'M': + return (int)((num * (float)M_MULT) * sizeof(long)); + case 'g': + return (int)(num * (float)G_MULT); + case 'G': + return (int)((num * (float)G_MULT) * sizeof(long)); + default: + return -1; + } +} + +long +str_to_lbytes(s) +char *s; +{ + char mult, junk; + long nconv; + float num; + + nconv = sscanf(s, "%f%c%c", &num, &mult, &junk); + if (nconv == 0 || nconv == 3 ) + return -1; + + if (nconv == 1) + return (long)num; + + switch (mult) { + case 'b': + return (long)(num * (float)B_MULT); + case 'k': + return (long)(num * (float)K_MULT); + case 'K': + return (long)((num * (float)K_MULT) * sizeof(long)); + case 'm': + return (long)(num * (float)M_MULT); + case 'M': + return (long)((num * (float)M_MULT) * sizeof(long)); + case 'g': + return (long)(num * (float)G_MULT); + case 'G': + return (long)((num * (float)G_MULT) * sizeof(long)); + default: + return -1; + } +} + +/* + * Force 64 bits number when compiled as 32 IRIX binary. + * This allows for a number bigger than 2G. + */ + +long long +str_to_llbytes(s) +char *s; +{ + char mult, junk; + long nconv; + double num; + + nconv = sscanf(s, "%lf%c%c", &num, &mult, &junk); + if (nconv == 0 || nconv == 3 ) + return -1; + + if (nconv == 1) + return (long long)num; + + switch (mult) { + case 'b': + return (long long)(num * (float)B_MULT); + case 'k': + return (long long)(num * (float)K_MULT); + case 'K': + return (long long)((num * (float)K_MULT) * sizeof(long long)); + case 'm': + return (long long)(num * (float)M_MULT); + case 'M': + return (long long)((num * (float)M_MULT) * sizeof(long long)); + case 'g': + return (long long)(num * (float)G_MULT); + case 'G': + return (long long)((num * (float)G_MULT) * sizeof(long long)); + default: + return -1; + } +} + +#ifdef UNIT_TEST + +main(int argc, char **argv) +{ + int ind; + + if (argc == 1 ) { + fprintf(stderr, "missing str_to_bytes() parameteres\n"); + exit(1); + } + + for (ind=1; ind +#include /* for string functions */ +#include "string_to_tokens.h" + +int +string_to_tokens(char *arg_string, char *arg_array[], int array_size, char *separator) +{ + int num_toks = 0; /* number of tokens found */ + char *strtok(); + + if ( arg_array == NULL || array_size <= 1 || separator == NULL ) + return -1; + + /* + * Use strtok() to parse 'arg_string', placing pointers to the + * individual tokens into the elements of 'arg_array'. + */ + if ( (arg_array[num_toks] = strtok(arg_string, separator)) == NULL ) { + return 0; + } + + for (num_toks=1;num_toks +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CRAY +#include +#include +#include +#else +/* for linux or sgi */ +#include /* readv(2)/writev(2) */ +#include /* bzero */ +#endif +#ifdef sgi +#include +#endif +#include /* atoi, abs */ + +#include "tlibio.h" /* defines LIO* marcos */ + +#ifndef PATH_MAX +#define PATH_MAX MAXPATHLEN +#endif + +#if 0 /* disabled until it's needed -- roehrich 6/11/97 */ +#define BUG1_workaround 1 /* Work around a condition where aio_return gives + * a value of zero but there is no errno followup + * and the read/write operation actually did its + * job. spr/pv 705244 + */ +#endif + + +#ifndef linux +static void lio_async_signal_handler(); +#endif +#ifdef sgi +static void lio_async_callback_handler(); +#endif + +/* + * Define the structure as used in lio_parse_arg1 and lio_help1 + */ +struct lio_info_type Lio_info1[] = { + { "s", LIO_IO_SYNC, "sync i/o" }, + { "p", LIO_IO_ASYNC|LIO_WAIT_SIGACTIVE, "async i/o using a loop to wait for a signal" }, + { "b", LIO_IO_ASYNC|LIO_WAIT_SIGPAUSE, "async i/o using pause" }, + { "a", LIO_IO_ASYNC|LIO_WAIT_RECALL, "async i/o using recall/aio_suspend" }, +#ifdef sgi + { "r", + LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, "random sync i/o types and wait methods" }, + { "R", + LIO_RANDOM|LIO_IO_ATYPES|LIO_WAIT_ATYPES, "random i/o types and wait methods" }, +#else + { "r", + LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, "random i/o types and wait methods" }, + { "R", + LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, "random i/o types and wait methods" }, +#endif + { "l", LIO_IO_SLISTIO|LIO_WAIT_RECALL, "single stride sync listio" }, + { "L", LIO_IO_ALISTIO|LIO_WAIT_RECALL, "single stride async listio using recall" }, + { "X", LIO_IO_ALISTIO|LIO_WAIT_SIGPAUSE, "single stride async listio using pause" }, + { "v", LIO_IO_SYNCV, "single buffer sync readv/writev" }, + { "P", LIO_IO_SYNCP, "sync pread/pwrite" }, +}; + +/* + * Define the structure used by lio_parse_arg2 and lio_help2 + */ +struct lio_info_type Lio_info2[] = { + { "sync", LIO_IO_SYNC, "sync i/o (read/write)"}, + { "async", LIO_IO_ASYNC, "async i/o (reada/writea/aio_read/aio_write)" }, + { "slistio", LIO_IO_SLISTIO, "single stride sync listio" }, + { "alistio", LIO_IO_ALISTIO, "single stride async listio" }, + { "syncv", LIO_IO_SYNCV, "single buffer sync readv/writev"}, + { "syncp", LIO_IO_SYNCP, "pread/pwrite"}, + { "active", LIO_WAIT_ACTIVE, "spin on status/control values" }, + { "recall", LIO_WAIT_RECALL, "use recall(2)/aio_suspend(3) to wait for i/o to complete" }, + { "sigactive", LIO_WAIT_SIGACTIVE, "spin waiting for signal" }, + { "sigpause", LIO_WAIT_SIGPAUSE, "call pause(2) to wait for signal" }, +/* nowait is a touchy thing, it's an accident that this implementation worked at all. 6/27/97 roehrich */ +/* { "nowait", LIO_WAIT_NONE, "do not wait for async io to complete" },*/ + { "random", LIO_RANDOM, "set random bit" }, + { "randomall", + LIO_RANDOM|LIO_IO_TYPES|LIO_WAIT_TYPES, + "all random i/o types and wait methods (except nowait)" }, +}; + +char Lio_SysCall[PATH_MAX]; /* string containing last i/o system call */ + +static volatile int Received_signal = 0; /* number of signals received */ +static volatile int Rec_signal; +#ifdef sgi +static volatile int Received_callback = 0; /* number of callbacks received */ +static volatile int Rec_callback; +#endif +static char Errormsg[500]; +static int Debug_level = 0; + + + +/*********************************************************************** + * stride_bounds() + * + * Determine the bounds of a strided request, normalized to offset. Returns + * the number of bytes needed to satisfy the request, and optionally sets + * *min and *max to the mininum and maximum bytes referenced, normalized + * around offset. + * + * Returns -1 on error - the only possible error conditions are illegal values + * for nstrides and/or bytes_per_stride - both parameters must be >= 0. + * + * (maule, 11/16/95) + ***********************************************************************/ + +int +stride_bounds(offset, stride, nstrides, bytes_per_stride, min, max) +int offset; +int stride; +int nstrides; +int bytes_per_stride; +int *min; +int *max; +{ + int nbytes, min_byte, max_byte; + + /* + * sanity checks ... + */ + + if (nstrides < 0 || bytes_per_stride < 0) { + return -1; + } + + if (stride == 0) { + stride = bytes_per_stride; + } + + /* + * Determine the # of bytes needed to satisfy the request. This + * value, along with the offset argument, determines the min and max + * bytes referenced. + */ + + + nbytes = abs(stride) * (nstrides-1) + bytes_per_stride; + + if (stride < 0) { + max_byte = offset + bytes_per_stride - 1; + min_byte = max_byte - nbytes + 1; + } else { + min_byte = offset; + max_byte = min_byte + nbytes - 1; + } + + if (min != NULL) { + *min = min_byte; + } + + if (max != NULL) { + *max = max_byte; + } + + return nbytes; +} + +/*********************************************************************** + * This function will allow someone to set the debug level. + ***********************************************************************/ +int +lio_set_debug(level) +{ + int old; + + old = Debug_level; + Debug_level = level; + return old; +} + +/*********************************************************************** + * This function will parse a string and return desired io-method. + * Only the first character of the string is used. + * + * This function does not provide for meaningful option arguments, + * but it supports current growfiles/btlk interface. + * + * (rrl 04/96) + ***********************************************************************/ +int +lio_parse_io_arg1(char *string) +{ + int ind; + int found=0; + int mask=0; + + /* + * Determine if token is a valid string. + */ + for(ind=0; ind 3 ) + printf("DEBUG %s/%d: method mask to choose from: %#o\n", __FILE__, __LINE__, method ); + method = lio_random_methods(method); + if ( Debug_level > 2 ) + printf("DEBUG %s/%d: random chosen method %#o\n", __FILE__, __LINE__, method); + } + + if ( errmsg != NULL ) + *errmsg = Errormsg; + + Rec_signal=Received_signal; /* get the current number of signals received */ +#ifdef sgi + Rec_callback=Received_callback; /* get the current number of callbacks received */ +#endif + +#ifdef CRAY + bzero(&status, sizeof(struct iosw)); + bzero(&request, sizeof(struct listreq)); + statptr[0] = &status; +#else + /* for linux or sgi */ + bzero(&iov, sizeof(struct iovec)); + iov.iov_base = buffer; + iov.iov_len = size; +#endif +#ifdef sgi + bzero(&aiocbp, sizeof(aiocb_t)); + aiocbp.aio_fildes = fd; + aiocbp.aio_nbytes = size; + aiocbp.aio_buf = buffer; +/* aiocbp.aio_offset = lseek( fd, 0, SEEK_CUR ); -- set below */ + aiocbp.aio_sigevent.sigev_notify = SIGEV_NONE; + aiocbp.aio_sigevent.sigev_signo = 0; + aiocbp.aio_sigevent.sigev_func = NULL; + aiocbp.aio_sigevent.sigev_value.sival_int = 0; + aiolist[0] = &aiocbp; + + if( (ret = lseek( fd, 0, SEEK_CUR )) == -1 ){ + ret = 0; + /* If there is an error and it is not ESPIPE then kick out the error. + * If the fd is a fifo then we have to make sure that + * lio_random_methods() didn't select pwrite/pread; if it did then + * switch to write/read. + */ + if( errno == ESPIPE ){ + if( method & LIO_IO_SYNCP ){ + if( omethod & LIO_RANDOM ){ + method &= ~LIO_IO_SYNCP; + method |= LIO_IO_SYNC; + if( Debug_level > 2 ) + printf("DEBUG %s/%d: random chosen method switched to %#o for fifo\n", __FILE__, __LINE__, method ); + } + else if( Debug_level ){ + printf("DEBUG %s/%d: pwrite will fail when it writes to a fifo\n", + __FILE__, __LINE__ ); + } + } + /* else: let it ride */ + } + else{ + sprintf(Errormsg, "%s/%d lseek(fd=%d,0,SEEK_CUR) failed, errno=%d %s", + __FILE__, __LINE__, fd, errno, strerror(errno)); + return -errno; + } + } + poffset = (off64_t)ret; + aiocbp.aio_offset = ret; + +#endif + + /* + * If the LIO_USE_SIGNAL bit is not set, only use the signal + * if the LIO_WAIT_SIGPAUSE or the LIO_WAIT_SIGACTIVE bits are bit. + * Otherwise there is not necessary a signal handler to trap + * the signal. + */ + if ( sig && !(method & LIO_USE_SIGNAL) && + ! (method & LIO_WAIT_SIGTYPES) ){ + + sig=0; /* ignore signal parameter */ + } + +#ifdef sgi + if ( sig && (method & LIO_WAIT_CBTYPES) ) + sig=0; /* ignore signal parameter */ +#endif + + /* + * only setup signal hander if sig was specified and + * a sig wait method was specified. + * Doing this will change the handler for this signal. The + * old signal handler will not be restored. + *** restoring the signal handler could be added *** + */ + + if ( sig && (method & LIO_WAIT_SIGTYPES) ){ +#ifdef CRAY + sigctl(SCTL_REG, sig, lio_async_signal_handler); +#endif +#ifdef sgi + aiocbp.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + aiocbp.aio_sigevent.sigev_signo = sig; + sigset(sig, lio_async_signal_handler); +#endif /* sgi */ + } +#ifdef sgi + else if( method & LIO_WAIT_CBTYPES ){ + /* sival_int just has to be something that I can use + * to identify the callback, and "size" happens to be handy... + */ + aiocbp.aio_sigevent.sigev_notify = SIGEV_CALLBACK; + aiocbp.aio_sigevent.sigev_func = lio_async_callback_handler; + aiocbp.aio_sigevent.sigev_value.sival_int = size; + } +#endif + + /* + * Determine the system call that will be called and produce + * the string of the system call and place it in Lio_SysCall. + * Also update the io_type char pointer to give brief description + * of system call. Execute the system call and check for + * system call failure. If sync i/o, return the number of + * bytes written/read. + */ + + if ( (method & LIO_IO_SYNC) || (method & LIO_IO_TYPES) == 0 ){ + /* + * write(2) is used if LIO_IO_SYNC bit is set or not none + * of the LIO_IO_TYPES bits are set (default). + */ + + sprintf(Lio_SysCall, + "write(%d, buf, %d)", fd, size); + io_type="write"; + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + if ((ret = write(fd, buffer, size)) == -1) { + sprintf(Errormsg, "%s/%d write(%d, buf, %d) ret:-1, errno=%d %s", + __FILE__, __LINE__, + fd, size, errno, strerror(errno)); + return -errno; + } + + if ( ret != size ) { + sprintf(Errormsg, + "%s/%d write(%d, buf, %d) returned=%d", + __FILE__, __LINE__, + fd, size, ret); + } + else if ( Debug_level > 1 ) + printf("DEBUG %s/%d: write completed without error (ret %d)\n", + __FILE__, __LINE__, ret); + + return ret; + + } + + else if ( method & LIO_IO_ASYNC ) { +#ifdef CRAY + sprintf(Lio_SysCall, + "writea(%d, buf, %d, &status, %d)", fd, size, sig); + io_type="writea"; + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + sigoff(); + if ((ret = writea(fd, buffer, size, &status, sig)) == -1) { + sprintf(Errormsg, + "%s/%d writea(%d, buf, %d, &stat, %d) ret:-1, errno=%d %s", + __FILE__, __LINE__, + fd, size, sig, errno, strerror(errno)); + sigon(); + return -errno; + } +#endif +#ifdef sgi + sprintf(Lio_SysCall, + "aio_write(fildes=%d, buf, nbytes=%d, signo=%d)", fd, size, sig); + io_type="aio_write"; + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + if( sig ) + sighold( sig ); + if ((ret = aio_write(&aiocbp)) == -1) { + sprintf(Errormsg, + "%s/%d aio_write(fildes=%d, buf, nbytes=%d, signo=%d) ret:-1, errno=%d %s", + __FILE__, __LINE__, + fd, size, sig, errno, strerror(errno)); + if( sig ) + sigrelse( sig ); + return -errno; + } +#endif + } /* LIO_IO_ASYNC */ + + else if ( method & LIO_IO_SLISTIO ) { +#ifdef CRAY + request.li_opcode = LO_WRITE; + request.li_fildes = fd; + request.li_buf = buffer; + request.li_nbyte = size; + request.li_status = &status; + request.li_signo = sig; + request.li_nstride = 0; + request.li_filstride = 0; + request.li_memstride = 0; + + listio_cmd=LC_WAIT; + io_type="listio(2) sync write"; + + sprintf(Lio_SysCall, + "listio(LC_WAIT, &req, 1) LO_WRITE, fd:%d, nbyte:%d", + fd, size); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + sigoff(); + if ( listio(listio_cmd, &request, 1) == -1 ) { + sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s", + __FILE__, __LINE__, + Lio_SysCall, fd, size, errno, strerror(errno)); + sigon(); + return -errno; + } + + if ( Debug_level > 1 ) + printf("DEBUG %s/%d: %s did not return -1\n", + __FILE__, __LINE__, Lio_SysCall); + + ret=lio_check_asyncio(io_type, size, &status); + return ret; + +#endif +#ifdef sgi + + aiocbp.aio_lio_opcode = LIO_WRITE; + listio_cmd=LIO_WAIT; + io_type="lio_listio(3) sync write"; + + sprintf(Lio_SysCall, + "lio_listio(LIO_WAIT, aiolist, 1, NULL) LIO_WRITE, fd:%d, nbyte:%d, sig:%d", + fd, size, sig ); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + if( sig ) + sighold( sig ); + if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) { + sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s", + __FILE__, __LINE__, + Lio_SysCall, fd, size, errno, strerror(errno)); + if( sig ) + sigrelse( sig ); + return -errno; + } + + if ( Debug_level > 1 ) + printf("DEBUG %s/%d: %s did not return -1\n", + __FILE__, __LINE__, Lio_SysCall); + + ret=lio_check_asyncio(io_type, size, &aiocbp, method); + return ret; +#endif + } /* LIO_IO_SLISTIO */ + + else if ( method & LIO_IO_ALISTIO ) { +#ifdef CRAY + request.li_opcode = LO_WRITE; + request.li_fildes = fd; + request.li_buf = buffer; + request.li_nbyte = size; + request.li_status = &status; + request.li_signo = sig; + request.li_nstride = 0; + request.li_filstride = 0; + request.li_memstride = 0; + + listio_cmd=LC_START; + io_type="listio(2) async write"; + + sprintf(Lio_SysCall, + "listio(LC_START, &req, 1) LO_WRITE, fd:%d, nbyte:%d", + fd, size); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + sigoff(); + if ( listio(listio_cmd, &request, 1) == -1 ) { + sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s", + __FILE__, __LINE__, + Lio_SysCall, fd, size, errno, strerror(errno)); + sigon(); + return -errno; + } +#endif +#ifdef sgi + aiocbp.aio_lio_opcode = LIO_WRITE; + listio_cmd=LIO_NOWAIT; + io_type="lio_listio(3) async write"; + + sprintf(Lio_SysCall, + "lio_listio(LIO_NOWAIT, aiolist, 1, NULL) LIO_WRITE, fd:%d, nbyte:%d", + fd, size); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + if( sig ) + sighold( sig ); + if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) { + sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s", + __FILE__, __LINE__, + Lio_SysCall, fd, size, errno, strerror(errno)); + if( sig ) + sigrelse( sig ); + return -errno; + } +#endif + }/* LIO_IO_ALISTIO */ + +#ifndef CRAY + else if ( method & LIO_IO_SYNCV ) { + io_type="writev(2)"; + + sprintf(Lio_SysCall, + "writev(%d, &iov, 1) nbyte:%d", fd, size); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + if ((ret = writev(fd, &iov, 1)) == -1) { + sprintf(Errormsg, "%s/%d writev(%d, iov, 1) nbyte:%d ret:-1, errno=%d %s", + __FILE__, __LINE__, + fd, size, errno, strerror(errno)); + return -errno; + } + + if ( ret != size ) { + sprintf(Errormsg, + "%s/%d writev(%d, iov, 1) nbyte:%d returned=%d", + __FILE__, __LINE__, + fd, size, ret); + } + else if ( Debug_level > 1 ) + printf("DEBUG %s/%d: writev completed without error (ret %d)\n", + __FILE__, __LINE__, ret); + + return ret; + } /* LIO_IO_SYNCV */ +#endif + +#ifdef sgi + else if ( method & LIO_IO_SYNCP ) { + io_type="pwrite(2)"; + + sprintf(Lio_SysCall, + "pwrite(%d, buf, %d, %lld)", fd, size, poffset); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + if ((ret = pwrite(fd, buffer, size, poffset)) == -1) { + sprintf(Errormsg, "%s/%d pwrite(%d, buf, %d, %lld) ret:-1, errno=%d %s", + __FILE__, __LINE__, + fd, size, poffset, errno, strerror(errno)); + return -errno; + } + + if ( ret != size ) { + sprintf(Errormsg, + "%s/%d pwrite(%d, buf, %d, %lld) returned=%d", + __FILE__, __LINE__, + fd, size, poffset, ret); + } + else if ( Debug_level > 1 ) + printf("DEBUG %s/%d: pwrite completed without error (ret %d)\n", + __FILE__, __LINE__, ret); + + return ret; + } /* LIO_IO_SYNCP */ +#endif + + else { + printf("DEBUG %s/%d: No I/O method chosen\n", __FILE__, __LINE__ ); + return -1; + } + + /* + * wait for async io to complete. + */ +#ifdef CRAY + ret=lio_wait4asyncio(method, fd, statptr); +#endif +#ifdef sgi + ret=lio_wait4asyncio(method, fd, &aiocbp); +#endif + + /* + * If there was an error waiting for async i/o to complete, + * return the error value (errno) to the caller. + * Note: Errormsg should already have been updated. + */ + if ( ret < 0 ) { + return ret; + } + + /* + * If i/o was not waited for (may not have been completed at this time), + * return the size that was requested. + */ + if ( ret == 1 ) + return size; + + /* + * check that async io was successful. + * Note: if the there was an system call failure, -errno + * was returned and Errormsg should already have been updated. + * If amount i/o was different than size, Errormsg should already + * have been updated but the actual i/o size if returned. + */ + +#ifdef CRAY + ret=lio_check_asyncio(io_type, size, &status); +#endif +#ifdef sgi + ret=lio_check_asyncio(io_type, size, &aiocbp, method); +#endif + + return ret; +} /* end of lio_write_buffer */ + +/*********************************************************************** + * Generic read function + * This function can be used to do a read using read(2), reada(2), + * aio_read(3), readv(2), pread(2), + * or single stride listio(2)/lio_listio(3). + * By setting the desired bits in the method + * bitmask, the caller can control the type of read and the wait method + * that will be used. If no io type bits are set, read will be used. + * + * If async io was attempted and no wait method bits are set then the + * wait method is: recall(2) for reada(2) and listio(2); aio_suspend(3) for + * aio_read(3) and lio_listio(3). + * + * If multiple wait methods are specified, + * only one wait method will be used. The order is predetermined. + * + * If the call specifies a signal and one of the two signal wait methods, + * a signal handler for the signal is set. This will reset an already + * set handler for this signal. + * + * If the LIO_RANDOM method bit is set, this function will randomly + * choose a io type and wait method from bits in the method argument. + * + * If an error is encountered, an error message will be generated + * in a internal static buffer. If errmsg is not NULL, it will + * be updated to point to the static buffer, allowing the caller + * to print the error message. + * + * Return Value + * If a system call fails, -errno is returned. + * If LIO_WAIT_NONE bit is set, the return value is the return value + * of the system call. + * If the io did not fail, the amount of data written is returned. + * If the size the system call say was written is different + * then what was asked to be written, errmsg is updated for + * this error condition. The return value is still the amount + * the system call says was written. + * + * (rrl 04/96) + ***********************************************************************/ +int +lio_read_buffer(fd, method, buffer, size, sig, errmsg, wrd) +int fd; /* open file descriptor */ +int method; /* contains io type and wait method bitmask */ +char *buffer; /* pointer to buffer */ +int size; /* the size of the io */ +int sig; /* signal to use if async io */ +char **errmsg; /* char pointer that will be updated to point to err message */ +long wrd; /* to allow future features, use zero for now */ +{ + int ret = 0; /* syscall return or used to get random method */ + char *io_type; /* Holds string of type of io */ +#ifndef linux + int listio_cmd; /* Holds the listio/lio_listio cmd */ + int omethod = method; +#endif +#ifdef CRAY + struct listreq request; /* Used when a listio is wanted */ + struct iosw status, *statptr[1]; +#else + /* for linux or sgi */ + struct iovec iov; /* iovec for readv(2) */ +#endif +#ifdef sgi + aiocb_t aiocbp; /* POSIX aio control block */ + aiocb_t *aiolist[1]; /* list of aio control blocks for lio_listio */ + off64_t poffset; /* pread(2) offset */ +#endif + + /* + * If LIO_RANDOM bit specified, get new method randomly. + */ + if ( method & LIO_RANDOM ) { + if( Debug_level > 3 ) + printf("DEBUG %s/%d: method mask to choose from: %#o\n", __FILE__, __LINE__, method ); + method = lio_random_methods(method); + if ( Debug_level > 2 ) + printf("DEBUG %s/%d: random chosen method %#o\n", __FILE__, __LINE__, method); + } + + if ( errmsg != NULL ) + *errmsg = Errormsg; + + Rec_signal=Received_signal; /* get the current number of signals received */ +#ifdef sgi + Rec_callback=Received_callback; /* get the current number of callbacks received */ +#endif + +#ifdef CRAY + bzero(&status, sizeof(struct iosw)); + bzero(&request, sizeof(struct listreq)); + statptr[0] = &status; +#else + /* for linux or sgi */ + bzero(&iov, sizeof(struct iovec)); + iov.iov_base = buffer; + iov.iov_len = size; +#endif +#ifdef sgi + bzero(&aiocbp, sizeof(aiocb_t)); + aiocbp.aio_fildes = fd; + aiocbp.aio_nbytes = size; + aiocbp.aio_buf = buffer; +/* aiocbp.aio_offset = lseek( fd, 0, SEEK_CUR ); -- set below */ + aiocbp.aio_sigevent.sigev_notify = SIGEV_NONE; + aiocbp.aio_sigevent.sigev_signo = 0; + aiocbp.aio_sigevent.sigev_func = NULL; + aiocbp.aio_sigevent.sigev_value.sival_int = 0; + aiolist[0] = &aiocbp; + + if( (ret = lseek( fd, 0, SEEK_CUR )) == -1 ){ + ret = 0; + /* If there is an error and it is not ESPIPE then kick out the error. + * If the fd is a fifo then we have to make sure that + * lio_random_methods() didn't select pwrite/pread; if it did then + * switch to write/read. + */ + if( errno == ESPIPE ){ + if( method & LIO_IO_SYNCP ){ + if( omethod & LIO_RANDOM ){ + method &= ~LIO_IO_SYNCP; + method |= LIO_IO_SYNC; + if( Debug_level > 2 ) + printf("DEBUG %s/%d: random chosen method switched to %#o for fifo\n", __FILE__, __LINE__, method ); + } + else if( Debug_level ){ + printf("DEBUG %s/%d: pread will fail when it reads from a fifo\n", + __FILE__, __LINE__ ); + } + } + /* else: let it ride */ + } + else{ + sprintf(Errormsg, "%s/%d lseek(fd=%d,0,SEEK_CUR) failed, errno=%d %s", + __FILE__, __LINE__, fd, errno, strerror(errno)); + return -errno; + } + } + poffset = (off64_t)ret; + aiocbp.aio_offset = ret; + +#endif + + /* + * If the LIO_USE_SIGNAL bit is not set, only use the signal + * if the LIO_WAIT_SIGPAUSE or the LIO_WAIT_SIGACTIVE bits are set. + * Otherwise there is not necessarily a signal handler to trap + * the signal. + */ + if ( sig && !(method & LIO_USE_SIGNAL) && + ! (method & LIO_WAIT_SIGTYPES) ){ + + sig=0; /* ignore signal parameter */ + } + +#ifdef sgi + if ( sig && (method & LIO_WAIT_CBTYPES) ) + sig=0; /* ignore signal parameter */ +#endif + + /* + * only setup signal hander if sig was specified and + * a sig wait method was specified. + * Doing this will change the handler for this signal. The + * old signal handler will not be restored. + *** restoring the signal handler could be added *** + */ + + if ( sig && (method & LIO_WAIT_SIGTYPES) ){ +#ifdef CRAY + sigctl(SCTL_REG, sig, lio_async_signal_handler); +#endif +#ifdef sgi + aiocbp.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + aiocbp.aio_sigevent.sigev_signo = sig; + sigset(sig, lio_async_signal_handler); +#endif /* CRAY */ + } +#ifdef sgi + else if( method & LIO_WAIT_CBTYPES ){ + aiocbp.aio_sigevent.sigev_notify = SIGEV_CALLBACK; + aiocbp.aio_sigevent.sigev_func = lio_async_callback_handler; + /* sival_int just has to be something that I can use + * to identify the callback, and "size" happens to be handy... + */ + aiocbp.aio_sigevent.sigev_value.sival_int = size; + } +#endif + + /* + * Determine the system call that will be called and produce + * the string of the system call and place it in Lio_SysCall. + * Also update the io_type char pointer to give brief description + * of system call. Execute the system call and check for + * system call failure. If sync i/o, return the number of + * bytes written/read. + */ + + if ( (method & LIO_IO_SYNC) || (method & LIO_IO_TYPES) == 0 ){ + /* + * read(2) is used if LIO_IO_SYNC bit is set or not none + * of the LIO_IO_TYPES bits are set (default). + */ + + sprintf(Lio_SysCall, + "read(%d, buf, %d)", fd, size); + io_type="read"; + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + if ((ret = read(fd, buffer, size)) == -1) { + sprintf(Errormsg, "%s/%d read(%d, buf, %d) ret:-1, errno=%d %s", + __FILE__, __LINE__, + fd, size, errno, strerror(errno)); + return -errno; + } + + if ( ret != size ) { + sprintf(Errormsg, + "%s/%d read(%d, buf, %d) returned=%d", + __FILE__, __LINE__, + fd, size, ret); + } + else if ( Debug_level > 1 ) + printf("DEBUG %s/%d: read completed without error (ret %d)\n", + __FILE__, __LINE__, ret); + + return ret; + + } + + else if ( method & LIO_IO_ASYNC ) { +#ifdef CRAY + sprintf(Lio_SysCall, + "reada(%d, buf, %d, &status, %d)", fd, size, sig); + io_type="reada"; + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + sigoff(); + if ((ret = reada(fd, buffer, size, &status, sig)) == -1) { + sprintf(Errormsg, + "%s/%d reada(%d, buf, %d, &stat, %d) ret:-1, errno=%d %s", + __FILE__, __LINE__, + fd, size, sig, errno, strerror(errno)); + sigon(); + return -errno; + } +#endif +#ifdef sgi + sprintf(Lio_SysCall, + "aio_read(fildes=%d, buf, nbytes=%d, signo=%d)", fd, size, sig); + io_type="aio_read"; + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + if( sig ) + sighold( sig ); + if ((ret = aio_read(&aiocbp)) == -1) { + sprintf(Errormsg, + "%s/%d aio_read(fildes=%d, buf, nbytes=%d, signo=%d) ret:-1, errno=%d %s", + __FILE__, __LINE__, + fd, size, sig, errno, strerror(errno)); + if( sig ) + sigrelse( sig ); + return -errno; + } +#endif + } /* LIO_IO_ASYNC */ + + else if ( method & LIO_IO_SLISTIO ) { +#ifdef CRAY + request.li_opcode = LO_READ; + request.li_fildes = fd; + request.li_buf = buffer; + request.li_nbyte = size; + request.li_status = &status; + request.li_signo = sig; + request.li_nstride = 0; + request.li_filstride = 0; + request.li_memstride = 0; + + listio_cmd=LC_WAIT; + io_type="listio(2) sync read"; + + sprintf(Lio_SysCall, + "listio(LC_WAIT, &req, 1) LO_READ, fd:%d, nbyte:%d", + fd, size); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + sigoff(); + if ( listio(listio_cmd, &request, 1) == -1 ) { + sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s", + __FILE__, __LINE__, + Lio_SysCall, fd, size, errno, strerror(errno)); + sigon(); + return -errno; + } + + if ( Debug_level > 1 ) + printf("DEBUG %s/%d: %s did not return -1\n", + __FILE__, __LINE__, Lio_SysCall); + + ret=lio_check_asyncio(io_type, size, &status); + return ret; +#endif +#ifdef sgi + aiocbp.aio_lio_opcode = LIO_READ; + listio_cmd=LIO_WAIT; + io_type="lio_listio(3) sync read"; + + sprintf(Lio_SysCall, + "lio_listio(LIO_WAIT, aiolist, 1, NULL) LIO_READ, fd:%d, nbyte:%d", + fd, size); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + if( sig ) + sighold( sig ); + if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) { + sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s", + __FILE__, __LINE__, + Lio_SysCall, fd, size, errno, strerror(errno)); + if( sig ) + sigrelse( sig ); + return -errno; + } + + if ( Debug_level > 1 ) + printf("DEBUG %s/%d: %s did not return -1\n", + __FILE__, __LINE__, Lio_SysCall); + + ret=lio_check_asyncio(io_type, size, &aiocbp, method); + return ret; +#endif + }/* LIO_IO_SLISTIO */ + + else if ( method & LIO_IO_ALISTIO ) { +#ifdef CRAY + request.li_opcode = LO_READ; + request.li_fildes = fd; + request.li_buf = buffer; + request.li_nbyte = size; + request.li_status = &status; + request.li_signo = sig; + request.li_nstride = 0; + request.li_filstride = 0; + request.li_memstride = 0; + + listio_cmd=LC_START; + io_type="listio(2) async read"; + + sprintf(Lio_SysCall, + "listio(LC_START, &req, 1) LO_READ, fd:%d, nbyte:%d", + fd, size); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + sigoff(); + if ( listio(listio_cmd, &request, 1) == -1 ) { + sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s", + __FILE__, __LINE__, + Lio_SysCall, fd, size, errno, strerror(errno)); + sigon(); + return -errno; + } +#endif +#ifdef sgi + aiocbp.aio_lio_opcode = LIO_READ; + listio_cmd=LIO_NOWAIT; + io_type="lio_listio(3) async read"; + + sprintf(Lio_SysCall, + "lio_listio(LIO_NOWAIT, aiolist, 1, NULL) LIO_READ, fd:%d, nbyte:%d", + fd, size); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + + if( sig ) + sighold( sig ); + if ( lio_listio(listio_cmd, aiolist, 1, NULL) == -1 ) { + sprintf(Errormsg, "%s/%d %s failed, fd:%d, nbyte:%d errno=%d %s", + __FILE__, __LINE__, + Lio_SysCall, fd, size, errno, strerror(errno)); + if( sig ) + sigrelse( sig ); + return -errno; + } +#endif + } /* LIO_IO_ALISTIO */ + +#ifndef CRAY + else if ( method & LIO_IO_SYNCV ) { + io_type="readv(2)"; + + sprintf(Lio_SysCall, + "readv(%d, &iov, 1) nbyte:%d", fd, size); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + if ((ret = readv(fd, &iov, 1)) == -1) { + sprintf(Errormsg, "%s/%d readv(%d, iov, 1) nbyte:%d ret:-1, errno=%d %s", + __FILE__, __LINE__, + fd, size, errno, strerror(errno)); + return -errno; + } + + if ( ret != size ) { + sprintf(Errormsg, + "%s/%d readv(%d, iov, 1) nbyte:%d returned=%d", + __FILE__, __LINE__, + fd, size, ret); + } + else if ( Debug_level > 1 ) + printf("DEBUG %s/%d: readv completed without error (ret %d)\n", + __FILE__, __LINE__, ret); + + return ret; + } /* LIO_IO_SYNCV */ +#endif + +#ifdef sgi + else if ( method & LIO_IO_SYNCP ) { + io_type="pread(2)"; + + sprintf(Lio_SysCall, + "pread(%d, buf, %d, %lld)", fd, size, poffset); + + if ( Debug_level ) { + printf("DEBUG %s/%d: %s\n", __FILE__, __LINE__, Lio_SysCall); + } + if ((ret = pread(fd, buffer, size, poffset)) == -1) { + sprintf(Errormsg, "%s/%d pread(%d, buf, %d, %lld) ret:-1, errno=%d %s", + __FILE__, __LINE__, + fd, size, poffset, errno, strerror(errno)); + return -errno; + } + + if ( ret != size ) { + sprintf(Errormsg, + "%s/%d pread(%d, buf, %d, %lld) returned=%d", + __FILE__, __LINE__, + fd, size, poffset, ret); + } + else if ( Debug_level > 1 ) + printf("DEBUG %s/%d: pread completed without error (ret %d)\n", + __FILE__, __LINE__, ret); + + return ret; + } /* LIO_IO_SYNCP */ +#endif + + else { + printf("DEBUG %s/%d: No I/O method chosen\n", __FILE__, __LINE__ ); + return -1; + } + + /* + * wait for async io to complete. + * Note: Sync io should have returned prior to getting here. + */ +#ifdef CRAY + ret=lio_wait4asyncio(method, fd, statptr); +#endif +#ifdef sgi + ret=lio_wait4asyncio(method, fd, &aiocbp); +#endif + + /* + * If there was an error waiting for async i/o to complete, + * return the error value (errno) to the caller. + * Note: Errormsg should already have been updated. + */ + if ( ret < 0 ) { + return ret; + } + + /* + * If i/o was not waited for (may not have been completed at this time), + * return the size that was requested. + */ + if ( ret == 1 ) + return size; + + /* + * check that async io was successful. + * Note: if the there was an system call failure, -errno + * was returned and Errormsg should already have been updated. + * If amount i/o was different than size, Errormsg should already + * have been updated but the actual i/o size if returned. + */ + +#ifdef CRAY + ret=lio_check_asyncio(io_type, size, &status); +#endif +#ifdef sgi + ret=lio_check_asyncio(io_type, size, &aiocbp, method); +#endif + + return ret; +} /* end of lio_read_buffer */ + + +#ifndef linux +/*********************************************************************** + * This function will check that async io was successful. + * It can also be used to check sync listio since it uses the + * same method. + * + * Return Values + * If status.sw_error is set, -status.sw_error is returned. + * Otherwise sw_count's field value is returned. + * + * (rrl 04/96) + ***********************************************************************/ +int +#ifdef CRAY +lio_check_asyncio(char *io_type, int size, struct iosw *status) +#else +lio_check_asyncio(char *io_type, int size, aiocb_t *aiocbp, int method) +#endif +{ + int ret; + +#ifdef CRAY + if ( status->sw_error ) { + sprintf(Errormsg, + "%s/%d %s, sw_error set = %d %s, sw_count = %d", + __FILE__, __LINE__, io_type, + status->sw_error, strerror(status->sw_error), status->sw_count); + return -status->sw_error; + } + else if ( status->sw_count != size ) { + sprintf(Errormsg, + "%s/%d %s, sw_count not as expected(%d), but actual:%d", + __FILE__, __LINE__, io_type, + size, status->sw_count); + } + else if ( Debug_level > 1 ) { + printf("DEBUG %s/%d: %s completed without error (sw_error == 0, sw_count == %d)\n", + __FILE__, __LINE__, io_type, status->sw_count); + } + + return status->sw_count; + +#else + + int cnt = 1; + + /* The I/O may have been synchronous with signal completion. It doesn't + * make sense, but the combination could be generated. Release the + * completion signal here otherwise it'll hang around and bite us + * later. + */ + if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ) + sigrelse( aiocbp->aio_sigevent.sigev_signo ); + + ret = aio_error( aiocbp ); + + while( ret == EINPROGRESS ){ + ret = aio_error( aiocbp ); + ++cnt; + } + if( cnt > 1 ){ + sprintf(Errormsg, + "%s/%d %s, aio_error had to loop on EINPROGRESS, cnt=%d; random method %#o; sigev_notify=%s", + __FILE__, __LINE__, io_type, cnt, method, + (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ? "signal" : + aiocbp->aio_sigevent.sigev_notify == SIGEV_NONE ? "none" : + aiocbp->aio_sigevent.sigev_notify == SIGEV_CALLBACK ? "callback" : + "unknown") ); + return -ret; + } + + if( ret != 0 ){ + sprintf(Errormsg, + "%s/%d %s, aio_error = %d %s; random method %#o", + __FILE__, __LINE__, io_type, + ret, strerror(ret), + method ); + return -ret; + } + ret = aio_return( aiocbp ); + if( ret != size ){ + sprintf(Errormsg, + "%s/%d %s, aio_return not as expected(%d), but actual:%d", + __FILE__, __LINE__, io_type, + size, ret); + +#ifdef BUG1_workaround + if( ret == 0 ){ + ret = size; + if( Debug_level > 1 ){ + printf("WARN %s/%d: %s completed with bug1_workaround (aio_error == 0, aio_return now == %d)\n", + __FILE__, __LINE__, io_type, ret); + } + } +#endif /* BUG1_workaround */ + + } + else if( Debug_level > 1 ){ + printf("DEBUG %s/%d: %s completed without error (aio_error == 0, aio_return == %d)\n", + __FILE__, __LINE__, io_type, ret); + } + + return ret; + +#endif + +} /* end of lio_check_asyncio */ + + +/*********************************************************************** + * + * This function will wait for async io to complete. + * If multiple wait methods are specified, the order is predetermined + * to LIO_WAIT_RECALL, + * LIO_WAIT_ACTIVE, LIO_WAIT_SIGPAUSE, LIO_WAIT_SIGACTIVE, + * then LIO_WAIT_NONE. + * + * If no wait method was specified the default wait method is: recall(2) + * or aio_suspend(3), as appropriate. + * + * Return Values + * <0: errno of failed recall + * 0 : async io was completed + * 1 : async was not waited for, io may not have completed. + * + * (rrl 04/96) + ***********************************************************************/ +int +#ifdef CRAY +lio_wait4asyncio(int method, int fd, struct iosw **statptr) +#else +lio_wait4asyncio(int method, int fd, aiocb_t *aiocbp) +#endif +{ + int cnt; +#ifdef sgi + int ret; + const aiocb_t *aioary[1]; +#endif + + if ( (method & LIO_WAIT_RECALL) +#ifdef sgi + || (method & LIO_WAIT_CBSUSPEND) + || (method & LIO_WAIT_SIGSUSPEND) +#endif + || ((method & LIO_WAIT_TYPES) == 0) ){ + /* + * If method has LIO_WAIT_RECALL bit set or method does + * not have any wait method bits set (default), use recall/aio_suspend. + */ +#ifdef CRAY + if ( Debug_level > 2 ) + printf("DEBUG %s/%d: wait method : recall\n", __FILE__, __LINE__); + sigon(); + if ( recall(fd, 1, statptr) ) { + sprintf(Errormsg, "%s/%d recall(%d, 1, stat) failed, errno:%d %s", + __FILE__, __LINE__, + fd, errno, strerror(errno)); + return -errno; + } +#else + if ( Debug_level > 2 ) + printf("DEBUG %s/%d: wait method : aio_suspend, sigev_notify=%s\n", __FILE__, __LINE__, + (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ? "signal" : + aiocbp->aio_sigevent.sigev_notify == SIGEV_NONE ? "none" : + aiocbp->aio_sigevent.sigev_notify == SIGEV_CALLBACK ? "callback" : + "unknown") ); + + aioary[0] = aiocbp; + ret = aio_suspend( aioary, 1, NULL ); + if( (ret == -1) && (errno == EINTR) ){ + if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ){ + if( Debug_level > 2 ){ + printf("DEBUG %s/%d: aio_suspend received EINTR, sigev_notify=SIGEV_SIGNAL -- ok\n", + __FILE__, __LINE__ ); + } + } + else { + sprintf(Errormsg, "%s/%d aio_suspend received EINTR, sigev_notify=%s, not ok\n", + __FILE__, __LINE__, + (aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ? "signal" : + aiocbp->aio_sigevent.sigev_notify == SIGEV_NONE ? "none" : + aiocbp->aio_sigevent.sigev_notify == SIGEV_CALLBACK ? "callback" : + "unknown") ); + return -errno; + } + } + else if ( ret ) { + sprintf(Errormsg, "%s/%d aio_suspend(fildes=%d, aioary, 1, NULL) failed, errno:%d %s", + __FILE__, __LINE__, + fd, errno, strerror(errno)); + return -errno; + } +#endif + + } else if ( method & LIO_WAIT_ACTIVE ) { + if ( Debug_level > 2 ) + printf("DEBUG %s/%d: wait method : active\n", __FILE__, __LINE__); +#ifdef CRAY + sigon(); + /* + * loop until sw_flag, sw_count or sw_error field elements + * change to non-zero. + */ + cnt=0; + while ( (*statptr)->sw_flag == 0 && + (*statptr)->sw_count == 0 && + (*statptr)->sw_error == 0 ) { + cnt++; + } +#else + /* loop while aio_error() returns EINPROGRESS */ + cnt=0; + while(1){ + ret = aio_error( aiocbp ); + if( (ret == 0) || (ret != EINPROGRESS) ){ + break; + } + ++cnt; + } + +#endif + if ( Debug_level > 5 && cnt && (cnt % 50) == 0 ) + printf("DEBUG %s/%d: wait active cnt = %d\n", + __FILE__, __LINE__, cnt); + + } else if ( method & LIO_WAIT_SIGPAUSE ) { + if ( Debug_level > 2 ) + printf("DEBUG %s/%d: wait method : sigpause\n", __FILE__, __LINE__); +#ifdef sgi + /* note: don't do the sigon() for CRAY in this case. why? -- roehrich 6/11/97 */ + if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ) + sigrelse( aiocbp->aio_sigevent.sigev_signo ); + else { + printf("DEBUG %s/%d: sigev_notify != SIGEV_SIGNAL\n", __FILE__, __LINE__ ); + return -1; + } +#endif + pause(); + + } else if ( method & LIO_WAIT_SIGACTIVE ) { + if ( Debug_level > 2 ) + printf("DEBUG %s/%d: wait method : sigactive\n", __FILE__, __LINE__); +#ifdef CRAY + sigon(); +#else + if( aiocbp->aio_sigevent.sigev_notify == SIGEV_SIGNAL ) + sigrelse( aiocbp->aio_sigevent.sigev_signo ); + else { + printf("DEBUG %s/%d: sigev_notify != SIGEV_SIGNAL\n", __FILE__, __LINE__ ); + return -1; + } +#endif + /* loop waiting for signal */ + while ( Received_signal == Rec_signal ){ +#ifdef CRAY + sigon(); +#else + sigrelse( aiocbp->aio_sigevent.sigev_signo ); +#endif + } + + } else if ( method & LIO_WAIT_NONE ) { + if ( Debug_level > 2 ) + printf("DEBUG %s/%d: wait method : none\n", __FILE__, __LINE__); + /* It's broken because the aiocb/iosw is an automatic variable in + * lio_{read,write}_buffer, so when the function returns and the + * I/O completes there will be nowhere to write the I/O status. + * It doesn't cause a problem on unicos--probably because of some + * compiler quirk, or an accident. It causes POSIX async I/O + * to core dump some threads. spr/pv 705909. 6/27/97 roehrich + */ + sprintf(Errormsg, "%s/%d LIO_WAIT_NONE was selected (this is broken)\n", + __FILE__, __LINE__ ); +#ifdef CRAY + sigon(); +#endif +/* return 1;*/ + return -1; + } + else { + if( Debug_level > 2 ) + printf("DEBUG %s/%d: no wait method was chosen\n", __FILE__, __LINE__ ); + return -1; + } + + return 0; + +} /* end of lio_wait4asyncio */ + +#endif /* ifndef linux */ + +#if UNIT_TEST +/*********************************************************************** + * The following code is provided as unit test. + * Just define add "-DUNIT_TEST=1" to the cc line. + * + * (rrl 04/96) + ***********************************************************************/ +struct unit_info_t { + int method; + int sig; + char *str; +} Unit_info[] = { + { LIO_IO_SYNC, 0, "sync io" }, + { LIO_IO_SYNCV, 0, "sync readv/writev" }, + { LIO_IO_SYNCP, 0, "sync pread/pwrite" }, + { LIO_IO_ASYNC, 0, "async io, def wait" }, + { LIO_IO_SLISTIO, 0, "sync listio" }, + { LIO_IO_ALISTIO, 0, "async listio, def wait" }, + { LIO_IO_ASYNC|LIO_WAIT_ACTIVE, 0, "async active" }, + { LIO_IO_ASYNC|LIO_WAIT_RECALL, 0, "async recall/suspend" }, + { LIO_IO_ASYNC|LIO_WAIT_SIGPAUSE, SIGUSR1, "async sigpause" }, + { LIO_IO_ASYNC|LIO_WAIT_SIGACTIVE, SIGUSR1, "async sigactive" }, + { LIO_IO_ALISTIO|LIO_WAIT_ACTIVE, 0, "async listio active" }, + { LIO_IO_ALISTIO|LIO_WAIT_RECALL, 0, "async listio recall" }, + { LIO_IO_ALISTIO|LIO_WAIT_SIGACTIVE, SIGUSR1, "async listio sigactive" }, + { LIO_IO_ALISTIO|LIO_WAIT_SIGPAUSE, SIGUSR1, "async listio sigpause" }, + { LIO_IO_ASYNC, SIGUSR2, "async io, def wait, sigusr2" }, + { LIO_IO_ALISTIO, SIGUSR2, "async listio, def wait, sigusr2" }, +}; + +int +main(argc, argv) +int argc; +char **argv; +{ + extern char *optarg; + extern int optind; + + int fd; + char *err; + char buffer[4096]; + int size=4096; + int ret; + int ind; + int iter=3; + int method; + int exit_status = 0; + int c; + int i; + char *symbols = NULL; + int die_on_err = 0; + + while( (c = getopt(argc,argv,"s:di:")) != -1 ){ + switch(c){ + case 's': symbols = optarg; break; + case 'd': ++die_on_err; break; + case 'i': iter = atoi(optarg); break; + } + } + + if ((fd=open("unit_test_file", O_CREAT|O_RDWR|O_TRUNC, 0777)) == -1 ) { + perror("open(unit_test_file, O_CREAT|O_RDWR|O_TRUNC, 0777) failed"); + exit(1); + } + + Debug_level=9; + + if ( symbols != NULL ) { + if ( (method=lio_parse_io_arg2(symbols, &err)) == -1 ){ + printf("lio_parse_io_arg2(%s, &err) failed, bad token starting at %s\n", + symbols, err); + if( die_on_err ) + exit(1); + } + else + printf("lio_parse_io_arg2(%s, &err) returned %#o\n", symbols, method); + + exit_status = 0; + for(ind=0; ind < iter; ind++ ) { + memset( buffer, 'A', 4096 ); + if( lseek(fd, 0, 0) == -1 ){ + printf("lseek(fd,0,0), %d, failed, errno %d\n", + __LINE__, errno ); + ++exit_status; + } + if ((ret=lio_write_buffer(fd, method, buffer, + size, SIGUSR1, &err, 0)) != size ) { + printf("lio_write_buffer returned -1, err = %s\n", err); + } else + printf("lio_write_buffer returned %d\n", ret); + + memset( buffer, 'B', 4096 ); + if( lseek(fd, 0, 0) == -1 ){ + printf("lseek(fd,0,0), %d, failed, errno %d\n", + __LINE__, errno ); + ++exit_status; + } + if ((ret=lio_read_buffer(fd, method, buffer, + size, SIGUSR2, &err, 0)) != size ) { + printf("lio_read_buffer returned -1, err = %s\n", err); + } else + printf("lio_read_buffer returned %d\n", ret); + + for( i = 0; i < 4096; ++i ){ + if( buffer[i] != 'A' ){ + printf(" buffer[%d] = %d\n", i, buffer[i] ); + ++exit_status; + break; + } + } + + if( exit_status ) + exit(exit_status); + + } + + unlink("unit_test_file"); + exit(0); + } + + for(ind=0; ind < sizeof(Unit_info)/sizeof(struct unit_info_t); ind++ ) { + + printf("\n********* write %s ***************\n", Unit_info[ind].str); + if( lseek(fd, 0, 0) == -1 ){ + printf("lseek(fd,0,0), %d, failed, errno %d\n", + __LINE__, errno ); + ++exit_status; + } + + memset( buffer, 'A', 4096 ); + if ((ret=lio_write_buffer(fd, Unit_info[ind].method, buffer, + size, Unit_info[ind].sig, &err, 0)) != size ) { + printf(">>>>> lio_write_buffer(fd,0%x,buffer,%d,%d,err,0) returned -1,\n err = %s\n", + Unit_info[ind].method, size, Unit_info[ind].sig, err); + ++exit_status; + if( die_on_err ) + exit(exit_status); + } else{ + printf("lio_write_buffer returned %d\n", ret); + } + + printf("\n********* read %s ***************\n", Unit_info[ind].str); + if( lseek(fd, 0, 0) == -1 ){ + printf("lseek(fd,0,0), %d, failed, errno %d\n", + __LINE__, errno ); + ++exit_status; + } + memset( buffer, 'B', 4096 ); + if ((ret=lio_read_buffer(fd, Unit_info[ind].method, buffer, + size, Unit_info[ind].sig, &err, 0)) != size ) { + printf(">>>>> lio_read_buffer(fd,0%x,buffer,%d,%d,err,0) returned -1,\n err = %s\n", + Unit_info[ind].method, size, Unit_info[ind].sig, err); + ++exit_status; + if( die_on_err ) + exit(exit_status); + } else { + printf("lio_read_buffer returned %d\n", ret); + } + + for( i = 0; i < 4096; ++i ){ + if( buffer[i] != 'A' ){ + printf(" buffer[%d] = %d\n", i, buffer[i] ); + ++exit_status; + if( die_on_err ) + exit(exit_status); + break; + } + } + + fflush(stdout); + fflush(stderr); + sleep(1); + + } + + unlink("unit_test_file"); + + exit(exit_status); +} +#endif diff --git a/lib/write_log.c b/lib/write_log.c new file mode 100644 index 00000000..851ab65a --- /dev/null +++ b/lib/write_log.c @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +/* + * This module contains code for logging writes to files, and for + * perusing the resultant logfile. The main intent of all this is + * to provide a 'write history' of a file which can be examined to + * judge the state of a file (ie. whether it is corrupted or not) based + * on the write activity. + * + * The main abstractions available to the user are the wlog_file, and + * the wlog_rec. A wlog_file is a handle encapsulating a write logfile. + * It is initialized with the wlog_open() function. This handle is + * then passed to the various wlog_xxx() functions to provide transparent + * access to the write logfile. + * + * The wlog_rec datatype is a structure which contains all the information + * about a file write. Examples include the file name, offset, length, + * pattern, etc. In addition there is a bit which is cleared/set based + * on whether or not the write has been confirmed as complete. This + * allows the write logfile to contain information on writes which have + * been initiated, but not yet completed (as in async io). + * + * There is also a function to scan a write logfile in reverse order. + * + * NOTE: For target file analysis based on a write logfile, the + * assumption is made that the file being written to is + * locked from simultaneous access, so that the order of + * write completion is predictable. This is an issue when + * more than 1 process is trying to write data to the same + * target file simultaneously. + * + * The history file created is a collection of variable length records + * described by scruct wlog_rec_disk in write_log.h. See that module for + * the layout of the data on disk. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "write_log.h" + +#ifndef BSIZE +#ifdef linux +#define BSIZE DEV_BSIZE +#else +#define BSIZE BBSIZE +#endif +#endif + +#ifndef PATH_MAX +#define PATH_MAX 255 +/*#define PATH_MAX pathconf("/", _PC_PATH_MAX)*/ +#endif + +char Wlog_Error_String[256]; + +#if __STDC__ +static int wlog_rec_pack(struct wlog_rec *wrec, char *buf, int flag); +static int wlog_rec_unpack(struct wlog_rec *wrec, char *buf); +#else +static int wlog_rec_pack(); +static int wlog_rec_unpack(); +#endif + +/* + * Initialize a write logfile. wfile is a wlog_file structure that has + * the w_file field filled in. The rest of the information in the + * structure is initialized by the routine. + * + * The trunc flag is used to indicate whether or not the logfile should + * be truncated if it currently exists. If it is non-zero, the file will + * be truncated, otherwise it will be appended to. + * + * The mode argument is the [absolute] mode which the file will be + * given if it does not exist. This mode is not affected by your process + * umask. + */ + +int +wlog_open(wfile, trunc, mode) +struct wlog_file *wfile; +int trunc; +int mode; +{ + int omask, oflags; + + if (trunc) + trunc = O_TRUNC; + + omask = umask(0); + + /* + * Open 1 file descriptor as O_APPEND + */ + + oflags = O_WRONLY | O_APPEND | O_CREAT | trunc; + wfile->w_afd = + open(wfile->w_file, oflags, mode); + umask(omask); + + if (wfile->w_afd == -1) { + sprintf(Wlog_Error_String, + "Could not open write_log - open(%s, %#o, %#o) failed: %s\n", + wfile->w_file, oflags, mode, strerror(errno)); + return -1; + } + + /* + * Open the next fd as a random access descriptor + */ + + oflags = O_RDWR; + if ((wfile->w_rfd = open(wfile->w_file, oflags)) == -1) { + sprintf(Wlog_Error_String, + "Could not open write log - open(%s, %#o) failed: %s\n", + wfile->w_file, oflags, strerror(errno)); + close(wfile->w_afd); + wfile->w_afd = -1; + return -1; + } + + return 0; +} + +/* + * Release all resources associated with a wlog_file structure allocated + * with the wlog_open() call. + */ + +int +wlog_close(wfile) +struct wlog_file *wfile; +{ + close(wfile->w_afd); + close(wfile->w_rfd); + return 0; +} + +/* + * Write a wlog_rec structure to a write logfile. Offset is used to + * control where the record will be written. If offset is < 0, the + * record will be appended to the end of the logfile. Otherwise, the + * record which exists at the indicated offset will be overlayed. This + * is so that we can record writes which are outstanding (with the w_done + * bit in wrec cleared), but not completed, and then later update the + * logfile when the write request completes (as with async io). When + * offset is >= 0, only the fixed length portion of the record is + * rewritten. See text in write_log.h for details on the format of an + * on-disk record. + * + * The return value of the function is the byte offset in the logfile + * where the record begins. + * + * Note: It is the callers responsibility to make sure that the offset + * parameter 'points' to a valid record location when a record is to be + * overlayed. This is guarenteed by saving the return value of a previous + * call to wlog_record_write() which wrote the record to be overlayed. + * + * Note2: The on-disk version of the wlog_rec is MUCH different than + * the user version. Don't expect to od the logfile and see data formatted + * as it is in the wlog_rec structure. Considerable data packing takes + * place before the record is written. + */ + +int +wlog_record_write(wfile, wrec, offset) +struct wlog_file *wfile; +struct wlog_rec *wrec; +long offset; +{ + int reclen; + char wbuf[WLOG_REC_MAX_SIZE + 2]; + + /* + * If offset is -1, we append the record at the end of file + * + * Otherwise, we overlay wrec at the file offset indicated and assume + * that the caller passed us the correct offset. We do not record the + * fname in this case. + */ + + reclen = wlog_rec_pack(wrec, wbuf, (offset < 0)); + + if (offset < 0) { + /* + * Since we're writing a complete new record, we must also tack + * its length onto the end so that wlog_scan_backward() will work. + * Length is asumed to fit into 2 bytes. + */ + + wbuf[reclen] = reclen / 256; + wbuf[reclen+1] = reclen % 256; + reclen += 2; + + write(wfile->w_afd, wbuf, reclen); + offset = lseek(wfile->w_afd, 0, SEEK_CUR) - reclen; + } else { + lseek(wfile->w_rfd, offset, SEEK_SET); + write(wfile->w_rfd, wbuf, reclen); + } + + return offset; +} + +/* + * Function to scan a logfile in reverse order. Wfile is a valid + * wlog_file structure initialized by wlog_open(). nrecs is the number + * of records to scan (all records are scanned if nrecs is 0). func is + * a user-supplied function to call for each record found. The function + * will be passed a single parameter - a wlog_rec structure . + */ + +int +wlog_scan_backward(wfile, nrecs, func, data) +struct wlog_file *wfile; +int nrecs; +int (*func)(); +long data; +{ + int fd, leftover, nbytes, offset, recnum, reclen, rval; + char buf[BSIZE*32], *bufend, *cp, *bufstart; + char albuf[WLOG_REC_MAX_SIZE]; + struct wlog_rec wrec; + + fd = wfile->w_rfd; + + /* + * Move to EOF. offset will always hold the current file offset + */ + + lseek(fd, 0, SEEK_END); + offset = lseek(fd, 0, SEEK_CUR); + + bufend = buf + sizeof(buf); + bufstart = buf; + + recnum = 0; + leftover = 0; + while ((!nrecs || recnum < nrecs) && offset > 0) { + /* + * Check for beginning of file - if there aren't enough bytes + * remaining to fill buf, adjust bufstart. + */ + + if (offset + leftover < sizeof(buf)) { + bufstart = bufend - (offset + leftover); + offset = 0; + } else { + offset -= sizeof(buf) - leftover; + } + + /* + * Move to the proper file offset, and read into buf + */ + + lseek(fd, offset, SEEK_SET); + nbytes = read(fd, bufstart, bufend - bufstart - leftover); + + if (nbytes == -1) { + sprintf(Wlog_Error_String, + "Could not read history file at offset %d - read(%d, %p, %d) failed: %s\n", + offset, fd, bufstart, + (int)(bufend - bufstart - leftover), strerror(errno)); + return -1; + } + + cp = bufend; + leftover = 0; + + while (cp >= bufstart) { + + /* + * If cp-bufstart is not large enough to hold a piece + * of record length information, copy remainder to end + * of buf and continue reading the file. + */ + + if (cp - bufstart < 2) { + leftover = cp - bufstart; + memcpy(bufend - leftover, bufstart, leftover); + break; + } + + /* + * Extract the record length. We must do it this way + * instead of casting cp to an int because cp might + * not be word aligned. + */ + + reclen = (*(cp-2) * 256) + *(cp -1); + + /* + * If cp-bufstart isn't large enough to hold a + * complete record, plus the length information, copy + * the leftover bytes to the end of buf and continue + * reading. + */ + + if (cp - bufstart < reclen + 2) { + leftover = cp - bufstart; + memcpy(bufend - leftover, bufstart, leftover); + break; + } + + /* + * Adjust cp to point at the start of the record. + * Copy the record into wbuf so that it is word + * aligned and pass the record to the user supplied + * function. + */ + + cp -= reclen + 2; + memcpy(albuf, cp, reclen); + + wlog_rec_unpack(&wrec, albuf); + + /* + * Call the user supplied function - + * stop if instructed to. + */ + + if ((rval = (*func)(&wrec, data)) == WLOG_STOP_SCAN) { + break; + } + + recnum++; + + if (nrecs && recnum >= nrecs) + break; + } + } + + return 0; +} + +/* + * The following 2 routines are used to pack and unpack the user + * visible wlog_rec structure to/from a character buffer which is + * stored or read from the write logfile. Any changes to either of + * these routines must be reflected in the other. + */ + +static int +wlog_rec_pack(wrec, buf, flag) +struct wlog_rec *wrec; +char *buf; +int flag; +{ + char *file, *host, *pattern; + struct wlog_rec_disk *wrecd; + + wrecd = (struct wlog_rec_disk *)buf; + + wrecd->w_pid = (uint)wrec->w_pid; + wrecd->w_offset = (uint)wrec->w_offset; + wrecd->w_nbytes = (uint)wrec->w_nbytes; + wrecd->w_oflags = (uint)wrec->w_oflags; + wrecd->w_done = (uint)wrec->w_done; + wrecd->w_async = (uint)wrec->w_async; + + wrecd->w_pathlen = (wrec->w_pathlen > 0) ? (uint)wrec->w_pathlen : 0; + wrecd->w_hostlen = (wrec->w_hostlen > 0) ? (uint)wrec->w_hostlen : 0; + wrecd->w_patternlen = (wrec->w_patternlen > 0) ? (uint)wrec->w_patternlen : 0; + + /* + * If flag is true, we should also pack the variable length parts + * of the wlog_rec. By default, we only pack the fixed length + * parts. + */ + + if (flag) { + file = buf + sizeof(struct wlog_rec_disk); + host = file + wrecd->w_pathlen; + pattern = host + wrecd->w_hostlen; + + if (wrecd->w_pathlen > 0) + memcpy(file, wrec->w_path, wrecd->w_pathlen); + + if (wrecd->w_hostlen > 0) + memcpy(host, wrec->w_host, wrecd->w_hostlen); + + if (wrecd->w_patternlen > 0) + memcpy(pattern, wrec->w_pattern, wrecd->w_patternlen); + + return (sizeof(struct wlog_rec_disk) + + wrecd->w_pathlen + wrecd->w_hostlen + wrecd->w_patternlen); + } else { + return sizeof(struct wlog_rec_disk); + } +} + +static int +wlog_rec_unpack(wrec, buf) +struct wlog_rec *wrec; +char *buf; +{ + char *file, *host, *pattern; + struct wlog_rec_disk *wrecd; + + bzero((char *)wrec, sizeof(struct wlog_rec)); + wrecd = (struct wlog_rec_disk *)buf; + + wrec->w_pid = wrecd->w_pid; + wrec->w_offset = wrecd->w_offset; + wrec->w_nbytes = wrecd->w_nbytes; + wrec->w_oflags = wrecd->w_oflags; + wrec->w_hostlen = wrecd->w_hostlen; + wrec->w_pathlen = wrecd->w_pathlen; + wrec->w_patternlen = wrecd->w_patternlen; + wrec->w_done = wrecd->w_done; + wrec->w_async = wrecd->w_async; + + if (wrec->w_pathlen > 0) { + file = buf + sizeof(struct wlog_rec_disk); + memcpy(wrec->w_path, file, wrec->w_pathlen); + } + + if (wrec->w_hostlen > 0) { + host = buf + sizeof(struct wlog_rec_disk) + wrec->w_pathlen; + memcpy(wrec->w_host, host, wrec->w_hostlen); + } + + if (wrec->w_patternlen > 0) { + pattern = buf + sizeof(struct wlog_rec_disk) + + wrec->w_pathlen + wrec->w_hostlen; + memcpy(wrec->w_pattern, pattern, wrec->w_patternlen); + } + + return 0; +} diff --git a/ltp/Makefile b/ltp/Makefile new file mode 100644 index 00000000..bc186a99 --- /dev/null +++ b/ltp/Makefile @@ -0,0 +1,60 @@ +# +# Copyright (c) 2003 Silicon Graphics, Inc. All Rights Reserved. +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of version 2 of the GNU General Public License as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it would be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# +# Further, this software is distributed without any warranty that it is +# free of the rightful claim of any third person regarding infringement +# or the like. Any license provided herein, whether implied or +# otherwise, applies only to this software file. Patent licenses, if +# any, provided herein do not apply to combinations of this program with +# other software, or any other product whatsoever. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write the Free Software Foundation, Inc., 59 +# Temple Place - Suite 330, Boston MA 02111-1307, USA. +# +# Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, +# Mountain View, CA 94043, or: +# +# http://www.sgi.com +# +# For further information regarding this notice, see: +# +# http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ +# + +TOPDIR = .. +include $(TOPDIR)/include/builddefs + +TARGETS = doio fsstress fsx growfiles iogen +CFILES = $(TARGETS:=.c) +HFILES = doio.h +LDIRT = $(TARGETS) +LCFLAGS = + +default: $(TARGETS) + +include $(BUILDRULES) +LINKTEST = $(LTLINK) $@.c -o $@ $(CFLAGS) $(LDFLAGS) + +doio: doio.c $(LIBTEST) + $(LINKTEST) $(LIBTEST) $(LDLIBS) + +fsstress: fsstress.c $(LIBATTR) $(LIBTEST) + $(LINKTEST) $(LIBATTR) $(LIBTEST) $(LDLIBS) + +fsx: fsx.c + $(LINKTEST) $(LDLIBS) + +growfiles: growfiles.c $(LIBTEST) + $(LINKTEST) $(LIBTEST) $(LDLIBS) + +iogen: iogen.c $(LIBTEST) + $(LINKTEST) $(LIBTEST) $(LDLIBS) diff --git a/ltp/doio.c b/ltp/doio.c new file mode 100644 index 00000000..731798e4 --- /dev/null +++ b/ltp/doio.c @@ -0,0 +1,5423 @@ +/* + * Copyright (c) 2000 Silicon Graphics, Inc. All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * Further, this software is distributed without any warranty that it is + * free of the rightful claim of any third person regarding infringement + * or the like. Any license provided herein, whether implied or + * otherwise, applies only to this software file. Patent licenses, if + * any, provided herein do not apply to combinations of this program with + * other software, or any other product whatsoever. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write the Free Software Foundation, Inc., 59 + * Temple Place - Suite 330, Boston MA 02111-1307, USA. + * + * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, + * Mountain View, CA 94043, or: + * + * http://www.sgi.com + * + * For further information regarding this notice, see: + * + * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ + */ +/* + * doio - a general purpose io initiator with system call and + * write logging. See doio.h for the structure which defines + * what doio requests should look like. + * + * programming + * notes: + * ----------- + * messages should generally be printed using doio_fprintf(). + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CRAY +#include +#endif +#ifdef sgi +#include /* for aio_read,write */ +#include /* for uint64_t type */ +#include /* signal handlers & SA_SIGINFO */ +#endif +#ifndef CRAY +#include /* for struct iovec (readv)*/ +#include /* for mmap(2) */ +#include /* for i/o buffer in shared memory */ +#include /* for i/o buffer in shared memory */ +#endif +#include +#ifdef CRAY +#include +#include +#endif +#include /* for delays */ + +#ifndef NO_XFS +#include +struct io_req; +int do_xfsctl(struct io_req *); +#endif + +#include "doio.h" +#include "pattern.h" +#include "write_log.h" +#include "random_range.h" +#include "string_to_tokens.h" + +#ifndef O_SSD +#define O_SSD 0 /* so code compiles on a CRAY2 */ +#endif + +#define UINT64_T unsigned long long + +#ifndef O_PARALLEL +#define O_PARALLEL 0 /* so O_PARALLEL may be used in expressions */ +#endif + +#define PPID_CHECK_INTERVAL 5 /* check ppid every <-- iterations */ +#define MAX_AIO 256 /* maximum number of async I/O ops */ +#ifdef _CRAYMPP +#define MPP_BUMP 16 /* page un-alignment for MPP */ +#else +#define MPP_BUMP 0 +#endif + + +#define SYSERR strerror(errno) + +/* + * getopt() string of supported cmdline arguments. + */ + +#define OPTS "aC:d:ehm:n:kr:w:vU:V:M:N:" + +#define DEF_RELEASE_INTERVAL 0 + +/* + * Flags set in parse_cmdline() to indicate which options were selected + * on the cmdline. + */ + +int a_opt = 0; /* abort on data compare errors */ +int e_opt = 0; /* exec() after fork()'ing */ +int C_opt = 0; /* Data Check Type */ +int d_opt = 0; /* delay between operations */ +int k_opt = 0; /* lock file regions during writes */ +int m_opt = 0; /* generate periodic messages */ +int n_opt = 0; /* nprocs */ +int r_opt = 0; /* resource release interval */ +int w_opt = 0; /* file write log file */ +int v_opt = 0; /* verify writes if set */ +int U_opt = 0; /* upanic() on varios conditions */ +int V_opt = 0; /* over-ride default validation fd type */ +int M_opt = 0; /* data buffer allocation types */ +char TagName[40]; /* name of this doio (see Monster) */ + + +/* + * Misc globals initialized in parse_cmdline() + */ + +char *Prog = NULL; /* set up in parse_cmdline() */ +int Upanic_Conditions; /* set by args to -U */ +int Release_Interval; /* arg to -r */ +int Nprocs; /* arg to -n */ +char *Write_Log; /* arg to -w */ +char *Infile; /* input file (defaults to stdin) */ +int *Children; /* pids of child procs */ +int Nchildren = 0; +int Nsiblings = 0; /* tfork'ed siblings */ +int Execd = 0; +int Message_Interval = 0; +int Npes = 0; /* non-zero if built as an mpp multi-pe app */ +int Vpe = -1; /* Virtual pe number if Npes >= 0 */ +int Reqno = 1; /* request # - used in some error messages */ +int Reqskipcnt = 0; /* count of I/O requests that are skipped */ +int Validation_Flags; +char *(*Data_Check)(); /* function to call for data checking */ +int (*Data_Fill)(); /* function to call for data filling */ +int Nmemalloc = 0; /* number of memory allocation strategies */ +int delayop = 0; /* delay between operations - type of delay */ +int delaytime = 0; /* delay between operations - how long */ + +struct wlog_file Wlog; + +int active_mmap_rw = 0; /* Indicates that mmapped I/O is occurring. */ + /* Used by sigbus_action() in the child doio. */ +int havesigint = 0; + +#define SKIP_REQ -2 /* skip I/O request */ + +#define NMEMALLOC 32 +#define MEM_DATA 1 /* data space */ +#define MEM_SHMEM 2 /* System V shared memory */ +#define MEM_T3ESHMEM 3 /* T3E Shared Memory */ +#define MEM_MMAP 4 /* mmap(2) */ + +#define MEMF_PRIVATE 0001 +#define MEMF_AUTORESRV 0002 +#define MEMF_LOCAL 0004 +#define MEMF_SHARED 0010 + +#define MEMF_FIXADDR 0100 +#define MEMF_ADDR 0200 +#define MEMF_AUTOGROW 0400 +#define MEMF_FILE 01000 /* regular file -- unlink on close */ +#define MEMF_MPIN 010000 /* use mpin(2) to lock pages in memory */ + +struct memalloc { + int memtype; + int flags; + int nblks; + char *name; + void *space; /* memory address of allocated space */ + int fd; /* FD open for mmaping */ + int size; +} Memalloc[NMEMALLOC]; + +/* + * Global file descriptors + */ + +int Wfd_Append; /* for appending to the write-log */ +int Wfd_Random; /* for overlaying write-log entries */ + +/* + * Structure for maintaining open file test descriptors. Used by + * alloc_fd(). + */ + +struct fd_cache { + char c_file[MAX_FNAME_LENGTH+1]; + int c_oflags; + int c_fd; + long c_rtc; +#ifndef NO_XFS + int c_memalign; /* from xfsctl(XFS_IOC_DIOINFO) */ + int c_miniosz; + int c_maxiosz; +#endif +#ifndef CRAY + void *c_memaddr; /* mmapped address */ + int c_memlen; /* length of above region */ +#endif +}; + +#define FD_ALLOC_INCR 32 /* allocate this many fd_map structs */ + /* at a time */ + +/* + * Globals for tracking Sds and Core usage + */ + +char *Memptr; /* ptr to core buffer space */ +int Memsize; /* # bytes pointed to by Memptr */ + /* maintained by alloc_mem() */ + +int Sdsptr; /* sds offset (always 0) */ +int Sdssize; /* # bytes of allocated sds space */ + /* Maintained by alloc_sds() */ +char Host[16]; +char Pattern[128]; +int Pattern_Length; + +/* + * Signal handlers, and related globals + */ + +void sigint_handler(); /* Catch SIGINT in parent doio, propagate + * to children, does not die. */ + +void die_handler(); /* Bad sig in child doios, exit 1. */ +void cleanup_handler(); /* Normal kill, exit 0. */ + +#ifndef CRAY +void sigbus_handler(); /* Handle sigbus--check active_mmap_rw to + decide if this should be a normal exit. */ +#endif + +void cb_handler(); /* Posix aio callback handler. */ +void noop_handler(); /* Delayop alarm, does nothing. */ +char *hms(); +char *format_rw(); +char *format_sds(); +char *format_listio(); +char *check_file(); +int doio_fprintf(FILE *stream, char *format, ...); +void doio_upanic(); +void doio(); +void help(); +void doio_delay(); +int alloc_fd( char *, int ); +int alloc_mem( int ); +int do_read( struct io_req * ); +int do_write( struct io_req * ); +int do_rw( struct io_req * ); +int do_sync( struct io_req * ); +int usage( FILE * ); +int aio_unregister( int ); +int parse_cmdline( int, char **, char * ); +int lock_file_region( char *, int, int, int, int ); +struct fd_cache *alloc_fdcache(char *, int); + +/* + * Upanic conditions, and a map from symbolics to values + */ + +#define U_CORRUPTION 0001 /* upanic on data corruption */ +#define U_IOSW 0002 /* upanic on bad iosw */ +#define U_RVAL 0004 /* upanic on bad rval */ + +#define U_ALL (U_CORRUPTION | U_IOSW | U_RVAL) + +/* + * Name-To-Value map + * Used to map cmdline arguments to values + */ +struct smap { + char *string; + int value; +}; + +struct smap Upanic_Args[] = { + { "corruption", U_CORRUPTION }, + { "iosw", U_IOSW }, + { "rval", U_RVAL }, + { "all", U_ALL }, + { NULL, 0 } +}; + +struct aio_info { + int busy; + int id; + int fd; + int strategy; + volatile int done; +#ifdef CRAY + struct iosw iosw; +#endif +#ifdef sgi + aiocb_t aiocb; + int aio_ret; /* from aio_return */ + int aio_errno; /* from aio_error */ +#endif + int sig; + int signalled; + struct sigaction osa; +}; + +struct aio_info Aio_Info[MAX_AIO]; + +struct aio_info *aio_slot(); +int aio_done( struct aio_info * ); + +/* -C data-fill/check type */ +#define C_DEFAULT 1 +struct smap checkmap[] = { + { "default", C_DEFAULT }, + { NULL, 0 }, +}; + +/* -d option delay types */ +#define DELAY_SELECT 1 +#define DELAY_SLEEP 2 +#define DELAY_SGINAP 3 +#define DELAY_ALARM 4 +#define DELAY_ITIMER 5 /* POSIX timer */ + +struct smap delaymap[] = { + { "select", DELAY_SELECT }, + { "sleep", DELAY_SLEEP }, +#ifdef sgi + { "sginap", DELAY_SGINAP }, +#endif + { "alarm", DELAY_ALARM }, + { NULL, 0 }, +}; + +/****** +* +* strerror() does similar actions. + +char * +syserrno(int err) +{ + static char sys_errno[10]; + sprintf(sys_errno, "%d", errno); + return(sys_errno); +} + +******/ + +int +main(argc, argv) +int argc; +char **argv; +{ + int i, pid, stat, ex_stat; +#ifdef CRAY + sigset_t omask; +#else + int omask; +#endif + struct sigaction sa; + + umask(0); /* force new file modes to known values */ +#if _CRAYMPP + Npes = sysconf(_SC_CRAY_NPES); /* must do this before parse_cmdline */ + Vpe = sysconf(_SC_CRAY_VPE); +#endif + + TagName[0] = '\0'; + parse_cmdline(argc, argv, OPTS); + + random_range_seed(getpid()); /* initialize random number generator */ + + /* + * If this is a re-exec of doio, jump directly into the doio function. + */ + + if (Execd) { + doio(); + exit(E_SETUP); + } + + /* + * Stop on all but a few signals... + */ + sigemptyset(&sa.sa_mask); + sa.sa_handler = sigint_handler; + sa.sa_flags = SA_RESETHAND; /* sigint is ignored after the */ + /* first time */ + for (i = 1; i <= NSIG; i++) { + switch(i) { +#ifdef SIGRECOVERY + case SIGRECOVERY: + break; +#endif +#ifdef SIGCKPT + case SIGCKPT: +#endif +#ifdef SIGRESTART + case SIGRESTART: +#endif + case SIGTSTP: + case SIGSTOP: + case SIGCONT: + case SIGCLD: + case SIGBUS: + case SIGSEGV: + case SIGQUIT: + break; + default: + sigaction(i, &sa, NULL); + } + } + + /* + * If we're logging write operations, make a dummy call to wlog_open + * to initialize the write history file. This call must be done in + * the parent, to ensure that the history file exists and/or has + * been truncated before any children attempt to open it, as the doio + * children are not allowed to truncate the file. + */ + + if (w_opt) { + strcpy(Wlog.w_file, Write_Log); + + if (wlog_open(&Wlog, 1, 0666) < 0) { + doio_fprintf(stderr, + "Could not create/truncate write log %s\n", + Write_Log); + exit(2); + } + + wlog_close(&Wlog); + } + + /* + * Malloc space for the children pid array. Initialize all entries + * to -1. + */ + + Children = (int *)malloc(sizeof(int) * Nprocs); + for (i = 0; i < Nprocs; i++) { + Children[i] = -1; + } + + omask = sigblock(sigmask(SIGCLD)); + + /* + * Fork Nprocs. This [parent] process is a watchdog, to notify the + * invoker of procs which exit abnormally, and to make sure that all + * child procs get cleaned up. If the -e option was used, we will also + * re-exec. This is mostly for unicos/mk on mpp's, to ensure that not + * all of the doio's don't end up in the same pe. + * + * Note - if Nprocs is 1, or this doio is a multi-pe app (Npes > 1), + * jump directly to doio(). multi-pe apps can't fork(), and there is + * no reason to fork() for 1 proc. + */ + + if (Nprocs == 1 || Npes > 1) { + doio(); + exit(0); + } else { + for (i = 0; i < Nprocs; i++) { + if ((pid = fork()) == -1) { + doio_fprintf(stderr, + "(parent) Could not fork %d children: %s (%d)\n", + i+1, SYSERR, errno); + exit(E_SETUP); + } + + Children[Nchildren] = pid; + Nchildren++; + + if (pid == 0) { + if (e_opt) { + char *exec_path; + + exec_path = argv[0]; + argv[0] = (char *)malloc(strlen(exec_path + 1)); + sprintf(argv[0], "-%s", exec_path); + + execvp(exec_path, argv); + doio_fprintf(stderr, + "(parent) Could not execvp %s: %s (%d)\n", + exec_path, SYSERR, errno); + exit(E_SETUP); + } else { + doio(); + exit(E_SETUP); + } + } + } + + /* + * Parent spins on wait(), until all children exit. + */ + + ex_stat = E_NORMAL; + + while (Nprocs) { + if ((pid = wait(&stat)) == -1) { + if (errno == EINTR) + continue; + } + + for (i = 0; i < Nchildren; i++) + if (Children[i] == pid) + Children[i] = -1; + + Nprocs--; + + if (WIFEXITED(stat)) { + switch (WEXITSTATUS(stat)) { + case E_NORMAL: + /* noop */ + break; + + case E_INTERNAL: + doio_fprintf(stderr, + "(parent) pid %d exited because of an internal error\n", + pid); + ex_stat |= E_INTERNAL; + break; + + case E_SETUP: + doio_fprintf(stderr, + "(parent) pid %d exited because of a setup error\n", + pid); + ex_stat |= E_SETUP; + break; + + case E_COMPARE: + doio_fprintf(stderr, + "(parent) pid %d exited because of data compare errors\n", + pid); + + ex_stat |= E_COMPARE; + + if (a_opt) + kill(0, SIGINT); + + break; + + case E_USAGE: + doio_fprintf(stderr, + "(parent) pid %d exited because of a usage error\n", + pid); + + ex_stat |= E_USAGE; + break; + + default: + doio_fprintf(stderr, + "(parent) pid %d exited with unknown status %d\n", + pid, WEXITSTATUS(stat)); + ex_stat |= E_INTERNAL; + break; + } + } else if (WIFSIGNALED(stat) && WTERMSIG(stat) != SIGINT) { + doio_fprintf(stderr, + "(parent) pid %d terminated by signal %d\n", + pid, WTERMSIG(stat)); + + ex_stat |= E_SIGNAL; + } + + fflush(NULL); + } + } + + exit(ex_stat); + +} /* main */ + +/* + * main doio function. Each doio child starts here, and never returns. + */ + +void +doio() +{ + int rval, i, infd, nbytes; + char *cp; + struct io_req ioreq; + struct sigaction sa, def_action, ignore_action, exit_action; +#ifndef CRAY + struct sigaction sigbus_action; +#endif + + Memsize = Sdssize = 0; + + /* + * Initialize the Pattern - write-type syscalls will replace Pattern[1] + * with the pattern passed in the request. Make sure that + * strlen(Pattern) is not mod 16 so that out of order words will be + * detected. + */ + + gethostname(Host, sizeof(Host)); + if ((cp = strchr(Host, '.')) != NULL) + *cp = '\0'; + + Pattern_Length = sprintf(Pattern, "-:%d:%s:%s*", getpid(), Host, Prog); + + if (!(Pattern_Length % 16)) { + Pattern_Length = sprintf(Pattern, "-:%d:%s:%s**", + getpid(), Host, Prog); + } + + /* + * Open a couple of descriptors for the write-log file. One descriptor + * is for appending, one for random access. Write logging is done for + * file corruption detection. The program doio_check is capable of + * doing corruption detection based on a doio write-log. + */ + + if (w_opt) { + + strcpy(Wlog.w_file, Write_Log); + + if (wlog_open(&Wlog, 0, 0666) == -1) { + doio_fprintf(stderr, + "Could not open write log file (%s): wlog_open() failed\n", + Write_Log); + exit(E_SETUP); + } + } + + /* + * Open the input stream - either a file or stdin + */ + + if (Infile == NULL) { + infd = 0; + } else { + if ((infd = open(Infile, O_RDWR)) == -1) { + doio_fprintf(stderr, + "Could not open input file (%s): %s (%d)\n", + Infile, SYSERR, errno); + exit(E_SETUP); + } + } + + /* + * Define a set of signals that should never be masked. Receipt of + * these signals generally indicates a programming error, and we want + * a corefile at the point of error. We put SIGQUIT in this list so + * that ^\ will force a user core dump. + * + * Note: the handler for these should be SIG_DFL, all of them + * produce a corefile as the default action. + */ + + ignore_action.sa_handler = SIG_IGN; + ignore_action.sa_flags = 0; + sigemptyset(&ignore_action.sa_mask); + + def_action.sa_handler = SIG_DFL; + def_action.sa_flags = 0; + sigemptyset(&def_action.sa_mask); + +#ifdef sgi + exit_action.sa_sigaction = cleanup_handler; + exit_action.sa_flags = SA_SIGINFO; + sigemptyset(&exit_action.sa_mask); + + sa.sa_sigaction = die_handler; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + + sigbus_action.sa_sigaction = sigbus_handler; + sigbus_action.sa_flags = SA_SIGINFO; + sigemptyset(&sigbus_action.sa_mask); +#else + exit_action.sa_handler = cleanup_handler; + exit_action.sa_flags = 0; + sigemptyset(&exit_action.sa_mask); + + sa.sa_handler = die_handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + +#ifndef CRAY + sigbus_action.sa_handler = sigbus_handler; + sigbus_action.sa_flags = 0; + sigemptyset(&sigbus_action.sa_mask); +#endif +#endif + + for (i = 1; i <= NSIG; i++) { + switch(i) { + /* Signals to terminate program on */ + case SIGINT: + sigaction(i, &exit_action, NULL); + break; + +#ifndef CRAY + /* This depends on active_mmap_rw */ + case SIGBUS: + sigaction(i, &sigbus_action, NULL); + break; +#endif + + /* Signals to Ignore... */ + case SIGSTOP: + case SIGCONT: +#ifdef SIGRECOVERY + case SIGRECOVERY: +#endif + sigaction(i, &ignore_action, NULL); + break; + + /* Signals to trap & report & die */ + /*case SIGTRAP:*/ + /*case SIGABRT:*/ +#ifdef SIGERR /* cray only signals */ + case SIGERR: + case SIGBUFIO: + case SIGINFO: +#endif + /*case SIGFPE:*/ + case SIGURG: + case SIGHUP: + case SIGTERM: + case SIGPIPE: + case SIGIO: + case SIGUSR1: + case SIGUSR2: + sigaction(i, &sa, NULL); + break; + + + /* Default Action for all other signals */ + default: + sigaction(i, &def_action, NULL); + break; + } + } + + /* + * Main loop - each doio proc does this until the read returns eof (0). + * Call the appropriate io function based on the request type. + */ + + while ((nbytes = read(infd, (char *)&ioreq, sizeof(ioreq)))) { + + /* + * Periodically check our ppid. If it is 1, the child exits to + * help clean up in the case that the main doio process was + * killed. + */ + + if (Reqno && ((Reqno % PPID_CHECK_INTERVAL) == 0)) { + if (getppid() == 1) { + doio_fprintf(stderr, + "Parent doio process has exited\n"); + alloc_mem(-1); + exit(E_SETUP); + } + } + + if (nbytes == -1) { + doio_fprintf(stderr, + "read of %d bytes from input failed: %s (%d)\n", + sizeof(ioreq), SYSERR, errno); + alloc_mem(-1); + exit(E_SETUP); + } + + if (nbytes != sizeof(ioreq)) { + doio_fprintf(stderr, + "read wrong # bytes from input stream, expected %d, got %d\n", + sizeof(ioreq), nbytes); + alloc_mem(-1); + exit(E_SETUP); + } + + if (ioreq.r_magic != DOIO_MAGIC) { + doio_fprintf(stderr, + "got a bad magic # from input stream. Expected 0%o, got 0%o\n", + DOIO_MAGIC, ioreq.r_magic); + alloc_mem(-1); + exit(E_SETUP); + } + + /* + * If we're on a Release_Interval multiple, relase all ssd and + * core space, and close all fd's in Fd_Map[]. + */ + + if (Reqno && Release_Interval && ! (Reqno%Release_Interval)) { + if (Memsize) { +#ifdef NOTDEF + sbrk(-1 * Memsize); +#else + alloc_mem(-1); +#endif + } + +#ifdef _CRAY1 + if (Sdssize) { + ssbreak(-1 * btoc(Sdssize)); + Sdsptr = 0; + Sdssize = 0; + } +#endif /* _CRAY1 */ + + alloc_fd(NULL, 0); + } + + switch (ioreq.r_type) { + case READ: + case READA: + rval = do_read(&ioreq); + break; + + case WRITE: + case WRITEA: + rval = do_write(&ioreq); + break; + + case READV: + case AREAD: + case PREAD: + case LREAD: + case LREADA: + case LSREAD: + case LSREADA: + case WRITEV: + case AWRITE: + case PWRITE: + case MMAPR: + case MMAPW: + case LWRITE: + case LWRITEA: + case LSWRITE: + case LSWRITEA: + case LEREAD: + case LEREADA: + case LEWRITE: + case LEWRITEA: + rval = do_rw(&ioreq); + break; + +#ifdef CRAY + case SSREAD: + case SSWRITE: + rval = do_ssdio(&ioreq); + break; + + case LISTIO: + rval = do_listio(&ioreq); + break; +#endif + +#ifndef NO_XFS + case RESVSP: + case UNRESVSP: + rval = do_xfsctl(&ioreq); + break; +#endif + +#ifndef CRAY + case FSYNC2: + case FDATASYNC: + rval = do_sync(&ioreq); + break; +#endif + default: + doio_fprintf(stderr, + "Don't know how to handle io request type %d\n", + ioreq.r_type); + alloc_mem(-1); + exit(E_SETUP); + } + + if (rval == SKIP_REQ){ + Reqskipcnt++; + } + else if (rval != 0) { + alloc_mem(-1); + doio_fprintf(stderr, + "doio(): operation %d returned != 0\n", + ioreq.r_type); + exit(E_SETUP); + } + + if (Message_Interval && Reqno % Message_Interval == 0) { + doio_fprintf(stderr, "Info: %d requests done (%d skipped) by this process\n", Reqno, Reqskipcnt); + } + + Reqno++; + + if(delayop != 0) + doio_delay(); + } + + /* + * Child exits normally + */ + alloc_mem(-1); + exit(E_NORMAL); + +} /* doio */ + +void +doio_delay() +{ + struct timeval tv_delay; + struct sigaction sa_al, sa_old; + sigset_t al_mask; + + switch(delayop) { + case DELAY_SELECT: + tv_delay.tv_sec = delaytime / 1000000; + tv_delay.tv_usec = delaytime % 1000000; + /*doio_fprintf(stdout, "delay_select: %d %d\n", + tv_delay.tv_sec, tv_delay.tv_usec);*/ + select(0, NULL, NULL, NULL, &tv_delay); + break; + + case DELAY_SLEEP: + sleep(delaytime); + break; + +#ifdef sgi + case DELAY_SGINAP: + sginap(delaytime); + break; +#endif + + case DELAY_ALARM: + sa_al.sa_flags = 0; + sa_al.sa_handler = noop_handler; + sigemptyset(&sa_al.sa_mask); + sigaction(SIGALRM, &sa_al, &sa_old); + sigemptyset(&al_mask); + alarm(delaytime); + sigsuspend(&al_mask); + sigaction(SIGALRM, &sa_old, 0); + break; + } +} + + +/* + * Format IO requests, returning a pointer to the formatted text. + * + * format_strat - formats the async i/o completion strategy + * format_rw - formats a read[a]/write[a] request + * format_sds - formats a ssread/sswrite request + * format_listio- formats a listio request + * + * ioreq is the doio io request structure. + */ + +struct smap sysnames[] = { + { "READ", READ }, + { "WRITE", WRITE }, + { "READA", READA }, + { "WRITEA", WRITEA }, + { "SSREAD", SSREAD }, + { "SSWRITE", SSWRITE }, + { "LISTIO", LISTIO }, + { "LREAD", LREAD }, + { "LREADA", LREADA }, + { "LWRITE", LWRITE }, + { "LWRITEA", LWRITEA }, + { "LSREAD", LSREAD }, + { "LSREADA", LSREADA }, + { "LSWRITE", LSWRITE }, + { "LSWRITEA", LSWRITEA }, + + /* Irix System Calls */ + { "PREAD", PREAD }, + { "PWRITE", PWRITE }, + { "AREAD", AREAD }, + { "AWRITE", AWRITE }, + { "LLREAD", LLREAD }, + { "LLAREAD", LLAREAD }, + { "LLWRITE", LLWRITE }, + { "LLAWRITE", LLAWRITE }, + { "RESVSP", RESVSP }, + { "UNRESVSP", UNRESVSP }, + + /* Irix and Linux System Calls */ + { "READV", READV }, + { "WRITEV", WRITEV }, + { "MMAPR", MMAPR }, + { "MMAPW", MMAPW }, + { "FSYNC2", FSYNC2 }, + { "FDATASYNC", FDATASYNC }, + + { "unknown", -1 }, +}; + +struct smap aionames[] = { + { "poll", A_POLL }, + { "signal", A_SIGNAL }, + { "recall", A_RECALL }, + { "recalla", A_RECALLA }, + { "recalls", A_RECALLS }, + { "suspend", A_SUSPEND }, + { "callback", A_CALLBACK }, + { "synch", 0 }, + { "unknown", -1 }, +}; + +char * +format_oflags(int oflags) +{ + char flags[255]; + + + flags[0]='\0'; + switch(oflags & 03) { + case O_RDONLY: strcat(flags,"O_RDONLY,"); break; + case O_WRONLY: strcat(flags,"O_WRONLY,"); break; + case O_RDWR: strcat(flags,"O_RDWR,"); break; + default: strcat(flags,"O_weird"); break; + } + + if(oflags & O_EXCL) + strcat(flags,"O_EXCL,"); + + if(oflags & O_SYNC) + strcat(flags,"O_SYNC,"); +#ifdef CRAY + if(oflags & O_RAW) + strcat(flags,"O_RAW,"); + if(oflags & O_WELLFORMED) + strcat(flags,"O_WELLFORMED,"); +#ifdef O_SSD + if(oflags & O_SSD) + strcat(flags,"O_SSD,"); +#endif + if(oflags & O_LDRAW) + strcat(flags,"O_LDRAW,"); + if(oflags & O_PARALLEL) + strcat(flags,"O_PARALLEL,"); + if(oflags & O_BIG) + strcat(flags,"O_BIG,"); + if(oflags & O_PLACE) + strcat(flags,"O_PLACE,"); + if(oflags & O_ASYNC) + strcat(flags,"O_ASYNC,"); +#endif + + if(oflags & O_DIRECT) + strcat(flags,"O_DIRECT,"); +#ifdef sgi + if(oflags & O_DSYNC) + strcat(flags,"O_DSYNC,"); + if(oflags & O_RSYNC) + strcat(flags,"O_RSYNC,"); +#endif + + return(strdup(flags)); +} + +char * +format_strat(int strategy) +{ + char msg[64]; + char *aio_strat; + + switch (strategy) { + case A_POLL: aio_strat = "POLL"; break; + case A_SIGNAL: aio_strat = "SIGNAL"; break; + case A_RECALL: aio_strat = "RECALL"; break; + case A_RECALLA: aio_strat = "RECALLA"; break; + case A_RECALLS: aio_strat = "RECALLS"; break; + case A_SUSPEND: aio_strat = "SUSPEND"; break; + case A_CALLBACK: aio_strat = "CALLBACK"; break; + case 0: aio_strat = ""; break; + default: + sprintf(msg, "", strategy); + aio_strat = strdup(msg); + break; + } + + return(aio_strat); +} + +char * +format_rw( + struct io_req *ioreq, + int fd, + void *buffer, + int signo, + char *pattern, +#ifdef CRAY + struct iosw *iosw +#else + void *iosw +#endif + ) +{ + static char *errbuf=NULL; + char *aio_strat, *cp; + struct read_req *readp = &ioreq->r_data.read; + struct write_req *writep = &ioreq->r_data.write; + struct read_req *readap = &ioreq->r_data.read; + struct write_req *writeap = &ioreq->r_data.write; + + if(errbuf == NULL) + errbuf = (char *)malloc(32768); + + cp = errbuf; + cp += sprintf(cp, "Request number %d\n", Reqno); + + switch (ioreq->r_type) { + case READ: + cp += sprintf(cp, "syscall: read(%d, %#lo, %d)\n", + fd, (unsigned long) buffer, readp->r_nbytes); + cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n", + fd, readp->r_file, readp->r_oflags); + cp += sprintf(cp, " read done at file offset %d\n", + readp->r_offset); + break; + + case WRITE: + cp += sprintf(cp, "syscall: write(%d, %#lo, %d)\n", + fd, (unsigned long) buffer, writep->r_nbytes); + cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n", + fd, writep->r_file, writep->r_oflags); + cp += sprintf(cp, " write done at file offset %d - pattern is %s\n", + writep->r_offset, pattern); + break; + + case READA: + aio_strat = format_strat(readap->r_aio_strat); + + cp += sprintf(cp, "syscall: reada(%d, %#lo, %d, %#lo, %d)\n", + fd, (unsigned long) buffer, readap->r_nbytes, + (unsigned long) iosw, signo); + cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n", + fd, readap->r_file, readp->r_oflags); + cp += sprintf(cp, " reada done at file offset %d\n", + readap->r_offset); + cp += sprintf(cp, " async io completion strategy is %s\n", + aio_strat); + break; + + case WRITEA: + aio_strat = format_strat(writeap->r_aio_strat); + + cp += sprintf(cp, "syscall: writea(%d, %#lo, %d, %#lo, %d)\n", + fd, (unsigned long) buffer, writeap->r_nbytes, + (unsigned long) iosw, signo); + cp += sprintf(cp, " fd %d is file %s - open flags are %#o\n", + fd, writeap->r_file, writeap->r_oflags); + cp += sprintf(cp, " writea done at file offset %d - pattern is %s\n", + writeap->r_offset, pattern); + cp += sprintf(cp, " async io completion strategy is %s\n", + aio_strat); + break; + + } + + return errbuf; +} + +#ifdef CRAY +char * +format_sds( + struct io_req *ioreq, + void *buffer, + int sds, + char *pattern + ) +{ + int i; + static char *errbuf=NULL; + char *cp; + + struct ssread_req *ssreadp = &ioreq->r_data.ssread; + struct sswrite_req *sswritep = &ioreq->r_data.sswrite; + + if(errbuf == NULL) + errbuf = (char *)malloc(32768); + + cp = errbuf; + cp += sprintf(cp, "Request number %d\n", Reqno); + + + switch (ioreq->r_type) { + case SSREAD: + cp += sprintf(cp, "syscall: ssread(%#o, %#o, %d)\n", + buffer, sds, ssreadp->r_nbytes); + break; + + case SSWRITE: + cp += sprintf(cp, "syscall: sswrite(%#o, %#o, %d) - pattern was %s\n", + buffer, sds, sswritep->r_nbytes, pattern); + break; + } + return errbuf; +} +#endif /* CRAY */ + +/* + * Perform the various sorts of disk reads + */ + +int +do_read(req) +struct io_req *req; +{ + int fd, offset, nbytes, oflags, rval; + char *addr, *file; +#ifdef CRAY + struct aio_info *aiop; + int aio_id, aio_strat, signo; +#endif +#ifndef NO_XFS + struct fd_cache *fdc; +#endif + + /* + * Initialize common fields - assumes r_oflags, r_file, r_offset, and + * r_nbytes are at the same offset in the read_req and reada_req + * structures. + */ + + file = req->r_data.read.r_file; + oflags = req->r_data.read.r_oflags; + offset = req->r_data.read.r_offset; + nbytes = req->r_data.read.r_nbytes; + + /*printf("read: %s, %#o, %d %d\n", file, oflags, offset, nbytes);*/ + + /* + * Grab an open file descriptor + * Note: must be done before memory allocation so that the direct i/o + * information is available in mem. allocate + */ + + if ((fd = alloc_fd(file, oflags)) == -1) + return -1; + + /* + * Allocate core or sds - based on the O_SSD flag + */ + +#ifndef wtob +#define wtob(x) (x * sizeof(UINT64_T)) +#endif + +#ifdef CRAY + if (oflags & O_SSD) { + if (alloc_sds(nbytes) == -1) + return -1; + + addr = (char *)Sdsptr; + } else { + if ((rval = alloc_mem(nbytes + wtob(1) * 2 + MPP_BUMP * sizeof(UINT64_T))) < 0) { + return rval; + } + + addr = Memptr; + + /* + * if io is not raw, bump the offset by a random amount + * to generate non-word-aligned io. + */ + if (! (req->r_data.read.r_uflags & F_WORD_ALIGNED)) { + addr += random_range(0, wtob(1) - 1, 1, NULL); + } + } +#else +#ifndef NO_XFS + /* get memory alignment for using DIRECT I/O */ + fdc = alloc_fdcache(file, oflags); + + if ((rval = alloc_mem(nbytes + wtob(1) * 2 + fdc->c_memalign)) < 0) { + return rval; + } + + addr = Memptr; + + + if( (req->r_data.read.r_uflags & F_WORD_ALIGNED) ) { + /* + * Force memory alignment for Direct I/O + */ + if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) { + addr += fdc->c_memalign - ((long)addr % fdc->c_memalign); + } + } else { + addr += random_range(0, wtob(1) - 1, 1, NULL); + } +#else + if ((rval = alloc_mem(nbytes + wtob(1) * 2)) < 0) { + return rval; + } + + addr = Memptr; +#endif /* !CRAY && sgi */ +#endif /* CRAY */ + + + switch (req->r_type) { + case READ: + /* move to the desired file position. */ + if (lseek(fd, offset, SEEK_SET) == -1) { + doio_fprintf(stderr, + "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n", + fd, offset, SYSERR, errno); + return -1; + } + + if ((rval = read(fd, addr, nbytes)) == -1) { + doio_fprintf(stderr, + "read() request failed: %s (%d)\n%s\n", + SYSERR, errno, + format_rw(req, fd, addr, -1, NULL, NULL)); + doio_upanic(U_RVAL); + return -1; + } else if (rval != nbytes) { + doio_fprintf(stderr, + "read() request returned wrong # of bytes - expected %d, got %d\n%s\n", + nbytes, rval, + format_rw(req, fd, addr, -1, NULL, NULL)); + doio_upanic(U_RVAL); + return -1; + } + break; + +#ifdef CRAY + case READA: + /* + * Async read + */ + + /* move to the desired file position. */ + if (lseek(fd, offset, SEEK_SET) == -1) { + doio_fprintf(stderr, + "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n", + fd, offset, SYSERR, errno); + return -1; + } + + aio_strat = req->r_data.read.r_aio_strat; + signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0; + + aio_id = aio_register(fd, aio_strat, signo); + aiop = aio_slot(aio_id); + + if (reada(fd, addr, nbytes, &aiop->iosw, signo) == -1) { + doio_fprintf(stderr, "reada() failed: %s (%d)\n%s\n", + SYSERR, errno, + format_rw(req, fd, addr, signo, NULL, &aiop->iosw)); + aio_unregister(aio_id); + doio_upanic(U_RVAL); + rval = -1; + } else { + /* + * Wait for io to complete + */ + + aio_wait(aio_id); + + /* + * make sure the io completed without error + */ + + if (aiop->iosw.sw_count != nbytes) { + doio_fprintf(stderr, + "Bad iosw from reada()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n", + 1, 0, nbytes, + aiop->iosw.sw_flag, + aiop->iosw.sw_error, + aiop->iosw.sw_count, + format_rw(req, fd, addr, signo, NULL, &aiop->iosw)); + aio_unregister(aio_id); + doio_upanic(U_IOSW); + rval = -1; + } else { + aio_unregister(aio_id); + rval = 0; + } + } + + if (rval == -1) + return rval; + break; +#endif /* CRAY */ + } + + return 0; /* if we get here, everything went ok */ +} + +/* + * Perform the verious types of disk writes. + */ + +int +do_write(req) +struct io_req *req; +{ + static int pid = -1; + int fd, nbytes, oflags, signo; + int logged_write, rval, got_lock; + long offset, woffset = 0; + char *addr, pattern, *file, *msg; + struct wlog_rec wrec; +#ifdef CRAY + int aio_strat, aio_id; + struct aio_info *aiop; +#endif +#ifndef NO_XFS + struct fd_cache *fdc; +#endif + + /* + * Misc variable setup + */ + + signo = 0; + nbytes = req->r_data.write.r_nbytes; + offset = req->r_data.write.r_offset; + pattern = req->r_data.write.r_pattern; + file = req->r_data.write.r_file; + oflags = req->r_data.write.r_oflags; + + /*printf("pwrite: %s, %#o, %d %d\n", file, oflags, offset, nbytes);*/ + + /* + * Allocate core memory and possibly sds space. Initialize the data + * to be written. + */ + + Pattern[0] = pattern; + + + /* + * Get a descriptor to do the io on + */ + + if ((fd = alloc_fd(file, oflags)) == -1) + return -1; + + /*printf("write: %d, %s, %#o, %d %d\n", + fd, file, oflags, offset, nbytes);*/ + + /* + * Allocate SDS space for backdoor write if desired + */ + +#ifdef CRAY + if (oflags & O_SSD) { +#ifndef _CRAYMPP + if ((rval = alloc_mem(nbytes + wtob(1))) < 0) { + return rval; + } + + (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0); + /*pattern_fill(Memptr, nbytes, Pattern, Pattern_Length, 0);*/ + + if (alloc_sds(nbytes) == -1) + return -1; + + if (sswrite((long)Memptr, Sdsptr, btoc(nbytes)) == -1) { + doio_fprintf(stderr, "sswrite(%d, %d, %d) failed: %s (%d)\n", + (long)Memptr, Sdsptr, btoc(nbytes), + SYSERR, errno); + fflush(stderr); + return -1; + } + + addr = (char *)Sdsptr; +#else + doio_fprintf(stderr, "Invalid O_SSD flag was generated for MPP system\n"); + fflush(stderr); + return -1; +#endif /* !CRAYMPP */ + } else { + if ((rval = alloc_mem(nbytes + wtob(1)) < 0)) { + return rval; + } + + addr = Memptr; + + /* + * if io is not raw, bump the offset by a random amount + * to generate non-word-aligned io. + */ + + if (! (req->r_data.write.r_uflags & F_WORD_ALIGNED)) { + addr += random_range(0, wtob(1) - 1, 1, NULL); + } + + (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0); + if( addr != Memptr ) + memmove( addr, Memptr, nbytes); + } +#else /* CRAY */ +#ifndef NO_XFS + /* get memory alignment for using DIRECT I/O */ + fdc = alloc_fdcache(file, oflags); + + if ((rval = alloc_mem(nbytes + wtob(1) * 2 + fdc->c_memalign)) < 0) { + return rval; + } + + addr = Memptr; + + if( (req->r_data.write.r_uflags & F_WORD_ALIGNED) ) { + /* + * Force memory alignment for Direct I/O + */ + if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) { + addr += fdc->c_memalign - ((long)addr % fdc->c_memalign); + } + } else { + addr += random_range(0, wtob(1) - 1, 1, NULL); + } + + (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0); + if( addr != Memptr ) + memmove( addr, Memptr, nbytes); + +#else /* sgi */ + if ((rval = alloc_mem(nbytes + wtob(1) * 2)) < 0) { + return rval; + } + + addr = Memptr; + + (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0); + if( addr != Memptr ) + memmove( addr, Memptr, nbytes); +#endif /* sgi */ +#endif /* CRAY */ + + rval = -1; + got_lock = 0; + logged_write = 0; + + if (k_opt) { + if (lock_file_region(file, fd, F_WRLCK, offset, nbytes) < 0) { + alloc_mem(-1); + exit(E_INTERNAL); + } + + got_lock = 1; + } + + /* + * Write a preliminary write-log entry. This is done so that + * doio_check can do corruption detection across an interrupt/crash. + * Note that w_done is set to 0. If doio_check sees this, it + * re-creates the file extents as if the write completed, but does not + * do any checking - see comments in doio_check for more details. + */ + + if (w_opt) { + if (pid == -1) { + pid = getpid(); + } + wrec.w_async = (req->r_type == WRITEA) ? 1 : 0; + wrec.w_oflags = oflags; + wrec.w_pid = pid; + wrec.w_offset = offset; + wrec.w_nbytes = nbytes; + + wrec.w_pathlen = strlen(file); + memcpy(wrec.w_path, file, wrec.w_pathlen); + wrec.w_hostlen = strlen(Host); + memcpy(wrec.w_host, Host, wrec.w_hostlen); + wrec.w_patternlen = Pattern_Length; + memcpy(wrec.w_pattern, Pattern, wrec.w_patternlen); + + wrec.w_done = 0; + + if ((woffset = wlog_record_write(&Wlog, &wrec, -1)) == -1) { + doio_fprintf(stderr, + "Could not append to write-log: %s (%d)\n", + SYSERR, errno); + } else { + logged_write = 1; + } + } + + switch (req->r_type ) { + case WRITE: + /* + * sync write + */ + + if (lseek(fd, offset, SEEK_SET) == -1) { + doio_fprintf(stderr, + "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n", + fd, offset, SYSERR, errno); + return -1; + } + + rval = write(fd, addr, nbytes); + + if (rval == -1) { + doio_fprintf(stderr, + "write() failed: %s (%d)\n%s\n", + SYSERR, errno, + format_rw(req, fd, addr, -1, Pattern, NULL)); +#ifndef NO_XFS + doio_fprintf(stderr, + "write() failed: %s\n\twrite(%d, %#o, %d)\n\toffset %d, nbytes%%miniou(%d)=%d, oflags=%#o memalign=%d, addr%%memalign=%d\n", + strerror(errno), + fd, addr, nbytes, + offset, + fdc->c_miniosz, nbytes%fdc->c_miniosz, + oflags, fdc->c_memalign, (long)addr%fdc->c_memalign); +#else + doio_fprintf(stderr, + "write() failed: %s\n\twrite(%d, %#o, %d)\n\toffset %d, nbytes%%1B=%d, oflags=%#o\n", + strerror(errno), + fd, addr, nbytes, + offset, nbytes%4096, oflags); +#endif + doio_upanic(U_RVAL); + } else if (rval != nbytes) { + doio_fprintf(stderr, + "write() returned wrong # bytes - expected %d, got %d\n%s\n", + nbytes, rval, + format_rw(req, fd, addr, -1, Pattern, NULL)); + doio_upanic(U_RVAL); + rval = -1; + } + + break; + +#ifdef CRAY + case WRITEA: + /* + * async write + */ + if (lseek(fd, offset, SEEK_SET) == -1) { + doio_fprintf(stderr, + "lseek(%d, %d, SEEK_SET) failed: %s (%d)\n", + fd, offset, SYSERR, errno); + return -1; + } + + aio_strat = req->r_data.write.r_aio_strat; + signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0; + + aio_id = aio_register(fd, aio_strat, signo); + aiop = aio_slot(aio_id); + + /* + * init iosw and do the async write + */ + + if (writea(fd, addr, nbytes, &aiop->iosw, signo) == -1) { + doio_fprintf(stderr, + "writea() failed: %s (%d)\n%s\n", + SYSERR, errno, + format_rw(req, fd, addr, -1, Pattern, NULL)); + doio_upanic(U_RVAL); + aio_unregister(aio_id); + rval = -1; + } else { + + /* + * Wait for io to complete + */ + + aio_wait(aio_id); + + /* + * check that iosw is ok + */ + + if (aiop->iosw.sw_count != nbytes) { + doio_fprintf(stderr, + "Bad iosw from writea()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n", + 1, 0, nbytes, + aiop->iosw.sw_flag, + aiop->iosw.sw_error, + aiop->iosw.sw_count, + format_rw(req, fd, addr, -1, Pattern, &aiop->iosw)); + aio_unregister(aio_id); + doio_upanic(U_IOSW); + rval = -1; + } else { + aio_unregister(aio_id); + rval = 0; + } + } + break; + +#endif /* CRAY */ + } + + /* + * Verify that the data was written correctly - check_file() returns + * a non-null pointer which contains an error message if there are + * problems. + */ + + if (v_opt) { + msg = check_file(file, offset, nbytes, Pattern, Pattern_Length, + 0, oflags & O_PARALLEL); + if (msg != NULL) { + doio_fprintf(stderr, "%s%s\n", + msg, +#ifdef CRAY + format_rw(req, fd, addr, -1, Pattern, &aiop->iosw) +#else + format_rw(req, fd, addr, -1, Pattern, NULL) +#endif + ); + doio_upanic(U_CORRUPTION); + exit(E_COMPARE); + + } + } + + /* + * General cleanup ... + * + * Write extent information to the write-log, so that doio_check can do + * corruption detection. Note that w_done is set to 1, indicating that + * the write has been verified as complete. We don't need to write the + * filename on the second logging. + */ + + if (w_opt && logged_write) { + wrec.w_done = 1; + wlog_record_write(&Wlog, &wrec, woffset); + } + + /* + * Unlock file region if necessary + */ + + if (got_lock) { + if (lock_file_region(file, fd, F_UNLCK, offset, nbytes) < 0) { + alloc_mem(-1); + exit(E_INTERNAL); + } + } + + return( (rval == -1) ? -1 : 0); +} + + +/* + * Simple routine to lock/unlock a file using fcntl() + */ + +int +lock_file_region(fname, fd, type, start, nbytes) +char *fname; +int fd; +int type; +int start; +int nbytes; +{ + struct flock flk; + + flk.l_type = type; + flk.l_whence = 0; + flk.l_start = start; + flk.l_len = nbytes; + + if (fcntl(fd, F_SETLKW, &flk) < 0) { + doio_fprintf(stderr, + "fcntl(%d, %d, %#o) failed for file %s, lock type %d, offset %d, length %d: %s (%d), open flags: %#o\n", + fd, F_SETLKW, &flk, fname, type, + start, nbytes, SYSERR, errno, + fcntl(fd, F_GETFL, 0)); + return -1; + } + + return 0; +} + +/* + * Perform a listio request. + */ + +#ifdef CRAY +char * +format_listio( + struct io_req *ioreq, + int lcmd, + struct listreq *list, + int nent, + int fd, + char *pattern + ) +{ + static char *errbuf=NULL; + struct listio_req *liop = &ioreq->r_data.listio; + struct listreq *listreq; + char *cp, *cmd, *opcode, *aio_strat; + int i; + + switch (lcmd) { + case LC_START: cmd = "LC_START"; break; + case LC_WAIT: cmd = "LC_WAIT"; break; + default: cmd = "???"; break; + } + + if(errbuf == NULL) + errbuf = (char *)malloc(32768); + + cp = errbuf; + cp += sprintf(cp, "Request number %d\n", Reqno); + + cp += sprintf(cp, "syscall: listio(%s, %#o, %d)\n\n", + cmd, list, nent); + + aio_strat = format_strat(liop->r_aio_strat); + + for (i = 0; i < nent; i++) { + cp += sprintf(cp, "struct lioreq for request element %d\n", i); + cp += sprintf(cp, "----------------------------------------\n"); + + listreq = list + i; + + switch (listreq->li_opcode) { + case LO_READ: opcode = "LO_READ"; break; + case LO_WRITE: opcode = "LO_WRITE"; break; + default: opcode = "???"; break; + } + + cp += sprintf(cp, " li_opcode = %s\n", opcode); + cp += sprintf(cp, " li_drvr = %#o\n", listreq->li_drvr); + cp += sprintf(cp, " li_flags = %#o\n", listreq->li_flags); + cp += sprintf(cp, " li_offset = %d\n", listreq->li_offset); + cp += sprintf(cp, " li_fildes = %d\n", listreq->li_fildes); + cp += sprintf(cp, " li_buf = %#o\n", listreq->li_buf); + cp += sprintf(cp, " li_nbyte = %d\n", listreq->li_nbyte); + cp += sprintf(cp, " li_status = %#o (%d, %d, %d)\n", listreq->li_status, listreq->li_status->sw_flag, listreq->li_status->sw_error, listreq->li_status->sw_count); + cp += sprintf(cp, " li_signo = %d\n", listreq->li_signo); + cp += sprintf(cp, " li_nstride = %d\n", listreq->li_nstride); + cp += sprintf(cp, " li_filstride = %d\n", listreq->li_filstride); + cp += sprintf(cp, " li_memstride = %d\n", listreq->li_memstride); + cp += sprintf(cp, " io completion strategy is %s\n", aio_strat); + } + return errbuf; +} +#endif /* CRAY */ + +int +do_listio(req) +struct io_req *req; +{ +#ifdef CRAY + struct listio_req *lio; + int fd, oflags, signo, nb, i; + int logged_write, rval, got_lock; + int aio_strat, aio_id; + int min_byte, max_byte; + int mem_needed; + int foffset, fstride, mstride, nstrides; + char *moffset; + long offset, woffset; + char *addr, *msg; + sigset_t block_mask, omask; + struct wlog_rec wrec; + struct aio_info *aiop; + struct listreq lio_req; + + lio = &req->r_data.listio; + + /* + * If bytes per stride is less than the stride size, drop the request + * since it will cause overlapping strides, and we cannot predict + * the order they will complete in. + */ + + if (lio->r_filestride && abs(lio->r_filestride) < lio->r_nbytes) { + doio_fprintf(stderr, "do_listio(): Bogus listio request - abs(filestride) [%d] < nbytes [%d]\n", + abs(lio->r_filestride), lio->r_nbytes); + return -1; + } + + /* + * Allocate core memory. Initialize the data to be written. Make + * sure we get enough, based on the memstride. + */ + + mem_needed = + stride_bounds(0, lio->r_memstride, lio->r_nstrides, + lio->r_nbytes, NULL, NULL); + + if ((rval = alloc_mem(mem_needed + wtob(1))) < 0) { + return rval; + } + + /* + * Set the memory address pointer. If the io is not raw, adjust + * addr by a random amount, so that non-raw io is not necessarily + * word aligned. + */ + + addr = Memptr; + + if (! (lio->r_uflags & F_WORD_ALIGNED)) { + addr += random_range(0, wtob(1) - 1, 1, NULL); + } + + if (lio->r_opcode == LO_WRITE) { + Pattern[0] = lio->r_pattern; + (*Data_Fill)(Memptr, mem_needed, Pattern, Pattern_Length, 0); + if( addr != Memptr ) + memmove( addr, Memptr, mem_needed); + } + + /* + * Get a descriptor to do the io on. No need to do an lseek, as this + * is encoded in the listio request. + */ + + if ((fd = alloc_fd(lio->r_file, lio->r_oflags)) == -1) { + return -1; + } + + rval = -1; + got_lock = 0; + logged_write = 0; + + /* + * If the opcode is LO_WRITE, lock all regions of the file that + * are touched by this listio request. Currently, we use + * stride_bounds() to figure out the min and max bytes affected, and + * lock the entire region, regardless of the file stride. + */ + + if (lio->r_opcode == LO_WRITE && k_opt) { + stride_bounds(lio->r_offset, + lio->r_filestride, lio->r_nstrides, + lio->r_nbytes, &min_byte, &max_byte); + + if (lock_file_region(lio->r_file, fd, F_WRLCK, + min_byte, (max_byte-min_byte+1)) < 0) { + doio_fprintf(stderr, "stride_bounds(%d, %d, %d, %d, ..., ...) set min_byte to %d, max_byte to %d\n", + lio->r_offset, lio->r_filestride, + lio->r_nstrides, lio->r_nbytes, min_byte, + max_byte); + return -1; + } else { + got_lock = 1; + } + } + + /* + * async write + */ + + aio_strat = lio->r_aio_strat; + signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0; + + aio_id = aio_register(fd, aio_strat, signo); + aiop = aio_slot(aio_id); + + /* + * Form the listio request, and make the call. + */ + + lio_req.li_opcode = lio->r_opcode; + lio_req.li_drvr = 0; + lio_req.li_flags = LF_LSEEK; + lio_req.li_offset = lio->r_offset; + lio_req.li_fildes = fd; + + if (lio->r_memstride >= 0 || lio->r_nstrides <= 1) { + lio_req.li_buf = addr; + } else { + lio_req.li_buf = addr + mem_needed - lio->r_nbytes; + } + + lio_req.li_nbyte = lio->r_nbytes; + lio_req.li_status = &aiop->iosw; + lio_req.li_signo = signo; + lio_req.li_nstride = lio->r_nstrides; + lio_req.li_filstride = lio->r_filestride; + lio_req.li_memstride = lio->r_memstride; + + /* + * If signo != 0, block signo while we're in the system call, so that + * we don't get interrupted syscall failures. + */ + + if (signo) { + sigemptyset(&block_mask); + sigaddset(&block_mask, signo); + sigprocmask(SIG_BLOCK, &block_mask, &omask); + } + + if (listio(lio->r_cmd, &lio_req, 1) < 0) { + doio_fprintf(stderr, + "listio() failed: %s (%d)\n%s\n", + SYSERR, errno, + format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern)); + aio_unregister(aio_id); + doio_upanic(U_RVAL); + goto lio_done; + } + + if (signo) { + sigprocmask(SIG_SETMASK, &omask, NULL); + } + + /* + * Wait for io to complete + */ + + aio_wait(aio_id); + + nstrides = lio->r_nstrides ? lio->r_nstrides : 1; + if (aiop->iosw.sw_count != lio->r_nbytes * nstrides) { + doio_fprintf(stderr, + "Bad iosw from listio()\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n", + 1, 0, lio->r_nbytes * lio->r_nstrides, + aiop->iosw.sw_flag, + aiop->iosw.sw_error, aiop->iosw.sw_count, + format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern)); + aio_unregister(aio_id); + doio_upanic(U_IOSW); + goto lio_done; + } + + aio_unregister(aio_id); + + /* + * Verify that the data was written correctly - check_file() returns + * a non-null pointer which contains an error message if there are + * problems. + * + * For listio, we basically have to make 1 call to check_file for each + * stride. + */ + + if (v_opt && lio_req.li_opcode == LO_WRITE) { + fstride = lio->r_filestride ? lio->r_filestride : lio->r_nbytes; + mstride = lio->r_memstride ? lio->r_memstride : lio->r_nbytes; + foffset = lio->r_offset; + + if (mstride> 0 || lio->r_nstrides <= 1) { + moffset = addr; + } else { + moffset = addr + mem_needed - lio->r_nbytes; + } + + for (i = 0; i < lio_req.li_nstride; i++) { + msg = check_file(lio->r_file, + foffset, lio->r_nbytes, + Pattern, Pattern_Length, + moffset - addr, + lio->r_oflags & O_PARALLEL); + + if (msg != NULL) { + doio_fprintf(stderr, "%s\n%s\n", + msg, + format_listio(req, lio->r_cmd, &lio_req, 1, fd, Pattern)); + doio_upanic(U_CORRUPTION); + exit(E_COMPARE); + } + + moffset += mstride; + foffset += fstride; + } + + } + + rval = 0; + + lio_done: + + /* + * General cleanup ... + * + */ + + /* + * Release file locks if necessary + */ + + if (got_lock) { + if (lock_file_region(lio->r_file, fd, F_UNLCK, + min_byte, (max_byte-min_byte+1)) < 0) { + return -1; + } + } + + return rval; +#else + return -1; +#endif +} + +/* + * perform ssread/sswrite operations + */ + +#ifdef _CRAY1 + +int +do_ssdio(req) +struct io_req *req; +{ + int nbytes, nb; + char errbuf[BSIZE]; + + nbytes = req->r_data.ssread.r_nbytes; + + /* + * Grab core and sds space + */ + + if ((nb = alloc_mem(nbytes)) < 0) + return nb; + + if (alloc_sds(nbytes) == -1) + return -1; + + if (req->r_type == SSWRITE) { + + /* + * Init data and ship it to the ssd + */ + + Pattern[0] = req->r_data.sswrite.r_pattern; + /*pattern_fill(Memptr, nbytes, Pattern, Pattern_Length, 0);*/ + (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0); + + if (sswrite((long)Memptr, (long)Sdsptr, btoc(nbytes)) == -1) { + doio_fprintf(stderr, "sswrite() failed: %s (%d)\n%s\n", + SYSERR, errno, + format_sds(req, Memptr, Sdsptr, Pattern)); + doio_upanic(U_RVAL); + return -1; + } + } else { + /* + * read from sds + */ + + if (ssread((long)Memptr, (long)Sdsptr, btoc(nbytes)) == -1) { + doio_fprintf(stderr, "ssread() failed: %s (%d)\n%s\n", + SYSERR, errno, + format_sds(req, Memptr, Sdsptr, Pattern)); + + doio_upanic(U_RVAL); + return -1; + } + } + + /* + * Verify data if SSWRITE and v_opt + */ + + if (v_opt && req->r_type == SSWRITE) { + ssread((long)Memptr, (long)Sdsptr, btoc(nbytes)); + + if (pattern_check(Memptr, nbytes, Pattern, Pattern_Length, 0) == -1) { + doio_fprintf(stderr, + "sds DATA COMPARE ERROR - ABORTING\n%s\n", + format_sds(req, Memptr, Sdsptr, Pattern)); + + doio_upanic(U_CORRUPTION); + exit(E_COMPARE); + } + } +} + +#else + +#ifdef CRAY + +int +do_ssdio(req) +struct io_req *req; +{ + doio_fprintf(stderr, + "Internal Error - do_ssdio() called on a non-cray1 system\n"); + alloc_mem(-1); + exit(E_INTERNAL); +} + +#endif + +#endif /* _CRAY1 */ + + +/* --------------------------------------------------------------------------- + * + * A new paradigm of doing the r/w system call where there is a "stub" + * function that builds the info for the system call, then does the system + * call; this is called by code that is common to all system calls and does + * the syscall return checking, async I/O wait, iosw check, etc. + * + * Flags: + * WRITE, ASYNC, SSD/SDS, + * FILE_LOCK, WRITE_LOG, VERIFY_DATA, + */ + +struct status { + int rval; /* syscall return */ + int err; /* errno */ + int *aioid; /* list of async I/O structures */ +}; + +struct syscall_info { + char *sy_name; + int sy_type; + struct status *(*sy_syscall)(); + int (*sy_buffer)(); + char *(*sy_format)(); + int sy_flags; + int sy_bits; +}; + +#define SY_WRITE 00001 +#define SY_ASYNC 00010 +#define SY_IOSW 00020 +#define SY_SDS 00100 + +char * +fmt_ioreq(struct io_req *ioreq, struct syscall_info *sy, int fd) +{ + static char *errbuf=NULL; + char *cp; + struct rw_req *io; + struct smap *aname; +#ifdef CRAY + struct stat sbuf; +#endif + + if(errbuf == NULL) + errbuf = (char *)malloc(32768); + + io = &ioreq->r_data.io; + + /* + * Look up async I/O completion strategy + */ + for(aname=aionames; + aname->value != -1 && aname->value != io->r_aio_strat; + aname++) + ; + + cp = errbuf; + cp += sprintf(cp, "Request number %d\n", Reqno); + + cp += sprintf(cp, " fd %d is file %s - open flags are %#o %s\n", + fd, io->r_file, io->r_oflags, format_oflags(io->r_oflags)); + + if(sy->sy_flags & SY_WRITE) { + cp += sprintf(cp, " write done at file offset %d - pattern is %c (%#o)\n", + io->r_offset, + (io->r_pattern == '\0') ? '?' : io->r_pattern, + io->r_pattern); + } else { + cp += sprintf(cp, " read done at file offset %d\n", + io->r_offset); + } + + if(sy->sy_flags & SY_ASYNC) { + cp += sprintf(cp, " async io completion strategy is %s\n", + aname->string); + } + + cp += sprintf(cp, " number of requests is %d, strides per request is %d\n", + io->r_nent, io->r_nstrides); + + cp += sprintf(cp, " i/o byte count = %d\n", + io->r_nbytes); + + cp += sprintf(cp, " memory alignment is %s\n", + (io->r_uflags & F_WORD_ALIGNED) ? "aligned" : "unaligned"); + +#ifdef CRAY + if(io->r_oflags & O_RAW) { + cp += sprintf(cp, " RAW I/O: offset %% 4096 = %d length %% 4096 = %d\n", + io->r_offset % 4096, io->r_nbytes % 4096); + fstat(fd, &sbuf); + cp += sprintf(cp, " optimal file xfer size: small: %d large: %d\n", + sbuf.st_blksize, sbuf.st_oblksize); + cp += sprintf(cp, " cblks %d cbits %#o\n", + sbuf.st_cblks, sbuf.st_cbits); + } +#endif +#ifndef NO_XFS + if(io->r_oflags & O_DIRECT) { + struct dioattr finfo; + + if(xfsctl(io->r_file, fd, XFS_IOC_DIOINFO, &finfo) == -1) { + cp += sprintf(cp, " Error %s (%d) getting direct I/O info\n", + strerror(errno), errno); + finfo.d_mem = 1; + finfo.d_miniosz = 1; + finfo.d_maxiosz = 1; + } + + cp += sprintf(cp, " DIRECT I/O: offset %% %d = %d length %% %d = %d\n", + finfo.d_miniosz, + io->r_offset % finfo.d_miniosz, + io->r_nbytes, + io->r_nbytes % finfo.d_miniosz); + cp += sprintf(cp, " mem alignment 0x%x xfer size: small: %d large: %d\n", + finfo.d_mem, finfo.d_miniosz, finfo.d_maxiosz); + } +#endif + + return(errbuf); +} + +/* + * Issue listio requests + */ +#ifdef CRAY +struct status * +sy_listio(req, sysc, fd, addr) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +{ + int offset, nbytes, nstrides, nents, aio_strat; + int aio_id, signo, o, i, lc; + char *a; + struct listreq *lio_req, *l; + struct aio_info *aiop; + struct status *status; + + /* + * Initialize common fields - assumes r_oflags, r_file, r_offset, and + * r_nbytes are at the same offset in the read_req and reada_req + * structures. + */ + offset = req->r_data.io.r_offset; + nbytes = req->r_data.io.r_nbytes; + nstrides = req->r_data.io.r_nstrides; + nents = req->r_data.io.r_nent; + aio_strat = req->r_data.io.r_aio_strat; + + lc = (sysc->sy_flags & SY_ASYNC) ? LC_START : LC_WAIT; + + status = (struct status *)malloc(sizeof(struct status)); + if( status == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + status->aioid = (int *)malloc( (nents+1) * sizeof(int) ); + if( status->aioid == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + + signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0; + + lio_req = (struct listreq *)malloc(nents * sizeof(struct listreq)); + if( lio_req == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + for(l=lio_req,a=addr,o=offset,i=0; + i < nents; + l++, a+=nbytes, o+=nbytes, i++) { + + aio_id = aio_register(fd, aio_strat, signo); + aiop = aio_slot(aio_id); + status->aioid[i] = aio_id; + + l->li_opcode = (sysc->sy_flags & SY_WRITE) ? LO_WRITE : LO_READ; + l->li_offset = o; + l->li_fildes = fd; + l->li_buf = a; + l->li_nbyte = nbytes; + l->li_status = &aiop->iosw; + l->li_signo = signo; + l->li_nstride = nstrides; + l->li_filstride = 0; + l->li_memstride = 0; + l->li_drvr = 0; + l->li_flags = LF_LSEEK; + } + + status->aioid[nents] = -1; /* end sentinel */ + + if( (status->rval = listio(lc, lio_req, nents)) == -1) { + status->err = errno; + } + + free(lio_req); + return(status); +} + +/* + * Calculate the size of a request in bytes and min/max boundaries + * + * This assumes filestride & memstride = 0. + */ +int +listio_mem(struct io_req *req, int offset, int fmstride, + int *min, int *max) +{ + int i, size; + + size = stride_bounds(offset, fmstride, + req->r_data.io.r_nstrides*req->r_data.io.r_nent, + req->r_data.io.r_nbytes, min, max); + return(size); +} + +char * +fmt_listio(struct io_req *req, struct syscall_info *sy, int fd, char *addr) +{ + static char *errbuf = NULL; + char *cp; + char *c, *opcode; + int i; + + if(errbuf == NULL){ + errbuf = (char *)malloc(32768); + if( errbuf == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + } + + c = (sy->sy_flags & SY_ASYNC) ? "lc_wait" : "lc_start"; + + cp = errbuf; + cp += sprintf(cp, "syscall: listio(%s, (?), %d)\n", + c, req->r_data.io.r_nent); + + cp += sprintf(cp, " data buffer at %#o\n", addr); + + return(errbuf); +} +#endif /* CRAY */ + +struct status * +sy_pread(req, sysc, fd, addr) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +{ + int rc; + struct status *status; + + rc = pread(fd, addr, req->r_data.io.r_nbytes, + req->r_data.io.r_offset); + + status = (struct status *)malloc(sizeof(struct status)); + if( status == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + status->aioid = NULL; + status->rval = rc; + status->err = errno; + + return(status); +} + +struct status * +sy_pwrite(req, sysc, fd, addr) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +{ + int rc; + struct status *status; + + rc = pwrite(fd, addr, req->r_data.io.r_nbytes, + req->r_data.io.r_offset); + + status = (struct status *)malloc(sizeof(struct status)); + if( status == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + status->aioid = NULL; + status->rval = rc; + status->err = errno; + + return(status); +} + +char * +fmt_pread(struct io_req *req, struct syscall_info *sy, int fd, char *addr) +{ + static char *errbuf = NULL; + char *cp; + + if(errbuf == NULL){ + errbuf = (char *)malloc(32768); + if( errbuf == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + } + + cp = errbuf; + cp += sprintf(cp, "syscall: %s(%d, 0x%p, %d)\n", + sy->sy_name, fd, addr, req->r_data.io.r_nbytes); + return(errbuf); +} + +#ifndef CRAY +struct status * +sy_readv(req, sysc, fd, addr) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +{ + struct status *sy_rwv(); + return sy_rwv(req, sysc, fd, addr, 0); +} + +struct status * +sy_writev(req, sysc, fd, addr) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +{ + struct status *sy_rwv(); + return sy_rwv(req, sysc, fd, addr, 1); +} + +struct status * +sy_rwv(req, sysc, fd, addr, rw) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +int rw; +{ + int rc; + struct status *status; + struct iovec iov[2]; + + status = (struct status *)malloc(sizeof(struct status)); + if( status == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + status->aioid = NULL; + + /* move to the desired file position. */ + if ((rc=lseek(fd, req->r_data.io.r_offset, SEEK_SET)) == -1) { + status->rval = rc; + status->err = errno; + return(status); + } + + iov[0].iov_base = addr; + iov[0].iov_len = req->r_data.io.r_nbytes; + + if(rw) + rc = writev(fd, iov, 1); + else + rc = readv(fd, iov, 1); + status->aioid = NULL; + status->rval = rc; + status->err = errno; + return(status); +} + +char * +fmt_readv(struct io_req *req, struct syscall_info *sy, int fd, char *addr) +{ + static char errbuf[32768]; + char *cp; + + cp = errbuf; + cp += sprintf(cp, "syscall: %s(%d, (iov on stack), 1)\n", + sy->sy_name, fd); + return(errbuf); +} +#endif /* !CRAY */ + +#ifdef sgi +struct status * +sy_aread(req, sysc, fd, addr) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +{ + struct status *sy_arw(); + return sy_arw(req, sysc, fd, addr, 0); +} + +struct status * +sy_awrite(req, sysc, fd, addr) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +{ + struct status *sy_arw(); + return sy_arw(req, sysc, fd, addr, 1); +} + +/* + #define sy_aread(A, B, C, D) sy_arw(A, B, C, D, 0) + #define sy_awrite(A, B, C, D) sy_arw(A, B, C, D, 1) + */ + +struct status * +sy_arw(req, sysc, fd, addr, rw) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +int rw; +{ + /* POSIX 1003.1b-1993 Async read */ + struct status *status; + int rc; + int aio_id, aio_strat, signo; + struct aio_info *aiop; + + status = (struct status *)malloc(sizeof(struct status)); + if( status == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + aio_strat = req->r_data.io.r_aio_strat; + signo = (aio_strat == A_SIGNAL) ? SIGUSR1 : 0; + + aio_id = aio_register(fd, aio_strat, signo); + aiop = aio_slot(aio_id); + + memset( (void *)&aiop->aiocb, 0, sizeof(aiocb_t)); + + aiop->aiocb.aio_fildes = fd; + aiop->aiocb.aio_nbytes = req->r_data.io.r_nbytes; + aiop->aiocb.aio_offset = req->r_data.io.r_offset; + aiop->aiocb.aio_buf = addr; + aiop->aiocb.aio_reqprio = 0; /* must be 0 */ + aiop->aiocb.aio_lio_opcode = 0; + + if(aio_strat == A_SIGNAL) { /* siginfo(2) stuff */ + aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_SIGNAL; + aiop->aiocb.aio_sigevent.sigev_signo = signo; + } else if(aio_strat == A_CALLBACK) { + aiop->aiocb.aio_sigevent.sigev_signo = 0; + aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_CALLBACK; + aiop->aiocb.aio_sigevent.sigev_func = cb_handler; + aiop->aiocb.aio_sigevent.sigev_value.sival_int = aio_id; + } else { + aiop->aiocb.aio_sigevent.sigev_notify = SIGEV_NONE; + aiop->aiocb.aio_sigevent.sigev_signo = 0; + } + + if(rw) + rc = aio_write(&aiop->aiocb); + else + rc = aio_read(&aiop->aiocb); + + status->aioid = (int *)malloc( 2 * sizeof(int) ); + if( status->aioid == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + status->aioid[0] = aio_id; + status->aioid[1] = -1; + status->rval = rc; + status->err = errno; + return(status); +} + +char * +fmt_aread(struct io_req *req, struct syscall_info *sy, int fd, char *addr) +{ + static char errbuf[32768]; + char *cp; + + cp = errbuf; + cp += sprintf(cp, "syscall: %s(&aiop->aiocb)\n", + sy->sy_name); + return(errbuf); +} +#endif /* sgi */ + +#ifndef CRAY + +struct status * +sy_mmread(req, sysc, fd, addr) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +{ + struct status *sy_mmrw(); + return sy_mmrw(req, sysc, fd, addr, 0); +} + +struct status * +sy_mmwrite(req, sysc, fd, addr) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +{ + struct status *sy_mmrw(); + return sy_mmrw(req, sysc, fd, addr, 1); +} + +struct status * +sy_mmrw(req, sysc, fd, addr, rw) +struct io_req *req; +struct syscall_info *sysc; +int fd; +char *addr; +int rw; +{ + /* + * mmap read/write + * This version is oriented towards mmaping the file to memory + * ONCE and keeping it mapped. + */ + struct status *status; + void *mrc, *memaddr; + struct fd_cache *fdc; + struct stat sbuf; + + status = (struct status *)malloc(sizeof(struct status)); + if( status == NULL ){ + doio_fprintf(stderr, "malloc failed, %s/%d\n", + __FILE__, __LINE__); + return NULL; + } + status->aioid = NULL; + status->rval = -1; + + fdc = alloc_fdcache(req->r_data.io.r_file, req->r_data.io.r_oflags); + + if( fdc->c_memaddr == NULL ) { + if( fstat(fd, &sbuf) < 0 ){ + doio_fprintf(stderr, "fstat failed, errno=%d\n", + errno); + status->err = errno; + return(status); + } + + fdc->c_memlen = (int)sbuf.st_size; + mrc = mmap(NULL, (int)sbuf.st_size, + rw ? PROT_WRITE|PROT_READ : PROT_READ, + MAP_SHARED, fd, 0); + + if( mrc == MAP_FAILED ) { + doio_fprintf(stderr, "mmap() failed - 0x%lx %d\n", + mrc, errno); + status->err = errno; + return(status); + } + + fdc->c_memaddr = mrc; + } + + memaddr = (void *)((char *)fdc->c_memaddr + req->r_data.io.r_offset); + + active_mmap_rw = 1; + if(rw) + memcpy(memaddr, addr, req->r_data.io.r_nbytes); + else + memcpy(addr, memaddr, req->r_data.io.r_nbytes); + active_mmap_rw = 0; + + status->rval = req->r_data.io.r_nbytes; + status->err = 0; + return(status); +} + +char * +fmt_mmrw(struct io_req *req, struct syscall_info *sy, int fd, char *addr) +{ + static char errbuf[32768]; + char *cp; + struct fd_cache *fdc; + void *memaddr; + + fdc = alloc_fdcache(req->r_data.io.r_file, req->r_data.io.r_oflags); + + cp = errbuf; + cp += sprintf(cp, "syscall: %s(NULL, %d, %s, MAP_SHARED, %d, 0)\n", + sy->sy_name, + fdc->c_memlen, + (sy->sy_flags & SY_WRITE) ? "PROT_WRITE" : "PROT_READ", + fd); + + cp += sprintf(cp, "\tfile is mmaped to: 0x%lx\n", + (unsigned long) fdc->c_memaddr); + + memaddr = (void *)((char *)fdc->c_memaddr + req->r_data.io.r_offset); + + cp += sprintf(cp, "\tfile-mem=0x%lx, length=%d, buffer=0x%lx\n", + (unsigned long) memaddr, req->r_data.io.r_nbytes, + (unsigned long) addr); + + return(errbuf); +} +#endif /* !CRAY */ + +struct syscall_info syscalls[] = { +#ifdef CRAY + { "listio-read-sync", LREAD, + sy_listio, NULL, fmt_listio, + SY_IOSW + }, + { "listio-read-strides-sync", LSREAD, + sy_listio, listio_mem, fmt_listio, + SY_IOSW + }, + { "listio-read-reqs-sync", LEREAD, + sy_listio, listio_mem, fmt_listio, + SY_IOSW + }, + { "listio-read-async", LREADA, + sy_listio, NULL, fmt_listio, + SY_IOSW | SY_ASYNC + }, + { "listio-read-strides-async", LSREADA, + sy_listio, listio_mem, fmt_listio, + SY_IOSW | SY_ASYNC + }, + { "listio-read-reqs-async", LEREADA, + sy_listio, listio_mem, fmt_listio, + SY_IOSW | SY_ASYNC + }, + { "listio-write-sync", LWRITE, + sy_listio, listio_mem, fmt_listio, + SY_IOSW | SY_WRITE + }, + { "listio-write-strides-sync", LSWRITE, + sy_listio, listio_mem, fmt_listio, + SY_IOSW | SY_WRITE + }, + { "listio-write-reqs-sync", LEWRITE, + sy_listio, listio_mem, fmt_listio, + SY_IOSW | SY_WRITE + }, + { "listio-write-async", LWRITEA, + sy_listio, listio_mem, fmt_listio, + SY_IOSW | SY_WRITE | SY_ASYNC + }, + { "listio-write-strides-async", LSWRITEA, + sy_listio, listio_mem, fmt_listio, + SY_IOSW | SY_WRITE | SY_ASYNC + }, + { "listio-write-reqs-async", LEWRITEA, + sy_listio, listio_mem, fmt_listio, + SY_IOSW | SY_WRITE | SY_ASYNC + }, +#endif + +#ifdef sgi + { "aread", AREAD, + sy_aread, NULL, fmt_aread, + SY_IOSW | SY_ASYNC + }, + { "awrite", AWRITE, + sy_awrite, NULL, fmt_aread, + SY_IOSW | SY_WRITE | SY_ASYNC + }, +#endif + { "pread", PREAD, + sy_pread, NULL, fmt_pread, + 0 + }, + { "pwrite", PWRITE, + sy_pwrite, NULL, fmt_pread, + SY_WRITE + }, + +#ifndef CRAY + { "readv", READV, + sy_readv, NULL, fmt_readv, + 0 + }, + { "writev", WRITEV, + sy_writev, NULL, fmt_readv, + SY_WRITE + }, + { "mmap-read", MMAPR, + sy_mmread, NULL, fmt_mmrw, + 0 + }, + { "mmap-write", MMAPW, + sy_mmwrite, NULL, fmt_mmrw, + SY_WRITE + }, +#endif + + { NULL, 0, + 0, 0, 0, + 0 + }, +}; + +int +do_rw(req) + struct io_req *req; +{ + static int pid = -1; + int fd, offset, nbytes, nstrides, nents, oflags; + int rval, mem_needed, i; + int logged_write, got_lock, woffset = 0, pattern; + int min_byte, max_byte; + char *addr, *file, *msg; + struct status *s; + struct wlog_rec wrec; + struct syscall_info *sy; +#if defined(CRAY) || defined(sgi) + struct aio_info *aiop; + struct iosw *iosw; +#endif +#ifndef NO_XFS + struct fd_cache *fdc; +#endif + + /* + * Initialize common fields - assumes r_oflags, r_file, r_offset, and + * r_nbytes are at the same offset in the read_req and reada_req + * structures. + */ + file = req->r_data.io.r_file; + oflags = req->r_data.io.r_oflags; + offset = req->r_data.io.r_offset; + nbytes = req->r_data.io.r_nbytes; + nstrides= req->r_data.io.r_nstrides; + nents = req->r_data.io.r_nent; + pattern = req->r_data.io.r_pattern; + + if( nents >= MAX_AIO ) { + doio_fprintf(stderr, "do_rw: too many list requests, %d. Maximum is %d\n", + nents, MAX_AIO); + return(-1); + } + + /* + * look up system call info + */ + for(sy=syscalls; sy->sy_name != NULL && sy->sy_type != req->r_type; sy++) + ; + + if(sy->sy_name == NULL) { + doio_fprintf(stderr, "do_rw: unknown r_type %d.\n", + req->r_type); + return(-1); + } + + /* + * Get an open file descriptor + * Note: must be done before memory allocation so that the direct i/o + * information is available in mem. allocate + */ + + if ((fd = alloc_fd(file, oflags)) == -1) + return -1; + + /* + * Allocate core memory and possibly sds space. Initialize the + * data to be written. Make sure we get enough, based on the + * memstride. + * + * need: + * 1 extra word for possible partial-word address "bump" + * 1 extra word for dynamic pattern overrun + * MPP_BUMP extra words for T3E non-hw-aligned memory address. + */ + + if( sy->sy_buffer != NULL ) { + mem_needed = (*sy->sy_buffer)(req, 0, 0, NULL, NULL); + } else { + mem_needed = nbytes; + } + +#ifdef CRAY + if ((rval = alloc_mem(mem_needed + wtob(1) * 2 + MPP_BUMP * sizeof(UINT64_T))) < 0) { + return rval; + } +#else +#ifndef NO_XFS + /* get memory alignment for using DIRECT I/O */ + fdc = alloc_fdcache(file, oflags); + + if ((rval = alloc_mem(mem_needed + wtob(1) * 2 + fdc->c_memalign)) < 0) { + return rval; + } +#else + if ((rval = alloc_mem(mem_needed + wtob(1) * 2)) < 0) { + return rval; + } +#endif +#endif /* CRAY */ + + Pattern[0] = pattern; + + /* + * Allocate SDS space for backdoor write if desired + */ + + if (oflags & O_SSD) { +#ifdef CRAY +#ifndef _CRAYMPP + if (alloc_sds(nbytes) == -1) + return -1; + + if( sy->sy_flags & SY_WRITE ) { + /*pattern_fill(Memptr, mem_needed, Pattern, Pattern_Length, 0);*/ + (*Data_Fill)(Memptr, nbytes, Pattern, Pattern_Length, 0); + + if (sswrite((long)Memptr, Sdsptr, btoc(mem_needed)) == -1) { + doio_fprintf(stderr, "sswrite(%d, %d, %d) failed: %s (%d)\n", + (long)Memptr, Sdsptr, + btoc(mem_needed), SYSERR, errno); + fflush(stderr); + return -1; + } + } + + addr = (char *)Sdsptr; +#else + doio_fprintf(stderr, "Invalid O_SSD flag was generated for MPP system\n"); + fflush(stderr); + return -1; +#endif /* _CRAYMPP */ +#else /* CRAY */ + doio_fprintf(stderr, "Invalid O_SSD flag was generated for non-Cray system\n"); + fflush(stderr); + return -1; +#endif /* CRAY */ + } else { + addr = Memptr; + + /* + * if io is not raw, bump the offset by a random amount + * to generate non-word-aligned io. + * + * On MPP systems, raw I/O must start on an 0x80 byte boundary. + * For non-aligned I/O, bump the address from 1 to 8 words. + */ + + if (! (req->r_data.io.r_uflags & F_WORD_ALIGNED)) { +#ifdef _CRAYMPP + addr += random_range(0, MPP_BUMP, 1, NULL) * sizeof(int); +#endif + addr += random_range(0, wtob(1) - 1, 1, NULL); + } + +#ifndef NO_XFS + /* + * Force memory alignment for Direct I/O + */ + if( (oflags & O_DIRECT) && ((long)addr % fdc->c_memalign != 0) ) { + addr += fdc->c_memalign - ((long)addr % fdc->c_memalign); + } +#endif + + /* + * FILL must be done on a word-aligned buffer. + * Call the fill function with Memptr which is aligned, + * then memmove it to the right place. + */ + if (sy->sy_flags & SY_WRITE) { + (*Data_Fill)(Memptr, mem_needed, Pattern, Pattern_Length, 0); + if( addr != Memptr ) + memmove( addr, Memptr, mem_needed); + } + } + + rval = 0; + got_lock = 0; + logged_write = 0; + + /* + * Lock data if this is a write and locking option is set + */ + if (sy->sy_flags & SY_WRITE && k_opt) { + if( sy->sy_buffer != NULL ) { + (*sy->sy_buffer)(req, offset, 0, &min_byte, &max_byte); + } else { + min_byte = offset; + max_byte = offset + (nbytes * nstrides * nents); + } + + if (lock_file_region(file, fd, F_WRLCK, + min_byte, (max_byte-min_byte+1)) < 0) { + doio_fprintf(stderr, + "file lock failed:\n%s\n", + fmt_ioreq(req, sy, fd)); + doio_fprintf(stderr, + " buffer(req, %d, 0, 0x%x, 0x%x)\n", + offset, min_byte, max_byte); + alloc_mem(-1); + exit(E_INTERNAL); + } + + got_lock = 1; + } + + /* + * Write a preliminary write-log entry. This is done so that + * doio_check can do corruption detection across an interrupt/crash. + * Note that w_done is set to 0. If doio_check sees this, it + * re-creates the file extents as if the write completed, but does not + * do any checking - see comments in doio_check for more details. + */ + + if (sy->sy_flags & SY_WRITE && w_opt) { + if (pid == -1) { + pid = getpid(); + } + + wrec.w_async = (sy->sy_flags & SY_ASYNC) ? 1 : 0; + wrec.w_oflags = oflags; + wrec.w_pid = pid; + wrec.w_offset = offset; + wrec.w_nbytes = nbytes; /* mem_needed -- total length */ + + wrec.w_pathlen = strlen(file); + memcpy(wrec.w_path, file, wrec.w_pathlen); + wrec.w_hostlen = strlen(Host); + memcpy(wrec.w_host, Host, wrec.w_hostlen); + wrec.w_patternlen = Pattern_Length; + memcpy(wrec.w_pattern, Pattern, wrec.w_patternlen); + + wrec.w_done = 0; + + if ((woffset = wlog_record_write(&Wlog, &wrec, -1)) == -1) { + doio_fprintf(stderr, + "Could not append to write-log: %s (%d)\n", + SYSERR, errno); + } else { + logged_write = 1; + } + } + + s = (*sy->sy_syscall)(req, sy, fd, addr); + + if( s->rval == -1 ) { + doio_fprintf(stderr, + "%s() request failed: %s (%d)\n%s\n%s\n", + sy->sy_name, SYSERR, errno, + fmt_ioreq(req, sy, fd), + (*sy->sy_format)(req, sy, fd, addr)); + + doio_upanic(U_RVAL); + + for(i=0; i < nents; i++) { + if(s->aioid == NULL) + break; + aio_unregister(s->aioid[i]); + } + rval = -1; + } else { + /* + * If the syscall was async, wait for I/O to complete + */ +#ifndef linux + if(sy->sy_flags & SY_ASYNC) { + for(i=0; i < nents; i++) { + aio_wait(s->aioid[i]); + } + } +#endif + + /* + * Check the syscall how-much-data-written return. Look + * for this in either the return value or the 'iosw' + * structure. + */ + + if( sy->sy_flags & SY_IOSW ) { +#ifdef CRAY + for( i=0; i < nents; i++ ) { + if(s->aioid == NULL) + break; /* >>> error condition? */ + aiop = aio_slot(s->aioid[i]); + iosw = &aiop->iosw; + if(iosw->sw_error != 0) { + doio_fprintf(stderr, + "%s() iosw error set: %s\n%s\n%s\n", + sy->sy_name, + strerror(iosw->sw_error), + fmt_ioreq(req, sy, fd), + (*sy->sy_format)(req, sy, fd, addr)); + doio_upanic(U_IOSW); + rval = -1; + } else if(iosw->sw_count != nbytes*nstrides) { + doio_fprintf(stderr, + "Bad iosw from %s() #%d\nExpected (%d,%d,%d), got (%d,%d,%d)\n%s\n%s\n", + sy->sy_name, i, + 1, 0, nbytes*nstrides, + iosw->sw_flag, + iosw->sw_error, + iosw->sw_count, + fmt_ioreq(req, sy, fd), + (*sy->sy_format)(req, sy, fd, addr)); + doio_upanic(U_IOSW); + rval = -1; + } + + aio_unregister(s->aioid[i]); + } +#endif /* CRAY */ +#ifdef sgi + for( i=0; s->aioid[i] != -1; i++ ) { + if(s->aioid == NULL) { + doio_fprintf(stderr, + "aioid == NULL!\n"); + break; + } + aiop = aio_slot(s->aioid[i]); + + /* + * make sure the io completed without error + */ + if (aiop->aio_errno != 0) { + doio_fprintf(stderr, + "%s() aio error set: %s (%d)\n%s\n%s\n", + sy->sy_name, + strerror(aiop->aio_errno), + aiop->aio_errno, + fmt_ioreq(req, sy, fd), + (*sy->sy_format)(req, sy, fd, addr)); + doio_upanic(U_IOSW); + rval = -1; + } else if (aiop->aio_ret != nbytes) { + doio_fprintf(stderr, + "Bad aio return from %s() #%d\nExpected (%d,%d), got (%d,%d)\n%s\n%s\n", + sy->sy_name, i, + 0, nbytes, + aiop->aio_errno, + aiop->aio_ret, + fmt_ioreq(req, sy, fd), + (*sy->sy_format)(req, sy, fd, addr)); + aio_unregister(s->aioid[i]); + doio_upanic(U_IOSW); + return -1; + } else { + aio_unregister(s->aioid[i]); + rval = 0; + } + } +#endif /* sgi */ + } else { + + if(s->rval != mem_needed) { + doio_fprintf(stderr, + "%s() request returned wrong # of bytes - expected %d, got %d\n%s\n%s\n", + sy->sy_name, nbytes, s->rval, + fmt_ioreq(req, sy, fd), + (*sy->sy_format)(req, sy, fd, addr)); + rval = -1; + doio_upanic(U_RVAL); + } + } + } + + + /* + * Verify that the data was written correctly - check_file() returns + * a non-null pointer which contains an error message if there are + * problems. + */ + + if ( rval == 0 && sy->sy_flags & SY_WRITE && v_opt) { + msg = check_file(file, offset, nbytes*nstrides*nents, + Pattern, Pattern_Length, 0, + oflags & O_PARALLEL); + if (msg != NULL) { + doio_fprintf(stderr, "%s\n%s\n%s\n", + msg, + fmt_ioreq(req, sy, fd), + (*sy->sy_format)(req, sy, fd, addr)); + doio_upanic(U_CORRUPTION); + exit(E_COMPARE); + } + } + + /* + * General cleanup ... + * + * Write extent information to the write-log, so that doio_check can do + * corruption detection. Note that w_done is set to 1, indicating that + * the write has been verified as complete. We don't need to write the + * filename on the second logging. + */ + + if (w_opt && logged_write) { + wrec.w_done = 1; + wlog_record_write(&Wlog, &wrec, woffset); + } + + /* + * Unlock file region if necessary + */ + + if (got_lock) { + if (lock_file_region(file, fd, F_UNLCK, + min_byte, (max_byte-min_byte+1)) < 0) { + alloc_mem(-1); + exit(E_INTERNAL); + } + } + + if(s->aioid != NULL) + free(s->aioid); + free(s); + return (rval == -1) ? -1 : 0; +} + + +/* + * xfsctl-based requests + * - XFS_IOC_RESVSP + * - XFS_IOC_UNRESVSP + */ +#ifndef NO_XFS +int +do_xfsctl(req) + struct io_req *req; +{ + int fd, oflags, offset, nbytes; + int rval, op = 0; + int got_lock; + int min_byte = 0, max_byte = 0; + char *file, *msg = NULL; + struct xfs_flock64 flk; + + /* + * Initialize common fields - assumes r_oflags, r_file, r_offset, and + * r_nbytes are at the same offset in the read_req and reada_req + * structures. + */ + file = req->r_data.io.r_file; + oflags = req->r_data.io.r_oflags; + offset = req->r_data.io.r_offset; + nbytes = req->r_data.io.r_nbytes; + + flk.l_type=0; + flk.l_whence=SEEK_SET; + flk.l_start=offset; + flk.l_len=nbytes; + + /* + * Get an open file descriptor + */ + + if ((fd = alloc_fd(file, oflags)) == -1) + return -1; + + rval = 0; + got_lock = 0; + + /* + * Lock data if this is locking option is set + */ + if (k_opt) { + min_byte = offset; + max_byte = offset + nbytes; + + if (lock_file_region(file, fd, F_WRLCK, + min_byte, (nbytes+1)) < 0) { + doio_fprintf(stderr, + "file lock failed:\n"); + doio_fprintf(stderr, + " buffer(req, %d, 0, 0x%x, 0x%x)\n", + offset, min_byte, max_byte); + alloc_mem(-1); + exit(E_INTERNAL); + } + + got_lock = 1; + } + + switch (req->r_type) { + case RESVSP: op=XFS_IOC_RESVSP; msg="resvsp"; break; + case UNRESVSP: op=XFS_IOC_UNRESVSP; msg="unresvsp"; break; + } + + rval = xfsctl(file, fd, op, &flk); + + if( rval == -1 ) { + doio_fprintf(stderr, +"xfsctl %s request failed: %s (%d)\n\txfsctl(%d, %s %d, {%d %lld ==> %lld}\n", + msg, SYSERR, errno, + fd, msg, op, flk.l_whence, + (long long)flk.l_start, + (long long)flk.l_len); + + doio_upanic(U_RVAL); + rval = -1; + } + + /* + * Unlock file region if necessary + */ + + if (got_lock) { + if (lock_file_region(file, fd, F_UNLCK, + min_byte, (max_byte-min_byte+1)) < 0) { + alloc_mem(-1); + exit(E_INTERNAL); + } + } + + return (rval == -1) ? -1 : 0; +} +#endif + +/* + * fsync(2) and fdatasync(2) + */ +#ifndef CRAY +int +do_sync(req) + struct io_req *req; +{ + int fd, oflags; + int rval; + char *file; + + /* + * Initialize common fields - assumes r_oflags, r_file, r_offset, and + * r_nbytes are at the same offset in the read_req and reada_req + * structures. + */ + file = req->r_data.io.r_file; + oflags = req->r_data.io.r_oflags; + + /* + * Get an open file descriptor + */ + + if ((fd = alloc_fd(file, oflags)) == -1) + return -1; + + rval = 0; + switch(req->r_type) { + case FSYNC2: + rval = fsync(fd); + break; + case FDATASYNC: + rval = fdatasync(fd); + break; + default: + rval = -1; + } + return (rval == -1) ? -1 : 0; +} +#endif + + +int +doio_pat_fill(char *addr, int mem_needed, char *Pattern, int Pattern_Length, + int shift) +{ + return pattern_fill(addr, mem_needed, Pattern, Pattern_Length, 0); +} + +char * +doio_pat_check(buf, offset, length, pattern, pattern_length, patshift) +char *buf; +int offset; +int length; +char *pattern; +int pattern_length; +int patshift; +{ + static char errbuf[4096]; + int nb, i, pattern_index; + char *cp, *bufend, *ep; + char actual[33], expected[33]; + + if (pattern_check(buf, length, pattern, pattern_length, patshift) != 0) { + ep = errbuf; + ep += sprintf(ep, "Corrupt regions follow - unprintable chars are represented as '.'\n"); + ep += sprintf(ep, "-----------------------------------------------------------------\n"); + + pattern_index = patshift % pattern_length;; + cp = buf; + bufend = buf + length; + + while (cp < bufend) { + if (*cp != pattern[pattern_index]) { + nb = bufend - cp; + if (nb > sizeof(expected)-1) { + nb = sizeof(expected)-1; + } + + ep += sprintf(ep, "corrupt bytes starting at file offset %d\n", offset + (int)(cp-buf)); + + /* + * Fill in the expected and actual patterns + */ + bzero(expected, sizeof(expected)); + bzero(actual, sizeof(actual)); + + for (i = 0; i < nb; i++) { + expected[i] = pattern[(pattern_index + i) % pattern_length]; + if (! isprint(expected[i])) { + expected[i] = '.'; + } + + actual[i] = cp[i]; + if (! isprint(actual[i])) { + actual[i] = '.'; + } + } + + ep += sprintf(ep, " 1st %2d expected bytes: %s\n", nb, expected); + ep += sprintf(ep, " 1st %2d actual bytes: %s\n", nb, actual); + fflush(stderr); + return errbuf; + } else { + cp++; + pattern_index++; + + if (pattern_index == pattern_length) { + pattern_index = 0; + } + } + } + return errbuf; + } + + return(NULL); +} + + +/* + * Check the contents of a file beginning at offset, for length bytes. It + * is assumed that there is a string of pattern bytes in this area of the + * file. Use normal buffered reads to do the verification. + * + * If there is a data mismatch, write a detailed message into a static buffer + * suitable for the caller to print. Otherwise print NULL. + * + * The fsa flag is set to non-zero if the buffer should be read back through + * the FSA (unicos/mk). This implies the file will be opened + * O_PARALLEL|O_RAW|O_WELLFORMED to do the validation. We must do this because + * FSA will not allow the file to be opened for buffered io if it was + * previously opened for O_PARALLEL io. + */ + +char * +check_file(file, offset, length, pattern, pattern_length, patshift, fsa) +char *file; +int offset; +int length; +char *pattern; +int pattern_length; +int patshift; +int fsa; +{ + static char errbuf[4096]; + int fd, nb, flags; + char *buf, *em, *ep; +#ifndef NO_XFS + struct fd_cache *fdc; +#endif + + buf = Memptr; + + if (V_opt) { + flags = Validation_Flags | O_RDONLY; + } else { + flags = O_RDONLY; + if (fsa) { +#ifdef CRAY + flags |= O_PARALLEL | O_RAW | O_WELLFORMED; +#endif + } + } + + if ((fd = alloc_fd(file, flags)) == -1) { + sprintf(errbuf, + "Could not open file %s with flags %#o (%s) for data comparison: %s (%d)\n", + file, flags, format_oflags(flags), + SYSERR, errno); + return errbuf; + } + + if (lseek(fd, offset, SEEK_SET) == -1) { + sprintf(errbuf, + "Could not lseek to offset %d in %s for verification: %s (%d)\n", + offset, file, SYSERR, errno); + return errbuf; + } + +#ifndef NO_XFS + /* Guarantee a properly aligned address on Direct I/O */ + fdc = alloc_fdcache(file, flags); + if( (flags & O_DIRECT) && ((long)buf % fdc->c_memalign != 0) ) { + buf += fdc->c_memalign - ((long)buf % fdc->c_memalign); + } +#endif + + if ((nb = read(fd, buf, length)) == -1) { +#ifndef NO_XFS + sprintf(errbuf, + "Could not read %d bytes from %s for verification: %s (%d)\n\tread(%d, 0x%p, %d)\n\tbuf %% alignment(%d) = %ld\n", + length, file, SYSERR, errno, + fd, buf, length, + fdc->c_memalign, (long)buf % fdc->c_memalign); +#else + sprintf(errbuf, + "Could not read %d bytes from %s for verification: %s (%d)\n", + length, file, SYSERR, errno); + +#endif + return errbuf; + } + + if (nb != length) { + sprintf(errbuf, + "Read wrong # bytes from %s. Expected %d, got %d\n", + file, length, nb); + return errbuf; + } + + if( (em = (*Data_Check)(buf, offset, length, pattern, pattern_length, patshift)) != NULL ) { + ep = errbuf; + ep += sprintf(ep, "*** DATA COMPARISON ERROR ***\n"); + ep += sprintf(ep, "check_file(%s, %d, %d, %s, %d, %d) failed\n\n", + file, offset, length, pattern, pattern_length, patshift); + ep += sprintf(ep, "Comparison fd is %d, with open flags %#o\n", + fd, flags); + strcpy(ep, em); + return(errbuf); + } + return NULL; +} + +/* + * Function to single-thread stdio output. + */ + +int +doio_fprintf(FILE *stream, char *format, ...) +{ + static int pid = -1; + char *date; + int rval; + struct flock flk; + va_list arglist; + + date = hms(time(0)); + + if (pid == -1) { + pid = getpid(); + } + + flk.l_whence = flk.l_start = flk.l_len = 0; + flk.l_type = F_WRLCK; + fcntl(fileno(stream), F_SETLKW, &flk); + + va_start(arglist, format); + rval = fprintf(stream, "\n%s%s (%5d) %s\n", Prog, TagName, pid, date); + rval += fprintf(stream, "---------------------\n"); + vfprintf(stream, format, arglist); + va_end(arglist); + + fflush(stream); + + flk.l_type = F_UNLCK; + fcntl(fileno(stream), F_SETLKW, &flk); + + return rval; +} + +/* + * Simple function for allocating core memory. Uses Memsize and Memptr to + * keep track of the current amount allocated. + */ +#ifndef CRAY +int +alloc_mem(nbytes) +int nbytes; +{ + char *cp; + void *addr; + int me = 0, flags, key, shmid; + static int mturn = 0; /* which memory type to use */ + struct memalloc *M; + char filename[255]; +#ifdef linux + struct shmid_ds shm_ds; +#endif + +#ifdef linux + bzero( &shm_ds, sizeof(struct shmid_ds) ); +#endif + + /* nbytes = -1 means "free all allocated memory" */ + if( nbytes == -1 ) { + + for(me=0; me < Nmemalloc; me++) { + if(Memalloc[me].space == NULL) + continue; + + switch(Memalloc[me].memtype) { + case MEM_DATA: +#ifdef sgi + if(Memalloc[me].flags & MEMF_MPIN) + munpin(Memalloc[me].space, + Memalloc[me].size); +#endif + free(Memalloc[me].space); + Memalloc[me].space = NULL; + Memptr = NULL; + Memsize = 0; + break; + case MEM_SHMEM: +#ifdef sgi + if(Memalloc[me].flags & MEMF_MPIN) + munpin(Memalloc[me].space, + Memalloc[me].size); +#endif + shmdt(Memalloc[me].space); + Memalloc[me].space = NULL; +#ifdef sgi + shmctl(Memalloc[me].fd, IPC_RMID); +#else + shmctl(Memalloc[me].fd, IPC_RMID, &shm_ds); +#endif + break; + case MEM_MMAP: +#ifdef sgi + if(Memalloc[me].flags & MEMF_MPIN) + munpin(Memalloc[me].space, + Memalloc[me].size); +#endif + munmap(Memalloc[me].space, + Memalloc[me].size); + close(Memalloc[me].fd); + if(Memalloc[me].flags & MEMF_FILE) { + unlink(Memalloc[me].name); + } + Memalloc[me].space = NULL; + break; + default: + doio_fprintf(stderr, "alloc_mem: HELP! Unknown memory space type %d index %d\n", + Memalloc[me].memtype, me); + break; + } + } + return 0; + } + + /* + * Select a memory area (currently round-robbin) + */ + + if(mturn >= Nmemalloc) + mturn=0; + + M = &Memalloc[mturn]; + + switch(M->memtype) { + case MEM_DATA: + if( nbytes > M->size ) { + if( M->space != NULL ){ +#ifdef sgi + if( M->flags & MEMF_MPIN ) + munpin( M->space, M->size ); +#endif + free(M->space); + } + M->space = NULL; + M->size = 0; + } + + if( M->space == NULL ) { + if( (cp = malloc( nbytes )) == NULL ) { + doio_fprintf(stderr, "malloc(%d) failed: %s (%d)\n", + nbytes, SYSERR, errno); + return -1; + } +#ifdef sgi + if(M->flags & MEMF_MPIN) { + if( mpin(cp, nbytes) == -1 ) { + doio_fprintf(stderr, "mpin(0x%lx, %d) failed: %s (%d)\n", + cp, nbytes, SYSERR, errno); + } + } +#endif + M->space = (void *)cp; + M->size = nbytes; + } + break; + + case MEM_MMAP: + if( nbytes > M->size ) { + if( M->space != NULL ) { +#ifdef sgi + if( M->flags & MEMF_MPIN ) + munpin(M->space, M->size); +#endif + munmap(M->space, M->size); + close(M->fd); + if( M->flags & MEMF_FILE ) + unlink( M->name ); + } + M->space = NULL; + M->size = 0; + } + + if( M->space == NULL ) { + if(strchr(M->name, '%')) { + sprintf(filename, M->name, getpid()); + M->name = strdup(filename); + } + + if( (M->fd = open(M->name, O_CREAT|O_RDWR, 0666)) == -1) { + doio_fprintf(stderr, "alloc_mmap: error %d (%s) opening '%s'\n", + errno, SYSERR, + M->name); + return(-1); + } + + addr = NULL; + flags = 0; + M->size = nbytes * 4; + + /* bias addr if MEMF_ADDR | MEMF_FIXADDR */ + /* >>> how to pick a memory address? */ + + /* bias flags on MEMF_PRIVATE etc */ + if(M->flags & MEMF_PRIVATE) + flags |= MAP_PRIVATE; +#ifdef sgi + if(M->flags & MEMF_LOCAL) + flags |= MAP_LOCAL; + if(M->flags & MEMF_AUTORESRV) + flags |= MAP_AUTORESRV; + if(M->flags & MEMF_AUTOGROW) + flags |= MAP_AUTOGROW; +#endif + if(M->flags & MEMF_SHARED) + flags |= MAP_SHARED; + +/*printf("alloc_mem, about to mmap, fd=%d, name=(%s)\n", M->fd, M->name);*/ + if( (M->space = mmap(addr, M->size, + PROT_READ|PROT_WRITE, + flags, M->fd, 0)) + == MAP_FAILED) { + doio_fprintf(stderr, "alloc_mem: mmap error. errno %d (%s)\n\tmmap(addr 0x%x, size %d, read|write 0x%x, mmap flags 0x%x [%#o], fd %d, 0)\n\tfile %s\n", + errno, SYSERR, + addr, M->size, + PROT_READ|PROT_WRITE, + flags, M->flags, M->fd, + M->name); + doio_fprintf(stderr, "\t%s%s%s%s%s", + (flags & MAP_PRIVATE) ? "private " : "", +#ifdef sgi + (flags & MAP_LOCAL) ? "local " : "", + (flags & MAP_AUTORESRV) ? "autoresrv " : "", + (flags & MAP_AUTOGROW) ? "autogrow " : "", +#endif + (flags & MAP_SHARED) ? "shared" : ""); + return(-1); + } + } + break; + + case MEM_SHMEM: + if( nbytes > M->size ) { + if( M->space != NULL ) { +#ifdef sgi + if( M->flags & MEMF_MPIN ) + munpin(M->space, M->size); +#endif + shmdt( M->space ); +#ifdef sgi + shmctl( M->fd, IPC_RMID ); +#else + shmctl( M->fd, IPC_RMID, &shm_ds ); +#endif + } + M->space = NULL; + M->size = 0; + } + + if(M->space == NULL) { + if(!strcmp(M->name, "private")) { + key = IPC_PRIVATE; + } else { + sscanf(M->name, "%i", &key); + } + + M->size = M->nblks ? M->nblks * 512 : nbytes; + + if( nbytes > M->size ){ +#ifdef DEBUG + doio_fprintf(stderr, "MEM_SHMEM: nblks(%d) too small: nbytes=%d Msize=%d, skipping this req.\n", + M->nblks, nbytes, M->size ); +#endif + return SKIP_REQ; + } + + shmid = shmget(key, M->size, IPC_CREAT|0666); + if( shmid == -1 ) { + doio_fprintf(stderr, "shmget(0x%x, %d, CREAT) failed: %s (%d)\n", + key, M->size, SYSERR, errno); + return(-1); + } + M->fd = shmid; + M->space = shmat(shmid, NULL, SHM_RND); + if( M->space == (void *)-1 ) { + doio_fprintf(stderr, "shmat(0x%x, NULL, SHM_RND) failed: %s (%d)\n", + shmid, SYSERR, errno); + return(-1); + } +#ifdef sgi + if(M->flags & MEMF_MPIN) { + if( mpin(M->space, M->size) == -1 ) { + doio_fprintf(stderr, "mpin(0x%lx, %d) failed: %s (%d)\n", + M->space, M->size, SYSERR, errno); + } + } +#endif + } + break; + + default: + doio_fprintf(stderr, "alloc_mem: HELP! Unknown memory space type %d index %d\n", + Memalloc[me].memtype, mturn); + break; + } + + Memptr = M->space; + Memsize = M->size; + + mturn++; + return 0; +} +#endif /* !CRAY */ + +#ifdef CRAY +int +alloc_mem(nbytes) +int nbytes; +{ + char *cp; + int ip; + static char *malloc_space; + + /* + * The "unicos" version of this did some stuff with sbrk; + * this caused problems with async I/O on irix, and now appears + * to be causing problems with FSA I/O on unicos/mk. + */ +#ifdef NOTDEF + if (nbytes > Memsize) { + if ((cp = (char *)sbrk(nbytes - Memsize)) == (char *)-1) { + doio_fprintf(stderr, "sbrk(%d) failed: %s (%d)\n", + nbytes - Memsize, SYSERR, errno); + return -1; + } + + if (Memsize == 0) + Memptr = cp; + Memsize += nbytes - Memsize; + } +#else + + /* nbytes = -1 means "free all allocated memory" */ + if( nbytes == -1 ) { + free( malloc_space ); + Memptr = NULL; + Memsize = 0; + return 0; + } + + if( nbytes > Memsize ) { + if( Memsize != 0 ) + free( malloc_space ); + + if( (cp = malloc_space = malloc( nbytes )) == NULL ) { + doio_fprintf(stderr, "malloc(%d) failed: %s (%d)\n", + nbytes, SYSERR, errno); + return -1; + } + +#ifdef _CRAYT3E + /* T3E requires memory to be aligned on 0x40 word boundaries */ + ip = (int)cp; + if( ip & 0x3F != 0 ) { + doio_fprintf(stderr, "malloc(%d) = 0x%x(0x%x) not aligned by 0x%x\n", + nbytes, cp, ip, ip & 0x3f); + + free(cp); + if( (cp = malloc_space = malloc( nbytes + 0x40 )) == NULL ) { + doio_fprintf(stderr, "malloc(%d) failed: %s (%d)\n", + nbytes, SYSERR, errno); + return -1; + } + ip = (int)cp; + cp += (0x40 - (ip & 0x3F)); + } +#endif /* _CRAYT3E */ + Memptr = cp; + Memsize = nbytes; + } +#endif /* NOTDEF */ + return 0; +} +#endif /* CRAY */ + +/* + * Simple function for allocating sds space. Uses Sdssize and Sdsptr to + * keep track of location and size of currently allocated chunk. + */ + +#ifdef _CRAY1 + +int +alloc_sds(nbytes) +int nbytes; +{ + int nblks; + + if (nbytes > Sdssize) { + if ((nblks = ssbreak(btoc(nbytes - Sdssize))) == -1) { + doio_fprintf(stderr, "ssbreak(%d) failed: %s (%d)\n", + btoc(nbytes - Sdssize), SYSERR, errno); + return -1; + } + + Sdssize = ctob(nblks); + Sdsptr = 0; + } + + return 0; +} + +#else + +#ifdef CRAY + +int +alloc_sds(nbytes) +int nbytes; +{ + doio_fprintf(stderr, + "Internal Error - alloc_sds() called on a CRAY2 system\n"); + alloc_mem(-1); + exit(E_INTERNAL); +} + +#endif + +#endif /* _CRAY1 */ + +/* + * Function to maintain a file descriptor cache, so that doio does not have + * to do so many open() and close() calls. Descriptors are stored in the + * cache by file name, and open flags. Each entry also has a _rtc value + * associated with it which is used in aging. If doio cannot open a file + * because it already has too many open (ie. system limit hit) it will close + * the one in the cache that has the oldest _rtc value. + * + * If alloc_fd() is called with a file of NULL, it will close all descriptors + * in the cache, and free the memory in the cache. + */ + +int +alloc_fd(file, oflags) +char *file; +int oflags; +{ + struct fd_cache *fdc; + struct fd_cache *alloc_fdcache(char *file, int oflags); + + fdc = alloc_fdcache(file, oflags); + if(fdc != NULL) + return(fdc->c_fd); + else + return(-1); +} + +struct fd_cache * +alloc_fdcache(file, oflags) +char *file; +int oflags; +{ + int fd; + struct fd_cache *free_slot, *oldest_slot, *cp; + static int cache_size = 0; + static struct fd_cache *cache = NULL; +#ifndef NO_XFS + struct dioattr finfo; +#endif + + /* + * If file is NULL, it means to free up the fd cache. + */ + + if (file == NULL && cache != NULL) { + for (cp = cache; cp < &cache[cache_size]; cp++) { + if (cp->c_fd != -1) { + close(cp->c_fd); + } +#ifndef CRAY + if (cp->c_memaddr != NULL) { + munmap(cp->c_memaddr, cp->c_memlen); + } +#endif + } + + free(cache); + cache = NULL; + cache_size = 0; + return 0; + } + + free_slot = NULL; + oldest_slot = NULL; + + /* + * Look for a fd in the cache. If one is found, return it directly. + * Otherwise, when this loop exits, oldest_slot will point to the + * oldest fd slot in the cache, and free_slot will point to an + * unoccupied slot if there are any. + */ + + for (cp = cache; cp != NULL && cp < &cache[cache_size]; cp++) { + if (cp->c_fd != -1 && + cp->c_oflags == oflags && + strcmp(cp->c_file, file) == 0) { +#ifdef CRAY + cp->c_rtc = _rtc(); +#else + cp->c_rtc = Reqno; +#endif + return cp; + } + + if (cp->c_fd == -1) { + if (free_slot == NULL) { + free_slot = cp; + } + } else { + if (oldest_slot == NULL || + cp->c_rtc < oldest_slot->c_rtc) { + oldest_slot = cp; + } + } + } + + /* + * No matching file/oflags pair was found in the cache. Attempt to + * open a new fd. + */ + + if ((fd = open(file, oflags, 0666)) < 0) { + if (errno != EMFILE) { + doio_fprintf(stderr, + "Could not open file %s with flags %#o (%s): %s (%d)\n", + file, oflags, format_oflags(oflags), + SYSERR, errno); + alloc_mem(-1); + exit(E_SETUP); + } + + /* + * If we get here, we have as many open fd's as we can have. + * Close the oldest one in the cache (pointed to by + * oldest_slot), and attempt to re-open. + */ + + close(oldest_slot->c_fd); + oldest_slot->c_fd = -1; + free_slot = oldest_slot; + + if ((fd = open(file, oflags, 0666)) < 0) { + doio_fprintf(stderr, + "Could not open file %s with flags %#o (%s): %s (%d)\n", + file, oflags, format_oflags(oflags), + SYSERR, errno); + alloc_mem(-1); + exit(E_SETUP); + } + } + +/*printf("alloc_fd: new file %s flags %#o fd %d\n", file, oflags, fd);*/ + + /* + * If we get here, fd is our open descriptor. If free_slot is NULL, + * we need to grow the cache, otherwise free_slot is the slot that + * should hold the fd info. + */ + + if (free_slot == NULL) { + cache = (struct fd_cache *)realloc(cache, sizeof(struct fd_cache) * (FD_ALLOC_INCR + cache_size)); + if (cache == NULL) { + doio_fprintf(stderr, "Could not malloc() space for fd chace"); + alloc_mem(-1); + exit(E_SETUP); + } + + cache_size += FD_ALLOC_INCR; + + for (cp = &cache[cache_size-FD_ALLOC_INCR]; + cp < &cache[cache_size]; cp++) { + cp->c_fd = -1; + } + + free_slot = &cache[cache_size - FD_ALLOC_INCR]; + } + + /* + * finally, fill in the cache slot info + */ + + free_slot->c_fd = fd; + free_slot->c_oflags = oflags; + strcpy(free_slot->c_file, file); +#ifdef CRAY + free_slot->c_rtc = _rtc(); +#else + free_slot->c_rtc = Reqno; +#endif + +#ifndef NO_XFS + if (oflags & O_DIRECT) { + if (xfsctl(file, fd, XFS_IOC_DIOINFO, &finfo) == -1) { + finfo.d_mem = 1; + finfo.d_miniosz = 1; + finfo.d_maxiosz = 1; + } + } else { + finfo.d_mem = 1; + finfo.d_miniosz = 1; + finfo.d_maxiosz = 1; + } + + free_slot->c_memalign = finfo.d_mem; + free_slot->c_miniosz = finfo.d_miniosz; + free_slot->c_maxiosz = finfo.d_maxiosz; +#endif +#ifndef CRAY + free_slot->c_memaddr = NULL; + free_slot->c_memlen = 0; +#endif + + return free_slot; +} + +/* + * + * Signal Handling Section + * + * + */ + +#ifdef sgi +/* + * "caller-id" for signals + */ +void +signal_info(int sig, siginfo_t *info, void *v) +{ + int haveit = 0; + + if(info != NULL) { + switch(info->si_code) { + case SI_USER: + doio_fprintf(stderr, + "signal_info: si_signo %d si_errno %d si_code SI_USER pid %d uid %d\n", + info->si_signo, info->si_errno, + info->si_pid, info->si_uid); + haveit = 1; + break; + + case SI_QUEUE: + doio_fprintf(stderr, "signal_info si_signo %d si_code = SI_QUEUE\n", + info->si_signo); + haveit = 1; + break; + } + + if( ! haveit ){ + if( (info->si_signo == SIGSEGV) || + (info->si_signo == SIGBUS) ){ + doio_fprintf(stderr, "signal_info si_signo %d si_errno %d si_code = %d si_addr=%p active_mmap_rw=%d havesigint=%d\n", + info->si_signo, info->si_errno, + info->si_code, info->si_addr, + active_mmap_rw, + havesigint); + haveit = 1; + } + } + + if( !haveit ){ + doio_fprintf(stderr, "signal_info: si_signo %d si_errno %d unknown code %d\n", + info->si_signo, info->si_errno, + info->si_code); + } + } else { + doio_fprintf(stderr, "signal_info: sig %d\n", sig); + } +} +#endif + +#ifdef sgi +void +cleanup_handler(int sig, siginfo_t *info, void *v) +{ + havesigint=1; /* in case there's a followup signal */ + /*signal_info(sig, info, v);*/ /* be quiet on "normal" kill */ + alloc_mem(-1); + exit(0); +} + + +void +die_handler(int sig, siginfo_t *info, void *v) +{ + doio_fprintf(stderr, "terminating on signal %d\n", sig); + signal_info(sig, info, v); + alloc_mem(-1); + exit(1); +} + +void +sigbus_handler(int sig, siginfo_t *info, void *v) +{ + /* While we are doing a memcpy to/from an mmapped region we can + get a SIGBUS for a variety of reasons--and not all of them + should be considered failures. + + Under normal conditions if we get a SIGINT it means we've been + told to shutdown. However, if we're currently doing the above- + mentioned memcopy then the kernel will follow that SIGINT with + a SIGBUS. We can guess that we're in this situation by seeing + that the si_errno field in the siginfo structure has EINTR as + an errno. (We might make the guess stronger by looking at the + si_addr field to see that it's not faulting off the end of the + mmapped region, but it seems that in such a case havesigint + would not have been set so maybe that doesn't make the guess + stronger.) + */ + + + if( active_mmap_rw && havesigint && (info->si_errno == EINTR) ){ + cleanup_handler( sig, info, v ); + } + else{ + die_handler( sig, info, v ); + } +} +#else + +void +cleanup_handler() +{ + havesigint=1; /* in case there's a followup signal */ + alloc_mem(-1); + exit(0); +} + +void +die_handler(sig) +int sig; +{ + doio_fprintf(stderr, "terminating on signal %d\n", sig); + alloc_mem(-1); + exit(1); +} + +#ifndef CRAY +void +sigbus_handler(sig) +int sig; +{ + /* See sigbus_handler() in the 'ifdef sgi' case for details. Here, + we don't have the siginfo stuff so the guess is weaker but we'll + do it anyway. + */ + + if( active_mmap_rw && havesigint ) + cleanup_handler(); + else + die_handler(sig); +} +#endif /* !CRAY */ +#endif /* sgi */ + + +void +noop_handler(sig) +int sig; +{ + return; +} + + +/* + * SIGINT handler for the parent (original doio) process. It simply sends + * a SIGINT to all of the doio children. Since they're all in the same + * pgrp, this can be done with a single kill(). + */ + +void +sigint_handler() +{ + int i; + + for (i = 0; i < Nchildren; i++) { + if (Children[i] != -1) { + kill(Children[i], SIGINT); + } + } +} + +/* + * Signal handler used to inform a process when async io completes. Referenced + * in do_read() and do_write(). Note that the signal handler is not + * re-registered. + */ + +void +aio_handler(sig) +int sig; +{ + int i; + struct aio_info *aiop; + + for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) { + aiop = &Aio_Info[i]; + + if (aiop->strategy == A_SIGNAL && aiop->sig == sig) { + aiop->signalled++; + + if (aio_done(aiop)) { + aiop->done++; + } + } + } +} + +/* + * dump info on all open aio slots + */ +void +dump_aio() +{ + int i, count; + + count=0; + for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) { + if (Aio_Info[i].busy) { + count++; + fprintf(stderr, + "Aio_Info[%03d] id=%d fd=%d signal=%d signaled=%d\n", + i, Aio_Info[i].id, + Aio_Info[i].fd, + Aio_Info[i].sig, + Aio_Info[i].signalled); + fprintf(stderr, "\tstrategy=%s\n", + format_strat(Aio_Info[i].strategy)); + } + } + fprintf(stderr, "%d active async i/os\n", count); +} + + +#ifdef sgi +/* + * Signal handler called as a callback, not as a signal. + * 'val' is the value from sigev_value and is assumed to be the + * Aio_Info[] index. + */ +void +cb_handler(val) +sigval_t val; +{ + struct aio_info *aiop; + +/*printf("cb_handler requesting slot %d\n", val.sival_int);*/ + aiop = aio_slot( val.sival_int ); +/*printf("cb_handler, aiop=%p\n", aiop);*/ + +/*printf("%d in cb_handler\n", getpid() );*/ + if (aiop->strategy == A_CALLBACK) { + aiop->signalled++; + + if (aio_done(aiop)) { + aiop->done++; + } + } +} +#endif + +struct aio_info * +aio_slot(aio_id) +int aio_id; +{ + int i; + static int id = 1; + struct aio_info *aiop; + + aiop = NULL; + + for (i = 0; i < sizeof(Aio_Info) / sizeof(Aio_Info[0]); i++) { + if (aio_id == -1) { + if (! Aio_Info[i].busy) { + aiop = &Aio_Info[i]; + aiop->busy = 1; + aiop->id = id++; + break; + } + } else { + if (Aio_Info[i].busy && Aio_Info[i].id == aio_id) { + aiop = &Aio_Info[i]; + break; + } + } + } + + if( aiop == NULL ){ + doio_fprintf(stderr,"aio_slot(%d) not found. Request %d\n", + aio_id, Reqno); + dump_aio(); + alloc_mem(-1); + exit(E_INTERNAL); + } + + return aiop; +} + +int +aio_register(fd, strategy, sig) +int fd; +int strategy; +int sig; +{ + struct aio_info *aiop; + void aio_handler(); + struct sigaction sa; + + aiop = aio_slot(-1); + + aiop->fd = fd; + aiop->strategy = strategy; + aiop->done = 0; +#ifdef CRAY + bzero((char *)&aiop->iosw, sizeof(aiop->iosw)); +#endif + + if (strategy == A_SIGNAL) { + aiop->sig = sig; + aiop->signalled = 0; + + sa.sa_handler = aio_handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + + sigaction(sig, &sa, &aiop->osa); + } else { + aiop->sig = -1; + aiop->signalled = 0; + } + + return aiop->id; +} + +int +aio_unregister(aio_id) +int aio_id; +{ + struct aio_info *aiop; + + aiop = aio_slot(aio_id); + + if (aiop->strategy == A_SIGNAL) { + sigaction(aiop->sig, &aiop->osa, NULL); + } + + aiop->busy = 0; + return 0; +} + +#ifndef linux +int +aio_wait(aio_id) +int aio_id; +{ +#ifdef RECALL_SIZEOF + long mask[RECALL_SIZEOF]; +#endif + sigset_t sigset; + struct aio_info *aiop; +#ifdef CRAY + struct iosw *ioswlist[1]; +#endif +#ifdef sgi + const aiocb_t *aioary[1]; +#endif + int r, cnt; + + + aiop = aio_slot(aio_id); +/*printf("%d aiop B =%p\n", getpid(), aiop);*/ + + switch (aiop->strategy) { + case A_POLL: + while (! aio_done(aiop)) + ; + break; + + case A_SIGNAL: + sigemptyset(&sigset); + sighold( aiop->sig ); + + while ( !aiop->signalled || !aiop->done ) { + sigsuspend(&sigset); + sighold( aiop->sig ); + } + break; + +#ifdef CRAY + case A_RECALL: + ioswlist[0] = &aiop->iosw; + if (recall(aiop->fd, 1, ioswlist) < 0) { + doio_fprintf(stderr, "recall() failed: %s (%d)\n", + SYSERR, errno); + exit(E_SETUP); + } + break; + +#ifdef RECALL_SIZEOF + + case A_RECALLA: + RECALL_INIT(mask); + RECALL_SET(mask, aiop->fd); + if (recalla(mask) < 0) { + doio_fprintf(stderr, "recalla() failed: %s (%d)\n", + SYSERR, errno); + exit(E_SETUP); + } + + RECALL_CLR(mask, aiop->fd); + break; +#endif + + case A_RECALLS: + ioswlist[0] = &aiop->iosw; + if (recalls(1, ioswlist) < 0) { + doio_fprintf(stderr, "recalls failed: %s (%d)\n", + SYSERR, errno); + exit(E_SETUP); + } + break; +#endif /* CRAY */ + +#ifdef sgi + case A_CALLBACK: + aioary[0] = &aiop->aiocb; + cnt=0; + do { + r = aio_suspend(aioary, 1, NULL); + if( r == -1 ){ + doio_fprintf(stderr, "aio_suspend failed: %s (%d)\n", + SYSERR, errno ); + exit(E_SETUP); + } + cnt++; + } while(aiop->done == 0); + +#if 0 + /* + * after having this set for a while, I've decided that + * it's too noisy + */ + if(cnt > 1) + doio_fprintf(stderr, "aio_wait: callback wait took %d tries\n", cnt); +#endif + + /* + * Note: cb_handler already calls aio_done + */ + break; + + + case A_SUSPEND: + aioary[0] = &aiop->aiocb; + r = aio_suspend(aioary, 1, NULL); + if( r == -1 ){ + doio_fprintf(stderr, "aio_suspend failed: %s (%d)\n", + SYSERR, errno ); + exit(E_SETUP); + } + + aio_done(aiop); + break; +#endif + } + +/*printf("aio_wait: errno %d return %d\n", aiop->aio_errno, aiop->aio_ret);*/ + + return 0; +} +#endif /* !linux */ + +/* + * Format specified time into HH:MM:SS format. t is the time to format + * in seconds (as returned from time(2)). + */ + +char * +hms(t) +time_t t; +{ + static char ascii_time[9]; + struct tm *ltime; + + ltime = localtime(&t); + strftime(ascii_time, sizeof(ascii_time), "%H:%M:%S", ltime); + + return ascii_time; +} + +/* + * Simple routine to check if an async io request has completed. + */ + +int +aio_done(struct aio_info *ainfo) +{ +#ifdef CRAY + return ainfo->iosw.sw_flag; +#endif + +#ifdef sgi + if( (ainfo->aio_errno = aio_error(&ainfo->aiocb)) == -1 ){ + doio_fprintf(stderr, "aio_done: aio_error failed: %s (%d)\n", + SYSERR, errno ); + exit(E_SETUP); + } + /*printf("%d aio_done aio_errno=%d\n", getpid(), ainfo->aio_errno);*/ + if( ainfo->aio_errno != EINPROGRESS ){ + if( (ainfo->aio_ret = aio_return(&ainfo->aiocb)) == -1 ){ + doio_fprintf(stderr, "aio_done: aio_return failed: %s (%d)\n", + SYSERR, errno ); + exit(E_SETUP); + } + } + + return (ainfo->aio_errno != EINPROGRESS); +#else + return -1; /* invalid */ +#endif +} + +/* + * Routine to handle upanic() - it first attempts to set the panic flag. If + * the flag cannot be set, an error message is issued. A call to upanic + * with PA_PANIC is then done unconditionally, in case the panic flag was set + * from outside the program (as with the panic(8) program). + * + * Note - we only execute the upanic code if -U was used, and the passed in + * mask is set in the Upanic_Conditions bitmask. + */ + +void +doio_upanic(mask) +int mask; +{ + if (U_opt == 0 || (mask & Upanic_Conditions) == 0) { + return; + } + +#ifdef CRAY + if (upanic(PA_SET) < 0) { + doio_fprintf(stderr, "WARNING - Could not set the panic flag - upanic(PA_SET) failed: %s (%d)\n", + SYSERR, errno); + } + + upanic(PA_PANIC); +#endif +#ifdef sgi + syssgi(1005); /* syssgi test panic - DEBUG kernels only */ +#endif + doio_fprintf(stderr, "WARNING - upanic() failed\n"); +} + +/* + * Parse cmdline options/arguments and set appropriate global variables. + * If the cmdline is valid, return 0 to caller. Otherwise exit with a status + * of 1. + */ + +int +parse_cmdline(argc, argv, opts) +int argc; +char **argv; +char *opts; +{ + int c; + char cc, *cp, *tok = NULL; + extern int opterr; + extern int optind; + extern char *optarg; + struct smap *s; + char *memargs[NMEMALLOC]; + int nmemargs, ma; + void parse_memalloc(char *arg); + void parse_delay(char *arg); + void dump_memalloc(); + + if (*argv[0] == '-') { + argv[0]++; + Execd = 1; + } + + if ((Prog = strrchr(argv[0], '/')) == NULL) { + Prog = argv[0]; + } else { + Prog++; + } + + opterr = 0; + while ((c = getopt(argc, argv, opts)) != EOF) { + switch ((char)c) { + case 'a': + a_opt++; + break; + + case 'C': + C_opt++; + for(s=checkmap; s->string != NULL; s++) + if(!strcmp(s->string, optarg)) + break; + if (s->string == NULL) { + fprintf(stderr, + "%s%s: Illegal -C arg (%s). Must be one of: ", + Prog, TagName, tok); + + for (s = checkmap; s->string != NULL; s++) + fprintf(stderr, "%s ", s->string); + fprintf(stderr, "\n"); + exit(1); + } + + switch(s->value) { + case C_DEFAULT: + Data_Fill = doio_pat_fill; + Data_Check = doio_pat_check; + break; + default: + fprintf(stderr, + "%s%s: Unrecognised -C arg '%s' %d", + Prog, TagName, s->string, s->value); + exit(1); + } + break; + + case 'd': /* delay between i/o ops */ + parse_delay(optarg); + break; + + case 'e': + if (Npes > 1 && Nprocs > 1) { + fprintf(stderr, "%s%s: Warning - Program is a multi-pe application - exec option is ignored.\n", Prog, TagName); + } + e_opt++; + break; + + case 'h': + help(stdout); + exit(0); + break; + + case 'k': + k_opt++; + break; + + case 'm': + Message_Interval = strtol(optarg, &cp, 10); + if (*cp != '\0' || Message_Interval < 0) { + fprintf(stderr, "%s%s: Illegal -m arg (%s): Must be an integer >= 0\n", Prog, TagName, optarg); + exit(1); + } + m_opt++; + break; + + case 'M': /* memory allocation types */ +#ifndef CRAY + nmemargs = string_to_tokens(optarg, memargs, 32, ","); + for(ma=0; ma < nmemargs; ma++) { + parse_memalloc(memargs[ma]); + } + /*dump_memalloc();*/ +#else + fprintf(stderr, "%s%s: Error: -M isn't supported on this platform\n", Prog, TagName); + exit(1); +#endif + M_opt++; + break; + + case 'N': + sprintf( TagName, "(%.39s)", optarg ); + break; + + case 'n': + Nprocs = strtol(optarg, &cp, 10); + if (*cp != '\0' || Nprocs < 1) { + fprintf(stderr, + "%s%s: Illegal -n arg (%s): Must be integer > 0\n", + Prog, TagName, optarg); + exit(E_USAGE); + } + + if (Npes > 1 && Nprocs > 1) { + fprintf(stderr, "%s%s: Program has been built as a multi-pe app. -n1 is the only nprocs value allowed\n", Prog, TagName); + exit(E_SETUP); + } + n_opt++; + break; + + case 'r': + Release_Interval = strtol(optarg, &cp, 10); + if (*cp != '\0' || Release_Interval < 0) { + fprintf(stderr, + "%s%s: Illegal -r arg (%s): Must be integer >= 0\n", + Prog, TagName, optarg); + exit(E_USAGE); + } + + r_opt++; + break; + + case 'w': + Write_Log = optarg; + w_opt++; + break; + + case 'v': + v_opt++; + break; + + case 'V': + if (strcasecmp(optarg, "sync") == 0) { + Validation_Flags = O_SYNC; + } else if (strcasecmp(optarg, "buffered") == 0) { + Validation_Flags = 0; +#ifdef CRAY + } else if (strcasecmp(optarg, "parallel") == 0) { + Validation_Flags = O_PARALLEL; + } else if (strcasecmp(optarg, "ldraw") == 0) { + Validation_Flags = O_LDRAW; + } else if (strcasecmp(optarg, "raw") == 0) { + Validation_Flags = O_RAW; +#endif + } else if (strcasecmp(optarg, "direct") == 0) { + Validation_Flags = O_DIRECT; + } else { + if (sscanf(optarg, "%i%c", &Validation_Flags, &cc) != 1) { + fprintf(stderr, "%s: Invalid -V argument (%s) - must be a decimal, hex, or octal\n", Prog, optarg); + fprintf(stderr, " number, or one of the following strings: 'sync',\n"); + fprintf(stderr, " 'buffered', 'parallel', 'ldraw', or 'raw'\n"); + exit(E_USAGE); + } + } + V_opt++; + break; + case 'U': + tok = strtok(optarg, ","); + while (tok != NULL) { + for (s = Upanic_Args; s->string != NULL; s++) + if (strcmp(s->string, tok) == 0) + break; + + if (s->string == NULL) { + fprintf(stderr, + "%s%s: Illegal -U arg (%s). Must be one of: ", + Prog, TagName, tok); + + for (s = Upanic_Args; s->string != NULL; s++) + fprintf(stderr, "%s ", s->string); + + fprintf(stderr, "\n"); + + exit(1); + } + + Upanic_Conditions |= s->value; + tok = strtok(NULL, ","); + } + + U_opt++; + break; + + case '?': + usage(stderr); + exit(E_USAGE); + break; + } + } + + /* + * Supply defaults + */ + + if (! C_opt) { + Data_Fill = doio_pat_fill; + Data_Check = doio_pat_check; + } + + if (! U_opt) + Upanic_Conditions = 0; + + if (! n_opt) + Nprocs = 1; + + if (! r_opt) + Release_Interval = DEF_RELEASE_INTERVAL; + + if (! M_opt) { + Memalloc[Nmemalloc].memtype = MEM_DATA; + Memalloc[Nmemalloc].flags = 0; + Memalloc[Nmemalloc].name = NULL; + Memalloc[Nmemalloc].space = NULL; + Nmemalloc++; + } + + /* + * Initialize input stream + */ + + if (argc == optind) { + Infile = NULL; + } else { + Infile = argv[optind++]; + } + + if (argc != optind) { + usage(stderr); + exit(E_USAGE); + } + + return 0; +} + + + +/* + * Parse memory allocation types + * + * Types are: + * Data + * T3E-shmem:blksize[:nblks] + * SysV-shmem:shmid:blksize:nblks + * if shmid is "private", use IPC_PRIVATE + * and nblks is not required + * + * mmap:flags:filename:blksize[:nblks] + * flags are one of: + * p - private (MAP_PRIVATE) + * a - private, MAP_AUTORESRV + * l - local (MAP_LOCAL) + * s - shared (nblks required) + * + * plus any of: + * f - fixed address (MAP_FIXED) + * A - use an address without MAP_FIXED + * a - autogrow (map once at startup) + * + * mmap:flags:devzero + * mmap /dev/zero (shared not allowd) + * maps the first 4096 bytes of /dev/zero + * + * - put a directory at the beginning of the shared + * regions saying what pid has what region. + * DIRMAGIC + * BLKSIZE + * NBLKS + * nblks worth of directories - 1 int pids + */ +#ifndef CRAY +void +parse_memalloc(char *arg) +{ + char *allocargs[NMEMALLOC]; + int nalloc; + struct memalloc *M; + + if(Nmemalloc >= NMEMALLOC) { + doio_fprintf(stderr, "Error - too many memory types (%d).\n", + Nmemalloc); + return; + } + + M = &Memalloc[Nmemalloc]; + + nalloc = string_to_tokens(arg, allocargs, 32, ":"); + if(!strcmp(allocargs[0], "data")) { + M->memtype = MEM_DATA; + M->flags = 0; + M->name = NULL; + M->space = NULL; + Nmemalloc++; + if(nalloc >= 2) { + if(strchr(allocargs[1], 'p')) + M->flags |= MEMF_MPIN; + } + } else if(!strcmp(allocargs[0], "mmap")) { + /* mmap:flags:filename[:size] */ + M->memtype = MEM_MMAP; + M->flags = 0; + M->space = NULL; + if(nalloc >= 1) { + if(strchr(allocargs[1], 'p')) + M->flags |= MEMF_PRIVATE; + if(strchr(allocargs[1], 'a')) + M->flags |= MEMF_AUTORESRV; + if(strchr(allocargs[1], 'l')) + M->flags |= MEMF_LOCAL; + if(strchr(allocargs[1], 's')) + M->flags |= MEMF_SHARED; + + if(strchr(allocargs[1], 'f')) + M->flags |= MEMF_FIXADDR; + if(strchr(allocargs[1], 'A')) + M->flags |= MEMF_ADDR; + if(strchr(allocargs[1], 'G')) + M->flags |= MEMF_AUTOGROW; + + if(strchr(allocargs[1], 'U')) + M->flags |= MEMF_FILE; + } else { + M->flags |= MEMF_PRIVATE; + } + + if(nalloc > 2) { + if(!strcmp(allocargs[2], "devzero")) { + M->name = "/dev/zero"; + if(M->flags & + ((MEMF_PRIVATE|MEMF_LOCAL) == 0)) + M->flags |= MEMF_PRIVATE; + } else { + M->name = allocargs[2]; + } + } else { + M->name = "/dev/zero"; + if(M->flags & + ((MEMF_PRIVATE|MEMF_LOCAL) == 0)) + M->flags |= MEMF_PRIVATE; + } + Nmemalloc++; + + } else if(!strcmp(allocargs[0], "shmem")) { + /* shmem:shmid:size */ + M->memtype = MEM_SHMEM; + M->flags = 0; + M->space = NULL; + if(nalloc >= 2) { + M->name = allocargs[1]; + } else { + M->name = NULL; + } + if(nalloc >= 3) { + sscanf(allocargs[2], "%i", &M->nblks); + } else { + M->nblks = 0; + } + if(nalloc >= 4) { + if(strchr(allocargs[3], 'p')) + M->flags |= MEMF_MPIN; + } + + Nmemalloc++; + } else { + doio_fprintf(stderr, "Error - unknown memory type '%s'.\n", + allocargs[0]); + exit(1); + } +} + +void +dump_memalloc() +{ + int ma; + char *mt; + + if(Nmemalloc == 0) { + printf("No memory allocation strategies devined\n"); + return; + } + + for(ma=0; ma < Nmemalloc; ma++) { + switch(Memalloc[ma].memtype) { + case MEM_DATA: mt = "data"; break; + case MEM_SHMEM: mt = "shmem"; break; + case MEM_MMAP: mt = "mmap"; break; + default: mt = "unknown"; break; + } + printf("mstrat[%d] = %d %s\n", ma, Memalloc[ma].memtype, mt); + printf("\tflags=%#o name='%s' nblks=%d\n", + Memalloc[ma].flags, + Memalloc[ma].name, + Memalloc[ma].nblks); + } +} + +#endif /* !CRAY */ + +/* + * -d :