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:
commit
8102df91f2
@ -203,7 +203,34 @@ Description: Shows total written kbytes issued to disk.
|
||||
What: /sys/fs/f2fs/<disk>/features
|
||||
Date: July 2017
|
||||
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
|
||||
Date: May 2016
|
||||
|
@ -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
|
||||
compression/decompression on the compression enabled files using
|
||||
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
|
||||
files using the blk-crypto framework rather than
|
||||
filesystem-layer encryption. This allows the use of
|
||||
@ -717,10 +720,10 @@ users.
|
||||
===================== ======================== ===================
|
||||
User F2FS Block
|
||||
===================== ======================== ===================
|
||||
META WRITE_LIFE_NOT_SET
|
||||
HOT_NODE "
|
||||
WARM_NODE "
|
||||
COLD_NODE "
|
||||
N/A META WRITE_LIFE_NOT_SET
|
||||
N/A HOT_NODE "
|
||||
N/A WARM_NODE "
|
||||
N/A COLD_NODE "
|
||||
ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME
|
||||
extension list " "
|
||||
|
||||
@ -746,10 +749,10 @@ WRITE_LIFE_LONG " WRITE_LIFE_LONG
|
||||
===================== ======================== ===================
|
||||
User F2FS Block
|
||||
===================== ======================== ===================
|
||||
META WRITE_LIFE_MEDIUM;
|
||||
HOT_NODE WRITE_LIFE_NOT_SET
|
||||
WARM_NODE "
|
||||
COLD_NODE WRITE_LIFE_NONE
|
||||
N/A META WRITE_LIFE_MEDIUM;
|
||||
N/A HOT_NODE WRITE_LIFE_NOT_SET
|
||||
N/A WARM_NODE "
|
||||
N/A COLD_NODE WRITE_LIFE_NONE
|
||||
ioctl(COLD) COLD_DATA WRITE_LIFE_EXTREME
|
||||
extension list " "
|
||||
|
||||
|
@ -691,6 +691,9 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
||||
}
|
||||
|
||||
#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
|
||||
* filesystem has quota feature, so that they are updated correctly.
|
||||
|
@ -12,9 +12,11 @@
|
||||
#include <linux/lzo.h>
|
||||
#include <linux/lz4.h>
|
||||
#include <linux/zstd.h>
|
||||
#include <linux/pagevec.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
#include "segment.h"
|
||||
#include <trace/events/f2fs.h>
|
||||
|
||||
static struct kmem_cache *cic_entry_slab;
|
||||
@ -736,7 +738,7 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
|
||||
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_inode_info *fi = F2FS_I(dic->inode);
|
||||
@ -835,7 +837,8 @@ static void f2fs_decompress_cluster(struct decompress_io_ctx *dic)
|
||||
* 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).
|
||||
*/
|
||||
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 *)page_private(page);
|
||||
@ -845,6 +848,9 @@ void f2fs_end_read_compressed_page(struct page *page, bool failed)
|
||||
|
||||
if (failed)
|
||||
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))
|
||||
f2fs_decompress_cluster(dic);
|
||||
@ -1660,6 +1666,164 @@ void f2fs_put_page_dic(struct page *page)
|
||||
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)
|
||||
{
|
||||
dev_t dev = sbi->sb->s_bdev->bd_dev;
|
||||
|
287
fs/f2fs/data.c
287
fs/f2fs/data.c
@ -133,7 +133,7 @@ static void f2fs_finish_read_bio(struct bio *bio)
|
||||
|
||||
if (f2fs_is_compressed_page(page)) {
|
||||
if (bio->bi_status)
|
||||
f2fs_end_read_compressed_page(page, true);
|
||||
f2fs_end_read_compressed_page(page, true, 0);
|
||||
f2fs_put_page_dic(page);
|
||||
continue;
|
||||
}
|
||||
@ -229,15 +229,19 @@ static void f2fs_handle_step_decompress(struct bio_post_read_ctx *ctx)
|
||||
struct bio_vec *bv;
|
||||
struct bvec_iter_all iter_all;
|
||||
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) {
|
||||
struct page *page = bv->bv_page;
|
||||
|
||||
/* PG_error was set if decryption failed. */
|
||||
if (f2fs_is_compressed_page(page))
|
||||
f2fs_end_read_compressed_page(page, PageError(page));
|
||||
f2fs_end_read_compressed_page(page, PageError(page),
|
||||
blkaddr);
|
||||
else
|
||||
all_compressed = false;
|
||||
|
||||
blkaddr++;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1368,9 +1372,11 @@ static int __allocate_data_block(struct dnode_of_data *dn, int seg_type)
|
||||
old_blkaddr = dn->data_blkaddr;
|
||||
f2fs_allocate_data_block(sbi, NULL, old_blkaddr, &dn->data_blkaddr,
|
||||
&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),
|
||||
old_blkaddr, old_blkaddr);
|
||||
f2fs_invalidate_compress_page(sbi, old_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;
|
||||
}
|
||||
|
||||
for (i = 0; i < dic->nr_cpages; i++) {
|
||||
for (i = 0; i < cc->nr_cpages; i++) {
|
||||
struct page *page = dic->cpages[i];
|
||||
block_t blkaddr;
|
||||
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,
|
||||
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,
|
||||
*last_block_in_bio, blkaddr) ||
|
||||
!f2fs_crypt_mergeable_bio(bio, inode, page->index, NULL))) {
|
||||
@ -2219,8 +2233,6 @@ int f2fs_read_multi_pages(struct compress_ctx *cc, struct bio **bio_ret,
|
||||
}
|
||||
}
|
||||
|
||||
f2fs_wait_on_block_writeback(inode, blkaddr);
|
||||
|
||||
if (bio_add_page(bio, page, blocksize, 0) < blocksize)
|
||||
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)
|
||||
{
|
||||
/* 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))
|
||||
return true;
|
||||
|
||||
@ -2498,6 +2514,11 @@ bool f2fs_should_update_outplace(struct inode *inode, struct f2fs_io_info *fio)
|
||||
return true;
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
return true;
|
||||
|
||||
/* swap file is migrating in aligned write mode */
|
||||
if (is_inode_flag_set(inode, FI_ALIGNED_WRITE))
|
||||
return true;
|
||||
|
||||
if (fio) {
|
||||
if (page_private_gcing(fio->page))
|
||||
return true;
|
||||
@ -3675,6 +3696,13 @@ void f2fs_invalidate_page(struct page *page, unsigned int offset,
|
||||
|
||||
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))
|
||||
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))
|
||||
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);
|
||||
|
||||
detach_page_private(page);
|
||||
@ -3858,67 +3896,66 @@ int f2fs_migrate_page(struct address_space *mapping,
|
||||
#endif
|
||||
|
||||
#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);
|
||||
block_t main_blkaddr = SM_I(sbi)->main_blkaddr;
|
||||
block_t cur_lblock;
|
||||
block_t last_lblock;
|
||||
block_t pblock;
|
||||
unsigned long nr_pblocks;
|
||||
unsigned int blocks_per_sec = BLKS_PER_SEC(sbi);
|
||||
unsigned int not_aligned = 0;
|
||||
unsigned int blkofs;
|
||||
unsigned int blk_per_sec = BLKS_PER_SEC(sbi);
|
||||
unsigned int secidx = start_blk / blk_per_sec;
|
||||
unsigned int end_sec = secidx + blkcnt / blk_per_sec;
|
||||
int ret = 0;
|
||||
|
||||
cur_lblock = 0;
|
||||
last_lblock = bytes_to_blks(inode, i_size_read(inode));
|
||||
down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
down_write(&F2FS_I(inode)->i_mmap_sem);
|
||||
|
||||
while (cur_lblock < last_lblock) {
|
||||
struct f2fs_map_blocks map;
|
||||
set_inode_flag(inode, FI_ALIGNED_WRITE);
|
||||
|
||||
memset(&map, 0, sizeof(map));
|
||||
map.m_lblk = cur_lblock;
|
||||
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;
|
||||
for (; secidx < end_sec; secidx++) {
|
||||
down_write(&sbi->pin_sem);
|
||||
|
||||
ret = f2fs_map_blocks(inode, &map, 0, F2FS_GET_BLOCK_FIEMAP);
|
||||
if (ret)
|
||||
goto out;
|
||||
f2fs_lock_op(sbi);
|
||||
f2fs_allocate_new_section(sbi, CURSEG_COLD_DATA_PINNED, false);
|
||||
f2fs_unlock_op(sbi);
|
||||
|
||||
/* hole */
|
||||
if (!(map.m_flags & F2FS_MAP_FLAGS)) {
|
||||
f2fs_err(sbi, "Swapfile has holes\n");
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
set_inode_flag(inode, FI_DO_DEFRAG);
|
||||
|
||||
pblock = map.m_pblk;
|
||||
nr_pblocks = map.m_len;
|
||||
for (blkofs = 0; blkofs < blk_per_sec; blkofs++) {
|
||||
struct page *page;
|
||||
unsigned int blkidx = secidx * blk_per_sec + blkofs;
|
||||
|
||||
if ((pblock - main_blkaddr) & (blocks_per_sec - 1) ||
|
||||
nr_pblocks & (blocks_per_sec - 1)) {
|
||||
if (f2fs_is_pinned_file(inode)) {
|
||||
f2fs_err(sbi, "Swapfile does not align to section");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
page = f2fs_get_lock_data_page(inode, blkidx, true);
|
||||
if (IS_ERR(page)) {
|
||||
up_write(&sbi->pin_sem);
|
||||
ret = PTR_ERR(page);
|
||||
goto done;
|
||||
}
|
||||
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"
|
||||
"\t1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate()",
|
||||
not_aligned);
|
||||
out:
|
||||
|
||||
done:
|
||||
clear_inode_flag(inode, FI_DO_DEFRAG);
|
||||
clear_inode_flag(inode, FI_ALIGNED_WRITE);
|
||||
|
||||
up_write(&F2FS_I(inode)->i_mmap_sem);
|
||||
up_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||
|
||||
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 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;
|
||||
int nr_extents = 0;
|
||||
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;
|
||||
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) {
|
||||
struct f2fs_map_blocks map;
|
||||
|
||||
retry:
|
||||
cond_resched();
|
||||
|
||||
memset(&map, 0, sizeof(map));
|
||||
@ -3961,7 +3999,7 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
|
||||
|
||||
/* hole */
|
||||
if (!(map.m_flags & F2FS_MAP_FLAGS)) {
|
||||
f2fs_err(sbi, "Swapfile has holes\n");
|
||||
f2fs_err(sbi, "Swapfile has holes");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -3969,16 +4007,28 @@ static int check_swap_activate_fast(struct swap_info_struct *sis,
|
||||
pblock = map.m_pblk;
|
||||
nr_pblocks = map.m_len;
|
||||
|
||||
if ((pblock - SM_I(sbi)->main_blkaddr) & (blocks_per_sec - 1) ||
|
||||
nr_pblocks & (blocks_per_sec - 1)) {
|
||||
if (f2fs_is_pinned_file(inode)) {
|
||||
f2fs_err(sbi, "Swapfile does not align to section");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if ((pblock - SM_I(sbi)->main_blkaddr) & sec_blks_mask ||
|
||||
nr_pblocks & sec_blks_mask) {
|
||||
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)
|
||||
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->pages = cur_lblock - 1;
|
||||
sis->highest_bit = cur_lblock - 1;
|
||||
|
||||
out:
|
||||
if (not_aligned)
|
||||
f2fs_warn(sbi, "Swapfile (%u) is not align to section: \n"
|
||||
"\t1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate()",
|
||||
not_aligned);
|
||||
out:
|
||||
f2fs_warn(sbi, "Swapfile (%u) is not align to section: 1) creat(), 2) ioctl(F2FS_IOC_SET_PIN_FILE), 3) fallocate(%u * N)",
|
||||
not_aligned, blks_per_sec * F2FS_BLKSIZE);
|
||||
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,
|
||||
sector_t *span)
|
||||
{
|
||||
|
@ -152,6 +152,12 @@ static void update_general_status(struct f2fs_sb_info *sbi)
|
||||
si->node_pages = NODE_MAPPING(sbi)->nrpages;
|
||||
if (sbi->meta_inode)
|
||||
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->dirty_nats = NM_I(sbi)->nat_cnt[DIRTY_NAT];
|
||||
si->sits = MAIN_SEGS(sbi);
|
||||
@ -309,6 +315,12 @@ static void update_mem_info(struct f2fs_sb_info *sbi)
|
||||
|
||||
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)
|
||||
@ -476,6 +488,7 @@ static int stat_show(struct seq_file *s, void *v)
|
||||
"volatile IO: %4d (Max. %4d)\n",
|
||||
si->inmem_pages, si->aw_cnt, si->max_aw_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",
|
||||
si->ndirty_node, si->node_pages);
|
||||
seq_printf(s, " - dents: %4d in dirs:%4d (%4d)\n",
|
||||
|
@ -16,6 +16,10 @@
|
||||
#include "xattr.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)
|
||||
{
|
||||
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
|
||||
struct super_block *sb = dir->i_sb;
|
||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||
|
||||
if (IS_CASEFOLDED(dir)) {
|
||||
fname->cf_name.name = f2fs_kmalloc(sbi, F2FS_NAME_LEN,
|
||||
GFP_NOFS);
|
||||
fname->cf_name.name = kmem_cache_alloc(f2fs_cf_name_slab,
|
||||
GFP_NOFS);
|
||||
if (!fname->cf_name.name)
|
||||
return -ENOMEM;
|
||||
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,
|
||||
F2FS_NAME_LEN);
|
||||
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;
|
||||
if (sb_has_strict_encoding(sb))
|
||||
return -EINVAL;
|
||||
@ -172,8 +175,10 @@ void f2fs_free_filename(struct f2fs_filename *fname)
|
||||
fname->crypto_buf.name = NULL;
|
||||
#endif
|
||||
#ifdef CONFIG_UNICODE
|
||||
kfree(fname->cf_name.name);
|
||||
fname->cf_name.name = NULL;
|
||||
if (fname->cf_name.name) {
|
||||
kmem_cache_free(f2fs_cf_name_slab, fname->cf_name.name);
|
||||
fname->cf_name.name = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
117
fs/f2fs/f2fs.h
117
fs/f2fs/f2fs.h
@ -98,6 +98,7 @@ extern const char *f2fs_fault_name[FAULT_MAX];
|
||||
#define F2FS_MOUNT_ATGC 0x08000000
|
||||
#define F2FS_MOUNT_MERGE_CHECKPOINT 0x10000000
|
||||
#define F2FS_MOUNT_GC_MERGE 0x20000000
|
||||
#define F2FS_MOUNT_COMPRESS_CACHE 0x40000000
|
||||
|
||||
#define F2FS_OPTION(sbi) ((sbi)->mount_opt)
|
||||
#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_CASEFOLD 0x1000
|
||||
#define F2FS_FEATURE_COMPRESSION 0x2000
|
||||
#define F2FS_FEATURE_RO 0x4000
|
||||
|
||||
#define __F2FS_HAS_FEATURE(raw_super, mask) \
|
||||
((raw_super->feature & cpu_to_le32(mask)) != 0)
|
||||
@ -707,6 +709,7 @@ enum {
|
||||
FI_MMAP_FILE, /* indicate file was mmapped */
|
||||
FI_ENABLE_COMPRESS, /* enable compression in "user" compression mode */
|
||||
FI_COMPRESS_RELEASED, /* compressed blocks were released */
|
||||
FI_ALIGNED_WRITE, /* enable aligned write */
|
||||
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_NODE_TYPE (3)
|
||||
#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_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(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 */
|
||||
enum compress_algorithm_type {
|
||||
COMPRESS_LZO,
|
||||
@ -1386,6 +1421,9 @@ enum compress_flag {
|
||||
COMPRESS_MAX_FLAG,
|
||||
};
|
||||
|
||||
#define COMPRESS_WATERMARK 20
|
||||
#define COMPRESS_PERCENT 20
|
||||
|
||||
#define COMPRESS_DATA_RESERVED_SIZE 4
|
||||
struct compress_data {
|
||||
__le32 clen; /* compressed data size */
|
||||
@ -1663,6 +1701,9 @@ struct f2fs_sb_info {
|
||||
struct kobject s_stat_kobj; /* /sys/fs/f2fs/<devname>/stat */
|
||||
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 */
|
||||
struct list_head s_list;
|
||||
int s_ndevs; /* number of devices */
|
||||
@ -1695,6 +1736,12 @@ struct f2fs_sb_info {
|
||||
u64 compr_written_block;
|
||||
u64 compr_saved_block;
|
||||
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
|
||||
};
|
||||
|
||||
@ -3101,25 +3148,6 @@ static inline bool is_dot_dotdot(const u8 *name, size_t len)
|
||||
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,
|
||||
size_t size, gfp_t flags)
|
||||
{
|
||||
@ -3662,7 +3690,8 @@ struct f2fs_stat_info {
|
||||
unsigned int bimodal, avg_vblocks;
|
||||
int util_free, util_valid, util_invalid;
|
||||
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 tot_segs, node_segs, data_segs, free_segs, free_secs;
|
||||
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);
|
||||
int f2fs_init_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_can_merge_page(struct compress_ctx *cc, pgoff_t index);
|
||||
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);
|
||||
void f2fs_destroy_compress_ctx(struct compress_ctx *cc, bool reuse);
|
||||
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);
|
||||
void f2fs_destroy_page_array_cache(struct f2fs_sb_info *sbi);
|
||||
int __init f2fs_init_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) \
|
||||
do { \
|
||||
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 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);
|
||||
}
|
||||
@ -4056,10 +4098,20 @@ static inline void f2fs_put_page_dic(struct page *page)
|
||||
{
|
||||
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 void f2fs_destroy_page_array_cache(struct f2fs_sb_info *sbi) { }
|
||||
static inline int __init f2fs_init_compress_cache(void) { return 0; }
|
||||
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)
|
||||
#endif
|
||||
|
||||
@ -4124,6 +4176,27 @@ F2FS_FEATURE_FUNCS(verity, VERITY);
|
||||
F2FS_FEATURE_FUNCS(sb_chksum, SB_CHKSUM);
|
||||
F2FS_FEATURE_FUNCS(casefold, CASEFOLD);
|
||||
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
|
||||
static inline bool f2fs_blkz_is_seq(struct f2fs_sb_info *sbi, int devi,
|
||||
|
@ -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))) {
|
||||
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);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@ -4144,9 +4144,8 @@ static int f2fs_ioc_decompress_file(struct file *filp, unsigned long arg)
|
||||
LLONG_MAX);
|
||||
|
||||
if (ret)
|
||||
f2fs_warn(sbi, "%s: The file might be partially decompressed "
|
||||
"(errno=%d). Please delete the file.\n",
|
||||
__func__, ret);
|
||||
f2fs_warn(sbi, "%s: The file might be partially decompressed (errno=%d). Please delete the file.",
|
||||
__func__, ret);
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
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);
|
||||
|
||||
if (ret)
|
||||
f2fs_warn(sbi, "%s: The file might be partially compressed "
|
||||
"(errno=%d). Please delete the file.\n",
|
||||
__func__, ret);
|
||||
f2fs_warn(sbi, "%s: The file might be partially compressed (errno=%d). Please delete the file.",
|
||||
__func__, ret);
|
||||
out:
|
||||
inode_unlock(inode);
|
||||
file_end_write(filp);
|
||||
|
@ -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 (!test_and_set_bit(segno, SIT_I(sbi)->invalid_segmap)) {
|
||||
f2fs_err(sbi, "mismatched blkaddr %u (source_blkaddr %u) in seg %u\n",
|
||||
blkaddr, source_blkaddr, segno);
|
||||
f2fs_err(sbi, "mismatched blkaddr %u (source_blkaddr %u) in seg %u",
|
||||
blkaddr, source_blkaddr, segno);
|
||||
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);
|
||||
invalidate_mapping_pages(META_MAPPING(fio.sbi),
|
||||
fio.old_blkaddr, fio.old_blkaddr);
|
||||
f2fs_invalidate_compress_page(fio.sbi, fio.old_blkaddr);
|
||||
|
||||
set_page_dirty(fio.encrypted_page);
|
||||
if (clear_page_dirty_for_io(fio.encrypted_page))
|
||||
@ -1450,10 +1451,8 @@ static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
|
||||
if (phase == 3) {
|
||||
inode = f2fs_iget(sb, dni.ino);
|
||||
if (IS_ERR(inode) || is_bad_inode(inode)) {
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
if (IS_ERR(inode) || is_bad_inode(inode))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!down_write_trylock(
|
||||
&F2FS_I(inode)->i_gc_rwsem[WRITE])) {
|
||||
|
@ -18,6 +18,10 @@
|
||||
|
||||
#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)
|
||||
{
|
||||
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))
|
||||
goto make_now;
|
||||
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
if (ino == F2FS_COMPRESS_INO(sbi))
|
||||
goto make_now;
|
||||
#endif
|
||||
|
||||
ret = do_read_inode(inode);
|
||||
if (ret)
|
||||
goto bad_inode;
|
||||
@ -504,6 +513,12 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
|
||||
} else if (ino == F2FS_META_INO(sbi)) {
|
||||
inode->i_mapping->a_ops = &f2fs_meta_aops;
|
||||
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)) {
|
||||
inode->i_op = &f2fs_file_inode_operations;
|
||||
inode->i_fop = &f2fs_file_operations;
|
||||
@ -723,8 +738,12 @@ void f2fs_evict_inode(struct inode *inode)
|
||||
trace_f2fs_evict_inode(inode);
|
||||
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) ||
|
||||
inode->i_ino == F2FS_META_INO(sbi))
|
||||
inode->i_ino == F2FS_META_INO(sbi) ||
|
||||
inode->i_ino == F2FS_COMPRESS_INO(sbi))
|
||||
goto out_clear;
|
||||
|
||||
f2fs_bug_on(sbi, get_dirty_pages(inode));
|
||||
|
@ -97,6 +97,20 @@ bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type)
|
||||
mem_size = (atomic_read(&dcc->discard_cmd_cnt) *
|
||||
sizeof(struct discard_cmd)) >> PAGE_SHIFT;
|
||||
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 {
|
||||
if (!sbi->sb->s_bdi->wb.dirty_exceeded)
|
||||
return true;
|
||||
|
@ -38,6 +38,9 @@
|
||||
/* return value for read_node_page */
|
||||
#define LOCKED_PAGE 1
|
||||
|
||||
/* check pinned file's alignment status of physical blocks */
|
||||
#define FILE_NOT_ALIGNED 1
|
||||
|
||||
/* For flag in struct node_info */
|
||||
enum {
|
||||
IS_CHECKPOINTED, /* is it checkpointed before? */
|
||||
@ -148,6 +151,7 @@ enum mem_type {
|
||||
EXTENT_CACHE, /* indicates extent cache */
|
||||
INMEM_PAGES, /* indicates inmemory pages */
|
||||
DISCARD_CACHE, /* indicates memory of cached discard cmds */
|
||||
COMPRESS_PAGE, /* indicates memory of cached compressed pages */
|
||||
BASE_CHECK, /* check kernel status */
|
||||
};
|
||||
|
||||
|
@ -45,6 +45,10 @@
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
#ifdef CONFIG_UNICODE
|
||||
/* 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;
|
||||
#endif
|
||||
} else {
|
||||
@ -782,6 +786,8 @@ int f2fs_recover_fsync_data(struct f2fs_sb_info *sbi, bool check_only)
|
||||
}
|
||||
|
||||
#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 */
|
||||
quota_enabled = f2fs_enable_quota_files(sbi, s_flags & SB_RDONLY);
|
||||
#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);
|
||||
if (!err)
|
||||
f2fs_bug_on(sbi, !list_empty(&inode_list));
|
||||
else
|
||||
f2fs_bug_on(sbi, sbi->sb->s_flags & SB_ACTIVE);
|
||||
else {
|
||||
/* restore s_flags to let iput() trash data */
|
||||
sbi->sb->s_flags = s_flags;
|
||||
}
|
||||
skip:
|
||||
fix_curseg_write_pointer = !check_only || list_empty(&inode_list);
|
||||
|
||||
|
@ -2322,6 +2322,7 @@ void f2fs_invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr)
|
||||
return;
|
||||
|
||||
invalidate_mapping_pages(META_MAPPING(sbi), addr, addr);
|
||||
f2fs_invalidate_compress_page(sbi, addr);
|
||||
|
||||
/* add it into sit main buffer */
|
||||
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) {
|
||||
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 (fio->sbi->am.atgc_enabled &&
|
||||
(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:
|
||||
f2fs_allocate_data_block(fio->sbi, fio->page, fio->old_blkaddr,
|
||||
&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),
|
||||
fio->old_blkaddr, fio->old_blkaddr);
|
||||
f2fs_invalidate_compress_page(fio->sbi, fio->old_blkaddr);
|
||||
}
|
||||
|
||||
/* writeout dirty page into bdev */
|
||||
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) {
|
||||
invalidate_mapping_pages(META_MAPPING(sbi),
|
||||
old_blkaddr, old_blkaddr);
|
||||
f2fs_invalidate_compress_page(sbi, old_blkaddr);
|
||||
if (!from_gc)
|
||||
update_segment_mtime(sbi, old_blkaddr, 0);
|
||||
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 */
|
||||
if (nats_in_cursum(nat_j) > NAT_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));
|
||||
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);
|
||||
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);
|
||||
|
||||
if (f2fs_test_bit(blkofs, se->cur_valid_map))
|
||||
|
111
fs/f2fs/super.c
111
fs/f2fs/super.c
@ -150,6 +150,7 @@ enum {
|
||||
Opt_compress_extension,
|
||||
Opt_compress_chksum,
|
||||
Opt_compress_mode,
|
||||
Opt_compress_cache,
|
||||
Opt_atgc,
|
||||
Opt_gc_merge,
|
||||
Opt_nogc_merge,
|
||||
@ -224,6 +225,7 @@ static match_table_t f2fs_tokens = {
|
||||
{Opt_compress_extension, "compress_extension=%s"},
|
||||
{Opt_compress_chksum, "compress_chksum"},
|
||||
{Opt_compress_mode, "compress_mode=%s"},
|
||||
{Opt_compress_cache, "compress_cache"},
|
||||
{Opt_atgc, "atgc"},
|
||||
{Opt_gc_merge, "gc_merge"},
|
||||
{Opt_nogc_merge, "nogc_merge"},
|
||||
@ -275,6 +277,24 @@ static int f2fs_sb_read_encoding(const struct f2fs_super_block *sb,
|
||||
|
||||
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
|
||||
|
||||
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;
|
||||
|
||||
if (!options)
|
||||
return 0;
|
||||
goto default_check;
|
||||
|
||||
while ((p = strsep(&options, ",")) != NULL) {
|
||||
int token;
|
||||
@ -1066,12 +1086,16 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
||||
}
|
||||
kfree(name);
|
||||
break;
|
||||
case Opt_compress_cache:
|
||||
set_opt(sbi, COMPRESS_CACHE);
|
||||
break;
|
||||
#else
|
||||
case Opt_compress_algorithm:
|
||||
case Opt_compress_log_size:
|
||||
case Opt_compress_extension:
|
||||
case Opt_compress_chksum:
|
||||
case Opt_compress_mode:
|
||||
case Opt_compress_cache:
|
||||
f2fs_info(sbi, "compression options not supported");
|
||||
break;
|
||||
#endif
|
||||
@ -1090,6 +1114,7 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
default_check:
|
||||
#ifdef CONFIG_QUOTA
|
||||
if (f2fs_check_quota_options(sbi))
|
||||
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)) {
|
||||
f2fs_err(sbi, "LFS not compatible with checkpoint=disable\n");
|
||||
f2fs_err(sbi, "LFS not compatible with checkpoint=disable");
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1406,6 +1436,8 @@ static void f2fs_put_super(struct super_block *sb)
|
||||
|
||||
f2fs_bug_on(sbi, sbi->fsync_node_num);
|
||||
|
||||
f2fs_destroy_compress_inode(sbi);
|
||||
|
||||
iput(sbi->node_inode);
|
||||
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");
|
||||
else if (F2FS_OPTION(sbi).compress_mode == COMPR_MODE_USER)
|
||||
seq_printf(seq, ",compress_mode=%s", "user");
|
||||
|
||||
if (test_opt(sbi, COMPRESS_CACHE))
|
||||
seq_puts(seq, ",compress_cache");
|
||||
}
|
||||
#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)
|
||||
{
|
||||
/* 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).whint_mode = WHINT_MODE_OFF;
|
||||
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)
|
||||
{
|
||||
unsigned int s_flags = sbi->sb->s_flags;
|
||||
struct cp_control cpc;
|
||||
int err = 0;
|
||||
int ret;
|
||||
block_t unusable;
|
||||
|
||||
if (sbi->sb->s_flags & SB_RDONLY) {
|
||||
if (s_flags & SB_RDONLY) {
|
||||
f2fs_err(sbi, "checkpoint=disable on readonly fs");
|
||||
return -EINVAL;
|
||||
}
|
||||
sbi->sb->s_flags |= SB_ACTIVE;
|
||||
|
||||
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);
|
||||
if (ret || err) {
|
||||
err = ret ? ret : err;
|
||||
goto out;
|
||||
goto restore_flag;
|
||||
}
|
||||
|
||||
unusable = f2fs_get_unusable_blocks(sbi);
|
||||
if (f2fs_disable_cp_again(sbi, unusable)) {
|
||||
err = -EAGAIN;
|
||||
goto out;
|
||||
goto restore_flag;
|
||||
}
|
||||
|
||||
down_write(&sbi->gc_lock);
|
||||
@ -1917,7 +1958,8 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
|
||||
|
||||
out_unlock:
|
||||
up_write(&sbi->gc_lock);
|
||||
out:
|
||||
restore_flag:
|
||||
sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */
|
||||
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 no_io_align = !F2FS_IO_ALIGNED(sbi);
|
||||
bool no_atgc = !test_opt(sbi, ATGC);
|
||||
bool no_compress_cache = !test_opt(sbi, COMPRESS_CACHE);
|
||||
bool checkpoint_changed;
|
||||
#ifdef CONFIG_QUOTA
|
||||
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))
|
||||
goto skip;
|
||||
|
||||
if (f2fs_sb_has_readonly(sbi) && !(*flags & SB_RDONLY)) {
|
||||
err = -EROFS;
|
||||
goto restore_opts;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
if (!f2fs_readonly(sb) && (*flags & SB_RDONLY)) {
|
||||
err = dquot_suspend(sb, -1);
|
||||
@ -2041,6 +2089,12 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
||||
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)) {
|
||||
err = -EINVAL;
|
||||
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);
|
||||
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)) {
|
||||
f2fs_err(sbi, "Wrong layout: check mkfs.f2fs version");
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
if (!user_block_count || user_block_count >=
|
||||
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 ||
|
||||
le16_to_cpu(ckpt->cur_node_blkoff[i]) >= blocks_per_seg)
|
||||
return 1;
|
||||
|
||||
if (f2fs_sb_has_readonly(sbi))
|
||||
goto check_data;
|
||||
|
||||
for (j = i + 1; j < NR_CURSEG_NODE_TYPE; j++) {
|
||||
if (le32_to_cpu(ckpt->cur_node_segno[i]) ==
|
||||
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++) {
|
||||
if (le32_to_cpu(ckpt->cur_data_segno[i]) >= main_segs ||
|
||||
le16_to_cpu(ckpt->cur_data_blkoff[i]) >= blocks_per_seg)
|
||||
return 1;
|
||||
|
||||
if (f2fs_sb_has_readonly(sbi))
|
||||
goto skip_cross;
|
||||
|
||||
for (j = i + 1; j < NR_CURSEG_DATA_TYPE; j++) {
|
||||
if (le32_to_cpu(ckpt->cur_data_segno[i]) ==
|
||||
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);
|
||||
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
|
||||
if (bdev_zoned_model(FDEV(i).bdev) == BLK_ZONED_HM &&
|
||||
!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;
|
||||
}
|
||||
if (bdev_zoned_model(FDEV(i).bdev) != BLK_ZONED_NONE) {
|
||||
@ -3940,10 +4004,14 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
goto free_node_inode;
|
||||
}
|
||||
|
||||
err = f2fs_register_sysfs(sbi);
|
||||
err = f2fs_init_compress_inode(sbi);
|
||||
if (err)
|
||||
goto free_root_inode;
|
||||
|
||||
err = f2fs_register_sysfs(sbi);
|
||||
if (err)
|
||||
goto free_compress_inode;
|
||||
|
||||
#ifdef CONFIG_QUOTA
|
||||
/* Enable quota usage during mount */
|
||||
if (f2fs_sb_has_quota_ino(sbi) && !f2fs_readonly(sb)) {
|
||||
@ -4084,6 +4152,8 @@ static int f2fs_fill_super(struct super_block *sb, void *data, int silent)
|
||||
/* evict some inodes being cached by GC */
|
||||
evict_inodes(sb);
|
||||
f2fs_unregister_sysfs(sbi);
|
||||
free_compress_inode:
|
||||
f2fs_destroy_compress_inode(sbi);
|
||||
free_root_inode:
|
||||
dput(sb->s_root);
|
||||
sb->s_root = NULL;
|
||||
@ -4162,6 +4232,15 @@ static void kill_f2fs_super(struct super_block *sb)
|
||||
f2fs_stop_gc_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) ||
|
||||
!is_set_ckpt_flags(sbi, CP_UMOUNT_FLAG)) {
|
||||
struct cp_control cpc = {
|
||||
@ -4261,7 +4340,12 @@ static int __init init_f2fs_fs(void)
|
||||
err = f2fs_init_compress_cache();
|
||||
if (err)
|
||||
goto free_compress_mempool;
|
||||
err = f2fs_create_casefold_cache();
|
||||
if (err)
|
||||
goto free_compress_cache;
|
||||
return 0;
|
||||
free_compress_cache:
|
||||
f2fs_destroy_compress_cache();
|
||||
free_compress_mempool:
|
||||
f2fs_destroy_compress_mempool();
|
||||
free_bioset:
|
||||
@ -4297,6 +4381,7 @@ static int __init init_f2fs_fs(void)
|
||||
|
||||
static void __exit exit_f2fs_fs(void)
|
||||
{
|
||||
f2fs_destroy_casefold_cache();
|
||||
f2fs_destroy_compress_cache();
|
||||
f2fs_destroy_compress_mempool();
|
||||
f2fs_destroy_bioset();
|
||||
|
205
fs/f2fs/sysfs.c
205
fs/f2fs/sysfs.c
@ -158,6 +158,9 @@ static ssize_t features_show(struct f2fs_attr *a,
|
||||
if (f2fs_sb_has_casefold(sbi))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
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))
|
||||
len += scnprintf(buf + len, PAGE_SIZE - len, "%s%s",
|
||||
len ? ", " : "", "compression");
|
||||
@ -563,46 +566,49 @@ static void f2fs_sb_release(struct kobject *kobj)
|
||||
complete(&sbi->s_kobj_unregister);
|
||||
}
|
||||
|
||||
enum feat_id {
|
||||
FEAT_CRYPTO = 0,
|
||||
FEAT_BLKZONED,
|
||||
FEAT_ATOMIC_WRITE,
|
||||
FEAT_EXTRA_ATTR,
|
||||
FEAT_PROJECT_QUOTA,
|
||||
FEAT_INODE_CHECKSUM,
|
||||
FEAT_FLEXIBLE_INLINE_XATTR,
|
||||
FEAT_QUOTA_INO,
|
||||
FEAT_INODE_CRTIME,
|
||||
FEAT_LOST_FOUND,
|
||||
FEAT_VERITY,
|
||||
FEAT_SB_CHECKSUM,
|
||||
FEAT_CASEFOLD,
|
||||
FEAT_COMPRESSION,
|
||||
FEAT_TEST_DUMMY_ENCRYPTION_V2,
|
||||
};
|
||||
|
||||
/*
|
||||
* Note that there are three feature list entries:
|
||||
* 1) /sys/fs/f2fs/features
|
||||
* : shows runtime features supported by in-kernel f2fs along with Kconfig.
|
||||
* - ref. F2FS_FEATURE_RO_ATTR()
|
||||
*
|
||||
* 2) /sys/fs/f2fs/$s_id/features <deprecated>
|
||||
* : shows on-disk features enabled by mkfs.f2fs, used for old kernels. This
|
||||
* won't add new feature anymore, and thus, users should check entries in 3)
|
||||
* instead of this 2).
|
||||
*
|
||||
* 3) /sys/fs/f2fs/$s_id/feature_list
|
||||
* : shows on-disk features enabled by mkfs.f2fs per instance, which follows
|
||||
* sysfs entry rule where each entry should expose single value.
|
||||
* This list covers old feature list provided by 2) and beyond. Therefore,
|
||||
* 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,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
switch (a->id) {
|
||||
case FEAT_CRYPTO:
|
||||
case FEAT_BLKZONED:
|
||||
case FEAT_ATOMIC_WRITE:
|
||||
case FEAT_EXTRA_ATTR:
|
||||
case FEAT_PROJECT_QUOTA:
|
||||
case FEAT_INODE_CHECKSUM:
|
||||
case FEAT_FLEXIBLE_INLINE_XATTR:
|
||||
case FEAT_QUOTA_INO:
|
||||
case FEAT_INODE_CRTIME:
|
||||
case FEAT_LOST_FOUND:
|
||||
case FEAT_VERITY:
|
||||
case FEAT_SB_CHECKSUM:
|
||||
case FEAT_CASEFOLD:
|
||||
case FEAT_COMPRESSION:
|
||||
case FEAT_TEST_DUMMY_ENCRYPTION_V2:
|
||||
return sprintf(buf, "supported\n");
|
||||
}
|
||||
|
||||
#define F2FS_FEATURE_RO_ATTR(_name) \
|
||||
static struct f2fs_attr f2fs_attr_##_name = { \
|
||||
.attr = {.name = __stringify(_name), .mode = 0444 }, \
|
||||
.show = f2fs_feature_show, \
|
||||
}
|
||||
|
||||
static ssize_t f2fs_sb_feature_show(struct f2fs_attr *a,
|
||||
struct f2fs_sb_info *sbi, char *buf)
|
||||
{
|
||||
if (F2FS_HAS_FEATURE(sbi, a->id))
|
||||
return sprintf(buf, "supported\n");
|
||||
}
|
||||
return 0;
|
||||
return sprintf(buf, "unsupported\n");
|
||||
}
|
||||
|
||||
#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) \
|
||||
@ -622,13 +628,6 @@ static struct f2fs_attr f2fs_attr_##_name = { \
|
||||
#define F2FS_GENERAL_RO_ATTR(name) \
|
||||
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) \
|
||||
static struct f2fs_attr f2fs_attr_##_name = { \
|
||||
.attr = {.name = __stringify(_name), .mode = 0444 }, \
|
||||
@ -702,31 +701,39 @@ F2FS_GENERAL_RO_ATTR(avg_vblocks);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
F2FS_FEATURE_RO_ATTR(encryption, FEAT_CRYPTO);
|
||||
F2FS_FEATURE_RO_ATTR(test_dummy_encryption_v2, FEAT_TEST_DUMMY_ENCRYPTION_V2);
|
||||
F2FS_FEATURE_RO_ATTR(encryption);
|
||||
F2FS_FEATURE_RO_ATTR(test_dummy_encryption_v2);
|
||||
#ifdef CONFIG_UNICODE
|
||||
F2FS_FEATURE_RO_ATTR(encrypted_casefold);
|
||||
#endif
|
||||
#endif /* CONFIG_FS_ENCRYPTION */
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
F2FS_FEATURE_RO_ATTR(block_zoned, FEAT_BLKZONED);
|
||||
F2FS_FEATURE_RO_ATTR(block_zoned);
|
||||
#endif
|
||||
F2FS_FEATURE_RO_ATTR(atomic_write, FEAT_ATOMIC_WRITE);
|
||||
F2FS_FEATURE_RO_ATTR(extra_attr, FEAT_EXTRA_ATTR);
|
||||
F2FS_FEATURE_RO_ATTR(project_quota, FEAT_PROJECT_QUOTA);
|
||||
F2FS_FEATURE_RO_ATTR(inode_checksum, FEAT_INODE_CHECKSUM);
|
||||
F2FS_FEATURE_RO_ATTR(flexible_inline_xattr, FEAT_FLEXIBLE_INLINE_XATTR);
|
||||
F2FS_FEATURE_RO_ATTR(quota_ino, FEAT_QUOTA_INO);
|
||||
F2FS_FEATURE_RO_ATTR(inode_crtime, FEAT_INODE_CRTIME);
|
||||
F2FS_FEATURE_RO_ATTR(lost_found, FEAT_LOST_FOUND);
|
||||
F2FS_FEATURE_RO_ATTR(atomic_write);
|
||||
F2FS_FEATURE_RO_ATTR(extra_attr);
|
||||
F2FS_FEATURE_RO_ATTR(project_quota);
|
||||
F2FS_FEATURE_RO_ATTR(inode_checksum);
|
||||
F2FS_FEATURE_RO_ATTR(flexible_inline_xattr);
|
||||
F2FS_FEATURE_RO_ATTR(quota_ino);
|
||||
F2FS_FEATURE_RO_ATTR(inode_crtime);
|
||||
F2FS_FEATURE_RO_ATTR(lost_found);
|
||||
#ifdef CONFIG_FS_VERITY
|
||||
F2FS_FEATURE_RO_ATTR(verity, FEAT_VERITY);
|
||||
F2FS_FEATURE_RO_ATTR(verity);
|
||||
#endif
|
||||
F2FS_FEATURE_RO_ATTR(sb_checksum, FEAT_SB_CHECKSUM);
|
||||
F2FS_FEATURE_RO_ATTR(casefold, FEAT_CASEFOLD);
|
||||
F2FS_FEATURE_RO_ATTR(sb_checksum);
|
||||
#ifdef CONFIG_UNICODE
|
||||
F2FS_FEATURE_RO_ATTR(casefold);
|
||||
#endif
|
||||
F2FS_FEATURE_RO_ATTR(readonly);
|
||||
#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_saved_block, compr_saved_block);
|
||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, compr_new_inode, compr_new_inode);
|
||||
#endif
|
||||
F2FS_FEATURE_RO_ATTR(pin_file);
|
||||
|
||||
/* For ATGC */
|
||||
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);
|
||||
@ -813,7 +820,10 @@ static struct attribute *f2fs_feat_attrs[] = {
|
||||
#ifdef CONFIG_FS_ENCRYPTION
|
||||
ATTR_LIST(encryption),
|
||||
ATTR_LIST(test_dummy_encryption_v2),
|
||||
#ifdef CONFIG_UNICODE
|
||||
ATTR_LIST(encrypted_casefold),
|
||||
#endif
|
||||
#endif /* CONFIG_FS_ENCRYPTION */
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
ATTR_LIST(block_zoned),
|
||||
#endif
|
||||
@ -829,10 +839,14 @@ static struct attribute *f2fs_feat_attrs[] = {
|
||||
ATTR_LIST(verity),
|
||||
#endif
|
||||
ATTR_LIST(sb_checksum),
|
||||
#ifdef CONFIG_UNICODE
|
||||
ATTR_LIST(casefold),
|
||||
#endif
|
||||
ATTR_LIST(readonly),
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
ATTR_LIST(compression),
|
||||
#endif
|
||||
ATTR_LIST(pin_file),
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(f2fs_feat);
|
||||
@ -844,6 +858,40 @@ static struct attribute *f2fs_stat_attrs[] = {
|
||||
};
|
||||
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 = {
|
||||
.show = f2fs_attr_show,
|
||||
.store = f2fs_attr_store,
|
||||
@ -910,6 +958,33 @@ static struct kobj_type f2fs_stat_ktype = {
|
||||
.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,
|
||||
void *offset)
|
||||
{
|
||||
@ -1126,6 +1201,14 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi)
|
||||
if (err)
|
||||
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)
|
||||
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);
|
||||
}
|
||||
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:
|
||||
kobject_put(&sbi->s_stat_kobj);
|
||||
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_put(&sbi->s_stat_kobj);
|
||||
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_put(&sbi->s_kobj);
|
||||
|
@ -34,6 +34,7 @@
|
||||
#define F2FS_ROOT_INO(sbi) ((sbi)->root_ino_num)
|
||||
#define F2FS_NODE_INO(sbi) ((sbi)->node_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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user