xref: /freebsd/tools/test/stress2/misc/mmap48.sh (revision 94a294e59d1772f316fef613b83e92f37294954c)
1*94a294e5SPeter Holm#!/bin/sh
2*94a294e5SPeter Holm
3*94a294e5SPeter Holm#
4*94a294e5SPeter Holm# Copyright (c) 2024 Peter Holm <pho@FreeBSD.org>
5*94a294e5SPeter Holm#
6*94a294e5SPeter Holm# SPDX-License-Identifier: BSD-2-Clause
7*94a294e5SPeter Holm#
8*94a294e5SPeter Holm
9*94a294e5SPeter Holm# Demonstrate issue described in:
10*94a294e5SPeter Holm# [Bug 276002] nfscl: data corruption using both copy_file_range and mmap'd I/O
11*94a294e5SPeter Holm
12*94a294e5SPeter Holm# This version only uses mapped read/write, read(2)/write(2), fstat(2)  and ftruncate(2)
13*94a294e5SPeter Holm
14*94a294e5SPeter Holm# Issue seen:
15*94a294e5SPeter Holm# 20241003 10:04:24 all: mmap48.sh
16*94a294e5SPeter Holm# 5257c5257
17*94a294e5SPeter Holm# < 0244200    80  81  82  83  84  85  86  87  88  89  8a  8b  8c  8d  8e  8f
18*94a294e5SPeter Holm# ---
19*94a294e5SPeter Holm# > 0244200    80  81  82  83  84  85  86  87  88  ee  8a  8b  8c  8d  8e  8f
20*94a294e5SPeter Holm# 256 -rw-------  1 root wheel 262144 Oct  3 10:05 file
21*94a294e5SPeter Holm# 256 -rw-------  1 root wheel 262144 Oct  3 10:04 file.orig
22*94a294e5SPeter Holm# FAIL mmap48.sh exit code 2
23*94a294e5SPeter Holm
24*94a294e5SPeter Holm[ `id -u ` -ne 0 ] && echo "Must be root!" && exit 1
25*94a294e5SPeter Holm. ../default.cfg
26*94a294e5SPeter Holmset -u
27*94a294e5SPeter Holmprog=$(basename "$0" .sh)
28*94a294e5SPeter Holmlog=/tmp/$prog.log
29*94a294e5SPeter Holmserial=/tmp/$prog.serial
30*94a294e5SPeter Holmgrep -q $mntpoint /etc/exports ||
31*94a294e5SPeter Holm    { echo "$mntpoint missing from /etc/exports"; exit 0; }
32*94a294e5SPeter Holmrpcinfo 2>/dev/null | grep -q mountd || exit 0
33*94a294e5SPeter Holm
34*94a294e5SPeter Holmcat > /tmp/$prog.c <<EOF
35*94a294e5SPeter Holm#include <sys/mman.h>
36*94a294e5SPeter Holm#include <sys/stat.h>
37*94a294e5SPeter Holm
38*94a294e5SPeter Holm#include <assert.h>
39*94a294e5SPeter Holm#include <err.h>
40*94a294e5SPeter Holm#include <fcntl.h>
41*94a294e5SPeter Holm#include <pthread.h>
42*94a294e5SPeter Holm#include <pthread_np.h>
43*94a294e5SPeter Holm#include <stdio.h>
44*94a294e5SPeter Holm#include <stdlib.h>
45*94a294e5SPeter Holm#include <string.h>
46*94a294e5SPeter Holm#include <unistd.h>
47*94a294e5SPeter Holm
48*94a294e5SPeter Holmstatic off_t siz;
49*94a294e5SPeter Holmstatic pthread_mutex_t write_mutex;
50*94a294e5SPeter Holmstatic int fd, go;
51*94a294e5SPeter Holmstatic char *cp;
52*94a294e5SPeter Holm
53*94a294e5SPeter Holm#define THREADS 100
54*94a294e5SPeter Holm
55*94a294e5SPeter Holmstatic void *
56*94a294e5SPeter Holmmemread(void *arg __unused)
57*94a294e5SPeter Holm{
58*94a294e5SPeter Holm	int i;
59*94a294e5SPeter Holm	char c;
60*94a294e5SPeter Holm
61*94a294e5SPeter Holm	if (arc4random() % 100 < 10)
62*94a294e5SPeter Holm		return (0);
63*94a294e5SPeter Holm	pthread_set_name_np(pthread_self(), __func__);
64*94a294e5SPeter Holm	while (go == -1)
65*94a294e5SPeter Holm		usleep(50);
66*94a294e5SPeter Holm	while (go == 1) {
67*94a294e5SPeter Holm		i = arc4random() % siz;
68*94a294e5SPeter Holm		c = cp[i];
69*94a294e5SPeter Holm		if (c != 0x77) /* No unused vars here */
70*94a294e5SPeter Holm			usleep(arc4random() % 400);
71*94a294e5SPeter Holm	}
72*94a294e5SPeter Holm	return (NULL);
73*94a294e5SPeter Holm}
74*94a294e5SPeter Holm
75*94a294e5SPeter Holmstatic void *
76*94a294e5SPeter Holmmemwrite(void *arg __unused)
77*94a294e5SPeter Holm{
78*94a294e5SPeter Holm	int i;
79*94a294e5SPeter Holm	char c;
80*94a294e5SPeter Holm
81*94a294e5SPeter Holm	if (arc4random() % 100 < 10)
82*94a294e5SPeter Holm		return (0);
83*94a294e5SPeter Holm	pthread_set_name_np(pthread_self(), __func__);
84*94a294e5SPeter Holm	while (go == -1)
85*94a294e5SPeter Holm		usleep(50);
86*94a294e5SPeter Holm	while (go == 1) {
87*94a294e5SPeter Holm		i = arc4random() % siz;
88*94a294e5SPeter Holm		pthread_mutex_lock(&write_mutex);
89*94a294e5SPeter Holm		c = cp[i];
90*94a294e5SPeter Holm		cp[i] = 0xee;	/* This value seems to linger with NFS */
91*94a294e5SPeter Holm		cp[i] = c;
92*94a294e5SPeter Holm		pthread_mutex_unlock(&write_mutex);
93*94a294e5SPeter Holm		usleep(arc4random() % 400);
94*94a294e5SPeter Holm	}
95*94a294e5SPeter Holm	return (NULL);
96*94a294e5SPeter Holm}
97*94a294e5SPeter Holm
98*94a294e5SPeter Holmstatic void *
99*94a294e5SPeter Holmwr(void *arg __unused)
100*94a294e5SPeter Holm{
101*94a294e5SPeter Holm	off_t pos;
102*94a294e5SPeter Holm	int r, s;
103*94a294e5SPeter Holm	char buf[1024];
104*94a294e5SPeter Holm
105*94a294e5SPeter Holm	if (arc4random() % 100 < 10)
106*94a294e5SPeter Holm		return (0);
107*94a294e5SPeter Holm	pthread_set_name_np(pthread_self(), __func__);
108*94a294e5SPeter Holm	while (go == -1)
109*94a294e5SPeter Holm		usleep(50);
110*94a294e5SPeter Holm	while (go == 1) {
111*94a294e5SPeter Holm		s = arc4random() % sizeof(buf) + 1;
112*94a294e5SPeter Holm		pos = arc4random() % (siz - s);
113*94a294e5SPeter Holm		pthread_mutex_lock(&write_mutex);
114*94a294e5SPeter Holm		if (lseek(fd, pos, SEEK_SET) == -1)
115*94a294e5SPeter Holm			err(1, "lseek(%d)", (int)pos);
116*94a294e5SPeter Holm		if ((r = read(fd, buf, s)) != s) {
117*94a294e5SPeter Holm			fprintf(stderr, "r = %d, s = %d, pos = %d\n", r, s, (int)pos);
118*94a294e5SPeter Holm			err(1, "read():2");
119*94a294e5SPeter Holm		}
120*94a294e5SPeter Holm		if (lseek(fd, pos, SEEK_SET) == -1)
121*94a294e5SPeter Holm			err(1, "lseek(%d)", (int)pos);
122*94a294e5SPeter Holm		if (write(fd, buf, s) != s)
123*94a294e5SPeter Holm			err(1, "write()");
124*94a294e5SPeter Holm		pthread_mutex_unlock(&write_mutex);
125*94a294e5SPeter Holm		usleep(arc4random() % 200);
126*94a294e5SPeter Holm	}
127*94a294e5SPeter Holm	return (NULL);
128*94a294e5SPeter Holm}
129*94a294e5SPeter Holm
130*94a294e5SPeter Holm/* Both ftruncate() and fdatasync() triggers the problem */
131*94a294e5SPeter Holm
132*94a294e5SPeter Holmstatic void *
133*94a294e5SPeter Holmsy(void *arg __unused)
134*94a294e5SPeter Holm{
135*94a294e5SPeter Holm
136*94a294e5SPeter Holm	if (arc4random() % 100 < 10)
137*94a294e5SPeter Holm		return (0);
138*94a294e5SPeter Holm	pthread_set_name_np(pthread_self(), __func__);
139*94a294e5SPeter Holm	while (go == -1)
140*94a294e5SPeter Holm		usleep(50);
141*94a294e5SPeter Holm	while (go == 1) {
142*94a294e5SPeter Holm		if (fdatasync(fd) == -1)
143*94a294e5SPeter Holm			err(1, "fdatasync()");
144*94a294e5SPeter Holm		usleep(arc4random() % 1000);
145*94a294e5SPeter Holm	}
146*94a294e5SPeter Holm	return (NULL);
147*94a294e5SPeter Holm}
148*94a294e5SPeter Holm
149*94a294e5SPeter Holmstatic void *
150*94a294e5SPeter Holmtr(void *arg __unused)
151*94a294e5SPeter Holm{
152*94a294e5SPeter Holm
153*94a294e5SPeter Holm	if (arc4random() % 100 < 10)
154*94a294e5SPeter Holm		return (0);
155*94a294e5SPeter Holm	pthread_set_name_np(pthread_self(), __func__);
156*94a294e5SPeter Holm	while (go == -1)
157*94a294e5SPeter Holm		usleep(50);
158*94a294e5SPeter Holm	while (go == 1) {
159*94a294e5SPeter Holm		if (ftruncate(fd, siz) == -1) /* No size change */
160*94a294e5SPeter Holm			err(1, "truncate)");
161*94a294e5SPeter Holm		usleep(arc4random() % 1000);
162*94a294e5SPeter Holm	}
163*94a294e5SPeter Holm	return (NULL);
164*94a294e5SPeter Holm}
165*94a294e5SPeter Holm
166*94a294e5SPeter Holmstatic void *
167*94a294e5SPeter Holmfs(void *arg __unused)
168*94a294e5SPeter Holm{
169*94a294e5SPeter Holm	struct stat st;
170*94a294e5SPeter Holm
171*94a294e5SPeter Holm	if (arc4random() % 100 < 10)
172*94a294e5SPeter Holm		return (0);
173*94a294e5SPeter Holm	pthread_set_name_np(pthread_self(), __func__);
174*94a294e5SPeter Holm	while (go == -1)
175*94a294e5SPeter Holm		usleep(50);
176*94a294e5SPeter Holm	while (go == 1) {
177*94a294e5SPeter Holm		if (fstat(fd, &st) == -1)
178*94a294e5SPeter Holm			err(1, "stat()");
179*94a294e5SPeter Holm		usleep(arc4random() % 1000);
180*94a294e5SPeter Holm	}
181*94a294e5SPeter Holm	return (NULL);
182*94a294e5SPeter Holm}
183*94a294e5SPeter Holm
184*94a294e5SPeter Holmint
185*94a294e5SPeter Holmmain(int argc, char *argv[])
186*94a294e5SPeter Holm{
187*94a294e5SPeter Holm	struct stat st;
188*94a294e5SPeter Holm	pthread_t tp[THREADS];
189*94a294e5SPeter Holm	int e, i, idx;
190*94a294e5SPeter Holm
191*94a294e5SPeter Holm	if (argc != 2) {
192*94a294e5SPeter Holm		fprintf(stderr, "Usage: %s <file>\n", argv[0]);
193*94a294e5SPeter Holm		exit(1);
194*94a294e5SPeter Holm	}
195*94a294e5SPeter Holm	if ((fd = open(argv[1], O_RDWR)) == -1)
196*94a294e5SPeter Holm		err(1, "open(%s)", argv[1]);
197*94a294e5SPeter Holm	if (fstat(fd, &st) == -1)
198*94a294e5SPeter Holm		err(1, "stat(%s)", argv[1]);
199*94a294e5SPeter Holm	siz = st.st_size;
200*94a294e5SPeter Holm	cp = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
201*94a294e5SPeter Holm	if (cp == MAP_FAILED)
202*94a294e5SPeter Holm		err(1, "mmap()");
203*94a294e5SPeter Holm
204*94a294e5SPeter Holm	go = -1;
205*94a294e5SPeter Holm	pthread_mutex_init(&write_mutex, NULL);
206*94a294e5SPeter Holm	idx = 0;
207*94a294e5SPeter Holm	for (i = 0; i < (int)(arc4random() % 3 + 1); i++) {
208*94a294e5SPeter Holm		if ((e = pthread_create(&tp[idx++], NULL, memread, NULL)) != 0)
209*94a294e5SPeter Holm			errc(1, e, "pthread_create");
210*94a294e5SPeter Holm	}
211*94a294e5SPeter Holm	for (i = 0; i < (int)(arc4random() % 3 + 1); i++) {
212*94a294e5SPeter Holm		if ((e = pthread_create(&tp[idx++], NULL, memwrite, NULL)) != 0)
213*94a294e5SPeter Holm			errc(1, e, "pthread_create");
214*94a294e5SPeter Holm	}
215*94a294e5SPeter Holm	for (i = 0; i < (int)(arc4random() % 3 + 1); i++) {
216*94a294e5SPeter Holm		if ((e = pthread_create(&tp[idx++], NULL, wr, NULL)) != 0)
217*94a294e5SPeter Holm			errc(1, e, "pthread_create");
218*94a294e5SPeter Holm	}
219*94a294e5SPeter Holm	for (i = 0; i < (int)(arc4random() % 3 + 1); i++) {
220*94a294e5SPeter Holm		if ((e = pthread_create(&tp[idx++], NULL, fs, NULL)) != 0)
221*94a294e5SPeter Holm			errc(1, e, "pthread_create");
222*94a294e5SPeter Holm	}
223*94a294e5SPeter Holm	for (i = 0; i < (int)(arc4random() % 3 + 1); i++) {
224*94a294e5SPeter Holm		if ((e = pthread_create(&tp[idx++], NULL, tr, NULL)) != 0)
225*94a294e5SPeter Holm			errc(1, e, "pthread_create");
226*94a294e5SPeter Holm	}
227*94a294e5SPeter Holm	for (i = 0; i < (int)(arc4random() % 3 + 1); i++) {
228*94a294e5SPeter Holm		if ((e = pthread_create(&tp[idx++], NULL, sy, NULL)) != 0)
229*94a294e5SPeter Holm			errc(1, e, "pthread_create");
230*94a294e5SPeter Holm	}
231*94a294e5SPeter Holm	assert(idx <= THREADS);
232*94a294e5SPeter Holm
233*94a294e5SPeter Holm	sleep(1);
234*94a294e5SPeter Holm	go = 1;
235*94a294e5SPeter Holm	sleep(60);
236*94a294e5SPeter Holm	go = 0;
237*94a294e5SPeter Holm	for (i = 0; i < idx; i++)
238*94a294e5SPeter Holm		pthread_join(tp[i], NULL);
239*94a294e5SPeter Holm	if (munmap(cp, siz) == -1)
240*94a294e5SPeter Holm		err(1, "munmap()");
241*94a294e5SPeter Holm	close(fd);
242*94a294e5SPeter Holm}
243*94a294e5SPeter HolmEOF
244*94a294e5SPeter Holmmycc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c -lpthread || exit 1
245*94a294e5SPeter Holm
246*94a294e5SPeter Holmmycc -o $serial -Wall -Wextra -O2 ../tools/serial.c || exit 1
247*94a294e5SPeter Holmmount | grep -q "on $mntpoint " && umount -f $mntpoint
248*94a294e5SPeter Holmmdconfig -l | grep -q md$mdstart && mdconfig -d -u $mdstart
249*94a294e5SPeter Holmmdconfig -s 5g -u $mdstart
250*94a294e5SPeter Holmnewfs -n $newfs_flags /dev/md$mdstart > /dev/null
251*94a294e5SPeter Holmmount /dev/md$mdstart $mntpoint
252*94a294e5SPeter Holm
253*94a294e5SPeter Holmmp2=${mntpoint}2
254*94a294e5SPeter Holmmkdir -p $mp2
255*94a294e5SPeter Holmmount | grep -q "on $mp2 " && umount -f $mp2
256*94a294e5SPeter Holmmount -t nfs -o retrycnt=3 127.0.0.1:$mntpoint $mp2 || exit 1
257*94a294e5SPeter Holmsleep .2
258*94a294e5SPeter Holm
259*94a294e5SPeter Holmhere=`pwd`
260*94a294e5SPeter Holmcd $mp2
261*94a294e5SPeter Holm#$here/../testcases/swap/swap -t 5m -i 20 > /dev/null &
262*94a294e5SPeter Holmsleep 2
263*94a294e5SPeter Holm
264*94a294e5SPeter Holmsize=262144
265*94a294e5SPeter Holm$serial file $size
266*94a294e5SPeter Holmcp file file.orig
267*94a294e5SPeter Holm
268*94a294e5SPeter Holms=0
269*94a294e5SPeter Holm#ktrace -id -f $here/ktrace.out /tmp/$prog file || s=1
270*94a294e5SPeter Holm/tmp/$prog file || s=1
271*94a294e5SPeter Holm
272*94a294e5SPeter Holmwhile pgrep -q swap; do pkill swap; done
273*94a294e5SPeter Holmwait
274*94a294e5SPeter Holmif ! cmp -s file.orig file; then
275*94a294e5SPeter Holm	od -t x1 file.orig > /var/tmp/$prog.file1
276*94a294e5SPeter Holm	od -t x1 file      > /var/tmp/$prog.file2
277*94a294e5SPeter Holm	diff /var/tmp/$prog.file1 /var/tmp/$prog.file2 > $log
278*94a294e5SPeter Holm	head -20 $log
279*94a294e5SPeter Holm	rm /var/tmp/$prog.file1 /var/tmp/$prog.file2
280*94a294e5SPeter Holm	ls -ls file.orig file
281*94a294e5SPeter Holm	s=2
282*94a294e5SPeter Holmfi
283*94a294e5SPeter Holm
284*94a294e5SPeter Holmcd $here
285*94a294e5SPeter Holmumount $mp2
286*94a294e5SPeter Holmumount $mntpoint
287*94a294e5SPeter Holmmdconfig -d -u $mdstart
288*94a294e5SPeter Holmrm -f $serial /tmp/$prog /tmp/$prog.c $log
289*94a294e5SPeter Holmexit $s
290