| ##/bin/bash |
| # SPDX-License-Identifier: GPL-2.0+ |
| # Copyright (c) 2025 Oracle. All Rights Reserved. |
| # |
| # Routines for testing atomic writes. |
| |
| export STATX_WRITE_ATOMIC=0x10000 |
| |
| _get_atomic_write_unit_min() |
| { |
| $XFS_IO_PROG -c "statx -r -m $STATX_WRITE_ATOMIC" $1 | \ |
| grep atomic_write_unit_min | grep -o '[0-9]\+' |
| } |
| |
| _get_atomic_write_unit_max() |
| { |
| $XFS_IO_PROG -c "statx -r -m $STATX_WRITE_ATOMIC" $1 | \ |
| grep -w atomic_write_unit_max | grep -o '[0-9]\+' |
| } |
| |
| _get_atomic_write_unit_max_opt() |
| { |
| $XFS_IO_PROG -c "statx -r -m $STATX_WRITE_ATOMIC" $1 | \ |
| grep -w atomic_write_unit_max_opt | grep -o '[0-9]\+' |
| } |
| |
| _get_atomic_write_segments_max() |
| { |
| $XFS_IO_PROG -c "statx -r -m $STATX_WRITE_ATOMIC" $1 | \ |
| grep -w atomic_write_segments_max | grep -o '[0-9]\+' |
| } |
| |
| _require_scratch_write_atomic_multi_fsblock() |
| { |
| _require_scratch |
| |
| _scratch_mkfs > /dev/null 2>&1 || \ |
| _notrun "cannot format scratch device for atomic write checks" |
| _try_scratch_mount || \ |
| _notrun "cannot mount scratch device for atomic write checks" |
| |
| local testfile=$SCRATCH_MNT/testfile |
| touch $testfile |
| |
| local bsize=$(_get_file_block_size $SCRATCH_MNT) |
| local awu_max_fs=$(_get_atomic_write_unit_max $testfile) |
| |
| _scratch_unmount |
| |
| if [ -z "$awu_max_fs" -o $awu_max_fs -lt $((bsize * 2)) ];then |
| _notrun "multi-block atomic writes not supported by this filesystem" |
| fi |
| } |
| |
| _require_scratch_write_atomic() |
| { |
| _require_scratch |
| |
| local awu_min_bdev=$(_get_atomic_write_unit_min $SCRATCH_DEV) |
| local awu_max_bdev=$(_get_atomic_write_unit_max $SCRATCH_DEV) |
| |
| if [ -z "$awu_min_bdev" -o -z "$awu_max_bdev" ] || \ |
| [ $awu_min_bdev -eq 0 -a $awu_max_bdev -eq 0 ];then |
| _notrun "write atomic not supported by this block device" |
| fi |
| |
| _scratch_mkfs > /dev/null 2>&1 || \ |
| _notrun "cannot format scratch device for atomic write checks" |
| _try_scratch_mount || \ |
| _notrun "cannot mount scratch device for atomic write checks" |
| |
| local testfile=$SCRATCH_MNT/testfile |
| touch $testfile |
| |
| local awu_min_fs=$(_get_atomic_write_unit_min $testfile) |
| local awu_max_fs=$(_get_atomic_write_unit_max $testfile) |
| |
| _scratch_unmount |
| |
| if [ -z "$awu_min_fs" -o -z "$awu_max_fs" ] || \ |
| [ $awu_min_fs -eq 0 -a $awu_max_fs -eq 0 ];then |
| _notrun "write atomic not supported by this filesystem" |
| fi |
| } |
| |
| # Check for xfs_io commands required to run _test_atomic_file_writes |
| _require_atomic_write_test_commands() |
| { |
| _require_xfs_io_command "falloc" |
| _require_xfs_io_command "fpunch" |
| _require_xfs_io_command pwrite -A |
| } |
| |
| _test_atomic_file_writes() |
| { |
| local bsize="$1" |
| local testfile="$2" |
| local bytes_written |
| local testfile_cp="$testfile.copy" |
| |
| # Check that we can perform an atomic write of len = FS block size |
| bytes_written=$($XFS_IO_PROG -dc "pwrite -A -D -V1 -b $bsize 0 $bsize" $testfile | \ |
| grep wrote | awk -F'[/ ]' '{print $2}') |
| test $bytes_written -eq $bsize || echo "atomic write len=$bsize failed" |
| |
| # Check that we can perform an atomic single-block cow write |
| if cp --reflink=always $testfile $testfile_cp 2>> $seqres.full; then |
| bytes_written=$($XFS_IO_PROG -dc "pwrite -A -D -V1 -b $bsize 0 $bsize" $testfile_cp | \ |
| grep wrote | awk -F'[/ ]' '{print $2}') |
| test $bytes_written -eq $bsize || echo "atomic write on reflinked file failed" |
| fi |
| |
| # Check that we can perform an atomic write on an unwritten block |
| $XFS_IO_PROG -c "falloc $bsize $bsize" $testfile |
| bytes_written=$($XFS_IO_PROG -dc "pwrite -A -D -V1 -b $bsize $bsize $bsize" $testfile | \ |
| grep wrote | awk -F'[/ ]' '{print $2}') |
| test $bytes_written -eq $bsize || echo "atomic write to unwritten block failed" |
| |
| # Check that we can perform an atomic write on a sparse hole |
| $XFS_IO_PROG -c "fpunch 0 $bsize" $testfile |
| bytes_written=$($XFS_IO_PROG -dc "pwrite -A -D -V1 -b $bsize 0 $bsize" $testfile | \ |
| grep wrote | awk -F'[/ ]' '{print $2}') |
| test $bytes_written -eq $bsize || echo "atomic write to sparse hole failed" |
| |
| # Check that we can perform an atomic write on a fully mapped block |
| bytes_written=$($XFS_IO_PROG -dc "pwrite -A -D -V1 -b $bsize 0 $bsize" $testfile | \ |
| grep wrote | awk -F'[/ ]' '{print $2}') |
| test $bytes_written -eq $bsize || echo "atomic write to mapped block failed" |
| |
| # Reject atomic write if len is out of bounds |
| $XFS_IO_PROG -dc "pwrite -A -D -V1 -b $bsize 0 $((bsize - 1))" $testfile 2>> $seqres.full && \ |
| echo "atomic write len=$((bsize - 1)) should fail" |
| $XFS_IO_PROG -dc "pwrite -A -D -V1 -b $bsize 0 $((bsize + 1))" $testfile 2>> $seqres.full && \ |
| echo "atomic write len=$((bsize + 1)) should fail" |
| |
| # Reject atomic write when iovecs > 1 |
| $XFS_IO_PROG -dc "pwrite -A -D -V2 -b $bsize 0 $bsize" $testfile 2>> $seqres.full && \ |
| echo "atomic write only supports iovec count of 1" |
| |
| # Reject atomic write when not using direct I/O |
| $XFS_IO_PROG -c "pwrite -A -V1 -b $bsize 0 $bsize" $testfile 2>> $seqres.full && \ |
| echo "atomic write requires direct I/O" |
| |
| # Reject atomic write when offset % bsize != 0 |
| $XFS_IO_PROG -dc "pwrite -A -D -V1 -b $bsize 1 $bsize" $testfile 2>> $seqres.full && \ |
| echo "atomic write requires offset to be aligned to bsize" |
| } |
| |
| _simple_atomic_write() { |
| local pos=$1 |
| local count=$2 |
| local file=$3 |
| local directio=$4 |
| |
| echo "testing pos=$pos count=$count file=$file directio=$directio" >> $seqres.full |
| $XFS_IO_PROG $directio -c "pwrite -b $count -V 1 -A -D $pos $count" $file >> $seqres.full |
| } |