Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142529889
D11124.id43730.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
49 KB
Referenced Files
None
Subscribers
None
D11124.id43730.diff
View Options
Index: Makefile.inc1
===================================================================
--- Makefile.inc1
+++ Makefile.inc1
@@ -2428,7 +2428,7 @@
${_cddl_lib_libctf} \
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
LIBZFSBOOT= ${BOOTOBJ}/zfs/libzfsboot.a
.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
@@ -32,6 +32,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
@@ -31,6 +31,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
@@ -81,6 +81,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|<stddef.h>|"stand.h"|' \
+ -e 's|<string.h>|"stand.h"|' \
+ -e 's|<stdio.h>|"stand.h"|' \
+ -e 's|<stdlib.h>|"stand.h"|' \
+ -e 's|<stdint.h>|"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/zfs/Makefile
===================================================================
--- stand/zfs/Makefile
+++ stand/zfs/Makefile
@@ -13,6 +13,7 @@
CFLAGS+= -DBOOTPROG=\"zfsloader\"
CFLAGS+= -I${LDRSRC}
CFLAGS+= -I${SYSDIR}/cddl/boot/zfs
+CFLAGS+= -I${SYSDIR}/contrib/zstd/lib
CFLAGS+= -I${SYSDIR}/crypto/skein
CFLAGS+= -Wformat -Wall
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,88 @@
+/*
+ * 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-2017 by ScaleEngine Inc. All rights reserved.
+ * Copyright (c) 2017 Allan Jude <allanjude@freebsd.org>. All rights reserved.
+ */
+
+#include <netinet/in.h>
+
+#define ZSTD_STATIC_LINKING_ONLY
+#include <zstd.h>
+
+#define ZSTD_COOKIE_SHIFT 26
+#define ZSTD_COOKIE_MASK ((1U<<ZSTD_COOKIE_SHIFT)-1)
+
+/* 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 cookie = htonl(*(uint32_t *)src);
+ uint32_t bufsiz = cookie & ZSTD_COOKIE_MASK;
+ size_t result;
+
+ ASSERT(d_len >= s_len);
+
+ /* invalid compressed buffer size encoded at start */
+ if (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);
+ }
+printf("dctxbuf=%p ", dctxbuf);
+ dctx = ZSTD_initStaticDCtx(dctxbuf, dctxsize);
+printf("dctx=%p\n", dctx);
+ if (dctx == NULL) {
+ printf("ZFS: failed to initialize ZSTD decompress context\n");
+ return (1);
+ }
+
+ result = ZSTD_decompressDCtx(dctx, d_start, d_len,
+ &src[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,52 @@
{ "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) },
{ NULL }
};
@@ -250,7 +296,8 @@
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]",
"COMPRESS", compress_table);
zprop_register_index(ZFS_PROP_SNAPDIR, "snapdir", ZFS_SNAPDIR_HIDDEN,
PROP_INHERIT, ZFS_TYPE_FILESYSTEM,
@@ -462,6 +509,9 @@
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, "[1-19]", "ZSTDLEVEL", B_TRUE, B_TRUE, NULL);
zprop_register_impl(ZFS_PROP_CREATION, "creation", PROP_TYPE_NUMBER, 0,
NULL, PROP_READONLY, ZFS_TYPE_DATASET | ZFS_TYPE_BOOKMARK,
"<date>", "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;
+ enum zio_zstd_levels 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;
+ ASSERT3U(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,21 @@
}
static void
+zstd_level_changed_cb(void *arg, uint64_t newval)
+{
+ objset_t *os = arg;
+
+ /*
+ * Inheritance and range checking should have been done by now.
+ */
+ /* XXX: Changing from inherited zstd to gzip trips this assert */
+ //ASSERT(newval != ZIO_ZSTDLVL_INHERIT);
+
+ 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 +464,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 +510,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,8 @@
if (compressok) {
featureflags |= DMU_BACKUP_FEATURE_COMPRESSED;
}
+ if (compressok && 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)) {
@@ -1432,6 +1434,16 @@
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)))
+ printf("ALLAN: would have rejected the stream here!\n");
+#if 0
+ return (SET_ERROR(ENOTSUP));
+#endif
error = dsl_dataset_hold(dp, tofs, FTAG, &ds);
if (error == 0) {
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<<SPA_COMPRESSBITS)-1)
/*
* All SPA data is represented by 128-bit data virtual addresses (DVAs).
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zfs_ioctl.h
@@ -94,6 +94,8 @@
/* flag #21 is reserved for a Delphix feature */
#define DMU_BACKUP_FEATURE_COMPRESSED (1 << 22)
/* flag #23 is reserved for the large dnode feature */
+/* flag #24 is reserved for the raw send feature */
+#define DMU_BACKUP_FEATURE_ZSTD (1 << 25)
/*
* Mask of all supported backup features
@@ -103,7 +105,7 @@
DMU_BACKUP_FEATURE_EMBED_DATA | DMU_BACKUP_FEATURE_LZ4 | \
DMU_BACKUP_FEATURE_RESUMING | \
DMU_BACKUP_FEATURE_LARGE_BLOCKS | \
- DMU_BACKUP_FEATURE_COMPRESSED)
+ DMU_BACKUP_FEATURE_COMPRESSED | DMU_BACKUP_FEATURE_ZSTD)
/* Are all features in the given flag word currently supported? */
#define DMU_STREAM_SUPPORTED(x) (!((x) & ~DMU_BACKUP_FEATURE_MASK))
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio.h
@@ -124,6 +124,7 @@
#define BOOTFS_COMPRESS_VALID(compress) \
((compress) == ZIO_COMPRESS_LZJB || \
(compress) == ZIO_COMPRESS_LZ4 || \
+ (compress) == ZIO_COMPRESS_ZSTD || \
(compress) == ZIO_COMPRESS_ON || \
(compress) == ZIO_COMPRESS_OFF)
@@ -295,6 +296,7 @@
typedef struct zio_prop {
enum zio_checksum zp_checksum;
enum zio_compress zp_compress;
+ enum zio_zstd_levels zp_zstd_level;
dmu_object_type_t zp_type;
uint8_t zp_level;
uint8_t zp_copies;
@@ -604,6 +606,8 @@
enum zio_checksum child, enum zio_checksum parent);
extern enum zio_compress zio_compress_select(spa_t *spa,
enum zio_compress child, enum zio_compress parent);
+extern enum zio_zstd_levels zio_zstd_level_select(spa_t *spa,
+ enum zio_zstd_levels child, enum zio_zstd_levels parent);
extern void zio_suspend(spa_t *spa, zio_t *zio);
extern int zio_resume(spa_t *spa);
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_compress.h
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_compress.h
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_compress.h
@@ -54,15 +54,51 @@
ZIO_COMPRESS_GZIP_9,
ZIO_COMPRESS_ZLE,
ZIO_COMPRESS_LZ4,
+ ZIO_COMPRESS_ZSTD,
ZIO_COMPRESS_FUNCTIONS
};
+#define ZIO_ZSTD_LEVEL_MIN 1
+#define ZIO_ZSTD_LEVEL_DEFAULT 3
+#define ZIO_ZSTD_LEVEL_MAX 19
+
+enum zio_zstd_levels {
+ ZIO_ZSTDLVL_INHERIT = 0,
+ ZIO_ZSTDLVL_1,
+ ZIO_ZSTDLVL_2,
+ ZIO_ZSTDLVL_3,
+ ZIO_ZSTDLVL_4,
+ ZIO_ZSTDLVL_5,
+ ZIO_ZSTDLVL_6,
+ ZIO_ZSTDLVL_7,
+ ZIO_ZSTDLVL_8,
+ ZIO_ZSTDLVL_9,
+ ZIO_ZSTDLVL_10,
+ ZIO_ZSTDLVL_11,
+ ZIO_ZSTDLVL_12,
+ ZIO_ZSTDLVL_13,
+ ZIO_ZSTDLVL_14,
+ ZIO_ZSTDLVL_15,
+ ZIO_ZSTDLVL_16,
+ ZIO_ZSTDLVL_17,
+ ZIO_ZSTDLVL_18,
+ ZIO_ZSTDLVL_19,
+ ZIO_ZSTDLVL_DEFAULT = 30, /* Allow the default level to change */
+ ZIO_ZSTDLVL_AUTO = 31, /* Reserved for future use */
+ ZIO_ZSTDLVL_LEVELS
+};
+
+/* Forward Declaration to avoid visibility problems */
+struct zio_prop;
+
/* Common signature for all zio compress functions. */
typedef size_t zio_compress_func_t(void *src, void *dst,
size_t s_len, size_t d_len, int);
/* Common signature for all zio decompress functions. */
typedef int zio_decompress_func_t(void *src, void *dst,
size_t s_len, size_t d_len, int);
+/* Common signature for all zio get-compression-level functions. */
+typedef int zio_getcomplevel_func_t(void *src, size_t s_len);
/*
* Common signature for all zio decompress functions using an ABD as input.
* This is helpful if you have both compressed ARC and scatter ABDs enabled,
@@ -79,6 +115,7 @@
int ci_level;
zio_compress_func_t *ci_compress;
zio_decompress_func_t *ci_decompress;
+ zio_getcomplevel_func_t *ci_getlevel;
} zio_compress_info_t;
extern zio_compress_info_t zio_compress_table[ZIO_COMPRESS_FUNCTIONS];
@@ -104,16 +141,26 @@
int level);
extern int lz4_decompress(void *src, void *dst, size_t s_len, size_t d_len,
int level);
+extern void zstd_init(void);
+extern void zstd_fini(void);
+extern size_t zstd_compress(void *src, void *dst, size_t s_len, size_t d_len,
+ int level);
+extern int zstd_decompress(void *src, void *dst, size_t s_len, size_t d_len,
+ int level);
+extern int zstd_getlevel(void *src, size_t s_len);
/*
* Compress and decompress data if necessary.
*/
extern size_t zio_compress_data(enum zio_compress c, abd_t *src, void *dst,
- size_t s_len);
+ size_t s_len, struct zio_prop *zp);
extern int zio_decompress_data(enum zio_compress c, abd_t *src, void *dst,
size_t s_len, size_t d_len);
extern int zio_decompress_data_buf(enum zio_compress c, void *src, void *dst,
size_t s_len, size_t d_len);
+extern int zio_getcomplevel(enum zio_compress c, abd_t *src, size_t s_len);
+extern int zio_decompress_getlevel(enum zio_compress c, abd_t *src, void *dst,
+ size_t s_len, size_t d_len, enum zio_zstd_levels *level);
/*
* Module lifetime management.
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_impl.h
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_impl.h
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/sys/zio_impl.h
@@ -76,9 +76,9 @@
* the supported transformations:
*
* Compression:
- * ZFS supports three different flavors of compression -- gzip, lzjb, and
- * zle. Compression occurs as part of the write pipeline and is performed
- * in the ZIO_STAGE_WRITE_BP_INIT stage.
+ * ZFS supports five different flavors of compression -- gzip, lzjb, lz4, zle,
+ * and zstd. Compression occurs as part of the write pipeline and is
+ * performed in the ZIO_STAGE_WRITE_BP_INIT stage.
*
* Dedup:
* Dedup reads are handled by the ZIO_STAGE_DDT_READ_START and
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_get.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_get.c
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zcp_get.c
@@ -325,6 +325,7 @@
char setpoint[ZFS_MAX_DATASET_NAME_LEN] =
"Internal error - setpoint not determined";
zfs_type_t ds_type;
+ const char *prop_name = zfs_prop_to_name(zfs_prop);
zprop_type_t prop_type = zfs_prop_get_type(zfs_prop);
(void) get_objset_type(ds, &ds_type);
@@ -463,6 +464,24 @@
}
break;
}
+ case ZFS_PROP_COMPRESSION:
+ error = dsl_prop_get_ds(ds, prop_name, sizeof (numval), 1,
+ &numval, setpoint);
+ /* Special handling is only required for ZSTD */
+ if (error || numval != ZIO_COMPRESS_ZSTD)
+ break;
+
+ uint64_t levelval;
+ const char *zstd_name = zfs_prop_to_name(ZFS_PROP_ZSTD_LEVEL);
+
+ error = dsl_prop_get_ds(ds, zstd_name, sizeof (levelval),
+ 1, &levelval, setpoint);
+ if (error == 0) {
+ if (levelval == ZIO_ZSTDLVL_DEFAULT)
+ break;
+ numval |= levelval << SPA_COMPRESSBITS;
+ }
+ break;
default:
/* Did not match these props, check in the dsl_dir */
error = get_dsl_dir_prop(ds, zfs_prop, &numval);
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zfs_ioctl.c
@@ -2153,6 +2153,34 @@
}
static int
+get_prop_uint64(nvlist_t *nv, const char *prop, nvlist_t **nvp,
+ uint64_t *val)
+{
+ int err = 0;
+ nvlist_t *subnv;
+ nvpair_t *pair;
+ nvpair_t *propval;
+
+ if (nvlist_lookup_nvpair(nv, prop, &pair) != 0)
+ return (EINVAL);
+
+ /* decode the property value */
+ propval = pair;
+ if (nvpair_type(pair) == DATA_TYPE_NVLIST) {
+ subnv = fnvpair_value_nvlist(pair);
+ if (nvp != NULL)
+ *nvp = subnv;
+ if (nvlist_lookup_nvpair(subnv, ZPROP_VALUE, &propval) != 0)
+ err = EINVAL;
+ }
+ if (nvpair_type(propval) == DATA_TYPE_UINT64) {
+ *val = fnvpair_value_uint64(propval);
+ }
+
+ return (err);
+}
+
+static int
zfs_ioc_objset_stats_impl(zfs_cmd_t *zc, objset_t *os)
{
int error = 0;
@@ -2177,6 +2205,28 @@
return (error);
VERIFY0(error);
}
+ /*
+ * ZSTD stores the compression level in a separate hidden
+ * property to avoid using up a large chunk of space in the
+ * on-disk compression algorithm enum. We need to swap things
+ * back around when the property is read.
+ */
+ nvlist_t *cnv;
+ uint64_t compval, levelval;
+
+ if (get_prop_uint64(nv, "compression", &cnv, &compval) != 0)
+ error = EINVAL;
+
+ if (error == 0 && compval == ZIO_COMPRESS_ZSTD &&
+ get_prop_uint64(nv, "zstd_compress_level", NULL,
+ &levelval) == 0) {
+ if (levelval == ZIO_ZSTDLVL_DEFAULT)
+ levelval = 0;
+ fnvlist_remove(cnv, ZPROP_VALUE);
+ fnvlist_add_uint64(cnv, ZPROP_VALUE,
+ compval | (levelval << SPA_COMPRESSBITS));
+ }
+
error = put_nvlist(zc, nv);
nvlist_free(nv);
}
@@ -2576,6 +2626,32 @@
}
break;
}
+ case ZFS_PROP_COMPRESSION:
+ /* Special handling is only required for ZSTD */
+ if (intval & SPA_COMPRESSMASK != ZIO_COMPRESS_ZSTD) {
+ err = -1;
+ break;
+ }
+ /*
+ * Store the ZSTD compression level separate from the compress
+ * property in its own hidden property.
+ */
+ uint64_t levelval;
+
+ if (intval == ZIO_COMPRESS_ZSTD) {
+ levelval = ZIO_ZSTDLVL_DEFAULT;
+ } else {
+ levelval = (intval & ~SPA_COMPRESSMASK)
+ >> SPA_COMPRESSBITS;
+ }
+ err = dsl_prop_set_int(dsname, "zstd_compress_level", source,
+ levelval);
+ if (err == 0) {
+ /* Store the compression algorithm normally */
+ err = dsl_prop_set_int(dsname, propname, source,
+ intval & SPA_COMPRESSMASK);
+ }
+ break;
default:
err = -1;
}
@@ -4075,6 +4151,20 @@
if (!spa_feature_is_enabled(spa,
SPA_FEATURE_LZ4_COMPRESS)) {
+ spa_close(spa, FTAG);
+ return (SET_ERROR(ENOTSUP));
+ }
+ spa_close(spa, FTAG);
+ }
+
+ if (intval == ZIO_COMPRESS_ZSTD) {
+ spa_t *spa;
+
+ if ((err = spa_open(dsname, &spa, FTAG)) != 0)
+ return (err);
+
+ if (!spa_feature_is_enabled(spa,
+ SPA_FEATURE_ZSTD_COMPRESS)) {
spa_close(spa, FTAG);
return (SET_ERROR(ENOTSUP));
}
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio.c
@@ -407,8 +407,17 @@
{
if (zio->io_error == 0) {
void *tmp = abd_borrow_buf(data, size);
- int ret = zio_decompress_data(BP_GET_COMPRESS(zio->io_bp),
- zio->io_abd, tmp, zio->io_size, size);
+ int ret;
+
+ if (BP_GET_COMPRESS(zio->io_bp) == ZIO_COMPRESS_ZSTD) {
+ ret = zio_decompress_getlevel(
+ BP_GET_COMPRESS(zio->io_bp), zio->io_abd, tmp,
+ zio->io_size, size, &zio->io_prop.zp_zstd_level);
+ } else {
+ ret = zio_decompress_data(BP_GET_COMPRESS(zio->io_bp),
+ zio->io_abd, tmp, zio->io_size, size);
+ }
+
abd_return_buf_copy(data, tmp, size);
if (ret != 0)
@@ -1449,7 +1458,8 @@
/* If it's a compressed write that is not raw, compress the buffer. */
if (compress != ZIO_COMPRESS_OFF && psize == lsize) {
void *cbuf = zio_buf_alloc(lsize);
- psize = zio_compress_data(compress, zio->io_abd, cbuf, lsize);
+ psize = zio_compress_data(compress, zio->io_abd, cbuf, lsize,
+ zp);
if (psize == 0 || psize == lsize) {
compress = ZIO_COMPRESS_OFF;
zio_buf_free(cbuf, lsize);
@@ -2400,6 +2410,7 @@
zp.zp_checksum = gio->io_prop.zp_checksum;
zp.zp_compress = ZIO_COMPRESS_OFF;
+ zp.zp_zstd_level = ZIO_ZSTDLVL_DEFAULT;
zp.zp_type = DMU_OT_NONE;
zp.zp_level = 0;
zp.zp_copies = gio->io_prop.zp_copies;
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_compress.c
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_compress.c
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zio_compress.c
@@ -59,24 +59,42 @@
* Compression vectors.
*/
zio_compress_info_t zio_compress_table[ZIO_COMPRESS_FUNCTIONS] = {
- {"inherit", 0, NULL, NULL},
- {"on", 0, NULL, NULL},
- {"uncompressed", 0, NULL, NULL},
- {"lzjb", 0, lzjb_compress, lzjb_decompress},
- {"empty", 0, NULL, NULL},
- {"gzip-1", 1, gzip_compress, gzip_decompress},
- {"gzip-2", 2, gzip_compress, gzip_decompress},
- {"gzip-3", 3, gzip_compress, gzip_decompress},
- {"gzip-4", 4, gzip_compress, gzip_decompress},
- {"gzip-5", 5, gzip_compress, gzip_decompress},
- {"gzip-6", 6, gzip_compress, gzip_decompress},
- {"gzip-7", 7, gzip_compress, gzip_decompress},
- {"gzip-8", 8, gzip_compress, gzip_decompress},
- {"gzip-9", 9, gzip_compress, gzip_decompress},
- {"zle", 64, zle_compress, zle_decompress},
- {"lz4", 0, lz4_compress, lz4_decompress}
+ {"inherit", 0, NULL, NULL, NULL},
+ {"on", 0, NULL, NULL, NULL},
+ {"uncompressed", 0, NULL, NULL, NULL},
+ {"lzjb", 0, lzjb_compress, lzjb_decompress, NULL},
+ {"empty", 0, NULL, NULL, NULL},
+ {"gzip-1", 1, gzip_compress, gzip_decompress, NULL},
+ {"gzip-2", 2, gzip_compress, gzip_decompress, NULL},
+ {"gzip-3", 3, gzip_compress, gzip_decompress, NULL},
+ {"gzip-4", 4, gzip_compress, gzip_decompress, NULL},
+ {"gzip-5", 5, gzip_compress, gzip_decompress, NULL},
+ {"gzip-6", 6, gzip_compress, gzip_decompress, NULL},
+ {"gzip-7", 7, gzip_compress, gzip_decompress, NULL},
+ {"gzip-8", 8, gzip_compress, gzip_decompress, NULL},
+ {"gzip-9", 9, gzip_compress, gzip_decompress, NULL},
+ {"zle", 64, zle_compress, zle_decompress, NULL},
+ {"lz4", 0, lz4_compress, lz4_decompress, NULL},
+ {"zstd", 0, zstd_compress, zstd_decompress, zstd_getlevel},
};
+enum zio_zstd_levels
+zio_zstd_level_select(spa_t *spa, enum zio_zstd_levels child,
+ enum zio_zstd_levels parent)
+{
+ enum zio_zstd_levels result;
+
+ ASSERT(child < ZIO_ZSTDLVL_LEVELS);
+ ASSERT(parent < ZIO_ZSTDLVL_LEVELS);
+ ASSERT(parent != ZIO_ZSTDLVL_INHERIT);
+
+ result = child;
+ if (result == ZIO_ZSTDLVL_INHERIT)
+ result = parent;
+
+ return (result);
+}
+
enum zio_compress
zio_compress_select(spa_t *spa, enum zio_compress child,
enum zio_compress parent)
@@ -114,9 +132,11 @@
}
size_t
-zio_compress_data(enum zio_compress c, abd_t *src, void *dst, size_t s_len)
+zio_compress_data(enum zio_compress c, abd_t *src, void *dst, size_t s_len,
+ zio_prop_t *zp)
{
size_t c_len, d_len;
+ int level;
zio_compress_info_t *ci = &zio_compress_table[c];
ASSERT((uint_t)c < ZIO_COMPRESS_FUNCTIONS);
@@ -139,9 +159,19 @@
/* Compress at least 12.5% */
d_len = s_len - (s_len >> 3);
+ level = ci->ci_level;
+
+ if (c == ZIO_COMPRESS_ZSTD) {
+ ASSERT(zp != NULL);
+ if (zp->zp_zstd_level == ZIO_ZSTDLVL_DEFAULT)
+ level = ZIO_ZSTD_LEVEL_DEFAULT;
+ else
+ level = zp->zp_zstd_level;
+ }
+
/* No compression algorithms can read from ABDs directly */
void *tmp = abd_borrow_buf_copy(src, s_len);
- c_len = ci->ci_compress(tmp, dst, s_len, d_len, ci->ci_level);
+ c_len = ci->ci_compress(tmp, dst, s_len, d_len, level);
abd_return_buf(src, tmp, s_len);
if (c_len > d_len) {
@@ -170,6 +200,41 @@
{
void *tmp = abd_borrow_buf_copy(src, s_len);
int ret = zio_decompress_data_buf(c, tmp, dst, s_len, d_len);
+ abd_return_buf(src, tmp, s_len);
+
+ return (ret);
+}
+
+int
+zio_getcomplevel(enum zio_compress c, abd_t *src, size_t s_len)
+{
+ void *tmp = abd_borrow_buf_copy(src, s_len);
+ zio_compress_info_t *ci = &zio_compress_table[c];
+ int ret;
+
+ if ((uint_t)c >= ZIO_COMPRESS_FUNCTIONS || ci->ci_getlevel == NULL)
+ return (-1);
+
+ ret = ci->ci_getlevel(tmp, s_len);
+
+ abd_return_buf(src, tmp, s_len);
+
+ return (ret);
+}
+
+int
+zio_decompress_getlevel(enum zio_compress c, abd_t *src, void *dst,
+ size_t s_len, size_t d_len, enum zio_zstd_levels *level)
+{
+ void *tmp = abd_borrow_buf_copy(src, s_len);
+ int ret = zio_decompress_data_buf(c, tmp, dst, s_len, d_len);
+
+ zio_compress_info_t *ci = &zio_compress_table[c];
+ if ((uint_t)c >= ZIO_COMPRESS_FUNCTIONS || ci->ci_getlevel == NULL)
+ return (-1);
+
+ *level = ci->ci_getlevel(tmp, s_len);
+
abd_return_buf(src, tmp, s_len);
return (ret);
Index: sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zstd.c
===================================================================
--- /dev/null
+++ sys/cddl/contrib/opensolaris/uts/common/fs/zfs/zstd.c
@@ -0,0 +1,352 @@
+/*
+ * 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 <allanjude@freebsd.org>.
+ */
+
+#include <sys/param.h>
+#include <sys/zfs_context.h>
+#include <sys/zio_compress.h>
+#include <sys/spa.h>
+#include <sys/malloc.h>
+
+#define ZSTD_STATIC_LINKING_ONLY
+#include <zstd.h>
+#include <zstd_errors.h>
+
+#define ZSTD_KMEM_MAGIC 0x20160831
+#define ZSTD_COOKIE_SHIFT 26
+#define ZSTD_COOKIE_MASK ((1U<<ZSTD_COOKIE_SHIFT)-1)
+
+/*
+ * XXX: TODO: Investigate using ZSTD_compressBlock for small blocks
+ */
+
+static size_t real_zstd_compress(const char *source, char *dest, int isize,
+ int osize, int level);
+static size_t real_zstd_decompress(const char *source, char *dest, int isize,
+ int maxosize);
+
+void *zstd_alloc(void *opaque, size_t size);
+void zstd_free(void *opaque, void *ptr);
+
+static const ZSTD_customMem zstd_malloc = {
+ zstd_alloc,
+ zstd_free,
+ NULL,
+};
+
+enum zstd_kmem_type {
+ ZSTD_KMEM_UNKNOWN = 0,
+ ZSTD_KMEM_CCTX,
+ ZSTD_KMEM_WRKSPC_4K_MIN,
+ ZSTD_KMEM_WRKSPC_4K_DEF,
+ ZSTD_KMEM_WRKSPC_4K_MAX,
+ ZSTD_KMEM_WRKSPC_16K_MIN,
+ ZSTD_KMEM_WRKSPC_16K_DEF,
+ ZSTD_KMEM_WRKSPC_16K_MAX,
+ ZSTD_KMEM_WRKSPC_128K_MIN,
+ ZSTD_KMEM_WRKSPC_128K_DEF,
+ ZSTD_KMEM_WRKSPC_128K_MAX,
+ /* SPA_MAXBLOCKSIZE */
+ ZSTD_KMEM_WRKSPC_MBS_MIN,
+ ZSTD_KMEM_WRKSPC_MBS_DEF,
+ ZSTD_KMEM_WRKSPC_MBS_MAX,
+ ZSTD_KMEM_DCTX,
+ ZSTD_KMEM_COUNT,
+};
+
+struct zstd_kmem {
+ uint_t kmem_magic;
+ enum zstd_kmem_type kmem_type;
+ size_t kmem_size;
+};
+
+struct zstd_kmem_config {
+ size_t block_size;
+ int compress_level;
+ char* cache_name;
+};
+
+static kmem_cache_t *zstd_kmem_cache[ZSTD_KMEM_COUNT] = { NULL };
+static struct zstd_kmem zstd_cache_size[ZSTD_KMEM_COUNT] = {
+ { ZSTD_KMEM_MAGIC, 0, 0 } };
+static struct zstd_kmem_config zstd_cache_config[ZSTD_KMEM_COUNT] = {
+ { 0, 0, "zstd_unknown" },
+ { 0, 0, "zstd_cctx" },
+ { 4096, ZIO_ZSTD_LEVEL_MIN, "zstd_wrkspc_4k_min" },
+ { 4096, ZIO_ZSTD_LEVEL_DEFAULT, "zstd_wrkspc_4k_def" },
+ { 4096, ZIO_ZSTD_LEVEL_MAX, "zstd_wrkspc_4k_max" },
+ { 16384, ZIO_ZSTD_LEVEL_MIN, "zstd_wrkspc_16k_min" },
+ { 16384, ZIO_ZSTD_LEVEL_DEFAULT, "zstd_wrkspc_16k_def" },
+ { 16384, ZIO_ZSTD_LEVEL_MAX, "zstd_wrkspc_16k_max" },
+ { SPA_OLD_MAXBLOCKSIZE, ZIO_ZSTD_LEVEL_MIN, "zstd_wrkspc_128k_min" },
+ { SPA_OLD_MAXBLOCKSIZE, ZIO_ZSTD_LEVEL_DEFAULT,
+ "zstd_wrkspc_128k_def" },
+ { SPA_OLD_MAXBLOCKSIZE, ZIO_ZSTD_LEVEL_MAX, "zstd_wrkspc_128k_max" },
+ { SPA_MAXBLOCKSIZE, ZIO_ZSTD_LEVEL_MIN, "zstd_wrkspc_mbs_min" },
+ { SPA_MAXBLOCKSIZE, ZIO_ZSTD_LEVEL_DEFAULT, "zstd_wrkspc_mbs_def" },
+ { SPA_MAXBLOCKSIZE, ZIO_ZSTD_LEVEL_MAX, "zstd_wrkspc_mbs_max" },
+ { 0, 0, "zstd_dctx" },
+ };
+
+static int
+zstd_compare(const void *a, const void *b)
+{
+ struct zstd_kmem *x, *y;
+
+ x = (struct zstd_kmem*)a;
+ y = (struct zstd_kmem*)b;
+
+ ASSERT(x->kmem_magic == ZSTD_KMEM_MAGIC);
+ ASSERT(y->kmem_magic == ZSTD_KMEM_MAGIC);
+
+ if (x->kmem_size > y->kmem_size) {
+ return (1);
+ } else if (x->kmem_size == y->kmem_size) {
+ return (0);
+ } else {
+ return (-1);
+ }
+}
+
+size_t
+zstd_compress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n)
+{
+ size_t c_len;
+ uint32_t cookie;
+ char *dest = d_start;
+
+ ASSERT(d_len >= sizeof (cookie));
+ ASSERT(d_len <= s_len);
+
+ /* XXX: this could overflow, but we never have blocks that big */
+ c_len = real_zstd_compress(s_start, &dest[sizeof (cookie)], s_len,
+ d_len - sizeof (cookie), n);
+
+ /* Signal an error if the compression routine returned an error. */
+ if (ZSTD_isError(c_len))
+ return (s_len);
+
+ /*
+ * Encode the compresed buffer size, and compression level (top 6 bits)
+ * at the start of the block. We'll need the size later in decompression
+ * to counter the effects of padding which might be added to the
+ * compressed buffer and which, if unhandled, would confuse the hell out
+ * of our decompression function. We may need the compression level
+ * if compressed_arc is disabled, to match the compression settings
+ * to write to the L2ARC.
+ */
+ cookie = c_len | (n << ZSTD_COOKIE_SHIFT);
+ *(uint32_t *)dest = BE_32(cookie);
+
+ return (c_len + sizeof (cookie));
+}
+
+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 cookie = BE_IN32(src);
+ uint32_t bufsiz = cookie & ZSTD_COOKIE_MASK;
+
+ ASSERT(d_len >= s_len);
+
+ /* invalid compressed buffer size encoded at start */
+ if (bufsiz + sizeof (bufsiz) > s_len)
+ return (1);
+
+ /*
+ * Returns 0 on success (decompression function returned non-negative)
+ * and non-zero on failure (decompression function returned negative.
+ */
+ if (ZSTD_isError(real_zstd_decompress(&src[sizeof (bufsiz)], d_start,
+ bufsiz, d_len)))
+ return (1);
+
+ return (0);
+}
+
+int
+zstd_getlevel(void *s_start, size_t s_len __unused)
+{
+ const char *src = s_start;
+ uint32_t cookie = BE_IN32(src);
+ int res;
+
+ res = (cookie & ~SPA_COMPRESSMASK) >> ZSTD_COOKIE_SHIFT;
+
+ ASSERT(res <= ZIO_ZSTDLVL_LEVELS);
+
+ return (res);
+}
+
+static size_t
+real_zstd_compress(const char *source, char *dest, int isize, int osize,
+ int level)
+{
+ size_t result;
+ ZSTD_CCtx *cctx;
+
+ ASSERT(level != 0);
+ if (level == ZIO_ZSTDLVL_DEFAULT)
+ level = ZIO_ZSTD_LEVEL_DEFAULT;
+
+ /* XXX: In ZSTD 1.3+ consider using ZSTD_initStaticCCtx() instead */
+ cctx = ZSTD_createCCtx_advanced(zstd_malloc);
+ /*
+ * out of kernel memory, gently fall through - this will disable
+ * compression in zio_compress_data
+ */
+ if (cctx == NULL)
+ return (0);
+
+ result = ZSTD_compressCCtx(cctx, dest, osize, source, isize, level);
+
+ ZSTD_freeCCtx(cctx);
+ return (result);
+}
+
+
+static size_t
+real_zstd_decompress(const char *source, char *dest, int isize, int maxosize)
+{
+ size_t result;
+ ZSTD_DCtx *dctx;
+
+ dctx = ZSTD_createDCtx_advanced(zstd_malloc);
+ if (dctx == NULL)
+ return (ZSTD_error_memory_allocation);
+
+ result = ZSTD_decompressDCtx(dctx, dest, maxosize, source, isize);
+
+ ZSTD_freeDCtx(dctx);
+ return (result);
+}
+
+extern void *
+zstd_alloc(void *opaque __unused, size_t size)
+{
+ size_t nbytes = sizeof(struct zstd_kmem) + size;
+ struct zstd_kmem *z;
+ enum zstd_kmem_type type;
+ int i;
+
+ type = ZSTD_KMEM_UNKNOWN;
+ for (i = 0; i < ZSTD_KMEM_COUNT; i++) {
+ if (nbytes <= zstd_cache_size[i].kmem_size) {
+ type = zstd_cache_size[i].kmem_type;
+ z = kmem_cache_alloc(zstd_kmem_cache[type],
+ KM_NOSLEEP | M_ZERO);
+ break;
+ }
+ }
+ /* No matching cache */
+ if (type == ZSTD_KMEM_UNKNOWN) {
+ z = kmem_alloc(nbytes, KM_NOSLEEP | M_ZERO);
+ }
+ if (z == NULL) {
+ return (NULL);
+ }
+
+ z->kmem_magic = ZSTD_KMEM_MAGIC;
+ z->kmem_type = type;
+ z->kmem_size = nbytes;
+
+ return ((void*)z + (sizeof(struct zstd_kmem)));
+}
+
+extern void
+zstd_free(void *opaque __unused, void *ptr)
+{
+ struct zstd_kmem *z = ptr - sizeof(struct zstd_kmem);
+
+ ASSERT(z->kmem_magic == ZSTD_KMEM_MAGIC);
+ ASSERT(z->kmem_type < ZSTD_KMEM_COUNT);
+ ASSERT(z->kmem_type >= ZSTD_KMEM_UNKNOWN);
+
+ if (z->kmem_type == ZSTD_KMEM_UNKNOWN) {
+ kmem_free(z, z->kmem_size);
+ } else {
+ kmem_cache_free(zstd_kmem_cache[z->kmem_type], z);
+ }
+}
+
+extern void
+zstd_init(void)
+{
+ int i;
+
+ /* There is no estimate function for the CCtx itself */
+ zstd_cache_size[1].kmem_magic = ZSTD_KMEM_MAGIC;
+ zstd_cache_size[1].kmem_type = 1;
+ zstd_cache_size[1].kmem_size = roundup2(zstd_cache_config[1].block_size
+ + sizeof(struct zstd_kmem), PAGE_SIZE);
+ zstd_kmem_cache[1] = kmem_cache_create(
+ zstd_cache_config[1].cache_name, zstd_cache_size[1].kmem_size,
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+
+ /*
+ * Estimate the size of the ZSTD CCtx workspace required for each record
+ * size at each compression level.
+ */
+ for (i = 2; i < ZSTD_KMEM_DCTX; i++) {
+ ASSERT(zstd_cache_config[i].cache_name != NULL);
+ zstd_cache_size[i].kmem_magic = ZSTD_KMEM_MAGIC;
+ zstd_cache_size[i].kmem_type = i;
+ zstd_cache_size[i].kmem_size = roundup2(
+ ZSTD_estimateCCtxSize_usingCParams(
+ ZSTD_getCParams(zstd_cache_config[i].compress_level,
+ zstd_cache_config[i].block_size, 0)) +
+ sizeof(struct zstd_kmem), PAGE_SIZE);
+ zstd_kmem_cache[i] = kmem_cache_create(
+ zstd_cache_config[i].cache_name,
+ zstd_cache_size[i].kmem_size,
+ 0, NULL, NULL, NULL, NULL, NULL, 0);
+ }
+ /* Estimate the size of the decompression context */
+ zstd_cache_size[i].kmem_magic = ZSTD_KMEM_MAGIC;
+ zstd_cache_size[i].kmem_type = i;
+ zstd_cache_size[i].kmem_size = roundup2(ZSTD_estimateDCtxSize() +
+ sizeof(struct zstd_kmem), PAGE_SIZE);
+ zstd_kmem_cache[i] = kmem_cache_create(zstd_cache_config[i].cache_name,
+ zstd_cache_size[i].kmem_size, 0, NULL, NULL, NULL, NULL, NULL, 0);
+
+ /* Sort the kmem caches for later searching */
+ qsort(zstd_cache_size, ZSTD_KMEM_COUNT, sizeof(struct zstd_kmem),
+ zstd_compare);
+
+}
+
+extern void
+zstd_fini(void)
+{
+ int i, type;
+
+ for (i = 0; i < ZSTD_KMEM_COUNT; i++) {
+ type = zstd_cache_size[i].kmem_type;
+ if (zstd_kmem_cache[type] != NULL) {
+ kmem_cache_destroy(zstd_kmem_cache[type]);
+ }
+ }
+}
Index: sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
===================================================================
--- sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
+++ sys/cddl/contrib/opensolaris/uts/common/sys/fs/zfs.h
@@ -166,6 +166,7 @@
ZFS_PROP_PREV_SNAP,
ZFS_PROP_RECEIVE_RESUME_TOKEN,
ZFS_PROP_REMAPTXG, /* not exposed to the user */
+ ZFS_PROP_ZSTD_LEVEL, /* not exposed to the user */
ZFS_NUM_PROPS
} zfs_prop_t;
Index: sys/modules/zfs/Makefile
===================================================================
--- sys/modules/zfs/Makefile
+++ sys/modules/zfs/Makefile
@@ -72,6 +72,11 @@
.PATH: ${SYSDIR}/crypto/skein
SRCS+= skein.c skein_block.c
+.PATH: ${SYSDIR}/contrib/zstd/lib
+CFLAGS+=-I${SYSDIR}/contrib/zstd/lib
+CFLAGS+=-I${SYSDIR}/contrib/zstd/lib/freebsd
+CFLAGS+=-I${SYSDIR}/contrib/zstd/lib/common
+
.PATH: ${SUNW}/common/zfs
.include "${SUNW}/uts/common/Makefile.files"
.PATH: ${SUNW}/uts/common/fs/zfs
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Jan 21, 4:19 PM (6 h, 34 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27813246
Default Alt Text
D11124.id43730.diff (49 KB)
Attached To
Mode
D11124: ZSTD compression for ZFS
Attached
Detach File
Event Timeline
Log In to Comment