From 4ba95c0a6d7f9c0acecbcc776e252de6478c2875 Mon Sep 17 00:00:00 2001 From: Andrew Perepechko Date: Wed, 9 Nov 2022 20:59:27 -0800 Subject: [PATCH] LU-15404 ldiskfs: truncate during setxattr leads to kernel panic When changing a large xattr value to a different large xattr value, the old xattr inode is freed. Truncate during the final iput causes current transaction restart. Eventually, parent inode bh is marked dirty and kernel panic happens when jbd2 figures out that this bh belongs to the committed transaction. A possible fix is to call this final iput in a separate thread. This way, setxattr transactions will never be split into two. Since the setxattr code adds xattr inodes with nlink=0 into the orphan list, old xattr inodes will be properly cleaned up in any case. Lustre-change: https://review.whamcloud.com/46358 Lustre-commit: e239a14001b62d96c186ae2c9f58402f73e63dcc Change-Id: Idd70befa6a83818ece06daccf9bb6256812674b9 Signed-off-by: Andrew Perepechko HPE-bug-id: LUS-10534 Reviewed-by: Andreas Dilger Reviewed-by: Alexander Zarochentsev Reviewed-on: https://review.whamcloud.com/c/ex/lustre-release/+/48999 Tested-by: jenkins Tested-by: Maloo --- .../patches/base/ext4-delayed-iput.patch | 79 ++++++++++++++++++++++ .../series/ldiskfs-4.18-rhel8.1.series | 1 + .../series/ldiskfs-4.18-rhel8.2.series | 1 + .../series/ldiskfs-4.18-rhel8.3.series | 1 + .../series/ldiskfs-4.18-rhel8.4.series | 1 + .../series/ldiskfs-4.18-rhel8.5.series | 1 + .../series/ldiskfs-4.18-rhel8.6.series | 1 + .../series/ldiskfs-4.18-rhel8.series | 1 + .../series/ldiskfs-5.4.0-42-ubuntu20.series | 1 + .../series/ldiskfs-5.4.0-66-ubuntu20.series | 1 + .../kernel_patches/series/ldiskfs-5.4.0-ml.series | 1 + .../kernel_patches/series/ldiskfs-5.4.21-ml.series | 1 + 12 files changed, 90 insertions(+) create mode 100644 ldiskfs/kernel_patches/patches/base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/patches/base/ext4-delayed-iput.patch b/ldiskfs/kernel_patches/patches/base/ext4-delayed-iput.patch new file mode 100644 index 0000000..7543cce --- /dev/null +++ b/ldiskfs/kernel_patches/patches/base/ext4-delayed-iput.patch @@ -0,0 +1,79 @@ +diff --git a/fs/ext4/super.c b/fs/ext4/super.c +index 1ac2f2cdc..197dd546d 100644 +--- a/fs/ext4/super.c ++++ b/fs/ext4/super.c +@@ -972,6 +972,8 @@ static void ext4_put_super(struct super_block *sb) + int aborted = 0; + int i, err; + ++ flush_scheduled_work(); ++ + ext4_unregister_li_request(sb); + ext4_quota_off_umount(sb); + +diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c +index 491f9ee40..6a77f12d1 100644 +--- a/fs/ext4/xattr.c ++++ b/fs/ext4/xattr.c +@@ -1551,6 +1551,31 @@ static int ext4_xattr_inode_lookup_create(handle_t *handle, struct inode *inode, + return 0; + } + ++struct delayed_iput_work { ++ struct work_struct work; ++ struct inode *inode; ++}; ++ ++static void delayed_iput_fn(struct work_struct *work) ++{ ++ struct delayed_iput_work *diwork; ++ ++ diwork = container_of(work, struct delayed_iput_work, work); ++ iput(diwork->inode); ++ kfree(diwork); ++} ++ ++static void delayed_iput(struct inode *inode, struct delayed_iput_work *work) ++{ ++ if (!work) { ++ iput(inode); ++ } else { ++ INIT_WORK(&work->work, delayed_iput_fn); ++ work->inode = inode; ++ schedule_work(&work->work); ++ } ++} ++ + /* + * Reserve min(block_size/8, 1024) bytes for xattr entries/names if ea_inode + * feature is enabled. +@@ -1568,6 +1593,7 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i, + int in_inode = i->in_inode; + struct inode *old_ea_inode = NULL; + struct inode *new_ea_inode = NULL; ++ struct delayed_iput_work *diwork = NULL; + size_t old_size, new_size; + int ret; + +@@ -1644,7 +1670,11 @@ static int ext4_xattr_set_entry(struct ext4_xattr_info *i, + * Finish that work before doing any modifications to the xattr data. + */ + if (!s->not_found && here->e_value_inum) { +- ret = ext4_xattr_inode_iget(inode, ++ diwork = kmalloc(sizeof(*diwork), GFP_NOFS); ++ if (!diwork) ++ ret = -ENOMEM; ++ else ++ ret = ext4_xattr_inode_iget(inode, + le32_to_cpu(here->e_value_inum), + le32_to_cpu(here->e_hash), + &old_ea_inode); +@@ -1797,7 +1827,7 @@ update_hash: + + ret = 0; + out: +- iput(old_ea_inode); ++ delayed_iput(old_ea_inode, diwork); + iput(new_ea_inode); + return ret; + } diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.1.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.1.series index 366db8c..b62a740 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.1.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.1.series @@ -30,3 +30,4 @@ rhel8/linux-5.4/ext4-enc-flag.patch rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch base/ext4-projid-xattrs.patch rhel8/ext4-filename-encode.patch +base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.2.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.2.series index 232de9f..5e7b1ea 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.2.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.2.series @@ -30,3 +30,4 @@ rhel8/ext4-enc-flag.patch rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch base/ext4-projid-xattrs.patch rhel8/ext4-filename-encode.patch +base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.3.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.3.series index 95c5724..eac24a6 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.3.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.3.series @@ -30,3 +30,4 @@ rhel8/ext4-enc-flag.patch rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch base/ext4-projid-xattrs.patch rhel8/ext4-filename-encode.patch +base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.4.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.4.series index be8c0bd..c274fdd 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.4.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.4.series @@ -31,3 +31,4 @@ rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch base/ext4-projid-xattrs.patch rhel8.5/ext4-filename-encode.patch rhel8/ext4-old_ea_inodes_handling_fix.patch +base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.5.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.5.series index be8c0bd..c274fdd 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.5.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.5.series @@ -31,3 +31,4 @@ rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch base/ext4-projid-xattrs.patch rhel8.5/ext4-filename-encode.patch rhel8/ext4-old_ea_inodes_handling_fix.patch +base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.6.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.6.series index be8c0bd..c274fdd 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.6.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.6.series @@ -31,3 +31,4 @@ rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch base/ext4-projid-xattrs.patch rhel8.5/ext4-filename-encode.patch rhel8/ext4-old_ea_inodes_handling_fix.patch +base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.series b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.series index 773c1aa..d794945 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.series @@ -32,3 +32,4 @@ rhel8/ext4-enc-flag.patch rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch ubuntu18/ext4-projid-xattrs.patch rhel8/ext4-filename-encode.patch +base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-42-ubuntu20.series b/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-42-ubuntu20.series index 9bb1c6b..f5cb25c 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-42-ubuntu20.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-42-ubuntu20.series @@ -29,3 +29,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch linux-5.4/ext4-enc-flag.patch rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch base/ext4-projid-xattrs.patch +base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-66-ubuntu20.series b/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-66-ubuntu20.series index 49a9b3c..0d47ecf 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-66-ubuntu20.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-66-ubuntu20.series @@ -29,3 +29,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch linux-5.4/ext4-enc-flag.patch rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch base/ext4-projid-xattrs.patch +base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-ml.series b/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-ml.series index 6a66a8d..a370d0d 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-ml.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-5.4.0-ml.series @@ -29,3 +29,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch linux-5.4/ext4-enc-flag.patch rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch base/ext4-projid-xattrs.patch +base/ext4-delayed-iput.patch diff --git a/ldiskfs/kernel_patches/series/ldiskfs-5.4.21-ml.series b/ldiskfs/kernel_patches/series/ldiskfs-5.4.21-ml.series index 9bb1c6b..f5cb25c 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-5.4.21-ml.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-5.4.21-ml.series @@ -29,3 +29,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch linux-5.4/ext4-enc-flag.patch rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch base/ext4-projid-xattrs.patch +base/ext4-delayed-iput.patch -- 1.8.3.1