#! /bin/bash # SPDX-License-Identifier: GPL-2.0 # Copyright (c) 2020 IBM Corporation. All Rights Reserved. # # FS QA Test 619 # # ENOSPC regression test in a multi-threaded scenario. Test allocation # strategies of the file system and validate space anomalies as reported by # the system versus the allocated by the program. # # The test is motivated by a bug in ext4 systems where-in ENOSPC is # reported by the file system even though enough space for allocations is # available[1]. # [1]: https://patchwork.ozlabs.org/patch/1294003 # # Linux kernel patch series that fixes the above regression: # 53f86b170dfa ("ext4: mballoc: add blocks to PA list under same spinlock # after allocating blocks") # cf5e2ca6c990 ("ext4: mballoc: refactor ext4_mb_discard_preallocations()") # 07b5b8e1ac40 ("ext4: mballoc: introduce pcpu seqcnt for freeing PA to # improve ENOSPC handling") # 8ef123fe02ca ("ext4: mballoc: refactor ext4_mb_good_group()") # 993778306e79 ("ext4: mballoc: use lock for checking free blocks while # retrying") # . ./common/preamble _begin_fstest auto rw enospc FS_SIZE=$((240*1024*1024)) # 240MB DEBUG=1 # set to 0 to disable debug statements in shell and c-prog FACT=0.7 # Disk allocation methods FALLOCATE=1 FTRUNCATE=2 # Helps to build TEST_VECTORS SMALL_FILE_SIZE=$((512 * 1024)) # in Bytes BIG_FILE_SIZE=$((1536 * 1024)) # in Bytes MIX_FILE_SIZE=$((2048 * 1024)) # (BIG + SMALL small file size) # Import common functions. . ./common/filter # Modify as appropriate. _supported_fs generic _require_scratch _require_test_program "t_enospc" _require_xfs_io_command "falloc" debug() { if [ $DEBUG -eq 1 ]; then echo "$1" >> $seqres.full fi } # Calculate the number of threads needed to fill the disk space # Arguments # $1: the size of a file # $2: ratio in which $1 file should be split into multiple files. # $3: percentage of the disk space should be used during the test # Calculate the number of threads needed to fill the disk space calc_thread_cnt() { local file_ratio_unit=$1 local file_ratio=$2 local disk_saturation=$3 local tot_avail_size local avail_size local thread_cnt IFS=',' read -ra fratio <<< $file_ratio file_ratio_cnt=${#fratio[@]} tot_avail_size=$($DF_PROG --block-size=1 $SCRATCH_MNT | $AWK_PROG 'FNR == 2 { print $5 }') avail_size=$(echo $tot_avail_size*$disk_saturation | $BC_PROG) thread_cnt=$(echo "$file_ratio_cnt*($avail_size/$file_ratio_unit)" | $BC_PROG) debug "Total available size: $tot_avail_size" debug "Available size: $avail_size" debug "Thread count: $thread_cnt" echo ${thread_cnt} } # Arguments # $1: a string containing test configuration separated by a colon. # $1 is treated as an array of arguments to the function. # Description of each array element is given below. # # @1: name of the test # @2: thread in t_enospec exerciser will allocate file of @2 size # @3: defines the proportion in which the file size defined in @2 # should be divided into two files. # (valid @3: more than two values are not allowed) # values should be comma separated) # sum of all values must be 1) # @4: define the percentage of available memory should be used to # during the test. # @5: defines the disk allocation method (fallocate/ftruncate) # @6: number of the test should run run_testcase() { IFS=':' read -ra args <<< $1 local test_name=${args[0]} local file_ratio_unit=${args[1]} local file_ratio=${args[2]} local disk_saturation=${args[3]} local disk_alloc_method=${args[4]} local test_iteration_cnt=${args[5]} local extra_args="" local thread_cnt if [ "$disk_alloc_method" == "$FALLOCATE" ]; then extra_args="$extra_args -f" fi # enable the debug statements in c program if [ "$DEBUG" -eq 1 ]; then extra_args="$extra_args -v" fi debug "============ Test details start ============" debug "Test name: $test_name" debug "File ratio unit: $file_ratio_unit" debug "File ratio: $file_ratio" debug "Disk saturation $disk_saturation" debug "Disk alloc method $disk_alloc_method" debug "Test iteration count: $test_iteration_cnt" debug "Extra arg: $extra_args" for i in $(eval echo "{1..$test_iteration_cnt}"); do # Setup the device _scratch_mkfs_sized $FS_SIZE >> $seqres.full 2>&1 _scratch_mount debug "===== Test: $test_name iteration: $i starts =====" thread_cnt=$(calc_thread_cnt $file_ratio_unit $file_ratio $disk_saturation) # Start the test $here/src/t_enospc -t $thread_cnt -s $file_ratio_unit -r $file_ratio -p $SCRATCH_MNT $extra_args >> $seqres.full status=$(echo $?) if [ $status -ne 0 ]; then use_per=$($DF_PROG -h | grep $SCRATCH_MNT | awk '{print substr($6, 1, length($6)-1)}' | $BC_PROG) alloc_per=$(echo "$FACT * 100" | $BC_PROG) # We are here since t_enospc failed with an error code. # If the used filesystem space is still < available space - that means # the test failed due to FS wrongly reported ENOSPC. if [ $(echo "$use_per < $alloc_per" | $BC_PROG) -ne 0 ]; then if [ $status -eq 134 ]; then # SIGABRT asserted exit code = 134 echo "FAIL: Aborted assertion faliure" elif [ $status -eq 7 ]; then # SIGBUS asserted exit code = 7 echo "FAIL: ENOSPC BUS faliure" fi echo "$test_name failed at iteration count: $i" echo "$($DF_PROG -h $SCRATCH_MNT)" echo "Allocated: $alloc_per% Used: $use_per%" exit fi fi # Make space for other tests _scratch_unmount debug "===== Test: $test_name iteration: $i ends =====" done debug "============ Test details end =============" } declare -a TEST_VECTORS=( # test-name:file-ratio-unit:file-ratio:disk-saturation:disk-alloc-method:test-iteration-cnt "Small-file-fallocate-test:$SMALL_FILE_SIZE:1:$FACT:$FALLOCATE:3" "Big-file-fallocate-test:$BIG_FILE_SIZE:1:$FACT:$FALLOCATE:3" "Mix-file-fallocate-test:$MIX_FILE_SIZE:0.75,0.25:$FACT:$FALLOCATE:3" "Small-file-ftruncate-test:$SMALL_FILE_SIZE:1:$FACT:$FTRUNCATE:3" "Big-file-ftruncate-test:$BIG_FILE_SIZE:1:$FACT:$FTRUNCATE:3" "Mix-file-ftruncate-test:$MIX_FILE_SIZE:0.75,0.25:$FACT:$FTRUNCATE:3" ) # real QA test starts here for i in "${TEST_VECTORS[@]}"; do run_testcase $i done echo "Silence is golden" status=0 exit