Commit 4c8ff709 authored by Chao Yu's avatar Chao Yu Committed by Jaegeuk Kim

f2fs: support data compression

This patch tries to support compression in f2fs.

- New term named cluster is defined as basic unit of compression, file can
be divided into multiple clusters logically. One cluster includes 4 << n
(n >= 0) logical pages, compression size is also cluster size, each of
cluster can be compressed or not.

- In cluster metadata layout, one special flag is used to indicate cluster
is compressed one or normal one, for compressed cluster, following metadata
maps cluster to [1, 4 << n - 1] physical blocks, in where f2fs stores
data including compress header and compressed data.

- In order to eliminate write amplification during overwrite, F2FS only
support compression on write-once file, data can be compressed only when
all logical blocks in file are valid and cluster compress ratio is lower
than specified threshold.

- To enable compression on regular inode, there are three ways:
* chattr +c file
* chattr +c dir; touch dir/file
* mount w/ -o compress_extension=ext; touch file.ext

Compress metadata layout:
                             [Dnode Structure]
             +-----------------------------------------------+
             | cluster 1 | cluster 2 | ......... | cluster N |
             +-----------------------------------------------+
             .           .                       .           .
       .                       .                .                      .
  .         Compressed Cluster       .        .        Normal Cluster            .
+----------+---------+---------+---------+  +---------+---------+---------+---------+
|compr flag| block 1 | block 2 | block 3 |  | block 1 | block 2 | block 3 | block 4 |
+----------+---------+---------+---------+  +---------+---------+---------+---------+
           .                             .
         .                                           .
       .                                                           .
      +-------------+-------------+----------+----------------------------+
      | data length | data chksum | reserved |      compressed data       |
      +-------------+-------------+----------+----------------------------+

Changelog:

20190326:
- fix error handling of read_end_io().
- remove unneeded comments in f2fs_encrypt_one_page().

20190327:
- fix wrong use of f2fs_cluster_is_full() in f2fs_mpage_readpages().
- don't jump into loop directly to avoid uninitialized variables.
- add TODO tag in error path of f2fs_write_cache_pages().

20190328:
- fix wrong merge condition in f2fs_read_multi_pages().
- check compressed file in f2fs_post_read_required().

20190401
- allow overwrite on non-compressed cluster.
- check cluster meta before writing compressed data.

20190402
- don't preallocate blocks for compressed file.

- add lz4 compress algorithm
- process multiple post read works in one workqueue
  Now f2fs supports processing post read work in multiple workqueue,
  it shows low performance due to schedule overhead of multiple
  workqueue executing orderly.

20190921
- compress: support buffered overwrite
C: compress cluster flag
V: valid block address
N: NEW_ADDR

One cluster contain 4 blocks

 before overwrite   after overwrite

- VVVV		->	CVNN
- CVNN		->	VVVV

- CVNN		->	CVNN
- CVNN		->	CVVV

- CVVV		->	CVNN
- CVVV		->	CVVV

20191029
- add kconfig F2FS_FS_COMPRESSION to isolate compression related
codes, add kconfig F2FS_FS_{LZO,LZ4} to cover backend algorithm.
note that: will remove lzo backend if Jaegeuk agreed that too.
- update codes according to Eric's comments.

20191101
- apply fixes from Jaegeuk

20191113
- apply fixes from Jaegeuk
- split workqueue for fsverity

20191216
- apply fixes from Jaegeuk

20200117
- fix to avoid NULL pointer dereference

[Jaegeuk Kim]
- add tracepoint for f2fs_{,de}compress_pages()
- fix many bugs and add some compression stats
- fix overwrite/mmap bugs
- address 32bit build error, reported by Geert.
- bug fixes when handling errors and i_compressed_blocks

Reported-by: <noreply@ellerman.id.au>
Signed-off-by: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarJaegeuk Kim <jaegeuk@kernel.org>
parent 820d3667
...@@ -235,6 +235,17 @@ checkpoint=%s[:%u[%]] Set to "disable" to turn off checkpointing. Set to "en ...@@ -235,6 +235,17 @@ checkpoint=%s[:%u[%]] Set to "disable" to turn off checkpointing. Set to "en
hide up to all remaining free space. The actual space that hide up to all remaining free space. The actual space that
would be unusable can be viewed at /sys/fs/f2fs/<disk>/unusable would be unusable can be viewed at /sys/fs/f2fs/<disk>/unusable
This space is reclaimed once checkpoint=enable. This space is reclaimed once checkpoint=enable.
compress_algorithm=%s Control compress algorithm, currently f2fs supports "lzo"
and "lz4" 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.
compress_extension=%s Support adding specified extension, so that f2fs can enable
compression on those corresponding files, e.g. if all files
with '.ext' has high compression rate, we can set the '.ext'
on compression extension list and enable compression on
these file by default rather than to enable it via ioctl.
For other files, we can still enable compression via ioctl.
================================================================================ ================================================================================
DEBUGFS ENTRIES DEBUGFS ENTRIES
...@@ -840,3 +851,44 @@ zero or random data, which is useful to the below scenario where: ...@@ -840,3 +851,44 @@ zero or random data, which is useful to the below scenario where:
4. address = fibmap(fd, offset) 4. address = fibmap(fd, offset)
5. open(blkdev) 5. open(blkdev)
6. write(blkdev, address) 6. write(blkdev, address)
Compression implementation
--------------------------
- New term named cluster is defined as basic unit of compression, file can
be divided into multiple clusters logically. One cluster includes 4 << n
(n >= 0) logical pages, compression size is also cluster size, each of
cluster can be compressed or not.
- In cluster metadata layout, one special block address is used to indicate
cluster is compressed one or normal one, for compressed cluster, following
metadata maps cluster to [1, 4 << n - 1] physical blocks, in where f2fs
stores data including compress header and compressed data.
- In order to eliminate write amplification during overwrite, F2FS only
support compression on write-once file, data can be compressed only when
all logical blocks in file are valid and cluster compress ratio is lower
than specified threshold.
- To enable compression on regular inode, there are three ways:
* chattr +c file
* chattr +c dir; touch dir/file
* mount w/ -o compress_extension=ext; touch file.ext
Compress metadata layout:
[Dnode Structure]
+-----------------------------------------------+
| cluster 1 | cluster 2 | ......... | cluster N |
+-----------------------------------------------+
. . . .
. . . .
. Compressed Cluster . . Normal Cluster .
+----------+---------+---------+---------+ +---------+---------+---------+---------+
|compr flag| block 1 | block 2 | block 3 | | block 1 | block 2 | block 3 | block 4 |
+----------+---------+---------+---------+ +---------+---------+---------+---------+
. .
. .
. .
+-------------+-------------+----------+----------------------------+
| data length | data chksum | reserved | compressed data |
+-------------+-------------+----------+----------------------------+
...@@ -92,3 +92,28 @@ config F2FS_FAULT_INJECTION ...@@ -92,3 +92,28 @@ config F2FS_FAULT_INJECTION
Test F2FS to inject faults such as ENOMEM, ENOSPC, and so on. Test F2FS to inject faults such as ENOMEM, ENOSPC, and so on.
If unsure, say N. If unsure, say N.
config F2FS_FS_COMPRESSION
bool "F2FS compression feature"
depends on F2FS_FS
help
Enable filesystem-level compression on f2fs regular files,
multiple back-end compression algorithms are supported.
config F2FS_FS_LZO
bool "LZO compression support"
depends on F2FS_FS_COMPRESSION
select LZO_COMPRESS
select LZO_DECOMPRESS
default y
help
Support LZO compress algorithm, if unsure, say Y.
config F2FS_FS_LZ4
bool "LZ4 compression support"
depends on F2FS_FS_COMPRESSION
select LZ4_COMPRESS
select LZ4_DECOMPRESS
default y
help
Support LZ4 compress algorithm, if unsure, say Y.
...@@ -9,3 +9,4 @@ f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o ...@@ -9,3 +9,4 @@ f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o
f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o
f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o f2fs-$(CONFIG_F2FS_IO_TRACE) += trace.o
f2fs-$(CONFIG_FS_VERITY) += verity.o f2fs-$(CONFIG_FS_VERITY) += verity.o
f2fs-$(CONFIG_F2FS_FS_COMPRESSION) += compress.o
This diff is collapsed.
This diff is collapsed.
...@@ -94,6 +94,8 @@ static void update_general_status(struct f2fs_sb_info *sbi) ...@@ -94,6 +94,8 @@ static void update_general_status(struct f2fs_sb_info *sbi)
si->inline_xattr = atomic_read(&sbi->inline_xattr); si->inline_xattr = atomic_read(&sbi->inline_xattr);
si->inline_inode = atomic_read(&sbi->inline_inode); si->inline_inode = atomic_read(&sbi->inline_inode);
si->inline_dir = atomic_read(&sbi->inline_dir); si->inline_dir = atomic_read(&sbi->inline_dir);
si->compr_inode = atomic_read(&sbi->compr_inode);
si->compr_blocks = atomic_read(&sbi->compr_blocks);
si->append = sbi->im[APPEND_INO].ino_num; si->append = sbi->im[APPEND_INO].ino_num;
si->update = sbi->im[UPDATE_INO].ino_num; si->update = sbi->im[UPDATE_INO].ino_num;
si->orphans = sbi->im[ORPHAN_INO].ino_num; si->orphans = sbi->im[ORPHAN_INO].ino_num;
...@@ -315,6 +317,8 @@ static int stat_show(struct seq_file *s, void *v) ...@@ -315,6 +317,8 @@ static int stat_show(struct seq_file *s, void *v)
si->inline_inode); si->inline_inode);
seq_printf(s, " - Inline_dentry Inode: %u\n", seq_printf(s, " - Inline_dentry Inode: %u\n",
si->inline_dir); si->inline_dir);
seq_printf(s, " - Compressed Inode: %u, Blocks: %u\n",
si->compr_inode, si->compr_blocks);
seq_printf(s, " - Orphan/Append/Update Inode: %u, %u, %u\n", seq_printf(s, " - Orphan/Append/Update Inode: %u, %u, %u\n",
si->orphans, si->append, si->update); si->orphans, si->append, si->update);
seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n", seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n",
...@@ -491,6 +495,8 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi) ...@@ -491,6 +495,8 @@ int f2fs_build_stats(struct f2fs_sb_info *sbi)
atomic_set(&sbi->inline_xattr, 0); atomic_set(&sbi->inline_xattr, 0);
atomic_set(&sbi->inline_inode, 0); atomic_set(&sbi->inline_inode, 0);
atomic_set(&sbi->inline_dir, 0); atomic_set(&sbi->inline_dir, 0);
atomic_set(&sbi->compr_inode, 0);
atomic_set(&sbi->compr_blocks, 0);
atomic_set(&sbi->inplace_count, 0); atomic_set(&sbi->inplace_count, 0);
for (i = META_CP; i < META_MAX; i++) for (i = META_CP; i < META_MAX; i++)
atomic_set(&sbi->meta_count[i], 0); atomic_set(&sbi->meta_count[i], 0);
......
This diff is collapsed.
This diff is collapsed.
...@@ -200,6 +200,7 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) ...@@ -200,6 +200,7 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
struct f2fs_inode_info *fi = F2FS_I(inode); struct f2fs_inode_info *fi = F2FS_I(inode);
struct f2fs_inode *ri = F2FS_INODE(node_page);
unsigned long long iblocks; unsigned long long iblocks;
iblocks = le64_to_cpu(F2FS_INODE(node_page)->i_blocks); iblocks = le64_to_cpu(F2FS_INODE(node_page)->i_blocks);
...@@ -286,6 +287,19 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page) ...@@ -286,6 +287,19 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page)
return false; return false;
} }
if (f2fs_has_extra_attr(inode) && f2fs_sb_has_compression(sbi) &&
fi->i_flags & F2FS_COMPR_FL &&
F2FS_FITS_IN_INODE(ri, fi->i_extra_isize,
i_log_cluster_size)) {
if (ri->i_compress_algorithm >= COMPRESS_MAX)
return false;
if (le64_to_cpu(ri->i_compr_blocks) > inode->i_blocks)
return false;
if (ri->i_log_cluster_size < MIN_COMPRESS_LOG_SIZE ||
ri->i_log_cluster_size > MAX_COMPRESS_LOG_SIZE)
return false;
}
return true; return true;
} }
...@@ -407,6 +421,18 @@ static int do_read_inode(struct inode *inode) ...@@ -407,6 +421,18 @@ static int do_read_inode(struct inode *inode)
fi->i_crtime.tv_nsec = le32_to_cpu(ri->i_crtime_nsec); fi->i_crtime.tv_nsec = le32_to_cpu(ri->i_crtime_nsec);
} }
if (f2fs_has_extra_attr(inode) && f2fs_sb_has_compression(sbi) &&
(fi->i_flags & F2FS_COMPR_FL)) {
if (F2FS_FITS_IN_INODE(ri, fi->i_extra_isize,
i_log_cluster_size)) {
fi->i_compr_blocks = le64_to_cpu(ri->i_compr_blocks);
fi->i_compress_algorithm = ri->i_compress_algorithm;
fi->i_log_cluster_size = ri->i_log_cluster_size;
fi->i_cluster_size = 1 << fi->i_log_cluster_size;
set_inode_flag(inode, FI_COMPRESSED_FILE);
}
}
F2FS_I(inode)->i_disk_time[0] = inode->i_atime; F2FS_I(inode)->i_disk_time[0] = inode->i_atime;
F2FS_I(inode)->i_disk_time[1] = inode->i_ctime; F2FS_I(inode)->i_disk_time[1] = inode->i_ctime;
F2FS_I(inode)->i_disk_time[2] = inode->i_mtime; F2FS_I(inode)->i_disk_time[2] = inode->i_mtime;
...@@ -416,6 +442,8 @@ static int do_read_inode(struct inode *inode) ...@@ -416,6 +442,8 @@ static int do_read_inode(struct inode *inode)
stat_inc_inline_xattr(inode); stat_inc_inline_xattr(inode);
stat_inc_inline_inode(inode); stat_inc_inline_inode(inode);
stat_inc_inline_dir(inode); stat_inc_inline_dir(inode);
stat_inc_compr_inode(inode);
stat_add_compr_blocks(inode, F2FS_I(inode)->i_compr_blocks);
return 0; return 0;
} }
...@@ -569,6 +597,17 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page) ...@@ -569,6 +597,17 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page)
ri->i_crtime_nsec = ri->i_crtime_nsec =
cpu_to_le32(F2FS_I(inode)->i_crtime.tv_nsec); cpu_to_le32(F2FS_I(inode)->i_crtime.tv_nsec);
} }
if (f2fs_sb_has_compression(F2FS_I_SB(inode)) &&
F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize,
i_log_cluster_size)) {
ri->i_compr_blocks =
cpu_to_le64(F2FS_I(inode)->i_compr_blocks);
ri->i_compress_algorithm =
F2FS_I(inode)->i_compress_algorithm;
ri->i_log_cluster_size =
F2FS_I(inode)->i_log_cluster_size;
}
} }
__set_inode_rdev(inode, ri); __set_inode_rdev(inode, ri);
...@@ -711,6 +750,8 @@ void f2fs_evict_inode(struct inode *inode) ...@@ -711,6 +750,8 @@ void f2fs_evict_inode(struct inode *inode)
stat_dec_inline_xattr(inode); stat_dec_inline_xattr(inode);
stat_dec_inline_dir(inode); stat_dec_inline_dir(inode);
stat_dec_inline_inode(inode); stat_dec_inline_inode(inode);
stat_dec_compr_inode(inode);
stat_sub_compr_blocks(inode, F2FS_I(inode)->i_compr_blocks);
if (likely(!f2fs_cp_error(sbi) && if (likely(!f2fs_cp_error(sbi) &&
!is_sbi_flag_set(sbi, SBI_CP_DISABLED))) !is_sbi_flag_set(sbi, SBI_CP_DISABLED)))
......
...@@ -119,6 +119,13 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) ...@@ -119,6 +119,13 @@ static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode)
if (F2FS_I(inode)->i_flags & F2FS_PROJINHERIT_FL) if (F2FS_I(inode)->i_flags & F2FS_PROJINHERIT_FL)
set_inode_flag(inode, FI_PROJ_INHERIT); set_inode_flag(inode, FI_PROJ_INHERIT);
if (f2fs_sb_has_compression(sbi)) {
/* Inherit the compression flag in directory */
if ((F2FS_I(dir)->i_flags & F2FS_COMPR_FL) &&
f2fs_may_compress(inode))
set_compress_context(inode);
}
f2fs_set_inode_flags(inode); f2fs_set_inode_flags(inode);
trace_f2fs_new_inode(inode, 0); trace_f2fs_new_inode(inode, 0);
...@@ -149,6 +156,9 @@ static inline int is_extension_exist(const unsigned char *s, const char *sub) ...@@ -149,6 +156,9 @@ static inline int is_extension_exist(const unsigned char *s, const char *sub)
size_t sublen = strlen(sub); size_t sublen = strlen(sub);
int i; int i;
if (sublen == 1 && *sub == '*')
return 1;
/* /*
* filename format of multimedia file should be defined as: * filename format of multimedia file should be defined as:
* "filename + '.' + extension + (optional: '.' + temp extension)". * "filename + '.' + extension + (optional: '.' + temp extension)".
...@@ -262,6 +272,45 @@ int f2fs_update_extension_list(struct f2fs_sb_info *sbi, const char *name, ...@@ -262,6 +272,45 @@ int f2fs_update_extension_list(struct f2fs_sb_info *sbi, const char *name,
return 0; return 0;
} }
static void set_compress_inode(struct f2fs_sb_info *sbi, struct inode *inode,
const unsigned char *name)
{
__u8 (*extlist)[F2FS_EXTENSION_LEN] = sbi->raw_super->extension_list;
unsigned char (*ext)[F2FS_EXTENSION_LEN];
unsigned int ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
int i, cold_count, hot_count;
if (!f2fs_sb_has_compression(sbi) ||
is_inode_flag_set(inode, FI_COMPRESSED_FILE) ||
F2FS_I(inode)->i_flags & F2FS_NOCOMP_FL ||
!f2fs_may_compress(inode))
return;
down_read(&sbi->sb_lock);
cold_count = le32_to_cpu(sbi->raw_super->extension_count);
hot_count = sbi->raw_super->hot_ext_count;
for (i = cold_count; i < cold_count + hot_count; i++) {
if (is_extension_exist(name, extlist[i])) {
up_read(&sbi->sb_lock);
return;
}
}
up_read(&sbi->sb_lock);
ext = F2FS_OPTION(sbi).extensions;
for (i = 0; i < ext_cnt; i++) {
if (!is_extension_exist(name, ext[i]))
continue;
set_compress_context(inode);
return;
}
}
static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
bool excl) bool excl)
{ {
...@@ -286,6 +335,8 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, ...@@ -286,6 +335,8 @@ static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode,
if (!test_opt(sbi, DISABLE_EXT_IDENTIFY)) if (!test_opt(sbi, DISABLE_EXT_IDENTIFY))
set_file_temperature(sbi, inode, dentry->d_name.name); set_file_temperature(sbi, inode, dentry->d_name.name);
set_compress_inode(sbi, inode, dentry->d_name.name);
inode->i_op = &f2fs_file_inode_operations; inode->i_op = &f2fs_file_inode_operations;
inode->i_fop = &f2fs_file_operations; inode->i_fop = &f2fs_file_operations;
inode->i_mapping->a_ops = &f2fs_dblock_aops; inode->i_mapping->a_ops = &f2fs_dblock_aops;
......
...@@ -2224,7 +2224,7 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) ...@@ -2224,7 +2224,7 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
struct sit_info *sit_i = SIT_I(sbi); struct sit_info *sit_i = SIT_I(sbi);
f2fs_bug_on(sbi, addr == NULL_ADDR); f2fs_bug_on(sbi, addr == NULL_ADDR);
if (addr == NEW_ADDR) if (addr == NEW_ADDR || addr == COMPRESS_ADDR)
return; return;
invalidate_mapping_pages(META_MAPPING(sbi), addr, addr); invalidate_mapping_pages(META_MAPPING(sbi), addr, addr);
...@@ -3035,7 +3035,8 @@ static int __get_segment_type_6(struct f2fs_io_info *fio) ...@@ -3035,7 +3035,8 @@ static int __get_segment_type_6(struct f2fs_io_info *fio)
if (fio->type == DATA) { if (fio->type == DATA) {
struct inode *inode = fio->page->mapping->host; struct inode *inode = fio->page->mapping->host;
if (is_cold_data(fio->page) || file_is_cold(inode)) if (is_cold_data(fio->page) || file_is_cold(inode) ||
f2fs_compressed_file(inode))
return CURSEG_COLD_DATA; return CURSEG_COLD_DATA;
if (file_is_hot(inode) || if (file_is_hot(inode) ||
is_inode_flag_set(inode, FI_HOT_DATA) || is_inode_flag_set(inode, FI_HOT_DATA) ||
......
...@@ -200,18 +200,6 @@ struct segment_allocation { ...@@ -200,18 +200,6 @@ struct segment_allocation {
void (*allocate_segment)(struct f2fs_sb_info *, int, bool); void (*allocate_segment)(struct f2fs_sb_info *, int, bool);
}; };
/*
* this value is set in page as a private data which indicate that
* the page is atomically written, and it is in inmem_pages list.
*/
#define ATOMIC_WRITTEN_PAGE ((unsigned long)-1)
#define DUMMY_WRITTEN_PAGE ((unsigned long)-2)
#define IS_ATOMIC_WRITTEN_PAGE(page) \
(page_private(page) == (unsigned long)ATOMIC_WRITTEN_PAGE)
#define IS_DUMMY_WRITTEN_PAGE(page) \
(page_private(page) == (unsigned long)DUMMY_WRITTEN_PAGE)
#define MAX_SKIP_GC_COUNT 16 #define MAX_SKIP_GC_COUNT 16
struct inmem_pages { struct inmem_pages {
......
...@@ -141,6 +141,9 @@ enum { ...@@ -141,6 +141,9 @@ enum {
Opt_checkpoint_disable_cap, Opt_checkpoint_disable_cap,
Opt_checkpoint_disable_cap_perc, Opt_checkpoint_disable_cap_perc,
Opt_checkpoint_enable, Opt_checkpoint_enable,
Opt_compress_algorithm,
Opt_compress_log_size,
Opt_compress_extension,
Opt_err, Opt_err,
}; };
...@@ -203,6 +206,9 @@ static match_table_t f2fs_tokens = { ...@@ -203,6 +206,9 @@ static match_table_t f2fs_tokens = {
{Opt_checkpoint_disable_cap, "checkpoint=disable:%u"}, {Opt_checkpoint_disable_cap, "checkpoint=disable:%u"},
{Opt_checkpoint_disable_cap_perc, "checkpoint=disable:%u%%"}, {Opt_checkpoint_disable_cap_perc, "checkpoint=disable:%u%%"},
{Opt_checkpoint_enable, "checkpoint=enable"}, {Opt_checkpoint_enable, "checkpoint=enable"},
{Opt_compress_algorithm, "compress_algorithm=%s"},
{Opt_compress_log_size, "compress_log_size=%u"},
{Opt_compress_extension, "compress_extension=%s"},
{Opt_err, NULL}, {Opt_err, NULL},
}; };
...@@ -391,8 +397,9 @@ static int parse_options(struct super_block *sb, char *options) ...@@ -391,8 +397,9 @@ static int parse_options(struct super_block *sb, char *options)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(sb); struct f2fs_sb_info *sbi = F2FS_SB(sb);
substring_t args[MAX_OPT_ARGS]; substring_t args[MAX_OPT_ARGS];
unsigned char (*ext)[F2FS_EXTENSION_LEN];
char *p, *name; char *p, *name;
int arg = 0; int arg = 0, ext_cnt;
kuid_t uid; kuid_t uid;
kgid_t gid; kgid_t gid;
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
...@@ -810,6 +817,66 @@ static int parse_options(struct super_block *sb, char *options) ...@@ -810,6 +817,66 @@ static int parse_options(struct super_block *sb, char *options)
case Opt_checkpoint_enable: case Opt_checkpoint_enable:
clear_opt(sbi, DISABLE_CHECKPOINT); clear_opt(sbi, DISABLE_CHECKPOINT);
break; break;
case Opt_compress_algorithm:
if (!f2fs_sb_has_compression(sbi)) {
f2fs_err(sbi, "Compression feature if off");
return -EINVAL;
}
name = match_strdup(&args[0]);
if (!name)
return -ENOMEM;
if (strlen(name) == 3 && !strcmp(name, "lzo")) {
F2FS_OPTION(sbi).compress_algorithm =
COMPRESS_LZO;
} else if (strlen(name) == 3 &&
!strcmp(name, "lz4")) {
F2FS_OPTION(sbi).compress_algorithm =
COMPRESS_LZ4;
} else {
kfree(name);
return -EINVAL;
}
kfree(name);
break;
case Opt_compress_log_size:
if (!f2fs_sb_has_compression(sbi)) {
f2fs_err(sbi, "Compression feature is off");
return -EINVAL;
}
if (args->from && match_int(args, &arg))
return -EINVAL;
if (arg < MIN_COMPRESS_LOG_SIZE ||
arg > MAX_COMPRESS_LOG_SIZE) {
f2fs_err(sbi,
"Compress cluster log size is out of range");
return -EINVAL;
}
F2FS_OPTION(sbi).compress_log_size = arg;
break;
case Opt_compress_extension:
if (!f2fs_sb_has_compression(sbi)) {
f2fs_err(sbi, "Compression feature is off");
return -EINVAL;
}
name = match_strdup(&args[0]);
if (!name)
return -ENOMEM;
ext = F2FS_OPTION(sbi).extensions;
ext_cnt = F2FS_OPTION(sbi).compress_ext_cnt;
if (strlen(name) >= F2FS_EXTENSION_LEN ||
ext_cnt >= COMPRESS_EXT_NUM) {
f2fs_err(sbi,
"invalid extension length/number");
kfree(name);
return -EINVAL;
}
strcpy(ext[ext_cnt], name);
F2FS_OPTION(sbi).compress_ext_cnt++;
kfree(name);
break;
default: default:
f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value", f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
p); p);
...@@ -1125,6 +1192,8 @@ static void f2fs_put_super(struct super_block *sb) ...@@ -1125,6 +1192,8 @@ static void f2fs_put_super(struct super_block *sb)
f2fs_destroy_node_manager(sbi); f2fs_destroy_node_manager(sbi);
f2fs_destroy_segment_manager(sbi); f2fs_destroy_segment_manager(sbi);
f2fs_destroy_post_read_wq(sbi);
kvfree(sbi->ckpt); kvfree(sbi->ckpt);
f2fs_unregister_sysfs(sbi); f2fs_unregister_sysfs(sbi);
...@@ -1340,6 +1409,35 @@ static inline void f2fs_show_quota_options(struct seq_file *seq, ...@@ -1340,6 +1409,35 @@ static inline void f2fs_show_quota_options(struct seq_file *seq,
#endif #endif
} }
static inline void f2fs_show_compress_options(struct seq_file *seq,
struct super_block *sb)
{
struct f2fs_sb_info *sbi = F2FS_SB(sb);
char *algtype = "";
int i;
if (!f2fs_sb_has_compression(sbi))
return;
switch (F2FS_OPTION(sbi).compress_algorithm) {
case COMPRESS_LZO:
algtype = "lzo";
break;
case COMPRESS_LZ4:
algtype = "lz4";
break;
}
seq_printf(seq, ",compress_algorithm=%s", algtype);
seq_printf(seq, ",compress_log_size=%u",
F2FS_OPTION(sbi).compress_log_size);
for (i = 0; i < F2FS_OPTION(sbi).compress_ext_cnt; i++) {
seq_printf(seq, ",compress_extension=%s",
F2FS_OPTION(sbi).extensions[i]);
}
}
static int f2fs_show_options(struct seq_file *seq, struct dentry *root) static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
{ {
struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb); struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb);
...@@ -1462,6 +1560,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root) ...@@ -1462,6 +1560,8 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
seq_printf(seq, ",fsync_mode=%s", "strict"); seq_printf(seq, ",fsync_mode=%s", "strict");
else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_NOBARRIER) else if (F2FS_OPTION(sbi).fsync_mode == FSYNC_MODE_NOBARRIER)
seq_printf(seq, ",fsync_mode=%s", "nobarrier"); seq_printf(seq, ",fsync_mode=%s", "nobarrier");
f2fs_show_compress_options(seq, sbi->sb);
return 0; return 0;
} }
...@@ -1476,6 +1576,9 @@ static void default_options(struct f2fs_sb_info *sbi) ...@@ -1476,6 +1576,9 @@ static void default_options(struct f2fs_sb_info *sbi)
F2FS_OPTION(sbi).test_dummy_encryption = false; F2FS_OPTION(sbi).test_dummy_encryption = false;
F2FS_OPTION(sbi).s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID); F2FS_OPTION(sbi).s_resuid = make_kuid(&init_user_ns, F2FS_DEF_RESUID);
F2FS_OPTION(sbi).s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID); F2FS_OPTION(sbi).s_resgid = make_kgid(&init_user_ns, F2FS_DEF_RESGID);
F2FS_OPTION(sbi).compress_algorithm = COMPRESS_LZO;
F2FS_OPTION(sbi).compress_log_size = MIN_COMPRESS_LOG_SIZE;
F2FS_OPTION(sbi).compress_ext_cnt = 0;
set_opt(sbi, BG_GC); set_opt(sbi, BG_GC);
set_opt(sbi, INLINE_XATTR); set_opt(sbi, INLINE_XATTR);
...@@ -3390,6 +3493,12 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3390,6 +3493,12 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
goto free_devices; goto free_devices;
} }
err = f2fs_init_post_read_wq(sbi);
if (err) {
f2fs_err(sbi, "Failed to initialize post read workqueue");
goto free_devices;
}
sbi->total_valid_node_count = sbi->total_valid_node_count =
le32_to_cpu(sbi->ckpt->valid_node_count); le32_to_cpu(sbi->ckpt->valid_node_count);
percpu_counter_set(&sbi->total_valid_inode_count, percpu_counter_set(&sbi->total_valid_inode_count,
...@@ -3622,6 +3731,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent) ...@@ -3622,6 +3731,7 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
f2fs_destroy_node_manager(sbi); f2fs_destroy_node_manager(sbi);
free_sm: free_sm:
f2fs_destroy_segment_manager(sbi); f2fs_destroy_segment_manager(sbi);
f2fs_destroy_post_read_wq(sbi);
free_devices: free_devices:
destroy_device_list(sbi); destroy_device_list(sbi);
kvfree(sbi->ckpt); kvfree(sbi->ckpt);
......
...@@ -154,6 +154,9 @@ static ssize_t features_show(struct f2fs_attr *a, ...@@ -154,6 +154,9 @@ static ssize_t features_show(struct f2fs_attr *a,
if (f2fs_sb_has_casefold(sbi)) if (f2fs_sb_has_casefold(sbi))
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "casefold"); len ? ", " : "", "casefold");
if (f2fs_sb_has_compression(sbi))
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "compression");
len += snprintf(buf + len, PAGE_SIZE - len, "%s%s", len += snprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "pin_file"); len ? ", " : "", "pin_file");
len += snprintf(buf + len, PAGE_SIZE - len, "\n"); len += snprintf(buf + len, PAGE_SIZE - len, "\n");
...@@ -389,6 +392,7 @@ enum feat_id { ...@@ -389,6 +392,7 @@ enum feat_id {
FEAT_VERITY, FEAT_VERITY,
FEAT_SB_CHECKSUM, FEAT_SB_CHECKSUM,
FEAT_CASEFOLD, FEAT_CASEFOLD,
FEAT_COMPRESSION,
}; };
static ssize_t f2fs_feature_show(struct f2fs_attr *a, static ssize_t f2fs_feature_show(struct f2fs_attr *a,
...@@ -408,6 +412,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a, ...@@ -408,6 +412,7 @@ static ssize_t f2fs_feature_show(struct f2fs_attr *a,
case FEAT_VERITY: case FEAT_VERITY:
case FEAT_SB_CHECKSUM: case FEAT_SB_CHECKSUM:
case FEAT_CASEFOLD: case FEAT_CASEFOLD:
case FEAT_COMPRESSION:
return snprintf(buf, PAGE_SIZE, "supported\n"); return snprintf(buf, PAGE_SIZE, "supported\n");
} }
return 0; return 0;
...@@ -503,6 +508,7 @@ F2FS_FEATURE_RO_ATTR(verity, FEAT_VERITY); ...@@ -503,6 +508,7 @@ F2FS_FEATURE_RO_ATTR(verity, FEAT_VERITY);
#endif #endif
F2FS_FEATURE_RO_ATTR(sb_checksum, FEAT_SB_CHECKSUM); F2FS_FEATURE_RO_ATTR(sb_checksum, FEAT_SB_CHECKSUM);
F2FS_FEATURE_RO_ATTR(casefold, FEAT_CASEFOLD); F2FS_FEATURE_RO_ATTR(casefold, FEAT_CASEFOLD);
F2FS_FEATURE_RO_ATTR(compression, FEAT_COMPRESSION);
#define ATTR_LIST(name) (&f2fs_attr_##name.attr) #define ATTR_LIST(name) (&f2fs_attr_##name.attr)
static struct attribute *f2fs_attrs[] = { static struct attribute *f2fs_attrs[] = {
...@@ -573,6 +579,7 @@ static struct attribute *f2fs_feat_attrs[] = { ...@@ -573,6 +579,7 @@ static struct attribute *f2fs_feat_attrs[] = {
#endif #endif
ATTR_LIST(sb_checksum), ATTR_LIST(sb_checksum),
ATTR_LIST(casefold), ATTR_LIST(casefold),
ATTR_LIST(compression),
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(f2fs_feat); ATTRIBUTE_GROUPS(f2fs_feat);
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#define NULL_ADDR ((block_t)0) /* used as block_t addresses */ #define NULL_ADDR ((block_t)0) /* used as block_t addresses */
#define NEW_ADDR ((block_t)-1) /* used as block_t addresses */ #define NEW_ADDR ((block_t)-1) /* used as block_t addresses */
#define COMPRESS_ADDR ((block_t)-2) /* used as compressed data flag */
#define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS) #define F2FS_BYTES_TO_BLK(bytes) ((bytes) >> F2FS_BLKSIZE_BITS)
#define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS) #define F2FS_BLK_TO_BYTES(blk) ((blk) << F2FS_BLKSIZE_BITS)
...@@ -271,6 +272,10 @@ struct f2fs_inode { ...@@ -271,6 +272,10 @@ struct f2fs_inode {
__le32 i_inode_checksum;/* inode meta checksum */ __le32 i_inode_checksum;/* inode meta checksum */
__le64 i_crtime; /* creation time */ __le64 i_crtime; /* creation time */
__le32 i_crtime_nsec; /* creation time in nano scale */ __le32 i_crtime_nsec; /* creation time in nano scale */
__le64 i_compr_blocks; /* # of compressed blocks */
__u8 i_compress_algorithm; /* compress algorithm */
__u8 i_log_cluster_size; /* log of cluster size */
__le16 i_padding; /* padding */
__le32 i_extra_end[0]; /* for attribute size calculation */ __le32 i_extra_end[0]; /* for attribute size calculation */
} __packed; } __packed;
__le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */ __le32 i_addr[DEF_ADDRS_PER_INODE]; /* Pointers to data blocks */
......
...@@ -131,6 +131,7 @@ TRACE_DEFINE_ENUM(CP_TRIMMED); ...@@ -131,6 +131,7 @@ TRACE_DEFINE_ENUM(CP_TRIMMED);
__print_symbolic(type, \ __print_symbolic(type, \
{ CP_NO_NEEDED, "no needed" }, \ { CP_NO_NEEDED, "no needed" }, \
{ CP_NON_REGULAR, "non regular" }, \ { CP_NON_REGULAR, "non regular" }, \
{ CP_COMPRESSED, "compreesed" }, \
{ CP_HARDLINK, "hardlink" }, \ { CP_HARDLINK, "hardlink" }, \
{ CP_SB_NEED_CP, "sb needs cp" }, \ { CP_SB_NEED_CP, "sb needs cp" }, \
{ CP_WRONG_PINO, "wrong pino" }, \ { CP_WRONG_PINO, "wrong pino" }, \
...@@ -148,6 +149,11 @@ TRACE_DEFINE_ENUM(CP_TRIMMED); ...@@ -148,6 +149,11 @@ TRACE_DEFINE_ENUM(CP_TRIMMED);
{ F2FS_GOING_DOWN_METAFLUSH, "meta flush" }, \ { F2FS_GOING_DOWN_METAFLUSH, "meta flush" }, \
{ F2FS_GOING_DOWN_NEED_FSCK, "need fsck" }) { F2FS_GOING_DOWN_NEED_FSCK, "need fsck" })
#define show_compress_algorithm(type) \
__print_symbolic(type, \
{ COMPRESS_LZO, "LZO" }, \
{ COMPRESS_LZ4, "LZ4" })
struct f2fs_sb_info; struct f2fs_sb_info;
struct f2fs_io_info; struct f2fs_io_info;
struct extent_info; struct extent_info;
...@@ -1710,6 +1716,100 @@ TRACE_EVENT(f2fs_shutdown, ...@@ -1710,6 +1716,100 @@ TRACE_EVENT(f2fs_shutdown,
__entry->ret) __entry->ret)
); );
DECLARE_EVENT_CLASS(f2fs_zip_start,
TP_PROTO(struct inode *inode, pgoff_t cluster_idx,
unsigned int cluster_size, unsigned char algtype),
TP_ARGS(inode, cluster_idx, cluster_size, algtype),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(pgoff_t, idx)
__field(unsigned int, size)
__field(unsigned int, algtype)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->idx = cluster_idx;
__entry->size = cluster_size;
__entry->algtype = algtype;
),
TP_printk("dev = (%d,%d), ino = %lu, cluster_idx:%lu, "
"cluster_size = %u, algorithm = %s",
show_dev_ino(__entry),
__entry->idx,
__entry->size,
show_compress_algorithm(__entry->algtype))
);
DECLARE_EVENT_CLASS(f2fs_zip_end,
TP_PROTO(struct inode *inode, pgoff_t cluster_idx,
unsigned int compressed_size, int ret),
TP_ARGS(inode, cluster_idx, compressed_size, ret),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(ino_t, ino)
__field(pgoff_t, idx)
__field(unsigned int, size)
__field(unsigned int, ret)
),
TP_fast_assign(
__entry->dev = inode->i_sb->s_dev;
__entry->ino = inode->i_ino;
__entry->idx = cluster_idx;
__entry->size = compressed_size;
__entry->ret = ret;
),
TP_printk("dev = (%d,%d), ino = %lu, cluster_idx:%lu, "
"compressed_size = %u, ret = %d",
show_dev_ino(__entry),
__entry->idx,
__entry->size,
__entry->ret)
);
DEFINE_EVENT(f2fs_zip_start, f2fs_compress_pages_start,
TP_PROTO(struct inode *inode, pgoff_t cluster_idx,
unsigned int cluster_size, unsigned char algtype),
TP_ARGS(inode, cluster_idx, cluster_size, algtype)
);
DEFINE_EVENT(f2fs_zip_start, f2fs_decompress_pages_start,
TP_PROTO(struct inode *inode, pgoff_t cluster_idx,
unsigned int cluster_size, unsigned char algtype),
TP_ARGS(inode, cluster_idx, cluster_size, algtype)
);
DEFINE_EVENT(f2fs_zip_end, f2fs_compress_pages_end,
TP_PROTO(struct inode *inode, pgoff_t cluster_idx,
unsigned int compressed_size, int ret),
TP_ARGS(inode, cluster_idx, compressed_size, ret)
);
DEFINE_EVENT(f2fs_zip_end, f2fs_decompress_pages_end,
TP_PROTO(struct inode *inode, pgoff_t cluster_idx,
unsigned int compressed_size, int ret),
TP_ARGS(inode, cluster_idx, compressed_size, ret)
);
#endif /* _TRACE_F2FS_H */ #endif /* _TRACE_F2FS_H */
/* This part must be outside protection */ /* This part must be outside protection */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment