git: fb7687edea52 - main - stress2: New tests added

From: Peter Holm <pho_at_FreeBSD.org>
Date: Thu, 03 Apr 2025 09:21:37 UTC
The branch main has been updated by pho:

URL: https://cgit.FreeBSD.org/src/commit/?id=fb7687edea52ff1ddbae5fe947055fb37bd23ae3

commit fb7687edea52ff1ddbae5fe947055fb37bd23ae3
Author:     Peter Holm <pho@FreeBSD.org>
AuthorDate: 2025-04-03 09:20:41 +0000
Commit:     Peter Holm <pho@FreeBSD.org>
CommitDate: 2025-04-03 09:20:41 +0000

    stress2: New tests added
---
 datamove6.sh   |  50 ++++++
 ftruncate3.sh  |  96 +++++++++++
 growfs3.sh     |  41 +++++
 kcmp.sh        |  67 +++++++
 marcus8.sh     |  41 +++++
 mprotect3.sh   |  70 ++++++++
 mprotect4.sh   | 109 ++++++++++++
 mprotect5.sh   | 118 +++++++++++++
 mprotect6.sh   | 146 ++++++++++++++++
 msdos17.sh     | 144 ++++++++++++++++
 msdos20.sh     |  87 ++++++++++
 msdos21.sh     |  25 +++
 newfs8.sh      |  69 ++++++++
 nullfs31.sh    |  75 ++++++++
 nullfs32.sh    |  43 +++++
 pthread10.sh   | 106 ++++++++++++
 rangelocks.sh  | 194 +++++++++++++++++++++
 rangelocks2.sh | 178 +++++++++++++++++++
 rename16.sh    | 261 ++++++++++++++++++++++++++++
 rmdir.sh       | 119 +++++++++++++
 rsync.sh       |  49 ++++++
 rsync2.sh      |  17 ++
 rsync3.sh      |  43 +++++
 seekhole2.sh   |  65 +++++++
 syzkaller71.sh | 171 ++++++++++++++++++
 syzkaller72.sh |  70 ++++++++
 syzkaller73.sh | 537 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 27 files changed, 2991 insertions(+)

diff --git a/datamove6.sh b/datamove6.sh
new file mode 100755
index 000000000000..88bfea425bdc
--- /dev/null
+++ b/datamove6.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2025 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+# Variation of the datamove.sh, using MSDOSFS
+
+# No problems seen
+
+[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
+
+. ../default.cfg
+
+here=`pwd`
+prog=$(basename "$0" .sh)
+cd /tmp
+sed '1,/^EOF/d' < $here/datamove.sh > $prog.c
+mycc -o $prog -Wall -Wextra -O2 -g $prog.c
+rm -f $prog.c
+
+set -eu
+mount | grep "on $mntpoint " | grep -q /dev/md && umount -f $mntpoint
+[ -c /dev/md$mdstart ] &&  mdconfig -d -u $mdstart
+mdconfig -a -t swap -s 2g -u $mdstart
+newfs_msdos -F 32 -b 8192 /dev/md$mdstart 2> /dev/null
+#mount -t msdosfs /dev/md$mdstart $mntpoint
+mount_msdosfs -m 777 /dev/md$mdstart $mntpoint
+set +e
+
+$here/../testcases/swap/swap -t 5m -i 100 -h &
+for i in `jot 5`; do
+	su $testuser -c "cd $mntpoint; /tmp/$prog"
+done
+mv /tmp/$prog $mntpoint
+for i in `jot 5`; do
+	mkdir -p $mntpoint/datamove.dir.$i
+	cd $mntpoint/datamove.dir.$i
+	$mntpoint/$prog &
+done
+pkill swap
+wait
+while mount | grep -q $mntpoint; do
+	umount -f $mntpoint > /dev/null 2>&1
+done
+mdconfig -d -u $mdstart
+
+exit 0
diff --git a/ftruncate3.sh b/ftruncate3.sh
new file mode 100755
index 000000000000..7373ae8d22a8
--- /dev/null
+++ b/ftruncate3.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+# Test scenario from Bug 64816: [nfs] [patch] mmap and/or ftruncate does not work correctly on nfs mounted file systems
+
+. ../default.cfg
+
+set -u
+grep -q $mntpoint /etc/exports ||
+    { echo "$mntpoint missing from /etc/exports"; exit 0; }
+rpcinfo 2>/dev/null | grep -q mountd || exit 0
+
+prog=$(basename "$0" .sh)
+cat > /tmp/$prog.c <<EOF
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void error(char *msg)
+{
+	fprintf(stderr, "Error: %s\nSystem error %d: %s\n", msg, errno, strerror(errno));
+	exit(-1);
+}
+
+#define SZ 1024 // Less than page size
+
+int main(int argn, char *argv[])
+{
+	int fd, s;
+	char buffer[SZ];
+	char *map;
+
+	if (argn!=2)
+	{
+		fprintf(stderr, "Usage:\n %s [filename]\n", argv[0]);
+		_exit(-1);
+	}
+
+	memset(buffer, 0, SZ);
+	s = 0;
+
+	fd=open(argv[1], O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
+	if (fd==-1)
+		error("Could not create file");
+
+	if (write(fd, buffer, SZ)!=SZ)
+		error("Could not write buffer");
+
+	map=mmap(NULL, SZ, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+	if (map==MAP_FAILED)
+		error("Map failed");
+	map[SZ-1]=1;
+
+	if (ftruncate(fd, SZ+1)!=0)
+		error("Could not truncate file");
+
+	if (map[SZ-1]==1)
+		printf("Test passed\n");
+	else {
+		printf("Test failed\n");
+		s = 1;
+	}
+
+	exit(s);
+}
+EOF
+mycc -o /tmp/$prog -Wall -Wextra -O0 -g /tmp/$prog.c || exit 1
+
+mount | grep -q "on $mntpoint " && umount -f $mntpoint
+mdconfig -l | grep -q md$mdstart && mdconfig -d -u $mdstart
+mdconfig -s 1g -u $mdstart
+newfs -n $newfs_flags /dev/md$mdstart > /dev/null
+mount /dev/md$mdstart $mntpoint
+
+mp2=${mntpoint}2
+mkdir -p $mp2
+mount | grep -q "on $mp2 " && umount -f $mp2
+mount -t nfs -o retrycnt=3 127.0.0.1:$mntpoint $mp2 || exit 1
+sleep .2
+mount | grep  $mntpoint
+
+cd $mp2
+/tmp/$prog $prog.data; s=$?
+ls -ls $mp2/$prog.data
+cd -
+
+umount $mp2
+umount $mntpoint
+mdconfig -d -u $mdstart
+rm -f /tmp/$prog /tmp/$prog.c
+exit $s
diff --git a/growfs3.sh b/growfs3.sh
new file mode 100755
index 000000000000..33e8327cdbbc
--- /dev/null
+++ b/growfs3.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2025 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+. ../default.cfg
+[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
+
+set -eu
+prog=$(basename "$0" .sh)
+log=/tmp/$prog.log
+mount | grep "on $mntpoint " | grep -q /dev/md && umount -f $mntpoint
+[ -c /dev/md$mdstart ] &&  mdconfig -d -u $mdstart
+mdconfig -a -t swap -s 32g -u $mdstart
+/sbin/gpart create -s GPT md$mdstart > /dev/null
+/sbin/gpart add -t freebsd-ufs -s 2g -a 4k md$mdstart > /dev/null
+set +e
+
+newfs_flags=$(echo "-O1" "-O2" "-U" "-j" | awk -v N=`jot -r 1 1 4` '{print $N}')
+echo "newfs $newfs_flags md${mdstart}p1"
+newfs $newfs_flags md${mdstart}p1 > /dev/null
+[ "$newfs_flags" = "-O2" ] &&
+    tunefs -n disable md${mdstart}p1 > /dev/null 2>&1
+mount /dev/md${mdstart}p1 $mntpoint
+cp -r /usr/include $mntpoint/inc1
+umount $mntpoint
+
+gpart resize -i 1 -s 31g -a 4k md$mdstart
+growfs -y md${mdstart}p1 > /dev/null
+
+mount /dev/md${mdstart}p1 $mntpoint
+cp -r /usr/include $mntpoint/inc2
+umount $mntpoint
+fsck -fy /dev/md${mdstart}p1 > $log 2>&1; s=$?
+grep -q "WAS MODIFIED" $log && { cat $log; s=1; }
+rm -f $log
+mdconfig -d -u $mdstart
+exit $s
diff --git a/kcmp.sh b/kcmp.sh
new file mode 100755
index 000000000000..7c571dd8e8a1
--- /dev/null
+++ b/kcmp.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2025 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+# Seen:
+# UID  PID PPID  C PRI NI   VSZ  RSS MWCHAN   STAT TT     TIME COMMAND
+#   0 3730 3668 11  20  0 13596 2904 exithold DE+   0  1:59.68 ./kcmp
+
+# Fixed by: 5b3e5c6ce3e5
+
+. ../default.cfg
+
+set -u
+prog=$(basename "$0" .sh)
+cat > /tmp/$prog.c <<EOF
+#include <sys/types.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+static void *
+t1(void *data __unused)
+{
+	for (;;)
+		pause();
+
+	return (NULL);
+}
+
+int
+main(void)
+{
+	pid_t p1, p2;
+	pthread_t tid[2];
+	time_t start;
+	uintptr_t idx1, idx2;
+	int r;
+
+	if ((r = pthread_create(&tid[0], NULL, t1, NULL)) != 0)
+		errc(1, r, "pthread_create");
+	if ((r = pthread_create(&tid[1], NULL, t1, NULL)) != 0)
+		errc(1, r, "pthread_create");
+
+	start = time(NULL);
+	while (time(NULL) - start < 60) {
+		idx1 = idx2 = 0;
+		p1 = arc4random() % 1000000;
+		p2 = arc4random() % 1000000;
+		kcmp(p1, p2, KCMP_VM, idx1, idx2);
+	}
+}
+EOF
+mycc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c -lpthread || exit 1
+
+/tmp/$prog
+
+rm /tmp/$prog.c /tmp/$prog
+exit 0
diff --git a/marcus8.sh b/marcus8.sh
new file mode 100755
index 000000000000..0c6110c8ec4c
--- /dev/null
+++ b/marcus8.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2025 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
+
+# Run with marcus.cfg on a 5g swap backed MD with UFS non SU fs.
+# Check for non empty file system after test.
+
+. ../default.cfg
+
+set -u
+mount | grep $mntpoint | grep -q /dev/md && umount -f $mntpoint
+mdconfig -l | grep -q md$mdstart &&  mdconfig -d -u $mdstart
+mdconfig -a -t swap -s 4g -u $mdstart
+newfs_flags=""	# With SU this test runs out of disk space
+newfs $newfs_flags md$mdstart > /dev/null
+tunefs -n disable md$mdstart	# Remove the default SU flag
+mount /dev/md$mdstart $mntpoint
+chmod 777 $mntpoint
+
+export runRUNTIME=5m
+export CTRLDIR=$mntpoint/stressX.control
+export RUNDIR=$mntpoint/stressX
+
+su $testuser -c 'cd ..; ./run.sh marcus.cfg'
+
+nb=`find $RUNDIR | wc -l`
+[ $nb -gt 1 ] && { find $RUNDIR -ls | head -12; s=1; } || s=0
+n=0
+while mount | grep $mntpoint | grep -q /dev/md; do
+	umount $mntpoint || sleep 1
+	[ $((n += 1)) -gt 300 ] && { echo FAIL; exit 1; }
+done
+checkfs /dev/md$mdstart; s2=$?
+mdconfig -d -u $mdstart
+exit $((s + s2))
diff --git a/mprotect3.sh b/mprotect3.sh
new file mode 100755
index 000000000000..9bd4a6f9be79
--- /dev/null
+++ b/mprotect3.sh
@@ -0,0 +1,70 @@
+#!/bin/sh
+
+# Test scenario from:
+# Bug 272585 - calling mprotect in an mmap-ed stack can affect non-target pages 
+# Test scenario by: John F. Carr <jfc mit edu>
+
+. ../default.cfg
+set -u
+prog=$(basename "$0" .sh)
+cat > /tmp/$prog.c <<EOF
+/* Test program from:
+   Bug 272585 - calling mprotect in an mmap-ed stack can affect non-target pages
+ */
+#include <err.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#ifndef MAP_GROWSDOWN
+#define MAP_GROWSDOWN 0
+#endif
+#ifndef MAP_STACK
+#define MAP_STACK 0
+#endif
+
+int main(void)
+{
+  long pagesize;
+  char *addr, *guard;
+  size_t alloc_size;
+
+  pagesize = sysconf(_SC_PAGESIZE);
+  if (pagesize < 0)
+    err(EX_OSERR, "getPAGESIZE");
+
+  alloc_size = 0x200000 + pagesize;
+
+  addr = mmap(0, alloc_size, PROT_READ|PROT_WRITE,
+              MAP_GROWSDOWN|MAP_STACK|MAP_PRIVATE|MAP_ANONYMOUS,
+              -1, 0);
+  if (addr == MAP_FAILED) {
+    err(EX_OSERR, "mmap");
+  }
+
+  /* Only 0x20 causes a failure. */
+  guard = addr + alloc_size - 0x20 * pagesize;
+
+  if (mprotect(guard, pagesize, PROT_NONE)) {
+    err(EX_OSERR, "mprotect");
+  }
+
+  printf("mapped %p..%p, guard at %p\n", addr, addr + alloc_size, guard);
+  fflush(stdout);
+
+  ((volatile char *)guard)[-1];
+
+  return 0;
+}
+EOF
+mycc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c || exit 0
+
+cd /tmp
+./$prog; s=$?
+cd -
+
+rm -f /tmp/$prog /tmp/$prog.c /tmp/$prog.core
+exit $s
diff --git a/mprotect4.sh b/mprotect4.sh
new file mode 100755
index 000000000000..c233d20852a2
--- /dev/null
+++ b/mprotect4.sh
@@ -0,0 +1,109 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2025 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+. ../default.cfg
+set -u
+prog=$(basename "$0" .sh)
+cat > /tmp/$prog.c <<EOF
+/* N readers and 1 writer threaded test scenario */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <assert.h>
+#include <err.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+static int go, n, ps;
+static char *cp;
+static volatile char v;
+
+void *
+rd(void *arg __unused)
+{
+	int i;
+
+	while (go == 0)
+		usleep(100);
+	while (go == 1) {
+		for (i = 0; i < n; i += ps) {
+			v = cp[i];
+		}
+		pthread_yield();
+	}
+	return(NULL);
+}
+
+void
+usage(char *prog) {
+	fprintf(stderr, "Usage: %s <number of threads>\n", prog);
+	_exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	pthread_t *tid;
+	time_t start;
+	int e, i, nb;
+
+	if (argc != 2)
+		usage(argv[0]);
+	if (sscanf(argv[1], "%d", &n) != 1)
+		usage(argv[0]);
+	if (n > 1)
+		n--;
+	if ((tid = calloc(n, sizeof(pthread_t *))) == NULL)
+		err(1, "calloc()");
+
+	ps = getpagesize();
+	cp = mmap(NULL, n * ps, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
+	go = 0;
+	for (i = 0; i < n; i++) {
+		if ((e = pthread_create(&tid[i], NULL, rd, NULL)) != 0)
+			errc(1, e, "pthread_create()");
+	}
+	go = 1;
+
+	nb = 0;
+	start = time(NULL);
+	while (time(NULL) - start < 120) {
+		for (i = 0; i < n; i += ps) {
+			if (mprotect(&cp[i], ps, PROT_READ|PROT_WRITE) == -1)
+				err(1, "mprotect(PROT_READ)");
+			cp[i] = 1;
+			if (mprotect(&cp[i], ps, PROT_READ) == -1)
+				err(1, "mprotect(PROT_READ)");
+			nb++;
+		}
+	}
+	go = 0;
+	for (i = 0; i < n; i++) {
+		if ((e = pthread_join(tid[i], NULL)) != 0)
+			errc(1, e, "pthread_join() in loop %d", i);
+	}
+	if (nb >= 0) {
+#if defined(DEBUG)
+		fprintf(stderr, "%d loops\n", nb);
+#endif
+		;
+	}
+}
+EOF
+mycc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c -lpthread || exit 1
+
+/tmp/$prog `sysctl -n hw.ncpu`; s=$?
+
+rm -d /tmp/$prog /tmp/$prog.c
+exit $s
diff --git a/mprotect5.sh b/mprotect5.sh
new file mode 100755
index 000000000000..ab4d2eeee118
--- /dev/null
+++ b/mprotect5.sh
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2025 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+. ../default.cfg
+set -u
+prog=$(basename "$0" .sh)
+cat > /tmp/$prog.c <<EOF
+/* N writers threaded test scenario */
+
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <assert.h>
+#include <err.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+static pthread_mutex_t write_mutex;
+static int go, n, ps;
+static char *cp, *wp;
+
+void *
+wr(void *arg __unused)
+{
+	while (go == 0)
+		usleep(100);
+	while (go == 1) {
+		pthread_mutex_lock(&write_mutex);
+		if (wp != NULL)
+			*wp += 1;
+		pthread_mutex_unlock(&write_mutex);
+	}
+	return(NULL);
+}
+
+void
+usage(char *prog) {
+	fprintf(stderr, "Usage: %s <number of threads>\n", prog);
+	_exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	pthread_t *tid;
+	time_t start;
+	int e, i, nb;
+
+	if (argc != 2)
+		usage(argv[0]);
+	if (sscanf(argv[1], "%d", &n) != 1)
+		usage(argv[0]);
+	if (n > 1)
+		n--;
+	if ((tid = calloc(n, sizeof(pthread_t *))) == NULL)
+		err(1, "calloc()");
+
+	ps = getpagesize();
+	cp = mmap(NULL, n * ps, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
+	pthread_mutex_init(&write_mutex, NULL);
+	pthread_mutex_lock(&write_mutex);
+	go = 0;
+	for (i = 0; i < n; i++) {
+		if ((e = pthread_create(&tid[i], NULL, wr, NULL)) != 0)
+			errc(1, e, "pthread_create()");
+	}
+	go = 1;
+
+	nb = 0;
+	start = time(NULL);
+	while (time(NULL) - start < 120) {
+		for (i = 0; i < n; i += ps) {
+			pthread_mutex_lock(&write_mutex);
+			if (mprotect(&cp[i], ps, PROT_READ|PROT_WRITE) == -1)
+				err(1, "mprotect(PROT_READ)");
+			cp[i] = 0;
+			wp = &cp[i];
+			pthread_mutex_unlock(&write_mutex);
+
+			usleep(100);
+
+			pthread_mutex_lock(&write_mutex);
+			if (mprotect(&cp[i], ps, PROT_READ) == -1)
+				err(1, "mprotect(PROT_READ)");
+			wp = NULL;
+			pthread_mutex_unlock(&write_mutex);
+			nb++;
+		}
+	}
+	go = 0;
+	for (i = 0; i < n; i++) {
+		if ((e = pthread_join(tid[i], NULL)) != 0)
+			errc(1, e, "pthread_join() in loop %d", i);
+	}
+	if (nb >= 0) {
+#if defined(DEBUG)
+		fprintf(stderr, "%d loops\n", nb);
+#endif
+		;
+	}
+}
+EOF
+mycc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c -lpthread || exit 1
+
+/tmp/$prog `sysctl -n hw.ncpu`; s=$?
+
+rm -d /tmp/$prog /tmp/$prog.c
+exit $s
diff --git a/mprotect6.sh b/mprotect6.sh
new file mode 100755
index 000000000000..ef1443c216d3
--- /dev/null
+++ b/mprotect6.sh
@@ -0,0 +1,146 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2025 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+. ../default.cfg
+set -u
+prog=$(basename "$0" .sh)
+cat > /tmp/$prog.c <<EOF
+#include <sys/types.h>
+#include <sys/mman.h>
+
+#include <assert.h>
+#include <err.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+static pthread_mutex_t write_mutex;
+static volatile int done;
+static int go, n, *once, *p, ps;
+
+static void *
+wr(void *arg)
+{
+	int idx;
+
+	alarm(180);
+	idx = *(int *)arg;
+	while (go == 0)
+		usleep(100);
+	while (go == 1) {
+		while (go == 1 && once[idx] == 0)
+			usleep(100);
+		if (go == 0)
+			break;
+		p[idx]++;
+		once[idx] = 0;
+		pthread_mutex_lock(&write_mutex);
+		done++;
+		pthread_mutex_unlock(&write_mutex);
+	}
+	return(NULL);
+}
+
+static void
+setonce(int val)
+{
+	int i;
+
+	for (i = 0; i < n; i++)
+		once[i] = val;
+}
+
+static void
+usage(char *prog) {
+	fprintf(stderr, "Usage: %s <number of threads>\n", prog);
+	_exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+	pthread_t *tid;
+	time_t start;
+	int *arg;
+	int e, i, nb, r;
+
+	if (argc != 2)
+		usage(argv[0]);
+	if (sscanf(argv[1], "%d", &n) != 1)
+		usage(argv[0]);
+	if (n > 1)
+		n--;
+	if ((tid = calloc(n, sizeof(pthread_t *))) == NULL)
+		err(1, "calloc()");
+	if ((once = calloc(n, sizeof(int *))) == NULL)
+		err(1, "calloc()");
+	setonce(0);
+
+	ps = getpagesize();
+	p = mmap(NULL, n * ps, PROT_READ, MAP_PRIVATE | MAP_ANON, -1, 0);
+	go = 0;
+	pthread_mutex_init(&write_mutex, NULL);
+	for (i = 0; i < n; i++) {
+		arg = malloc(sizeof(int));
+		*arg = i;
+		if ((e = pthread_create(&tid[i], NULL, wr, (void *)arg)) != 0)
+			errc(1, e, "pthread_create()");
+	}
+	go = 1;
+
+	nb = 0;
+	start = time(NULL);
+	while (time(NULL) - start < 120) {
+		if (mprotect(p, n * ps, PROT_READ|PROT_WRITE) == -1)
+			err(1, "mprotect(PROT_READ)");
+		done = 0;
+		setonce(1);
+		while (done != n)
+			usleep(100);
+		if (mprotect(p, n * ps, PROT_READ) == -1)
+			err(1, "mprotect(PROT_READ)");
+		nb++;
+		usleep(100);
+	}
+	go = 0;
+	for (i = 0; i < n; i++) {
+		if ((e = pthread_join(tid[i], NULL)) != 0)
+			errc(1, e, "pthread_join() in loop %d", i);
+	}
+	r = 0;
+	for (i = 1; i < n; i++) {
+		if (p[0] != p[i])
+			r++;
+	}
+	if (r != 0) {
+		fprintf(stderr, "%d loops.\n", nb);
+		for (i = 0; i < n; i++)
+			fprintf(stderr, "p[%3d] = %d\n", i, p[i]);
+	}
+
+	return (r);
+}
+EOF
+mycc -o /tmp/$prog -Wall -Wextra -O0 -g /tmp/$prog.c -lpthread || exit 1
+
+n=`sysctl -n hw.ncpu`
+if [ $# -eq 1 ]; then
+	echo $1 | grep -Eq '^[0-9]+$' && n=$1
+fi
+../testcases/swap/swap -t 2m > /dev/null &
+sleep 10
+/tmp/$prog $n; s=$?
+pkill -9 swap
+wait
+
+rm -d /tmp/$prog /tmp/$prog.c
+exit $s
diff --git a/msdos17.sh b/msdos17.sh
new file mode 100755
index 000000000000..392a9a622b9a
--- /dev/null
+++ b/msdos17.sh
@@ -0,0 +1,144 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2025 Peter Holm <pho@FreeBSD.org>
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+# No problems observed
+
+[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
+
+. ../default.cfg
+
+set -u
+prog=$(basename "$0" .sh)
+mount | grep -q "on $mntpoint " && umount $mntpoint
+[ -c /dev/md$mdstart ] && mdconfig -d -u $mdstart
+
+mdconfig -a -t swap -s 1g -u $mdstart
+gpart create -s bsd md$mdstart > /dev/null
+gpart add -t freebsd-ufs md$mdstart > /dev/null
+part=a
+newfs_msdos -F 32 -b 8192 /dev/md${mdstart}$part > /dev/null || exit 1
+mount -t msdosfs /dev/md${mdstart}$part $mntpoint
+
+here=`pwd`
+cd /tmp
+cat > $prog.c <<EOF
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <machine/atomic.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static volatile u_int *share;
+static char file1[80], file2[80];
+
+#define SYNC 0
+#define STOP 1
+
+static void
+test0(void)
+{
+	struct stat sb;
+
+	while (share[STOP] == 0) {
+		while (share[SYNC] != 0)
+			usleep(100);
+		if (rename(file1, file2) == -1)
+			err(1, "rename(%s, %s)", file1, file2);
+		if (stat(file1, &sb) == 0)
+			err(1, "stat(%s)", file1);
+		atomic_add_int(&share[SYNC], 1);
+	}
+
+	_exit(0);
+}
+
+static void
+test1(void)
+{
+	struct stat sb;
+
+	while (share[STOP] == 0) {
+		while (share[SYNC] != 1)
+			usleep(100);
+		if (rename(file2, file1) == -1)
+			err(1, "rename(%s, %s)", file2, file1);
+		if (stat(file2, &sb) == 0)
+			err(1, "stat(%s)", file2);
+		atomic_add_int(&share[SYNC], -1);
+	}
+
+	_exit(0);
+}
+
+int
+main(void)
+{
+	pid_t pids[2];
+	size_t len;
+	int fd;
+	char cwd[80];
+
+	len = PAGE_SIZE;
+	if ((share = mmap(NULL, len, PROT_READ | PROT_WRITE,
+	    MAP_ANON | MAP_SHARED, -1, 0)) == MAP_FAILED)
+		err(1, "mmap");
+
+	if (getcwd(cwd, sizeof(cwd)) == NULL)
+		err(1, "getcwd()");
+	snprintf(file1, sizeof(file1), "%s/a.%06d", cwd, getpid());
+	snprintf(file2, sizeof(file2), "%s/b.%06d", cwd, getpid());
+	if ((fd = open(file1, O_CREAT, 0640)) == -1)
+		err(1, "open(%s)", file1);
+	close(fd);
+
+	if ((pids[0] = fork()) == 0)
+		test0();
+	if ((pids[1] = fork()) == 0)
+		test1();
+
+	sleep(120);
+	share[STOP] = 1;
+
+	if (waitpid(pids[0], NULL, 0) == -1)
+		err(1, "waitpid(%d)", pids[0]);
+	if (waitpid(pids[1], NULL, 0) == -1)
+		err(1, "waitpid(%d)", pids[1]);
+	unlink(file1);
+	unlink(file2);
+}
+EOF
+mycc -o $prog -Wall $prog.c || exit 1
+rm -f $prog.c
+cd $here
+
+(cd ../testcases/swap; ./swap -t 5m -i 20 -l 100) &
+cd $mntpoint
+pids=""
+for i in `jot 30`; do
+	/tmp/$prog &
+	pids="$pids $!"
+done
+for pid in $pids; do
+	wait $pid
+done
+cd $here
+while pkill swap; do :; done
+wait
+
+umount $mntpoint
+mdconfig -d -u $mdstart
+rm -f /tmp/$prog
+exit 0
diff --git a/msdos20.sh b/msdos20.sh
new file mode 100755
index 000000000000..96c224a629f3
--- /dev/null
*** 2207 LINES SKIPPED ***