f2fs: compress: support compress level
authorChao Yu <yuchao0@huawei.com>
Fri, 22 Jan 2021 09:46:43 +0000 (17:46 +0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Wed, 27 Jan 2021 23:20:02 +0000 (15:20 -0800)
Expand 'compress_algorithm' mount option to accept parameter as format of
<algorithm>:<level>, by this way, it gives a way to allow user to do more
specified config on lz4 and zstd compression level, then f2fs compression
can provide higher compress ratio.

In order to set compress level for lz4 algorithm, it needs to set
CONFIG_LZ4HC_COMPRESS and CONFIG_F2FS_FS_LZ4HC config to enable lz4hc
compress algorithm.

CR and performance number on lz4/lz4hc algorithm:

dd if=enwik9 of=compressed_file conv=fsync

Original blocks: 244382

lz4 lz4hc-9
compressed blocks 170647 163270
compress ratio 69.8% 66.8%
speed 16.4207 s, 60.9 MB/s 26.7299 s, 37.4 MB/s

compress ratio = after / before

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

index dae15c96e659e2bdaaf24cf760912cec855f079e..5eff4009e77e7ce9ec5240674571b3ba6e7f62ab 100644 (file)
@@ -249,6 +249,11 @@ checkpoint=%s[:%u[%]]       Set to "disable" to turn off checkpointing. Set to "enabl
                         This space is reclaimed once checkpoint=enable.
 compress_algorithm=%s   Control compress algorithm, currently f2fs supports "lzo",
                         "lz4", "zstd" and "lzo-rle" algorithm.
+compress_algorithm=%s:%d Control compress algorithm and its compress level, now, only
+                        "lz4" and "zstd" support compress level config.
+                        algorithm      level range
+                        lz4            3 - 16
+                        zstd           1 - 22
 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 d13c5c6a978769b69eef060d50569c578616ba14..63c1fc1a0e3b295af2155990b097a8e5ac633970 100644 (file)
@@ -119,6 +119,16 @@ config F2FS_FS_LZ4
        help
          Support LZ4 compress algorithm, if unsure, say Y.
 
+config F2FS_FS_LZ4HC
+       bool "LZ4HC compression support"
+       depends on F2FS_FS_COMPRESSION
+       depends on F2FS_FS_LZ4
+       select LZ4HC_COMPRESS
+       default y
+       help
+         Support LZ4HC compress algorithm, LZ4HC has compatible on-disk
+         layout with LZ4, if unsure, say Y.
+
 config F2FS_FS_ZSTD
        bool "ZSTD compression support"
        depends on F2FS_FS_COMPRESSION
index 4bcbacfe33259201ec6c37c1080fff2b01bfe2cc..a345a41e211999fe386a02f7a85ae03b3220e20b 100644 (file)
@@ -252,8 +252,14 @@ static const struct f2fs_compress_ops f2fs_lzo_ops = {
 #ifdef CONFIG_F2FS_FS_LZ4
 static int lz4_init_compress_ctx(struct compress_ctx *cc)
 {
-       cc->private = f2fs_kvmalloc(F2FS_I_SB(cc->inode),
-                               LZ4_MEM_COMPRESS, GFP_NOFS);
+       unsigned int size = LZ4_MEM_COMPRESS;
+
+#ifdef CONFIG_F2FS_FS_LZ4HC
+       if (F2FS_I(cc->inode)->i_compress_flag >> COMPRESS_LEVEL_OFFSET)
+               size = LZ4HC_MEM_COMPRESS;
+#endif
+
+       cc->private = f2fs_kvmalloc(F2FS_I_SB(cc->inode), size, GFP_NOFS);
        if (!cc->private)
                return -ENOMEM;
 
@@ -272,10 +278,34 @@ static void lz4_destroy_compress_ctx(struct compress_ctx *cc)
        cc->private = NULL;
 }
 
+#ifdef CONFIG_F2FS_FS_LZ4HC
+static int lz4hc_compress_pages(struct compress_ctx *cc)
+{
+       unsigned char level = F2FS_I(cc->inode)->i_compress_flag >>
+                                               COMPRESS_LEVEL_OFFSET;
+       int len;
+
+       if (level)
+               len = LZ4_compress_HC(cc->rbuf, cc->cbuf->cdata, cc->rlen,
+                                       cc->clen, level, cc->private);
+       else
+               len = LZ4_compress_default(cc->rbuf, cc->cbuf->cdata, cc->rlen,
+                                               cc->clen, cc->private);
+       if (!len)
+               return -EAGAIN;
+
+       cc->clen = len;
+       return 0;
+}
+#endif
+
 static int lz4_compress_pages(struct compress_ctx *cc)
 {
        int len;
 
+#ifdef CONFIG_F2FS_FS_LZ4HC
+       return lz4hc_compress_pages(cc);
+#endif
        len = LZ4_compress_default(cc->rbuf, cc->cbuf->cdata, cc->rlen,
                                                cc->clen, cc->private);
        if (!len)
@@ -325,8 +355,13 @@ static int zstd_init_compress_ctx(struct compress_ctx *cc)
        ZSTD_CStream *stream;
        void *workspace;
        unsigned int workspace_size;
+       unsigned char level = F2FS_I(cc->inode)->i_compress_flag >>
+                                               COMPRESS_LEVEL_OFFSET;
+
+       if (!level)
+               level = F2FS_ZSTD_DEFAULT_CLEVEL;
 
-       params = ZSTD_getParams(F2FS_ZSTD_DEFAULT_CLEVEL, cc->rlen, 0);
+       params = ZSTD_getParams(level, cc->rlen, 0);
        workspace_size = ZSTD_CStreamWorkspaceBound(params.cParams);
 
        workspace = f2fs_kvmalloc(F2FS_I_SB(cc->inode),
index bb11759191dcc9a963953125b362395d2a7cd13d..36012181c17f9769848d9d767eb5b29fec14c5b0 100644 (file)
@@ -146,6 +146,7 @@ struct f2fs_mount_info {
        /* For compression */
        unsigned char compress_algorithm;       /* algorithm type */
        unsigned char compress_log_size;        /* cluster log size */
+       unsigned char compress_level;           /* compress level */
        bool compress_chksum;                   /* compressed data chksum */
        unsigned char compress_ext_cnt;         /* extension count */
        int compress_mode;                      /* compression mode */
@@ -735,6 +736,7 @@ struct f2fs_inode_info {
        atomic_t i_compr_blocks;                /* # of compressed blocks */
        unsigned char i_compress_algorithm;     /* algorithm type */
        unsigned char i_log_cluster_size;       /* log of cluster size */
+       unsigned char i_compress_level;         /* compress level (lz4hc,zstd) */
        unsigned short i_compress_flag;         /* compress flag */
        unsigned int i_cluster_size;            /* cluster size */
 };
@@ -1310,6 +1312,8 @@ struct compress_data {
 
 #define F2FS_COMPRESSED_PAGE_MAGIC     0xF5F2C000
 
+#define        COMPRESS_LEVEL_OFFSET   8
+
 /* compress context */
 struct compress_ctx {
        struct inode *inode;            /* inode the context belong to */
@@ -3934,6 +3938,11 @@ static inline void set_compress_context(struct inode *inode)
                                1 << COMPRESS_CHKSUM : 0;
        F2FS_I(inode)->i_cluster_size =
                        1 << F2FS_I(inode)->i_log_cluster_size;
+       if (F2FS_I(inode)->i_compress_algorithm == COMPRESS_LZ4 &&
+                       F2FS_OPTION(sbi).compress_level)
+               F2FS_I(inode)->i_compress_flag |=
+                               F2FS_OPTION(sbi).compress_level <<
+                               COMPRESS_LEVEL_OFFSET;
        F2FS_I(inode)->i_flags |= F2FS_COMPR_FL;
        set_inode_flag(inode, FI_COMPRESSED_FILE);
        stat_inc_compr_inode(inode);
index a275bd312ae5610facdcd15adc0e8e323a0c3c76..c8be27a9eed6b9b500e0ab6dd53a3ebb31f27415 100644 (file)
@@ -25,6 +25,8 @@
 #include <linux/quota.h>
 #include <linux/unicode.h>
 #include <linux/part_stat.h>
+#include <linux/zstd.h>
+#include <linux/lz4.h>
 
 #include "f2fs.h"
 #include "node.h"
@@ -464,6 +466,74 @@ static int f2fs_set_test_dummy_encryption(struct super_block *sb,
        return 0;
 }
 
+#ifdef CONFIG_F2FS_FS_COMPRESSION
+#ifdef CONFIG_F2FS_FS_LZ4
+static int f2fs_set_lz4hc_level(struct f2fs_sb_info *sbi, const char *str)
+{
+#ifdef CONFIG_F2FS_FS_LZ4HC
+       unsigned int level;
+#endif
+
+       if (strlen(str) == 3) {
+               F2FS_OPTION(sbi).compress_level = 0;
+               return 0;
+       }
+
+#ifdef CONFIG_F2FS_FS_LZ4HC
+       str += 3;
+
+       if (str[0] != ':') {
+               f2fs_info(sbi, "wrong format, e.g. <alg_name>:<compr_level>");
+               return -EINVAL;
+       }
+       if (kstrtouint(str + 1, 10, &level))
+               return -EINVAL;
+
+       if (level < LZ4HC_MIN_CLEVEL || level > LZ4HC_MAX_CLEVEL) {
+               f2fs_info(sbi, "invalid lz4hc compress level: %d", level);
+               return -EINVAL;
+       }
+
+       F2FS_OPTION(sbi).compress_level = level;
+       return 0;
+#else
+       f2fs_info(sbi, "kernel doesn't support lz4hc compression");
+       return -EINVAL;
+#endif
+}
+#endif
+
+#ifdef CONFIG_F2FS_FS_ZSTD
+static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
+{
+       unsigned int level;
+       int len = 4;
+
+       if (strlen(str) == len) {
+               F2FS_OPTION(sbi).compress_level = 0;
+               return 0;
+       }
+
+       str += len;
+
+       if (str[0] != ':') {
+               f2fs_info(sbi, "wrong format, e.g. <alg_name>:<compr_level>");
+               return -EINVAL;
+       }
+       if (kstrtouint(str + 1, 10, &level))
+               return -EINVAL;
+
+       if (!level || level > ZSTD_maxCLevel()) {
+               f2fs_info(sbi, "invalid zstd compress level: %d", level);
+               return -EINVAL;
+       }
+
+       F2FS_OPTION(sbi).compress_level = level;
+       return 0;
+}
+#endif
+#endif
+
 static int parse_options(struct super_block *sb, char *options, bool is_remount)
 {
        struct f2fs_sb_info *sbi = F2FS_SB(sb);
@@ -883,20 +953,31 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
                                return -ENOMEM;
                        if (!strcmp(name, "lzo")) {
 #ifdef CONFIG_F2FS_FS_LZO
+                               F2FS_OPTION(sbi).compress_level = 0;
                                F2FS_OPTION(sbi).compress_algorithm =
                                                                COMPRESS_LZO;
 #else
                                f2fs_info(sbi, "kernel doesn't support lzo compression");
 #endif
-                       } else if (!strcmp(name, "lz4")) {
+                       } else if (!strncmp(name, "lz4", 3)) {
 #ifdef CONFIG_F2FS_FS_LZ4
+                               ret = f2fs_set_lz4hc_level(sbi, name);
+                               if (ret) {
+                                       kfree(name);
+                                       return -EINVAL;
+                               }
                                F2FS_OPTION(sbi).compress_algorithm =
                                                                COMPRESS_LZ4;
 #else
                                f2fs_info(sbi, "kernel doesn't support lz4 compression");
 #endif
-                       } else if (!strcmp(name, "zstd")) {
+                       } else if (!strncmp(name, "zstd", 4)) {
 #ifdef CONFIG_F2FS_FS_ZSTD
+                               ret = f2fs_set_zstd_level(sbi, name);
+                               if (ret) {
+                                       kfree(name);
+                                       return -EINVAL;
+                               }
                                F2FS_OPTION(sbi).compress_algorithm =
                                                                COMPRESS_ZSTD;
 #else
@@ -904,6 +985,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
 #endif
                        } else if (!strcmp(name, "lzo-rle")) {
 #ifdef CONFIG_F2FS_FS_LZORLE
+                               F2FS_OPTION(sbi).compress_level = 0;
                                F2FS_OPTION(sbi).compress_algorithm =
                                                                COMPRESS_LZORLE;
 #else
@@ -1555,6 +1637,9 @@ static inline void f2fs_show_compress_options(struct seq_file *seq,
        }
        seq_printf(seq, ",compress_algorithm=%s", algtype);
 
+       if (F2FS_OPTION(sbi).compress_level)
+               seq_printf(seq, ":%d", F2FS_OPTION(sbi).compress_level);
+
        seq_printf(seq, ",compress_log_size=%u",
                        F2FS_OPTION(sbi).compress_log_size);
 
index 7dc2a06cf19a119e11bc8abdf119eec9b54ccb82..c6cc0a566ef5c6de2568514de151d653514a83f8 100644 (file)
@@ -274,6 +274,9 @@ struct f2fs_inode {
                        __u8 i_compress_algorithm;      /* compress algorithm */
                        __u8 i_log_cluster_size;        /* log of cluster size */
                        __le16 i_compress_flag;         /* compress flag */
+                                               /* 0 bit: chksum flag
+                                                * [10,15] bits: compress level
+                                                */
                        __le32 i_extra_end[0];  /* for attribute size calculation */
                } __packed;
                __le32 i_addr[DEF_ADDRS_PER_INODE];     /* Pointers to data blocks */