Page MenuHomeFreeBSD

D59356.diff
No OneTemporary

D59356.diff

diff --git a/sys/ufs/ffs/ffs_softdep.c b/sys/ufs/ffs/ffs_softdep.c
--- a/sys/ufs/ffs/ffs_softdep.c
+++ b/sys/ufs/ffs/ffs_softdep.c
@@ -712,7 +712,8 @@
static int flush_pagedep_deps(struct vnode *, struct mount *,
struct diraddhd *, struct buf *);
static int free_pagedep(struct pagedep *);
-static int flush_newblk_dep(struct vnode *, struct mount *, ufs_lbn_t);
+static int flush_newblk_dep(struct vnode *, struct mount *, ufs_lbn_t,
+ struct diradd *);
static int flush_inodedep_deps(struct vnode *, struct mount *, ino_t);
static int flush_deplist(struct allocdirectlst *, int, int *);
static int sync_cgs(struct mount *, int);
@@ -13210,13 +13211,16 @@
}
/*
- * Flush dependencies associated with an allocdirect block.
+ * Flush dependencies associated with an allocdirect block. The softdep
+ * lock must be held on entry and is released on return. The locked parent
+ * directory buffer keeps dap valid while the lock is dropped.
*/
static int
flush_newblk_dep(
struct vnode *vp,
struct mount *mp,
- ufs_lbn_t lbn)
+ ufs_lbn_t lbn,
+ struct diradd *dap)
{
struct newblk *newblk;
struct ufsmount *ump;
@@ -13233,14 +13237,23 @@
if (blkno == 0)
panic("flush_newblk_dep: Missing block");
ump = VFSTOUFS(mp);
- ACQUIRE_LOCK(ump);
+ LOCK_OWNED(ump);
+ KASSERT(dap->da_newinum == ip->i_number,
+ ("flush_newblk_dep: inode mismatch %ju != %ju",
+ (uintmax_t)dap->da_newinum, (uintmax_t)ip->i_number));
/*
* Loop until all dependencies related to this block are satisfied.
* We must be careful to restart after each sleep in case a write
* completes some part of this process for us.
*/
for (;;) {
- if (newblk_lookup(mp, blkno, 0, &newblk) == 0) {
+ /*
+ * The mkdir dependency may have completed while we dropped the
+ * lock. Its allocdirect may then have been freed, exposing an
+ * older dependency for a previous use of the same block number.
+ */
+ if ((dap->da_state & MKDIR_BODY) == 0 ||
+ newblk_lookup(mp, blkno, 0, &newblk) == 0) {
FREE_LOCK(ump);
break;
}
@@ -13328,9 +13341,19 @@
LIST_INIT(&unfinished);
ump = VFSTOUFS(mp);
+ vp = NULL;
LOCK_OWNED(ump);
restart:
while ((dap = LIST_FIRST(diraddhdp)) != NULL) {
+ /*
+ * Only retain a vnode while restarting the lookup of the same
+ * MKDIR_BODY dependency. Dropping it may change the dependency
+ * list, so restart again after vput().
+ */
+ if (vp != NULL &&
+ ((dap->da_state & (MKDIR_PARENT | MKDIR_BODY)) != MKDIR_BODY ||
+ VTOI(vp)->i_number != dap->da_newinum))
+ goto vput_restart;
/*
* Flush ourselves if this directory entry
* has a MKDIR_PARENT dependency.
@@ -13375,25 +13398,38 @@
TAILQ_FOREACH(inoref, &inodedep->id_inoreflst, if_deps) {
if ((inoref->if_state & (DEPCOMPLETE | GOINGAWAY))
== DEPCOMPLETE) {
+ if (vp != NULL)
+ goto vput_restart;
jwait(&inoref->if_list, MNT_WAIT);
goto restart;
}
}
if (dap->da_state & MKDIR_BODY) {
- FREE_LOCK(ump);
- error = get_parent_vp(pvp, mp, inum, locked_bp,
- diraddhdp, &unfinished, &vp);
- if (error != 0)
- break;
- error = flush_newblk_dep(vp, mp, 0);
+ if (vp == NULL) {
+ FREE_LOCK(ump);
+ error = get_parent_vp(pvp, mp, inum, locked_bp,
+ diraddhdp, &unfinished, &vp);
+ if (error != 0)
+ break;
+ ACQUIRE_LOCK(ump);
+ goto restart;
+ }
+ error = flush_newblk_dep(vp, mp, 0, dap);
/*
* If we still have the dependency we might need to
* update the vnode to sync the new link count to
* disk.
*/
- if (error == 0 && dap == LIST_FIRST(diraddhdp))
- error = ffs_update(vp, 1);
+ if (error == 0) {
+ ACQUIRE_LOCK(ump);
+ if (dap == LIST_FIRST(diraddhdp)) {
+ FREE_LOCK(ump);
+ error = ffs_update(vp, 1);
+ } else
+ FREE_LOCK(ump);
+ }
vput(vp);
+ vp = NULL;
if (error != 0)
break;
ACQUIRE_LOCK(ump);
@@ -13452,6 +13488,7 @@
break;
error = ffs_update(vp, 1);
vput(vp);
+ vp = NULL;
if (error)
break;
ACQUIRE_LOCK(ump);
@@ -13469,11 +13506,23 @@
}
if (error)
ACQUIRE_LOCK(ump);
+ if (vp != NULL) {
+ FREE_LOCK(ump);
+ vput(vp);
+ ACQUIRE_LOCK(ump);
+ }
while ((dap = LIST_FIRST(&unfinished)) != NULL) {
LIST_REMOVE(dap, da_pdlist);
LIST_INSERT_HEAD(diraddhdp, dap, da_pdlist);
}
return (error);
+
+vput_restart:
+ FREE_LOCK(ump);
+ vput(vp);
+ vp = NULL;
+ ACQUIRE_LOCK(ump);
+ goto restart;
}
/*
diff --git a/tools/test/stress2/misc/mkdir_blkreuse.sh b/tools/test/stress2/misc/mkdir_blkreuse.sh
new file mode 100755
--- /dev/null
+++ b/tools/test/stress2/misc/mkdir_blkreuse.sh
@@ -0,0 +1,528 @@
+#!/bin/sh
+
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2026 Maksym Sobolyev <sobomax@sippysoft.com>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+# Hunt for "panic: flush_newblk_dep: Bad newblk" (PR 297976).
+#
+# flush_pagedep_deps() drops the soft updates lock to obtain the vnode of a
+# newly created directory (MKDIR_BODY). If the mkdir dependency completes
+# in that window, the directory's allocdirect is retired and a lookup of its
+# first block by block number can return a stale allocindir left behind by a
+# previous owner of the same physical block that was relocated by
+# ffs_reallocblks() and freed.
+#
+# Three kinds of workers on a small SU (no journal) file system:
+# - "wal" writers grow two interleaved files past UFS_NDADDR so that
+# clusters referenced from an indirect block keep getting relocated,
+# freeing the old blocks while their dependencies are retained.
+# - "dir" workers fill a directory, unlink the trailing entries so the next
+# insert sets IN_ENDOFF, then mkdir subdirectories. ffs_vput_pair()
+# then truncates the parent and calls ffs_syncvnode(MNT_WAIT), which
+# is the path into flush_pagedep_deps() / flush_newblk_dep().
+# - "sync" workers fsync() the parents and their subdirectories to
+# complete the mkdir dependencies while a flush is in progress.
+#
+# Several details are needed for a new directory to land on such a block:
+# directory blocks are allocated from the metadata area of the cylinder
+# group, so it is filled with placeholder directories first; fragments
+# are as large as blocks so that a directory block takes over a whole
+# freed block; the writers complete the dependencies of their blocks
+# with fdatasync() plus fsync() of a small helper file, neither of which
+# writes the writers' inodes; and the writers' inodes are isolated in
+# their own inode blocks so that no other fsync() writes them. Keep the
+# load light: dirty buffer pressure or dependency cleanups sync every
+# inode and retire the stale dependencies.
+#
+# Tunables: NWAL, NDIR, NSYNC, NFILES, NSUB, WALSIZE (bytes),
+# WALDELAY (us between 32k writes), WALFSYNC (fsync instead of fdatasync
+# every N writes, 0 off), DIRDELAY (us between mkdirs), NPAD (directories
+# used to fill the metadata area of the cylinder group), RUNTIME.
+# Set dtrace=1 to count how often the affected functions were entered and
+# how often a new directory reused a block with a stale dependency. The
+# latter is the precondition for the panic and can be observed on a fixed
+# kernel.
+#
+# Panics within a few minutes on 14.3 without the fix.
+
+. ../default.cfg
+[ `id -u` -ne 0 ] && echo "Must be root!" && exit 1
+
+dir=/tmp
+odir=`pwd`
+cd $dir
+sed '1,/^EOF/d' < $odir/$0 > $dir/mkdir_blkreuse.c
+mycc -o mkdir_blkreuse -Wall -Wextra -O0 -g mkdir_blkreuse.c || exit 1
+rm -f mkdir_blkreuse.c
+cd $odir
+
+runtime=`echo ${RUNTIME:-10m} | sed -e 's/m$/*60/' -e 's/s$//' | bc`
+s=0
+set -e
+mount | grep "on $mntpoint " | grep -q /dev/md && umount -f $mntpoint
+[ -c /dev/md$mdstart ] && mdconfig -d -u $mdstart
+mdconfig -a -t swap -s 1g -u $mdstart
+# Fragment size equal to the block size makes every directory block a full
+# block allocation, so a new directory's block 0 can take over exactly the
+# block number a relocated cluster gave back, instead of a fragment carved
+# from a partially used block. SU defers inode frees; use the highest
+# inode density to limit "out of inodes".
+newfs -U -b 32768 -f 32768 -i 32768 md$mdstart > /dev/null
+mount -o noatime /dev/md$mdstart $mntpoint
+set +e
+
+if [ $dtrace ]; then
+ # flush_pagedep_deps() and flush_newblk_dep() are inlined;
+ # get_parent_vp() is only called from flush_pagedep_deps().
+ # The second clause detects the precondition for the panic: block 0
+ # of a new directory is allocated while the newblk hash still holds a
+ # dependency of a previous owner of that block, an allocindir
+ # (D_ALLOCINDIR == 7) or an allocdirect (D_ALLOCDIRECT == 5) of
+ # another inode. A non-zero count means an unfixed kernel would
+ # have panicked.
+ cat > /tmp/mkdir_blkreuse.d <<'EOD'
+fbt::softdep_setup_mkdir:entry,
+fbt::get_parent_vp:entry,
+fbt::ffs_reallocblks:entry
+{
+ @[probefunc] = count();
+}
+
+fbt::softdep_setup_allocdirect:entry
+/args[1] == 0 && args[3] == 0 && (args[0]->i_mode & 0170000) == 0040000/
+{
+ this->ino = args[0]->i_number;
+ this->blk = args[2];
+ this->sd = args[0]->i_ump->um_softdep;
+ this->nb = this->sd->sd_newblkhash[this->blk &
+ this->sd->sd_newblkhashsize].lh_first;
+ this->hit = 0;
+ this->hit += this->nb != NULL && this->nb->nb_newblkno == this->blk &&
+ (this->nb->nb_list.wk_type == 7 ||
+ (this->nb->nb_list.wk_type == 5 &&
+ ((struct allocdirect *)this->nb)->ad_inodedep->id_ino !=
+ this->ino)) ? 1 : 0;
+ this->nb = this->nb != NULL ? this->nb->nb_hash.le_next : NULL;
+ this->hit += this->nb != NULL && this->nb->nb_newblkno == this->blk &&
+ (this->nb->nb_list.wk_type == 7 ||
+ (this->nb->nb_list.wk_type == 5 &&
+ ((struct allocdirect *)this->nb)->ad_inodedep->id_ino !=
+ this->ino)) ? 1 : 0;
+ this->nb = this->nb != NULL ? this->nb->nb_hash.le_next : NULL;
+ this->hit += this->nb != NULL && this->nb->nb_newblkno == this->blk &&
+ (this->nb->nb_list.wk_type == 7 ||
+ (this->nb->nb_list.wk_type == 5 &&
+ ((struct allocdirect *)this->nb)->ad_inodedep->id_ino !=
+ this->ino)) ? 1 : 0;
+ this->nb = this->nb != NULL ? this->nb->nb_hash.le_next : NULL;
+ this->hit += this->nb != NULL && this->nb->nb_newblkno == this->blk &&
+ (this->nb->nb_list.wk_type == 7 ||
+ (this->nb->nb_list.wk_type == 5 &&
+ ((struct allocdirect *)this->nb)->ad_inodedep->id_ino !=
+ this->ino)) ? 1 : 0;
+ @["new directory block 0 allocated"] = count();
+}
+
+fbt::softdep_setup_allocdirect:entry
+/this->hit != 0/
+{
+ @["new directory block 0 with stale dependency (would panic)"] =
+ count();
+}
+EOD
+ dtrace -s /tmp/mkdir_blkreuse.d &
+ dpid=$!
+ sleep 2
+fi
+
+/tmp/mkdir_blkreuse $mntpoint $runtime || s=1
+
+if [ $dtrace ]; then
+ kill -s TERM $dpid
+ wait $dpid
+fi
+
+for i in `jot 6`; do
+ mount | grep -q "on $mntpoint " || break
+ umount $mntpoint && break || sleep 10
+ [ $i -eq 6 ] &&
+ { echo FATAL; fstat -mf $mntpoint; exit 1; }
+done
+checkfs /dev/md$mdstart || s=2
+mdconfig -d -u $mdstart
+rm -f /tmp/mkdir_blkreuse /tmp/mkdir_blkreuse.d
+exit $s
+EOF
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define CHUNK 32768
+#define INOPAD 128 /* UFS2 inodes per 32k block */
+
+static const char *path;
+static int nwal, ndir, nsync, nfiles, nsub, walfsync;
+static long walsize;
+static useconds_t waldelay, dirdelay;
+static volatile sig_atomic_t done;
+
+static int
+getenvint(const char *name, int def)
+{
+ char *v;
+
+ if ((v = getenv(name)) == NULL || *v == '\0')
+ return (def);
+ return (atoi(v));
+}
+
+static void
+handler(int sig __unused)
+{
+
+ done = 1;
+}
+
+/*
+ * Inodes are handed out by a rotor, so surround a file with enough
+ * placeholder inodes that its inode block holds nothing anybody else will
+ * fsync(). An fsync() of an unrelated inode in the same block would
+ * write the writer's inode and retire the stale dependencies.
+ */
+static int
+padded_create(const char *dir, const char *name, int *seq)
+{
+ char pad[MAXPATHLEN];
+ int fd, i;
+
+ for (i = 0; i < 2 * INOPAD + 1; i++) {
+ if (i == INOPAD) {
+ snprintf(pad, sizeof(pad), "%s/%s", dir, name);
+ if ((fd = open(pad, O_RDWR | O_CREAT | O_TRUNC,
+ 0644)) == -1)
+ err(1, "open(%s)", pad);
+ continue;
+ }
+ snprintf(pad, sizeof(pad), "%s/pad.%d", dir, (*seq)++);
+ if (close(open(pad, O_CREAT | O_WRONLY, 0644)) == -1)
+ err(1, "creat(%s)", pad);
+ }
+ return (fd);
+}
+
+/*
+ * Create the writer's files before any other worker starts allocating
+ * inodes, so that the padding is not interleaved with their inodes.
+ */
+static void
+wal_setup(int idx, int *fd, int *fdcg)
+{
+ char dir[MAXPATHLEN];
+ int seq;
+
+ snprintf(dir, sizeof(dir), "%s/w%d", path, idx);
+ if (mkdir(dir, 0755) == -1 && errno != EEXIST)
+ err(1, "mkdir(%s)", dir);
+ seq = 0;
+ fd[0] = padded_create(dir, "wal", &seq);
+ *fdcg = padded_create(dir, "cg", &seq);
+ fd[1] = padded_create(dir, "db", &seq);
+}
+
+/*
+ * Directory blocks are placed in the metadata area at the start of the
+ * cylinder group, file data after it. Fill the metadata area with
+ * placeholder directories so that the directories created later have to
+ * take the lowest free block of the data area, where the relocated
+ * clusters of the writers leave their holes.
+ */
+static void
+metapad(int npad)
+{
+ char dir[MAXPATHLEN];
+ int i;
+
+ snprintf(dir, sizeof(dir), "%s/m", path);
+ if (mkdir(dir, 0755) == -1 && errno != EEXIST)
+ err(1, "mkdir(%s)", dir);
+ for (i = 0; i < npad; i++) {
+ snprintf(dir, sizeof(dir), "%s/m/%d", path, i);
+ if (mkdir(dir, 0755) == -1 && errno != EEXIST)
+ err(1, "mkdir(%s)", dir);
+ }
+}
+
+/*
+ * Slowly grow two files in lockstep, like a database and its WAL, so that
+ * their blocks are interleaved and ffs_reallocblks() has to relocate the
+ * clusters referenced from the indirect blocks, freeing the old blocks for
+ * reuse.
+ *
+ * A relocated block only leaves its old allocindir behind if that
+ * dependency had already completed, and the leftover is only retained
+ * while the indirect block pointer in the inode is not yet on disk.
+ * fdatasync() writes the data but neither the indirect blocks nor the
+ * inode; its first pass is asynchronous, so it does not write the
+ * cylinder group map either. fsync() of a small third file that just
+ * allocated a block in the same cylinder group writes that map
+ * synchronously. Together they complete the dependencies of the new
+ * blocks without touching the writer's inode, which is exactly the state
+ * needed. The syncer then writes the indirect block, freeing the old
+ * blocks in a burst, and the inode pointer only becomes durable at the
+ * following flush of the device vnode. Never fsync() the two files.
+ */
+static void
+wal(int idx, int *fd, int fdcg)
+{
+ char buf[CHUNK];
+ long off;
+ int i;
+
+ memset(buf, idx + 1, sizeof(buf));
+ while (done == 0) {
+ for (i = 0; i < 2; i++)
+ if (ftruncate(fd[i], 0) == -1)
+ err(1, "ftruncate");
+ for (off = 0; off < walsize && done == 0; off += CHUNK) {
+ for (i = 0; i < 2; i++) {
+ if (write(fd[i], buf, sizeof(buf)) !=
+ sizeof(buf)) {
+ if (errno == ENOSPC) {
+ sleep(1);
+ break;
+ }
+ err(1, "write");
+ }
+ }
+ if ((off / CHUNK) % 8 == 0 && ftruncate(fdcg, 0) == -1)
+ err(1, "ftruncate");
+ if (pwrite(fdcg, buf, sizeof(buf),
+ ((off / CHUNK) % 8) * CHUNK) != sizeof(buf) &&
+ errno != ENOSPC)
+ err(1, "pwrite");
+ fsync(fdcg);
+ for (i = 0; i < 2; i++) {
+ if (walfsync != 0 &&
+ (off / CHUNK) % walfsync == 0)
+ fsync(fd[i]);
+ else
+ fdatasync(fd[i]);
+ }
+ usleep(waldelay);
+ }
+ }
+ _exit(0);
+}
+
+/*
+ * Fill a directory, remove the entries at the end so the next insert leaves
+ * unused blocks after the last entry (IN_ENDOFF), then create new
+ * directories in it. ffs_vput_pair() truncates and syncs the parent.
+ */
+static void
+dirwork(int idx)
+{
+ char parent[MAXPATHLEN], name[MAXPATHLEN];
+ int fd, i, keep;
+
+ snprintf(parent, sizeof(parent), "%s/p%d", path, idx);
+ if (mkdir(parent, 0755) == -1 && errno != EEXIST)
+ err(1, "mkdir(%s)", parent);
+ keep = nfiles / 4;
+ while (done == 0) {
+ for (i = 0; i < nfiles; i++) {
+ snprintf(name, sizeof(name),
+ "%s/f%08d-%0200d", parent, i, idx);
+ if ((fd = open(name, O_CREAT | O_WRONLY, 0644)) ==
+ -1) {
+ if (errno == ENOSPC)
+ break;
+ err(1, "creat(%s)", name);
+ }
+ close(fd);
+ }
+ for (i = nfiles - 1; i >= keep; i--) {
+ snprintf(name, sizeof(name),
+ "%s/f%08d-%0200d", parent, i, idx);
+ if (unlink(name) == -1 && errno != ENOENT)
+ err(1, "unlink(%s)", name);
+ }
+ for (i = 0; i < nsub; i++) {
+ snprintf(name, sizeof(name), "%s/d%d", parent, i);
+ if (mkdir(name, 0755) == -1) {
+ if (errno == ENOSPC)
+ break;
+ if (errno != EEXIST)
+ err(1, "mkdir(%s)", name);
+ }
+ /*
+ * A trickle, not a flood: running out of inodes or
+ * dependencies triggers cleanups that sync every
+ * inode, retiring the stale dependencies.
+ */
+ usleep(dirdelay);
+ }
+ for (i = 0; i < nsub; i++) {
+ snprintf(name, sizeof(name), "%s/d%d", parent, i);
+ if (rmdir(name) == -1 && errno != ENOENT)
+ err(1, "rmdir(%s)", name);
+ }
+ for (i = keep - 1; i >= 0; i--) {
+ snprintf(name, sizeof(name),
+ "%s/f%08d-%0200d", parent, i, idx);
+ if (unlink(name) == -1 && errno != ENOENT)
+ err(1, "unlink(%s)", name);
+ }
+ }
+ _exit(0);
+}
+
+/*
+ * Push the mkdir dependencies to completion while the dir workers are
+ * flushing their parents.
+ */
+static void
+syncwork(int idx)
+{
+ char name[MAXPATHLEN];
+ int fd, i, j;
+
+ i = idx;
+ while (done == 0) {
+ i = (i + 1) % ndir;
+ snprintf(name, sizeof(name), "%s/p%d", path, i);
+ if ((fd = open(name, O_RDONLY | O_DIRECTORY)) != -1) {
+ fsync(fd);
+ close(fd);
+ }
+ for (j = 0; j < nsub; j += 7) {
+ snprintf(name, sizeof(name), "%s/p%d/d%d", path,
+ i, j);
+ if ((fd = open(name, O_RDONLY | O_DIRECTORY)) !=
+ -1) {
+ fsync(fd);
+ close(fd);
+ }
+ }
+ usleep(1000);
+ }
+ _exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ pid_t *pids;
+ time_t start, runtime;
+ int e, i, n, npad, status, *walcg, *walfd;
+
+ if (argc != 3) {
+ fprintf(stderr, "Usage %s <path> <runtime seconds>\n",
+ argv[0]);
+ exit(1);
+ }
+ path = argv[1];
+ runtime = atol(argv[2]);
+ nwal = getenvint("NWAL", 1);
+ ndir = getenvint("NDIR", 2);
+ nsync = getenvint("NSYNC", 1);
+ nfiles = getenvint("NFILES", 200);
+ nsub = getenvint("NSUB", 50);
+ walsize = getenvint("WALSIZE", 64 * 1024 * 1024);
+ waldelay = getenvint("WALDELAY", 40000);
+ walfsync = getenvint("WALFSYNC", 0);
+ dirdelay = getenvint("DIRDELAY", 2000);
+ npad = getenvint("NPAD", 3000);
+ if (nwal < 0 || ndir < 1 || nsync < 0 || nfiles < 8 || nsub < 1 ||
+ walsize < CHUNK || walfsync < 0 || npad < 0)
+ errx(1, "bad tunables");
+ fprintf(stderr, "wal=%d dir=%d sync=%d files=%d sub=%d walsize=%ld "
+ "waldelay=%uus walfsync=%d dirdelay=%uus npad=%d runtime=%lds\n",
+ nwal, ndir, nsync, nfiles, nsub, walsize, (unsigned)waldelay,
+ walfsync, (unsigned)dirdelay, npad, (long)runtime);
+
+ n = nwal + ndir + nsync;
+ if ((pids = calloc(n, sizeof(*pids))) == NULL ||
+ (walfd = calloc(nwal + 1, 2 * sizeof(*walfd))) == NULL ||
+ (walcg = calloc(nwal + 1, sizeof(*walcg))) == NULL)
+ err(1, "calloc");
+ for (i = 0; i < nwal; i++)
+ wal_setup(i, &walfd[2 * i], &walcg[i]);
+ metapad(npad);
+ signal(SIGTERM, handler);
+ signal(SIGALRM, handler);
+ n = 0;
+ for (i = 0; i < nwal; i++)
+ if ((pids[n++] = fork()) == 0)
+ wal(i, &walfd[2 * i], walcg[i]);
+ for (i = 0; i < ndir; i++)
+ if ((pids[n++] = fork()) == 0)
+ dirwork(i);
+ for (i = 0; i < nsync; i++)
+ if ((pids[n++] = fork()) == 0)
+ syncwork(i);
+ for (i = 0; i < n; i++)
+ if (pids[i] == -1)
+ err(1, "fork()");
+
+ start = time(NULL);
+ while (time(NULL) - start < runtime) {
+ sleep(1);
+ if (waitpid(-1, &status, WNOHANG) > 0 && status != 0) {
+ fprintf(stderr, "worker failed early\n");
+ break;
+ }
+ }
+ for (i = 0; i < n; i++)
+ kill(pids[i], SIGTERM);
+ e = 0;
+ for (i = 0; i < n; i++) {
+ if (waitpid(pids[i], &status, 0) == -1) {
+ if (errno == ECHILD)
+ continue;
+ err(1, "waitpid(%d)", pids[i]);
+ }
+ if (status != 0) {
+ if (WIFSIGNALED(status))
+ fprintf(stderr, "pid %d exit signal %d\n",
+ pids[i], WTERMSIG(status));
+ e++;
+ }
+ }
+
+ return (e);
+}

File Metadata

Mime Type
text/plain
Expires
Tue, Sep 29, 9:35 AM (22 h, 13 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
39870201
Default Alt Text
D59356.diff (21 KB)

Event Timeline