From 2e2699c48a7e6b7cbf13611a601a6cd264a80ec5 Mon Sep 17 00:00:00 2001
From: Peter Holm <pho@FreeBSD.org>
Date: Thu, 28 Nov 2024 13:16:35 +0100
Subject: [PATCH] stress2: Added new tmpfs test scenarios

---
 tools/test/stress2/misc/tmpfs26.sh | 179 +++++++++++++++++++++++++++++
 tools/test/stress2/misc/tmpfs27.sh |  49 ++++++++
 tools/test/stress2/misc/tmpfs28.sh |  61 ++++++++++
 3 files changed, 289 insertions(+)
 create mode 100755 tools/test/stress2/misc/tmpfs26.sh
 create mode 100755 tools/test/stress2/misc/tmpfs27.sh
 create mode 100755 tools/test/stress2/misc/tmpfs28.sh

diff --git a/tools/test/stress2/misc/tmpfs26.sh b/tools/test/stress2/misc/tmpfs26.sh
new file mode 100755
index 000000000000..25fa59ff37c3
--- /dev/null
+++ b/tools/test/stress2/misc/tmpfs26.sh
@@ -0,0 +1,179 @@
+#!/bin/sh
+
+# Bug 272678 - VFS: Incorrect data in read from concurrent write 
+
+# Test scenario by: Kristian Nielsen <knielsen@knielsen-hq.org>
+
+. ../default.cfg
+
+prog=$(basename "$0" .sh)
+cat > /tmp/$prog.c <<EOF
+#include <stdio.h>
+#include <pthread.h>
+#include <sys/select.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAGIC 0x42
+
+const char *filename = "testfile.bin";
+
+static FILE *write_file;
+static FILE *read_file;
+static pthread_mutex_t write_mutex;
+static pthread_cond_t write_cond;
+static pthread_mutex_t read_mutex;
+static pthread_cond_t read_cond;
+static pthread_mutex_t state_mutex;
+static pthread_cond_t state_cond;
+static int write_state;
+static int read_state;
+
+void *
+writer_routine(void *arg __unused)
+{
+	unsigned char data[44];
+
+	memset(data, MAGIC, sizeof(data));
+	pthread_mutex_lock(&write_mutex);
+
+	for (;;) {
+
+		while (write_state != 1)
+			pthread_cond_wait(&write_cond, &write_mutex);
+
+		fwrite(data, 1, sizeof(data), write_file);
+		fflush(write_file);
+
+		pthread_mutex_lock(&state_mutex);
+		write_state = 2;
+		pthread_cond_signal(&state_cond);
+		pthread_mutex_unlock(&state_mutex);
+	}
+}
+
+void *
+reader_routine(void *arg __unused)
+{
+
+	for (;;) {
+		unsigned char buf[387];
+		int len;
+
+		while (read_state != 1)
+			pthread_cond_wait(&read_cond, &read_mutex);
+
+		len = fread(buf, 1, sizeof(buf), read_file);
+		if (len < (int)sizeof(buf) && ferror(read_file)) {
+			perror(" read file");
+			exit(1);
+		}
+		for (int i = 0; i < len; ++i) {
+			if (buf[i] != MAGIC) {
+				fprintf(stderr, "ERROR! invalid value read 0x%2x at %d of %d, pos %ld\n",
+						buf[i], i, len, ftell(read_file));
+				exit(126);
+			}
+		}
+
+		pthread_mutex_lock(&state_mutex);
+		read_state = 2;
+		pthread_cond_signal(&state_cond);
+		pthread_mutex_unlock(&state_mutex);
+	}
+}
+
+void
+create_threads(void)
+{
+	pthread_t write_thread_id, read_thread_id;
+	pthread_attr_t attr;
+
+	pthread_mutex_init(&write_mutex, NULL);
+	pthread_mutex_init(&read_mutex, NULL);
+	pthread_mutex_init(&state_mutex, NULL);
+	pthread_cond_init(&write_cond, NULL);
+	pthread_cond_init(&read_cond, NULL);
+	pthread_cond_init(&state_cond, NULL);
+
+	pthread_attr_init(&attr);
+	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
+	pthread_create(&write_thread_id, &attr, writer_routine, NULL);
+	pthread_create(&read_thread_id, &attr, reader_routine, NULL);
+}
+
+int
+main(int argc, char *argv[])
+{
+	int num_iter = 1000;
+	int i;
+	unsigned char buf[343];
+
+	if (argc >= 2)
+		num_iter = atoi(argv[1]);
+
+	write_state = 0;
+	read_state = 0;
+
+	create_threads();
+	memset(buf, MAGIC, sizeof(buf));
+
+	for (i = 0; i < num_iter; ++i) {
+		/* Write the first part of the file. */
+		pthread_mutex_lock(&write_mutex);
+		write_file = fopen(filename, "wb");
+		if (!write_file) {
+			perror(" open file");
+			exit(1);
+		}
+		fwrite(buf, 1, sizeof(buf), write_file);
+		fflush(write_file);
+
+		/* Open a read handle on the file. */
+		pthread_mutex_lock(&read_mutex);
+		read_file = fopen(filename, "rb");
+		if (!read_file) {
+			perror(" open read file");
+			exit(1);
+		}
+
+		write_state = 1;
+		read_state = 1;
+		pthread_cond_signal(&write_cond);
+		pthread_mutex_unlock(&write_mutex);
+		pthread_cond_signal(&read_cond);
+		pthread_mutex_unlock(&read_mutex);
+
+		pthread_mutex_lock(&state_mutex);
+		while (write_state != 2 || read_state != 2)
+			pthread_cond_wait(&state_cond, &state_mutex);
+		pthread_mutex_unlock(&state_mutex);
+
+		/* Close and remove the file, ready for another iteration. */
+		pthread_mutex_lock(&write_mutex);
+		fclose(write_file);
+		write_state = 0;
+		pthread_mutex_unlock(&write_mutex);
+
+		pthread_mutex_lock(&read_mutex);
+		fclose(read_file);
+		read_state = 0;
+		pthread_mutex_unlock(&read_mutex);
+
+		unlink(filename);
+	}
+
+	return (0);
+}
+EOF
+mycc -o /tmp/$prog -Wall -Wextra -O2 /tmp/$prog.c -lpthread || exit 1
+
+mount -t tmpfs dummy $mntpoint
+cd $mntpoint
+/tmp/$prog; s=$?
+cd -
+umount $mntpoint
+
+rm /tmp/$prog /tmp/$prog.c
+exit $s
diff --git a/tools/test/stress2/misc/tmpfs27.sh b/tools/test/stress2/misc/tmpfs27.sh
new file mode 100755
index 000000000000..5479dcd9188a
--- /dev/null
+++ b/tools/test/stress2/misc/tmpfs27.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2024 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+# umount FS with memory mapped file. tmpfs version.
+
+# "panic: object with writable mappings does not have a reference" seen:
+# https://people.freebsd.org/~pho/stress/log/log0518.txt
+
+[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
+
+. ../default.cfg
+
+set -eu
+prog=$(basename "$0" .sh)
+here=`pwd`
+mp1=$mntpoint
+
+mount -t tmpfs dummy $mp1
+
+export RUNDIR=$mp1/stressX
+export runRUNTIME=2m
+export LOAD=70
+export mmapLOAD=100
+export TESTPROGS="testcases/mmap/mmap testcases/swap/swap"
+set +e
+
+(cd ..; ./testcases/run/run $TESTPROGS > /dev/null 2>&1) & rpid=$!
+sleep 5
+
+start=`date +%s`
+while [ $((`date +%s` - start)) -lt 120 ]; do
+	umount -f $mp1 &&
+	    mount -t tmpfs dummy $mp1
+	mount | grep -q "on $mp1 " || break
+	pgrep -q mmap || break
+done
+pkill run swap mmap
+while pgrep -q swap; do pkill swap; done
+wait $rpid
+
+while mount | grep -q "on $mp1 "; do
+	umount $mp1
+done
+exit 0
diff --git a/tools/test/stress2/misc/tmpfs28.sh b/tools/test/stress2/misc/tmpfs28.sh
new file mode 100755
index 000000000000..d73e957b7f28
--- /dev/null
+++ b/tools/test/stress2/misc/tmpfs28.sh
@@ -0,0 +1,61 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2024 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+# A SEEK_HOLE / SEEK_DATA test scenario, variation of tmpfs24.sh
+
+# A regression test for "40c1672e886b - main - swap_pager: fix
+# seek_data with invalid first page"
+
+[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
+
+. ../default.cfg
+
+prog=$(basename "$0" .sh)
+exp=/tmp/$prog.exp
+here=`pwd`
+log=/tmp/$prog.log
+
+cc -o /tmp/lsholes -Wall -Wextra -O2 $here/../tools/lsholes.c | exit 1
+cat > $exp <<EXP
+Min hole size is 4096, file size is 524288000.
+data #1 @ 0, size=4096)
+hole #2 @ 4096, size=4096
+data #3 @ 8192, size=4096)
+hole #4 @ 12288, size=4096
+data #5 @ 16384, size=4096)
+hole #6 @ 20480, size=524267520
+EXP
+
+set -eu
+mount -t tmpfs dummy $mntpoint
+set +e
+
+file=$mntpoint/file
+copy=$mntpoint/copy
+truncate -s 500m $file
+bs=`getconf MIN_HOLE_SIZE $file`
+printf "\001" | dd of=$file seek=$((0*bs)) bs=1 count=1 conv=notrunc status=none
+printf "\002" | dd of=$file seek=$((2*bs)) bs=1 count=1 conv=notrunc status=none
+printf "\003" | dd of=$file seek=$((4*bs)) bs=1 count=1 conv=notrunc status=none
+s1=0
+s2=0
+s3=0
+/tmp/lsholes $file > $log 2>&1 || s1=1
+
+cmp -s $exp $log || { s2=2; sdiff $exp $log; }
+
+$here/../testcases/swap/swap -t 2m -i 20 -h > /dev/null &
+sleep 10
+cp $file $copy
+while pkill swap; do :; done
+wait
+cmp $file $copy || { echo "copy error"; s3=4; }
+
+umount $mntpoint
+rm -f /tmp/lsholes $exp $log
+exit $((s1 + s2 + s3))