Index: Makefile.inc1 =================================================================== --- Makefile.inc1 +++ Makefile.inc1 @@ -2529,7 +2529,7 @@ lib/libufs \ lib/libutil lib/libpjdlog ${_lib_libypclnt} lib/libz lib/msun \ ${_secure_lib_libcrypto} ${_lib_libldns} \ - ${_secure_lib_libssh} ${_secure_lib_libssl} + ${_secure_lib_libssh} ${_secure_lib_libssl} lib/libzstd .if ${MK_GNUCXX} != "no" _prebuild_libs+= gnu/lib/libstdc++ gnu/lib/libsupc++ Index: cddl/contrib/opensolaris/cmd/zpool/zpool-features.7 =================================================================== --- cddl/contrib/opensolaris/cmd/zpool/zpool-features.7 +++ cddl/contrib/opensolaris/cmd/zpool/zpool-features.7 @@ -590,6 +590,52 @@ once all filesystems that have ever had their checksum set to .Sy skein are destroyed. +.It Sy zstd_compress +.Bl -column "READ\-ONLY COMPATIBLE" "org.freebsd:zstd_compress" +.It GUID Ta org.freebsd:zstd_compress +.It READ\-ONLY COMPATIBLE Ta no +.It DEPENDENCIES Ta none +.El +.Pp +.Sy zstd +is a high-performance compression algorithm that features a combination +of high compression ratios and high speed. +Compared to +.Sy gzip , +.Sy zstd +offers slighty better compression at much higher speeds. +Compared to +.Sy lz4 , +.Sy zstd +offers much better compression while being only modestly slower. +Typically, +.Sy zstd +compression speed is approximately 250 megabytes/second/thread, and +decompression speed is 1 gigabyte/second/thread. +Booting off of +.Sy zstd Ns +-compressed root pools is supported. +.Pp +When the +.Sy zstd +feature is set to +.Sy enabled , +the administrator can turn on +.Sy zstd +compression of any dataset using the +.Dl # zfs set compress=zstd Ar dataset +command. +This feature becomes +.Sy active +once a +.Sy compress +property has been set to +.Sy zstd , +and will return to being +.Sy enabled +once all filesystems that have ever had their compress property set to +.Sy zstd +are destroyed. .El .Sh SEE ALSO .Xr zpool 8 Index: cddl/lib/libzfs/Makefile =================================================================== --- cddl/lib/libzfs/Makefile +++ cddl/lib/libzfs/Makefile @@ -7,7 +7,7 @@ .PATH: ${SRCTOP}/cddl/contrib/opensolaris/lib/libcmdutils/common LIB= zfs -LIBADD= md pthread umem util uutil m avl bsdxml geom nvpair z zfs_core +LIBADD= md pthread umem util uutil m avl bsdxml geom nvpair z zfs_core zstd SRCS= deviceid.c \ fsshare.c \ mkdirp.c \ Index: cddl/lib/libzpool/Makefile =================================================================== --- cddl/lib/libzpool/Makefile +++ cddl/lib/libzpool/Makefile @@ -62,8 +62,10 @@ CFLAGS+= -I${SRCTOP}/lib/libpthread/thread CFLAGS+= -I${SRCTOP}/lib/libpthread/sys CFLAGS+= -I${SRCTOP}/lib/libthr/arch/${MACHINE_CPUARCH}/include +CFLAGS+= -I${SRCTOP}/sys/contrib/zstd/lib +CFLAGS+= -I${SRCTOP}/sys/contrib/zstd/lib/common -LIBADD= md pthread z nvpair avl umem +LIBADD= md pthread z nvpair avl umem zstd # atomic.S doesn't like profiling. MK_PROFILE= no Index: rescue/rescue/Makefile =================================================================== --- rescue/rescue/Makefile +++ rescue/rescue/Makefile @@ -126,7 +126,7 @@ CRUNCH_LIBS+= -l80211 -lalias -lcam -lncursesw -ldevstat -lipsec -llzma .if ${MK_ZFS} != "no" -CRUNCH_LIBS+= -lavl -lzpool -lzfs_core -lzfs -lnvpair -lpthread -luutil -lumem +CRUNCH_LIBS+= -lavl -lzpool -lzfs_core -lzfs -lnvpair -lpthread -luutil -lumem -lprivatezstd .else # liblzma needs pthread CRUNCH_LIBS+= -lpthread Index: share/mk/src.libnames.mk =================================================================== --- share/mk/src.libnames.mk +++ share/mk/src.libnames.mk @@ -332,9 +332,9 @@ _DP_fifolog= z _DP_ipf= kvm _DP_zfs= md pthread umem util uutil m nvpair avl bsdxml geom nvpair z \ - zfs_core + zfs_core zstd _DP_zfs_core= nvpair -_DP_zpool= md pthread z nvpair avl umem +_DP_zpool= md pthread z nvpair avl umem zstd # OFED support .if ${MK_OFED} != "no" Index: stand/efi/boot1/Makefile =================================================================== --- stand/efi/boot1/Makefile +++ stand/efi/boot1/Makefile @@ -29,6 +29,7 @@ CFLAGS.zfs_module.c+= -I${ZFSSRC} CFLAGS.zfs_module.c+= -I${SYSDIR}/cddl/boot/zfs CFLAGS.zfs_module.c+= -I${SYSDIR}/crypto/skein +CFLAGS+= -I${SYSDIR}/contrib/zstd/lib CFLAGS+= -DEFI_ZFS_BOOT .endif Index: stand/efi/boot1/boot1.c =================================================================== --- stand/efi/boot1/boot1.c +++ stand/efi/boot1/boot1.c @@ -71,6 +71,18 @@ return (NULL); } +void * +Calloc(size_t number, size_t len, const char *file __unused, int line __unused) +{ + uintptr_t bytes = (uintptr_t)number * (uintptr_t)len; + void *out; + + if ((out = Malloc(bytes, file, line)) != NULL) + bzero(out, bytes); + + return(out); +} + void Free(void *buf, const char *file __unused, int line __unused) { Index: stand/i386/gptzfsboot/Makefile =================================================================== --- stand/i386/gptzfsboot/Makefile +++ stand/i386/gptzfsboot/Makefile @@ -30,6 +30,7 @@ -I${ZFSSRC} \ -I${SYSDIR}/crypto/skein \ -I${SYSDIR}/cddl/boot/zfs \ + -I${SYSDIR}/contrib/zstd/lib \ -I${BOOTSRC}/i386/btx/lib \ -I${BOOTSRC}/i386/boot2 \ -Wall -Waggregate-return -Wbad-function-cast \ Index: stand/i386/zfsboot/Makefile =================================================================== --- stand/i386/zfsboot/Makefile +++ stand/i386/zfsboot/Makefile @@ -29,6 +29,7 @@ -I${ZFSSRC} \ -I${SYSDIR}/crypto/skein \ -I${SYSDIR}/cddl/boot/zfs \ + -I${SYSDIR}/contrib/zstd/lib \ -I${BOOTSRC}/i386/boot2 \ -Wall -Waggregate-return -Wbad-function-cast -Wno-cast-align \ -Wmissing-declarations -Wmissing-prototypes -Wnested-externs \ Index: stand/libsa/Makefile =================================================================== --- stand/libsa/Makefile +++ stand/libsa/Makefile @@ -85,6 +85,31 @@ .PATH: ${SASRC}/${LIBSA_CPUARCH} SRCS+= _setjmp.S +# decompression functionality from libzstd +.PATH: ${SRCTOP}/sys/contrib/zstd/lib +CFLAGS+= -I${SRCTOP}/sys/contrib/zstd/lib +.PATH: ${SRCTOP}/sys/contrib/zstd/lib/common +CFLAGS+= -I${SRCTOP}/sys/contrib/zstd/lib/common +.PATH: ${SRCTOP}/sys/contrib/zstd/lib/compress +CFLAGS+= -I${SRCTOP}/sys/contrib/zstd/lib/compress +.PATH: ${SRCTOP}/sys/contrib/zstd/lib/decompress +CFLAGS+= -I${SRCTOP}/sys/contrib/zstd/lib/decompress +.for file in zstd_common.c fse_decompress.c entropy_common.c error_private.c \ + xxhash.c zstd_compress.c fse_compress.c huf_compress.c \ + zstd_double_fast.c zstd_fast.c zstd_lazy.c zstd_ldm.c zstd_opt.c \ + zstd_decompress.c huf_decompress.c +SRCS+= _${file} +CLEANFILES+= _${file} + +_${file}: ${file} + sed -e 's||"stand.h"|' \ + -e 's||"stand.h"|' \ + -e 's||"stand.h"|' \ + -e 's||"stand.h"|' \ + -e 's||"stand.h"|' \ + ${.ALLSRC} > ${.TARGET} +.endfor + # decompression functionality from libbz2 # NOTE: to actually test this functionality after libbz2 upgrade compile # loader(8) with LOADER_BZIP2_SUPPORT defined Index: stand/libsa/zfs/zfsimpl.c =================================================================== --- stand/libsa/zfs/zfsimpl.c +++ stand/libsa/zfs/zfsimpl.c @@ -62,6 +62,7 @@ "org.illumos:sha512", "org.illumos:skein", "org.zfsonlinux:large_dnode", + "org.freebsd:zstd_compress", NULL }; Index: sys/cddl/boot/zfs/zfsimpl.h =================================================================== --- sys/cddl/boot/zfs/zfsimpl.h +++ sys/cddl/boot/zfs/zfsimpl.h @@ -561,6 +561,7 @@ ZIO_COMPRESS_GZIP_9, ZIO_COMPRESS_ZLE, ZIO_COMPRESS_LZ4, + ZIO_COMPRESS_ZSTD, ZIO_COMPRESS_FUNCTIONS }; Index: sys/cddl/boot/zfs/zfssubr.c =================================================================== --- sys/cddl/boot/zfs/zfssubr.c +++ sys/cddl/boot/zfs/zfssubr.c @@ -162,6 +162,7 @@ #include "lzjb.c" #include "zle.c" #include "lz4.c" +#include "zstd.c" /* * Compression vectors. @@ -183,6 +184,7 @@ {NULL, NULL, 9, "gzip-9"}, {NULL, zle_decompress, 64, "zle"}, {NULL, lz4_decompress, 0, "lz4"}, + {NULL, zstd_decompress, 0, "zstd"}, }; static void Index: sys/cddl/boot/zfs/zstd.c =================================================================== --- /dev/null +++ sys/cddl/boot/zfs/zstd.c @@ -0,0 +1,83 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright (c) 2016-2018 by Klara Systems Inc. + * Copyright (c) 2016-2018 Allan Jude . + */ + +#include + +#define ZSTD_STATIC_LINKING_ONLY +#include + +/* Allocate just one decompression context and reuse it */ +static char *dctxbuf = NULL; +static size_t dctxsize; + +static int +zstd_decompress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n) +{ + const char *src = s_start; + uint32_t bufsiz = htonl(*(uint32_t *)src); + uint32_t cookie = htonl(*(uint32_t *)(&src[sizeof (bufsiz)])); + size_t result; + + ASSERT(d_len >= s_len); + + /* invalid compressed buffer size encoded at start */ + if (bufsiz + sizeof (bufsiz) + sizeof (cookie) > s_len) { + printf("ZFS: Failed to decode ZSTD decompression header\n"); + return (1); + } + + /* + * Returns 0 on success (decompression function returned non-negative) + * and non-zero on failure (decompression function returned negative. + */ + ZSTD_DCtx *dctx; + + if (dctxbuf == NULL) { + dctxsize = ZSTD_estimateDCtxSize(); + dctxbuf = malloc(dctxsize + 8); + if (dctxbuf == NULL) { + printf("ZFS: memory allocation failure\n"); + return (1); + } + /* Pointer must be 8 byte aligned */ + dctxbuf = (char *)roundup2((uintptr_t)dctxbuf, 8); + } + dctx = ZSTD_initStaticDCtx(dctxbuf, dctxsize); + if (dctx == NULL) { + printf("ZFS: failed to initialize ZSTD decompress context\n"); + return (1); + } + + result = ZSTD_decompressDCtx(dctx, d_start, d_len, + &src[sizeof (bufsiz) + sizeof (cookie)], bufsiz); + if (ZSTD_isError(result)) { + printf("ZFS: Failed to decompress block: %s\n", + ZSTD_getErrorName(result)); + return (1); + } + + return (0); +} Index: sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.h =================================================================== --- sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.h +++ sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.h @@ -60,6 +60,7 @@ SPA_FEATURE_DEVICE_REMOVAL, SPA_FEATURE_OBSOLETE_COUNTS, SPA_FEATURE_POOL_CHECKPOINT, + SPA_FEATURE_ZSTD_COMPRESS, SPA_FEATURES } spa_feature_t; Index: sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.c =================================================================== --- sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.c +++ sys/cddl/contrib/opensolaris/common/zfs/zfeature_common.c @@ -268,4 +268,9 @@ "Reduce memory used by removed devices when their blocks are " "freed or remapped.", ZFEATURE_FLAG_READONLY_COMPAT, obsolete_counts_deps); + + zfeature_register(SPA_FEATURE_ZSTD_COMPRESS, + "org.freebsd:zstd_compress", "zstd_compress", + "zstd compression algorithm support.", + ZFEATURE_FLAG_PER_DATASET, NULL); } Index: sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c =================================================================== --- sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c +++ sys/cddl/contrib/opensolaris/common/zfs/zfs_prop.c @@ -116,6 +116,97 @@ { "gzip-9", ZIO_COMPRESS_GZIP_9 }, { "zle", ZIO_COMPRESS_ZLE }, { "lz4", ZIO_COMPRESS_LZ4 }, + { "zstd", ZIO_COMPRESS_ZSTD }, + /* + * ZSTD 1-19 are synthetic. We store the compression level in a + * separate hidden property to avoid wasting a large amount of + * space in the ZIO_COMPRESS enum. We do not need to know the + * compression level at decompress time, so it does not need + * to be stored on disk in the block pointer. + */ + { "zstd-1", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_1 << SPA_COMPRESSBITS) }, + { "zstd-2", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_2 << SPA_COMPRESSBITS) }, + { "zstd-3", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_3 << SPA_COMPRESSBITS) }, + { "zstd-4", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_4 << SPA_COMPRESSBITS) }, + { "zstd-5", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_5 << SPA_COMPRESSBITS) }, + { "zstd-6", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_6 << SPA_COMPRESSBITS) }, + { "zstd-7", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_7 << SPA_COMPRESSBITS) }, + { "zstd-8", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_8 << SPA_COMPRESSBITS) }, + { "zstd-9", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_9 << SPA_COMPRESSBITS) }, + { "zstd-10", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_10 << SPA_COMPRESSBITS) }, + { "zstd-11", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_11 << SPA_COMPRESSBITS) }, + { "zstd-12", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_12 << SPA_COMPRESSBITS) }, + { "zstd-13", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_13 << SPA_COMPRESSBITS) }, + { "zstd-14", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_14 << SPA_COMPRESSBITS) }, + { "zstd-15", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_15 << SPA_COMPRESSBITS) }, + { "zstd-16", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_16 << SPA_COMPRESSBITS) }, + { "zstd-17", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_17 << SPA_COMPRESSBITS) }, + { "zstd-18", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_18 << SPA_COMPRESSBITS) }, + { "zstd-19", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_19 << SPA_COMPRESSBITS) }, + /* + * The ZSTD-Fast levels are also synthetic. + */ + { "zstd-fast-1", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_1 << SPA_COMPRESSBITS) }, + { "zstd-fast-2", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_2 << SPA_COMPRESSBITS) }, + { "zstd-fast-3", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_3 << SPA_COMPRESSBITS) }, + { "zstd-fast-4", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_4 << SPA_COMPRESSBITS) }, + { "zstd-fast-5", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_5 << SPA_COMPRESSBITS) }, + { "zstd-fast-6", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_6 << SPA_COMPRESSBITS) }, + { "zstd-fast-7", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_7 << SPA_COMPRESSBITS) }, + { "zstd-fast-8", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_8 << SPA_COMPRESSBITS) }, + { "zstd-fast-9", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_9 << SPA_COMPRESSBITS) }, + { "zstd-fast-10", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_10 << SPA_COMPRESSBITS) }, + { "zstd-fast-20", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_20 << SPA_COMPRESSBITS) }, + { "zstd-fast-30", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_30 << SPA_COMPRESSBITS) }, + { "zstd-fast-40", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_40 << SPA_COMPRESSBITS) }, + { "zstd-fast-50", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_50 << SPA_COMPRESSBITS) }, + { "zstd-fast-60", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_60 << SPA_COMPRESSBITS) }, + { "zstd-fast-70", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_70 << SPA_COMPRESSBITS) }, + { "zstd-fast-80", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_80 << SPA_COMPRESSBITS) }, + { "zstd-fast-90", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_90 << SPA_COMPRESSBITS) }, + { "zstd-fast-100", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_100 << SPA_COMPRESSBITS) }, + { "zstd-fast-500", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_500 << SPA_COMPRESSBITS) }, + { "zstd-fast-1000", ZIO_COMPRESS_ZSTD | + (ZIO_ZSTDLVL_FAST_1000 << SPA_COMPRESSBITS) }, { NULL } }; @@ -250,7 +341,9 @@ zprop_register_index(ZFS_PROP_COMPRESSION, "compression", ZIO_COMPRESS_DEFAULT, PROP_INHERIT, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, - "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4", + "on | off | lzjb | gzip | gzip-[1-9] | zle | lz4 | " + "zstd | zstd-[1-19] | " + "zstd-fast-[1-10,20,30,40,50,60,70,80,90,100,500,1000]", "COMPRESS", compress_table); zprop_register_index(ZFS_PROP_SNAPDIR, "snapdir", ZFS_SNAPDIR_HIDDEN, PROP_INHERIT, ZFS_TYPE_FILESYSTEM, @@ -462,6 +555,10 @@ PROP_READONLY, ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME, "PREVSNAP"); /* oddball properties */ + zprop_register_impl(ZFS_PROP_ZSTD_LEVEL, "zstd_compress_level", + PROP_TYPE_NUMBER, ZIO_ZSTDLVL_DEFAULT, NULL, PROP_INHERIT, + ZFS_TYPE_DATASET, "", "ZSTDLEVEL", B_TRUE, + B_FALSE, NULL); zprop_register_impl(ZFS_PROP_CREATION, "creation", PROP_TYPE_NUMBER, 0, NULL, PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK, "", "CREATION", B_FALSE, B_TRUE, NULL); Index: sys/cddl/contrib/opensolaris/uts/common/Makefile.files =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/Makefile.files +++ sys/cddl/contrib/opensolaris/uts/common/Makefile.files @@ -152,6 +152,7 @@ zio_inject.o \ zle.o \ zrlock.o \ + zstd.o \ zthr.o ZFS_SHARED_OBJS += \ Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/arc.c @@ -1077,6 +1077,7 @@ arc_buf_contents_t b_type; arc_buf_hdr_t *b_hash_next; arc_flags_t b_flags; + uint8_t b_complevel; /* * This field stores the size of the data buffer after @@ -1913,11 +1914,23 @@ uint64_t lsize = HDR_GET_LSIZE(hdr); uint64_t csize; - abd_t *cdata = abd_alloc_linear(HDR_GET_PSIZE(hdr), B_TRUE); + abd_t *cdata = abd_alloc_linear(lsize, B_TRUE); csize = zio_compress_data(compress, zio->io_abd, - abd_to_buf(cdata), lsize); + abd_to_buf(cdata), lsize, &zio->io_prop); - ASSERT3U(csize, <=, HDR_GET_PSIZE(hdr)); + if (csize == 0 || csize == lsize) { + /* Compression has failed */ + /* XXX: Should we retry here? */ + return (B_FALSE); + } + + if (csize > HDR_GET_PSIZE(hdr)) { + /* + * Compression produced a larger output, the checksum + * cannot possibly match. + */ + return (B_FALSE); + } if (csize < HDR_GET_PSIZE(hdr)) { /* * Compressed blocks are always a multiple of the @@ -5218,6 +5231,9 @@ } else { hdr->b_l1hdr.b_byteswap = DMU_BSWAP_NUMFUNCS; } + if (BP_GET_COMPRESS(zio->io_bp) == ZIO_COMPRESS_ZSTD) { + hdr->b_complevel = zio->io_prop.zp_zstd_level; + } } arc_hdr_clear_flags(hdr, ARC_FLAG_L2_EVICTED); @@ -6235,6 +6251,9 @@ * the pre-compressed buffer's compression algorithm. */ localprop.zp_compress = HDR_GET_COMPRESS(hdr); + if (localprop.zp_compress == ZIO_COMPRESS_ZSTD) { + localprop.zp_zstd_level = hdr->b_complevel; + } ASSERT3U(HDR_GET_LSIZE(hdr), !=, arc_buf_size(buf)); zio_flags |= ZIO_FLAG_RAW; @@ -7349,6 +7368,9 @@ zio->io_bp_copy = cb->l2rcb_bp; /* XXX fix in L2ARC 2.0 */ zio->io_bp = &zio->io_bp_copy; /* XXX fix in L2ARC 2.0 */ + if (BP_GET_COMPRESS(zio->io_bp) == ZIO_COMPRESS_ZSTD) { + zio->io_prop.zp_zstd_level = hdr->b_complevel; + } valid_cksum = arc_cksum_is_equal(hdr, zio); if (valid_cksum && zio->io_error == 0 && !HDR_L2_EVICTED(hdr)) { mutex_exit(hash_lock); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu.c @@ -2345,6 +2345,7 @@ (wp & WP_SPILL)); enum zio_checksum checksum = os->os_checksum; enum zio_compress compress = os->os_compress; + enum zio_zstd_levels zstd_level = os->os_zstd_level; enum zio_checksum dedup_checksum = os->os_dedup_checksum; boolean_t dedup = B_FALSE; boolean_t nopwrite = B_FALSE; @@ -2439,6 +2440,8 @@ zp->zp_checksum = checksum; zp->zp_compress = compress; ASSERT3U(zp->zp_compress, !=, ZIO_COMPRESS_INHERIT); + zp->zp_zstd_level = zstd_level; + ASSERT(zp->zp_zstd_level != ZIO_ZSTDLVL_INHERIT); zp->zp_type = (wp & WP_SPILL) ? dn->dn_bonustype : type; zp->zp_level = level; Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_objset.c @@ -179,6 +179,15 @@ } static void +zstd_level_changed_cb(void *arg, uint64_t newval) +{ + objset_t *os = arg; + + os->os_zstd_level = zio_zstd_level_select(os->os_spa, newval, + ZIO_ZSTDLVL_DEFAULT); +} + +static void copies_changed_cb(void *arg, uint64_t newval) { objset_t *os = arg; @@ -449,6 +458,11 @@ } if (err == 0) { err = dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_ZSTD_LEVEL), + zstd_level_changed_cb, os); + } + if (err == 0) { + err = dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_COPIES), copies_changed_cb, os); } @@ -490,6 +504,7 @@ /* It's the meta-objset. */ os->os_checksum = ZIO_CHECKSUM_FLETCHER_4; os->os_compress = ZIO_COMPRESS_ON; + os->os_zstd_level = ZIO_ZSTDLVL_DEFAULT; os->os_copies = spa_max_replication(spa); os->os_dedup_checksum = ZIO_CHECKSUM_OFF; os->os_dedup_verify = B_FALSE; Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dmu_send.c @@ -835,6 +835,9 @@ if (compressok) { featureflags |= DMU_BACKUP_FEATURE_COMPRESSED; } + if ((compressok || embedok) && + to_ds->ds_feature_inuse[SPA_FEATURE_ZSTD_COMPRESS]) + featureflags |= DMU_BACKUP_FEATURE_ZSTD; if ((featureflags & (DMU_BACKUP_FEATURE_EMBED_DATA | DMU_BACKUP_FEATURE_COMPRESSED)) != 0 && spa_feature_is_active(dp->dp_spa, SPA_FEATURE_LZ4_COMPRESS)) { @@ -1431,6 +1434,13 @@ */ if ((featureflags & DMU_BACKUP_FEATURE_LARGE_BLOCKS) && !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_LARGE_BLOCKS)) + return (SET_ERROR(ENOTSUP)); + + if ((featureflags & DMU_BACKUP_FEATURE_ZSTD) && + !spa_feature_is_enabled(dp->dp_spa, SPA_FEATURE_ZSTD_COMPRESS)) + return (SET_ERROR(ENOTSUP)); + + if (!(DMU_STREAM_SUPPORTED(featureflags))) return (SET_ERROR(ENOTSUP)); error = dsl_dataset_hold(dp, tofs, FTAG, &ds); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/dsl_dataset.c @@ -128,6 +128,7 @@ int compressed = BP_GET_PSIZE(bp); int uncompressed = BP_GET_UCSIZE(bp); int64_t delta; + spa_feature_t f; dprintf_bp(bp, "ds=%p", ds); @@ -157,7 +158,11 @@ B_TRUE; } - spa_feature_t f = zio_checksum_to_feature(BP_GET_CHECKSUM(bp)); + f = zio_checksum_to_feature(BP_GET_CHECKSUM(bp)); + if (f != SPA_FEATURE_NONE) + ds->ds_feature_activation_needed[f] = B_TRUE; + + f = zio_compress_to_feature(BP_GET_COMPRESS(bp)); if (f != SPA_FEATURE_NONE) ds->ds_feature_activation_needed[f] = B_TRUE; Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/spa_misc.c @@ -2086,6 +2086,7 @@ metaslab_alloc_trace_init(); zio_init(); lz4_init(); + zstd_init(); dmu_init(); zil_init(); vdev_cache_stat_init(); @@ -2116,6 +2117,7 @@ zil_fini(); dmu_fini(); lz4_fini(); + zstd_fini(); zio_fini(); metaslab_alloc_trace_fini(); range_tree_fini(); Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/dmu_objset.h @@ -91,6 +91,7 @@ /* can change, under dsl_dir's locks: */ enum zio_checksum os_checksum; enum zio_compress os_compress; + enum zio_zstd_levels os_zstd_level; uint8_t os_copies; enum zio_checksum os_dedup_checksum; boolean_t os_dedup_verify; Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h =================================================================== --- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h +++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/spa.h @@ -154,6 +154,7 @@ #define SPA_ASIZEBITS 24 /* ASIZE up to 64 times larger */ #define SPA_COMPRESSBITS 7 +#define SPA_COMPRESSMASK ((1U<