xref: /freebsd/tools/test/stress2/misc/mmap44.sh (revision a91a2465)
1#!/bin/sh
2
3#
4# SPDX-License-Identifier: BSD-2-Clause
5#
6# Copyright (c) 2024 Peter Holm <pho@FreeBSD.org>
7#
8# Redistribution and use in source and binary forms, with or without
9# modification, are permitted provided that the following conditions
10# are met:
11# 1. Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13# 2. Redistributions in binary form must reproduce the above copyright
14#    notice, this list of conditions and the following disclaimer in the
15#    documentation and/or other materials provided with the distribution.
16#
17# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27# SUCH DAMAGE.
28#
29
30# Demonstrate issue described in:
31# [Bug 276002] nfscl: data corruption using both copy_file_range and mmap'd I/O
32
33[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
34. ../default.cfg
35set -u
36prog=$(basename "$0" .sh)
37log=/tmp/$prog.log
38grep -q $mntpoint /etc/exports ||
39    { echo "$mntpoint missing from /etc/exports"; 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		i = arc4random() % siz;
66		c = cp[i];
67		if (c != 0x77) /* No unused vars here */
68			usleep(arc4random() % 400);
69	}
70	return (0);
71}
72
73static void *
74memwrite(void *arg __unused)
75{
76	int i;
77	char c;
78
79	while (go == 1) {
80		i = arc4random() % siz;
81		pthread_mutex_lock(&write_mutex);
82		c = cp[i];
83		cp[i] = 0xee;	/* This value seems to linger with NFS */
84		cp[i] = c;
85		pthread_mutex_unlock(&write_mutex);
86		usleep(arc4random() % 400);
87	}
88	return (0);
89}
90
91static void *
92wr(void *arg __unused)
93{
94	off_t pos;
95	int r, s;
96	char buf[1024];
97
98	while (go == 1) {
99		s = arc4random() % sizeof(buf) + 1;
100		pos = arc4random() % (siz - s);
101		pthread_mutex_lock(&write_mutex);
102		if (lseek(fd, pos, SEEK_SET) == -1)
103			err(1, "lseek(%d)", (int)pos);
104		if ((r = read(fd, buf, s)) != s) {
105			fprintf(stderr, "r = %d, s = %d, pos = %d\n", r, s, (int)pos);
106			err(1, "read():2");
107		}
108		if (lseek(fd, pos, SEEK_SET) == -1)
109			err(1, "lseek(%d)", (int)pos);
110		if (write(fd, buf, s) != s)
111			err(1, "write()");
112		pthread_mutex_unlock(&write_mutex);
113		usleep(arc4random() % 400);
114	}
115	return (0);
116}
117
118static void *
119s1(void *arg __unused)
120{
121
122	while (go == 1) {
123		if (fdatasync(fd) == -1)
124			err(1, "fdatasync()");
125		usleep(arc4random() % 1000);
126	}
127	return (0);
128}
129
130static void *
131s2(void *arg __unused)
132{
133
134	while (go == 1) {
135		if (fsync(fd) == -1)
136			err(1, "fdatasync()");
137		usleep(arc4random() % 1000);
138	}
139	return (0);
140}
141
142static void *
143tr(void *arg __unused)
144{
145	int i, s;
146	char buf[1024];
147
148	memset(buf, 0x5a, sizeof(buf));
149	while (go == 1) {
150		pthread_mutex_lock(&write_mutex);
151		if (lseek(fd, arc4random() % siz, SEEK_END) == -1)
152			err(1, "lseek() END");
153		s = sizeof(buf);
154		for (i = 0; i < 50; i++) {
155			if (write(fd, buf, s) != s)
156				warn("write()");
157		}
158		if (ftruncate(fd, siz) == -1)
159			err(1, "truncate()");
160		pthread_mutex_unlock(&write_mutex);
161		usleep(arc4random() % 400);
162	}
163	return (0);
164}
165
166int
167main(int argc, char *argv[])
168{
169	struct stat st;
170	pthread_t tp[6];
171	int e, i;
172
173	if (argc != 2) {
174		fprintf(stderr, "Usage: %s <file>\n", argv[0]);
175		exit(1);
176	}
177	if ((fd = open(argv[1], O_RDWR)) == -1)
178		err(1, "open(%s)", argv[1]);
179	if (fstat(fd, &st) == -1)
180		err(1, "stat(%s)", argv[1]);
181	siz = st.st_size;
182	cp = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
183	if (cp == MAP_FAILED)
184		err(1, "mmap()");
185
186	go = 1;
187	pthread_mutex_init(&write_mutex, NULL);
188	if ((e = pthread_create(&tp[0], NULL, memwrite, NULL)) != 0)
189		errc(1, e, "pthread_create");
190	if ((e = pthread_create(&tp[1], NULL, memread, NULL)) != 0)
191		errc(1, e, "pthread_create");
192	if ((e = pthread_create(&tp[2], NULL, wr, NULL)) != 0)
193		errc(1, e, "pthread_create");
194	if ((e = pthread_create(&tp[3], NULL, s1, NULL)) != 0)
195		errc(1, e, "pthread_create");
196	if ((e = pthread_create(&tp[4], NULL, s2, NULL)) != 0)
197		errc(1, e, "pthread_create");
198	if ((e = pthread_create(&tp[5], NULL, tr, NULL)) != 0)
199		errc(1, e, "pthread_create");
200
201	sleep(60);
202	go = 0;
203	for (i = 0; i < (int)(sizeof(tp) / sizeof(tp[0])); i++)
204		pthread_join(tp[i], NULL);
205	if (munmap(cp, siz) == -1)
206		err(1, "munmap()");
207	close(fd);
208}
209EOF
210mycc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c -lpthread || exit 1
211
212mycc -o /tmp/serial -Wall -Wextra -O2 ../tools/serial.c || exit 1
213mount | grep -q "on $mntpoint " && umount -f $mntpoint
214mdconfig -l | grep -q md$mdstart && mdconfig -d -u $mdstart
215mdconfig -s 5g -u $mdstart
216newfs -n $newfs_flags /dev/md$mdstart > /dev/null
217mount /dev/md$mdstart $mntpoint
218
219mp2=${mntpoint}2
220mkdir -p $mp2
221mount | grep -q "on $mp2 " && umount -f $mp2
222mount -t nfs 127.0.0.1:$mntpoint $mp2; s=$?
223sleep .2
224
225here=`pwd`
226mount | grep $mntpoint
227cd $mp2
228$here/../testcases/swap/swap -t 5m -i 20 > /dev/null &
229sleep 2
230
231size=262144
232/tmp/serial file $size
233cp file file.orig
234
235s=0
236/tmp/$prog file || s=1
237
238while pgrep -q swap; do pkill swap; done
239wait
240if ! cmp -s file.orig file; then
241	od -t x1 file.orig > /var/tmp/$prog.file1
242	od -t x1 file      > /var/tmp/$prog.file2
243	diff /var/tmp/$prog.file1 /var/tmp/$prog.file2 > $log
244	head -20 $log
245	rm /var/tmp/$prog.file1 /var/tmp/$prog.file2
246	ls -ls file.orig file
247	s=2
248fi
249
250cd $here
251umount $mp2
252umount $mntpoint
253mdconfig -d -u $mdstart
254rm -f /tmp/serial /tmp/$prog /tmp/$prog.c $log
255exit $s
256