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
e6ca28c970
commit
992d8871eb
|
@ -630,7 +630,7 @@ tests = ['compress_001_pos', 'compress_002_pos', 'compress_003_pos',
|
||||||
tags = ['functional', 'compression']
|
tags = ['functional', 'compression']
|
||||||
|
|
||||||
[tests/functional/cp_files]
|
[tests/functional/cp_files]
|
||||||
tests = ['cp_files_001_pos']
|
tests = ['cp_files_001_pos', 'cp_stress']
|
||||||
tags = ['functional', 'cp_files']
|
tags = ['functional', 'cp_files']
|
||||||
|
|
||||||
[tests/functional/crtime]
|
[tests/functional/crtime]
|
||||||
|
|
|
@ -13,6 +13,9 @@ scripts_zfs_tests_functional_hkdf_PROGRAMS = %D%/tests/functional/hkdf/hkdf_test
|
||||||
%C%_tests_functional_hkdf_hkdf_test_LDADD = \
|
%C%_tests_functional_hkdf_hkdf_test_LDADD = \
|
||||||
libzpool.la
|
libzpool.la
|
||||||
|
|
||||||
|
scripts_zfs_tests_functional_cp_filesdir = $(datadir)/$(PACKAGE)/zfs-tests/tests/functional/cp_files
|
||||||
|
scripts_zfs_tests_functional_cp_files_PROGRAMS = %D%/tests/functional/cp_files/seekflood
|
||||||
|
|
||||||
if BUILD_LINUX
|
if BUILD_LINUX
|
||||||
scripts_zfs_tests_functional_tmpfiledir = $(datadir)/$(PACKAGE)/zfs-tests/tests/functional/tmpfile
|
scripts_zfs_tests_functional_tmpfiledir = $(datadir)/$(PACKAGE)/zfs-tests/tests/functional/tmpfile
|
||||||
scripts_zfs_tests_functional_tmpfile_PROGRAMS = \
|
scripts_zfs_tests_functional_tmpfile_PROGRAMS = \
|
||||||
|
|
|
@ -1393,6 +1393,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \
|
||||||
functional/compression/setup.ksh \
|
functional/compression/setup.ksh \
|
||||||
functional/cp_files/cleanup.ksh \
|
functional/cp_files/cleanup.ksh \
|
||||||
functional/cp_files/cp_files_001_pos.ksh \
|
functional/cp_files/cp_files_001_pos.ksh \
|
||||||
|
functional/cp_files/cp_stress.ksh \
|
||||||
functional/cp_files/setup.ksh \
|
functional/cp_files/setup.ksh \
|
||||||
functional/crtime/cleanup.ksh \
|
functional/crtime/cleanup.ksh \
|
||||||
functional/crtime/crtime_001_pos.ksh \
|
functional/crtime/crtime_001_pos.ksh \
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
seekflood
|
|
@ -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"
|
|
@ -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