#! /bin/bash
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2021 Oracle.  All Rights Reserved.
#
# FS QA Test No. 643
#
# Regression test for commit:
#
# 36ca7943ac18 ("mm/swap: consider max pages in iomap_swapfile_add_extent")
#
# Xu Yu found that the iomap swapfile activation code failed to constrain
# itself to activating however many swap pages that the mm asked us for.  This
# is an deviation in behavior from the classic swapfile code.  It also leads to
# kernel memory corruption if the swapfile is cleverly constructed.
#
. ./common/preamble
_begin_fstest auto swap

# Override the default cleanup function.
_cleanup()
{
	cd /
	rm -f $tmp.*
	test -n "$swapfile" && swapoff $swapfile &> /dev/null
}

. ./common/filter

# real QA test starts here
_supported_fs generic
_require_scratch_swapfile

_scratch_mkfs >> $seqres.full
_scratch_mount >> $seqres.full

# Assuming we're not borrowing a FAT16 partition from Windows 3.1, we need an
# unlikely enough name that we can grep /proc/swaps for this.
swapfile=$SCRATCH_MNT/386spart.par
_format_swapfile $swapfile 1m >> $seqres.full

page_size=$(getconf PAGE_SIZE)

swapfile_blocks() {
	local swapfile="$1"

	grep "$swapfile" /proc/swaps | awk '{print $3}'
}

_swapon_file $swapfile
before_blocks=$(swapfile_blocks "$swapfile")
swapoff $swapfile

# Extend the length of the swapfile but do not rewrite the header.
# The subsequent swapon should set up 1MB worth of blocks, not 2MB.
$XFS_IO_PROG -f -c 'pwrite 1m 1m' $swapfile >> $seqres.full

_swapon_file $swapfile
after_blocks=$(swapfile_blocks "$swapfile")
swapoff $swapfile

# Both swapon attempts should have found approximately the same number of
# blocks.  Unfortunately, mkswap and the kernel are a little odd -- the number
# of pages that mkswap writes into the swapfile header is one page less than
# the file size, and then the kernel itself doesn't always grab all the pages
# advertised in the header.  Hence we let the number of swap pages increase by
# two pages.  I'm looking at you, Mr. 64k pages on arm64...
page_variance=$(( page_size / 512 ))
_within_tolerance "swap blocks" $after_blocks $before_blocks 0 $page_variance -v

echo "pagesize: $page_size; before: $before_blocks; after: $after_blocks" >> $seqres.full

status=0
exit
