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.
 
        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
 
 #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;
 
        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)
        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),
 
        /* 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 */
        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 */
 };
 
 #define F2FS_COMPRESSED_PAGE_MAGIC     0xF5F2C000
 
+#define        COMPRESS_LEVEL_OFFSET   8
+
 /* compress context */
 struct compress_ctx {
        struct inode *inode;            /* inode the context belong to */
                                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);
 
 #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"
        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);
                                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
 #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
        }
        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);
 
 
                        __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 */