Commit dbbdbd00 authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: repair problems in CoW forks

Try to repair errors that we see in file CoW forks so that we don't do
stupid things like remap garbage into a file.  There's not a lot we can
do with the COW fork -- the ondisk metadata record only that the COW
staging extents are owned by the refcount btree, which effectively means
that we can't reconstruct this incore structure from scratch.

Actually, this is even worse -- we can't touch written extents, because
those map space that are actively under writeback, and there's not much
to do with delalloc reservations.  Hence we can only detect crosslinked
unwritten extents and fix them by punching out the problematic parts and
replacing them with delalloc extents.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent d12bf8ba
...@@ -184,6 +184,7 @@ xfs-y += $(addprefix scrub/, \ ...@@ -184,6 +184,7 @@ xfs-y += $(addprefix scrub/, \
agheader_repair.o \ agheader_repair.o \
alloc_repair.o \ alloc_repair.o \
bmap_repair.o \ bmap_repair.o \
cow_repair.o \
ialloc_repair.o \ ialloc_repair.o \
inode_repair.o \ inode_repair.o \
newbt.o \ newbt.o \
......
This diff is collapsed.
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2022-2023 Oracle. All Rights Reserved.
* Author: Darrick J. Wong <djwong@kernel.org>
*/
#ifndef __XFS_SCRUB_OFF_BITMAP_H__
#define __XFS_SCRUB_OFF_BITMAP_H__
/* Bitmaps, but for type-checked for xfs_fileoff_t */
struct xoff_bitmap {
struct xbitmap64 offbitmap;
};
static inline void xoff_bitmap_init(struct xoff_bitmap *bitmap)
{
xbitmap64_init(&bitmap->offbitmap);
}
static inline void xoff_bitmap_destroy(struct xoff_bitmap *bitmap)
{
xbitmap64_destroy(&bitmap->offbitmap);
}
static inline int xoff_bitmap_set(struct xoff_bitmap *bitmap,
xfs_fileoff_t off, xfs_filblks_t len)
{
return xbitmap64_set(&bitmap->offbitmap, off, len);
}
static inline int xoff_bitmap_walk(struct xoff_bitmap *bitmap,
xbitmap64_walk_fn fn, void *priv)
{
return xbitmap64_walk(&bitmap->offbitmap, fn, priv);
}
#endif /* __XFS_SCRUB_OFF_BITMAP_H__ */
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "xfs_ialloc_btree.h" #include "xfs_ialloc_btree.h"
#include "xfs_rmap.h" #include "xfs_rmap.h"
#include "xfs_rmap_btree.h" #include "xfs_rmap_btree.h"
#include "xfs_refcount.h"
#include "xfs_refcount_btree.h" #include "xfs_refcount_btree.h"
#include "xfs_extent_busy.h" #include "xfs_extent_busy.h"
#include "xfs_ag.h" #include "xfs_ag.h"
...@@ -380,6 +381,17 @@ xreap_agextent_iter( ...@@ -380,6 +381,17 @@ xreap_agextent_iter(
trace_xreap_dispose_unmap_extent(sc->sa.pag, agbno, *aglenp); trace_xreap_dispose_unmap_extent(sc->sa.pag, agbno, *aglenp);
rs->force_roll = true; rs->force_roll = true;
if (rs->oinfo == &XFS_RMAP_OINFO_COW) {
/*
* If we're unmapping CoW staging extents, remove the
* records from the refcountbt, which will remove the
* rmap record as well.
*/
xfs_refcount_free_cow_extent(sc->tp, fsbno, *aglenp);
return 0;
}
return xfs_rmap_free(sc->tp, sc->sa.agf_bp, sc->sa.pag, agbno, return xfs_rmap_free(sc->tp, sc->sa.agf_bp, sc->sa.pag, agbno,
*aglenp, rs->oinfo); *aglenp, rs->oinfo);
} }
...@@ -398,6 +410,26 @@ xreap_agextent_iter( ...@@ -398,6 +410,26 @@ xreap_agextent_iter(
return 0; return 0;
} }
/*
* If we're getting rid of CoW staging extents, use deferred work items
* to remove the refcountbt records (which removes the rmap records)
* and free the extent. We're not worried about the system going down
* here because log recovery walks the refcount btree to clean out the
* CoW staging extents.
*/
if (rs->oinfo == &XFS_RMAP_OINFO_COW) {
ASSERT(rs->resv == XFS_AG_RESV_NONE);
xfs_refcount_free_cow_extent(sc->tp, fsbno, *aglenp);
error = xfs_free_extent_later(sc->tp, fsbno, *aglenp, NULL,
rs->resv, true);
if (error)
return error;
rs->force_roll = true;
return 0;
}
/* Put blocks back on the AGFL one at a time. */ /* Put blocks back on the AGFL one at a time. */
if (rs->resv == XFS_AG_RESV_AGFL) { if (rs->resv == XFS_AG_RESV_AGFL) {
ASSERT(*aglenp == 1); ASSERT(*aglenp == 1);
......
...@@ -111,6 +111,7 @@ int xrep_refcountbt(struct xfs_scrub *sc); ...@@ -111,6 +111,7 @@ int xrep_refcountbt(struct xfs_scrub *sc);
int xrep_inode(struct xfs_scrub *sc); int xrep_inode(struct xfs_scrub *sc);
int xrep_bmap_data(struct xfs_scrub *sc); int xrep_bmap_data(struct xfs_scrub *sc);
int xrep_bmap_attr(struct xfs_scrub *sc); int xrep_bmap_attr(struct xfs_scrub *sc);
int xrep_bmap_cow(struct xfs_scrub *sc);
int xrep_reinit_pagf(struct xfs_scrub *sc); int xrep_reinit_pagf(struct xfs_scrub *sc);
int xrep_reinit_pagi(struct xfs_scrub *sc); int xrep_reinit_pagi(struct xfs_scrub *sc);
...@@ -173,6 +174,7 @@ xrep_setup_nothing( ...@@ -173,6 +174,7 @@ xrep_setup_nothing(
#define xrep_inode xrep_notsupported #define xrep_inode xrep_notsupported
#define xrep_bmap_data xrep_notsupported #define xrep_bmap_data xrep_notsupported
#define xrep_bmap_attr xrep_notsupported #define xrep_bmap_attr xrep_notsupported
#define xrep_bmap_cow xrep_notsupported
#endif /* CONFIG_XFS_ONLINE_REPAIR */ #endif /* CONFIG_XFS_ONLINE_REPAIR */
......
...@@ -298,7 +298,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { ...@@ -298,7 +298,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.type = ST_INODE, .type = ST_INODE,
.setup = xchk_setup_inode_bmap, .setup = xchk_setup_inode_bmap,
.scrub = xchk_bmap_cow, .scrub = xchk_bmap_cow,
.repair = xrep_notsupported, .repair = xrep_bmap_cow,
}, },
[XFS_SCRUB_TYPE_DIR] = { /* directory */ [XFS_SCRUB_TYPE_DIR] = { /* directory */
.type = ST_INODE, .type = ST_INODE,
......
...@@ -1596,6 +1596,90 @@ TRACE_EVENT(xrep_dinode_count_rmaps, ...@@ -1596,6 +1596,90 @@ TRACE_EVENT(xrep_dinode_count_rmaps,
__entry->attr_extents) __entry->attr_extents)
); );
TRACE_EVENT(xrep_cow_mark_file_range,
TP_PROTO(struct xfs_inode *ip, xfs_fsblock_t startblock,
xfs_fileoff_t startoff, xfs_filblks_t blockcount),
TP_ARGS(ip, startblock, startoff, blockcount),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_fsblock_t, startblock)
__field(xfs_fileoff_t, startoff)
__field(xfs_filblks_t, blockcount)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->startoff = startoff;
__entry->startblock = startblock;
__entry->blockcount = blockcount;
),
TP_printk("dev %d:%d ino 0x%llx fileoff 0x%llx startblock 0x%llx fsbcount 0x%llx",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->startoff,
__entry->startblock,
__entry->blockcount)
);
TRACE_EVENT(xrep_cow_replace_mapping,
TP_PROTO(struct xfs_inode *ip, const struct xfs_bmbt_irec *irec,
xfs_fsblock_t new_startblock, xfs_extlen_t new_blockcount),
TP_ARGS(ip, irec, new_startblock, new_blockcount),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_fsblock_t, startblock)
__field(xfs_fileoff_t, startoff)
__field(xfs_filblks_t, blockcount)
__field(xfs_exntst_t, state)
__field(xfs_fsblock_t, new_startblock)
__field(xfs_extlen_t, new_blockcount)
),
TP_fast_assign(
__entry->dev = ip->i_mount->m_super->s_dev;
__entry->ino = ip->i_ino;
__entry->startoff = irec->br_startoff;
__entry->startblock = irec->br_startblock;
__entry->blockcount = irec->br_blockcount;
__entry->state = irec->br_state;
__entry->new_startblock = new_startblock;
__entry->new_blockcount = new_blockcount;
),
TP_printk("dev %d:%d ino 0x%llx startoff 0x%llx startblock 0x%llx fsbcount 0x%llx state 0x%x new_startblock 0x%llx new_fsbcount 0x%x",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->startoff,
__entry->startblock,
__entry->blockcount,
__entry->state,
__entry->new_startblock,
__entry->new_blockcount)
);
TRACE_EVENT(xrep_cow_free_staging,
TP_PROTO(struct xfs_perag *pag, xfs_agblock_t agbno,
xfs_extlen_t blockcount),
TP_ARGS(pag, agbno, blockcount),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_agnumber_t, agno)
__field(xfs_agblock_t, agbno)
__field(xfs_extlen_t, blockcount)
),
TP_fast_assign(
__entry->dev = pag->pag_mount->m_super->s_dev;
__entry->agno = pag->pag_agno;
__entry->agbno = agbno;
__entry->blockcount = blockcount;
),
TP_printk("dev %d:%d agno 0x%x agbno 0x%x fsbcount 0x%x",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->agno,
__entry->agbno,
__entry->blockcount)
);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */ #endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
#endif /* _TRACE_XFS_SCRUB_TRACE_H */ #endif /* _TRACE_XFS_SCRUB_TRACE_H */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment