Skip to content

Commit 1aec7c3

Browse files
author
Darrick J. Wong
committed
xfs: remove obsolete AGF counter debugging
In commit f8f2835 we changed the behavior of XFS to use EFIs to remove blocks from an overfilled AGFL because there were complaints about transaction overruns that stemmed from trying to free multiple blocks in a single transaction. Unfortunately, that commit missed a subtlety in the debug-mode transaction accounting when a realtime volume is attached. If a realtime file undergoes a data fork mapping change such that realtime extents are allocated (or freed) in the same transaction that a data device block is also allocated (or freed), we can trip a debugging assertion. This can happen (for example) if a realtime extent is allocated and it is necessary to reshape the bmbt to hold the new mapping. When we go to allocate a bmbt block from an AG, the first thing the data device block allocator does is ensure that the freelist is the proper length. If the freelist is too long, it will trim the freelist to the proper length. In debug mode, trimming the freelist calls xfs_trans_agflist_delta() to record the decrement in the AG free list count. Prior to f8f28 we would put the free block back in the free space btrees in the same transaction, which calls xfs_trans_agblocks_delta() to record the increment in the AG free block count. Since AGFL blocks are included in the global free block count (fdblocks), there is no corresponding fdblocks update, so the AGFL free satisfies the following condition in xfs_trans_apply_sb_deltas: /* * Check that superblock mods match the mods made to AGF counters. */ ASSERT((tp->t_fdblocks_delta + tp->t_res_fdblocks_delta) == (tp->t_ag_freeblks_delta + tp->t_ag_flist_delta + tp->t_ag_btree_delta)); The comparison here used to be: (X + 0) == ((X+1) + -1 + 0), where X is the number blocks that were allocated. After commit f8f28 we defer the block freeing to the next chained transaction, which means that the calls to xfs_trans_agflist_delta and xfs_trans_agblocks_delta occur in separate transactions. The (first) transaction that shortens the free list trips on the comparison, which has now become: (X + 0) == ((X) + -1 + 0) because we haven't freed the AGFL block yet; we've only logged an intention to free it. When the second transaction (the deferred free) commits, it will evaluate the expression as: (0 + 0) == (1 + 0 + 0) and trip over that in turn. At this point, the astute reader may note that the two commits tagged by this patch have been in the kernel for a long time but haven't generated any bug reports. How is it that the author became aware of this bug? This originally surfaced as an intermittent failure when I was testing realtime rmap, but a different bug report by Zorro Lang reveals the same assertion occuring on !lazysbcount filesystems. The common factor to both reports (and why this problem wasn't previously reported) becomes apparent if we consider when xfs_trans_apply_sb_deltas is called by __xfs_trans_commit(): if (tp->t_flags & XFS_TRANS_SB_DIRTY) xfs_trans_apply_sb_deltas(tp); With a modern lazysbcount filesystem, transactions update only the percpu counters, so they don't need to set XFS_TRANS_SB_DIRTY, hence xfs_trans_apply_sb_deltas is rarely called. However, updates to the count of free realtime extents are not part of lazysbcount, so XFS_TRANS_SB_DIRTY will be set on transactions adding or removing data fork mappings to realtime files; similarly, XFS_TRANS_SB_DIRTY is always set on !lazysbcount filesystems. Dave mentioned in response to an earlier version of this patch: "IIUC, what you are saying is that this debug code is simply not exercised in normal testing and hasn't been for the past decade? And it still won't be exercised on anything other than realtime device testing? "...it was debugging code from 1994 that was largely turned into dead code when lazysbcounters were introduced in 2007. Hence I'm not sure it holds any value anymore." This debugging code isn't especially helpful - you can modify the flcount on one AG and the freeblks of another AG, and it won't trigger. Add the fact that nobody noticed for a decade, and let's just get rid of it (and start testing realtime :P). This bug was found by running generic/051 on either a V4 filesystem lacking lazysbcount; or a V5 filesystem with a realtime volume. Cc: [email protected], [email protected] Fixes: f8f2835 ("xfs: defer agfl block frees when dfops is available") Signed-off-by: Darrick J. Wong <[email protected]> Reviewed-by: Brian Foster <[email protected]>
1 parent 732de7d commit 1aec7c3

File tree

6 files changed

+0
-31
lines changed

6 files changed

+0
-31
lines changed

fs/xfs/libxfs/xfs_alloc.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,6 @@ xfs_alloc_update_counters(
718718
agbp->b_pag->pagf_freeblks += len;
719719
be32_add_cpu(&agf->agf_freeblks, len);
720720

721-
xfs_trans_agblocks_delta(tp, len);
722721
if (unlikely(be32_to_cpu(agf->agf_freeblks) >
723722
be32_to_cpu(agf->agf_length))) {
724723
xfs_buf_mark_corrupt(agbp);
@@ -2739,7 +2738,6 @@ xfs_alloc_get_freelist(
27392738
pag = agbp->b_pag;
27402739
ASSERT(!pag->pagf_agflreset);
27412740
be32_add_cpu(&agf->agf_flcount, -1);
2742-
xfs_trans_agflist_delta(tp, -1);
27432741
pag->pagf_flcount--;
27442742

27452743
logflags = XFS_AGF_FLFIRST | XFS_AGF_FLCOUNT;
@@ -2846,7 +2844,6 @@ xfs_alloc_put_freelist(
28462844
pag = agbp->b_pag;
28472845
ASSERT(!pag->pagf_agflreset);
28482846
be32_add_cpu(&agf->agf_flcount, 1);
2849-
xfs_trans_agflist_delta(tp, 1);
28502847
pag->pagf_flcount++;
28512848

28522849
logflags = XFS_AGF_FLLAST | XFS_AGF_FLCOUNT;

fs/xfs/libxfs/xfs_alloc_btree.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ xfs_allocbt_alloc_block(
7373

7474
xfs_extent_busy_reuse(cur->bc_mp, cur->bc_ag.agno, bno, 1, false);
7575

76-
xfs_trans_agbtree_delta(cur->bc_tp, 1);
7776
new->s = cpu_to_be32(bno);
7877

7978
*stat = 1;
@@ -97,7 +96,6 @@ xfs_allocbt_free_block(
9796

9897
xfs_extent_busy_insert(cur->bc_tp, be32_to_cpu(agf->agf_seqno), bno, 1,
9998
XFS_EXTENT_BUSY_SKIP_DISCARD);
100-
xfs_trans_agbtree_delta(cur->bc_tp, -1);
10199
return 0;
102100
}
103101

fs/xfs/libxfs/xfs_rmap_btree.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ xfs_rmapbt_alloc_block(
103103
xfs_extent_busy_reuse(cur->bc_mp, cur->bc_ag.agno, bno, 1,
104104
false);
105105

106-
xfs_trans_agbtree_delta(cur->bc_tp, 1);
107106
new->s = cpu_to_be32(bno);
108107
be32_add_cpu(&agf->agf_rmap_blocks, 1);
109108
xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_RMAP_BLOCKS);
@@ -136,7 +135,6 @@ xfs_rmapbt_free_block(
136135

137136
xfs_extent_busy_insert(cur->bc_tp, be32_to_cpu(agf->agf_seqno), bno, 1,
138137
XFS_EXTENT_BUSY_SKIP_DISCARD);
139-
xfs_trans_agbtree_delta(cur->bc_tp, -1);
140138

141139
pag = cur->bc_ag.agbp->b_pag;
142140
xfs_ag_resv_free_extent(pag, XFS_AG_RESV_RMAPBT, NULL, 1);

fs/xfs/xfs_fsops.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,6 @@ xfs_resizefs_init_new_ags(
6969
if (error)
7070
return error;
7171

72-
xfs_trans_agblocks_delta(tp, id->nfree);
73-
7472
if (delta) {
7573
*lastag_extended = true;
7674
error = xfs_ag_extend_space(mp, tp, id, delta);

fs/xfs/xfs_trans.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -487,13 +487,6 @@ xfs_trans_apply_sb_deltas(
487487
bp = xfs_trans_getsb(tp);
488488
sbp = bp->b_addr;
489489

490-
/*
491-
* Check that superblock mods match the mods made to AGF counters.
492-
*/
493-
ASSERT((tp->t_fdblocks_delta + tp->t_res_fdblocks_delta) ==
494-
(tp->t_ag_freeblks_delta + tp->t_ag_flist_delta +
495-
tp->t_ag_btree_delta));
496-
497490
/*
498491
* Only update the superblock counters if we are logging them
499492
*/

fs/xfs/xfs_trans.h

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,6 @@ typedef struct xfs_trans {
140140
int64_t t_res_fdblocks_delta; /* on-disk only chg */
141141
int64_t t_frextents_delta;/* superblock freextents chg*/
142142
int64_t t_res_frextents_delta; /* on-disk only chg */
143-
#if defined(DEBUG) || defined(XFS_WARN)
144-
int64_t t_ag_freeblks_delta; /* debugging counter */
145-
int64_t t_ag_flist_delta; /* debugging counter */
146-
int64_t t_ag_btree_delta; /* debugging counter */
147-
#endif
148143
int64_t t_dblocks_delta;/* superblock dblocks change */
149144
int64_t t_agcount_delta;/* superblock agcount change */
150145
int64_t t_imaxpct_delta;/* superblock imaxpct change */
@@ -165,16 +160,6 @@ typedef struct xfs_trans {
165160
*/
166161
#define xfs_trans_set_sync(tp) ((tp)->t_flags |= XFS_TRANS_SYNC)
167162

168-
#if defined(DEBUG) || defined(XFS_WARN)
169-
#define xfs_trans_agblocks_delta(tp, d) ((tp)->t_ag_freeblks_delta += (int64_t)d)
170-
#define xfs_trans_agflist_delta(tp, d) ((tp)->t_ag_flist_delta += (int64_t)d)
171-
#define xfs_trans_agbtree_delta(tp, d) ((tp)->t_ag_btree_delta += (int64_t)d)
172-
#else
173-
#define xfs_trans_agblocks_delta(tp, d)
174-
#define xfs_trans_agflist_delta(tp, d)
175-
#define xfs_trans_agbtree_delta(tp, d)
176-
#endif
177-
178163
/*
179164
* XFS transaction mechanism exported interfaces.
180165
*/

0 commit comments

Comments
 (0)