#!/bin/sh

# TODO
# заворачивать все устройства в device mapper
# отключение устройства делать через замену устройства на генерирующее ошибки

# md resync continue bug checker

# exit status: 0 - ok, 1 - bug, 2 - other error

status=2

# cleanup on exit
onexit() {
	$mdcmd -f $backdev1 >/dev/null 2>&1
	$mdcmd -f $backdev2 >/dev/null 2>&1
	mdadm -S /dev/$md
	dmsetup remove -f $backdev1
	dmsetup remove -f $backdev2
	losetup -d $loopdev1 $loopdev2
	rm -f $backfile1 $backfile2
}

trap onexit EXIT
trap exit INT

# creating backing devices
# we use file backed loop device and wrap it to device mapper as linear target

backdevsz=32 # 32 Mb
backdevsec=$((backdevsz*1024*2))	# sectors

backfile1=/tmp/mdfile1.$$
backfile2=/tmp/mdfile2.$$

dd if=/dev/zero of=$backfile1 bs=1M count=0 seek=$backdevsz 2>/dev/null
dd if=/dev/zero of=$backfile2 bs=1M count=0 seek=$backdevsz 2>/dev/null 

loopdev1=`losetup -f --show $backfile1`
loopdev2=`losetup -f --show $backfile2`

dm1="dm1_$$"
dm2="dm2_$$"

echo "0 $backdevsec linear $loopdev1 0" | dmsetup create $dm1
echo "0 $backdevsec linear $loopdev2 0" | dmsetup create $dm2

backdev1=/dev/mapper/$dm1
backdev2=/dev/mapper/$dm2

# create md mirror with backing devices
i=0
while [ $i -lt 100 ]; do
	if ! [ -e /dev/md$i ]; then
		mdadm --create /dev/md$i -e 1.0 --level=mirror -n 2 --assume-clean --bitmap-chunk=64 --bitmap=internal $backdev1 $backdev2 || exit $status
		md=md$i
		mdcmd="mdadm -q /dev/$md"
		break
	fi
	i=$((i+1))
done

if [ -z "$md" ]; then
	echo "can't find free md device" >&2
	exit $status
fi

# slow down sync speed
echo 1 > /sys/block/$md/md/sync_speed_min
echo 1 > /sys/block/$md/md/sync_speed_max

# make the first mirror side failing by replacing to a dm error target

dmsetup suspend $dm1
echo "0 $backdevsec error" | dmsetup reload $dm1
dmsetup resume $dm1

# write a first change in the middle of the mirror device
dd if=/dev/urandom of=/dev/$md bs=1M oflag=direct count=1 seek=$((backdevsz/2)) 2>/dev/null

# return the first mirror side to valid device by replacing it to the loop device

while ! $mdcmd -r $backdev1; do sleep 1; done
dmsetup suspend $dm1
echo "0 $backdevsec linear $loopdev1 0" | dmsetup reload $dm1
dmsetup resume $dm1

# re-add the first mirror side and let it resync a little bit
$mdcmd --re-add $backdev1

sleep 0.1

echo 'first resync'
mdadm -D /dev/$md | grep -E '(State|Rebuild Status) :'

# make the first mirror side failing again

dmsetup suspend $dm1
echo "0 $backdevsec error" | dmsetup reload $dm1
dmsetup resume $dm1

# release sync speed to max
echo 1000000 > /sys/block/$md/md/sync_speed_max

# !!! write a first change in the beginning of the mirror device, before the checkpoint of previous resync, this should invoke resync bug
echo "writing random data to the beginning of the mirror"
dd if=/dev/urandom of=/dev/$md bs=1M oflag=direct count=1 seek=0 2>/dev/null

# return the first mirror side to valid device, re-add it again and wait for resync done

while ! $mdcmd -r $backdev1; do sleep 1; done
dmsetup suspend $dm1
echo "0 $backdevsec linear $loopdev1 0" | dmsetup reload $dm1
dmsetup resume $dm1

# slow down sync speed XXX
echo 1 > /sys/block/$md/md/sync_speed_max
$mdcmd --re-add $backdev1


echo 'second resync'
mdadm -D /dev/$md | grep -E '(State|Rebuild Status) :'

# release sync speed to max
#echo 1000000 > /sys/block/$md/md/sync_speed_max
echo 500 > /sys/block/$md/md/sync_speed_max
# wait for resync done
while true; do
	read mdstate < /sys/block/$md/md/degraded
	if [ $mdstate = 0 ]; then break; fi
	mdadm -D /dev/$md | grep -E '(State|Rebuild Status) :'
	sleep 1
done

echo 'resync done'
mdadm -D /dev/$md | grep -E '(State|Rebuild Status) :'

# compare data section of both backing devices
mdsize=`blockdev --getsize64 /dev/$md`
echo
echo '---------'
if cmp -n $mdsize $backdev1 $backdev2; then
	echo 'Ok, bug not found'
	status=0
else
	echo 'Bug found! The backing devices differ'
	status=1
fi
echo '---------'
echo

exit $status
