From d780f15a2d63c8bde5ae6345aed85b4b44904fb5 Mon Sep 17 00:00:00 2001 From: Jian Yu Date: Fri, 20 Sep 2019 23:15:42 +0800 Subject: [PATCH] LU-12755 ldiskfs: fix project quota unpon unpatched kernel The value of MAXQUOTAS is the number of quota types supported by kernel. With project quotas patch applied, MAXQUOTAS is equal to EXT4_MAXQUOTAS. However, on an unpatched kernel, project quota type is not supported and MAXQUOTAS is one less than EXT4_MAXQUOTAS. In ldiskfs, we need to make sure that the loop in ext4_quota_off_umount() is limiting the EXT4_MAXQUOTAS loop to the kernel MAXQUOTAS value. Otherwise, it is trying to dereference sb_dqopt(sb)->files[2] which is not an inode at all, and cause the kernel stick on a spinlock in ext4_quota_off() as follows during unmount: Call Trace: [] queued_spin_lock_slowpath+0xb/0xf [] _raw_spin_lock+0x20/0x30 [] igrab+0x1e/0x60 [] ldiskfs_quota_off+0x3b/0x130 [ldiskfs] [] ldiskfs_put_super+0x4d/0x400 [ldiskfs] [] generic_shutdown_super+0x6d/0x100 [] kill_block_super+0x27/0x70 [] deactivate_locked_super+0x4e/0x70 [] deactivate_super+0x46/0x60 [] cleanup_mnt+0x3f/0x80 [] __cleanup_mnt+0x12/0x20 [] task_work_run+0xbb/0xe0 [] do_notify_resume+0xa5/0xc0 [] int_signal+0x12/0x17 Test-Parameters: clientdistro=el7.7 serverdistro=el7.7 Change-Id: I18a4d97656e2f8478754943424c0fac927f843ca Signed-off-by: Jian Yu Reviewed-on: https://review.whamcloud.com/36203 Reviewed-by: Wang Shilong Tested-by: jenkins Reviewed-by: Andreas Dilger Reviewed-by: Li Xi Tested-by: Maloo Reviewed-by: Oleg Drokin --- .../ext4-fix-project-with-unpatched-kernel.patch | 96 ++++++++++++++++++++++ .../series/ldiskfs-3.10-rhel7.7.series | 1 + 2 files changed, 97 insertions(+) create mode 100644 ldiskfs/kernel_patches/patches/rhel7.7/ext4-fix-project-with-unpatched-kernel.patch diff --git a/ldiskfs/kernel_patches/patches/rhel7.7/ext4-fix-project-with-unpatched-kernel.patch b/ldiskfs/kernel_patches/patches/rhel7.7/ext4-fix-project-with-unpatched-kernel.patch new file mode 100644 index 0000000..cf4da55 --- /dev/null +++ b/ldiskfs/kernel_patches/patches/rhel7.7/ext4-fix-project-with-unpatched-kernel.patch @@ -0,0 +1,96 @@ +diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h +index 388b6bcd..111ac5e1 100644 +--- a/fs/ext4/ext4.h ++++ b/fs/ext4/ext4.h +@@ -1485,6 +1485,8 @@ struct ext4_sb_info { + struct ratelimit_state s_warning_ratelimit_state; + struct ratelimit_state s_msg_ratelimit_state; + struct dax_device *s_daxdev; ++ ++ bool s_proj_kernel_supported; + }; + + static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) +diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c +index e6566eb9..ac0dd4a7 100644 +--- a/fs/ext4/inode.c ++++ b/fs/ext4/inode.c +@@ -4390,6 +4390,17 @@ int ext4_get_projid(struct inode *inode, kprojid_t *projid) + { + struct ext4_super_block *sbi = EXT4_SB(inode->i_sb)->s_es; + ++ /* ++ * This is tricky and just used to detect whether kernel ++ * supports project quota, return error to make sure dquot_initialize() ++ * doesn't do anything except calling this function. ++ */ ++ if (inode->i_ino == EXT4_ROOT_INO && ++ !EXT4_SB(inode->i_sb)->s_proj_kernel_supported) { ++ EXT4_SB(inode->i_sb)->s_proj_kernel_supported = true; ++ return -EINVAL; ++ } ++ + if (!EXT4_HAS_RO_COMPAT_FEATURE(inode->i_sb, EXT4_FEATURE_RO_COMPAT_PROJECT) && + !sbi->s_prj_quota_inum) + return -EOPNOTSUPP; +diff --git a/fs/ext4/super.c b/fs/ext4/super.c +index 1c81d178..bb92ea94 100644 +--- a/fs/ext4/super.c ++++ b/fs/ext4/super.c +@@ -819,8 +819,11 @@ static inline void ext4_quota_off_umount(struct super_block *sb) + int type; + + /* Use our quota_off function to clear inode flags etc. */ +- for (type = 0; type < EXT4_MAXQUOTAS; type++) ++ for (type = 0; type < EXT4_MAXQUOTAS; type++) { ++ if (!EXT4_SB(sb)->s_proj_kernel_supported && type == PRJQUOTA) ++ continue; + ext4_quota_off(sb, type); ++ } + } + #else + static inline void ext4_quota_off_umount(struct super_block *sb) +@@ -2430,6 +2433,9 @@ static void ext4_orphan_cleanup(struct super_block *sb, + /* Turn off quotas if they were enabled for orphan cleanup */ + if (quota_update) { + for (i = 0; i < EXT4_MAXQUOTAS; i++) { ++ if (!EXT4_SB(sb)->s_proj_kernel_supported && ++ i == PRJQUOTA) ++ continue; + if (sb_dqopt(sb)->files[i]) + dquot_quota_off(sb, i); + } +@@ -3630,6 +3636,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) + int err = 0; + unsigned int journal_ioprio = DEFAULT_JOURNAL_IOPRIO; + ext4_group_t first_not_zeroed; ++ unsigned int quota_flags; + + if ((data && !orig_data) || !sbi) + goto out_free_base; +@@ -4356,6 +4363,14 @@ no_journal: + root = NULL; + goto failed_mount4; + } ++ /* ++ * Enable project usage temporarily to give dquota_initialize() a ++ * chance to check whether kernel supports Project quota. ++ */ ++ quota_flags = sb_dqopt(sb)->flags; ++ sb_dqopt(sb)->flags |= dquot_state_flag(DQUOT_USAGE_ENABLED, PRJQUOTA); ++ dquot_initialize(root); ++ sb_dqopt(sb)->flags = quota_flags; + if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) { + ext4_msg(sb, KERN_ERR, "corrupt root inode, run e2fsck"); + iput(root); +@@ -5671,6 +5686,10 @@ static int ext4_enable_quotas(struct super_block *sb) + + sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE; + for (type = 0; type < EXT4_MAXQUOTAS; type++) { ++ ++ if (!EXT4_SB(sb)->s_proj_kernel_supported && type == PRJQUOTA) ++ continue; ++ + if (qf_inums[type]) { + err = ext4_quota_enable(sb, type, QFMT_VFS_V1, + DQUOT_USAGE_ENABLED); diff --git a/ldiskfs/kernel_patches/series/ldiskfs-3.10-rhel7.7.series b/ldiskfs/kernel_patches/series/ldiskfs-3.10-rhel7.7.series index f2472a5..0f62125 100644 --- a/ldiskfs/kernel_patches/series/ldiskfs-3.10-rhel7.7.series +++ b/ldiskfs/kernel_patches/series/ldiskfs-3.10-rhel7.7.series @@ -37,3 +37,4 @@ rhel7/ext4-export-orphan-add.patch rhel7/ext4-include-terminating-u32-in-size-of-xattr-entries-when-expanding-inodes.patch rhel7.2/ext4-export-mb-stream-allocator-variables.patch rhel7/ext4-optimize-ext4_find_delalloc_range-in-nodelalloc.patch +rhel7.7/ext4-fix-project-with-unpatched-kernel.patch -- 1.8.3.1