summaryrefslogtreecommitdiff
path: root/fs/xfs/scrub/fscounters_repair.c
blob: ee2ade8a5f2d5c952f303bd44741ee23cef77962 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2018 Oracle.  All Rights Reserved.
 * Author: Darrick J. Wong <darrick.wong@oracle.com>
 */
#include "xfs.h"
#include "xfs_fs.h"
#include "xfs_shared.h"
#include "xfs_format.h"
#include "xfs_trans_resv.h"
#include "xfs_mount.h"
#include "xfs_defer.h"
#include "xfs_btree.h"
#include "xfs_bit.h"
#include "xfs_log_format.h"
#include "xfs_trans.h"
#include "xfs_sb.h"
#include "xfs_inode.h"
#include "xfs_alloc.h"
#include "xfs_ialloc.h"
#include "xfs_rmap.h"
#include "scrub/xfs_scrub.h"
#include "scrub/scrub.h"
#include "scrub/common.h"
#include "scrub/trace.h"
#include "scrub/repair.h"

/*
 * FS Summary Counters
 * ===================
 *
 * To repair the filesystem summary counters we compute the correct values,
 * take the difference between those values and the ones in m_sb, and modify
 * both the percpu and the m_sb counters by the corresponding amounts.  The
 * filesystem must be frozen to do anything.
 */

/*
 * Reset the superblock counters.
 *
 * The filesystem must be frozen so that the counters do not change while
 * we're computing the summary counters.
 */
int
xrep_fscounters(
	struct xfs_scrub	*sc)
{
	struct xfs_mount	*mp = sc->mp;
	struct xchk_fscounters	*fsc = sc->buf;
	int64_t			delta_icount;
	int64_t			delta_ifree;
	int64_t			delta_fdblocks;
	int			error;

	/*
	 * Reinitialize the counters.  We know that the counters in mp->m_sb
	 * are supposed to match the counters we calculated, so we therefore
	 * need to calculate the deltas...
	 */
	spin_lock(&mp->m_sb_lock);
	delta_icount = (int64_t)fsc->icount - mp->m_sb.sb_icount;
	delta_ifree = (int64_t)fsc->ifree - mp->m_sb.sb_ifree;
	delta_fdblocks = (int64_t)fsc->fdblocks - mp->m_sb.sb_fdblocks;
	spin_unlock(&mp->m_sb_lock);

	trace_xrep_reset_counters(mp, delta_icount, delta_ifree,
			delta_fdblocks);

	/* ...and then update the per-cpu counters... */
	if (delta_icount) {
		error = xfs_mod_icount(mp, delta_icount);
		if (error)
			return error;
	}
	if (delta_ifree) {
		error = xfs_mod_ifree(mp, delta_ifree);
		if (error)
			goto err_icount;
	}
	if (delta_fdblocks) {
		error = xfs_mod_fdblocks(mp, delta_fdblocks, false);
		if (error)
			goto err_ifree;
	}

	/* ...and finally log the superblock changes. */
	spin_lock(&mp->m_sb_lock);
	mp->m_sb.sb_icount = fsc->icount;
	mp->m_sb.sb_ifree = fsc->ifree;
	mp->m_sb.sb_fdblocks = fsc->fdblocks;
	mp->m_flags &= ~XFS_MOUNT_BAD_SUMMARY;
	spin_unlock(&mp->m_sb_lock);
	xfs_log_sb(sc->tp);

	return 0;
err_icount:
	xfs_mod_icount(mp, -delta_icount);
err_ifree:
	xfs_mod_ifree(mp, -delta_ifree);
	return error;
}