blob: bbcc4e7c5c3c883c2ff51fbdde18009e5a4e7c17 [file] [log] [blame]
##/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
}