1 /* 2 * BSD 3-Clause New License (https://spdx.org/licenses/BSD-3-Clause.html) 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * 1. Redistributions of source code must retain the above copyright notice, 8 * this list of conditions and the following disclaimer. 9 * 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * 3. Neither the name of the copyright holder nor the names of its 15 * contributors may be used to endorse or promote products derived from this 16 * software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /* 32 * Copyright (c) 2016-2018, Klara Inc. 33 * Copyright (c) 2016-2018, Allan Jude 34 * Copyright (c) 2018-2020, Sebastian Gottschall 35 * Copyright (c) 2019-2020, Michael Niewöhner 36 * Copyright (c) 2020, The FreeBSD Foundation [1] 37 * 38 * [1] Portions of this software were developed by Allan Jude 39 * under sponsorship from the FreeBSD Foundation. 40 */ 41 42 #ifndef _ZFS_ZSTD_H 43 #define _ZFS_ZSTD_H 44 45 #ifdef __cplusplus 46 extern "C" { 47 #endif 48 49 /* 50 * ZSTD block header 51 * NOTE: all fields in this header are in big endian order. 52 */ 53 typedef struct zfs_zstd_header { 54 /* Compressed size of data */ 55 uint32_t c_len; 56 57 /* 58 * Version and compression level 59 * We used to use a union to reference compression level 60 * and version easily, but as it turns out, relying on the 61 * ordering of bitfields is not remotely portable. 62 * So now we have get/set functions in zfs_zstd.c for 63 * manipulating this in just the right way forever. 64 */ 65 uint32_t raw_version_level; 66 char data[]; 67 } zfs_zstdhdr_t; 68 69 /* 70 * Simple struct to pass the data from raw_version_level around. 71 */ 72 typedef struct zfs_zstd_meta { 73 uint8_t level; 74 uint32_t version; 75 } zfs_zstdmeta_t; 76 77 /* 78 * kstat helper macros 79 */ 80 #define ZSTDSTAT(stat) (zstd_stats.stat.value.ui64) 81 #define ZSTDSTAT_ZERO(stat) \ 82 atomic_store_64(&zstd_stats.stat.value.ui64, 0) 83 #define ZSTDSTAT_ADD(stat, val) \ 84 atomic_add_64(&zstd_stats.stat.value.ui64, (val)) 85 #define ZSTDSTAT_SUB(stat, val) \ 86 atomic_sub_64(&zstd_stats.stat.value.ui64, (val)) 87 #define ZSTDSTAT_BUMP(stat) ZSTDSTAT_ADD(stat, 1) 88 89 /* (de)init for user space / kernel emulation */ 90 int zstd_init(void); 91 void zstd_fini(void); 92 93 size_t zfs_zstd_compress(void *s_start, void *d_start, size_t s_len, 94 size_t d_len, int level); 95 size_t zfs_zstd_compress_wrap(void *s_start, void *d_start, size_t s_len, 96 size_t d_len, int level); 97 int zfs_zstd_get_level(void *s_start, size_t s_len, uint8_t *level); 98 int zfs_zstd_decompress_level(void *s_start, void *d_start, size_t s_len, 99 size_t d_len, uint8_t *level); 100 int zfs_zstd_decompress(void *s_start, void *d_start, size_t s_len, 101 size_t d_len, int n); 102 void zfs_zstd_cache_reap_now(void); 103 104 /* 105 * So, the reason we have all these complicated set/get functions is that 106 * originally, in the zstd "header" we wrote out to disk, we used a 32-bit 107 * bitfield to store the "level" (8 bits) and "version" (24 bits). 108 * 109 * Unfortunately, bitfields make few promises about how they're arranged in 110 * memory... 111 * 112 * By way of example, if we were using version 1.4.5 and level 3, it'd be 113 * level = 0x03, version = 10405/0x0028A5, which gets broken into Vhigh = 0x00, 114 * Vmid = 0x28, Vlow = 0xA5. We include these positions below to help follow 115 * which data winds up where. 116 * 117 * As a consequence, we wound up with little endian platforms with a layout 118 * like this in memory: 119 * 120 * 0 8 16 24 32 121 * +-------+-------+-------+-------+ 122 * | Vlow | Vmid | Vhigh | level | 123 * +-------+-------+-------+-------+ 124 * =A5 =28 =00 =03 125 * 126 * ...and then, after being run through BE_32(), serializing this out to 127 * disk: 128 * 129 * 0 8 16 24 32 130 * +-------+-------+-------+-------+ 131 * | level | Vhigh | Vmid | Vlow | 132 * +-------+-------+-------+-------+ 133 * =03 =00 =28 =A5 134 * 135 * while on big-endian systems, since BE_32() is a noop there, both in 136 * memory and on disk, we wind up with: 137 * 138 * 0 8 16 24 32 139 * +-------+-------+-------+-------+ 140 * | Vhigh | Vmid | Vlow | level | 141 * +-------+-------+-------+-------+ 142 * =00 =28 =A5 =03 143 * 144 * (Vhigh is always 0 until version exceeds 6.55.35. Vmid and Vlow are the 145 * other two bytes of the "version" data.) 146 * 147 * So now we use the BF32_SET macros to get consistent behavior (the 148 * ondisk LE encoding, since x86 currently rules the world) across 149 * platforms, but the "get" behavior requires that we check each of the 150 * bytes in the aforementioned former-bitfield for 0x00, and from there, 151 * we can know which possible layout we're dealing with. (Only the two 152 * that have been observed in the wild are illustrated above, but handlers 153 * for all 4 positions of 0x00 are implemented. 154 */ 155 156 static inline void 157 zfs_get_hdrmeta(const zfs_zstdhdr_t *blob, zfs_zstdmeta_t *res) 158 { 159 uint32_t raw = blob->raw_version_level; 160 uint8_t findme = 0xff; 161 int shift; 162 for (shift = 0; shift < 4; shift++) { 163 findme = BF32_GET(raw, 8*shift, 8); 164 if (findme == 0) 165 break; 166 } 167 switch (shift) { 168 case 0: 169 res->level = BF32_GET(raw, 24, 8); 170 res->version = BSWAP_32(raw); 171 res->version = BF32_GET(res->version, 8, 24); 172 break; 173 case 1: 174 res->level = BF32_GET(raw, 0, 8); 175 res->version = BSWAP_32(raw); 176 res->version = BF32_GET(res->version, 0, 24); 177 break; 178 case 2: 179 res->level = BF32_GET(raw, 24, 8); 180 res->version = BF32_GET(raw, 0, 24); 181 break; 182 case 3: 183 res->level = BF32_GET(raw, 0, 8); 184 res->version = BF32_GET(raw, 8, 24); 185 break; 186 default: 187 res->level = 0; 188 res->version = 0; 189 break; 190 } 191 } 192 193 static inline uint8_t 194 zfs_get_hdrlevel(const zfs_zstdhdr_t *blob) 195 { 196 uint8_t level = 0; 197 zfs_zstdmeta_t res; 198 zfs_get_hdrmeta(blob, &res); 199 level = res.level; 200 return (level); 201 } 202 203 static inline uint32_t 204 zfs_get_hdrversion(const zfs_zstdhdr_t *blob) 205 { 206 uint32_t version = 0; 207 zfs_zstdmeta_t res; 208 zfs_get_hdrmeta(blob, &res); 209 version = res.version; 210 return (version); 211 212 } 213 214 static inline void 215 zfs_set_hdrversion(zfs_zstdhdr_t *blob, uint32_t version) 216 { 217 /* cppcheck-suppress syntaxError */ 218 BF32_SET(blob->raw_version_level, 0, 24, version); 219 } 220 221 static inline void 222 zfs_set_hdrlevel(zfs_zstdhdr_t *blob, uint8_t level) 223 { 224 /* cppcheck-suppress syntaxError */ 225 BF32_SET(blob->raw_version_level, 24, 8, level); 226 } 227 228 229 #ifdef __cplusplus 230 } 231 #endif 232 233 #endif /* _ZFS_ZSTD_H */ 234