The main change in this cycle was to add support for ZSTD-compressed

kernel and initrd images.
 
 ZSTD has a very fast decompressor, yet it compresses better than gzip.
 
 Signed-off-by: Ingo Molnar <mingo@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAl8oNX0RHG1pbmdvQGtl
 cm5lbC5vcmcACgkQEnMQ0APhK1jdcg/9GaPGjmNgMqi3tbfzU3z11OrbraRBgMj5
 jHIZ89DuzwsqU+jbwGHGiF45ge85iPK6i2ovR3ePzL0LAlLYT3gqzPcl3kkog4E9
 0E0JAddx974uW4toc8cGFEHNf4vXtvvi45FL2yvDoap9xLEcpJsQRdu9upPB4U3s
 +qotO6wJitM74g4l2WdbStzCAcL4ZXFA/ix19nUyLh4QlFBDqUHwufIhW1G0ciL4
 txMXJ23L7e+b6FUvGyK3vFhba1isPdz5xQdQTy2DCK20rQhGu1IBsqzymEibbgIp
 /j4yHfUKSpxdblFcpZfknI1VM1mbt/WN5dKDKm9UnYBhA/R/2PN0klfrAQAT4SOS
 sP3bxXqTRXBjmop0NjOLCdjGCySYnPLFPlB6REIrMcvs6LYUSTqMZEusj7McwD7h
 IqS4zGEMa5A+c6Q4160Qz+zrXIyh/n/bTR/6uOKUktkUQaJ+079P64NK9RtCYZTk
 dkIHJChjmWZGxxXHEbo+4e7bM8gAMHDmX2pdWE5u72oYJRqBv7PVyl+SHBk+onxM
 crtKvqOp8Q8coirlfjx5UynZeZmH1VuIFjpvnwlAtqxZGvuTWZ0ojq3E3Y/XwHQj
 bVejr9AQ1gS9ZBTKwwd5cf7mnOuiXrHrBP3E7buoRw8bWtL+yqHyybqccZnSOUVN
 lGFshs+7J5o=
 =bARW
 -----END PGP SIGNATURE-----

Merge tag 'x86-boot-2020-08-03' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 boot updates from Ingo Molnar:
 "The main change in this cycle was to add support for ZSTD-compressed
  kernel and initrd images.

  ZSTD has a very fast decompressor, yet it compresses better than gzip"

* tag 'x86-boot-2020-08-03' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  Documentation: dontdiff: Add zstd compressed files
  .gitignore: Add ZSTD-compressed files
  x86: Add support for ZSTD compressed kernel
  x86: Bump ZO_z_extra_bytes margin for zstd
  usr: Add support for zstd compressed initramfs
  init: Add support for zstd compressed kernel
  lib: Add zstd support to decompress
  lib: Prepare zstd for preboot environment, improve performance
This commit is contained in:
Linus Torvalds 2020-08-03 16:03:23 -07:00
commit c0dfadfed8
21 changed files with 469 additions and 26 deletions

1
.gitignore vendored
View File

@ -44,6 +44,7 @@
*.tab.[ch] *.tab.[ch]
*.tar *.tar
*.xz *.xz
*.zst
Module.symvers Module.symvers
modules.builtin modules.builtin
modules.order modules.order

View File

@ -55,6 +55,7 @@
*.ver *.ver
*.xml *.xml
*.xz *.xz
*.zst
*_MODULES *_MODULES
*_vga16.c *_vga16.c
*~ *~

View File

@ -782,9 +782,9 @@ Protocol: 2.08+
uncompressed data should be determined using the standard magic uncompressed data should be determined using the standard magic
numbers. The currently supported compression formats are gzip numbers. The currently supported compression formats are gzip
(magic numbers 1F 8B or 1F 9E), bzip2 (magic number 42 5A), LZMA (magic numbers 1F 8B or 1F 9E), bzip2 (magic number 42 5A), LZMA
(magic number 5D 00), XZ (magic number FD 37), and LZ4 (magic number (magic number 5D 00), XZ (magic number FD 37), LZ4 (magic number
02 21). The uncompressed payload is currently always ELF (magic 02 21) and ZSTD (magic number 28 B5). The uncompressed payload is
number 7F 45 4C 46). currently always ELF (magic number 7F 45 4C 46).
============ ============== ============ ==============
Field name: payload_length Field name: payload_length

View File

@ -464,6 +464,7 @@ KLZOP = lzop
LZMA = lzma LZMA = lzma
LZ4 = lz4c LZ4 = lz4c
XZ = xz XZ = xz
ZSTD = zstd
CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \ CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ \
-Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF) -Wbitwise -Wno-return-void -Wno-unknown-attribute $(CF)
@ -512,7 +513,7 @@ CLANG_FLAGS :=
export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC export ARCH SRCARCH CONFIG_SHELL BASH HOSTCC KBUILD_HOSTCFLAGS CROSS_COMPILE LD CC
export CPP AR NM STRIP OBJCOPY OBJDUMP OBJSIZE READELF PAHOLE LEX YACC AWK INSTALLKERNEL export CPP AR NM STRIP OBJCOPY OBJDUMP OBJSIZE READELF PAHOLE LEX YACC AWK INSTALLKERNEL
export PERL PYTHON PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX export PERL PYTHON PYTHON3 CHECK CHECKFLAGS MAKE UTS_MACHINE HOSTCXX
export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ export KGZIP KBZIP2 KLZOP LZMA LZ4 XZ ZSTD
export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE export KBUILD_HOSTCXXFLAGS KBUILD_HOSTLDFLAGS KBUILD_HOSTLDLIBS LDFLAGS_MODULE
export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS KBUILD_LDFLAGS export KBUILD_CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS KBUILD_LDFLAGS

View File

@ -188,6 +188,7 @@ config X86
select HAVE_KERNEL_LZMA select HAVE_KERNEL_LZMA
select HAVE_KERNEL_LZO select HAVE_KERNEL_LZO
select HAVE_KERNEL_XZ select HAVE_KERNEL_XZ
select HAVE_KERNEL_ZSTD
select HAVE_KPROBES select HAVE_KPROBES
select HAVE_KPROBES_ON_FTRACE select HAVE_KPROBES_ON_FTRACE
select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_ERROR_INJECTION

View File

@ -26,7 +26,7 @@ OBJECT_FILES_NON_STANDARD := y
KCOV_INSTRUMENT := n KCOV_INSTRUMENT := n
targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \ targets := vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 vmlinux.bin.lzma \
vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4 vmlinux.bin.xz vmlinux.bin.lzo vmlinux.bin.lz4 vmlinux.bin.zst
KBUILD_CFLAGS := -m$(BITS) -O2 KBUILD_CFLAGS := -m$(BITS) -O2
KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC) KBUILD_CFLAGS += -fno-strict-aliasing $(call cc-option, -fPIE, -fPIC)
@ -42,6 +42,7 @@ KBUILD_CFLAGS += $(call cc-disable-warning, gnu)
KBUILD_CFLAGS += -Wno-pointer-sign KBUILD_CFLAGS += -Wno-pointer-sign
KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=) KBUILD_CFLAGS += $(call cc-option,-fmacro-prefix-map=$(srctree)/=)
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
KBUILD_CFLAGS += -D__DISABLE_EXPORTS
KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__ KBUILD_AFLAGS := $(KBUILD_CFLAGS) -D__ASSEMBLY__
GCOV_PROFILE := n GCOV_PROFILE := n
@ -145,6 +146,8 @@ $(obj)/vmlinux.bin.lzo: $(vmlinux.bin.all-y) FORCE
$(call if_changed,lzo) $(call if_changed,lzo)
$(obj)/vmlinux.bin.lz4: $(vmlinux.bin.all-y) FORCE $(obj)/vmlinux.bin.lz4: $(vmlinux.bin.all-y) FORCE
$(call if_changed,lz4) $(call if_changed,lz4)
$(obj)/vmlinux.bin.zst: $(vmlinux.bin.all-y) FORCE
$(call if_changed,zstd22)
suffix-$(CONFIG_KERNEL_GZIP) := gz suffix-$(CONFIG_KERNEL_GZIP) := gz
suffix-$(CONFIG_KERNEL_BZIP2) := bz2 suffix-$(CONFIG_KERNEL_BZIP2) := bz2
@ -152,6 +155,7 @@ suffix-$(CONFIG_KERNEL_LZMA) := lzma
suffix-$(CONFIG_KERNEL_XZ) := xz suffix-$(CONFIG_KERNEL_XZ) := xz
suffix-$(CONFIG_KERNEL_LZO) := lzo suffix-$(CONFIG_KERNEL_LZO) := lzo
suffix-$(CONFIG_KERNEL_LZ4) := lz4 suffix-$(CONFIG_KERNEL_LZ4) := lz4
suffix-$(CONFIG_KERNEL_ZSTD) := zst
quiet_cmd_mkpiggy = MKPIGGY $@ quiet_cmd_mkpiggy = MKPIGGY $@
cmd_mkpiggy = $(obj)/mkpiggy $< > $@ cmd_mkpiggy = $(obj)/mkpiggy $< > $@

View File

@ -19,13 +19,6 @@
*/ */
#define BOOT_CTYPE_H #define BOOT_CTYPE_H
/*
* _ctype[] in lib/ctype.c is needed by isspace() of linux/ctype.h.
* While both lib/ctype.c and lib/cmdline.c will bring EXPORT_SYMBOL
* which is meaningless and will cause compiling error in some cases.
*/
#define __DISABLE_EXPORTS
#include "misc.h" #include "misc.h"
#include "error.h" #include "error.h"
#include "../string.h" #include "../string.h"

View File

@ -77,6 +77,10 @@ static int lines, cols;
#ifdef CONFIG_KERNEL_LZ4 #ifdef CONFIG_KERNEL_LZ4
#include "../../../../lib/decompress_unlz4.c" #include "../../../../lib/decompress_unlz4.c"
#endif #endif
#ifdef CONFIG_KERNEL_ZSTD
#include "../../../../lib/decompress_unzstd.c"
#endif
/* /*
* NOTE: When adding a new decompressor, please update the analysis in * NOTE: When adding a new decompressor, please update the analysis in
* ../header.S. * ../header.S.

View File

@ -539,8 +539,14 @@ pref_address: .quad LOAD_PHYSICAL_ADDR # preferred load addr
# the size-dependent part now grows so fast. # the size-dependent part now grows so fast.
# #
# extra_bytes = (uncompressed_size >> 8) + 65536 # extra_bytes = (uncompressed_size >> 8) + 65536
#
# ZSTD compressed data grows by at most 3 bytes per 128K, and only has a 22
# byte fixed overhead but has a maximum block size of 128K, so it needs a
# larger margin.
#
# extra_bytes = (uncompressed_size >> 8) + 131072
#define ZO_z_extra_bytes ((ZO_z_output_len >> 8) + 65536) #define ZO_z_extra_bytes ((ZO_z_output_len >> 8) + 131072)
#if ZO_z_output_len > ZO_z_input_len #if ZO_z_output_len > ZO_z_input_len
# define ZO_z_extract_offset (ZO_z_output_len + ZO_z_extra_bytes - \ # define ZO_z_extract_offset (ZO_z_output_len + ZO_z_extra_bytes - \
ZO_z_input_len) ZO_z_input_len)

View File

@ -24,9 +24,16 @@
# error "Invalid value for CONFIG_PHYSICAL_ALIGN" # error "Invalid value for CONFIG_PHYSICAL_ALIGN"
#endif #endif
#ifdef CONFIG_KERNEL_BZIP2 #if defined(CONFIG_KERNEL_BZIP2)
# define BOOT_HEAP_SIZE 0x400000 # define BOOT_HEAP_SIZE 0x400000
#else /* !CONFIG_KERNEL_BZIP2 */ #elif defined(CONFIG_KERNEL_ZSTD)
/*
* Zstd needs to allocate the ZSTD_DCtx in order to decompress the kernel.
* The ZSTD_DCtx is ~160KB, so set the heap size to 192KB because it is a
* round number and to allow some slack.
*/
# define BOOT_HEAP_SIZE 0x30000
#else
# define BOOT_HEAP_SIZE 0x10000 # define BOOT_HEAP_SIZE 0x10000
#endif #endif

View File

@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef LINUX_DECOMPRESS_UNZSTD_H
#define LINUX_DECOMPRESS_UNZSTD_H
int unzstd(unsigned char *inbuf, long len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *output,
long *pos,
void (*error_fn)(char *x));
#endif

View File

@ -191,13 +191,16 @@ config HAVE_KERNEL_LZO
config HAVE_KERNEL_LZ4 config HAVE_KERNEL_LZ4
bool bool
config HAVE_KERNEL_ZSTD
bool
config HAVE_KERNEL_UNCOMPRESSED config HAVE_KERNEL_UNCOMPRESSED
bool bool
choice choice
prompt "Kernel compression mode" prompt "Kernel compression mode"
default KERNEL_GZIP default KERNEL_GZIP
depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO || HAVE_KERNEL_LZ4 || HAVE_KERNEL_UNCOMPRESSED depends on HAVE_KERNEL_GZIP || HAVE_KERNEL_BZIP2 || HAVE_KERNEL_LZMA || HAVE_KERNEL_XZ || HAVE_KERNEL_LZO || HAVE_KERNEL_LZ4 || HAVE_KERNEL_ZSTD || HAVE_KERNEL_UNCOMPRESSED
help help
The linux kernel is a kind of self-extracting executable. The linux kernel is a kind of self-extracting executable.
Several compression algorithms are available, which differ Several compression algorithms are available, which differ
@ -276,6 +279,16 @@ config KERNEL_LZ4
is about 8% bigger than LZO. But the decompression speed is is about 8% bigger than LZO. But the decompression speed is
faster than LZO. faster than LZO.
config KERNEL_ZSTD
bool "ZSTD"
depends on HAVE_KERNEL_ZSTD
help
ZSTD is a compression algorithm targeting intermediate compression
with fast decompression speed. It will compress better than GZIP and
decompress around the same speed as LZO, but slower than LZ4. You
will need at least 192 KB RAM or more for booting. The zstd command
line tool is required for compression.
config KERNEL_UNCOMPRESSED config KERNEL_UNCOMPRESSED
bool "None" bool "None"
depends on HAVE_KERNEL_UNCOMPRESSED depends on HAVE_KERNEL_UNCOMPRESSED

View File

@ -342,6 +342,10 @@ config DECOMPRESS_LZ4
select LZ4_DECOMPRESS select LZ4_DECOMPRESS
tristate tristate
config DECOMPRESS_ZSTD
select ZSTD_DECOMPRESS
tristate
# #
# Generic allocator support is selected if needed # Generic allocator support is selected if needed
# #

View File

@ -170,6 +170,7 @@ lib-$(CONFIG_DECOMPRESS_LZMA) += decompress_unlzma.o
lib-$(CONFIG_DECOMPRESS_XZ) += decompress_unxz.o lib-$(CONFIG_DECOMPRESS_XZ) += decompress_unxz.o
lib-$(CONFIG_DECOMPRESS_LZO) += decompress_unlzo.o lib-$(CONFIG_DECOMPRESS_LZO) += decompress_unlzo.o
lib-$(CONFIG_DECOMPRESS_LZ4) += decompress_unlz4.o lib-$(CONFIG_DECOMPRESS_LZ4) += decompress_unlz4.o
lib-$(CONFIG_DECOMPRESS_ZSTD) += decompress_unzstd.o
obj-$(CONFIG_TEXTSEARCH) += textsearch.o obj-$(CONFIG_TEXTSEARCH) += textsearch.o
obj-$(CONFIG_TEXTSEARCH_KMP) += ts_kmp.o obj-$(CONFIG_TEXTSEARCH_KMP) += ts_kmp.o

View File

@ -13,6 +13,7 @@
#include <linux/decompress/inflate.h> #include <linux/decompress/inflate.h>
#include <linux/decompress/unlzo.h> #include <linux/decompress/unlzo.h>
#include <linux/decompress/unlz4.h> #include <linux/decompress/unlz4.h>
#include <linux/decompress/unzstd.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/string.h> #include <linux/string.h>
@ -37,6 +38,9 @@
#ifndef CONFIG_DECOMPRESS_LZ4 #ifndef CONFIG_DECOMPRESS_LZ4
# define unlz4 NULL # define unlz4 NULL
#endif #endif
#ifndef CONFIG_DECOMPRESS_ZSTD
# define unzstd NULL
#endif
struct compress_format { struct compress_format {
unsigned char magic[2]; unsigned char magic[2];
@ -52,6 +56,7 @@ static const struct compress_format compressed_formats[] __initconst = {
{ {0xfd, 0x37}, "xz", unxz }, { {0xfd, 0x37}, "xz", unxz },
{ {0x89, 0x4c}, "lzo", unlzo }, { {0x89, 0x4c}, "lzo", unlzo },
{ {0x02, 0x21}, "lz4", unlz4 }, { {0x02, 0x21}, "lz4", unlz4 },
{ {0x28, 0xb5}, "zstd", unzstd },
{ {0, 0}, NULL, NULL } { {0, 0}, NULL, NULL }
}; };

345
lib/decompress_unzstd.c Normal file
View File

@ -0,0 +1,345 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Important notes about in-place decompression
*
* At least on x86, the kernel is decompressed in place: the compressed data
* is placed to the end of the output buffer, and the decompressor overwrites
* most of the compressed data. There must be enough safety margin to
* guarantee that the write position is always behind the read position.
*
* The safety margin for ZSTD with a 128 KB block size is calculated below.
* Note that the margin with ZSTD is bigger than with GZIP or XZ!
*
* The worst case for in-place decompression is that the beginning of
* the file is compressed extremely well, and the rest of the file is
* uncompressible. Thus, we must look for worst-case expansion when the
* compressor is encoding uncompressible data.
*
* The structure of the .zst file in case of a compresed kernel is as follows.
* Maximum sizes (as bytes) of the fields are in parenthesis.
*
* Frame Header: (18)
* Blocks: (N)
* Checksum: (4)
*
* The frame header and checksum overhead is at most 22 bytes.
*
* ZSTD stores the data in blocks. Each block has a header whose size is
* a 3 bytes. After the block header, there is up to 128 KB of payload.
* The maximum uncompressed size of the payload is 128 KB. The minimum
* uncompressed size of the payload is never less than the payload size
* (excluding the block header).
*
* The assumption, that the uncompressed size of the payload is never
* smaller than the payload itself, is valid only when talking about
* the payload as a whole. It is possible that the payload has parts where
* the decompressor consumes more input than it produces output. Calculating
* the worst case for this would be tricky. Instead of trying to do that,
* let's simply make sure that the decompressor never overwrites any bytes
* of the payload which it is currently reading.
*
* Now we have enough information to calculate the safety margin. We need
* - 22 bytes for the .zst file format headers;
* - 3 bytes per every 128 KiB of uncompressed size (one block header per
* block); and
* - 128 KiB (biggest possible zstd block size) to make sure that the
* decompressor never overwrites anything from the block it is currently
* reading.
*
* We get the following formula:
*
* safety_margin = 22 + uncompressed_size * 3 / 131072 + 131072
* <= 22 + (uncompressed_size >> 15) + 131072
*/
/*
* Preboot environments #include "path/to/decompress_unzstd.c".
* All of the source files we depend on must be #included.
* zstd's only source dependeny is xxhash, which has no source
* dependencies.
*
* When UNZSTD_PREBOOT is defined we declare __decompress(), which is
* used for kernel decompression, instead of unzstd().
*
* Define __DISABLE_EXPORTS in preboot environments to prevent symbols
* from xxhash and zstd from being exported by the EXPORT_SYMBOL macro.
*/
#ifdef STATIC
# define UNZSTD_PREBOOT
# include "xxhash.c"
# include "zstd/entropy_common.c"
# include "zstd/fse_decompress.c"
# include "zstd/huf_decompress.c"
# include "zstd/zstd_common.c"
# include "zstd/decompress.c"
#endif
#include <linux/decompress/mm.h>
#include <linux/kernel.h>
#include <linux/zstd.h>
/* 128MB is the maximum window size supported by zstd. */
#define ZSTD_WINDOWSIZE_MAX (1 << ZSTD_WINDOWLOG_MAX)
/*
* Size of the input and output buffers in multi-call mode.
* Pick a larger size because it isn't used during kernel decompression,
* since that is single pass, and we have to allocate a large buffer for
* zstd's window anyway. The larger size speeds up initramfs decompression.
*/
#define ZSTD_IOBUF_SIZE (1 << 17)
static int INIT handle_zstd_error(size_t ret, void (*error)(char *x))
{
const int err = ZSTD_getErrorCode(ret);
if (!ZSTD_isError(ret))
return 0;
switch (err) {
case ZSTD_error_memory_allocation:
error("ZSTD decompressor ran out of memory");
break;
case ZSTD_error_prefix_unknown:
error("Input is not in the ZSTD format (wrong magic bytes)");
break;
case ZSTD_error_dstSize_tooSmall:
case ZSTD_error_corruption_detected:
case ZSTD_error_checksum_wrong:
error("ZSTD-compressed data is corrupt");
break;
default:
error("ZSTD-compressed data is probably corrupt");
break;
}
return -1;
}
/*
* Handle the case where we have the entire input and output in one segment.
* We can allocate less memory (no circular buffer for the sliding window),
* and avoid some memcpy() calls.
*/
static int INIT decompress_single(const u8 *in_buf, long in_len, u8 *out_buf,
long out_len, long *in_pos,
void (*error)(char *x))
{
const size_t wksp_size = ZSTD_DCtxWorkspaceBound();
void *wksp = large_malloc(wksp_size);
ZSTD_DCtx *dctx = ZSTD_initDCtx(wksp, wksp_size);
int err;
size_t ret;
if (dctx == NULL) {
error("Out of memory while allocating ZSTD_DCtx");
err = -1;
goto out;
}
/*
* Find out how large the frame actually is, there may be junk at
* the end of the frame that ZSTD_decompressDCtx() can't handle.
*/
ret = ZSTD_findFrameCompressedSize(in_buf, in_len);
err = handle_zstd_error(ret, error);
if (err)
goto out;
in_len = (long)ret;
ret = ZSTD_decompressDCtx(dctx, out_buf, out_len, in_buf, in_len);
err = handle_zstd_error(ret, error);
if (err)
goto out;
if (in_pos != NULL)
*in_pos = in_len;
err = 0;
out:
if (wksp != NULL)
large_free(wksp);
return err;
}
static int INIT __unzstd(unsigned char *in_buf, long in_len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *out_buf, long out_len,
long *in_pos,
void (*error)(char *x))
{
ZSTD_inBuffer in;
ZSTD_outBuffer out;
ZSTD_frameParams params;
void *in_allocated = NULL;
void *out_allocated = NULL;
void *wksp = NULL;
size_t wksp_size;
ZSTD_DStream *dstream;
int err;
size_t ret;
if (out_len == 0)
out_len = LONG_MAX; /* no limit */
if (fill == NULL && flush == NULL)
/*
* We can decompress faster and with less memory when we have a
* single chunk.
*/
return decompress_single(in_buf, in_len, out_buf, out_len,
in_pos, error);
/*
* If in_buf is not provided, we must be using fill(), so allocate
* a large enough buffer. If it is provided, it must be at least
* ZSTD_IOBUF_SIZE large.
*/
if (in_buf == NULL) {
in_allocated = large_malloc(ZSTD_IOBUF_SIZE);
if (in_allocated == NULL) {
error("Out of memory while allocating input buffer");
err = -1;
goto out;
}
in_buf = in_allocated;
in_len = 0;
}
/* Read the first chunk, since we need to decode the frame header. */
if (fill != NULL)
in_len = fill(in_buf, ZSTD_IOBUF_SIZE);
if (in_len < 0) {
error("ZSTD-compressed data is truncated");
err = -1;
goto out;
}
/* Set the first non-empty input buffer. */
in.src = in_buf;
in.pos = 0;
in.size = in_len;
/* Allocate the output buffer if we are using flush(). */
if (flush != NULL) {
out_allocated = large_malloc(ZSTD_IOBUF_SIZE);
if (out_allocated == NULL) {
error("Out of memory while allocating output buffer");
err = -1;
goto out;
}
out_buf = out_allocated;
out_len = ZSTD_IOBUF_SIZE;
}
/* Set the output buffer. */
out.dst = out_buf;
out.pos = 0;
out.size = out_len;
/*
* We need to know the window size to allocate the ZSTD_DStream.
* Since we are streaming, we need to allocate a buffer for the sliding
* window. The window size varies from 1 KB to ZSTD_WINDOWSIZE_MAX
* (8 MB), so it is important to use the actual value so as not to
* waste memory when it is smaller.
*/
ret = ZSTD_getFrameParams(&params, in.src, in.size);
err = handle_zstd_error(ret, error);
if (err)
goto out;
if (ret != 0) {
error("ZSTD-compressed data has an incomplete frame header");
err = -1;
goto out;
}
if (params.windowSize > ZSTD_WINDOWSIZE_MAX) {
error("ZSTD-compressed data has too large a window size");
err = -1;
goto out;
}
/*
* Allocate the ZSTD_DStream now that we know how much memory is
* required.
*/
wksp_size = ZSTD_DStreamWorkspaceBound(params.windowSize);
wksp = large_malloc(wksp_size);
dstream = ZSTD_initDStream(params.windowSize, wksp, wksp_size);
if (dstream == NULL) {
error("Out of memory while allocating ZSTD_DStream");
err = -1;
goto out;
}
/*
* Decompression loop:
* Read more data if necessary (error if no more data can be read).
* Call the decompression function, which returns 0 when finished.
* Flush any data produced if using flush().
*/
if (in_pos != NULL)
*in_pos = 0;
do {
/*
* If we need to reload data, either we have fill() and can
* try to get more data, or we don't and the input is truncated.
*/
if (in.pos == in.size) {
if (in_pos != NULL)
*in_pos += in.pos;
in_len = fill ? fill(in_buf, ZSTD_IOBUF_SIZE) : -1;
if (in_len < 0) {
error("ZSTD-compressed data is truncated");
err = -1;
goto out;
}
in.pos = 0;
in.size = in_len;
}
/* Returns zero when the frame is complete. */
ret = ZSTD_decompressStream(dstream, &out, &in);
err = handle_zstd_error(ret, error);
if (err)
goto out;
/* Flush all of the data produced if using flush(). */
if (flush != NULL && out.pos > 0) {
if (out.pos != flush(out.dst, out.pos)) {
error("Failed to flush()");
err = -1;
goto out;
}
out.pos = 0;
}
} while (ret != 0);
if (in_pos != NULL)
*in_pos += in.pos;
err = 0;
out:
if (in_allocated != NULL)
large_free(in_allocated);
if (out_allocated != NULL)
large_free(out_allocated);
if (wksp != NULL)
large_free(wksp);
return err;
}
#ifndef UNZSTD_PREBOOT
STATIC int INIT unzstd(unsigned char *buf, long len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *out_buf,
long *pos,
void (*error)(char *x))
{
return __unzstd(buf, len, fill, flush, out_buf, 0, pos, error);
}
#else
STATIC int INIT __decompress(unsigned char *buf, long len,
long (*fill)(void*, unsigned long),
long (*flush)(void*, unsigned long),
unsigned char *out_buf, long out_len,
long *pos,
void (*error)(char *x))
{
return __unzstd(buf, len, fill, flush, out_buf, out_len, pos, error);
}
#endif

View File

@ -47,6 +47,7 @@
****************************************************************/ ****************************************************************/
#include "bitstream.h" #include "bitstream.h"
#include "fse.h" #include "fse.h"
#include "zstd_internal.h"
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/string.h> /* memcpy, memset */ #include <linux/string.h> /* memcpy, memset */
@ -60,14 +61,6 @@
enum { FSE_static_assert = 1 / (int)(!!(c)) }; \ enum { FSE_static_assert = 1 / (int)(!!(c)) }; \
} /* use only *after* variable declarations */ } /* use only *after* variable declarations */
/* check and forward error code */
#define CHECK_F(f) \
{ \
size_t const e = f; \
if (FSE_isError(e)) \
return e; \
}
/* ************************************************************** /* **************************************************************
* Templates * Templates
****************************************************************/ ****************************************************************/

View File

@ -127,7 +127,14 @@ static const U32 OF_defaultNormLog = OF_DEFAULTNORMLOG;
* Shared functions to include for inlining * Shared functions to include for inlining
*********************************************/ *********************************************/
ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) { ZSTD_STATIC void ZSTD_copy8(void *dst, const void *src) {
memcpy(dst, src, 8); /*
* zstd relies heavily on gcc being able to analyze and inline this
* memcpy() call, since it is called in a tight loop. Preboot mode
* is compiled in freestanding mode, which stops gcc from analyzing
* memcpy(). Use __builtin_memcpy() to tell gcc to analyze this as a
* regular memcpy().
*/
__builtin_memcpy(dst, src, 8);
} }
/*! ZSTD_wildcopy() : /*! ZSTD_wildcopy() :
* custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */ * custom version of memcpy(), can copy up to 7 bytes too many (8 bytes if length==0) */
@ -137,13 +144,16 @@ ZSTD_STATIC void ZSTD_wildcopy(void *dst, const void *src, ptrdiff_t length)
const BYTE* ip = (const BYTE*)src; const BYTE* ip = (const BYTE*)src;
BYTE* op = (BYTE*)dst; BYTE* op = (BYTE*)dst;
BYTE* const oend = op + length; BYTE* const oend = op + length;
/* Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388. #if defined(GCC_VERSION) && GCC_VERSION >= 70000 && GCC_VERSION < 70200
/*
* Work around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=81388.
* Avoid the bad case where the loop only runs once by handling the * Avoid the bad case where the loop only runs once by handling the
* special case separately. This doesn't trigger the bug because it * special case separately. This doesn't trigger the bug because it
* doesn't involve pointer/integer overflow. * doesn't involve pointer/integer overflow.
*/ */
if (length <= 8) if (length <= 8)
return ZSTD_copy8(dst, src); return ZSTD_copy8(dst, src);
#endif
do { do {
ZSTD_copy8(op, ip); ZSTD_copy8(op, ip);
op += 8; op += 8;

View File

@ -413,6 +413,28 @@ quiet_cmd_xzkern = XZKERN $@
quiet_cmd_xzmisc = XZMISC $@ quiet_cmd_xzmisc = XZMISC $@
cmd_xzmisc = cat $(real-prereqs) | $(XZ) --check=crc32 --lzma2=dict=1MiB > $@ cmd_xzmisc = cat $(real-prereqs) | $(XZ) --check=crc32 --lzma2=dict=1MiB > $@
# ZSTD
# ---------------------------------------------------------------------------
# Appends the uncompressed size of the data using size_append. The .zst
# format has the size information available at the beginning of the file too,
# but it's in a more complex format and it's good to avoid changing the part
# of the boot code that reads the uncompressed size.
#
# Note that the bytes added by size_append will make the zstd tool think that
# the file is corrupt. This is expected.
#
# zstd uses a maximum window size of 8 MB. zstd22 uses a maximum window size of
# 128 MB. zstd22 is used for kernel compression because it is decompressed in a
# single pass, so zstd doesn't need to allocate a window buffer. When streaming
# decompression is used, like initramfs decompression, zstd22 should likely not
# be used because it would require zstd to allocate a 128 MB buffer.
quiet_cmd_zstd = ZSTD $@
cmd_zstd = { cat $(real-prereqs) | $(ZSTD) -19; $(size_append); } > $@
quiet_cmd_zstd22 = ZSTD22 $@
cmd_zstd22 = { cat $(real-prereqs) | $(ZSTD) -22 --ultra; $(size_append); } > $@
# ASM offsets # ASM offsets
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View File

@ -100,6 +100,15 @@ config RD_LZ4
Support loading of a LZ4 encoded initial ramdisk or cpio buffer Support loading of a LZ4 encoded initial ramdisk or cpio buffer
If unsure, say N. If unsure, say N.
config RD_ZSTD
bool "Support initial ramdisk/ramfs compressed using ZSTD"
default y
depends on BLK_DEV_INITRD
select DECOMPRESS_ZSTD
help
Support loading of a ZSTD encoded initial ramdisk or cpio buffer.
If unsure, say N.
choice choice
prompt "Built-in initramfs compression mode" prompt "Built-in initramfs compression mode"
depends on INITRAMFS_SOURCE != "" depends on INITRAMFS_SOURCE != ""
@ -196,6 +205,17 @@ config INITRAMFS_COMPRESSION_LZ4
If you choose this, keep in mind that most distros don't provide lz4 If you choose this, keep in mind that most distros don't provide lz4
by default which could cause a build failure. by default which could cause a build failure.
config INITRAMFS_COMPRESSION_ZSTD
bool "ZSTD"
depends on RD_ZSTD
help
ZSTD is a compression algorithm targeting intermediate compression
with fast decompression speed. It will compress better than GZIP and
decompress around the same speed as LZO, but slower than LZ4.
If you choose this, keep in mind that you may need to install the zstd
tool to be able to compress the initram.
config INITRAMFS_COMPRESSION_NONE config INITRAMFS_COMPRESSION_NONE
bool "None" bool "None"
help help

View File

@ -15,6 +15,7 @@ compress-$(CONFIG_INITRAMFS_COMPRESSION_LZMA) := lzma
compress-$(CONFIG_INITRAMFS_COMPRESSION_XZ) := xzmisc compress-$(CONFIG_INITRAMFS_COMPRESSION_XZ) := xzmisc
compress-$(CONFIG_INITRAMFS_COMPRESSION_LZO) := lzo compress-$(CONFIG_INITRAMFS_COMPRESSION_LZO) := lzo
compress-$(CONFIG_INITRAMFS_COMPRESSION_LZ4) := lz4 compress-$(CONFIG_INITRAMFS_COMPRESSION_LZ4) := lz4
compress-$(CONFIG_INITRAMFS_COMPRESSION_ZSTD) := zstd
obj-$(CONFIG_BLK_DEV_INITRD) := initramfs_data.o obj-$(CONFIG_BLK_DEV_INITRD) := initramfs_data.o