From 8776dbb729653e4486e2333864b0a0556f4247cb Mon Sep 17 00:00:00 2001 From: Rob Norris Date: Wed, 3 Jul 2024 16:13:17 +1000 Subject: [PATCH] compress: add "slack" compression option The "slack" option simply searches from the end of the block backwards to the last non-zero byte, and sets that position as the "compressed" size. Sponsored-by: Klara, Inc. Sponsored-by: Wasabi Technology, Inc. Signed-off-by: Rob Norris --- cmd/dbufstat.in | 3 +- cmd/zstream/zstream_decompress.c | 6 +- include/sys/zio_compress.h | 5 ++ include/zfeature_common.h | 1 + lib/libnvpair/libnvpair.abi | 5 ++ lib/libuutil/libuutil.abi | 5 ++ lib/libzfs/libzfs.abi | 16 ++-- lib/libzfs_core/libzfs_core.abi | 5 ++ lib/libzfsbootenv/libzfsbootenv.abi | 2 +- lib/libzpool/Makefile.am | 1 + man/man7/zfsprops.7 | 10 ++- man/man7/zpool-features.7 | 37 +++++++++- man/man8/zstream.8 | 1 + module/Kbuild.in | 1 + module/Makefile.bsd | 1 + module/zcommon/zfeature_common.c | 14 ++++ module/zcommon/zfs_prop.c | 1 + module/zfs/slack.c | 74 +++++++++++++++++++ module/zfs/zfs_ioctl.c | 14 ++++ module/zfs/zio_compress.c | 4 + tests/zfs-tests/include/properties.shlib | 2 +- .../cli_root/zpool_get/zpool_get.cfg | 1 + 22 files changed, 196 insertions(+), 13 deletions(-) create mode 100644 module/zfs/slack.c diff --git a/cmd/dbufstat.in b/cmd/dbufstat.in index 1252496577bc..7d2952eaeec8 100755 --- a/cmd/dbufstat.in +++ b/cmd/dbufstat.in @@ -359,7 +359,8 @@ def get_compstring(c): "ZIO_COMPRESS_GZIP_6", "ZIO_COMPRESS_GZIP_7", "ZIO_COMPRESS_GZIP_8", "ZIO_COMPRESS_GZIP_9", "ZIO_COMPRESS_ZLE", "ZIO_COMPRESS_LZ4", - "ZIO_COMPRESS_ZSTD", "ZIO_COMPRESS_FUNCTION"] + "ZIO_COMPRESS_ZSTD", "ZIO_COMPRESS_SLACK", + "ZIO_COMPRESS_FUNCTION"] # If "-rr" option is used, don't convert to string representation if raw > 1: diff --git a/cmd/zstream/zstream_decompress.c b/cmd/zstream/zstream_decompress.c index c64011e3822a..1853d2125c33 100644 --- a/cmd/zstream/zstream_decompress.c +++ b/cmd/zstream/zstream_decompress.c @@ -125,14 +125,16 @@ zstream_do_decompress(int argc, char *argv[]) type = ZIO_COMPRESS_LZJB; else if (0 == strcmp("gzip", argv[i])) type = ZIO_COMPRESS_GZIP_1; + else if (0 == strcmp("slack", argv[i])) + type = ZIO_COMPRESS_SLACK; else if (0 == strcmp("zle", argv[i])) type = ZIO_COMPRESS_ZLE; else if (0 == strcmp("zstd", argv[i])) type = ZIO_COMPRESS_ZSTD; else { fprintf(stderr, "Invalid compression type %s.\n" - "Supported types are off, lz4, lzjb, gzip, " - "zle, and zstd\n", + "Supported types are off, lz4, lzjb, " + "gzip, slack, zle, and zstd\n", argv[i]); exit(2); } diff --git a/include/sys/zio_compress.h b/include/sys/zio_compress.h index ceef757abd20..b2e51da999f0 100644 --- a/include/sys/zio_compress.h +++ b/include/sys/zio_compress.h @@ -55,6 +55,7 @@ enum zio_compress { ZIO_COMPRESS_ZLE, ZIO_COMPRESS_LZ4, ZIO_COMPRESS_ZSTD, + ZIO_COMPRESS_SLACK, ZIO_COMPRESS_FUNCTIONS }; @@ -170,6 +171,10 @@ extern size_t zfs_lz4_compress(abd_t *src, abd_t *dst, size_t s_len, size_t d_len, int level); extern int zfs_lz4_decompress(abd_t *src, abd_t *dst, size_t s_len, size_t d_len, int level); +extern size_t zfs_slack_compress(abd_t *src, abd_t *dst, size_t s_len, + size_t d_len, int level); +extern int zfs_slack_decompress(abd_t *src, abd_t *dst, size_t s_len, + size_t d_len, int level); /* * Compress and decompress data if necessary. diff --git a/include/zfeature_common.h b/include/zfeature_common.h index ac42b5c0cd6b..336c8d7ac0b3 100644 --- a/include/zfeature_common.h +++ b/include/zfeature_common.h @@ -86,6 +86,7 @@ typedef enum spa_feature { SPA_FEATURE_FAST_DEDUP, SPA_FEATURE_LONGNAME, SPA_FEATURE_LARGE_MICROZAP, + SPA_FEATURE_SLACK_COMPRESS, SPA_FEATURES } spa_feature_t; diff --git a/lib/libnvpair/libnvpair.abi b/lib/libnvpair/libnvpair.abi index e3eacb195463..fa8efbcd9d7e 100644 --- a/lib/libnvpair/libnvpair.abi +++ b/lib/libnvpair/libnvpair.abi @@ -2194,6 +2194,7 @@ + @@ -2306,6 +2307,10 @@ + + + + diff --git a/lib/libuutil/libuutil.abi b/lib/libuutil/libuutil.abi index 7cb92ac9f3f8..220b26228dac 100644 --- a/lib/libuutil/libuutil.abi +++ b/lib/libuutil/libuutil.abi @@ -652,6 +652,7 @@ + @@ -763,6 +764,10 @@ + + + + diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi index 1f9fde6677d8..25efe7ee2fe2 100644 --- a/lib/libzfs/libzfs.abi +++ b/lib/libzfs/libzfs.abi @@ -629,7 +629,7 @@ - + @@ -1170,6 +1170,7 @@ + @@ -1275,6 +1276,10 @@ + + + + @@ -6197,7 +6202,8 @@ - + + @@ -9376,8 +9382,8 @@ - - + + @@ -9454,7 +9460,7 @@ - + diff --git a/lib/libzfs_core/libzfs_core.abi b/lib/libzfs_core/libzfs_core.abi index 6a9c20a2bb88..1f6711c7fb58 100644 --- a/lib/libzfs_core/libzfs_core.abi +++ b/lib/libzfs_core/libzfs_core.abi @@ -651,6 +651,7 @@ + @@ -762,6 +763,10 @@ + + + + diff --git a/lib/libzfsbootenv/libzfsbootenv.abi b/lib/libzfsbootenv/libzfsbootenv.abi index 5903d5dcbe21..bf866b0fa61b 100644 --- a/lib/libzfsbootenv/libzfsbootenv.abi +++ b/lib/libzfsbootenv/libzfsbootenv.abi @@ -1,6 +1,6 @@ - + diff --git a/lib/libzpool/Makefile.am b/lib/libzpool/Makefile.am index 404b737c204d..f6b8863dc0c4 100644 --- a/lib/libzpool/Makefile.am +++ b/lib/libzpool/Makefile.am @@ -126,6 +126,7 @@ nodist_libzpool_la_SOURCES = \ module/zfs/rrwlock.c \ module/zfs/sa.c \ module/zfs/sha2_zfs.c \ + module/zfs/slack.c \ module/zfs/skein_zfs.c \ module/zfs/spa.c \ module/zfs/spa_checkpoint.c \ diff --git a/man/man7/zfsprops.7 b/man/man7/zfsprops.7 index 06e2797968ea..11ef7af82eff 100644 --- a/man/man7/zfsprops.7 +++ b/man/man7/zfsprops.7 @@ -37,8 +37,9 @@ .\" Copyright 2019 Joyent, Inc. .\" Copyright (c) 2019, Kjeld Schouten-Lebbing .\" Copyright (c) 2022 Hewlett Packard Enterprise Development LP. +.\" Copyright (c) 2024, Klara, Inc. .\" -.Dd June 29, 2024 +.Dd November 18, 2024 .Dt ZFSPROPS 7 .Os . @@ -796,7 +797,7 @@ Changing this property affects only newly-written data. .It Xo .Sy compression Ns = Ns Sy on Ns | Ns Sy off Ns | Ns Sy gzip Ns | Ns .Sy gzip- Ns Ar N Ns | Ns Sy lz4 Ns | Ns Sy lzjb Ns | Ns Sy zle Ns | Ns Sy zstd Ns | Ns -.Sy zstd- Ns Ar N Ns | Ns Sy zstd-fast Ns | Ns Sy zstd-fast- Ns Ar N +.Sy zstd- Ns Ar N Ns | Ns Sy zstd-fast Ns | Ns Sy zstd-fast- Ns | Ns Sy slack Ns Ar N .Xc Controls the compression algorithm used for this dataset. .Pp @@ -906,6 +907,11 @@ The .Sy zle compression algorithm compresses runs of zeros. .Pp +The +.Sy slack +compression algorithm removes runs of zeroes from the end of blocks. +It is useful when using very large block sizes with incompressible data. +.Pp This property can also be referred to by its shortened column name .Sy compress . Changing this property affects only newly-written data. diff --git a/man/man7/zpool-features.7 b/man/man7/zpool-features.7 index 7b392a896150..81a04e55e0f1 100644 --- a/man/man7/zpool-features.7 +++ b/man/man7/zpool-features.7 @@ -18,7 +18,7 @@ .\" Copyright (c) 2019, Allan Jude .\" Copyright (c) 2021, Colm Buckley .\" -.Dd October 2, 2024 +.Dd November 18, 2024 .Dt ZPOOL-FEATURES 7 .Os . @@ -932,6 +932,41 @@ preventing hash collision attacks on systems with dedup. .Pp .checksum-spiel skein . +.feature com.klarasystems slack_compress no extensible_dataset +.Sy slack +is a compression option that simply removes the trailing run of zero bytes at +the end of each block. +It is designed for when very large block sizes are used with to store largely +incompressible data. +Normally in this scenario +.Sy compress Ns = Ns Sy none +would be used, +but with large block sizes that can often leave a multi-megabyte run of zeroes +at the end of a block, adding memory and checksumming overhead. +.Pp +When the +.Sy slack_compress +feature is set to +.Sy enabled , +the administrator can turn on +.Sy slack +compression of any dataset using +.Nm zfs Cm set Sy compress Ns = Ns Sy slack Ar dset +.Po see Xr zfs-set 8 Pc . +This feature becomes +.Sy active +once a +.Sy compress +property has been set to +.Sy slack , +and will return to being +.Sy enabled +once all filesystems that have ever had their +.Sy compress +property set to +.Sy slack +are destroyed. +. .feature com.delphix spacemap_histogram yes This features allows ZFS to maintain more information about how free space is organized within the pool. diff --git a/man/man8/zstream.8 b/man/man8/zstream.8 index c09a20ad20b6..e8417a045af0 100644 --- a/man/man8/zstream.8 +++ b/man/man8/zstream.8 @@ -104,6 +104,7 @@ Valid compression types include .Sy gzip , .Sy lz4 , .Sy lzjb , +.Sy slack , .Sy zstd , and .Sy zle . diff --git a/module/Kbuild.in b/module/Kbuild.in index fc14d5cb535e..afe9f8d7e8de 100644 --- a/module/Kbuild.in +++ b/module/Kbuild.in @@ -369,6 +369,7 @@ ZFS_OBJS := \ sa.o \ sha2_zfs.o \ skein_zfs.o \ + slack.o \ spa.o \ spa_checkpoint.o \ spa_config.o \ diff --git a/module/Makefile.bsd b/module/Makefile.bsd index c605069d07d3..a8e4429abca4 100644 --- a/module/Makefile.bsd +++ b/module/Makefile.bsd @@ -297,6 +297,7 @@ SRCS+= abd.c \ sa.c \ sha2_zfs.c \ skein_zfs.c \ + slack.c \ spa.c \ space_map.c \ space_reftree.c \ diff --git a/module/zcommon/zfeature_common.c b/module/zcommon/zfeature_common.c index 96f0086d7858..d543c509ad01 100644 --- a/module/zcommon/zfeature_common.c +++ b/module/zcommon/zfeature_common.c @@ -785,6 +785,20 @@ zpool_feature_init(void) ZFEATURE_TYPE_BOOLEAN, large_microzap_deps, sfeatures); } + { + { + static const spa_feature_t slack_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_SLACK_COMPRESS, + "com.klarasystems:slack_compress", "slack_compress", + "slack compression support", + ZFEATURE_FLAG_PER_DATASET, ZFEATURE_TYPE_BOOLEAN, + slack_deps, sfeatures); + } + } + zfs_mod_list_supported_free(sfeatures); } diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 40254c8d9567..221fd310201f 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -131,6 +131,7 @@ zfs_prop_init(void) { "gzip-9", ZIO_COMPRESS_GZIP_9 }, { "zle", ZIO_COMPRESS_ZLE }, { "lz4", ZIO_COMPRESS_LZ4 }, + { "slack", ZIO_COMPRESS_SLACK }, { "zstd", ZIO_COMPRESS_ZSTD }, { "zstd-fast", ZIO_COMPLEVEL_ZSTD(ZIO_ZSTD_LEVEL_FAST_DEFAULT) }, diff --git a/module/zfs/slack.c b/module/zfs/slack.c new file mode 100644 index 000000000000..e7422778cc63 --- /dev/null +++ b/module/zfs/slack.c @@ -0,0 +1,74 @@ +/* + * 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) 2024, Klara, Inc. + */ + +#include +#include + +/* + * Slack compression simply searches for the last non-zero byte in the buffer, + * and sets the position as the size of the "compressed" data. + */ + +static size_t +zfs_slack_compress_buf(void *src, void *dst, size_t s_len, size_t d_len, + int level) +{ + (void) level; + + ASSERT3U(s_len, >, 0); + ASSERT0(P2PHASE(s_len, sizeof (uint64_t))); + + uint64_t *buf = (uint64_t *)src; + + int p = (s_len / sizeof (uint64_t)) - 1; + for (; p >= 0; p--) + if (buf[p] != 0) + break; + + if (p < 0) + return (s_len); + + size_t c_len = (p + 1) * sizeof (uint64_t); + if (c_len > d_len) + return (s_len); + + memcpy(dst, src, c_len); + return (c_len); +} + +static int +zfs_slack_decompress_buf(void *src, void *dst, size_t s_len, size_t d_len, + int level) +{ + (void) level; + ASSERT3U(d_len, >=, s_len); + memcpy(dst, src, s_len); + if (d_len > s_len) + memset(dst+s_len, 0, d_len-s_len); + return (0); +} + +ZFS_COMPRESS_WRAP_DECL(zfs_slack_compress) +ZFS_DECOMPRESS_WRAP_DECL(zfs_slack_decompress) diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index b1b0ae54460b..a7c0f406ffa5 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -4843,6 +4843,20 @@ zfs_check_settable(const char *dsname, nvpair_t *pair, cred_t *cr) } spa_close(spa, FTAG); } + + if (compval == ZIO_COMPRESS_SLACK) { + spa_t *spa; + + if ((err = spa_open(dsname, &spa, FTAG)) != 0) + return (err); + + if (!spa_feature_is_enabled(spa, + SPA_FEATURE_SLACK_COMPRESS)) { + spa_close(spa, FTAG); + return (SET_ERROR(ENOTSUP)); + } + spa_close(spa, FTAG); + } } break; diff --git a/module/zfs/zio_compress.c b/module/zfs/zio_compress.c index 1a0178eb2830..ed9f908841cd 100644 --- a/module/zfs/zio_compress.c +++ b/module/zfs/zio_compress.c @@ -76,6 +76,8 @@ zio_compress_info_t zio_compress_table[ZIO_COMPRESS_FUNCTIONS] = { zfs_lz4_compress, zfs_lz4_decompress, NULL}, {"zstd", ZIO_ZSTD_LEVEL_DEFAULT, zfs_zstd_compress, zfs_zstd_decompress, zfs_zstd_decompress_level}, + {"slack", 0, + zfs_slack_compress, zfs_slack_decompress, NULL }, }; uint8_t @@ -188,6 +190,8 @@ zio_compress_to_feature(enum zio_compress comp) switch (comp) { case ZIO_COMPRESS_ZSTD: return (SPA_FEATURE_ZSTD_COMPRESS); + case ZIO_COMPRESS_SLACK: + return (SPA_FEATURE_SLACK_COMPRESS); default: break; } diff --git a/tests/zfs-tests/include/properties.shlib b/tests/zfs-tests/include/properties.shlib index 5a39eb3f36f5..3b4795f337b2 100644 --- a/tests/zfs-tests/include/properties.shlib +++ b/tests/zfs-tests/include/properties.shlib @@ -16,7 +16,7 @@ . $STF_SUITE/include/libtest.shlib -typeset -a compress_prop_vals=('off' 'lzjb' 'lz4' 'gzip' 'zle' 'zstd') +typeset -a compress_prop_vals=('off' 'lzjb' 'lz4' 'gzip' 'zle' 'zstd' 'slack') typeset -a checksum_prop_vals=('on' 'off' 'fletcher2' 'fletcher4' 'sha256' 'noparity' 'sha512' 'skein' 'blake3') if ! is_freebsd; then diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg index e5a8b9026e03..29bdb9fea29b 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg @@ -113,5 +113,6 @@ if is_linux || is_freebsd; then "feature@fast_dedup" "feature@longname" "feature@large_microzap" + "feature@slack_compress" ) fi