xref: /freebsd/tools/test/stress2/misc/sendfile26.sh (revision 4d846d260e2b9a3d4d0a701462568268cbfe7a5b)
18a272653SPeter Holm#!/bin/sh
28a272653SPeter Holm
38a272653SPeter Holm#
4*4d846d26SWarner Losh# SPDX-License-Identifier: BSD-2-Clause
58a272653SPeter Holm#
68a272653SPeter Holm# Copyright (c) 2021 Jean-S�bastien P�dron <dumbbell@FreeBSD.org>
78a272653SPeter Holm#
88a272653SPeter Holm# Redistribution and use in source and binary forms, with or without
98a272653SPeter Holm# modification, are permitted provided that the following conditions
108a272653SPeter Holm# are met:
118a272653SPeter Holm# 1. Redistributions of source code must retain the above copyright
128a272653SPeter Holm#    notice, this list of conditions and the following disclaimer.
138a272653SPeter Holm# 2. Redistributions in binary form must reproduce the above copyright
148a272653SPeter Holm#    notice, this list of conditions and the following disclaimer in the
158a272653SPeter Holm#    documentation and/or other materials provided with the distribution.
168a272653SPeter Holm#
178a272653SPeter Holm# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
188a272653SPeter Holm# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
198a272653SPeter Holm# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
208a272653SPeter Holm# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
218a272653SPeter Holm# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
228a272653SPeter Holm# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
238a272653SPeter Holm# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
248a272653SPeter Holm# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
258a272653SPeter Holm# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
268a272653SPeter Holm# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
278a272653SPeter Holm# SUCH DAMAGE.
288a272653SPeter Holm#
298a272653SPeter Holm
308a272653SPeter Holm# "Written file doesn't match memory buffer" seen on main-n244961-e6bb49f12ca
318a272653SPeter Holm# https://reviews.freebsd.org/D28811
328a272653SPeter Holm
338a272653SPeter Holm. ../default.cfg
348a272653SPeter Holmkldstat -v | grep -q zfs.ko  || { kldload zfs.ko ||
358a272653SPeter Holm    exit 0; loaded=1; }
368a272653SPeter Holm
378a272653SPeter Holmcat > /tmp/write_vs_sendfile.c <<EOF
388a272653SPeter Holm#include <sys/errno.h>
398a272653SPeter Holm#include <sys/socket.h>
408a272653SPeter Holm#include <sys/stat.h>
418a272653SPeter Holm#include <sys/types.h>
428a272653SPeter Holm#include <sys/uio.h>
438a272653SPeter Holm#include <netinet/in.h>
448a272653SPeter Holm
458a272653SPeter Holm#include <assert.h>
468a272653SPeter Holm#include <fcntl.h>
478a272653SPeter Holm#include <netdb.h>
488a272653SPeter Holm#include <stdio.h>
498a272653SPeter Holm#include <stdlib.h>
508a272653SPeter Holm#include <string.h>
518a272653SPeter Holm#include <pthread.h>
528a272653SPeter Holm#include <unistd.h>
538a272653SPeter Holm
548a272653SPeter Holm#define FILENAME "myfile.bin"
558a272653SPeter Holm#define MAX_SIZE 50 * 1000 * 1000
568a272653SPeter Holm
578a272653SPeter Holmint tcp_port;
588a272653SPeter Holmint chunk_size;
598a272653SPeter Holm
608a272653SPeter Holmstatic void *
618a272653SPeter Holmsender_start(void *buffer __unused)
628a272653SPeter Holm{
638a272653SPeter Holm	int fd, sock, ret;
648a272653SPeter Holm	struct sockaddr_in sa = { 0 };
658a272653SPeter Holm	off_t cursor;
668a272653SPeter Holm
678a272653SPeter Holm	printf("Sender: opening connection to TCP port %d\n", tcp_port);
688a272653SPeter Holm	sock = socket(AF_INET, SOCK_STREAM, 0);
698a272653SPeter Holm	if (sock < 0) {
708a272653SPeter Holm		perror("Sender: failed to create socket");
718a272653SPeter Holm		return (NULL);
728a272653SPeter Holm	}
738a272653SPeter Holm
748a272653SPeter Holm	sa.sin_family = AF_INET;
758a272653SPeter Holm	sa.sin_port = htons(tcp_port);
768a272653SPeter Holm	sa.sin_addr.s_addr = htonl((((((127 << 8) | 0) << 8) | 0) << 8) | 1);
778a272653SPeter Holm
788a272653SPeter Holm	ret = connect(sock, (struct sockaddr *)&sa, sizeof(sa));
798a272653SPeter Holm	if (ret < 0) {
808a272653SPeter Holm		perror("Sender: failed to connect to localhost");
818a272653SPeter Holm		close(sock);
828a272653SPeter Holm		return (NULL);
838a272653SPeter Holm	}
848a272653SPeter Holm
858a272653SPeter Holm	printf("Sender: opening %s\n", FILENAME);
868a272653SPeter Holm	fd = open(FILENAME, O_CREAT|O_RDONLY, 0644);
878a272653SPeter Holm	if (fd < 0) {
888a272653SPeter Holm		perror("Sender: failed to open file");
898a272653SPeter Holm		close(sock);
908a272653SPeter Holm		return (NULL);
918a272653SPeter Holm	}
928a272653SPeter Holm
938a272653SPeter Holm	printf("Sender: starting sendfile(2) loop\n");
948a272653SPeter Holm	cursor = 0;
958a272653SPeter Holm	do {
968a272653SPeter Holm		size_t to_send = chunk_size;
978a272653SPeter Holm		off_t sbytes = 0;
988a272653SPeter Holm
998a272653SPeter Holm		do {
1008a272653SPeter Holm#if defined(__FreeBSD__)
1018a272653SPeter Holm			ret = sendfile(fd, sock, cursor, to_send,
1028a272653SPeter Holm			    NULL, &sbytes, 0);
1038a272653SPeter Holm			if (ret == 0) {
1048a272653SPeter Holm				cursor += sbytes;
1058a272653SPeter Holm				to_send -= sbytes;
1068a272653SPeter Holm			}
1078a272653SPeter Holm#elif defined(__APPLE__)
1088a272653SPeter Holm			sbytes = to_send;
1098a272653SPeter Holm			ret = sendfile(fd, sock, cursor, &sbytes, NULL, 0);
1108a272653SPeter Holm			if (ret < 0 && (errno == EAGAIN || errno == EINTR)) {
1118a272653SPeter Holm				ret = 0;
1128a272653SPeter Holm			}
1138a272653SPeter Holm			if (ret == 0) {
1148a272653SPeter Holm				ret = 0;
1158a272653SPeter Holm				cursor += sbytes;
1168a272653SPeter Holm				to_send -= sbytes;
1178a272653SPeter Holm			}
1188a272653SPeter Holm#else
1198a272653SPeter Holm#error Not implemented
1208a272653SPeter Holm#endif
1218a272653SPeter Holm		} while (ret == 0 && to_send > 0);
1228a272653SPeter Holm	} while (cursor < MAX_SIZE);
1238a272653SPeter Holm
1248a272653SPeter Holm	printf("Sender: closing socket\n");
1258a272653SPeter Holm	close(fd);
1268a272653SPeter Holm	printf("Sender: closing %s\n", FILENAME);
1278a272653SPeter Holm	close(sock);
1288a272653SPeter Holm
1298a272653SPeter Holm	return (NULL);
1308a272653SPeter Holm}
1318a272653SPeter Holm
1328a272653SPeter Holmstatic void *
1338a272653SPeter Holmwriter_start(void *buffer)
1348a272653SPeter Holm{
1358a272653SPeter Holm	int fd, cursor, ret;
1368a272653SPeter Holm
1378a272653SPeter Holm	printf("Writer: opening %s\n", FILENAME);
1388a272653SPeter Holm	fd = open(FILENAME, O_CREAT|O_RDWR|O_TRUNC|O_DIRECT, 0644);
1398a272653SPeter Holm	if (fd < 0) {
1408a272653SPeter Holm		perror("Writer: failed to open file");
1418a272653SPeter Holm		return (NULL);
1428a272653SPeter Holm	}
1438a272653SPeter Holm
1448a272653SPeter Holm	/* We sleep one second to give a head start to the sendfile(2) thread
1458a272653SPeter Holm	 * above. */
1468a272653SPeter Holm	sleep(1);
1478a272653SPeter Holm
1488a272653SPeter Holm	printf(
1498a272653SPeter Holm	    "Writer: writing chunks of %u bytes to a max of %u bytes\n",
1508a272653SPeter Holm	    chunk_size, MAX_SIZE);
1518a272653SPeter Holm	cursor = 0;
1528a272653SPeter Holm	do {
1538a272653SPeter Holm		ret = write(fd, buffer, chunk_size);
1548a272653SPeter Holm		if (ret < 0) {
1558a272653SPeter Holm			perror("Writer: failed to write file");
1568a272653SPeter Holm			break;
1578a272653SPeter Holm		}
1588a272653SPeter Holm		assert(ret == chunk_size);
1598a272653SPeter Holm
1608a272653SPeter Holm		cursor += ret;
1618a272653SPeter Holm	} while (cursor < MAX_SIZE);
1628a272653SPeter Holm
1638a272653SPeter Holm	printf("Writer: closing %s\n", FILENAME);
1648a272653SPeter Holm	close(fd);
1658a272653SPeter Holm
1668a272653SPeter Holm	return (NULL);
1678a272653SPeter Holm}
1688a272653SPeter Holm
1698a272653SPeter Holmint
1708a272653SPeter Holmcheck_file(void *buffer, int flags)
1718a272653SPeter Holm{
1728a272653SPeter Holm	int fd, ret, cursor;
1738a272653SPeter Holm	void *read_buffer;
1748a272653SPeter Holm
1758a272653SPeter Holm	printf("Writer: opening %s\n", FILENAME);
1768a272653SPeter Holm	fd = open(FILENAME, O_RDONLY | flags | O_DIRECT);
1778a272653SPeter Holm	if (fd < 0) {
1788a272653SPeter Holm		perror("Checker: failed to open file");
1798a272653SPeter Holm		return (1);
1808a272653SPeter Holm	}
1818a272653SPeter Holm
1828a272653SPeter Holm	read_buffer = malloc(chunk_size);
1838a272653SPeter Holm	if (buffer == NULL) {
1848a272653SPeter Holm		perror("Checker: failed to allocate buffer");
1858a272653SPeter Holm		close(fd);
1868a272653SPeter Holm		return (1);
1878a272653SPeter Holm	}
1888a272653SPeter Holm
1898a272653SPeter Holm	cursor = 0;
1908a272653SPeter Holm	do {
1918a272653SPeter Holm		ret = read(fd, read_buffer, chunk_size);
1928a272653SPeter Holm		if (ret < 0) {
1938a272653SPeter Holm			perror("Checker: failed to read file");
1948a272653SPeter Holm			close(fd);
1958a272653SPeter Holm			free(read_buffer);
1968a272653SPeter Holm			return (1);
1978a272653SPeter Holm		}
1988a272653SPeter Holm		assert(ret == chunk_size);
1998a272653SPeter Holm
2008a272653SPeter Holm		cursor += ret;
2018a272653SPeter Holm
2028a272653SPeter Holm		ret = memcmp(buffer, read_buffer, chunk_size);
2038a272653SPeter Holm	} while (ret == 0 && cursor < MAX_SIZE);
2048a272653SPeter Holm
2058a272653SPeter Holm	return (ret);
2068a272653SPeter Holm}
2078a272653SPeter Holm
2088a272653SPeter Holmint
2098a272653SPeter Holmmain(int argc, char *argv[])
2108a272653SPeter Holm{
2118a272653SPeter Holm	int ret;
2128a272653SPeter Holm	void *buffer;
2138a272653SPeter Holm	pthread_t sender;
2148a272653SPeter Holm	pthread_t writer;
2158a272653SPeter Holm
2168a272653SPeter Holm	/* The sender thread will connect to the TCP port on the local host.
2178a272653SPeter Holm	 * The user is responsible for starting an instance of netcat like
2188a272653SPeter Holm	 * this:
2198a272653SPeter Holm	 *
2208a272653SPeter Holm	 *     nc -k -l 8080
2218a272653SPeter Holm	 */
2228a272653SPeter Holm	tcp_port = argc >= 2 ? atoi(argv[1]) : 8080;
2238a272653SPeter Holm	chunk_size = argc >= 3 ? atoi(argv[2]) : 32128;
2248a272653SPeter Holm
2258a272653SPeter Holm	/* We initialize a buffer and fill it with 0xff bytes. The buffer is
2268a272653SPeter Holm	 * written many times to a file by the writer thread (see below). */
2278a272653SPeter Holm	buffer = malloc(chunk_size);
2288a272653SPeter Holm	if (buffer == NULL) {
2298a272653SPeter Holm		perror("Main: failed to allocate buffer");
2308a272653SPeter Holm		return (1);
2318a272653SPeter Holm	}
2328a272653SPeter Holm
2338a272653SPeter Holm	memset(buffer, 255, chunk_size);
2348a272653SPeter Holm
2358a272653SPeter Holm	unlink(FILENAME);
2368a272653SPeter Holm
2378a272653SPeter Holm	/* The sender thread is responsible for sending the file written by the
2388a272653SPeter Holm	 * writer thread to a local TCP port. The goal is always try to send
2398a272653SPeter Holm	 * data which is not written to the file yet. */
2408a272653SPeter Holm	ret = pthread_create(&sender, NULL, &sender_start, buffer);
2418a272653SPeter Holm	if (ret != 0) {
2428a272653SPeter Holm		free(buffer);
2438a272653SPeter Holm		return (2);
2448a272653SPeter Holm	}
2458a272653SPeter Holm
2468a272653SPeter Holm	/* The writer thread is responsible for writing the allocated buffer to
2478a272653SPeter Holm	 * the file until it reaches a size of 50 MB. */
2488a272653SPeter Holm	ret = pthread_create(&writer, NULL, &writer_start, buffer);
2498a272653SPeter Holm	if (ret != 0) {
2508a272653SPeter Holm		pthread_cancel(sender);
2518a272653SPeter Holm		pthread_join(sender, NULL);
2528a272653SPeter Holm		free(buffer);
2538a272653SPeter Holm		return (2);
2548a272653SPeter Holm	}
2558a272653SPeter Holm
2568a272653SPeter Holm	pthread_join(writer, NULL);
2578a272653SPeter Holm	pthread_cancel(sender);
2588a272653SPeter Holm	pthread_join(sender, NULL);
2598a272653SPeter Holm
2608a272653SPeter Holm	/* Now that both threads terminated, we check the content of the
2618a272653SPeter Holm	 * written file. The bug on ZFS on FreeBSD is that some portions of the
2628a272653SPeter Holm	 * file contains zeros instead of the expected 0xff bytes. */
2638a272653SPeter Holm	ret = check_file(buffer, 0);
2648a272653SPeter Holm	free(buffer);
2658a272653SPeter Holm
2668a272653SPeter Holm	if (ret != 0) {
2678a272653SPeter Holm		fprintf(stderr, "\033[1;31mWritten file doesn't match memory buffer\033[0m\n");
2688a272653SPeter Holm	}
2698a272653SPeter Holm
2708a272653SPeter Holm	return (ret);
2718a272653SPeter Holm}
2728a272653SPeter HolmEOF
2738a272653SPeter Holm
2748a272653SPeter Holmmycc -o /tmp/write_vs_sendfile -Wall -Wextra -O2 /tmp/write_vs_sendfile.c -lpthread || exit 1
2758a272653SPeter Holmrm /tmp/write_vs_sendfile.c
2768a272653SPeter Holm
2778a272653SPeter Holmu1=$mdstart
2788a272653SPeter Holmu2=$((u1 + 1))
2798a272653SPeter Holm
2808a272653SPeter Holmmdconfig -l | grep -q md$u1 && mdconfig -d -u $u1
2818a272653SPeter Holmmdconfig -l | grep -q md$u2 && mdconfig -d -u $u2
2828a272653SPeter Holm
2838a272653SPeter Holmmdconfig -s 2g -u $u1
2848a272653SPeter Holmmdconfig -s 2g -u $u2
2858a272653SPeter Holm
2868a272653SPeter Holmzpool list | egrep -q "^stress2_tank" && zpool destroy stress2_tank
2878a272653SPeter Holm[ -d /stress2_tank ] && rm -rf /stress2_tank
2888a272653SPeter Holmzpool create stress2_tank raidz md$u1 md$u2
2898a272653SPeter Holmzfs create stress2_tank/test
2908a272653SPeter Holm
2918a272653SPeter Holmhere=`pwd`
2928a272653SPeter Holmcd /stress2_tank/test
2938a272653SPeter Holmnc -k -l 8080 >/dev/null &
2948a272653SPeter Holmsleep .5
2958a272653SPeter Holm/tmp/write_vs_sendfile; s=$?
2968a272653SPeter Holmkill $!
2978a272653SPeter Holmwait
2988a272653SPeter Holmcd $here
2998a272653SPeter Holm
3008a272653SPeter Holmzfs umount stress2_tank/test
3018a272653SPeter Holmzfs destroy -r stress2_tank
3028a272653SPeter Holmzpool destroy stress2_tank
3038a272653SPeter Holm
3048a272653SPeter Holmmdconfig -d -u $u1
3058a272653SPeter Holmmdconfig -d -u $u2
3068a272653SPeter Holm[ -n "$loaded" ] && kldunload zfs.ko
3078a272653SPeter Holmrm /tmp/write_vs_sendfile
3088a272653SPeter Holmexit $s
309