mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 02:17:36 -04:00
Merge tag 'f2fs-for-7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs
Pull f2fs updates from Jaegeuk Kim: "The changes primarily focus on filesystem error reporting, reducing memory footprint by reverting in-memory data structures used for runtime validation, honoring FDP hints, and adding trace and debug logs. In addition, there are critical bug fixes resolving out-of-bounds read vulnerabilities in inline directory and ACL handling, potential deadlocks in balance_fs, use-after-free issues in atomic writes, and false data/node type assignments in large sections. Enhancements: - Revert in-memory sit version and block bitmaps - support to report fserror - add trace_f2fs_fault_report - add iostat latency tracking for direct IO - add logs in f2fs_disable_checkpoint() - honor per-I/O write streams for direct writes - map data writes to FDP streams - skip inode folio lookup for cached overwrite - skip direct I/O iostat context when disabled - revert "check in-memory block bitmap" - revert "check in-memory sit version bitmap" Fixes: - optimize representative type determination in GC - fix incorrect FI_NO_EXTENT handling in __destroy_extent_node() - fix potential deadlock in f2fs_balance_fs() - fix potential deadlock in gc_merge path of f2fs_balance_fs() - atomic: fix UAF issue on f2fs_inode_info.atomic_inode - fix missing read bio submission on large folio error - pass correct iostat type for single node writes - fix to do sanity check on f2fs_get_node_folio_ra() - validate orphan inode entry count - keep atomic write retry from zeroing original data - read COW data with the original inode during atomic write - validate inline dentry name lengths before conversion - validate dentry name length before lookup compares it - reject setattr size changes on large folio files - revert "remove non-uptodate folio from the page cache in move_data_block" - validate ACL entry sizes in f2fs_acl_from_disk() - bound i_inline_xattr_size for non-inline-xattr inodes - fix listxattr handling of corrupted xattr entries - fix to round down start offset of fallocate for pin file" * tag 'f2fs-for-7.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs: (42 commits) f2fs: fix to round down start offset of fallocate for pin file f2fs: fix listxattr handling of corrupted xattr entries f2fs: skip direct I/O iostat context when disabled f2fs: remove unneeded f2fs_is_compressed_page() f2fs: avoid unnecessary fscrypt_finalize_bounce_page() f2fs: avoid unnecessary sanity check on ckpt_valid_blocks f2fs: misc cleanup in f2fs_record_stop_reason() f2fs: fix wrong description in printed log f2fs: bound i_inline_xattr_size for non-inline-xattr inodes f2fs: validate ACL entry sizes in f2fs_acl_from_disk() Revert "f2fs: remove non-uptodate folio from the page cache in move_data_block" f2fs: Split f2fs_write_end_io() f2fs: Rename f2fs_post_read_wq into f2fs_wq f2fs: Prepare for supporting delayed bio completion f2fs: reject setattr size changes on large folio files f2fs: validate dentry name length before lookup compares it f2fs: validate inline dentry name lengths before conversion f2fs: read COW data with the original inode during atomic write f2fs: skip inode folio lookup for cached overwrite f2fs: keep atomic write retry from zeroing original data ...
This commit is contained in:
@@ -270,7 +270,8 @@ Description: Shows all enabled kernel features.
|
||||
inode_checksum, flexible_inline_xattr, quota_ino,
|
||||
inode_crtime, lost_found, verity, sb_checksum,
|
||||
casefold, readonly, compression, test_dummy_encryption_v2,
|
||||
atomic_write, pin_file, encrypted_casefold, linear_lookup.
|
||||
atomic_write, pin_file, encrypted_casefold, linear_lookup,
|
||||
fserror.
|
||||
|
||||
What: /sys/fs/f2fs/<disk>/inject_rate
|
||||
Date: May 2016
|
||||
@@ -1000,4 +1001,4 @@ Contact: "Chao Yu" <chao@kernel.org>
|
||||
Description: It can be used to tune priority of f2fs critical task, e.g. f2fs_ckpt, f2fs_gc
|
||||
threads, limitation as below:
|
||||
- it requires user has CAP_SYS_NICE capability.
|
||||
- the range is [100, 139], by default the value is 100.
|
||||
- the range is [100, 139], by default the value is 120.
|
||||
|
||||
@@ -137,6 +137,15 @@ noacl Disable POSIX Access Control List. Note: acl is enabled
|
||||
active_logs=%u Support configuring the number of active logs. In the
|
||||
current design, f2fs supports only 2, 4, and 6 logs.
|
||||
Default number is 6.
|
||||
When the underlying block device exposes write
|
||||
streams, the default active_logs=6 configuration
|
||||
maps hot, warm, and cold DATA writes to streams 1,
|
||||
2, and 3, respectively. If only one or two write
|
||||
streams are available, f2fs falls back to mapping
|
||||
all DATA writes to stream 1 or mapping hot/warm
|
||||
to stream 1 and cold to stream 2. If no write
|
||||
streams are exposed, f2fs leaves the stream
|
||||
unset.
|
||||
disable_ext_identify Disable the extension list configured by mkfs, so f2fs
|
||||
is not aware of cold files such as media files.
|
||||
inline_xattr Enable the inline xattrs feature.
|
||||
|
||||
@@ -47,6 +47,7 @@ static inline int f2fs_acl_count(size_t size)
|
||||
static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
|
||||
{
|
||||
int i, count;
|
||||
int err = -EINVAL;
|
||||
struct posix_acl *acl;
|
||||
struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value;
|
||||
struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1);
|
||||
@@ -70,8 +71,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
|
||||
if ((char *)entry > end)
|
||||
if (unlikely((char *)entry +
|
||||
sizeof(struct f2fs_acl_entry_short) > end)) {
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag);
|
||||
acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm);
|
||||
@@ -86,6 +90,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
|
||||
break;
|
||||
|
||||
case ACL_USER:
|
||||
if (unlikely((char *)entry +
|
||||
sizeof(struct f2fs_acl_entry) > end)) {
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
acl->a_entries[i].e_uid =
|
||||
make_kuid(&init_user_ns,
|
||||
le32_to_cpu(entry->e_id));
|
||||
@@ -93,6 +102,11 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
|
||||
sizeof(struct f2fs_acl_entry));
|
||||
break;
|
||||
case ACL_GROUP:
|
||||
if (unlikely((char *)entry +
|
||||
sizeof(struct f2fs_acl_entry) > end)) {
|
||||
err = -EFSCORRUPTED;
|
||||
goto fail;
|
||||
}
|
||||
acl->a_entries[i].e_gid =
|
||||
make_kgid(&init_user_ns,
|
||||
le32_to_cpu(entry->e_id));
|
||||
@@ -108,7 +122,7 @@ static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size)
|
||||
return acl;
|
||||
fail:
|
||||
posix_acl_release(acl);
|
||||
return ERR_PTR(-EINVAL);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static void *f2fs_acl_to_disk(struct f2fs_sb_info *sbi,
|
||||
|
||||
@@ -943,6 +943,7 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
||||
for (i = 0; i < orphan_blocks; i++) {
|
||||
struct folio *folio;
|
||||
struct f2fs_orphan_block *orphan_blk;
|
||||
unsigned int entry_count;
|
||||
|
||||
folio = f2fs_get_meta_folio(sbi, start_blk + i);
|
||||
if (IS_ERR(folio)) {
|
||||
@@ -951,7 +952,18 @@ int f2fs_recover_orphan_inodes(struct f2fs_sb_info *sbi)
|
||||
}
|
||||
|
||||
orphan_blk = folio_address(folio);
|
||||
for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) {
|
||||
entry_count = le32_to_cpu(orphan_blk->entry_count);
|
||||
if (entry_count > F2FS_ORPHANS_PER_BLOCK) {
|
||||
f2fs_err(sbi, "invalid orphan inode entry count %u",
|
||||
entry_count);
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_handle_error(sbi, ERROR_INCONSISTENT_ORPHAN);
|
||||
err = -EFSCORRUPTED;
|
||||
f2fs_folio_put(folio, true);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (j = 0; j < entry_count; j++) {
|
||||
nid_t ino = le32_to_cpu(orphan_blk->ino[j]);
|
||||
|
||||
err = recover_orphan_inode(sbi, ino);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <linux/lz4.h>
|
||||
#include <linux/zstd.h>
|
||||
#include <linux/folio_batch.h>
|
||||
#include <linux/fserror.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@@ -760,6 +761,7 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
|
||||
|
||||
/* Avoid f2fs_commit_super in irq context */
|
||||
f2fs_handle_error(sbi, ERROR_FAIL_DECOMPRESSION);
|
||||
fserror_report_file_metadata(dic->inode, ret, GFP_NOFS);
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
@@ -1453,6 +1455,9 @@ static int f2fs_write_compressed_pages(struct compress_ctx *cc,
|
||||
out_destroy_crypt:
|
||||
page_array_free(sbi, cic->rpages, cc->cluster_size);
|
||||
|
||||
if (!fio.encrypted)
|
||||
goto out_put_cic;
|
||||
|
||||
for (--i; i >= 0; i--) {
|
||||
if (!cc->cpages[i])
|
||||
continue;
|
||||
@@ -1482,8 +1487,7 @@ void f2fs_compress_write_end_io(struct bio *bio, struct folio *folio)
|
||||
struct page *page = &folio->page;
|
||||
struct f2fs_sb_info *sbi = bio->bi_private;
|
||||
struct compress_io_ctx *cic = folio->private;
|
||||
enum count_type type = WB_DATA_TYPE(folio,
|
||||
f2fs_is_compressed_page(folio));
|
||||
enum count_type type = WB_DATA_TYPE(folio, true);
|
||||
int i;
|
||||
|
||||
if (unlikely(bio->bi_status != BLK_STS_OK))
|
||||
@@ -1807,7 +1811,7 @@ static void f2fs_put_dic(struct decompress_io_ctx *dic, bool in_task)
|
||||
f2fs_free_dic(dic, false);
|
||||
} else {
|
||||
INIT_WORK(&dic->free_work, f2fs_late_free_dic);
|
||||
queue_work(dic->sbi->post_read_wq, &dic->free_work);
|
||||
queue_work(dic->sbi->wq, &dic->free_work);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
133
fs/f2fs/data.c
133
fs/f2fs/data.c
@@ -20,6 +20,7 @@
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/fiemap.h>
|
||||
#include <linux/iomap.h>
|
||||
#include <linux/fserror.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@@ -40,12 +41,17 @@ struct f2fs_folio_state {
|
||||
unsigned int read_pages_pending;
|
||||
};
|
||||
|
||||
struct f2fs_bio {
|
||||
struct work_struct work;
|
||||
struct bio bio;
|
||||
};
|
||||
|
||||
#define F2FS_BIO_POOL_SIZE NR_CURSEG_TYPE
|
||||
|
||||
int __init f2fs_init_bioset(void)
|
||||
{
|
||||
return bioset_init(&f2fs_bioset, F2FS_BIO_POOL_SIZE,
|
||||
0, BIOSET_NEED_BVECS);
|
||||
offsetof(struct f2fs_bio, bio), BIOSET_NEED_BVECS);
|
||||
}
|
||||
|
||||
void f2fs_destroy_bioset(void)
|
||||
@@ -336,7 +342,7 @@ static void f2fs_read_end_io(struct bio *bio)
|
||||
f2fs_handle_step_decompress(ctx, intask);
|
||||
} else if (enabled_steps) {
|
||||
INIT_WORK(&ctx->work, f2fs_post_read_work);
|
||||
queue_work(ctx->sbi->post_read_wq, &ctx->work);
|
||||
queue_work(ctx->sbi->wq, &ctx->work);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -344,14 +350,11 @@ static void f2fs_read_end_io(struct bio *bio)
|
||||
f2fs_verify_and_finish_bio(bio, intask);
|
||||
}
|
||||
|
||||
static void f2fs_write_end_io(struct bio *bio)
|
||||
static void f2fs_write_end_bio(struct bio *bio)
|
||||
{
|
||||
struct f2fs_sb_info *sbi;
|
||||
struct f2fs_sb_info *sbi = bio->bi_private;
|
||||
struct folio_iter fi;
|
||||
|
||||
iostat_update_and_unbind_ctx(bio);
|
||||
sbi = bio->bi_private;
|
||||
|
||||
if (time_to_inject(sbi, FAULT_WRITE_IO))
|
||||
bio->bi_status = BLK_STS_IOERR;
|
||||
|
||||
@@ -377,9 +380,10 @@ static void f2fs_write_end_io(struct bio *bio)
|
||||
|
||||
if (unlikely(bio->bi_status != BLK_STS_OK)) {
|
||||
mapping_set_error(folio->mapping, -EIO);
|
||||
if (type == F2FS_WB_CP_DATA)
|
||||
if (type == F2FS_WB_CP_DATA) {
|
||||
f2fs_stop_checkpoint(sbi, true,
|
||||
STOP_CP_REASON_WRITE_FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_node_folio(folio)) {
|
||||
@@ -407,6 +411,13 @@ static void f2fs_write_end_io(struct bio *bio)
|
||||
bio_put(bio);
|
||||
}
|
||||
|
||||
static void f2fs_write_end_io(struct bio *bio)
|
||||
{
|
||||
iostat_update_and_unbind_ctx(bio);
|
||||
|
||||
f2fs_write_end_bio(bio);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_ZONED
|
||||
static void f2fs_zone_write_end_io(struct bio *bio)
|
||||
{
|
||||
@@ -509,6 +520,8 @@ static struct bio *__bio_alloc(struct f2fs_io_info *fio, int npages)
|
||||
bio->bi_private = sbi;
|
||||
bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi,
|
||||
fio->type, fio->temp);
|
||||
bio->bi_write_stream = f2fs_io_type_to_write_stream(bdev, fio->type,
|
||||
fio->temp);
|
||||
}
|
||||
iostat_alloc_and_bind_ctx(sbi, bio, NULL);
|
||||
|
||||
@@ -943,6 +956,35 @@ void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi,
|
||||
}
|
||||
}
|
||||
|
||||
void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct bio_entry *be, *tmp;
|
||||
struct f2fs_bio_info *io;
|
||||
enum temp_type temp;
|
||||
|
||||
for (temp = HOT; temp < NR_TEMP_TYPE; temp++) {
|
||||
LIST_HEAD(list);
|
||||
|
||||
io = sbi->write_io[DATA] + temp;
|
||||
|
||||
/* A lockless list_empty() check is safe here: any bios from
|
||||
* other kworkers that we miss will be submitted by those
|
||||
* kworkers accordingly.
|
||||
*/
|
||||
if (list_empty(&io->bio_list))
|
||||
continue;
|
||||
|
||||
f2fs_down_write(&io->bio_list_lock);
|
||||
list_splice_init(&io->bio_list, &list);
|
||||
f2fs_up_write(&io->bio_list_lock);
|
||||
|
||||
list_for_each_entry_safe(be, tmp, &list, list) {
|
||||
f2fs_submit_write_bio(sbi, be->bio, DATA);
|
||||
del_bio_entry(be);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int f2fs_merge_page_bio(struct f2fs_io_info *fio)
|
||||
{
|
||||
struct bio *bio = *fio->bio;
|
||||
@@ -1748,6 +1790,7 @@ int f2fs_map_blocks(struct inode *inode, struct f2fs_map_blocks *map, int flag)
|
||||
err = -EFSCORRUPTED;
|
||||
f2fs_handle_error(sbi,
|
||||
ERROR_CORRUPTED_CLUSTER);
|
||||
fserror_report_file_metadata(inode, err, GFP_NOFS);
|
||||
goto sync_out;
|
||||
}
|
||||
|
||||
@@ -2495,7 +2538,7 @@ static int f2fs_read_data_large_folio(struct inode *inode,
|
||||
unsigned nrpages;
|
||||
struct f2fs_folio_state *ffs;
|
||||
int ret = 0;
|
||||
bool folio_in_bio;
|
||||
bool folio_in_bio = false;
|
||||
|
||||
if (!IS_IMMUTABLE(inode) || f2fs_compressed_file(inode)) {
|
||||
if (folio)
|
||||
@@ -2611,18 +2654,17 @@ static int f2fs_read_data_large_folio(struct inode *inode,
|
||||
}
|
||||
trace_f2fs_read_folio(folio, DATA);
|
||||
err_out:
|
||||
if (!folio_in_bio) {
|
||||
if (!folio_in_bio)
|
||||
folio_end_read(folio, !ret);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
if (rac) {
|
||||
folio = readahead_folio(rac);
|
||||
goto next_folio;
|
||||
}
|
||||
out:
|
||||
f2fs_submit_read_bio(F2FS_I_SB(inode), bio, DATA);
|
||||
if (ret) {
|
||||
if (ret && folio_in_bio) {
|
||||
/* Wait bios and clear uptodate. */
|
||||
folio_lock(folio);
|
||||
folio_clear_uptodate(folio);
|
||||
@@ -3686,6 +3728,11 @@ static int prepare_write_begin(struct f2fs_sb_info *sbi,
|
||||
int flag = F2FS_GET_BLOCK_PRE_AIO;
|
||||
int err = 0;
|
||||
|
||||
if (!f2fs_has_inline_data(inode) && !f2fs_compressed_file(inode) &&
|
||||
(pos & PAGE_MASK) < i_size_read(inode) &&
|
||||
f2fs_lookup_read_extent_cache_block(inode, index, blk_addr))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If a whole page is being written and we already preallocated all the
|
||||
* blocks, then there is no need to get a block address now.
|
||||
@@ -3822,13 +3869,14 @@ static int __reserve_data_block(struct inode *inode, pgoff_t index,
|
||||
|
||||
static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
|
||||
struct folio *folio, loff_t pos, unsigned int len,
|
||||
block_t *blk_addr, bool *node_changed, bool *use_cow)
|
||||
block_t *blk_addr, bool *node_changed)
|
||||
{
|
||||
struct inode *inode = folio->mapping->host;
|
||||
struct inode *cow_inode = F2FS_I(inode)->cow_inode;
|
||||
pgoff_t index = folio->index;
|
||||
int err = 0;
|
||||
block_t ori_blk_addr = NULL_ADDR;
|
||||
bool cow_has_reserved_block = false;
|
||||
|
||||
/* If pos is beyond the end of file, reserve a new block in COW inode */
|
||||
if ((pos & PAGE_MASK) >= i_size_read(inode))
|
||||
@@ -3836,12 +3884,14 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
|
||||
|
||||
/* Look for the block in COW inode first */
|
||||
err = __find_data_block(cow_inode, index, blk_addr);
|
||||
if (err) {
|
||||
if (err)
|
||||
return err;
|
||||
} else if (*blk_addr != NULL_ADDR) {
|
||||
*use_cow = true;
|
||||
|
||||
if (__is_valid_data_blkaddr(*blk_addr))
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (*blk_addr == NEW_ADDR)
|
||||
cow_has_reserved_block = true;
|
||||
|
||||
if (is_inode_flag_set(inode, FI_ATOMIC_REPLACE))
|
||||
goto reserve_block;
|
||||
@@ -3853,10 +3903,13 @@ static int prepare_atomic_write_begin(struct f2fs_sb_info *sbi,
|
||||
|
||||
reserve_block:
|
||||
/* Finally, we should reserve a new block in COW inode for the update */
|
||||
err = __reserve_data_block(cow_inode, index, blk_addr, node_changed);
|
||||
if (err)
|
||||
return err;
|
||||
inc_atomic_write_cnt(inode);
|
||||
if (!cow_has_reserved_block) {
|
||||
err = __reserve_data_block(cow_inode, index, blk_addr,
|
||||
node_changed);
|
||||
if (err)
|
||||
return err;
|
||||
inc_atomic_write_cnt(inode);
|
||||
}
|
||||
|
||||
if (ori_blk_addr != NULL_ADDR)
|
||||
*blk_addr = ori_blk_addr;
|
||||
@@ -3873,7 +3926,6 @@ static int f2fs_write_begin(const struct kiocb *iocb,
|
||||
struct folio *folio;
|
||||
pgoff_t index = pos >> PAGE_SHIFT;
|
||||
bool need_balance = false;
|
||||
bool use_cow = false;
|
||||
block_t blkaddr = NULL_ADDR;
|
||||
int err = 0;
|
||||
|
||||
@@ -3936,7 +3988,7 @@ static int f2fs_write_begin(const struct kiocb *iocb,
|
||||
|
||||
if (f2fs_is_atomic_file(inode))
|
||||
err = prepare_atomic_write_begin(sbi, folio, pos, len,
|
||||
&blkaddr, &need_balance, &use_cow);
|
||||
&blkaddr, &need_balance);
|
||||
else
|
||||
err = prepare_write_begin(sbi, folio, pos, len,
|
||||
&blkaddr, &need_balance);
|
||||
@@ -3976,8 +4028,15 @@ static int f2fs_write_begin(const struct kiocb *iocb,
|
||||
err = -EFSCORRUPTED;
|
||||
goto put_folio;
|
||||
}
|
||||
f2fs_submit_page_read(use_cow ? F2FS_I(inode)->cow_inode :
|
||||
inode,
|
||||
/*
|
||||
* Although the block may be stored in the COW inode, the folio
|
||||
* belongs to @inode and its data was encrypted (or not) using
|
||||
* @inode's context (see f2fs_encrypt_one_page()). Read with
|
||||
* @inode so the post-read decryption decision matches the
|
||||
* folio's owner; otherwise an unencrypted @inode whose COW inode
|
||||
* is encrypted hits a NULL ->i_crypt_info on decryption.
|
||||
*/
|
||||
f2fs_submit_page_read(inode,
|
||||
NULL, /* can't write to fsverity files */
|
||||
folio, blkaddr, 0, true);
|
||||
|
||||
@@ -4472,23 +4531,17 @@ void f2fs_destroy_post_read_processing(void)
|
||||
kmem_cache_destroy(bio_post_read_ctx_cache);
|
||||
}
|
||||
|
||||
int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi)
|
||||
int f2fs_init_wq(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (!f2fs_sb_has_encrypt(sbi) &&
|
||||
!f2fs_sb_has_verity(sbi) &&
|
||||
!f2fs_sb_has_compression(sbi))
|
||||
return 0;
|
||||
|
||||
sbi->post_read_wq = alloc_workqueue("f2fs_post_read_wq",
|
||||
WQ_UNBOUND | WQ_HIGHPRI,
|
||||
num_online_cpus());
|
||||
return sbi->post_read_wq ? 0 : -ENOMEM;
|
||||
sbi->wq = alloc_workqueue("f2fs_wq", WQ_UNBOUND | WQ_HIGHPRI,
|
||||
num_online_cpus());
|
||||
return sbi->wq ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
void f2fs_destroy_post_read_wq(struct f2fs_sb_info *sbi)
|
||||
void f2fs_destroy_wq(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
if (sbi->post_read_wq)
|
||||
destroy_workqueue(sbi->post_read_wq);
|
||||
if (sbi->wq)
|
||||
destroy_workqueue(sbi->wq);
|
||||
}
|
||||
|
||||
int __init f2fs_init_bio_entry_cache(void)
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/filelock.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/unicode.h>
|
||||
#include <linux/fserror.h>
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
#include "acl.h"
|
||||
@@ -249,6 +250,11 @@ struct f2fs_dir_entry *f2fs_find_target_dentry(const struct f2fs_dentry_ptr *d,
|
||||
continue;
|
||||
}
|
||||
|
||||
if (unlikely(le16_to_cpu(de->name_len) > F2FS_NAME_LEN ||
|
||||
bit_pos + GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)) >
|
||||
d->max))
|
||||
return ERR_PTR(-EFSCORRUPTED);
|
||||
|
||||
if (!use_hash || de->hash_code == fname->hash) {
|
||||
res = f2fs_match_name(d->inode, fname,
|
||||
d->filename[bit_pos],
|
||||
@@ -1020,6 +1026,7 @@ int f2fs_fill_dentries(struct dir_context *ctx, struct f2fs_dentry_ptr *d,
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
err = -EFSCORRUPTED;
|
||||
f2fs_handle_error(sbi, ERROR_CORRUPTED_DIRENT);
|
||||
fserror_report_file_metadata(d->inode, err, GFP_NOFS);
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,10 +119,9 @@ static bool __may_extent_tree(struct inode *inode, enum extent_type type)
|
||||
if (!__init_may_extent_tree(inode, type))
|
||||
return false;
|
||||
|
||||
if (is_inode_flag_set(inode, FI_NO_EXTENT))
|
||||
return false;
|
||||
|
||||
if (type == EX_READ) {
|
||||
if (is_inode_flag_set(inode, FI_NO_EXTENT))
|
||||
return false;
|
||||
if (is_inode_flag_set(inode, FI_COMPRESSED_FILE) &&
|
||||
!f2fs_sb_has_readonly(F2FS_I_SB(inode)))
|
||||
return false;
|
||||
@@ -645,14 +644,10 @@ static unsigned int __destroy_extent_node(struct inode *inode,
|
||||
|
||||
while (atomic_read(&et->node_cnt)) {
|
||||
write_lock(&et->lock);
|
||||
if (!is_inode_flag_set(inode, FI_NO_EXTENT))
|
||||
set_inode_flag(inode, FI_NO_EXTENT);
|
||||
node_cnt += __free_extent_tree(sbi, et, nr_shrink);
|
||||
write_unlock(&et->lock);
|
||||
}
|
||||
|
||||
f2fs_bug_on(sbi, atomic_read(&et->node_cnt));
|
||||
|
||||
return node_cnt;
|
||||
}
|
||||
|
||||
@@ -691,12 +686,12 @@ static void __update_extent_tree_range(struct inode *inode,
|
||||
|
||||
write_lock(&et->lock);
|
||||
|
||||
if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
|
||||
write_unlock(&et->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == EX_READ) {
|
||||
if (is_inode_flag_set(inode, FI_NO_EXTENT)) {
|
||||
write_unlock(&et->lock);
|
||||
return;
|
||||
}
|
||||
|
||||
prev = et->largest;
|
||||
dei.len = 0;
|
||||
|
||||
|
||||
@@ -96,6 +96,15 @@ extern const char *f2fs_fault_name[FAULT_MAX];
|
||||
#define DEFAULT_FAILURE_RETRY_COUNT 1
|
||||
#endif
|
||||
|
||||
enum {
|
||||
REPORT_FAULT_NEED_FSCK,
|
||||
REPORT_FAULT_STOP_CP,
|
||||
REPORT_FAULT_MAX,
|
||||
};
|
||||
|
||||
void f2fs_fault_report(struct super_block *sb, unsigned int err_code,
|
||||
const char *func, unsigned int data);
|
||||
|
||||
/*
|
||||
* For mount options
|
||||
*/
|
||||
@@ -1583,6 +1592,7 @@ enum node_type {
|
||||
NODE_TYPE_INODE,
|
||||
NODE_TYPE_XATTR,
|
||||
NODE_TYPE_NON_INODE,
|
||||
NODE_TYPE_NON_IXNODE, /* non inode and xnode */
|
||||
};
|
||||
|
||||
/* a threshold of maximum elapsed time in critical region to print tracepoint */
|
||||
@@ -1969,7 +1979,7 @@ struct f2fs_sb_info {
|
||||
/* Precomputed FS UUID checksum for seeding other checksums */
|
||||
__u32 s_chksum_seed;
|
||||
|
||||
struct workqueue_struct *post_read_wq; /* post read workqueue */
|
||||
struct workqueue_struct *wq; /* bio completion workqueue */
|
||||
|
||||
/*
|
||||
* If we are in irq context, let's update error information into
|
||||
@@ -1980,6 +1990,7 @@ struct f2fs_sb_info {
|
||||
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 stop_reason_dirty; /* stop reason of sb is dirty */
|
||||
|
||||
/* For reclaimed segs statistics per each GC mode */
|
||||
unsigned int gc_segment_mode; /* GC state for reclaimed segments */
|
||||
@@ -2125,12 +2136,12 @@ static inline void f2fs_update_time(struct f2fs_sb_info *sbi, int type)
|
||||
{
|
||||
unsigned long now = jiffies;
|
||||
|
||||
sbi->last_time[type] = now;
|
||||
WRITE_ONCE(sbi->last_time[type], now);
|
||||
|
||||
/* DISCARD_TIME and GC_TIME are based on REQ_TIME */
|
||||
if (type == REQ_TIME) {
|
||||
sbi->last_time[DISCARD_TIME] = now;
|
||||
sbi->last_time[GC_TIME] = now;
|
||||
WRITE_ONCE(sbi->last_time[DISCARD_TIME], now);
|
||||
WRITE_ONCE(sbi->last_time[GC_TIME], now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2138,7 +2149,7 @@ static inline bool f2fs_time_over(struct f2fs_sb_info *sbi, int type)
|
||||
{
|
||||
unsigned long interval = sbi->interval_time[type] * HZ;
|
||||
|
||||
return time_after(jiffies, sbi->last_time[type] + interval);
|
||||
return time_after(jiffies, READ_ONCE(sbi->last_time[type]) + interval);
|
||||
}
|
||||
|
||||
static inline unsigned int f2fs_time_to_wait(struct f2fs_sb_info *sbi,
|
||||
@@ -2148,7 +2159,7 @@ static inline unsigned int f2fs_time_to_wait(struct f2fs_sb_info *sbi,
|
||||
unsigned int wait_ms = 0;
|
||||
long delta;
|
||||
|
||||
delta = (sbi->last_time[type] + interval) - jiffies;
|
||||
delta = (READ_ONCE(sbi->last_time[type]) + interval) - jiffies;
|
||||
if (delta > 0)
|
||||
wait_ms = jiffies_to_msecs(delta);
|
||||
|
||||
@@ -2279,11 +2290,18 @@ static inline bool is_sbi_flag_set(struct f2fs_sb_info *sbi, unsigned int type)
|
||||
return test_bit(type, &sbi->s_flag);
|
||||
}
|
||||
|
||||
static inline void set_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type)
|
||||
static inline void __set_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type)
|
||||
{
|
||||
set_bit(type, &sbi->s_flag);
|
||||
}
|
||||
|
||||
#define set_sbi_flag(sbi, type) \
|
||||
do { \
|
||||
__set_sbi_flag(sbi, type); \
|
||||
if ((type) == SBI_NEED_FSCK) \
|
||||
f2fs_fault_report(sbi->sb, REPORT_FAULT_NEED_FSCK, __func__, __LINE__); \
|
||||
} while (0)
|
||||
|
||||
static inline void clear_sbi_flag(struct f2fs_sb_info *sbi, unsigned int type)
|
||||
{
|
||||
clear_bit(type, &sbi->s_flag);
|
||||
@@ -4061,6 +4079,8 @@ void f2fs_destroy_segment_manager_caches(void);
|
||||
int f2fs_rw_hint_to_seg_type(struct f2fs_sb_info *sbi, enum rw_hint hint);
|
||||
enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi,
|
||||
enum page_type type, enum temp_type temp);
|
||||
u8 f2fs_io_type_to_write_stream(struct block_device *bdev,
|
||||
enum page_type type, enum temp_type temp);
|
||||
unsigned int f2fs_usable_segs_in_sec(struct f2fs_sb_info *sbi);
|
||||
unsigned int f2fs_usable_blks_in_seg(struct f2fs_sb_info *sbi,
|
||||
unsigned int segno);
|
||||
@@ -4153,6 +4173,7 @@ void f2fs_submit_merged_write_folio(struct f2fs_sb_info *sbi,
|
||||
struct folio *folio, enum page_type type);
|
||||
void f2fs_submit_merged_ipu_write(struct f2fs_sb_info *sbi,
|
||||
struct bio **bio, struct folio *folio);
|
||||
void f2fs_submit_all_merged_ipu_writes(struct f2fs_sb_info *sbi);
|
||||
void f2fs_flush_merged_writes(struct f2fs_sb_info *sbi);
|
||||
int f2fs_submit_page_bio(struct f2fs_io_info *fio);
|
||||
int f2fs_merge_page_bio(struct f2fs_io_info *fio);
|
||||
@@ -4193,8 +4214,8 @@ bool f2fs_overwrite_io(struct inode *inode, loff_t pos, size_t len);
|
||||
void f2fs_clear_page_cache_dirty_tag(struct folio *folio);
|
||||
int f2fs_init_post_read_processing(void);
|
||||
void f2fs_destroy_post_read_processing(void);
|
||||
int f2fs_init_post_read_wq(struct f2fs_sb_info *sbi);
|
||||
void f2fs_destroy_post_read_wq(struct f2fs_sb_info *sbi);
|
||||
int f2fs_init_wq(struct f2fs_sb_info *sbi);
|
||||
void f2fs_destroy_wq(struct f2fs_sb_info *sbi);
|
||||
extern const struct iomap_ops f2fs_iomap_ops;
|
||||
|
||||
/*
|
||||
|
||||
@@ -1098,6 +1098,8 @@ int f2fs_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
|
||||
return -EPERM;
|
||||
|
||||
if ((attr->ia_valid & ATTR_SIZE)) {
|
||||
if (mapping_large_folio_support(inode->i_mapping))
|
||||
return -EOPNOTSUPP;
|
||||
if (!f2fs_is_compress_backend_ready(inode) ||
|
||||
IS_DEVICE_ALIASING(inode))
|
||||
return -EOPNOTSUPP;
|
||||
@@ -1914,8 +1916,15 @@ static int f2fs_expand_inode_data(struct inode *inode, loff_t offset,
|
||||
|
||||
if (f2fs_is_pinned_file(inode)) {
|
||||
block_t sec_blks = CAP_BLKS_PER_SEC(sbi);
|
||||
block_t sec_len = roundup(map.m_len, sec_blks);
|
||||
block_t sec_len;
|
||||
|
||||
if (map.m_lblk % sec_blks) {
|
||||
map.m_lblk = rounddown(map.m_lblk, sec_blks);
|
||||
map.m_len = pg_end - map.m_lblk;
|
||||
if (off_end)
|
||||
map.m_len++;
|
||||
}
|
||||
sec_len = roundup(map.m_len, sec_blks);
|
||||
map.m_len = sec_blks;
|
||||
next_alloc:
|
||||
f2fs_down_write(&sbi->pin_sem);
|
||||
@@ -4784,6 +4793,33 @@ static bool f2fs_should_use_dio(struct inode *inode, struct kiocb *iocb,
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_F2FS_IOSTAT
|
||||
static void f2fs_dio_end_bio(struct bio *bio)
|
||||
{
|
||||
struct bio_iostat_ctx *iostat_ctx = bio->bi_private;
|
||||
void *orig_bi_private = iostat_ctx->post_read_ctx;
|
||||
|
||||
iostat_update_and_unbind_ctx(bio);
|
||||
bio->bi_private = orig_bi_private;
|
||||
iomap_dio_bio_end_io(bio);
|
||||
}
|
||||
|
||||
static void f2fs_dio_iostat_start(struct f2fs_sb_info *sbi, struct bio *bio)
|
||||
{
|
||||
void *bi_private = bio->bi_private;
|
||||
|
||||
if (!sbi->iostat_enable)
|
||||
return;
|
||||
|
||||
iostat_alloc_and_bind_ctx(sbi, bio, bi_private);
|
||||
iostat_update_submit_ctx(bio, DATA);
|
||||
bio->bi_end_io = f2fs_dio_end_bio;
|
||||
}
|
||||
#else
|
||||
static inline void f2fs_dio_iostat_start(struct f2fs_sb_info *sbi,
|
||||
struct bio *bio) {}
|
||||
#endif
|
||||
|
||||
static int f2fs_dio_read_end_io(struct kiocb *iocb, ssize_t size, int error,
|
||||
unsigned int flags)
|
||||
{
|
||||
@@ -4796,8 +4832,18 @@ static int f2fs_dio_read_end_io(struct kiocb *iocb, ssize_t size, int error,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void f2fs_dio_read_submit_io(const struct iomap_iter *iter,
|
||||
struct bio *bio, loff_t file_offset)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(iter->inode);
|
||||
|
||||
f2fs_dio_iostat_start(sbi, bio);
|
||||
blk_crypto_submit_bio(bio);
|
||||
}
|
||||
|
||||
static const struct iomap_dio_ops f2fs_iomap_dio_read_ops = {
|
||||
.end_io = f2fs_dio_read_end_io,
|
||||
.submit_io = f2fs_dio_read_submit_io,
|
||||
};
|
||||
|
||||
static ssize_t f2fs_dio_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
||||
@@ -5067,15 +5113,35 @@ static int f2fs_dio_write_end_io(struct kiocb *iocb, ssize_t size, int error,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool f2fs_valid_write_stream(struct f2fs_sb_info *sbi, u8 write_stream)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!write_stream)
|
||||
return true;
|
||||
if (!f2fs_is_multi_device(sbi))
|
||||
return write_stream <= bdev_max_write_streams(sbi->sb->s_bdev);
|
||||
|
||||
for (i = 0; i < sbi->s_ndevs; i++)
|
||||
if (write_stream > bdev_max_write_streams(FDEV(i).bdev))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void f2fs_dio_write_submit_io(const struct iomap_iter *iter,
|
||||
struct bio *bio, loff_t file_offset)
|
||||
{
|
||||
struct inode *inode = iter->inode;
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct kiocb *iocb = iter->private;
|
||||
enum log_type type = f2fs_rw_hint_to_seg_type(sbi, inode->i_write_hint);
|
||||
enum temp_type temp = f2fs_get_segment_temp(sbi, type);
|
||||
|
||||
bio->bi_write_hint = f2fs_io_type_to_rw_hint(sbi, DATA, temp);
|
||||
bio->bi_write_stream =
|
||||
iocb->ki_write_stream ? iocb->ki_write_stream :
|
||||
f2fs_io_type_to_write_stream(bio->bi_bdev, DATA, temp);
|
||||
f2fs_dio_iostat_start(sbi, bio);
|
||||
blk_crypto_submit_bio(bio);
|
||||
}
|
||||
|
||||
@@ -5113,6 +5179,11 @@ static ssize_t f2fs_dio_write_iter(struct kiocb *iocb, struct iov_iter *from,
|
||||
|
||||
trace_f2fs_direct_IO_enter(inode, iocb, count, WRITE);
|
||||
|
||||
if (!f2fs_valid_write_stream(sbi, iocb->ki_write_stream)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (iocb->ki_flags & IOCB_NOWAIT) {
|
||||
/* f2fs_convert_inline_inode() and block allocation can block */
|
||||
if (f2fs_has_inline_data(inode) ||
|
||||
@@ -5150,7 +5221,7 @@ static ssize_t f2fs_dio_write_iter(struct kiocb *iocb, struct iov_iter *from,
|
||||
if (pos + count > inode->i_size)
|
||||
dio_flags |= IOMAP_DIO_FORCE_WAIT;
|
||||
dio = __iomap_dio_rw(iocb, from, &f2fs_iomap_ops,
|
||||
&f2fs_iomap_dio_write_ops, dio_flags, NULL, 0);
|
||||
&f2fs_iomap_dio_write_ops, dio_flags, iocb, 0);
|
||||
if (IS_ERR_OR_NULL(dio)) {
|
||||
ret = PTR_ERR_OR_ZERO(dio);
|
||||
if (ret == -ENOTBLK)
|
||||
|
||||
69
fs/f2fs/gc.c
69
fs/f2fs/gc.c
@@ -1220,8 +1220,8 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
|
||||
static int ra_data_block(struct inode *inode, pgoff_t index)
|
||||
{
|
||||
struct f2fs_sb_info *sbi = F2FS_I_SB(inode);
|
||||
struct address_space *mapping = f2fs_is_cow_file(inode) ?
|
||||
F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct inode *atomic_inode = NULL;
|
||||
struct dnode_of_data dn;
|
||||
struct folio *folio, *efolio;
|
||||
struct f2fs_io_info fio = {
|
||||
@@ -1236,9 +1236,22 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
|
||||
};
|
||||
int err = 0;
|
||||
|
||||
f2fs_down_read(&F2FS_I(inode)->i_sem);
|
||||
if (f2fs_is_cow_file(inode)) {
|
||||
atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
|
||||
if (!atomic_inode) {
|
||||
f2fs_up_read(&F2FS_I(inode)->i_sem);
|
||||
return -EBUSY;
|
||||
}
|
||||
mapping = atomic_inode->i_mapping;
|
||||
}
|
||||
f2fs_up_read(&F2FS_I(inode)->i_sem);
|
||||
|
||||
folio = f2fs_grab_cache_folio(mapping, index, true);
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
if (IS_ERR(folio)) {
|
||||
err = PTR_ERR(folio);
|
||||
goto out_iput;
|
||||
}
|
||||
|
||||
if (f2fs_lookup_read_extent_cache_block(inode, index,
|
||||
&dn.data_blkaddr)) {
|
||||
@@ -1299,11 +1312,16 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
|
||||
f2fs_update_iostat(sbi, inode, FS_DATA_READ_IO, F2FS_BLKSIZE);
|
||||
f2fs_update_iostat(sbi, NULL, FS_GDATA_READ_IO, F2FS_BLKSIZE);
|
||||
|
||||
if (atomic_inode)
|
||||
iput(atomic_inode);
|
||||
return 0;
|
||||
put_encrypted_page:
|
||||
f2fs_put_page(fio.encrypted_page, true);
|
||||
put_folio:
|
||||
f2fs_folio_put(folio, true);
|
||||
out_iput:
|
||||
if (atomic_inode)
|
||||
iput(atomic_inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1314,8 +1332,8 @@ static int ra_data_block(struct inode *inode, pgoff_t index)
|
||||
static int move_data_block(struct inode *inode, block_t bidx,
|
||||
int gc_type, unsigned int segno, int off)
|
||||
{
|
||||
struct address_space *mapping = f2fs_is_cow_file(inode) ?
|
||||
F2FS_I(inode)->atomic_inode->i_mapping : inode->i_mapping;
|
||||
struct address_space *mapping = inode->i_mapping;
|
||||
struct inode *atomic_inode = NULL;
|
||||
struct f2fs_io_info fio = {
|
||||
.sbi = F2FS_I_SB(inode),
|
||||
.ino = inode->i_ino,
|
||||
@@ -1337,10 +1355,23 @@ static int move_data_block(struct inode *inode, block_t bidx,
|
||||
(fio.sbi->gc_mode != GC_URGENT_HIGH) ?
|
||||
CURSEG_ALL_DATA_ATGC : CURSEG_COLD_DATA;
|
||||
|
||||
f2fs_down_read(&F2FS_I(inode)->i_sem);
|
||||
if (f2fs_is_cow_file(inode)) {
|
||||
atomic_inode = igrab(F2FS_I(inode)->atomic_inode);
|
||||
if (!atomic_inode) {
|
||||
f2fs_up_read(&F2FS_I(inode)->i_sem);
|
||||
return -EBUSY;
|
||||
}
|
||||
mapping = atomic_inode->i_mapping;
|
||||
}
|
||||
f2fs_up_read(&F2FS_I(inode)->i_sem);
|
||||
|
||||
/* do not read out */
|
||||
folio = f2fs_grab_cache_folio(mapping, bidx, false);
|
||||
if (IS_ERR(folio))
|
||||
return PTR_ERR(folio);
|
||||
if (IS_ERR(folio)) {
|
||||
err = PTR_ERR(folio);
|
||||
goto out_iput;
|
||||
}
|
||||
|
||||
if (!check_valid_map(F2FS_I_SB(inode), segno, off)) {
|
||||
err = -ENOENT;
|
||||
@@ -1468,11 +1499,10 @@ static int move_data_block(struct inode *inode, block_t bidx,
|
||||
put_out:
|
||||
f2fs_put_dnode(&dn);
|
||||
out:
|
||||
if (!folio_test_uptodate(folio))
|
||||
__folio_set_dropbehind(folio);
|
||||
folio_unlock(folio);
|
||||
folio_end_dropbehind(folio);
|
||||
folio_put(folio);
|
||||
f2fs_folio_put(folio, true);
|
||||
out_iput:
|
||||
if (atomic_inode)
|
||||
iput(atomic_inode);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1754,9 +1784,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
unsigned int end_segno = start_segno + SEGS_PER_SEC(sbi);
|
||||
unsigned int sec_end_segno;
|
||||
int seg_freed = 0, migrated = 0;
|
||||
unsigned char type = IS_DATASEG(get_seg_entry(sbi, segno)->type) ?
|
||||
SUM_TYPE_DATA : SUM_TYPE_NODE;
|
||||
unsigned char data_type = (type == SUM_TYPE_DATA) ? DATA : NODE;
|
||||
unsigned char type;
|
||||
unsigned char data_type;
|
||||
int submitted = 0, sum_blk_cnt;
|
||||
|
||||
if (__is_large_section(sbi)) {
|
||||
@@ -1855,10 +1884,16 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi,
|
||||
migrated >= sbi->migration_granularity)
|
||||
continue;
|
||||
|
||||
if (migrated == 0) {
|
||||
type = IS_DATASEG(get_seg_entry(sbi, cur_segno)->type) ?
|
||||
SUM_TYPE_DATA : SUM_TYPE_NODE;
|
||||
data_type = (type == SUM_TYPE_DATA) ? DATA : NODE;
|
||||
}
|
||||
|
||||
sum = SUM_BLK_PAGE_ADDR(sbi, sum_folio, cur_segno);
|
||||
if (type != GET_SUM_TYPE(sum_footer(sbi, sum))) {
|
||||
f2fs_err(sbi, "Inconsistent segment (%u) type "
|
||||
"[%d, %d] in SSA and SIT",
|
||||
"[%d, %d] in SIT and SSA",
|
||||
cur_segno, type,
|
||||
GET_SUM_TYPE(
|
||||
sum_footer(sbi, sum)));
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/fiemap.h>
|
||||
#include <linux/fserror.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@@ -179,6 +180,7 @@ int f2fs_convert_inline_folio(struct dnode_of_data *dn, struct folio *folio)
|
||||
f2fs_warn(fio.sbi, "%s: corrupted inline inode ino=%llu, i_addr[0]:0x%x, run fsck to fix.",
|
||||
__func__, dn->inode->i_ino, dn->data_blkaddr);
|
||||
f2fs_handle_error(fio.sbi, ERROR_INVALID_BLKADDR);
|
||||
fserror_report_file_metadata(dn->inode, -EFSCORRUPTED, GFP_NOFS);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
@@ -435,6 +437,7 @@ static int f2fs_move_inline_dirents(struct inode *dir, struct folio *ifolio,
|
||||
__func__, dir->i_ino, dn.data_blkaddr);
|
||||
f2fs_handle_error(F2FS_F_SB(folio), ERROR_INVALID_BLKADDR);
|
||||
err = -EFSCORRUPTED;
|
||||
fserror_report_file_metadata(dn.inode, err, GFP_NOFS);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -507,6 +510,12 @@ static int f2fs_add_inline_entries(struct inode *dir, void *inline_dentry)
|
||||
bit_pos++;
|
||||
continue;
|
||||
}
|
||||
if (unlikely(le16_to_cpu(de->name_len) > F2FS_NAME_LEN ||
|
||||
bit_pos + GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)) >
|
||||
d.max)) {
|
||||
err = -EFSCORRUPTED;
|
||||
goto punch_dentry_pages;
|
||||
}
|
||||
|
||||
/*
|
||||
* We only need the disk_name and hash to move the dentry.
|
||||
@@ -527,6 +536,7 @@ static int f2fs_add_inline_entries(struct inode *dir, void *inline_dentry)
|
||||
bit_pos += GET_DENTRY_SLOTS(le16_to_cpu(de->name_len));
|
||||
}
|
||||
return 0;
|
||||
|
||||
punch_dentry_pages:
|
||||
truncate_inode_pages(&dir->i_data, 0);
|
||||
f2fs_truncate_blocks(dir, 0, false);
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/lz4.h>
|
||||
#include <linux/zstd.h>
|
||||
#include <linux/fserror.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@@ -324,9 +325,9 @@ static bool sanity_check_inode(struct inode *inode, struct folio *node_folio)
|
||||
}
|
||||
|
||||
if (f2fs_sb_has_flexible_inline_xattr(sbi) &&
|
||||
f2fs_has_inline_xattr(inode) &&
|
||||
(fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE ||
|
||||
fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE)) {
|
||||
(fi->i_inline_xattr_size > MAX_INLINE_XATTR_SIZE ||
|
||||
(f2fs_has_inline_xattr(inode) &&
|
||||
fi->i_inline_xattr_size < MIN_INLINE_XATTR_SIZE))) {
|
||||
f2fs_warn(sbi, "%s: inode (ino=%llx) has corrupted i_inline_xattr_size: %d, min: %zu, max: %lu",
|
||||
__func__, inode->i_ino, fi->i_inline_xattr_size,
|
||||
MIN_INLINE_XATTR_SIZE, MAX_INLINE_XATTR_SIZE);
|
||||
@@ -480,6 +481,7 @@ static int do_read_inode(struct inode *inode)
|
||||
f2fs_folio_put(node_folio, true);
|
||||
set_sbi_flag(sbi, SBI_NEED_FSCK);
|
||||
f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE);
|
||||
fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_NOFS);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
@@ -541,6 +543,7 @@ static int do_read_inode(struct inode *inode)
|
||||
if (!sanity_check_extent_cache(inode, node_folio)) {
|
||||
f2fs_folio_put(node_folio, true);
|
||||
f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE);
|
||||
fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_NOFS);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
@@ -561,8 +564,13 @@ static int do_read_inode(struct inode *inode)
|
||||
|
||||
static bool is_meta_ino(struct f2fs_sb_info *sbi, unsigned int ino)
|
||||
{
|
||||
return ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi) ||
|
||||
ino == F2FS_COMPRESS_INO(sbi);
|
||||
if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi))
|
||||
return true;
|
||||
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
||||
if (test_opt(sbi, COMPRESS_CACHE) && ino == F2FS_COMPRESS_INO(sbi))
|
||||
return true;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
|
||||
@@ -583,6 +591,7 @@ struct inode *f2fs_iget(struct super_block *sb, unsigned long ino)
|
||||
trace_f2fs_iget_exit(inode, ret);
|
||||
iput(inode);
|
||||
f2fs_handle_error(sbi, ERROR_CORRUPTED_INODE);
|
||||
fserror_report_file_metadata(inode, ret, GFP_NOFS);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
@@ -787,6 +796,7 @@ void f2fs_update_inode_page(struct inode *inode)
|
||||
if (err == -ENOMEM || ++count <= DEFAULT_RETRY_IO_COUNT)
|
||||
goto retry;
|
||||
stop_checkpoint:
|
||||
fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_NOFS);
|
||||
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_UPDATE_INODE);
|
||||
return;
|
||||
}
|
||||
@@ -858,10 +868,15 @@ void f2fs_evict_inode(struct inode *inode)
|
||||
f2fs_abort_atomic_write(inode, true);
|
||||
|
||||
if (fi->cow_inode && f2fs_is_cow_file(fi->cow_inode)) {
|
||||
clear_inode_flag(fi->cow_inode, FI_COW_FILE);
|
||||
F2FS_I(fi->cow_inode)->atomic_inode = NULL;
|
||||
iput(fi->cow_inode);
|
||||
struct inode *cow_inode = fi->cow_inode;
|
||||
|
||||
f2fs_down_write(&F2FS_I(cow_inode)->i_sem);
|
||||
clear_inode_flag(cow_inode, FI_COW_FILE);
|
||||
F2FS_I(cow_inode)->atomic_inode = NULL;
|
||||
fi->cow_inode = NULL;
|
||||
f2fs_up_write(&F2FS_I(cow_inode)->i_sem);
|
||||
|
||||
iput(cow_inode);
|
||||
}
|
||||
|
||||
trace_f2fs_evict_inode(inode);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/folio_batch.h>
|
||||
#include <linux/swap.h>
|
||||
#include <linux/fserror.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@@ -72,7 +73,11 @@ bool f2fs_available_free_memory(struct f2fs_sb_info *sbi, int type)
|
||||
sizeof(struct free_nid)) >> PAGE_SHIFT;
|
||||
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);
|
||||
} else if (type == NAT_ENTRIES) {
|
||||
mem_size = (nm_i->nat_cnt[TOTAL_NAT] *
|
||||
/*
|
||||
* nat_cnt[] is heuristic accounting. Sample it locklessly here
|
||||
* to avoid taking nat_tree_lock in the balance path.
|
||||
*/
|
||||
mem_size = (data_race(READ_ONCE(nm_i->nat_cnt[TOTAL_NAT])) *
|
||||
sizeof(struct nat_entry)) >> PAGE_SHIFT;
|
||||
res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);
|
||||
if (excess_cached_nats(sbi))
|
||||
@@ -1265,6 +1270,8 @@ int f2fs_truncate_inode_blocks(struct inode *inode, pgoff_t from)
|
||||
if (err == -ENOENT) {
|
||||
set_sbi_flag(F2FS_F_SB(folio), SBI_NEED_FSCK);
|
||||
f2fs_handle_error(sbi, ERROR_INVALID_BLKADDR);
|
||||
fserror_report_file_metadata(dn.inode, -EFSCORRUPTED,
|
||||
GFP_NOFS);
|
||||
f2fs_err_ratelimited(sbi,
|
||||
"truncate node fail, ino:%llu, nid:%u, "
|
||||
"offset[0]:%d, offset[1]:%d, nofs:%d",
|
||||
@@ -1541,6 +1548,10 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi,
|
||||
if (is_inode)
|
||||
goto out_err;
|
||||
break;
|
||||
case NODE_TYPE_NON_IXNODE:
|
||||
if (is_inode || is_xnode)
|
||||
goto out_err;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -1556,6 +1567,8 @@ int f2fs_sanity_check_node_footer(struct f2fs_sb_info *sbi,
|
||||
next_blkaddr_of_node(folio));
|
||||
|
||||
f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER);
|
||||
fserror_report_file_metadata(folio->mapping->host,
|
||||
-EFSCORRUPTED, in_irq ? GFP_NOWAIT : GFP_NOFS);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
|
||||
@@ -1634,7 +1647,7 @@ static struct folio *f2fs_get_node_folio_ra(struct folio *parent, int start)
|
||||
struct f2fs_sb_info *sbi = F2FS_F_SB(parent);
|
||||
nid_t nid = get_nid(parent, start, false);
|
||||
|
||||
return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_REGULAR);
|
||||
return __get_node_folio(sbi, nid, parent, start, NODE_TYPE_NON_IXNODE);
|
||||
}
|
||||
|
||||
static void flush_inline_data(struct f2fs_sb_info *sbi, nid_t ino)
|
||||
@@ -1778,6 +1791,7 @@ static bool __write_node_folio(struct folio *folio, bool atomic, bool do_fsync,
|
||||
|
||||
if (f2fs_sanity_check_node_footer(sbi, folio, nid,
|
||||
NODE_TYPE_REGULAR, false)) {
|
||||
fserror_report_metadata(sbi->sb, -EFSCORRUPTED, GFP_NOFS);
|
||||
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_NID);
|
||||
goto redirty_out;
|
||||
}
|
||||
@@ -1875,7 +1889,7 @@ int f2fs_write_single_node_folio(struct folio *node_folio, int sync_mode,
|
||||
}
|
||||
|
||||
if (!__write_node_folio(node_folio, false, false, NULL,
|
||||
&wbc, false, FS_GC_NODE_IO, NULL))
|
||||
&wbc, false, io_type, NULL))
|
||||
err = -EAGAIN;
|
||||
goto release_folio;
|
||||
out_folio:
|
||||
@@ -2703,6 +2717,8 @@ bool f2fs_alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid)
|
||||
spin_unlock(&nm_i->nid_list_lock);
|
||||
f2fs_err(sbi, "Corrupted nid %u in free_nid_list",
|
||||
i->nid);
|
||||
fserror_report_metadata(sbi->sb, -EFSCORRUPTED,
|
||||
GFP_NOFS);
|
||||
f2fs_stop_checkpoint(sbi, false,
|
||||
STOP_CP_REASON_CORRUPTED_NID);
|
||||
return false;
|
||||
|
||||
@@ -129,13 +129,17 @@ static inline void raw_nat_from_node_info(struct f2fs_nat_entry *raw_ne,
|
||||
|
||||
static inline bool excess_dirty_nats(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return NM_I(sbi)->nat_cnt[DIRTY_NAT] >= NM_I(sbi)->max_nid *
|
||||
/* nat_cnt[] is heuristic accounting sampled locklessly here. */
|
||||
return data_race(READ_ONCE(NM_I(sbi)->nat_cnt[DIRTY_NAT])) >=
|
||||
NM_I(sbi)->max_nid *
|
||||
NM_I(sbi)->dirty_nats_ratio / 100;
|
||||
}
|
||||
|
||||
static inline bool excess_cached_nats(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
return NM_I(sbi)->nat_cnt[TOTAL_NAT] >= DEF_NAT_CACHE_THRESHOLD;
|
||||
/* nat_cnt[] is heuristic accounting sampled locklessly here. */
|
||||
return data_race(READ_ONCE(NM_I(sbi)->nat_cnt[TOTAL_NAT])) >=
|
||||
DEF_NAT_CACHE_THRESHOLD;
|
||||
}
|
||||
|
||||
enum mem_type {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/fs.h>
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/fserror.h>
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
#include "segment.h"
|
||||
@@ -679,6 +680,7 @@ static int do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode,
|
||||
ofs_of_node(folio));
|
||||
err = -EFSCORRUPTED;
|
||||
f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER);
|
||||
fserror_report_file_metadata(dn.inode, err, GFP_NOFS);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/sched/signal.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/fserror.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "segment.h"
|
||||
@@ -444,6 +445,13 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
|
||||
if (has_enough_free_secs(sbi, 0, 0))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Submit all cached OPU/IPU DATA bios before triggering
|
||||
* foreground GC to avoid potential deadlocks.
|
||||
*/
|
||||
f2fs_submit_merged_write(sbi, DATA);
|
||||
f2fs_submit_all_merged_ipu_writes(sbi);
|
||||
|
||||
if (test_opt(sbi, GC_MERGE) && sbi->gc_thread &&
|
||||
sbi->gc_thread->f2fs_gc_task) {
|
||||
DEFINE_WAIT(wait);
|
||||
@@ -462,6 +470,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)
|
||||
.should_migrate_blocks = false,
|
||||
.err_gc_skipped = false,
|
||||
.nr_free_secs = 1 };
|
||||
|
||||
f2fs_down_write_trace(&sbi->gc_lock, &gc_control.lc);
|
||||
stat_inc_gc_call_count(sbi, FOREGROUND);
|
||||
f2fs_gc(sbi, &gc_control);
|
||||
@@ -2446,9 +2455,6 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent
|
||||
unsigned int segno, block_t blkaddr, unsigned int offset, int del)
|
||||
{
|
||||
bool exist;
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
bool mir_exist;
|
||||
#endif
|
||||
int i;
|
||||
int del_count = -del;
|
||||
|
||||
@@ -2456,15 +2462,6 @@ static int update_sit_entry_for_release(struct f2fs_sb_info *sbi, struct seg_ent
|
||||
|
||||
for (i = 0; i < del_count; i++) {
|
||||
exist = f2fs_test_and_clear_bit(offset + i, se->cur_valid_map);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
mir_exist = f2fs_test_and_clear_bit(offset + i,
|
||||
se->cur_valid_map_mir);
|
||||
if (unlikely(exist != mir_exist)) {
|
||||
f2fs_err(sbi, "Inconsistent error when clearing bitmap, blk:%u, old bit:%d",
|
||||
blkaddr + i, exist);
|
||||
f2fs_bug_on(sbi, 1);
|
||||
}
|
||||
#endif
|
||||
if (unlikely(!exist)) {
|
||||
f2fs_err(sbi, "Bitmap was wrongly cleared, blk:%u", blkaddr + i);
|
||||
f2fs_bug_on(sbi, 1);
|
||||
@@ -2505,20 +2502,8 @@ static int update_sit_entry_for_alloc(struct f2fs_sb_info *sbi, struct seg_entry
|
||||
unsigned int segno, block_t blkaddr, unsigned int offset, int del)
|
||||
{
|
||||
bool exist;
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
bool mir_exist;
|
||||
#endif
|
||||
|
||||
exist = f2fs_test_and_set_bit(offset, se->cur_valid_map);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
mir_exist = f2fs_test_and_set_bit(offset,
|
||||
se->cur_valid_map_mir);
|
||||
if (unlikely(exist != mir_exist)) {
|
||||
f2fs_err(sbi, "Inconsistent error when setting bitmap, blk:%u, old bit:%d",
|
||||
blkaddr, exist);
|
||||
f2fs_bug_on(sbi, 1);
|
||||
}
|
||||
#endif
|
||||
if (unlikely(exist)) {
|
||||
f2fs_err(sbi, "Bitmap was wrongly set, blk:%u", blkaddr);
|
||||
f2fs_bug_on(sbi, 1);
|
||||
@@ -2896,6 +2881,7 @@ static int get_new_segment(struct f2fs_sb_info *sbi,
|
||||
/* set it as dirty segment in free segmap */
|
||||
if (test_bit(segno, free_i->free_segmap)) {
|
||||
ret = -EFSCORRUPTED;
|
||||
fserror_report_metadata(sbi->sb, -EFSCORRUPTED, GFP_NOFS);
|
||||
f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_FREE_BITMAP);
|
||||
goto out_unlock;
|
||||
}
|
||||
@@ -3636,6 +3622,19 @@ enum rw_hint f2fs_io_type_to_rw_hint(struct f2fs_sb_info *sbi,
|
||||
}
|
||||
}
|
||||
|
||||
u8 f2fs_io_type_to_write_stream(struct block_device *bdev,
|
||||
enum page_type type, enum temp_type temp)
|
||||
{
|
||||
unsigned short nr = bdev_max_write_streams(bdev);
|
||||
|
||||
if (type != DATA || !nr)
|
||||
return 0;
|
||||
if (nr < NR_TEMP_TYPE)
|
||||
return temp == COLD ? nr : HOT + 1;
|
||||
|
||||
return temp + 1;
|
||||
}
|
||||
|
||||
static int __get_segment_type_2(struct f2fs_io_info *fio)
|
||||
{
|
||||
if (fio->type == DATA)
|
||||
@@ -4748,11 +4747,6 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
int offset, sit_offset;
|
||||
|
||||
se = get_seg_entry(sbi, segno);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
if (memcmp(se->cur_valid_map, se->cur_valid_map_mir,
|
||||
SIT_VBLOCK_MAP_SIZE))
|
||||
f2fs_bug_on(sbi, 1);
|
||||
#endif
|
||||
|
||||
/* add discard candidates */
|
||||
if (!(cpc->reason & CP_DISCARD)) {
|
||||
@@ -4779,10 +4773,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)
|
||||
}
|
||||
|
||||
/* update ckpt_valid_block */
|
||||
if (__is_large_section(sbi)) {
|
||||
if (__is_large_section(sbi))
|
||||
set_ckpt_valid_blocks(sbi, segno);
|
||||
sanity_check_valid_blocks(sbi, segno);
|
||||
}
|
||||
|
||||
__clear_bit(segno, bitmap);
|
||||
sit_i->dirty_sentries--;
|
||||
@@ -4843,11 +4835,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
|
||||
if (!sit_i->dirty_sentries_bitmap)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (3 + discard_map);
|
||||
#else
|
||||
bitmap_size = MAIN_SEGS(sbi) * SIT_VBLOCK_MAP_SIZE * (2 + discard_map);
|
||||
#endif
|
||||
sit_i->bitmap = f2fs_kvzalloc(sbi, bitmap_size, GFP_KERNEL);
|
||||
if (!sit_i->bitmap)
|
||||
return -ENOMEM;
|
||||
@@ -4861,11 +4849,6 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
|
||||
sit_i->sentries[start].ckpt_valid_map = bitmap;
|
||||
bitmap += SIT_VBLOCK_MAP_SIZE;
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
sit_i->sentries[start].cur_valid_map_mir = bitmap;
|
||||
bitmap += SIT_VBLOCK_MAP_SIZE;
|
||||
#endif
|
||||
|
||||
if (discard_map) {
|
||||
sit_i->sentries[start].discard_map = bitmap;
|
||||
bitmap += SIT_VBLOCK_MAP_SIZE;
|
||||
@@ -4897,11 +4880,6 @@ static int build_sit_info(struct f2fs_sb_info *sbi)
|
||||
return -ENOMEM;
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
sit_i->sit_bitmap_mir = kmemdup(src_bitmap,
|
||||
sit_bitmap_size, GFP_KERNEL);
|
||||
if (!sit_i->sit_bitmap_mir)
|
||||
return -ENOMEM;
|
||||
|
||||
sit_i->invalid_segmap = f2fs_kvzalloc(sbi,
|
||||
main_bitmap_size, GFP_KERNEL);
|
||||
if (!sit_i->invalid_segmap)
|
||||
@@ -5110,10 +5088,8 @@ static int build_sit_entries(struct f2fs_sb_info *sbi)
|
||||
if (__is_large_section(sbi)) {
|
||||
unsigned int segno;
|
||||
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno += SEGS_PER_SEC(sbi)) {
|
||||
for (segno = 0; segno < MAIN_SEGS(sbi); segno += SEGS_PER_SEC(sbi))
|
||||
set_ckpt_valid_blocks(sbi, segno);
|
||||
sanity_check_valid_blocks(sbi, segno);
|
||||
}
|
||||
}
|
||||
|
||||
if (err)
|
||||
@@ -5864,7 +5840,6 @@ static void destroy_sit_info(struct f2fs_sb_info *sbi)
|
||||
SM_I(sbi)->sit_info = NULL;
|
||||
kfree(sit_i->sit_bitmap);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
kfree(sit_i->sit_bitmap_mir);
|
||||
kvfree(sit_i->invalid_segmap);
|
||||
#endif
|
||||
kfree(sit_i);
|
||||
|
||||
@@ -177,9 +177,6 @@ struct seg_entry {
|
||||
unsigned int ckpt_valid_blocks:10; /* # of valid blocks last cp */
|
||||
unsigned int padding:6; /* padding */
|
||||
unsigned char *cur_valid_map; /* validity bitmap of blocks */
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
unsigned char *cur_valid_map_mir; /* mirror of current valid bitmap */
|
||||
#endif
|
||||
/*
|
||||
* # of valid blocks and the validity bitmap stored in the last
|
||||
* checkpoint pack. This information is used by the SSR mode.
|
||||
@@ -209,8 +206,6 @@ struct sit_info {
|
||||
char *bitmap; /* all bitmaps pointer */
|
||||
char *sit_bitmap; /* SIT bitmap pointer */
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
char *sit_bitmap_mir; /* SIT bitmap mirror */
|
||||
|
||||
/* bitmap of segments to be ignored by GC in case of errors */
|
||||
unsigned long *invalid_segmap;
|
||||
#endif
|
||||
@@ -408,9 +403,6 @@ static inline void seg_info_from_raw_sit(struct seg_entry *se,
|
||||
se->ckpt_valid_blocks = GET_SIT_VBLOCKS(rs);
|
||||
memcpy(se->cur_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
|
||||
memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
memcpy(se->cur_valid_map_mir, rs->valid_map, SIT_VBLOCK_MAP_SIZE);
|
||||
#endif
|
||||
se->type = GET_SIT_TYPE(rs);
|
||||
se->mtime = le64_to_cpu(rs->mtime);
|
||||
}
|
||||
@@ -555,11 +547,6 @@ static inline void get_sit_bitmap(struct f2fs_sb_info *sbi,
|
||||
{
|
||||
struct sit_info *sit_i = SIT_I(sbi);
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
if (memcmp(sit_i->sit_bitmap, sit_i->sit_bitmap_mir,
|
||||
sit_i->bitmap_size))
|
||||
f2fs_bug_on(sbi, 1);
|
||||
#endif
|
||||
memcpy(dst_addr, sit_i->sit_bitmap, sit_i->bitmap_size);
|
||||
}
|
||||
|
||||
@@ -900,12 +887,6 @@ static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi,
|
||||
|
||||
f2fs_bug_on(sbi, !valid_main_segno(sbi, start));
|
||||
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
if (f2fs_test_bit(offset, sit_i->sit_bitmap) !=
|
||||
f2fs_test_bit(offset, sit_i->sit_bitmap_mir))
|
||||
f2fs_bug_on(sbi, 1);
|
||||
#endif
|
||||
|
||||
/* calculate sit block address */
|
||||
if (f2fs_test_bit(offset, sit_i->sit_bitmap))
|
||||
blk_addr += sit_i->sit_blocks;
|
||||
@@ -931,9 +912,6 @@ static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start)
|
||||
unsigned int block_off = SIT_BLOCK_OFFSET(start);
|
||||
|
||||
f2fs_change_bit(block_off, sit_i->sit_bitmap);
|
||||
#ifdef CONFIG_F2FS_CHECK_FS
|
||||
f2fs_change_bit(block_off, sit_i->sit_bitmap_mir);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi,
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include <linux/lz4.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/fs_parser.h>
|
||||
#include <linux/fserror.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "node.h"
|
||||
@@ -2074,7 +2075,7 @@ static void f2fs_put_super(struct super_block *sb)
|
||||
/* flush s_error_work before sbi destroy */
|
||||
flush_work(&sbi->s_error_work);
|
||||
|
||||
f2fs_destroy_post_read_wq(sbi);
|
||||
f2fs_destroy_wq(sbi);
|
||||
|
||||
kvfree(sbi->ckpt);
|
||||
|
||||
@@ -2632,6 +2633,9 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
|
||||
|
||||
/* check if we need more GC first */
|
||||
unusable = f2fs_get_unusable_blocks(sbi);
|
||||
|
||||
f2fs_info(sbi, "%s starts, unusable: %u", __func__, unusable);
|
||||
|
||||
if (!f2fs_disable_cp_again(sbi, unusable))
|
||||
goto skip_gc;
|
||||
|
||||
@@ -2639,6 +2643,8 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
|
||||
|
||||
sbi->gc_mode = GC_URGENT_HIGH;
|
||||
|
||||
f2fs_info(sbi, "%s: run f2fs_gc() to migrate blocks", __func__);
|
||||
|
||||
while (!f2fs_time_over(sbi, DISABLE_TIME)) {
|
||||
struct f2fs_gc_control gc_control = {
|
||||
.victim_segno = NULL_SEGNO,
|
||||
@@ -2659,6 +2665,12 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
|
||||
break;
|
||||
}
|
||||
|
||||
f2fs_info(sbi, "%s: call sync_filesystem() to persist meta: %lld, node: %lld, data: %lld",
|
||||
__func__,
|
||||
get_pages(sbi, F2FS_DIRTY_META),
|
||||
get_pages(sbi, F2FS_DIRTY_NODES),
|
||||
get_pages(sbi, F2FS_DIRTY_DATA));
|
||||
|
||||
ret = sync_filesystem(sbi->sb);
|
||||
if (ret || err) {
|
||||
err = ret ? ret : err;
|
||||
@@ -2672,6 +2684,12 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
|
||||
}
|
||||
|
||||
skip_gc:
|
||||
f2fs_info(sbi, "%s: call f2fs_write_checkpoint(), meta: %lld, node: %lld, data: %lld",
|
||||
__func__,
|
||||
get_pages(sbi, F2FS_DIRTY_META),
|
||||
get_pages(sbi, F2FS_DIRTY_NODES),
|
||||
get_pages(sbi, F2FS_DIRTY_DATA));
|
||||
|
||||
f2fs_down_write_trace(&sbi->gc_lock, &lc);
|
||||
cpc.reason = CP_PAUSE;
|
||||
set_sbi_flag(sbi, SBI_CP_DISABLED);
|
||||
@@ -2689,7 +2707,7 @@ static int f2fs_disable_checkpoint(struct f2fs_sb_info *sbi)
|
||||
restore_flag:
|
||||
sbi->gc_mode = gc_mode;
|
||||
sbi->sb->s_flags = s_flags; /* Restore SB_RDONLY status */
|
||||
f2fs_info(sbi, "f2fs_disable_checkpoint() finish, err:%d", err);
|
||||
f2fs_info(sbi, "%s finishes, err:%d", __func__, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -4608,6 +4626,7 @@ static void save_stop_reason(struct f2fs_sb_info *sbi, unsigned char reason)
|
||||
spin_lock_irqsave(&sbi->error_lock, flags);
|
||||
if (sbi->stop_reason[reason] < GENMASK(BITS_PER_BYTE - 1, 0))
|
||||
sbi->stop_reason[reason]++;
|
||||
sbi->stop_reason_dirty = true;
|
||||
spin_unlock_irqrestore(&sbi->error_lock, flags);
|
||||
}
|
||||
|
||||
@@ -4615,17 +4634,21 @@ static void f2fs_record_stop_reason(struct f2fs_sb_info *sbi)
|
||||
{
|
||||
struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi);
|
||||
unsigned long flags;
|
||||
bool report_shutdown = false;
|
||||
int err;
|
||||
|
||||
f2fs_down_write(&sbi->sb_lock);
|
||||
|
||||
spin_lock_irqsave(&sbi->error_lock, flags);
|
||||
if (sbi->error_dirty) {
|
||||
memcpy(F2FS_RAW_SUPER(sbi)->s_errors, sbi->errors,
|
||||
MAX_F2FS_ERRORS);
|
||||
memcpy(raw_super->s_errors, sbi->errors, MAX_F2FS_ERRORS);
|
||||
sbi->error_dirty = false;
|
||||
}
|
||||
memcpy(raw_super->s_stop_reason, sbi->stop_reason, MAX_STOP_REASON);
|
||||
if (sbi->stop_reason_dirty) {
|
||||
report_shutdown = true;
|
||||
sbi->stop_reason_dirty = false;
|
||||
}
|
||||
spin_unlock_irqrestore(&sbi->error_lock, flags);
|
||||
|
||||
err = f2fs_commit_super(sbi, false);
|
||||
@@ -4635,6 +4658,9 @@ static void f2fs_record_stop_reason(struct f2fs_sb_info *sbi)
|
||||
f2fs_err_ratelimited(sbi,
|
||||
"f2fs_commit_super fails to record stop_reason, err:%d",
|
||||
err);
|
||||
|
||||
if (report_shutdown)
|
||||
fserror_report_shutdown(sbi->sb, GFP_NOFS);
|
||||
}
|
||||
|
||||
void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag)
|
||||
@@ -4649,6 +4675,27 @@ void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag)
|
||||
spin_unlock_irqrestore(&sbi->error_lock, flags);
|
||||
}
|
||||
|
||||
static void f2fs_report_fserror(struct f2fs_sb_info *sbi, unsigned char error)
|
||||
{
|
||||
switch (error) {
|
||||
case ERROR_INVALID_BLKADDR:
|
||||
case ERROR_CORRUPTED_INODE:
|
||||
case ERROR_INCONSISTENT_SUMMARY:
|
||||
case ERROR_INCONSISTENT_SUM_TYPE:
|
||||
case ERROR_CORRUPTED_JOURNAL:
|
||||
case ERROR_INCONSISTENT_NODE_COUNT:
|
||||
case ERROR_INCONSISTENT_BLOCK_COUNT:
|
||||
case ERROR_INVALID_CURSEG:
|
||||
case ERROR_INCONSISTENT_SIT:
|
||||
case ERROR_INVALID_NODE_REFERENCE:
|
||||
case ERROR_INCONSISTENT_NAT:
|
||||
fserror_report_metadata(sbi->sb, -EFSCORRUPTED, GFP_NOFS);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error)
|
||||
{
|
||||
f2fs_save_errors(sbi, error);
|
||||
@@ -4658,6 +4705,8 @@ void f2fs_handle_error(struct f2fs_sb_info *sbi, unsigned char error)
|
||||
if (!test_bit(error, (unsigned long *)sbi->errors))
|
||||
return;
|
||||
schedule_work(&sbi->s_error_work);
|
||||
|
||||
f2fs_report_fserror(sbi, error);
|
||||
}
|
||||
|
||||
static bool system_going_down(void)
|
||||
@@ -4724,9 +4773,18 @@ static void f2fs_handle_critical_error(struct f2fs_sb_info *sbi,
|
||||
*/
|
||||
}
|
||||
|
||||
void f2fs_fault_report(struct super_block *sb, unsigned int err_code,
|
||||
const char *func, unsigned int data)
|
||||
{
|
||||
trace_f2fs_fault_report(sb, err_code, func, data);
|
||||
}
|
||||
|
||||
void f2fs_stop_checkpoint(struct f2fs_sb_info *sbi, bool end_io,
|
||||
unsigned char reason)
|
||||
{
|
||||
if (reason != STOP_CP_REASON_SHUTDOWN)
|
||||
f2fs_fault_report(sbi->sb, REPORT_FAULT_STOP_CP, __func__, reason);
|
||||
|
||||
f2fs_build_fault_attr(sbi, 0, 0, FAULT_ALL);
|
||||
if (!end_io)
|
||||
f2fs_flush_merged_writes(sbi);
|
||||
@@ -5130,9 +5188,9 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
goto free_devices;
|
||||
}
|
||||
|
||||
err = f2fs_init_post_read_wq(sbi);
|
||||
err = f2fs_init_wq(sbi);
|
||||
if (err) {
|
||||
f2fs_err(sbi, "Failed to initialize post read workqueue");
|
||||
f2fs_err(sbi, "Failed to create workqueue");
|
||||
goto free_devices;
|
||||
}
|
||||
|
||||
@@ -5419,7 +5477,7 @@ static int f2fs_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
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_wq(sbi);
|
||||
free_devices:
|
||||
destroy_device_list(sbi);
|
||||
kvfree(sbi->ckpt);
|
||||
|
||||
@@ -1399,6 +1399,7 @@ F2FS_FEATURE_RO_ATTR(pin_file);
|
||||
F2FS_FEATURE_RO_ATTR(linear_lookup);
|
||||
#endif
|
||||
F2FS_FEATURE_RO_ATTR(packed_ssa);
|
||||
F2FS_FEATURE_RO_ATTR(fserror);
|
||||
|
||||
#define ATTR_LIST(name) (&f2fs_attr_##name.attr)
|
||||
static struct attribute *f2fs_attrs[] = {
|
||||
@@ -1566,6 +1567,7 @@ static struct attribute *f2fs_feat_attrs[] = {
|
||||
BASE_ATTR_LIST(linear_lookup),
|
||||
#endif
|
||||
BASE_ATTR_LIST(packed_ssa),
|
||||
BASE_ATTR_LIST(fserror),
|
||||
NULL,
|
||||
};
|
||||
ATTRIBUTE_GROUPS(f2fs_feat);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/fserror.h>
|
||||
|
||||
#include "f2fs.h"
|
||||
#include "xattr.h"
|
||||
@@ -243,6 +244,7 @@ static int f2fs_get_verity_descriptor(struct inode *inode, void *buf,
|
||||
f2fs_warn(F2FS_I_SB(inode), "invalid verity xattr");
|
||||
f2fs_handle_error(F2FS_I_SB(inode),
|
||||
ERROR_CORRUPTED_VERITY_XATTR);
|
||||
fserror_report_file_metadata(inode, -EFSCORRUPTED, GFP_NOFS);
|
||||
return -EFSCORRUPTED;
|
||||
}
|
||||
if (buf_size) {
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <linux/f2fs_fs.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/posix_acl_xattr.h>
|
||||
#include <linux/fserror.h>
|
||||
#include "f2fs.h"
|
||||
#include "xattr.h"
|
||||
#include "segment.h"
|
||||
@@ -371,6 +372,7 @@ static int lookup_all_xattrs(struct inode *inode, struct folio *ifolio,
|
||||
err = -ENODATA;
|
||||
f2fs_handle_error(F2FS_I_SB(inode),
|
||||
ERROR_CORRUPTED_XATTR);
|
||||
fserror_report_file_metadata(inode, err, GFP_NOFS);
|
||||
goto out;
|
||||
}
|
||||
check:
|
||||
@@ -581,8 +583,6 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||
size_t prefix_len;
|
||||
size_t size;
|
||||
|
||||
prefix = f2fs_xattr_prefix(entry->e_name_index, dentry);
|
||||
|
||||
if ((void *)(entry) + sizeof(__u32) > last_base_addr ||
|
||||
(void *)XATTR_NEXT_ENTRY(entry) > last_base_addr) {
|
||||
f2fs_err(F2FS_I_SB(inode), "list inode (%llu) has corrupted xattr",
|
||||
@@ -590,9 +590,13 @@ ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size)
|
||||
set_sbi_flag(F2FS_I_SB(inode), SBI_NEED_FSCK);
|
||||
f2fs_handle_error(F2FS_I_SB(inode),
|
||||
ERROR_CORRUPTED_XATTR);
|
||||
break;
|
||||
fserror_report_file_metadata(inode,
|
||||
-EFSCORRUPTED, GFP_NOFS);
|
||||
error = -EFSCORRUPTED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
prefix = f2fs_xattr_prefix(entry->e_name_index, dentry);
|
||||
if (!prefix)
|
||||
continue;
|
||||
|
||||
@@ -677,6 +681,7 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
||||
error = -EFSCORRUPTED;
|
||||
f2fs_handle_error(F2FS_I_SB(inode),
|
||||
ERROR_CORRUPTED_XATTR);
|
||||
fserror_report_file_metadata(inode, error, GFP_NOFS);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
@@ -705,6 +710,7 @@ static int __f2fs_setxattr(struct inode *inode, int index,
|
||||
error = -EFSCORRUPTED;
|
||||
f2fs_handle_error(F2FS_I_SB(inode),
|
||||
ERROR_CORRUPTED_XATTR);
|
||||
fserror_report_file_metadata(inode, error, GFP_NOFS);
|
||||
goto exit;
|
||||
}
|
||||
last = XATTR_NEXT_ENTRY(last);
|
||||
|
||||
@@ -107,6 +107,7 @@ enum f2fs_error {
|
||||
ERROR_CORRUPTED_XATTR,
|
||||
ERROR_INVALID_NODE_REFERENCE,
|
||||
ERROR_INCONSISTENT_NAT,
|
||||
ERROR_INCONSISTENT_ORPHAN,
|
||||
ERROR_MAX,
|
||||
};
|
||||
|
||||
|
||||
@@ -2595,6 +2595,34 @@ DEFINE_EVENT(f2fs_priority_update, f2fs_priority_restore,
|
||||
TP_ARGS(sbi, lock_name, is_write, p, orig_prio, new_prio)
|
||||
);
|
||||
|
||||
TRACE_EVENT(f2fs_fault_report,
|
||||
|
||||
TP_PROTO(struct super_block *sb, unsigned int err_code,
|
||||
const char *func, unsigned int data),
|
||||
|
||||
TP_ARGS(sb, err_code, func, data),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(dev_t, dev)
|
||||
__field(unsigned int, err_code)
|
||||
__string(func, func)
|
||||
__field(unsigned int, data)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = sb->s_dev;
|
||||
__entry->err_code = err_code;
|
||||
__assign_str(func);
|
||||
__entry->data = data;
|
||||
),
|
||||
|
||||
TP_printk("dev = (%d,%d), err_code = %u, func = %s, data = %u",
|
||||
show_dev(__entry->dev),
|
||||
__entry->err_code,
|
||||
__get_str(func),
|
||||
__entry->data)
|
||||
);
|
||||
|
||||
#endif /* _TRACE_F2FS_H */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
||||
Reference in New Issue
Block a user