1*8a272653SPeter Holm#!/bin/sh 2*8a272653SPeter Holm 3*8a272653SPeter Holm# 4*8a272653SPeter Holm# SPDX-License-Identifier: BSD-2-Clause-FreeBSD 5*8a272653SPeter Holm# 6*8a272653SPeter Holm# Copyright (c) 2021 Jean-S�bastien P�dron <dumbbell@FreeBSD.org> 7*8a272653SPeter Holm# 8*8a272653SPeter Holm# Redistribution and use in source and binary forms, with or without 9*8a272653SPeter Holm# modification, are permitted provided that the following conditions 10*8a272653SPeter Holm# are met: 11*8a272653SPeter Holm# 1. Redistributions of source code must retain the above copyright 12*8a272653SPeter Holm# notice, this list of conditions and the following disclaimer. 13*8a272653SPeter Holm# 2. Redistributions in binary form must reproduce the above copyright 14*8a272653SPeter Holm# notice, this list of conditions and the following disclaimer in the 15*8a272653SPeter Holm# documentation and/or other materials provided with the distribution. 16*8a272653SPeter Holm# 17*8a272653SPeter Holm# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18*8a272653SPeter Holm# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19*8a272653SPeter Holm# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20*8a272653SPeter Holm# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21*8a272653SPeter Holm# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22*8a272653SPeter Holm# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23*8a272653SPeter Holm# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24*8a272653SPeter Holm# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25*8a272653SPeter Holm# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26*8a272653SPeter Holm# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27*8a272653SPeter Holm# SUCH DAMAGE. 28*8a272653SPeter Holm# 29*8a272653SPeter Holm 30*8a272653SPeter Holm# "Written file doesn't match memory buffer" seen on main-n244961-e6bb49f12ca 31*8a272653SPeter Holm# https://reviews.freebsd.org/D28811 32*8a272653SPeter Holm 33*8a272653SPeter Holm. ../default.cfg 34*8a272653SPeter Holmkldstat -v | grep -q zfs.ko || { kldload zfs.ko || 35*8a272653SPeter Holm exit 0; loaded=1; } 36*8a272653SPeter Holm 37*8a272653SPeter Holmcat > /tmp/write_vs_sendfile.c <<EOF 38*8a272653SPeter Holm#include <sys/errno.h> 39*8a272653SPeter Holm#include <sys/socket.h> 40*8a272653SPeter Holm#include <sys/stat.h> 41*8a272653SPeter Holm#include <sys/types.h> 42*8a272653SPeter Holm#include <sys/uio.h> 43*8a272653SPeter Holm#include <netinet/in.h> 44*8a272653SPeter Holm 45*8a272653SPeter Holm#include <assert.h> 46*8a272653SPeter Holm#include <fcntl.h> 47*8a272653SPeter Holm#include <netdb.h> 48*8a272653SPeter Holm#include <stdio.h> 49*8a272653SPeter Holm#include <stdlib.h> 50*8a272653SPeter Holm#include <string.h> 51*8a272653SPeter Holm#include <pthread.h> 52*8a272653SPeter Holm#include <unistd.h> 53*8a272653SPeter Holm 54*8a272653SPeter Holm#define FILENAME "myfile.bin" 55*8a272653SPeter Holm#define MAX_SIZE 50 * 1000 * 1000 56*8a272653SPeter Holm 57*8a272653SPeter Holmint tcp_port; 58*8a272653SPeter Holmint chunk_size; 59*8a272653SPeter Holm 60*8a272653SPeter Holmstatic void * 61*8a272653SPeter Holmsender_start(void *buffer __unused) 62*8a272653SPeter Holm{ 63*8a272653SPeter Holm int fd, sock, ret; 64*8a272653SPeter Holm struct sockaddr_in sa = { 0 }; 65*8a272653SPeter Holm off_t cursor; 66*8a272653SPeter Holm 67*8a272653SPeter Holm printf("Sender: opening connection to TCP port %d\n", tcp_port); 68*8a272653SPeter Holm sock = socket(AF_INET, SOCK_STREAM, 0); 69*8a272653SPeter Holm if (sock < 0) { 70*8a272653SPeter Holm perror("Sender: failed to create socket"); 71*8a272653SPeter Holm return (NULL); 72*8a272653SPeter Holm } 73*8a272653SPeter Holm 74*8a272653SPeter Holm sa.sin_family = AF_INET; 75*8a272653SPeter Holm sa.sin_port = htons(tcp_port); 76*8a272653SPeter Holm sa.sin_addr.s_addr = htonl((((((127 << 8) | 0) << 8) | 0) << 8) | 1); 77*8a272653SPeter Holm 78*8a272653SPeter Holm ret = connect(sock, (struct sockaddr *)&sa, sizeof(sa)); 79*8a272653SPeter Holm if (ret < 0) { 80*8a272653SPeter Holm perror("Sender: failed to connect to localhost"); 81*8a272653SPeter Holm close(sock); 82*8a272653SPeter Holm return (NULL); 83*8a272653SPeter Holm } 84*8a272653SPeter Holm 85*8a272653SPeter Holm printf("Sender: opening %s\n", FILENAME); 86*8a272653SPeter Holm fd = open(FILENAME, O_CREAT|O_RDONLY, 0644); 87*8a272653SPeter Holm if (fd < 0) { 88*8a272653SPeter Holm perror("Sender: failed to open file"); 89*8a272653SPeter Holm close(sock); 90*8a272653SPeter Holm return (NULL); 91*8a272653SPeter Holm } 92*8a272653SPeter Holm 93*8a272653SPeter Holm printf("Sender: starting sendfile(2) loop\n"); 94*8a272653SPeter Holm cursor = 0; 95*8a272653SPeter Holm do { 96*8a272653SPeter Holm size_t to_send = chunk_size; 97*8a272653SPeter Holm off_t sbytes = 0; 98*8a272653SPeter Holm 99*8a272653SPeter Holm do { 100*8a272653SPeter Holm#if defined(__FreeBSD__) 101*8a272653SPeter Holm ret = sendfile(fd, sock, cursor, to_send, 102*8a272653SPeter Holm NULL, &sbytes, 0); 103*8a272653SPeter Holm if (ret == 0) { 104*8a272653SPeter Holm cursor += sbytes; 105*8a272653SPeter Holm to_send -= sbytes; 106*8a272653SPeter Holm } 107*8a272653SPeter Holm#elif defined(__APPLE__) 108*8a272653SPeter Holm sbytes = to_send; 109*8a272653SPeter Holm ret = sendfile(fd, sock, cursor, &sbytes, NULL, 0); 110*8a272653SPeter Holm if (ret < 0 && (errno == EAGAIN || errno == EINTR)) { 111*8a272653SPeter Holm ret = 0; 112*8a272653SPeter Holm } 113*8a272653SPeter Holm if (ret == 0) { 114*8a272653SPeter Holm ret = 0; 115*8a272653SPeter Holm cursor += sbytes; 116*8a272653SPeter Holm to_send -= sbytes; 117*8a272653SPeter Holm } 118*8a272653SPeter Holm#else 119*8a272653SPeter Holm#error Not implemented 120*8a272653SPeter Holm#endif 121*8a272653SPeter Holm } while (ret == 0 && to_send > 0); 122*8a272653SPeter Holm } while (cursor < MAX_SIZE); 123*8a272653SPeter Holm 124*8a272653SPeter Holm printf("Sender: closing socket\n"); 125*8a272653SPeter Holm close(fd); 126*8a272653SPeter Holm printf("Sender: closing %s\n", FILENAME); 127*8a272653SPeter Holm close(sock); 128*8a272653SPeter Holm 129*8a272653SPeter Holm return (NULL); 130*8a272653SPeter Holm} 131*8a272653SPeter Holm 132*8a272653SPeter Holmstatic void * 133*8a272653SPeter Holmwriter_start(void *buffer) 134*8a272653SPeter Holm{ 135*8a272653SPeter Holm int fd, cursor, ret; 136*8a272653SPeter Holm 137*8a272653SPeter Holm printf("Writer: opening %s\n", FILENAME); 138*8a272653SPeter Holm fd = open(FILENAME, O_CREAT|O_RDWR|O_TRUNC|O_DIRECT, 0644); 139*8a272653SPeter Holm if (fd < 0) { 140*8a272653SPeter Holm perror("Writer: failed to open file"); 141*8a272653SPeter Holm return (NULL); 142*8a272653SPeter Holm } 143*8a272653SPeter Holm 144*8a272653SPeter Holm /* We sleep one second to give a head start to the sendfile(2) thread 145*8a272653SPeter Holm * above. */ 146*8a272653SPeter Holm sleep(1); 147*8a272653SPeter Holm 148*8a272653SPeter Holm printf( 149*8a272653SPeter Holm "Writer: writing chunks of %u bytes to a max of %u bytes\n", 150*8a272653SPeter Holm chunk_size, MAX_SIZE); 151*8a272653SPeter Holm cursor = 0; 152*8a272653SPeter Holm do { 153*8a272653SPeter Holm ret = write(fd, buffer, chunk_size); 154*8a272653SPeter Holm if (ret < 0) { 155*8a272653SPeter Holm perror("Writer: failed to write file"); 156*8a272653SPeter Holm break; 157*8a272653SPeter Holm } 158*8a272653SPeter Holm assert(ret == chunk_size); 159*8a272653SPeter Holm 160*8a272653SPeter Holm cursor += ret; 161*8a272653SPeter Holm } while (cursor < MAX_SIZE); 162*8a272653SPeter Holm 163*8a272653SPeter Holm printf("Writer: closing %s\n", FILENAME); 164*8a272653SPeter Holm close(fd); 165*8a272653SPeter Holm 166*8a272653SPeter Holm return (NULL); 167*8a272653SPeter Holm} 168*8a272653SPeter Holm 169*8a272653SPeter Holmint 170*8a272653SPeter Holmcheck_file(void *buffer, int flags) 171*8a272653SPeter Holm{ 172*8a272653SPeter Holm int fd, ret, cursor; 173*8a272653SPeter Holm void *read_buffer; 174*8a272653SPeter Holm 175*8a272653SPeter Holm printf("Writer: opening %s\n", FILENAME); 176*8a272653SPeter Holm fd = open(FILENAME, O_RDONLY | flags | O_DIRECT); 177*8a272653SPeter Holm if (fd < 0) { 178*8a272653SPeter Holm perror("Checker: failed to open file"); 179*8a272653SPeter Holm return (1); 180*8a272653SPeter Holm } 181*8a272653SPeter Holm 182*8a272653SPeter Holm read_buffer = malloc(chunk_size); 183*8a272653SPeter Holm if (buffer == NULL) { 184*8a272653SPeter Holm perror("Checker: failed to allocate buffer"); 185*8a272653SPeter Holm close(fd); 186*8a272653SPeter Holm return (1); 187*8a272653SPeter Holm } 188*8a272653SPeter Holm 189*8a272653SPeter Holm cursor = 0; 190*8a272653SPeter Holm do { 191*8a272653SPeter Holm ret = read(fd, read_buffer, chunk_size); 192*8a272653SPeter Holm if (ret < 0) { 193*8a272653SPeter Holm perror("Checker: failed to read file"); 194*8a272653SPeter Holm close(fd); 195*8a272653SPeter Holm free(read_buffer); 196*8a272653SPeter Holm return (1); 197*8a272653SPeter Holm } 198*8a272653SPeter Holm assert(ret == chunk_size); 199*8a272653SPeter Holm 200*8a272653SPeter Holm cursor += ret; 201*8a272653SPeter Holm 202*8a272653SPeter Holm ret = memcmp(buffer, read_buffer, chunk_size); 203*8a272653SPeter Holm } while (ret == 0 && cursor < MAX_SIZE); 204*8a272653SPeter Holm 205*8a272653SPeter Holm return (ret); 206*8a272653SPeter Holm} 207*8a272653SPeter Holm 208*8a272653SPeter Holmint 209*8a272653SPeter Holmmain(int argc, char *argv[]) 210*8a272653SPeter Holm{ 211*8a272653SPeter Holm int ret; 212*8a272653SPeter Holm void *buffer; 213*8a272653SPeter Holm pthread_t sender; 214*8a272653SPeter Holm pthread_t writer; 215*8a272653SPeter Holm 216*8a272653SPeter Holm /* The sender thread will connect to the TCP port on the local host. 217*8a272653SPeter Holm * The user is responsible for starting an instance of netcat like 218*8a272653SPeter Holm * this: 219*8a272653SPeter Holm * 220*8a272653SPeter Holm * nc -k -l 8080 221*8a272653SPeter Holm */ 222*8a272653SPeter Holm tcp_port = argc >= 2 ? atoi(argv[1]) : 8080; 223*8a272653SPeter Holm chunk_size = argc >= 3 ? atoi(argv[2]) : 32128; 224*8a272653SPeter Holm 225*8a272653SPeter Holm /* We initialize a buffer and fill it with 0xff bytes. The buffer is 226*8a272653SPeter Holm * written many times to a file by the writer thread (see below). */ 227*8a272653SPeter Holm buffer = malloc(chunk_size); 228*8a272653SPeter Holm if (buffer == NULL) { 229*8a272653SPeter Holm perror("Main: failed to allocate buffer"); 230*8a272653SPeter Holm return (1); 231*8a272653SPeter Holm } 232*8a272653SPeter Holm 233*8a272653SPeter Holm memset(buffer, 255, chunk_size); 234*8a272653SPeter Holm 235*8a272653SPeter Holm unlink(FILENAME); 236*8a272653SPeter Holm 237*8a272653SPeter Holm /* The sender thread is responsible for sending the file written by the 238*8a272653SPeter Holm * writer thread to a local TCP port. The goal is always try to send 239*8a272653SPeter Holm * data which is not written to the file yet. */ 240*8a272653SPeter Holm ret = pthread_create(&sender, NULL, &sender_start, buffer); 241*8a272653SPeter Holm if (ret != 0) { 242*8a272653SPeter Holm free(buffer); 243*8a272653SPeter Holm return (2); 244*8a272653SPeter Holm } 245*8a272653SPeter Holm 246*8a272653SPeter Holm /* The writer thread is responsible for writing the allocated buffer to 247*8a272653SPeter Holm * the file until it reaches a size of 50 MB. */ 248*8a272653SPeter Holm ret = pthread_create(&writer, NULL, &writer_start, buffer); 249*8a272653SPeter Holm if (ret != 0) { 250*8a272653SPeter Holm pthread_cancel(sender); 251*8a272653SPeter Holm pthread_join(sender, NULL); 252*8a272653SPeter Holm free(buffer); 253*8a272653SPeter Holm return (2); 254*8a272653SPeter Holm } 255*8a272653SPeter Holm 256*8a272653SPeter Holm pthread_join(writer, NULL); 257*8a272653SPeter Holm pthread_cancel(sender); 258*8a272653SPeter Holm pthread_join(sender, NULL); 259*8a272653SPeter Holm 260*8a272653SPeter Holm /* Now that both threads terminated, we check the content of the 261*8a272653SPeter Holm * written file. The bug on ZFS on FreeBSD is that some portions of the 262*8a272653SPeter Holm * file contains zeros instead of the expected 0xff bytes. */ 263*8a272653SPeter Holm ret = check_file(buffer, 0); 264*8a272653SPeter Holm free(buffer); 265*8a272653SPeter Holm 266*8a272653SPeter Holm if (ret != 0) { 267*8a272653SPeter Holm fprintf(stderr, "\033[1;31mWritten file doesn't match memory buffer\033[0m\n"); 268*8a272653SPeter Holm } 269*8a272653SPeter Holm 270*8a272653SPeter Holm return (ret); 271*8a272653SPeter Holm} 272*8a272653SPeter HolmEOF 273*8a272653SPeter Holm 274*8a272653SPeter Holmmycc -o /tmp/write_vs_sendfile -Wall -Wextra -O2 /tmp/write_vs_sendfile.c -lpthread || exit 1 275*8a272653SPeter Holmrm /tmp/write_vs_sendfile.c 276*8a272653SPeter Holm 277*8a272653SPeter Holmu1=$mdstart 278*8a272653SPeter Holmu2=$((u1 + 1)) 279*8a272653SPeter Holm 280*8a272653SPeter Holmmdconfig -l | grep -q md$u1 && mdconfig -d -u $u1 281*8a272653SPeter Holmmdconfig -l | grep -q md$u2 && mdconfig -d -u $u2 282*8a272653SPeter Holm 283*8a272653SPeter Holmmdconfig -s 2g -u $u1 284*8a272653SPeter Holmmdconfig -s 2g -u $u2 285*8a272653SPeter Holm 286*8a272653SPeter Holmzpool list | egrep -q "^stress2_tank" && zpool destroy stress2_tank 287*8a272653SPeter Holm[ -d /stress2_tank ] && rm -rf /stress2_tank 288*8a272653SPeter Holmzpool create stress2_tank raidz md$u1 md$u2 289*8a272653SPeter Holmzfs create stress2_tank/test 290*8a272653SPeter Holm 291*8a272653SPeter Holmhere=`pwd` 292*8a272653SPeter Holmcd /stress2_tank/test 293*8a272653SPeter Holmnc -k -l 8080 >/dev/null & 294*8a272653SPeter Holmsleep .5 295*8a272653SPeter Holm/tmp/write_vs_sendfile; s=$? 296*8a272653SPeter Holmkill $! 297*8a272653SPeter Holmwait 298*8a272653SPeter Holmcd $here 299*8a272653SPeter Holm 300*8a272653SPeter Holmzfs umount stress2_tank/test 301*8a272653SPeter Holmzfs destroy -r stress2_tank 302*8a272653SPeter Holmzpool destroy stress2_tank 303*8a272653SPeter Holm 304*8a272653SPeter Holmmdconfig -d -u $u1 305*8a272653SPeter Holmmdconfig -d -u $u2 306*8a272653SPeter Holm[ -n "$loaded" ] && kldunload zfs.ko 307*8a272653SPeter Holmrm /tmp/write_vs_sendfile 308*8a272653SPeter Holmexit $s 309