Commit 2a794f7e authored by Jan Kara's avatar Jan Kara Committed by Baokun Li
Browse files

fs: fix a hungtask problem when freeze/unfreeze fs

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: default avatarShijie Luo <luoshijie1@huawei.com>
Closes: https://lore.kernel.org/r/20201226095641.17290-1-luoshijie1@huawei.com/


Fixes: 18e9e510 ("Introduce freeze_super and thaw_super for the fsfreeze ioctl")
Signed-off-by: default avatargeruijun <geruijun@huawei.com>
Signed-off-by: default avatarJan Kara <jack@suse.cz>
Reviewed-by: default avatarZhang Yi <yi.zhang@huawei.com>
Signed-off-by: default avatarZheng Zengkai <zhengzengkai@huawei.com>
Signed-off-by: default avatarBaokun Li <libaokun1@huawei.com>
parent c2f1662c
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -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);
+2 −0
Original line number Diff line number Diff line
@@ -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];
};