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

. ./common/atomicwrites

_require_scratch_write_atomic
_require_atomic_write_test_commands

get_supported_bsize()
{
    case "$FSTYP" in
    "xfs")
        min_bsize=1024
        for ((i = 65536; i >= 1024; i /= 2)); do
            _scratch_mkfs -b size=$i >> $seqres.full || continue
            if _try_scratch_mount >> $seqres.full 2>&1; then
                max_bsize=$i
                _scratch_unmount
                break;
            fi
        done
        ;;
    "ext4")
        min_bsize=1024
        max_bsize=$(_get_page_size)
        ;;
    *)
        _notrun "$FSTYP does not support atomic writes"
        ;;
    esac

    echo "fs config ------------" >> $seqres.full
    echo "min_bsize $min_bsize" >> $seqres.full
    echo "max_bsize $max_bsize" >> $seqres.full
}

get_mkfs_opts()
{
    local bsize=$1

    case "$FSTYP" in
    "xfs")
        mkfs_opts="-b size=$bsize"
        ;;
    "ext4")
        mkfs_opts="-b $bsize"
        ;;
    *)
        _notrun "$FSTYP does not support atomic writes"
        ;;
    esac
}

test_atomic_writes()
{
    local bsize=$1

    get_mkfs_opts $bsize
    _scratch_mkfs $mkfs_opts >> $seqres.full
    _scratch_mount

    test "$FSTYP" = "xfs" && _xfs_force_bdev data $SCRATCH_MNT

    testfile=$SCRATCH_MNT/testfile
    touch $testfile

    file_min_write=$(_get_atomic_write_unit_min $testfile)
    file_max_write=$(_get_atomic_write_unit_max $testfile)
    file_max_segments=$(_get_atomic_write_segments_max $testfile)

    echo "test $bsize --------------" >> $seqres.full
    echo "file awu_min $file_min_write" >> $seqres.full
    echo "file awu_max $file_max_write" >> $seqres.full
    echo "file awu_segments $file_max_segments" >> $seqres.full

    # Check that atomic min/max = FS block size
    test $file_min_write -eq $bsize || \
        echo "atomic write min $file_min_write, should be fs block size $bsize"
    test $file_max_write -ge $bsize || \
        echo "atomic write max $file_max_write, should be at least fs block size $bsize"
    test $file_max_segments -eq 1 || \
        echo "atomic write max segments $file_max_segments, should be 1"

    _test_atomic_file_writes "$bsize" "$testfile"

    _scratch_unmount
}

sys_min_write=$(cat "/sys/block/$(_short_dev $SCRATCH_DEV)/queue/atomic_write_unit_min_bytes")
sys_max_write=$(cat "/sys/block/$(_short_dev $SCRATCH_DEV)/queue/atomic_write_unit_max_bytes")

bdev_min_write=$(_get_atomic_write_unit_min $SCRATCH_DEV)
bdev_max_write=$(_get_atomic_write_unit_max $SCRATCH_DEV)

echo "sysfs awu_min $sys_min_write" >> $seqres.full
echo "sysfs awu_min $sys_max_write" >> $seqres.full
echo "bdev awu_min $bdev_min_write" >> $seqres.full
echo "bdev awu_min $bdev_max_write" >> $seqres.full

# Test that statx atomic values are the same as sysfs values
if [ "$sys_min_write" -ne "$bdev_min_write" ]; then
    echo "bdev min write != sys min write"
fi
if [ "$sys_max_write" -ne "$bdev_max_write" ]; then
    echo "bdev max write != sys max write"
fi

get_supported_bsize

# Test all supported block sizes between bdev min and max
for ((bsize=$bdev_min_write; bsize<=bdev_max_write; bsize*=2)); do
    if [ "$bsize" -ge "$min_bsize" ] && [ "$bsize" -le "$max_bsize" ]; then
        test_atomic_writes $bsize
    fi
done;

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