Notable upstream pull request merges:

 #16761 eb1738bcb FreeBSD: Enable Direct IO by default
 #16747 -multiple On-demand log-spacemap flush
 #18474 0d0eae2ab implement thorough scrub support (zpool scrub -t)
 #18562 6721ab981 Calling thread IO
 #18657 7ed1268d3 Fix race between device removal completion and pool export
 #18707 0d1f3b1c6 RAIDZ: Fix parity regeneration/check condition
 #18713 -multiple libzfs: fix key unload failure when unmounting an
                  encryption root
 #18714 d902eec64 Disconnect metaslab tracing from default builds
 #18716 e78fa488a zstream: multithreading
 #18718 6acb99cb1 Do not return ESTALE for open-unlinked files
 #18720 f607ef7e7 Fix insufficient locking in dedup verify
 #18722 -multiple snapdir: misc cleanups
 #18724 9bf75b4b1 Fix reads for blocks freed after being cloned
 #18725 9b7642df9 Harden recv record validation
 #18732 37f066e3c FreeBSD: Wire sha512 offload to the build
 #18736 -multiple Remove idmap/userns concept from core & FreeBSD platform
                  code
 #18742 d63f14057 libzutil: keep valid spare and l2cache paths on import
 #18749 f217627d4 Fix receive of split large blocks with a short trailing
                  chunk
 #18754 83bf40784 Use a single creation time for a recursive snapshot
 #18755 ebd9e2765 zstream: add "drop records" chain module
 #18763 7d565cdfa zfs bookmark: add recursive (-r) bookmark creation
 #18765 50557cc80 zpool initialize: add -z to write zeroes
 #18766 0467ba01c Add SECURITY.md policy filE
 #18773 ffde77051 Fix deduce_nblkptr for large dnodes on receive
 #18784 -multiple libzfs: clarify the raw incremental "IV set guid mismatch"
                  error
 #18793 9a02e53ad libzfs: don't abort receiving a raw encrypted send that
                  carries holds
 #18795 30426217b Rate limit Direct I/O verify zevents
 #18798 73b202451 Bound explicit user prefetch to a fraction of the ARC
 #18801 2a5331f8e Fix dmu_zfetch_prime() assuming a stream was created
 #18802 fb5fdefd8 libzfs: fallback VDEV_UPATH to VDEV_PATH for non-DM
                  devices
 #18809 bd2d87399 zdb: output refcounts from verify_spacemap_refcounts()
 #18811 5d530b8be ABD: Validate borrowed buffer length
 #18812 5d687a975 dbuf: use dirty record size for overridden writes
 #18819 -multiple ddt: fix refcount bypass and gang member leak for dedup
                  gang blocks
 #18821 87e2e4047 BRT: Implement partial bv_entcount writing
 #18822 -multiple dnode_sync: Relax constraint on indirect freeing - #18822
 #18827 d98fa72ca L2ARC: bound the rebuild by the write hand on a first
                  sweep
 #18831 90cdd9aef Batch object reallocation syncs in zfs receive
 #18833 466d90e15 zed: let autoexpand see capacity changes on partitioned
                  disks
 #18835 1ac3f2786 mmp: skip non-writeable vdevs during activity check
 #18838 a04c40138 DDT: Fix several bugs in pruning
 #18841 0bb41751f zpool export: return EBUSY when zvol minors are in use
 #18848 3020c18ca dmu_recv: Avoid potential null deref
 #18855 8cdd9b2b7 mmp: do not require writes to mirror legs the config
                  marks absent
 #18858 596c7a0ec Reduce dp_lock scope for MOS writes
 #18859 3ebf3ffc4 Predict and throttle buffers dirtied by the sync context
 #18860 -multiple Further parallelize block cloning
 #18865 412b17a29 Fix DMU bonus hold leak on I/O error
 #18867 b5bb1f816 Add missing checks to zfs_clone_range_replay()
 #18868 3df5b2e7f libzfs: String trimming should not operate out of bounds
 #18869 be62c5385 libzfs: Do not call munmap() when mmap() fails
 #18871 74e76f2d8 libzfs: don't truncate a resolved vdev path in
                  zpool_vdev_name()
 #18874 7312322b5 nvpair: Fix operator precedence
 #18883 3bd8cefdc libzfs: don't read a dataset handle after closing it in
                  resume send
 #18886 b4f70cb9e Fix negative time overflows in DDT pruning
 #18888 674b1ae5c DDT: Make ddt_zap_walk() use cursor _by_dnode functions
 #18889 fd1ae59c7 DDT: Skip DDT log lookups in ddt_prune_walk()
 #18892 -multiple zhack: add "mmp reclaim" to recover a pool stranded by MMP
 #18897 216de07d8 arc: fix race between arc_release() and arc_read_done()
 #18899 28afe8b08 CodeQL: Flag implicit compare-then-assign in branch
                  conditions
 #18917 0a79c039f arc: save on buf_hash_find() call at arc_read_done()
 #18920 7026d3335 Fix race condition in raidz expansion startup
 #18927 be55e01bc FreeBSD: Do not leak TSD zfs_geom_probe_vdev_key on errors
 #18928 5bd7d4466 scan: count skipped blocks as examined
 #18937 a5d678864 Allow pool import with corrupted spare/l2arc configs
 #18960 -multiple vdev_open: use calling credential to check for device
                  access
 #18961 acbcdd92c dmu_recv_begin_check: dsl_dataset_rele() should be called
                  on ds
 #18962 df3ff37fc dmu_redact_snap: Do proper cleanup on ENAMETOOLONG
 #18964 84aa7e7e0 libspl: consult ZFS_HOSTID on FreeBSD as well

Obtained from:	OpenZFS
OpenZFS commit:	84aa7e7e09
This commit is contained in:
Martin Matuska committed 2026-08-24 21:54:22 +02:00
commit 22649d4dba
3388 files changed
+40555 -47588

No files matched your search

+5
View File
@@ -8,6 +8,7 @@ MLINKS= zstream.8 zstreamdump.8
INCS= zstream.h
SRCS= \
zstream.c \
zstream_backtrace.c \
zstream_byteswap.c \
zstream_chain.c \
zstream_decompress.c \
@@ -15,8 +16,12 @@ SRCS= \
zstream_dump.c \
zstream_fletcher4.c \
zstream_io.c \
zstream_queue.c \
zstream_raw.c \
zstream_recompress.c \
zstream_redup.c \
zstream_selftest.c \
zstream_selftest_queue.c \
zstream_token.c \
zstream_util.c \
zstream_validate.c
+3 -3
View File
@@ -16,7 +16,7 @@
<!--- If your change is a performance enhancement, please provide benchmarks here. -->
<!--- Please think about using the draft PR feature if appropriate -->
### Types of changes
### Types of Changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
@@ -24,10 +24,10 @@
- [ ] Code cleanup (non-breaking change which makes code smaller or more readable)
- [ ] Quality assurance (non-breaking change which makes the code more robust against bugs)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Library ABI change (libzfs, libzfs\_core, libnvpair, libuutil and libzfsbootenv)
- [ ] Library ABI change (libzfs, libzfs\_core, libnvpair and libzfsbootenv)
- [ ] Documentation (a change to man pages or other documentation)
### Checklist:
### Checklist
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code follows the OpenZFS [code style requirements](https://github.com/openzfs/zfs/blob/master/.github/CONTRIBUTING.md#coding-conventions).
+1
View File
@@ -3,3 +3,4 @@ name: "Custom CodeQL Analysis"
queries:
- uses: ./.github/codeql/custom-queries/cpp/deprecatedFunctionUsage.ql
- uses: ./.github/codeql/custom-queries/cpp/dslDatasetHoldReleMismatch.ql
- uses: ./.github/codeql/custom-queries/cpp/AssignmentOfComparisonAsCondition.ql
@@ -0,0 +1,96 @@
/**
* @name Ambiguous assignment-of-comparison in a condition
* @description Finds any assignment (`=`, `+=`, `-=`, `|=`, …) that appears in
* a branching condition and whose rvalue is an unparenthesized
* comparison.
*
* Comparison binds tighter than assignment, so
* `if ((x = foo() < 0))` / `if ((x += foo() < 0))`
* assigns the boolean result of the comparison. Programmers often
* meant
* `if ((x = foo()) < 0)` / `if ((x += foo()) < 0)`
* (assign/update then compare). GCC `-Wparentheses` is silenced by
* the outer parentheses in both forms, so the compiler does not
* catch the mistake (see openzfs/zfs#18874).
*
* Rule (intentionally simple):
* assignment in a branch condition
* AND rvalue is a comparison
* AND that comparison is not parenthesized.
*
* That is enough:
* - Explicit assign-then-compare `((x = foo()) < 0)` has no
* comparison as the assignment's rvalue.
* - Explicit compare-then-assign `((x = (foo() < 0)))` has a
* parenthesized comparison as the rvalue.
* - Parentheses that only wrap a subexpression of the comparison
* (e.g. `(foo())`) do not count as parenthesizing the
* comparison itself.
* @kind problem
* @problem.severity error
* @precision high
* @id cpp/ambiguous-assignment-of-comparison-in-condition
* @tags reliability
* correctness
* external/cwe/cwe-783
* external/cwe/cwe-480
*/
import cpp
/**
* The condition expression of a branching construct
* (if / while / do-while / for / ternary `?:`).
*/
Expr branchCondition() {
result = any(IfStmt s).getCondition()
or
result = any(WhileStmt s).getCondition()
or
result = any(DoStmt s).getCondition()
or
result = any(ForStmt s).getCondition()
or
result = any(ConditionalExpr c).getCondition()
}
/**
* Holds if `e` is the branch condition, or appears anywhere under it
* (including under `&&` / `||` / `!`, and through parentheses/conversions).
*/
predicate inBranchCondition(Expr e) {
e = branchCondition()
or
// Structural child of something already in a branch condition
exists(Expr parent |
parent.getAChild() = e and
inBranchCondition(parent)
)
or
// ParenthesisExpr and other conversions hang off getConversion(), not getAChild()
exists(Expr base |
base.getConversion+() = e and
inBranchCondition(base)
)
or
exists(Expr conv |
e.getConversion+() = conv and
inBranchCondition(conv)
)
}
from Assignment a, ComparisonOperation cmp
where
// Core rule: assignment in a branch condition whose rvalue is an
// unparenthesized comparison. Covers `=` and all compound forms (`+=`, …)
// because Assignment is the common base class.
a.getRValue() = cmp and
not cmp.isParenthesised() and
inBranchCondition(a) and
// Skip AST from templates that were never instantiated (incomplete / non-code).
not a.isFromUninstantiatedTemplate(_)
select a,
"Assignment (`" + a.getOperator() +
"`) in a branch condition has an unparenthesized comparison as its rvalue; " +
"likely meant `((x " + a.getOperator() + " expr) op val)` rather than " +
"`(x " + a.getOperator() + " expr op val)`."
@@ -44,11 +44,11 @@ case "$OS" in
OSv="almalinux9"
URL="https://repo.almalinux.org/almalinux/10/cloud/x86_64/images/AlmaLinux-10-GenericCloud-latest.x86_64.qcow2"
;;
alpine3-23)
OSNAME="Alpine Linux 3.23.2"
alpine3-24)
OSNAME="Alpine Linux 3.24.1"
# Alpine Linux v3.22 and v3.23 are unknown to osinfo as of 2025-12-26.
OSv="alpinelinux3.21"
URL="https://dl-cdn.alpinelinux.org/alpine/v3.23/releases/cloud/generic_alpine-3.23.2-x86_64-bios-cloudinit-r0.qcow2"
URL="https://dl-cdn.alpinelinux.org/alpine/v3.24/releases/cloud/generic_alpine-3.24.1-x86_64-bios-cloudinit-r0.qcow2"
;;
archlinux)
OSNAME="Archlinux"
@@ -349,7 +349,7 @@ else
while pidof /usr/bin/qemu-system-x86_64 >/dev/null; do
ssh 2>/dev/null root@vm0 "uname -a" && break
done
ssh root@vm0 "env IGNORE_OSVERSION=yes pkg install -y bash ca_root_nss git qemu-guest-agent python3 py311-cloud-init"
ssh root@vm0 "env IGNORE_OSVERSION=yes pkg install -y bash ca_root_nss git qemu-guest-agent python3 net/cloud-init"
ssh root@vm0 "chsh -s $BASH root"
ssh root@vm0 'sysrc qemu_guest_agent_enable="YES"'
ssh root@vm0 'sysrc cloudinit_enable="YES"'
@@ -16,7 +16,7 @@ set -eu
function alpine() {
echo "##[group]Install Development Tools"
sudo apk add \
acl alpine-sdk attr autoconf automake bash build-base clang21 coreutils \
acl alpine-sdk attr autoconf automake bash build-base clang22 coreutils \
cpio cryptsetup curl curl-dev dhcpcd eudev eudev-dev eudev-libs findutils \
fio gawk gdb gettext-dev git grep jq libaio libaio-dev libcurl \
libtirpc-dev libtool libunwind libunwind-dev linux-headers linux-tools \
+31 -10
View File
@@ -23,8 +23,14 @@ function prefix() {
M=$((DIFF/60))
S=$((DIFF-(M*60)))
CTR=$(cat /tmp/ctr)
echo $LINE| grep -q '^\[.*] Test[: ]' && CTR=$((CTR+1)) && echo $CTR > /tmp/ctr
CTR=$(cat /tmp/ctr-vm${ID})
# Publish the counter with a rename so it is never momentarily empty: the
# other VM's reader reads this file, and a plain redirect truncates before
# it writes. A read landing in that window fails, which under set -e ends
# the reader and silently stops prefixing that VM's output.
echo $LINE| grep -q '^\[.*] Test[: ]' && CTR=$((CTR+1)) \
&& echo $CTR > "/tmp/ctr-vm${ID}.new" \
&& mv "/tmp/ctr-vm${ID}.new" "/tmp/ctr-vm${ID}"
BASE="$HOME/work/zfs/zfs"
COLOR="$BASE/scripts/zfs-tests-color.sh"
@@ -35,9 +41,17 @@ function prefix() {
if [ -z "$CLINE" ]; then
printf "vm${ID}: %s\n" "$LINE"
else
# [vm2: 00:15:54 256] Test: functional/checksum/setup (run as root) [00:00] [PASS]
printf "[vm${ID}: %02d:%02d:%02d %4d] %s\n" \
"$H" "$M" "$S" "$CTR" "$CLINE"
TOTAL=0
BREAKDOWN=""
TOTAL_VMS="$3"
for ((v=1; v<=TOTAL_VMS; v++)); do
read -r val < "/tmp/ctr-vm${v}"
TOTAL=$((TOTAL + val))
BREAKDOWN="${BREAKDOWN}${BREAKDOWN:+|}${val}"
done
# [vm2: 00:15:54 256(200|56)] Test: functional/checksum/setup (run as root) [00:00] [PASS]
printf "[vm${ID}: %02d:%02d:%02d %4d(%s)] %s\n" \
"$H" "$M" "$S" "$TOTAL" "$BREAKDOWN" "$CLINE"
fi
}
@@ -96,10 +110,10 @@ if [ -z ${1:-} ]; then
source env.txt
SSH=$(which ssh)
TESTS='$HOME/zfs/.github/workflows/scripts/qemu-6-tests.sh'
echo 0 > /tmp/ctr
date "+%s" > /tmp/tsstart
for ((i=1; i<=VMs; i++)); do
echo 0 > /tmp/ctr-vm${i}
IP="192.168.122.1$i"
# We do an additional test build of Lustre against ZFS if we're vm2
@@ -121,7 +135,7 @@ if [ -z ${1:-} ]; then
$SSH zfs@$IP $TESTS $OS $i $VMs $extra $CI_TYPE
# handly line by line and add info prefix
stdbuf -oL tail -fq vm${i}log.txt \
| while read -r line; do prefix "$i" "$line"; done &
| while read -r line; do prefix "$i" "$line" "$VMs"; done &
echo $! > vm${i}log.pid
# don't mix up the initial --- Configuration --- part
sleep 0.13
@@ -132,7 +146,10 @@ if [ -z ${1:-} ]; then
tail --pid=$(cat vm${i}.pid) -f /dev/null
pid=$(cat vm${i}log.pid)
rm -f vm${i}log.pid
kill $pid
# Tolerate a reader that has already exited. The ESRCH would otherwise
# end this script under set -e and fail a job whose tests all passed.
# It stays on stderr: a dead reader means output was lost.
kill $pid || true
done
exit 0
@@ -187,12 +204,16 @@ case "$OS" in
sudo chmod 1777 /var/tmp
sudo mv -f /tmp/*.txt /var/tmp
# Allow for longer RCU timeouts due to the heavily virtualized and
# potentially oversubscribed nature of the CI environment.
# Allow for longer RCU and watchdog timeouts due to the heavily virtualized
# and potentially over-subscribed nature of the CI environment.
rcu_cpu_stall_timeout="/sys/module/rcupdate/parameters/rcu_cpu_stall_timeout"
if test -f $rcu_cpu_stall_timeout; then
echo 120 | sudo sh -c "cat > '$rcu_cpu_stall_timeout'"
fi
if test -c /dev/watchdog; then
sudo wdctl --settimeout 120 >/dev/null
fi
;;
esac
+1 -1
View File
@@ -14,7 +14,7 @@ on:
type: string
required: false
default: ""
description: "(optional) Only run on this specific OS (like 'fedora44' or 'alpine3-23')"
description: "(optional) Only run on this specific OS (like 'fedora44' or 'alpine3-24')"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+1 -2
View File
@@ -49,10 +49,9 @@ jobs:
sudo modprobe zfs
- name: Tests
run: |
[ -r /etc/hostid ] && [ -s /etc/hostid ] || sudo zgenhostid -f
sudo truncate -s 256G /mnt/vdev
sudo zpool create cipool -m $WORK_DIR -O compression=on -o autotrim=on /mnt/vdev
sudo /usr/share/zfs/zloop.sh -t 600 -I 6 -l -m 1 -c $CORE_DIR -f $WORK_DIR -- -T 120 -P 60
sudo /usr/share/zfs/zloop.sh -t 600 -I 6 -l -m 1 -M -c $CORE_DIR -f $WORK_DIR -- -T 120 -P 60
- name: Prepare artifacts
if: failure()
run: |
+1
View File
@@ -59,6 +59,7 @@
!NOTICE
!README.md
!RELEASES.md
!SECURITY.md
!TEST
!zfs.release.in
+6
View File
@@ -304,6 +304,7 @@ CONTRIBUTORS:
hedong zhang <[email protected]>
Heitor Alves de Siqueira <[email protected]>
Henrik Riomar <[email protected]>
HeonJe Lee <[email protected]>
Herb Wartens <[email protected]>
Hiếu Lê <[email protected]>
hoshinomori <[email protected]>
@@ -506,6 +507,7 @@ CONTRIBUTORS:
mischivus <[email protected]>
mnrx <[email protected]>
Mohamed Tawfik <[email protected]>
MorganaFuture <[email protected]>
Morgan Jones <[email protected]>
Moritz Maxeiner <[email protected]>
Mo Zhou <[email protected]>
@@ -523,6 +525,7 @@ CONTRIBUTORS:
Nick Black <[email protected]>
Nick Garvey <[email protected]>
Nick Mattis <[email protected]>
Nick Price <[email protected]>
Nick Terrell <[email protected]>
Niklas Haas <[email protected]>
Nikolay Borisov <[email protected]>
@@ -609,6 +612,7 @@ CONTRIBUTORS:
Ryan Lahfa <[email protected]>
Ryan Libby <[email protected]>
Ryan Moeller <[email protected]>
Saju Palayur <[email protected]>
Sam Atkinson <[email protected]>
Sam Hathaway <[email protected]>
Sam James <[email protected]>
@@ -677,6 +681,7 @@ CONTRIBUTORS:
Thomas Bertschinger <[email protected]>
Thomas Geppert <[email protected]>
Thomas Lamprecht <[email protected]>
tiehexue <[email protected]>
Till Maas <[email protected]>
Tim Chase <[email protected]>
Tim Connors <[email protected]>
@@ -746,6 +751,7 @@ CONTRIBUTORS:
Yuxin Wang <[email protected]>
Yuxuan Shui <[email protected]>
Zachary Bedell <[email protected]>
Zachary Sandberg <[email protected]>
Zach Dykstra <[email protected]>
zgock <[email protected]>
Zhao Yongming <[email protected]>
+1 -1
View File
@@ -6,5 +6,5 @@ Release: 1
Release-Tags: relext
License: CDDL
Author: OpenZFS
Linux-Maximum: 7.1
Linux-Maximum: 7.2
Linux-Minimum: 4.18
+2 -1
View File
@@ -31,10 +31,11 @@ if BUILD_LINUX
include $(srcdir)/%D%/udev/Makefile.am
endif
endif
CPPCHECKDIRS += module
if CONFIG_KERNEL
SUBDIRS += module
CPPCHECKDIRS += module
extradir = $(prefix)/src/zfs-$(VERSION)
extra_HEADERS = zfs.release.in zfs_config.h.in
endif
+29
View File
@@ -0,0 +1,29 @@
# Security Policy
## Reporting a Vulnerability
We encourage responsible disclosure of security vulnerabilities. If you
find something suspicious, we encourage and appreciate your report.
The preferred way to report a vulnerability is to use the
**"Report a vulnerability"** button under the **Security** tab of the
OpenZFS GitHub repository. This creates a private communication channel
between you and the maintainers, allowing us to review the report
confidentially and respond as quickly as possible.
Please include, if possible:
- A clear description of the issue
- Steps to reproduce the problem
- Affected versions or branches
- Any proof of concept, logs, or screenshots
- Your assessment of the potential impact
## What to Expect
- We will review security reports as soon as practical.
- We may ask follow-up questions to better understand the issue.
- Please allow time for investigation and coordination before public
disclosure.
- If the issue is confirmed, we will work on a fix and release a security
update as needed for supported OpenZFS versions.
+7 -18
View File
@@ -5,25 +5,14 @@
# is available through the dbufs kstat and may be post-processed as
# needed by the script.
#
# CDDL HEADER START
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (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
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# https://opensource.org/license/CDDL-1.0.
#
# Copyright (C) 2013 Lawrence Livermore National Security, LLC.
# Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
+7 -18
View File
@@ -22,25 +22,14 @@
#
# and has been improved by many people since.
#
# CDDL HEADER START
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (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
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# https://opensource.org/license/CDDL-1.0.
#
#
# Fields have a fixed width. Every interval, we fill the "v"
+56 -40
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -37,6 +27,7 @@
* Copyright (c) 2023, 2024, Klara Inc.
* Copyright (c) 2023, Rob Norris <robn@despairlabs.com>
* Copyright (c) 2026, TrueNAS.
* Copyright 2026 Edgecast Cloud LLC.
*/
#include <stdio.h>
@@ -937,13 +928,24 @@ dump_packed_nvlist(objset_t *os, uint64_t object, void *data, size_t size)
nvlist_t *nv;
size_t nvsize = *(uint64_t *)data;
char *packed = umem_alloc(nvsize, UMEM_NOFAIL);
int err;
VERIFY0(dmu_read(os, object, 0, nvsize, packed, DMU_READ_PREFETCH));
err = dmu_read(os, object, 0, nvsize, packed, DMU_READ_PREFETCH);
if (err != 0) {
(void) printf("got error %u from dmu_read\n", err);
umem_free(packed, nvsize);
return;
}
VERIFY0(nvlist_unpack(packed, nvsize, &nv, 0));
err = nvlist_unpack(packed, nvsize, &nv, 0);
umem_free(packed, nvsize);
if (err != 0) {
(void) printf("got error %u from nvlist_unpack\n", err);
return;
}
dump_nvlist(nv, 8);
nvlist_free(nv);
@@ -1447,10 +1449,10 @@ dump_zpldir(objset_t *os, uint64_t object, void *data, size_t size)
zap_attribute_free(attrp);
}
static int
static uint64_t
get_dtl_refcount(vdev_t *vd)
{
int refcount = 0;
uint64_t refcount = 0;
if (vd->vdev_ops->vdev_op_leaf) {
space_map_t *sm = vd->vdev_dtl_sm;
@@ -1466,10 +1468,10 @@ get_dtl_refcount(vdev_t *vd)
return (refcount);
}
static int
static uint64_t
get_metaslab_refcount(vdev_t *vd)
{
int refcount = 0;
uint64_t refcount = 0;
if (vd->vdev_top == vd) {
for (uint64_t m = 0; m < vd->vdev_ms_count; m++) {
@@ -1486,11 +1488,11 @@ get_metaslab_refcount(vdev_t *vd)
return (refcount);
}
static int
static uint64_t
get_obsolete_refcount(vdev_t *vd)
{
uint64_t obsolete_sm_object;
int refcount = 0;
uint64_t refcount = 0;
VERIFY0(vdev_obsolete_sm_object(vd, &obsolete_sm_object));
if (vd->vdev_top == vd && obsolete_sm_object != 0) {
@@ -1511,7 +1513,7 @@ get_obsolete_refcount(vdev_t *vd)
return (refcount);
}
static int
static uint64_t
get_prev_obsolete_spacemap_refcount(spa_t *spa)
{
uint64_t prev_obj =
@@ -1526,10 +1528,10 @@ get_prev_obsolete_spacemap_refcount(spa_t *spa)
return (0);
}
static int
static uint64_t
get_checkpoint_refcount(vdev_t *vd)
{
int refcount = 0;
uint64_t refcount = 0;
if (vd->vdev_top == vd && vd->vdev_top_zap != 0 &&
zap_contains(spa_meta_objset(vd->vdev_spa),
@@ -1542,7 +1544,7 @@ get_checkpoint_refcount(vdev_t *vd)
return (refcount);
}
static int
static uint64_t
get_log_spacemap_refcount(spa_t *spa)
{
return (avl_numnodes(&spa->spa_sm_logs_by_txg));
@@ -1552,23 +1554,37 @@ static int
verify_spacemap_refcounts(spa_t *spa)
{
uint64_t expected_refcount = 0;
uint64_t actual_refcount;
uint64_t actual_refcount = 0;
uint64_t dtl_refcount, metaslab_refcount, obsolete_refcount,
prev_obsolete_refcount, checkpoint_refcount, log_spacemap_refcount;
(void) feature_get_refcount(spa,
&spa_feature_table[SPA_FEATURE_SPACEMAP_HISTOGRAM],
&expected_refcount);
actual_refcount = get_dtl_refcount(spa->spa_root_vdev);
actual_refcount += get_metaslab_refcount(spa->spa_root_vdev);
actual_refcount += get_obsolete_refcount(spa->spa_root_vdev);
actual_refcount += get_prev_obsolete_spacemap_refcount(spa);
actual_refcount += get_checkpoint_refcount(spa->spa_root_vdev);
actual_refcount += get_log_spacemap_refcount(spa);
dtl_refcount = get_dtl_refcount(spa->spa_root_vdev);
metaslab_refcount = get_metaslab_refcount(spa->spa_root_vdev);
obsolete_refcount = get_obsolete_refcount(spa->spa_root_vdev);
prev_obsolete_refcount = get_prev_obsolete_spacemap_refcount(spa);
checkpoint_refcount = get_checkpoint_refcount(spa->spa_root_vdev);
log_spacemap_refcount = get_log_spacemap_refcount(spa);
actual_refcount = dtl_refcount + metaslab_refcount +
obsolete_refcount + prev_obsolete_refcount +
checkpoint_refcount + log_spacemap_refcount;
if (expected_refcount != actual_refcount) {
(void) printf("space map refcount mismatch: expected %lld != "
"actual %lld\n",
(longlong_t)expected_refcount,
(longlong_t)actual_refcount);
(void) printf("space map refcount mismatch: expected %llu != "
"actual %llu\n",
(u_longlong_t)expected_refcount,
(u_longlong_t)actual_refcount);
(void) printf("\tDTL: %llu, metaslab: %llu, obsolete: %llu, "
"prev obsolete: %llu, checkpoint: %llu, "
"log spacemap: %llu\n",
(u_longlong_t)dtl_refcount,
(u_longlong_t)metaslab_refcount,
(u_longlong_t)obsolete_refcount,
(u_longlong_t)prev_obsolete_refcount,
(u_longlong_t)checkpoint_refcount,
(u_longlong_t)log_spacemap_refcount);
return (2);
}
return (0);
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright 2017 Spectra Logic Corp Inc. All rights reserved.
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
+7 -18
View File
@@ -1,24 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
+7 -18
View File
@@ -1,24 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
@@ -1,14 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -1,14 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -559,7 +549,7 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
uint64_t ena, pool_guid, vdev_guid, parent_guid;
uint64_t checksum_n, checksum_t;
uint64_t io_n, io_t;
er_timeval_t pool_load;
er_timeval_t pool_load = {0};
er_timeval_t er_when;
nvlist_t *detector;
boolean_t pool_found = B_FALSE;
+64 -19
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
@@ -1080,8 +1070,63 @@ zfsdle_vdev_online(zpool_handle_t *zhp, void *data)
zed_log_msg(LOG_INFO, "zfsdle_vdev_online: searching for '%s' in '%s'",
devname, zpool_get_name(zhp));
if ((tgt = zpool_find_vdev_by_physpath(zhp, devname,
&avail_spare, &l2cache, NULL)) != NULL) {
tgt = zpool_find_vdev_by_physpath(zhp, devname,
&avail_spare, &l2cache, NULL);
/*
* A whole-disk event carries no vdev guid (the label lives on
* the partition), and udev provides no ID_PATH on some buses,
* so neither lookup above can match. Identify the vdev by
* reading the label off the whole-disk partition instead: the
* pool and vdev guids stored there don't depend on which name
* the pool was imported with (by-id, by-path or a bare device
* node), and they can't be forged by a stale config path left
* behind in an unrelated pool.
*/
if (tgt == NULL && nvlist_lookup_uint64(udev_nvl, ZFS_EV_VDEV_GUID,
&guid) != 0 && nvlist_lookup_string(udev_nvl, DEV_NAME,
&tmp_devname) == 0 && !nvlist_exists(udev_nvl, DEV_IS_PART)) {
nvlist_t *label = NULL;
uint64_t label_pool_guid = 0, label_vdev_guid = 0;
int fd;
strlcpy(devname, tmp_devname, MAXPATHLEN);
zfs_append_partition(devname, MAXPATHLEN);
if ((fd = open(devname, O_RDONLY | O_CLOEXEC)) >= 0) {
if (zpool_read_label(fd, &label, NULL) == 0 &&
label != NULL &&
nvlist_lookup_uint64(label,
ZPOOL_CONFIG_POOL_GUID, &label_pool_guid) == 0 &&
nvlist_lookup_uint64(label, ZPOOL_CONFIG_GUID,
&label_vdev_guid) == 0 &&
label_pool_guid == zpool_get_prop_int(zhp,
ZPOOL_PROP_GUID, NULL)) {
zed_log_msg(LOG_INFO, "zfsdle_vdev_online: "
"matched vdev %llu by the label on '%s'",
(u_longlong_t)label_vdev_guid, devname);
(void) snprintf(devname, MAXPATHLEN, "%llu",
(u_longlong_t)label_vdev_guid);
tgt = zpool_find_vdev(zhp, devname,
&avail_spare, &l2cache, NULL);
if (tgt != NULL && (avail_spare || l2cache))
tgt = NULL;
if (tgt != NULL) {
uint64_t wd = 0;
(void) nvlist_lookup_uint64(tgt,
ZPOOL_CONFIG_WHOLE_DISK, &wd);
if (!wd)
tgt = NULL;
}
}
nvlist_free(label);
(void) close(fd);
}
}
if (tgt != NULL) {
const char *path;
char fullpath[MAXPATHLEN];
uint64_t wholedisk = 0;
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#include <errno.h>
@@ -2,15 +2,14 @@
# SPDX-License-Identifier: CDDL-1.0
# shellcheck disable=SC2154
#
# CDDL HEADER START
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License Version 1.0 (CDDL-1.0).
# You can obtain a copy of the license from the top-level file
# "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
# You may not use this file except in compliance with the license.
#
# CDDL HEADER END
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# https://opensource.org/license/CDDL-1.0.
#
#
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#ifndef ZED_H
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#include <assert.h>
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#ifndef ZED_CONF_H
+23 -8
View File
@@ -1,14 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -242,6 +241,9 @@ zed_udev_monitor(void *arg)
part != NULL && part[0] != '\0') {
const char *devname =
udev_device_get_property_value(dev, "DEVNAME");
const char *resize =
udev_device_get_property_value(dev, "RESIZE");
const char *dev_action = udev_device_get_action(dev);
if (strcmp(part, "atari") == 0) {
zed_log_msg(LOG_INFO,
@@ -249,6 +251,19 @@ zed_udev_monitor(void *arg)
"but we're going to assume it's a false "
"positive and still use it (issue #13497)",
__func__, devname);
} else if (dev_action != NULL &&
strcmp(dev_action, "change") == 0 &&
resize != NULL && strcmp(resize, "1") == 0) {
/*
* A capacity change is reported on the disk
* itself, not on its partitions, so this is
* the only chance to notice it. Pass the
* event on for a possible autoexpand of a
* whole-disk vdev (issue #12505).
*/
zed_log_msg(LOG_INFO,
"%s: %s capacity change on partitioned "
"disk", __func__, devname);
} else {
zed_log_msg(LOG_INFO,
"%s: skip %s since it has a %s partition "
+7 -8
View File
@@ -1,14 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#include <ctype.h>
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#ifndef ZED_EVENT_H
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#include <assert.h>
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#ifndef ZED_EXEC_H
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#include <dirent.h>
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#ifndef ZED_FILE_H
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#include <assert.h>
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#ifndef ZED_LOG_H
+11 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#include <assert.h>
@@ -115,6 +118,7 @@ _zed_strings_node_create(const char *key, const char *val)
} else {
np->key = np->val;
}
/* cppcheck-suppress memleak */
return (np);
nomem:
+10 -7
View File
@@ -1,16 +1,19 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* This file is part of the ZFS Event Daemon (ZED).
*
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*/
#ifndef ZED_STRINGS_H
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
+146 -60
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -420,7 +410,7 @@ get_usage(zfs_help_t idx)
return (gettext("\tdiff [-FHth] <snapshot> "
"[snapshot|filesystem]\n"));
case HELP_BOOKMARK:
return (gettext("\tbookmark <snapshot|bookmark> "
return (gettext("\tbookmark [-r] <snapshot|bookmark> "
"<newbookmark>\n"));
case HELP_CHANNEL_PROGRAM:
return (gettext("\tprogram [-jn] [-t <instruction limit>] "
@@ -7715,6 +7705,8 @@ unshare_unmount_path(int op, char *path, int flags, boolean_t is_manual)
}
(void) fprintf(stderr, gettext("warning: %s not in"
"/proc/self/mounts\n"), path);
/* libzfs-internal flags; umount2(2) rejects them */
flags &= ~(MS_CRYPT | MS_OVERLAY);
if ((ret = umount2(path, flags)) != 0)
(void) fprintf(stderr, gettext("%s: %s\n"), path,
strerror(errno));
@@ -8212,11 +8204,92 @@ out:
return (err != 0);
}
typedef struct bookmark_cbdata {
nvlist_t *cb_nvl;
const char *cb_snapname; /* source snapshot name (after '@') */
const char *cb_bookname; /* new bookmark name (after '#') */
} bookmark_cbdata_t;
/*
* zfs bookmark <fs@source>|<fs#source> <fs#bookmark>
* Recursively gather "<dataset>#bookname" -> "<dataset>@snapname" pairs for
* every descendant that actually has the source snapshot, mirroring the way
* "zfs snapshot -r" collects its targets. Descendants that lack the snapshot
* (or whose name is too long to form the pair) are skipped rather than failing
* the whole request. Unlike snapshotting, bookmarking only needs the snapshot
* to exist, so an inconsistent (e.g. mid-receive) dataset is not skipped.
*/
static int
zfs_bookmark_cb(zfs_handle_t *zhp, void *arg)
{
bookmark_cbdata_t *cb = arg;
char snap[ZFS_MAX_DATASET_NAME_LEN];
char book[ZFS_MAX_DATASET_NAME_LEN];
int rv = 0;
int n;
n = snprintf(snap, sizeof (snap), "%s@%s", zfs_get_name(zhp),
cb->cb_snapname);
if (n >= 0 && (size_t)n < sizeof (snap) && lzc_exists(snap)) {
n = snprintf(book, sizeof (book), "%s#%s",
zfs_get_name(zhp), cb->cb_bookname);
if (n >= 0 && (size_t)n < sizeof (book))
fnvlist_add_string(cb->cb_nvl, book, snap);
}
rv = zfs_iter_filesystems_v2(zhp, 0, zfs_bookmark_cb, cb);
zfs_close(zhp);
return (rv);
}
static void
zfs_bookmark_perror(const char *bookname, int err)
{
const char *err_msg = NULL;
char errbuf[1024];
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN, "cannot create bookmark '%s'"), bookname);
switch (err) {
case EXDEV:
err_msg = "bookmark is in a different pool";
break;
case ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR:
err_msg = "source is not an ancestor of the "
"new bookmark's dataset";
break;
case EEXIST:
err_msg = "bookmark exists";
break;
case EINVAL:
err_msg = "invalid argument";
break;
case ENOTSUP:
err_msg = "bookmark feature not enabled";
break;
case ENOSPC:
err_msg = "out of space";
break;
case ENOENT:
err_msg = "dataset does not exist";
break;
default:
(void) zfs_standard_error(g_zfs, err, errbuf);
break;
}
if (err_msg != NULL) {
(void) fprintf(stderr, "%s: %s\n", errbuf,
dgettext(TEXT_DOMAIN, err_msg));
}
}
/*
* zfs bookmark [-r] <fs@source>|<fs#source> <fs#bookmark>
*
* Creates a bookmark with the given name from the source snapshot
* or creates a copy of an existing source bookmark.
* or creates a copy of an existing source bookmark. With -r, a bookmark
* is created for the source snapshot of every descendant dataset that has
* one.
*/
static int
zfs_do_bookmark(int argc, char **argv)
@@ -8225,12 +8298,17 @@ zfs_do_bookmark(int argc, char **argv)
char expbuf[ZFS_MAX_DATASET_NAME_LEN];
int source_type;
nvlist_t *nvl;
nvlist_t *errlist = NULL;
boolean_t recursive = B_FALSE;
int ret = 0;
int c;
/* check options */
while ((c = getopt(argc, argv, "")) != -1) {
while ((c = getopt(argc, argv, "r")) != -1) {
switch (c) {
case 'r':
recursive = B_TRUE;
break;
case '?':
(void) fprintf(stderr,
gettext("invalid option '%c'\n"), optopt);
@@ -8308,6 +8386,12 @@ zfs_do_bookmark(int argc, char **argv)
default: abort();
}
if (recursive && source_type != ZFS_TYPE_SNAPSHOT) {
(void) fprintf(stderr, gettext("recursive bookmarks (-r) can "
"only be created from a snapshot source\n"));
goto usage;
}
/* test the source exists */
zfs_handle_t *zhp;
zhp = zfs_open(g_zfs, source, source_type);
@@ -8316,51 +8400,53 @@ zfs_do_bookmark(int argc, char **argv)
zfs_close(zhp);
nvl = fnvlist_alloc();
fnvlist_add_string(nvl, bookname, source);
ret = lzc_bookmark(nvl, NULL);
fnvlist_free(nvl);
if (recursive) {
bookmark_cbdata_t cb = { 0 };
char dsname[ZFS_MAX_DATASET_NAME_LEN];
/* recurse from the dataset the source snapshot belongs to */
(void) strlcpy(dsname, source, sizeof (dsname));
*strchr(dsname, '@') = '\0';
cb.cb_nvl = nvl;
cb.cb_snapname = strchr(source, '@') + 1;
cb.cb_bookname = strchr(bookname, '#') + 1;
zhp = zfs_open(g_zfs, dsname,
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME);
if (zhp == NULL) {
fnvlist_free(nvl);
goto usage;
}
/* zfs_bookmark_cb() closes zhp */
if (zfs_bookmark_cb(zhp, &cb) != 0) {
fnvlist_free(nvl);
return (1);
}
} else {
fnvlist_add_string(nvl, bookname, source);
}
ret = lzc_bookmark(nvl, &errlist);
if (ret != 0) {
const char *err_msg = NULL;
char errbuf[1024];
boolean_t reported = B_FALSE;
(void) snprintf(errbuf, sizeof (errbuf),
dgettext(TEXT_DOMAIN,
"cannot create bookmark '%s'"), bookname);
switch (ret) {
case EXDEV:
err_msg = "bookmark is in a different pool";
break;
case ZFS_ERR_BOOKMARK_SOURCE_NOT_ANCESTOR:
err_msg = "source is not an ancestor of the "
"new bookmark's dataset";
break;
case EEXIST:
err_msg = "bookmark exists";
break;
case EINVAL:
err_msg = "invalid argument";
break;
case ENOTSUP:
err_msg = "bookmark feature not enabled";
break;
case ENOSPC:
err_msg = "out of space";
break;
case ENOENT:
err_msg = "dataset does not exist";
break;
default:
(void) zfs_standard_error(g_zfs, ret, errbuf);
break;
}
if (err_msg != NULL) {
(void) fprintf(stderr, "%s: %s\n", errbuf,
dgettext(TEXT_DOMAIN, err_msg));
for (nvpair_t *pair = nvlist_next_nvpair(errlist, NULL);
pair != NULL; pair = nvlist_next_nvpair(errlist, pair)) {
zfs_bookmark_perror(nvpair_name(pair),
fnvpair_value_int32(pair));
reported = B_TRUE;
}
/* fall back to the overall error if none was itemized */
if (!reported)
zfs_bookmark_perror(bookname, ret);
}
fnvlist_free(nvl);
nvlist_free(errlist);
return (ret != 0);
usage:
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2017, Intel Corporation. All rights reserved.
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2019 by Delphix. All rights reserved.
+55 -23
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -34,9 +24,39 @@
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
/*
* Fill buf with len bytes from the system CSPRNG. Returns 0 on success
* and -1 on failure, with errno set.
*/
static int
get_random_bytes(void *buf, size_t len)
{
int fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC);
if (fd < 0)
return (-1);
size_t got = 0;
while (got < len) {
ssize_t n = read(fd, (char *)buf + got, len - got);
if (n <= 0) {
if (n < 0 && errno == EINTR)
continue;
if (n == 0)
errno = EIO;
int saved = errno;
(void) close(fd);
errno = saved;
return (-1);
}
got += (size_t)n;
}
(void) close(fd);
return (0);
}
static __attribute__((noreturn)) void
usage(void)
{
@@ -59,7 +79,7 @@ main(int argc, char **argv)
{
/* default file path, can be optionally set by user */
const char *path = "/etc/hostid";
/* holds converted user input or lrand48() generated value */
/* holds converted user input or generated value */
unsigned long input_i = 0;
int opt;
@@ -112,12 +132,24 @@ main(int argc, char **argv)
}
/*
* generate if not provided by user
* also handle unlikely zero return from lrand48()
* Generate if not provided by user. The hostid identifies
* this machine to ZFS multihost protection, so it must be
* unique. Obtain a value from the system CSPRNG to ensure
* that even on systems that share a common image and boot
* deterministically different hostids will be generated.
* The loop handles the unlikely zero return case.
*/
while (input_i == 0) {
srand48(getpid() ^ time(NULL));
input_i = lrand48();
uint32_t rnd;
if (get_random_bytes(&rnd, sizeof (rnd)) != 0) {
(void) fprintf(stderr,
"zgenhostid: failed to read /dev/urandom: %s\n",
strerror(errno));
exit(EXIT_FAILURE);
}
input_i = rnd;
}
FILE *fp = fopen(path, "wb");
+185 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -45,6 +35,7 @@
#include <sys/dsl_synctask.h>
#include <sys/vdev.h>
#include <sys/vdev_impl.h>
#include <sys/mmp.h>
#include <sys/fs/zfs.h>
#include <sys/dmu_objset.h>
#include <sys/dsl_pool.h>
@@ -111,6 +102,13 @@ usage(void)
"\n"
" <device> : path to vdev\n"
"\n"
" mmp reclaim <pool>\n"
" import a pool whose MMP claim cannot reach every mirror\n"
" leg the config still expects, then mark the unreachable\n"
" leaves offline so ordinary imports succeed. Manual\n"
" recovery: fence the peer first, this cannot see a\n"
" live host whose legs are all invisible from here\n"
"\n"
" metaslab leak <pool>\n"
" apply allocation map from zdb to specified pool\n");
exit(1);
@@ -601,6 +599,174 @@ zhack_do_action_idle(int argc, char **argv)
spa_close(spa, FTAG);
}
/*
* Collect the mirror legs this host could not open. vdev_not_present is set
* during import for any leaf whose open failed (vdev.c), and a leg in that
* state is what the relaxed claim forgives, so it is also what has to be
* marked offline for the ordinary imports which follow to succeed.
*/
static void
zhack_collect_absent(vdev_t *vd, uint64_t *guids, uint_t *n, uint_t max)
{
/*
* Skip the same subtrees mmp_claim_uberblock_sync() skips. The claim
* never counts these, so offlining them buys nothing, and offlining a
* log leg would drag in spa_reset_logs(). Pruned at the interior node
* because the log flag lives on the top-level vdev.
*/
if (vd->vdev_islog || vd->vdev_isspare || vd->vdev_isl2cache ||
vd->vdev_ishole || vd->vdev_ops == &vdev_indirect_ops)
return;
for (uint64_t c = 0; c < vd->vdev_children; c++)
zhack_collect_absent(vd->vdev_child[c], guids, n, max);
if (!vd->vdev_ops->vdev_op_leaf || !vd->vdev_not_present)
return;
/*
* Take only the legs the relaxed claim can forgive, which are the
* direct children of a top-level mirror: the relaxation lives in the
* nparity == 0 branch and walks that vdev's children. A raidz or
* draid member is required as parity+1 in aggregate and never demanded
* individually, so an absent one does not raise the requirement and
* offlining it would be a persistent change that buys nothing.
*/
if (vd->vdev_parent != vd->vdev_top ||
vdev_get_nparity(vd->vdev_top) != 0)
return;
VERIFY3U(*n, <, max);
guids[(*n)++] = vd->vdev_guid;
}
static int
zhack_do_mmp_reclaim(int argc, char **argv)
{
spa_t *spa;
char *target;
uint64_t *guids;
uint_t nguids = 0, max;
int c, failed = 0;
optind = 1;
while ((c = getopt(argc, argv, "+")) != -1) {
switch (c) {
default:
usage();
break;
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
(void) fprintf(stderr, "error: missing pool name\n");
usage();
}
target = argv[0];
/*
* Relax the claim for this import only. The write, the wait and the
* re-read are untouched, so a competing importer which shares any
* visibility with us is still refused.
*/
mmp_claim_relaxed = B_TRUE;
zhack_spa_open(target, B_FALSE, FTAG, &spa);
mmp_claim_relaxed = B_FALSE;
/*
* Nothing to recover on a pool without multihost: no claim runs, so an
* ordinary import already succeeds with the absent leaves simply
* missing. Offlining them here would be a permanent change to a pool
* that never needed this tool.
*/
if (!spa_multihost(spa)) {
(void) fprintf(stderr, "%s: multihost is off, so no uberblock "
"claim runs and an ordinary import will succeed; refusing "
"to offline anything\n", target);
spa_close(spa, FTAG);
return (1);
}
/* Takes SCL_VDEV itself, so size the array before we hold it. */
max = MAX(vdev_count_leaves(spa), 1);
guids = umem_zalloc(max * sizeof (uint64_t), UMEM_NOFAIL);
spa_config_enter(spa, SCL_VDEV, FTAG, RW_READER);
zhack_collect_absent(spa->spa_root_vdev, guids, &nguids, max);
spa_config_exit(spa, SCL_VDEV, FTAG);
if (nguids == 0) {
(void) fprintf(stdout, "%s: imported, no absent leaves to "
"mark offline\n", target);
}
for (uint_t i = 0; i < nguids; i++) {
int error = vdev_offline(spa, guids[i], 0);
if (error == 0) {
(void) fprintf(stdout, "%s: marked absent leaf %llu "
"offline\n", target, (u_longlong_t)guids[i]);
continue;
}
failed++;
if (error == EBUSY) {
(void) fprintf(stderr, "%s: leaf %llu holds data no "
"other leaf has, left online\n", target,
(u_longlong_t)guids[i]);
} else {
(void) fprintf(stderr, "%s: could not offline leaf "
"%llu: %s\n", target, (u_longlong_t)guids[i],
strerror(error));
}
}
if (failed != 0) {
/*
* Not "the pool still needs zhack": this run exports cleanly
* under our own hostid, so the next import here skips the
* activity check entirely. The cost lands on the next host
* to take the pool, whose claim will count the leaves left
* online and refuse.
*/
(void) fprintf(stderr, "%s: %d absent leaves are still "
"online; a later import from another host will count "
"them and be refused\n", target, failed);
}
umem_free(guids, max * sizeof (uint64_t));
spa_close(spa, FTAG);
return (failed == 0 ? 0 : 1);
}
static int
zhack_do_mmp(int argc, char **argv)
{
char *subcommand;
argc--;
argv++;
if (argc == 0) {
(void) fprintf(stderr,
"error: no mmp operation specified\n");
usage();
}
subcommand = argv[0];
if (strcmp(subcommand, "reclaim") == 0) {
return (zhack_do_mmp_reclaim(argc, argv));
} else {
(void) fprintf(stderr, "error: unknown subcommand: %s\n",
subcommand);
usage();
}
return (0);
}
static int
zhack_do_action(int argc, char **argv)
{
@@ -1372,6 +1538,8 @@ main(int argc, char **argv)
rv = zhack_do_action(argc, argv);
} else if (strcmp(subcommand, "feature") == 0) {
rv = zhack_do_feature(argc, argv);
} else if (strcmp(subcommand, "mmp") == 0) {
rv = zhack_do_mmp(argc, argv);
} else if (strcmp(subcommand, "label") == 0) {
return (zhack_do_label(argc, argv));
} else if (strcmp(subcommand, "metaslab") == 0) {
+7 -16
View File
@@ -4,23 +4,14 @@
# Print out statistics for all zil stats. This information is
# available through the zil kstat.
#
# CDDL HEADER START
# This file and its contents are supplied under the terms of the
# Common Development and Distribution License ("CDDL"), version 1.0.
# You may only use this file in accordance with the terms of version
# 1.0 of the CDDL.
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License, Version 1.0 only
# (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]
# A full copy of the text of the CDDL should have accompanied this
# source. A copy of the CDDL is also available via the Internet at
# https://opensource.org/license/CDDL-1.0.
#
# This script must remain compatible with Python 3.6+.
#
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+420 -67
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -33,8 +23,9 @@
* Copyright (c) 2017, Intel Corporation.
* Copyright (c) 2019, loli10K <ezomori.nozomu@gmail.com>
* Copyright (c) 2021, Colm Buckley <colm@tuatha.org>
* Copyright (c) 2021, 2023, 2025, Klara, Inc.
* Copyright (c) 2021, 2023-2026, Klara, Inc.
* Copyright (c) 2021, 2025 Hewlett Packard Enterprise Development LP.
* Copyright (c) 2026, TrueNAS.
*/
#include <assert.h>
@@ -127,6 +118,7 @@ static int zpool_do_get(int, char **);
static int zpool_do_set(int, char **);
static int zpool_do_sync(int, char **);
static int zpool_do_condense(int, char **);
static int zpool_do_version(int, char **);
@@ -146,7 +138,8 @@ enum zpool_options {
ZPOOL_OPTION_ALLOW_ASHIFT_MISMATCH,
ZPOOL_OPTION_POOL_KEY_GUID,
ZPOOL_OPTION_JSON_NUMS_AS_INT,
ZPOOL_OPTION_JSON_FLAT_VDEVS
ZPOOL_OPTION_JSON_FLAT_VDEVS,
ZPOOL_OPTION_CONDENSE_LIST_TYPES
};
/*
@@ -174,6 +167,7 @@ typedef enum {
HELP_CLEAR,
HELP_CREATE,
HELP_CHECKPOINT,
HELP_CONDENSE,
HELP_DDT_PRUNE,
HELP_DESTROY,
HELP_DETACH,
@@ -361,6 +355,16 @@ static const char *vdev_trim_state_str[] = {
"COMPLETE"
};
static const char *condense_unit(const char *type) {
if (strcmp(type, POOL_CONDENSE_LOG_SPACEMAP) == 0)
return ("metaslabs");
#ifdef ZFS_DEBUG
if (strcmp(type, "debug") == 0)
return ("nits"); /* fundamental unit of debugging? ;) */
#endif
return ("items");
}
#define ZFS_NICE_TIMESTAMP 100
/*
@@ -417,6 +421,7 @@ static zpool_command_t command_table[] = {
{ "resilver", zpool_do_resilver, HELP_RESILVER },
{ "scrub", zpool_do_scrub, HELP_SCRUB },
{ "trim", zpool_do_trim, HELP_TRIM },
{ "condense", zpool_do_condense, HELP_CONDENSE },
{ NULL },
{ "import", zpool_do_import, HELP_IMPORT },
{ "export", zpool_do_export, HELP_EXPORT },
@@ -428,6 +433,7 @@ static zpool_command_t command_table[] = {
{ NULL },
{ "get", zpool_do_get, HELP_GET },
{ "set", zpool_do_set, HELP_SET },
{ NULL },
{ "sync", zpool_do_sync, HELP_SYNC },
{ NULL },
{ "wait", zpool_do_wait, HELP_WAIT },
@@ -509,11 +515,12 @@ get_usage(zpool_help_t idx)
case HELP_REOPEN:
return (gettext("\treopen [-n] <pool>\n"));
case HELP_INITIALIZE:
return (gettext("\tinitialize [-c | -s | -u] [-w] <-a | <pool> "
"[<device> ...]>\n"));
return (gettext("\tinitialize [-c | -s | -u] [-w] [-z] "
"<-a | <pool> [<device> ...]>\n"));
case HELP_SCRUB:
return (gettext("\tscrub [-e | -s | -p | -C | -E | -S] [-w] "
"<-a | <pool> [<pool> ...]>\n"));
return (gettext("\tscrub [-e | -s | -p | -t | -C [-t] | "
"[-S date] [-E date] [-t]] [-w]\n"
"\t <-a | <pool> [<pool> ...]>\n"));
case HELP_RESILVER:
return (gettext("\tresilver <pool> ...\n"));
case HELP_TRIM:
@@ -547,6 +554,10 @@ get_usage(zpool_help_t idx)
return (gettext("\treguid [-g guid] <pool>\n"));
case HELP_SYNC:
return (gettext("\tsync [pool] ...\n"));
case HELP_CONDENSE:
return (gettext("\tcondense [-t <type>] [-c | -w] "
"[-a | <pool> ...]\n"
"\tcondense --types\n"));
case HELP_VERSION:
return (gettext("\tversion [-j]\n"));
case HELP_WAIT:
@@ -751,7 +762,7 @@ usage(boolean_t requested)
}
/*
* zpool initialize [-c | -s | -u] [-w] <-a | pool> [<vdev> ...]
* zpool initialize [-c | -s | -u] [-w] [-z] <-a | pool> [<vdev> ...]
* Initialize all unused blocks in the specified vdevs, or all vdevs in the pool
* if none specified.
*
@@ -770,6 +781,7 @@ zpool_do_initialize(int argc, char **argv)
int err = 0;
boolean_t wait = B_FALSE;
boolean_t initialize_all = B_FALSE;
boolean_t zero = B_FALSE;
struct option long_options[] = {
{"cancel", no_argument, NULL, 'c'},
@@ -777,16 +789,20 @@ zpool_do_initialize(int argc, char **argv)
{"uninit", no_argument, NULL, 'u'},
{"wait", no_argument, NULL, 'w'},
{"all", no_argument, NULL, 'a'},
{"zero", no_argument, NULL, 'z'},
{0, 0, 0, 0}
};
pool_initialize_func_t cmd_type = POOL_INITIALIZE_START;
while ((c = getopt_long(argc, argv, "acsuw", long_options,
while ((c = getopt_long(argc, argv, "acsuwz", long_options,
NULL)) != -1) {
switch (c) {
case 'a':
initialize_all = B_TRUE;
break;
case 'z':
zero = B_TRUE;
break;
case 'c':
if (cmd_type != POOL_INITIALIZE_START &&
cmd_type != POOL_INITIALIZE_CANCEL) {
@@ -835,7 +851,9 @@ zpool_do_initialize(int argc, char **argv)
initialize_cbdata_t cbdata = {
.wait = wait,
.cmd_type = cmd_type
.cmd_type = cmd_type,
.value = 0,
.value_provided = zero
};
if (initialize_all && argc > 0) {
@@ -855,6 +873,12 @@ zpool_do_initialize(int argc, char **argv)
usage(B_FALSE);
}
if (zero && (cmd_type != POOL_INITIALIZE_START)) {
(void) fprintf(stderr, gettext("-z cannot be used with -c, -s "
"or -u\n"));
usage(B_FALSE);
}
if (argc == 0 && initialize_all) {
/* Initilize each pool recursively */
err = for_each_pool(argc, argv, B_TRUE, NULL, ZFS_TYPE_POOL,
@@ -878,9 +902,11 @@ zpool_do_initialize(int argc, char **argv)
fnvlist_add_boolean(vdevs, argv[i]);
}
if (wait)
err = zpool_initialize_wait(zhp, cmd_type, vdevs);
err = zpool_initialize_wait(zhp, cmd_type, vdevs,
cbdata.value, cbdata.value_provided);
else
err = zpool_initialize(zhp, cmd_type, vdevs);
err = zpool_initialize(zhp, cmd_type, vdevs,
cbdata.value, cbdata.value_provided);
fnvlist_free(vdevs);
}
@@ -8431,6 +8457,7 @@ zpool_do_reopen(int argc, char **argv)
typedef struct scrub_cbdata {
int cb_type;
pool_scrub_cmd_t cb_scrub_cmd;
pool_scrub_flags_t cb_scrub_flags;
time_t cb_date_start;
time_t cb_date_end;
} scrub_cbdata_t;
@@ -8477,7 +8504,7 @@ scrub_callback(zpool_handle_t *zhp, void *data)
}
err = zpool_scan_range(zhp, cb->cb_type, cb->cb_scrub_cmd,
cb->cb_date_start, cb->cb_date_end);
cb->cb_scrub_flags, cb->cb_date_start, cb->cb_date_end);
if (err == 0 && zpool_has_checkpoint(zhp) &&
cb->cb_type == POOL_SCAN_SCRUB) {
(void) printf(gettext("warning: will not scrub state that "
@@ -8518,7 +8545,7 @@ date_string_to_sec(const char *timestr, boolean_t rounding)
}
/*
* zpool scrub [-e | -s | -p | -C | -E | -S] [-w] [-a | <pool> ...]
* zpool scrub [-e | -s | -p | -C | -E | -S | -t] [-w] [-a | <pool> ...]
*
* -a Scrub all pools.
* -e Only scrub blocks in the error log.
@@ -8527,6 +8554,7 @@ date_string_to_sec(const char *timestr, boolean_t rounding)
* -s Stop. Stops any in-progress scrub.
* -p Pause. Pause in-progress scrub.
* -w Wait. Blocks until scrub has completed.
* -t Decompress and decrypt (if key is loaded) scrubbed blocks.
* -C Scrub from last saved txg.
*/
int
@@ -8538,17 +8566,17 @@ zpool_do_scrub(int argc, char **argv)
int error;
cb.cb_type = POOL_SCAN_SCRUB;
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
cb.cb_scrub_cmd = 0;
cb.cb_scrub_flags = 0;
cb.cb_date_start = cb.cb_date_end = 0;
boolean_t is_error_scrub = B_FALSE;
boolean_t is_pause = B_FALSE;
boolean_t is_stop = B_FALSE;
boolean_t is_txg_continue = B_FALSE;
boolean_t scrub_all = B_FALSE;
/* check options */
while ((c = getopt(argc, argv, "aspweCE:S:")) != -1) {
while ((c = getopt(argc, argv, "aspweCE:S:t")) != -1) {
switch (c) {
case 'a':
scrub_all = B_TRUE;
@@ -8556,6 +8584,9 @@ zpool_do_scrub(int argc, char **argv)
case 'e':
is_error_scrub = B_TRUE;
break;
case 't':
cb.cb_scrub_flags |= POOL_SCRUB_THOROUGH;
break;
case 'E':
/*
* Round the date. It's better to scrub more data than
@@ -8576,7 +8607,7 @@ zpool_do_scrub(int argc, char **argv)
wait = B_TRUE;
break;
case 'C':
is_txg_continue = B_TRUE;
cb.cb_scrub_cmd |= POOL_SCRUB_FROM_LAST_TXG;
break;
case '?':
(void) fprintf(stderr, gettext("invalid option '%c'\n"),
@@ -8589,17 +8620,39 @@ zpool_do_scrub(int argc, char **argv)
(void) fprintf(stderr, gettext("invalid option "
"combination: -s and -p are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_pause && is_txg_continue) {
} else if (is_error_scrub && is_pause) {
(void) fprintf(stderr, gettext("invalid option "
"combination: -e and -p are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_error_scrub && is_stop) {
(void) fprintf(stderr, gettext("invalid option "
"combination: -e and -s are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_error_scrub &&
(cb.cb_scrub_cmd & POOL_SCRUB_FROM_LAST_TXG)) {
(void) fprintf(stderr, gettext("invalid option "
"combination: -e and -C are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_error_scrub &&
(cb.cb_scrub_flags & POOL_SCRUB_THOROUGH)) {
(void) fprintf(stderr, gettext("invalid option "
"combination: -e and -t are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_pause && (cb.cb_scrub_cmd & POOL_SCRUB_FROM_LAST_TXG)) {
(void) fprintf(stderr, gettext("invalid option "
"combination: -p and -C are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_stop && is_txg_continue) {
} else if (is_pause && (cb.cb_scrub_flags & POOL_SCRUB_THOROUGH)) {
(void) fprintf(stderr, gettext("invalid option "
"combination: -p and -t are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_stop && (cb.cb_scrub_cmd & POOL_SCRUB_FROM_LAST_TXG)) {
(void) fprintf(stderr, gettext("invalid option "
"combination: -s and -C are mutually exclusive\n"));
usage(B_FALSE);
} else if (is_error_scrub && is_txg_continue) {
} else if (is_stop && (cb.cb_scrub_flags & POOL_SCRUB_THOROUGH)) {
(void) fprintf(stderr, gettext("invalid option "
"combination: -e and -C are mutually exclusive\n"));
"combination: -s and -t are mutually exclusive\n"));
usage(B_FALSE);
} else {
if (is_error_scrub)
@@ -8609,19 +8662,26 @@ zpool_do_scrub(int argc, char **argv)
cb.cb_scrub_cmd = POOL_SCRUB_PAUSE;
} else if (is_stop) {
cb.cb_type = POOL_SCAN_NONE;
} else if (is_txg_continue) {
cb.cb_scrub_cmd = POOL_SCRUB_FROM_LAST_TXG;
} else {
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
}
}
boolean_t is_thorough =
(cb.cb_scrub_flags & POOL_SCRUB_THOROUGH) != 0;
if ((cb.cb_date_start != 0 || cb.cb_date_end != 0) &&
cb.cb_scrub_cmd != POOL_SCRUB_NORMAL) {
(void) fprintf(stderr, gettext("invalid option combination: "
"start/end date is available only with normal scrub\n"));
(cb.cb_scrub_cmd & POOL_SCRUB_FROM_LAST_TXG)) {
(void) fprintf(stderr, gettext("invalid option "
"combination: -C and -S/-E date are mutually "
"exclusive\n"));
usage(B_FALSE);
} else if ((cb.cb_date_start != 0 || cb.cb_date_end != 0) &&
(is_error_scrub || is_stop || is_pause ||
(!is_thorough && cb.cb_scrub_cmd != POOL_SCRUB_NORMAL))) {
(void) fprintf(stderr, gettext("invalid option "
"combination: start/end date is available only "
"with normal or thorough scrub\n"));
usage(B_FALSE);
}
if (cb.cb_date_start != 0 && cb.cb_date_end != 0 &&
cb.cb_date_start > cb.cb_date_end) {
(void) fprintf(stderr, gettext("invalid arguments: "
@@ -8669,6 +8729,7 @@ zpool_do_resilver(int argc, char **argv)
cb.cb_type = POOL_SCAN_RESILVER;
cb.cb_scrub_cmd = POOL_SCRUB_NORMAL;
cb.cb_scrub_flags = 0;
cb.cb_date_start = cb.cb_date_end = 0;
/* check options */
@@ -8850,6 +8911,156 @@ zpool_do_trim(int argc, char **argv)
return (error);
}
static const char *condense_types[] = {
POOL_CONDENSE_LOG_SPACEMAP,
#ifdef ZFS_DEBUG
"debug",
#endif
NULL,
};
typedef struct {
const char *cmd;
const char *type;
} condense_cb_t;
static int
condense_cb(zpool_handle_t *zhp, void *data)
{
condense_cb_t *cb = data;
if (cb->type != NULL)
return (zpool_condense(zhp, cb->cmd, cb->type));
int err = 0;
for (uint_t i = 0; condense_types[i] != NULL; i++) {
#ifdef ZFS_DEBUG
/*
* Skip the debug type when no type is specificed. This is
* the "condense everything" mode, and people _do_ run debug
* builds, and we don't want them to be accidentally running
* the debug condense op.
*/
if (strcmp(condense_types[i], "debug") == 0)
continue;
#endif
err = zpool_condense(zhp, cb->cmd, condense_types[i]);
/*
* Quietly ignore ENOTSUP, so we don't fail all condense types
* just because one isn't supporte.d
*/
if (err != 0 && err != ENOTSUP)
break;
}
return (err);
}
/*
* zpool condense [-t <type>] [-c | -w] [-a | <pool> ...]
*
* -t <type> What to condense.
* -a Condense all pools.
* -c Cancel. Ends any in-progress condense.
* -w Wait. Blocks until condense has completed.
*
* Condense (flush, garbage-collect) the pool metadata on the specfied pools.
*/
static int
zpool_do_condense(int argc, char **argv)
{
struct option long_options[] = {
{"type", required_argument, NULL, 't'},
{"all", no_argument, NULL, 'a'},
{"cancel", no_argument, NULL, 'c'},
{"wait", no_argument, NULL, 'w'},
{"types", no_argument, NULL,
ZPOOL_OPTION_CONDENSE_LIST_TYPES},
{0, 0, 0, 0}
};
condense_cb_t cb = {
.cmd = "start",
.type = NULL,
};
boolean_t cancel = B_FALSE;
boolean_t all_pools = B_FALSE;
boolean_t wait = B_FALSE;
int c;
while ((c = getopt_long(argc, argv, "at:cw", long_options, NULL))
!= -1) {
switch (c) {
case 'a':
all_pools = B_TRUE;
break;
case 't': {
for (uint_t i = 0; condense_types[i] != NULL; i++) {
if (strcmp(condense_types[i], optarg) == 0) {
cb.type = condense_types[i];
break;
}
}
if (cb.type == NULL) {
(void) fprintf(stderr,
gettext("invalid condense type '%s'\n"),
optarg);
usage(B_FALSE);
}
break;
}
case 'c':
cb.cmd = "cancel";
cancel = B_TRUE;
break;
case 'w':
wait = B_TRUE;
break;
case ZPOOL_OPTION_CONDENSE_LIST_TYPES:
for (uint_t i = 0; condense_types[i] != NULL; i++)
printf("%s\n", condense_types[i]);
return (0);
case '?':
if (optopt != 0) {
(void) fprintf(stderr,
gettext("invalid option '%c'\n"), optopt);
} else {
(void) fprintf(stderr,
gettext("invalid option '%s'\n"),
argv[optind - 1]);
}
usage(B_FALSE);
}
}
argc -= optind;
argv += optind;
if (argc < 1 && !all_pools) {
(void) fprintf(stderr, gettext("missing pool name argument\n"));
usage(B_FALSE);
return (-1);
}
if (wait && cancel) {
(void) fprintf(stderr, gettext("-w cannot be used with -c\n"));
usage(B_FALSE);
}
int error = for_each_pool(argc, argv, B_FALSE, NULL, ZFS_TYPE_POOL,
B_FALSE, condense_cb, &cb);
if (wait && !error) {
zpool_wait_activity_t act = ZPOOL_WAIT_CONDENSE;
error = for_each_pool(argc, argv, B_FALSE, NULL, ZFS_TYPE_POOL,
B_FALSE, wait_callback, &act);
}
return (error);
}
/*
* Converts a total number of seconds to a human readable string broken
* down in to days/hours/minutes/seconds.
@@ -8941,7 +9152,7 @@ print_err_scrub_status(pool_scan_stat_t *ps)
* Print out detailed scrub status.
*/
static void
print_scan_scrub_resilver_status(pool_scan_stat_t *ps)
print_scan_scrub_resilver_status(pool_scan_stat_t *ps, boolean_t is_thorough)
{
time_t start, end, pause;
uint64_t pass_scanned, scanned, pass_issued, issued, total_s, total_i;
@@ -8976,10 +9187,17 @@ print_scan_scrub_resilver_status(pool_scan_stat_t *ps)
secs_to_dhms(end - start, time_buf);
if (is_scrub) {
(void) printf(gettext("scrub repaired %s "
"in %s with %llu errors on %s"), processed_buf,
time_buf, (u_longlong_t)ps->pss_errors,
ctime(&end));
if (is_thorough) {
(void) printf(gettext("thorough scrub "
"repaired %s in %s with %llu errors on %s"),
processed_buf, time_buf,
(u_longlong_t)ps->pss_errors, ctime(&end));
} else {
(void) printf(gettext("scrub repaired %s "
"in %s with %llu errors on %s"),
processed_buf, time_buf,
(u_longlong_t)ps->pss_errors, ctime(&end));
}
} else if (is_resilver) {
(void) printf(gettext("resilvered %s "
"in %s with %llu errors on %s"), processed_buf,
@@ -8989,8 +9207,13 @@ print_scan_scrub_resilver_status(pool_scan_stat_t *ps)
return;
} else if (ps->pss_state == DSS_CANCELED) {
if (is_scrub) {
(void) printf(gettext("scrub canceled on %s"),
ctime(&end));
if (is_thorough) {
(void) printf(gettext("thorough scrub canceled "
"on %s"), ctime(&end));
} else {
(void) printf(gettext("scrub canceled on %s"),
ctime(&end));
}
} else if (is_resilver) {
(void) printf(gettext("resilver canceled on %s"),
ctime(&end));
@@ -9003,13 +9226,25 @@ print_scan_scrub_resilver_status(pool_scan_stat_t *ps)
/* Scan is in progress. Resilvers can't be paused. */
if (is_scrub) {
if (pause == 0) {
(void) printf(gettext("scrub in progress since %s"),
ctime(&start));
if (is_thorough) {
(void) printf(gettext("thorough scrub "
"in progress since %s"), ctime(&start));
} else {
(void) printf(gettext("scrub in progress "
"since %s"), ctime(&start));
}
} else {
(void) printf(gettext("scrub paused since %s"),
ctime(&pause));
(void) printf(gettext("\tscrub started on %s"),
ctime(&start));
if (is_thorough) {
(void) printf(gettext("thorough scrub paused "
"since %s"), ctime(&pause));
(void) printf(gettext("\tthorough scrub "
"started on %s"), ctime(&start));
} else {
(void) printf(gettext("scrub paused since %s"),
ctime(&pause));
(void) printf(gettext("\tscrub started on %s"),
ctime(&start));
}
}
} else if (is_resilver) {
(void) printf(gettext("resilver in progress since %s"),
@@ -9929,6 +10164,53 @@ removal_status_nvlist(zpool_handle_t *zhp, status_cbdata_t *cb,
}
}
static void
condense_status_nvlist(nvlist_t *nvroot, status_cbdata_t *cb, nvlist_t *item)
{
nvlist_t *cnv = NULL;
nvlist_lookup_nvlist(nvroot, ZPOOL_CONFIG_CONDENSE_STATS, &cnv);
if (cnv == NULL)
return;
nvlist_t *onv = fnvlist_alloc();
for (nvpair_t *nvp = nvlist_next_nvpair(cnv, NULL);
nvp != NULL; nvp = nvlist_next_nvpair(cnv, nvp)) {
const char *type = nvpair_name(nvp);
nvlist_t *tnv = fnvpair_value_nvlist(nvp);
uint64_t start_time = fnvlist_lookup_uint64(tnv, "start_time");
if (start_time == 0)
continue;
uint64_t end_time = fnvlist_lookup_uint64(tnv, "end_time");
uint64_t processed = fnvlist_lookup_uint64(tnv, "processed");
uint64_t total = fnvlist_lookup_uint64(tnv, "total");
nvlist_t *nv = fnvlist_alloc();
nice_num_str_nvlist(nv, "start_time", start_time,
cb->cb_literal, cb->cb_json_as_int, ZFS_NICE_TIMESTAMP);
if (end_time > 0)
nice_num_str_nvlist(nv, "end_time", end_time,
cb->cb_literal, cb->cb_json_as_int,
ZFS_NICE_TIMESTAMP);
nice_num_str_nvlist(nv, "processed", processed,
cb->cb_literal, cb->cb_json_as_int, ZFS_NICENUM_1024);
nice_num_str_nvlist(nv, "total", total,
cb->cb_literal, cb->cb_json_as_int, ZFS_NICENUM_1024);
fnvlist_add_string(nv, "unit", condense_unit(type));
fnvlist_add_nvlist(onv, type, nv);
fnvlist_free(nv);
}
if (fnvlist_num_pairs(onv))
fnvlist_add_nvlist(item, "condense", onv);
fnvlist_free(onv);
}
static void
scan_status_nvlist(zpool_handle_t *zhp, status_cbdata_t *cb,
nvlist_t *nvroot, nvlist_t *item)
@@ -10099,6 +10381,7 @@ print_scan_status(zpool_handle_t *zhp, nvlist_t *nvroot)
pool_checkpoint_stat_t *pcs = NULL;
pool_scan_stat_t *ps = NULL;
uint_t c;
boolean_t is_thorough = B_FALSE;
time_t scrub_start = 0, errorscrub_start = 0;
if (nvlist_lookup_uint64_array(nvroot, ZPOOL_CONFIG_SCAN_STATS,
@@ -10111,8 +10394,10 @@ print_scan_status(zpool_handle_t *zhp, nvlist_t *nvroot)
have_resilver = (ps->pss_func == POOL_SCAN_RESILVER);
have_scrub = (ps->pss_func == POOL_SCAN_SCRUB);
scrub_start = ps->pss_start_time;
if (c > offsetof(pool_scan_stat_t,
pss_pass_error_scrub_pause) / 8) {
if (POOL_SCAN_STAT_VALID(pss_pass_scrub_flags, c) &&
(ps->pss_pass_scrub_flags & POOL_SCRUB_THOROUGH) != 0)
is_thorough = B_TRUE;
if (POOL_SCAN_STAT_VALID(pss_pass_error_scrub_pause, c)) {
have_errorscrub = (ps->pss_error_scrub_func ==
POOL_SCAN_ERRORSCRUB);
errorscrub_start = ps->pss_error_scrub_start;
@@ -10124,7 +10409,7 @@ print_scan_status(zpool_handle_t *zhp, nvlist_t *nvroot)
/* Always print the scrub status when available. */
if (have_scrub && scrub_start > errorscrub_start)
print_scan_scrub_resilver_status(ps);
print_scan_scrub_resilver_status(ps, is_thorough);
else if (have_errorscrub && errorscrub_start >= scrub_start)
print_err_scrub_status(ps);
@@ -10134,7 +10419,7 @@ print_scan_status(zpool_handle_t *zhp, nvlist_t *nvroot)
*/
if (active_resilver || (!active_rebuild && have_resilver &&
resilver_end_time && resilver_end_time > rebuild_end_time)) {
print_scan_scrub_resilver_status(ps);
print_scan_scrub_resilver_status(ps, is_thorough);
} else if (active_rebuild || (!active_resilver && have_rebuild &&
rebuild_end_time && rebuild_end_time > resilver_end_time)) {
print_rebuild_status(zhp, nvroot);
@@ -10373,6 +10658,50 @@ print_checkpoint_status(pool_checkpoint_stat_t *pcs)
space_buf);
}
static void
print_condense_status(nvlist_t *nv)
{
if (nv == NULL)
return;
for (nvpair_t *nvp = nvlist_next_nvpair(nv, NULL);
nvp != NULL; nvp = nvlist_next_nvpair(nv, nvp)) {
const char *type = nvpair_name(nvp);
nvlist_t *tnv = fnvpair_value_nvlist(nvp);
uint64_t start_time = fnvlist_lookup_uint64(tnv, "start_time");
if (start_time == 0)
continue;
uint64_t end_time = fnvlist_lookup_uint64(tnv, "end_time");
uint64_t processed = fnvlist_lookup_uint64(tnv, "processed");
uint64_t total = fnvlist_lookup_uint64(tnv, "total");
const char *units = condense_unit(type);
char cur[32], tot[32], elapsed[32];
zfs_nicenum(processed, cur, sizeof (cur));
zfs_nicenum(total, tot, sizeof (tot));
if (end_time == 0) {
secs_to_dhms(time(NULL) - start_time, elapsed);
(void) printf(gettext(
"condense: %s: condensing, %s/%s %s done in %s\n"),
type, cur, tot, units, elapsed);
} else if (processed < total) {
secs_to_dhms(end_time - start_time, elapsed);
(void) printf(gettext(
"condense: %s: cancelled, %s/%s %s done in %s\n"),
type, cur, tot, units, elapsed);
} else {
secs_to_dhms(end_time - start_time, elapsed);
(void) printf(gettext(
"condense: %s: done, %s %s done in %s\n"),
type, cur, units, elapsed);
}
}
}
static void
print_error_log(zpool_handle_t *zhp)
{
@@ -10672,8 +11001,8 @@ print_status_reason(zpool_handle_t *zhp, status_cbdata_t *cbp,
(void) snprintf(action, AC_SIZE,
gettext("Check the value of the "
"'compatibility' property against the\n\t"
"appropriate file in " ZPOOL_SYSCONF_COMPAT_D " or "
ZPOOL_DATA_COMPAT_D ".\n"));
"appropriate file in %s or %s.\n"),
ZPOOL_SYSCONF_COMPAT_D, ZPOOL_DATA_COMPAT_D);
break;
case ZPOOL_STATUS_INCOMPATIBLE_FEAT:
@@ -10683,7 +11012,8 @@ print_status_reason(zpool_handle_t *zhp, status_cbdata_t *cbp,
(void) snprintf(action, AC_SIZE, gettext("Consider setting "
"'compatibility' to an appropriate value, or\n\t"
"adding needed features to the relevant file in\n\t"
ZPOOL_SYSCONF_COMPAT_D " or " ZPOOL_DATA_COMPAT_D ".\n"));
"%s or %s.\n"),
ZPOOL_SYSCONF_COMPAT_D, ZPOOL_DATA_COMPAT_D);
break;
case ZPOOL_STATUS_UNSUP_FEAT_READ:
@@ -10952,6 +11282,7 @@ status_callback_json(zpool_handle_t *zhp, void *data)
scan_status_nvlist(zhp, cbp, nvroot, item);
removal_status_nvlist(zhp, cbp, nvroot, item);
checkpoint_status_nvlist(nvroot, cbp, item);
condense_status_nvlist(nvroot, cbp, item);
raidz_expand_status_nvlist(zhp, cbp, nvroot, item);
vdev_stats_nvlist(zhp, cbp, nvroot, 0, B_FALSE, NULL, vds);
if (cbp->cb_flat_vdevs) {
@@ -11099,6 +11430,10 @@ status_callback(zpool_handle_t *zhp, void *data)
ZPOOL_CONFIG_RAIDZ_EXPAND_STATS, (uint64_t **)&pres, &c);
print_raidz_expand_status(zhp, pres);
nvlist_t *cnv = NULL;
nvlist_lookup_nvlist(nvroot, ZPOOL_CONFIG_CONDENSE_STATS, &cnv);
print_condense_status(cnv);
cbp->cb_namewidth = max_width(zhp, nvroot, 0, 0,
cbp->cb_name_flags | VDEV_NAME_TYPE_ID);
if (cbp->cb_namewidth < 10)
@@ -13325,8 +13660,10 @@ print_wait_status_row(wait_data_t *wd, zpool_handle_t *zhp, int row)
pool_scan_stat_t *pss = NULL;
pool_removal_stat_t *prs = NULL;
pool_raidz_expand_stat_t *pres = NULL;
nvlist_t *cnv = NULL;
const char *const headers[] = {"DISCARD", "FREE", "INITIALIZE",
"REPLACE", "REMOVE", "RESILVER", "SCRUB", "TRIM", "RAIDZ_EXPAND"};
"REPLACE", "REMOVE", "RESILVER", "SCRUB", "TRIM", "RAIDZ_EXPAND",
"CONDENSE"};
int col_widths[ZPOOL_WAIT_NUM_ACTIVITIES];
/* Calculate the width of each column */
@@ -13395,6 +13732,22 @@ print_wait_status_row(wait_data_t *wd, zpool_handle_t *zhp, int row)
bytes_rem[ZPOOL_WAIT_RAIDZ_EXPAND] = rem;
}
/*
* Count each outstanding condense item as a "byte". Its not true,
* but its a counter, and it'll display nicely.
*/
if (nvlist_lookup_nvlist(nvroot,
ZPOOL_CONFIG_CONDENSE_STATS, &cnv) == 0) {
for (nvpair_t *nvp = nvlist_next_nvpair(cnv, NULL);
nvp != NULL; nvp = nvlist_next_nvpair(cnv, nvp)) {
nvlist_t *tnv = fnvpair_value_nvlist(nvp);
uint64_t total = fnvlist_lookup_uint64(tnv, "total");
uint64_t processed =
fnvlist_lookup_uint64(tnv, "processed");
bytes_rem[ZPOOL_WAIT_CONDENSE] += (total - processed);
}
}
bytes_rem[ZPOOL_WAIT_INITIALIZE] =
vdev_activity_remaining(nvroot, ZPOOL_WAIT_INITIALIZE);
bytes_rem[ZPOOL_WAIT_TRIM] =
@@ -13533,7 +13886,7 @@ zpool_do_wait(int argc, char **argv)
static const char *const col_opts[] = {
"discard", "free", "initialize", "replace",
"remove", "resilver", "scrub", "trim",
"raidz_expand" };
"raidz_expand", "condense" };
for (i = 0; i < ARRAY_SIZE(col_opts); ++i)
if (strcmp(tok, col_opts[i]) == 0) {
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
+7 -17
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -50,22 +50,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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.
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
* You can obtain a copy of the license from the top-level file
* "OPENSOLARIS.LICENSE" or at <http://opensource.org/licenses/CDDL-1.0>.
* You may not use this file except in compliance with the license.
*
* See the License for the specific language governing permissions
* and limitations under the License.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
#include <string.h>
#include <getopt.h>
@@ -361,6 +353,7 @@ print_summary_stats(nvlist_t *nvroot, const char *pool_name,
print_kv(",write_ops", vs->vs_ops[ZIO_TYPE_WRITE]);
print_kv(",checksum_errors", vs->vs_checksum_errors);
print_kv(",fragmentation", vs->vs_fragmentation);
print_kv(",slow_ios", vs->vs_slow_ios);
printf(" %llu\n", (u_longlong_t)timestamp);
return (0);
}
@@ -7,6 +7,8 @@ CPPCHECKTARGETS += zstream
zstream_SOURCES = \
%D%/zstream.c \
%D%/zstream.h \
%D%/zstream_backtrace.c \
%D%/zstream_backtrace.h \
%D%/zstream_byteswap.c \
%D%/zstream_byteswap.h \
%D%/zstream_chain.c \
@@ -14,15 +16,22 @@ zstream_SOURCES = \
%D%/zstream_decompress.c \
%D%/zstream_drop_record.c \
%D%/zstream_dump.c \
%D%/zstream_dump.h \
%D%/zstream_fletcher4.c \
%D%/zstream_fletcher4.h \
%D%/zstream_io.c \
%D%/zstream_io.h \
%D%/zstream_modules.h \
%D%/zstream_raw.c \
%D%/zstream_recompress.c \
%D%/zstream_recompress.h \
%D%/zstream_redup.c \
%D%/zstream_selftest.c \
%D%/zstream_selftest.h \
%D%/zstream_selftest_queue.c \
%D%/zstream_token.c \
%D%/zstream_queue.c \
%D%/zstream_queue.h \
%D%/zstream_util.c \
%D%/zstream_util.h \
%D%/zstream_validate.c \
+30 -6
View File
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
@@ -9,9 +7,7 @@
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -19,11 +15,13 @@
* Copyright (c) 2020 by Datto Inc. All rights reserved.
*/
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "zstream.h"
#include "zstream_util.h"
void
zstream_usage(void)
@@ -39,7 +37,10 @@ zstream_usage(void)
"\n"
"\tzstream drop_record [-v] [OBJECT,OFFSET] ...\n"
"\n"
"\tzstream recompress [ -l level] TYPE\n"
"\tzstream raw [-v] [-b blocks] [-g guid] IMAGE|DEVICE FILE\n"
"\t... | zstream raw [-v] [-b blocks] [-g guid] IMAGE|DEVICE\n"
"\n"
"\tzstream recompress [-t num_threads] [-l level] TYPE\n"
"\n"
"\tzstream token resume_token\n"
"\n"
@@ -47,9 +48,27 @@ zstream_usage(void)
exit(1);
}
/*
* Set the signal mask to allow THREAD_BACKTRACE_SIGNAL. WATCHDOG_SIGNAL
* must be blocked in all threads so that its intended recipient can listen
* for it with sigwait(), which detects only pending signals.
*/
static void
set_signal_mask(void)
{
sigset_t mask;
safe_pthread_sigmask(SIG_SETMASK, NULL, &mask);
sigaddset(&mask, WATCHDOG_SIGNAL);
sigdelset(&mask, THREAD_BACKTRACE_SIGNAL);
safe_pthread_sigmask(SIG_SETMASK, &mask, NULL);
}
int
main(int argc, char *argv[])
{
set_signal_mask();
char *basename = strrchr(argv[0], '/');
basename = basename ? (basename + 1) : argv[0];
if (argc >= 1 && strcmp(basename, "zstreamdump") == 0)
@@ -66,12 +85,17 @@ main(int argc, char *argv[])
return (zstream_do_decompress(argc - 1, argv + 1));
} else if (strcmp(subcommand, "drop_record") == 0) {
return (zstream_do_drop_record(argc - 1, argv + 1));
} else if (strcmp(subcommand, "raw") == 0) {
return (zstream_do_raw(argc - 1, argv + 1));
} else if (strcmp(subcommand, "recompress") == 0) {
return (zstream_do_recompress(argc - 1, argv + 1));
} else if (strcmp(subcommand, "token") == 0) {
return (zstream_do_token(argc - 1, argv + 1));
} else if (strcmp(subcommand, "redup") == 0) {
return (zstream_do_redup(argc - 1, argv + 1));
} else if (strcmp(subcommand, "selftest") == 0) {
/* Undocumented; used by the ZFS test suite */
return (zstream_do_selftest(argc - 1, argv + 1));
} else {
zstream_usage();
}
+13 -6
View File
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
@@ -9,9 +7,7 @@
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -21,17 +17,28 @@
#ifndef _ZSTREAM_H
#define _ZSTREAM_H
#include <signal.h>
#ifdef __cplusplus
extern "C" {
#endif
/*
* Signals used by the watchdog timer. The signal mask must be set properly
* before any threads are spawned.
*/
#define WATCHDOG_SIGNAL SIGALRM
#define THREAD_BACKTRACE_SIGNAL SIGRTMIN
extern int zstream_do_redup(int, char *[]);
extern int zstream_do_dump(int, char *[]);
extern int zstream_do_decompress(int argc, char *argv[]);
extern int zstream_do_drop_record(int argc, char *argv[]);
extern int zstream_do_recompress(int argc, char *argv[]);
extern int zstream_do_token(int, char *[]);
extern void zstream_usage(void);
extern int zstream_do_raw(int, char *[]);
extern int zstream_do_selftest(int, char *[]);
extern void zstream_usage(void) __attribute__((noreturn));
#ifdef __cplusplus
}
@@ -0,0 +1,181 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2026 by Garth Snyder. All rights reserved.
*/
/*
* This is a watchdog timer and multithread backtrace dumper that's used by
* zstream selftest. libspl_backtrace() does all the real work, but it can
* only dump the current thread's stack. We need to get every thread to call
* libspl_backtrace() in an organized sequence. However, there's no "give me
* a list of all pthreads" function in the POSIX API.
*
* Rather than constructing an ad-hoc thread registry, we can approach the
* problem by unblocking THREAD_BACKTRACE_SIGNAL (an arbitrary choice) when
* zstream first starts. All created threads then inherit this signal mask.
*
* In selftest mode, we add the backtrace dumper as a handler for
* THREAD_BACKTRACE_SIGNAL. The handler calls libspl_backtrace(), which is
* signal-handler safe. It then signals a semaphore to indicate that the
* current thread has finished its dump and suspends itself.
*
* The watchdog supervisor is a separate thread that sigwait()s for SIGALRM.
* It blocks THREAD_BACKTRACE_SIGNAL and runs its own libspl_backtrace(). It
* then enters a loop in which it sends THREAD_BACKTRACE_SIGNAL to the
* process as a whole and runs sem_timedwait() to see if any thread woke up
* and dumped its backtrace. If that call times out, either the receiving
* thread wedged while trying to backtrace or there were no more threads to
* backtrace.
*
* These two scenarios can be distinguished by calling sigpending(). If
* THREAD_BACKTRACE_SIGNAL still shows as being pending on the process, then
* there was no thread to receive it and we are done. If there's no pending
* signal, then some thread did receive the signal but failed to post to the
* semaphore; we print a "thread wedged while backtracing" message and
* continue the loop.
*/
#include <err.h>
#include <errno.h>
#include <semaphore.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/backtrace.h>
#include <sys/debug.h>
#include <sys/stdtypes.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>
#include "zstream.h"
#include "zstream_backtrace.h"
#include "zstream_util.h"
/*
* Watchdog timeout in seconds. A test that hits this limit is almost
* certainly deadlocked, and the watchdog converts the hang into a test
* failure instead of a stuck test run.
*/
#define WATCHDOG_TIMEOUT_SECS 120
#define MAX_SECS_FOR_BACKTRACE 2
static sem_t sem_thread_bt_complete; /* thread -> watchdog */
/*
* Signal handler for THREAD_BACKTRACE_SIGNAL, run by all threads except the
* watchdog thread
*/
static void
backtrace_self(int signal)
{
(void) signal;
ssize_t dummy __maybe_unused = write(STDERR_FILENO, "\n", 1);
libspl_backtrace(STDERR_FILENO);
sem_post(&sem_thread_bt_complete);
sigset_t mask;
sigfillset(&mask);
sigsuspend(&mask);
}
static void
backtrace_all_threads(void)
{
while (B_TRUE) {
if (kill(getpid(), THREAD_BACKTRACE_SIGNAL) != 0)
err(1, "failed to send thread backtrace signal");
struct timespec deadline;
clock_gettime(CLOCK_REALTIME, &deadline);
deadline.tv_sec += MAX_SECS_FOR_BACKTRACE;
if (sem_timedwait(&sem_thread_bt_complete, &deadline) != 0) {
sigset_t pending;
if (sigpending(&pending) != 0)
err(1, "sigpending failed");
if (sigismember(&pending, THREAD_BACKTRACE_SIGNAL)) {
return;
} else {
warnx("a thread failed to generate a backtrace,"
" continuing...");
}
}
}
}
/*
* Body of the watchdog thread
*/
static void *
watchdog(void *nope)
{
(void) nope;
int signal;
sigset_t bt_mask, dog_mask;
/*
* This thread does its own backtrace, so we block the
* backtrace signal.
*/
sigemptyset(&bt_mask);
sigaddset(&bt_mask, THREAD_BACKTRACE_SIGNAL);
if (pthread_sigmask(SIG_BLOCK, &bt_mask, NULL) != 0)
err(1, "pthread_sigmask failed");
sigemptyset(&dog_mask);
sigaddset(&dog_mask, WATCHDOG_SIGNAL);
int rc = sigwait(&dog_mask, &signal);
if (rc != 0) {
errno = rc;
err(1, "watchdog sigwait failed");
} else if (signal != WATCHDOG_SIGNAL) {
errx(1, "unexpected signal %d received by watchdog", signal);
}
fprintf(stderr, "\n\nWATCHDOG TIMER EXPIRED\n"
"Dumping backtrace for all threads...\n\n");
fflush(stderr);
libspl_backtrace(STDERR_FILENO);
backtrace_all_threads();
fprintf(stderr, "\nAll threads backtraced, exiting.\n");
exit(1);
}
void
watchdog_init(void)
{
if (sem_init(&sem_thread_bt_complete, 0, 0) != 0)
err(1, "watchdog sem_init failed");
safe_create_thread(watchdog, NULL, "watchdog", B_TRUE);
struct sigaction sa = {
.sa_handler = backtrace_self,
.sa_flags = SA_RESTART
};
if (sigaction(THREAD_BACKTRACE_SIGNAL, &sa, NULL) != 0)
err(1, "backtrace sigaction failed");
}
void
watchdog_arm(void)
{
(void) alarm(WATCHDOG_TIMEOUT_SECS);
}
void
watchdog_disarm(void)
{
(void) alarm(0);
}
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2026 by Garth Snyder. All rights reserved.
*/
#ifndef _ZSTREAM_BACKTRACE_H
#define _ZSTREAM_BACKTRACE_H
#ifdef __cplusplus
extern "C" {
#endif
void
watchdog_init(void);
void
watchdog_arm(void);
void
watchdog_disarm(void);
#ifdef __cplusplus
}
#endif
#endif /* _ZSTREAM_BACKTRACE_H */
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* This file and its contents are supplied under the terms of the Common
* Development and Distribution License ("CDDL"), version 1.0. You may only use
* this file in accordance with the terms of version 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this source. A
* copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -40,8 +37,11 @@ static byteswap_context_t byteswap_contexts[MAX_BYTESWAP];
static int next_context = 0;
static disposition_t
chain_byteswap(drr_packet_t *item, byteswap_context_t *context)
chain_byteswap(void *item_in, void *context_in)
{
drr_packet_t *item = (drr_packet_t *)item_in;
byteswap_context_t *context = (byteswap_context_t *)context_in;
if (item == NULL) {
return (D_OK);
}
@@ -191,7 +191,7 @@ serial_byteswap(byteswap_stage_t stage)
.cs_out_size = sizeof (drr_packet_t),
.cs_context = bsc,
.cs_serial = {
.process = (zc_serial_process_f *)chain_byteswap,
.process = chain_byteswap,
}
};
return (step);
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* This file and its contents are supplied under the terms of the Common
* Development and Distribution License ("CDDL"), version 1.0. You may only use
* this file in accordance with the terms of version 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this source. A
* copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
+248 -73
View File
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* This file and its contents are supplied under the terms of the Common
* Development and Distribution License ("CDDL"), version 1.0. You may only use
* this file in accordance with the terms of version 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this source. A
* copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -20,41 +17,47 @@
#include <assert.h>
#include <err.h>
#include <libspl.h>
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/abd.h>
#include <sys/param.h>
#include <sys/stdtypes.h>
#include <sys/zio.h>
#include <sys/zstd/zstd.h>
#include <sys/zfs_refcount.h>
#include <zfs_fletcher.h>
#include "zstream_chain.h"
#include "zstream_queue.h"
#include "zstream_util.h"
#define MAX_CHAIN_LENGTH 32
/*
* Calculated information about a chain. Not to be confused with
* chain_attrs_t, which is a global set of options and stream attributes
* available to all chain steps.
*/
typedef struct {
int ct_num_steps;
int ct_num_queues;
size_t ct_item_size;
} chain_stats_t;
/*
* Data passed to worker threads
*/
typedef struct {
chain_step_t *wc_steps;
int wc_num_steps;
size_t wc_buffer_size;
zstream_queue_t *wc_in_queue;
zstream_queue_t *wc_out_queue;
} worker_context_t;
chain_attrs_t *chain_attrs;
static disposition_t
chain_null_step(void *item, void *context)
{
(void) item;
(void) context;
return (D_OK);
}
chain_step_t
serial_null_step(void)
{
chain_step_t step = {
.cs_type = CS_SERIAL,
.cs_serial = {
.process = (zc_serial_process_f *)chain_null_step
}
};
return (step);
}
chain_step_t
chain_terminator(void)
{
@@ -85,67 +88,239 @@ libraries_fini(void)
}
/*
* Execute a chain of serial processing steps.
* Body function for worker threads
*/
static void *
zstream_chain_worker(void *ctxt_in)
{
worker_context_t *ctxt = (worker_context_t *)ctxt_in;
uint8_t buffer[ctxt->wc_buffer_size];
boolean_t done = B_FALSE;
while (!done) {
for (int i = 0; i < ctxt->wc_num_steps; i++) {
chain_step_t *step = &ctxt->wc_steps[i];
if (step->cs_type == CS_SERIAL) {
if (done) {
(void) step->cs_serial.process(NULL,
step->cs_context);
} else {
disposition_t dispo =
step->cs_serial.process(buffer,
step->cs_context);
if (dispo == D_EOF) {
done = B_TRUE;
} else if (dispo == D_DROP) {
break;
}
}
} else if (i == 0) {
done = done ||
!zstream_dequeue(ctxt->wc_in_queue, buffer);
} else if (done) {
zstream_queue_fini(ctxt->wc_out_queue);
} else {
zstream_enqueue(ctxt->wc_out_queue, buffer);
}
}
}
return (NULL);
}
/*
* Validate chain and calculate number of steps, max packet size, and number
* of zstream_queues that must be created.
*/
static chain_stats_t
validate_chain(zstream_chain_t chain)
{
int num_steps = 0;
int num_queues = 0;
size_t item_size = 0;
while (chain[num_steps].cs_type != CS_TERMINATE) {
if (num_steps >= MAX_CHAIN_LENGTH) {
errx(1, "unterminated zstream_chain");
}
chain_step_t *step = &chain[num_steps];
item_size = MAX(item_size, step->cs_out_size);
if (step->cs_type == CS_PARALLEL) {
num_queues++;
}
num_steps++;
}
VERIFY3U(num_steps, >, 0);
boolean_t first_parallel = chain[0].cs_type == CS_PARALLEL;
boolean_t last_parallel = chain[num_steps-1].cs_type == CS_PARALLEL;
if (first_parallel || last_parallel) {
errx(1, "a chain cannot start or end with a parallel step");
}
/*
* Check for consistency of input and output packet sizes in
* adjacent steps.
*/
for (int i = 0; i < num_steps; i++) {
if (i > 0 && chain[i].cs_in_size != chain[i-1].cs_out_size) {
warnx("adjacent chain steps %d and %d declare "
"incompatible packet sizes", i - 1, i);
}
}
chain_stats_t stats = {
.ct_num_steps = num_steps,
.ct_num_queues = num_queues,
.ct_item_size = item_size
};
return (stats);
}
/*
* Execute a chain of processing steps, some parallel and some serial.
*
* For simplicity, we normalize the chain item size to that of the largest
* output of any step. Packets with data beyond the base drr_record_t should
* add their additional data to the end of the packet, and this area may be
* reused for different purposes as items travel down the chain.
* output of any step. Payloads are allocated on the heap, so the maximal
* item size is typically on the order of 512 bytes.
*
* Each item traverses the entire chain before the next item is read.
* Packets with data beyond the base drr_record_t should add their
* additional data to the end of the packet, and this area may be reused for
* different purposes as items travel down the chain.
*
* One worker thread is assigned to every contiguous sequence of serial
* steps plus the parallel steps on either side of that block (if any).
* Adjacent parallel steps also receive a worker. This isn't a special case,
* it's just the same rule with the serial block consisting of zero steps.
*
* Parallel steps are double-covered, which is the intended behavior. If a
* worker's domain begins with a parallel step, it dequeues items from the
* associated queue. If the domain ends with a parallel step, it submits
* items to that queue.
*/
void
zstream_chain_exec(zstream_chain_t chain, chain_attrs_t *attrs)
{
int num_steps = 0;
size_t packet_size = 0;
chain_attrs_t backup_attrs = {0};
chain_stats_t stats = validate_chain(chain);
chain_attrs = attrs ? attrs : &backup_attrs;
while (chain[num_steps].cs_type != CS_TERMINATE) {
packet_size = MAX(packet_size, chain[num_steps].cs_out_size);
num_steps++;
if (num_steps >= MAX_CHAIN_LENGTH) {
errx(1, "unterminated zstream_chain");
}
}
VERIFY3U(num_steps, >, 0);
int num_workers = stats.ct_num_queues + 1;
worker_context_t contexts[num_workers];
pthread_t worker_threads[num_workers];
zstream_queue_t *queue;
/*
* Check for consistency of input and output packet sizes in
* adjacent steps. A declared packet size of zero waives this check.
* Create parallel queues and worker thread contexts
*
* We do not need to track zstream_queues independently of worker
* contexts because queues clean themselves up once the last item
* has been dequeued. The stream eventually ends, so some worker
* thread will eventually call zstream_queue_fini() on every queue.
*/
for (int i = 0; i < num_steps; i++) {
boolean_t mismatch = i > 0 &&
chain[i].cs_in_size != 0 &&
chain[i-1].cs_out_size != 0 &&
chain[i].cs_in_size != chain[i-1].cs_out_size;
if (mismatch) {
warnx("note - chain steps %d and %d have "
"mismatched packet sizes", i - 1, i);
int worker = 0;
worker_context_t context = {
.wc_steps = chain,
.wc_num_steps = 0,
.wc_buffer_size = stats.ct_item_size,
.wc_in_queue = NULL
};
contexts[worker] = context;
for (int i = 0; i < stats.ct_num_steps; i++) {
contexts[worker].wc_num_steps++;
if (chain[i].cs_type == CS_PARALLEL) {
chain_step_t *cs = &chain[i];
zq_params_t queue_params = {
.qp_process = cs->cs_parallel.process,
.qp_cost = cs->cs_parallel.cost,
.qp_item_size = stats.ct_item_size,
.qp_batch_budget = cs->cs_parallel.batch_budget,
.qp_queue_length = cs->cs_parallel.queue_length,
.qp_context = cs->cs_context
};
queue = zstream_queue_create(&queue_params);
contexts[worker].wc_out_queue = queue;
worker++;
worker_context_t next_context = {
.wc_steps = cs,
.wc_num_steps = 1,
.wc_buffer_size = stats.ct_item_size,
.wc_in_queue = queue
};
contexts[worker] = next_context;
}
}
contexts[worker].wc_out_queue = NULL;
chain_attrs_t backup_attrs = {0};
chain_attrs = attrs ? attrs : &backup_attrs;
libraries_init();
uint8_t buffer[packet_size];
/* Spawn threads */
for (int i = 0; i < num_workers; i++) {
char name[32];
snprintf(name, sizeof (name), "chain-%d", i);
worker_threads[i] = safe_create_thread(zstream_chain_worker,
&contexts[i], name, B_FALSE);
}
/* Reap threads */
for (int i = 0; i < num_workers; i++) {
int ret = pthread_join(worker_threads[i], NULL);
VERIFY3S(ret, ==, 0);
}
libraries_fini();
}
/*
* Execute a chain linearly, without queues and without multithreading. This
* form of execution is intended as a debugging aid, both for clients and
* for the chain mechanism itself. If this variant doesn't produce results
* identical to zstream_chain_exec(), there's a multithreading-related bug
* somewhere.
*
* It is not necessary to remove parallel steps from the input chain. They
* are accepted as-is, but their execution won't be parallelized.
*/
void
zstream_chain_exec_serialized(zstream_chain_t chain, chain_attrs_t *attrs)
{
chain_stats_t stats = validate_chain(chain);
uint8_t buffer[stats.ct_item_size];
boolean_t done = B_FALSE;
chain_attrs_t backup_attrs = {0};
chain_attrs = attrs ? attrs : &backup_attrs;
libraries_init();
while (!done) {
for (int i = 0; i < num_steps; i++) {
if (done) {
(void) chain[i].cs_serial.process(NULL,
chain[i].cs_context);
} else {
disposition_t dispo =
chain[i].cs_serial.process(buffer,
chain[i].cs_context);
if (dispo == D_EOF) {
VERIFY0(i);
done = B_TRUE;
} else if (dispo == D_DROP) {
break;
for (int i = 0; i < stats.ct_num_steps; i++) {
chain_step_t *step = &chain[i];
if (step->cs_type == CS_SERIAL) {
if (done) {
(void) step->cs_serial.process(NULL,
step->cs_context);
} else {
disposition_t dispo =
step->cs_serial.process(buffer,
step->cs_context);
if (dispo == D_EOF) {
done = B_TRUE;
} else if (dispo == D_DROP) {
break;
}
}
} else if (!done) {
size_t cost = step->cs_parallel.cost(buffer,
step->cs_context);
if (cost > 0) {
step->cs_parallel.process(buffer,
step->cs_context);
}
}
}
+70 -46
View File
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* This file and its contents are supplied under the terms of the Common
* Development and Distribution License ("CDDL"), version 1.0. You may only use
* this file in accordance with the terms of version 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this source. A
* copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -28,6 +25,8 @@ extern "C" {
#include <stdint.h>
#include <sys/zfs_ioctl.h>
#include "zstream_queue.h"
/*
* A chain is a linear series of steps that process packets of data. It's
* designed to modularize common functionality, reduce code duplication, and
@@ -36,20 +35,21 @@ extern "C" {
* Some terms:
*
* **STEP** - A chain_step_t struct that represents a packet-processing
* module and any arguments or context that it needs. Chain modules
* generally define a function named serial_* that produces a chain_step_t
* that can be incorporated directly into a chain.
* module and any arguments or context that it needs. Chain participants
* generally define a function named serial_xxx() or parallel_xxx() that
* produces a chain_step_t that can be incorporated directly into a chain.
*
* **CHAIN** - An array of chain_step_t's. It's just data, so you can create
* the array however you like. But normally you'd just declare the whole
* thing:
* chain at once, e.g.:
*
* zstream_chain_t dump_chain = {
* serial_read_stream(infile),
* parallel_calc_fletcher4(1024),
* serial_validate_fletcher4(),
* serial_byteswap(BS_INCOMING),
* serial_validate_records(),
* serial_dump_records(&dump_args),
* serial_dump_records(),
* serial_null_output(),
* chain_terminator()
* }
@@ -58,7 +58,7 @@ extern "C" {
*
* zstream_chain_t dump_chain = {
* STANDARD_INPUT_STACK(infile),
* serial_dump_records(&dump_args),
* serial_dump_records(),
* NULL_OUTPUT_STACK()
* };
*
@@ -73,20 +73,26 @@ extern "C" {
* **PROCESSING FUNCTION** - Each step names a processing function that does
* the actual work of transforming an input buffer into an output buffer.
* The transformation happens in place, in a single buffer provided by the
* chain.
* chain. (Steps that run in parallel must also identify a cost function; see
* zstream_queue.h.)
*
* The processing function should return a disposition_t, normally D_OK. A
* function can return D_DROP to remove an item from the stream entirely. It
* can also return D_EOF to indicate that no more data will be forthcoming.
* However, only the first step in the chain should ever return D_EOF.
* The processing function for a serial step should return a disposition_t,
* normally D_OK. A processing function can return D_DROP to remove an item
* from the stream entirely. It can also return D_EOF to indicate that no
* more data will be forthcoming. However, only the first step in the chain
* should ever return D_EOF.
*
* Functions are called with a NULL packet pointer when the end of the
* stream passes by them.
* Serial functions are called with a NULL packet when the end of the
* stream passes by them. Since parallel functions may see packets in any
* order, they have no concept of "end of stream" and do not receive this
* notification.
*
* **CONTEXT** - An arbitrary void * that the chain passes along to the
* processing function as an argument.
*
* **CHAIN ATTRIBUTES** - A global set of flags available to all steps.
* **CHAIN ATTRIBUTES** - A global set of flags available to all steps. The
* chain is also responsible for tracking general statistics such as the
* number of records of each type that have been processed.
*/
#define CA_BYTESWAPPED (1ULL << 0) /* ca_attrs */
@@ -94,17 +100,19 @@ extern "C" {
#define CA_LITTLE_ENDIAN_INPUT (1ULL << 2)
#define CA_VERBOSE (1ULL << 0) /* ca_command_opts */
#define CA_VERY_VERBOSE (1ULL << 1)
#define CA_DUMP_DATA (1ULL << 2)
#define CA_IGNORE_CKSUMS (1ULL << 3)
#define CA_DO_NOT_VALIDATE (1ULL << 4)
#define CA_FORBID_DEDUP (1ULL << 5)
#define CA_REQUIRE_DEDUP (1ULL << 6)
#define CA_REQUIRE_NATIVE_ENDIAN (1ULL << 7)
#define CA_BYTESWAP_ON_OUTPUT (1ULL << 8)
#define CA_BIG_ENDIAN_OUT (1ULL << 9)
#define CA_LITTLE_ENDIAN_OUT (1ULL << 10)
#define CA_OPPOSITE_ENDIAN_OUT (1ULL << 11)
#define CA_DUMP_BEGIN_AND_END (1ULL << 1)
#define CA_DUMP_ALL_RECORDS (1ULL << 2)
#define CA_DUMP_CHECKSUMS (1ULL << 3)
#define CA_DUMP_DATA (1ULL << 4)
#define CA_IGNORE_CKSUMS (1ULL << 5)
#define CA_DO_NOT_VALIDATE (1ULL << 6)
#define CA_FORBID_DEDUP (1ULL << 7)
#define CA_REQUIRE_DEDUP (1ULL << 8)
#define CA_REQUIRE_NATIVE_ENDIAN (1ULL << 9)
#define CA_BYTESWAP_ON_OUTPUT (1ULL << 10)
#define CA_BIG_ENDIAN_OUT (1ULL << 11)
#define CA_LITTLE_ENDIAN_OUT (1ULL << 12)
#define CA_OPPOSITE_ENDIAN_OUT (1ULL << 13)
#define OPTION_ENABLED(option) (!!(chain_attrs->ca_command_opts & (option)))
#define STREAM_HAS_FEATURE(feat) (!!(chain_attrs->ca_feature_flags & (feat)))
@@ -133,7 +141,7 @@ typedef struct {
record_stats_t ca_stats_out[DRR_NUMTYPES];
} chain_attrs_t;
typedef enum { CS_SERIAL, CS_TERMINATE } step_type_t;
typedef enum { CS_SERIAL, CS_PARALLEL, CS_TERMINATE } step_type_t;
typedef enum { D_OK, D_EOF, D_DROP } disposition_t;
typedef disposition_t
@@ -145,31 +153,47 @@ typedef struct chain_step
size_t cs_in_size;
size_t cs_out_size;
void *cs_context;
struct {
zc_serial_process_f *process;
} cs_serial;
union {
struct {
zc_serial_process_f *process; /* serial */
} cs_serial;
struct {
size_t queue_length; /* parallel */
size_t batch_budget;
zq_estimate_cost_f *cost;
zq_process_item_f *process;
} cs_parallel;
};
} chain_step_t;
typedef chain_step_t zstream_chain_t[];
/*
* Chain attributes accessible to any step on the chain. In theory this
* could cause a race condition between reading and setting, but all
* attributes are typically set by the time the first record has been read.
* Ergo, nobody else will be executing while that first chain_read() runs.
* Chain attributes accessible to any step on the chain. These are normally
* accessed through the macros defined above.
*/
extern chain_attrs_t *chain_attrs;
/*
* Execute a chain. Returns once execution is complete. You can pass NULL
* for the attrs if you're not interested in preserving them after the chain
* has run.
* has run. (The chain will allocate and dispose of a buffer for them.)
*/
void
zstream_chain_exec(zstream_chain_t chain, chain_attrs_t *attrs);
chain_step_t
serial_null_step(void);
/*
* Execute a chain linearly, without queues and without multithreading. This
* form of execution is intended as a debugging aid, both for clients and
* for the chain mechanism itself. If this variant doesn't produce results
* identical to zstream_chain_exec(), there's a multithreading-related bug
* somewhere.
*
* It is not necessary to remove parallel steps from the input chain. They
* are accepted as-is, but their execution won't be parallelized.
*/
void
zstream_chain_exec_serialized(zstream_chain_t chain, chain_attrs_t *attrs);
chain_step_t
chain_terminator(void);
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -47,9 +37,11 @@
#define KEYSIZE 64
static disposition_t
chain_decompress_named_writes(drr_packet_t *item, void *context)
chain_decompress_named_writes(void *item_in, void *context)
{
(void) context;
drr_packet_t *item = (drr_packet_t *)item_in;
if (item == NULL) {
return (D_OK);
}
@@ -134,8 +126,7 @@ serial_decompress_named_writes(void)
.cs_out_size = sizeof (drr_packet_t),
.cs_context = NULL,
.cs_serial = {
.process =
(zc_serial_process_f *)chain_decompress_named_writes
.process = chain_decompress_named_writes
}
};
return (step);
@@ -155,7 +146,6 @@ zstream_do_decompress(int argc, char *argv[])
case '?':
fprintf(stderr, "invalid option '%c'\n", optopt);
zstream_usage();
break;
}
}
@@ -176,10 +166,8 @@ zstream_do_decompress(int argc, char *argv[])
enum zio_compress type = ZIO_COMPRESS_INHERIT;
obj_str = strsep(&argv[i], ",");
if (argv[i] == NULL) {
if (argv[i] == NULL)
zstream_usage();
exit(2);
}
errno = 0;
object = strtoull(obj_str, &end, 0);
if (errno || *end != '\0')
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -42,9 +32,11 @@
#define KEYSIZE 64
static disposition_t
chain_drop_records(drr_packet_t *item, void *context)
chain_drop_records(void *item_in, void *context)
{
(void) context;
drr_packet_t *item = (drr_packet_t *)item_in;
if (item == NULL)
return (D_OK);
@@ -105,7 +97,7 @@ serial_drop_records(void)
.cs_out_size = sizeof (drr_packet_t),
.cs_context = NULL,
.cs_serial = {
.process = (zc_serial_process_f *)chain_drop_records
.process = chain_drop_records
}
};
return (step);
@@ -125,7 +117,6 @@ zstream_do_drop_record(int argc, char *argv[])
case '?':
warnx("invalid option '%c'\n", optopt);
zstream_usage();
break;
}
}
@@ -146,10 +137,8 @@ zstream_do_drop_record(int argc, char *argv[])
char *end;
obj_str = strsep(&argv[i], ",");
if (argv[i] == NULL) {
if (argv[i] == NULL)
zstream_usage();
exit(2);
}
errno = 0;
object = strtoull(obj_str, &end, 0);
if (errno || *end != '\0')
+66 -53
View File
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -67,7 +57,7 @@ typedef void dumper_f(drr_packet_t *item);
typedef struct {
const char *rt_typename;
dumper_f *rt_dumper;
} record_type_t;
} record_dumper_t;
static int stream_error;
@@ -183,6 +173,10 @@ dump_begin_record(drr_packet_t *item)
dmu_replay_record_t *drr = &item->dp_drr;
struct drr_begin *drrb = &item->dp_drr.drr_u.drr_begin;
if (!OPTION_ENABLED(CA_DUMP_BEGIN_AND_END) &&
!OPTION_ENABLED(CA_DUMP_ALL_RECORDS))
return;
printf("BEGIN record\n");
printf("\thdrtype = %llu\n",
DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo));
@@ -240,6 +234,10 @@ dump_end_record(drr_packet_t *item)
{
struct drr_end *drre = &item->dp_drr.drr_u.drr_end;
if (!OPTION_ENABLED(CA_DUMP_BEGIN_AND_END) &&
!OPTION_ENABLED(CA_DUMP_ALL_RECORDS))
return;
printf("END checksum = %llx/%llx/%llx/%llx\n",
(u_longlong_t)drre->drr_checksum.zc_word[0],
(u_longlong_t)drre->drr_checksum.zc_word[1],
@@ -252,7 +250,7 @@ dump_object_record(drr_packet_t *item)
{
struct drr_object *drro = &item->dp_drr.drr_u.drr_object;
if (OPTION_ENABLED(CA_VERBOSE)) {
if (OPTION_ENABLED(CA_DUMP_ALL_RECORDS)) {
printf("OBJECT object = %llu type = %u "
"bonustype = %u blksz = %u bonuslen = %u "
"dn_slots = %u raw_bonuslen = %u "
@@ -282,7 +280,7 @@ dump_freeobjects_record(drr_packet_t *item)
{
struct drr_freeobjects *drrfo = &item->dp_drr.drr_u.drr_freeobjects;
if (OPTION_ENABLED(CA_VERBOSE)) {
if (OPTION_ENABLED(CA_DUMP_ALL_RECORDS)) {
printf("FREEOBJECTS firstobj = %llu numobjs = %llu\n",
(u_longlong_t)drrfo->drr_firstobj,
(u_longlong_t)drrfo->drr_numobjs);
@@ -294,7 +292,7 @@ dump_write_record(drr_packet_t *item)
{
struct drr_write *drrw = &item->dp_drr.drr_u.drr_write;
if (OPTION_ENABLED(CA_VERBOSE)) {
if (OPTION_ENABLED(CA_DUMP_ALL_RECORDS)) {
printf("WRITE object = %llu type = %u "
"checksum type = %u compression type = %u "
"flags = %u offset = %llu "
@@ -322,7 +320,7 @@ dump_write_byref_record(drr_packet_t *item)
{
struct drr_write_byref *drrwbr = &item->dp_drr.drr_u.drr_write_byref;
if (OPTION_ENABLED(CA_VERBOSE)) {
if (OPTION_ENABLED(CA_DUMP_ALL_RECORDS)) {
printf("WRITE_BYREF object = %llu "
"checksum type = %u props = %llx "
"offset = %llu length = %llu "
@@ -345,7 +343,7 @@ dump_free_record(drr_packet_t *item)
{
struct drr_free *drrf = &item->dp_drr.drr_u.drr_free;
if (OPTION_ENABLED(CA_VERBOSE)) {
if (OPTION_ENABLED(CA_DUMP_ALL_RECORDS)) {
printf("FREE object = %llu "
"offset = %llu length = %lld\n",
(u_longlong_t)drrf->drr_object,
@@ -359,7 +357,7 @@ dump_spill_record(drr_packet_t *item)
{
struct drr_spill *drrs = &item->dp_drr.drr_u.drr_spill;
if (OPTION_ENABLED(CA_VERBOSE)) {
if (OPTION_ENABLED(CA_DUMP_ALL_RECORDS)) {
printf("SPILL block for object = %llu "
"length = %llu flags = %u "
"compression type = %u "
@@ -383,7 +381,7 @@ dump_write_embedded_record(drr_packet_t *item)
struct drr_write_embedded *drrwe =
&item->dp_drr.drr_u.drr_write_embedded;
if (OPTION_ENABLED(CA_VERBOSE)) {
if (OPTION_ENABLED(CA_DUMP_ALL_RECORDS)) {
printf("WRITE_EMBEDDED object = %llu "
"offset = %llu length = %llu "
"toguid = %llx comp = %u etype = %u "
@@ -405,7 +403,7 @@ dump_object_range_record(drr_packet_t *item)
{
struct drr_object_range *drror = &item->dp_drr.drr_u.drr_object_range;
if (OPTION_ENABLED(CA_VERBOSE)) {
if (OPTION_ENABLED(CA_DUMP_ALL_RECORDS)) {
printf("OBJECT_RANGE firstobj = %llu "
"numslots = %llu flags = %u "
"%s\n",
@@ -421,7 +419,7 @@ dump_redact_record(drr_packet_t *item)
{
struct drr_redact *drrr = &item->dp_drr.drr_u.drr_redact;
if (OPTION_ENABLED(CA_VERBOSE)) {
if (OPTION_ENABLED(CA_DUMP_ALL_RECORDS)) {
printf("REDACT object = %llu offset = "
"%llu length = %llu\n",
(u_longlong_t)drrr->drr_object,
@@ -430,9 +428,26 @@ dump_redact_record(drr_packet_t *item)
}
}
static const record_dumper_t record_dumpers[] = {
{ "DRR_BEGIN", dump_begin_record },
{ "DRR_OBJECT", dump_object_record },
{ "DRR_FREEOBJECTS", dump_freeobjects_record },
{ "DRR_WRITE", dump_write_record },
{ "DRR_FREE", dump_free_record },
{ "DRR_END", dump_end_record },
{ "DRR_WRITE_BYREF", dump_write_byref_record },
{ "DRR_SPILL", dump_spill_record },
{ "DRR_WRITE_EMBEDDED", dump_write_embedded_record },
{ "DRR_OBJECT_RANGE", dump_object_range_record },
{ "DRR_REDACT", dump_redact_record }
};
static disposition_t
chain_dump_record(drr_packet_t *item, record_type_t *context)
chain_dump_records(void *item_in, void *context)
{
(void) context;
drr_packet_t *item = (drr_packet_t *)item_in;
if (item == NULL) {
return (D_OK);
}
@@ -441,9 +456,12 @@ chain_dump_record(drr_packet_t *item, record_type_t *context)
zio_cksum_t *cksum = &drr->drr_u.drr_checksum.drr_checksum;
int type = (int)drr->drr_type;
context[type].rt_dumper(item);
if (type < 0 || type >= DRR_NUMTYPES)
errx(1, "unknown record type: %d", type);
if (type != DRR_BEGIN && OPTION_ENABLED(CA_VERY_VERBOSE)) {
record_dumpers[type].rt_dumper(item);
if (type != DRR_BEGIN && OPTION_ENABLED(CA_DUMP_CHECKSUMS)) {
printf(" checksum = %llx/%llx/%llx/%llx\n",
(u_longlong_t)cksum->zc_word[0],
(u_longlong_t)cksum->zc_word[1],
@@ -454,16 +472,23 @@ chain_dump_record(drr_packet_t *item, record_type_t *context)
return (D_OK);
}
static chain_step_t
serial_dump_records(record_type_t *context)
chain_step_t
serial_dump_records(void)
{
size_t all_dumpers = sizeof (record_dumpers);
size_t one_dumper = sizeof (record_dumpers[0]);
if (all_dumpers / one_dumper != DRR_NUMTYPES) {
errx(1, "number of record_dumpers must match DRR_NUMTYPES");
}
chain_step_t step = {
.cs_type = CS_SERIAL,
.cs_in_size = sizeof (drr_packet_t),
.cs_out_size = sizeof (drr_packet_t),
.cs_context = context,
.cs_context = NULL,
.cs_serial = {
.process = (zc_serial_process_f *)chain_dump_record
.process = chain_dump_records
}
};
return (step);
@@ -476,19 +501,7 @@ zstream_do_dump(int argc, char *argv[])
const char *input_file = NULL;
int c;
record_type_t record_types[] = {
{ "DRR_BEGIN", dump_begin_record },
{ "DRR_OBJECT", dump_object_record },
{ "DRR_FREEOBJECTS", dump_freeobjects_record },
{ "DRR_WRITE", dump_write_record },
{ "DRR_FREE", dump_free_record },
{ "DRR_END", dump_end_record },
{ "DRR_WRITE_BYREF", dump_write_byref_record },
{ "DRR_SPILL", dump_spill_record },
{ "DRR_WRITE_EMBEDDED", dump_write_embedded_record },
{ "DRR_OBJECT_RANGE", dump_object_range_record },
{ "DRR_REDACT", dump_redact_record }
};
ENABLE_OPTION(&attrs, CA_DUMP_BEGIN_AND_END);
while ((c = getopt(argc, argv, ":vCd")) != -1) {
switch (c) {
@@ -497,24 +510,24 @@ zstream_do_dump(int argc, char *argv[])
break;
case 'v':
if (attrs.ca_command_opts & CA_VERBOSE) {
ENABLE_OPTION(&attrs, CA_VERY_VERBOSE);
ENABLE_OPTION(&attrs, CA_DUMP_CHECKSUMS);
} else {
ENABLE_OPTION(&attrs, CA_VERBOSE);
ENABLE_OPTION(&attrs, CA_DUMP_ALL_RECORDS);
}
break;
case 'd':
ENABLE_OPTION(&attrs, CA_VERBOSE);
ENABLE_OPTION(&attrs, CA_VERY_VERBOSE);
ENABLE_OPTION(&attrs, CA_DUMP_ALL_RECORDS);
ENABLE_OPTION(&attrs, CA_DUMP_CHECKSUMS);
ENABLE_OPTION(&attrs, CA_DUMP_DATA);
break;
case ':':
warnx("missing argument for '%c' option\n", optopt);
zstream_usage();
break;
case '?':
warnx("invalid option '%c'\n", optopt);
zstream_usage();
break;
}
}
@@ -524,7 +537,7 @@ zstream_do_dump(int argc, char *argv[])
zstream_chain_t dump_chain = {
STANDARD_INPUT_STACK(input_file),
serial_dump_records(record_types),
serial_dump_records(),
NULL_OUTPUT_STACK()
};
@@ -543,7 +556,7 @@ zstream_do_dump(int argc, char *argv[])
printf("SUMMARY:\n");
for (int i = 0; i < DRR_NUMTYPES; i++) {
int type = print_order[i];
record_type_t *rec = &record_types[type];
const record_dumper_t *rec = &record_dumpers[type];
record_stats_t *stats = &attrs.ca_stats_in[type];
printf("\tTotal %s records = %llu (%llu bytes)\n",
rec->rt_typename,
@@ -0,0 +1,33 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2026 by Garth Snyder. All rights reserved.
*/
#ifndef _ZSTREAM_DUMP_H
#define _ZSTREAM_DUMP_H
#ifdef __cplusplus
extern "C" {
#endif
#include "zstream_chain.h"
chain_step_t
serial_dump_records(void);
#ifdef __cplusplus
}
#endif
#endif /* _ZSTREAM_DUMP_H */
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* This file and its contents are supplied under the terms of the Common
* Development and Distribution License ("CDDL"), version 1.0. You may only use
* this file in accordance with the terms of version 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this source. A
* copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -20,22 +17,36 @@
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/byteorder.h>
#include <sys/param.h>
#include <sys/spa_checksum.h>
#include <sys/stdtypes.h>
#include <sys/sysmacros.h>
#include <sys/types.h>
#include <sys/zfs_ioctl.h>
#include <zfs_fletcher.h>
#include "zstream_fletcher4.h"
#include "zstream_modules.h"
#include "zstream_queue.h"
#include "zstream_util.h"
#define CK_OFFSET offsetof(dmu_replay_record_t, drr_u.drr_checksum.drr_checksum)
#define END_CK_OFFSET offsetof(dmu_replay_record_t, drr_u.drr_end.drr_checksum)
/*
* Copied from zfs_fletcher.c. See comments below regarding the
* fletcher_4_incremental_combine() function.
*/
#define MAX_FLETCHER_BLOCK (8ULL << 20)
typedef enum { F4_SET, F4_VALIDATE } fletcher4_op_t;
typedef zio_cksum_t fletcher4_context_t;
typedef struct {
zio_cksum_t fc_stream_cksum;
fletcher4_op_t fc_operation;
} fletcher4_context_t;
static fletcher4_context_t fletcher4_contexts[MAX_FLETCHER_4];
static int next_context = 0;
@@ -50,9 +61,130 @@ fletcher_4_incremental(boolean_t swap, void *buff, size_t size, void *cksum)
}
}
static inline void
fletcher_4(boolean_t swap, void *buff, size_t size, void *cksum)
{
if (swap) {
fletcher_4_byteswap(buff, size, NULL, cksum);
} else {
fletcher_4_native(buff, size, NULL, cksum);
}
}
/*
* Emit or validate (below) a replay record with proper checksums and with
* proper maintenance of the stream checksum. That is:
* The function below and the MAX_FLETCHER_BLOCK define are copied from
* zfs_fletcher.c, where they're internal.
*
* Fletcher checksums CAN be computed in parallel, with the segments later
* being reassembled. However, the combine function needs to know the
* original length of each segment, and there's a hard limit as to how long
* any given segment can be because 64-bit coefficients used in the combine
* operation may overflow if the size is larger than 8MB.
*
* My understanding of this is that the checksum fields themselves can and
* will overflow for long hash texts. However, they still function properly
* as checksums when this happens. But overflow has to be handled correctly
* in a structured fashion, not by allowing intermediate calculations to
* overflow.
*/
static inline void
fletcher4_incremental_combine(zio_cksum_t *zcp, const uint64_t size,
const zio_cksum_t *nzcp)
{
const uint64_t c1 = size / sizeof (uint32_t);
const uint64_t c2 = c1 * (c1 + 1) / 2;
const uint64_t c3 = c2 * (c1 + 2) / 3;
/*
* Value of 'c3' overflows on buffer sizes close to 16MiB. For that
* reason we split incremental fletcher4 computation of large buffers
* to steps of (MAX_FLETCHER_BLOCK) size.
*/
ASSERT3U(size, <=, MAX_FLETCHER_BLOCK);
zcp->zc_word[3] += nzcp->zc_word[3] + c1 * zcp->zc_word[2] +
c2 * zcp->zc_word[1] + c3 * zcp->zc_word[0];
zcp->zc_word[2] += nzcp->zc_word[2] + c1 * zcp->zc_word[1] +
c2 * zcp->zc_word[0];
zcp->zc_word[1] += nzcp->zc_word[1] + c1 * zcp->zc_word[0];
zcp->zc_word[0] += nzcp->zc_word[0];
}
/*
* This is the parallel portion of checksum calculation. We calculate only
* the checksum blocks for payloads. The records themselves are summed in
* the serial step.
*
* Because MAX_FLETCHER_BLOCK is 8MB, the great majority of payloads need
* only a single checksum calculation. The drr_fletcher4_t struct has both a
* first-block checksum field and a pointer to an overflow block on the
* heap. The overflow block is not allocated unless the payload size is
* greater than MAX_FLETCHER_BLOCK.
*/
static void
chain_calc_fletcher4(queue_item_t *item_in, void *context)
{
(void) context;
drr_fletcher4_t *item = (drr_fletcher4_t *)item_in;
VERIFY3U(item->dp_base.dp_payload_size, >, 0);
ssize_t remaining = item->dp_base.dp_payload_size;
uint8_t *data = item->dp_base.dp_payload;
size_t write_size = MIN(remaining, MAX_FLETCHER_BLOCK);
int num_overflow = DIV_ROUND_UP(remaining, MAX_FLETCHER_BLOCK) - 1;
zio_cksum_t *fragment = &item->dp_fletcher4_payload;
boolean_t swap = ATTR_IS_SET(CA_BYTESWAPPED);
fletcher_4(swap, data, write_size, fragment);
if (num_overflow) {
fragment = safe_calloc(num_overflow * sizeof (zio_cksum_t));
item->dp_fletcher4_overflow = fragment;
} else {
item->dp_fletcher4_overflow = NULL;
}
while (remaining -= write_size) {
data += write_size;
write_size = MIN(remaining, MAX_FLETCHER_BLOCK);
fletcher_4(swap, data, write_size, fragment);
fragment++;
}
}
/*
* Combine the payload checksum(s) produced by the parallel phase into the
* stream checksum. Used by the serial validation or inscription step.
*/
static void
assemble_payload_cksum(drr_fletcher4_t *item, zio_cksum_t *stream_ck)
{
ssize_t remaining = item->dp_base.dp_payload_size;
size_t read_size = MIN(remaining, MAX_FLETCHER_BLOCK);
zio_cksum_t *fragment = item->dp_fletcher4_overflow;
if (remaining == 0)
return;
fletcher4_incremental_combine(stream_ck, read_size,
&item->dp_fletcher4_payload);
while (remaining -= read_size) {
read_size = MIN(remaining, MAX_FLETCHER_BLOCK);
fletcher4_incremental_combine(stream_ck, read_size, fragment);
fragment++;
}
if (item->dp_fletcher4_overflow != NULL) {
free(item->dp_fletcher4_overflow);
item->dp_fletcher4_overflow = NULL;
}
}
/*
* This function implements the serial portions of both validation and
* inscription, based on the fc_operation field of the context struct.
* Checksumming is either very early in a chain or very late, so records
* are potentially in non-native endianness in either mode.
*
* This function emits or validates a replay record with proper checksums
* and with proper maintenance of the stream checksum. That is:
*
* 1) Update stream checksum with the record header up to drr_checksum.
* 2) Update drr_checksum field in the record header from stream checksum.
@@ -62,33 +194,74 @@ fletcher_4_incremental(boolean_t swap, void *buff, size_t size, void *cksum)
* DRR_BEGIN records do not have record checksums. They can't, because the
* drr_begin struct overlaps with space that would otherwise be used for the
* end-record checksum.
*
* DRR_END records normally do have end-record checksums. However, records
* emitted by send_conclusion_record() in libzfs_sendrecv.c have the
* checksum set to zero. zfs receive ignores those checksums. DRR_END records
* also have an internal checksum that applies to the stream-to-date since the
* most recent DRR_BEGIN.
*
* Null zstream transformations should be idempotent. E.g., a zstream redup
* that does not redup anything should yield a stream that is bit-for-bit
* identical to the original stream. So, it's helpful to emulate zfs send's
* checksumming pattern just to minimize spurious differences between
* input and output streams.
*/
static disposition_t
chain_add_fletcher4(drr_packet_t *item, zio_cksum_t *stream_cksum)
chain_fletcher4(queue_item_t *item_in, void *context_in)
{
if (item == NULL)
drr_fletcher4_t *item = (drr_fletcher4_t *)item_in;
fletcher4_context_t *context = (fletcher4_context_t *)context_in;
if (item == NULL || (context->fc_operation == F4_VALIDATE &&
OPTION_ENABLED(CA_IGNORE_CKSUMS))) {
return (D_OK);
}
dmu_replay_record_t *drr = &item->dp_drr;
struct drr_end *drre = &item->dp_drr.drr_u.drr_end;
zio_cksum_t *record_cksum = &drr->drr_u.drr_checksum.drr_checksum;
zio_cksum_t *end_cksum = &drre->drr_checksum;
zio_cksum_t *stream_cksum = &context->fc_stream_cksum;
dmu_replay_record_t *drr = &item->dp_base.dp_drr;
struct drr_end *drre = &drr->drr_u.drr_end;
zio_cksum_t *record_cksum = &drr->drr_u.drr_checksum.drr_checksum;
zio_cksum_t *end_cksum = &drre->drr_checksum;
boolean_t swap = OPTION_ENABLED(CA_BYTESWAP_ON_OUTPUT);
boolean_t swap = (context->fc_operation == F4_VALIDATE &&
ATTR_IS_SET(CA_BYTESWAPPED)) || (context->fc_operation == F4_SET &&
OPTION_ENABLED(CA_BYTESWAP_ON_OUTPUT));
uint32_t drr_type = swap ? BSWAP_32(drr->drr_type) : drr->drr_type;
off_t ck_offset = offsetof(dmu_replay_record_t,
drr_u.drr_checksum.drr_checksum);
if (item->dp_base.dp_stream_offset == 0) {
VERIFY3U(ck_offset, ==, sizeof (dmu_replay_record_t) -
sizeof (zio_cksum_t));
}
if (drr_type == DRR_BEGIN) {
ZIO_SET_CHECKSUM(stream_cksum, 0, 0, 0, 0);
} else if (drr_type == DRR_END) {
*end_cksum = *stream_cksum;
if (swap)
ZIO_CHECKSUM_BSWAP(end_cksum);
if (context->fc_operation == F4_VALIDATE) {
off_t stream_offset = item->dp_base.dp_stream_offset +
offsetof(dmu_replay_record_t,
drr_u.drr_end.drr_checksum);
validate_or_exit(stream_cksum, end_cksum, swap,
"in DRR_END record", stream_offset);
} else {
*end_cksum = *stream_cksum;
if (swap)
ZIO_CHECKSUM_BSWAP(end_cksum);
}
}
fletcher_4_incremental(swap, drr, CK_OFFSET, stream_cksum);
fletcher_4_incremental(swap, drr, ck_offset, stream_cksum);
if (drr_type != DRR_BEGIN && !IS_CONCLUSION(drr, drr_type)) {
*record_cksum = *stream_cksum;
if (swap)
ZIO_CHECKSUM_BSWAP(record_cksum);
if (context->fc_operation == F4_VALIDATE) {
off_t stream_offset =
item->dp_base.dp_stream_offset + ck_offset;
validate_or_exit(stream_cksum, record_cksum,
swap, "at DRR record end", stream_offset);
} else {
*record_cksum = *stream_cksum;
if (swap)
ZIO_CHECKSUM_BSWAP(record_cksum);
}
}
if (drr_type == DRR_END) {
ZIO_SET_CHECKSUM(stream_cksum, 0, 0, 0, 0);
@@ -96,54 +269,32 @@ chain_add_fletcher4(drr_packet_t *item, zio_cksum_t *stream_cksum)
fletcher_4_incremental(swap, record_cksum,
sizeof (drr->drr_u.drr_checksum.drr_checksum),
stream_cksum);
if (item->dp_payload_size > 0) {
fletcher_4_incremental(swap, item->dp_payload,
item->dp_payload_size, stream_cksum);
}
assemble_payload_cksum(item, stream_cksum);
}
return (D_OK);
}
static disposition_t
chain_validate_fletcher4(drr_packet_t *item, zio_cksum_t *stream_cksum)
/*
* Since checksumming is either very early or very late in the chain, these
* queues effectively double as I/O buffers. Ergo, the default queue length
* is long. The batch budget is also large because Fletcher 4 calculations
* are fast.
*/
chain_step_t
parallel_calc_fletcher4(int queue_length)
{
if (item == NULL || OPTION_ENABLED(CA_IGNORE_CKSUMS)) {
return (D_OK);
}
dmu_replay_record_t *drr = &item->dp_drr;
struct drr_end *drre = &item->dp_drr.drr_u.drr_end;
zio_cksum_t *record_cksum = &drr->drr_u.drr_checksum.drr_checksum;
zio_cksum_t *end_cksum = &drre->drr_checksum;
boolean_t swap = ATTR_IS_SET(CA_BYTESWAPPED);
uint32_t drr_type = swap ? BSWAP_32(drr->drr_type) : drr->drr_type;
if (drr_type == DRR_BEGIN) {
ZIO_SET_CHECKSUM(stream_cksum, 0, 0, 0, 0);
} else if (drr_type == DRR_END) {
off_t stream_offset = item->dp_stream_offset + END_CK_OFFSET;
validate_or_exit(stream_cksum, end_cksum, swap,
"in DRR_END record", stream_offset);
}
fletcher_4_incremental(swap, drr, CK_OFFSET, stream_cksum);
if (drr_type != DRR_BEGIN && !IS_CONCLUSION(drr, drr_type)) {
off_t stream_offset = item->dp_stream_offset + CK_OFFSET;
validate_or_exit(stream_cksum, record_cksum,
swap, "at DRR record end", stream_offset);
}
if (drr_type == DRR_END) {
ZIO_SET_CHECKSUM(stream_cksum, 0, 0, 0, 0);
} else {
fletcher_4_incremental(swap, record_cksum,
sizeof (drr->drr_u.drr_checksum.drr_checksum),
stream_cksum);
if (item->dp_payload_size > 0) {
fletcher_4_incremental(swap, item->dp_payload,
item->dp_payload_size, stream_cksum);
}
}
return (D_OK);
chain_step_t step = {
.cs_type = CS_PARALLEL,
.cs_in_size = sizeof (drr_packet_t),
.cs_out_size = sizeof (drr_fletcher4_t),
.cs_parallel = {
.queue_length = queue_length,
.batch_budget = 256 * 1024,
.process = chain_calc_fletcher4,
.cost = payload_size_as_cost
}
};
return (step);
}
static chain_step_t
@@ -152,17 +303,17 @@ fletcher4_serial_step(fletcher4_op_t operation)
int context_ix = next_context++ % MAX_FLETCHER_4;
fletcher4_context_t *context = &fletcher4_contexts[context_ix];
ZIO_SET_CHECKSUM(context, 0, 0, 0, 0);
context->fc_operation = operation;
ZIO_SET_CHECKSUM(&context->fc_stream_cksum, 0, 0, 0, 0);
chain_step_t step = {
.cs_type = CS_SERIAL,
.cs_in_size = sizeof (drr_packet_t),
.cs_out_size = sizeof (drr_packet_t),
.cs_context = context,
.cs_serial = {
.process = (zc_serial_process_f *)
((operation == F4_VALIDATE) ?
chain_validate_fletcher4 : chain_add_fletcher4)
}
.cs_type = CS_SERIAL,
.cs_in_size = sizeof (drr_fletcher4_t),
.cs_out_size = sizeof (drr_packet_t),
.cs_context = context,
.cs_serial = {
.process = chain_fletcher4,
}
};
return (step);
}
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* This file and its contents are supplied under the terms of the Common
* Development and Distribution License ("CDDL"), version 1.0. You may only use
* this file in accordance with the terms of version 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this source. A
* copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -24,19 +21,30 @@
extern "C" {
#endif
#include <sys/spa_checksum.h>
#include <sys/zfs_ioctl.h>
#include "zstream_io.h"
/*
* zstream_chain module for calculating, validating, and inscribing
* Fletcher4 checksums.
*
* serial_validate_fletcher4() validates record checksums against the
* running stream checksum and fails loudly on any mismatch.
* Checksums are calculated for the entire stream between a DRR_BEGIN record
* and its corresponding DRR_END, so the final checksum assembly must be
* performed as a serial step. However, we can pre-calculate the checksums
* for individual payloads in parallel.
*
* serial_add_fletcher4() inscribes record checksums from the running
* stream checksum, in theory replacing whatever was there before. But
* see note in zstream_fletcher4.c regarding zero checksums generated
* by send_conclusion_record(), which are preserved.
* The normal sequence is a parallel_calc_fletcher4() step followed by a
* serial_validate_fletcher4() or serial_add_fletcher4() step.
*
* Fletcher4 is just addition, so it's quite fast on its own. However, not
* parallelizing these calculations reduces aggregate throughput because of
* [Amdahl's Law](https://en.wikipedia.org/wiki/Amdahl%27s_law). Briefly
* stated: nonparallelizable work limits the total speedup obtainable
* through parallelization, often to a nonintuitive degree. Here, the effect
* is magnified because parallelizable work such as recompression lies
* beyond a Fletcher4 bottleneck.
*/
/*
@@ -65,6 +73,15 @@ extern "C" {
*/
#define MAX_FLETCHER_4 8
typedef struct {
drr_packet_t dp_base;
zio_cksum_t dp_fletcher4_payload;
zio_cksum_t *dp_fletcher4_overflow;
} drr_fletcher4_t;
chain_step_t
parallel_calc_fletcher4(int queue_length);
chain_step_t
serial_validate_fletcher4(void);
+95 -24
View File
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* This file and its contents are supplied under the terms of the Common
* Development and Distribution License ("CDDL"), version 1.0. You may only use
* this file in accordance with the terms of version 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this source. A
* copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -19,11 +16,10 @@
#include <arpa/inet.h>
#include <err.h>
#include <errno.h>
#include <libzutil.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/byteorder.h>
#include <sys/stdtypes.h>
#include <sys/sysmacros.h>
@@ -32,7 +28,6 @@
#include <time.h>
#include <unistd.h>
#include "zstream_chain.h"
#include "zstream_modules.h"
#include "zstream_util.h"
@@ -59,6 +54,9 @@ static int next_io_context = 0;
static checkpoint_context_t checkpoint_contexts[MAX_IO_STREAMS];
static int next_checkpoint_context = 0;
static uint32_t drop_contexts[MAX_DROP_FILTERS];
static int next_drop_context = 0;
/*
* Run from within chain execution to initialize I/O. A NULL filename
* indicates stdin or stdout.
@@ -213,8 +211,11 @@ set_stream_attributes(drr_packet_t *item)
}
static disposition_t
chain_read(drr_packet_t *item, io_context_t *context)
chain_read(void *item_in, void *context_in)
{
drr_packet_t *item = (drr_packet_t *)item_in;
io_context_t *context = (io_context_t *)context_in;
if (item == NULL)
return (D_OK);
@@ -293,8 +294,11 @@ chain_read(drr_packet_t *item, io_context_t *context)
}
static disposition_t
chain_write(drr_packet_t *item, io_context_t *context)
chain_write(void *item_in, void *context_in)
{
drr_packet_t *item = (drr_packet_t *)item_in;
io_context_t *context = (io_context_t *)context_in;
if (item == NULL) {
if (context->ic_fp) {
if (fclose(context->ic_fp) != 0)
@@ -343,9 +347,11 @@ chain_write(drr_packet_t *item, io_context_t *context)
* Even if the chain doesn't write out a stream, payloads still need freed.
*/
static disposition_t
chain_null_output(drr_packet_t *item, void *context)
chain_null_output(void *item_in, void *context)
{
(void) context;
drr_packet_t *item = (drr_packet_t *)item_in;
if (item && item->dp_payload != NULL && item->dp_payload_size > 0) {
free(item->dp_payload);
item->dp_payload = NULL;
@@ -370,12 +376,11 @@ setup_io(const char *filename, boolean_t for_reading)
chain_step_t step = {
.cs_type = CS_SERIAL,
.cs_in_size = 0,
.cs_out_size = sizeof (drr_packet_t),
.cs_in_size = for_reading ? 0 : sizeof (drr_packet_t),
.cs_out_size = for_reading ? sizeof (drr_packet_t) : 0,
.cs_context = &io_contexts[context_num],
.cs_serial = {
.process = (zc_serial_process_f *)
(for_reading ? chain_read : chain_write),
.process = for_reading ? chain_read : chain_write
}
};
return (step);
@@ -402,15 +407,34 @@ serial_null_output(void)
.cs_out_size = 0,
.cs_context = NULL,
.cs_serial = {
.process = (zc_serial_process_f *)chain_null_output
.process = chain_null_output
}
};
return (step);
}
static disposition_t
chain_checkpoint(drr_packet_t *item, checkpoint_context_t *ctxt)
size_t
constant_cost_of_one(queue_item_t *packet, void *context)
{
(void) context;
(void) packet;
return (1);
}
size_t
payload_size_as_cost(queue_item_t *packet_in, void *context)
{
(void) context;
drr_packet_t *packet = (drr_packet_t *)packet_in;
return (packet->dp_payload_size);
}
static disposition_t
chain_checkpoint(void *item_in, void *ctxt_in)
{
drr_packet_t *item = (drr_packet_t *)item_in;
checkpoint_context_t *ctxt = (checkpoint_context_t *)ctxt_in;
struct timespec now;
char buff[32];
uint64_t delta_b, dbdt;
@@ -455,7 +479,54 @@ serial_checkpoint(const char *name)
.cs_out_size = sizeof (drr_packet_t),
.cs_context = &checkpoint_contexts[context_no],
.cs_serial = {
.process = (zc_serial_process_f *)chain_checkpoint
.process = chain_checkpoint
},
};
return (step);
}
static disposition_t
chain_drop_record_types(void *item_in, void *context_in)
{
drr_packet_t *item = (drr_packet_t *)item_in;
uint32_t *context = (uint32_t *)context_in;
if (item == NULL)
return (D_OK);
uint32_t type = (uint32_t)item->dp_drr.drr_type;
if (type >= DRR_NUMTYPES) {
errx(1, "invalid record type %u found at offset %llu "
"(place drop filter downstream of byteswapping?)",
type, (u_longlong_t)item->dp_stream_offset);
}
if (((UINT32_C(1) << type) & *context) != 0) {
if (item->dp_payload != NULL) {
free(item->dp_payload);
item->dp_payload = NULL;
item->dp_payload_size = 0;
}
return (D_DROP);
}
return (D_OK);
}
chain_step_t
serial_drop_record_types(uint32_t drop_mask)
{
int context_no = next_drop_context++ % MAX_DROP_FILTERS;
uint32_t *context = &drop_contexts[context_no];
*context = drop_mask;
chain_step_t step = {
.cs_type = CS_SERIAL,
.cs_in_size = sizeof (drr_packet_t),
.cs_out_size = sizeof (drr_packet_t),
.cs_context = context,
.cs_serial = {
.process = chain_drop_record_types
},
};
return (step);
+56 -12
View File
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* This file and its contents are supplied under the terms of the Common
* Development and Distribution License ("CDDL"), version 1.0. You may only use
* this file in accordance with the terms of version 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this source. A
* copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -24,12 +21,31 @@
extern "C" {
#endif
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/zfs_ioctl.h>
#include "zstream_chain.h"
#define MAX_IO_STREAMS 4
#define MAX_IO_STREAMS 4
#define MAX_DROP_FILTERS 4
/*
* Masks for serial_drop_record_types()
*/
#define DROP_BEGIN (UINT32_C(1) << DRR_BEGIN)
#define DROP_OBJECT (UINT32_C(1) << DRR_OBJECT)
#define DROP_FREEOBJECTS (UINT32_C(1) << DRR_FREEOBJECTS)
#define DROP_WRITE (UINT32_C(1) << DRR_WRITE)
#define DROP_FREE (UINT32_C(1) << DRR_FREE)
#define DROP_END (UINT32_C(1) << DRR_END)
#define DROP_WRITE_BYREF (UINT32_C(1) << DRR_WRITE_BYREF)
#define DROP_SPILL (UINT32_C(1) << DRR_SPILL)
#define DROP_WRITE_EMBEDDED (UINT32_C(1) << DRR_WRITE_EMBEDDED)
#define DROP_OBJECT_RANGE (UINT32_C(1) << DRR_OBJECT_RANGE)
#define DROP_REDACT (UINT32_C(1) << DRR_REDACT)
/*
* The stream offset is the offset within the original source stream.
@@ -55,10 +71,30 @@ serial_read_stream(const char *filename);
chain_step_t
serial_write_stream(const char *filename);
/* Report throughput periodically */
/*
* Report throughput periodically
*/
chain_step_t
serial_checkpoint(const char *name);
/*
* Winnow the stream by dropping records of the given types. This frees up
* payload memory used by records you won't be inspecting. If there are
* parallel operations downstream of the filter, removing records allows the
* parallel queues to be used more efficiently.
*
* Use the DROP_* defines to construct a mask of the records you want to
* remove. If you want to remove most records, it's fine to pass an inverted
* mask formed by enumerating only the records you want to keep, e.g.:
*
* serial_drop_record_types((uint32_t)~(DROP_WRITE | DROP_WRITE_EMBEDDED))
*
* This step should be placed downstream of byteswapping, since it relies on
* being able to read drr->drr_type.
*/
chain_step_t
serial_drop_record_types(uint32_t drop_mask);
/*
* Usually the output step is responsible for freeing payloads. Subcommands
* that don't have stream outputs still need to free this memory. A
@@ -67,6 +103,14 @@ serial_checkpoint(const char *name);
chain_step_t
serial_null_output(void);
/* Off-the-shelf zstream_queue cost functions */
size_t
constant_cost_of_one(queue_item_t *packet, void *context);
size_t
payload_size_as_cost(queue_item_t *packet, void *context);
#ifdef __cplusplus
}
#endif
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* This file and its contents are supplied under the terms of the Common
* Development and Distribution License ("CDDL"), version 1.0. You may only use
* this file in accordance with the terms of version 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this source. A
* copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -31,26 +28,30 @@ extern "C" {
#include "zstream_byteswap.h"
#include "zstream_chain.h"
#include "zstream_dump.h"
#include "zstream_fletcher4.h"
#include "zstream_io.h"
#include "zstream_recompress.h"
#include "zstream_util.h"
#include "zstream_validate.h"
#define READ_STEP 0
#define STANDARD_INPUT_STACK(infile) \
#define STANDARD_INPUT_STACK_Q(infile, queue_size) \
serial_read_stream(infile), \
parallel_calc_fletcher4(queue_size), \
serial_validate_fletcher4(), \
serial_byteswap(BS_INCOMING), \
serial_validate_records()
#define STANDARD_OUTPUT_STACK(outfile) \
#define STANDARD_OUTPUT_STACK_Q(outfile, queue_size) \
serial_byteswap(BS_OUTGOING), \
parallel_calc_fletcher4(queue_size), \
serial_add_fletcher4(), \
serial_write_stream(outfile), \
chain_terminator()
#define STANDARD_INPUT_STACK(infile) STANDARD_INPUT_STACK_Q(infile, 1024)
#define STANDARD_OUTPUT_STACK(outfile) STANDARD_OUTPUT_STACK_Q(outfile, 512)
#define NULL_OUTPUT_STACK() \
serial_null_output(), \
chain_terminator()
@@ -0,0 +1,925 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2026 by Garth Snyder. All rights reserved.
*/
#include <assert.h>
#include <atomic.h>
#include <err.h>
#include <errno.h>
#include <pthread.h>
#include <sched.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/random.h>
#include <sys/stdtypes.h>
#include <sys/sysmacros.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>
#include "zstream_queue.h"
#include "zstream_util.h"
#define ENQUEUE_DELAY_NSEC (100 * 1000) /* 100us */
#define DISPATCH_BACKUP_NSEC (1000 * 1000) /* 1ms */
#define PLENTY_OF_WORK 6 /* "Many" items to claim */
#define NO_WORK 1.0E-6 /* No-work score threshold */
#define DEQUEUE_SCORE_WEIGHT 0.3 /* Dequeue score relative weight */
#define Q_MOD(queue, index) ((index) % (queue)->zq_params.qp_queue_length)
#define Q_SLOT(queue, index) ((queue)->zq_slots[Q_MOD((queue), (index))])
#define Q_FULL(queue) ((queue)->zq_ix.enqueue - (queue)->zq_ix.dequeue >= \
(queue)->zq_params.qp_queue_length)
/*
* A zstream_queue is a ring buffer with four indexes: enqueue, claim,
* complete, and dequeue, in that order. No index can move beyond its
* preceding index. Every interval between indexes contains work items in a
* particular state: enqueued, claimed for work, or completed. Items never
* leave the ring buffer, so FIFO order is guaranteed on dequeueing.
*
* In concept, every index has a corresponding condition that threads can
* wait on if they are interested in knowing when that index moves:
* enqueued, claimed, completed, dequeued. However, the reality deviates
* from this model in two ways:
*
* - There is no "claimed" condition, because no thread would wait on it.
* Claiming and processing are one unified operation. Dequeuers await the
* "completed" condition.
*
* - All queues share one thread pool, so idle threads are not bound to any
* particular queue. Instead of having queue-specific "enqueued" conditions,
* queues share a centralized dispatch system. On being awakened, worker
* threads assign themselves to a queue through a scoring mechanism
* described in the comments at score_queue().
*
* LOCKING
*
* There are three types of lock:
*
* - One global lock that gates changes to the thread pool and queue cohort.
* This lock also acts as the mutex for the tp_wake_worker condition.
*
* - A second, low-contention global lock that protects the dispatch system
*
* - One lock for each queue
*
* Any operation that adds or removes queues or threads should hold the pool
* lock. Any operation that moves a queue's indexes should hold the queue
* lock. Any thread waiting for work waits on tp_wake_worker.
*
* Worker threads hold no locks while they are actually processing items.
*
* The global locking order is dispatch -> pool -> queue.
*
* DISPATCH
*
* Four events trigger dispatch loops:
*
* 1) A worker thread completing its batch. Threads always check to see if
* there's more claimable work before going to sleep.
*
* 2) A worker thread discovering more work than it can handle on its own.
* Before starting work on its own batch, the worker attempts to signal
* another thread to wake up and assess the current state.
*
* 3) Enqueues. These go through the dispatch system and are batched.
* Roughly ENQUEUE_DELAY_NSEC after an enqueue (on any queue), the dispatch
* thread attempts to awaken a worker.
*
* 4) The expiration of a backup timer. The atomic value tp_unclaimed tracks
* the total number of enqueued-but-unclaimed items across all queues. When
* zero, it indicates that no worker dispatch is currently necessary. This
* value is rigorously maintained and the increments and decrements are
* sequentially consistent. However, the reads are relaxed, so a reader may
* see a stale value. In the event that a critical worker wakeup is dropped,
* the backup timer intervenes to keep dispatches running.
*/
typedef struct {
queue_item_t *qs_item;
size_t qs_cost;
boolean_t qs_completed;
boolean_t qs_end_of_stream;
} queue_slot_t;
typedef struct {
uint64_t enqueue;
uint64_t claim;
uint64_t complete;
uint64_t dequeue;
} zq_indexes_t;
typedef struct {
pthread_cond_t completed;
pthread_cond_t dequeued;
} zq_conditions_t;
typedef struct {
int min_depth;
int max_depth;
} zq_stats_t;
struct zstream_queue {
int zq_id;
queue_slot_t *zq_slots;
pthread_mutex_t zq_mutex;
zq_indexes_t zq_ix;
zq_conditions_t zq_cond;
zq_params_t zq_params;
boolean_t zq_disallow_enqueue;
#ifdef MONITOR_QUEUES
zq_stats_t zq_stats;
uint64_t zq_histogram[ZQ_MAX_BATCH+1]; /* Batch sizes */
#endif
};
typedef struct {
pthread_mutex_t tp_pool_mutex;
pthread_cond_t tp_wake_worker; /* Awaited by workers */
pthread_mutex_t tp_dispatch_mutex;
pthread_cond_t tp_request_dispatch; /* By dispatch thread */
boolean_t tp_dispatch_requested;
zstream_queue_t *tp_queues[ZQ_MAX_QUEUES];
int tp_num_queues;
boolean_t tp_threads_created;
int tp_num_threads;
uint64_t tp_unclaimed; /* Atomic, all queues */
} thread_pool_t;
typedef union {
long long ll;
long double ld;
void *p;
void (*fp)(void);
} worst_case_alignment_t;
static void *queue_worker(void *);
static void *dispatch_worker(void *);
#ifdef MONITOR_QUEUES
static void *cpu_and_queue_monitor(void *);
static void print_batch_size_histogram(zstream_queue_t *);
#endif
static thread_pool_t pool = {0};
static pthread_once_t once_control = PTHREAD_ONCE_INIT;
/*
* The dispatch timer needs sub-millisecond accuracy. POSIX timers on
* FreeBSD don't implement that, but nanosleep() works fine.
*/
static void
sleep_nsec(uint64_t nsec)
{
struct timespec ts = {
.tv_sec = nsec / NANOSEC,
.tv_nsec = nsec % NANOSEC
};
while (nanosleep(&ts, &ts) != 0) {
if (errno != EINTR)
err(1, "nanosleep failed");
}
}
static void
thread_pool_init(void)
{
pthread_mutex_init(&pool.tp_pool_mutex, NULL);
pthread_cond_init(&pool.tp_wake_worker, NULL);
pthread_mutex_init(&pool.tp_dispatch_mutex, NULL);
pthread_cond_init(&pool.tp_request_dispatch, NULL);
safe_create_thread(dispatch_worker, NULL, "dispatch", B_TRUE);
}
/*
* If this function is to be called at all, it must be called before any
* queues have been created.
*/
void
zstream_queue_set_num_threads(int n)
{
pthread_once(&once_control, thread_pool_init);
pthread_mutex_lock(&pool.tp_pool_mutex);
if (pool.tp_threads_created) {
errx(1, "thread pool size must be set before creating queues");
} else if (n < 1) {
errx(1, "number of threads must be at least 1");
} else if (n < ZQ_MIN_THREADS) {
warnx("using only %d threads may limit performance, setting "
"anyway...", n);
} else if (n > 256) {
warnx("num_threads = %d seems suspiciously high, setting "
"anyway...", n);
}
pool.tp_num_threads = n;
pthread_mutex_unlock(&pool.tp_pool_mutex);
}
/*
* Locking: the caller must hold the pool mutex.
*
* If tp_num_threads is nonzero, it sets the number of threads to spawn.
* Otherwise, one thread is spawned per core, with a minimum of 6 threads.
*
* sched_getaffinity() is a better estimate of available threads than
* sysconf because sysconf doesn't account for limits that might be set on,
* e.g., a container.
*/
static void
thread_pool_spinup(void)
{
if (pool.tp_num_threads == 0) {
#ifdef CPU_COUNT
cpu_set_t cpu_set;
if (sched_getaffinity(0, sizeof (cpu_set_t), &cpu_set) != 0) {
warn("sched_getaffinity failed, using sysconf");
pool.tp_num_threads = sysconf(_SC_NPROCESSORS_ONLN);
} else {
pool.tp_num_threads = CPU_COUNT(&cpu_set);
}
#else
pool.tp_num_threads = sysconf(_SC_NPROCESSORS_ONLN);
#endif
pool.tp_num_threads = MAX(pool.tp_num_threads, ZQ_MIN_THREADS);
}
for (int i = 0; i < pool.tp_num_threads; i++) {
char name[32];
snprintf(name, sizeof (name), "queue-%d", i);
safe_create_thread(queue_worker, NULL, name, B_TRUE);
}
#ifdef MONITOR_QUEUES
safe_create_thread(cpu_and_queue_monitor, NULL, "monitor", B_TRUE);
#endif
}
zstream_queue_t *
zstream_queue_create(zq_params_t *params)
{
static int next_queue_id = 0;
VERIFY3P(params->qp_process, !=, NULL);
VERIFY3P(params->qp_cost, !=, NULL);
VERIFY3U(params->qp_item_size, >, 0);
VERIFY3U(params->qp_queue_length, >, 0);
VERIFY3U(params->qp_queue_length, <, 1 << 18);
pthread_once(&once_control, thread_pool_init);
pthread_mutex_lock(&pool.tp_pool_mutex);
VERIFY3S(pool.tp_num_queues, <, ZQ_MAX_QUEUES);
if (!pool.tp_threads_created) {
thread_pool_spinup();
pool.tp_threads_created = B_TRUE;
}
zstream_queue_t *queue = safe_malloc(sizeof (zstream_queue_t));
*queue = (zstream_queue_t) {
.zq_id = next_queue_id++,
.zq_params = *params,
.zq_slots = safe_malloc(params->qp_queue_length *
(sizeof (queue_slot_t))),
#ifdef MONITOR_QUEUES
.zq_stats.min_depth = INT_MAX
#endif
};
pool.tp_queues[pool.tp_num_queues] = queue;
size_t qpis_rounded = P2ROUNDUP(params->qp_item_size,
_Alignof(worst_case_alignment_t));
uint8_t *items = safe_malloc(params->qp_queue_length * qpis_rounded);
for (size_t i = 0; i < params->qp_queue_length; i++) {
queue->zq_slots[i].qs_item =
(queue_item_t *)(items + i * qpis_rounded);
}
pthread_mutex_init(&queue->zq_mutex, NULL);
pthread_cond_init(&queue->zq_cond.completed, NULL);
pthread_cond_init(&queue->zq_cond.dequeued, NULL);
pool.tp_num_queues++;
pthread_mutex_unlock(&pool.tp_pool_mutex);
return (queue);
}
/*
* Try to advance the "claim" and "complete" indexes as far as possible by
* examining the qs_completed flag on each item. This can't be done directly
* by the threads that complete work, for a couple of reasons:
*
* - Items can be completed in any order. Just because you (a thread) have
* finished your batch doesn't mean that all prior batches have completed.
* If there are uncompleted items ahead of you in the ring buffer, you can't
* advance the completion index past them on your way out.
*
* - Items for which the cost function returns 0 are marked as qs_completed
* on enqueue and are never seen by a worker thread. So, there needs to be
* an independent mechanism to sweep the completion index past these items
* whenever that becomes possible.
*
* This function is called:
*
* - Whenever a thread completes a batch
* - Whenever a thread claims a batch
* - Whenever an item of cost 0 is enqueued
*
* Strictly speaking, advancing on claiming a batch is not logically
* necessary. However, the claimer already holds the queue mutex, and it's
* in our interest to make completed items available for dequeueing as
* expeditiously as possible.
*
* Sweeping of the "claim" index is also an optimization. It is not
* necessary for correctness. However, if we don't do it here, it can only
* be done by threads as they claim jobs to work on. In some cases, not
* advancing the "claim" index here can result in an empty batch and a
* wasted claim cycle.
*
* Locking: the caller must hold the queue mutex.
*/
static inline void
advance_indexes(zstream_queue_t *queue)
{
boolean_t any_completed = B_FALSE;
uint64_t claimed = 0;
while (queue->zq_ix.claim < queue->zq_ix.enqueue &&
Q_SLOT(queue, queue->zq_ix.claim).qs_completed) {
queue->zq_ix.claim++;
claimed++;
}
if (claimed > 0) {
/*
* tp_unclaimed is decremented both here and in
* claim_batch(). The conditions are mutually exclusive, so
* double counting will not occur.
*/
atomic_sub_64(&pool.tp_unclaimed, claimed);
}
while (queue->zq_ix.complete < queue->zq_ix.claim &&
Q_SLOT(queue, queue->zq_ix.complete).qs_completed) {
queue->zq_ix.complete++;
any_completed = B_TRUE;
}
if (any_completed) {
pthread_cond_signal(&queue->zq_cond.completed);
}
}
/*
* Score a queue according to its need for workers. Higher is better. The
* scoring tries to assign threads to queues that are running out of space
* for new enqueuements or that have little completed work available to
* dequeue. The broader goal is to try to avoid pipeline stalls.
*
* Two measures are used for scoring. The "open score" is 1/M where M is the
* number of slots available to receive new items. The "dequeue score" is
* 1/N where N is the number of completed items available to dequeue. These
* two measures are added together with the dequeue score scaled by
* DEQUEUE_SCORE_WEIGHT.
*
* The composite score is scaled by a factor that reflects how much work is
* actually available to be claimed on the queue; there's no point assigning
* threads to queues that have no work.
*
* Locking: the caller must hold the thread pool mutex and the queue mutex.
*/
static inline double
score_queue(zstream_queue_t *queue)
{
uint64_t claimable = queue->zq_ix.enqueue - queue->zq_ix.claim;
uint64_t dequeueable = queue->zq_ix.complete - queue->zq_ix.dequeue;
uint64_t in_queue = queue->zq_ix.enqueue - queue->zq_ix.dequeue;
uint64_t open_slots = queue->zq_params.qp_queue_length - in_queue;
double open_score = (open_slots > 0) ? (1.0 / open_slots) : 2.0;
double dq_score = (dequeueable > 0) ? (1.0 / dequeueable) : 2.0;
double claim_factor = MIN(claimable, (uint64_t)PLENTY_OF_WORK) /
(double)PLENTY_OF_WORK;
double need = open_score + dq_score * DEQUEUE_SCORE_WEIGHT;
return (need * claim_factor);
}
/*
* Return a random index from an array of doubles, with the likelihood of
* index i being selected equal to weights[i] / sum(weights). Returns index
* 0 if no weight is greater than 0.
*/
static inline int
select_stochastic(double weights[], int num_values)
{
const double denominator = (double)UINT64_MAX;
uint64_t numerator;
double total = 0.0;
for (int i = 0; i < num_values; i++) {
total += weights[i];
}
random_get_pseudo_bytes((uint8_t *)&numerator, sizeof (numerator));
double select_val = total * numerator / denominator;
for (int i = 0; i < num_values; i++) {
if (select_val < weights[i])
return (i);
select_val -= weights[i];
}
/* Fallback in case of FP rounding not producing a winner */
for (int i = num_values - 1; i >= 0; i--) {
if (weights[i] != 0.0)
return (i);
}
return (0);
}
/*
* Claim up to ZQ_MAX_BATCH work items from the given queue, trying to
* accumulate at least qp_batch_budget worth of work data (== "cost"). All
* items in a batch will be drawn from the same queue.
*
* Does not block waiting to fill the budget; returns whatever is available.
*
* Locking: this function must be called with both the queue mutex and the
* thread pool mutex held. zstream_queue_destroy() can't hold a queue's
* mutex while destroying it (because destruction entails destroying the
* queue mutex, which must be unlocked), so holding the queue mutex while
* attempting to claim work is not a sufficient guarantee of correctness.
*
* In other contexts, we have more certainty about whether a queue still has
* work to do. If it does, it can't be destroyed while we hold the queue
* mutex alone. But here, we merely suspect that there's work available
* based on possibly outdated queue scoring information. By the time we get
* here, the queue might already have been finalized. Holding the thread
* pool mutex guarantees that the queue won't have been destroyed out from
* under us.
*/
static int
claim_batch(zstream_queue_t *queue, queue_slot_t **batch)
{
size_t cost_claimed = 0;
int count = 0;
uint64_t passed = 0;
boolean_t more_to_claim, more_slots, more_budget;
boolean_t first_and_only, ok_to_claim;
while (B_TRUE) {
more_to_claim = queue->zq_ix.claim < queue->zq_ix.enqueue;
more_slots = count < ZQ_MAX_BATCH;
more_budget = cost_claimed < queue->zq_params.qp_batch_budget;
first_and_only = queue->zq_params.qp_batch_budget == 0 &&
count == 0;
ok_to_claim = first_and_only || more_budget;
if (!more_to_claim || !more_slots || !ok_to_claim) {
break;
}
queue_slot_t *slot = &Q_SLOT(queue, queue->zq_ix.claim);
if (!slot->qs_completed) {
cost_claimed += slot->qs_cost;
batch[count++] = slot;
}
queue->zq_ix.claim++;
passed++;
}
/*
* Every slot the claim index moved over leaves the unclaimed pool,
* whether we took it for the batch or skipped it as already complete.
*/
if (passed > 0) {
atomic_sub_64(&pool.tp_unclaimed, passed);
}
advance_indexes(queue);
#ifdef MONITOR_QUEUES
queue->zq_histogram[count]++;
#endif
return (count);
}
/*
* Threads are assigned to a queue on each loop so they can be shifted
* dynamically to follow available work. Idle threads will typically be
* waiting on the tp_wake_worker condition within this function.
*
* Locking: we hold the pool mutex throughout, both to keep a queue from
* being destroyed out from under us while we score it or claim from it, and
* because it is the mutex for tp_wake_worker. Individual queues are locked
* for only as long as it takes to score or claim from them.
*/
static int
assign_queue_and_get_work(zstream_queue_t **queue, queue_slot_t **batch)
{
pthread_mutex_lock(&pool.tp_pool_mutex);
while (B_TRUE) {
int num_queues = pool.tp_num_queues;
double weights[ZQ_MAX_QUEUES];
int queues_with_work = 0;
for (int i = 0; i < num_queues; i++) {
zstream_queue_t *to_score = pool.tp_queues[i];
pthread_mutex_lock(&to_score->zq_mutex);
weights[i] = score_queue(to_score);
pthread_mutex_unlock(&to_score->zq_mutex);
if (weights[i] > NO_WORK)
queues_with_work++;
}
if (!queues_with_work) {
pthread_cond_wait(&pool.tp_wake_worker,
&pool.tp_pool_mutex);
} else {
int q = select_stochastic(weights, num_queues);
*queue = pool.tp_queues[q];
pthread_mutex_lock(&(*queue)->zq_mutex);
int count = claim_batch(*queue, batch);
pthread_mutex_unlock(&(*queue)->zq_mutex);
/*
* Try to wake up another worker thread if there
* still seems to be work available (on any queue).
*/
if (atomic_load_64(&pool.tp_unclaimed) > 0) {
pthread_cond_signal(&pool.tp_wake_worker);
}
pthread_mutex_unlock(&pool.tp_pool_mutex);
return (count);
}
}
}
/*
* Batches are processed without holding any locks. The existence of the
* items we're working on guarantees that the queue can't be destroyed out
* from under us.
*
* However, we can't mark items completed without holding the queue lock
* because that creates a potential race condition with advance_indexes()
* being called on another thread.
*/
static void *
queue_worker(void *dummy)
{
(void) dummy;
zstream_queue_t *queue;
queue_slot_t *batch[ZQ_MAX_BATCH];
int count;
while (B_TRUE) {
count = assign_queue_and_get_work(&queue, batch);
if (count) {
zq_process_item_f *process =
queue->zq_params.qp_process;
void *context = queue->zq_params.qp_context;
for (int i = 0; i < count; i++) {
process(batch[i]->qs_item, context);
}
pthread_mutex_lock(&queue->zq_mutex);
for (int i = 0; i < count; i++) {
batch[i]->qs_completed = B_TRUE;
}
advance_indexes(queue);
pthread_mutex_unlock(&queue->zq_mutex);
}
}
return (NULL);
}
/*
* Locking: must be called with the dispatch mutex held
*
* Skips the wakeup if tp_unclaimed == 0.
*/
static inline void
maybe_wake_worker(void)
{
pool.tp_dispatch_requested = B_FALSE;
if (atomic_load_64(&pool.tp_unclaimed) > 0) {
pthread_mutex_lock(&pool.tp_pool_mutex);
pthread_cond_signal(&pool.tp_wake_worker);
pthread_mutex_unlock(&pool.tp_pool_mutex);
}
}
static inline struct timespec
timeout_timespec(void)
{
struct timespec expire;
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0)
err(1, "couldn't gettimeofday()");
uint64_t nsec = tv.tv_usec * 1000 + DISPATCH_BACKUP_NSEC;
expire.tv_sec = tv.tv_sec + nsec / NANOSEC;
expire.tv_nsec = nsec % NANOSEC;
return (expire);
}
/*
* The enqueue notification pacing thread, which converts a notification
* from an enqueuer into a possible worker wakeup roughly ENQUEUE_DELAY_NSEC
* later. The delay facilitates larger batch sizes and keeps enqueuers on a
* less-contested mutex.
*
* The condwait timeout is necessary because the tp_unclaimed count is not
* the final word on whether there is actually any work to claim. It is
* calculated rigorously. However, it's a bare atomic and therefore
* potentially out of date at any given moment. A backup strategy is
* necessary to restart processing in the event of a race.
*/
static void *
dispatch_worker(void *nope)
{
(void) nope;
pthread_mutex_lock(&pool.tp_dispatch_mutex);
while (B_TRUE) {
while (!pool.tp_dispatch_requested) {
int rc;
struct timespec expire = timeout_timespec();
rc = pthread_cond_timedwait(&pool.tp_request_dispatch,
&pool.tp_dispatch_mutex, &expire);
if (rc == ETIMEDOUT) {
maybe_wake_worker();
} else if (rc != 0) {
errx(1, "pthread_cond_timedwait() failed: %s",
strerror(rc));
}
}
pthread_mutex_unlock(&pool.tp_dispatch_mutex);
sleep_nsec(ENQUEUE_DELAY_NSEC);
pthread_mutex_lock(&pool.tp_dispatch_mutex);
maybe_wake_worker();
}
return (NULL);
}
/*
* Implements both _enqueue and _fini. item == NULL for fini.
*/
void
zstream_enqueue(zstream_queue_t *queue, queue_item_t *item)
{
VERIFY3P(queue, !=, NULL);
pthread_mutex_lock(&queue->zq_mutex);
VERIFY3B(queue->zq_disallow_enqueue, ==, B_FALSE);
while (Q_FULL(queue)) {
pthread_cond_wait(&queue->zq_cond.dequeued, &queue->zq_mutex);
}
VERIFY3B(queue->zq_disallow_enqueue, ==, B_FALSE);
queue_slot_t *slot = &Q_SLOT(queue, queue->zq_ix.enqueue);
if (item) {
slot->qs_cost =
queue->zq_params.qp_cost(item, queue->zq_params.qp_context);
slot->qs_completed = slot->qs_cost == 0;
slot->qs_end_of_stream = B_FALSE;
memcpy(slot->qs_item, item, queue->zq_params.qp_item_size);
} else {
slot->qs_cost = 0;
slot->qs_completed = B_TRUE;
slot->qs_end_of_stream = B_TRUE;
queue->zq_disallow_enqueue = B_TRUE;
}
queue->zq_ix.enqueue++;
atomic_inc_64(&pool.tp_unclaimed);
if (slot->qs_cost == 0)
advance_indexes(queue);
#ifdef MONITOR_QUEUES
/* Maintain queue usage data per monitor interval */
uint64_t depth = queue->zq_ix.enqueue - queue->zq_ix.dequeue;
queue->zq_stats.max_depth = MAX(queue->zq_stats.max_depth, depth);
queue->zq_stats.min_depth = MIN(queue->zq_stats.min_depth, depth);
#endif
pthread_mutex_unlock(&queue->zq_mutex);
pthread_mutex_lock(&pool.tp_dispatch_mutex);
pool.tp_dispatch_requested = B_TRUE;
pthread_cond_signal(&pool.tp_request_dispatch);
pthread_mutex_unlock(&pool.tp_dispatch_mutex);
}
void
zstream_queue_fini(zstream_queue_t *queue)
{
zstream_enqueue(queue, NULL);
}
/*
* This function is not public. The only way to destroy a queue through the
* public API is to call zstream_queue_fini(), wait for all items to be
* processed, and then dequeue all items. As a consequence, threads are
* entitled to assume that any queue with unprocessed work will not be
* removed without locking the pool mutex.
*
* Locking: the caller must NOT hold the queue lock. The pool mutex is held
* while destroying the queue.
*/
static void
zstream_queue_destroy(zstream_queue_t *queue)
{
pthread_mutex_lock(&pool.tp_pool_mutex);
#ifdef MONITOR_QUEUES
print_batch_size_histogram(queue);
#endif
VERIFY0(pthread_mutex_destroy(&queue->zq_mutex));
VERIFY0(pthread_cond_destroy(&queue->zq_cond.dequeued));
if (pthread_cond_destroy(&queue->zq_cond.completed) != 0) {
errx(1, "cannot destroy zstream_queue completed condition - "
"are you attempting to dequeue from multiple threads "
"simultaneously?");
}
pool.tp_num_queues--;
if (pool.tp_num_queues > 0) {
/* Gaps are not allowed in the tp_queues array */
zstream_queue_t **qscan = &pool.tp_queues[0];
int i = pool.tp_num_queues;
while (*qscan != queue) { qscan++; i--; }
if (i > 0)
memmove(qscan, qscan + 1, i * sizeof (*qscan));
}
/*
* Items are allocated as a single block. The address of the first
* item field is in fact the start of the block.
*/
free(queue->zq_slots[0].qs_item);
free(queue->zq_slots);
queue->zq_slots = NULL;
free(queue);
pthread_mutex_unlock(&pool.tp_pool_mutex);
}
/*
* Locking: if more than one thread attempts to dequeue items
* simultaneously, disaster is likely. It will work fine until the end of
* the stream, at which point it becomes a tossup between a race condition
* with multiple attempts to destroy the whole queue vs. an attempt to
* delete a condition that another thread is waiting on. Hence the warning
* not to do multithreaded dequeues in zstream_queue.h.
*
* Returns B_TRUE if real data is returned, B_FALSE if the end of the queue
* has been reached.
*/
boolean_t
zstream_dequeue(zstream_queue_t *queue, queue_item_t *item)
{
pthread_mutex_lock(&queue->zq_mutex);
while (queue->zq_ix.dequeue >= queue->zq_ix.complete) {
pthread_cond_wait(&queue->zq_cond.completed, &queue->zq_mutex);
}
queue_slot_t *slot = &Q_SLOT(queue, queue->zq_ix.dequeue);
queue->zq_ix.dequeue++;
if (slot->qs_end_of_stream) {
pthread_mutex_unlock(&queue->zq_mutex);
/* Potential multi-dequeuer race point */
zstream_queue_destroy(queue);
return (B_FALSE);
} else {
memcpy(item, slot->qs_item, queue->zq_params.qp_item_size);
pthread_cond_signal(&queue->zq_cond.dequeued);
pthread_mutex_unlock(&queue->zq_mutex);
return (B_TRUE);
}
}
#ifdef MONITOR_QUEUES
#define USEC_PER_JIFFY 10000
#define SAMPLE_DURATION_USEC 1000000
#define CPU_FIELD_WIDTH 14
/*
* Called only during zstream_queue_destroy(), under the pool mutex
*/
static void
print_batch_size_histogram(zstream_queue_t *queue)
{
int last_nonzero = 0;
static int lines_printed = 0;
if (lines_printed++ == 0)
fprintf(stderr, "\nBatch size histograms:\n");
for (last_nonzero = ZQ_MAX_BATCH; last_nonzero >= 0; last_nonzero--) {
if (queue->zq_histogram[last_nonzero] > 0)
break;
}
fprintf(stderr, "Queue %d: ", queue->zq_id);
const char *sep = "";
for (int i = 0; i <= last_nonzero; i++) {
fprintf(stderr, "%s%llu", sep,
(u_longlong_t)queue->zq_histogram[i]);
sep = ", ";
}
fprintf(stderr, "\n");
fflush(stderr);
}
/*
* Monitor queue and CPU usage from a separate thread. This is all
* Linux-specific, but it's needed only while tuning queue lengths and
* batch sizes. Prints the minimum and maximum queue depth observed
* during each period.
*
* Example output:
*
* CPU: 99.85% Queue 0: 745-1024 Queue 1: 183-256
*/
static void *
cpu_and_queue_monitor(void *dummy)
{
(void) dummy;
uint64_t period = SAMPLE_DURATION_USEC;
struct timespec clock = {0};
uint64_t start_us, end_us;
uint64_t cpu_jif_prior = 0;
uint64_t delta_jif, delta_cpu_jif;
long unsigned int utime, stime;
char buff[1024];
FILE *fp;
fprintf(stderr, "Queue depths:\n");
while (B_TRUE) {
usleep(period);
fp = fopen("/proc/self/stat", "r");
VERIFY3P(fp, !=, NULL);
VERIFY3P(fgets(buff, sizeof (buff), fp), !=, NULL);
fclose(fp);
char *p = strrchr(buff, ')');
VERIFY3P(p, !=, NULL);
p += 2; /* skip ") " and fields 3-13 */
for (int i = 0; i < 11; i++) {
p = strchr(p, ' ');
VERIFY3P(p, !=, NULL);
p++;
}
VERIFY3U(sscanf(p, "%lu %lu", &utime, &stime), ==, 2);
pthread_mutex_lock(&pool.tp_pool_mutex);
clock_gettime(CLOCK_MONOTONIC, &clock);
end_us = clock.tv_sec * 1000000 + clock.tv_nsec / 1000;
if (cpu_jif_prior > 0) {
delta_cpu_jif = utime + stime - cpu_jif_prior;
delta_jif = (end_us - start_us) / USEC_PER_JIFFY;
double cpu_pct = (double)delta_cpu_jif /
(pool.tp_num_threads * delta_jif);
cpu_pct = MIN(cpu_pct, 0.9999); /* Don't print 100% */
fprintf(stderr, "CPU: %5.2f%% ", 100 * cpu_pct);
} else {
/* No CPU data available for the first interval */
fprintf(stderr, "%*s", CPU_FIELD_WIDTH, "");
}
for (int i = 0; i < pool.tp_num_queues; i++) {
zstream_queue_t *q = pool.tp_queues[i];
pthread_mutex_lock(&q->zq_mutex);
int min = q->zq_stats.min_depth;
int max = q->zq_stats.max_depth;
if (min > max)
min = max = 0;
fprintf(stderr, "Queue %d: %4d-%-4d ",
q->zq_id, min, max);
q->zq_stats.min_depth = INT_MAX;
q->zq_stats.max_depth = 0;
pthread_mutex_unlock(&q->zq_mutex);
}
pthread_mutex_unlock(&pool.tp_pool_mutex);
fprintf(stderr, "\n");
fflush(stderr);
cpu_jif_prior = utime + stime;
start_us = end_us;
}
return (NULL);
}
#endif /* MONITOR_QUEUES */
@@ -0,0 +1,140 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2026 by Garth Snyder. All rights reserved.
*/
#ifndef _ZSTREAM_QUEUE_H
#define _ZSTREAM_QUEUE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <sys/stdtypes.h>
/*
* This is a generalized implementation of multithreaded FIFO work queues.
*
* Callers define a fixed item size to be used by each queue and supply two
* thread-safe functions that 1) estimate individual items' processing costs
* and 2) perform the actual processing. The queue never inspects or
* interprets items in the queue, so processing functions can modify them as
* they wish.
*
* The cost function assigns a size_t cost that estimates the amount of work
* needed to process an item. For operations like hashing and data
* compression, the natural cost is typically payload length.
*
* It's expected that only a subset of input items will require processing.
* If an item's cost is 0, it is fast-tracked and never presented to the
* processing function.
*
* The cost function is run as items enter the queue, with the queue mutex
* held, so it should return a value promptly. If cost estimation is
* expensive and important, use a separate queue to implement it.
*
* Dispatch granularity is specified as a per-batch budget that is set for
* each queue in the same (arbitrary) units used for item costs. Threads
* claim items until the budget is met, there are no more items available,
* or ZQ_MAX_BATCH items have been claimed. When claiming items to work on,
* threads never block waiting for additional work to arrive. They start
* work as quickly as possible even if the budget has not been reached.
*
* A batch budget of 0 means that all batches will have a size of 1.
*
* All queues share a single thread pool that is managed to avoid
* contention. Threads are assigned to queues dynamically according to where
* work is available. When multiple queues have work, threads are allocated
* among them stochastically with an eye toward preventing pipeline stalls.
*
* The shared thread pool persists until the process exits.
*/
#define ZQ_MAX_BATCH 32 /* The most items that can be claimed at once */
#define ZQ_MAX_QUEUES 16 /* The maximum number of simultaneous queues */
#define ZQ_MIN_THREADS 6
typedef void queue_item_t;
typedef struct zstream_queue zstream_queue_t;
/*
* Signatures that cost and processing functions must conform to.
*/
typedef size_t
zq_estimate_cost_f(queue_item_t *item, void *context);
typedef void
zq_process_item_f(queue_item_t *item, void *context);
/*
* Set the number of threads to be spawned for queue work. Since all queues
* share a thread pool, this value affects all queues. The value must be set
* before any queues are created. By default, one thread is spawned for
* every CPU core, but always at least ZQ_MIN_THREADS threads.
*/
void
zstream_queue_set_num_threads(int num_threads);
/*
* Create a queue. The qp_context field is passed to the cost and processing
* functions and is not examined by the queue itself.
*/
typedef struct {
zq_process_item_f *qp_process;
zq_estimate_cost_f *qp_cost;
void *qp_context;
size_t qp_item_size;
size_t qp_batch_budget;
size_t qp_queue_length;
} zq_params_t;
zstream_queue_t *
zstream_queue_create(zq_params_t *params);
/*
* Submit a work item. Blocks if the queue is full. The work item is
* shallow-copied into the queue. Multiple threads may enqueue at once.
*/
void
zstream_enqueue(zstream_queue_t *queue, queue_item_t *item);
/*
* Retrieve a completed work item. The caller must provide a buffer into
* which the dequeued item is shallow-copied. If the next item is not yet
* ready, this call will block.
*
* If zstream_dequeue returns B_FALSE, the stream is complete. The returned
* item is not valid and no further calls may be made on the queue.
*
* Only one thread may dequeue at once.
*/
boolean_t
zstream_dequeue(zstream_queue_t *queue, queue_item_t *item);
/*
* Declare that all items have been submitted. The queue will continue to
* function normally for dequeuers and worker threads until zstream_dequeue()
* returns B_FALSE, at which point the queue will be destroyed.
*/
void
zstream_queue_fini(zstream_queue_t *queue);
#ifdef __cplusplus
}
#endif
#endif /* _ZSTREAM_QUEUE_H */
@@ -0,0 +1,522 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright 2026 Klara, Inc.
*/
#ifdef __FreeBSD__
#include <sys/disk.h>
#endif
#include <sys/dmu.h>
#include <sys/ioctl.h>
#include <sys/spa.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/zap.h>
#include <sys/zap_impl.h>
#include <sys/zfs_ioctl.h>
#include <sys/zio.h>
#include <sys/zstd/zstd.h>
#include <sys/zvol.h>
#include <err.h>
#include <libnvpair.h>
#ifdef __linux__
#include <linux/falloc.h>
#include <linux/fs.h>
#endif
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "zstream.h"
#include "zstream_modules.h"
#include "zstream_util.h"
/*
* Supported feature flags (in drr_versioninfo)
*/
#define SUPPORTED_FEATURES (DMU_BACKUP_FEATURE_EMBED_DATA | \
DMU_BACKUP_FEATURE_LZ4 | DMU_BACKUP_FEATURE_LARGE_BLOCKS | \
DMU_BACKUP_FEATURE_COMPRESSED | DMU_BACKUP_FEATURE_ZSTD)
typedef struct {
struct raw_stream {
uint64_t guid;
boolean_t inprop;
boolean_t inzvol;
} stream;
struct raw_volume {
int fd;
size_t size;
uint64_t sectorsize;
boolean_t isreg;
boolean_t punch_holes;
unsigned long freeop;
} volume;
struct raw_limits {
long iov_max;
long buffers_max;
long pagesize;
} limits;
struct raw_zeros {
struct iovec *iov;
} zeros;
struct raw_buffer {
struct iovec *iov;
off_t position;
size_t length;
int iovcnt;
} buffer;
} raw_context_t;
static void
write_zeros(raw_context_t *context, off_t offset, size_t length)
{
ASSERT3U(offset + length, >=, offset);
struct raw_limits *limits = &context->limits;
int iovcnt = MIN(howmany(length, limits->pagesize), limits->iov_max);
if (iovcnt == 0)
return;
int fd = context->volume.fd;
long pagesize = limits->pagesize;
struct iovec *iov = context->zeros.iov;
size_t resid = length;
while (resid > 0) {
size_t iovsz = resid;
int i;
for (i = 0; i < iovcnt && resid > 0; i++) {
iov[i].iov_len = MIN(resid, pagesize);
resid -= iov[i].iov_len;
}
ssize_t res = pwritev(fd, iov, i, offset);
if (res < 0)
err(EXIT_FAILURE, "pwritev");
iovsz -= resid;
VERIFY3U(res, ==, iovsz);
offset += iovsz;
}
ASSERT0(resid);
}
/*
* buffer_write - pwrite with buffer vectoring and error handling
*
* Appends buf to a buffer vector, issuing the pending vector if not contiguous.
* Ownership of buf is taken; it will be freed after issuing the write.
*/
static void
buffer_write(raw_context_t *context, void *buf, size_t nbytes, off_t offset)
{
struct raw_limits *limits = &context->limits;
struct raw_buffer *buffer = &context->buffer;
struct iovec *iov = buffer->iov;
if (buffer->iovcnt == 0)
buffer->position = offset;
else if (buffer->position + buffer->length != offset ||
buffer->iovcnt == limits->buffers_max) {
ASSERT3U(offset + nbytes, >=, offset);
ASSERT3U(buffer->iovcnt, >, 0);
ssize_t res = pwritev(context->volume.fd, iov, buffer->iovcnt,
buffer->position);
if (res < 0)
err(EXIT_FAILURE, "pwritev");
VERIFY3U(res, ==, buffer->length);
buffer->position = offset;
buffer->length = 0;
for (int i = 0; i < buffer->iovcnt; i++) {
free(iov[i].iov_base);
iov[i].iov_base = NULL;
}
buffer->iovcnt = 0;
}
if (buf == NULL) {
/* Sentinel buf for flush. */
ASSERT0(buffer->iovcnt);
return;
}
iov[buffer->iovcnt].iov_base = buf;
iov[buffer->iovcnt].iov_len = nbytes;
buffer->length += nbytes;
buffer->iovcnt++;
}
static inline void
buffer_finish(raw_context_t *context)
{
buffer_write(context, NULL, 0, 0);
if (fsync(context->volume.fd) != 0)
err(EXIT_FAILURE, "fsync");
}
static inline void
resize(raw_context_t *context, size_t size)
{
if (ftruncate(context->volume.fd, size) < 0)
err(EXIT_FAILURE, "ftruncate");
}
static inline void
free_tail(raw_context_t *context, off_t offset)
{
/*
* This style of FREE is frequently a large range covering most of the
* volume from the offset to the end. Truncating the file and extending
* it back out works cheaply even on filesystems that do not support
* hole punching, avoiding the need to write zeros.
*/
resize(context, offset);
if (offset < context->volume.size)
resize(context, context->volume.size);
else
/* Volume size unknown, but it must be at least this big. */
context->volume.size = offset;
}
static inline boolean_t
punch_hole(raw_context_t *context, off_t offset, size_t length)
{
int fd = context->volume.fd;
#if defined(__FreeBSD__)
struct spacectl_range range = { offset, length };
return (fspacectl(fd, SPACECTL_DEALLOC, &range, 0, NULL) == 0);
#elif defined(__linux__)
int mode = FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE;
return (fallocate(fd, mode, offset, length) == 0);
#else
(void) fd, (void) offset, (void) length;
return (B_FALSE);
#endif
}
static boolean_t
free_blocks(raw_context_t *context, off_t offset, size_t length)
{
/*
* Ensure sector alignment in case the source zvol's sector size is
* smaller than the target device's.
*
* TODO: Optional secure erase.
*/
uint64_t sectorsize = context->volume.sectorsize;
uint64_t start = P2ROUNDUP(offset, sectorsize);
uint64_t limit = offset + length;
uint64_t end = P2ALIGN_TYPED(limit, sectorsize, uint64_t);
if (offset < start)
write_zeros(context, offset, start - offset);
if (start < end) {
#ifdef __FreeBSD__
off_t range[2];
#else
uint64_t range[2];
#endif
int fd = context->volume.fd;
range[0] = start;
range[1] = end - start;
if (ioctl(fd, context->volume.freeop, range) != 0) {
ASSERT3U(errno, ==, EOPNOTSUPP);
context->volume.freeop = B_FALSE;
return (B_FALSE);
}
}
if (end < limit)
write_zeros(context, end, limit - end);
return (B_TRUE);
}
static void
free_range(raw_context_t *context, off_t offset, size_t length)
{
if (context->volume.isreg) {
if (length == (size_t)-1) {
free_tail(context, offset);
return;
}
if (context->volume.punch_holes) {
if (punch_hole(context, offset, length))
return;
context->volume.punch_holes = B_FALSE;
}
}
if (length == (size_t)-1)
length = context->volume.size - offset;
if (context->volume.freeop && free_blocks(context, offset, length))
return;
/* If all else fails, the range must be zeroed the slow way. */
write_zeros(context, offset, length);
}
/*
* apply_properties - read the properties zap to adjust file size.
*
* Returns the value of the "size" property.
*/
static uint64_t
apply_properties(raw_context_t *context, void *buf, size_t len)
{
const mzap_phys_t *mzap = buf;
ASSERT(context->volume.isreg);
ASSERT3U(len, >=, sizeof (*mzap));
ASSERT3U(MZAP_ENT_LEN, ==, sizeof (mzap_ent_phys_t));
if (mzap->mz_block_type == BSWAP_64(ZBT_MICRO))
zap_byteswap(buf, len);
ASSERT3U(mzap->mz_block_type, ==, ZBT_MICRO);
ASSERT0(strcmp(mzap->mz_chunk[0].mze_name, "size"));
uint64_t size = mzap->mz_chunk[0].mze_value;
resize(context, size);
return (size);
}
static disposition_t
chain_replay_raw(void *item_in, void *context_in)
{
drr_packet_t *item = item_in;
if (item == NULL)
return (D_OK);
raw_context_t *context = context_in;
dmu_replay_record_t *drr = &item->dp_drr;
switch (drr->drr_type) {
case DRR_BEGIN: {
struct drr_begin *drrb = &drr->drr_u.drr_begin;
uint64_t featureflags, unsupported_features;
featureflags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
unsupported_features = featureflags & ~SUPPORTED_FEATURES;
if (unsupported_features != 0)
errx(EXIT_FAILURE, "unsupported stream features: "
"%#llx of %#llx, aborting...",
(u_longlong_t)unsupported_features,
(u_longlong_t)featureflags);
int hdrtype = DMU_GET_STREAM_HDRTYPE(drrb->drr_versioninfo);
if (hdrtype == DMU_SUBSTREAM) {
uint64_t guid = context->stream.guid;
if (guid != 0 && drrb->drr_fromguid != guid)
errx(EXIT_FAILURE, "wrong fromguid: "
"%llu != %llu, aborting...",
(u_longlong_t)drrb->drr_fromguid,
(u_longlong_t)guid);
context->stream.guid = drrb->drr_toguid;
}
break;
}
case DRR_OBJECT: {
struct drr_object *drro = &drr->drr_u.drr_object;
context->stream.inzvol = drro->drr_object == ZVOL_OBJ &&
drro->drr_type == DMU_OT_ZVOL;
context->stream.inprop = drro->drr_object == ZVOL_ZAP_OBJ &&
drro->drr_type == DMU_OT_ZVOL_PROP;
break;
}
case DRR_WRITE: {
struct drr_write *drrw = &drr->drr_u.drr_write;
if (context->stream.inzvol) {
buffer_write(context, item->dp_payload,
item->dp_payload_size, drrw->drr_offset);
/*
* The buffer is no longer owned by the chain. We will
* free it when safe.
*/
item->dp_payload = NULL;
} else if (context->volume.isreg && context->stream.inprop) {
ASSERT0(drrw->drr_offset);
context->volume.size = apply_properties(context,
item->dp_payload, item->dp_payload_size);
}
break;
}
case DRR_FREE: {
if (!context->stream.inzvol)
break;
struct drr_free *drrf = &drr->drr_u.drr_free;
free_range(context, drrf->drr_offset, drrf->drr_length);
break;
}
case DRR_WRITE_EMBEDDED: {
struct drr_write_embedded *drrwe =
&drr->drr_u.drr_write_embedded;
if (!ctype_is_uncompressed(drrwe->drr_compression)) {
uint8_t *buffer = item->dp_payload;
uint32_t lsize = drrwe->drr_lsize;
ASSERT3U(item->dp_payload_size, <=, lsize);
item->dp_payload = decompress_buffer(buffer,
item->dp_payload_size, lsize,
drrwe->drr_compression);
if (item->dp_payload == NULL)
errx(EXIT_FAILURE,
"decompression failed at offset %llu",
(u_longlong_t)drrwe->drr_offset);
item->dp_payload_size = lsize;
free(buffer);
}
if (context->stream.inzvol) {
buffer_write(context, item->dp_payload,
item->dp_payload_size, drrwe->drr_offset);
/*
* The buffer is no longer owned by the chain. We will
* free it when safe.
*/
item->dp_payload = NULL;
} else if (context->volume.isreg && context->stream.inprop) {
ASSERT0(drrwe->drr_offset);
context->volume.size = apply_properties(context,
item->dp_payload, item->dp_payload_size);
}
break;
}
default:
break;
}
return (D_OK);
}
/* Keep this small enough to not accidentally run systems out of memory. */
#define BUFFERS_MAX_DEFAULT 32
int
zstream_do_raw(int argc, char *argv[])
{
raw_context_t context = { 0 };
context.limits.buffers_max = BUFFERS_MAX_DEFAULT;
chain_attrs_t attrs = { 0 };
ENABLE_OPTION(&attrs, CA_FORBID_DEDUP);
int c;
while ((c = getopt(argc, argv, ":b:g:v")) != -1) {
switch (c) {
case 'b':
context.limits.buffers_max = strtol(optarg, NULL, 0);
if (context.limits.buffers_max <= 0) {
warnx("invalid number of buffers");
zstream_usage();
}
break;
case 'g':
context.stream.guid = strtoull(optarg, NULL, 0);
if (context.stream.guid == 0) {
warnx("invalid guid");
zstream_usage();
}
break;
case 'v':
ENABLE_OPTION(&attrs, CA_VERBOSE);
ENABLE_OPTION(&attrs, CA_DUMP_ALL_RECORDS);
ENABLE_OPTION(&attrs, CA_DUMP_CHECKSUMS);
break;
case ':':
warnx("missing argument for '%c' option", optopt);
zstream_usage();
case '?':
warnx("invalid option '%c'", optopt);
zstream_usage();
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
warnx("missing path to raw volume");
zstream_usage();
}
const char *raw_path = argv[0];
int fd = open(raw_path, O_WRONLY | O_CREAT, 0666);
if (fd < 0)
err(EXIT_FAILURE, "error while opening file '%s'", raw_path);
context.volume.fd = fd;
struct stat64 st;
if (fstat64_blk(fd, &st) < 0)
err(EXIT_FAILURE, "fstat64_blk");
context.volume.size = st.st_size;
context.volume.isreg = S_ISREG(st.st_mode);
if (!context.volume.isreg) {
#if defined(__FreeBSD__)
uint_t sectorsize;
if (ioctl(fd, DIOCGSECTORSIZE, &sectorsize) == 0) {
context.volume.sectorsize = sectorsize;
context.volume.freeop = DIOCGDELETE;
}
#elif defined(__linux__)
if (ioctl(fd, BLKSSZGET, &context.volume.sectorsize) == 0) {
/* TODO: optional BLKSECDISCARD/BLKZEROOUT */
context.volume.freeop = BLKDISCARD;
}
#endif
}
long iov_max = sysconf(_SC_IOV_MAX);
long pagesize = sysconf(_SC_PAGESIZE);
context.limits.iov_max = iov_max;
context.limits.buffers_max = MIN(context.limits.buffers_max, iov_max);
context.limits.pagesize = pagesize;
context.buffer.iov = safe_calloc(context.limits.buffers_max *
sizeof (struct iovec));
context.zeros.iov = safe_malloc(iov_max * sizeof (struct iovec));
void *zero_page = safe_calloc(pagesize);
for (int i = 0; i < iov_max; i++)
context.zeros.iov[i].iov_base = zero_page;
uint32_t drop_mask = DROP_END | DROP_FREEOBJECTS | DROP_OBJECT_RANGE |
DROP_REDACT | DROP_SPILL;
zstream_chain_t raw_chain = {
STANDARD_INPUT_STACK((argc > 1) ? argv[1] : NULL),
serial_dump_records(),
serial_drop_record_types(drop_mask),
parallel_decompress_writes(NULL),
{
.cs_type = CS_SERIAL,
.cs_in_size = sizeof (drr_packet_t),
.cs_out_size = sizeof (drr_packet_t),
.cs_context = &context,
.cs_serial = {.process = chain_replay_raw},
},
NULL_OUTPUT_STACK()
};
zstream_chain_exec(raw_chain, &attrs);
buffer_finish(&context);
free(zero_page);
free(context.zeros.iov);
free(context.buffer.iov);
if (OPTION_ENABLED(CA_VERBOSE))
(void) printf("now at guid %llu\n",
(u_longlong_t)context.stream.guid);
else
(void) printf("%llu\n", (u_longlong_t)context.stream.guid);
return (EXIT_SUCCESS);
}
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -35,6 +25,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/dmu.h>
#include <sys/zfs_ioctl.h>
#include <sys/zio_compress.h>
#include <sys/zstd/zstd.h>
@@ -44,7 +35,8 @@
#include "zstream.h"
#include "zstream_chain.h"
#include "zstream_modules.h"
#include "zstream_util.h"
#include "zstream_queue.h"
#include "zstream_recompress.h"
#define MAX_COMPRESSION_STEPS 4
@@ -125,20 +117,22 @@ needs_decompression(drr_packet_t *item, compression_spec_t *context)
return (needs_modification(item, context));
}
static disposition_t
chain_decompress_writes(drr_packet_t *item, compression_spec_t *context)
/*
* We can ignore the context here because it's already been evaluated by the
* cost function. If the cost function returned something other than zero,
* we have to decompress.
*/
static void
chain_decompress_writes(queue_item_t *item_in, void *context)
{
if (item == NULL)
return (D_OK);
(void) context;
drr_packet_t *item = (drr_packet_t *)item_in;
dmu_replay_record_t *drr = &item->dp_drr;
struct drr_write *drrw = &drr->drr_u.drr_write;
struct drr_write *drrw = &drr->drr_u.drr_write;
uint8_t *debuff;
if (drr->drr_type != DRR_WRITE || !needs_decompression(item, context)) {
return (D_OK);
}
VERIFY3U(drr->drr_type, ==, DRR_WRITE);
debuff = decompress_buffer(item->dp_payload, item->dp_payload_size,
drrw->drr_logical_size, drrw->drr_compressiontype);
if (debuff == NULL) {
@@ -152,29 +146,30 @@ chain_decompress_writes(drr_packet_t *item, compression_spec_t *context)
item->dp_payload_size = drrw->drr_logical_size;
drrw->drr_compressed_size = 0;
drrw->drr_compressiontype = 0;
return (D_OK);
}
static disposition_t
chain_compress_writes(drr_packet_t *item, compression_spec_t *context)
/*
* As with chain_decompress_writes(), all the important decisions were made
* by the cost function. If we're here, we need to compress.
*/
static void
chain_compress_writes(queue_item_t *item_in, void *context_in)
{
if (item == NULL)
return (D_OK);
drr_packet_t *item = (drr_packet_t *)item_in;
compression_spec_t *context = (compression_spec_t *)context_in;
dmu_replay_record_t *drr = &item->dp_drr;
if (drr->drr_type != DRR_WRITE || !needs_compression(item, context)) {
return (D_OK);
}
struct drr_write *drrw = &drr->drr_u.drr_write;
struct drr_write *drrw = &drr->drr_u.drr_write;
enum zio_compress ctype = drrw->drr_compressiontype;
uint8_t *cbuff;
size_t csize;
VERIFY3U(drr->drr_type, ==, DRR_WRITE);
VERIFY3B(ctype_is_uncompressed(ctype), ==, B_TRUE);
cbuff = compress_buffer(item->dp_payload, item->dp_payload_size,
*context, &csize);
if (cbuff == NULL) {
drrw->drr_compressiontype = 0;
drrw->drr_compressed_size = 0;
@@ -185,7 +180,51 @@ chain_compress_writes(drr_packet_t *item, compression_spec_t *context)
drrw->drr_compressed_size = csize;
drrw->drr_compressiontype = context->cs_type;
}
return (D_OK);
}
/*
* A cost of zero waives processing for the current item. If we want to
* process it, the cost will always be item->dp_payload_size. So in these
* two cost functions, we're mostly determining which packets need
* attention. A packet that's already compressed with the target compression
* profile can be ignored.
*/
static size_t
chain_compress_cost(queue_item_t *item_in, void *context_in)
{
compression_spec_t *context = (compression_spec_t *)context_in;
drr_packet_t *item = (drr_packet_t *)item_in;
dmu_replay_record_t *drr = &item->dp_drr;
if (drr->drr_type != DRR_WRITE) {
return (0);
}
struct drr_write *drrw = &drr->drr_u.drr_write;
return (needs_compression(item, context) ? drrw->drr_logical_size : 0);
}
/*
* Don't decompress packets that aren't compressed. And don't decompress
* them if their ultimate fate is to be recompressed using the compression
* profile that's already in use.
*/
static size_t
chain_decompress_cost(queue_item_t *item_in, void *context_in)
{
compression_spec_t *context = (compression_spec_t *)context_in;
drr_packet_t *item = (drr_packet_t *)item_in;
dmu_replay_record_t *drr = &item->dp_drr;
if (drr->drr_type != DRR_WRITE)
return (0);
struct drr_write *drrw = &drr->drr_u.drr_write;
enum zio_compress ctype = drrw->drr_compressiontype;
if (ctype_is_uncompressed(ctype))
return (0);
return (needs_decompression(item, context) ? item->dp_payload_size : 0);
}
/*
@@ -194,7 +233,7 @@ chain_compress_writes(drr_packet_t *item, compression_spec_t *context)
* uncompressed).
*/
chain_step_t
serial_decompress_writes(compression_spec_t *target)
parallel_decompress_writes(compression_spec_t *target)
{
int this_spec = next_spec++ % MAX_COMPRESSION_STEPS;
compression_spec_t *context = &specs[this_spec];
@@ -205,34 +244,107 @@ serial_decompress_writes(compression_spec_t *target)
*context = *target;
}
chain_step_t step = {
.cs_type = CS_SERIAL,
.cs_in_size = sizeof (drr_packet_t),
.cs_out_size = sizeof (drr_packet_t),
.cs_context = context,
.cs_serial = {
.process = (zc_serial_process_f *)chain_decompress_writes
}
.cs_type = CS_PARALLEL,
.cs_in_size = sizeof (drr_packet_t),
.cs_out_size = sizeof (drr_packet_t),
.cs_context = context,
.cs_parallel = {
.queue_length = 256,
.batch_budget = 256 * 1024,
.process = chain_decompress_writes,
.cost = chain_decompress_cost
}
};
return (step);
}
chain_step_t
serial_compress_writes(compression_spec_t *target)
parallel_compress_writes(compression_spec_t *target)
{
int this_spec = next_spec++ % MAX_COMPRESSION_STEPS;
compression_spec_t *context = &specs[this_spec];
VERIFY3P(target, !=, NULL);
*context = *target;
chain_step_t step = {
.cs_type = CS_SERIAL,
.cs_in_size = sizeof (drr_packet_t),
.cs_out_size = sizeof (drr_packet_t),
.cs_context = context,
.cs_serial = {
.process =
(zc_serial_process_f *)chain_compress_writes
.cs_type = CS_PARALLEL,
.cs_in_size = sizeof (drr_packet_t),
.cs_out_size = sizeof (drr_packet_t),
.cs_context = context,
.cs_parallel = {
.queue_length = 1024,
.batch_budget = 32 * 1024,
.process = chain_compress_writes,
.cost = chain_compress_cost
}
};
return (step);
}
/*
* Keep DRR_BEGIN feature flags consistent with the WRITE payloads we emit.
* Compressed WRITEs require DMU_BACKUP_FEATURE_COMPRESSED (and LZ4/ZSTD as
* appropriate). Recompressing to off clears COMPRESSED for non-raw streams;
* raw streams may retain encrypted compressed WRITEs that recompress skips.
* LZ4/ZSTD are cleared only when neither EMBED_DATA nor RAW remains, since
* recompress does not rewrite those record types.
*/
static disposition_t
chain_update_compress_features(void *item_in, void *context_in)
{
drr_packet_t *item = (drr_packet_t *)item_in;
compression_spec_t *spec = (compression_spec_t *)context_in;
struct drr_begin *drrb;
uint64_t flags;
if (item == NULL)
return (D_OK);
if (item->dp_drr.drr_type != DRR_BEGIN)
return (D_OK);
drrb = &item->dp_drr.drr_u.drr_begin;
flags = DMU_GET_FEATUREFLAGS(drrb->drr_versioninfo);
if (ctype_is_uncompressed(spec->cs_type)) {
if (!(flags & DMU_BACKUP_FEATURE_RAW))
flags &= ~DMU_BACKUP_FEATURE_COMPRESSED;
if ((flags & (DMU_BACKUP_FEATURE_EMBED_DATA |
DMU_BACKUP_FEATURE_RAW)) == 0) {
flags &= ~(DMU_BACKUP_FEATURE_LZ4 |
DMU_BACKUP_FEATURE_ZSTD);
}
} else {
flags |= DMU_BACKUP_FEATURE_COMPRESSED;
if (spec->cs_type == ZIO_COMPRESS_ZSTD) {
flags |= DMU_BACKUP_FEATURE_ZSTD;
} else if (spec->cs_type >= ZIO_COMPRESS_LZ4) {
flags |= DMU_BACKUP_FEATURE_LZ4;
}
}
DMU_SET_FEATUREFLAGS(drrb->drr_versioninfo, flags);
return (D_OK);
}
static chain_step_t
serial_update_compress_features(compression_spec_t *target)
{
int this_spec = next_spec++ % MAX_COMPRESSION_STEPS;
compression_spec_t *context = &specs[this_spec];
VERIFY3P(target, !=, NULL);
*context = *target;
chain_step_t step = {
.cs_type = CS_SERIAL,
.cs_in_size = sizeof (drr_packet_t),
.cs_out_size = sizeof (drr_packet_t),
.cs_context = context,
.cs_serial = {
.process = chain_update_compress_features,
}
};
return (step);
}
@@ -242,10 +354,11 @@ zstream_do_recompress(int argc, char *argv[])
{
int c;
int level = ZIO_COMPLEVEL_DEFAULT;
int num_threads = 0;
chain_attrs_t attrs = { .ca_command_opts = CA_FORBID_DEDUP };
while ((c = getopt(argc, argv, "l:")) != -1) {
while ((c = getopt(argc, argv, "t:l:")) != -1) {
switch (c) {
case 'l':
if (sscanf(optarg, "%d", &level) != 1) {
@@ -253,10 +366,17 @@ zstream_do_recompress(int argc, char *argv[])
zstream_usage();
}
break;
case 't':
if (sscanf(optarg, "%d", &num_threads) != 1) {
warnx("failed to parse num_threads '%s'",
optarg);
zstream_usage();
}
zstream_queue_set_num_threads(num_threads);
break;
case '?':
warnx("invalid option '%c'", optopt);
zstream_usage();
break;
}
}
@@ -284,8 +404,9 @@ zstream_do_recompress(int argc, char *argv[])
zstream_chain_t recompress_chain = {
STANDARD_INPUT_STACK(NULL),
serial_decompress_writes(&spec),
serial_compress_writes(&spec),
parallel_decompress_writes(&spec),
parallel_compress_writes(&spec),
serial_update_compress_features(&spec),
STANDARD_OUTPUT_STACK(NULL)
};
@@ -1,16 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* This file and its contents are supplied under the terms of the Common
* Development and Distribution License ("CDDL"), version 1.0. You may only use
* this file in accordance with the terms of version 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this source. A
* copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -28,10 +25,10 @@ extern "C" {
#include "zstream_io.h"
chain_step_t
serial_decompress_writes(compression_spec_t *target);
parallel_decompress_writes(compression_spec_t *target);
chain_step_t
serial_compress_writes(compression_spec_t *target);
parallel_compress_writes(compression_spec_t *target);
#ifdef __cplusplus
}
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
@@ -9,9 +7,7 @@
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -102,8 +98,11 @@ rdt_lookup(redup_table_t *rdt,
}
static disposition_t
chain_redup_writes(drr_packet_t *item, redup_context_t *context)
chain_redup_writes(void *item_in, void *context_in)
{
drr_packet_t *item = (drr_packet_t *)item_in;
redup_context_t *context = (redup_context_t *)context_in;
if (item == NULL) {
return (D_OK);
}
@@ -188,7 +187,7 @@ serial_redup_writes(redup_context_t *context)
.cs_out_size = sizeof (drr_packet_t),
.cs_context = context,
.cs_serial = {
.process = (zc_serial_process_f *)chain_redup_writes
.process = chain_redup_writes
}
};
return (step);
@@ -210,7 +209,6 @@ zstream_do_redup(int argc, char *argv[])
case '?':
warnx("invalid option '%c'", optopt);
zstream_usage();
break;
}
}
@@ -0,0 +1,205 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2026 by Garth Snyder. All rights reserved.
*/
/*
* zstream selftest: in-process unit tests for zstream's internal machinery.
*
* zstream selftest [-l] [-s seed] [-t nthreads] module [test ...]
*
* Tests are grouped into modules (see zstream_selftest.h). With no test
* names, all of a module's tests run in order. -l lists the available
* tests. -s replays a previous run's PRNG seed. -t sets the size of the
* shared worker thread pool before any test runs.
*
* This subcommand is intentionally undocumented in zstream_usage() and the
* man page; it exists to be driven by the ZFS test suite.
*/
#include <assert.h>
#include <err.h>
#include <libspl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/random.h>
#include <sys/stdtypes.h>
#include <unistd.h>
#include "zstream.h"
#include "zstream_backtrace.h"
#include "zstream_queue.h"
#include "zstream_selftest.h"
typedef struct {
const char *sm_name;
const test_case_t *sm_cases;
} selftest_module_t;
static const selftest_module_t modules[] = {
{ "queue", selftest_queue_cases },
};
#define NUM_MODULES (sizeof (modules) / sizeof (modules[0]))
uint64_t selftest_seed;
static const char *current_test = "(startup)";
static void
selftest_usage(void)
{
(void) fprintf(stderr,
"usage: zstream selftest [-l] [-s seed] [-t nthreads] "
"module [test ...]\n"
"\n"
"\t-l list available tests\n"
"\t-s seed seed for pseudo-random workloads (for replays)\n"
"\t-t num size of the shared worker thread pool\n"
"\n"
"Available modules:");
for (int i = 0; i < NUM_MODULES; i++)
(void) fprintf(stderr, " %s", modules[i].sm_name);
(void) fprintf(stderr, "\n");
exit(1);
}
static const selftest_module_t *
find_module(const char *name)
{
for (int i = 0; i < NUM_MODULES; i++) {
if (strcmp(name, modules[i].sm_name) == 0)
return (&modules[i]);
}
warnx("unknown module '%s'", name);
selftest_usage();
return (NULL); /* NOTREACHED */
}
static void
list_tests(const selftest_module_t *module)
{
for (int i = 0; i < NUM_MODULES; i++) {
if (module != NULL && module != &modules[i])
continue;
(void) printf("%s:\n", modules[i].sm_name);
for (const test_case_t *tc = modules[i].sm_cases;
tc->tc_name != NULL; tc++) {
(void) printf("\t%s\n", tc->tc_name);
}
}
}
static void
run_case(const test_case_t *tc)
{
(void) printf("Running %-20s ... ", tc->tc_name);
(void) fflush(stdout);
current_test = tc->tc_name;
watchdog_arm();
tc->tc_func();
watchdog_disarm();
(void) printf("OK\n");
}
static const test_case_t *
find_case(const selftest_module_t *module, const char *name)
{
for (const test_case_t *tc = module->sm_cases;
tc->tc_name != NULL; tc++) {
if (strcmp(name, tc->tc_name) == 0)
return (tc);
}
errx(2, "module '%s' has no test named '%s' (try -l)",
module->sm_name, name);
return (NULL); /* NOTREACHED */
}
int
zstream_do_selftest(int argc, char *argv[])
{
boolean_t list_only = B_FALSE;
boolean_t have_seed = B_FALSE;
int nthreads = 0;
char *end;
int c;
while ((c = getopt(argc, argv, "ls:t:")) != -1) {
switch (c) {
case 'l':
list_only = B_TRUE;
break;
case 's':
selftest_seed = strtoull(optarg, &end, 0);
if (*optarg == '\0' || *end != '\0') {
warnx("failed to parse seed '%s'", optarg);
selftest_usage();
}
have_seed = B_TRUE;
break;
case 't':
if (sscanf(optarg, "%d", &nthreads) != 1) {
warnx("failed to parse num_threads '%s'",
optarg);
selftest_usage();
}
zstream_queue_set_num_threads(nthreads);
break;
case '?':
warnx("invalid option '%c'", optopt);
selftest_usage();
break;
}
}
argc -= optind;
argv += optind;
if (list_only) {
list_tests(argc > 0 ? find_module(argv[0]) : NULL);
return (0);
}
if (argc < 1)
selftest_usage();
const selftest_module_t *module = find_module(argv[0]);
/* Needed for random_get_pseudo_bytes() */
libspl_init();
watchdog_init();
if (!have_seed)
random_get_pseudo_bytes((uint8_t *)&selftest_seed,
sizeof (selftest_seed));
(void) printf("Using seed 0x%016jx (replay with -s 0x%jx)\n",
(uintmax_t)selftest_seed, (uintmax_t)selftest_seed);
int count = 0;
if (argc == 1) {
for (const test_case_t *tc = module->sm_cases;
tc->tc_name != NULL; tc++) {
run_case(tc);
count++;
}
} else {
for (int i = 1; i < argc; i++) {
run_case(find_case(module, argv[i]));
count++;
}
}
(void) printf("All %d %s selftest%s passed\n", count, module->sm_name,
count == 1 ? "" : "s");
libspl_fini();
return (0);
}
@@ -0,0 +1,96 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2026 by Garth Snyder. All rights reserved.
*/
#ifndef _ZSTREAM_SELFTEST_H
#define _ZSTREAM_SELFTEST_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/*
* Shared harness for "zstream selftest". Each module under test supplies a
* NULL-terminated array of named test cases. The harness in
* zstream_selftest.c handles argument parsing, test selection, seeding of
* pseudo-random number generators, watchdog timeouts, and status output.
*
* Test cases report failure by exiting with a nonzero exit code. Any test
* case that returns has passed.
*/
typedef void test_function_f(void);
typedef struct {
const char *tc_name;
test_function_f *tc_func;
} test_case_t;
/*
* Modules with test cases to offer. Each array ends with a NULL tc_name.
*/
extern const test_case_t selftest_queue_cases[];
/*
* The seed for this run, set by the harness before any test runs. Printed
* at startup and settable with -s so failures can be replayed.
*/
extern uint64_t selftest_seed;
/*
* A small deterministic PRNG (splitmix64). Tests derive per-thread
* generators from selftest_seed plus a caller-chosen stream number, so
* workloads are reproducible for a given seed regardless of scheduling.
*/
typedef struct {
uint64_t sr_state;
} selftest_rng_t;
static inline uint64_t
selftest_mix64(uint64_t z)
{
z += 0x9e3779b97f4a7c15ULL;
z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
return (z ^ (z >> 31));
}
static inline void
selftest_rng_init(selftest_rng_t *rng, uint64_t stream)
{
rng->sr_state = selftest_seed ^ selftest_mix64(stream);
}
static inline uint64_t
selftest_rng_next(selftest_rng_t *rng)
{
rng->sr_state += 0x9e3779b97f4a7c15ULL;
return (selftest_mix64(rng->sr_state));
}
/* Uniform value in [0, bound); returns 0 if bound is 0 */
static inline uint64_t
selftest_rng_below(selftest_rng_t *rng, uint64_t bound)
{
return (bound ? selftest_rng_next(rng) % bound : 0);
}
#ifdef __cplusplus
}
#endif
#endif /* _ZSTREAM_SELFTEST_H */
@@ -0,0 +1,564 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
* Copyright (c) 2026 by Garth Snyder. All rights reserved.
*/
/*
* Selftests for the zstream_queue multithreaded FIFO queue API.
*
* All tests are built on one generic workload runner. A workload is
* described by a qtest_config_t: some number of producer threads each
* enqueue a stream of self-describing items with randomized costs,
* payloads, and processing delays, while one consumer thread per queue
* dequeues and verifies. Several workloads can run concurrently on separate
* queues to exercise the shared thread pool.
*
* Every item carries enough information to be verified independently:
*
* - The tuple (qi_producer, qi_seq) identifies each item; the consumer
* checks that each producer's items arrive in the same order they
* were enqueued.
*
* - qi_check is a hash of (qi_seed, qi_producer, qi_seq). The processing
* function verifies it and then XORs in TRANSFORM_MAGIC. The consumer
* checks that the transform happened iff cost > 0.
*
* - qi_pattern[] is filled from qi_check and verified both by the process
* function and the consumer, to catch any corruption of the shallow
* copies in and out of the ring buffer.
*
* - qi_process_count counts invocations of the process function, which
* must be exactly one for cost > 0 items and zero for cost == 0 items.
*
* Global conservation checks: the number of items dequeued must equal the
* number enqueued, and the total number of process-function invocations
* must equal the number of nonzero-cost items enqueued.
*/
#include <assert.h>
#include <atomic.h>
#include <err.h>
#include <pthread.h>
#include <stdalign.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "zstream_queue.h"
#include "zstream_selftest.h"
#define TRANSFORM_MAGIC 0xf00dfeedbeefcafeULL
/*
* Number of times per 1000 processing function invocations to use an
* extra-long "outlier" processing delay to force overtly out-of-order
* completion.
*/
#define LONG_DELAYS_PER_THOUSAND 3
#define LONG_DELAY_MULTIPLIER 20
typedef struct {
uint32_t qi_producer;
uint32_t qi_delay_us;
uint64_t qi_seq;
uint64_t qi_check;
size_t qi_cost;
uint32_t qi_process_count;
uint8_t qi_pattern[];
} qtest_item_t;
typedef struct {
uint32_t qc_producers; /* Number of producers */
uint64_t qc_items; /* Items per producer */
size_t qc_queue_length;
size_t qc_batch_budget;
size_t qc_pattern_len; /* Extra payload bytes */
uint32_t qc_zero_cost_pct; /* % of items fast-tracked */
size_t qc_max_cost; /* Nonzero costs are 1..max */
uint32_t qc_delay_pct; /* % of items slept on */
uint32_t qc_max_delay_us;
uint32_t qc_producer_stall_pct; /* % chance producer naps */
uint32_t qc_consumer_stall_pct; /* % chance consumer naps */
uint32_t qc_stall_max_us;
uint64_t qc_rng_stream; /* base PRNG stream number */
} qtest_config_t;
typedef struct {
const qtest_config_t *qr_cfg;
zstream_queue_t *qr_queue;
uint32_t qr_producers_left;
uint64_t qr_expect_processed; /* Atomic */
uint64_t qr_processed; /* Atomic */
uint64_t qr_dequeued;
} qtest_run_t;
typedef struct {
qtest_run_t *qp_run;
uint32_t qp_id;
} qtest_producer_arg_t;
static uint64_t
item_check_value(uint32_t producer, uint64_t seq)
{
return (selftest_mix64(selftest_seed ^
(((uint64_t)producer << 40) + seq)));
}
static void
fill_pattern(uint8_t *pattern, size_t len, uint64_t check)
{
for (size_t i = 0; i < len; i++)
pattern[i] = (uint8_t)(check >> ((i & 7) << 3)) ^ (uint8_t)i;
}
static void
verify_pattern(const uint8_t *pattern, size_t len, uint64_t check,
const char *who)
{
for (size_t i = 0; i < len; i++) {
uint8_t expect =
(uint8_t)(check >> ((i & 7) << 3)) ^ (uint8_t)i;
if (pattern[i] != expect) {
errx(1, "%s: payload corrupted at byte %zu "
"(0x%02x != 0x%02x)", who, i, pattern[i], expect);
}
}
}
static size_t
qtest_cost(void *item_in, void *context)
{
(void) context;
qtest_item_t *item = item_in;
return (item->qi_cost);
}
static void
qtest_process(void *item_in, void *context)
{
qtest_run_t *run = context;
qtest_item_t *item = item_in;
/* Cost-0 items should never reach the process function */
VERIFY3U(item->qi_cost, >, 0);
VERIFY3U(item->qi_check, ==,
item_check_value(item->qi_producer, item->qi_seq));
verify_pattern(item->qi_pattern, run->qr_cfg->qc_pattern_len,
item->qi_check, "process");
VERIFY3U(atomic_add_32_nv(&item->qi_process_count, 1), ==, 1);
if (item->qi_delay_us > 0)
(void) usleep(item->qi_delay_us);
item->qi_check ^= TRANSFORM_MAGIC;
atomic_add_64(&run->qr_processed, 1);
}
/*
* Pthreads worker function for enqueuers
*/
static void *
qtest_producer(void *arg)
{
qtest_producer_arg_t *pa = arg;
qtest_run_t *run = pa->qp_run;
const qtest_config_t *cfg = run->qr_cfg;
uint64_t local_expect = 0;
selftest_rng_t rng;
alignas(uint64_t) uint8_t item_buffer[sizeof (qtest_item_t) +
cfg->qc_pattern_len];
qtest_item_t *item = (qtest_item_t *)item_buffer;
selftest_rng_init(&rng, cfg->qc_rng_stream + 1000 + pa->qp_id);
for (uint64_t seq = 0; seq < cfg->qc_items; seq++) {
qtest_item_t item_xfer = {
.qi_producer = pa->qp_id,
.qi_seq = seq,
.qi_process_count = 0,
.qi_check = item_check_value(pa->qp_id, seq)
};
*item = item_xfer;
fill_pattern(item->qi_pattern, cfg->qc_pattern_len,
item->qi_check);
if (selftest_rng_below(&rng, 100) < cfg->qc_zero_cost_pct) {
item->qi_cost = 0;
} else {
item->qi_cost =
1 + selftest_rng_below(&rng, cfg->qc_max_cost);
local_expect++;
}
if (item->qi_cost > 0 && cfg->qc_max_delay_us > 0) {
if (selftest_rng_below(&rng, 1000) <
LONG_DELAYS_PER_THOUSAND) {
item->qi_delay_us = cfg->qc_max_delay_us *
LONG_DELAY_MULTIPLIER;
} else if (selftest_rng_below(&rng, 100) <
cfg->qc_delay_pct) {
item->qi_delay_us = selftest_rng_below(&rng,
cfg->qc_max_delay_us);
}
}
if (cfg->qc_producer_stall_pct > 0 &&
selftest_rng_below(&rng, 100) < cfg->qc_producer_stall_pct)
(void) usleep(selftest_rng_below(&rng,
cfg->qc_stall_max_us));
zstream_enqueue(run->qr_queue, item);
}
atomic_add_64(&run->qr_expect_processed, local_expect);
if (atomic_add_32_nv(&run->qr_producers_left, -1) == 0)
zstream_queue_fini(run->qr_queue);
return (NULL);
}
/*
* Pthreads worker function for dequeuers
*/
static void *
qtest_consumer(void *arg)
{
qtest_run_t *run = arg;
const qtest_config_t *cfg = run->qr_cfg;
selftest_rng_t rng;
uint64_t expected_seq[cfg->qc_producers];
alignas(uint64_t) uint8_t item_buffer[sizeof (qtest_item_t) +
cfg->qc_pattern_len];
qtest_item_t *item = (qtest_item_t *)item_buffer;
memset(expected_seq, 0, sizeof (expected_seq));
selftest_rng_init(&rng, cfg->qc_rng_stream + 999);
while (zstream_dequeue(run->qr_queue, item)) {
VERIFY3U(item->qi_producer, <, cfg->qc_producers);
if (item->qi_seq != expected_seq[item->qi_producer]) {
errx(1, "consumer: FIFO order violated: got "
"producer %u seq %ju, expected seq %ju",
item->qi_producer, (uintmax_t)item->qi_seq,
(uintmax_t)expected_seq[item->qi_producer]);
}
expected_seq[item->qi_producer]++;
uint64_t check =
item_check_value(item->qi_producer, item->qi_seq);
if (item->qi_cost > 0) {
VERIFY3U(item->qi_process_count, ==, 1);
VERIFY3U(item->qi_check, ==, check ^ TRANSFORM_MAGIC);
} else {
VERIFY3U(item->qi_process_count, ==, 0);
VERIFY3U(item->qi_check, ==, check);
}
verify_pattern(item->qi_pattern, cfg->qc_pattern_len, check,
"consumer");
run->qr_dequeued++;
if (cfg->qc_consumer_stall_pct > 0 &&
selftest_rng_below(&rng, 100) < cfg->qc_consumer_stall_pct)
(void) usleep(selftest_rng_below(&rng,
cfg->qc_stall_max_us));
}
for (uint32_t p = 0; p < cfg->qc_producers; p++)
VERIFY3U(expected_seq[p], ==, cfg->qc_items);
VERIFY3U(run->qr_dequeued, ==,
(uint64_t)cfg->qc_producers * cfg->qc_items);
return (NULL);
}
/*
* Run several workloads at once, one queue per config, with a dedicated
* consumer thread and qc_producers producer threads per queue. Returns
* after every queue has been drained to end-of-stream (and therefore
* destroyed) and all verification checks have passed.
*/
static void
run_queue_workloads(const qtest_config_t *cfgs, int ncfg)
{
qtest_run_t runs[ncfg];
pthread_t consumers[ncfg];
uint32_t total_producers = 0;
for (int i = 0; i < ncfg; i++)
total_producers += cfgs[i].qc_producers;
pthread_t producers[total_producers];
qtest_producer_arg_t pargs[total_producers];
memset(runs, 0, sizeof (runs));
memset(pargs, 0, sizeof (pargs));
for (int i = 0; i < ncfg; i++) {
runs[i].qr_cfg = &cfgs[i];
runs[i].qr_producers_left = cfgs[i].qc_producers;
zq_params_t params = {
.qp_process = qtest_process,
.qp_cost = qtest_cost,
.qp_context = &runs[i],
.qp_item_size =
sizeof (qtest_item_t) + cfgs[i].qc_pattern_len,
.qp_batch_budget = cfgs[i].qc_batch_budget,
.qp_queue_length = cfgs[i].qc_queue_length,
};
runs[i].qr_queue = zstream_queue_create(&params);
}
int p = 0;
for (int i = 0; i < ncfg; i++) {
VERIFY3S(pthread_create(&consumers[i], NULL, qtest_consumer,
&runs[i]), ==, 0);
for (uint32_t j = 0; j < cfgs[i].qc_producers; j++, p++) {
pargs[p].qp_run = &runs[i];
pargs[p].qp_id = j;
VERIFY3S(pthread_create(&producers[p], NULL,
qtest_producer, &pargs[p]), ==, 0);
}
}
for (uint32_t i = 0; i < total_producers; i++)
VERIFY3S(pthread_join(producers[i], NULL), ==, 0);
for (int i = 0; i < ncfg; i++)
VERIFY3S(pthread_join(consumers[i], NULL), ==, 0);
for (int i = 0; i < ncfg; i++)
VERIFY3U(runs[i].qr_processed, ==, runs[i].qr_expect_processed);
}
static void
run_queue_workload(const qtest_config_t *cfg)
{
run_queue_workloads(cfg, 1);
}
/*
* Basic single-producer smoke test: deterministic-ish costs, no delays.
*/
static void
queue_basic(void)
{
qtest_config_t cfg = {
.qc_producers = 1,
.qc_items = 5000,
.qc_queue_length = 64,
.qc_batch_budget = 256,
.qc_pattern_len = 32,
.qc_zero_cost_pct = 20,
.qc_max_cost = 64,
};
run_queue_workload(&cfg);
}
/*
* A long, randomized stream with heavy-tailed processing delays, a large
* fraction of fast-tracked items, costs that exceed the batch budget, and a
* consumer that periodically stalls so the queue backs up and enqueue
* blocks on Q_FULL. The ring indices wrap hundreds of times.
*/
static void
queue_torture(void)
{
qtest_config_t cfg = {
.qc_producers = 1,
.qc_items = 100000,
.qc_queue_length = 512,
.qc_batch_budget = 2048,
.qc_pattern_len = 64,
.qc_zero_cost_pct = 30,
.qc_max_cost = 4096,
.qc_delay_pct = 5,
.qc_max_delay_us = 100,
.qc_consumer_stall_pct = 1,
.qc_stall_max_us = 500,
.qc_rng_stream = 100,
};
run_queue_workload(&cfg);
}
/*
* Off-by-one hunting: sweep the degenerate corners of queue length,
* batch budget, and stream length, including a zero-item stream and
* zero-length payloads.
*/
static void
queue_edge_cases(void)
{
static const size_t lengths[] =
{ 1, 2, ZQ_MAX_BATCH - 1, ZQ_MAX_BATCH, ZQ_MAX_BATCH + 1, 64 };
static const size_t budgets[] = { 0, 1, 16, SIZE_MAX / 2 };
uint64_t stream = 200;
for (int l = 0; l < 6; l++) {
for (int b = 0; b < 4; b++) {
uint64_t counts[] = { 0, lengths[l], lengths[l] + 1,
4 * lengths[l] + 3 };
for (int n = 0; n < 4; n++) {
qtest_config_t cfg = {
.qc_producers = 1,
.qc_items = counts[n],
.qc_queue_length = lengths[l],
.qc_batch_budget = budgets[b],
.qc_pattern_len =
(lengths[l] & 1) ? 0 : 24,
.qc_zero_cost_pct = 25,
.qc_max_cost = 8,
.qc_rng_stream = stream++,
};
run_queue_workload(&cfg);
}
}
}
}
/*
* All items cost 0, so every item takes the fast track and the process
* function must never run (qtest_process VERIFYs cost > 0, and the
* conservation check at the end of the run confirms zero invocations).
* This exercises the completion-index sweep for items no worker ever
* touches.
*/
static void
queue_zero_cost(void)
{
qtest_config_t cfg = {
.qc_producers = 1,
.qc_items = 20000,
.qc_queue_length = 128,
.qc_batch_budget = 1024,
.qc_pattern_len = 16,
.qc_zero_cost_pct = 100,
.qc_max_cost = 8,
.qc_rng_stream = 300,
};
run_queue_workload(&cfg);
}
/*
* Eight producer threads hammering one queue with random pacing. The
* consumer verifies per-producer FIFO order and exact counts.
*/
static void
queue_multi_producer(void)
{
qtest_config_t cfg = {
.qc_producers = 8,
.qc_items = 15000,
.qc_queue_length = 256,
.qc_batch_budget = 512,
.qc_pattern_len = 24,
.qc_zero_cost_pct = 25,
.qc_max_cost = 512,
.qc_delay_pct = 2,
.qc_max_delay_us = 50,
.qc_producer_stall_pct = 1,
.qc_stall_max_us = 200,
.qc_rng_stream = 400,
};
run_queue_workload(&cfg);
}
/*
* Many dissimilar queues live at once, stressing worker scoring and
* assignment, per-queue index isolation, and destruction of queues while
* others remain active (which compacts the pool's queue array).
*/
static void
queue_multi_queue(void)
{
qtest_config_t cfgs[12];
for (int i = 0; i < 12; i++) {
uint32_t producers = 1 + i % 3;
qtest_config_t cfg = {
.qc_producers = producers,
.qc_items = 4000 / producers,
.qc_queue_length = (size_t)4 << (i % 6),
.qc_batch_budget =
(i % 4 == 0) ? 0 : (size_t)64 << (i % 5),
.qc_pattern_len = 8 * (i % 5),
.qc_zero_cost_pct = 10 * (i % 6),
.qc_max_cost = (size_t)16 << (i % 8),
.qc_delay_pct = i % 3,
.qc_max_delay_us = 60,
.qc_rng_stream = 500 + i * 10000,
};
cfgs[i] = cfg;
}
run_queue_workloads(cfgs, 12);
}
/*
* Seeded chaos: randomize every workload parameter within sane bounds
* and run a few rounds of concurrent queues. Whatever the targeted tests
* miss, this net catches over many CI runs; failures replay with -s.
*/
static void
queue_stress(void)
{
selftest_rng_t rng;
selftest_rng_init(&rng, 900);
for (int iter = 0; iter < 8; iter++) {
int nqueues = 1 + selftest_rng_below(&rng, 4);
qtest_config_t cfgs[4];
for (int i = 0; i < nqueues; i++) {
uint32_t producers = 1 + selftest_rng_below(&rng, 4);
qtest_config_t cfg = {
.qc_producers = producers,
.qc_items = (2000 +
selftest_rng_below(&rng, 8000)) /
producers,
.qc_queue_length = (size_t)1 <<
selftest_rng_below(&rng, 10),
.qc_batch_budget =
(selftest_rng_below(&rng, 3) == 0) ? 0 :
selftest_rng_below(&rng, 4096),
.qc_pattern_len =
selftest_rng_below(&rng, 64),
.qc_zero_cost_pct =
selftest_rng_below(&rng, 101),
.qc_max_cost =
1 + selftest_rng_below(&rng, 2048),
.qc_delay_pct = selftest_rng_below(&rng, 4),
.qc_max_delay_us =
selftest_rng_below(&rng, 120),
.qc_producer_stall_pct =
selftest_rng_below(&rng, 2),
.qc_consumer_stall_pct =
selftest_rng_below(&rng, 2),
.qc_stall_max_us =
selftest_rng_below(&rng, 400),
.qc_rng_stream = 1000000 + iter * 1000 +
i * 100,
};
cfgs[i] = cfg;
}
run_queue_workloads(cfgs, nqueues);
}
}
const test_case_t selftest_queue_cases[] = {
{ "queue_basic", queue_basic },
{ "queue_edge_cases", queue_edge_cases },
{ "queue_zero_cost", queue_zero_cost },
{ "queue_torture", queue_torture },
{ "queue_multi_producer", queue_multi_producer },
{ "queue_multi_queue", queue_multi_queue },
{ "queue_stress", queue_stress },
{ NULL, NULL },
};
@@ -1,23 +1,13 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
* 1.0 of the CDDL.
*
* 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
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* https://opensource.org/license/CDDL-1.0.
*/
/*
+50 -7
View File
@@ -1,7 +1,5 @@
// SPDX-License-Identifier: CDDL-1.0
/*
* CDDL HEADER START
*
* This file and its contents are supplied under the terms of the
* Common Development and Distribution License ("CDDL"), version 1.0.
* You may only use this file in accordance with the terms of version
@@ -9,9 +7,7 @@
*
* A full copy of the text of the CDDL should have accompanied this
* source. A copy of the CDDL is also available via the Internet at
* http://www.illumos.org/license/CDDL.
*
* CDDL HEADER END
* https://opensource.org/license/CDDL-1.0.
*/
/*
@@ -31,10 +27,10 @@
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <pthread.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/abd.h>
#include <sys/fs/zfs.h>
#include <sys/stdtypes.h>
@@ -43,7 +39,6 @@
#include <sys/zio.h>
#include <sys/zio_compress.h>
#include <unistd.h>
#include <zfs_fletcher.h>
#include "zstream_util.h"
@@ -67,6 +62,54 @@ safe_calloc(size_t size)
return (rv);
}
void
safe_pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset)
{
int error = pthread_sigmask(how, set, oldset);
if (error != 0) {
errno = error;
err(1, "pthread_sigmask failed");
}
}
pthread_t
safe_create_thread(thread_f *body, void *body_arg, const char *name,
boolean_t detach)
{
pthread_t tid;
int ret;
int name_attempts = 3;
ret = pthread_create(&tid, NULL, body, body_arg);
if (ret != 0) {
errno = ret;
err(1, "pthread_create for %s failed", name);
}
/*
* pthread_setname_np() fails randomly on some Debian systems
* because of difficulty reading /proc/self/task. The characteristic
* error is "No such file or directory." The name is just a
* debugging aid, so we can ignore the error, but we'll make three
* attempts to set it. This code previously printed a warning
* message, but that interferes with zstream dump output comparisons
* in ZTS.
*/
while (name_attempts-- > 0) {
ret = pthread_setname_np(tid, name);
if (ret == 0)
break;
usleep(100);
}
if (detach) {
ret = pthread_detach(tid);
if (ret != 0) {
errno = ret;
err(1, "failed to detach %s thread", name);
}
}
return (tid);
}
char *
checksum_str(zio_cksum_t *cksum, char *buff, size_t buff_size)
{
Loaded 100 of 3388 files, more files were not shown because too many files have changed in this diff. Show more