]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ext4: mark inode format migration fast-commit ineligible
authorLi Chen <me@linux.beauty>
Thu, 11 Dec 2025 11:51:38 +0000 (19:51 +0800)
committerTheodore Ts'o <tytso@mit.edu>
Tue, 20 Jan 2026 00:26:35 +0000 (19:26 -0500)
Fast commits only log operations that have dedicated replay support.
Inode format migration (indirect<->extent layout changes via
EXT4_IOC_MIGRATE or toggling EXT4_EXTENTS_FL) rewrites the block mapping
representation without going through the fast commit tracking paths.
In practice these migrations are rare and usually followed by further
updates, but mixing them into a fast commit makes the overall semantics
harder to reason about and risks replay gaps if new call sites appear.

Teach ext4 to mark the filesystem fast-commit ineligible when
ext4_ext_migrate() or ext4_ind_migrate() start their journal transactions.
This forces those transactions to fall back to a full commit, ensuring
that the entire inode layout change is captured by the normal journal
rather than partially encoded in fast commit TLVs. This change should
not affect common workloads but makes format migrations safer and easier
to reason about under fast commit.

Testing:
1. prepare:
    dd if=/dev/zero of=/root/fc.img bs=1M count=0 seek=128
    mkfs.ext4 -O fast_commit -F /root/fc.img
    mkdir -p /mnt/fc && mount -t ext4 -o loop /root/fc.img /mnt/fc
2.  Created a test file and toggled the extents flag to exercise both
    ext4_ind_migrate() and ext4_ext_migrate():
    touch /mnt/fc/migtest
    chattr -e /mnt/fc/migtest
    chattr +e /mnt/fc/migtest
3. Verified fast-commit ineligible statistics:
    tail -n 1 /proc/fs/ext4/loop0/fc_info
    "Inode format migration":       2

Signed-off-by: Li Chen <me@linux.beauty>
Link: https://patch.msgid.link/20251211115146.897420-2-me@linux.beauty
Signed-off-by: Theodore Ts'o <tytso@mit.edu>
fs/ext4/fast_commit.c
fs/ext4/fast_commit.h
fs/ext4/migrate.c
include/trace/events/ext4.h

index fa66b08de9994ecf115e0422ea037d0677de0caa..afb28b3e52bb109b56b422c6b778c5dab9f50ed2 100644 (file)
@@ -2302,6 +2302,7 @@ static const char * const fc_ineligible_reasons[] = {
        [EXT4_FC_REASON_FALLOC_RANGE] = "Falloc range op",
        [EXT4_FC_REASON_INODE_JOURNAL_DATA] = "Data journalling",
        [EXT4_FC_REASON_ENCRYPTED_FILENAME] = "Encrypted filename",
+       [EXT4_FC_REASON_MIGRATE] = "Inode format migration",
 };
 
 int ext4_fc_info_show(struct seq_file *seq, void *v)
index 3bd534e4dbbfefa240ebf6a661a2e419ef09f9e7..be3b84a74c322ecca9907b3ab059fabf63777b2b 100644 (file)
@@ -97,6 +97,7 @@ enum {
        EXT4_FC_REASON_FALLOC_RANGE,
        EXT4_FC_REASON_INODE_JOURNAL_DATA,
        EXT4_FC_REASON_ENCRYPTED_FILENAME,
+       EXT4_FC_REASON_MIGRATE,
        EXT4_FC_REASON_MAX
 };
 
index 1b0dfd963d3f01992dc70893849fd4c701c30ef7..96ab95167bd6e10ba86e61a60cb0be9fbafe157f 100644 (file)
@@ -449,6 +449,12 @@ int ext4_ext_migrate(struct inode *inode)
                retval = PTR_ERR(handle);
                goto out_unlock;
        }
+       /*
+        * This operation rewrites the inode's block mapping layout
+        * (indirect to extents) and is not tracked in the fast commit
+        * log, so disable fast commits for this transaction.
+        */
+       ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_MIGRATE, handle);
        goal = (((inode->i_ino - 1) / EXT4_INODES_PER_GROUP(inode->i_sb)) *
                EXT4_INODES_PER_GROUP(inode->i_sb)) + 1;
        owner[0] = i_uid_read(inode);
@@ -630,6 +636,12 @@ int ext4_ind_migrate(struct inode *inode)
                ret = PTR_ERR(handle);
                goto out_unlock;
        }
+       /*
+        * This operation rewrites the inode's block mapping layout
+        * (extents to indirect blocks) and is not tracked in the fast
+        * commit log, so disable fast commits for this transaction.
+        */
+       ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_MIGRATE, handle);
 
        down_write(&EXT4_I(inode)->i_data_sem);
        ret = ext4_ext_check_inode(inode);
index fd76d14c2776ec789f94ca3f98b802eee73ca9f6..31598a7810d6cabd58170393d94673d44f9590b6 100644 (file)
@@ -102,6 +102,7 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_RENAME_DIR);
 TRACE_DEFINE_ENUM(EXT4_FC_REASON_FALLOC_RANGE);
 TRACE_DEFINE_ENUM(EXT4_FC_REASON_INODE_JOURNAL_DATA);
 TRACE_DEFINE_ENUM(EXT4_FC_REASON_ENCRYPTED_FILENAME);
+TRACE_DEFINE_ENUM(EXT4_FC_REASON_MIGRATE);
 TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX);
 
 #define show_fc_reason(reason)                                         \
@@ -115,7 +116,8 @@ TRACE_DEFINE_ENUM(EXT4_FC_REASON_MAX);
                { EXT4_FC_REASON_RENAME_DIR,    "RENAME_DIR"},          \
                { EXT4_FC_REASON_FALLOC_RANGE,  "FALLOC_RANGE"},        \
                { EXT4_FC_REASON_INODE_JOURNAL_DATA,    "INODE_JOURNAL_DATA"}, \
-               { EXT4_FC_REASON_ENCRYPTED_FILENAME,    "ENCRYPTED_FILENAME"})
+               { EXT4_FC_REASON_ENCRYPTED_FILENAME,    "ENCRYPTED_FILENAME"}, \
+               { EXT4_FC_REASON_MIGRATE,               "MIGRATE"})
 
 TRACE_DEFINE_ENUM(CR_POWER2_ALIGNED);
 TRACE_DEFINE_ENUM(CR_GOAL_LEN_FAST);