f2fs: compress: support zstd compress algorithm
authorChao Yu <yuchao0@huawei.com>
Tue, 3 Mar 2020 09:46:02 +0000 (17:46 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Fri, 3 Apr 2020 17:21:10 +0000 (10:21 -0700)
Add zstd compress algorithm support, use "compress_algorithm=zstd"
mountoption to enable it.

Signed-off-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
Documentation/filesystems/f2fs.txt
fs/f2fs/Kconfig
fs/f2fs/compress.c
fs/f2fs/f2fs.h
fs/f2fs/super.c
include/trace/events/f2fs.h

index 4eb3e2ddd00ec082e8ce8cee6b7b252a535e95e1..b1a66cf0e9678cd2df1692c34f0d3c36728aac89 100644 (file)
@@ -235,8 +235,8 @@ checkpoint=%s[:%u[%]]     Set to "disable" to turn off checkpointing. Set to "en
                        hide up to all remaining free space. The actual space that
                        would be unusable can be viewed at /sys/fs/f2fs/<disk>/unusable
                        This space is reclaimed once checkpoint=enable.
-compress_algorithm=%s  Control compress algorithm, currently f2fs supports "lzo"
-                       and "lz4" algorithm.
+compress_algorithm=%s  Control compress algorithm, currently f2fs supports "lzo",
+                       "lz4" and "zstd" algorithm.
 compress_log_size=%u   Support configuring compress cluster size, the size will
                        be 4KB * (1 << %u), 16KB is minimum size, also it's
                        default size.
index f0faada30f30af12edca4434cca85149c911f91a..bb68d21e1f8c699dc196f93152c26b027209de07 100644 (file)
@@ -118,3 +118,12 @@ config F2FS_FS_LZ4
        default y
        help
          Support LZ4 compress algorithm, if unsure, say Y.
+
+config F2FS_FS_ZSTD
+       bool "ZSTD compression support"
+       depends on F2FS_FS_COMPRESSION
+       select ZSTD_COMPRESS
+       select ZSTD_DECOMPRESS
+       default y
+       help
+         Support ZSTD compress algorithm, if unsure, say Y.
index 13666735f56757c06cfd431ab0fa3bc0d8b62fc7..c7284dd7d52faeac15545e8da7362b81427a4a0f 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/backing-dev.h>
 #include <linux/lzo.h>
 #include <linux/lz4.h>
+#include <linux/zstd.h>
 
 #include "f2fs.h"
 #include "node.h"
@@ -291,6 +292,165 @@ static const struct f2fs_compress_ops f2fs_lz4_ops = {
 };
 #endif
 
+#ifdef CONFIG_F2FS_FS_ZSTD
+#define F2FS_ZSTD_DEFAULT_CLEVEL       1
+
+static int zstd_init_compress_ctx(struct compress_ctx *cc)
+{
+       ZSTD_parameters params;
+       ZSTD_CStream *stream;
+       void *workspace;
+       unsigned int workspace_size;
+
+       params = ZSTD_getParams(F2FS_ZSTD_DEFAULT_CLEVEL, cc->rlen, 0);
+       workspace_size = ZSTD_CStreamWorkspaceBound(params.cParams);
+
+       workspace = f2fs_kvmalloc(F2FS_I_SB(cc->inode),
+                                       workspace_size, GFP_NOFS);
+       if (!workspace)
+               return -ENOMEM;
+
+       stream = ZSTD_initCStream(params, 0, workspace, workspace_size);
+       if (!stream) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD_initCStream failed\n",
+                               KERN_ERR, F2FS_I_SB(cc->inode)->sb->s_id,
+                               __func__);
+               kvfree(workspace);
+               return -EIO;
+       }
+
+       cc->private = workspace;
+       cc->private2 = stream;
+
+       cc->clen = cc->rlen - PAGE_SIZE - COMPRESS_HEADER_SIZE;
+       return 0;
+}
+
+static void zstd_destroy_compress_ctx(struct compress_ctx *cc)
+{
+       kvfree(cc->private);
+       cc->private = NULL;
+       cc->private2 = NULL;
+}
+
+static int zstd_compress_pages(struct compress_ctx *cc)
+{
+       ZSTD_CStream *stream = cc->private2;
+       ZSTD_inBuffer inbuf;
+       ZSTD_outBuffer outbuf;
+       int src_size = cc->rlen;
+       int dst_size = src_size - PAGE_SIZE - COMPRESS_HEADER_SIZE;
+       int ret;
+
+       inbuf.pos = 0;
+       inbuf.src = cc->rbuf;
+       inbuf.size = src_size;
+
+       outbuf.pos = 0;
+       outbuf.dst = cc->cbuf->cdata;
+       outbuf.size = dst_size;
+
+       ret = ZSTD_compressStream(stream, &outbuf, &inbuf);
+       if (ZSTD_isError(ret)) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD_compressStream failed, ret: %d\n",
+                               KERN_ERR, F2FS_I_SB(cc->inode)->sb->s_id,
+                               __func__, ZSTD_getErrorCode(ret));
+               return -EIO;
+       }
+
+       ret = ZSTD_endStream(stream, &outbuf);
+       if (ZSTD_isError(ret)) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD_endStream returned %d\n",
+                               KERN_ERR, F2FS_I_SB(cc->inode)->sb->s_id,
+                               __func__, ZSTD_getErrorCode(ret));
+               return -EIO;
+       }
+
+       cc->clen = outbuf.pos;
+       return 0;
+}
+
+static int zstd_init_decompress_ctx(struct decompress_io_ctx *dic)
+{
+       ZSTD_DStream *stream;
+       void *workspace;
+       unsigned int workspace_size;
+
+       workspace_size = ZSTD_DStreamWorkspaceBound(MAX_COMPRESS_WINDOW_SIZE);
+
+       workspace = f2fs_kvmalloc(F2FS_I_SB(dic->inode),
+                                       workspace_size, GFP_NOFS);
+       if (!workspace)
+               return -ENOMEM;
+
+       stream = ZSTD_initDStream(MAX_COMPRESS_WINDOW_SIZE,
+                                       workspace, workspace_size);
+       if (!stream) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD_initDStream failed\n",
+                               KERN_ERR, F2FS_I_SB(dic->inode)->sb->s_id,
+                               __func__);
+               kvfree(workspace);
+               return -EIO;
+       }
+
+       dic->private = workspace;
+       dic->private2 = stream;
+
+       return 0;
+}
+
+static void zstd_destroy_decompress_ctx(struct decompress_io_ctx *dic)
+{
+       kvfree(dic->private);
+       dic->private = NULL;
+       dic->private2 = NULL;
+}
+
+static int zstd_decompress_pages(struct decompress_io_ctx *dic)
+{
+       ZSTD_DStream *stream = dic->private2;
+       ZSTD_inBuffer inbuf;
+       ZSTD_outBuffer outbuf;
+       int ret;
+
+       inbuf.pos = 0;
+       inbuf.src = dic->cbuf->cdata;
+       inbuf.size = dic->clen;
+
+       outbuf.pos = 0;
+       outbuf.dst = dic->rbuf;
+       outbuf.size = dic->rlen;
+
+       ret = ZSTD_decompressStream(stream, &outbuf, &inbuf);
+       if (ZSTD_isError(ret)) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD_compressStream failed, ret: %d\n",
+                               KERN_ERR, F2FS_I_SB(dic->inode)->sb->s_id,
+                               __func__, ZSTD_getErrorCode(ret));
+               return -EIO;
+       }
+
+       if (dic->rlen != outbuf.pos) {
+               printk_ratelimited("%sF2FS-fs (%s): %s ZSTD invalid rlen:%zu, "
+                               "expected:%lu\n", KERN_ERR,
+                               F2FS_I_SB(dic->inode)->sb->s_id,
+                               __func__, dic->rlen,
+                               PAGE_SIZE << dic->log_cluster_size);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static const struct f2fs_compress_ops f2fs_zstd_ops = {
+       .init_compress_ctx      = zstd_init_compress_ctx,
+       .destroy_compress_ctx   = zstd_destroy_compress_ctx,
+       .compress_pages         = zstd_compress_pages,
+       .init_decompress_ctx    = zstd_init_decompress_ctx,
+       .destroy_decompress_ctx = zstd_destroy_decompress_ctx,
+       .decompress_pages       = zstd_decompress_pages,
+};
+#endif
+
 static const struct f2fs_compress_ops *f2fs_cops[COMPRESS_MAX] = {
 #ifdef CONFIG_F2FS_FS_LZO
        &f2fs_lzo_ops,
@@ -302,6 +462,11 @@ static const struct f2fs_compress_ops *f2fs_cops[COMPRESS_MAX] = {
 #else
        NULL,
 #endif
+#ifdef CONFIG_F2FS_FS_ZSTD
+       &f2fs_zstd_ops,
+#else
+       NULL,
+#endif
 };
 
 bool f2fs_is_compress_backend_ready(struct inode *inode)
index dea45bb578620ad60043720973a66ec19b8d928f..8d206dcc33ab1334db458a4cae25b98df7163957 100644 (file)
@@ -1267,6 +1267,7 @@ enum fsync_mode {
 enum compress_algorithm_type {
        COMPRESS_LZO,
        COMPRESS_LZ4,
+       COMPRESS_ZSTD,
        COMPRESS_MAX,
 };
 
@@ -1296,6 +1297,7 @@ struct compress_ctx {
        size_t rlen;                    /* valid data length in rbuf */
        size_t clen;                    /* valid data length in cbuf */
        void *private;                  /* payload buffer for specified compression algorithm */
+       void *private2;                 /* extra payload buffer */
 };
 
 /* compress context for write IO path */
@@ -1325,11 +1327,14 @@ struct decompress_io_ctx {
        size_t clen;                    /* valid data length in cbuf */
        refcount_t ref;                 /* referrence count of compressed page */
        bool failed;                    /* indicate IO error during decompression */
+       void *private;                  /* payload buffer for specified decompression algorithm */
+       void *private2;                 /* extra payload buffer */
 };
 
 #define NULL_CLUSTER                   ((unsigned int)(~0))
 #define MIN_COMPRESS_LOG_SIZE          2
 #define MAX_COMPRESS_LOG_SIZE          8
+#define MAX_COMPRESS_WINDOW_SIZE       ((PAGE_SIZE) << MAX_COMPRESS_LOG_SIZE)
 
 struct f2fs_sb_info {
        struct super_block *sb;                 /* pointer to VFS super block */
index 0c901a4d3d445a02b4973d2fef0c9db1c44b26f3..b83b17b54a0a6ddf4e6051f8c8a67bb57f5e455c 100644 (file)
@@ -829,6 +829,10 @@ static int parse_options(struct super_block *sb, char *options)
                                        !strcmp(name, "lz4")) {
                                F2FS_OPTION(sbi).compress_algorithm =
                                                                COMPRESS_LZ4;
+                       } else if (strlen(name) == 4 &&
+                                       !strcmp(name, "zstd")) {
+                               F2FS_OPTION(sbi).compress_algorithm =
+                                                               COMPRESS_ZSTD;
                        } else {
                                kfree(name);
                                return -EINVAL;
@@ -1419,6 +1423,9 @@ static inline void f2fs_show_compress_options(struct seq_file *seq,
        case COMPRESS_LZ4:
                algtype = "lz4";
                break;
+       case COMPRESS_ZSTD:
+               algtype = "zstd";
+               break;
        }
        seq_printf(seq, ",compress_algorithm=%s", algtype);
 
index 67a97838c2a08476326b99898df67ed3752edcae..d97adfc327f030100666e2ce03a30e227c65e501 100644 (file)
@@ -153,7 +153,8 @@ TRACE_DEFINE_ENUM(CP_PAUSE);
 #define show_compress_algorithm(type)                                  \
        __print_symbolic(type,                                          \
                { COMPRESS_LZO,         "LZO" },                        \
-               { COMPRESS_LZ4,         "LZ4" })
+               { COMPRESS_LZ4,         "LZ4" },                        \
+               { COMPRESS_ZSTD,        "ZSTD" })
 
 struct f2fs_sb_info;
 struct f2fs_io_info;