mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 14:11:52 +00:00
xfs: fix sb write verify for lazysbcount
When lazysbcount is enabled, fsstress and loop mount/unmount test report
the following problems:
XFS (loop0): SB summary counter sanity check failed
XFS (loop0): Metadata corruption detected at xfs_sb_write_verify+0x13b/0x460,
xfs_sb block 0x0
XFS (loop0): Unmount and run xfs_repair
XFS (loop0): First 128 bytes of corrupted metadata buffer:
00000000: 58 46 53 42 00 00 10 00 00 00 00 00 00 28 00 00 XFSB.........(..
00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000020: 69 fb 7c cd 5f dc 44 af 85 74 e0 cc d4 e3 34 5a i.|._.D..t....4Z
00000030: 00 00 00 00 00 20 00 06 00 00 00 00 00 00 00 80 ..... ..........
00000040: 00 00 00 00 00 00 00 81 00 00 00 00 00 00 00 82 ................
00000050: 00 00 00 01 00 0a 00 00 00 00 00 04 00 00 00 00 ................
00000060: 00 00 0a 00 b4 b5 02 00 02 00 00 08 00 00 00 00 ................
00000070: 00 00 00 00 00 00 00 00 0c 09 09 03 14 00 00 19 ................
XFS (loop0): Corruption of in-memory data (0x8) detected at _xfs_buf_ioapply
+0xe1e/0x10e0 (fs/xfs/xfs_buf.c:1580). Shutting down filesystem.
XFS (loop0): Please unmount the filesystem and rectify the problem(s)
XFS (loop0): log mount/recovery failed: error -117
XFS (loop0): log mount failed
This corruption will shutdown the file system and the file system will
no longer be mountable. The following script can reproduce the problem,
but it may take a long time.
#!/bin/bash
device=/dev/sda
testdir=/mnt/test
round=0
function fail()
{
echo "$*"
exit 1
}
mkdir -p $testdir
while [ $round -lt 10000 ]
do
echo "******* round $round ********"
mkfs.xfs -f $device
mount $device $testdir || fail "mount failed!"
fsstress -d $testdir -l 0 -n 10000 -p 4 >/dev/null &
sleep 4
killall -w fsstress
umount $testdir
xfs_repair -e $device > /dev/null
if [ $? -eq 2 ];then
echo "ERR CODE 2: Dirty log exception during repair."
exit 1
fi
round=$(($round+1))
done
With lazysbcount is enabled, There is no additional lock protection for
reading m_ifree and m_icount in xfs_log_sb(), if other cpu modifies the
m_ifree, this will make the m_ifree greater than m_icount. For example,
consider the following sequence and ifreedelta is postive:
CPU0 CPU1
xfs_log_sb xfs_trans_unreserve_and_mod_sb
---------- ------------------------------
percpu_counter_sum(&mp->m_icount)
percpu_counter_add_batch(&mp->m_icount,
idelta, XFS_ICOUNT_BATCH)
percpu_counter_add(&mp->m_ifree, ifreedelta);
percpu_counter_sum(&mp->m_ifree)
After this, incorrect inode count (sb_ifree > sb_icount) will be writen to
the log. In the subsequent writing of sb, incorrect inode count (sb_ifree >
sb_icount) will fail to pass the boundary check in xfs_validate_sb_write()
that cause the file system shutdown.
When lazysbcount is enabled, we don't need to guarantee that Lazy sb
counters are completely correct, but we do need to guarantee that sb_ifree
<= sb_icount. On the other hand, the constraint that m_ifree <= m_icount
must be satisfied any time that there /cannot/ be other threads allocating
or freeing inode chunks. If the constraint is violated under these
circumstances, sb_i{count,free} (the ondisk superblock inode counters)
maybe incorrect and need to be marked sick at unmount, the count will
be rebuilt on the next mount.
Fixes: 8756a5af18
("libxfs: add more bounds checking to sb sanity checks")
Signed-off-by: Long Li <leo.lilong@huawei.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
This commit is contained in:
parent
2653d53345
commit
59f6ab40fd
@ -972,7 +972,9 @@ xfs_log_sb(
|
||||
*/
|
||||
if (xfs_has_lazysbcount(mp)) {
|
||||
mp->m_sb.sb_icount = percpu_counter_sum(&mp->m_icount);
|
||||
mp->m_sb.sb_ifree = percpu_counter_sum(&mp->m_ifree);
|
||||
mp->m_sb.sb_ifree = min_t(uint64_t,
|
||||
percpu_counter_sum(&mp->m_ifree),
|
||||
mp->m_sb.sb_icount);
|
||||
mp->m_sb.sb_fdblocks = percpu_counter_sum(&mp->m_fdblocks);
|
||||
}
|
||||
|
||||
|
@ -538,6 +538,20 @@ xfs_check_summary_counts(
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
xfs_unmount_check(
|
||||
struct xfs_mount *mp)
|
||||
{
|
||||
if (xfs_is_shutdown(mp))
|
||||
return;
|
||||
|
||||
if (percpu_counter_sum(&mp->m_ifree) >
|
||||
percpu_counter_sum(&mp->m_icount)) {
|
||||
xfs_alert(mp, "ifree/icount mismatch at unmount");
|
||||
xfs_fs_mark_sick(mp, XFS_SICK_FS_COUNTERS);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush and reclaim dirty inodes in preparation for unmount. Inodes and
|
||||
* internal inode structures can be sitting in the CIL and AIL at this point,
|
||||
@ -1077,6 +1091,7 @@ xfs_unmountfs(
|
||||
if (error)
|
||||
xfs_warn(mp, "Unable to free reserved block pool. "
|
||||
"Freespace may not be correct on next mount.");
|
||||
xfs_unmount_check(mp);
|
||||
|
||||
xfs_log_unmount(mp);
|
||||
xfs_da_unmount(mp);
|
||||
|
Loading…
Reference in New Issue
Block a user