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