Commit 3fde13f8 authored by Chao Yu's avatar Chao Yu Committed by Jaegeuk Kim
Browse files

f2fs: compress: support compress level



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: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent 32be0e97
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -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.
+10 −0
Original line number Diff line number Diff line
@@ -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
+38 −3
Original line number Diff line number Diff line
@@ -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),
+9 −0
Original line number Diff line number Diff line
@@ -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);
+87 −2
Original line number Diff line number Diff line
@@ -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);

Loading