Commit 2d295fe6 authored by Darrick J. Wong's avatar Darrick J. Wong

xfs: repair inode records

If an inode is so badly damaged that it cannot be loaded into the cache,
fix the ondisk metadata and try again.  If there /is/ a cached inode,
fix any problems and apply any optimizations that can be solved incore.
Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent d9041681
......@@ -184,6 +184,7 @@ xfs-y += $(addprefix scrub/, \
agheader_repair.o \
alloc_repair.o \
ialloc_repair.o \
inode_repair.o \
newbt.o \
reap.o \
refcount_repair.o \
......
......@@ -25,6 +25,7 @@
#include "scrub/common.h"
#include "scrub/btree.h"
#include "scrub/trace.h"
#include "scrub/repair.h"
/* Prepare the attached inode for scrubbing. */
static inline int
......@@ -185,8 +186,11 @@ xchk_setup_inode(
* saying the inode is allocated and the icache being unable to load
* the inode until we can flag the corruption in xchk_inode. The
* scrub function has to note the corruption, since we're not really
* supposed to do that from the setup function.
* supposed to do that from the setup function. Save the mapping to
* make repairs to the ondisk inode buffer.
*/
if (xchk_could_repair(sc))
xrep_setup_inode(sc, &imap);
return 0;
out_cancel:
......
This diff is collapsed.
......@@ -176,6 +176,16 @@ xrep_roll_ag_trans(
return 0;
}
/* Roll the scrub transaction, holding the primary metadata locked. */
int
xrep_roll_trans(
struct xfs_scrub *sc)
{
if (!sc->ip)
return xrep_roll_ag_trans(sc);
return xfs_trans_roll_inode(&sc->tp, sc->ip);
}
/* Finish all deferred work attached to the repair transaction. */
int
xrep_defer_finish(
......@@ -740,6 +750,38 @@ xrep_ino_dqattach(
}
#endif /* CONFIG_XFS_QUOTA */
/*
* Ensure that the inode being repaired is ready to handle a certain number of
* extents, or return EFSCORRUPTED. Caller must hold the ILOCK of the inode
* being repaired and have joined it to the scrub transaction.
*/
int
xrep_ino_ensure_extent_count(
struct xfs_scrub *sc,
int whichfork,
xfs_extnum_t nextents)
{
xfs_extnum_t max_extents;
bool inode_has_nrext64;
inode_has_nrext64 = xfs_inode_has_large_extent_counts(sc->ip);
max_extents = xfs_iext_max_nextents(inode_has_nrext64, whichfork);
if (nextents <= max_extents)
return 0;
if (inode_has_nrext64)
return -EFSCORRUPTED;
if (!xfs_has_large_extent_counts(sc->mp))
return -EFSCORRUPTED;
max_extents = xfs_iext_max_nextents(true, whichfork);
if (nextents > max_extents)
return -EFSCORRUPTED;
sc->ip->i_diflags2 |= XFS_DIFLAG2_NREXT64;
xfs_trans_log_inode(sc->tp, sc->ip, XFS_ILOG_CORE);
return 0;
}
/*
* Initialize all the btree cursors for an AG repair except for the btree that
* we're rebuilding.
......
......@@ -30,11 +30,22 @@ static inline int xrep_notsupported(struct xfs_scrub *sc)
int xrep_attempt(struct xfs_scrub *sc, struct xchk_stats_run *run);
void xrep_failure(struct xfs_mount *mp);
int xrep_roll_ag_trans(struct xfs_scrub *sc);
int xrep_roll_trans(struct xfs_scrub *sc);
int xrep_defer_finish(struct xfs_scrub *sc);
bool xrep_ag_has_space(struct xfs_perag *pag, xfs_extlen_t nr_blocks,
enum xfs_ag_resv_type type);
xfs_extlen_t xrep_calc_ag_resblks(struct xfs_scrub *sc);
static inline int
xrep_trans_commit(
struct xfs_scrub *sc)
{
int error = xfs_trans_commit(sc->tp);
sc->tp = NULL;
return error;
}
struct xbitmap;
struct xagb_bitmap;
......@@ -66,11 +77,16 @@ int xrep_ino_dqattach(struct xfs_scrub *sc);
# define xrep_ino_dqattach(sc) (0)
#endif /* CONFIG_XFS_QUOTA */
int xrep_ino_ensure_extent_count(struct xfs_scrub *sc, int whichfork,
xfs_extnum_t nextents);
int xrep_reset_perag_resv(struct xfs_scrub *sc);
/* Repair setup functions */
int xrep_setup_ag_allocbt(struct xfs_scrub *sc);
struct xfs_imap;
int xrep_setup_inode(struct xfs_scrub *sc, const struct xfs_imap *imap);
void xrep_ag_btcur_init(struct xfs_scrub *sc, struct xchk_ag *sa);
/* Metadata revalidators */
......@@ -88,6 +104,7 @@ int xrep_agi(struct xfs_scrub *sc);
int xrep_allocbt(struct xfs_scrub *sc);
int xrep_iallocbt(struct xfs_scrub *sc);
int xrep_refcountbt(struct xfs_scrub *sc);
int xrep_inode(struct xfs_scrub *sc);
int xrep_reinit_pagf(struct xfs_scrub *sc);
int xrep_reinit_pagi(struct xfs_scrub *sc);
......@@ -133,6 +150,8 @@ xrep_setup_nothing(
}
#define xrep_setup_ag_allocbt xrep_setup_nothing
#define xrep_setup_inode(sc, imap) ((void)0)
#define xrep_revalidate_allocbt (NULL)
#define xrep_revalidate_iallocbt (NULL)
......@@ -144,6 +163,7 @@ xrep_setup_nothing(
#define xrep_allocbt xrep_notsupported
#define xrep_iallocbt xrep_notsupported
#define xrep_refcountbt xrep_notsupported
#define xrep_inode xrep_notsupported
#endif /* CONFIG_XFS_ONLINE_REPAIR */
......
......@@ -282,7 +282,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.type = ST_INODE,
.setup = xchk_setup_inode,
.scrub = xchk_inode,
.repair = xrep_notsupported,
.repair = xrep_inode,
},
[XFS_SCRUB_TYPE_BMBTD] = { /* inode data fork */
.type = ST_INODE,
......
......@@ -1393,6 +1393,135 @@ DEFINE_NEWBT_EXTENT_EVENT(xrep_newbt_alloc_file_blocks);
DEFINE_NEWBT_EXTENT_EVENT(xrep_newbt_free_blocks);
DEFINE_NEWBT_EXTENT_EVENT(xrep_newbt_claim_block);
DECLARE_EVENT_CLASS(xrep_dinode_class,
TP_PROTO(struct xfs_scrub *sc, struct xfs_dinode *dip),
TP_ARGS(sc, dip),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(uint16_t, mode)
__field(uint8_t, version)
__field(uint8_t, format)
__field(uint32_t, uid)
__field(uint32_t, gid)
__field(uint64_t, size)
__field(uint64_t, nblocks)
__field(uint32_t, extsize)
__field(uint32_t, nextents)
__field(uint16_t, anextents)
__field(uint8_t, forkoff)
__field(uint8_t, aformat)
__field(uint16_t, flags)
__field(uint32_t, gen)
__field(uint64_t, flags2)
__field(uint32_t, cowextsize)
),
TP_fast_assign(
__entry->dev = sc->mp->m_super->s_dev;
__entry->ino = sc->sm->sm_ino;
__entry->mode = be16_to_cpu(dip->di_mode);
__entry->version = dip->di_version;
__entry->format = dip->di_format;
__entry->uid = be32_to_cpu(dip->di_uid);
__entry->gid = be32_to_cpu(dip->di_gid);
__entry->size = be64_to_cpu(dip->di_size);
__entry->nblocks = be64_to_cpu(dip->di_nblocks);
__entry->extsize = be32_to_cpu(dip->di_extsize);
__entry->nextents = be32_to_cpu(dip->di_nextents);
__entry->anextents = be16_to_cpu(dip->di_anextents);
__entry->forkoff = dip->di_forkoff;
__entry->aformat = dip->di_aformat;
__entry->flags = be16_to_cpu(dip->di_flags);
__entry->gen = be32_to_cpu(dip->di_gen);
__entry->flags2 = be64_to_cpu(dip->di_flags2);
__entry->cowextsize = be32_to_cpu(dip->di_cowextsize);
),
TP_printk("dev %d:%d ino 0x%llx mode 0x%x version %u format %u uid %u gid %u disize 0x%llx nblocks 0x%llx extsize %u nextents %u anextents %u forkoff 0x%x aformat %u flags 0x%x gen 0x%x flags2 0x%llx cowextsize %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->mode,
__entry->version,
__entry->format,
__entry->uid,
__entry->gid,
__entry->size,
__entry->nblocks,
__entry->extsize,
__entry->nextents,
__entry->anextents,
__entry->forkoff,
__entry->aformat,
__entry->flags,
__entry->gen,
__entry->flags2,
__entry->cowextsize)
)
#define DEFINE_REPAIR_DINODE_EVENT(name) \
DEFINE_EVENT(xrep_dinode_class, name, \
TP_PROTO(struct xfs_scrub *sc, struct xfs_dinode *dip), \
TP_ARGS(sc, dip))
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_header);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_mode);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_flags);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_size);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_extsize_hints);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_zap_symlink);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_zap_dir);
DEFINE_REPAIR_DINODE_EVENT(xrep_dinode_fixed);
DECLARE_EVENT_CLASS(xrep_inode_class,
TP_PROTO(struct xfs_scrub *sc),
TP_ARGS(sc),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
__field(xfs_fsize_t, size)
__field(xfs_rfsblock_t, nblocks)
__field(uint16_t, flags)
__field(uint64_t, flags2)
__field(uint32_t, nextents)
__field(uint8_t, format)
__field(uint32_t, anextents)
__field(uint8_t, aformat)
),
TP_fast_assign(
__entry->dev = sc->mp->m_super->s_dev;
__entry->ino = sc->sm->sm_ino;
__entry->size = sc->ip->i_disk_size;
__entry->nblocks = sc->ip->i_nblocks;
__entry->flags = sc->ip->i_diflags;
__entry->flags2 = sc->ip->i_diflags2;
__entry->nextents = sc->ip->i_df.if_nextents;
__entry->format = sc->ip->i_df.if_format;
__entry->anextents = sc->ip->i_af.if_nextents;
__entry->aformat = sc->ip->i_af.if_format;
),
TP_printk("dev %d:%d ino 0x%llx disize 0x%llx nblocks 0x%llx flags 0x%x flags2 0x%llx nextents %u format %u anextents %u aformat %u",
MAJOR(__entry->dev), MINOR(__entry->dev),
__entry->ino,
__entry->size,
__entry->nblocks,
__entry->flags,
__entry->flags2,
__entry->nextents,
__entry->format,
__entry->anextents,
__entry->aformat)
)
#define DEFINE_REPAIR_INODE_EVENT(name) \
DEFINE_EVENT(xrep_inode_class, name, \
TP_PROTO(struct xfs_scrub *sc), \
TP_ARGS(sc))
DEFINE_REPAIR_INODE_EVENT(xrep_inode_blockcounts);
DEFINE_REPAIR_INODE_EVENT(xrep_inode_ids);
DEFINE_REPAIR_INODE_EVENT(xrep_inode_flags);
DEFINE_REPAIR_INODE_EVENT(xrep_inode_blockdir_size);
DEFINE_REPAIR_INODE_EVENT(xrep_inode_sfdir_size);
DEFINE_REPAIR_INODE_EVENT(xrep_inode_dir_size);
DEFINE_REPAIR_INODE_EVENT(xrep_inode_fixed);
#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */
#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