#!/bin/bash
# This is a basic test to generate some IO in the background for a while
# before running a program that faults a lot of anon pages to measure
# how many THP pages were faulted and how long it took

P=thpavail
WRITE_DEVICE=
READ_DEVICE=
READ_FILE=
FILESYSTEM=
ITERATIONS=10

. $SHELLPACK_INCLUDE/common.sh
. $SHELLPACK_INCLUDE/include-monitor.sh

while [ "$1" != "" ]; do
	case "$1" in
		--read-device)
			READ_DEVICE=$2
			shift 2;;
		--read-file)
			READ_FILE=$2
			shift 2;;
		--write-device)
			WRITE_DEVICE=$2
			shift 2;;
		--iterations)
			ITERATIONS=$2
			shift 2
			;;
		--filesystem)
			FILESYSTEM=$2
			shift 2
			;;
	esac
done

if [ "$READ_DEVICE" != "" -a ! -e "$READ_DEVICE" ]; then
	die Read device "$READ_DEVICE" does not exist
fi
if [ ! -e $WRITE_DEVICE ]; then
	die Write device $WRITE_DEVICE does not exist
	if [ "$FILESYSTEM" = "" ]; then
		die Specify a filesystem
	fi
fi
if [ "$READ_DEVICE" = "" -a "$READ_FILE" = "" ]; then
	die Specify something to read
fi

MEMTOTAL_BYTES=`free -b | grep Mem: | awk '{print $2}'`
MEMTOTAL_THRESHOLD=$((MEMTOTAL_BYTES/6))

if [ "$READ_FILE" != "" ]; then
	echo o Creating $READ_FILE for reading
	dd if=/dev/zero of=$READ_FILE ibs=4096 count=$((MEMTOTAL_BYTES*2/4096))
fi

# Build a basic alloc program
cat > $SHELLPACK_TEMP/alloc.c << EOFA
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#define SIZE $MEMTOTAL_BYTES

int nr_events(char *counter)
{
	int fd, len;
	char vmstat[4096];
	char *p;

	fd = open("/proc/vmstat", O_RDONLY);
	if (fd < 0) {
		perror("open");
		exit(-1);
	}

	len = read(fd, vmstat, sizeof(vmstat));
	if (len < 0) {
		perror("read");
		exit(-1);
	}
	if (len == sizeof(vmstat)) {
		printf("buf exceeded\n");
		exit(-1);
	}
	close(fd);

	vmstat[len] = 0;
	p = strstr(vmstat, counter);
	return strtoul(p + strlen(counter), NULL, 10);
}

int main(int argc, char **argv) {
	int nr_thp_before, nr_thp;
	int nr_thp_fault_alloc_before, nr_thp_fault_alloc;
	int nr_thp_fault_fallback_before, nr_thp_fault_fallback;
	int i;
	int stride = getpagesize();
	char *buf = mmap(0, SIZE, PROT_READ|PROT_WRITE,
		MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);

	if (buf == MAP_FAILED) {
		perror("mmap");
		exit(-1);
	}

	nr_thp_before                = nr_events("nr_anon_transparent_hugepages ");
	nr_thp_fault_alloc_before    = nr_events("thp_fault_alloc ");
	nr_thp_fault_fallback_before = nr_events("thp_fault_fallback ");
	for (i = 0; i < SIZE; i += stride)
		buf[i] = i;
	nr_thp                = nr_events("nr_anon_transparent_hugepages") - nr_thp_before;
	nr_thp_fault_alloc    = nr_events("thp_fault_alloc") - nr_thp_fault_alloc_before;
	nr_thp_fault_fallback = nr_events("thp_fault_fallback") - nr_thp_fault_fallback_before;
	if (nr_thp < 0)
		nr_thp = 0;

	printf("%d %d %d\n", nr_thp_fault_alloc, nr_thp_fault_fallback, nr_thp);

	munmap(buf, SIZE);
	return(0);
}
EOFA
gcc $SHELLPACK_TEMP/alloc.c -o $SHELLPACK_TEMP/alloc || die Failed to build alloc program

# Build a loop copy script
cat > $SHELLPACK_TEMP/loopcp.sh << EOFB
#!/bin/bash

PID_CP=
EXITING=0

shutdown_cp() {
	export EXITING=1
}

trap shutdown_cp TERM
while [ 1 ]; do
        cp \$@ 2> /dev/null &
        PID_CP=\$!
	echo o start \$PID_CP cp \$@
        while [ -e /proc/\$PID_CP/status -a \$EXITING -eq 0 ]; do
                sleep 1
        done
	if [ \$EXITING -eq 1 ]; then
        	while [ -e /proc/\$PID_CP/status ]; do
			echo o shutting down cp pid \$PID_CP
                	kill -9 \$PID_CP
                	sleep 1
        	done
		exit 0
	fi
done
EOFB
chmod u+x $SHELLPACK_TEMP/loopcp.sh

mkdir $SHELLPACK_TEMP/mnt
mkfs -t $FILESYSTEM $WRITE_DEVICE || die Failed to mkfs on $WRITE_DEVICE
mount $WRITE_DEVICE $SHELLPACK_TEMP/mnt || die Failed to mount device $WRITE_DEVICE

monitor_pre_hook $LOGDIR_RESULTS thpavail
for ITERATION in `seq 1 $ITERATIONS`; do
	echo Iteration $ITERATION

	echo o Settling system for reset: nr_dirty == `grep "^nr_dirty " /proc/vmstat | awk '{print $2}'`
	NR_DIRTY=1025
	while [ $NR_DIRTY -gt 1024 ]; do
		sync
		echo 3 > /proc/sys/vm/drop_caches
		sync
		NR_DIRTY=`grep "^nr_dirty " /proc/vmstat | awk '{print $2}'`
		echo o Waiting system to settle: nr_dirty == $NR_DIRTY
	done
	echo 3 > /proc/sys/vm/drop_caches

	MEMFREE_CURRENT=`free -b | grep Mem: | awk '{print $4}'`
	echo o mem free $MEMFREE_CURRENT
	echo o threshold $MEMTOTAL_THRESHOLD

	if [ "$READ_DEVICE" != "" ]; then
		$SHELLPACK_TEMP/loopcp.sh $READ_DEVICE /dev/null &
		PID_READ_DEVICE=$!
	fi
	if [ "$READ_FILE" != "" ]; then
		$SHELLPACK_TEMP/loopcp.sh $READ_FILE /dev/null &
		PID_READ_FILE=$!
	fi

	$SHELLPACK_TEMP/loopcp.sh /dev/zero $SHELLPACK_TEMP/mnt &
	PID_WRITE=$!

	while [ $MEMFREE_CURRENT -gt $MEMTOTAL_THRESHOLD ]; do
		echo o $MEMFREE_CURRENT gt $MEMTOTAL_THRESHOLD
		sleep 1
		if [ "$PID_READ_DEVICE" != "" -a ! -e "/proc/$PID_READ_DEVICE/status" ]; then
			kill -TERM $PID_READ_FILE
			kill -TERM $PID_WRITE
			die Reading process devices died unexpectedly
		fi
		if [ "$PID_READ_FILE" != "" -a ! -e "/proc/$PID_READ_FILE/status" ]; then
			kill -TERM $PID_READ_DEVICE
			kill -TERM $PID_WRITE
			die Reading process files died unexpectedly
		fi
		if [ ! -e /proc/$PID_WRITE/status ]; then
			kill -TERM $PID_READ_FILE
			kill -TERM $PID_READ_DEVICE
			die Writing process died unexpectedly
		fi
		MEMFREE_CURRENT=`free -b | grep Mem: | awk '{print $4}'`
	done
	sleep 2
	if [ "$PID_READ_DEVICE" != "" -a ! -e "/proc/$PID_READ_DEVICE/status" ]; then
		kill -TERM $PID_WRITE
		kill -TERM $PID_READ_FILE
		die Reading process died unexpectedly
	fi
	if [ "$PID_READ_FILE" != "" -a ! -e "/proc/$PID_READ_FILE/status" ]; then
		kill -TERM $PID_WRITE
		kill -TERM $PID_READ_FILE
		die Reading process died unexpectedly
	fi
	if [ ! -e /proc/$PID_WRITE/status ]; then
		kill -TERM $PID_READ_FILE
		kill -TERM $PID_READ_DEVICE
		die Writing process died unexpectedly
	fi

	echo o $MEMFREE_CURRENT lt $MEMTOTAL_THRESHOLD
	THP_STATS=`/usr/bin/time -f "%S %U %e" -o $SHELLPACK_TEMP/time $SHELLPACK_TEMP/alloc`
	DURATION=`cat $SHELLPACK_TEMP/time`

	echo $DURATION :: $THP_STATS | tee -a $LOGDIR_RESULTS/thp.log

	while [ "$PID_READ_DEVICE" != "" -a -e "/proc/$PID_READ_DEVICE/status" ]; do
		echo o Waiting on read pid device $PID_READ_DEVICE to exit
		kill -TERM $PID_READ_DEVICE
		sleep 2
	done
	while [ "$PID_READ_FILE" != "" -a -e "/proc/$PID_READ_FILE/status" ]; do
		echo o Waiting on read pid file $PID_READ_FILE to exit
		kill -TERM $PID_READ_FILE
		sleep 2
	done
	while [ -e /proc/$PID_WRITE/status ]; do
		echo o Waiting on write pid $PID_WRITE to exit
		kill -TERM $PID_WRITE
		sleep 2
	done
	rm $SHELLPACK_TEMP/mnt/zero
done
monitor_post_hook $LOGDIR_RESULTS thpavail

umount $SHELLPACK_TEMP/mnt
echo $SHELLPACK_SUCCESS
#### Description Basic test for availability of THP
#### Details thpavail-bench 39
