2021-03-16 20:31:23

by Rui Salvaterra

[permalink] [raw]
Subject: [RFC PATCH v2] jffs2: add support for zstd compression

Implement support for zstd compression in jffs2 at the default compression
level (3).

Lightly tested in OpenWrt, on a single CPU embedded MIPS32 system (AirGrid M2).

Signed-off-by: Rui Salvaterra <[email protected]>
---
v2: fix blunder in compression context allocation

fs/jffs2/Kconfig | 9 +++
fs/jffs2/Makefile | 1 +
fs/jffs2/compr.c | 10 +++
fs/jffs2/compr.h | 6 ++
fs/jffs2/compr_zstd.c | 131 +++++++++++++++++++++++++++++++++++++
fs/jffs2/super.c | 7 ++
include/uapi/linux/jffs2.h | 1 +
7 files changed, 165 insertions(+)
create mode 100644 fs/jffs2/compr_zstd.c

diff --git a/fs/jffs2/Kconfig b/fs/jffs2/Kconfig
index 7c96bc107218..31308eb7267b 100644
--- a/fs/jffs2/Kconfig
+++ b/fs/jffs2/Kconfig
@@ -136,6 +136,15 @@ config JFFS2_LZO
This feature was added in July, 2007. Say 'N' if you need
compatibility with older bootloaders or kernels.

+config JFFS2_ZSTD
+ bool "JFFS2 zstd compression support" if JFFS2_COMPRESSION_OPTIONS
+ select ZSTD_COMPRESS
+ select ZSTD_DECOMPRESS
+ depends on JFFS2_FS
+ default n
+ help
+ Zstd compression.
+
config JFFS2_RTIME
bool "JFFS2 RTIME compression support" if JFFS2_COMPRESSION_OPTIONS
depends on JFFS2_FS
diff --git a/fs/jffs2/Makefile b/fs/jffs2/Makefile
index 5294969d5bf9..75f84b1467c5 100644
--- a/fs/jffs2/Makefile
+++ b/fs/jffs2/Makefile
@@ -19,4 +19,5 @@ jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o
jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o
jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o
jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o
+jffs2-$(CONFIG_JFFS2_ZSTD) += compr_zstd.o
jffs2-$(CONFIG_JFFS2_SUMMARY) += summary.o
diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c
index 4849a4c9a0e2..d65e0c39c9c5 100644
--- a/fs/jffs2/compr.c
+++ b/fs/jffs2/compr.c
@@ -237,6 +237,10 @@ uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
ret = jffs2_selected_compress(JFFS2_COMPR_ZLIB, data_in,
cpage_out, datalen, cdatalen);
break;
+ case JFFS2_COMPR_MODE_FORCEZSTD:
+ ret = jffs2_selected_compress(JFFS2_COMPR_ZSTD, data_in,
+ cpage_out, datalen, cdatalen);
+ break;
default:
pr_err("unknown compression mode\n");
}
@@ -378,6 +382,9 @@ int __init jffs2_compressors_init(void)
#ifdef CONFIG_JFFS2_LZO
jffs2_lzo_init();
#endif
+#ifdef CONFIG_JFFS2_ZSTD
+ jffs2_zstd_init();
+#endif
/* Setting default compression mode */
#ifdef CONFIG_JFFS2_CMODE_NONE
jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
@@ -413,6 +420,9 @@ int jffs2_compressors_exit(void)
#endif
#ifdef CONFIG_JFFS2_ZLIB
jffs2_zlib_exit();
+#endif
+#ifdef CONFIG_JFFS2_ZSTD
+ jffs2_zstd_exit();
#endif
return 0;
}
diff --git a/fs/jffs2/compr.h b/fs/jffs2/compr.h
index 5e91d578f4ed..8f7032c5ecb2 100644
--- a/fs/jffs2/compr.h
+++ b/fs/jffs2/compr.h
@@ -31,6 +31,7 @@
#define JFFS2_RTIME_PRIORITY 50
#define JFFS2_ZLIB_PRIORITY 60
#define JFFS2_LZO_PRIORITY 80
+#define JFFS2_ZSTD_PRIORITY 90


#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only */
@@ -42,6 +43,7 @@
#define JFFS2_COMPR_MODE_FAVOURLZO 3
#define JFFS2_COMPR_MODE_FORCELZO 4
#define JFFS2_COMPR_MODE_FORCEZLIB 5
+#define JFFS2_COMPR_MODE_FORCEZSTD 6

#define FAVOUR_LZO_PERCENT 80

@@ -101,5 +103,9 @@ void jffs2_zlib_exit(void);
int jffs2_lzo_init(void);
void jffs2_lzo_exit(void);
#endif
+#ifdef CONFIG_JFFS2_ZSTD
+int jffs2_zstd_init(void);
+void jffs2_zstd_exit(void);
+#endif

#endif /* __JFFS2_COMPR_H__ */
diff --git a/fs/jffs2/compr_zstd.c b/fs/jffs2/compr_zstd.c
new file mode 100644
index 000000000000..bfe4607575da
--- /dev/null
+++ b/fs/jffs2/compr_zstd.c
@@ -0,0 +1,131 @@
+
+#include <linux/zstd.h>
+#include "compr.h"
+
+#define ZSTD_DEF_LEVEL 3
+
+static ZSTD_CCtx *cctx;
+static ZSTD_DCtx *dctx;
+static void *cwksp;
+static void *dwksp;
+
+static ZSTD_parameters zstd_params(void)
+{
+ return ZSTD_getParams(ZSTD_DEF_LEVEL, 0, 0);
+}
+
+static int zstd_comp_init(void)
+{
+ int ret = 0;
+ const ZSTD_parameters params = zstd_params();
+ const size_t wksp_size = ZSTD_CCtxWorkspaceBound(params.cParams);
+
+ cwksp = vzalloc(wksp_size);
+ if (!cwksp) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ cctx = ZSTD_initCCtx(cwksp, wksp_size);
+ if (!cctx) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+out:
+ return ret;
+out_free:
+ vfree(cwksp);
+ goto out;
+}
+
+static int zstd_decomp_init(void)
+{
+ int ret = 0;
+ const size_t wksp_size = ZSTD_DCtxWorkspaceBound();
+
+ dwksp = vzalloc(wksp_size);
+ if (!dwksp) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ dctx = ZSTD_initDCtx(dwksp, wksp_size);
+ if (!dctx) {
+ ret = -EINVAL;
+ goto out_free;
+ }
+out:
+ return ret;
+out_free:
+ vfree(dwksp);
+ goto out;
+}
+
+static void zstd_comp_exit(void)
+{
+ vfree(cwksp);
+ cwksp = NULL;
+ cctx = NULL;
+}
+
+static void zstd_decomp_exit(void)
+{
+ vfree(dwksp);
+ dwksp = NULL;
+ dctx = NULL;
+}
+
+static int jffs2_zstd_compress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t *sourcelen, uint32_t *dstlen)
+{
+ size_t out_len;
+ const ZSTD_parameters params = zstd_params();
+
+ out_len = ZSTD_compressCCtx(cctx, cpage_out, *dstlen, data_in, *sourcelen, params);
+ if (ZSTD_isError(out_len) || out_len > *dstlen)
+ return -1;
+ *dstlen = out_len;
+ return 0;
+}
+
+static int jffs2_zstd_decompress(unsigned char *data_in, unsigned char *cpage_out,
+ uint32_t srclen, uint32_t destlen)
+{
+ size_t out_len;
+
+ out_len = ZSTD_decompressDCtx(dctx, cpage_out, destlen, data_in, srclen);
+ if (ZSTD_isError(out_len) || out_len != destlen)
+ return -1;
+
+ return 0;
+}
+
+static struct jffs2_compressor jffs2_zstd_comp = {
+ .priority = JFFS2_ZSTD_PRIORITY,
+ .name = "zstd",
+ .compr = JFFS2_COMPR_ZSTD,
+ .compress = &jffs2_zstd_compress,
+ .decompress = &jffs2_zstd_decompress,
+ .disabled = 0,
+};
+
+int __init jffs2_zstd_init(void)
+{
+ int ret;
+
+ ret = zstd_comp_init();
+ if (ret)
+ return ret;
+ ret = zstd_decomp_init();
+ if (ret)
+ zstd_comp_exit();
+ ret = jffs2_register_compressor(&jffs2_zstd_comp);
+ return ret;
+}
+
+void jffs2_zstd_exit(void)
+{
+ jffs2_unregister_compressor(&jffs2_zstd_comp);
+ zstd_comp_exit();
+ zstd_decomp_exit();
+}
diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c
index 81ca58c10b72..ddce95c55dde 100644
--- a/fs/jffs2/super.c
+++ b/fs/jffs2/super.c
@@ -73,6 +73,10 @@ static const char *jffs2_compr_name(unsigned int compr)
#ifdef CONFIG_JFFS2_ZLIB
case JFFS2_COMPR_MODE_FORCEZLIB:
return "zlib";
+#endif
+#ifdef CONFIG_JFFS2_ZSTD
+ case JFFS2_COMPR_MODE_FORCEZSTD:
+ return "zstd";
#endif
default:
/* should never happen; programmer error */
@@ -174,6 +178,9 @@ static const struct constant_table jffs2_param_compr[] = {
#endif
#ifdef CONFIG_JFFS2_ZLIB
{"zlib", JFFS2_COMPR_MODE_FORCEZLIB },
+#endif
+#ifdef CONFIG_JFFS2_ZSTD
+ {"zstd", JFFS2_COMPR_MODE_FORCEZSTD },
#endif
{}
};
diff --git a/include/uapi/linux/jffs2.h b/include/uapi/linux/jffs2.h
index 784ba0b9690a..af4fb69c8d69 100644
--- a/include/uapi/linux/jffs2.h
+++ b/include/uapi/linux/jffs2.h
@@ -46,6 +46,7 @@
#define JFFS2_COMPR_DYNRUBIN 0x05
#define JFFS2_COMPR_ZLIB 0x06
#define JFFS2_COMPR_LZO 0x07
+#define JFFS2_COMPR_ZSTD 0x08
/* Compatibility flags. */
#define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
#define JFFS2_NODE_ACCURATE 0x2000
--
2.31.0


2021-03-25 14:48:56

by Rui Salvaterra

[permalink] [raw]
Subject: Re: [RFC PATCH v2] jffs2: add support for zstd compression

Friendly ping (and also cc'ing dwmw2).

On Tue, 16 Mar 2021 at 14:19, Rui Salvaterra <[email protected]> wrote:
>
> Implement support for zstd compression in jffs2 at the default compression
> level (3).
>
> Lightly tested in OpenWrt, on a single CPU embedded MIPS32 system (AirGrid M2).

2021-04-26 07:57:38

by Rui Salvaterra

[permalink] [raw]
Subject: Re: [RFC PATCH v2] jffs2: add support for zstd compression

On Thu, 25 Mar 2021 at 14:47, Rui Salvaterra <[email protected]> wrote:
>
> Friendly ping (and also cc'ing dwmw2).

Ping^2, now the merge window is closed.

2021-04-26 08:25:23

by David Woodhouse

[permalink] [raw]
Subject: Re: [RFC PATCH v2] jffs2: add support for zstd compression

On Thu, 2021-03-25 at 14:47 +0000, Rui Salvaterra wrote:
> Friendly ping (and also cc'ing dwmw2).
>
> On Tue, 16 Mar 2021 at 14:19, Rui Salvaterra <[email protected]> wrote:
> >
> > Implement support for zstd compression in jffs2 at the default compression
> > level (3).
> >
> > Lightly tested in OpenWrt, on a single CPU embedded MIPS32 system (AirGrid M2).

Seems reasonable. Do you have the corresponding addition for mkfs.jffs2
and can you report the boot time / image sizes with it vs. zlib on a
typical platform?

And if it doesn't make a difference to boot time or image size... why
are we doing it?


Attachments:
smime.p7s (5.05 kB)

2021-04-26 08:53:02

by Rui Salvaterra

[permalink] [raw]
Subject: Re: [RFC PATCH v2] jffs2: add support for zstd compression

Hi, David,

On Mon, 26 Apr 2021 at 09:24, David Woodhouse <[email protected]> wrote:
>
> On Thu, 2021-03-25 at 14:47 +0000, Rui Salvaterra wrote:
> > Friendly ping (and also cc'ing dwmw2).
> >
> > On Tue, 16 Mar 2021 at 14:19, Rui Salvaterra <[email protected]> wrote:
> > >
> > > Implement support for zstd compression in jffs2 at the default compression
> > > level (3).
> > >
> > > Lightly tested in OpenWrt, on a single CPU embedded MIPS32 system (AirGrid M2).
>
> Seems reasonable. Do you have the corresponding addition for mkfs.jffs2
> and can you report the boot time / image sizes with it vs. zlib on a
> typical platform?

I haven't done any changes to userspace, other than OpenWrt's fstools
(to force-enable zstd compression at mount time, with compr=zstd).

> And if it doesn't make a difference to boot time or image size... why
> are we doing it?

In OpenWrt, we're carrying an enormous patch [1] which enables lzma
compression in (and only in) jffs2. Having a single compression
algorithm with good all-around performance, usable by other subsystems
(e.g. zram, ubifs) would allow us to disable other redundant
algorithms and consequently reduce the total image size (I measured an
increase of nearly 200 kB by the lzma patch alone).

[1] https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob;f=target/linux/generic/pending-5.10/530-jffs2_make_lzma_available.patch;h=1bccb30a69f69ff8e2e70a747b1f3b25d1079c8f;hb=HEAD