Whamcloud - gitweb
LU-15404 ldiskfs: truncate during setxattr leads to kernel panic
authorAndrew Perepechko <andrew.perepechko@hpe.com>
Thu, 10 Nov 2022 04:59:27 +0000 (20:59 -0800)
committerAndreas Dilger <adilger@whamcloud.com>
Fri, 11 Nov 2022 09:37:56 +0000 (09:37 +0000)
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 <andrew.perepechko@hpe.com>
HPE-bug-id: LUS-10534
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Alexander Zarochentsev <alexander.zarochentsev@hpe.com>
Reviewed-on: https://review.whamcloud.com/c/ex/lustre-release/+/48999
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
12 files changed:
ldiskfs/kernel_patches/patches/base/ext4-delayed-iput.patch [new file with mode: 0644]
ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.1.series
ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.2.series
ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.3.series
ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.4.series
ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.5.series
ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.6.series
ldiskfs/kernel_patches/series/ldiskfs-4.18-rhel8.series
ldiskfs/kernel_patches/series/ldiskfs-5.4.0-42-ubuntu20.series
ldiskfs/kernel_patches/series/ldiskfs-5.4.0-66-ubuntu20.series
ldiskfs/kernel_patches/series/ldiskfs-5.4.0-ml.series
ldiskfs/kernel_patches/series/ldiskfs-5.4.21-ml.series

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 (file)
index 0000000..7543cce
--- /dev/null
@@ -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;
+ }
index 366db8c..b62a740 100644 (file)
@@ -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
index 232de9f..5e7b1ea 100644 (file)
@@ -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
index 95c5724..eac24a6 100644 (file)
@@ -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
index be8c0bd..c274fdd 100644 (file)
@@ -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
index be8c0bd..c274fdd 100644 (file)
@@ -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
index be8c0bd..c274fdd 100644 (file)
@@ -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
index 773c1aa..d794945 100644 (file)
@@ -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
index 9bb1c6b..f5cb25c 100644 (file)
@@ -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
index 49a9b3c..0d47ecf 100644 (file)
@@ -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
index 6a66a8d..a370d0d 100644 (file)
@@ -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
index 9bb1c6b..f5cb25c 100644 (file)
@@ -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