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