--- /dev/null
+##/bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2025 Oracle. All Rights Reserved.
+#
+# Routines for testing atomic writes.
+
+_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_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()
+{
+ _require_scratch
+
+ export STATX_WRITE_ATOMIC=0x10000
+
+ awu_min_bdev=$(_get_atomic_write_unit_min $SCRATCH_DEV)
+ 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
+ _scratch_mount
+
+ testfile=$SCRATCH_MNT/testfile
+ touch $testfile
+
+ awu_min_fs=$(_get_atomic_write_unit_min $testfile)
+ 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
+}
+
+_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 [ "$FSTYP" == "xfs" ]; then
+ testfile_cp=$SCRATCH_MNT/testfile_copy
+ if _xfs_has_feature $SCRATCH_MNT reflink; then
+ cp --reflink $testfile $testfile_cp
+ fi
+ 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"
+}
_scratch_unmount
}
-_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_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()
-{
- _require_scratch
-
- export STATX_WRITE_ATOMIC=0x10000
-
- awu_min_bdev=$(_get_atomic_write_unit_min $SCRATCH_DEV)
- 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
- _scratch_mount
-
- testfile=$SCRATCH_MNT/testfile
- touch $testfile
-
- awu_min_fs=$(_get_atomic_write_unit_min $testfile)
- 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
-}
-
_require_inode_limits()
{
if [ $(_get_free_inode $TEST_DIR) -eq 0 ]; then
. ./common/preamble
_begin_fstest auto quick rw atomicwrites
+. ./common/atomicwrites
+
_require_scratch_write_atomic
_require_xfs_io_command pwrite -A
test $file_max_segments -eq 1 || \
echo "atomic write max segments $file_max_segments, should be 1"
- # 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 [ "$FSTYP" == "xfs" ]; then
- testfile_cp=$SCRATCH_MNT/testfile_copy
- if _xfs_has_feature $SCRATCH_MNT reflink; then
- cp --reflink $testfile $testfile_cp
- fi
- 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"
+ _test_atomic_file_writes "$bsize" "$testfile"
_scratch_unmount
}