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/, \ ...@@ -184,6 +184,7 @@ xfs-y += $(addprefix scrub/, \
agheader_repair.o \ agheader_repair.o \
alloc_repair.o \ alloc_repair.o \
ialloc_repair.o \ ialloc_repair.o \
inode_repair.o \
newbt.o \ newbt.o \
reap.o \ reap.o \
refcount_repair.o \ refcount_repair.o \
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include "scrub/common.h" #include "scrub/common.h"
#include "scrub/btree.h" #include "scrub/btree.h"
#include "scrub/trace.h" #include "scrub/trace.h"
#include "scrub/repair.h"
/* Prepare the attached inode for scrubbing. */ /* Prepare the attached inode for scrubbing. */
static inline int static inline int
...@@ -185,8 +186,11 @@ xchk_setup_inode( ...@@ -185,8 +186,11 @@ xchk_setup_inode(
* saying the inode is allocated and the icache being unable to load * saying the inode is allocated and the icache being unable to load
* the inode until we can flag the corruption in xchk_inode. The * the inode until we can flag the corruption in xchk_inode. The
* scrub function has to note the corruption, since we're not really * 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; return 0;
out_cancel: out_cancel:
......
This diff is collapsed.
...@@ -176,6 +176,16 @@ xrep_roll_ag_trans( ...@@ -176,6 +176,16 @@ xrep_roll_ag_trans(
return 0; 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. */ /* Finish all deferred work attached to the repair transaction. */
int int
xrep_defer_finish( xrep_defer_finish(
...@@ -740,6 +750,38 @@ xrep_ino_dqattach( ...@@ -740,6 +750,38 @@ xrep_ino_dqattach(
} }
#endif /* CONFIG_XFS_QUOTA */ #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 * Initialize all the btree cursors for an AG repair except for the btree that
* we're rebuilding. * we're rebuilding.
......
...@@ -30,11 +30,22 @@ static inline int xrep_notsupported(struct xfs_scrub *sc) ...@@ -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); int xrep_attempt(struct xfs_scrub *sc, struct xchk_stats_run *run);
void xrep_failure(struct xfs_mount *mp); void xrep_failure(struct xfs_mount *mp);
int xrep_roll_ag_trans(struct xfs_scrub *sc); 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); int xrep_defer_finish(struct xfs_scrub *sc);
bool xrep_ag_has_space(struct xfs_perag *pag, xfs_extlen_t nr_blocks, bool xrep_ag_has_space(struct xfs_perag *pag, xfs_extlen_t nr_blocks,
enum xfs_ag_resv_type type); enum xfs_ag_resv_type type);
xfs_extlen_t xrep_calc_ag_resblks(struct xfs_scrub *sc); 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 xbitmap;
struct xagb_bitmap; struct xagb_bitmap;
...@@ -66,11 +77,16 @@ int xrep_ino_dqattach(struct xfs_scrub *sc); ...@@ -66,11 +77,16 @@ int xrep_ino_dqattach(struct xfs_scrub *sc);
# define xrep_ino_dqattach(sc) (0) # define xrep_ino_dqattach(sc) (0)
#endif /* CONFIG_XFS_QUOTA */ #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); int xrep_reset_perag_resv(struct xfs_scrub *sc);
/* Repair setup functions */ /* Repair setup functions */
int xrep_setup_ag_allocbt(struct xfs_scrub *sc); 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); void xrep_ag_btcur_init(struct xfs_scrub *sc, struct xchk_ag *sa);
/* Metadata revalidators */ /* Metadata revalidators */
...@@ -88,6 +104,7 @@ int xrep_agi(struct xfs_scrub *sc); ...@@ -88,6 +104,7 @@ int xrep_agi(struct xfs_scrub *sc);
int xrep_allocbt(struct xfs_scrub *sc); int xrep_allocbt(struct xfs_scrub *sc);
int xrep_iallocbt(struct xfs_scrub *sc); int xrep_iallocbt(struct xfs_scrub *sc);
int xrep_refcountbt(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_pagf(struct xfs_scrub *sc);
int xrep_reinit_pagi(struct xfs_scrub *sc); int xrep_reinit_pagi(struct xfs_scrub *sc);
...@@ -133,6 +150,8 @@ xrep_setup_nothing( ...@@ -133,6 +150,8 @@ xrep_setup_nothing(
} }
#define xrep_setup_ag_allocbt 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_allocbt (NULL)
#define xrep_revalidate_iallocbt (NULL) #define xrep_revalidate_iallocbt (NULL)
...@@ -144,6 +163,7 @@ xrep_setup_nothing( ...@@ -144,6 +163,7 @@ xrep_setup_nothing(
#define xrep_allocbt xrep_notsupported #define xrep_allocbt xrep_notsupported
#define xrep_iallocbt xrep_notsupported #define xrep_iallocbt xrep_notsupported
#define xrep_refcountbt xrep_notsupported #define xrep_refcountbt xrep_notsupported
#define xrep_inode xrep_notsupported
#endif /* CONFIG_XFS_ONLINE_REPAIR */ #endif /* CONFIG_XFS_ONLINE_REPAIR */
......
...@@ -282,7 +282,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { ...@@ -282,7 +282,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = {
.type = ST_INODE, .type = ST_INODE,
.setup = xchk_setup_inode, .setup = xchk_setup_inode,
.scrub = xchk_inode, .scrub = xchk_inode,
.repair = xrep_notsupported, .repair = xrep_inode,
}, },
[XFS_SCRUB_TYPE_BMBTD] = { /* inode data fork */ [XFS_SCRUB_TYPE_BMBTD] = { /* inode data fork */
.type = ST_INODE, .type = ST_INODE,
......
...@@ -1393,6 +1393,135 @@ DEFINE_NEWBT_EXTENT_EVENT(xrep_newbt_alloc_file_blocks); ...@@ -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_free_blocks);
DEFINE_NEWBT_EXTENT_EVENT(xrep_newbt_claim_block); 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 /* 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