Whamcloud - gitweb
LU-15404 ldiskfs: truncate during setxattr leads to kernel panic 58/46358/9
authorAndrew Perepechko <andrew.perepechko@hpe.com>
Mon, 31 Jan 2022 16:55:31 +0000 (19:55 +0300)
committerOleg Drokin <green@whamcloud.com>
Mon, 7 Feb 2022 04:45:49 +0000 (04:45 +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.

Change-Id: Idd70befa6a83818ece06daccf9bb6256812674b9
Signed-off-by: Andrew Perepechko <andrew.perepechko@hpe.com>
HPE-bug-id: LUS-10534
Reviewed-on: https://review.whamcloud.com/46358
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Reviewed-by: Alexander Zarochentsev <alexander.zarochentsev@hpe.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Maloo <maloo@whamcloud.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
14 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.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-80-ubuntu20.series
ldiskfs/kernel_patches/series/ldiskfs-5.4.0-90-ubuntu20.series
ldiskfs/kernel_patches/series/ldiskfs-5.4.0-ml.series
ldiskfs/kernel_patches/series/ldiskfs-5.4.136-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 2b9b8be..dd0475e 100644 (file)
@@ -29,3 +29,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 rhel8/linux-5.4/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index ffcd76b..e5911c2 100644 (file)
@@ -29,3 +29,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 rhel8/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index 9dffa05..8092cd9 100644 (file)
@@ -29,3 +29,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 rhel8/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index f5b966c..97a3200 100644 (file)
@@ -29,3 +29,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 rhel8.4/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index a739214..0a5a156 100644 (file)
@@ -29,3 +29,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 rhel8.5/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index 1f9a593..2bfffb9 100644 (file)
@@ -31,3 +31,4 @@ base/ext4-reset-exts-for-gcc10.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 ubuntu18/ext4-projid-xattrs.patch
 rhel8/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index 4843c9b..40732f0 100644 (file)
@@ -30,3 +30,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 linux-5.4/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index e9d7770..f00707c 100644 (file)
@@ -30,3 +30,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 linux-5.4/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index 105612b..a468bb0 100644 (file)
@@ -30,3 +30,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 linux-5.4/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index da2b6ee..8960c0b 100644 (file)
@@ -30,3 +30,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 linux-5.4/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index 424ae1c..d79ae0f 100644 (file)
@@ -30,3 +30,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 linux-5.4/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index 834806c..ce959ff 100644 (file)
@@ -30,3 +30,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 linux-5.4/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch
index 4843c9b..40732f0 100644 (file)
@@ -30,3 +30,4 @@ base/ext4-no-max-dir-size-limit-for-iam-objects.patch
 rhel8/ext4-ialloc-uid-gid-and-pass-owner-down.patch
 base/ext4-projid-xattrs.patch
 linux-5.4/ext4-enc-flag.patch
+base/ext4-delayed-iput.patch