
From: Jan Kara <jack@suse.cz> maillist inclusion category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/IBEQJ3 Reference: https://lore.kernel.org/r/20210104160457.GG4018@quack2.suse.cz/ -------------------------------- We found the following deadlock when running xfstests generic/390 with ext4 filesystem, and simutaneously offlining/onlining the disk we tested. It will cause a deadlock whose call trace is like this: fsstress D 0 11672 11625 0x00000080 Call Trace: ? __schedule+0x2fc/0x930 ? filename_parentat+0x10b/0x1a0 schedule+0x28/0x70 rwsem_down_read_failed+0x102/0x1c0 ? __percpu_down_read+0x93/0xb0 __percpu_down_read+0x93/0xb0 __sb_start_write+0x5f/0x70 mnt_want_write+0x20/0x50 do_renameat2+0x1f3/0x550 __x64_sys_rename+0x1c/0x20 do_syscall_64+0x5b/0x1b0 entry_SYSCALL_64_after_hwframe+0x65/0xca The root cause is that when ext4 hits IO error due to disk being offline, it will switch itself into read-only state. When it is frozen at that moment, following thaw_super() call will not unlock percpu freeze semaphores (as the fs is read-only) causing the deadlock. Fix the problem by tracking whether the superblock was read-only at the time we were freezing it. Reported-and-tested-by: Shijie Luo <luoshijie1@huawei.com> Closes: https://lore.kernel.org/r/20201226095641.17290-1-luoshijie1@huawei.com/ Fixes: 18e9e5104fcd ("Introduce freeze_super and thaw_super for the fsfreeze ioctl") Signed-off-by: geruijun <geruijun@huawei.com> Signed-off-by: Jan Kara <jack@suse.cz> Reviewed-by: Zhang Yi <yi.zhang@huawei.com> Signed-off-by: Zheng Zengkai <zhengzengkai@huawei.com> Signed-off-by: Baokun Li <libaokun1@huawei.com> --- fs/super.c | 9 ++++++++- include/linux/fs.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/fs/super.c b/fs/super.c index b142e71eb8df..42c9754e3add 100644 --- a/fs/super.c +++ b/fs/super.c @@ -1996,11 +1996,13 @@ int freeze_super(struct super_block *sb, enum freeze_holder who) /* Nothing to do really... */ sb->s_writers.freeze_holders |= who; sb->s_writers.frozen = SB_FREEZE_COMPLETE; + sb->s_writers.frozen_ro = 1; wake_up_var(&sb->s_writers.frozen); super_unlock_excl(sb); return 0; } + sb->s_writers.frozen_ro = 0; sb->s_writers.frozen = SB_FREEZE_WRITE; /* Release s_umount to preserve sb_start_write -> s_umount ordering */ super_unlock_excl(sb); @@ -2082,7 +2084,12 @@ static int thaw_super_locked(struct super_block *sb, enum freeze_holder who) return -EINVAL; } - if (sb_rdonly(sb)) { + /* + * Was the fs frozen in read-only state? Note that we cannot just check + * sb_rdonly(sb) as the filesystem might have switched to read-only + * state due to internal errors or so. + */ + if (sb->s_writers.frozen_ro) { sb->s_writers.freeze_holders &= ~who; sb->s_writers.frozen = SB_UNFROZEN; wake_up_var(&sb->s_writers.frozen); diff --git a/include/linux/fs.h b/include/linux/fs.h index 0efc8bea182d..8c0cdb73a838 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1225,6 +1225,8 @@ enum { struct sb_writers { unsigned short frozen; /* Is sb frozen? */ + unsigned short frozen_ro; /* Was sb read-only + * when frozen? */ unsigned short freeze_holders; /* Who froze fs? */ struct percpu_rw_semaphore rw_sem[SB_FREEZE_LEVELS]; }; -- 2.46.1