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 usleep(50); 58 while (go == 1) { 59 i = arc4random() % siz; 60 c = cp[i]; 61 if (c != 0x77) /* No unused vars here */ 62 usleep(arc4random() % 200); 63 } 64 return (0); 65} 66 67static void * 68memwrite(void *arg __unused) 69{ 70 int i; 71 char c; 72 73 while (go == -1) 74 usleep(50); 75 while (go == 1) { 76 i = arc4random() % siz; 77 pthread_mutex_lock(&write_mutex); 78 c = cp[i]; 79 cp[i] = 0xee; /* This value seems to linger with NFS */ 80 cp[i] = c; 81 pthread_mutex_unlock(&write_mutex); 82 usleep(arc4random() % 200); 83 } 84 return (0); 85} 86 87static void * 88wr(void *arg __unused) 89{ 90 off_t pos; 91 int r, s; 92 char buf[1024]; 93 94 while (go == -1) 95 usleep(50); 96 while (go == 1) { 97 s = arc4random() % sizeof(buf) + 1; 98 pos = arc4random() % (siz - s); 99 pthread_mutex_lock(&write_mutex); 100 if (lseek(fd, pos, SEEK_SET) == -1) 101 err(1, "lseek(%d)", (int)pos); 102 if ((r = read(fd, buf, s)) != s) { 103 fprintf(stderr, "r = %d, s = %d, pos = %d\n", r, s, (int)pos); 104 err(1, "read():2"); 105 } 106 if (lseek(fd, pos, SEEK_SET) == -1) 107 err(1, "lseek(%d)", (int)pos); 108 if (write(fd, buf, s) != s) 109 err(1, "write()"); 110 pthread_mutex_unlock(&write_mutex); 111 usleep(arc4random() % 200); 112 } 113 return (0); 114} 115 116static void * 117tr(void *arg __unused) 118{ 119 while (go == -1) 120 usleep(50); 121 while (go == 1) { 122 if (ftruncate(fd, siz) == -1) /* No size change */ 123 err(1, "truncate)"); 124 usleep(arc4random() % 1000); 125 } 126 return (0); 127} 128 129int 130main(int argc, char *argv[]) 131{ 132 struct stat st; 133 pthread_t tp[13]; 134 int e, i; 135 136 if (argc != 2) { 137 fprintf(stderr, "Usage: %s <file>\n", argv[0]); 138 exit(1); 139 } 140 if ((fd = open(argv[1], O_RDWR)) == -1) 141 err(1, "open(%s)", argv[1]); 142 if (fstat(fd, &st) == -1) 143 err(1, "stat(%s)", argv[1]); 144 siz = st.st_size; 145 cp = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); 146 if (cp == MAP_FAILED) 147 err(1, "mmap()"); 148 149 go = -1; 150 pthread_mutex_init(&write_mutex, NULL); 151 if ((e = pthread_create(&tp[0], NULL, memwrite, NULL)) != 0) 152 errc(1, e, "pthread_create"); 153 if ((e = pthread_create(&tp[1], NULL, memwrite, NULL)) != 0) 154 errc(1, e, "pthread_create"); 155 if ((e = pthread_create(&tp[2], NULL, memread, NULL)) != 0) 156 errc(1, e, "pthread_create"); 157 if ((e = pthread_create(&tp[3], NULL, memread, NULL)) != 0) 158 errc(1, e, "pthread_create"); 159 if ((e = pthread_create(&tp[4], NULL, wr, NULL)) != 0) 160 errc(1, e, "pthread_create"); 161 if ((e = pthread_create(&tp[5], NULL, wr, NULL)) != 0) 162 errc(1, e, "pthread_create"); 163 if ((e = pthread_create(&tp[6], NULL, wr, NULL)) != 0) 164 errc(1, e, "pthread_create"); 165 if ((e = pthread_create(&tp[7], NULL, wr, NULL)) != 0) 166 errc(1, e, "pthread_create"); 167 if ((e = pthread_create(&tp[8], NULL, wr, NULL)) != 0) 168 errc(1, e, "pthread_create"); 169 if ((e = pthread_create(&tp[9], NULL, wr, NULL)) != 0) 170 errc(1, e, "pthread_create"); 171 if ((e = pthread_create(&tp[10], NULL, wr, NULL)) != 0) 172 errc(1, e, "pthread_create"); 173 if ((e = pthread_create(&tp[11], NULL, wr, NULL)) != 0) 174 errc(1, e, "pthread_create"); 175 if ((e = pthread_create(&tp[12], NULL, tr, NULL)) != 0) 176 errc(1, e, "pthread_create"); 177 178 sleep(1); 179 go = 1; 180 sleep(60); 181 go = 0; 182 for (i = 0; i < (int)(sizeof(tp) / sizeof(tp[0])); i++) 183 pthread_join(tp[i], NULL); 184 if (munmap(cp, siz) == -1) 185 err(1, "munmap()"); 186 close(fd); 187} 188EOF 189mycc -o /tmp/$prog -Wall -Wextra -O0 /tmp/$prog.c -lpthread || exit 1 190 191mycc -o $serial -Wall -Wextra -O2 ../tools/serial.c || exit 1 192mount | grep -q "on $mntpoint " && umount -f $mntpoint 193mdconfig -l | grep -q md$mdstart && mdconfig -d -u $mdstart 194mdconfig -s 5g -u $mdstart 195newfs -n $newfs_flags /dev/md$mdstart > /dev/null 196mount /dev/md$mdstart $mntpoint 197 198mp2=${mntpoint}2 199mkdir -p $mp2 200mount | grep -q "on $mp2 " && umount -f $mp2 201mount -t nfs -o retrycnt=3 127.0.0.1:$mntpoint $mp2 || exit 1 202sleep .2 203 204here=`pwd` 205cd $mp2 206$here/../testcases/swap/swap -t 5m -i 20 > /dev/null & 207sleep 2 208 209size=262144 210$serial file $size 211cp file file.orig 212 213s=0 214/tmp/$prog file || s=1 215 216while pgrep -q swap; do pkill swap; done 217wait 218if ! cmp -s file.orig file; then 219 od -t x1 file.orig > /var/tmp/$prog.file1 220 od -t x1 file > /var/tmp/$prog.file2 221 diff /var/tmp/$prog.file1 /var/tmp/$prog.file2 > $log 222 head -20 $log 223 rm /var/tmp/$prog.file1 /var/tmp/$prog.file2 224 ls -ls file.orig file 225 s=2 226fi 227 228cd $here 229umount $mp2 230umount $mntpoint 231mdconfig -d -u $mdstart 232rm -f $serial /tmp/$prog /tmp/$prog.c $log 233exit $s 234