| ##/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 | |
| test $awu_max_fs -ge $((bsize * 2)) || \ | |
| _notrun "multi-block atomic writes not supported by this filesystem" | |
| } | |
| _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 [ $awu_min_bdev -eq 0 ] && [ $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 [ $awu_min_fs -eq 0 ] && [ $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 | |
| } |