xref: /freebsd/tools/test/stress2/misc/mmap47.sh (revision 94a294e59d1772f316fef613b83e92f37294954c)
1#!/bin/sh
2
3#
4# Copyright (c) 2024 Peter Holm <pho@FreeBSD.org>
5#
6# SPDX-License-Identifier: BSD-2-Clause
7#
8
9# Demonstrate issue described in:
10# [Bug 276002] nfscl: data corruption using both copy_file_range and mmap'd I/O
11
12# This version only uses mapped read/write, read(2)/write(2) and ftruncate(2)
13
14# Issue seen:
15
16# 19:50:53 Start test of mmap47.sh
17# 19:51:56, elapsed 0 days, 00:01.03
18# 19:53:01, elapsed 0 days, 00:02.08
19# 19:54:06, elapsed 0 days, 00:03.13
20# 19:55:40, elapsed 0 days, 00:04.47
21# 617c617
22# < 0023200    80  81  82  83  84  85  86  87  88  89  8a  8b  8c  8d  8e  8f
23# ---
24# > 0023200    80  81  82  83  84  85  86  87  88  89  8a  8b  8c  8d  8e  ee
25# 256 -rw-------  1 root wheel 262144 Mar  1 19:56 file
26# 256 -rw-------  1 root wheel 262144 Mar  1 19:55 file.orig
27# 19:56:44, elapsed 0 days, 00:05.51
28# Failed with exit code 2 after 5 loops of mmap47.sh.
29# 19:56 /usr/src/tools/test/stress2/misc $
30
31[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
32. ../default.cfg
33set -u
34prog=$(basename "$0" .sh)
35log=/tmp/$prog.log
36serial=/tmp/$prog.serial
37grep -q $mntpoint /etc/exports ||
38    { echo "$mntpoint missing from /etc/exports"; exit 0; }
39rpcinfo 2>/dev/null | grep -q mountd || exit 0
40
41cat > /tmp/$prog.c <<EOF
42#include <sys/mman.h>
43#include <sys/stat.h>
44
45#include <err.h>
46#include <fcntl.h>
47#include <pthread.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <unistd.h>
52
53static off_t siz;
54static pthread_mutex_t write_mutex;
55static int fd, go;
56static char *cp;
57
58static void *
59memread(void *arg __unused)
60{
61	int i;
62	char c;
63
64	while (go == -1)
65		usleep(50);
66	while (go == 1) {
67		i = arc4random() % siz;
68		c = cp[i];
69		if (c != 0x77) /* No unused vars here */
70			usleep(arc4random() % 200);
71	}
72	return (0);
73}
74
75static void *
76memwrite(void *arg __unused)
77{
78	int i;
79	char c;
80
81	while (go == -1)
82		usleep(50);
83	while (go == 1) {
84		i = arc4random() % siz;
85		pthread_mutex_lock(&write_mutex);
86		c = cp[i];
87		cp[i] = 0xee;	/* This value seems to linger with NFS */
88		cp[i] = c;
89		pthread_mutex_unlock(&write_mutex);
90		usleep(arc4random() % 200);
91	}
92	return (0);
93}
94
95static void *
96wr(void *arg __unused)
97{
98	off_t pos;
99	int r, s;
100	char buf[1024];
101
102	while (go == -1)
103		usleep(50);
104	while (go == 1) {
105		s = arc4random() % sizeof(buf) + 1;
106		pos = arc4random() % (siz - s);
107		pthread_mutex_lock(&write_mutex);
108		if (lseek(fd, pos, SEEK_SET) == -1)
109			err(1, "lseek(%d)", (int)pos);
110		if ((r = read(fd, buf, s)) != s) {
111			fprintf(stderr, "r = %d, s = %d, pos = %d\n", r, s, (int)pos);
112			err(1, "read():2");
113		}
114		if (lseek(fd, pos, SEEK_SET) == -1)
115			err(1, "lseek(%d)", (int)pos);
116		if (write(fd, buf, s) != s)
117			err(1, "write()");
118		pthread_mutex_unlock(&write_mutex);
119		usleep(arc4random() % 200);
120	}
121	return (0);
122}
123
124/* Both ftruncate() and fdatasync() triggers the problem */
125
126static void *
127tr(void *arg __unused)
128{
129	while (go == -1)
130		usleep(50);
131	while (go == 1) {
132#if 0
133		if (ftruncate(fd, siz) == -1) /* No size change */
134			err(1, "truncate)");
135#else
136		if (fdatasync(fd) == -1)
137			err(1, "fdatasync()");
138#endif
139		usleep(arc4random() % 1000);
140	}
141	return (0);
142}
143
144int
145main(int argc, char *argv[])
146{
147	struct stat st;
148	pthread_t tp[31];
149	int e, i, idx;
150
151	if (argc != 2) {
152		fprintf(stderr, "Usage: %s <file>\n", argv[0]);
153		exit(1);
154	}
155	if ((fd = open(argv[1], O_RDWR)) == -1)
156		err(1, "open(%s)", argv[1]);
157	if (fstat(fd, &st) == -1)
158		err(1, "stat(%s)", argv[1]);
159	siz = st.st_size;
160	cp = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
161	if (cp == MAP_FAILED)
162		err(1, "mmap()");
163
164	go = -1;
165	pthread_mutex_init(&write_mutex, NULL);
166	idx = 0;
167	for (i = 0; i < (int)(arc4random() % 10 + 1); i++) {
168		if ((e = pthread_create(&tp[idx++], NULL, memread, NULL)) != 0)
169			errc(1, e, "pthread_create");
170	}
171	for (i = 0; i < (int)(arc4random() % 10 + 1); i++) {
172		if ((e = pthread_create(&tp[idx++], NULL, memwrite, NULL)) != 0)
173			errc(1, e, "pthread_create");
174	}
175	for (i = 0; i < (int)(arc4random() % 10 + 1); i++) {
176		if ((e = pthread_create(&tp[idx++], NULL, wr, NULL)) != 0)
177			errc(1, e, "pthread_create");
178	}
179	if ((e = pthread_create(&tp[idx++], NULL, tr, NULL)) != 0)
180		errc(1, e, "pthread_create");
181
182	sleep(1);
183	go = 1;
184	sleep(60);
185	go = 0;
186	for (i = 0; i < idx; i++)
187		pthread_join(tp[i], NULL);
188	if (munmap(cp, siz) == -1)
189		err(1, "munmap()");
190	close(fd);
191}
192EOF
193mycc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c -lpthread || exit 1
194
195mycc -o $serial -Wall -Wextra -O2 ../tools/serial.c || exit 1
196mount | grep -q "on $mntpoint " && umount -f $mntpoint
197mdconfig -l | grep -q md$mdstart && mdconfig -d -u $mdstart
198mdconfig -s 5g -u $mdstart
199newfs -n $newfs_flags /dev/md$mdstart > /dev/null
200mount /dev/md$mdstart $mntpoint
201
202mp2=${mntpoint}2
203mkdir -p $mp2
204mount | grep -q "on $mp2 " && umount -f $mp2
205mount -t nfs -o retrycnt=3 127.0.0.1:$mntpoint $mp2 || exit 1
206sleep .2
207
208here=`pwd`
209cd $mp2
210$here/../testcases/swap/swap -t 5m -i 20 > /dev/null &
211sleep 2
212
213size=262144
214$serial file $size
215cp file file.orig
216
217s=0
218/tmp/$prog file || s=1
219
220while pgrep -q swap; do pkill swap; done
221wait
222if ! cmp -s file.orig file; then
223	od -t x1 file.orig > /var/tmp/$prog.file1
224	od -t x1 file      > /var/tmp/$prog.file2
225	diff /var/tmp/$prog.file1 /var/tmp/$prog.file2 > $log
226	head -20 $log
227	rm /var/tmp/$prog.file1 /var/tmp/$prog.file2
228	ls -ls file.orig file
229	s=2
230fi
231
232cd $here
233umount $mp2
234umount $mntpoint
235mdconfig -d -u $mdstart
236rm -f $serial /tmp/$prog /tmp/$prog.c $log
237exit $s
238