]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
f2fs: support to report fserror
authorChao Yu <chao@kernel.org>
Sat, 28 Mar 2026 08:36:02 +0000 (08:36 +0000)
committerJaegeuk Kim <jaegeuk@kernel.org>
Fri, 22 May 2026 03:49:06 +0000 (03:49 +0000)
This patch supports to report fserror, it provides another way to let
userspace to monitor filesystem level error. In addition, it exports
/sys/fs/f2fs/features/fserror once f2fs kernel module start to support
the new feature, then generic/791 of fstests can notice the feature,
and verify validation of fserror report.

Cc: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Chao Yu <chao@kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
13 files changed:
Documentation/ABI/testing/sysfs-fs-f2fs
fs/f2fs/compress.c
fs/f2fs/data.c
fs/f2fs/dir.c
fs/f2fs/inline.c
fs/f2fs/inode.c
fs/f2fs/node.c
fs/f2fs/recovery.c
fs/f2fs/segment.c
fs/f2fs/super.c
fs/f2fs/sysfs.c
fs/f2fs/verity.c
fs/f2fs/xattr.c

index 423ec40e2e4e2da5eb52bb907329fe0cbb733823..27d5e88facbe33aa16351da7c781835b99150611 100644 (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
index 881e76158b967a8c9eab0624c2e101ed5131d031..caf522d667d613ec1cdbf0b1cdc04c2b3d4aef2d 100644 (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;
        }
 
index 0f92a8805635ca2bef6988578e145569bd4a280b..b7b8a72d64860037e0621c8ae425e70058f14911 100644 (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"
@@ -377,9 +378,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)) {
@@ -1750,6 +1752,7 @@ next_block:
                        err = -EFSCORRUPTED;
                        f2fs_handle_error(sbi,
                                        ERROR_CORRUPTED_CLUSTER);
+                       fserror_report_file_metadata(inode, err, GFP_NOFS);
                        goto sync_out;
                }
 
index 38802ee2e40deb4995a4e5f4118330b90b2f10e2..b1697194c3c4d1c214d15a4567c86aaa0e7dbdd0 100644 (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"
@@ -1020,6 +1021,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;
                }
 
index 7aabfc9b43cb81916258a181e66c79de9b70c916..099f7208970162e63c7613669818f3b15a29cf93 100644 (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;
        }
 
index c6dcda447882f09dd67ffc31421087bb6411e25d..1694726122e60c44dfee2f2e8307fc7a825b47d8 100644 (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"
@@ -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;
        }
 
@@ -583,6 +586,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 +791,7 @@ retry:
                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;
        }
index 4e5bd9e4cfc32da5e4393b45c03fbaf4cc5cf681..b1247de254115bf69a2113f98e7a842353c9c5c3 100644 (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"
@@ -1265,6 +1266,8 @@ skip_partial:
                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",
@@ -1556,6 +1559,8 @@ out_err:
                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;
 }
 
@@ -1778,6 +1783,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;
        }
@@ -2703,6 +2709,8 @@ retry:
                        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;
index 3d3dacec94825f6695d67043fd3ca42329aa5929..89af8407b66733d16ddde4e687c2623edf653fdc 100644 (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 @@ retry_dn:
                          ofs_of_node(folio));
                err = -EFSCORRUPTED;
                f2fs_handle_error(sbi, ERROR_INCONSISTENT_FOOTER);
+               fserror_report_file_metadata(dn.inode, err, GFP_NOFS);
                goto err;
        }
 
index 9cce4d94ac8258de5c0b8f7ab7fec062364fd3a8..008432d674dc124a04a77405031a42bb6a2590af 100644 (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"
@@ -2896,6 +2897,7 @@ got_it:
        /* 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;
        }
index ccf806b676f5385fe8c9719aaae159691f5a2826..aab4345f3ee7c148c99de657a39d2f965f2cd064 100644 (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"
@@ -4635,6 +4636,8 @@ 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);
+
+       fserror_report_shutdown(sbi->sb, GFP_NOFS);
 }
 
 void f2fs_save_errors(struct f2fs_sb_info *sbi, unsigned char flag)
@@ -4649,6 +4652,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 +4682,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)
index 352e96ad5c3a561091364f03b16de23685f67a24..665687244c939ca81f2e96591cde2b2b58f5f10e 100644 (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);
index 92ebcc19cab09926db7bd53b6458e5c919b76bc7..39f4825154452a345353bc92b57f680d4b384087 100644 (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) {
index 610d5810074dc501d02c93a4feb308e12a21a056..24cef7e1f56a560eaa69130e2e0030d3bde6b7fb 100644 (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:
@@ -590,6 +592,8 @@ 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);
+                       fserror_report_file_metadata(inode,
+                                               -EFSCORRUPTED, GFP_NOFS);
                        break;
                }
 
@@ -677,6 +681,7 @@ retry:
                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 @@ retry:
                        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);