228 lines
7.0 KiB
C
228 lines
7.0 KiB
C
/*
|
|
* BSD 3-Clause New License (https://spdx.org/licenses/BSD-3-Clause.html)
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of the copyright holder nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2016-2018, Klara Inc.
|
|
* Copyright (c) 2016-2018, Allan Jude
|
|
* Copyright (c) 2018-2020, Sebastian Gottschall
|
|
* Copyright (c) 2019-2020, Michael Niewöhner
|
|
* Copyright (c) 2020, The FreeBSD Foundation [1]
|
|
*
|
|
* [1] Portions of this software were developed by Allan Jude
|
|
* under sponsorship from the FreeBSD Foundation.
|
|
*/
|
|
|
|
#ifndef _ZFS_ZSTD_H
|
|
#define _ZFS_ZSTD_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/*
|
|
* ZSTD block header
|
|
* NOTE: all fields in this header are in big endian order.
|
|
*/
|
|
typedef struct zfs_zstd_header {
|
|
/* Compressed size of data */
|
|
uint32_t c_len;
|
|
|
|
/*
|
|
* Version and compression level
|
|
* We used to use a union to reference compression level
|
|
* and version easily, but as it turns out, relying on the
|
|
* ordering of bitfields is not remotely portable.
|
|
* So now we have get/set functions in zfs_zstd.c for
|
|
* manipulating this in just the right way forever.
|
|
*/
|
|
uint32_t raw_version_level;
|
|
char data[];
|
|
} zfs_zstdhdr_t;
|
|
|
|
/*
|
|
* Simple struct to pass the data from raw_version_level around.
|
|
*/
|
|
typedef struct zfs_zstd_meta {
|
|
uint8_t level;
|
|
uint32_t version;
|
|
} zfs_zstdmeta_t;
|
|
|
|
/*
|
|
* kstat helper macros
|
|
*/
|
|
#define ZSTDSTAT(stat) (zstd_stats.stat.value.ui64)
|
|
#define ZSTDSTAT_ADD(stat, val) \
|
|
atomic_add_64(&zstd_stats.stat.value.ui64, (val))
|
|
#define ZSTDSTAT_SUB(stat, val) \
|
|
atomic_sub_64(&zstd_stats.stat.value.ui64, (val))
|
|
#define ZSTDSTAT_BUMP(stat) ZSTDSTAT_ADD(stat, 1)
|
|
|
|
/* (de)init for user space / kernel emulation */
|
|
int zstd_init(void);
|
|
void zstd_fini(void);
|
|
|
|
size_t zfs_zstd_compress(void *s_start, void *d_start, size_t s_len,
|
|
size_t d_len, int level);
|
|
int zfs_zstd_get_level(void *s_start, size_t s_len, uint8_t *level);
|
|
int zfs_zstd_decompress_level(void *s_start, void *d_start, size_t s_len,
|
|
size_t d_len, uint8_t *level);
|
|
int zfs_zstd_decompress(void *s_start, void *d_start, size_t s_len,
|
|
size_t d_len, int n);
|
|
void zfs_zstd_cache_reap_now(void);
|
|
|
|
/*
|
|
* So, the reason we have all these complicated set/get functions is that
|
|
* originally, in the zstd "header" we wrote out to disk, we used a 32-bit
|
|
* bitfield to store the "level" (8 bits) and "version" (24 bits).
|
|
*
|
|
* Unfortunately, bitfields make few promises about how they're arranged in
|
|
* memory...
|
|
*
|
|
* By way of example, if we were using version 1.4.5 and level 3, it'd be
|
|
* level = 0x03, version = 10405/0x0028A5, which gets broken into Vhigh = 0x00,
|
|
* Vmid = 0x28, Vlow = 0xA5. We include these positions below to help follow
|
|
* which data winds up where.
|
|
*
|
|
* As a consequence, we wound up with little endian platforms with a layout
|
|
* like this in memory:
|
|
*
|
|
* 0 8 16 24 32
|
|
* +-------+-------+-------+-------+
|
|
* | Vlow | Vmid | Vhigh | level |
|
|
* +-------+-------+-------+-------+
|
|
* =A5 =28 =00 =03
|
|
*
|
|
* ...and then, after being run through BE_32(), serializing this out to
|
|
* disk:
|
|
*
|
|
* 0 8 16 24 32
|
|
* +-------+-------+-------+-------+
|
|
* | level | Vhigh | Vmid | Vlow |
|
|
* +-------+-------+-------+-------+
|
|
* =03 =00 =28 =A5
|
|
*
|
|
* while on big-endian systems, since BE_32() is a noop there, both in
|
|
* memory and on disk, we wind up with:
|
|
*
|
|
* 0 8 16 24 32
|
|
* +-------+-------+-------+-------+
|
|
* | Vhigh | Vmid | Vlow | level |
|
|
* +-------+-------+-------+-------+
|
|
* =00 =28 =A5 =03
|
|
*
|
|
* (Vhigh is always 0 until version exceeds 6.55.35. Vmid and Vlow are the
|
|
* other two bytes of the "version" data.)
|
|
*
|
|
* So now we use the BF32_SET macros to get consistent behavior (the
|
|
* ondisk LE encoding, since x86 currently rules the world) across
|
|
* platforms, but the "get" behavior requires that we check each of the
|
|
* bytes in the aforementioned former-bitfield for 0x00, and from there,
|
|
* we can know which possible layout we're dealing with. (Only the two
|
|
* that have been observed in the wild are illustrated above, but handlers
|
|
* for all 4 positions of 0x00 are implemented.
|
|
*/
|
|
|
|
static inline void
|
|
zfs_get_hdrmeta(const zfs_zstdhdr_t *blob, zfs_zstdmeta_t *res)
|
|
{
|
|
uint32_t raw = blob->raw_version_level;
|
|
uint8_t findme = 0xff;
|
|
int shift;
|
|
for (shift = 0; shift < 4; shift++) {
|
|
findme = BF32_GET(raw, 8*shift, 8);
|
|
if (findme == 0)
|
|
break;
|
|
}
|
|
switch (shift) {
|
|
case 0:
|
|
res->level = BF32_GET(raw, 24, 8);
|
|
res->version = BSWAP_32(raw);
|
|
res->version = BF32_GET(res->version, 8, 24);
|
|
break;
|
|
case 1:
|
|
res->level = BF32_GET(raw, 0, 8);
|
|
res->version = BSWAP_32(raw);
|
|
res->version = BF32_GET(res->version, 0, 24);
|
|
break;
|
|
case 2:
|
|
res->level = BF32_GET(raw, 24, 8);
|
|
res->version = BF32_GET(raw, 0, 24);
|
|
break;
|
|
case 3:
|
|
res->level = BF32_GET(raw, 0, 8);
|
|
res->version = BF32_GET(raw, 8, 24);
|
|
break;
|
|
default:
|
|
res->level = 0;
|
|
res->version = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static inline uint8_t
|
|
zfs_get_hdrlevel(const zfs_zstdhdr_t *blob)
|
|
{
|
|
uint8_t level = 0;
|
|
zfs_zstdmeta_t res;
|
|
zfs_get_hdrmeta(blob, &res);
|
|
level = res.level;
|
|
return (level);
|
|
}
|
|
|
|
static inline uint32_t
|
|
zfs_get_hdrversion(const zfs_zstdhdr_t *blob)
|
|
{
|
|
uint32_t version = 0;
|
|
zfs_zstdmeta_t res;
|
|
zfs_get_hdrmeta(blob, &res);
|
|
version = res.version;
|
|
return (version);
|
|
|
|
}
|
|
|
|
static inline void
|
|
zfs_set_hdrversion(zfs_zstdhdr_t *blob, uint32_t version)
|
|
{
|
|
BF32_SET(blob->raw_version_level, 0, 24, version);
|
|
}
|
|
|
|
static inline void
|
|
zfs_set_hdrlevel(zfs_zstdhdr_t *blob, uint8_t level)
|
|
{
|
|
BF32_SET(blob->raw_version_level, 24, 8, level);
|
|
}
|
|
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* _ZFS_ZSTD_H */
|