| #!/bin/bash |
| # SPDX-License-Identifier: GPL-2.0 |
| # Copyright 2010 (C) Red Hat, Inc., Lukas Czerner <lczerner@redhat.com> |
| # |
| # FS QA Test No. 251 |
| # |
| # This test was created in order to verify filesystem FITRIM implementation. |
| # By many concurrent copy and remove operations and checking that files |
| # does not change after copied into SCRATCH_MNT test if FITRIM implementation |
| # corrupts the filesystem (data/metadata). |
| # |
| . ./common/preamble |
| _begin_fstest ioctl trim auto |
| |
| tmp=`mktemp -d` |
| trap "_cleanup; exit \$status" 0 1 3 |
| trap "_destroy; exit \$status" 2 15 |
| mypid=$$ |
| |
| # Import common functions. |
| . ./common/filter |
| |
| _require_scratch |
| _scratch_mkfs >/dev/null 2>&1 |
| _scratch_mount |
| _require_batched_discard $SCRATCH_MNT |
| |
| # Override the default cleanup function. |
| _cleanup() |
| { |
| rm -rf $tmp |
| } |
| |
| _destroy() |
| { |
| kill $pids $fstrim_pid 2> /dev/null |
| wait $pids $fstrim_pid 2> /dev/null |
| rm -rf $tmp |
| } |
| |
| _destroy_fstrim() |
| { |
| test -n "$fpid" && kill $fpid 2> /dev/null |
| test -n "$fpid" && wait $fpid 2> /dev/null |
| rm -f $tmp.fstrim_loop |
| } |
| |
| _fail() |
| { |
| echo "$1" |
| kill $mypid 2> /dev/null |
| } |
| |
| # Set FSTRIM_{MIN,MAX}_MINLEN to the lower and upper bounds of the -m(inlen) |
| # parameter to fstrim on the scratch filesystem. |
| set_minlen_constraints() |
| { |
| local mmlen |
| |
| for ((mmlen = 100000; mmlen > 0; mmlen /= 2)); do |
| $FSTRIM_PROG -l $(($mmlen*2))k -m ${mmlen}k $SCRATCH_MNT &> /dev/null && break |
| done |
| test $mmlen -gt 0 || \ |
| _notrun "could not determine maximum FSTRIM minlen param" |
| export FSTRIM_MAX_MINLEN=$mmlen |
| |
| for ((mmlen = 1; mmlen < FSTRIM_MAX_MINLEN; mmlen *= 2)); do |
| $FSTRIM_PROG -l $(($mmlen*2))k -m ${mmlen}k $SCRATCH_MNT &> /dev/null && break |
| done |
| test $mmlen -le $FSTRIM_MAX_MINLEN || \ |
| _notrun "could not determine minimum FSTRIM minlen param" |
| export FSTRIM_MIN_MINLEN=$mmlen |
| } |
| |
| # Set FSTRIM_{MIN,MAX}_LEN to the lower and upper bounds of the -l(ength) |
| # parameter to fstrim on the scratch filesystem. |
| set_length_constraints() |
| { |
| local mmlen |
| |
| for ((mmlen = 100000; mmlen > 0; mmlen /= 2)); do |
| $FSTRIM_PROG -l ${mmlen}k $SCRATCH_MNT &> /dev/null && break |
| done |
| test $mmlen -gt 0 || \ |
| _notrun "could not determine maximum FSTRIM length param" |
| export FSTRIM_MAX_LEN=$mmlen |
| |
| for ((mmlen = 1; mmlen < FSTRIM_MAX_LEN; mmlen *= 2)); do |
| $FSTRIM_PROG -l ${mmlen}k $SCRATCH_MNT &> /dev/null && break |
| done |
| test $mmlen -le $FSTRIM_MAX_LEN || \ |
| _notrun "could not determine minimum FSTRIM length param" |
| export FSTRIM_MIN_LEN=$mmlen |
| } |
| |
| ## |
| # Background FSTRIM loop. We are trimming the device in the loop and for |
| # test coverage, we are doing whole device trim followed by several smaller |
| # trims. |
| ## |
| fstrim_loop() |
| { |
| # always remove the $tmp.fstrim_loop file on exit |
| trap "_destroy_fstrim; exit \$status" 2 15 EXIT |
| |
| fsize=$(_discard_max_offset_kb "$SCRATCH_MNT" "$SCRATCH_DEV") |
| |
| while true ; do |
| while true; do |
| step=$((RANDOM*$RANDOM+4)) |
| test "$step" -ge "$FSTRIM_MIN_LEN" && break |
| done |
| while true; do |
| minlen=$(( (RANDOM * (RANDOM % 2 + 1)) % FSTRIM_MAX_MINLEN )) |
| test "$minlen" -ge "$FSTRIM_MIN_MINLEN" && break |
| done |
| |
| start=$RANDOM |
| if [ $((RANDOM%10)) -gt 7 ]; then |
| $FSTRIM_PROG $SCRATCH_MNT & |
| fpid=$! |
| wait $fpid |
| fi |
| while [ $start -lt $fsize ] ; do |
| test -s $tmp.fstrim_loop || break |
| $FSTRIM_PROG -m ${minlen}k -o ${start}k -l ${step}k $SCRATCH_MNT & |
| fpid=$! |
| wait $fpid |
| start=$(( $start + $step )) |
| done |
| test -s $tmp.fstrim_loop || break |
| done |
| rm -f $tmp.fstrim_loop |
| } |
| |
| function check_sums() { |
| ( |
| cd $SCRATCH_MNT/$p |
| find -P . -xdev -type f -print0 | xargs -0 md5sum | sort -o $tmp/stress.$$.$p |
| ) |
| |
| diff $tmp/content.sums $tmp/stress.$$.$p |
| if [ $? -ne 0 ]; then |
| _fail "!!!Checksums has changed - Filesystem possibly corrupted!!!\n" |
| fi |
| rm -f $tmp/stress.$$.$p |
| } |
| |
| function run_process() { |
| local p=$1 |
| if [ -n "$SOAK_DURATION" ]; then |
| local duration="$SOAK_DURATION" |
| else |
| local duration="$((30 * TIME_FACTOR))" |
| fi |
| local stopat="$(( $(date +%s) + duration))" |
| |
| sleep $((5*$p))s |
| |
| while [ "$(date +%s)" -lt "$stopat" ]; do |
| # Remove old directories. |
| rm -rf $SCRATCH_MNT/$p |
| |
| # Copy content -> partition. |
| mkdir $SCRATCH_MNT/$p |
| cp -axT $content/ $SCRATCH_MNT/$p/ |
| |
| check_sums |
| test -e $tmp.fstrim_loop || break |
| done |
| } |
| |
| nproc=$((4 * LOAD_FACTOR)) |
| |
| # Discover the fstrim constraints before we do anything else |
| set_minlen_constraints |
| set_length_constraints |
| echo "MINLEN max=$FSTRIM_MAX_MINLEN min=$FSTRIM_MIN_MINLEN" >> $seqres.full |
| echo "LENGTH max=$FSTRIM_MAX_LEN min=$FSTRIM_MIN_LEN" >> $seqres.full |
| |
| # Use fsstress to create a directory tree with some variability |
| content=$SCRATCH_MNT/orig |
| mkdir -p $content |
| FSSTRESS_ARGS=$(_scale_fsstress_args -p 4 -d $content -n 1000 $FSSTRESS_AVOID) |
| $FSSTRESS_PROG $FSSTRESS_ARGS >> $seqres.full |
| mkdir -p $tmp |
| |
| ( |
| cd $content |
| find -P . -xdev -type f -print0 | xargs -0 md5sum | sort -o $tmp/content.sums |
| ) |
| |
| echo -n "Running the test: " |
| pids="" |
| echo run > $tmp.fstrim_loop |
| fstrim_loop & |
| fstrim_pid=$! |
| for ((p = 1; p < nproc; p++)); do |
| run_process $p & |
| pids="$pids $!" |
| done |
| echo "done." |
| |
| wait $pids |
| test -e "$tmp.fstrim_loop" && truncate -s 0 $tmp.fstrim_loop |
| while test -e $tmp.fstrim_loop; do |
| sleep 1 |
| done |
| |
| status=0 |
| |
| exit |