Message ID | 20230415002949.GZ360889@frogsfrogsfrogs (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | None | expand |
On Fri, Apr 14, 2023 at 05:29:49PM -0700, Darrick J. Wong wrote: > From: Darrick J. Wong <djwong@kernel.org> > > Make it so that test runners can schedule long soak stress test programs > for an exact number of seconds by setting the SOAK_DURATION config > variable. > > Signed-off-by: Darrick J. Wong <djwong@kernel.org> > Reviewed-by: Andrey Albershteyn <aalbersh@redhat.com> > --- > v2: fix commit message > --- > check | 14 +++++++++ > common/config | 7 ++++ > common/fuzzy | 7 ++++ > common/report | 1 + > ltp/fsstress.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++-- > ltp/fsx.c | 50 +++++++++++++++++++++++++++++++ > src/soak_duration.awk | 23 ++++++++++++++ > tests/generic/476 | 5 +++ > tests/generic/521 | 1 + > tests/generic/522 | 1 + > tests/generic/642 | 1 + > 11 files changed, 182 insertions(+), 6 deletions(-) > create mode 100644 src/soak_duration.awk > > diff --git a/check b/check > index e32b70d301..1d78cf27f4 100755 > --- a/check > +++ b/check > @@ -366,6 +366,20 @@ if ! . ./common/rc; then > exit 1 > fi > > +# If the test config specified a soak test duration, see if there are any > +# unit suffixes that need converting to an integer seconds count. > +if [ -n "$SOAK_DURATION" ]; then > + SOAK_DURATION="$(echo "$SOAK_DURATION" | \ > + sed -e 's/^\([.0-9]*\)\([a-z]\)*/\1 \2/g' | \ > + $AWK_PROG -f $here/src/soak_duration.awk)" > + if [ $? -ne 0 ]; then > + echo "$SOAK_DURATION" echo SOAK_DURATION=$SOAK_DURATION ? > + status=1 > + exit 1 > + fi > + export SOAK_DURATION Is this necessary? As you've exported SOAK_DURATION in common/config: export SOAK_DURATION=${SOAK_DURATION:=} > +fi > + > if [ -n "$subdir_xfile" ]; then > for d in $SRC_GROUPS $FSTYP; do > [ -f $SRC_DIR/$d/$subdir_xfile ] || continue > diff --git a/common/config b/common/config > index 6c8cb3a5ba..fdd0aadbeb 100644 > --- a/common/config > +++ b/common/config > @@ -57,6 +57,13 @@ export SOAK_PROC=3 # -p option to fsstress > export SOAK_STRESS=10000 # -n option to fsstress > export SOAK_PASSES=-1 # count of repetitions of fsstress (while soaking) > export EMAIL=root@localhost # where auto-qa will send its status messages > + > +# For certain tests that run in tight loops, setting this variable allows the > +# test runner to specify exactly how long the test should continue looping. > +# This is independent of TIME_FACTOR. Floating point numbers are allowed, and > +# the unit suffixes m(inutes), h(ours), d(ays), and w(eeks) are supported. > +export SOAK_DURATION=${SOAK_DURATION:=} Better to describe more about the SOAK_DURATION (and TIME/LOAD_FACTOR) in documentation (README?). To clarify the relationship of SOAK_DURATION with other related parameters, and how the SOAK_DURATION works/be used. > + > export HOST_OPTIONS=${HOST_OPTIONS:=local.config} > export CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"} > export BENCH_PASSES=${BENCH_PASSES:=5} > diff --git a/common/fuzzy b/common/fuzzy > index 744d9ed65d..9c04bb5318 100644 > --- a/common/fuzzy > +++ b/common/fuzzy > @@ -1360,7 +1360,12 @@ _scratch_xfs_stress_scrub() { > fi > > local start="$(date +%s)" > - local end="$((start + (30 * TIME_FACTOR) ))" > + local end > + if [ -n "$SOAK_DURATION" ]; then > + end="$((start + SOAK_DURATION))" > + else > + end="$((start + (30 * TIME_FACTOR) ))" > + fi > local scrub_startat="$((start + scrub_delay))" > test "$scrub_startat" -gt "$((end - 10))" && > scrub_startat="$((end - 10))" > diff --git a/common/report b/common/report > index be930e0b06..9bfa09ecce 100644 > --- a/common/report > +++ b/common/report > @@ -67,6 +67,7 @@ __generate_report_vars() { > REPORT_VARS["CPUS"]="$(getconf _NPROCESSORS_ONLN 2>/dev/null)" > REPORT_VARS["MEM_KB"]="$(grep MemTotal: /proc/meminfo | awk '{print $2}')" > REPORT_VARS["SWAP_KB"]="$(grep SwapTotal: /proc/meminfo | awk '{print $2}')" > + test -n "$SOAK_DURATION" && REPORT_VARS["SOAK_DURATION"]="$SOAK_DURATION" > > test -e /sys/devices/system/node/possible && \ > REPORT_VARS["NUMA_NODES"]="$(cat /sys/devices/system/node/possible 2>/dev/null)" > diff --git a/ltp/fsstress.c b/ltp/fsstress.c > index e60f2da929..0dc6545448 100644 > --- a/ltp/fsstress.c > +++ b/ltp/fsstress.c > @@ -386,6 +386,8 @@ char *execute_cmd = NULL; > int execute_freq = 1; > struct print_string flag_str = {0}; > > +struct timespec deadline = { 0 }; > + > void add_to_flist(int, int, int, int); > void append_pathname(pathname_t *, char *); > int attr_list_path(pathname_t *, char *, const int); > @@ -459,6 +461,34 @@ void sg_handler(int signum) > } > } > > +bool > +keep_looping(int i, int loops) > +{ > + int ret; > + > + if (deadline.tv_nsec) { > + struct timespec now; > + > + ret = clock_gettime(CLOCK_MONOTONIC, &now); > + if (ret) { > + perror("CLOCK_MONOTONIC"); > + return false; > + } > + > + return now.tv_sec <= deadline.tv_sec; > + } > + > + if (!loops) > + return true; > + > + return i < loops; > +} > + > +static struct option longopts[] = { > + {"duration", optional_argument, 0, 256}, OK, we'd better to check those test cases use fsstress "-n" later, think about if some of them should be replaced by "--duration". Thanks, Zorro > + { } > +}; > + > int main(int argc, char **argv) > { > char buf[10]; > @@ -478,13 +508,14 @@ int main(int argc, char **argv) > struct sigaction action; > int loops = 1; > const char *allopts = "cd:e:f:i:l:m:M:n:o:p:rRs:S:vVwx:X:zH"; > + long long duration; > > errrange = errtag = 0; > umask(0); > nops = sizeof(ops) / sizeof(ops[0]); > ops_end = &ops[nops]; > myprog = argv[0]; > - while ((c = getopt(argc, argv, allopts)) != -1) { > + while ((c = getopt_long(argc, argv, allopts, longopts, NULL)) != -1) { > switch (c) { > case 'c': > cleanup = 1; > @@ -579,6 +610,26 @@ int main(int argc, char **argv) > case 'X': > execute_freq = strtoul(optarg, NULL, 0); > break; > + case 256: /* --duration */ > + if (!optarg) { > + fprintf(stderr, "Specify time with --duration=\n"); > + exit(87); > + } > + duration = strtoll(optarg, NULL, 0); > + if (duration < 1) { > + fprintf(stderr, "%lld: invalid duration\n", duration); > + exit(88); > + } > + > + i = clock_gettime(CLOCK_MONOTONIC, &deadline); > + if (i) { > + perror("CLOCK_MONOTONIC"); > + exit(89); > + } > + > + deadline.tv_sec += duration; > + deadline.tv_nsec = 1; > + break; > case '?': > fprintf(stderr, "%s - invalid parameters\n", > myprog); > @@ -721,7 +772,7 @@ int main(int argc, char **argv) > } > } > #endif > - for (i = 0; !loops || (i < loops); i++) > + for (i = 0; keep_looping(i, loops); i++) > doproc(); > #ifdef AIO > if(io_destroy(io_ctx) != 0) { > @@ -1121,6 +1172,26 @@ dirid_to_fent(int dirid) > return NULL; > } > > +bool > +keep_running(opnum_t opno, opnum_t operations) > +{ > + int ret; > + > + if (deadline.tv_nsec) { > + struct timespec now; > + > + ret = clock_gettime(CLOCK_MONOTONIC, &now); > + if (ret) { > + perror("CLOCK_MONOTONIC"); > + return false; > + } > + > + return now.tv_sec <= deadline.tv_sec; > + } > + > + return opno < operations; > +} > + > void > doproc(void) > { > @@ -1149,7 +1220,7 @@ doproc(void) > srandom(seed); > if (namerand) > namerand = random(); > - for (opno = 0; opno < operations; opno++) { > + for (opno = 0; keep_running(opno, operations); opno++) { > if (execute_cmd && opno && opno % dividend == 0) { > if (verbose) > printf("%lld: execute command %s\n", opno, > @@ -1935,6 +2006,7 @@ usage(void) > printf(" -V specifies verifiable logging mode (omitting inode numbers)\n"); > printf(" -X ncmd number of calls to the -x command (default 1)\n"); > printf(" -H prints usage and exits\n"); > + printf(" --duration=s run for this many seconds\n"); > } > > void > diff --git a/ltp/fsx.c b/ltp/fsx.c > index ee4b8fe45d..761d5e467f 100644 > --- a/ltp/fsx.c > +++ b/ltp/fsx.c > @@ -193,6 +193,8 @@ int fsx_rw(int rw, int fd, char *buf, unsigned len, unsigned offset); > #define fsxread(a,b,c,d) fsx_rw(READ, a,b,c,d) > #define fsxwrite(a,b,c,d) fsx_rw(WRITE, a,b,c,d) > > +struct timespec deadline; > + > const char *replayops = NULL; > const char *recordops = NULL; > FILE * fsxlogf = NULL; > @@ -2457,6 +2459,7 @@ usage(void) > -Z: O_DIRECT (use -R, -W, -r and -w too)\n\ > --replay-ops opsfile: replay ops from recorded .fsxops file\n\ > --record-ops[=opsfile]: dump ops file also on success. optionally specify ops file name\n\ > + --duration=seconds: run for this many seconds\n\ > fname: this filename is REQUIRED (no default)\n"); > exit(90); > } > @@ -2739,9 +2742,33 @@ __test_fallocate(int mode, const char *mode_str) > #endif > } > > +bool > +keep_running(void) > +{ > + int ret; > + > + if (deadline.tv_nsec) { > + struct timespec now; > + > + ret = clock_gettime(CLOCK_MONOTONIC, &now); > + if (ret) { > + perror("CLOCK_MONOTONIC"); > + return false; > + } > + > + return now.tv_sec <= deadline.tv_sec; > + } > + > + if (numops == -1) > + return true; > + > + return numops-- != 0; > +} > + > static struct option longopts[] = { > {"replay-ops", required_argument, 0, 256}, > {"record-ops", optional_argument, 0, 255}, > + {"duration", optional_argument, 0, 254}, > { } > }; > > @@ -2753,6 +2780,7 @@ main(int argc, char **argv) > char logfile[PATH_MAX]; > struct stat statbuf; > int o_flags = O_RDWR|O_CREAT|O_TRUNC; > + long long duration; > > logfile[0] = 0; > dname[0] = 0; > @@ -2950,6 +2978,26 @@ main(int argc, char **argv) > o_direct = O_DIRECT; > o_flags |= O_DIRECT; > break; > + case 254: /* --duration */ > + if (!optarg) { > + fprintf(stderr, "Specify time with --duration=\n"); > + exit(87); > + } > + duration = strtoll(optarg, NULL, 0); > + if (duration < 1) { > + fprintf(stderr, "%lld: invalid duration\n", duration); > + exit(88); > + } > + > + i = clock_gettime(CLOCK_MONOTONIC, &deadline); > + if (i) { > + perror("CLOCK_MONOTONIC"); > + exit(89); > + } > + > + deadline.tv_sec += duration; > + deadline.tv_nsec = 1; > + break; > case 255: /* --record-ops */ > if (optarg) > snprintf(opsfile, sizeof(opsfile), "%s", optarg); > @@ -3145,7 +3193,7 @@ main(int argc, char **argv) > if (xchg_range_calls) > xchg_range_calls = test_xchg_range(); > > - while (numops == -1 || numops--) > + while (keep_running()) > if (!test()) > break; > > diff --git a/src/soak_duration.awk b/src/soak_duration.awk > new file mode 100644 > index 0000000000..6c38d09b39 > --- /dev/null > +++ b/src/soak_duration.awk > @@ -0,0 +1,23 @@ > +#!/usr/bin/awk > +# > +# Convert time interval specifications with suffixes to an integer number of > +# seconds. > +{ > + nr = $1; > + if ($2 == "" || $2 ~ /s/) # seconds > + ; > + else if ($2 ~ /m/) # minutes > + nr *= 60; > + else if ($2 ~ /h/) # hours > + nr *= 3600; > + else if ($2 ~ /d/) # days > + nr *= 86400; > + else if ($2 ~ /w/) # weeks > + nr *= 604800; > + else { > + printf("%s: unknown suffix\n", $2); > + exit 1; > + } > + > + printf("%d\n", nr); > +} > diff --git a/tests/generic/476 b/tests/generic/476 > index edb0be7b50..a162cda6b1 100755 > --- a/tests/generic/476 > +++ b/tests/generic/476 > @@ -33,7 +33,10 @@ _scratch_mount >> $seqres.full 2>&1 > > nr_cpus=$((LOAD_FACTOR * 4)) > nr_ops=$((25000 * nr_cpus * TIME_FACTOR)) > -$FSSTRESS_PROG $FSSTRESS_AVOID -w -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus >> $seqres.full > +fsstress_args=(-w -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus) > +test -n "$SOAK_DURATION" && fsstress_args+=(--duration="$SOAK_DURATION") > + > +$FSSTRESS_PROG $FSSTRESS_AVOID "${fsstress_args[@]}" >> $seqres.full > > # success, all done > status=0 > diff --git a/tests/generic/521 b/tests/generic/521 > index cde9d44775..22dd31a8ec 100755 > --- a/tests/generic/521 > +++ b/tests/generic/521 > @@ -35,6 +35,7 @@ fsx_args+=(-r $min_dio_sz) > fsx_args+=(-t $min_dio_sz) > fsx_args+=(-w $min_dio_sz) > fsx_args+=(-Z) > +test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION") > > run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d' > > diff --git a/tests/generic/522 b/tests/generic/522 > index ae84fe04bb..f0cbcb245c 100755 > --- a/tests/generic/522 > +++ b/tests/generic/522 > @@ -29,6 +29,7 @@ fsx_args+=(-N $nr_ops) > fsx_args+=(-p $((nr_ops / 100))) > fsx_args+=(-o $op_sz) > fsx_args+=(-l $file_sz) > +test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION") > > run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d' > > diff --git a/tests/generic/642 b/tests/generic/642 > index c0e274d843..eba90903a3 100755 > --- a/tests/generic/642 > +++ b/tests/generic/642 > @@ -49,6 +49,7 @@ for verb in attr_remove removefattr; do > done > args+=('-f' "setfattr=20") > args+=('-f' "attr_set=60") # sets larger xattrs > +test -n "$DURATION" && args+=(--duration="$DURATION") > > $FSSTRESS_PROG "${args[@]}" $FSSTRESS_AVOID -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus >> $seqres.full > >
On Sat, Apr 22, 2023 at 09:33:16PM +0800, Zorro Lang wrote: > On Fri, Apr 14, 2023 at 05:29:49PM -0700, Darrick J. Wong wrote: > > From: Darrick J. Wong <djwong@kernel.org> > > > > Make it so that test runners can schedule long soak stress test programs > > for an exact number of seconds by setting the SOAK_DURATION config > > variable. > > > > Signed-off-by: Darrick J. Wong <djwong@kernel.org> > > Reviewed-by: Andrey Albershteyn <aalbersh@redhat.com> > > --- > > v2: fix commit message > > --- > > check | 14 +++++++++ > > common/config | 7 ++++ > > common/fuzzy | 7 ++++ > > common/report | 1 + > > ltp/fsstress.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++-- > > ltp/fsx.c | 50 +++++++++++++++++++++++++++++++ > > src/soak_duration.awk | 23 ++++++++++++++ > > tests/generic/476 | 5 +++ > > tests/generic/521 | 1 + > > tests/generic/522 | 1 + > > tests/generic/642 | 1 + > > 11 files changed, 182 insertions(+), 6 deletions(-) > > create mode 100644 src/soak_duration.awk > > > > diff --git a/check b/check > > index e32b70d301..1d78cf27f4 100755 > > --- a/check > > +++ b/check > > @@ -366,6 +366,20 @@ if ! . ./common/rc; then > > exit 1 > > fi > > > > +# If the test config specified a soak test duration, see if there are any > > +# unit suffixes that need converting to an integer seconds count. > > +if [ -n "$SOAK_DURATION" ]; then > > + SOAK_DURATION="$(echo "$SOAK_DURATION" | \ > > + sed -e 's/^\([.0-9]*\)\([a-z]\)*/\1 \2/g' | \ > > + $AWK_PROG -f $here/src/soak_duration.awk)" > > + if [ $? -ne 0 ]; then > > + echo "$SOAK_DURATION" > > echo SOAK_DURATION=$SOAK_DURATION ? Hmm. On nonzero return, the awk script already specified the reason that the input was rejected, so I think this line can go away. > > + status=1 > > + exit 1 > > + fi > > + export SOAK_DURATION > > Is this necessary? As you've exported SOAK_DURATION in common/config: > > export SOAK_DURATION=${SOAK_DURATION:=} You're correct, it does not need to be re-exported. > > +fi > > + > > if [ -n "$subdir_xfile" ]; then > > for d in $SRC_GROUPS $FSTYP; do > > [ -f $SRC_DIR/$d/$subdir_xfile ] || continue > > diff --git a/common/config b/common/config > > index 6c8cb3a5ba..fdd0aadbeb 100644 > > --- a/common/config > > +++ b/common/config > > @@ -57,6 +57,13 @@ export SOAK_PROC=3 # -p option to fsstress > > export SOAK_STRESS=10000 # -n option to fsstress > > export SOAK_PASSES=-1 # count of repetitions of fsstress (while soaking) > > export EMAIL=root@localhost # where auto-qa will send its status messages > > + > > +# For certain tests that run in tight loops, setting this variable allows the > > +# test runner to specify exactly how long the test should continue looping. > > +# This is independent of TIME_FACTOR. Floating point numbers are allowed, and > > +# the unit suffixes m(inutes), h(ours), d(ays), and w(eeks) are supported. > > +export SOAK_DURATION=${SOAK_DURATION:=} > > Better to describe more about the SOAK_DURATION (and TIME/LOAD_FACTOR) in > documentation (README?). To clarify the relationship of SOAK_DURATION with > other related parameters, and how the SOAK_DURATION works/be used. TIME_FACTOR is not documented in the README. I'll add another patch to do that... > > + > > export HOST_OPTIONS=${HOST_OPTIONS:=local.config} > > export CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"} > > export BENCH_PASSES=${BENCH_PASSES:=5} > > diff --git a/common/fuzzy b/common/fuzzy > > index 744d9ed65d..9c04bb5318 100644 > > --- a/common/fuzzy > > +++ b/common/fuzzy > > @@ -1360,7 +1360,12 @@ _scratch_xfs_stress_scrub() { > > fi > > > > local start="$(date +%s)" > > - local end="$((start + (30 * TIME_FACTOR) ))" > > + local end > > + if [ -n "$SOAK_DURATION" ]; then > > + end="$((start + SOAK_DURATION))" > > + else > > + end="$((start + (30 * TIME_FACTOR) ))" > > + fi > > local scrub_startat="$((start + scrub_delay))" > > test "$scrub_startat" -gt "$((end - 10))" && > > scrub_startat="$((end - 10))" > > diff --git a/common/report b/common/report > > index be930e0b06..9bfa09ecce 100644 > > --- a/common/report > > +++ b/common/report > > @@ -67,6 +67,7 @@ __generate_report_vars() { > > REPORT_VARS["CPUS"]="$(getconf _NPROCESSORS_ONLN 2>/dev/null)" > > REPORT_VARS["MEM_KB"]="$(grep MemTotal: /proc/meminfo | awk '{print $2}')" > > REPORT_VARS["SWAP_KB"]="$(grep SwapTotal: /proc/meminfo | awk '{print $2}')" > > + test -n "$SOAK_DURATION" && REPORT_VARS["SOAK_DURATION"]="$SOAK_DURATION" > > > > test -e /sys/devices/system/node/possible && \ > > REPORT_VARS["NUMA_NODES"]="$(cat /sys/devices/system/node/possible 2>/dev/null)" > > diff --git a/ltp/fsstress.c b/ltp/fsstress.c > > index e60f2da929..0dc6545448 100644 > > --- a/ltp/fsstress.c > > +++ b/ltp/fsstress.c > > @@ -386,6 +386,8 @@ char *execute_cmd = NULL; > > int execute_freq = 1; > > struct print_string flag_str = {0}; > > > > +struct timespec deadline = { 0 }; > > + > > void add_to_flist(int, int, int, int); > > void append_pathname(pathname_t *, char *); > > int attr_list_path(pathname_t *, char *, const int); > > @@ -459,6 +461,34 @@ void sg_handler(int signum) > > } > > } > > > > +bool > > +keep_looping(int i, int loops) > > +{ > > + int ret; > > + > > + if (deadline.tv_nsec) { > > + struct timespec now; > > + > > + ret = clock_gettime(CLOCK_MONOTONIC, &now); > > + if (ret) { > > + perror("CLOCK_MONOTONIC"); > > + return false; > > + } > > + > > + return now.tv_sec <= deadline.tv_sec; > > + } > > + > > + if (!loops) > > + return true; > > + > > + return i < loops; > > +} > > + > > +static struct option longopts[] = { > > + {"duration", optional_argument, 0, 256}, > > OK, we'd better to check those test cases use fsstress "-n" later, think about > if some of them should be replaced by "--duration". <nod> I'll update the fsstress/fsx --help screens to state that --duration overrides -n/-N. --D > Thanks, > Zorro > > > + { } > > +}; > > + > > int main(int argc, char **argv) > > { > > char buf[10]; > > @@ -478,13 +508,14 @@ int main(int argc, char **argv) > > struct sigaction action; > > int loops = 1; > > const char *allopts = "cd:e:f:i:l:m:M:n:o:p:rRs:S:vVwx:X:zH"; > > + long long duration; > > > > errrange = errtag = 0; > > umask(0); > > nops = sizeof(ops) / sizeof(ops[0]); > > ops_end = &ops[nops]; > > myprog = argv[0]; > > - while ((c = getopt(argc, argv, allopts)) != -1) { > > + while ((c = getopt_long(argc, argv, allopts, longopts, NULL)) != -1) { > > switch (c) { > > case 'c': > > cleanup = 1; > > @@ -579,6 +610,26 @@ int main(int argc, char **argv) > > case 'X': > > execute_freq = strtoul(optarg, NULL, 0); > > break; > > + case 256: /* --duration */ > > + if (!optarg) { > > + fprintf(stderr, "Specify time with --duration=\n"); > > + exit(87); > > + } > > + duration = strtoll(optarg, NULL, 0); > > + if (duration < 1) { > > + fprintf(stderr, "%lld: invalid duration\n", duration); > > + exit(88); > > + } > > + > > + i = clock_gettime(CLOCK_MONOTONIC, &deadline); > > + if (i) { > > + perror("CLOCK_MONOTONIC"); > > + exit(89); > > + } > > + > > + deadline.tv_sec += duration; > > + deadline.tv_nsec = 1; > > + break; > > case '?': > > fprintf(stderr, "%s - invalid parameters\n", > > myprog); > > @@ -721,7 +772,7 @@ int main(int argc, char **argv) > > } > > } > > #endif > > - for (i = 0; !loops || (i < loops); i++) > > + for (i = 0; keep_looping(i, loops); i++) > > doproc(); > > #ifdef AIO > > if(io_destroy(io_ctx) != 0) { > > @@ -1121,6 +1172,26 @@ dirid_to_fent(int dirid) > > return NULL; > > } > > > > +bool > > +keep_running(opnum_t opno, opnum_t operations) > > +{ > > + int ret; > > + > > + if (deadline.tv_nsec) { > > + struct timespec now; > > + > > + ret = clock_gettime(CLOCK_MONOTONIC, &now); > > + if (ret) { > > + perror("CLOCK_MONOTONIC"); > > + return false; > > + } > > + > > + return now.tv_sec <= deadline.tv_sec; > > + } > > + > > + return opno < operations; > > +} > > + > > void > > doproc(void) > > { > > @@ -1149,7 +1220,7 @@ doproc(void) > > srandom(seed); > > if (namerand) > > namerand = random(); > > - for (opno = 0; opno < operations; opno++) { > > + for (opno = 0; keep_running(opno, operations); opno++) { > > if (execute_cmd && opno && opno % dividend == 0) { > > if (verbose) > > printf("%lld: execute command %s\n", opno, > > @@ -1935,6 +2006,7 @@ usage(void) > > printf(" -V specifies verifiable logging mode (omitting inode numbers)\n"); > > printf(" -X ncmd number of calls to the -x command (default 1)\n"); > > printf(" -H prints usage and exits\n"); > > + printf(" --duration=s run for this many seconds\n"); > > } > > > > void > > diff --git a/ltp/fsx.c b/ltp/fsx.c > > index ee4b8fe45d..761d5e467f 100644 > > --- a/ltp/fsx.c > > +++ b/ltp/fsx.c > > @@ -193,6 +193,8 @@ int fsx_rw(int rw, int fd, char *buf, unsigned len, unsigned offset); > > #define fsxread(a,b,c,d) fsx_rw(READ, a,b,c,d) > > #define fsxwrite(a,b,c,d) fsx_rw(WRITE, a,b,c,d) > > > > +struct timespec deadline; > > + > > const char *replayops = NULL; > > const char *recordops = NULL; > > FILE * fsxlogf = NULL; > > @@ -2457,6 +2459,7 @@ usage(void) > > -Z: O_DIRECT (use -R, -W, -r and -w too)\n\ > > --replay-ops opsfile: replay ops from recorded .fsxops file\n\ > > --record-ops[=opsfile]: dump ops file also on success. optionally specify ops file name\n\ > > + --duration=seconds: run for this many seconds\n\ > > fname: this filename is REQUIRED (no default)\n"); > > exit(90); > > } > > @@ -2739,9 +2742,33 @@ __test_fallocate(int mode, const char *mode_str) > > #endif > > } > > > > +bool > > +keep_running(void) > > +{ > > + int ret; > > + > > + if (deadline.tv_nsec) { > > + struct timespec now; > > + > > + ret = clock_gettime(CLOCK_MONOTONIC, &now); > > + if (ret) { > > + perror("CLOCK_MONOTONIC"); > > + return false; > > + } > > + > > + return now.tv_sec <= deadline.tv_sec; > > + } > > + > > + if (numops == -1) > > + return true; > > + > > + return numops-- != 0; > > +} > > + > > static struct option longopts[] = { > > {"replay-ops", required_argument, 0, 256}, > > {"record-ops", optional_argument, 0, 255}, > > + {"duration", optional_argument, 0, 254}, > > { } > > }; > > > > @@ -2753,6 +2780,7 @@ main(int argc, char **argv) > > char logfile[PATH_MAX]; > > struct stat statbuf; > > int o_flags = O_RDWR|O_CREAT|O_TRUNC; > > + long long duration; > > > > logfile[0] = 0; > > dname[0] = 0; > > @@ -2950,6 +2978,26 @@ main(int argc, char **argv) > > o_direct = O_DIRECT; > > o_flags |= O_DIRECT; > > break; > > + case 254: /* --duration */ > > + if (!optarg) { > > + fprintf(stderr, "Specify time with --duration=\n"); > > + exit(87); > > + } > > + duration = strtoll(optarg, NULL, 0); > > + if (duration < 1) { > > + fprintf(stderr, "%lld: invalid duration\n", duration); > > + exit(88); > > + } > > + > > + i = clock_gettime(CLOCK_MONOTONIC, &deadline); > > + if (i) { > > + perror("CLOCK_MONOTONIC"); > > + exit(89); > > + } > > + > > + deadline.tv_sec += duration; > > + deadline.tv_nsec = 1; > > + break; > > case 255: /* --record-ops */ > > if (optarg) > > snprintf(opsfile, sizeof(opsfile), "%s", optarg); > > @@ -3145,7 +3193,7 @@ main(int argc, char **argv) > > if (xchg_range_calls) > > xchg_range_calls = test_xchg_range(); > > > > - while (numops == -1 || numops--) > > + while (keep_running()) > > if (!test()) > > break; > > > > diff --git a/src/soak_duration.awk b/src/soak_duration.awk > > new file mode 100644 > > index 0000000000..6c38d09b39 > > --- /dev/null > > +++ b/src/soak_duration.awk > > @@ -0,0 +1,23 @@ > > +#!/usr/bin/awk > > +# > > +# Convert time interval specifications with suffixes to an integer number of > > +# seconds. > > +{ > > + nr = $1; > > + if ($2 == "" || $2 ~ /s/) # seconds > > + ; > > + else if ($2 ~ /m/) # minutes > > + nr *= 60; > > + else if ($2 ~ /h/) # hours > > + nr *= 3600; > > + else if ($2 ~ /d/) # days > > + nr *= 86400; > > + else if ($2 ~ /w/) # weeks > > + nr *= 604800; > > + else { > > + printf("%s: unknown suffix\n", $2); > > + exit 1; > > + } > > + > > + printf("%d\n", nr); > > +} > > diff --git a/tests/generic/476 b/tests/generic/476 > > index edb0be7b50..a162cda6b1 100755 > > --- a/tests/generic/476 > > +++ b/tests/generic/476 > > @@ -33,7 +33,10 @@ _scratch_mount >> $seqres.full 2>&1 > > > > nr_cpus=$((LOAD_FACTOR * 4)) > > nr_ops=$((25000 * nr_cpus * TIME_FACTOR)) > > -$FSSTRESS_PROG $FSSTRESS_AVOID -w -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus >> $seqres.full > > +fsstress_args=(-w -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus) > > +test -n "$SOAK_DURATION" && fsstress_args+=(--duration="$SOAK_DURATION") > > + > > +$FSSTRESS_PROG $FSSTRESS_AVOID "${fsstress_args[@]}" >> $seqres.full > > > > # success, all done > > status=0 > > diff --git a/tests/generic/521 b/tests/generic/521 > > index cde9d44775..22dd31a8ec 100755 > > --- a/tests/generic/521 > > +++ b/tests/generic/521 > > @@ -35,6 +35,7 @@ fsx_args+=(-r $min_dio_sz) > > fsx_args+=(-t $min_dio_sz) > > fsx_args+=(-w $min_dio_sz) > > fsx_args+=(-Z) > > +test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION") > > > > run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d' > > > > diff --git a/tests/generic/522 b/tests/generic/522 > > index ae84fe04bb..f0cbcb245c 100755 > > --- a/tests/generic/522 > > +++ b/tests/generic/522 > > @@ -29,6 +29,7 @@ fsx_args+=(-N $nr_ops) > > fsx_args+=(-p $((nr_ops / 100))) > > fsx_args+=(-o $op_sz) > > fsx_args+=(-l $file_sz) > > +test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION") > > > > run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d' > > > > diff --git a/tests/generic/642 b/tests/generic/642 > > index c0e274d843..eba90903a3 100755 > > --- a/tests/generic/642 > > +++ b/tests/generic/642 > > @@ -49,6 +49,7 @@ for verb in attr_remove removefattr; do > > done > > args+=('-f' "setfattr=20") > > args+=('-f' "attr_set=60") # sets larger xattrs > > +test -n "$DURATION" && args+=(--duration="$DURATION") > > > > $FSSTRESS_PROG "${args[@]}" $FSSTRESS_AVOID -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus >> $seqres.full > > > > >
diff --git a/check b/check index e32b70d301..1d78cf27f4 100755 --- a/check +++ b/check @@ -366,6 +366,20 @@ if ! . ./common/rc; then exit 1 fi +# If the test config specified a soak test duration, see if there are any +# unit suffixes that need converting to an integer seconds count. +if [ -n "$SOAK_DURATION" ]; then + SOAK_DURATION="$(echo "$SOAK_DURATION" | \ + sed -e 's/^\([.0-9]*\)\([a-z]\)*/\1 \2/g' | \ + $AWK_PROG -f $here/src/soak_duration.awk)" + if [ $? -ne 0 ]; then + echo "$SOAK_DURATION" + status=1 + exit 1 + fi + export SOAK_DURATION +fi + if [ -n "$subdir_xfile" ]; then for d in $SRC_GROUPS $FSTYP; do [ -f $SRC_DIR/$d/$subdir_xfile ] || continue diff --git a/common/config b/common/config index 6c8cb3a5ba..fdd0aadbeb 100644 --- a/common/config +++ b/common/config @@ -57,6 +57,13 @@ export SOAK_PROC=3 # -p option to fsstress export SOAK_STRESS=10000 # -n option to fsstress export SOAK_PASSES=-1 # count of repetitions of fsstress (while soaking) export EMAIL=root@localhost # where auto-qa will send its status messages + +# For certain tests that run in tight loops, setting this variable allows the +# test runner to specify exactly how long the test should continue looping. +# This is independent of TIME_FACTOR. Floating point numbers are allowed, and +# the unit suffixes m(inutes), h(ours), d(ays), and w(eeks) are supported. +export SOAK_DURATION=${SOAK_DURATION:=} + export HOST_OPTIONS=${HOST_OPTIONS:=local.config} export CHECK_OPTIONS=${CHECK_OPTIONS:="-g auto"} export BENCH_PASSES=${BENCH_PASSES:=5} diff --git a/common/fuzzy b/common/fuzzy index 744d9ed65d..9c04bb5318 100644 --- a/common/fuzzy +++ b/common/fuzzy @@ -1360,7 +1360,12 @@ _scratch_xfs_stress_scrub() { fi local start="$(date +%s)" - local end="$((start + (30 * TIME_FACTOR) ))" + local end + if [ -n "$SOAK_DURATION" ]; then + end="$((start + SOAK_DURATION))" + else + end="$((start + (30 * TIME_FACTOR) ))" + fi local scrub_startat="$((start + scrub_delay))" test "$scrub_startat" -gt "$((end - 10))" && scrub_startat="$((end - 10))" diff --git a/common/report b/common/report index be930e0b06..9bfa09ecce 100644 --- a/common/report +++ b/common/report @@ -67,6 +67,7 @@ __generate_report_vars() { REPORT_VARS["CPUS"]="$(getconf _NPROCESSORS_ONLN 2>/dev/null)" REPORT_VARS["MEM_KB"]="$(grep MemTotal: /proc/meminfo | awk '{print $2}')" REPORT_VARS["SWAP_KB"]="$(grep SwapTotal: /proc/meminfo | awk '{print $2}')" + test -n "$SOAK_DURATION" && REPORT_VARS["SOAK_DURATION"]="$SOAK_DURATION" test -e /sys/devices/system/node/possible && \ REPORT_VARS["NUMA_NODES"]="$(cat /sys/devices/system/node/possible 2>/dev/null)" diff --git a/ltp/fsstress.c b/ltp/fsstress.c index e60f2da929..0dc6545448 100644 --- a/ltp/fsstress.c +++ b/ltp/fsstress.c @@ -386,6 +386,8 @@ char *execute_cmd = NULL; int execute_freq = 1; struct print_string flag_str = {0}; +struct timespec deadline = { 0 }; + void add_to_flist(int, int, int, int); void append_pathname(pathname_t *, char *); int attr_list_path(pathname_t *, char *, const int); @@ -459,6 +461,34 @@ void sg_handler(int signum) } } +bool +keep_looping(int i, int loops) +{ + int ret; + + if (deadline.tv_nsec) { + struct timespec now; + + ret = clock_gettime(CLOCK_MONOTONIC, &now); + if (ret) { + perror("CLOCK_MONOTONIC"); + return false; + } + + return now.tv_sec <= deadline.tv_sec; + } + + if (!loops) + return true; + + return i < loops; +} + +static struct option longopts[] = { + {"duration", optional_argument, 0, 256}, + { } +}; + int main(int argc, char **argv) { char buf[10]; @@ -478,13 +508,14 @@ int main(int argc, char **argv) struct sigaction action; int loops = 1; const char *allopts = "cd:e:f:i:l:m:M:n:o:p:rRs:S:vVwx:X:zH"; + long long duration; errrange = errtag = 0; umask(0); nops = sizeof(ops) / sizeof(ops[0]); ops_end = &ops[nops]; myprog = argv[0]; - while ((c = getopt(argc, argv, allopts)) != -1) { + while ((c = getopt_long(argc, argv, allopts, longopts, NULL)) != -1) { switch (c) { case 'c': cleanup = 1; @@ -579,6 +610,26 @@ int main(int argc, char **argv) case 'X': execute_freq = strtoul(optarg, NULL, 0); break; + case 256: /* --duration */ + if (!optarg) { + fprintf(stderr, "Specify time with --duration=\n"); + exit(87); + } + duration = strtoll(optarg, NULL, 0); + if (duration < 1) { + fprintf(stderr, "%lld: invalid duration\n", duration); + exit(88); + } + + i = clock_gettime(CLOCK_MONOTONIC, &deadline); + if (i) { + perror("CLOCK_MONOTONIC"); + exit(89); + } + + deadline.tv_sec += duration; + deadline.tv_nsec = 1; + break; case '?': fprintf(stderr, "%s - invalid parameters\n", myprog); @@ -721,7 +772,7 @@ int main(int argc, char **argv) } } #endif - for (i = 0; !loops || (i < loops); i++) + for (i = 0; keep_looping(i, loops); i++) doproc(); #ifdef AIO if(io_destroy(io_ctx) != 0) { @@ -1121,6 +1172,26 @@ dirid_to_fent(int dirid) return NULL; } +bool +keep_running(opnum_t opno, opnum_t operations) +{ + int ret; + + if (deadline.tv_nsec) { + struct timespec now; + + ret = clock_gettime(CLOCK_MONOTONIC, &now); + if (ret) { + perror("CLOCK_MONOTONIC"); + return false; + } + + return now.tv_sec <= deadline.tv_sec; + } + + return opno < operations; +} + void doproc(void) { @@ -1149,7 +1220,7 @@ doproc(void) srandom(seed); if (namerand) namerand = random(); - for (opno = 0; opno < operations; opno++) { + for (opno = 0; keep_running(opno, operations); opno++) { if (execute_cmd && opno && opno % dividend == 0) { if (verbose) printf("%lld: execute command %s\n", opno, @@ -1935,6 +2006,7 @@ usage(void) printf(" -V specifies verifiable logging mode (omitting inode numbers)\n"); printf(" -X ncmd number of calls to the -x command (default 1)\n"); printf(" -H prints usage and exits\n"); + printf(" --duration=s run for this many seconds\n"); } void diff --git a/ltp/fsx.c b/ltp/fsx.c index ee4b8fe45d..761d5e467f 100644 --- a/ltp/fsx.c +++ b/ltp/fsx.c @@ -193,6 +193,8 @@ int fsx_rw(int rw, int fd, char *buf, unsigned len, unsigned offset); #define fsxread(a,b,c,d) fsx_rw(READ, a,b,c,d) #define fsxwrite(a,b,c,d) fsx_rw(WRITE, a,b,c,d) +struct timespec deadline; + const char *replayops = NULL; const char *recordops = NULL; FILE * fsxlogf = NULL; @@ -2457,6 +2459,7 @@ usage(void) -Z: O_DIRECT (use -R, -W, -r and -w too)\n\ --replay-ops opsfile: replay ops from recorded .fsxops file\n\ --record-ops[=opsfile]: dump ops file also on success. optionally specify ops file name\n\ + --duration=seconds: run for this many seconds\n\ fname: this filename is REQUIRED (no default)\n"); exit(90); } @@ -2739,9 +2742,33 @@ __test_fallocate(int mode, const char *mode_str) #endif } +bool +keep_running(void) +{ + int ret; + + if (deadline.tv_nsec) { + struct timespec now; + + ret = clock_gettime(CLOCK_MONOTONIC, &now); + if (ret) { + perror("CLOCK_MONOTONIC"); + return false; + } + + return now.tv_sec <= deadline.tv_sec; + } + + if (numops == -1) + return true; + + return numops-- != 0; +} + static struct option longopts[] = { {"replay-ops", required_argument, 0, 256}, {"record-ops", optional_argument, 0, 255}, + {"duration", optional_argument, 0, 254}, { } }; @@ -2753,6 +2780,7 @@ main(int argc, char **argv) char logfile[PATH_MAX]; struct stat statbuf; int o_flags = O_RDWR|O_CREAT|O_TRUNC; + long long duration; logfile[0] = 0; dname[0] = 0; @@ -2950,6 +2978,26 @@ main(int argc, char **argv) o_direct = O_DIRECT; o_flags |= O_DIRECT; break; + case 254: /* --duration */ + if (!optarg) { + fprintf(stderr, "Specify time with --duration=\n"); + exit(87); + } + duration = strtoll(optarg, NULL, 0); + if (duration < 1) { + fprintf(stderr, "%lld: invalid duration\n", duration); + exit(88); + } + + i = clock_gettime(CLOCK_MONOTONIC, &deadline); + if (i) { + perror("CLOCK_MONOTONIC"); + exit(89); + } + + deadline.tv_sec += duration; + deadline.tv_nsec = 1; + break; case 255: /* --record-ops */ if (optarg) snprintf(opsfile, sizeof(opsfile), "%s", optarg); @@ -3145,7 +3193,7 @@ main(int argc, char **argv) if (xchg_range_calls) xchg_range_calls = test_xchg_range(); - while (numops == -1 || numops--) + while (keep_running()) if (!test()) break; diff --git a/src/soak_duration.awk b/src/soak_duration.awk new file mode 100644 index 0000000000..6c38d09b39 --- /dev/null +++ b/src/soak_duration.awk @@ -0,0 +1,23 @@ +#!/usr/bin/awk +# +# Convert time interval specifications with suffixes to an integer number of +# seconds. +{ + nr = $1; + if ($2 == "" || $2 ~ /s/) # seconds + ; + else if ($2 ~ /m/) # minutes + nr *= 60; + else if ($2 ~ /h/) # hours + nr *= 3600; + else if ($2 ~ /d/) # days + nr *= 86400; + else if ($2 ~ /w/) # weeks + nr *= 604800; + else { + printf("%s: unknown suffix\n", $2); + exit 1; + } + + printf("%d\n", nr); +} diff --git a/tests/generic/476 b/tests/generic/476 index edb0be7b50..a162cda6b1 100755 --- a/tests/generic/476 +++ b/tests/generic/476 @@ -33,7 +33,10 @@ _scratch_mount >> $seqres.full 2>&1 nr_cpus=$((LOAD_FACTOR * 4)) nr_ops=$((25000 * nr_cpus * TIME_FACTOR)) -$FSSTRESS_PROG $FSSTRESS_AVOID -w -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus >> $seqres.full +fsstress_args=(-w -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus) +test -n "$SOAK_DURATION" && fsstress_args+=(--duration="$SOAK_DURATION") + +$FSSTRESS_PROG $FSSTRESS_AVOID "${fsstress_args[@]}" >> $seqres.full # success, all done status=0 diff --git a/tests/generic/521 b/tests/generic/521 index cde9d44775..22dd31a8ec 100755 --- a/tests/generic/521 +++ b/tests/generic/521 @@ -35,6 +35,7 @@ fsx_args+=(-r $min_dio_sz) fsx_args+=(-t $min_dio_sz) fsx_args+=(-w $min_dio_sz) fsx_args+=(-Z) +test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION") run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d' diff --git a/tests/generic/522 b/tests/generic/522 index ae84fe04bb..f0cbcb245c 100755 --- a/tests/generic/522 +++ b/tests/generic/522 @@ -29,6 +29,7 @@ fsx_args+=(-N $nr_ops) fsx_args+=(-p $((nr_ops / 100))) fsx_args+=(-o $op_sz) fsx_args+=(-l $file_sz) +test -n "$SOAK_DURATION" && fsx_args+=(--duration="$SOAK_DURATION") run_fsx "${fsx_args[@]}" | sed -e '/^fsx.*/d' diff --git a/tests/generic/642 b/tests/generic/642 index c0e274d843..eba90903a3 100755 --- a/tests/generic/642 +++ b/tests/generic/642 @@ -49,6 +49,7 @@ for verb in attr_remove removefattr; do done args+=('-f' "setfattr=20") args+=('-f' "attr_set=60") # sets larger xattrs +test -n "$DURATION" && args+=(--duration="$DURATION") $FSSTRESS_PROG "${args[@]}" $FSSTRESS_AVOID -d $SCRATCH_MNT -n $nr_ops -p $nr_cpus >> $seqres.full