Merge remote-tracking branch 'aosp/upstream-f2fs-stable-linux-5.10.y' into android12-5.10

* aosp/upstream-f2fs-stable-linux-5.10.y:
  Revert "f2fs: avoid attaching SB_ACTIVE flag during mount/remount"
  f2fs: remove false alarm on iget failure during GC
  f2fs: enable extent cache for compression files in read-only
  f2fs: fix to avoid adding tab before doc section
  f2fs: introduce f2fs_casefolded_name slab cache
  f2fs: swap: support migrating swapfile in aligned write mode
  f2fs: swap: remove dead codes
  f2fs: compress: add compress_inode to cache compressed blocks
  f2fs: clean up /sys/fs/f2fs/<disk>/features
  f2fs: add pin_file in feature list
  f2fs: Advertise encrypted casefolding in sysfs
  f2fs: Show casefolding support only when supported
  f2fs: support RO feature
  f2fs: logging neatening

Bug: 186107892
Bug: 190759634
Bug: 190517210
Signed-off-by: Jaegeuk Kim <jaegeuk@google.com>
Change-Id: I8eb93d8a43304b98166676da52a9c2434b15b942
This commit is contained in:
Jaegeuk Kim
2021-06-23 18:31:53 -07:00
18 changed files with 758 additions and 301 deletions

View File

@@ -203,7 +203,34 @@ Description: Shows total written kbytes issued to disk.
What: /sys/fs/f2fs/<disk>/features What: /sys/fs/f2fs/<disk>/features
Date: July 2017 Date: July 2017
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org> Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
Description: Shows all enabled features in current device. Description: <deprecated: should use /sys/fs/f2fs/<disk>/feature_list/
Shows all enabled features in current device.
Supported features:
encryption, blkzoned, extra_attr, projquota, inode_checksum,
flexible_inline_xattr, quota_ino, inode_crtime, lost_found,
verity, sb_checksum, casefold, readonly, compression, pin_file.
What: /sys/fs/f2fs/<disk>/feature_list/
Date: June 2021
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
Description: Expand /sys/fs/f2fs/<disk>/features to meet sysfs rule.
Supported on-disk features:
encryption, block_zoned (aka blkzoned), extra_attr,
project_quota (aka projquota), inode_checksum,
flexible_inline_xattr, quota_ino, inode_crtime, lost_found,
verity, sb_checksum, casefold, readonly, compression.
Note that, pin_file is moved into /sys/fs/f2fs/features/.
What: /sys/fs/f2fs/features/
Date: July 2017
Contact: "Jaegeuk Kim" <jaegeuk@kernel.org>
Description: Shows all enabled kernel features.
Supported features:
encryption, block_zoned, extra_attr, project_quota,
inode_checksum, flexible_inline_xattr, quota_ino,
inode_crtime, lost_found, verity, sb_checksum,
casefold, readonly, compression, test_dummy_encryption_v2,
atomic_write, pin_file, encrypted_casefold.
What: /sys/fs/f2fs/<disk>/inject_rate What: /sys/fs/f2fs/<disk>/inject_rate
Date: May 2016 Date: May 2016

View File

@@ -289,6 +289,9 @@ compress_mode=%s Control file compression mode. This supports "fs" and "user"
choosing the target file and the timing. The user can do manual choosing the target file and the timing. The user can do manual
compression/decompression on the compression enabled files using compression/decompression on the compression enabled files using
ioctls. ioctls.
compress_cache Support to use address space of a filesystem managed inode to
cache compressed block, in order to improve cache hit ratio of
random read.
inlinecrypt When possible, encrypt/decrypt the contents of encrypted inlinecrypt When possible, encrypt/decrypt the contents of encrypted
files using the blk-crypto framework rather than files using the blk-crypto framework rather than
filesystem-layer encryption. This allows the use of filesystem-layer encryption. This allows the use of
@@ -717,10 +720,10 @@ users.
===================== ======================== =================== ===================== ======================== ===================
User F2FS Block User F2FS Block
===================== ======================== =================== ===================== ======================== ===================
META WRITE_LIFE_NOT_SET N/A META WRITE_LIFE_NOT_SET
HOT_NODE " N/A HOT_NODE "
WARM_NODE " N/A WARM_NODE "
COLD_NODE " N/A COLD_NODE "
ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME
extension list " " extension list " "
@@ -746,10 +749,10 @@ WRITE_LIFE_LONG " WRITE_LIFE_LONG
===================== ======================== =================== ===================== ======================== ===================
User F2FS Block User F2FS Block
===================== ======================== =================== ===================== ======================== ===================
META WRITE_LIFE_MEDIUM; N/A META WRITE_LIFE_MEDIUM;
HOT_NODE WRITE_LIFE_NOT_SET N/A HOT_NODE WRITE_LIFE_NOT_SET
WARM_NODE " N/A WARM_NODE "
COLD_NODE WRITE_LIFE_NONE N/A COLD_NODE WRITE_LIFE_NONE
ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME
extension list " " extension list " "

View File

@@ -691,6 +691,9 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
} }
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
/* Needed for iput() to work correctly and not trash data */
sbi->sb->s_flags |= SB_ACTIVE;
/* /*
* Turn on quotas which were not enabled for read-only mounts if * Turn on quotas which were not enabled for read-only mounts if
* filesystem has quota feature, so that they are updated correctly. * filesystem has quota feature, so that they are updated correctly.

View File

@@ -12,9 +12,11 @@
#include <linux/lzo.h> #include <linux/lzo.h>
#include <linux/lz4.h> #include <linux/lz4.h>
#include <linux/zstd.h> #include <linux/zstd.h>
#include <linux/pagevec.h>
#include "f2fs.h" #include "f2fs.h"
#include "node.h" #include "node.h"
#include "segment.h"
#include <trace/events/f2fs.h> #include <trace/events/f2fs.h>
static struct kmem_cache *cic_entry_slab; static struct kmem_cache *cic_entry_slab;
@@ -736,7 +738,7 @@ out:
return ret; return ret;
} }
static void f2fs_decompress_cluster(struct decompress_io_ctx *dic) void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode); struct f2fs_sb_info *sbi = F2FS_I_SB(dic->inode);
struct f2fs_inode_info *fi = F2FS_I(dic->inode); struct f2fs_inode_info *fi = F2FS_I(dic->inode);
@@ -835,7 +837,8 @@ out_end_io:
* page being waited on in the cluster, and if so, it decompresses the cluster * page being waited on in the cluster, and if so, it decompresses the cluster
* (or in the case of a failure, cleans up without actually decompressing). * (or in the case of a failure, cleans up without actually decompressing).
*/ */
void f2fs_end_read_compressed_page(struct page *page, bool failed) void f2fs_end_read_compressed_page(struct page *page, bool failed,
block_t blkaddr)
{ {
struct decompress_io_ctx *dic = struct decompress_io_ctx *dic =
(struct decompress_io_ctx *)page_private(page); (struct decompress_io_ctx *)page_private(page);
@@ -845,6 +848,9 @@ void f2fs_end_read_compressed_page(struct page *page, bool failed)
if (failed) if (failed)
WRITE_ONCE(dic->failed, true); WRITE_ONCE(dic->failed, true);
else if (blkaddr)
f2fs_cache_compressed_page(sbi, page,
dic->inode->i_ino, blkaddr);
if (atomic_dec_and_test(&dic->remaining_pages)) if (atomic_dec_and_test(&dic->remaining_pages))
f2fs_decompress_cluster(dic); f2fs_decompress_cluster(dic);
@@ -1660,6 +1666,164 @@ void f2fs_put_page_dic(struct page *page)
f2fs_put_dic(dic); f2fs_put_dic(dic);
} }
const struct address_space_operations f2fs_compress_aops = {
.releasepage = f2fs_release_page,
.invalidatepage = f2fs_invalidate_page,
};
struct address_space *COMPRESS_MAPPING(struct f2fs_sb_info *sbi)
{
return sbi->compress_inode->i_mapping;
}
void f2fs_invalidate_compress_page(struct f2fs_sb_info *sbi, block_t blkaddr)
{
if (!sbi->compress_inode)
return;
invalidate_mapping_pages(COMPRESS_MAPPING(sbi), blkaddr, blkaddr);
}
void f2fs_cache_compressed_page(struct f2fs_sb_info *sbi, struct page *page,
nid_t ino, block_t blkaddr)
{
struct page *cpage;
int ret;
if (!test_opt(sbi, COMPRESS_CACHE))
return;
if (!f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC_ENHANCE_READ))
return;
if (!f2fs_available_free_memory(sbi, COMPRESS_PAGE))
return;
cpage = find_get_page(COMPRESS_MAPPING(sbi), blkaddr);
if (cpage) {
f2fs_put_page(cpage, 0);
return;
}
cpage = alloc_page(__GFP_NOWARN | __GFP_IO);
if (!cpage)
return;
ret = add_to_page_cache_lru(cpage, COMPRESS_MAPPING(sbi),
blkaddr, GFP_NOFS);
if (ret) {
f2fs_put_page(cpage, 0);
return;
}
set_page_private_data(cpage, ino);
if (!f2fs_is_valid_blkaddr(sbi, blkaddr, DATA_GENERIC_ENHANCE_READ))
goto out;
memcpy(page_address(cpage), page_address(page), PAGE_SIZE);
SetPageUptodate(cpage);
out:
f2fs_put_page(cpage, 1);
}
bool f2fs_load_compressed_page(struct f2fs_sb_info *sbi, struct page *page,
block_t blkaddr)
{
struct page *cpage;
bool hitted = false;
if (!test_opt(sbi, COMPRESS_CACHE))
return false;
cpage = f2fs_pagecache_get_page(COMPRESS_MAPPING(sbi),
blkaddr, FGP_LOCK | FGP_NOWAIT, GFP_NOFS);
if (cpage) {
if (PageUptodate(cpage)) {
atomic_inc(&sbi->compress_page_hit);
memcpy(page_address(page),
page_address(cpage), PAGE_SIZE);
hitted = true;
}
f2fs_put_page(cpage, 1);
}
return hitted;
}
void f2fs_invalidate_compress_pages(struct f2fs_sb_info *sbi, nid_t ino)
{
struct address_space *mapping = sbi->compress_inode->i_mapping;
struct pagevec pvec;
pgoff_t index = 0;
pgoff_t end = MAX_BLKADDR(sbi);
if (!mapping->nrpages)
return;
pagevec_init(&pvec);
do {
unsigned int nr_pages;
int i;
nr_pages = pagevec_lookup_range(&pvec, mapping,
&index, end - 1);
if (!nr_pages)
break;
for (i = 0; i < nr_pages; i++) {
struct page *page = pvec.pages[i];
if (page->index > end)
break;
lock_page(page);
if (page->mapping != mapping) {
unlock_page(page);
continue;
}
if (ino != get_page_private_data(page)) {
unlock_page(page);
continue;
}
generic_error_remove_page(mapping, page);
unlock_page(page);
}
pagevec_release(&pvec);
cond_resched();
} while (index < end);
}
int f2fs_init_compress_inode(struct f2fs_sb_info *sbi)
{
struct inode *inode;
if (!test_opt(sbi, COMPRESS_CACHE))
return 0;
inode = f2fs_iget(sbi->sb, F2FS_COMPRESS_INO(sbi));
if (IS_ERR(inode))
return PTR_ERR(inode);
sbi->compress_inode = inode;
sbi->compress_percent = COMPRESS_PERCENT;
sbi->compress_watermark = COMPRESS_WATERMARK;
atomic_set(&sbi->compress_page_hit, 0);
return 0;
}
void f2fs_destroy_compress_inode(struct f2fs_sb_info *sbi)
{
if (!sbi->compress_inode)
return;
iput(sbi->compress_inode);
sbi->compress_inode = NULL;
}
int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi) int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi)
{ {
dev_t dev = sbi->sb->s_bdev->bd_dev; dev_t dev = sbi->sb->s_bdev->bd_dev;

View File

@@ -133,7 +133,7 @@ static void f2fs_finish_read_bio(struct bio *bio)
if (f2fs_is_compressed_page(page)) { if (f2fs_is_compressed_page(page)) {
if (bio->bi_status) if (bio->bi_status)
f2fs_end_read_compressed_page(page, true); f2fs_end_read_compressed_page(page, true, 0);
f2fs_put_page_dic(page); f2fs_put_page_dic(page);
continue; continue;
} }
@@ -229,15 +229,19 @@ static void f2fs_handle_step_decompress(struct bio_post_read_ctx *ctx)
struct bio_vec *bv; struct bio_vec *bv;
struct bvec_iter_all iter_all; struct bvec_iter_all iter_all;
bool all_compressed = true; bool all_compressed = true;
block_t blkaddr = SECTOR_TO_BLOCK(ctx->bio->bi_iter.bi_sector);
bio_for_each_segment_all(bv, ctx->bio, iter_all) { bio_for_each_segment_all(bv, ctx->bio, iter_all) {
struct page *page = bv->bv_page; struct page *page = bv->bv_page;
/* PG_error was set if decryption failed. */ /* PG_error was set if decryption failed. */
if (f2fs_is_compressed_page(page)) if (f2fs_is_compressed_page(page))
f2fs_end_read_compressed_page(page, PageError(page)); f2fs_end_read_compressed_page(page, PageError(page),
blkaddr);
else else
all_compressed = false; all_compressed = false;
blkaddr++;
} }
/* /*
@@ -1368,9 +1372,11 @@ alloc:
old_blkaddr = dn->data_blkaddr; old_blkaddr = dn->data_blkaddr;
f2fs_allocate_data_block(sbi, NULL, old_blkaddr, &dn->data_blkaddr, f2fs_allocate_data_block(sbi, NULL, old_blkaddr, &dn->data_blkaddr,
&sum, seg_type, NULL); &sum, seg_type, NULL);
if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) {
invalidate_mapping_pages(META_MAPPING(sbi), invalidate_mapping_pages(META_MAPPING(sbi),
old_blkaddr, old_blkaddr); old_blkaddr, old_blkaddr);
f2fs_invalidate_compress_page(sbi, old_blkaddr);
}
f2fs_update_data_blkaddr(dn, dn->data_blkaddr); f2fs_update_data_blkaddr(dn, dn->data_blkaddr);
/* /*
@@ -2190,7 +2196,7 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
goto out_put_dnode; goto out_put_dnode;
} }
for (i = 0; i < dic->nr_cpages; i++) { for (i = 0; i < cc->nr_cpages; i++) {
struct page *page = dic->cpages[i]; struct page *page = dic->cpages[i];
block_t blkaddr; block_t blkaddr;
struct bio_post_read_ctx *ctx; struct bio_post_read_ctx *ctx;
@@ -2198,6 +2204,14 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
blkaddr = data_blkaddr(dn.inode, dn.node_page, blkaddr = data_blkaddr(dn.inode, dn.node_page,
dn.ofs_in_node + i + 1); dn.ofs_in_node + i + 1);
f2fs_wait_on_block_writeback(inode, blkaddr);
if (f2fs_load_compressed_page(sbi, page, blkaddr)) {
if (atomic_dec_and_test(&dic->remaining_pages))
f2fs_decompress_cluster(dic);
continue;
}
if (bio && (!page_is_mergeable(sbi, bio, if (bio && (!page_is_mergeable(sbi, bio,
*last_block_in_bio, blkaddr) || *last_block_in_bio, blkaddr) ||
!f2fs_crypt_mergeable_bio(bio, inode, page->index, NULL))) { !f2fs_crypt_mergeable_bio(bio, inode, page->index, NULL))) {
@@ -2219,8 +2233,6 @@ submit_and_realloc:
} }
} }
f2fs_wait_on_block_writeback(inode, blkaddr);
if (bio_add_page(bio, page, blocksize, 0) < blocksize) if (bio_add_page(bio, page, blocksize, 0) < blocksize)
goto submit_and_realloc; goto submit_and_realloc;
@@ -2476,6 +2488,10 @@ static inline bool check_inplace_update_policy(struct inode *inode,
bool f2fs_should_update_inplace(struct inode *inode, struct f2fs_io_info *fio) bool f2fs_should_update_inplace(struct inode *inode, struct f2fs_io_info *fio)
{ {
/* swap file is migrating in aligned write mode */
if (is_inode_flag_set(inode, FI_ALIGNED_WRITE))
return false;
if (f2fs_is_pinned_file(inode)) if (f2fs_is_pinned_file(inode))
return true; return true;
@@ -2498,6 +2514,11 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
return true; return true;
if (f2fs_is_atomic_file(inode)) if (f2fs_is_atomic_file(inode))
return true; return true;
/* swap file is migrating in aligned write mode */
if (is_inode_flag_set(inode, FI_ALIGNED_WRITE))
return true;
if (fio) { if (fio) {
if (page_private_gcing(fio->page)) if (page_private_gcing(fio->page))
return true; return true;
@@ -3675,6 +3696,13 @@ void f2fs_invalidate_page(struct page *page, unsigned int offset,
clear_page_private_gcing(page); clear_page_private_gcing(page);
if (test_opt(sbi, COMPRESS_CACHE)) {
if (f2fs_compressed_file(inode))
f2fs_invalidate_compress_pages(sbi, inode->i_ino);
if (inode->i_ino == F2FS_COMPRESS_INO(sbi))
clear_page_private_data(page);
}
if (page_private_atomic(page)) if (page_private_atomic(page))
return f2fs_drop_inmem_page(inode, page); return f2fs_drop_inmem_page(inode, page);
@@ -3692,6 +3720,16 @@ int f2fs_release_page(struct page *page, gfp_t wait)
if (page_private_atomic(page)) if (page_private_atomic(page))
return 0; return 0;
if (test_opt(F2FS_P_SB(page), COMPRESS_CACHE)) {
struct f2fs_sb_info *sbi = F2FS_P_SB(page);
struct inode *inode = page->mapping->host;
if (f2fs_compressed_file(inode))
f2fs_invalidate_compress_pages(sbi, inode->i_ino);
if (inode->i_ino == F2FS_COMPRESS_INO(sbi))
clear_page_private_data(page);
}
clear_page_private_gcing(page); clear_page_private_gcing(page);
detach_page_private(page); detach_page_private(page);
@@ -3858,67 +3896,66 @@ int f2fs_migrate_page(struct address_space *mapping,
#endif #endif
#ifdef CONFIG_SWAP #ifdef CONFIG_SWAP
static int f2fs_is_file_aligned(struct inode *inode) static int f2fs_migrate_blocks(struct inode *inode, block_t start_blk,
unsigned int blkcnt)
{ {
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
block_t main_blkaddr = SM_I(sbi)->main_blkaddr; unsigned int blkofs;
block_t cur_lblock; unsigned int blk_per_sec = BLKS_PER_SEC(sbi);
block_t last_lblock; unsigned int secidx = start_blk / blk_per_sec;
block_t pblock; unsigned int end_sec = secidx + blkcnt / blk_per_sec;
unsigned long nr_pblocks;
unsigned int blocks_per_sec = BLKS_PER_SEC(sbi);
unsigned int not_aligned = 0;
int ret = 0; int ret = 0;
cur_lblock = 0; down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
last_lblock = bytes_to_blks(inode, i_size_read(inode)); down_write(&F2FS_I(inode)->i_mmap_sem);
while (cur_lblock < last_lblock) { set_inode_flag(inode, FI_ALIGNED_WRITE);
struct f2fs_map_blocks map;
memset(&map, 0, sizeof(map)); for (; secidx < end_sec; secidx++) {
map.m_lblk = cur_lblock; down_write(&sbi->pin_sem);
map.m_len = last_lblock - cur_lblock;
map.m_next_pgofs = NULL;
map.m_next_extent = NULL;
map.m_seg_type = NO_CHECK_TYPE;
map.m_may_create = false;
ret = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_FIEMAP); f2fs_lock_op(sbi);
if (ret) f2fs_allocate_new_section(sbi, CURSEG_COLD_DATA_PINNED, false);
goto out; f2fs_unlock_op(sbi);
/* hole */ set_inode_flag(inode, FI_DO_DEFRAG);
if (!(map.m_flags & F2FS_MAP_FLAGS)) {
f2fs_err(sbi, "Swapfile has holes\n");
ret = -ENOENT;
goto out;
}
pblock = map.m_pblk; for (blkofs = 0; blkofs < blk_per_sec; blkofs++) {
nr_pblocks = map.m_len; struct page *page;
unsigned int blkidx = secidx * blk_per_sec + blkofs;
if ((pblock - main_blkaddr) & (blocks_per_sec - 1) || page = f2fs_get_lock_data_page(inode, blkidx, true);
nr_pblocks & (blocks_per_sec - 1)) { if (IS_ERR(page)) {
if (f2fs_is_pinned_file(inode)) { up_write(&sbi->pin_sem);
f2fs_err(sbi, "Swapfile does not align to section"); ret = PTR_ERR(page);
ret = -EINVAL; goto done;
goto out;
} }
not_aligned++;
set_page_dirty(page);
f2fs_put_page(page, 1);
} }
cur_lblock += nr_pblocks; clear_inode_flag(inode, FI_DO_DEFRAG);
ret = filemap_fdatawrite(inode->i_mapping);
up_write(&sbi->pin_sem);
if (ret)
break;
} }
if (not_aligned)
f2fs_warn(sbi, "Swapfile (%u) is not align to section: \n" done:
"\t1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate()", clear_inode_flag(inode, FI_DO_DEFRAG);
not_aligned); clear_inode_flag(inode, FI_ALIGNED_WRITE);
out:
up_write(&F2FS_I(inode)->i_mmap_sem);
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
return ret; return ret;
} }
static int check_swap_activate_fast(struct swap_info_struct *sis, static int check_swap_activate(struct swap_info_struct *sis,
struct file *swap_file, sector_t *span) struct file *swap_file, sector_t *span)
{ {
struct address_space *mapping = swap_file->f_mapping; struct address_space *mapping = swap_file->f_mapping;
@@ -3931,7 +3968,8 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
sector_t highest_pblock = 0; sector_t highest_pblock = 0;
int nr_extents = 0; int nr_extents = 0;
unsigned long nr_pblocks; unsigned long nr_pblocks;
unsigned int blocks_per_sec = BLKS_PER_SEC(sbi); unsigned int blks_per_sec = BLKS_PER_SEC(sbi);
unsigned int sec_blks_mask = BLKS_PER_SEC(sbi) - 1;
unsigned int not_aligned = 0; unsigned int not_aligned = 0;
int ret = 0; int ret = 0;
@@ -3944,7 +3982,7 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
while (cur_lblock < last_lblock && cur_lblock < sis->max) { while (cur_lblock < last_lblock && cur_lblock < sis->max) {
struct f2fs_map_blocks map; struct f2fs_map_blocks map;
retry:
cond_resched(); cond_resched();
memset(&map, 0, sizeof(map)); memset(&map, 0, sizeof(map));
@@ -3961,7 +3999,7 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
/* hole */ /* hole */
if (!(map.m_flags & F2FS_MAP_FLAGS)) { if (!(map.m_flags & F2FS_MAP_FLAGS)) {
f2fs_err(sbi, "Swapfile has holes\n"); f2fs_err(sbi, "Swapfile has holes");
ret = -EINVAL; ret = -EINVAL;
goto out; goto out;
} }
@@ -3969,16 +4007,28 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
pblock = map.m_pblk; pblock = map.m_pblk;
nr_pblocks = map.m_len; nr_pblocks = map.m_len;
if ((pblock - SM_I(sbi)->main_blkaddr) & (blocks_per_sec - 1) || if ((pblock - SM_I(sbi)->main_blkaddr) & sec_blks_mask ||
nr_pblocks & (blocks_per_sec - 1)) { nr_pblocks & sec_blks_mask) {
if (f2fs_is_pinned_file(inode)) {
f2fs_err(sbi, "Swapfile does not align to section");
ret = -EINVAL;
goto out;
}
not_aligned++; not_aligned++;
}
nr_pblocks = roundup(nr_pblocks, blks_per_sec);
if (cur_lblock + nr_pblocks > sis->max)
nr_pblocks -= blks_per_sec;
if (!nr_pblocks) {
/* this extent is last one */
nr_pblocks = map.m_len;
f2fs_warn(sbi, "Swapfile: last extent is not aligned to section");
goto next;
}
ret = f2fs_migrate_blocks(inode, cur_lblock,
nr_pblocks);
if (ret)
goto out;
goto retry;
}
next:
if (cur_lblock + nr_pblocks >= sis->max) if (cur_lblock + nr_pblocks >= sis->max)
nr_pblocks = sis->max - cur_lblock; nr_pblocks = sis->max - cur_lblock;
@@ -4005,122 +4055,13 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
sis->max = cur_lblock; sis->max = cur_lblock;
sis->pages = cur_lblock - 1; sis->pages = cur_lblock - 1;
sis->highest_bit = cur_lblock - 1; sis->highest_bit = cur_lblock - 1;
out:
if (not_aligned) if (not_aligned)
f2fs_warn(sbi, "Swapfile (%u) is not align to section: \n" f2fs_warn(sbi, "Swapfile (%u) is not align to section: 1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate(%u * N)",
"\t1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate()", not_aligned, blks_per_sec * F2FS_BLKSIZE);
not_aligned);
out:
return ret; return ret;
} }
/* Copied from generic_swapfile_activate() to check any holes */
static int check_swap_activate(struct swap_info_struct *sis,
struct file *swap_file, sector_t *span)
{
struct address_space *mapping = swap_file->f_mapping;
struct inode *inode = mapping->host;
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
unsigned blocks_per_page;
unsigned long page_no;
sector_t probe_block;
sector_t last_block;
sector_t lowest_block = -1;
sector_t highest_block = 0;
int nr_extents = 0;
int ret = 0;
if (PAGE_SIZE == F2FS_BLKSIZE)
return check_swap_activate_fast(sis, swap_file, span);
ret = f2fs_is_file_aligned(inode);
if (ret)
goto out;
blocks_per_page = bytes_to_blks(inode, PAGE_SIZE);
/*
* Map all the blocks into the extent list. This code doesn't try
* to be very smart.
*/
probe_block = 0;
page_no = 0;
last_block = bytes_to_blks(inode, i_size_read(inode));
while ((probe_block + blocks_per_page) <= last_block &&
page_no < sis->max) {
unsigned block_in_page;
sector_t first_block;
sector_t block = 0;
cond_resched();
block = probe_block;
ret = bmap(inode, &block);
if (ret)
goto out;
if (!block)
goto bad_bmap;
first_block = block;
/*
* It must be PAGE_SIZE aligned on-disk
*/
if (first_block & (blocks_per_page - 1)) {
probe_block++;
goto reprobe;
}
for (block_in_page = 1; block_in_page < blocks_per_page;
block_in_page++) {
block = probe_block + block_in_page;
ret = bmap(inode, &block);
if (ret)
goto out;
if (!block)
goto bad_bmap;
if (block != first_block + block_in_page) {
/* Discontiguity */
probe_block++;
goto reprobe;
}
}
first_block >>= (PAGE_SHIFT - inode->i_blkbits);
if (page_no) { /* exclude the header page */
if (first_block < lowest_block)
lowest_block = first_block;
if (first_block > highest_block)
highest_block = first_block;
}
/*
* We found a PAGE_SIZE-length, PAGE_SIZE-aligned run of blocks
*/
ret = add_swap_extent(sis, page_no, 1, first_block);
if (ret < 0)
goto out;
nr_extents += ret;
page_no++;
probe_block += blocks_per_page;
reprobe:
continue;
}
ret = nr_extents;
*span = 1 + highest_block - lowest_block;
if (page_no == 0)
page_no = 1; /* force Empty message */
sis->max = page_no;
sis->pages = page_no - 1;
sis->highest_bit = page_no - 1;
out:
return ret;
bad_bmap:
f2fs_err(sbi, "Swapfile has holes\n");
return -EINVAL;
}
static int f2fs_swap_activate(struct swap_info_struct *sis, struct file *file, static int f2fs_swap_activate(struct swap_info_struct *sis, struct file *file,
sector_t *span) sector_t *span)
{ {

View File

@@ -152,6 +152,12 @@ static void update_general_status(struct f2fs_sb_info *sbi)
si->node_pages = NODE_MAPPING(sbi)->nrpages; si->node_pages = NODE_MAPPING(sbi)->nrpages;
if (sbi->meta_inode) if (sbi->meta_inode)
si->meta_pages = META_MAPPING(sbi)->nrpages; si->meta_pages = META_MAPPING(sbi)->nrpages;
#ifdef CONFIG_F2FS_FS_COMPRESSION
if (sbi->compress_inode) {
si->compress_pages = COMPRESS_MAPPING(sbi)->nrpages;
si->compress_page_hit = atomic_read(&sbi->compress_page_hit);
}
#endif
si->nats = NM_I(sbi)->nat_cnt[TOTAL_NAT]; si->nats = NM_I(sbi)->nat_cnt[TOTAL_NAT];
si->dirty_nats = NM_I(sbi)->nat_cnt[DIRTY_NAT]; si->dirty_nats = NM_I(sbi)->nat_cnt[DIRTY_NAT];
si->sits = MAIN_SEGS(sbi); si->sits = MAIN_SEGS(sbi);
@@ -309,6 +315,12 @@ get_cache:
si->page_mem += (unsigned long long)npages << PAGE_SHIFT; si->page_mem += (unsigned long long)npages << PAGE_SHIFT;
} }
#ifdef CONFIG_F2FS_FS_COMPRESSION
if (sbi->compress_inode) {
unsigned npages = COMPRESS_MAPPING(sbi)->nrpages;
si->page_mem += (unsigned long long)npages << PAGE_SHIFT;
}
#endif
} }
static int stat_show(struct seq_file *s, void *v) static int stat_show(struct seq_file *s, void *v)
@@ -476,6 +488,7 @@ static int stat_show(struct seq_file *s, void *v)
"volatile IO: %4d (Max. %4d)\n", "volatile IO: %4d (Max. %4d)\n",
si->inmem_pages, si->aw_cnt, si->max_aw_cnt, si->inmem_pages, si->aw_cnt, si->max_aw_cnt,
si->vw_cnt, si->max_vw_cnt); si->vw_cnt, si->max_vw_cnt);
seq_printf(s, " - compress: %4d, hit:%8d\n", si->compress_pages, si->compress_page_hit);
seq_printf(s, " - nodes: %4d in %4d\n", seq_printf(s, " - nodes: %4d in %4d\n",
si->ndirty_node, si->node_pages); si->ndirty_node, si->node_pages);
seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n", seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n",

View File

@@ -16,6 +16,10 @@
#include "xattr.h" #include "xattr.h"
#include <trace/events/f2fs.h> #include <trace/events/f2fs.h>
#ifdef CONFIG_UNICODE
extern struct kmem_cache *f2fs_cf_name_slab;
#endif
static unsigned long dir_blocks(struct inode *inode) static unsigned long dir_blocks(struct inode *inode)
{ {
return ((unsigned long long) (i_size_read(inode) + PAGE_SIZE - 1)) return ((unsigned long long) (i_size_read(inode) + PAGE_SIZE - 1))
@@ -77,11 +81,10 @@ int f2fs_init_casefolded_name(const struct inode *dir,
{ {
#ifdef CONFIG_UNICODE #ifdef CONFIG_UNICODE
struct super_block *sb = dir->i_sb; struct super_block *sb = dir->i_sb;
struct f2fs_sb_info *sbi = F2FS_SB(sb);
if (IS_CASEFOLDED(dir)) { if (IS_CASEFOLDED(dir)) {
fname->cf_name.name = f2fs_kmalloc(sbi, F2FS_NAME_LEN, fname->cf_name.name = kmem_cache_alloc(f2fs_cf_name_slab,
GFP_NOFS); GFP_NOFS);
if (!fname->cf_name.name) if (!fname->cf_name.name)
return -ENOMEM; return -ENOMEM;
fname->cf_name.len = utf8_casefold(sb->s_encoding, fname->cf_name.len = utf8_casefold(sb->s_encoding,
@@ -89,7 +92,7 @@ int f2fs_init_casefolded_name(const struct inode *dir,
fname->cf_name.name, fname->cf_name.name,
F2FS_NAME_LEN); F2FS_NAME_LEN);
if ((int)fname->cf_name.len <= 0) { if ((int)fname->cf_name.len <= 0) {
kfree(fname->cf_name.name); kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name);
fname->cf_name.name = NULL; fname->cf_name.name = NULL;
if (sb_has_strict_encoding(sb)) if (sb_has_strict_encoding(sb))
return -EINVAL; return -EINVAL;
@@ -172,8 +175,10 @@ void f2fs_free_filename(struct f2fs_filename *fname)
fname->crypto_buf.name = NULL; fname->crypto_buf.name = NULL;
#endif #endif
#ifdef CONFIG_UNICODE #ifdef CONFIG_UNICODE
kfree(fname->cf_name.name); if (fname->cf_name.name) {
fname->cf_name.name = NULL; kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name);
fname->cf_name.name = NULL;
}
#endif #endif
} }

View File

@@ -98,6 +98,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
#define F2FS_MOUNT_ATGC 0x08000000 #define F2FS_MOUNT_ATGC 0x08000000
#define F2FS_MOUNT_MERGE_CHECKPOINT 0x10000000 #define F2FS_MOUNT_MERGE_CHECKPOINT 0x10000000
#define F2FS_MOUNT_GC_MERGE 0x20000000 #define F2FS_MOUNT_GC_MERGE 0x20000000
#define F2FS_MOUNT_COMPRESS_CACHE 0x40000000
#define F2FS_OPTION(sbi) ((sbi)->mount_opt) #define F2FS_OPTION(sbi) ((sbi)->mount_opt)
#define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option) #define clear_opt(sbi, option) (F2FS_OPTION(sbi).opt &= ~F2FS_MOUNT_##option)
@@ -168,6 +169,7 @@ struct f2fs_mount_info {
#define F2FS_FEATURE_SB_CHKSUM 0x0800 #define F2FS_FEATURE_SB_CHKSUM 0x0800
#define F2FS_FEATURE_CASEFOLD 0x1000 #define F2FS_FEATURE_CASEFOLD 0x1000
#define F2FS_FEATURE_COMPRESSION 0x2000 #define F2FS_FEATURE_COMPRESSION 0x2000
#define F2FS_FEATURE_RO 0x4000
#define __F2FS_HAS_FEATURE(raw_super, mask) \ #define __F2FS_HAS_FEATURE(raw_super, mask) \
((raw_super->feature & cpu_to_le32(mask)) != 0) ((raw_super->feature & cpu_to_le32(mask)) != 0)
@@ -707,6 +709,7 @@ enum {
FI_MMAP_FILE, /* indicate file was mmapped */ FI_MMAP_FILE, /* indicate file was mmapped */
FI_ENABLE_COMPRESS, /* enable compression in "user" compression mode */ FI_ENABLE_COMPRESS, /* enable compression in "user" compression mode */
FI_COMPRESS_RELEASED, /* compressed blocks were released */ FI_COMPRESS_RELEASED, /* compressed blocks were released */
FI_ALIGNED_WRITE, /* enable aligned write */
FI_MAX, /* max flag, never be used */ FI_MAX, /* max flag, never be used */
}; };
@@ -940,6 +943,7 @@ static inline void set_new_dnode(struct dnode_of_data *dn, struct inode *inode,
#define NR_CURSEG_DATA_TYPE (3) #define NR_CURSEG_DATA_TYPE (3)
#define NR_CURSEG_NODE_TYPE (3) #define NR_CURSEG_NODE_TYPE (3)
#define NR_CURSEG_INMEM_TYPE (2) #define NR_CURSEG_INMEM_TYPE (2)
#define NR_CURSEG_RO_TYPE (2)
#define NR_CURSEG_PERSIST_TYPE (NR_CURSEG_DATA_TYPE + NR_CURSEG_NODE_TYPE) #define NR_CURSEG_PERSIST_TYPE (NR_CURSEG_DATA_TYPE + NR_CURSEG_NODE_TYPE)
#define NR_CURSEG_TYPE (NR_CURSEG_INMEM_TYPE + NR_CURSEG_PERSIST_TYPE) #define NR_CURSEG_TYPE (NR_CURSEG_INMEM_TYPE + NR_CURSEG_PERSIST_TYPE)
@@ -1372,6 +1376,37 @@ PAGE_PRIVATE_CLEAR_FUNC(gcing, ONGOING_MIGRATION);
PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE); PAGE_PRIVATE_CLEAR_FUNC(atomic, ATOMIC_WRITE);
PAGE_PRIVATE_CLEAR_FUNC(dummy, DUMMY_WRITE); PAGE_PRIVATE_CLEAR_FUNC(dummy, DUMMY_WRITE);
static inline unsigned long get_page_private_data(struct page *page)
{
unsigned long data = page_private(page);
if (!test_bit(PAGE_PRIVATE_NOT_POINTER, &data))
return 0;
return data >> PAGE_PRIVATE_MAX;
}
static inline void set_page_private_data(struct page *page, unsigned long data)
{
if (!PagePrivate(page)) {
get_page(page);
SetPagePrivate(page);
}
set_bit(PAGE_PRIVATE_NOT_POINTER, &page_private(page));
page_private(page) |= data << PAGE_PRIVATE_MAX;
}
static inline void clear_page_private_data(struct page *page)
{
page_private(page) &= (1 << PAGE_PRIVATE_MAX) - 1;
if (page_private(page) == 1 << PAGE_PRIVATE_NOT_POINTER) {
set_page_private(page, 0);
if (PagePrivate(page)) {
ClearPagePrivate(page);
put_page(page);
}
}
}
/* For compression */ /* For compression */
enum compress_algorithm_type { enum compress_algorithm_type {
COMPRESS_LZO, COMPRESS_LZO,
@@ -1386,6 +1421,9 @@ enum compress_flag {
COMPRESS_MAX_FLAG, COMPRESS_MAX_FLAG,
}; };
#define COMPRESS_WATERMARK 20
#define COMPRESS_PERCENT 20
#define COMPRESS_DATA_RESERVED_SIZE 4 #define COMPRESS_DATA_RESERVED_SIZE 4
struct compress_data { struct compress_data {
__le32 clen; /* compressed data size */ __le32 clen; /* compressed data size */
@@ -1663,6 +1701,9 @@ struct f2fs_sb_info {
struct kobject s_stat_kobj; /* /sys/fs/f2fs/<devname>/stat */ struct kobject s_stat_kobj; /* /sys/fs/f2fs/<devname>/stat */
struct completion s_stat_kobj_unregister; struct completion s_stat_kobj_unregister;
struct kobject s_feature_list_kobj; /* /sys/fs/f2fs/<devname>/feature_list */
struct completion s_feature_list_kobj_unregister;
/* For shrinker support */ /* For shrinker support */
struct list_head s_list; struct list_head s_list;
int s_ndevs; /* number of devices */ int s_ndevs; /* number of devices */
@@ -1695,6 +1736,12 @@ struct f2fs_sb_info {
u64 compr_written_block; u64 compr_written_block;
u64 compr_saved_block; u64 compr_saved_block;
u32 compr_new_inode; u32 compr_new_inode;
/* For compressed block cache */
struct inode *compress_inode; /* cache compressed blocks */
unsigned int compress_percent; /* cache page percentage */
unsigned int compress_watermark; /* cache page watermark */
atomic_t compress_page_hit; /* cache hit count */
#endif #endif
}; };
@@ -3101,25 +3148,6 @@ static inline bool is_dot_dotdot(const u8 *name, size_t len)
return false; return false;
} }
static inline bool f2fs_may_extent_tree(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
if (!test_opt(sbi, EXTENT_CACHE) ||
is_inode_flag_set(inode, FI_NO_EXTENT) ||
is_inode_flag_set(inode, FI_COMPRESSED_FILE))
return false;
/*
* for recovered files during mount do not create extents
* if shrinker is not registered.
*/
if (list_empty(&sbi->s_list))
return false;
return S_ISREG(inode->i_mode);
}
static inline void *f2fs_kmalloc(struct f2fs_sb_info *sbi, static inline void *f2fs_kmalloc(struct f2fs_sb_info *sbi,
size_t size, gfp_t flags) size_t size, gfp_t flags)
{ {
@@ -3662,7 +3690,8 @@ struct f2fs_stat_info {
unsigned int bimodal, avg_vblocks; unsigned int bimodal, avg_vblocks;
int util_free, util_valid, util_invalid; int util_free, util_valid, util_invalid;
int rsvd_segs, overp_segs; int rsvd_segs, overp_segs;
int dirty_count, node_pages, meta_pages; int dirty_count, node_pages, meta_pages, compress_pages;
int compress_page_hit;
int prefree_count, call_count, cp_count, bg_cp_count; int prefree_count, call_count, cp_count, bg_cp_count;
int tot_segs, node_segs, data_segs, free_segs, free_secs; int tot_segs, node_segs, data_segs, free_segs, free_secs;
int bg_node_segs, bg_data_segs; int bg_node_segs, bg_data_segs;
@@ -3998,7 +4027,9 @@ void f2fs_compress_write_end_io(struct bio *bio, struct page *page);
bool f2fs_is_compress_backend_ready(struct inode *inode); bool f2fs_is_compress_backend_ready(struct inode *inode);
int f2fs_init_compress_mempool(void); int f2fs_init_compress_mempool(void);
void f2fs_destroy_compress_mempool(void); void f2fs_destroy_compress_mempool(void);
void f2fs_end_read_compressed_page(struct page *page, bool failed); void f2fs_decompress_cluster(struct decompress_io_ctx *dic);
void f2fs_end_read_compressed_page(struct page *page, bool failed,
block_t blkaddr);
bool f2fs_cluster_is_empty(struct compress_ctx *cc); bool f2fs_cluster_is_empty(struct compress_ctx *cc);
bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index); bool f2fs_cluster_can_merge_page(struct compress_ctx *cc, pgoff_t index);
void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page); void f2fs_compress_ctx_add_page(struct compress_ctx *cc, struct page *page);
@@ -4016,10 +4047,19 @@ void f2fs_put_page_dic(struct page *page);
int f2fs_init_compress_ctx(struct compress_ctx *cc); int f2fs_init_compress_ctx(struct compress_ctx *cc);
void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse); void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse);
void f2fs_init_compress_info(struct f2fs_sb_info *sbi); void f2fs_init_compress_info(struct f2fs_sb_info *sbi);
int f2fs_init_compress_inode(struct f2fs_sb_info *sbi);
void f2fs_destroy_compress_inode(struct f2fs_sb_info *sbi);
int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi); int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi);
void f2fs_destroy_page_array_cache(struct f2fs_sb_info *sbi); void f2fs_destroy_page_array_cache(struct f2fs_sb_info *sbi);
int __init f2fs_init_compress_cache(void); int __init f2fs_init_compress_cache(void);
void f2fs_destroy_compress_cache(void); void f2fs_destroy_compress_cache(void);
struct address_space *COMPRESS_MAPPING(struct f2fs_sb_info *sbi);
void f2fs_invalidate_compress_page(struct f2fs_sb_info *sbi, block_t blkaddr);
void f2fs_cache_compressed_page(struct f2fs_sb_info *sbi, struct page *page,
nid_t ino, block_t blkaddr);
bool f2fs_load_compressed_page(struct f2fs_sb_info *sbi, struct page *page,
block_t blkaddr);
void f2fs_invalidate_compress_pages(struct f2fs_sb_info *sbi, nid_t ino);
#define inc_compr_inode_stat(inode) \ #define inc_compr_inode_stat(inode) \
do { \ do { \
struct f2fs_sb_info *sbi = F2FS_I_SB(inode); \ struct f2fs_sb_info *sbi = F2FS_I_SB(inode); \
@@ -4048,7 +4088,9 @@ static inline struct page *f2fs_compress_control_page(struct page *page)
} }
static inline int f2fs_init_compress_mempool(void) { return 0; } static inline int f2fs_init_compress_mempool(void) { return 0; }
static inline void f2fs_destroy_compress_mempool(void) { } static inline void f2fs_destroy_compress_mempool(void) { }
static inline void f2fs_end_read_compressed_page(struct page *page, bool failed) static inline void f2fs_decompress_cluster(struct decompress_io_ctx *dic) { }
static inline void f2fs_end_read_compressed_page(struct page *page,
bool failed, block_t blkaddr)
{ {
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
@@ -4056,10 +4098,20 @@ static inline void f2fs_put_page_dic(struct page *page)
{ {
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
static inline int f2fs_init_compress_inode(struct f2fs_sb_info *sbi) { return 0; }
static inline void f2fs_destroy_compress_inode(struct f2fs_sb_info *sbi) { }
static inline int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi) { return 0; } static inline int f2fs_init_page_array_cache(struct f2fs_sb_info *sbi) { return 0; }
static inline void f2fs_destroy_page_array_cache(struct f2fs_sb_info *sbi) { } static inline void f2fs_destroy_page_array_cache(struct f2fs_sb_info *sbi) { }
static inline int __init f2fs_init_compress_cache(void) { return 0; } static inline int __init f2fs_init_compress_cache(void) { return 0; }
static inline void f2fs_destroy_compress_cache(void) { } static inline void f2fs_destroy_compress_cache(void) { }
static inline void f2fs_invalidate_compress_page(struct f2fs_sb_info *sbi,
block_t blkaddr) { }
static inline void f2fs_cache_compressed_page(struct f2fs_sb_info *sbi,
struct page *page, nid_t ino, block_t blkaddr) { }
static inline bool f2fs_load_compressed_page(struct f2fs_sb_info *sbi,
struct page *page, block_t blkaddr) { return false; }
static inline void f2fs_invalidate_compress_pages(struct f2fs_sb_info *sbi,
nid_t ino) { }
#define inc_compr_inode_stat(inode) do { } while (0) #define inc_compr_inode_stat(inode) do { } while (0)
#endif #endif
@@ -4124,6 +4176,27 @@ F2FS_FEATURE_FUNCS(verity, VERITY);
F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM); F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM);
F2FS_FEATURE_FUNCS(casefold, CASEFOLD); F2FS_FEATURE_FUNCS(casefold, CASEFOLD);
F2FS_FEATURE_FUNCS(compression, COMPRESSION); F2FS_FEATURE_FUNCS(compression, COMPRESSION);
F2FS_FEATURE_FUNCS(readonly, RO);
static inline bool f2fs_may_extent_tree(struct inode *inode)
{
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
if (!test_opt(sbi, EXTENT_CACHE) ||
is_inode_flag_set(inode, FI_NO_EXTENT) ||
(is_inode_flag_set(inode, FI_COMPRESSED_FILE) &&
!f2fs_sb_has_readonly(sbi)))
return false;
/*
* for recovered files during mount do not create extents
* if shrinker is not registered.
*/
if (list_empty(&sbi->s_list))
return false;
return S_ISREG(inode->i_mode);
}
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi, static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi,

View File

@@ -3360,7 +3360,7 @@ static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg)
if (!f2fs_sb_has_verity(F2FS_I_SB(inode))) { if (!f2fs_sb_has_verity(F2FS_I_SB(inode))) {
f2fs_warn(F2FS_I_SB(inode), f2fs_warn(F2FS_I_SB(inode),
"Can't enable fs-verity on inode %lu: the verity feature is not enabled on this filesystem.\n", "Can't enable fs-verity on inode %lu: the verity feature is not enabled on this filesystem",
inode->i_ino); inode->i_ino);
return -EOPNOTSUPP; return -EOPNOTSUPP;
} }
@@ -4144,9 +4144,8 @@ static int f2fs_ioc_decompress_file(struct file *filp, unsigned long arg)
LLONG_MAX); LLONG_MAX);
if (ret) if (ret)
f2fs_warn(sbi, "%s: The file might be partially decompressed " f2fs_warn(sbi, "%s: The file might be partially decompressed (errno=%d). Please delete the file.",
"(errno=%d). Please delete the file.\n", __func__, ret);
__func__, ret);
out: out:
inode_unlock(inode); inode_unlock(inode);
file_end_write(filp); file_end_write(filp);
@@ -4218,9 +4217,8 @@ static int f2fs_ioc_compress_file(struct file *filp, unsigned long arg)
clear_inode_flag(inode, FI_ENABLE_COMPRESS); clear_inode_flag(inode, FI_ENABLE_COMPRESS);
if (ret) if (ret)
f2fs_warn(sbi, "%s: The file might be partially compressed " f2fs_warn(sbi, "%s: The file might be partially compressed (errno=%d). Please delete the file.",
"(errno=%d). Please delete the file.\n", __func__, ret);
__func__, ret);
out: out:
inode_unlock(inode); inode_unlock(inode);
file_end_write(filp); file_end_write(filp);

View File

@@ -1031,8 +1031,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
if (unlikely(check_valid_map(sbi, segno, offset))) { if (unlikely(check_valid_map(sbi, segno, offset))) {
if (!test_and_set_bit(segno, SIT_I(sbi)->invalid_segmap)) { if (!test_and_set_bit(segno, SIT_I(sbi)->invalid_segmap)) {
f2fs_err(sbi, "mismatched blkaddr %u (source_blkaddr %u) in seg %u\n", f2fs_err(sbi, "mismatched blkaddr %u (source_blkaddr %u) in seg %u",
blkaddr, source_blkaddr, segno); blkaddr, source_blkaddr, segno);
f2fs_bug_on(sbi, 1); f2fs_bug_on(sbi, 1);
} }
} }
@@ -1261,6 +1261,7 @@ static int move_data_block(struct inode *inode, block_t bidx,
f2fs_put_page(mpage, 1); f2fs_put_page(mpage, 1);
invalidate_mapping_pages(META_MAPPING(fio.sbi), invalidate_mapping_pages(META_MAPPING(fio.sbi),
fio.old_blkaddr, fio.old_blkaddr); fio.old_blkaddr, fio.old_blkaddr);
f2fs_invalidate_compress_page(fio.sbi, fio.old_blkaddr);
set_page_dirty(fio.encrypted_page); set_page_dirty(fio.encrypted_page);
if (clear_page_dirty_for_io(fio.encrypted_page)) if (clear_page_dirty_for_io(fio.encrypted_page))
@@ -1450,10 +1451,8 @@ next_step:
if (phase == 3) { if (phase == 3) {
inode = f2fs_iget(sb, dni.ino); inode = f2fs_iget(sb, dni.ino);
if (IS_ERR(inode) || is_bad_inode(inode)) { if (IS_ERR(inode) || is_bad_inode(inode))
set_sbi_flag(sbi, SBI_NEED_FSCK);
continue; continue;
}
if (!down_write_trylock( if (!down_write_trylock(
&F2FS_I(inode)->i_gc_rwsem[WRITE])) { &F2FS_I(inode)->i_gc_rwsem[WRITE])) {

View File

@@ -18,6 +18,10 @@
#include <trace/events/f2fs.h> #include <trace/events/f2fs.h>
#ifdef CONFIG_F2FS_FS_COMPRESSION
extern const struct address_space_operations f2fs_compress_aops;
#endif
void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync) void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync)
{ {
if (is_inode_flag_set(inode, FI_NEW_INODE)) if (is_inode_flag_set(inode, FI_NEW_INODE))
@@ -494,6 +498,11 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi)) if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi))
goto make_now; goto make_now;
#ifdef CONFIG_F2FS_FS_COMPRESSION
if (ino == F2FS_COMPRESS_INO(sbi))
goto make_now;
#endif
ret = do_read_inode(inode); ret = do_read_inode(inode);
if (ret) if (ret)
goto bad_inode; goto bad_inode;
@@ -504,6 +513,12 @@ make_now:
} else if (ino == F2FS_META_INO(sbi)) { } else if (ino == F2FS_META_INO(sbi)) {
inode->i_mapping->a_ops = &f2fs_meta_aops; inode->i_mapping->a_ops = &f2fs_meta_aops;
mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS); mapping_set_gfp_mask(inode->i_mapping, GFP_NOFS);
} else if (ino == F2FS_COMPRESS_INO(sbi)) {
#ifdef CONFIG_F2FS_FS_COMPRESSION
inode->i_mapping->a_ops = &f2fs_compress_aops;
#endif
mapping_set_gfp_mask(inode->i_mapping,
GFP_NOFS | __GFP_HIGHMEM | __GFP_MOVABLE);
} else if (S_ISREG(inode->i_mode)) { } else if (S_ISREG(inode->i_mode)) {
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;
@@ -723,8 +738,12 @@ void f2fs_evict_inode(struct inode *inode)
trace_f2fs_evict_inode(inode); trace_f2fs_evict_inode(inode);
truncate_inode_pages_final(&inode->i_data); truncate_inode_pages_final(&inode->i_data);
if (test_opt(sbi, COMPRESS_CACHE) && f2fs_compressed_file(inode))
f2fs_invalidate_compress_pages(sbi, inode->i_ino);
if (inode->i_ino == F2FS_NODE_INO(sbi) || if (inode->i_ino == F2FS_NODE_INO(sbi) ||
inode->i_ino == F2FS_META_INO(sbi)) inode->i_ino == F2FS_META_INO(sbi) ||
inode->i_ino == F2FS_COMPRESS_INO(sbi))
goto out_clear; goto out_clear;
f2fs_bug_on(sbi, get_dirty_pages(inode)); f2fs_bug_on(sbi, get_dirty_pages(inode));

View File

@@ -97,6 +97,20 @@ bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type)
mem_size = (atomic_read(&dcc->discard_cmd_cnt) * mem_size = (atomic_read(&dcc->discard_cmd_cnt) *
sizeof(struct discard_cmd)) >> PAGE_SHIFT; sizeof(struct discard_cmd)) >> PAGE_SHIFT;
res = mem_size < (avail_ram * nm_i->ram_thresh / 100); res = mem_size < (avail_ram * nm_i->ram_thresh / 100);
} else if (type == COMPRESS_PAGE) {
#ifdef CONFIG_F2FS_FS_COMPRESSION
unsigned long free_ram = val.freeram;
/*
* free memory is lower than watermark or cached page count
* exceed threshold, deny caching compress page.
*/
res = (free_ram > avail_ram * sbi->compress_watermark / 100) &&
(COMPRESS_MAPPING(sbi)->nrpages <
free_ram * sbi->compress_percent / 100);
#else
res = false;
#endif
} else { } else {
if (!sbi->sb->s_bdi->wb.dirty_exceeded) if (!sbi->sb->s_bdi->wb.dirty_exceeded)
return true; return true;

View File

@@ -38,6 +38,9 @@
/* return value for read_node_page */ /* return value for read_node_page */
#define LOCKED_PAGE 1 #define LOCKED_PAGE 1
/* check pinned file's alignment status of physical blocks */
#define FILE_NOT_ALIGNED 1
/* For flag in struct node_info */ /* For flag in struct node_info */
enum { enum {
IS_CHECKPOINTED, /* is it checkpointed before? */ IS_CHECKPOINTED, /* is it checkpointed before? */
@@ -148,6 +151,7 @@ enum mem_type {
EXTENT_CACHE, /* indicates extent cache */ EXTENT_CACHE, /* indicates extent cache */
INMEM_PAGES, /* indicates inmemory pages */ INMEM_PAGES, /* indicates inmemory pages */
DISCARD_CACHE, /* indicates memory of cached discard cmds */ DISCARD_CACHE, /* indicates memory of cached discard cmds */
COMPRESS_PAGE, /* indicates memory of cached compressed pages */
BASE_CHECK, /* check kernel status */ BASE_CHECK, /* check kernel status */
}; };

View File

@@ -45,6 +45,10 @@
static struct kmem_cache *fsync_entry_slab; static struct kmem_cache *fsync_entry_slab;
#ifdef CONFIG_UNICODE
extern struct kmem_cache *f2fs_cf_name_slab;
#endif
bool f2fs_space_for_roll_forward(struct f2fs_sb_info *sbi) bool f2fs_space_for_roll_forward(struct f2fs_sb_info *sbi)
{ {
s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count); s64 nalloc = percpu_counter_sum_positive(&sbi->alloc_valid_block_count);
@@ -145,7 +149,7 @@ static int init_recovered_filename(const struct inode *dir,
f2fs_hash_filename(dir, fname); f2fs_hash_filename(dir, fname);
#ifdef CONFIG_UNICODE #ifdef CONFIG_UNICODE
/* Case-sensitive match is fine for recovery */ /* Case-sensitive match is fine for recovery */
kfree(fname->cf_name.name); kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name);
fname->cf_name.name = NULL; fname->cf_name.name = NULL;
#endif #endif
} else { } else {
@@ -782,6 +786,8 @@ int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
} }
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
/* Needed for iput() to work correctly and not trash data */
sbi->sb->s_flags |= SB_ACTIVE;
/* Turn on quotas so that they are updated correctly */ /* Turn on quotas so that they are updated correctly */
quota_enabled = f2fs_enable_quota_files(sbi, s_flags & SB_RDONLY); quota_enabled = f2fs_enable_quota_files(sbi, s_flags & SB_RDONLY);
#endif #endif
@@ -809,8 +815,10 @@ int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
err = recover_data(sbi, &inode_list, &tmp_inode_list, &dir_list); err = recover_data(sbi, &inode_list, &tmp_inode_list, &dir_list);
if (!err) if (!err)
f2fs_bug_on(sbi, !list_empty(&inode_list)); f2fs_bug_on(sbi, !list_empty(&inode_list));
else else {
f2fs_bug_on(sbi, sbi->sb->s_flags & SB_ACTIVE); /* restore s_flags to let iput() trash data */
sbi->sb->s_flags = s_flags;
}
skip: skip:
fix_curseg_write_pointer = !check_only || list_empty(&inode_list); fix_curseg_write_pointer = !check_only || list_empty(&inode_list);

View File

@@ -2322,6 +2322,7 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
return; return;
invalidate_mapping_pages(META_MAPPING(sbi), addr, addr); invalidate_mapping_pages(META_MAPPING(sbi), addr, addr);
f2fs_invalidate_compress_page(sbi, addr);
/* add it into sit main buffer */ /* add it into sit main buffer */
down_write(&sit_i->sentry_lock); down_write(&sit_i->sentry_lock);
@@ -3290,6 +3291,9 @@ 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_inode_flag_set(inode, FI_ALIGNED_WRITE))
return CURSEG_COLD_DATA_PINNED;
if (page_private_gcing(fio->page)) { if (page_private_gcing(fio->page)) {
if (fio->sbi->am.atgc_enabled && if (fio->sbi->am.atgc_enabled &&
(fio->io_type == FS_DATA_IO) && (fio->io_type == FS_DATA_IO) &&
@@ -3469,9 +3473,11 @@ static void do_write_page(struct f2fs_summary *sum, struct f2fs_io_info *fio)
reallocate: reallocate:
f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr, f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
&fio->new_blkaddr, sum, type, fio); &fio->new_blkaddr, sum, type, fio);
if (GET_SEGNO(fio->sbi, fio->old_blkaddr) != NULL_SEGNO) if (GET_SEGNO(fio->sbi, fio->old_blkaddr) != NULL_SEGNO) {
invalidate_mapping_pages(META_MAPPING(fio->sbi), invalidate_mapping_pages(META_MAPPING(fio->sbi),
fio->old_blkaddr, fio->old_blkaddr); fio->old_blkaddr, fio->old_blkaddr);
f2fs_invalidate_compress_page(fio->sbi, fio->old_blkaddr);
}
/* writeout dirty page into bdev */ /* writeout dirty page into bdev */
f2fs_submit_page_write(fio); f2fs_submit_page_write(fio);
@@ -3661,6 +3667,7 @@ void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) { if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) {
invalidate_mapping_pages(META_MAPPING(sbi), invalidate_mapping_pages(META_MAPPING(sbi),
old_blkaddr, old_blkaddr); old_blkaddr, old_blkaddr);
f2fs_invalidate_compress_page(sbi, old_blkaddr);
if (!from_gc) if (!from_gc)
update_segment_mtime(sbi, old_blkaddr, 0); update_segment_mtime(sbi, old_blkaddr, 0);
update_sit_entry(sbi, old_blkaddr, -1); update_sit_entry(sbi, old_blkaddr, -1);
@@ -3920,7 +3927,7 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi)
/* sanity check for summary blocks */ /* sanity check for summary blocks */
if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES || if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES ||
sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) { sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) {
f2fs_err(sbi, "invalid journal entries nats %u sits %u\n", f2fs_err(sbi, "invalid journal entries nats %u sits %u",
nats_in_cursum(nat_j), sits_in_cursum(sit_j)); nats_in_cursum(nat_j), sits_in_cursum(sit_j));
return -EINVAL; return -EINVAL;
} }
@@ -4683,6 +4690,10 @@ static int sanity_check_curseg(struct f2fs_sb_info *sbi)
struct seg_entry *se = get_seg_entry(sbi, curseg->segno); struct seg_entry *se = get_seg_entry(sbi, curseg->segno);
unsigned int blkofs = curseg->next_blkoff; unsigned int blkofs = curseg->next_blkoff;
if (f2fs_sb_has_readonly(sbi) &&
i != CURSEG_HOT_DATA && i != CURSEG_HOT_NODE)
continue;
sanity_check_seg_type(sbi, curseg->seg_type); sanity_check_seg_type(sbi, curseg->seg_type);
if (f2fs_test_bit(blkofs, se->cur_valid_map)) if (f2fs_test_bit(blkofs, se->cur_valid_map))

View File

@@ -150,6 +150,7 @@ enum {
Opt_compress_extension, Opt_compress_extension,
Opt_compress_chksum, Opt_compress_chksum,
Opt_compress_mode, Opt_compress_mode,
Opt_compress_cache,
Opt_atgc, Opt_atgc,
Opt_gc_merge, Opt_gc_merge,
Opt_nogc_merge, Opt_nogc_merge,
@@ -224,6 +225,7 @@ static match_table_t f2fs_tokens = {
{Opt_compress_extension, "compress_extension=%s"}, {Opt_compress_extension, "compress_extension=%s"},
{Opt_compress_chksum, "compress_chksum"}, {Opt_compress_chksum, "compress_chksum"},
{Opt_compress_mode, "compress_mode=%s"}, {Opt_compress_mode, "compress_mode=%s"},
{Opt_compress_cache, "compress_cache"},
{Opt_atgc, "atgc"}, {Opt_atgc, "atgc"},
{Opt_gc_merge, "gc_merge"}, {Opt_gc_merge, "gc_merge"},
{Opt_nogc_merge, "nogc_merge"}, {Opt_nogc_merge, "nogc_merge"},
@@ -275,6 +277,24 @@ static int f2fs_sb_read_encoding(const struct f2fs_super_block *sb,
return 0; return 0;
} }
struct kmem_cache *f2fs_cf_name_slab;
static int __init f2fs_create_casefold_cache(void)
{
f2fs_cf_name_slab = f2fs_kmem_cache_create("f2fs_casefolded_name",
F2FS_NAME_LEN);
if (!f2fs_cf_name_slab)
return -ENOMEM;
return 0;
}
static void f2fs_destroy_casefold_cache(void)
{
kmem_cache_destroy(f2fs_cf_name_slab);
}
#else
static int __init f2fs_create_casefold_cache(void) { return 0; }
static void f2fs_destroy_casefold_cache(void) { }
#endif #endif
static inline void limit_reserve_root(struct f2fs_sb_info *sbi) static inline void limit_reserve_root(struct f2fs_sb_info *sbi)
@@ -555,7 +575,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
int ret; int ret;
if (!options) if (!options)
return 0; goto default_check;
while ((p = strsep(&options, ",")) != NULL) { while ((p = strsep(&options, ",")) != NULL) {
int token; int token;
@@ -1066,12 +1086,16 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
} }
kfree(name); kfree(name);
break; break;
case Opt_compress_cache:
set_opt(sbi, COMPRESS_CACHE);
break;
#else #else
case Opt_compress_algorithm: case Opt_compress_algorithm:
case Opt_compress_log_size: case Opt_compress_log_size:
case Opt_compress_extension: case Opt_compress_extension:
case Opt_compress_chksum: case Opt_compress_chksum:
case Opt_compress_mode: case Opt_compress_mode:
case Opt_compress_cache:
f2fs_info(sbi, "compression options not supported"); f2fs_info(sbi, "compression options not supported");
break; break;
#endif #endif
@@ -1090,6 +1114,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
return -EINVAL; return -EINVAL;
} }
} }
default_check:
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
if (f2fs_check_quota_options(sbi)) if (f2fs_check_quota_options(sbi))
return -EINVAL; return -EINVAL;
@@ -1153,7 +1178,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
} }
if (test_opt(sbi, DISABLE_CHECKPOINT) && f2fs_lfs_mode(sbi)) { if (test_opt(sbi, DISABLE_CHECKPOINT) && f2fs_lfs_mode(sbi)) {
f2fs_err(sbi, "LFS not compatible with checkpoint=disable\n"); f2fs_err(sbi, "LFS not compatible with checkpoint=disable");
return -EINVAL; return -EINVAL;
} }
@@ -1162,6 +1187,11 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
*/ */
if (F2FS_OPTION(sbi).active_logs != NR_CURSEG_TYPE) if (F2FS_OPTION(sbi).active_logs != NR_CURSEG_TYPE)
F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF; F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
if (f2fs_sb_has_readonly(sbi) && !f2fs_readonly(sbi->sb)) {
f2fs_err(sbi, "Allow to mount readonly mode only");
return -EROFS;
}
return 0; return 0;
} }
@@ -1406,6 +1436,8 @@ static void f2fs_put_super(struct super_block *sb)
f2fs_bug_on(sbi, sbi->fsync_node_num); f2fs_bug_on(sbi, sbi->fsync_node_num);
f2fs_destroy_compress_inode(sbi);
iput(sbi->node_inode); iput(sbi->node_inode);
sbi->node_inode = NULL; sbi->node_inode = NULL;
@@ -1675,6 +1707,9 @@ static inline void f2fs_show_compress_options(struct seq_file *seq,
seq_printf(seq, ",compress_mode=%s", "fs"); seq_printf(seq, ",compress_mode=%s", "fs");
else if (F2FS_OPTION(sbi).compress_mode == COMPR_MODE_USER) else if (F2FS_OPTION(sbi).compress_mode == COMPR_MODE_USER)
seq_printf(seq, ",compress_mode=%s", "user"); seq_printf(seq, ",compress_mode=%s", "user");
if (test_opt(sbi, COMPRESS_CACHE))
seq_puts(seq, ",compress_cache");
} }
#endif #endif
@@ -1822,7 +1857,11 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
static void default_options(struct f2fs_sb_info *sbi) static void default_options(struct f2fs_sb_info *sbi)
{ {
/* init some FS parameters */ /* init some FS parameters */
F2FS_OPTION(sbi).active_logs = NR_CURSEG_PERSIST_TYPE; if (f2fs_sb_has_readonly(sbi))
F2FS_OPTION(sbi).active_logs = NR_CURSEG_RO_TYPE;
else
F2FS_OPTION(sbi).active_logs = NR_CURSEG_PERSIST_TYPE;
F2FS_OPTION(sbi).inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS; F2FS_OPTION(sbi).inline_xattr_size = DEFAULT_INLINE_XATTR_ADDRS;
F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF; F2FS_OPTION(sbi).whint_mode = WHINT_MODE_OFF;
F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT; F2FS_OPTION(sbi).alloc_mode = ALLOC_MODE_DEFAULT;
@@ -1869,15 +1908,17 @@ static int f2fs_enable_quotas(struct super_block *sb);
static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi) static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
{ {
unsigned int s_flags = sbi->sb->s_flags;
struct cp_control cpc; struct cp_control cpc;
int err = 0; int err = 0;
int ret; int ret;
block_t unusable; block_t unusable;
if (sbi->sb->s_flags & SB_RDONLY) { if (s_flags & SB_RDONLY) {
f2fs_err(sbi, "checkpoint=disable on readonly fs"); f2fs_err(sbi, "checkpoint=disable on readonly fs");
return -EINVAL; return -EINVAL;
} }
sbi->sb->s_flags |= SB_ACTIVE;
f2fs_update_time(sbi, DISABLE_TIME); f2fs_update_time(sbi, DISABLE_TIME);
@@ -1895,13 +1936,13 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
ret = sync_filesystem(sbi->sb); ret = sync_filesystem(sbi->sb);
if (ret || err) { if (ret || err) {
err = ret ? ret : err; err = ret ? ret : err;
goto out; goto restore_flag;
} }
unusable = f2fs_get_unusable_blocks(sbi); unusable = f2fs_get_unusable_blocks(sbi);
if (f2fs_disable_cp_again(sbi, unusable)) { if (f2fs_disable_cp_again(sbi, unusable)) {
err = -EAGAIN; err = -EAGAIN;
goto out; goto restore_flag;
} }
down_write(&sbi->gc_lock); down_write(&sbi->gc_lock);
@@ -1917,7 +1958,8 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
out_unlock: out_unlock:
up_write(&sbi->gc_lock); up_write(&sbi->gc_lock);
out: restore_flag:
sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */
return err; return err;
} }
@@ -1949,6 +1991,7 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
bool disable_checkpoint = test_opt(sbi, DISABLE_CHECKPOINT); bool disable_checkpoint = test_opt(sbi, DISABLE_CHECKPOINT);
bool no_io_align = !F2FS_IO_ALIGNED(sbi); bool no_io_align = !F2FS_IO_ALIGNED(sbi);
bool no_atgc = !test_opt(sbi, ATGC); bool no_atgc = !test_opt(sbi, ATGC);
bool no_compress_cache = !test_opt(sbi, COMPRESS_CACHE);
bool checkpoint_changed; bool checkpoint_changed;
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
int i, j; int i, j;
@@ -2004,6 +2047,11 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
if (f2fs_readonly(sb) && (*flags & SB_RDONLY)) if (f2fs_readonly(sb) && (*flags & SB_RDONLY))
goto skip; goto skip;
if (f2fs_sb_has_readonly(sbi) && !(*flags & SB_RDONLY)) {
err = -EROFS;
goto restore_opts;
}
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
if (!f2fs_readonly(sb) && (*flags & SB_RDONLY)) { if (!f2fs_readonly(sb) && (*flags & SB_RDONLY)) {
err = dquot_suspend(sb, -1); err = dquot_suspend(sb, -1);
@@ -2041,6 +2089,12 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
goto restore_opts; goto restore_opts;
} }
if (no_compress_cache == !!test_opt(sbi, COMPRESS_CACHE)) {
err = -EINVAL;
f2fs_warn(sbi, "switch compress_cache option is not allowed");
goto restore_opts;
}
if ((*flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) { if ((*flags & SB_RDONLY) && test_opt(sbi, DISABLE_CHECKPOINT)) {
err = -EINVAL; err = -EINVAL;
f2fs_warn(sbi, "disabling checkpoint not compatible with read-only"); f2fs_warn(sbi, "disabling checkpoint not compatible with read-only");
@@ -3137,14 +3191,15 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); ovp_segments = le32_to_cpu(ckpt->overprov_segment_count);
reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count);
if (unlikely(fsmeta < F2FS_MIN_META_SEGMENTS || if (!f2fs_sb_has_readonly(sbi) &&
unlikely(fsmeta < F2FS_MIN_META_SEGMENTS ||
ovp_segments == 0 || reserved_segments == 0)) { ovp_segments == 0 || reserved_segments == 0)) {
f2fs_err(sbi, "Wrong layout: check mkfs.f2fs version"); f2fs_err(sbi, "Wrong layout: check mkfs.f2fs version");
return 1; return 1;
} }
user_block_count = le64_to_cpu(ckpt->user_block_count); user_block_count = le64_to_cpu(ckpt->user_block_count);
segment_count_main = le32_to_cpu(raw_super->segment_count_main); segment_count_main = le32_to_cpu(raw_super->segment_count_main) +
(f2fs_sb_has_readonly(sbi) ? 1 : 0);
log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg); log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
if (!user_block_count || user_block_count >= if (!user_block_count || user_block_count >=
segment_count_main << log_blocks_per_seg) { segment_count_main << log_blocks_per_seg) {
@@ -3175,6 +3230,10 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
if (le32_to_cpu(ckpt->cur_node_segno[i]) >= main_segs || if (le32_to_cpu(ckpt->cur_node_segno[i]) >= main_segs ||
le16_to_cpu(ckpt->cur_node_blkoff[i]) >= blocks_per_seg) le16_to_cpu(ckpt->cur_node_blkoff[i]) >= blocks_per_seg)
return 1; return 1;
if (f2fs_sb_has_readonly(sbi))
goto check_data;
for (j = i + 1; j < NR_CURSEG_NODE_TYPE; j++) { for (j = i + 1; j < NR_CURSEG_NODE_TYPE; j++) {
if (le32_to_cpu(ckpt->cur_node_segno[i]) == if (le32_to_cpu(ckpt->cur_node_segno[i]) ==
le32_to_cpu(ckpt->cur_node_segno[j])) { le32_to_cpu(ckpt->cur_node_segno[j])) {
@@ -3185,10 +3244,15 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
} }
} }
} }
check_data:
for (i = 0; i < NR_CURSEG_DATA_TYPE; i++) { for (i = 0; i < NR_CURSEG_DATA_TYPE; i++) {
if (le32_to_cpu(ckpt->cur_data_segno[i]) >= main_segs || if (le32_to_cpu(ckpt->cur_data_segno[i]) >= main_segs ||
le16_to_cpu(ckpt->cur_data_blkoff[i]) >= blocks_per_seg) le16_to_cpu(ckpt->cur_data_blkoff[i]) >= blocks_per_seg)
return 1; return 1;
if (f2fs_sb_has_readonly(sbi))
goto skip_cross;
for (j = i + 1; j < NR_CURSEG_DATA_TYPE; j++) { for (j = i + 1; j < NR_CURSEG_DATA_TYPE; j++) {
if (le32_to_cpu(ckpt->cur_data_segno[i]) == if (le32_to_cpu(ckpt->cur_data_segno[i]) ==
le32_to_cpu(ckpt->cur_data_segno[j])) { le32_to_cpu(ckpt->cur_data_segno[j])) {
@@ -3210,7 +3274,7 @@ int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi)
} }
} }
} }
skip_cross:
sit_bitmap_size = le32_to_cpu(ckpt->sit_ver_bitmap_bytesize); sit_bitmap_size = le32_to_cpu(ckpt->sit_ver_bitmap_bytesize);
nat_bitmap_size = le32_to_cpu(ckpt->nat_ver_bitmap_bytesize); nat_bitmap_size = le32_to_cpu(ckpt->nat_ver_bitmap_bytesize);
@@ -3555,7 +3619,7 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi)
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
if (bdev_zoned_model(FDEV(i).bdev) == BLK_ZONED_HM && if (bdev_zoned_model(FDEV(i).bdev) == BLK_ZONED_HM &&
!f2fs_sb_has_blkzoned(sbi)) { !f2fs_sb_has_blkzoned(sbi)) {
f2fs_err(sbi, "Zoned block device feature not enabled\n"); f2fs_err(sbi, "Zoned block device feature not enabled");
return -EINVAL; return -EINVAL;
} }
if (bdev_zoned_model(FDEV(i).bdev) != BLK_ZONED_NONE) { if (bdev_zoned_model(FDEV(i).bdev) != BLK_ZONED_NONE) {
@@ -3940,10 +4004,14 @@ try_onemore:
goto free_node_inode; goto free_node_inode;
} }
err = f2fs_register_sysfs(sbi); err = f2fs_init_compress_inode(sbi);
if (err) if (err)
goto free_root_inode; goto free_root_inode;
err = f2fs_register_sysfs(sbi);
if (err)
goto free_compress_inode;
#ifdef CONFIG_QUOTA #ifdef CONFIG_QUOTA
/* Enable quota usage during mount */ /* Enable quota usage during mount */
if (f2fs_sb_has_quota_ino(sbi) && !f2fs_readonly(sb)) { if (f2fs_sb_has_quota_ino(sbi) && !f2fs_readonly(sb)) {
@@ -4084,6 +4152,8 @@ free_meta:
/* evict some inodes being cached by GC */ /* evict some inodes being cached by GC */
evict_inodes(sb); evict_inodes(sb);
f2fs_unregister_sysfs(sbi); f2fs_unregister_sysfs(sbi);
free_compress_inode:
f2fs_destroy_compress_inode(sbi);
free_root_inode: free_root_inode:
dput(sb->s_root); dput(sb->s_root);
sb->s_root = NULL; sb->s_root = NULL;
@@ -4162,6 +4232,15 @@ static void kill_f2fs_super(struct super_block *sb)
f2fs_stop_gc_thread(sbi); f2fs_stop_gc_thread(sbi);
f2fs_stop_discard_thread(sbi); f2fs_stop_discard_thread(sbi);
#ifdef CONFIG_F2FS_FS_COMPRESSION
/*
* latter evict_inode() can bypass checking and invalidating
* compress inode cache.
*/
if (test_opt(sbi, COMPRESS_CACHE))
truncate_inode_pages_final(COMPRESS_MAPPING(sbi));
#endif
if (is_sbi_flag_set(sbi, SBI_IS_DIRTY) || if (is_sbi_flag_set(sbi, SBI_IS_DIRTY) ||
!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) { !is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
struct cp_control cpc = { struct cp_control cpc = {
@@ -4261,7 +4340,12 @@ static int __init init_f2fs_fs(void)
err = f2fs_init_compress_cache(); err = f2fs_init_compress_cache();
if (err) if (err)
goto free_compress_mempool; goto free_compress_mempool;
err = f2fs_create_casefold_cache();
if (err)
goto free_compress_cache;
return 0; return 0;
free_compress_cache:
f2fs_destroy_compress_cache();
free_compress_mempool: free_compress_mempool:
f2fs_destroy_compress_mempool(); f2fs_destroy_compress_mempool();
free_bioset: free_bioset:
@@ -4297,6 +4381,7 @@ fail:
static void __exit exit_f2fs_fs(void) static void __exit exit_f2fs_fs(void)
{ {
f2fs_destroy_casefold_cache();
f2fs_destroy_compress_cache(); f2fs_destroy_compress_cache();
f2fs_destroy_compress_mempool(); f2fs_destroy_compress_mempool();
f2fs_destroy_bioset(); f2fs_destroy_bioset();

View File

@@ -158,6 +158,9 @@ static ssize_t features_show(struct f2fs_attr *a,
if (f2fs_sb_has_casefold(sbi)) if (f2fs_sb_has_casefold(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "casefold"); len ? ", " : "", "casefold");
if (f2fs_sb_has_readonly(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "readonly");
if (f2fs_sb_has_compression(sbi)) if (f2fs_sb_has_compression(sbi))
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s", len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
len ? ", " : "", "compression"); len ? ", " : "", "compression");
@@ -563,46 +566,49 @@ static void f2fs_sb_release(struct kobject *kobj)
complete(&sbi->s_kobj_unregister); complete(&sbi->s_kobj_unregister);
} }
enum feat_id { /*
FEAT_CRYPTO = 0, * Note that there are three feature list entries:
FEAT_BLKZONED, * 1) /sys/fs/f2fs/features
FEAT_ATOMIC_WRITE, * : shows runtime features supported by in-kernel f2fs along with Kconfig.
FEAT_EXTRA_ATTR, * - ref. F2FS_FEATURE_RO_ATTR()
FEAT_PROJECT_QUOTA, *
FEAT_INODE_CHECKSUM, * 2) /sys/fs/f2fs/$s_id/features <deprecated>
FEAT_FLEXIBLE_INLINE_XATTR, * : shows on-disk features enabled by mkfs.f2fs, used for old kernels. This
FEAT_QUOTA_INO, * won't add new feature anymore, and thus, users should check entries in 3)
FEAT_INODE_CRTIME, * instead of this 2).
FEAT_LOST_FOUND, *
FEAT_VERITY, * 3) /sys/fs/f2fs/$s_id/feature_list
FEAT_SB_CHECKSUM, * : shows on-disk features enabled by mkfs.f2fs per instance, which follows
FEAT_CASEFOLD, * sysfs entry rule where each entry should expose single value.
FEAT_COMPRESSION, * This list covers old feature list provided by 2) and beyond. Therefore,
FEAT_TEST_DUMMY_ENCRYPTION_V2, * please add new on-disk feature in this list only.
}; * - ref. F2FS_SB_FEATURE_RO_ATTR()
*/
static ssize_t f2fs_feature_show(struct f2fs_attr *a, static ssize_t f2fs_feature_show(struct f2fs_attr *a,
struct f2fs_sb_info *sbi, char *buf) struct f2fs_sb_info *sbi, char *buf)
{ {
switch (a->id) { return sprintf(buf, "supported\n");
case FEAT_CRYPTO: }
case FEAT_BLKZONED:
case FEAT_ATOMIC_WRITE: #define F2FS_FEATURE_RO_ATTR(_name) \
case FEAT_EXTRA_ATTR: static struct f2fs_attr f2fs_attr_##_name = { \
case FEAT_PROJECT_QUOTA: .attr = {.name = __stringify(_name), .mode = 0444 }, \
case FEAT_INODE_CHECKSUM: .show = f2fs_feature_show, \
case FEAT_FLEXIBLE_INLINE_XATTR: }
case FEAT_QUOTA_INO:
case FEAT_INODE_CRTIME: static ssize_t f2fs_sb_feature_show(struct f2fs_attr *a,
case FEAT_LOST_FOUND: struct f2fs_sb_info *sbi, char *buf)
case FEAT_VERITY: {
case FEAT_SB_CHECKSUM: if (F2FS_HAS_FEATURE(sbi, a->id))
case FEAT_CASEFOLD:
case FEAT_COMPRESSION:
case FEAT_TEST_DUMMY_ENCRYPTION_V2:
return sprintf(buf, "supported\n"); return sprintf(buf, "supported\n");
} return sprintf(buf, "unsupported\n");
return 0; }
#define F2FS_SB_FEATURE_RO_ATTR(_name, _feat) \
static struct f2fs_attr f2fs_attr_sb_##_name = { \
.attr = {.name = __stringify(_name), .mode = 0444 }, \
.show = f2fs_sb_feature_show, \
.id = F2FS_FEATURE_##_feat, \
} }
#define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \ #define F2FS_ATTR_OFFSET(_struct_type, _name, _mode, _show, _store, _offset) \
@@ -622,13 +628,6 @@ static struct f2fs_attr f2fs_attr_##_name = { \
#define F2FS_GENERAL_RO_ATTR(name) \ #define F2FS_GENERAL_RO_ATTR(name) \
static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL) static struct f2fs_attr f2fs_attr_##name = __ATTR(name, 0444, name##_show, NULL)
#define F2FS_FEATURE_RO_ATTR(_name, _id) \
static struct f2fs_attr f2fs_attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = 0444 }, \
.show = f2fs_feature_show, \
.id = _id, \
}
#define F2FS_STAT_ATTR(_struct_type, _struct_name, _name, _elname) \ #define F2FS_STAT_ATTR(_struct_type, _struct_name, _name, _elname) \
static struct f2fs_attr f2fs_attr_##_name = { \ static struct f2fs_attr f2fs_attr_##_name = { \
.attr = {.name = __stringify(_name), .mode = 0444 }, \ .attr = {.name = __stringify(_name), .mode = 0444 }, \
@@ -702,31 +701,39 @@ F2FS_GENERAL_RO_ATTR(avg_vblocks);
#endif #endif
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO); F2FS_FEATURE_RO_ATTR(encryption);
F2FS_FEATURE_RO_ATTR(test_dummy_encryption_v2, FEAT_TEST_DUMMY_ENCRYPTION_V2); F2FS_FEATURE_RO_ATTR(test_dummy_encryption_v2);
#ifdef CONFIG_UNICODE
F2FS_FEATURE_RO_ATTR(encrypted_casefold);
#endif #endif
#endif /* CONFIG_FS_ENCRYPTION */
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
F2FS_FEATURE_RO_ATTR(block_zoned, FEAT_BLKZONED); F2FS_FEATURE_RO_ATTR(block_zoned);
#endif #endif
F2FS_FEATURE_RO_ATTR(atomic_write, FEAT_ATOMIC_WRITE); F2FS_FEATURE_RO_ATTR(atomic_write);
F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR); F2FS_FEATURE_RO_ATTR(extra_attr);
F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA); F2FS_FEATURE_RO_ATTR(project_quota);
F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM); F2FS_FEATURE_RO_ATTR(inode_checksum);
F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR); F2FS_FEATURE_RO_ATTR(flexible_inline_xattr);
F2FS_FEATURE_RO_ATTR(quota_ino, FEAT_QUOTA_INO); F2FS_FEATURE_RO_ATTR(quota_ino);
F2FS_FEATURE_RO_ATTR(inode_crtime, FEAT_INODE_CRTIME); F2FS_FEATURE_RO_ATTR(inode_crtime);
F2FS_FEATURE_RO_ATTR(lost_found, FEAT_LOST_FOUND); F2FS_FEATURE_RO_ATTR(lost_found);
#ifdef CONFIG_FS_VERITY #ifdef CONFIG_FS_VERITY
F2FS_FEATURE_RO_ATTR(verity, FEAT_VERITY); F2FS_FEATURE_RO_ATTR(verity);
#endif #endif
F2FS_FEATURE_RO_ATTR(sb_checksum, FEAT_SB_CHECKSUM); F2FS_FEATURE_RO_ATTR(sb_checksum);
F2FS_FEATURE_RO_ATTR(casefold, FEAT_CASEFOLD); #ifdef CONFIG_UNICODE
F2FS_FEATURE_RO_ATTR(casefold);
#endif
F2FS_FEATURE_RO_ATTR(readonly);
#ifdef CONFIG_F2FS_FS_COMPRESSION #ifdef CONFIG_F2FS_FS_COMPRESSION
F2FS_FEATURE_RO_ATTR(compression, FEAT_COMPRESSION); F2FS_FEATURE_RO_ATTR(compression);
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, compr_written_block, compr_written_block); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, compr_written_block, compr_written_block);
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, compr_saved_block, compr_saved_block); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, compr_saved_block, compr_saved_block);
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, compr_new_inode, compr_new_inode); F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, compr_new_inode, compr_new_inode);
#endif #endif
F2FS_FEATURE_RO_ATTR(pin_file);
/* For ATGC */ /* For ATGC */
F2FS_RW_ATTR(ATGC_INFO, atgc_management, atgc_candidate_ratio, candidate_ratio); F2FS_RW_ATTR(ATGC_INFO, atgc_management, atgc_candidate_ratio, candidate_ratio);
F2FS_RW_ATTR(ATGC_INFO, atgc_management, atgc_candidate_count, max_candidate_count); F2FS_RW_ATTR(ATGC_INFO, atgc_management, atgc_candidate_count, max_candidate_count);
@@ -813,7 +820,10 @@ static struct attribute *f2fs_feat_attrs[] = {
#ifdef CONFIG_FS_ENCRYPTION #ifdef CONFIG_FS_ENCRYPTION
ATTR_LIST(encryption), ATTR_LIST(encryption),
ATTR_LIST(test_dummy_encryption_v2), ATTR_LIST(test_dummy_encryption_v2),
#ifdef CONFIG_UNICODE
ATTR_LIST(encrypted_casefold),
#endif #endif
#endif /* CONFIG_FS_ENCRYPTION */
#ifdef CONFIG_BLK_DEV_ZONED #ifdef CONFIG_BLK_DEV_ZONED
ATTR_LIST(block_zoned), ATTR_LIST(block_zoned),
#endif #endif
@@ -829,10 +839,14 @@ static struct attribute *f2fs_feat_attrs[] = {
ATTR_LIST(verity), ATTR_LIST(verity),
#endif #endif
ATTR_LIST(sb_checksum), ATTR_LIST(sb_checksum),
#ifdef CONFIG_UNICODE
ATTR_LIST(casefold), ATTR_LIST(casefold),
#endif
ATTR_LIST(readonly),
#ifdef CONFIG_F2FS_FS_COMPRESSION #ifdef CONFIG_F2FS_FS_COMPRESSION
ATTR_LIST(compression), ATTR_LIST(compression),
#endif #endif
ATTR_LIST(pin_file),
NULL, NULL,
}; };
ATTRIBUTE_GROUPS(f2fs_feat); ATTRIBUTE_GROUPS(f2fs_feat);
@@ -844,6 +858,40 @@ static struct attribute *f2fs_stat_attrs[] = {
}; };
ATTRIBUTE_GROUPS(f2fs_stat); ATTRIBUTE_GROUPS(f2fs_stat);
F2FS_SB_FEATURE_RO_ATTR(encryption, ENCRYPT);
F2FS_SB_FEATURE_RO_ATTR(block_zoned, BLKZONED);
F2FS_SB_FEATURE_RO_ATTR(extra_attr, EXTRA_ATTR);
F2FS_SB_FEATURE_RO_ATTR(project_quota, PRJQUOTA);
F2FS_SB_FEATURE_RO_ATTR(inode_checksum, INODE_CHKSUM);
F2FS_SB_FEATURE_RO_ATTR(flexible_inline_xattr, FLEXIBLE_INLINE_XATTR);
F2FS_SB_FEATURE_RO_ATTR(quota_ino, QUOTA_INO);
F2FS_SB_FEATURE_RO_ATTR(inode_crtime, INODE_CRTIME);
F2FS_SB_FEATURE_RO_ATTR(lost_found, LOST_FOUND);
F2FS_SB_FEATURE_RO_ATTR(verity, VERITY);
F2FS_SB_FEATURE_RO_ATTR(sb_checksum, SB_CHKSUM);
F2FS_SB_FEATURE_RO_ATTR(casefold, CASEFOLD);
F2FS_SB_FEATURE_RO_ATTR(compression, COMPRESSION);
F2FS_SB_FEATURE_RO_ATTR(readonly, RO);
static struct attribute *f2fs_sb_feat_attrs[] = {
ATTR_LIST(sb_encryption),
ATTR_LIST(sb_block_zoned),
ATTR_LIST(sb_extra_attr),
ATTR_LIST(sb_project_quota),
ATTR_LIST(sb_inode_checksum),
ATTR_LIST(sb_flexible_inline_xattr),
ATTR_LIST(sb_quota_ino),
ATTR_LIST(sb_inode_crtime),
ATTR_LIST(sb_lost_found),
ATTR_LIST(sb_verity),
ATTR_LIST(sb_sb_checksum),
ATTR_LIST(sb_casefold),
ATTR_LIST(sb_compression),
ATTR_LIST(sb_readonly),
NULL,
};
ATTRIBUTE_GROUPS(f2fs_sb_feat);
static const struct sysfs_ops f2fs_attr_ops = { static const struct sysfs_ops f2fs_attr_ops = {
.show = f2fs_attr_show, .show = f2fs_attr_show,
.store = f2fs_attr_store, .store = f2fs_attr_store,
@@ -910,6 +958,33 @@ static struct kobj_type f2fs_stat_ktype = {
.release = f2fs_stat_kobj_release, .release = f2fs_stat_kobj_release,
}; };
static ssize_t f2fs_sb_feat_attr_show(struct kobject *kobj,
struct attribute *attr, char *buf)
{
struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
s_feature_list_kobj);
struct f2fs_attr *a = container_of(attr, struct f2fs_attr, attr);
return a->show ? a->show(a, sbi, buf) : 0;
}
static void f2fs_feature_list_kobj_release(struct kobject *kobj)
{
struct f2fs_sb_info *sbi = container_of(kobj, struct f2fs_sb_info,
s_feature_list_kobj);
complete(&sbi->s_feature_list_kobj_unregister);
}
static const struct sysfs_ops f2fs_feature_list_attr_ops = {
.show = f2fs_sb_feat_attr_show,
};
static struct kobj_type f2fs_feature_list_ktype = {
.default_groups = f2fs_sb_feat_groups,
.sysfs_ops = &f2fs_feature_list_attr_ops,
.release = f2fs_feature_list_kobj_release,
};
static int __maybe_unused segment_info_seq_show(struct seq_file *seq, static int __maybe_unused segment_info_seq_show(struct seq_file *seq,
void *offset) void *offset)
{ {
@@ -1126,6 +1201,14 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi)
if (err) if (err)
goto put_stat_kobj; goto put_stat_kobj;
sbi->s_feature_list_kobj.kset = &f2fs_kset;
init_completion(&sbi->s_feature_list_kobj_unregister);
err = kobject_init_and_add(&sbi->s_feature_list_kobj,
&f2fs_feature_list_ktype,
&sbi->s_kobj, "feature_list");
if (err)
goto put_feature_list_kobj;
if (f2fs_proc_root) if (f2fs_proc_root)
sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root); sbi->s_proc = proc_mkdir(sb->s_id, f2fs_proc_root);
@@ -1140,6 +1223,9 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi)
victim_bits_seq_show, sb); victim_bits_seq_show, sb);
} }
return 0; return 0;
put_feature_list_kobj:
kobject_put(&sbi->s_feature_list_kobj);
wait_for_completion(&sbi->s_feature_list_kobj_unregister);
put_stat_kobj: put_stat_kobj:
kobject_put(&sbi->s_stat_kobj); kobject_put(&sbi->s_stat_kobj);
wait_for_completion(&sbi->s_stat_kobj_unregister); wait_for_completion(&sbi->s_stat_kobj_unregister);
@@ -1162,6 +1248,9 @@ void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi)
kobject_del(&sbi->s_stat_kobj); kobject_del(&sbi->s_stat_kobj);
kobject_put(&sbi->s_stat_kobj); kobject_put(&sbi->s_stat_kobj);
wait_for_completion(&sbi->s_stat_kobj_unregister); wait_for_completion(&sbi->s_stat_kobj_unregister);
kobject_del(&sbi->s_feature_list_kobj);
kobject_put(&sbi->s_feature_list_kobj);
wait_for_completion(&sbi->s_feature_list_kobj_unregister);
kobject_del(&sbi->s_kobj); kobject_del(&sbi->s_kobj);
kobject_put(&sbi->s_kobj); kobject_put(&sbi->s_kobj);

View File

@@ -34,6 +34,7 @@
#define F2FS_ROOT_INO(sbi) ((sbi)->root_ino_num) #define F2FS_ROOT_INO(sbi) ((sbi)->root_ino_num)
#define F2FS_NODE_INO(sbi) ((sbi)->node_ino_num) #define F2FS_NODE_INO(sbi) ((sbi)->node_ino_num)
#define F2FS_META_INO(sbi) ((sbi)->meta_ino_num) #define F2FS_META_INO(sbi) ((sbi)->meta_ino_num)
#define F2FS_COMPRESS_INO(sbi) (NM_I(sbi)->max_nid)
#define F2FS_MAX_QUOTAS 3 #define F2FS_MAX_QUOTAS 3