xref: /freebsd/tools/test/stress2/misc/sendfile26.sh (revision 8a272653d9fbd9fc37691c9aad6a05089b4ecb4d)
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