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:
Linus Torvalds
2026-06-23 17:59:36 -07:00
24 changed files with 502 additions and 183 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -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)

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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;
/*

View File

@@ -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)

View File

@@ -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)));

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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);

View File

@@ -107,6 +107,7 @@ enum f2fs_error {
ERROR_CORRUPTED_XATTR,
ERROR_INVALID_NODE_REFERENCE,
ERROR_INCONSISTENT_NAT,
ERROR_INCONSISTENT_ORPHAN,
ERROR_MAX,
};

View File

@@ -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 */