f2fs update for 6.5-rc1
In this cycle, we've mainly investigated the zoned block device support along with patches such as correcting write pointers between f2fs and storage, adding asynchronous zone reset flow, and managing the number of open zones. Other than them, f2fs adds another mount option, "errors=x" to specify how to handle when it detects an unexpected behavior at runtime. Enhancement: - support errors=remount-ro|continue|panic mountoption - enforce some inode flag policies - allow .tmp compression given extensions - add some ioctls to manage the f2fs compression - improve looped node chain flow - avoid issuing small-sized discard commands during checkpoint - implement an asynchronous zone reset Bug fix: - fix deadlock in xattr and inode page lock - fix and add sanity check in some error paths - fix to avoid NULL pointer dereference f2fs_write_end_io() along with put_super - set proper flags to quota files - fix potential deadlock due to unpaired node_write lock use - fix over-estimating free section during FG GC - fix the wrong condition to determine atomic context As usual, also there are a number of patches having code refactoring and minor clean-ups. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEE00UqedjCtOrGVvQiQBSofoJIUNIFAmSlracACgkQQBSofoJI UNJETA//RhFkVGbKlZ7cAuB6xwaLPmRi+aUSn3/rBbdq6CjBOslClEYwW981Fe19 q6VSIX7Zt5RKZg2+meHLhE28rGknLaZ4wFC/F4BDjoykN119g+KXkypjf+OVbatK zLImOzXVKjAWJevpYCENcUQY/xI2kNtg3csp6kq9GQoZCD2US/wzzI0QF6xCQ1q3 WZpHHy2fi6Lry2xGEbDLKlxg9e5nDqhOSp6S/taF+w+RyUMlTcoIU2fIWT0iZ2kI taFe+PWHHkJg2oJcgZ+hx5ZACGb1BWdHg8n1N0MK/IiNbA6CWe+iSM9T0TtctM5V Gkz4233bFR976O27Bu2znqil+AH3ECZ/F2HbRbh5vTFJIhUMwNQ1GJTgDqE5Vf8h R4W/ejbBSLM/g4TK5boyWrrKpAAVmhFhSj+OBboIlFFKchP4RKaqL7Az+m0tGSD3 0uCVAFtzmctBmgJ9ko+dxwwFwbbGiG6MDYeGIODMdqFQMQrcGqPoOrcWbj2hPoaW LRz/OzA8N0fQUfvCH6E31Ypd6cBFrG++FgtFA4lsv10KNsJUOx4MbEWxF5HobV3t axpcRsOOb95hAmqenY3sWyiR+IcIEAYlzx6D6QEbNPe47fuLxr9jx52Ollw6RDfj RvkaxzAIDeESMWWNftzJ8r8Wt6RzoBv5JjBkFIMbCz28V3v9R44= =TAG3 -----END PGP SIGNATURE----- Merge tag 'f2fs-for-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs Pull f2fs updates from Jaegeuk Kim: "In this cycle, we've mainly investigated the zoned block device support along with patches such as correcting write pointers between f2fs and storage, adding asynchronous zone reset flow, and managing the number of open zones. Other than them, f2fs adds another mount option, "errors=x" to specify how to handle when it detects an unexpected behavior at runtime. Enhancements: - support 'errors=remount-ro|continue|panic' mount option - enforce some inode flag policies - allow .tmp compression given extensions - add some ioctls to manage the f2fs compression - improve looped node chain flow - avoid issuing small-sized discard commands during checkpoint - implement an asynchronous zone reset Bug fixes: - fix deadlock in xattr and inode page lock - fix and add sanity check in some error paths - fix to avoid NULL pointer dereference f2fs_write_end_io() along with put_super - set proper flags to quota files - fix potential deadlock due to unpaired node_write lock use - fix over-estimating free section during FG GC - fix the wrong condition to determine atomic context As usual, also there are a number of patches with code refactoring and minor clean-ups" * tag 'f2fs-for-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (46 commits) f2fs: fix to do sanity check on direct node in truncate_dnode() f2fs: only set release for file that has compressed data f2fs: fix compile warning in f2fs_destroy_node_manager() f2fs: fix error path handling in truncate_dnode() f2fs: fix deadlock in i_xattr_sem and inode page lock f2fs: remove unneeded page uptodate check/set f2fs: update mtime and ctime in move file range method f2fs: compress tmp files given extension f2fs: refactor struct f2fs_attr macro f2fs: convert to use sbi directly f2fs: remove redundant assignment to variable err f2fs: do not issue small discard commands during checkpoint f2fs: check zone write pointer points to the end of zone f2fs: add f2fs_ioc_get_compress_blocks f2fs: cleanup MIN_INLINE_XATTR_SIZE f2fs: add helper to check compression level f2fs: set FMODE_CAN_ODIRECT instead of a dummy direct_IO method f2fs: do more sanity check on inode f2fs: compress: fix to check validity of i_compress_flag field f2fs: add sanity compress level check for compressed file ...
This commit is contained in:
commit
73a3fcdaa7
|
@ -351,6 +351,22 @@ age_extent_cache Enable an age extent cache based on rb-tree. It records
|
||||||
data block update frequency of the extent per inode, in
|
data block update frequency of the extent per inode, in
|
||||||
order to provide better temperature hints for data block
|
order to provide better temperature hints for data block
|
||||||
allocation.
|
allocation.
|
||||||
|
errors=%s Specify f2fs behavior on critical errors. This supports modes:
|
||||||
|
"panic", "continue" and "remount-ro", respectively, trigger
|
||||||
|
panic immediately, continue without doing anything, and remount
|
||||||
|
the partition in read-only mode. By default it uses "continue"
|
||||||
|
mode.
|
||||||
|
====================== =============== =============== ========
|
||||||
|
mode continue remount-ro panic
|
||||||
|
====================== =============== =============== ========
|
||||||
|
access ops normal noraml N/A
|
||||||
|
syscall errors -EIO -EROFS N/A
|
||||||
|
mount option rw ro N/A
|
||||||
|
pending dir write keep keep N/A
|
||||||
|
pending non-dir write drop keep N/A
|
||||||
|
pending node write drop keep N/A
|
||||||
|
pending meta write keep keep N/A
|
||||||
|
====================== =============== =============== ========
|
||||||
======================== ============================================================
|
======================== ============================================================
|
||||||
|
|
||||||
Debugfs Entries
|
Debugfs Entries
|
||||||
|
|
|
@ -30,12 +30,9 @@ void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io,
|
||||||
unsigned char reason)
|
unsigned char reason)
|
||||||
{
|
{
|
||||||
f2fs_build_fault_attr(sbi, 0, 0);
|
f2fs_build_fault_attr(sbi, 0, 0);
|
||||||
set_ckpt_flags(sbi, CP_ERROR_FLAG);
|
if (!end_io)
|
||||||
if (!end_io) {
|
|
||||||
f2fs_flush_merged_writes(sbi);
|
f2fs_flush_merged_writes(sbi);
|
||||||
|
f2fs_handle_critical_error(sbi, reason, end_io);
|
||||||
f2fs_handle_stop(sbi, reason);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -55,6 +55,7 @@ struct f2fs_compress_ops {
|
||||||
int (*init_decompress_ctx)(struct decompress_io_ctx *dic);
|
int (*init_decompress_ctx)(struct decompress_io_ctx *dic);
|
||||||
void (*destroy_decompress_ctx)(struct decompress_io_ctx *dic);
|
void (*destroy_decompress_ctx)(struct decompress_io_ctx *dic);
|
||||||
int (*decompress_pages)(struct decompress_io_ctx *dic);
|
int (*decompress_pages)(struct decompress_io_ctx *dic);
|
||||||
|
bool (*is_level_valid)(int level);
|
||||||
};
|
};
|
||||||
|
|
||||||
static unsigned int offset_in_cluster(struct compress_ctx *cc, pgoff_t index)
|
static unsigned int offset_in_cluster(struct compress_ctx *cc, pgoff_t index)
|
||||||
|
@ -308,17 +309,25 @@ static int lz4_decompress_pages(struct decompress_io_ctx *dic)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool lz4_is_level_valid(int lvl)
|
||||||
|
{
|
||||||
|
#ifdef CONFIG_F2FS_FS_LZ4HC
|
||||||
|
return !lvl || (lvl >= LZ4HC_MIN_CLEVEL && lvl <= LZ4HC_MAX_CLEVEL);
|
||||||
|
#else
|
||||||
|
return lvl == 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
static const struct f2fs_compress_ops f2fs_lz4_ops = {
|
static const struct f2fs_compress_ops f2fs_lz4_ops = {
|
||||||
.init_compress_ctx = lz4_init_compress_ctx,
|
.init_compress_ctx = lz4_init_compress_ctx,
|
||||||
.destroy_compress_ctx = lz4_destroy_compress_ctx,
|
.destroy_compress_ctx = lz4_destroy_compress_ctx,
|
||||||
.compress_pages = lz4_compress_pages,
|
.compress_pages = lz4_compress_pages,
|
||||||
.decompress_pages = lz4_decompress_pages,
|
.decompress_pages = lz4_decompress_pages,
|
||||||
|
.is_level_valid = lz4_is_level_valid,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_F2FS_FS_ZSTD
|
#ifdef CONFIG_F2FS_FS_ZSTD
|
||||||
#define F2FS_ZSTD_DEFAULT_CLEVEL 1
|
|
||||||
|
|
||||||
static int zstd_init_compress_ctx(struct compress_ctx *cc)
|
static int zstd_init_compress_ctx(struct compress_ctx *cc)
|
||||||
{
|
{
|
||||||
zstd_parameters params;
|
zstd_parameters params;
|
||||||
|
@ -327,6 +336,7 @@ static int zstd_init_compress_ctx(struct compress_ctx *cc)
|
||||||
unsigned int workspace_size;
|
unsigned int workspace_size;
|
||||||
unsigned char level = F2FS_I(cc->inode)->i_compress_level;
|
unsigned char level = F2FS_I(cc->inode)->i_compress_level;
|
||||||
|
|
||||||
|
/* Need to remain this for backward compatibility */
|
||||||
if (!level)
|
if (!level)
|
||||||
level = F2FS_ZSTD_DEFAULT_CLEVEL;
|
level = F2FS_ZSTD_DEFAULT_CLEVEL;
|
||||||
|
|
||||||
|
@ -477,6 +487,11 @@ static int zstd_decompress_pages(struct decompress_io_ctx *dic)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool zstd_is_level_valid(int lvl)
|
||||||
|
{
|
||||||
|
return lvl >= zstd_min_clevel() && lvl <= zstd_max_clevel();
|
||||||
|
}
|
||||||
|
|
||||||
static const struct f2fs_compress_ops f2fs_zstd_ops = {
|
static const struct f2fs_compress_ops f2fs_zstd_ops = {
|
||||||
.init_compress_ctx = zstd_init_compress_ctx,
|
.init_compress_ctx = zstd_init_compress_ctx,
|
||||||
.destroy_compress_ctx = zstd_destroy_compress_ctx,
|
.destroy_compress_ctx = zstd_destroy_compress_ctx,
|
||||||
|
@ -484,6 +499,7 @@ static const struct f2fs_compress_ops f2fs_zstd_ops = {
|
||||||
.init_decompress_ctx = zstd_init_decompress_ctx,
|
.init_decompress_ctx = zstd_init_decompress_ctx,
|
||||||
.destroy_decompress_ctx = zstd_destroy_decompress_ctx,
|
.destroy_decompress_ctx = zstd_destroy_decompress_ctx,
|
||||||
.decompress_pages = zstd_decompress_pages,
|
.decompress_pages = zstd_decompress_pages,
|
||||||
|
.is_level_valid = zstd_is_level_valid,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -542,6 +558,16 @@ bool f2fs_is_compress_backend_ready(struct inode *inode)
|
||||||
return f2fs_cops[F2FS_I(inode)->i_compress_algorithm];
|
return f2fs_cops[F2FS_I(inode)->i_compress_algorithm];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool f2fs_is_compress_level_valid(int alg, int lvl)
|
||||||
|
{
|
||||||
|
const struct f2fs_compress_ops *cops = f2fs_cops[alg];
|
||||||
|
|
||||||
|
if (cops->is_level_valid)
|
||||||
|
return cops->is_level_valid(lvl);
|
||||||
|
|
||||||
|
return lvl == 0;
|
||||||
|
}
|
||||||
|
|
||||||
static mempool_t *compress_page_pool;
|
static mempool_t *compress_page_pool;
|
||||||
static int num_compress_pages = 512;
|
static int num_compress_pages = 512;
|
||||||
module_param(num_compress_pages, uint, 0444);
|
module_param(num_compress_pages, uint, 0444);
|
||||||
|
@ -743,8 +769,8 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
|
||||||
ret = -EFSCORRUPTED;
|
ret = -EFSCORRUPTED;
|
||||||
|
|
||||||
/* Avoid f2fs_commit_super in irq context */
|
/* Avoid f2fs_commit_super in irq context */
|
||||||
if (in_task)
|
if (!in_task)
|
||||||
f2fs_save_errors(sbi, ERROR_FAIL_DECOMPRESSION);
|
f2fs_handle_error_async(sbi, ERROR_FAIL_DECOMPRESSION);
|
||||||
else
|
else
|
||||||
f2fs_handle_error(sbi, ERROR_FAIL_DECOMPRESSION);
|
f2fs_handle_error(sbi, ERROR_FAIL_DECOMPRESSION);
|
||||||
goto out_release;
|
goto out_release;
|
||||||
|
@ -1215,6 +1241,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
|
||||||
unsigned int last_index = cc->cluster_size - 1;
|
unsigned int last_index = cc->cluster_size - 1;
|
||||||
loff_t psize;
|
loff_t psize;
|
||||||
int i, err;
|
int i, err;
|
||||||
|
bool quota_inode = IS_NOQUOTA(inode);
|
||||||
|
|
||||||
/* we should bypass data pages to proceed the kworker jobs */
|
/* we should bypass data pages to proceed the kworker jobs */
|
||||||
if (unlikely(f2fs_cp_error(sbi))) {
|
if (unlikely(f2fs_cp_error(sbi))) {
|
||||||
|
@ -1222,7 +1249,7 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IS_NOQUOTA(inode)) {
|
if (quota_inode) {
|
||||||
/*
|
/*
|
||||||
* We need to wait for node_write to avoid block allocation during
|
* We need to wait for node_write to avoid block allocation during
|
||||||
* checkpoint. This can only happen to quota writes which can cause
|
* checkpoint. This can only happen to quota writes which can cause
|
||||||
|
@ -1344,7 +1371,7 @@ unlock_continue:
|
||||||
set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);
|
set_inode_flag(inode, FI_FIRST_BLOCK_WRITTEN);
|
||||||
|
|
||||||
f2fs_put_dnode(&dn);
|
f2fs_put_dnode(&dn);
|
||||||
if (IS_NOQUOTA(inode))
|
if (quota_inode)
|
||||||
f2fs_up_read(&sbi->node_write);
|
f2fs_up_read(&sbi->node_write);
|
||||||
else
|
else
|
||||||
f2fs_unlock_op(sbi);
|
f2fs_unlock_op(sbi);
|
||||||
|
@ -1370,7 +1397,7 @@ out_put_cic:
|
||||||
out_put_dnode:
|
out_put_dnode:
|
||||||
f2fs_put_dnode(&dn);
|
f2fs_put_dnode(&dn);
|
||||||
out_unlock_op:
|
out_unlock_op:
|
||||||
if (IS_NOQUOTA(inode))
|
if (quota_inode)
|
||||||
f2fs_up_read(&sbi->node_write);
|
f2fs_up_read(&sbi->node_write);
|
||||||
else
|
else
|
||||||
f2fs_unlock_op(sbi);
|
f2fs_unlock_op(sbi);
|
||||||
|
|
|
@ -383,6 +383,17 @@ static void f2fs_write_end_io(struct bio *bio)
|
||||||
bio_put(bio);
|
bio_put(bio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
static void f2fs_zone_write_end_io(struct bio *bio)
|
||||||
|
{
|
||||||
|
struct f2fs_bio_info *io = (struct f2fs_bio_info *)bio->bi_private;
|
||||||
|
|
||||||
|
bio->bi_private = io->bi_private;
|
||||||
|
complete(&io->zone_wait);
|
||||||
|
f2fs_write_end_io(bio);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
struct block_device *f2fs_target_device(struct f2fs_sb_info *sbi,
|
struct block_device *f2fs_target_device(struct f2fs_sb_info *sbi,
|
||||||
block_t blk_addr, sector_t *sector)
|
block_t blk_addr, sector_t *sector)
|
||||||
{
|
{
|
||||||
|
@ -639,6 +650,11 @@ int f2fs_init_write_merge_io(struct f2fs_sb_info *sbi)
|
||||||
INIT_LIST_HEAD(&sbi->write_io[i][j].io_list);
|
INIT_LIST_HEAD(&sbi->write_io[i][j].io_list);
|
||||||
INIT_LIST_HEAD(&sbi->write_io[i][j].bio_list);
|
INIT_LIST_HEAD(&sbi->write_io[i][j].bio_list);
|
||||||
init_f2fs_rwsem(&sbi->write_io[i][j].bio_list_lock);
|
init_f2fs_rwsem(&sbi->write_io[i][j].bio_list_lock);
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
init_completion(&sbi->write_io[i][j].zone_wait);
|
||||||
|
sbi->write_io[i][j].zone_pending_bio = NULL;
|
||||||
|
sbi->write_io[i][j].bi_private = NULL;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -965,6 +981,26 @@ alloc_new:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
static bool is_end_zone_blkaddr(struct f2fs_sb_info *sbi, block_t blkaddr)
|
||||||
|
{
|
||||||
|
int devi = 0;
|
||||||
|
|
||||||
|
if (f2fs_is_multi_device(sbi)) {
|
||||||
|
devi = f2fs_target_device_index(sbi, blkaddr);
|
||||||
|
if (blkaddr < FDEV(devi).start_blk ||
|
||||||
|
blkaddr > FDEV(devi).end_blk) {
|
||||||
|
f2fs_err(sbi, "Invalid block %x", blkaddr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
blkaddr -= FDEV(devi).start_blk;
|
||||||
|
}
|
||||||
|
return bdev_zoned_model(FDEV(devi).bdev) == BLK_ZONED_HM &&
|
||||||
|
f2fs_blkz_is_seq(sbi, devi, blkaddr) &&
|
||||||
|
(blkaddr % sbi->blocks_per_blkz == sbi->blocks_per_blkz - 1);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void f2fs_submit_page_write(struct f2fs_io_info *fio)
|
void f2fs_submit_page_write(struct f2fs_io_info *fio)
|
||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = fio->sbi;
|
struct f2fs_sb_info *sbi = fio->sbi;
|
||||||
|
@ -975,6 +1011,16 @@ void f2fs_submit_page_write(struct f2fs_io_info *fio)
|
||||||
f2fs_bug_on(sbi, is_read_io(fio->op));
|
f2fs_bug_on(sbi, is_read_io(fio->op));
|
||||||
|
|
||||||
f2fs_down_write(&io->io_rwsem);
|
f2fs_down_write(&io->io_rwsem);
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
if (f2fs_sb_has_blkzoned(sbi) && btype < META && io->zone_pending_bio) {
|
||||||
|
wait_for_completion_io(&io->zone_wait);
|
||||||
|
bio_put(io->zone_pending_bio);
|
||||||
|
io->zone_pending_bio = NULL;
|
||||||
|
io->bi_private = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
next:
|
next:
|
||||||
if (fio->in_list) {
|
if (fio->in_list) {
|
||||||
spin_lock(&io->io_lock);
|
spin_lock(&io->io_lock);
|
||||||
|
@ -1038,6 +1084,18 @@ skip:
|
||||||
if (fio->in_list)
|
if (fio->in_list)
|
||||||
goto next;
|
goto next;
|
||||||
out:
|
out:
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
if (f2fs_sb_has_blkzoned(sbi) && btype < META &&
|
||||||
|
is_end_zone_blkaddr(sbi, fio->new_blkaddr)) {
|
||||||
|
bio_get(io->bio);
|
||||||
|
reinit_completion(&io->zone_wait);
|
||||||
|
io->bi_private = io->bio->bi_private;
|
||||||
|
io->bio->bi_private = io;
|
||||||
|
io->bio->bi_end_io = f2fs_zone_write_end_io;
|
||||||
|
io->zone_pending_bio = io->bio;
|
||||||
|
__submit_merged_bio(io);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (is_sbi_flag_set(sbi, SBI_IS_SHUTDOWN) ||
|
if (is_sbi_flag_set(sbi, SBI_IS_SHUTDOWN) ||
|
||||||
!f2fs_is_checkpoint_ready(sbi))
|
!f2fs_is_checkpoint_ready(sbi))
|
||||||
__submit_merged_bio(io);
|
__submit_merged_bio(io);
|
||||||
|
@ -2173,7 +2231,6 @@ submit_and_realloc:
|
||||||
f2fs_update_iostat(F2FS_I_SB(inode), NULL, FS_DATA_READ_IO,
|
f2fs_update_iostat(F2FS_I_SB(inode), NULL, FS_DATA_READ_IO,
|
||||||
F2FS_BLKSIZE);
|
F2FS_BLKSIZE);
|
||||||
*last_block_in_bio = block_nr;
|
*last_block_in_bio = block_nr;
|
||||||
goto out;
|
|
||||||
out:
|
out:
|
||||||
*bio_ret = bio;
|
*bio_ret = bio;
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2775,6 +2832,7 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
|
||||||
loff_t psize = (loff_t)(page->index + 1) << PAGE_SHIFT;
|
loff_t psize = (loff_t)(page->index + 1) << PAGE_SHIFT;
|
||||||
unsigned offset = 0;
|
unsigned offset = 0;
|
||||||
bool need_balance_fs = false;
|
bool need_balance_fs = false;
|
||||||
|
bool quota_inode = IS_NOQUOTA(inode);
|
||||||
int err = 0;
|
int err = 0;
|
||||||
struct f2fs_io_info fio = {
|
struct f2fs_io_info fio = {
|
||||||
.sbi = sbi,
|
.sbi = sbi,
|
||||||
|
@ -2807,6 +2865,10 @@ int f2fs_write_single_data_page(struct page *page, int *submitted,
|
||||||
if (S_ISDIR(inode->i_mode) &&
|
if (S_ISDIR(inode->i_mode) &&
|
||||||
!is_sbi_flag_set(sbi, SBI_IS_CLOSE))
|
!is_sbi_flag_set(sbi, SBI_IS_CLOSE))
|
||||||
goto redirty_out;
|
goto redirty_out;
|
||||||
|
|
||||||
|
/* keep data pages in remount-ro mode */
|
||||||
|
if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_READONLY)
|
||||||
|
goto redirty_out;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2832,19 +2894,19 @@ write:
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Dentry/quota blocks are controlled by checkpoint */
|
/* Dentry/quota blocks are controlled by checkpoint */
|
||||||
if (S_ISDIR(inode->i_mode) || IS_NOQUOTA(inode)) {
|
if (S_ISDIR(inode->i_mode) || quota_inode) {
|
||||||
/*
|
/*
|
||||||
* We need to wait for node_write to avoid block allocation during
|
* We need to wait for node_write to avoid block allocation during
|
||||||
* checkpoint. This can only happen to quota writes which can cause
|
* checkpoint. This can only happen to quota writes which can cause
|
||||||
* the below discard race condition.
|
* the below discard race condition.
|
||||||
*/
|
*/
|
||||||
if (IS_NOQUOTA(inode))
|
if (quota_inode)
|
||||||
f2fs_down_read(&sbi->node_write);
|
f2fs_down_read(&sbi->node_write);
|
||||||
|
|
||||||
fio.need_lock = LOCK_DONE;
|
fio.need_lock = LOCK_DONE;
|
||||||
err = f2fs_do_write_data_page(&fio);
|
err = f2fs_do_write_data_page(&fio);
|
||||||
|
|
||||||
if (IS_NOQUOTA(inode))
|
if (quota_inode)
|
||||||
f2fs_up_read(&sbi->node_write);
|
f2fs_up_read(&sbi->node_write);
|
||||||
|
|
||||||
goto done;
|
goto done;
|
||||||
|
@ -4067,7 +4129,6 @@ const struct address_space_operations f2fs_dblock_aops = {
|
||||||
.migrate_folio = filemap_migrate_folio,
|
.migrate_folio = filemap_migrate_folio,
|
||||||
.invalidate_folio = f2fs_invalidate_folio,
|
.invalidate_folio = f2fs_invalidate_folio,
|
||||||
.release_folio = f2fs_release_folio,
|
.release_folio = f2fs_release_folio,
|
||||||
.direct_IO = noop_direct_IO,
|
|
||||||
.bmap = f2fs_bmap,
|
.bmap = f2fs_bmap,
|
||||||
.swap_activate = f2fs_swap_activate,
|
.swap_activate = f2fs_swap_activate,
|
||||||
.swap_deactivate = f2fs_swap_deactivate,
|
.swap_deactivate = f2fs_swap_deactivate,
|
||||||
|
|
|
@ -775,8 +775,15 @@ int f2fs_add_dentry(struct inode *dir, const struct f2fs_filename *fname,
|
||||||
{
|
{
|
||||||
int err = -EAGAIN;
|
int err = -EAGAIN;
|
||||||
|
|
||||||
if (f2fs_has_inline_dentry(dir))
|
if (f2fs_has_inline_dentry(dir)) {
|
||||||
|
/*
|
||||||
|
* Should get i_xattr_sem to keep the lock order:
|
||||||
|
* i_xattr_sem -> inode_page lock used by f2fs_setxattr.
|
||||||
|
*/
|
||||||
|
f2fs_down_read(&F2FS_I(dir)->i_xattr_sem);
|
||||||
err = f2fs_add_inline_entry(dir, fname, inode, ino, mode);
|
err = f2fs_add_inline_entry(dir, fname, inode, ino, mode);
|
||||||
|
f2fs_up_read(&F2FS_I(dir)->i_xattr_sem);
|
||||||
|
}
|
||||||
if (err == -EAGAIN)
|
if (err == -EAGAIN)
|
||||||
err = f2fs_add_regular_entry(dir, fname, inode, ino, mode);
|
err = f2fs_add_regular_entry(dir, fname, inode, ino, mode);
|
||||||
|
|
||||||
|
|
124
fs/f2fs/f2fs.h
124
fs/f2fs/f2fs.h
|
@ -80,34 +80,34 @@ extern const char *f2fs_fault_name[FAULT_MAX];
|
||||||
/*
|
/*
|
||||||
* For mount options
|
* For mount options
|
||||||
*/
|
*/
|
||||||
#define F2FS_MOUNT_DISABLE_ROLL_FORWARD 0x00000002
|
#define F2FS_MOUNT_DISABLE_ROLL_FORWARD 0x00000001
|
||||||
#define F2FS_MOUNT_DISCARD 0x00000004
|
#define F2FS_MOUNT_DISCARD 0x00000002
|
||||||
#define F2FS_MOUNT_NOHEAP 0x00000008
|
#define F2FS_MOUNT_NOHEAP 0x00000004
|
||||||
#define F2FS_MOUNT_XATTR_USER 0x00000010
|
#define F2FS_MOUNT_XATTR_USER 0x00000008
|
||||||
#define F2FS_MOUNT_POSIX_ACL 0x00000020
|
#define F2FS_MOUNT_POSIX_ACL 0x00000010
|
||||||
#define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040
|
#define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000020
|
||||||
#define F2FS_MOUNT_INLINE_XATTR 0x00000080
|
#define F2FS_MOUNT_INLINE_XATTR 0x00000040
|
||||||
#define F2FS_MOUNT_INLINE_DATA 0x00000100
|
#define F2FS_MOUNT_INLINE_DATA 0x00000080
|
||||||
#define F2FS_MOUNT_INLINE_DENTRY 0x00000200
|
#define F2FS_MOUNT_INLINE_DENTRY 0x00000100
|
||||||
#define F2FS_MOUNT_FLUSH_MERGE 0x00000400
|
#define F2FS_MOUNT_FLUSH_MERGE 0x00000200
|
||||||
#define F2FS_MOUNT_NOBARRIER 0x00000800
|
#define F2FS_MOUNT_NOBARRIER 0x00000400
|
||||||
#define F2FS_MOUNT_FASTBOOT 0x00001000
|
#define F2FS_MOUNT_FASTBOOT 0x00000800
|
||||||
#define F2FS_MOUNT_READ_EXTENT_CACHE 0x00002000
|
#define F2FS_MOUNT_READ_EXTENT_CACHE 0x00001000
|
||||||
#define F2FS_MOUNT_DATA_FLUSH 0x00008000
|
#define F2FS_MOUNT_DATA_FLUSH 0x00002000
|
||||||
#define F2FS_MOUNT_FAULT_INJECTION 0x00010000
|
#define F2FS_MOUNT_FAULT_INJECTION 0x00004000
|
||||||
#define F2FS_MOUNT_USRQUOTA 0x00080000
|
#define F2FS_MOUNT_USRQUOTA 0x00008000
|
||||||
#define F2FS_MOUNT_GRPQUOTA 0x00100000
|
#define F2FS_MOUNT_GRPQUOTA 0x00010000
|
||||||
#define F2FS_MOUNT_PRJQUOTA 0x00200000
|
#define F2FS_MOUNT_PRJQUOTA 0x00020000
|
||||||
#define F2FS_MOUNT_QUOTA 0x00400000
|
#define F2FS_MOUNT_QUOTA 0x00040000
|
||||||
#define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00800000
|
#define F2FS_MOUNT_INLINE_XATTR_SIZE 0x00080000
|
||||||
#define F2FS_MOUNT_RESERVE_ROOT 0x01000000
|
#define F2FS_MOUNT_RESERVE_ROOT 0x00100000
|
||||||
#define F2FS_MOUNT_DISABLE_CHECKPOINT 0x02000000
|
#define F2FS_MOUNT_DISABLE_CHECKPOINT 0x00200000
|
||||||
#define F2FS_MOUNT_NORECOVERY 0x04000000
|
#define F2FS_MOUNT_NORECOVERY 0x00400000
|
||||||
#define F2FS_MOUNT_ATGC 0x08000000
|
#define F2FS_MOUNT_ATGC 0x00800000
|
||||||
#define F2FS_MOUNT_MERGE_CHECKPOINT 0x10000000
|
#define F2FS_MOUNT_MERGE_CHECKPOINT 0x01000000
|
||||||
#define F2FS_MOUNT_GC_MERGE 0x20000000
|
#define F2FS_MOUNT_GC_MERGE 0x02000000
|
||||||
#define F2FS_MOUNT_COMPRESS_CACHE 0x40000000
|
#define F2FS_MOUNT_COMPRESS_CACHE 0x04000000
|
||||||
#define F2FS_MOUNT_AGE_EXTENT_CACHE 0x80000000
|
#define F2FS_MOUNT_AGE_EXTENT_CACHE 0x08000000
|
||||||
|
|
||||||
#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)
|
||||||
|
@ -162,6 +162,7 @@ struct f2fs_mount_info {
|
||||||
int fs_mode; /* fs mode: LFS or ADAPTIVE */
|
int fs_mode; /* fs mode: LFS or ADAPTIVE */
|
||||||
int bggc_mode; /* bggc mode: off, on or sync */
|
int bggc_mode; /* bggc mode: off, on or sync */
|
||||||
int memory_mode; /* memory mode */
|
int memory_mode; /* memory mode */
|
||||||
|
int errors; /* errors parameter */
|
||||||
int discard_unit; /*
|
int discard_unit; /*
|
||||||
* discard command's offset/size should
|
* discard command's offset/size should
|
||||||
* be aligned to this unit: block,
|
* be aligned to this unit: block,
|
||||||
|
@ -185,21 +186,21 @@ struct f2fs_mount_info {
|
||||||
unsigned char noextensions[COMPRESS_EXT_NUM][F2FS_EXTENSION_LEN]; /* extensions */
|
unsigned char noextensions[COMPRESS_EXT_NUM][F2FS_EXTENSION_LEN]; /* extensions */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define F2FS_FEATURE_ENCRYPT 0x0001
|
#define F2FS_FEATURE_ENCRYPT 0x00000001
|
||||||
#define F2FS_FEATURE_BLKZONED 0x0002
|
#define F2FS_FEATURE_BLKZONED 0x00000002
|
||||||
#define F2FS_FEATURE_ATOMIC_WRITE 0x0004
|
#define F2FS_FEATURE_ATOMIC_WRITE 0x00000004
|
||||||
#define F2FS_FEATURE_EXTRA_ATTR 0x0008
|
#define F2FS_FEATURE_EXTRA_ATTR 0x00000008
|
||||||
#define F2FS_FEATURE_PRJQUOTA 0x0010
|
#define F2FS_FEATURE_PRJQUOTA 0x00000010
|
||||||
#define F2FS_FEATURE_INODE_CHKSUM 0x0020
|
#define F2FS_FEATURE_INODE_CHKSUM 0x00000020
|
||||||
#define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x0040
|
#define F2FS_FEATURE_FLEXIBLE_INLINE_XATTR 0x00000040
|
||||||
#define F2FS_FEATURE_QUOTA_INO 0x0080
|
#define F2FS_FEATURE_QUOTA_INO 0x00000080
|
||||||
#define F2FS_FEATURE_INODE_CRTIME 0x0100
|
#define F2FS_FEATURE_INODE_CRTIME 0x00000100
|
||||||
#define F2FS_FEATURE_LOST_FOUND 0x0200
|
#define F2FS_FEATURE_LOST_FOUND 0x00000200
|
||||||
#define F2FS_FEATURE_VERITY 0x0400
|
#define F2FS_FEATURE_VERITY 0x00000400
|
||||||
#define F2FS_FEATURE_SB_CHKSUM 0x0800
|
#define F2FS_FEATURE_SB_CHKSUM 0x00000800
|
||||||
#define F2FS_FEATURE_CASEFOLD 0x1000
|
#define F2FS_FEATURE_CASEFOLD 0x00001000
|
||||||
#define F2FS_FEATURE_COMPRESSION 0x2000
|
#define F2FS_FEATURE_COMPRESSION 0x00002000
|
||||||
#define F2FS_FEATURE_RO 0x4000
|
#define F2FS_FEATURE_RO 0x00004000
|
||||||
|
|
||||||
#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)
|
||||||
|
@ -1175,6 +1176,7 @@ enum iostat_type {
|
||||||
/* other */
|
/* other */
|
||||||
FS_DISCARD_IO, /* discard */
|
FS_DISCARD_IO, /* discard */
|
||||||
FS_FLUSH_IO, /* flush */
|
FS_FLUSH_IO, /* flush */
|
||||||
|
FS_ZONE_RESET_IO, /* zone reset */
|
||||||
NR_IO_TYPE,
|
NR_IO_TYPE,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1217,6 +1219,11 @@ struct f2fs_bio_info {
|
||||||
struct bio *bio; /* bios to merge */
|
struct bio *bio; /* bios to merge */
|
||||||
sector_t last_block_in_bio; /* last block number */
|
sector_t last_block_in_bio; /* last block number */
|
||||||
struct f2fs_io_info fio; /* store buffered io info. */
|
struct f2fs_io_info fio; /* store buffered io info. */
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
struct completion zone_wait; /* condition value for the previous open zone to close */
|
||||||
|
struct bio *zone_pending_bio; /* pending bio for the previous zone */
|
||||||
|
void *bi_private; /* previous bi_private for pending bio */
|
||||||
|
#endif
|
||||||
struct f2fs_rwsem io_rwsem; /* blocking op for bio */
|
struct f2fs_rwsem io_rwsem; /* blocking op for bio */
|
||||||
spinlock_t io_lock; /* serialize DATA/NODE IOs */
|
spinlock_t io_lock; /* serialize DATA/NODE IOs */
|
||||||
struct list_head io_list; /* track fios */
|
struct list_head io_list; /* track fios */
|
||||||
|
@ -1370,6 +1377,12 @@ enum {
|
||||||
MEMORY_MODE_LOW, /* memory mode for low memry devices */
|
MEMORY_MODE_LOW, /* memory mode for low memry devices */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum errors_option {
|
||||||
|
MOUNT_ERRORS_READONLY, /* remount fs ro on errors */
|
||||||
|
MOUNT_ERRORS_CONTINUE, /* continue on errors */
|
||||||
|
MOUNT_ERRORS_PANIC, /* panic on errors */
|
||||||
|
};
|
||||||
|
|
||||||
static inline int f2fs_test_bit(unsigned int nr, char *addr);
|
static inline int f2fs_test_bit(unsigned int nr, char *addr);
|
||||||
static inline void f2fs_set_bit(unsigned int nr, char *addr);
|
static inline void f2fs_set_bit(unsigned int nr, char *addr);
|
||||||
static inline void f2fs_clear_bit(unsigned int nr, char *addr);
|
static inline void f2fs_clear_bit(unsigned int nr, char *addr);
|
||||||
|
@ -1427,6 +1440,8 @@ struct compress_data {
|
||||||
|
|
||||||
#define F2FS_COMPRESSED_PAGE_MAGIC 0xF5F2C000
|
#define F2FS_COMPRESSED_PAGE_MAGIC 0xF5F2C000
|
||||||
|
|
||||||
|
#define F2FS_ZSTD_DEFAULT_CLEVEL 1
|
||||||
|
|
||||||
#define COMPRESS_LEVEL_OFFSET 8
|
#define COMPRESS_LEVEL_OFFSET 8
|
||||||
|
|
||||||
/* compress context */
|
/* compress context */
|
||||||
|
@ -1721,8 +1736,14 @@ struct f2fs_sb_info {
|
||||||
|
|
||||||
struct workqueue_struct *post_read_wq; /* post read workqueue */
|
struct workqueue_struct *post_read_wq; /* post read workqueue */
|
||||||
|
|
||||||
unsigned char errors[MAX_F2FS_ERRORS]; /* error flags */
|
/*
|
||||||
spinlock_t error_lock; /* protect errors array */
|
* If we are in irq context, let's update error information into
|
||||||
|
* on-disk superblock in the work.
|
||||||
|
*/
|
||||||
|
struct work_struct s_error_work;
|
||||||
|
unsigned char errors[MAX_F2FS_ERRORS]; /* error flags */
|
||||||
|
unsigned char stop_reason[MAX_STOP_REASON]; /* stop reason */
|
||||||
|
spinlock_t error_lock; /* protect errors/stop_reason array */
|
||||||
bool error_dirty; /* errors of sb is dirty */
|
bool error_dirty; /* errors of sb is dirty */
|
||||||
|
|
||||||
struct kmem_cache *inline_xattr_slab; /* inline xattr entry */
|
struct kmem_cache *inline_xattr_slab; /* inline xattr entry */
|
||||||
|
@ -2941,6 +2962,8 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
|
||||||
#define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
|
#define F2FS_PROJINHERIT_FL 0x20000000 /* Create with parents projid */
|
||||||
#define F2FS_CASEFOLD_FL 0x40000000 /* Casefolded file */
|
#define F2FS_CASEFOLD_FL 0x40000000 /* Casefolded file */
|
||||||
|
|
||||||
|
#define F2FS_QUOTA_DEFAULT_FL (F2FS_NOATIME_FL | F2FS_IMMUTABLE_FL)
|
||||||
|
|
||||||
/* Flags that should be inherited by new inodes from their parent. */
|
/* Flags that should be inherited by new inodes from their parent. */
|
||||||
#define F2FS_FL_INHERITED (F2FS_SYNC_FL | F2FS_NODUMP_FL | F2FS_NOATIME_FL | \
|
#define F2FS_FL_INHERITED (F2FS_SYNC_FL | F2FS_NODUMP_FL | F2FS_NOATIME_FL | \
|
||||||
F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL | \
|
F2FS_DIRSYNC_FL | F2FS_PROJINHERIT_FL | \
|
||||||
|
@ -3394,6 +3417,8 @@ static inline int get_inline_xattr_addrs(struct inode *inode)
|
||||||
((is_inode_flag_set(i, FI_ACL_MODE)) ? \
|
((is_inode_flag_set(i, FI_ACL_MODE)) ? \
|
||||||
(F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
|
(F2FS_I(i)->i_acl_mode) : ((i)->i_mode))
|
||||||
|
|
||||||
|
#define F2FS_MIN_EXTRA_ATTR_SIZE (sizeof(__le32))
|
||||||
|
|
||||||
#define F2FS_TOTAL_EXTRA_ATTR_SIZE \
|
#define F2FS_TOTAL_EXTRA_ATTR_SIZE \
|
||||||
(offsetof(struct f2fs_inode, i_extra_end) - \
|
(offsetof(struct f2fs_inode, i_extra_end) - \
|
||||||
offsetof(struct f2fs_inode, i_extra_isize)) \
|
offsetof(struct f2fs_inode, i_extra_isize)) \
|
||||||
|
@ -3432,7 +3457,6 @@ static inline bool __is_valid_data_blkaddr(block_t blkaddr)
|
||||||
* file.c
|
* file.c
|
||||||
*/
|
*/
|
||||||
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync);
|
int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync);
|
||||||
void f2fs_truncate_data_blocks(struct dnode_of_data *dn);
|
|
||||||
int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock);
|
int f2fs_do_truncate_blocks(struct inode *inode, u64 from, bool lock);
|
||||||
int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock);
|
int f2fs_truncate_blocks(struct inode *inode, u64 from, bool lock);
|
||||||
int f2fs_truncate(struct inode *inode);
|
int f2fs_truncate(struct inode *inode);
|
||||||
|
@ -3541,9 +3565,11 @@ int f2fs_enable_quota_files(struct f2fs_sb_info *sbi, bool rdonly);
|
||||||
int f2fs_quota_sync(struct super_block *sb, int type);
|
int f2fs_quota_sync(struct super_block *sb, int type);
|
||||||
loff_t max_file_blocks(struct inode *inode);
|
loff_t max_file_blocks(struct inode *inode);
|
||||||
void f2fs_quota_off_umount(struct super_block *sb);
|
void f2fs_quota_off_umount(struct super_block *sb);
|
||||||
void f2fs_handle_stop(struct f2fs_sb_info *sbi, unsigned char reason);
|
|
||||||
void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag);
|
void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag);
|
||||||
|
void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason,
|
||||||
|
bool irq_context);
|
||||||
void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error);
|
void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error);
|
||||||
|
void f2fs_handle_error_async(struct f2fs_sb_info *sbi, unsigned char error);
|
||||||
int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover);
|
int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover);
|
||||||
int f2fs_sync_fs(struct super_block *sb, int sync);
|
int f2fs_sync_fs(struct super_block *sb, int sync);
|
||||||
int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi);
|
int f2fs_sanity_check_ckpt(struct f2fs_sb_info *sbi);
|
||||||
|
@ -3815,7 +3841,7 @@ void f2fs_stop_gc_thread(struct f2fs_sb_info *sbi);
|
||||||
block_t f2fs_start_bidx_of_node(unsigned int node_ofs, struct inode *inode);
|
block_t f2fs_start_bidx_of_node(unsigned int node_ofs, struct inode *inode);
|
||||||
int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control);
|
int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control);
|
||||||
void f2fs_build_gc_manager(struct f2fs_sb_info *sbi);
|
void f2fs_build_gc_manager(struct f2fs_sb_info *sbi);
|
||||||
int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count);
|
int f2fs_resize_fs(struct file *filp, __u64 block_count);
|
||||||
int __init f2fs_create_garbage_collection_cache(void);
|
int __init f2fs_create_garbage_collection_cache(void);
|
||||||
void f2fs_destroy_garbage_collection_cache(void);
|
void f2fs_destroy_garbage_collection_cache(void);
|
||||||
/* victim selection function for cleaning and SSR */
|
/* victim selection function for cleaning and SSR */
|
||||||
|
@ -4213,6 +4239,7 @@ bool f2fs_compress_write_end(struct inode *inode, void *fsdata,
|
||||||
int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock);
|
int f2fs_truncate_partial_cluster(struct inode *inode, u64 from, bool lock);
|
||||||
void f2fs_compress_write_end_io(struct bio *bio, struct page *page);
|
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);
|
||||||
|
bool f2fs_is_compress_level_valid(int alg, int lvl);
|
||||||
int __init f2fs_init_compress_mempool(void);
|
int __init f2fs_init_compress_mempool(void);
|
||||||
void f2fs_destroy_compress_mempool(void);
|
void f2fs_destroy_compress_mempool(void);
|
||||||
void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task);
|
void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task);
|
||||||
|
@ -4277,6 +4304,7 @@ static inline bool f2fs_is_compress_backend_ready(struct inode *inode)
|
||||||
/* not support compression */
|
/* not support compression */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
static inline bool f2fs_is_compress_level_valid(int alg, int lvl) { return false; }
|
||||||
static inline struct page *f2fs_compress_control_page(struct page *page)
|
static inline struct page *f2fs_compress_control_page(struct page *page)
|
||||||
{
|
{
|
||||||
WARN_ON_ONCE(1);
|
WARN_ON_ONCE(1);
|
||||||
|
|
|
@ -149,8 +149,6 @@ static vm_fault_t f2fs_vm_page_mkwrite(struct vm_fault *vmf)
|
||||||
zero_user_segment(page, offset, PAGE_SIZE);
|
zero_user_segment(page, offset, PAGE_SIZE);
|
||||||
}
|
}
|
||||||
set_page_dirty(page);
|
set_page_dirty(page);
|
||||||
if (!PageUptodate(page))
|
|
||||||
SetPageUptodate(page);
|
|
||||||
|
|
||||||
f2fs_update_iostat(sbi, inode, APP_MAPPED_IO, F2FS_BLKSIZE);
|
f2fs_update_iostat(sbi, inode, APP_MAPPED_IO, F2FS_BLKSIZE);
|
||||||
f2fs_update_time(sbi, REQ_TIME);
|
f2fs_update_time(sbi, REQ_TIME);
|
||||||
|
@ -546,7 +544,8 @@ static int f2fs_file_open(struct inode *inode, struct file *filp)
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
filp->f_mode |= FMODE_NOWAIT;
|
filp->f_mode |= FMODE_NOWAIT | FMODE_BUF_RASYNC;
|
||||||
|
filp->f_mode |= FMODE_CAN_ODIRECT;
|
||||||
|
|
||||||
return dquot_file_open(inode, filp);
|
return dquot_file_open(inode, filp);
|
||||||
}
|
}
|
||||||
|
@ -627,11 +626,6 @@ void f2fs_truncate_data_blocks_range(struct dnode_of_data *dn, int count)
|
||||||
dn->ofs_in_node, nr_free);
|
dn->ofs_in_node, nr_free);
|
||||||
}
|
}
|
||||||
|
|
||||||
void f2fs_truncate_data_blocks(struct dnode_of_data *dn)
|
|
||||||
{
|
|
||||||
f2fs_truncate_data_blocks_range(dn, ADDRS_PER_BLOCK(dn->inode));
|
|
||||||
}
|
|
||||||
|
|
||||||
static int truncate_partial_data_page(struct inode *inode, u64 from,
|
static int truncate_partial_data_page(struct inode *inode, u64 from,
|
||||||
bool cache_only)
|
bool cache_only)
|
||||||
{
|
{
|
||||||
|
@ -2225,7 +2219,6 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
f2fs_stop_checkpoint(sbi, false,
|
f2fs_stop_checkpoint(sbi, false,
|
||||||
STOP_CP_REASON_SHUTDOWN);
|
STOP_CP_REASON_SHUTDOWN);
|
||||||
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
|
|
||||||
trace_f2fs_shutdown(sbi, in, ret);
|
trace_f2fs_shutdown(sbi, in, ret);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -2238,7 +2231,6 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
|
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
|
||||||
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
|
|
||||||
thaw_bdev(sb->s_bdev);
|
thaw_bdev(sb->s_bdev);
|
||||||
break;
|
break;
|
||||||
case F2FS_GOING_DOWN_METASYNC:
|
case F2FS_GOING_DOWN_METASYNC:
|
||||||
|
@ -2247,16 +2239,13 @@ static int f2fs_ioc_shutdown(struct file *filp, unsigned long arg)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
|
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
|
||||||
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
|
|
||||||
break;
|
break;
|
||||||
case F2FS_GOING_DOWN_NOSYNC:
|
case F2FS_GOING_DOWN_NOSYNC:
|
||||||
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
|
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
|
||||||
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
|
|
||||||
break;
|
break;
|
||||||
case F2FS_GOING_DOWN_METAFLUSH:
|
case F2FS_GOING_DOWN_METAFLUSH:
|
||||||
f2fs_sync_meta_pages(sbi, META, LONG_MAX, FS_META_IO);
|
f2fs_sync_meta_pages(sbi, META, LONG_MAX, FS_META_IO);
|
||||||
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
|
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_SHUTDOWN);
|
||||||
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
|
|
||||||
break;
|
break;
|
||||||
case F2FS_GOING_DOWN_NEED_FSCK:
|
case F2FS_GOING_DOWN_NEED_FSCK:
|
||||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
|
@ -2593,6 +2582,11 @@ static int f2fs_defragment_range(struct f2fs_sb_info *sbi,
|
||||||
|
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
|
|
||||||
|
if (is_inode_flag_set(inode, FI_COMPRESS_RELEASED)) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto unlock_out;
|
||||||
|
}
|
||||||
|
|
||||||
/* if in-place-update policy is enabled, don't waste time here */
|
/* if in-place-update policy is enabled, don't waste time here */
|
||||||
set_inode_flag(inode, FI_OPU_WRITE);
|
set_inode_flag(inode, FI_OPU_WRITE);
|
||||||
if (f2fs_should_update_inplace(inode, NULL)) {
|
if (f2fs_should_update_inplace(inode, NULL)) {
|
||||||
|
@ -2717,6 +2711,7 @@ clear_out:
|
||||||
clear_inode_flag(inode, FI_SKIP_WRITES);
|
clear_inode_flag(inode, FI_SKIP_WRITES);
|
||||||
out:
|
out:
|
||||||
clear_inode_flag(inode, FI_OPU_WRITE);
|
clear_inode_flag(inode, FI_OPU_WRITE);
|
||||||
|
unlock_out:
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
if (!err)
|
if (!err)
|
||||||
range->len = (u64)total << PAGE_SHIFT;
|
range->len = (u64)total << PAGE_SHIFT;
|
||||||
|
@ -2876,6 +2871,17 @@ static int f2fs_move_file_range(struct file *file_in, loff_t pos_in,
|
||||||
f2fs_up_write(&F2FS_I(dst)->i_gc_rwsem[WRITE]);
|
f2fs_up_write(&F2FS_I(dst)->i_gc_rwsem[WRITE]);
|
||||||
out_src:
|
out_src:
|
||||||
f2fs_up_write(&F2FS_I(src)->i_gc_rwsem[WRITE]);
|
f2fs_up_write(&F2FS_I(src)->i_gc_rwsem[WRITE]);
|
||||||
|
if (ret)
|
||||||
|
goto out_unlock;
|
||||||
|
|
||||||
|
src->i_mtime = src->i_ctime = current_time(src);
|
||||||
|
f2fs_mark_inode_dirty_sync(src, false);
|
||||||
|
if (src != dst) {
|
||||||
|
dst->i_mtime = dst->i_ctime = current_time(dst);
|
||||||
|
f2fs_mark_inode_dirty_sync(dst, false);
|
||||||
|
}
|
||||||
|
f2fs_update_time(sbi, REQ_TIME);
|
||||||
|
|
||||||
out_unlock:
|
out_unlock:
|
||||||
if (src != dst)
|
if (src != dst)
|
||||||
inode_unlock(dst);
|
inode_unlock(dst);
|
||||||
|
@ -3278,7 +3284,7 @@ static int f2fs_ioc_resize_fs(struct file *filp, unsigned long arg)
|
||||||
sizeof(block_count)))
|
sizeof(block_count)))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
return f2fs_resize_fs(sbi, block_count);
|
return f2fs_resize_fs(filp, block_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg)
|
static int f2fs_ioc_enable_verity(struct file *filp, unsigned long arg)
|
||||||
|
@ -3375,18 +3381,29 @@ out:
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int f2fs_get_compress_blocks(struct file *filp, unsigned long arg)
|
static int f2fs_get_compress_blocks(struct inode *inode, __u64 *blocks)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(filp);
|
|
||||||
__u64 blocks;
|
|
||||||
|
|
||||||
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
|
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (!f2fs_compressed_file(inode))
|
if (!f2fs_compressed_file(inode))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
blocks = atomic_read(&F2FS_I(inode)->i_compr_blocks);
|
*blocks = atomic_read(&F2FS_I(inode)->i_compr_blocks);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int f2fs_ioc_get_compress_blocks(struct file *filp, unsigned long arg)
|
||||||
|
{
|
||||||
|
struct inode *inode = file_inode(filp);
|
||||||
|
__u64 blocks;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = f2fs_get_compress_blocks(inode, &blocks);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
return put_user(blocks, (u64 __user *)arg);
|
return put_user(blocks, (u64 __user *)arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3455,7 +3472,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg)
|
||||||
int ret;
|
int ret;
|
||||||
int writecount;
|
int writecount;
|
||||||
|
|
||||||
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
|
if (!f2fs_sb_has_compression(sbi))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (!f2fs_compressed_file(inode))
|
if (!f2fs_compressed_file(inode))
|
||||||
|
@ -3468,7 +3485,7 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg)
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
f2fs_balance_fs(F2FS_I_SB(inode), true);
|
f2fs_balance_fs(sbi, true);
|
||||||
|
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
|
|
||||||
|
@ -3488,13 +3505,15 @@ static int f2fs_release_compress_blocks(struct file *filp, unsigned long arg)
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
if (!atomic_read(&F2FS_I(inode)->i_compr_blocks)) {
|
||||||
|
ret = -EPERM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
set_inode_flag(inode, FI_COMPRESS_RELEASED);
|
set_inode_flag(inode, FI_COMPRESS_RELEASED);
|
||||||
inode->i_ctime = current_time(inode);
|
inode->i_ctime = current_time(inode);
|
||||||
f2fs_mark_inode_dirty_sync(inode, true);
|
f2fs_mark_inode_dirty_sync(inode, true);
|
||||||
|
|
||||||
if (!atomic_read(&F2FS_I(inode)->i_compr_blocks))
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
f2fs_down_write(&F2FS_I(inode)->i_gc_rwsem[WRITE]);
|
||||||
filemap_invalidate_lock(inode->i_mapping);
|
filemap_invalidate_lock(inode->i_mapping);
|
||||||
|
|
||||||
|
@ -3625,7 +3644,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg)
|
||||||
unsigned int reserved_blocks = 0;
|
unsigned int reserved_blocks = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!f2fs_sb_has_compression(F2FS_I_SB(inode)))
|
if (!f2fs_sb_has_compression(sbi))
|
||||||
return -EOPNOTSUPP;
|
return -EOPNOTSUPP;
|
||||||
|
|
||||||
if (!f2fs_compressed_file(inode))
|
if (!f2fs_compressed_file(inode))
|
||||||
|
@ -3641,7 +3660,7 @@ static int f2fs_reserve_compress_blocks(struct file *filp, unsigned long arg)
|
||||||
if (atomic_read(&F2FS_I(inode)->i_compr_blocks))
|
if (atomic_read(&F2FS_I(inode)->i_compr_blocks))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
f2fs_balance_fs(F2FS_I_SB(inode), true);
|
f2fs_balance_fs(sbi, true);
|
||||||
|
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
|
|
||||||
|
@ -4035,7 +4054,7 @@ static int f2fs_ioc_decompress_file(struct file *filp)
|
||||||
if (!f2fs_compressed_file(inode))
|
if (!f2fs_compressed_file(inode))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
f2fs_balance_fs(F2FS_I_SB(inode), true);
|
f2fs_balance_fs(sbi, true);
|
||||||
|
|
||||||
file_start_write(filp);
|
file_start_write(filp);
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
|
@ -4110,7 +4129,7 @@ static int f2fs_ioc_compress_file(struct file *filp)
|
||||||
if (!f2fs_compressed_file(inode))
|
if (!f2fs_compressed_file(inode))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
f2fs_balance_fs(F2FS_I_SB(inode), true);
|
f2fs_balance_fs(sbi, true);
|
||||||
|
|
||||||
file_start_write(filp);
|
file_start_write(filp);
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
|
@ -4238,7 +4257,7 @@ static long __f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||||
case FS_IOC_SETFSLABEL:
|
case FS_IOC_SETFSLABEL:
|
||||||
return f2fs_ioc_setfslabel(filp, arg);
|
return f2fs_ioc_setfslabel(filp, arg);
|
||||||
case F2FS_IOC_GET_COMPRESS_BLOCKS:
|
case F2FS_IOC_GET_COMPRESS_BLOCKS:
|
||||||
return f2fs_get_compress_blocks(filp, arg);
|
return f2fs_ioc_get_compress_blocks(filp, arg);
|
||||||
case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
|
case F2FS_IOC_RELEASE_COMPRESS_BLOCKS:
|
||||||
return f2fs_release_compress_blocks(filp, arg);
|
return f2fs_release_compress_blocks(filp, arg);
|
||||||
case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
|
case F2FS_IOC_RESERVE_COMPRESS_BLOCKS:
|
||||||
|
|
43
fs/f2fs/gc.c
43
fs/f2fs/gc.c
|
@ -59,7 +59,7 @@ static int gc_thread_func(void *data)
|
||||||
if (gc_th->gc_wake)
|
if (gc_th->gc_wake)
|
||||||
gc_th->gc_wake = false;
|
gc_th->gc_wake = false;
|
||||||
|
|
||||||
if (try_to_freeze()) {
|
if (try_to_freeze() || f2fs_readonly(sbi->sb)) {
|
||||||
stat_other_skip_bggc_count(sbi);
|
stat_other_skip_bggc_count(sbi);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1797,7 +1797,7 @@ int f2fs_gc(struct f2fs_sb_info *sbi, struct f2fs_gc_control *gc_control)
|
||||||
{
|
{
|
||||||
int gc_type = gc_control->init_gc_type;
|
int gc_type = gc_control->init_gc_type;
|
||||||
unsigned int segno = gc_control->victim_segno;
|
unsigned int segno = gc_control->victim_segno;
|
||||||
int sec_freed = 0, seg_freed = 0, total_freed = 0;
|
int sec_freed = 0, seg_freed = 0, total_freed = 0, total_sec_freed = 0;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
struct cp_control cpc;
|
struct cp_control cpc;
|
||||||
struct gc_inode_list gc_list = {
|
struct gc_inode_list gc_list = {
|
||||||
|
@ -1842,6 +1842,8 @@ gc_more:
|
||||||
ret = f2fs_write_checkpoint(sbi, &cpc);
|
ret = f2fs_write_checkpoint(sbi, &cpc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto stop;
|
goto stop;
|
||||||
|
/* Reset due to checkpoint */
|
||||||
|
sec_freed = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1866,15 +1868,17 @@ retry:
|
||||||
gc_control->should_migrate_blocks);
|
gc_control->should_migrate_blocks);
|
||||||
total_freed += seg_freed;
|
total_freed += seg_freed;
|
||||||
|
|
||||||
if (seg_freed == f2fs_usable_segs_in_sec(sbi, segno))
|
if (seg_freed == f2fs_usable_segs_in_sec(sbi, segno)) {
|
||||||
sec_freed++;
|
sec_freed++;
|
||||||
|
total_sec_freed++;
|
||||||
|
}
|
||||||
|
|
||||||
if (gc_type == FG_GC) {
|
if (gc_type == FG_GC) {
|
||||||
sbi->cur_victim_sec = NULL_SEGNO;
|
sbi->cur_victim_sec = NULL_SEGNO;
|
||||||
|
|
||||||
if (has_enough_free_secs(sbi, sec_freed, 0)) {
|
if (has_enough_free_secs(sbi, sec_freed, 0)) {
|
||||||
if (!gc_control->no_bg_gc &&
|
if (!gc_control->no_bg_gc &&
|
||||||
sec_freed < gc_control->nr_free_secs)
|
total_sec_freed < gc_control->nr_free_secs)
|
||||||
goto go_gc_more;
|
goto go_gc_more;
|
||||||
goto stop;
|
goto stop;
|
||||||
}
|
}
|
||||||
|
@ -1901,6 +1905,8 @@ retry:
|
||||||
ret = f2fs_write_checkpoint(sbi, &cpc);
|
ret = f2fs_write_checkpoint(sbi, &cpc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto stop;
|
goto stop;
|
||||||
|
/* Reset due to checkpoint */
|
||||||
|
sec_freed = 0;
|
||||||
}
|
}
|
||||||
go_gc_more:
|
go_gc_more:
|
||||||
segno = NULL_SEGNO;
|
segno = NULL_SEGNO;
|
||||||
|
@ -1913,7 +1919,7 @@ stop:
|
||||||
if (gc_type == FG_GC)
|
if (gc_type == FG_GC)
|
||||||
f2fs_unpin_all_sections(sbi, true);
|
f2fs_unpin_all_sections(sbi, true);
|
||||||
|
|
||||||
trace_f2fs_gc_end(sbi->sb, ret, total_freed, sec_freed,
|
trace_f2fs_gc_end(sbi->sb, ret, total_freed, total_sec_freed,
|
||||||
get_pages(sbi, F2FS_DIRTY_NODES),
|
get_pages(sbi, F2FS_DIRTY_NODES),
|
||||||
get_pages(sbi, F2FS_DIRTY_DENTS),
|
get_pages(sbi, F2FS_DIRTY_DENTS),
|
||||||
get_pages(sbi, F2FS_DIRTY_IMETA),
|
get_pages(sbi, F2FS_DIRTY_IMETA),
|
||||||
|
@ -1927,7 +1933,7 @@ stop:
|
||||||
put_gc_inode(&gc_list);
|
put_gc_inode(&gc_list);
|
||||||
|
|
||||||
if (gc_control->err_gc_skipped && !ret)
|
if (gc_control->err_gc_skipped && !ret)
|
||||||
ret = sec_freed ? 0 : -EAGAIN;
|
ret = total_sec_freed ? 0 : -EAGAIN;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2099,8 +2105,9 @@ static void update_fs_metadata(struct f2fs_sb_info *sbi, int secs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count)
|
int f2fs_resize_fs(struct file *filp, __u64 block_count)
|
||||||
{
|
{
|
||||||
|
struct f2fs_sb_info *sbi = F2FS_I_SB(file_inode(filp));
|
||||||
__u64 old_block_count, shrunk_blocks;
|
__u64 old_block_count, shrunk_blocks;
|
||||||
struct cp_control cpc = { CP_RESIZE, 0, 0, 0 };
|
struct cp_control cpc = { CP_RESIZE, 0, 0, 0 };
|
||||||
unsigned int secs;
|
unsigned int secs;
|
||||||
|
@ -2138,12 +2145,18 @@ int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = mnt_want_write_file(filp);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
shrunk_blocks = old_block_count - block_count;
|
shrunk_blocks = old_block_count - block_count;
|
||||||
secs = div_u64(shrunk_blocks, BLKS_PER_SEC(sbi));
|
secs = div_u64(shrunk_blocks, BLKS_PER_SEC(sbi));
|
||||||
|
|
||||||
/* stop other GC */
|
/* stop other GC */
|
||||||
if (!f2fs_down_write_trylock(&sbi->gc_lock))
|
if (!f2fs_down_write_trylock(&sbi->gc_lock)) {
|
||||||
return -EAGAIN;
|
err = -EAGAIN;
|
||||||
|
goto out_drop_write;
|
||||||
|
}
|
||||||
|
|
||||||
/* stop CP to protect MAIN_SEC in free_segment_range */
|
/* stop CP to protect MAIN_SEC in free_segment_range */
|
||||||
f2fs_lock_op(sbi);
|
f2fs_lock_op(sbi);
|
||||||
|
@ -2163,10 +2176,20 @@ int f2fs_resize_fs(struct f2fs_sb_info *sbi, __u64 block_count)
|
||||||
out_unlock:
|
out_unlock:
|
||||||
f2fs_unlock_op(sbi);
|
f2fs_unlock_op(sbi);
|
||||||
f2fs_up_write(&sbi->gc_lock);
|
f2fs_up_write(&sbi->gc_lock);
|
||||||
|
out_drop_write:
|
||||||
|
mnt_drop_write_file(filp);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
freeze_super(sbi->sb);
|
err = freeze_super(sbi->sb);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
if (f2fs_readonly(sbi->sb)) {
|
||||||
|
thaw_super(sbi->sb);
|
||||||
|
return -EROFS;
|
||||||
|
}
|
||||||
|
|
||||||
f2fs_down_write(&sbi->gc_lock);
|
f2fs_down_write(&sbi->gc_lock);
|
||||||
f2fs_down_write(&sbi->cp_global_sem);
|
f2fs_down_write(&sbi->cp_global_sem);
|
||||||
|
|
||||||
|
|
207
fs/f2fs/inode.c
207
fs/f2fs/inode.c
|
@ -10,6 +10,8 @@
|
||||||
#include <linux/buffer_head.h>
|
#include <linux/buffer_head.h>
|
||||||
#include <linux/writeback.h>
|
#include <linux/writeback.h>
|
||||||
#include <linux/sched/mm.h>
|
#include <linux/sched/mm.h>
|
||||||
|
#include <linux/lz4.h>
|
||||||
|
#include <linux/zstd.h>
|
||||||
|
|
||||||
#include "f2fs.h"
|
#include "f2fs.h"
|
||||||
#include "node.h"
|
#include "node.h"
|
||||||
|
@ -202,6 +204,80 @@ void f2fs_inode_chksum_set(struct f2fs_sb_info *sbi, struct page *page)
|
||||||
ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page));
|
ri->i_inode_checksum = cpu_to_le32(f2fs_inode_chksum(sbi, page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool sanity_check_compress_inode(struct inode *inode,
|
||||||
|
struct f2fs_inode *ri)
|
||||||
|
{
|
||||||
|
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||||
|
unsigned char clevel;
|
||||||
|
|
||||||
|
if (ri->i_compress_algorithm >= COMPRESS_MAX) {
|
||||||
|
f2fs_warn(sbi,
|
||||||
|
"%s: inode (ino=%lx) has unsupported compress algorithm: %u, run fsck to fix",
|
||||||
|
__func__, inode->i_ino, ri->i_compress_algorithm);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (le64_to_cpu(ri->i_compr_blocks) >
|
||||||
|
SECTOR_TO_BLOCK(inode->i_blocks)) {
|
||||||
|
f2fs_warn(sbi,
|
||||||
|
"%s: inode (ino=%lx) has inconsistent i_compr_blocks:%llu, i_blocks:%llu, run fsck to fix",
|
||||||
|
__func__, inode->i_ino, le64_to_cpu(ri->i_compr_blocks),
|
||||||
|
SECTOR_TO_BLOCK(inode->i_blocks));
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (ri->i_log_cluster_size < MIN_COMPRESS_LOG_SIZE ||
|
||||||
|
ri->i_log_cluster_size > MAX_COMPRESS_LOG_SIZE) {
|
||||||
|
f2fs_warn(sbi,
|
||||||
|
"%s: inode (ino=%lx) has unsupported log cluster size: %u, run fsck to fix",
|
||||||
|
__func__, inode->i_ino, ri->i_log_cluster_size);
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
clevel = le16_to_cpu(ri->i_compress_flag) >>
|
||||||
|
COMPRESS_LEVEL_OFFSET;
|
||||||
|
switch (ri->i_compress_algorithm) {
|
||||||
|
case COMPRESS_LZO:
|
||||||
|
#ifdef CONFIG_F2FS_FS_LZO
|
||||||
|
if (clevel)
|
||||||
|
goto err_level;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case COMPRESS_LZORLE:
|
||||||
|
#ifdef CONFIG_F2FS_FS_LZORLE
|
||||||
|
if (clevel)
|
||||||
|
goto err_level;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case COMPRESS_LZ4:
|
||||||
|
#ifdef CONFIG_F2FS_FS_LZ4
|
||||||
|
#ifdef CONFIG_F2FS_FS_LZ4HC
|
||||||
|
if (clevel &&
|
||||||
|
(clevel < LZ4HC_MIN_CLEVEL || clevel > LZ4HC_MAX_CLEVEL))
|
||||||
|
goto err_level;
|
||||||
|
#else
|
||||||
|
if (clevel)
|
||||||
|
goto err_level;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
case COMPRESS_ZSTD:
|
||||||
|
#ifdef CONFIG_F2FS_FS_ZSTD
|
||||||
|
if (clevel < zstd_min_clevel() || clevel > zstd_max_clevel())
|
||||||
|
goto err_level;
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto err_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
err_level:
|
||||||
|
f2fs_warn(sbi, "%s: inode (ino=%lx) has unsupported compress level: %u, run fsck to fix",
|
||||||
|
__func__, inode->i_ino, clevel);
|
||||||
|
err:
|
||||||
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
static bool sanity_check_inode(struct inode *inode, struct page *node_page)
|
static bool sanity_check_inode(struct inode *inode, struct page *node_page)
|
||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||||
|
@ -225,41 +301,77 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f2fs_sb_has_flexible_inline_xattr(sbi)
|
if (f2fs_has_extra_attr(inode)) {
|
||||||
&& !f2fs_has_extra_attr(inode)) {
|
if (!f2fs_sb_has_extra_attr(sbi)) {
|
||||||
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
|
f2fs_warn(sbi, "%s: inode (ino=%lx) is with extra_attr, but extra_attr feature is off",
|
||||||
|
__func__, inode->i_ino);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (fi->i_extra_isize > F2FS_TOTAL_EXTRA_ATTR_SIZE ||
|
||||||
|
fi->i_extra_isize < F2FS_MIN_EXTRA_ATTR_SIZE ||
|
||||||
|
fi->i_extra_isize % sizeof(__le32)) {
|
||||||
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
|
f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_extra_isize: %d, max: %zu",
|
||||||
|
__func__, inode->i_ino, fi->i_extra_isize,
|
||||||
|
F2FS_TOTAL_EXTRA_ATTR_SIZE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (f2fs_sb_has_flexible_inline_xattr(sbi) &&
|
||||||
|
f2fs_has_inline_xattr(inode) &&
|
||||||
|
(!fi->i_inline_xattr_size ||
|
||||||
|
fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) {
|
||||||
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
|
f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, max: %zu",
|
||||||
|
__func__, inode->i_ino, fi->i_inline_xattr_size,
|
||||||
|
MAX_INLINE_XATTR_SIZE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (f2fs_sb_has_compression(sbi) &&
|
||||||
|
fi->i_flags & F2FS_COMPR_FL &&
|
||||||
|
F2FS_FITS_IN_INODE(ri, fi->i_extra_isize,
|
||||||
|
i_compress_flag)) {
|
||||||
|
if (!sanity_check_compress_inode(inode, ri))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (f2fs_sb_has_flexible_inline_xattr(sbi)) {
|
||||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
f2fs_warn(sbi, "%s: corrupted inode ino=%lx, run fsck to fix.",
|
f2fs_warn(sbi, "%s: corrupted inode ino=%lx, run fsck to fix.",
|
||||||
__func__, inode->i_ino);
|
__func__, inode->i_ino);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f2fs_has_extra_attr(inode) &&
|
if (!f2fs_sb_has_extra_attr(sbi)) {
|
||||||
!f2fs_sb_has_extra_attr(sbi)) {
|
if (f2fs_sb_has_project_quota(sbi)) {
|
||||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
f2fs_warn(sbi, "%s: inode (ino=%lx) is with extra_attr, but extra_attr feature is off",
|
f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.",
|
||||||
__func__, inode->i_ino);
|
__func__, inode->i_ino, F2FS_FEATURE_PRJQUOTA);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (f2fs_sb_has_inode_chksum(sbi)) {
|
||||||
if (fi->i_extra_isize > F2FS_TOTAL_EXTRA_ATTR_SIZE ||
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
fi->i_extra_isize % sizeof(__le32)) {
|
f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.",
|
||||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
__func__, inode->i_ino, F2FS_FEATURE_INODE_CHKSUM);
|
||||||
f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_extra_isize: %d, max: %zu",
|
return false;
|
||||||
__func__, inode->i_ino, fi->i_extra_isize,
|
}
|
||||||
F2FS_TOTAL_EXTRA_ATTR_SIZE);
|
if (f2fs_sb_has_flexible_inline_xattr(sbi)) {
|
||||||
return false;
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
}
|
f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.",
|
||||||
|
__func__, inode->i_ino, F2FS_FEATURE_FLEXIBLE_INLINE_XATTR);
|
||||||
if (f2fs_has_extra_attr(inode) &&
|
return false;
|
||||||
f2fs_sb_has_flexible_inline_xattr(sbi) &&
|
}
|
||||||
f2fs_has_inline_xattr(inode) &&
|
if (f2fs_sb_has_inode_crtime(sbi)) {
|
||||||
(!fi->i_inline_xattr_size ||
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) {
|
f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.",
|
||||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
__func__, inode->i_ino, F2FS_FEATURE_INODE_CRTIME);
|
||||||
f2fs_warn(sbi, "%s: inode (ino=%lx) has corrupted i_inline_xattr_size: %d, max: %zu",
|
return false;
|
||||||
__func__, inode->i_ino, fi->i_inline_xattr_size,
|
}
|
||||||
MAX_INLINE_XATTR_SIZE);
|
if (f2fs_sb_has_compression(sbi)) {
|
||||||
return false;
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
|
f2fs_warn(sbi, "%s: corrupted inode ino=%lx, wrong feature flag: %u, run fsck to fix.",
|
||||||
|
__func__, inode->i_ino, F2FS_FEATURE_COMPRESSION);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f2fs_sanity_check_inline_data(inode)) {
|
if (f2fs_sanity_check_inline_data(inode)) {
|
||||||
|
@ -283,39 +395,6 @@ static bool sanity_check_inode(struct inode *inode, struct page *node_page)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f2fs_has_extra_attr(inode) && f2fs_sb_has_compression(sbi) &&
|
|
||||||
fi->i_flags & F2FS_COMPR_FL &&
|
|
||||||
F2FS_FITS_IN_INODE(ri, fi->i_extra_isize,
|
|
||||||
i_log_cluster_size)) {
|
|
||||||
if (ri->i_compress_algorithm >= COMPRESS_MAX) {
|
|
||||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
|
||||||
f2fs_warn(sbi, "%s: inode (ino=%lx) has unsupported "
|
|
||||||
"compress algorithm: %u, run fsck to fix",
|
|
||||||
__func__, inode->i_ino,
|
|
||||||
ri->i_compress_algorithm);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (le64_to_cpu(ri->i_compr_blocks) >
|
|
||||||
SECTOR_TO_BLOCK(inode->i_blocks)) {
|
|
||||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
|
||||||
f2fs_warn(sbi, "%s: inode (ino=%lx) has inconsistent "
|
|
||||||
"i_compr_blocks:%llu, i_blocks:%llu, run fsck to fix",
|
|
||||||
__func__, inode->i_ino,
|
|
||||||
le64_to_cpu(ri->i_compr_blocks),
|
|
||||||
SECTOR_TO_BLOCK(inode->i_blocks));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (ri->i_log_cluster_size < MIN_COMPRESS_LOG_SIZE ||
|
|
||||||
ri->i_log_cluster_size > MAX_COMPRESS_LOG_SIZE) {
|
|
||||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
|
||||||
f2fs_warn(sbi, "%s: inode (ino=%lx) has unsupported "
|
|
||||||
"log cluster size: %u, run fsck to fix",
|
|
||||||
__func__, inode->i_ino,
|
|
||||||
ri->i_log_cluster_size);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,7 +521,7 @@ static int do_read_inode(struct inode *inode)
|
||||||
if (f2fs_has_extra_attr(inode) && f2fs_sb_has_compression(sbi) &&
|
if (f2fs_has_extra_attr(inode) && f2fs_sb_has_compression(sbi) &&
|
||||||
(fi->i_flags & F2FS_COMPR_FL)) {
|
(fi->i_flags & F2FS_COMPR_FL)) {
|
||||||
if (F2FS_FITS_IN_INODE(ri, fi->i_extra_isize,
|
if (F2FS_FITS_IN_INODE(ri, fi->i_extra_isize,
|
||||||
i_log_cluster_size)) {
|
i_compress_flag)) {
|
||||||
unsigned short compress_flag;
|
unsigned short compress_flag;
|
||||||
|
|
||||||
atomic_set(&fi->i_compr_blocks,
|
atomic_set(&fi->i_compr_blocks,
|
||||||
|
@ -680,7 +759,7 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page)
|
||||||
|
|
||||||
if (f2fs_sb_has_compression(F2FS_I_SB(inode)) &&
|
if (f2fs_sb_has_compression(F2FS_I_SB(inode)) &&
|
||||||
F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize,
|
F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize,
|
||||||
i_log_cluster_size)) {
|
i_compress_flag)) {
|
||||||
unsigned short compress_flag;
|
unsigned short compress_flag;
|
||||||
|
|
||||||
ri->i_compr_blocks =
|
ri->i_compr_blocks =
|
||||||
|
|
|
@ -80,6 +80,7 @@ int __maybe_unused iostat_info_seq_show(struct seq_file *seq, void *offset)
|
||||||
seq_puts(seq, "[OTHER]\n");
|
seq_puts(seq, "[OTHER]\n");
|
||||||
IOSTAT_INFO_SHOW("fs discard", FS_DISCARD_IO);
|
IOSTAT_INFO_SHOW("fs discard", FS_DISCARD_IO);
|
||||||
IOSTAT_INFO_SHOW("fs flush", FS_FLUSH_IO);
|
IOSTAT_INFO_SHOW("fs flush", FS_FLUSH_IO);
|
||||||
|
IOSTAT_INFO_SHOW("fs zone reset", FS_ZONE_RESET_IO);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
#include <trace/events/f2fs.h>
|
#include <trace/events/f2fs.h>
|
||||||
|
|
||||||
static inline bool is_extension_exist(const unsigned char *s, const char *sub,
|
static inline bool is_extension_exist(const unsigned char *s, const char *sub,
|
||||||
bool tmp_ext)
|
bool tmp_ext, bool tmp_dot)
|
||||||
{
|
{
|
||||||
size_t slen = strlen(s);
|
size_t slen = strlen(s);
|
||||||
size_t sublen = strlen(sub);
|
size_t sublen = strlen(sub);
|
||||||
|
@ -49,13 +49,27 @@ static inline bool is_extension_exist(const unsigned char *s, const char *sub,
|
||||||
for (i = 1; i < slen - sublen; i++) {
|
for (i = 1; i < slen - sublen; i++) {
|
||||||
if (s[i] != '.')
|
if (s[i] != '.')
|
||||||
continue;
|
continue;
|
||||||
if (!strncasecmp(s + i + 1, sub, sublen))
|
if (!strncasecmp(s + i + 1, sub, sublen)) {
|
||||||
return true;
|
if (!tmp_dot)
|
||||||
|
return true;
|
||||||
|
if (i == slen - sublen - 1 || s[i + 1 + sublen] == '.')
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool is_temperature_extension(const unsigned char *s, const char *sub)
|
||||||
|
{
|
||||||
|
return is_extension_exist(s, sub, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool is_compress_extension(const unsigned char *s, const char *sub)
|
||||||
|
{
|
||||||
|
return is_extension_exist(s, sub, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
int f2fs_update_extension_list(struct f2fs_sb_info *sbi, const char *name,
|
int f2fs_update_extension_list(struct f2fs_sb_info *sbi, const char *name,
|
||||||
bool hot, bool set)
|
bool hot, bool set)
|
||||||
{
|
{
|
||||||
|
@ -148,7 +162,7 @@ static void set_compress_new_inode(struct f2fs_sb_info *sbi, struct inode *dir,
|
||||||
cold_count = le32_to_cpu(sbi->raw_super->extension_count);
|
cold_count = le32_to_cpu(sbi->raw_super->extension_count);
|
||||||
hot_count = sbi->raw_super->hot_ext_count;
|
hot_count = sbi->raw_super->hot_ext_count;
|
||||||
for (i = cold_count; i < cold_count + hot_count; i++)
|
for (i = cold_count; i < cold_count + hot_count; i++)
|
||||||
if (is_extension_exist(name, extlist[i], false))
|
if (is_temperature_extension(name, extlist[i]))
|
||||||
break;
|
break;
|
||||||
f2fs_up_read(&sbi->sb_lock);
|
f2fs_up_read(&sbi->sb_lock);
|
||||||
if (i < (cold_count + hot_count))
|
if (i < (cold_count + hot_count))
|
||||||
|
@ -156,12 +170,12 @@ static void set_compress_new_inode(struct f2fs_sb_info *sbi, struct inode *dir,
|
||||||
|
|
||||||
/* Don't compress unallowed extension. */
|
/* Don't compress unallowed extension. */
|
||||||
for (i = 0; i < noext_cnt; i++)
|
for (i = 0; i < noext_cnt; i++)
|
||||||
if (is_extension_exist(name, noext[i], false))
|
if (is_compress_extension(name, noext[i]))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* Compress wanting extension. */
|
/* Compress wanting extension. */
|
||||||
for (i = 0; i < ext_cnt; i++) {
|
for (i = 0; i < ext_cnt; i++) {
|
||||||
if (is_extension_exist(name, ext[i], false)) {
|
if (is_compress_extension(name, ext[i])) {
|
||||||
set_compress_context(inode);
|
set_compress_context(inode);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -189,7 +203,7 @@ static void set_file_temperature(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||||
cold_count = le32_to_cpu(sbi->raw_super->extension_count);
|
cold_count = le32_to_cpu(sbi->raw_super->extension_count);
|
||||||
hot_count = sbi->raw_super->hot_ext_count;
|
hot_count = sbi->raw_super->hot_ext_count;
|
||||||
for (i = 0; i < cold_count + hot_count; i++)
|
for (i = 0; i < cold_count + hot_count; i++)
|
||||||
if (is_extension_exist(name, extlist[i], true))
|
if (is_temperature_extension(name, extlist[i]))
|
||||||
break;
|
break;
|
||||||
f2fs_up_read(&sbi->sb_lock);
|
f2fs_up_read(&sbi->sb_lock);
|
||||||
|
|
||||||
|
@ -576,8 +590,8 @@ out_splice:
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
new = d_splice_alias(inode, dentry);
|
new = d_splice_alias(inode, dentry);
|
||||||
err = PTR_ERR_OR_ZERO(new);
|
trace_f2fs_lookup_end(dir, !IS_ERR_OR_NULL(new) ? new : dentry,
|
||||||
trace_f2fs_lookup_end(dir, dentry, ino, !new ? -ENOENT : err);
|
ino, IS_ERR(new) ? PTR_ERR(new) : err);
|
||||||
return new;
|
return new;
|
||||||
out_iput:
|
out_iput:
|
||||||
iput(inode);
|
iput(inode);
|
||||||
|
|
|
@ -925,6 +925,7 @@ static int truncate_node(struct dnode_of_data *dn)
|
||||||
|
|
||||||
static int truncate_dnode(struct dnode_of_data *dn)
|
static int truncate_dnode(struct dnode_of_data *dn)
|
||||||
{
|
{
|
||||||
|
struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);
|
||||||
struct page *page;
|
struct page *page;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -932,19 +933,30 @@ static int truncate_dnode(struct dnode_of_data *dn)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
/* get direct node */
|
/* get direct node */
|
||||||
page = f2fs_get_node_page(F2FS_I_SB(dn->inode), dn->nid);
|
page = f2fs_get_node_page(sbi, dn->nid);
|
||||||
if (PTR_ERR(page) == -ENOENT)
|
if (PTR_ERR(page) == -ENOENT)
|
||||||
return 1;
|
return 1;
|
||||||
else if (IS_ERR(page))
|
else if (IS_ERR(page))
|
||||||
return PTR_ERR(page);
|
return PTR_ERR(page);
|
||||||
|
|
||||||
|
if (IS_INODE(page) || ino_of_node(page) != dn->inode->i_ino) {
|
||||||
|
f2fs_err(sbi, "incorrect node reference, ino: %lu, nid: %u, ino_of_node: %u",
|
||||||
|
dn->inode->i_ino, dn->nid, ino_of_node(page));
|
||||||
|
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||||
|
f2fs_handle_error(sbi, ERROR_INVALID_NODE_REFERENCE);
|
||||||
|
f2fs_put_page(page, 1);
|
||||||
|
return -EFSCORRUPTED;
|
||||||
|
}
|
||||||
|
|
||||||
/* Make dnode_of_data for parameter */
|
/* Make dnode_of_data for parameter */
|
||||||
dn->node_page = page;
|
dn->node_page = page;
|
||||||
dn->ofs_in_node = 0;
|
dn->ofs_in_node = 0;
|
||||||
f2fs_truncate_data_blocks(dn);
|
f2fs_truncate_data_blocks_range(dn, ADDRS_PER_BLOCK(dn->inode));
|
||||||
err = truncate_node(dn);
|
err = truncate_node(dn);
|
||||||
if (err)
|
if (err) {
|
||||||
|
f2fs_put_page(page, 1);
|
||||||
return err;
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1596,6 +1608,9 @@ static int __write_node_page(struct page *page, bool atomic, bool *submitted,
|
||||||
trace_f2fs_writepage(page, NODE);
|
trace_f2fs_writepage(page, NODE);
|
||||||
|
|
||||||
if (unlikely(f2fs_cp_error(sbi))) {
|
if (unlikely(f2fs_cp_error(sbi))) {
|
||||||
|
/* keep node pages in remount-ro mode */
|
||||||
|
if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_READONLY)
|
||||||
|
goto redirty_out;
|
||||||
ClearPageUptodate(page);
|
ClearPageUptodate(page);
|
||||||
dec_page_count(sbi, F2FS_DIRTY_NODES);
|
dec_page_count(sbi, F2FS_DIRTY_NODES);
|
||||||
unlock_page(page);
|
unlock_page(page);
|
||||||
|
@ -2063,7 +2078,6 @@ int f2fs_wait_on_node_pages_writeback(struct f2fs_sb_info *sbi,
|
||||||
struct list_head *head = &sbi->fsync_node_list;
|
struct list_head *head = &sbi->fsync_node_list;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
unsigned int cur_seq_id = 0;
|
unsigned int cur_seq_id = 0;
|
||||||
int ret2, ret = 0;
|
|
||||||
|
|
||||||
while (seq_id && cur_seq_id < seq_id) {
|
while (seq_id && cur_seq_id < seq_id) {
|
||||||
spin_lock_irqsave(&sbi->fsync_node_lock, flags);
|
spin_lock_irqsave(&sbi->fsync_node_lock, flags);
|
||||||
|
@ -2084,16 +2098,9 @@ int f2fs_wait_on_node_pages_writeback(struct f2fs_sb_info *sbi,
|
||||||
f2fs_wait_on_page_writeback(page, NODE, true, false);
|
f2fs_wait_on_page_writeback(page, NODE, true, false);
|
||||||
|
|
||||||
put_page(page);
|
put_page(page);
|
||||||
|
|
||||||
if (ret)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret2 = filemap_check_errors(NODE_MAPPING(sbi));
|
return filemap_check_errors(NODE_MAPPING(sbi));
|
||||||
if (!ret)
|
|
||||||
ret = ret2;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int f2fs_write_node_pages(struct address_space *mapping,
|
static int f2fs_write_node_pages(struct address_space *mapping,
|
||||||
|
@ -3065,7 +3072,7 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||||
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);
|
||||||
struct f2fs_journal *journal = curseg->journal;
|
struct f2fs_journal *journal = curseg->journal;
|
||||||
struct nat_entry_set *setvec[SETVEC_SIZE];
|
struct nat_entry_set *setvec[NAT_VEC_SIZE];
|
||||||
struct nat_entry_set *set, *tmp;
|
struct nat_entry_set *set, *tmp;
|
||||||
unsigned int found;
|
unsigned int found;
|
||||||
nid_t set_idx = 0;
|
nid_t set_idx = 0;
|
||||||
|
@ -3098,7 +3105,7 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||||
remove_nats_in_journal(sbi);
|
remove_nats_in_journal(sbi);
|
||||||
|
|
||||||
while ((found = __gang_lookup_nat_set(nm_i,
|
while ((found = __gang_lookup_nat_set(nm_i,
|
||||||
set_idx, SETVEC_SIZE, setvec))) {
|
set_idx, NAT_VEC_SIZE, setvec))) {
|
||||||
unsigned idx;
|
unsigned idx;
|
||||||
|
|
||||||
set_idx = setvec[found - 1]->set + 1;
|
set_idx = setvec[found - 1]->set + 1;
|
||||||
|
@ -3319,8 +3326,9 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
||||||
struct free_nid *i, *next_i;
|
struct free_nid *i, *next_i;
|
||||||
struct nat_entry *natvec[NATVEC_SIZE];
|
void *vec[NAT_VEC_SIZE];
|
||||||
struct nat_entry_set *setvec[SETVEC_SIZE];
|
struct nat_entry **natvec = (struct nat_entry **)vec;
|
||||||
|
struct nat_entry_set **setvec = (struct nat_entry_set **)vec;
|
||||||
nid_t nid = 0;
|
nid_t nid = 0;
|
||||||
unsigned int found;
|
unsigned int found;
|
||||||
|
|
||||||
|
@ -3343,7 +3351,7 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi)
|
||||||
/* destroy nat cache */
|
/* destroy nat cache */
|
||||||
f2fs_down_write(&nm_i->nat_tree_lock);
|
f2fs_down_write(&nm_i->nat_tree_lock);
|
||||||
while ((found = __gang_lookup_nat_cache(nm_i,
|
while ((found = __gang_lookup_nat_cache(nm_i,
|
||||||
nid, NATVEC_SIZE, natvec))) {
|
nid, NAT_VEC_SIZE, natvec))) {
|
||||||
unsigned idx;
|
unsigned idx;
|
||||||
|
|
||||||
nid = nat_get_nid(natvec[found - 1]) + 1;
|
nid = nat_get_nid(natvec[found - 1]) + 1;
|
||||||
|
@ -3359,8 +3367,9 @@ void f2fs_destroy_node_manager(struct f2fs_sb_info *sbi)
|
||||||
|
|
||||||
/* destroy nat set cache */
|
/* destroy nat set cache */
|
||||||
nid = 0;
|
nid = 0;
|
||||||
|
memset(vec, 0, sizeof(void *) * NAT_VEC_SIZE);
|
||||||
while ((found = __gang_lookup_nat_set(nm_i,
|
while ((found = __gang_lookup_nat_set(nm_i,
|
||||||
nid, SETVEC_SIZE, setvec))) {
|
nid, NAT_VEC_SIZE, setvec))) {
|
||||||
unsigned idx;
|
unsigned idx;
|
||||||
|
|
||||||
nid = setvec[found - 1]->set + 1;
|
nid = setvec[found - 1]->set + 1;
|
||||||
|
|
|
@ -35,8 +35,7 @@
|
||||||
#define DEF_RF_NODE_BLOCKS 0
|
#define DEF_RF_NODE_BLOCKS 0
|
||||||
|
|
||||||
/* vector size for gang look-up from nat cache that consists of radix tree */
|
/* vector size for gang look-up from nat cache that consists of radix tree */
|
||||||
#define NATVEC_SIZE 64
|
#define NAT_VEC_SIZE 32
|
||||||
#define SETVEC_SIZE 32
|
|
||||||
|
|
||||||
/* return value for read_node_page */
|
/* return value for read_node_page */
|
||||||
#define LOCKED_PAGE 1
|
#define LOCKED_PAGE 1
|
||||||
|
|
|
@ -360,21 +360,63 @@ static unsigned int adjust_por_ra_blocks(struct f2fs_sb_info *sbi,
|
||||||
return ra_blocks;
|
return ra_blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Detect looped node chain with Floyd's cycle detection algorithm. */
|
||||||
|
static int sanity_check_node_chain(struct f2fs_sb_info *sbi, block_t blkaddr,
|
||||||
|
block_t *blkaddr_fast, bool *is_detecting)
|
||||||
|
{
|
||||||
|
unsigned int ra_blocks = RECOVERY_MAX_RA_BLOCKS;
|
||||||
|
struct page *page = NULL;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!*is_detecting)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++) {
|
||||||
|
if (!f2fs_is_valid_blkaddr(sbi, *blkaddr_fast, META_POR)) {
|
||||||
|
*is_detecting = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
page = f2fs_get_tmp_page(sbi, *blkaddr_fast);
|
||||||
|
if (IS_ERR(page))
|
||||||
|
return PTR_ERR(page);
|
||||||
|
|
||||||
|
if (!is_recoverable_dnode(page)) {
|
||||||
|
f2fs_put_page(page, 1);
|
||||||
|
*is_detecting = false;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ra_blocks = adjust_por_ra_blocks(sbi, ra_blocks, *blkaddr_fast,
|
||||||
|
next_blkaddr_of_node(page));
|
||||||
|
|
||||||
|
*blkaddr_fast = next_blkaddr_of_node(page);
|
||||||
|
f2fs_put_page(page, 1);
|
||||||
|
|
||||||
|
f2fs_ra_meta_pages_cond(sbi, *blkaddr_fast, ra_blocks);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*blkaddr_fast == blkaddr) {
|
||||||
|
f2fs_notice(sbi, "%s: Detect looped node chain on blkaddr:%u."
|
||||||
|
" Run fsck to fix it.", __func__, blkaddr);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
|
static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
|
||||||
bool check_only)
|
bool check_only)
|
||||||
{
|
{
|
||||||
struct curseg_info *curseg;
|
struct curseg_info *curseg;
|
||||||
struct page *page = NULL;
|
struct page *page = NULL;
|
||||||
block_t blkaddr;
|
block_t blkaddr, blkaddr_fast;
|
||||||
unsigned int loop_cnt = 0;
|
bool is_detecting = true;
|
||||||
unsigned int ra_blocks = RECOVERY_MAX_RA_BLOCKS;
|
|
||||||
unsigned int free_blocks = MAIN_SEGS(sbi) * sbi->blocks_per_seg -
|
|
||||||
valid_user_blocks(sbi);
|
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
/* get node pages in the current segment */
|
/* get node pages in the current segment */
|
||||||
curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
|
curseg = CURSEG_I(sbi, CURSEG_WARM_NODE);
|
||||||
blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
|
blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);
|
||||||
|
blkaddr_fast = blkaddr;
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
struct fsync_inode_entry *entry;
|
struct fsync_inode_entry *entry;
|
||||||
|
@ -418,10 +460,8 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
|
||||||
quota_inode);
|
quota_inode);
|
||||||
if (IS_ERR(entry)) {
|
if (IS_ERR(entry)) {
|
||||||
err = PTR_ERR(entry);
|
err = PTR_ERR(entry);
|
||||||
if (err == -ENOENT) {
|
if (err == -ENOENT)
|
||||||
err = 0;
|
|
||||||
goto next;
|
goto next;
|
||||||
}
|
|
||||||
f2fs_put_page(page, 1);
|
f2fs_put_page(page, 1);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -431,25 +471,14 @@ static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head,
|
||||||
if (IS_INODE(page) && is_dent_dnode(page))
|
if (IS_INODE(page) && is_dent_dnode(page))
|
||||||
entry->last_dentry = blkaddr;
|
entry->last_dentry = blkaddr;
|
||||||
next:
|
next:
|
||||||
/* sanity check in order to detect looped node chain */
|
|
||||||
if (++loop_cnt >= free_blocks ||
|
|
||||||
blkaddr == next_blkaddr_of_node(page)) {
|
|
||||||
f2fs_notice(sbi, "%s: detect looped node chain, blkaddr:%u, next:%u",
|
|
||||||
__func__, blkaddr,
|
|
||||||
next_blkaddr_of_node(page));
|
|
||||||
f2fs_put_page(page, 1);
|
|
||||||
err = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ra_blocks = adjust_por_ra_blocks(sbi, ra_blocks, blkaddr,
|
|
||||||
next_blkaddr_of_node(page));
|
|
||||||
|
|
||||||
/* check next segment */
|
/* check next segment */
|
||||||
blkaddr = next_blkaddr_of_node(page);
|
blkaddr = next_blkaddr_of_node(page);
|
||||||
f2fs_put_page(page, 1);
|
f2fs_put_page(page, 1);
|
||||||
|
|
||||||
f2fs_ra_meta_pages_cond(sbi, blkaddr, ra_blocks);
|
err = sanity_check_node_chain(sbi, blkaddr, &blkaddr_fast,
|
||||||
|
&is_detecting);
|
||||||
|
if (err)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1196,6 +1196,45 @@ static void __init_discard_policy(struct f2fs_sb_info *sbi,
|
||||||
static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
|
static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
|
||||||
struct block_device *bdev, block_t lstart,
|
struct block_device *bdev, block_t lstart,
|
||||||
block_t start, block_t len);
|
block_t start, block_t len);
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
static void __submit_zone_reset_cmd(struct f2fs_sb_info *sbi,
|
||||||
|
struct discard_cmd *dc, blk_opf_t flag,
|
||||||
|
struct list_head *wait_list,
|
||||||
|
unsigned int *issued)
|
||||||
|
{
|
||||||
|
struct discard_cmd_control *dcc = SM_I(sbi)->dcc_info;
|
||||||
|
struct block_device *bdev = dc->bdev;
|
||||||
|
struct bio *bio = bio_alloc(bdev, 0, REQ_OP_ZONE_RESET | flag, GFP_NOFS);
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
trace_f2fs_issue_reset_zone(bdev, dc->di.start);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&dc->lock, flags);
|
||||||
|
dc->state = D_SUBMIT;
|
||||||
|
dc->bio_ref++;
|
||||||
|
spin_unlock_irqrestore(&dc->lock, flags);
|
||||||
|
|
||||||
|
if (issued)
|
||||||
|
(*issued)++;
|
||||||
|
|
||||||
|
atomic_inc(&dcc->queued_discard);
|
||||||
|
dc->queued++;
|
||||||
|
list_move_tail(&dc->list, wait_list);
|
||||||
|
|
||||||
|
/* sanity check on discard range */
|
||||||
|
__check_sit_bitmap(sbi, dc->di.lstart, dc->di.lstart + dc->di.len);
|
||||||
|
|
||||||
|
bio->bi_iter.bi_sector = SECTOR_FROM_BLOCK(dc->di.start);
|
||||||
|
bio->bi_private = dc;
|
||||||
|
bio->bi_end_io = f2fs_submit_discard_endio;
|
||||||
|
submit_bio(bio);
|
||||||
|
|
||||||
|
atomic_inc(&dcc->issued_discard);
|
||||||
|
f2fs_update_iostat(sbi, NULL, FS_ZONE_RESET_IO, dc->di.len * F2FS_BLKSIZE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* this function is copied from blkdev_issue_discard from block/blk-lib.c */
|
/* this function is copied from blkdev_issue_discard from block/blk-lib.c */
|
||||||
static int __submit_discard_cmd(struct f2fs_sb_info *sbi,
|
static int __submit_discard_cmd(struct f2fs_sb_info *sbi,
|
||||||
struct discard_policy *dpolicy,
|
struct discard_policy *dpolicy,
|
||||||
|
@ -1217,6 +1256,13 @@ static int __submit_discard_cmd(struct f2fs_sb_info *sbi,
|
||||||
if (is_sbi_flag_set(sbi, SBI_NEED_FSCK))
|
if (is_sbi_flag_set(sbi, SBI_NEED_FSCK))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
if (f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(bdev)) {
|
||||||
|
__submit_zone_reset_cmd(sbi, dc, flag, wait_list, issued);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
trace_f2fs_issue_discard(bdev, dc->di.start, dc->di.len);
|
trace_f2fs_issue_discard(bdev, dc->di.start, dc->di.len);
|
||||||
|
|
||||||
lstart = dc->di.lstart;
|
lstart = dc->di.lstart;
|
||||||
|
@ -1461,6 +1507,19 @@ static void __update_discard_tree_range(struct f2fs_sb_info *sbi,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
static void __queue_zone_reset_cmd(struct f2fs_sb_info *sbi,
|
||||||
|
struct block_device *bdev, block_t blkstart, block_t lblkstart,
|
||||||
|
block_t blklen)
|
||||||
|
{
|
||||||
|
trace_f2fs_queue_reset_zone(bdev, blkstart);
|
||||||
|
|
||||||
|
mutex_lock(&SM_I(sbi)->dcc_info->cmd_lock);
|
||||||
|
__insert_discard_cmd(sbi, bdev, lblkstart, blkstart, blklen);
|
||||||
|
mutex_unlock(&SM_I(sbi)->dcc_info->cmd_lock);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static void __queue_discard_cmd(struct f2fs_sb_info *sbi,
|
static void __queue_discard_cmd(struct f2fs_sb_info *sbi,
|
||||||
struct block_device *bdev, block_t blkstart, block_t blklen)
|
struct block_device *bdev, block_t blkstart, block_t blklen)
|
||||||
{
|
{
|
||||||
|
@ -1724,6 +1783,19 @@ static void f2fs_wait_discard_bio(struct f2fs_sb_info *sbi, block_t blkaddr)
|
||||||
|
|
||||||
mutex_lock(&dcc->cmd_lock);
|
mutex_lock(&dcc->cmd_lock);
|
||||||
dc = __lookup_discard_cmd(sbi, blkaddr);
|
dc = __lookup_discard_cmd(sbi, blkaddr);
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
if (dc && f2fs_sb_has_blkzoned(sbi) && bdev_is_zoned(dc->bdev)) {
|
||||||
|
/* force submit zone reset */
|
||||||
|
if (dc->state == D_PREP)
|
||||||
|
__submit_zone_reset_cmd(sbi, dc, REQ_SYNC,
|
||||||
|
&dcc->wait_list, NULL);
|
||||||
|
dc->ref++;
|
||||||
|
mutex_unlock(&dcc->cmd_lock);
|
||||||
|
/* wait zone reset */
|
||||||
|
__wait_one_discard_bio(sbi, dc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (dc) {
|
if (dc) {
|
||||||
if (dc->state == D_PREP) {
|
if (dc->state == D_PREP) {
|
||||||
__punch_discard_cmd(sbi, dc, blkaddr);
|
__punch_discard_cmd(sbi, dc, blkaddr);
|
||||||
|
@ -1876,9 +1948,15 @@ static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi,
|
||||||
blkstart, blklen);
|
blkstart, blklen);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
trace_f2fs_issue_reset_zone(bdev, blkstart);
|
|
||||||
return blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET,
|
if (unlikely(is_sbi_flag_set(sbi, SBI_POR_DOING))) {
|
||||||
sector, nr_sects, GFP_NOFS);
|
trace_f2fs_issue_reset_zone(bdev, blkstart);
|
||||||
|
return blkdev_zone_mgmt(bdev, REQ_OP_ZONE_RESET,
|
||||||
|
sector, nr_sects, GFP_NOFS);
|
||||||
|
}
|
||||||
|
|
||||||
|
__queue_zone_reset_cmd(sbi, bdev, blkstart, lblkstart, blklen);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For conventional zones, use regular discard if supported */
|
/* For conventional zones, use regular discard if supported */
|
||||||
|
@ -2115,7 +2193,7 @@ find_next:
|
||||||
len = next_pos - cur_pos;
|
len = next_pos - cur_pos;
|
||||||
|
|
||||||
if (f2fs_sb_has_blkzoned(sbi) ||
|
if (f2fs_sb_has_blkzoned(sbi) ||
|
||||||
(force && len < cpc->trim_minlen))
|
!force || len < cpc->trim_minlen)
|
||||||
goto skip;
|
goto skip;
|
||||||
|
|
||||||
f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos,
|
f2fs_issue_discard(sbi, entry->start_blkaddr + cur_pos,
|
||||||
|
@ -4768,17 +4846,17 @@ static int check_zone_write_pointer(struct f2fs_sb_info *sbi,
|
||||||
{
|
{
|
||||||
unsigned int wp_segno, wp_blkoff, zone_secno, zone_segno, segno;
|
unsigned int wp_segno, wp_blkoff, zone_secno, zone_segno, segno;
|
||||||
block_t zone_block, wp_block, last_valid_block;
|
block_t zone_block, wp_block, last_valid_block;
|
||||||
unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT;
|
|
||||||
int i, s, b, ret;
|
int i, s, b, ret;
|
||||||
struct seg_entry *se;
|
struct seg_entry *se;
|
||||||
|
|
||||||
if (zone->type != BLK_ZONE_TYPE_SEQWRITE_REQ)
|
if (zone->type != BLK_ZONE_TYPE_SEQWRITE_REQ)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
wp_block = fdev->start_blk + (zone->wp >> log_sectors_per_block);
|
wp_block = fdev->start_blk + (zone->wp >> sbi->log_sectors_per_block);
|
||||||
wp_segno = GET_SEGNO(sbi, wp_block);
|
wp_segno = GET_SEGNO(sbi, wp_block);
|
||||||
wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno);
|
wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno);
|
||||||
zone_block = fdev->start_blk + (zone->start >> log_sectors_per_block);
|
zone_block = fdev->start_blk + (zone->start >>
|
||||||
|
sbi->log_sectors_per_block);
|
||||||
zone_segno = GET_SEGNO(sbi, zone_block);
|
zone_segno = GET_SEGNO(sbi, zone_block);
|
||||||
zone_secno = GET_SEC_FROM_SEG(sbi, zone_segno);
|
zone_secno = GET_SEC_FROM_SEG(sbi, zone_segno);
|
||||||
|
|
||||||
|
@ -4811,39 +4889,52 @@ static int check_zone_write_pointer(struct f2fs_sb_info *sbi,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If last valid block is beyond the write pointer, report the
|
* The write pointer matches with the valid blocks or
|
||||||
* inconsistency. This inconsistency does not cause write error
|
* already points to the end of the zone.
|
||||||
* because the zone will not be selected for write operation until
|
|
||||||
* it get discarded. Just report it.
|
|
||||||
*/
|
*/
|
||||||
if (last_valid_block >= wp_block) {
|
if ((last_valid_block + 1 == wp_block) ||
|
||||||
f2fs_notice(sbi, "Valid block beyond write pointer: "
|
(zone->wp == zone->start + zone->len))
|
||||||
"valid block[0x%x,0x%x] wp[0x%x,0x%x]",
|
|
||||||
GET_SEGNO(sbi, last_valid_block),
|
|
||||||
GET_BLKOFF_FROM_SEG0(sbi, last_valid_block),
|
|
||||||
wp_segno, wp_blkoff);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
if (last_valid_block + 1 == zone_block) {
|
||||||
* If there is no valid block in the zone and if write pointer is
|
/*
|
||||||
* not at zone start, reset the write pointer.
|
* If there is no valid block in the zone and if write pointer
|
||||||
*/
|
* is not at zone start, reset the write pointer.
|
||||||
if (last_valid_block + 1 == zone_block && zone->wp != zone->start) {
|
*/
|
||||||
f2fs_notice(sbi,
|
f2fs_notice(sbi,
|
||||||
"Zone without valid block has non-zero write "
|
"Zone without valid block has non-zero write "
|
||||||
"pointer. Reset the write pointer: wp[0x%x,0x%x]",
|
"pointer. Reset the write pointer: wp[0x%x,0x%x]",
|
||||||
wp_segno, wp_blkoff);
|
wp_segno, wp_blkoff);
|
||||||
ret = __f2fs_issue_discard_zone(sbi, fdev->bdev, zone_block,
|
ret = __f2fs_issue_discard_zone(sbi, fdev->bdev, zone_block,
|
||||||
zone->len >> log_sectors_per_block);
|
zone->len >> sbi->log_sectors_per_block);
|
||||||
if (ret) {
|
if (ret)
|
||||||
f2fs_err(sbi, "Discard zone failed: %s (errno=%d)",
|
f2fs_err(sbi, "Discard zone failed: %s (errno=%d)",
|
||||||
fdev->path, ret);
|
fdev->path, ret);
|
||||||
return ret;
|
|
||||||
}
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
/*
|
||||||
|
* If there are valid blocks and the write pointer doesn't
|
||||||
|
* match with them, we need to report the inconsistency and
|
||||||
|
* fill the zone till the end to close the zone. This inconsistency
|
||||||
|
* does not cause write error because the zone will not be selected
|
||||||
|
* for write operation until it get discarded.
|
||||||
|
*/
|
||||||
|
f2fs_notice(sbi, "Valid blocks are not aligned with write pointer: "
|
||||||
|
"valid block[0x%x,0x%x] wp[0x%x,0x%x]",
|
||||||
|
GET_SEGNO(sbi, last_valid_block),
|
||||||
|
GET_BLKOFF_FROM_SEG0(sbi, last_valid_block),
|
||||||
|
wp_segno, wp_blkoff);
|
||||||
|
|
||||||
|
ret = blkdev_issue_zeroout(fdev->bdev, zone->wp,
|
||||||
|
zone->len - (zone->wp - zone->start),
|
||||||
|
GFP_NOFS, 0);
|
||||||
|
if (ret)
|
||||||
|
f2fs_err(sbi, "Fill up zone failed: %s (errno=%d)",
|
||||||
|
fdev->path, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct f2fs_dev_info *get_target_zoned_dev(struct f2fs_sb_info *sbi,
|
static struct f2fs_dev_info *get_target_zoned_dev(struct f2fs_sb_info *sbi,
|
||||||
|
@ -4876,7 +4967,6 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type)
|
||||||
struct blk_zone zone;
|
struct blk_zone zone;
|
||||||
unsigned int cs_section, wp_segno, wp_blkoff, wp_sector_off;
|
unsigned int cs_section, wp_segno, wp_blkoff, wp_sector_off;
|
||||||
block_t cs_zone_block, wp_block;
|
block_t cs_zone_block, wp_block;
|
||||||
unsigned int log_sectors_per_block = sbi->log_blocksize - SECTOR_SHIFT;
|
|
||||||
sector_t zone_sector;
|
sector_t zone_sector;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
|
@ -4888,8 +4978,8 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* report zone for the sector the curseg points to */
|
/* report zone for the sector the curseg points to */
|
||||||
zone_sector = (sector_t)(cs_zone_block - zbd->start_blk)
|
zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) <<
|
||||||
<< log_sectors_per_block;
|
sbi->log_sectors_per_block;
|
||||||
err = blkdev_report_zones(zbd->bdev, zone_sector, 1,
|
err = blkdev_report_zones(zbd->bdev, zone_sector, 1,
|
||||||
report_one_zone_cb, &zone);
|
report_one_zone_cb, &zone);
|
||||||
if (err != 1) {
|
if (err != 1) {
|
||||||
|
@ -4901,10 +4991,10 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type)
|
||||||
if (zone.type != BLK_ZONE_TYPE_SEQWRITE_REQ)
|
if (zone.type != BLK_ZONE_TYPE_SEQWRITE_REQ)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
wp_block = zbd->start_blk + (zone.wp >> log_sectors_per_block);
|
wp_block = zbd->start_blk + (zone.wp >> sbi->log_sectors_per_block);
|
||||||
wp_segno = GET_SEGNO(sbi, wp_block);
|
wp_segno = GET_SEGNO(sbi, wp_block);
|
||||||
wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno);
|
wp_blkoff = wp_block - START_BLOCK(sbi, wp_segno);
|
||||||
wp_sector_off = zone.wp & GENMASK(log_sectors_per_block - 1, 0);
|
wp_sector_off = zone.wp & GENMASK(sbi->log_sectors_per_block - 1, 0);
|
||||||
|
|
||||||
if (cs->segno == wp_segno && cs->next_blkoff == wp_blkoff &&
|
if (cs->segno == wp_segno && cs->next_blkoff == wp_blkoff &&
|
||||||
wp_sector_off == 0)
|
wp_sector_off == 0)
|
||||||
|
@ -4931,8 +5021,8 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type)
|
||||||
if (!zbd)
|
if (!zbd)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
zone_sector = (sector_t)(cs_zone_block - zbd->start_blk)
|
zone_sector = (sector_t)(cs_zone_block - zbd->start_blk) <<
|
||||||
<< log_sectors_per_block;
|
sbi->log_sectors_per_block;
|
||||||
err = blkdev_report_zones(zbd->bdev, zone_sector, 1,
|
err = blkdev_report_zones(zbd->bdev, zone_sector, 1,
|
||||||
report_one_zone_cb, &zone);
|
report_one_zone_cb, &zone);
|
||||||
if (err != 1) {
|
if (err != 1) {
|
||||||
|
@ -4950,7 +5040,7 @@ static int fix_curseg_write_pointer(struct f2fs_sb_info *sbi, int type)
|
||||||
"Reset the zone: curseg[0x%x,0x%x]",
|
"Reset the zone: curseg[0x%x,0x%x]",
|
||||||
type, cs->segno, cs->next_blkoff);
|
type, cs->segno, cs->next_blkoff);
|
||||||
err = __f2fs_issue_discard_zone(sbi, zbd->bdev, cs_zone_block,
|
err = __f2fs_issue_discard_zone(sbi, zbd->bdev, cs_zone_block,
|
||||||
zone.len >> log_sectors_per_block);
|
zone.len >> sbi->log_sectors_per_block);
|
||||||
if (err) {
|
if (err) {
|
||||||
f2fs_err(sbi, "Discard zone failed: %s (errno=%d)",
|
f2fs_err(sbi, "Discard zone failed: %s (errno=%d)",
|
||||||
zbd->path, err);
|
zbd->path, err);
|
||||||
|
|
252
fs/f2fs/super.c
252
fs/f2fs/super.c
|
@ -164,6 +164,7 @@ enum {
|
||||||
Opt_discard_unit,
|
Opt_discard_unit,
|
||||||
Opt_memory_mode,
|
Opt_memory_mode,
|
||||||
Opt_age_extent_cache,
|
Opt_age_extent_cache,
|
||||||
|
Opt_errors,
|
||||||
Opt_err,
|
Opt_err,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -243,6 +244,7 @@ static match_table_t f2fs_tokens = {
|
||||||
{Opt_discard_unit, "discard_unit=%s"},
|
{Opt_discard_unit, "discard_unit=%s"},
|
||||||
{Opt_memory_mode, "memory=%s"},
|
{Opt_memory_mode, "memory=%s"},
|
||||||
{Opt_age_extent_cache, "age_extent_cache"},
|
{Opt_age_extent_cache, "age_extent_cache"},
|
||||||
|
{Opt_errors, "errors=%s"},
|
||||||
{Opt_err, NULL},
|
{Opt_err, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -587,14 +589,12 @@ static int f2fs_set_lz4hc_level(struct f2fs_sb_info *sbi, const char *str)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_F2FS_FS_LZ4HC
|
#ifdef CONFIG_F2FS_FS_LZ4HC
|
||||||
unsigned int level;
|
unsigned int level;
|
||||||
#endif
|
|
||||||
|
|
||||||
if (strlen(str) == 3) {
|
if (strlen(str) == 3) {
|
||||||
F2FS_OPTION(sbi).compress_level = 0;
|
F2FS_OPTION(sbi).compress_level = LZ4HC_DEFAULT_CLEVEL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_F2FS_FS_LZ4HC
|
|
||||||
str += 3;
|
str += 3;
|
||||||
|
|
||||||
if (str[0] != ':') {
|
if (str[0] != ':') {
|
||||||
|
@ -604,7 +604,7 @@ static int f2fs_set_lz4hc_level(struct f2fs_sb_info *sbi, const char *str)
|
||||||
if (kstrtouint(str + 1, 10, &level))
|
if (kstrtouint(str + 1, 10, &level))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (level < LZ4HC_MIN_CLEVEL || level > LZ4HC_MAX_CLEVEL) {
|
if (!f2fs_is_compress_level_valid(COMPRESS_LZ4, level)) {
|
||||||
f2fs_info(sbi, "invalid lz4hc compress level: %d", level);
|
f2fs_info(sbi, "invalid lz4hc compress level: %d", level);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -612,6 +612,10 @@ static int f2fs_set_lz4hc_level(struct f2fs_sb_info *sbi, const char *str)
|
||||||
F2FS_OPTION(sbi).compress_level = level;
|
F2FS_OPTION(sbi).compress_level = level;
|
||||||
return 0;
|
return 0;
|
||||||
#else
|
#else
|
||||||
|
if (strlen(str) == 3) {
|
||||||
|
F2FS_OPTION(sbi).compress_level = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
f2fs_info(sbi, "kernel doesn't support lz4hc compression");
|
f2fs_info(sbi, "kernel doesn't support lz4hc compression");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
#endif
|
#endif
|
||||||
|
@ -625,7 +629,7 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
|
||||||
int len = 4;
|
int len = 4;
|
||||||
|
|
||||||
if (strlen(str) == len) {
|
if (strlen(str) == len) {
|
||||||
F2FS_OPTION(sbi).compress_level = 0;
|
F2FS_OPTION(sbi).compress_level = F2FS_ZSTD_DEFAULT_CLEVEL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -638,7 +642,7 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
|
||||||
if (kstrtouint(str + 1, 10, &level))
|
if (kstrtouint(str + 1, 10, &level))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (!level || level > zstd_max_clevel()) {
|
if (!f2fs_is_compress_level_valid(COMPRESS_ZSTD, level)) {
|
||||||
f2fs_info(sbi, "invalid zstd compress level: %d", level);
|
f2fs_info(sbi, "invalid zstd compress level: %d", level);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -1268,6 +1272,25 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
||||||
case Opt_age_extent_cache:
|
case Opt_age_extent_cache:
|
||||||
set_opt(sbi, AGE_EXTENT_CACHE);
|
set_opt(sbi, AGE_EXTENT_CACHE);
|
||||||
break;
|
break;
|
||||||
|
case Opt_errors:
|
||||||
|
name = match_strdup(&args[0]);
|
||||||
|
if (!name)
|
||||||
|
return -ENOMEM;
|
||||||
|
if (!strcmp(name, "remount-ro")) {
|
||||||
|
F2FS_OPTION(sbi).errors =
|
||||||
|
MOUNT_ERRORS_READONLY;
|
||||||
|
} else if (!strcmp(name, "continue")) {
|
||||||
|
F2FS_OPTION(sbi).errors =
|
||||||
|
MOUNT_ERRORS_CONTINUE;
|
||||||
|
} else if (!strcmp(name, "panic")) {
|
||||||
|
F2FS_OPTION(sbi).errors =
|
||||||
|
MOUNT_ERRORS_PANIC;
|
||||||
|
} else {
|
||||||
|
kfree(name);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
kfree(name);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
|
f2fs_err(sbi, "Unrecognized mount option \"%s\" or missing value",
|
||||||
p);
|
p);
|
||||||
|
@ -1340,7 +1363,7 @@ default_check:
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
min_size = sizeof(struct f2fs_xattr_header) / sizeof(__le32);
|
min_size = MIN_INLINE_XATTR_SIZE;
|
||||||
max_size = MAX_INLINE_XATTR_SIZE;
|
max_size = MAX_INLINE_XATTR_SIZE;
|
||||||
|
|
||||||
if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
|
if (F2FS_OPTION(sbi).inline_xattr_size < min_size ||
|
||||||
|
@ -1550,6 +1573,7 @@ static void f2fs_put_super(struct super_block *sb)
|
||||||
{
|
{
|
||||||
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
struct f2fs_sb_info *sbi = F2FS_SB(sb);
|
||||||
int i;
|
int i;
|
||||||
|
int err = 0;
|
||||||
bool done;
|
bool done;
|
||||||
|
|
||||||
/* unregister procfs/sysfs entries in advance to avoid race case */
|
/* unregister procfs/sysfs entries in advance to avoid race case */
|
||||||
|
@ -1576,7 +1600,7 @@ static void f2fs_put_super(struct super_block *sb)
|
||||||
struct cp_control cpc = {
|
struct cp_control cpc = {
|
||||||
.reason = CP_UMOUNT,
|
.reason = CP_UMOUNT,
|
||||||
};
|
};
|
||||||
f2fs_write_checkpoint(sbi, &cpc);
|
err = f2fs_write_checkpoint(sbi, &cpc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* be sure to wait for any on-going discard commands */
|
/* be sure to wait for any on-going discard commands */
|
||||||
|
@ -1585,7 +1609,7 @@ static void f2fs_put_super(struct super_block *sb)
|
||||||
struct cp_control cpc = {
|
struct cp_control cpc = {
|
||||||
.reason = CP_UMOUNT | CP_TRIMMED,
|
.reason = CP_UMOUNT | CP_TRIMMED,
|
||||||
};
|
};
|
||||||
f2fs_write_checkpoint(sbi, &cpc);
|
err = f2fs_write_checkpoint(sbi, &cpc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1602,6 +1626,19 @@ static void f2fs_put_super(struct super_block *sb)
|
||||||
|
|
||||||
f2fs_wait_on_all_pages(sbi, F2FS_WB_CP_DATA);
|
f2fs_wait_on_all_pages(sbi, F2FS_WB_CP_DATA);
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
truncate_inode_pages_final(NODE_MAPPING(sbi));
|
||||||
|
truncate_inode_pages_final(META_MAPPING(sbi));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < NR_COUNT_TYPE; i++) {
|
||||||
|
if (!get_pages(sbi, i))
|
||||||
|
continue;
|
||||||
|
f2fs_err(sbi, "detect filesystem reference count leak during "
|
||||||
|
"umount, type: %d, count: %lld", i, get_pages(sbi, i));
|
||||||
|
f2fs_bug_on(sbi, 1);
|
||||||
|
}
|
||||||
|
|
||||||
f2fs_bug_on(sbi, sbi->fsync_node_num);
|
f2fs_bug_on(sbi, sbi->fsync_node_num);
|
||||||
|
|
||||||
f2fs_destroy_compress_inode(sbi);
|
f2fs_destroy_compress_inode(sbi);
|
||||||
|
@ -1622,6 +1659,9 @@ static void f2fs_put_super(struct super_block *sb)
|
||||||
f2fs_destroy_node_manager(sbi);
|
f2fs_destroy_node_manager(sbi);
|
||||||
f2fs_destroy_segment_manager(sbi);
|
f2fs_destroy_segment_manager(sbi);
|
||||||
|
|
||||||
|
/* flush s_error_work before sbi destroy */
|
||||||
|
flush_work(&sbi->s_error_work);
|
||||||
|
|
||||||
f2fs_destroy_post_read_wq(sbi);
|
f2fs_destroy_post_read_wq(sbi);
|
||||||
|
|
||||||
kvfree(sbi->ckpt);
|
kvfree(sbi->ckpt);
|
||||||
|
@ -2052,12 +2092,32 @@ static int f2fs_show_options(struct seq_file *seq, struct dentry *root)
|
||||||
else if (F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_LOW)
|
else if (F2FS_OPTION(sbi).memory_mode == MEMORY_MODE_LOW)
|
||||||
seq_printf(seq, ",memory=%s", "low");
|
seq_printf(seq, ",memory=%s", "low");
|
||||||
|
|
||||||
|
if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_READONLY)
|
||||||
|
seq_printf(seq, ",errors=%s", "remount-ro");
|
||||||
|
else if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_CONTINUE)
|
||||||
|
seq_printf(seq, ",errors=%s", "continue");
|
||||||
|
else if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_PANIC)
|
||||||
|
seq_printf(seq, ",errors=%s", "panic");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void default_options(struct f2fs_sb_info *sbi)
|
static void default_options(struct f2fs_sb_info *sbi, bool remount)
|
||||||
{
|
{
|
||||||
/* init some FS parameters */
|
/* init some FS parameters */
|
||||||
|
if (!remount) {
|
||||||
|
set_opt(sbi, READ_EXTENT_CACHE);
|
||||||
|
clear_opt(sbi, DISABLE_CHECKPOINT);
|
||||||
|
|
||||||
|
if (f2fs_hw_support_discard(sbi) || f2fs_hw_should_discard(sbi))
|
||||||
|
set_opt(sbi, DISCARD);
|
||||||
|
|
||||||
|
if (f2fs_sb_has_blkzoned(sbi))
|
||||||
|
F2FS_OPTION(sbi).discard_unit = DISCARD_UNIT_SECTION;
|
||||||
|
else
|
||||||
|
F2FS_OPTION(sbi).discard_unit = DISCARD_UNIT_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
if (f2fs_sb_has_readonly(sbi))
|
if (f2fs_sb_has_readonly(sbi))
|
||||||
F2FS_OPTION(sbi).active_logs = NR_CURSEG_RO_TYPE;
|
F2FS_OPTION(sbi).active_logs = NR_CURSEG_RO_TYPE;
|
||||||
else
|
else
|
||||||
|
@ -2080,29 +2140,23 @@ static void default_options(struct f2fs_sb_info *sbi)
|
||||||
}
|
}
|
||||||
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
|
F2FS_OPTION(sbi).bggc_mode = BGGC_MODE_ON;
|
||||||
F2FS_OPTION(sbi).memory_mode = MEMORY_MODE_NORMAL;
|
F2FS_OPTION(sbi).memory_mode = MEMORY_MODE_NORMAL;
|
||||||
|
F2FS_OPTION(sbi).errors = MOUNT_ERRORS_CONTINUE;
|
||||||
|
|
||||||
sbi->sb->s_flags &= ~SB_INLINECRYPT;
|
sbi->sb->s_flags &= ~SB_INLINECRYPT;
|
||||||
|
|
||||||
set_opt(sbi, INLINE_XATTR);
|
set_opt(sbi, INLINE_XATTR);
|
||||||
set_opt(sbi, INLINE_DATA);
|
set_opt(sbi, INLINE_DATA);
|
||||||
set_opt(sbi, INLINE_DENTRY);
|
set_opt(sbi, INLINE_DENTRY);
|
||||||
set_opt(sbi, READ_EXTENT_CACHE);
|
|
||||||
set_opt(sbi, NOHEAP);
|
set_opt(sbi, NOHEAP);
|
||||||
clear_opt(sbi, DISABLE_CHECKPOINT);
|
|
||||||
set_opt(sbi, MERGE_CHECKPOINT);
|
set_opt(sbi, MERGE_CHECKPOINT);
|
||||||
F2FS_OPTION(sbi).unusable_cap = 0;
|
F2FS_OPTION(sbi).unusable_cap = 0;
|
||||||
sbi->sb->s_flags |= SB_LAZYTIME;
|
sbi->sb->s_flags |= SB_LAZYTIME;
|
||||||
if (!f2fs_is_readonly(sbi))
|
if (!f2fs_is_readonly(sbi))
|
||||||
set_opt(sbi, FLUSH_MERGE);
|
set_opt(sbi, FLUSH_MERGE);
|
||||||
if (f2fs_hw_support_discard(sbi) || f2fs_hw_should_discard(sbi))
|
if (f2fs_sb_has_blkzoned(sbi))
|
||||||
set_opt(sbi, DISCARD);
|
|
||||||
if (f2fs_sb_has_blkzoned(sbi)) {
|
|
||||||
F2FS_OPTION(sbi).fs_mode = FS_MODE_LFS;
|
F2FS_OPTION(sbi).fs_mode = FS_MODE_LFS;
|
||||||
F2FS_OPTION(sbi).discard_unit = DISCARD_UNIT_SECTION;
|
else
|
||||||
} else {
|
|
||||||
F2FS_OPTION(sbi).fs_mode = FS_MODE_ADAPTIVE;
|
F2FS_OPTION(sbi).fs_mode = FS_MODE_ADAPTIVE;
|
||||||
F2FS_OPTION(sbi).discard_unit = DISCARD_UNIT_BLOCK;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_F2FS_FS_XATTR
|
#ifdef CONFIG_F2FS_FS_XATTR
|
||||||
set_opt(sbi, XATTR_USER);
|
set_opt(sbi, XATTR_USER);
|
||||||
|
@ -2274,13 +2328,16 @@ static int f2fs_remount(struct super_block *sb, int *flags, char *data)
|
||||||
clear_sbi_flag(sbi, SBI_NEED_SB_WRITE);
|
clear_sbi_flag(sbi, SBI_NEED_SB_WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
default_options(sbi);
|
default_options(sbi, true);
|
||||||
|
|
||||||
/* parse mount options */
|
/* parse mount options */
|
||||||
err = parse_options(sb, data, true);
|
err = parse_options(sb, data, true);
|
||||||
if (err)
|
if (err)
|
||||||
goto restore_opts;
|
goto restore_opts;
|
||||||
|
|
||||||
|
/* flush outstanding errors before changing fs state */
|
||||||
|
flush_work(&sbi->s_error_work);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Previous and new state of filesystem is RO,
|
* Previous and new state of filesystem is RO,
|
||||||
* so skip checking GC and FLUSH_MERGE conditions.
|
* so skip checking GC and FLUSH_MERGE conditions.
|
||||||
|
@ -2713,6 +2770,7 @@ static int f2fs_quota_enable(struct super_block *sb, int type, int format_id,
|
||||||
{
|
{
|
||||||
struct inode *qf_inode;
|
struct inode *qf_inode;
|
||||||
unsigned long qf_inum;
|
unsigned long qf_inum;
|
||||||
|
unsigned long qf_flag = F2FS_QUOTA_DEFAULT_FL;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
BUG_ON(!f2fs_sb_has_quota_ino(F2FS_SB(sb)));
|
BUG_ON(!f2fs_sb_has_quota_ino(F2FS_SB(sb)));
|
||||||
|
@ -2728,7 +2786,15 @@ static int f2fs_quota_enable(struct super_block *sb, int type, int format_id,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't account quota for quota files to avoid recursion */
|
/* Don't account quota for quota files to avoid recursion */
|
||||||
|
inode_lock(qf_inode);
|
||||||
qf_inode->i_flags |= S_NOQUOTA;
|
qf_inode->i_flags |= S_NOQUOTA;
|
||||||
|
|
||||||
|
if ((F2FS_I(qf_inode)->i_flags & qf_flag) != qf_flag) {
|
||||||
|
F2FS_I(qf_inode)->i_flags |= qf_flag;
|
||||||
|
f2fs_set_inode_flags(qf_inode);
|
||||||
|
}
|
||||||
|
inode_unlock(qf_inode);
|
||||||
|
|
||||||
err = dquot_load_quota_inode(qf_inode, type, format_id, flags);
|
err = dquot_load_quota_inode(qf_inode, type, format_id, flags);
|
||||||
iput(qf_inode);
|
iput(qf_inode);
|
||||||
return err;
|
return err;
|
||||||
|
@ -2859,18 +2925,29 @@ static int f2fs_quota_on(struct super_block *sb, int type, int format_id,
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (path->dentry->d_sb != sb)
|
||||||
|
return -EXDEV;
|
||||||
|
|
||||||
err = f2fs_quota_sync(sb, type);
|
err = f2fs_quota_sync(sb, type);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
|
inode = d_inode(path->dentry);
|
||||||
|
|
||||||
|
err = filemap_fdatawrite(inode->i_mapping);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = filemap_fdatawait(inode->i_mapping);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
err = dquot_quota_on(sb, type, format_id, path);
|
err = dquot_quota_on(sb, type, format_id, path);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
inode = d_inode(path->dentry);
|
|
||||||
|
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
F2FS_I(inode)->i_flags |= F2FS_NOATIME_FL | F2FS_IMMUTABLE_FL;
|
F2FS_I(inode)->i_flags |= F2FS_QUOTA_DEFAULT_FL;
|
||||||
f2fs_set_inode_flags(inode);
|
f2fs_set_inode_flags(inode);
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
f2fs_mark_inode_dirty_sync(inode, false);
|
f2fs_mark_inode_dirty_sync(inode, false);
|
||||||
|
@ -2895,7 +2972,7 @@ static int __f2fs_quota_off(struct super_block *sb, int type)
|
||||||
goto out_put;
|
goto out_put;
|
||||||
|
|
||||||
inode_lock(inode);
|
inode_lock(inode);
|
||||||
F2FS_I(inode)->i_flags &= ~(F2FS_NOATIME_FL | F2FS_IMMUTABLE_FL);
|
F2FS_I(inode)->i_flags &= ~F2FS_QUOTA_DEFAULT_FL;
|
||||||
f2fs_set_inode_flags(inode);
|
f2fs_set_inode_flags(inode);
|
||||||
inode_unlock(inode);
|
inode_unlock(inode);
|
||||||
f2fs_mark_inode_dirty_sync(inode, false);
|
f2fs_mark_inode_dirty_sync(inode, false);
|
||||||
|
@ -3926,55 +4003,73 @@ int f2fs_commit_super(struct f2fs_sb_info *sbi, bool recover)
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
void f2fs_handle_stop(struct f2fs_sb_info *sbi, unsigned char reason)
|
static void save_stop_reason(struct f2fs_sb_info *sbi, unsigned char reason)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sbi->error_lock, flags);
|
||||||
|
if (sbi->stop_reason[reason] < GENMASK(BITS_PER_BYTE - 1, 0))
|
||||||
|
sbi->stop_reason[reason]++;
|
||||||
|
spin_unlock_irqrestore(&sbi->error_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void f2fs_record_stop_reason(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
|
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
|
||||||
|
unsigned long flags;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
f2fs_down_write(&sbi->sb_lock);
|
f2fs_down_write(&sbi->sb_lock);
|
||||||
|
|
||||||
if (raw_super->s_stop_reason[reason] < GENMASK(BITS_PER_BYTE - 1, 0))
|
spin_lock_irqsave(&sbi->error_lock, flags);
|
||||||
raw_super->s_stop_reason[reason]++;
|
if (sbi->error_dirty) {
|
||||||
|
memcpy(F2FS_RAW_SUPER(sbi)->s_errors, sbi->errors,
|
||||||
|
MAX_F2FS_ERRORS);
|
||||||
|
sbi->error_dirty = false;
|
||||||
|
}
|
||||||
|
memcpy(raw_super->s_stop_reason, sbi->stop_reason, MAX_STOP_REASON);
|
||||||
|
spin_unlock_irqrestore(&sbi->error_lock, flags);
|
||||||
|
|
||||||
err = f2fs_commit_super(sbi, false);
|
err = f2fs_commit_super(sbi, false);
|
||||||
if (err)
|
|
||||||
f2fs_err(sbi, "f2fs_commit_super fails to record reason:%u err:%d",
|
|
||||||
reason, err);
|
|
||||||
f2fs_up_write(&sbi->sb_lock);
|
f2fs_up_write(&sbi->sb_lock);
|
||||||
|
if (err)
|
||||||
|
f2fs_err(sbi, "f2fs_commit_super fails to record err:%d", err);
|
||||||
}
|
}
|
||||||
|
|
||||||
void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag)
|
void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag)
|
||||||
{
|
{
|
||||||
spin_lock(&sbi->error_lock);
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&sbi->error_lock, flags);
|
||||||
if (!test_bit(flag, (unsigned long *)sbi->errors)) {
|
if (!test_bit(flag, (unsigned long *)sbi->errors)) {
|
||||||
set_bit(flag, (unsigned long *)sbi->errors);
|
set_bit(flag, (unsigned long *)sbi->errors);
|
||||||
sbi->error_dirty = true;
|
sbi->error_dirty = true;
|
||||||
}
|
}
|
||||||
spin_unlock(&sbi->error_lock);
|
spin_unlock_irqrestore(&sbi->error_lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool f2fs_update_errors(struct f2fs_sb_info *sbi)
|
static bool f2fs_update_errors(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
|
unsigned long flags;
|
||||||
bool need_update = false;
|
bool need_update = false;
|
||||||
|
|
||||||
spin_lock(&sbi->error_lock);
|
spin_lock_irqsave(&sbi->error_lock, flags);
|
||||||
if (sbi->error_dirty) {
|
if (sbi->error_dirty) {
|
||||||
memcpy(F2FS_RAW_SUPER(sbi)->s_errors, sbi->errors,
|
memcpy(F2FS_RAW_SUPER(sbi)->s_errors, sbi->errors,
|
||||||
MAX_F2FS_ERRORS);
|
MAX_F2FS_ERRORS);
|
||||||
sbi->error_dirty = false;
|
sbi->error_dirty = false;
|
||||||
need_update = true;
|
need_update = true;
|
||||||
}
|
}
|
||||||
spin_unlock(&sbi->error_lock);
|
spin_unlock_irqrestore(&sbi->error_lock, flags);
|
||||||
|
|
||||||
return need_update;
|
return need_update;
|
||||||
}
|
}
|
||||||
|
|
||||||
void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error)
|
static void f2fs_record_errors(struct f2fs_sb_info *sbi, unsigned char error)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
f2fs_save_errors(sbi, error);
|
|
||||||
|
|
||||||
f2fs_down_write(&sbi->sb_lock);
|
f2fs_down_write(&sbi->sb_lock);
|
||||||
|
|
||||||
if (!f2fs_update_errors(sbi))
|
if (!f2fs_update_errors(sbi))
|
||||||
|
@ -3988,6 +4083,83 @@ out_unlock:
|
||||||
f2fs_up_write(&sbi->sb_lock);
|
f2fs_up_write(&sbi->sb_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error)
|
||||||
|
{
|
||||||
|
f2fs_save_errors(sbi, error);
|
||||||
|
f2fs_record_errors(sbi, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void f2fs_handle_error_async(struct f2fs_sb_info *sbi, unsigned char error)
|
||||||
|
{
|
||||||
|
f2fs_save_errors(sbi, error);
|
||||||
|
|
||||||
|
if (!sbi->error_dirty)
|
||||||
|
return;
|
||||||
|
if (!test_bit(error, (unsigned long *)sbi->errors))
|
||||||
|
return;
|
||||||
|
schedule_work(&sbi->s_error_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool system_going_down(void)
|
||||||
|
{
|
||||||
|
return system_state == SYSTEM_HALT || system_state == SYSTEM_POWER_OFF
|
||||||
|
|| system_state == SYSTEM_RESTART;
|
||||||
|
}
|
||||||
|
|
||||||
|
void f2fs_handle_critical_error(struct f2fs_sb_info *sbi, unsigned char reason,
|
||||||
|
bool irq_context)
|
||||||
|
{
|
||||||
|
struct super_block *sb = sbi->sb;
|
||||||
|
bool shutdown = reason == STOP_CP_REASON_SHUTDOWN;
|
||||||
|
bool continue_fs = !shutdown &&
|
||||||
|
F2FS_OPTION(sbi).errors == MOUNT_ERRORS_CONTINUE;
|
||||||
|
|
||||||
|
set_ckpt_flags(sbi, CP_ERROR_FLAG);
|
||||||
|
|
||||||
|
if (!f2fs_hw_is_readonly(sbi)) {
|
||||||
|
save_stop_reason(sbi, reason);
|
||||||
|
|
||||||
|
if (irq_context && !shutdown)
|
||||||
|
schedule_work(&sbi->s_error_work);
|
||||||
|
else
|
||||||
|
f2fs_record_stop_reason(sbi);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We force ERRORS_RO behavior when system is rebooting. Otherwise we
|
||||||
|
* could panic during 'reboot -f' as the underlying device got already
|
||||||
|
* disabled.
|
||||||
|
*/
|
||||||
|
if (F2FS_OPTION(sbi).errors == MOUNT_ERRORS_PANIC &&
|
||||||
|
!shutdown && !system_going_down() &&
|
||||||
|
!is_sbi_flag_set(sbi, SBI_IS_SHUTDOWN))
|
||||||
|
panic("F2FS-fs (device %s): panic forced after error\n",
|
||||||
|
sb->s_id);
|
||||||
|
|
||||||
|
if (shutdown)
|
||||||
|
set_sbi_flag(sbi, SBI_IS_SHUTDOWN);
|
||||||
|
|
||||||
|
/* continue filesystem operators if errors=continue */
|
||||||
|
if (continue_fs || f2fs_readonly(sb))
|
||||||
|
return;
|
||||||
|
|
||||||
|
f2fs_warn(sbi, "Remounting filesystem read-only");
|
||||||
|
/*
|
||||||
|
* Make sure updated value of ->s_mount_flags will be visible before
|
||||||
|
* ->s_flags update
|
||||||
|
*/
|
||||||
|
smp_wmb();
|
||||||
|
sb->s_flags |= SB_RDONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void f2fs_record_error_work(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct f2fs_sb_info *sbi = container_of(work,
|
||||||
|
struct f2fs_sb_info, s_error_work);
|
||||||
|
|
||||||
|
f2fs_record_stop_reason(sbi);
|
||||||
|
}
|
||||||
|
|
||||||
static int f2fs_scan_devices(struct f2fs_sb_info *sbi)
|
static int f2fs_scan_devices(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
|
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
|
||||||
|
@ -4220,14 +4392,16 @@ try_onemore:
|
||||||
sb->s_fs_info = sbi;
|
sb->s_fs_info = sbi;
|
||||||
sbi->raw_super = raw_super;
|
sbi->raw_super = raw_super;
|
||||||
|
|
||||||
|
INIT_WORK(&sbi->s_error_work, f2fs_record_error_work);
|
||||||
memcpy(sbi->errors, raw_super->s_errors, MAX_F2FS_ERRORS);
|
memcpy(sbi->errors, raw_super->s_errors, MAX_F2FS_ERRORS);
|
||||||
|
memcpy(sbi->stop_reason, raw_super->s_stop_reason, MAX_STOP_REASON);
|
||||||
|
|
||||||
/* precompute checksum seed for metadata */
|
/* precompute checksum seed for metadata */
|
||||||
if (f2fs_sb_has_inode_chksum(sbi))
|
if (f2fs_sb_has_inode_chksum(sbi))
|
||||||
sbi->s_chksum_seed = f2fs_chksum(sbi, ~0, raw_super->uuid,
|
sbi->s_chksum_seed = f2fs_chksum(sbi, ~0, raw_super->uuid,
|
||||||
sizeof(raw_super->uuid));
|
sizeof(raw_super->uuid));
|
||||||
|
|
||||||
default_options(sbi);
|
default_options(sbi, false);
|
||||||
/* parse mount options */
|
/* parse mount options */
|
||||||
options = kstrdup((const char *)data, GFP_KERNEL);
|
options = kstrdup((const char *)data, GFP_KERNEL);
|
||||||
if (data && !options) {
|
if (data && !options) {
|
||||||
|
@ -4617,6 +4791,8 @@ free_sm:
|
||||||
f2fs_destroy_segment_manager(sbi);
|
f2fs_destroy_segment_manager(sbi);
|
||||||
stop_ckpt_thread:
|
stop_ckpt_thread:
|
||||||
f2fs_stop_ckpt_thread(sbi);
|
f2fs_stop_ckpt_thread(sbi);
|
||||||
|
/* flush s_error_work before sbi destroy */
|
||||||
|
flush_work(&sbi->s_error_work);
|
||||||
f2fs_destroy_post_read_wq(sbi);
|
f2fs_destroy_post_read_wq(sbi);
|
||||||
free_devices:
|
free_devices:
|
||||||
destroy_device_list(sbi);
|
destroy_device_list(sbi);
|
||||||
|
|
283
fs/f2fs/sysfs.c
283
fs/f2fs/sysfs.c
|
@ -842,68 +842,160 @@ 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_STAT_ATTR(_struct_type, _struct_name, _name, _elname) \
|
#ifdef CONFIG_F2FS_STAT_FS
|
||||||
static struct f2fs_attr f2fs_attr_##_name = { \
|
#define STAT_INFO_RO_ATTR(name, elname) \
|
||||||
.attr = {.name = __stringify(_name), .mode = 0444 }, \
|
F2FS_RO_ATTR(STAT_INFO, f2fs_stat_info, name, elname)
|
||||||
.show = f2fs_sbi_show, \
|
#endif
|
||||||
.struct_type = _struct_type, \
|
|
||||||
.offset = offsetof(struct _struct_name, _elname), \
|
#define GC_THREAD_RW_ATTR(name, elname) \
|
||||||
}
|
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, name, elname)
|
||||||
|
|
||||||
|
#define SM_INFO_RW_ATTR(name, elname) \
|
||||||
|
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, name, elname)
|
||||||
|
|
||||||
|
#define SM_INFO_GENERAL_RW_ATTR(elname) \
|
||||||
|
SM_INFO_RW_ATTR(elname, elname)
|
||||||
|
|
||||||
|
#define DCC_INFO_RW_ATTR(name, elname) \
|
||||||
|
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, name, elname)
|
||||||
|
|
||||||
|
#define DCC_INFO_GENERAL_RW_ATTR(elname) \
|
||||||
|
DCC_INFO_RW_ATTR(elname, elname)
|
||||||
|
|
||||||
|
#define NM_INFO_RW_ATTR(name, elname) \
|
||||||
|
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, name, elname)
|
||||||
|
|
||||||
|
#define NM_INFO_GENERAL_RW_ATTR(elname) \
|
||||||
|
NM_INFO_RW_ATTR(elname, elname)
|
||||||
|
|
||||||
|
#define F2FS_SBI_RW_ATTR(name, elname) \
|
||||||
|
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, name, elname)
|
||||||
|
|
||||||
|
#define F2FS_SBI_GENERAL_RW_ATTR(elname) \
|
||||||
|
F2FS_SBI_RW_ATTR(elname, elname)
|
||||||
|
|
||||||
|
#define F2FS_SBI_GENERAL_RO_ATTR(elname) \
|
||||||
|
F2FS_RO_ATTR(F2FS_SBI, f2fs_sb_info, elname, elname)
|
||||||
|
|
||||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_urgent_sleep_time,
|
|
||||||
urgent_sleep_time);
|
|
||||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_min_sleep_time, min_sleep_time);
|
|
||||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_max_sleep_time, max_sleep_time);
|
|
||||||
F2FS_RW_ATTR(GC_THREAD, f2fs_gc_kthread, gc_no_gc_sleep_time, no_gc_sleep_time);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_idle, gc_mode);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_urgent, gc_mode);
|
|
||||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, reclaim_segments, rec_prefree_segments);
|
|
||||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_small_discards, max_discards);
|
|
||||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_discard_request, max_discard_request);
|
|
||||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, min_discard_issue_time, min_discard_issue_time);
|
|
||||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, mid_discard_issue_time, mid_discard_issue_time);
|
|
||||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_discard_issue_time, max_discard_issue_time);
|
|
||||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, discard_io_aware_gran, discard_io_aware_gran);
|
|
||||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, discard_urgent_util, discard_urgent_util);
|
|
||||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, discard_granularity, discard_granularity);
|
|
||||||
F2FS_RW_ATTR(DCC_INFO, discard_cmd_control, max_ordered_discard, max_ordered_discard);
|
|
||||||
F2FS_RW_ATTR(RESERVED_BLOCKS, f2fs_sb_info, reserved_blocks, reserved_blocks);
|
|
||||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, ipu_policy, ipu_policy);
|
|
||||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ipu_util, min_ipu_util);
|
|
||||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_fsync_blocks, min_fsync_blocks);
|
|
||||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_seq_blocks, min_seq_blocks);
|
|
||||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_hot_blocks, min_hot_blocks);
|
|
||||||
F2FS_RW_ATTR(SM_INFO, f2fs_sm_info, min_ssr_sections, min_ssr_sections);
|
|
||||||
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ram_thresh, ram_thresh);
|
|
||||||
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, ra_nid_pages, ra_nid_pages);
|
|
||||||
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, dirty_nats_ratio, dirty_nats_ratio);
|
|
||||||
F2FS_RW_ATTR(NM_INFO, f2fs_nm_info, max_roll_forward_node_blocks, max_rf_node_blocks);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_victim_search, max_victim_search);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, migration_granularity, migration_granularity);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, dir_level, dir_level);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, cp_interval, interval_time[CP_TIME]);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, idle_interval, interval_time[REQ_TIME]);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, discard_idle_interval,
|
|
||||||
interval_time[DISCARD_TIME]);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_idle_interval, interval_time[GC_TIME]);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info,
|
|
||||||
umount_discard_timeout, interval_time[UMOUNT_DISCARD_TIMEOUT]);
|
|
||||||
#ifdef CONFIG_F2FS_IOSTAT
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_enable, iostat_enable);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, iostat_period_ms, iostat_period_ms);
|
|
||||||
#endif
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, readdir_ra, readdir_ra);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_io_bytes, max_io_bytes);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_pin_file_thresh, gc_pin_file_threshold);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_super_block, extension_list, extension_list);
|
|
||||||
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||||
F2FS_RW_ATTR(FAULT_INFO_RATE, f2fs_fault_info, inject_rate, inject_rate);
|
#define FAULT_INFO_GENERAL_RW_ATTR(type, elname) \
|
||||||
F2FS_RW_ATTR(FAULT_INFO_TYPE, f2fs_fault_info, inject_type, inject_type);
|
F2FS_RW_ATTR(type, f2fs_fault_info, elname, elname)
|
||||||
#endif
|
#endif
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, data_io_flag, data_io_flag);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, node_io_flag, node_io_flag);
|
#define RESERVED_BLOCKS_GENERAL_RW_ATTR(elname) \
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_remaining_trials, gc_remaining_trials);
|
F2FS_RW_ATTR(RESERVED_BLOCKS, f2fs_sb_info, elname, elname)
|
||||||
F2FS_RW_ATTR(CPRC_INFO, ckpt_req_control, ckpt_thread_ioprio, ckpt_thread_ioprio);
|
|
||||||
|
#define CPRC_INFO_GENERAL_RW_ATTR(elname) \
|
||||||
|
F2FS_RW_ATTR(CPRC_INFO, ckpt_req_control, elname, elname)
|
||||||
|
|
||||||
|
#define ATGC_INFO_RW_ATTR(name, elname) \
|
||||||
|
F2FS_RW_ATTR(ATGC_INFO, atgc_management, name, elname)
|
||||||
|
|
||||||
|
/* GC_THREAD ATTR */
|
||||||
|
GC_THREAD_RW_ATTR(gc_urgent_sleep_time, urgent_sleep_time);
|
||||||
|
GC_THREAD_RW_ATTR(gc_min_sleep_time, min_sleep_time);
|
||||||
|
GC_THREAD_RW_ATTR(gc_max_sleep_time, max_sleep_time);
|
||||||
|
GC_THREAD_RW_ATTR(gc_no_gc_sleep_time, no_gc_sleep_time);
|
||||||
|
|
||||||
|
/* SM_INFO ATTR */
|
||||||
|
SM_INFO_RW_ATTR(reclaim_segments, rec_prefree_segments);
|
||||||
|
SM_INFO_GENERAL_RW_ATTR(ipu_policy);
|
||||||
|
SM_INFO_GENERAL_RW_ATTR(min_ipu_util);
|
||||||
|
SM_INFO_GENERAL_RW_ATTR(min_fsync_blocks);
|
||||||
|
SM_INFO_GENERAL_RW_ATTR(min_seq_blocks);
|
||||||
|
SM_INFO_GENERAL_RW_ATTR(min_hot_blocks);
|
||||||
|
SM_INFO_GENERAL_RW_ATTR(min_ssr_sections);
|
||||||
|
|
||||||
|
/* DCC_INFO ATTR */
|
||||||
|
DCC_INFO_RW_ATTR(max_small_discards, max_discards);
|
||||||
|
DCC_INFO_GENERAL_RW_ATTR(max_discard_request);
|
||||||
|
DCC_INFO_GENERAL_RW_ATTR(min_discard_issue_time);
|
||||||
|
DCC_INFO_GENERAL_RW_ATTR(mid_discard_issue_time);
|
||||||
|
DCC_INFO_GENERAL_RW_ATTR(max_discard_issue_time);
|
||||||
|
DCC_INFO_GENERAL_RW_ATTR(discard_io_aware_gran);
|
||||||
|
DCC_INFO_GENERAL_RW_ATTR(discard_urgent_util);
|
||||||
|
DCC_INFO_GENERAL_RW_ATTR(discard_granularity);
|
||||||
|
DCC_INFO_GENERAL_RW_ATTR(max_ordered_discard);
|
||||||
|
|
||||||
|
/* NM_INFO ATTR */
|
||||||
|
NM_INFO_RW_ATTR(max_roll_forward_node_blocks, max_rf_node_blocks);
|
||||||
|
NM_INFO_GENERAL_RW_ATTR(ram_thresh);
|
||||||
|
NM_INFO_GENERAL_RW_ATTR(ra_nid_pages);
|
||||||
|
NM_INFO_GENERAL_RW_ATTR(dirty_nats_ratio);
|
||||||
|
|
||||||
|
/* F2FS_SBI ATTR */
|
||||||
|
F2FS_RW_ATTR(F2FS_SBI, f2fs_super_block, extension_list, extension_list);
|
||||||
|
F2FS_SBI_RW_ATTR(gc_idle, gc_mode);
|
||||||
|
F2FS_SBI_RW_ATTR(gc_urgent, gc_mode);
|
||||||
|
F2FS_SBI_RW_ATTR(cp_interval, interval_time[CP_TIME]);
|
||||||
|
F2FS_SBI_RW_ATTR(idle_interval, interval_time[REQ_TIME]);
|
||||||
|
F2FS_SBI_RW_ATTR(discard_idle_interval, interval_time[DISCARD_TIME]);
|
||||||
|
F2FS_SBI_RW_ATTR(gc_idle_interval, interval_time[GC_TIME]);
|
||||||
|
F2FS_SBI_RW_ATTR(umount_discard_timeout, interval_time[UMOUNT_DISCARD_TIMEOUT]);
|
||||||
|
F2FS_SBI_RW_ATTR(gc_pin_file_thresh, gc_pin_file_threshold);
|
||||||
|
F2FS_SBI_RW_ATTR(gc_reclaimed_segments, gc_reclaimed_segs);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(max_victim_search);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(migration_granularity);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(dir_level);
|
||||||
|
#ifdef CONFIG_F2FS_IOSTAT
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(iostat_enable);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(iostat_period_ms);
|
||||||
|
#endif
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(readdir_ra);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(max_io_bytes);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(data_io_flag);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(node_io_flag);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(gc_remaining_trials);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(seq_file_ra_mul);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(gc_segment_mode);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(max_fragment_chunk);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(max_fragment_hole);
|
||||||
|
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(compr_written_block);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(compr_saved_block);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(compr_new_inode);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(compress_percent);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(compress_watermark);
|
||||||
|
#endif
|
||||||
|
/* atomic write */
|
||||||
|
F2FS_SBI_GENERAL_RO_ATTR(current_atomic_write);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(peak_atomic_write);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(committed_atomic_block);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(revoked_atomic_block);
|
||||||
|
/* block age extent cache */
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(hot_data_age_threshold);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(warm_data_age_threshold);
|
||||||
|
F2FS_SBI_GENERAL_RW_ATTR(last_age_weight);
|
||||||
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
|
F2FS_SBI_GENERAL_RO_ATTR(unusable_blocks_per_sec);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* STAT_INFO ATTR */
|
||||||
|
#ifdef CONFIG_F2FS_STAT_FS
|
||||||
|
STAT_INFO_RO_ATTR(cp_foreground_calls, cp_count);
|
||||||
|
STAT_INFO_RO_ATTR(cp_background_calls, bg_cp_count);
|
||||||
|
STAT_INFO_RO_ATTR(gc_foreground_calls, call_count);
|
||||||
|
STAT_INFO_RO_ATTR(gc_background_calls, bg_gc);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* FAULT_INFO ATTR */
|
||||||
|
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
||||||
|
FAULT_INFO_GENERAL_RW_ATTR(FAULT_INFO_RATE, inject_rate);
|
||||||
|
FAULT_INFO_GENERAL_RW_ATTR(FAULT_INFO_TYPE, inject_type);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* RESERVED_BLOCKS ATTR */
|
||||||
|
RESERVED_BLOCKS_GENERAL_RW_ATTR(reserved_blocks);
|
||||||
|
|
||||||
|
/* CPRC_INFO ATTR */
|
||||||
|
CPRC_INFO_GENERAL_RW_ATTR(ckpt_thread_ioprio);
|
||||||
|
|
||||||
|
/* ATGC_INFO ATTR */
|
||||||
|
ATGC_INFO_RW_ATTR(atgc_candidate_ratio, candidate_ratio);
|
||||||
|
ATGC_INFO_RW_ATTR(atgc_candidate_count, max_candidate_count);
|
||||||
|
ATGC_INFO_RW_ATTR(atgc_age_weight, age_weight);
|
||||||
|
ATGC_INFO_RW_ATTR(atgc_age_threshold, age_threshold);
|
||||||
|
|
||||||
F2FS_GENERAL_RO_ATTR(dirty_segments);
|
F2FS_GENERAL_RO_ATTR(dirty_segments);
|
||||||
F2FS_GENERAL_RO_ATTR(free_segments);
|
F2FS_GENERAL_RO_ATTR(free_segments);
|
||||||
F2FS_GENERAL_RO_ATTR(ovp_segments);
|
F2FS_GENERAL_RO_ATTR(ovp_segments);
|
||||||
|
@ -917,10 +1009,6 @@ F2FS_GENERAL_RO_ATTR(main_blkaddr);
|
||||||
F2FS_GENERAL_RO_ATTR(pending_discard);
|
F2FS_GENERAL_RO_ATTR(pending_discard);
|
||||||
F2FS_GENERAL_RO_ATTR(gc_mode);
|
F2FS_GENERAL_RO_ATTR(gc_mode);
|
||||||
#ifdef CONFIG_F2FS_STAT_FS
|
#ifdef CONFIG_F2FS_STAT_FS
|
||||||
F2FS_STAT_ATTR(STAT_INFO, f2fs_stat_info, cp_foreground_calls, cp_count);
|
|
||||||
F2FS_STAT_ATTR(STAT_INFO, f2fs_stat_info, cp_background_calls, bg_cp_count);
|
|
||||||
F2FS_STAT_ATTR(STAT_INFO, f2fs_stat_info, gc_foreground_calls, call_count);
|
|
||||||
F2FS_STAT_ATTR(STAT_INFO, f2fs_stat_info, gc_background_calls, bg_gc);
|
|
||||||
F2FS_GENERAL_RO_ATTR(moved_blocks_background);
|
F2FS_GENERAL_RO_ATTR(moved_blocks_background);
|
||||||
F2FS_GENERAL_RO_ATTR(moved_blocks_foreground);
|
F2FS_GENERAL_RO_ATTR(moved_blocks_foreground);
|
||||||
F2FS_GENERAL_RO_ATTR(avg_vblocks);
|
F2FS_GENERAL_RO_ATTR(avg_vblocks);
|
||||||
|
@ -935,8 +1023,6 @@ F2FS_FEATURE_RO_ATTR(encrypted_casefold);
|
||||||
#endif /* CONFIG_FS_ENCRYPTION */
|
#endif /* CONFIG_FS_ENCRYPTION */
|
||||||
#ifdef CONFIG_BLK_DEV_ZONED
|
#ifdef CONFIG_BLK_DEV_ZONED
|
||||||
F2FS_FEATURE_RO_ATTR(block_zoned);
|
F2FS_FEATURE_RO_ATTR(block_zoned);
|
||||||
F2FS_RO_ATTR(F2FS_SBI, f2fs_sb_info, unusable_blocks_per_sec,
|
|
||||||
unusable_blocks_per_sec);
|
|
||||||
#endif
|
#endif
|
||||||
F2FS_FEATURE_RO_ATTR(atomic_write);
|
F2FS_FEATURE_RO_ATTR(atomic_write);
|
||||||
F2FS_FEATURE_RO_ATTR(extra_attr);
|
F2FS_FEATURE_RO_ATTR(extra_attr);
|
||||||
|
@ -956,37 +1042,9 @@ F2FS_FEATURE_RO_ATTR(casefold);
|
||||||
F2FS_FEATURE_RO_ATTR(readonly);
|
F2FS_FEATURE_RO_ATTR(readonly);
|
||||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||||
F2FS_FEATURE_RO_ATTR(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);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, compress_percent, compress_percent);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, compress_watermark, compress_watermark);
|
|
||||||
#endif
|
#endif
|
||||||
F2FS_FEATURE_RO_ATTR(pin_file);
|
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);
|
|
||||||
F2FS_RW_ATTR(ATGC_INFO, atgc_management, atgc_age_weight, age_weight);
|
|
||||||
F2FS_RW_ATTR(ATGC_INFO, atgc_management, atgc_age_threshold, age_threshold);
|
|
||||||
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, seq_file_ra_mul, seq_file_ra_mul);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_segment_mode, gc_segment_mode);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, gc_reclaimed_segments, gc_reclaimed_segs);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_fragment_chunk, max_fragment_chunk);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, max_fragment_hole, max_fragment_hole);
|
|
||||||
|
|
||||||
/* For atomic write */
|
|
||||||
F2FS_RO_ATTR(F2FS_SBI, f2fs_sb_info, current_atomic_write, current_atomic_write);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, peak_atomic_write, peak_atomic_write);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, committed_atomic_block, committed_atomic_block);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, revoked_atomic_block, revoked_atomic_block);
|
|
||||||
|
|
||||||
/* For block age extent cache */
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, hot_data_age_threshold, hot_data_age_threshold);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, warm_data_age_threshold, warm_data_age_threshold);
|
|
||||||
F2FS_RW_ATTR(F2FS_SBI, f2fs_sb_info, last_age_weight, last_age_weight);
|
|
||||||
|
|
||||||
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
|
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
|
||||||
static struct attribute *f2fs_attrs[] = {
|
static struct attribute *f2fs_attrs[] = {
|
||||||
ATTR_LIST(gc_urgent_sleep_time),
|
ATTR_LIST(gc_urgent_sleep_time),
|
||||||
|
@ -1386,12 +1444,19 @@ int __init f2fs_init_sysfs(void)
|
||||||
|
|
||||||
ret = kobject_init_and_add(&f2fs_feat, &f2fs_feat_ktype,
|
ret = kobject_init_and_add(&f2fs_feat, &f2fs_feat_ktype,
|
||||||
NULL, "features");
|
NULL, "features");
|
||||||
if (ret) {
|
if (ret)
|
||||||
kobject_put(&f2fs_feat);
|
goto put_kobject;
|
||||||
kset_unregister(&f2fs_kset);
|
|
||||||
} else {
|
f2fs_proc_root = proc_mkdir("fs/f2fs", NULL);
|
||||||
f2fs_proc_root = proc_mkdir("fs/f2fs", NULL);
|
if (!f2fs_proc_root) {
|
||||||
|
ret = -ENOMEM;
|
||||||
|
goto put_kobject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
put_kobject:
|
||||||
|
kobject_put(&f2fs_feat);
|
||||||
|
kset_unregister(&f2fs_kset);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1430,23 +1495,24 @@ int f2fs_register_sysfs(struct f2fs_sb_info *sbi)
|
||||||
if (err)
|
if (err)
|
||||||
goto put_feature_list_kobj;
|
goto put_feature_list_kobj;
|
||||||
|
|
||||||
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);
|
if (!sbi->s_proc) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto put_feature_list_kobj;
|
||||||
|
}
|
||||||
|
|
||||||
if (sbi->s_proc) {
|
proc_create_single_data("segment_info", 0444, sbi->s_proc,
|
||||||
proc_create_single_data("segment_info", 0444, sbi->s_proc,
|
|
||||||
segment_info_seq_show, sb);
|
segment_info_seq_show, sb);
|
||||||
proc_create_single_data("segment_bits", 0444, sbi->s_proc,
|
proc_create_single_data("segment_bits", 0444, sbi->s_proc,
|
||||||
segment_bits_seq_show, sb);
|
segment_bits_seq_show, sb);
|
||||||
#ifdef CONFIG_F2FS_IOSTAT
|
#ifdef CONFIG_F2FS_IOSTAT
|
||||||
proc_create_single_data("iostat_info", 0444, sbi->s_proc,
|
proc_create_single_data("iostat_info", 0444, sbi->s_proc,
|
||||||
iostat_info_seq_show, sb);
|
iostat_info_seq_show, sb);
|
||||||
#endif
|
#endif
|
||||||
proc_create_single_data("victim_bits", 0444, sbi->s_proc,
|
proc_create_single_data("victim_bits", 0444, sbi->s_proc,
|
||||||
victim_bits_seq_show, sb);
|
victim_bits_seq_show, sb);
|
||||||
proc_create_single_data("discard_plist_info", 0444, sbi->s_proc,
|
proc_create_single_data("discard_plist_info", 0444, sbi->s_proc,
|
||||||
discard_plist_seq_show, sb);
|
discard_plist_seq_show, sb);
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
put_feature_list_kobj:
|
put_feature_list_kobj:
|
||||||
kobject_put(&sbi->s_feature_list_kobj);
|
kobject_put(&sbi->s_feature_list_kobj);
|
||||||
|
@ -1462,8 +1528,7 @@ put_sb_kobj:
|
||||||
|
|
||||||
void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi)
|
void f2fs_unregister_sysfs(struct f2fs_sb_info *sbi)
|
||||||
{
|
{
|
||||||
if (sbi->s_proc)
|
remove_proc_subtree(sbi->sb->s_id, f2fs_proc_root);
|
||||||
remove_proc_subtree(sbi->sb->s_id, f2fs_proc_root);
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -528,10 +528,12 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
|
||||||
if (len > F2FS_NAME_LEN)
|
if (len > F2FS_NAME_LEN)
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
|
if (!ipage)
|
||||||
|
f2fs_down_read(&F2FS_I(inode)->i_xattr_sem);
|
||||||
error = lookup_all_xattrs(inode, ipage, index, len, name,
|
error = lookup_all_xattrs(inode, ipage, index, len, name,
|
||||||
&entry, &base_addr, &base_size, &is_inline);
|
&entry, &base_addr, &base_size, &is_inline);
|
||||||
f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
|
if (!ipage)
|
||||||
|
f2fs_up_read(&F2FS_I(inode)->i_xattr_sem);
|
||||||
if (error)
|
if (error)
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@ struct f2fs_xattr_entry {
|
||||||
sizeof(struct f2fs_xattr_header) - \
|
sizeof(struct f2fs_xattr_header) - \
|
||||||
sizeof(struct f2fs_xattr_entry))
|
sizeof(struct f2fs_xattr_entry))
|
||||||
|
|
||||||
|
#define MIN_INLINE_XATTR_SIZE (sizeof(struct f2fs_xattr_header) / sizeof(__le32))
|
||||||
#define MAX_INLINE_XATTR_SIZE \
|
#define MAX_INLINE_XATTR_SIZE \
|
||||||
(DEF_ADDRS_PER_INODE - \
|
(DEF_ADDRS_PER_INODE - \
|
||||||
F2FS_TOTAL_EXTRA_ATTR_SIZE / sizeof(__le32) - \
|
F2FS_TOTAL_EXTRA_ATTR_SIZE / sizeof(__le32) - \
|
||||||
|
|
|
@ -103,6 +103,7 @@ enum f2fs_error {
|
||||||
ERROR_INCONSISTENT_SIT,
|
ERROR_INCONSISTENT_SIT,
|
||||||
ERROR_CORRUPTED_VERITY_XATTR,
|
ERROR_CORRUPTED_VERITY_XATTR,
|
||||||
ERROR_CORRUPTED_XATTR,
|
ERROR_CORRUPTED_XATTR,
|
||||||
|
ERROR_INVALID_NODE_REFERENCE,
|
||||||
ERROR_MAX,
|
ERROR_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1512,7 +1512,7 @@ DEFINE_EVENT(f2fs_discard, f2fs_remove_discard,
|
||||||
TP_ARGS(dev, blkstart, blklen)
|
TP_ARGS(dev, blkstart, blklen)
|
||||||
);
|
);
|
||||||
|
|
||||||
TRACE_EVENT(f2fs_issue_reset_zone,
|
DECLARE_EVENT_CLASS(f2fs_reset_zone,
|
||||||
|
|
||||||
TP_PROTO(struct block_device *dev, block_t blkstart),
|
TP_PROTO(struct block_device *dev, block_t blkstart),
|
||||||
|
|
||||||
|
@ -1528,11 +1528,25 @@ TRACE_EVENT(f2fs_issue_reset_zone,
|
||||||
__entry->blkstart = blkstart;
|
__entry->blkstart = blkstart;
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("dev = (%d,%d), reset zone at block = 0x%llx",
|
TP_printk("dev = (%d,%d), zone at block = 0x%llx",
|
||||||
show_dev(__entry->dev),
|
show_dev(__entry->dev),
|
||||||
(unsigned long long)__entry->blkstart)
|
(unsigned long long)__entry->blkstart)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(f2fs_reset_zone, f2fs_queue_reset_zone,
|
||||||
|
|
||||||
|
TP_PROTO(struct block_device *dev, block_t blkstart),
|
||||||
|
|
||||||
|
TP_ARGS(dev, blkstart)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(f2fs_reset_zone, f2fs_issue_reset_zone,
|
||||||
|
|
||||||
|
TP_PROTO(struct block_device *dev, block_t blkstart),
|
||||||
|
|
||||||
|
TP_ARGS(dev, blkstart)
|
||||||
|
);
|
||||||
|
|
||||||
TRACE_EVENT(f2fs_issue_flush,
|
TRACE_EVENT(f2fs_issue_flush,
|
||||||
|
|
||||||
TP_PROTO(struct block_device *dev, unsigned int nobarrier,
|
TP_PROTO(struct block_device *dev, unsigned int nobarrier,
|
||||||
|
@ -1979,6 +1993,7 @@ TRACE_EVENT(f2fs_iostat,
|
||||||
__field(unsigned long long, fs_nrio)
|
__field(unsigned long long, fs_nrio)
|
||||||
__field(unsigned long long, fs_mrio)
|
__field(unsigned long long, fs_mrio)
|
||||||
__field(unsigned long long, fs_discard)
|
__field(unsigned long long, fs_discard)
|
||||||
|
__field(unsigned long long, fs_reset_zone)
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_fast_assign(
|
TP_fast_assign(
|
||||||
|
@ -2010,12 +2025,14 @@ TRACE_EVENT(f2fs_iostat,
|
||||||
__entry->fs_nrio = iostat[FS_NODE_READ_IO];
|
__entry->fs_nrio = iostat[FS_NODE_READ_IO];
|
||||||
__entry->fs_mrio = iostat[FS_META_READ_IO];
|
__entry->fs_mrio = iostat[FS_META_READ_IO];
|
||||||
__entry->fs_discard = iostat[FS_DISCARD_IO];
|
__entry->fs_discard = iostat[FS_DISCARD_IO];
|
||||||
|
__entry->fs_reset_zone = iostat[FS_ZONE_RESET_IO];
|
||||||
),
|
),
|
||||||
|
|
||||||
TP_printk("dev = (%d,%d), "
|
TP_printk("dev = (%d,%d), "
|
||||||
"app [write=%llu (direct=%llu, buffered=%llu), mapped=%llu, "
|
"app [write=%llu (direct=%llu, buffered=%llu), mapped=%llu, "
|
||||||
"compr(buffered=%llu, mapped=%llu)], "
|
"compr(buffered=%llu, mapped=%llu)], "
|
||||||
"fs [data=%llu, cdata=%llu, node=%llu, meta=%llu, discard=%llu], "
|
"fs [data=%llu, cdata=%llu, node=%llu, meta=%llu, discard=%llu, "
|
||||||
|
"reset_zone=%llu], "
|
||||||
"gc [data=%llu, node=%llu], "
|
"gc [data=%llu, node=%llu], "
|
||||||
"cp [data=%llu, node=%llu, meta=%llu], "
|
"cp [data=%llu, node=%llu, meta=%llu], "
|
||||||
"app [read=%llu (direct=%llu, buffered=%llu), mapped=%llu], "
|
"app [read=%llu (direct=%llu, buffered=%llu), mapped=%llu], "
|
||||||
|
@ -2026,6 +2043,7 @@ TRACE_EVENT(f2fs_iostat,
|
||||||
__entry->app_bio, __entry->app_mio, __entry->app_bcdio,
|
__entry->app_bio, __entry->app_mio, __entry->app_bcdio,
|
||||||
__entry->app_mcdio, __entry->fs_dio, __entry->fs_cdio,
|
__entry->app_mcdio, __entry->fs_dio, __entry->fs_cdio,
|
||||||
__entry->fs_nio, __entry->fs_mio, __entry->fs_discard,
|
__entry->fs_nio, __entry->fs_mio, __entry->fs_discard,
|
||||||
|
__entry->fs_reset_zone,
|
||||||
__entry->fs_gc_dio, __entry->fs_gc_nio, __entry->fs_cp_dio,
|
__entry->fs_gc_dio, __entry->fs_gc_nio, __entry->fs_cp_dio,
|
||||||
__entry->fs_cp_nio, __entry->fs_cp_mio,
|
__entry->fs_cp_nio, __entry->fs_cp_mio,
|
||||||
__entry->app_rio, __entry->app_drio, __entry->app_brio,
|
__entry->app_rio, __entry->app_drio, __entry->app_brio,
|
||||||
|
|
Loading…
Reference in New Issue