zstd is becoming a popular zlib replacement because it both tends to
get a better compression ratio as well performs considerably
better. As such, zstd is now one of the options that can be used to
compress the Linux kernel.
Lets enable it by using a system provided shared library, creating the
required boilerplate to match the existing zlib/lzma function
signatures, and creating build options to enable/disable it.
Signed-off-by: Jeremy Linton <jeremy.linton@xxxxxxx>
---
configure.ac | 10 +++
kexec/Makefile | 4 +-
kexec/kexec-zstd.h | 7 ++
kexec/zstd.c | 191 +++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 211 insertions(+), 1 deletion(-)
create mode 100644 kexec/kexec-zstd.h
create mode 100644 kexec/zstd.c
diff --git a/configure.ac b/configure.ac
index e030302..43cdb64 100644
--- a/configure.ac
+++ b/configure.ac
@@ -106,6 +106,9 @@ AC_ARG_WITH([zlib], AS_HELP_STRING([--without-zlib],[disable zlib support]),
AC_ARG_WITH([lzma], AS_HELP_STRING([--without-lzma],[disable lzma support]),
[ with_lzma="$withval"], [ with_lzma=yes ] )
+AC_ARG_WITH([zstd], AS_HELP_STRING([--without-zstd],[disable zstd support]),
+ [ with_zstd="$withval"], [ with_zstd=yes ] )
+
AC_ARG_WITH([xen], AS_HELP_STRING([--without-xen],
[disable extended xen support]), [ with_xen="$withval"], [ with_xen=yes ] )
@@ -180,6 +183,13 @@ if test "$with_lzma" = yes ; then
AC_MSG_NOTICE([lzma support disabled]))])
fi
+dnl See if I have a usable copy of zstd available
+if test "$with_zstd" = yes ; then
+ AC_CHECK_HEADER(zstd.h,
+ [AC_CHECK_LIB(zstd, ZSTD_decompress, ,
+ AC_MSG_NOTICE([zstd support disabled]))])
+fi
+
dnl find Xen control stack libraries
if test "$with_xen" = yes ; then
AC_CHECK_HEADER(xenctrl.h,
diff --git a/kexec/Makefile b/kexec/Makefile
index d4f26d7..e969d1e 100644
--- a/kexec/Makefile
+++ b/kexec/Makefile
@@ -26,6 +26,7 @@ KEXEC_SRCS_base += kexec/crashdump-xen.c
KEXEC_SRCS_base += kexec/phys_arch.c
KEXEC_SRCS_base += kexec/lzma.c
KEXEC_SRCS_base += kexec/zlib.c
+KEXEC_SRCS_base += kexec/zstd.c
KEXEC_SRCS_base += kexec/kexec-xen.c
KEXEC_SRCS_base += kexec/symbols.c
@@ -36,7 +37,8 @@ dist += kexec/Makefile \
kexec/crashdump.h kexec/firmware_memmap.h \
kexec/kexec-elf-boot.h \
kexec/kexec-elf.h kexec/kexec-sha256.h \
- kexec/kexec-zlib.h kexec/kexec-lzma.h \
+ kexec/kexec-zlib.h kexec/kexec-lzma.h \
+ kexec/kexec-zstd.h \
kexec/kexec-xen.h \
kexec/kexec-syscall.h kexec/kexec.h kexec/kexec.8
diff --git a/kexec/kexec-zstd.h b/kexec/kexec-zstd.h
new file mode 100644
index 0000000..c5dbb24
--- /dev/null
+++ b/kexec/kexec-zstd.h
@@ -0,0 +1,7 @@
+#ifndef __KEXEC_ZSTD_H
+#define __KEXEC_ZSTD_H
+
+#include <sys/types.h>
+
+char *zstd_decompress_file(const char *filename, off_t *r_size);
+#endif /* __KEXEC_ZSTD_H */
diff --git a/kexec/zstd.c b/kexec/zstd.c
new file mode 100644
index 0000000..9309251
--- /dev/null
+++ b/kexec/zstd.c
@@ -0,0 +1,191 @@
+#include "kexec-zstd.h"
+#include "kexec.h"
+
+#include "config.h"
+
+#ifdef HAVE_LIBZSTD
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <zstd.h>
+
+
+/*
+ * Reimplementation of private function available if lib is statically linked.
+ * Remove when said function becomes public.
+ */
+unsigned ZSTD_isFrame(const void* buffer, size_t size)
+{
+ uint8_t *buf = (uint8_t *)buffer;
+ /* Magic zstd frame header value */
+ if ((buf[0] == 0x28) &&
+ (buf[1] == 0xB5) &&
+ (buf[2] == 0x2F) &&
+ (buf[3] == 0xFD))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * A guess at the nax compression ratio for buffer overallocation
+ * Real values are frequently around 4:1, if this is wrong
+ * it just results in buffer reallocation and the decompression
+ * operation being restarted from where it left off.
+ */
+#define EXPECTED_RATIO 8
+
+/*
+ * This supports the streaming compression mode the kernel uses
+ * that can result in multiple zstd frames comprising a single
+ * compressed image. In order too be a bit more efficient than
+ * the libz/lzma implementations, we attempt to discover the input
+ * and output image sizes before performing the decompression.
+ * But, in streaming mode the first frame may not have a length
+ * or it seems the length could be incorrect if multiple frames
+ * are appended together. So, its written with buffer resize logic
+ * but a guess at the compression ratio is made to avoid the resize/copy
+ * operation. Ideally this code efficiently allocates the
+ * correct input buffer, and no more than 2-3x the output buffer
+ * size so that it can perform the decompress operation with a
+ * single decompress call.
+ */
+char *zstd_decompress_file(const char *filename, off_t *r_size)
+{
+ bool again;
+ void *cBuff = NULL;
+ FILE *fp = NULL; /* use c streams to match gzip/lzma logic */
+ struct stat fp_stats;
+ uint8_t magic[4];
+ ZSTD_DCtx* dctx = NULL;
+
+ ZSTD_outBuffer output = { NULL, 0, 0 };
+ ZSTD_inBuffer input = { NULL, 0, 0 };
+
+
+ *r_size = 0;
+ if (!filename) {
+ return NULL;
+ }
+ if (stat(filename, &fp_stats))
+ {