Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F173784222
D59356.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
21 KB
Referenced Files
None
Subscribers
None
D59356.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D59356: ffs: fix stale newblk lookup in flush_newblk_dep
Attached
Detach File
Event Timeline
Log In to Comment