Re: [PATCH] Add Zstandard support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

On Fri, Aug 21, 2020 at 3:46 PM Torge Matthies
<openglfreak@xxxxxxxxxxxxxx> wrote:
>
> I was not able to find any existing zstd patch for kmod, so I wrote my own.

thanks a lot for adding this.

>
> I was struggling to get the C code below the 80 character line length limit,
> that's why I used gotos. I used descriptive label names, and it still looks
> clean enough in my opinion.

Humn.. I'd rather prefer not to be on 80 chars than using some of the gotos,
but take a look below

>
> I changed the style of the hackargs variable in autogen.sh to multiline
> because said line was becoming a bit long with the new --with-zstd arg
> added.

ok

>
> This patch has been running on my two Arch Linux installations (with an
> accompanying mkinitcpio patch) for several months over many kernel updates
> without any issues.

great. Is Arch already using zstd for the kernel? Once this lands, is
it going to
zstd for modules?

+Dave


> Any additional testing and/or patch review would of course be appreciated.
>
> Signed-off-by: Torge Matthies <openglfreak@xxxxxxxxxxxxxx>
> ---
>  .semaphore/semaphore.yml     |   4 +-
>  .travis.yml                  |   1 +
>  Makefile.am                  |   4 +-
>  autogen.sh                   |   9 ++-
>  configure.ac                 |  13 +++-
>  libkmod/libkmod-file.c       | 119 +++++++++++++++++++++++++++++++++++
>  libkmod/libkmod.pc.in        |   2 +-
>  shared/util.c                |   3 +
>  testsuite/mkosi/mkosi.fedora |   1 +
>  testsuite/test-util.c        |   3 +
>  10 files changed, 153 insertions(+), 6 deletions(-)
>
> diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml
> index db47ca1..fe1d78a 100644
> --- a/.semaphore/semaphore.yml
> +++ b/.semaphore/semaphore.yml
> @@ -22,7 +22,7 @@ blocks:
>        prologue:
>          commands:
>            - sudo apt update
> -          - sudo apt --yes install docbook-xsl liblzma-dev zlib1g-dev cython linux-headers-generic libssl-dev
> +          - sudo apt --yes install docbook-xsl libzstd-dev liblzma-dev zlib1g-dev cython linux-headers-generic libssl-dev
>            - checkout
>
>        epilogue:
> @@ -42,5 +42,5 @@ blocks:
>        prologue:
>          commands:
>            - sudo apt update
> -          - sudo apt --yes install docbook-xsl liblzma-dev zlib1g-dev cython linux-headers-generic libssl-dev
> +          - sudo apt --yes install docbook-xsl libzstd-dev liblzma-dev zlib1g-dev cython linux-headers-generic libssl-dev
>            - checkout
> diff --git a/.travis.yml b/.travis.yml
> index 02c753e..0fcce14 100644
> --- a/.travis.yml
> +++ b/.travis.yml
> @@ -14,6 +14,7 @@ matrix:
>  before_install:
>    - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
>    - sudo apt-get update -qq
> +  - sudo apt-get install -qq libzstd-dev
>    - sudo apt-get install -qq liblzma-dev
>    - sudo apt-get install -qq zlib1g-dev
>    - sudo apt-get install -qq xsltproc docbook-xsl
> diff --git a/Makefile.am b/Makefile.am
> index 8eadb99..37d840b 100644
> --- a/Makefile.am
> +++ b/Makefile.am
> @@ -31,6 +31,8 @@ SED_PROCESS = \
>         -e 's,@exec_prefix\@,$(exec_prefix),g' \
>         -e 's,@libdir\@,$(libdir),g' \
>         -e 's,@includedir\@,$(includedir),g' \
> +       -e 's,@libzstd_CFLAGS\@,${libzstd_CFLAGS},g' \
> +       -e 's,@libzstd_LIBS\@,${libzstd_LIBS},g' \
>         -e 's,@liblzma_CFLAGS\@,${liblzma_CFLAGS},g' \
>         -e 's,@liblzma_LIBS\@,${liblzma_LIBS},g' \
>         -e 's,@zlib_CFLAGS\@,${zlib_CFLAGS},g' \
> @@ -90,7 +92,7 @@ libkmod_libkmod_la_DEPENDENCIES = \
>         ${top_srcdir}/libkmod/libkmod.sym
>  libkmod_libkmod_la_LIBADD = \
>         shared/libshared.la \
> -       ${liblzma_LIBS} ${zlib_LIBS} ${libcrypto_LIBS}
> +       ${libzstd_LIBS} ${liblzma_LIBS} ${zlib_LIBS} ${libcrypto_LIBS}
>
>  noinst_LTLIBRARIES += libkmod/libkmod-internal.la
>  libkmod_libkmod_internal_la_SOURCES = $(libkmod_libkmod_la_SOURCES)
> diff --git a/autogen.sh b/autogen.sh
> index 67b119f..e4997c4 100755
> --- a/autogen.sh
> +++ b/autogen.sh
> @@ -32,7 +32,14 @@ fi
>
>  cd $oldpwd
>
> -hackargs="--enable-debug --enable-python --with-xz --with-zlib --with-openssl"
> +hackargs="\
> +--enable-debug \
> +--enable-python \
> +--with-zstd \
> +--with-xz \
> +--with-zlib \
> +--with-openssl \
> +"
>
>  if [ "x$1" = "xc" ]; then
>          shift
> diff --git a/configure.ac b/configure.ac
> index 4a65d6b..a49f6b9 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -83,6 +83,17 @@ AC_ARG_WITH([rootlibdir],
>          [], [with_rootlibdir=$libdir])
>  AC_SUBST([rootlibdir], [$with_rootlibdir])
>
> +AC_ARG_WITH([zstd],
> +       AS_HELP_STRING([--with-zstd], [handle Zstandard-compressed modules @<:@default=disabled@:>@]),
> +       [], [with_zstd=no])
> +AS_IF([test "x$with_zstd" != "xno"], [
> +       PKG_CHECK_MODULES([libzstd], [libzstd >= 1.4.4])
> +       AC_DEFINE([ENABLE_ZSTD], [1], [Enable Zstandard for modules.])
> +], [
> +       AC_MSG_NOTICE([Zstandard support not requested])
> +])
> +CC_FEATURE_APPEND([with_features], [with_zstd], [ZSTD])
> +
>  AC_ARG_WITH([xz],
>         AS_HELP_STRING([--with-xz], [handle Xz-compressed modules @<:@default=disabled@:>@]),
>         [], [with_xz=no])
> @@ -307,7 +318,7 @@ AC_MSG_RESULT([
>         tools:                  ${enable_tools}
>         python bindings:        ${enable_python}
>         logging:                ${enable_logging}
> -       compression:            xz=${with_xz}  zlib=${with_zlib}
> +       compression:            zstd=${with_zstd}  xz=${with_xz}  zlib=${with_zlib}
>         debug:                  ${enable_debug}
>         coverage:               ${enable_coverage}
>         doc:                    ${enable_gtk_doc}
> diff --git a/libkmod/libkmod-file.c b/libkmod/libkmod-file.c
> index 5eeba6a..1a2e753 100644
> --- a/libkmod/libkmod-file.c
> +++ b/libkmod/libkmod-file.c
> @@ -26,6 +26,9 @@
>  #include <sys/stat.h>
>  #include <sys/types.h>
>  #include <unistd.h>
> +#ifdef ENABLE_ZSTD
> +#include <zstd.h>
> +#endif
>  #ifdef ENABLE_XZ
>  #include <lzma.h>
>  #endif
> @@ -45,6 +48,9 @@ struct file_ops {
>  };
>
>  struct kmod_file {
> +#ifdef ENABLE_ZSTD
> +       bool zstd_used;
> +#endif
>  #ifdef ENABLE_XZ
>         bool xz_used;
>  #endif
> @@ -60,6 +66,116 @@ struct kmod_file {
>         struct kmod_elf *elf;
>  };
>
> +#ifdef ENABLE_ZSTD
> +static int load_zstd(struct kmod_file *file)
> +{
> +       ZSTD_DStream *dstr = NULL;
> +       size_t in_buf_size, out_buf_min_size, out_buf_size;
> +       uint8_t *in_buf = NULL, *out_buf = NULL;
> +       ZSTD_inBuffer zst_input;
> +       ZSTD_outBuffer zst_output;
> +       int ret = -EINVAL;
> +
> +       dstr = ZSTD_createDStream();
> +       if (dstr == NULL) {
> +               ERR(file->ctx, "zstd: Failed to create decompression stream\n");
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       in_buf_size = ZSTD_initDStream(dstr);
> +       in_buf = malloc(in_buf_size);
> +       if (in_buf == NULL) {
> +               ERR(file->ctx, "zstd: %s\n", strerror(ENOMEM));

no big deal, but malloc() should set errno, so you could use
ret = -errno before printing and then use %m in the message.

Same thing in other places.

> +               ret = -ENOMEM;
> +               goto out;
> +       }
> +
> +       out_buf_size = out_buf_min_size = ZSTD_DStreamOutSize();
> +       out_buf = malloc(out_buf_size);
> +       if (out_buf == NULL) {
> +               ERR(file->ctx, "zstd: %s\n", strerror(ENOMEM));
> +               ret = -ENOMEM;
> +               goto out;
> +       }
> +
> +       zst_input.src = in_buf;
> +       zst_output.dst = out_buf;
> +       zst_output.size = out_buf_size;
> +       zst_output.pos = 0;
> +       while (true) {
> +               ssize_t rdret;
> +               size_t dsret;
> +               uint8_t *tmp;
> +
> +               rdret = read(file->fd, in_buf, in_buf_size);
> +               if (rdret < 0) {
> +                       ret = -errno;
> +                       goto out;
> +               }
> +               if (rdret == 0)
> +                       break;
> +
> +               zst_input.size = rdret;
> +               zst_input.pos = 0;
> +
> +               if (zst_output.size - zst_output.pos < out_buf_min_size)
> +                       goto realloc_buffer;

this could be a helper function to realloc the buffer. It can even be shared
for the first alloc if you initialize zst_output.size to 0.. Then you
have something like:

> +
> +try_decompress:
> +               dsret = ZSTD_decompressStream(dstr, &zst_output, &zst_input);
> +               if (ZSTD_isError(dsret)) {
> +                       ERR(file->ctx, "zstd: %s\n", ZSTD_getErrorName(dsret));
> +                       ret = -EINVAL;
> +                       goto out;
> +               }
> +
> +               if (zst_output.pos < zst_output.size
> +                       && zst_output.size - zst_output.pos
> +                       >= out_buf_min_size) {
> +                       if (zst_input.pos < zst_input.size)
> +                               goto try_decompress;

this looks like a while/for loop. Could even be in a helper function


> +                       else
> +                               continue;
> +               }
> +
> +realloc_buffer:
> +               tmp = realloc(out_buf, out_buf_size + out_buf_min_size);
> +               if (tmp == NULL) {
> +                       ret = -errno;
> +                       goto out;
> +               }
> +               out_buf_size += out_buf_min_size;
> +               out_buf = tmp;
> +               zst_output.dst = out_buf;
> +               zst_output.size = out_buf_size;
> +               goto try_decompress;
> +       }
> +
> +       ZSTD_freeDStream(dstr);
> +       free(in_buf);
> +       file->zstd_used = true;
> +       file->memory = out_buf;
> +       file->size = zst_output.pos;
> +       return 0;
> +out:
> +       if (dstr != NULL)
> +               ZSTD_freeDStream(dstr);
> +       free(out_buf);
> +       free(in_buf);
> +       return ret;
> +}
> +
> +static void unload_zstd(struct kmod_file *file)
> +{
> +       if (!file->zstd_used)
> +               return;
> +       free(file->memory);
> +}
> +
> +static const char magic_zstd[] = {0x28, 0xB5, 0x2F, 0xFD};
> +#endif
> +
>  #ifdef ENABLE_XZ
>  static void xz_uncompress_belch(struct kmod_file *file, lzma_ret ret)
>  {
> @@ -238,6 +354,9 @@ static const struct comp_type {
>         const char *magic_bytes;
>         const struct file_ops ops;
>  } comp_types[] = {
> +#ifdef ENABLE_ZSTD
> +       {sizeof(magic_zstd), magic_zstd, {load_zstd, unload_zstd}},
> +#endif
>  #ifdef ENABLE_XZ
>         {sizeof(magic_xz), magic_xz, {load_xz, unload_xz}},
>  #endif
> diff --git a/libkmod/libkmod.pc.in b/libkmod/libkmod.pc.in
> index e4fdf21..3acca6a 100644
> --- a/libkmod/libkmod.pc.in
> +++ b/libkmod/libkmod.pc.in
> @@ -7,5 +7,5 @@ Name: libkmod
>  Description: Library to deal with kernel modules
>  Version: @VERSION@
>  Libs: -L${libdir} -lkmod
> -Libs.private: @liblzma_LIBS@ @zlib_LIBS@
> +Libs.private: @libzstd_LIBS@ @liblzma_LIBS@ @zlib_LIBS@
>  Cflags: -I${includedir}
> diff --git a/shared/util.c b/shared/util.c
> index fd2028d..b487b5f 100644
> --- a/shared/util.c
> +++ b/shared/util.c
> @@ -45,6 +45,9 @@ static const struct kmod_ext {
>  #endif
>  #ifdef ENABLE_XZ
>         {".ko.xz", sizeof(".ko.xz") - 1},
> +#endif
> +#ifdef ENABLE_ZSTD
> +       {".ko.zst", sizeof(".ko.zst") - 1},
>  #endif
>         { }
>  };
> diff --git a/testsuite/mkosi/mkosi.fedora b/testsuite/mkosi/mkosi.fedora
> index 5252f87..7a2ee5e 100644
> --- a/testsuite/mkosi/mkosi.fedora
> +++ b/testsuite/mkosi/mkosi.fedora
> @@ -19,6 +19,7 @@ BuildPackages =
>         make
>         pkgconf-pkg-config
>         xml-common
> +       libzstd-devel
>         xz-devel
>         zlib-devel
>         openssl-devel
> diff --git a/testsuite/test-util.c b/testsuite/test-util.c
> index 5e25e58..621446b 100644
> --- a/testsuite/test-util.c
> +++ b/testsuite/test-util.c
> @@ -156,6 +156,9 @@ static int test_path_ends_with_kmod_ext(const struct test *t)
>  #endif
>  #ifdef ENABLE_XZ
>                 { "/bla.ko.xz", true },
> +#endif
> +#ifdef ENABLE_ZSTD
> +               { "/bla.ko.zst", true },

I think we want a zstd-compressed module in the testsuite as well to test this.

thanks
Lucas De Marchi

>  #endif
>                 { "/bla.ko.x", false },
>                 { "/bla.ko.", false },
> --
> 2.28.0
>



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux