mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	ZTS: Add dirty dnode stress test
Add a test for the dirty dnode SEEK_HOLE/SEEK_DATA bug described in https://github.com/openzfs/zfs/issues/15526 The bug was fixed in https://github.com/openzfs/zfs/pull/15571 and was backported to 2.2.2 and 2.1.14. This test case is just to make sure it does not come back. seekflood.c originally written by Rob Norris. Reviewed-by: Graham Perrin <grahamperrin@freebsd.org> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Rob Norris <robn@despairlabs.com> Signed-off-by: Tony Hutter <hutter2@llnl.gov> Closes #15608
This commit is contained in:
		
							parent
							
								
									bc42a2fb54
								
							
						
					
					
						commit
						53a55390fb
					
				@ -575,7 +575,7 @@ tests = ['compress_001_pos', 'compress_002_pos', 'compress_003_pos',
 | 
			
		||||
tags = ['functional', 'compression']
 | 
			
		||||
 | 
			
		||||
[tests/functional/cp_files]
 | 
			
		||||
tests = ['cp_files_001_pos']
 | 
			
		||||
tests = ['cp_files_001_pos', 'cp_stress']
 | 
			
		||||
tags = ['functional', 'cp_files']
 | 
			
		||||
 | 
			
		||||
[tests/functional/crtime]
 | 
			
		||||
 | 
			
		||||
@ -3362,6 +3362,18 @@ function is_te_enabled
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Return the number of CPUs (cross-platform)
 | 
			
		||||
function get_num_cpus
 | 
			
		||||
{
 | 
			
		||||
	if is_linux ; then
 | 
			
		||||
		grep -c '^processor' /proc/cpuinfo
 | 
			
		||||
	elif is_freebsd; then
 | 
			
		||||
		sysctl -n kern.smp.cpus
 | 
			
		||||
	else
 | 
			
		||||
		psrinfo | wc -l
 | 
			
		||||
	fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Utility function to determine if a system has multiple cpus.
 | 
			
		||||
function is_mp
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -1 +1,2 @@
 | 
			
		||||
/cp_files
 | 
			
		||||
cp_files
 | 
			
		||||
seekflood
 | 
			
		||||
 | 
			
		||||
@ -4,10 +4,12 @@ pkgdatadir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/cp_files
 | 
			
		||||
 | 
			
		||||
dist_pkgdata_SCRIPTS = \
 | 
			
		||||
	cp_files_001_pos.ksh \
 | 
			
		||||
	cp_stress.ksh \
 | 
			
		||||
	cleanup.ksh \
 | 
			
		||||
	setup.ksh
 | 
			
		||||
 | 
			
		||||
pkgexecdir = $(datadir)/@PACKAGE@/zfs-tests/tests/functional/cp_files
 | 
			
		||||
 | 
			
		||||
pkgexec_PROGRAMS = cp_files
 | 
			
		||||
pkgexec_PROGRAMS = cp_files seekflood
 | 
			
		||||
cp_files_SOURCES= cp_files.c
 | 
			
		||||
seekflood_SOURCES = seekflood.c
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										73
									
								
								tests/zfs-tests/tests/functional/cp_files/cp_stress.ksh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										73
									
								
								tests/zfs-tests/tests/functional/cp_files/cp_stress.ksh
									
									
									
									
									
										Executable file
									
								
							@ -0,0 +1,73 @@
 | 
			
		||||
#! /bin/ksh -p
 | 
			
		||||
#
 | 
			
		||||
# CDDL HEADER START
 | 
			
		||||
#
 | 
			
		||||
# The contents of this file are subject to the terms of the
 | 
			
		||||
# Common Development and Distribution License (the "License").
 | 
			
		||||
# You may not use this file except in compliance with the License.
 | 
			
		||||
#
 | 
			
		||||
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 | 
			
		||||
# or https://opensource.org/licenses/CDDL-1.0.
 | 
			
		||||
# See the License for the specific language governing permissions
 | 
			
		||||
# and limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
# When distributing Covered Code, include this CDDL HEADER in each
 | 
			
		||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 | 
			
		||||
# If applicable, add the following below this CDDL HEADER, with the
 | 
			
		||||
# fields enclosed by brackets "[]" replaced with your own identifying
 | 
			
		||||
# information: Portions Copyright [yyyy] [name of copyright owner]
 | 
			
		||||
#
 | 
			
		||||
# CDDL HEADER END
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2023 by Lawrence Livermore National Security, LLC.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
. $STF_SUITE/include/libtest.shlib
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# DESCRIPTION:
 | 
			
		||||
#
 | 
			
		||||
# https://github.com/openzfs/zfs/issues/15526 identified a dirty dnode
 | 
			
		||||
# SEEK_HOLE/SEEK_DATA bug.  https://github.com/openzfs/zfs/pull/15571
 | 
			
		||||
# fixed the bug, and was backported to 2.1.14 and 2.2.2.
 | 
			
		||||
#
 | 
			
		||||
# This test is to ensure that the bug, as understood, will not recur.
 | 
			
		||||
#
 | 
			
		||||
# STRATEGY:
 | 
			
		||||
#
 | 
			
		||||
# 1. Run the 'seekflood' binary, for creation of files with timing
 | 
			
		||||
#    characteristics that can trigger #15526.
 | 
			
		||||
# 2. A single run is not always a trigger, so run repeatedly.
 | 
			
		||||
 | 
			
		||||
verify_runnable "global"
 | 
			
		||||
 | 
			
		||||
function cleanup
 | 
			
		||||
{
 | 
			
		||||
	rm -rf /$TESTDIR/cp_stress
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
log_assert "Run the 'seekflood' binary repeatedly to try to trigger #15526"
 | 
			
		||||
 | 
			
		||||
log_onexit cleanup
 | 
			
		||||
 | 
			
		||||
log_must mkdir /$TESTPOOL/cp_stress
 | 
			
		||||
 | 
			
		||||
MYPWD="$PWD"
 | 
			
		||||
cd /$TESTPOOL/cp_stress
 | 
			
		||||
CPUS=$(get_num_cpus)
 | 
			
		||||
 | 
			
		||||
if is_freebsd ; then
 | 
			
		||||
	# 'seekflood' takes longer on FreeBSD and can timeout the test
 | 
			
		||||
	RUNS=3
 | 
			
		||||
else
 | 
			
		||||
	RUNS=10
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
for i in $(seq 1 $RUNS) ; do
 | 
			
		||||
	# Each run takes around 12 seconds.
 | 
			
		||||
	log_must $STF_SUITE/tests/functional/cp_files/seekflood 2000 $CPUS
 | 
			
		||||
done
 | 
			
		||||
cd "$MYPWD"
 | 
			
		||||
 | 
			
		||||
log_pass "No corruption detected"
 | 
			
		||||
							
								
								
									
										180
									
								
								tests/zfs-tests/tests/functional/cp_files/seekflood.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								tests/zfs-tests/tests/functional/cp_files/seekflood.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,180 @@
 | 
			
		||||
/*
 | 
			
		||||
 * SPDX-License-Identifier: MIT
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2023, Rob Norris <robn@despairlabs.com>
 | 
			
		||||
 *
 | 
			
		||||
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
 * of this software and associated documentation files (the "Software"), to
 | 
			
		||||
 * deal in the Software without restriction, including without limitation the
 | 
			
		||||
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 | 
			
		||||
 * sell copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
 * furnished to do so, subject to the following conditions:
 | 
			
		||||
 *
 | 
			
		||||
 * The above copyright notice and this permission notice shall be included in
 | 
			
		||||
 * all copies or substantial portions of the Software.
 | 
			
		||||
 *
 | 
			
		||||
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
			
		||||
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 | 
			
		||||
 * IN THE SOFTWARE.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef	_GNU_SOURCE
 | 
			
		||||
#define	_GNU_SOURCE
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <fcntl.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
#include <sys/wait.h>
 | 
			
		||||
 | 
			
		||||
#define	DATASIZE	(4096)
 | 
			
		||||
char data[DATASIZE];
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
_open_file(int n, int wr)
 | 
			
		||||
{
 | 
			
		||||
	char buf[256];
 | 
			
		||||
	int fd;
 | 
			
		||||
 | 
			
		||||
	snprintf(buf, sizeof (buf), "testdata_%d_%d", getpid(), n);
 | 
			
		||||
 | 
			
		||||
	if ((fd = open(buf, wr ? (O_WRONLY | O_CREAT) : O_RDONLY,
 | 
			
		||||
	    wr ? (S_IRUSR | S_IWUSR) : 0)) < 0) {
 | 
			
		||||
		fprintf(stderr, "Error: open '%s' (%s): %s\n",
 | 
			
		||||
		    buf, wr ? "write" : "read", strerror(errno));
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (fd);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
_write_file(int n, int fd)
 | 
			
		||||
{
 | 
			
		||||
	/* write a big ball of stuff */
 | 
			
		||||
	ssize_t nwr = write(fd, data, DATASIZE);
 | 
			
		||||
	if (nwr < 0) {
 | 
			
		||||
		fprintf(stderr, "Error: write '%d_%d': %s\n",
 | 
			
		||||
		    getpid(), n, strerror(errno));
 | 
			
		||||
		exit(1);
 | 
			
		||||
	} else if (nwr < DATASIZE) {
 | 
			
		||||
		fprintf(stderr, "Error: write '%d_%d': short write\n", getpid(),
 | 
			
		||||
		    n);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int
 | 
			
		||||
_seek_file(int n, int fd)
 | 
			
		||||
{
 | 
			
		||||
	struct stat st;
 | 
			
		||||
	if (fstat(fd, &st) < 0) {
 | 
			
		||||
		fprintf(stderr, "Error: fstat '%d_%d': %s\n", getpid(), n,
 | 
			
		||||
		    strerror(errno));
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * A zero-sized file correctly has no data, so seeking the file is
 | 
			
		||||
	 * pointless.
 | 
			
		||||
	 */
 | 
			
		||||
	if (st.st_size == 0)
 | 
			
		||||
		return (0);
 | 
			
		||||
 | 
			
		||||
	/* size is real, and we only write, so SEEK_DATA must find something */
 | 
			
		||||
	if (lseek(fd, 0, SEEK_DATA) < 0) {
 | 
			
		||||
		if (errno == ENXIO)
 | 
			
		||||
			return (1);
 | 
			
		||||
		fprintf(stderr, "Error: lseek '%d_%d': %s\n",
 | 
			
		||||
		    getpid(), n, strerror(errno));
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	int nfiles = 0;
 | 
			
		||||
	int nthreads = 0;
 | 
			
		||||
 | 
			
		||||
	if (argc < 3 || (nfiles = atoi(argv[1])) == 0 ||
 | 
			
		||||
	    (nthreads = atoi(argv[2])) == 0) {
 | 
			
		||||
		printf("usage: seekflood <nfiles> <threads>\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memset(data, 0x5a, DATASIZE);
 | 
			
		||||
 | 
			
		||||
	/* fork off some flood threads */
 | 
			
		||||
	for (int i = 0; i < nthreads; i++) {
 | 
			
		||||
		if (!fork()) {
 | 
			
		||||
			/* thread main */
 | 
			
		||||
 | 
			
		||||
			/* create zero file */
 | 
			
		||||
			int fd = _open_file(0, 1);
 | 
			
		||||
			_write_file(0, fd);
 | 
			
		||||
			close(fd);
 | 
			
		||||
 | 
			
		||||
			int count = 0;
 | 
			
		||||
 | 
			
		||||
			int h = 0, i, j, rfd, wfd;
 | 
			
		||||
			for (i = 0; i < nfiles; i += 2, h++) {
 | 
			
		||||
				j = i+1;
 | 
			
		||||
 | 
			
		||||
				/* seek h, write i */
 | 
			
		||||
				rfd = _open_file(h, 0);
 | 
			
		||||
				wfd = _open_file(i, 1);
 | 
			
		||||
				count += _seek_file(h, rfd);
 | 
			
		||||
				_write_file(i, wfd);
 | 
			
		||||
				close(rfd);
 | 
			
		||||
				close(wfd);
 | 
			
		||||
 | 
			
		||||
				/* seek i, write j */
 | 
			
		||||
				rfd = _open_file(i, 0);
 | 
			
		||||
				wfd = _open_file(j, 1);
 | 
			
		||||
				count += _seek_file(i, rfd);
 | 
			
		||||
				_write_file(j, wfd);
 | 
			
		||||
				close(rfd);
 | 
			
		||||
				close(wfd);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			/* return count of failed seeks to parent */
 | 
			
		||||
			exit(count < 256 ? count : 255);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* wait for threads, take their seek fail counts from exit code */
 | 
			
		||||
	int count = 0, crashed = 0;
 | 
			
		||||
	for (int i = 0; i < nthreads; i++) {
 | 
			
		||||
		int wstatus;
 | 
			
		||||
		wait(&wstatus);
 | 
			
		||||
		if (WIFEXITED(wstatus))
 | 
			
		||||
			count += WEXITSTATUS(wstatus);
 | 
			
		||||
		else
 | 
			
		||||
			crashed++;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (crashed) {
 | 
			
		||||
		fprintf(stderr, "Error: child crashed; test failed\n");
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (count) {
 | 
			
		||||
		fprintf(stderr, "Error: %d seek failures; test failed\n",
 | 
			
		||||
		    count);
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	exit(0);
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user