Skip to content

Commit

Permalink
xfs: Ensure we force all busy extents in range to disk
Browse files Browse the repository at this point in the history
When we search for and find a busy extent during allocation we
force the log out to ensure the extent free transaction is on
disk before the allocation transaction. The current implementation
has a subtle bug in it--it does not handle multiple overlapping
ranges.

That is, if we free lots of little extents into a single
contiguous extent, then allocate the contiguous extent, the busy
search code stops searching at the first extent it finds that
overlaps the allocated range. It then uses the commit LSN of the
transaction to force the log out to.

Unfortunately, the other busy ranges might have more recent
commit LSNs than the first busy extent that is found, and this
results in xfs_alloc_search_busy() returning before all the
extent free transactions are on disk for the range being
allocated. This can lead to potential metadata corruption or
stale data exposure after a crash because log replay won't replay
all the extent free transactions that cover the allocation range.

Modified-by: Alex Elder <aelder@sgi.com>

(Dropped the "found" argument from the xfs_alloc_busysearch trace
event.)

Signed-off-by: Dave Chinner <david@fromorbit.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
  • Loading branch information
dchinner authored and Alex Elder committed Jan 10, 2010
1 parent 44e08c4 commit fd45e47
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 29 deletions.
12 changes: 6 additions & 6 deletions fs/xfs/linux-2.6/xfs_trace.h
Original file line number Diff line number Diff line change
Expand Up @@ -1079,28 +1079,28 @@ TRACE_EVENT(xfs_alloc_unbusy,

TRACE_EVENT(xfs_alloc_busysearch,
TP_PROTO(struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agblock_t agbno,
xfs_extlen_t len, int found),
TP_ARGS(mp, agno, agbno, len, found),
xfs_extlen_t len, xfs_lsn_t lsn),
TP_ARGS(mp, agno, agbno, len, lsn),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_agnumber_t, agno)
__field(xfs_agblock_t, agbno)
__field(xfs_extlen_t, len)
__field(int, found)
__field(xfs_lsn_t, lsn)
),
TP_fast_assign(
__entry->dev = mp->m_super->s_dev;
__entry->agno = agno;
__entry->agbno = agbno;
__entry->len = len;
__entry->found = found;
__entry->lsn = lsn;
),
TP_printk("dev %d:%d agno %u agbno %u len %u %s",
TP_printk("dev %d:%d agno %u agbno %u len %u force lsn 0x%llx",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->agbno,
__entry->len,
__print_symbolic(__entry->found, XFS_BUSY_STATES))
__entry->lsn)
);

TRACE_EVENT(xfs_agf,
Expand Down
44 changes: 21 additions & 23 deletions fs/xfs/xfs_alloc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2563,43 +2563,41 @@ xfs_alloc_search_busy(xfs_trans_t *tp,
xfs_mount_t *mp;
xfs_perag_busy_t *bsy;
xfs_agblock_t uend, bend;
xfs_lsn_t lsn;
xfs_lsn_t lsn = 0;
int cnt;

mp = tp->t_mountp;

spin_lock(&mp->m_perag[agno].pagb_lock);
cnt = mp->m_perag[agno].pagb_count;

uend = bno + len - 1;

/* search pagb_list for this slot, skipping open slots */
for (bsy = mp->m_perag[agno].pagb_list; cnt; bsy++) {
/*
* search pagb_list for this slot, skipping open slots. We have to
* search the entire array as there may be multiple overlaps and
* we have to get the most recent LSN for the log force to push out
* all the transactions that span the range.
*/
for (cnt = 0; cnt < mp->m_perag[agno].pagb_count; cnt++) {
bsy = &mp->m_perag[agno].pagb_list[cnt];
if (!bsy->busy_tp)
continue;

/*
* (start1,length1) within (start2, length2)
*/
if (bsy->busy_tp != NULL) {
bend = bsy->busy_start + bsy->busy_length - 1;
if ((bno > bend) || (uend < bsy->busy_start)) {
cnt--;
} else {
break;
}
}
}
bend = bsy->busy_start + bsy->busy_length - 1;
if (bno > bend || uend < bsy->busy_start)
continue;

trace_xfs_alloc_busysearch(mp, agno, bno, len, !!cnt);
/* (start1,length1) within (start2, length2) */
if (XFS_LSN_CMP(bsy->busy_tp->t_commit_lsn, lsn) > 0)
lsn = bsy->busy_tp->t_commit_lsn;
}
spin_unlock(&mp->m_perag[agno].pagb_lock);
trace_xfs_alloc_busysearch(tp->t_mountp, agno, bno, len, lsn);

/*
* If a block was found, force the log through the LSN of the
* transaction that freed the block
*/
if (cnt) {
lsn = bsy->busy_tp->t_commit_lsn;
spin_unlock(&mp->m_perag[agno].pagb_lock);
if (lsn)
xfs_log_force(mp, lsn, XFS_LOG_FORCE|XFS_LOG_SYNC);
} else {
spin_unlock(&mp->m_perag[agno].pagb_lock);
}
}

0 comments on commit fd45e47

Please sign in to comment.