aboutsummaryrefslogtreecommitdiff
path: root/sys/contrib/openzfs/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'sys/contrib/openzfs/cmd')
-rw-r--r--sys/contrib/openzfs/cmd/Makefile.am11
-rwxr-xr-xsys/contrib/openzfs/cmd/zarcstat.in (renamed from sys/contrib/openzfs/cmd/arcstat.in)22
-rwxr-xr-xsys/contrib/openzfs/cmd/zarcsummary (renamed from sys/contrib/openzfs/cmd/arc_summary)6
-rw-r--r--sys/contrib/openzfs/cmd/zdb/zdb.c143
-rw-r--r--sys/contrib/openzfs/cmd/zdb/zdb.h2
-rw-r--r--sys/contrib/openzfs/cmd/zdb/zdb_il.c2
-rw-r--r--sys/contrib/openzfs/cmd/zed/zed.d/Makefile.am24
-rwxr-xr-xsys/contrib/openzfs/cmd/zed/zed.d/deadman-sync-slot_off.sh (renamed from sys/contrib/openzfs/cmd/zed/zed.d/deadman-slot_off.sh)0
l---------sys/contrib/openzfs/cmd/zed/zed.d/pool_import-led.sh1
l---------sys/contrib/openzfs/cmd/zed/zed.d/pool_import-sync-led.sh1
-rwxr-xr-xsys/contrib/openzfs/cmd/zed/zed.d/statechange-sync-led.sh (renamed from sys/contrib/openzfs/cmd/zed/zed.d/statechange-led.sh)0
-rwxr-xr-xsys/contrib/openzfs/cmd/zed/zed.d/statechange-sync-slot_off.sh (renamed from sys/contrib/openzfs/cmd/zed/zed.d/statechange-slot_off.sh)0
l---------sys/contrib/openzfs/cmd/zed/zed.d/vdev_attach-led.sh1
l---------sys/contrib/openzfs/cmd/zed/zed.d/vdev_attach-sync-led.sh1
l---------sys/contrib/openzfs/cmd/zed/zed.d/vdev_clear-led.sh1
l---------sys/contrib/openzfs/cmd/zed/zed.d/vdev_clear-sync-led.sh1
-rw-r--r--sys/contrib/openzfs/cmd/zed/zed_exec.c111
-rw-r--r--sys/contrib/openzfs/cmd/zfs/zfs_main.c6
-rw-r--r--sys/contrib/openzfs/cmd/zhack.c201
-rw-r--r--sys/contrib/openzfs/cmd/zpool/Makefile.am5
-rw-r--r--sys/contrib/openzfs/cmd/zpool/compatibility.d/openzfs-2.448
-rw-r--r--sys/contrib/openzfs/cmd/zstream/Makefile.am5
22 files changed, 495 insertions, 97 deletions
diff --git a/sys/contrib/openzfs/cmd/Makefile.am b/sys/contrib/openzfs/cmd/Makefile.am
index 96040976e53e..ca94f6b77e06 100644
--- a/sys/contrib/openzfs/cmd/Makefile.am
+++ b/sys/contrib/openzfs/cmd/Makefile.am
@@ -98,17 +98,16 @@ endif
if USING_PYTHON
-bin_SCRIPTS += arc_summary arcstat dbufstat zilstat
-CLEANFILES += arc_summary arcstat dbufstat zilstat
-dist_noinst_DATA += %D%/arc_summary %D%/arcstat.in %D%/dbufstat.in %D%/zilstat.in
+bin_SCRIPTS += zarcsummary zarcstat dbufstat zilstat
+CLEANFILES += zarcsummary zarcstat dbufstat zilstat
+dist_noinst_DATA += %D%/zarcsummary %D%/zarcstat.in %D%/dbufstat.in %D%/zilstat.in
-$(call SUBST,arcstat,%D%/)
+$(call SUBST,zarcstat,%D%/)
$(call SUBST,dbufstat,%D%/)
$(call SUBST,zilstat,%D%/)
-arc_summary: %D%/arc_summary
+zarcsummary: %D%/zarcsummary
$(AM_V_at)cp $< $@
endif
-
PHONY += cmd
cmd: $(bin_SCRIPTS) $(bin_PROGRAMS) $(sbin_SCRIPTS) $(sbin_PROGRAMS) $(dist_bin_SCRIPTS) $(zfsexec_PROGRAMS) $(mounthelper_PROGRAMS)
diff --git a/sys/contrib/openzfs/cmd/arcstat.in b/sys/contrib/openzfs/cmd/zarcstat.in
index 6f9abb39c3fb..8ffd20481166 100755
--- a/sys/contrib/openzfs/cmd/arcstat.in
+++ b/sys/contrib/openzfs/cmd/zarcstat.in
@@ -2,7 +2,7 @@
# SPDX-License-Identifier: CDDL-1.0
#
# Print out ZFS ARC Statistics exported via kstat(1)
-# For a definition of fields, or usage, use arcstat -v
+# For a definition of fields, or usage, use zarcstat -v
#
# This script was originally a fork of the original arcstat.pl (0.1)
# by Neelakanth Nadgir, originally published on his Sun blog on
@@ -56,6 +56,7 @@ import time
import getopt
import re
import copy
+import os
from signal import signal, SIGINT, SIGWINCH, SIG_DFL
@@ -171,7 +172,7 @@ cols = {
"zactive": [7, 1000, "zfetch prefetches active per second"],
}
-# ARC structural breakdown from arc_summary
+# ARC structural breakdown from zarcsummary
structfields = {
"cmp": ["compressed", "Compressed"],
"ovh": ["overhead", "Overhead"],
@@ -187,7 +188,7 @@ structstats = { # size stats
"sz": ["_size", "size"],
}
-# ARC types breakdown from arc_summary
+# ARC types breakdown from zarcsummary
typefields = {
"data": ["data", "ARC data"],
"meta": ["metadata", "ARC metadata"],
@@ -198,7 +199,7 @@ typestats = { # size stats
"sz": ["_size", "size"],
}
-# ARC states breakdown from arc_summary
+# ARC states breakdown from zarcsummary
statefields = {
"ano": ["anon", "Anonymous"],
"mfu": ["mfu", "MFU"],
@@ -261,7 +262,7 @@ hdr_intr = 20 # Print header every 20 lines of output
opfile = None
sep = " " # Default separator is 2 spaces
l2exist = False
-cmd = ("Usage: arcstat [-havxp] [-f fields] [-o file] [-s string] [interval "
+cmd = ("Usage: zarcstat [-havxp] [-f fields] [-o file] [-s string] [interval "
"[count]]\n")
cur = {}
d = {}
@@ -348,10 +349,10 @@ def usage():
"character or string\n")
sys.stderr.write("\t -p : Disable auto-scaling of numerical fields\n")
sys.stderr.write("\nExamples:\n")
- sys.stderr.write("\tarcstat -o /tmp/a.log 2 10\n")
- sys.stderr.write("\tarcstat -s \",\" -o /tmp/a.log 2 10\n")
- sys.stderr.write("\tarcstat -v\n")
- sys.stderr.write("\tarcstat -f time,hit%,dh%,ph%,mh% 1\n")
+ sys.stderr.write("\tzarcstat -o /tmp/a.log 2 10\n")
+ sys.stderr.write("\tzarcstat -s \",\" -o /tmp/a.log 2 10\n")
+ sys.stderr.write("\tzarcstat -v\n")
+ sys.stderr.write("\tzarcstat -f time,hit%,dh%,ph%,mh% 1\n")
sys.stderr.write("\n")
sys.exit(1)
@@ -366,7 +367,7 @@ def snap_stats():
cur = kstat
- # fill in additional values from arc_summary
+ # fill in additional values from zarcsummary
cur["caches_size"] = caches_size = cur["anon_data"]+cur["anon_metadata"]+\
cur["mfu_data"]+cur["mfu_metadata"]+cur["mru_data"]+cur["mru_metadata"]+\
cur["uncached_data"]+cur["uncached_metadata"]
@@ -766,6 +767,7 @@ def calculate():
def main():
+
global sint
global count
global hdr_intr
diff --git a/sys/contrib/openzfs/cmd/arc_summary b/sys/contrib/openzfs/cmd/zarcsummary
index e60c6b64e8a1..24a129d9ca70 100755
--- a/sys/contrib/openzfs/cmd/arc_summary
+++ b/sys/contrib/openzfs/cmd/zarcsummary
@@ -34,7 +34,7 @@ Provides basic information on the ARC, its efficiency, the L2ARC (if present),
the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See
the in-source documentation and code at
https://github.com/openzfs/zfs/blob/master/module/zfs/arc.c for details.
-The original introduction to arc_summary can be found at
+The original introduction to zarcsummary can be found at
http://cuddletech.com/?p=454
"""
@@ -161,7 +161,7 @@ elif sys.platform.startswith('linux'):
return get_params(TUNABLES_PATH)
def get_version_impl(request):
- # The original arc_summary called /sbin/modinfo/{spl,zfs} to get
+ # The original zarcsummary called /sbin/modinfo/{spl,zfs} to get
# the version information. We switch to /sys/module/{spl,zfs}/version
# to make sure we get what is really loaded in the kernel
try:
@@ -439,7 +439,7 @@ def print_header():
"""
# datetime is now recommended over time but we keep the exact formatting
- # from the older version of arc_summary in case there are scripts
+ # from the older version of zarcsummary in case there are scripts
# that expect it in this way
daydate = time.strftime(DATE_FORMAT)
spc_date = LINE_LENGTH-len(daydate)
diff --git a/sys/contrib/openzfs/cmd/zdb/zdb.c b/sys/contrib/openzfs/cmd/zdb/zdb.c
index 66d5fbd6adbe..70096b809656 100644
--- a/sys/contrib/openzfs/cmd/zdb/zdb.c
+++ b/sys/contrib/openzfs/cmd/zdb/zdb.c
@@ -107,7 +107,9 @@ extern uint_t zfs_reconstruct_indirect_combinations_max;
extern uint_t zfs_btree_verify_intensity;
static const char cmdname[] = "zdb";
-uint8_t dump_opt[256];
+uint8_t dump_opt[512];
+
+#define ALLOCATED_OPT 256
typedef void object_viewer_t(objset_t *, uint64_t, void *data, size_t size);
@@ -127,6 +129,7 @@ static zfs_range_tree_t *mos_refd_objs;
static spa_t *spa;
static objset_t *os;
static boolean_t kernel_init_done;
+static boolean_t corruption_found = B_FALSE;
static void snprintf_blkptr_compact(char *, size_t, const blkptr_t *,
boolean_t);
@@ -250,6 +253,7 @@ sublivelist_verify_func(void *args, dsl_deadlist_entry_t *dle)
&e->svbr_blk, B_TRUE);
(void) printf("\tERROR: %d unmatched FREE(s): %s\n",
e->svbr_refcnt, blkbuf);
+ corruption_found = B_TRUE;
}
zfs_btree_destroy(&sv->sv_pair);
@@ -405,6 +409,7 @@ verify_livelist_allocs(metaslab_verify_t *mv, uint64_t txg,
(u_longlong_t)DVA_GET_ASIZE(&found->svb_dva),
(u_longlong_t)found->svb_allocated_txg,
(u_longlong_t)txg);
+ corruption_found = B_TRUE;
}
}
}
@@ -426,6 +431,7 @@ metaslab_spacemap_validation_cb(space_map_entry_t *sme, void *arg)
(u_longlong_t)txg, (u_longlong_t)offset,
(u_longlong_t)size, (u_longlong_t)mv->mv_vdid,
(u_longlong_t)mv->mv_msid);
+ corruption_found = B_TRUE;
} else {
zfs_range_tree_add(mv->mv_allocated,
offset, size);
@@ -439,6 +445,7 @@ metaslab_spacemap_validation_cb(space_map_entry_t *sme, void *arg)
(u_longlong_t)txg, (u_longlong_t)offset,
(u_longlong_t)size, (u_longlong_t)mv->mv_vdid,
(u_longlong_t)mv->mv_msid);
+ corruption_found = B_TRUE;
} else {
zfs_range_tree_remove(mv->mv_allocated,
offset, size);
@@ -526,6 +533,7 @@ mv_populate_livelist_allocs(metaslab_verify_t *mv, sublivelist_verify_t *sv)
(u_longlong_t)DVA_GET_VDEV(&svb->svb_dva),
(u_longlong_t)DVA_GET_OFFSET(&svb->svb_dva),
(u_longlong_t)DVA_GET_ASIZE(&svb->svb_dva));
+ corruption_found = B_TRUE;
continue;
}
@@ -542,6 +550,7 @@ mv_populate_livelist_allocs(metaslab_verify_t *mv, sublivelist_verify_t *sv)
(u_longlong_t)DVA_GET_VDEV(&svb->svb_dva),
(u_longlong_t)DVA_GET_OFFSET(&svb->svb_dva),
(u_longlong_t)DVA_GET_ASIZE(&svb->svb_dva));
+ corruption_found = B_TRUE;
continue;
}
@@ -655,6 +664,7 @@ livelist_metaslab_validate(spa_t *spa)
}
(void) printf("ERROR: Found livelist blocks marked as allocated "
"for indirect vdevs:\n");
+ corruption_found = B_TRUE;
zfs_btree_index_t *where = NULL;
sublivelist_verify_block_t *svb;
@@ -827,7 +837,7 @@ usage(void)
(void) fprintf(stderr, "Specify an option more than once (e.g. -bb) "
"to make only that option verbose\n");
(void) fprintf(stderr, "Default is to dump everything non-verbosely\n");
- zdb_exit(1);
+ zdb_exit(2);
}
static void
@@ -1578,9 +1588,8 @@ dump_spacemap(objset_t *os, space_map_t *sm)
continue;
}
- uint8_t words;
char entry_type;
- uint64_t entry_off, entry_run, entry_vdev = SM_NO_VDEVID;
+ uint64_t entry_off, entry_run, entry_vdev;
if (sm_entry_is_single_word(word)) {
entry_type = (SM_TYPE_DECODE(word) == SM_ALLOC) ?
@@ -1588,35 +1597,43 @@ dump_spacemap(objset_t *os, space_map_t *sm)
entry_off = (SM_OFFSET_DECODE(word) << mapshift) +
sm->sm_start;
entry_run = SM_RUN_DECODE(word) << mapshift;
- words = 1;
+
+ (void) printf("\t [%6llu] %c "
+ "range: %012llx-%012llx size: %08llx\n",
+ (u_longlong_t)entry_id, entry_type,
+ (u_longlong_t)entry_off,
+ (u_longlong_t)(entry_off + entry_run - 1),
+ (u_longlong_t)entry_run);
} else {
/* it is a two-word entry so we read another word */
ASSERT(sm_entry_is_double_word(word));
uint64_t extra_word;
offset += sizeof (extra_word);
+ ASSERT3U(offset, <, space_map_length(sm));
VERIFY0(dmu_read(os, space_map_object(sm), offset,
sizeof (extra_word), &extra_word,
DMU_READ_PREFETCH));
- ASSERT3U(offset, <=, space_map_length(sm));
-
entry_run = SM2_RUN_DECODE(word) << mapshift;
entry_vdev = SM2_VDEV_DECODE(word);
entry_type = (SM2_TYPE_DECODE(extra_word) == SM_ALLOC) ?
'A' : 'F';
entry_off = (SM2_OFFSET_DECODE(extra_word) <<
mapshift) + sm->sm_start;
- words = 2;
- }
- (void) printf("\t [%6llu] %c range:"
- " %010llx-%010llx size: %06llx vdev: %06llu words: %u\n",
- (u_longlong_t)entry_id,
- entry_type, (u_longlong_t)entry_off,
- (u_longlong_t)(entry_off + entry_run),
- (u_longlong_t)entry_run,
- (u_longlong_t)entry_vdev, words);
+ if (zopt_metaslab_args == 0 ||
+ zopt_metaslab[0] == entry_vdev) {
+ (void) printf("\t [%6llu] %c "
+ "range: %012llx-%012llx size: %08llx "
+ "vdev: %llu\n",
+ (u_longlong_t)entry_id, entry_type,
+ (u_longlong_t)entry_off,
+ (u_longlong_t)(entry_off + entry_run - 1),
+ (u_longlong_t)entry_run,
+ (u_longlong_t)entry_vdev);
+ }
+ }
if (entry_type == 'A')
alloc += entry_run;
@@ -1652,6 +1669,16 @@ dump_metaslab_stats(metaslab_t *msp)
}
static void
+dump_allocated(void *arg, uint64_t start, uint64_t size)
+{
+ uint64_t *off = arg;
+ if (*off != start)
+ (void) printf("ALLOC: %"PRIu64" %"PRIu64"\n", *off,
+ start - *off);
+ *off = start + size;
+}
+
+static void
dump_metaslab(metaslab_t *msp)
{
vdev_t *vd = msp->ms_group->mg_vd;
@@ -1667,13 +1694,24 @@ dump_metaslab(metaslab_t *msp)
(u_longlong_t)msp->ms_id, (u_longlong_t)msp->ms_start,
(u_longlong_t)space_map_object(sm), freebuf);
- if (dump_opt['m'] > 2 && !dump_opt['L']) {
+ if (dump_opt[ALLOCATED_OPT] ||
+ (dump_opt['m'] > 2 && !dump_opt['L'])) {
mutex_enter(&msp->ms_lock);
VERIFY0(metaslab_load(msp));
+ }
+
+ if (dump_opt['m'] > 2 && !dump_opt['L']) {
zfs_range_tree_stat_verify(msp->ms_allocatable);
dump_metaslab_stats(msp);
- metaslab_unload(msp);
- mutex_exit(&msp->ms_lock);
+ }
+
+ if (dump_opt[ALLOCATED_OPT]) {
+ uint64_t off = msp->ms_start;
+ zfs_range_tree_walk(msp->ms_allocatable, dump_allocated,
+ &off);
+ if (off != msp->ms_start + msp->ms_size)
+ (void) printf("ALLOC: %"PRIu64" %"PRIu64"\n", off,
+ msp->ms_size - off);
}
if (dump_opt['m'] > 1 && sm != NULL &&
@@ -1688,6 +1726,12 @@ dump_metaslab(metaslab_t *msp)
SPACE_MAP_HISTOGRAM_SIZE, sm->sm_shift);
}
+ if (dump_opt[ALLOCATED_OPT] ||
+ (dump_opt['m'] > 2 && !dump_opt['L'])) {
+ metaslab_unload(msp);
+ mutex_exit(&msp->ms_lock);
+ }
+
if (vd->vdev_ops == &vdev_draid_ops)
ASSERT3U(msp->ms_size, <=, 1ULL << vd->vdev_ms_shift);
else
@@ -1724,8 +1768,9 @@ print_vdev_metaslab_header(vdev_t *vd)
}
}
- (void) printf("\tvdev %10llu %s",
- (u_longlong_t)vd->vdev_id, bias_str);
+ (void) printf("\tvdev %10llu\t%s metaslab shift %4llu",
+ (u_longlong_t)vd->vdev_id, bias_str,
+ (u_longlong_t)vd->vdev_ms_shift);
if (ms_flush_data_obj != 0) {
(void) printf(" ms_unflushed_phys object %llu",
@@ -1865,7 +1910,7 @@ dump_metaslabs(spa_t *spa)
(void) printf("\nMetaslabs:\n");
- if (!dump_opt['d'] && zopt_metaslab_args > 0) {
+ if (zopt_metaslab_args > 0) {
c = zopt_metaslab[0];
if (c >= children)
@@ -2583,19 +2628,17 @@ snprintf_blkptr_compact(char *blkbuf, size_t buflen, const blkptr_t *bp,
}
}
-static void
+static u_longlong_t
print_indirect(spa_t *spa, blkptr_t *bp, const zbookmark_phys_t *zb,
const dnode_phys_t *dnp)
{
char blkbuf[BP_SPRINTF_LEN];
+ u_longlong_t offset;
int l;
- if (!BP_IS_EMBEDDED(bp)) {
- ASSERT3U(BP_GET_TYPE(bp), ==, dnp->dn_type);
- ASSERT3U(BP_GET_LEVEL(bp), ==, zb->zb_level);
- }
+ offset = (u_longlong_t)blkid2offset(dnp, bp, zb);
- (void) printf("%16llx ", (u_longlong_t)blkid2offset(dnp, bp, zb));
+ (void) printf("%16llx ", offset);
ASSERT(zb->zb_level >= 0);
@@ -2610,19 +2653,38 @@ print_indirect(spa_t *spa, blkptr_t *bp, const zbookmark_phys_t *zb,
snprintf_blkptr_compact(blkbuf, sizeof (blkbuf), bp, B_FALSE);
if (dump_opt['Z'] && BP_GET_COMPRESS(bp) == ZIO_COMPRESS_ZSTD)
snprintf_zstd_header(spa, blkbuf, sizeof (blkbuf), bp);
- (void) printf("%s\n", blkbuf);
+ (void) printf("%s", blkbuf);
+
+ if (!BP_IS_EMBEDDED(bp)) {
+ if (BP_GET_TYPE(bp) != dnp->dn_type) {
+ (void) printf(" (ERROR: Block pointer type "
+ "(%llu) does not match dnode type (%hhu))",
+ BP_GET_TYPE(bp), dnp->dn_type);
+ corruption_found = B_TRUE;
+ }
+ if (BP_GET_LEVEL(bp) != zb->zb_level) {
+ (void) printf(" (ERROR: Block pointer level "
+ "(%llu) does not match bookmark level (%lld))",
+ BP_GET_LEVEL(bp), (longlong_t)zb->zb_level);
+ corruption_found = B_TRUE;
+ }
+ }
+ (void) printf("\n");
+
+ return (offset);
}
static int
visit_indirect(spa_t *spa, const dnode_phys_t *dnp,
blkptr_t *bp, const zbookmark_phys_t *zb)
{
+ u_longlong_t offset;
int err = 0;
if (BP_GET_BIRTH(bp) == 0)
return (0);
- print_indirect(spa, bp, zb, dnp);
+ offset = print_indirect(spa, bp, zb, dnp);
if (BP_GET_LEVEL(bp) > 0 && !BP_IS_HOLE(bp)) {
arc_flags_t flags = ARC_FLAG_WAIT;
@@ -2652,8 +2714,15 @@ visit_indirect(spa_t *spa, const dnode_phys_t *dnp,
break;
fill += BP_GET_FILL(cbp);
}
- if (!err)
- ASSERT3U(fill, ==, BP_GET_FILL(bp));
+ if (!err) {
+ if (fill != BP_GET_FILL(bp)) {
+ (void) printf("%16llx: Block pointer "
+ "fill (%llu) does not match calculated "
+ "value (%llu)\n", offset, BP_GET_FILL(bp),
+ (u_longlong_t)fill);
+ corruption_found = B_TRUE;
+ }
+ }
arc_buf_destroy(buf, &buf);
}
@@ -2909,6 +2978,7 @@ dump_full_bpobj(bpobj_t *bpo, const char *name, int indent)
(void) printf("ERROR %u while trying to open "
"subobj id %llu\n",
error, (u_longlong_t)subobj);
+ corruption_found = B_TRUE;
continue;
}
dump_full_bpobj(&subbpo, "subobj", indent + 1);
@@ -3088,6 +3158,7 @@ bpobj_count_refd(bpobj_t *bpo)
(void) printf("ERROR %u while trying to open "
"subobj id %llu\n",
error, (u_longlong_t)subobj);
+ corruption_found = B_TRUE;
continue;
}
bpobj_count_refd(&subbpo);
@@ -9334,6 +9405,8 @@ main(int argc, char **argv)
{"all-reconstruction", no_argument, NULL, 'Y'},
{"livelist", no_argument, NULL, 'y'},
{"zstd-headers", no_argument, NULL, 'Z'},
+ {"allocated-map", no_argument, NULL,
+ ALLOCATED_OPT},
{0, 0, 0, 0}
};
@@ -9364,6 +9437,7 @@ main(int argc, char **argv)
case 'u':
case 'y':
case 'Z':
+ case ALLOCATED_OPT:
dump_opt[c]++;
dump_all = 0;
break;
@@ -9634,7 +9708,7 @@ main(int argc, char **argv)
} else if (objset_str && !zdb_numeric(objset_str + 1) &&
dump_opt['N']) {
printf("Supply a numeric objset ID with -N\n");
- error = 1;
+ error = 2;
goto fini;
}
} else {
@@ -9936,5 +10010,8 @@ fini:
if (kernel_init_done)
kernel_fini();
+ if (corruption_found && error == 0)
+ error = 3;
+
return (error);
}
diff --git a/sys/contrib/openzfs/cmd/zdb/zdb.h b/sys/contrib/openzfs/cmd/zdb/zdb.h
index 6b6c9169816b..48b561eb202c 100644
--- a/sys/contrib/openzfs/cmd/zdb/zdb.h
+++ b/sys/contrib/openzfs/cmd/zdb/zdb.h
@@ -29,6 +29,6 @@
#define _ZDB_H
void dump_intent_log(zilog_t *);
-extern uint8_t dump_opt[256];
+extern uint8_t dump_opt[512];
#endif /* _ZDB_H */
diff --git a/sys/contrib/openzfs/cmd/zdb/zdb_il.c b/sys/contrib/openzfs/cmd/zdb/zdb_il.c
index 62e290cd122c..3d91fb28a4c7 100644
--- a/sys/contrib/openzfs/cmd/zdb/zdb_il.c
+++ b/sys/contrib/openzfs/cmd/zdb/zdb_il.c
@@ -48,8 +48,6 @@
#include "zdb.h"
-extern uint8_t dump_opt[256];
-
static char tab_prefix[4] = "\t\t\t";
static void
diff --git a/sys/contrib/openzfs/cmd/zed/zed.d/Makefile.am b/sys/contrib/openzfs/cmd/zed/zed.d/Makefile.am
index 093a04c4636a..c0b161ecf248 100644
--- a/sys/contrib/openzfs/cmd/zed/zed.d/Makefile.am
+++ b/sys/contrib/openzfs/cmd/zed/zed.d/Makefile.am
@@ -9,18 +9,18 @@ dist_zedexec_SCRIPTS = \
%D%/all-debug.sh \
%D%/all-syslog.sh \
%D%/data-notify.sh \
- %D%/deadman-slot_off.sh \
+ %D%/deadman-sync-slot_off.sh \
%D%/generic-notify.sh \
- %D%/pool_import-led.sh \
+ %D%/pool_import-sync-led.sh \
%D%/resilver_finish-notify.sh \
%D%/resilver_finish-start-scrub.sh \
%D%/scrub_finish-notify.sh \
- %D%/statechange-led.sh \
+ %D%/statechange-sync-led.sh \
%D%/statechange-notify.sh \
- %D%/statechange-slot_off.sh \
+ %D%/statechange-sync-slot_off.sh \
%D%/trim_finish-notify.sh \
- %D%/vdev_attach-led.sh \
- %D%/vdev_clear-led.sh
+ %D%/vdev_attach-sync-led.sh \
+ %D%/vdev_clear-sync-led.sh
nodist_zedexec_SCRIPTS = \
%D%/history_event-zfs-list-cacher.sh
@@ -30,17 +30,17 @@ SUBSTFILES += $(nodist_zedexec_SCRIPTS)
zedconfdefaults = \
all-syslog.sh \
data-notify.sh \
- deadman-slot_off.sh \
+ deadman-sync-slot_off.sh \
history_event-zfs-list-cacher.sh \
- pool_import-led.sh \
+ pool_import-sync-led.sh \
resilver_finish-notify.sh \
resilver_finish-start-scrub.sh \
scrub_finish-notify.sh \
- statechange-led.sh \
+ statechange-sync-led.sh \
statechange-notify.sh \
- statechange-slot_off.sh \
- vdev_attach-led.sh \
- vdev_clear-led.sh
+ statechange-sync-slot_off.sh \
+ vdev_attach-sync-led.sh \
+ vdev_clear-sync-led.sh
dist_noinst_DATA += %D%/README
diff --git a/sys/contrib/openzfs/cmd/zed/zed.d/deadman-slot_off.sh b/sys/contrib/openzfs/cmd/zed/zed.d/deadman-sync-slot_off.sh
index 7b339b3add01..7b339b3add01 100755
--- a/sys/contrib/openzfs/cmd/zed/zed.d/deadman-slot_off.sh
+++ b/sys/contrib/openzfs/cmd/zed/zed.d/deadman-sync-slot_off.sh
diff --git a/sys/contrib/openzfs/cmd/zed/zed.d/pool_import-led.sh b/sys/contrib/openzfs/cmd/zed/zed.d/pool_import-led.sh
deleted file mode 120000
index 7d7404398a4a..000000000000
--- a/sys/contrib/openzfs/cmd/zed/zed.d/pool_import-led.sh
+++ /dev/null
@@ -1 +0,0 @@
-statechange-led.sh \ No newline at end of file
diff --git a/sys/contrib/openzfs/cmd/zed/zed.d/pool_import-sync-led.sh b/sys/contrib/openzfs/cmd/zed/zed.d/pool_import-sync-led.sh
new file mode 120000
index 000000000000..8b9c10c11ebb
--- /dev/null
+++ b/sys/contrib/openzfs/cmd/zed/zed.d/pool_import-sync-led.sh
@@ -0,0 +1 @@
+statechange-sync-led.sh \ No newline at end of file
diff --git a/sys/contrib/openzfs/cmd/zed/zed.d/statechange-led.sh b/sys/contrib/openzfs/cmd/zed/zed.d/statechange-sync-led.sh
index 40cb61f17307..40cb61f17307 100755
--- a/sys/contrib/openzfs/cmd/zed/zed.d/statechange-led.sh
+++ b/sys/contrib/openzfs/cmd/zed/zed.d/statechange-sync-led.sh
diff --git a/sys/contrib/openzfs/cmd/zed/zed.d/statechange-slot_off.sh b/sys/contrib/openzfs/cmd/zed/zed.d/statechange-sync-slot_off.sh
index 06acce93b8aa..06acce93b8aa 100755
--- a/sys/contrib/openzfs/cmd/zed/zed.d/statechange-slot_off.sh
+++ b/sys/contrib/openzfs/cmd/zed/zed.d/statechange-sync-slot_off.sh
diff --git a/sys/contrib/openzfs/cmd/zed/zed.d/vdev_attach-led.sh b/sys/contrib/openzfs/cmd/zed/zed.d/vdev_attach-led.sh
deleted file mode 120000
index 7d7404398a4a..000000000000
--- a/sys/contrib/openzfs/cmd/zed/zed.d/vdev_attach-led.sh
+++ /dev/null
@@ -1 +0,0 @@
-statechange-led.sh \ No newline at end of file
diff --git a/sys/contrib/openzfs/cmd/zed/zed.d/vdev_attach-sync-led.sh b/sys/contrib/openzfs/cmd/zed/zed.d/vdev_attach-sync-led.sh
new file mode 120000
index 000000000000..8b9c10c11ebb
--- /dev/null
+++ b/sys/contrib/openzfs/cmd/zed/zed.d/vdev_attach-sync-led.sh
@@ -0,0 +1 @@
+statechange-sync-led.sh \ No newline at end of file
diff --git a/sys/contrib/openzfs/cmd/zed/zed.d/vdev_clear-led.sh b/sys/contrib/openzfs/cmd/zed/zed.d/vdev_clear-led.sh
deleted file mode 120000
index 7d7404398a4a..000000000000
--- a/sys/contrib/openzfs/cmd/zed/zed.d/vdev_clear-led.sh
+++ /dev/null
@@ -1 +0,0 @@
-statechange-led.sh \ No newline at end of file
diff --git a/sys/contrib/openzfs/cmd/zed/zed.d/vdev_clear-sync-led.sh b/sys/contrib/openzfs/cmd/zed/zed.d/vdev_clear-sync-led.sh
new file mode 120000
index 000000000000..8b9c10c11ebb
--- /dev/null
+++ b/sys/contrib/openzfs/cmd/zed/zed.d/vdev_clear-sync-led.sh
@@ -0,0 +1 @@
+statechange-sync-led.sh \ No newline at end of file
diff --git a/sys/contrib/openzfs/cmd/zed/zed_exec.c b/sys/contrib/openzfs/cmd/zed/zed_exec.c
index 036081decd64..a14af4f20a85 100644
--- a/sys/contrib/openzfs/cmd/zed/zed_exec.c
+++ b/sys/contrib/openzfs/cmd/zed/zed_exec.c
@@ -196,37 +196,29 @@ _nop(int sig)
(void) sig;
}
-static void *
-_reap_children(void *arg)
+static void
+wait_for_children(boolean_t do_pause, boolean_t wait)
{
- (void) arg;
- struct launched_process_node node, *pnode;
pid_t pid;
- int status;
struct rusage usage;
- struct sigaction sa = {};
-
- (void) sigfillset(&sa.sa_mask);
- (void) sigdelset(&sa.sa_mask, SIGCHLD);
- (void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
-
- (void) sigemptyset(&sa.sa_mask);
- sa.sa_handler = _nop;
- sa.sa_flags = SA_NOCLDSTOP;
- (void) sigaction(SIGCHLD, &sa, NULL);
+ int status;
+ struct launched_process_node node, *pnode;
for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) {
(void) pthread_mutex_lock(&_launched_processes_lock);
- pid = wait4(0, &status, WNOHANG, &usage);
-
+ pid = wait4(0, &status, wait ? 0 : WNOHANG, &usage);
if (pid == 0 || pid == (pid_t)-1) {
(void) pthread_mutex_unlock(&_launched_processes_lock);
- if (pid == 0 || errno == ECHILD)
- pause();
- else if (errno != EINTR)
+ if ((pid == 0) || (errno == ECHILD)) {
+ if (do_pause)
+ pause();
+ } else if (errno != EINTR)
zed_log_msg(LOG_WARNING,
"Failed to wait for children: %s",
strerror(errno));
+ if (!do_pause)
+ return;
+
} else {
memset(&node, 0, sizeof (node));
node.pid = pid;
@@ -278,6 +270,25 @@ _reap_children(void *arg)
}
}
+}
+
+static void *
+_reap_children(void *arg)
+{
+ (void) arg;
+ struct sigaction sa = {};
+
+ (void) sigfillset(&sa.sa_mask);
+ (void) sigdelset(&sa.sa_mask, SIGCHLD);
+ (void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
+
+ (void) sigemptyset(&sa.sa_mask);
+ sa.sa_handler = _nop;
+ sa.sa_flags = SA_NOCLDSTOP;
+ (void) sigaction(SIGCHLD, &sa, NULL);
+
+ wait_for_children(B_TRUE, B_FALSE);
+
return (NULL);
}
@@ -307,6 +318,45 @@ zed_exec_fini(void)
}
/*
+ * Check if the zedlet name indicates if it is a synchronous zedlet
+ *
+ * Synchronous zedlets have a "-sync-" immediately following the event name in
+ * their zedlet filename, like:
+ *
+ * EVENT_NAME-sync-ZEDLETNAME.sh
+ *
+ * For example, if you wanted a synchronous statechange script:
+ *
+ * statechange-sync-myzedlet.sh
+ *
+ * Synchronous zedlets are guaranteed to be the only zedlet running. No other
+ * zedlets may run in parallel with a synchronous zedlet. A synchronous
+ * zedlet will wait for all previously spawned zedlets to finish before running.
+ * Users should be careful to only use synchronous zedlets when needed, since
+ * they decrease parallelism.
+ */
+static boolean_t
+zedlet_is_sync(const char *zedlet, const char *event)
+{
+ const char *sync_str = "-sync-";
+ size_t sync_str_len;
+ size_t zedlet_len;
+ size_t event_len;
+
+ sync_str_len = strlen(sync_str);
+ zedlet_len = strlen(zedlet);
+ event_len = strlen(event);
+
+ if (event_len + sync_str_len >= zedlet_len)
+ return (B_FALSE);
+
+ if (strncmp(&zedlet[event_len], sync_str, sync_str_len) == 0)
+ return (B_TRUE);
+
+ return (B_FALSE);
+}
+
+/*
* Process the event [eid] by synchronously invoking all zedlets with a
* matching class prefix.
*
@@ -368,9 +418,28 @@ zed_exec_process(uint64_t eid, const char *class, const char *subclass,
z = zed_strings_next(zcp->zedlets)) {
for (csp = class_strings; *csp; csp++) {
n = strlen(*csp);
- if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
+ if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n])) {
+ boolean_t is_sync = zedlet_is_sync(z, *csp);
+
+ if (is_sync) {
+ /*
+ * Wait for previous zedlets to
+ * finish
+ */
+ wait_for_children(B_FALSE, B_TRUE);
+ }
+
_zed_exec_fork_child(eid, zcp->zedlet_dir,
z, e, zcp->zevent_fd, zcp->do_foreground);
+
+ if (is_sync) {
+ /*
+ * Wait for sync zedlet we just launched
+ * to finish.
+ */
+ wait_for_children(B_FALSE, B_TRUE);
+ }
+ }
}
}
free(e);
diff --git a/sys/contrib/openzfs/cmd/zfs/zfs_main.c b/sys/contrib/openzfs/cmd/zfs/zfs_main.c
index 235f011af953..f7a627a2fee6 100644
--- a/sys/contrib/openzfs/cmd/zfs/zfs_main.c
+++ b/sys/contrib/openzfs/cmd/zfs/zfs_main.c
@@ -5303,6 +5303,7 @@ zfs_do_receive(int argc, char **argv)
#define ZFS_DELEG_PERM_MOUNT "mount"
#define ZFS_DELEG_PERM_SHARE "share"
#define ZFS_DELEG_PERM_SEND "send"
+#define ZFS_DELEG_PERM_SEND_RAW "send:raw"
#define ZFS_DELEG_PERM_RECEIVE "receive"
#define ZFS_DELEG_PERM_RECEIVE_APPEND "receive:append"
#define ZFS_DELEG_PERM_ALLOW "allow"
@@ -5345,6 +5346,7 @@ static zfs_deleg_perm_tab_t zfs_deleg_perm_tbl[] = {
{ ZFS_DELEG_PERM_RENAME, ZFS_DELEG_NOTE_RENAME },
{ ZFS_DELEG_PERM_ROLLBACK, ZFS_DELEG_NOTE_ROLLBACK },
{ ZFS_DELEG_PERM_SEND, ZFS_DELEG_NOTE_SEND },
+ { ZFS_DELEG_PERM_SEND_RAW, ZFS_DELEG_NOTE_SEND_RAW },
{ ZFS_DELEG_PERM_SHARE, ZFS_DELEG_NOTE_SHARE },
{ ZFS_DELEG_PERM_SNAPSHOT, ZFS_DELEG_NOTE_SNAPSHOT },
{ ZFS_DELEG_PERM_BOOKMARK, ZFS_DELEG_NOTE_BOOKMARK },
@@ -5929,6 +5931,10 @@ deleg_perm_comment(zfs_deleg_note_t note)
case ZFS_DELEG_NOTE_SEND:
str = gettext("");
break;
+ case ZFS_DELEG_NOTE_SEND_RAW:
+ str = gettext("Allow sending ONLY encrypted (raw) replication"
+ "\n\t\t\t\tstreams");
+ break;
case ZFS_DELEG_NOTE_SHARE:
str = gettext("Allows sharing file systems over NFS or SMB"
"\n\t\t\t\tprotocols");
diff --git a/sys/contrib/openzfs/cmd/zhack.c b/sys/contrib/openzfs/cmd/zhack.c
index 2bd3051dce7b..edf9dfa2cece 100644
--- a/sys/contrib/openzfs/cmd/zhack.c
+++ b/sys/contrib/openzfs/cmd/zhack.c
@@ -54,6 +54,7 @@
#include <sys/dmu_tx.h>
#include <zfeature_common.h>
#include <libzutil.h>
+#include <sys/metaslab_impl.h>
static importargs_t g_importargs;
static char *g_pool;
@@ -69,7 +70,8 @@ static __attribute__((noreturn)) void
usage(void)
{
(void) fprintf(stderr,
- "Usage: zhack [-c cachefile] [-d dir] <subcommand> <args> ...\n"
+ "Usage: zhack [-o tunable] [-c cachefile] [-d dir] <subcommand> "
+ "<args> ...\n"
"where <subcommand> <args> is one of the following:\n"
"\n");
@@ -93,7 +95,10 @@ usage(void)
" -c repair corrupted label checksums\n"
" -u restore the label on a detached device\n"
"\n"
- " <device> : path to vdev\n");
+ " <device> : path to vdev\n"
+ "\n"
+ " metaslab leak <pool>\n"
+ " apply allocation map from zdb to specified pool\n");
exit(1);
}
@@ -363,10 +368,12 @@ feature_incr_sync(void *arg, dmu_tx_t *tx)
zfeature_info_t *feature = arg;
uint64_t refcount;
+ mutex_enter(&spa->spa_feat_stats_lock);
VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
feature_sync(spa, feature, refcount + 1, tx);
spa_history_log_internal(spa, "zhack feature incr", tx,
"name=%s", feature->fi_guid);
+ mutex_exit(&spa->spa_feat_stats_lock);
}
static void
@@ -376,10 +383,12 @@ feature_decr_sync(void *arg, dmu_tx_t *tx)
zfeature_info_t *feature = arg;
uint64_t refcount;
+ mutex_enter(&spa->spa_feat_stats_lock);
VERIFY0(feature_get_refcount_from_disk(spa, feature, &refcount));
feature_sync(spa, feature, refcount - 1, tx);
spa_history_log_internal(spa, "zhack feature decr", tx,
"name=%s", feature->fi_guid);
+ mutex_exit(&spa->spa_feat_stats_lock);
}
static void
@@ -496,6 +505,186 @@ zhack_do_feature(int argc, char **argv)
return (0);
}
+static boolean_t
+strstarts(const char *a, const char *b)
+{
+ return (strncmp(a, b, strlen(b)) == 0);
+}
+
+static void
+metaslab_force_alloc(metaslab_t *msp, uint64_t start, uint64_t size,
+ dmu_tx_t *tx)
+{
+ ASSERT(msp->ms_disabled);
+ ASSERT(MUTEX_HELD(&msp->ms_lock));
+ uint64_t txg = dmu_tx_get_txg(tx);
+
+ uint64_t off = start;
+ while (off < start + size) {
+ uint64_t ostart, osize;
+ boolean_t found = zfs_range_tree_find_in(msp->ms_allocatable,
+ off, start + size - off, &ostart, &osize);
+ if (!found)
+ break;
+ zfs_range_tree_remove(msp->ms_allocatable, ostart, osize);
+
+ if (zfs_range_tree_is_empty(msp->ms_allocating[txg & TXG_MASK]))
+ vdev_dirty(msp->ms_group->mg_vd, VDD_METASLAB, msp,
+ txg);
+
+ zfs_range_tree_add(msp->ms_allocating[txg & TXG_MASK], ostart,
+ osize);
+ msp->ms_allocating_total += osize;
+ off = ostart + osize;
+ }
+}
+
+static void
+zhack_do_metaslab_leak(int argc, char **argv)
+{
+ int c;
+ char *target;
+ spa_t *spa;
+
+ optind = 1;
+ boolean_t force = B_FALSE;
+ while ((c = getopt(argc, argv, "f")) != -1) {
+ switch (c) {
+ case 'f':
+ force = B_TRUE;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ (void) fprintf(stderr, "error: missing pool name\n");
+ usage();
+ }
+ target = argv[0];
+
+ zhack_spa_open(target, B_FALSE, FTAG, &spa);
+ spa_config_enter(spa, SCL_VDEV | SCL_ALLOC, FTAG, RW_READER);
+
+ char *line = NULL;
+ size_t cap = 0;
+
+ vdev_t *vd = NULL;
+ metaslab_t *prev = NULL;
+ dmu_tx_t *tx = NULL;
+ while (getline(&line, &cap, stdin) > 0) {
+ if (strstarts(line, "\tvdev ")) {
+ uint64_t vdev_id, ms_shift;
+ if (sscanf(line,
+ "\tvdev %10"PRIu64"\t%*s metaslab shift %4"PRIu64,
+ &vdev_id, &ms_shift) == 1) {
+ VERIFY3U(sscanf(line, "\tvdev %"PRIu64
+ "\t metaslab shift %4"PRIu64,
+ &vdev_id, &ms_shift), ==, 2);
+ }
+ vd = vdev_lookup_top(spa, vdev_id);
+ if (vd == NULL) {
+ fprintf(stderr, "error: no such vdev with "
+ "id %"PRIu64"\n", vdev_id);
+ break;
+ }
+ if (tx) {
+ dmu_tx_commit(tx);
+ mutex_exit(&prev->ms_lock);
+ metaslab_enable(prev, B_FALSE, B_FALSE);
+ tx = NULL;
+ prev = NULL;
+ }
+ if (vd->vdev_ms_shift != ms_shift) {
+ fprintf(stderr, "error: ms_shift mismatch: %"
+ PRIu64" != %"PRIu64"\n", vd->vdev_ms_shift,
+ ms_shift);
+ break;
+ }
+ } else if (strstarts(line, "\tmetaslabs ")) {
+ uint64_t ms_count;
+ VERIFY3U(sscanf(line, "\tmetaslabs %"PRIu64, &ms_count),
+ ==, 1);
+ ASSERT(vd);
+ if (!force && vd->vdev_ms_count != ms_count) {
+ fprintf(stderr, "error: ms_count mismatch: %"
+ PRIu64" != %"PRIu64"\n", vd->vdev_ms_count,
+ ms_count);
+ break;
+ }
+ } else if (strstarts(line, "ALLOC:")) {
+ uint64_t start, size;
+ VERIFY3U(sscanf(line, "ALLOC: %"PRIu64" %"PRIu64"\n",
+ &start, &size), ==, 2);
+
+ ASSERT(vd);
+ metaslab_t *cur =
+ vd->vdev_ms[start >> vd->vdev_ms_shift];
+ if (prev != cur) {
+ if (prev) {
+ dmu_tx_commit(tx);
+ mutex_exit(&prev->ms_lock);
+ metaslab_enable(prev, B_FALSE, B_FALSE);
+ }
+ ASSERT(cur);
+ metaslab_disable(cur);
+ mutex_enter(&cur->ms_lock);
+ metaslab_load(cur);
+ prev = cur;
+ tx = dmu_tx_create_dd(
+ spa_get_dsl(vd->vdev_spa)->dp_root_dir);
+ dmu_tx_assign(tx, DMU_TX_WAIT);
+ }
+
+ metaslab_force_alloc(cur, start, size, tx);
+ } else {
+ continue;
+ }
+ }
+ if (tx) {
+ dmu_tx_commit(tx);
+ mutex_exit(&prev->ms_lock);
+ metaslab_enable(prev, B_FALSE, B_FALSE);
+ tx = NULL;
+ prev = NULL;
+ }
+ if (line)
+ free(line);
+
+ spa_config_exit(spa, SCL_VDEV | SCL_ALLOC, FTAG);
+ spa_close(spa, FTAG);
+}
+
+static int
+zhack_do_metaslab(int argc, char **argv)
+{
+ char *subcommand;
+
+ argc--;
+ argv++;
+ if (argc == 0) {
+ (void) fprintf(stderr,
+ "error: no metaslab operation specified\n");
+ usage();
+ }
+
+ subcommand = argv[0];
+ if (strcmp(subcommand, "leak") == 0) {
+ zhack_do_metaslab_leak(argc, argv);
+ } else {
+ (void) fprintf(stderr, "error: unknown subcommand: %s\n",
+ subcommand);
+ usage();
+ }
+
+ return (0);
+}
+
#define ASHIFT_UBERBLOCK_SHIFT(ashift) \
MIN(MAX(ashift, UBERBLOCK_SHIFT), \
MAX_UBERBLOCK_SHIFT)
@@ -981,7 +1170,7 @@ main(int argc, char **argv)
dprintf_setup(&argc, argv);
zfs_prop_init();
- while ((c = getopt(argc, argv, "+c:d:")) != -1) {
+ while ((c = getopt(argc, argv, "+c:d:o:")) != -1) {
switch (c) {
case 'c':
g_importargs.cachefile = optarg;
@@ -990,6 +1179,10 @@ main(int argc, char **argv)
assert(g_importargs.paths < MAX_NUM_PATHS);
g_importargs.path[g_importargs.paths++] = optarg;
break;
+ case 'o':
+ if (handle_tunable_option(optarg, B_FALSE) != 0)
+ exit(1);
+ break;
default:
usage();
break;
@@ -1011,6 +1204,8 @@ main(int argc, char **argv)
rv = zhack_do_feature(argc, argv);
} else if (strcmp(subcommand, "label") == 0) {
return (zhack_do_label(argc, argv));
+ } else if (strcmp(subcommand, "metaslab") == 0) {
+ rv = zhack_do_metaslab(argc, argv);
} else {
(void) fprintf(stderr, "error: unknown subcommand: %s\n",
subcommand);
diff --git a/sys/contrib/openzfs/cmd/zpool/Makefile.am b/sys/contrib/openzfs/cmd/zpool/Makefile.am
index 2f962408e5a3..5bb6d8160b18 100644
--- a/sys/contrib/openzfs/cmd/zpool/Makefile.am
+++ b/sys/contrib/openzfs/cmd/zpool/Makefile.am
@@ -148,6 +148,7 @@ dist_zpoolcompat_DATA = \
%D%/compatibility.d/openzfs-2.1-linux \
%D%/compatibility.d/openzfs-2.2 \
%D%/compatibility.d/openzfs-2.3 \
+ %D%/compatibility.d/openzfs-2.4 \
%D%/compatibility.d/openzfsonosx-1.7.0 \
%D%/compatibility.d/openzfsonosx-1.8.1 \
%D%/compatibility.d/openzfsonosx-1.9.3 \
@@ -187,7 +188,9 @@ zpoolcompatlinks = \
"openzfs-2.2 openzfs-2.2-linux" \
"openzfs-2.2 openzfs-2.2-freebsd" \
"openzfs-2.3 openzfs-2.3-linux" \
- "openzfs-2.3 openzfs-2.3-freebsd"
+ "openzfs-2.3 openzfs-2.3-freebsd" \
+ "openzfs-2.4 openzfs-2.4-linux" \
+ "openzfs-2.4 openzfs-2.4-freebsd"
zpoolconfdir = $(sysconfdir)/zfs/zpool.d
INSTALL_DATA_HOOKS += zpool-install-data-hook
diff --git a/sys/contrib/openzfs/cmd/zpool/compatibility.d/openzfs-2.4 b/sys/contrib/openzfs/cmd/zpool/compatibility.d/openzfs-2.4
new file mode 100644
index 000000000000..3fbd91014c95
--- /dev/null
+++ b/sys/contrib/openzfs/cmd/zpool/compatibility.d/openzfs-2.4
@@ -0,0 +1,48 @@
+# Features supported by OpenZFS 2.4 on Linux and FreeBSD
+allocation_classes
+async_destroy
+blake3
+block_cloning
+block_cloning_endian
+bookmark_v2
+bookmark_written
+bookmarks
+device_rebuild
+device_removal
+draid
+dynamic_gang_header
+edonr
+embedded_data
+empty_bpobj
+enabled_txg
+encryption
+extensible_dataset
+fast_dedup
+filesystem_limits
+head_errlog
+hole_birth
+large_blocks
+large_dnode
+large_microzap
+livelist
+log_spacemap
+longname
+lz4_compress
+multi_vdev_crash_dump
+obsolete_counts
+physical_rewrite
+project_quota
+raidz_expansion
+redacted_datasets
+redaction_bookmarks
+redaction_list_spill
+resilver_defer
+sha512
+skein
+spacemap_histogram
+spacemap_v2
+userobj_accounting
+vdev_zaps_v2
+zilsaxattr
+zpool_checkpoint
+zstd_compress
diff --git a/sys/contrib/openzfs/cmd/zstream/Makefile.am b/sys/contrib/openzfs/cmd/zstream/Makefile.am
index be3539fe905d..80ef1ea7ca11 100644
--- a/sys/contrib/openzfs/cmd/zstream/Makefile.am
+++ b/sys/contrib/openzfs/cmd/zstream/Makefile.am
@@ -18,6 +18,7 @@ zstream_LDADD = \
libzpool.la \
libnvpair.la
-PHONY += install-exec-hook
-install-exec-hook:
+cmd-zstream-install-exec-hook:
cd $(DESTDIR)$(sbindir) && $(LN_S) -f zstream zstreamdump
+
+INSTALL_EXEC_HOOKS += cmd-zstream-install-exec-hook