#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2025 Oracle.  All Rights Reserved.
#
# FS QA Test No. 767
#
# Validate multi-fsblock atomic write support with simulated hardware support
#
. ./common/preamble
_begin_fstest auto quick rw atomicwrites

. ./common/scsi_debug
. ./common/atomicwrites

_cleanup()
{
	_scratch_unmount &>/dev/null
	_put_scsi_debug_dev &>/dev/null
	cd /
	rm -r -f $tmp.*
}

_require_scsi_debug
_require_scratch
_require_block_device $SCRATCH_DEV
_require_xfs_io_command "falloc"

# Format something so that ./check doesn't freak out
_scratch_mkfs >> $seqres.full

# 512b logical/physical sectors, 512M size, atomic writes enabled
dev=$(_get_scsi_debug_dev 512 512 0 512 "atomic_wr=1")
test -b "$dev" || _notrun "could not create atomic writes scsi_debug device"

export SCRATCH_DEV=$dev
unset USE_EXTERNAL

_require_scratch_write_atomic

# Check that xfs_io supports the commands needed to run this test
# Note: _require_xfs_io_command is not used here because we want to
# run this test even if $TEST_DIR does not support atomic writes
$XFS_IO_PROG -c 'help pwrite' | grep -q RWF_ATOMIC || _notrun "xfs_io pwrite -A failed"
$XFS_IO_PROG -c 'help falloc' | grep -q 'not found' && _notrun "xfs_io falloc failed"
$XFS_IO_PROG -c 'help statx' | grep -q -- '-r' || _notrun "xfs_io statx -r failed"

echo "scsi_debug atomic write properties" >> $seqres.full
$XFS_IO_PROG -c "statx -r -m $STATX_WRITE_ATOMIC" $SCRATCH_DEV >> $seqres.full

_scratch_mkfs >> $seqres.full
_scratch_mount
test "$FSTYP" = "xfs" && _xfs_force_bdev data $SCRATCH_MNT

testfile=$SCRATCH_MNT/testfile
touch $testfile

echo "filesystem atomic write properties" >> $seqres.full
$XFS_IO_PROG -c "statx -r -m $STATX_WRITE_ATOMIC" $testfile >> $seqres.full

sector_size=$(blockdev --getss $SCRATCH_DEV)
min_awu=$(_get_atomic_write_unit_min $testfile)
max_awu=$(_get_atomic_write_unit_max $testfile)
opt_awu=$(_get_atomic_write_unit_max_opt $testfile)

echo "min:$min_awu max:$max_awu opt:$opt_awu" >> $seqres.full

# We want to test hardware support, so use that if detected
if [ -n "$opt_awu" ] && [ "$opt_awu" != "0" ]; then
	write_size="$opt_awu"
else
	write_size="$max_awu"
fi

$XFS_IO_PROG -f -c "falloc 0 $((write_size * 2))" -c fsync $testfile

# try outside the advertised sizes
echo "two EINVAL for unsupported sizes"
min_i=$((min_awu / 2))
_simple_atomic_write $min_i $min_i $testfile -d
max_i=$((max_awu * 2))
_simple_atomic_write $max_i $max_i $testfile -d

# try all of the advertised sizes
echo "all should work"
for ((i = min_awu; i <= write_size; i *= 2)); do
	$XFS_IO_PROG -f -c "falloc 0 $((write_size * 2))" -c fsync $testfile
	_test_atomic_file_writes $i $testfile
done

# does not support buffered io
echo "one EOPNOTSUPP for buffered atomic"
_simple_atomic_write 0 $min_awu $testfile

# does not support unaligned directio
echo "one EINVAL for unaligned directio"
_simple_atomic_write $sector_size $min_awu $testfile -d

_scratch_unmount
_put_scsi_debug_dev

# success, all done
echo Silence is golden
status=0
exit
