mirror of https://github.com/torvalds/linux.git
xfs: support repairing metadata btrees rooted in metadir inodes
Adapt the repair code so that we can stage a new btree in the data fork area of a metadir inode and reap the old blocks. We already have nearly all of the infrastructure; the only parts that were missing were the metadata inode reservation handling. Signed-off-by: "Darrick J. Wong" <djwong@kernel.org> Reviewed-by: Christoph Hellwig <hch@lst.de>
This commit is contained in:
parent
8defee8dff
commit
c6904f6788
|
|
@ -19,6 +19,8 @@
|
||||||
#include "xfs_rmap.h"
|
#include "xfs_rmap.h"
|
||||||
#include "xfs_ag.h"
|
#include "xfs_ag.h"
|
||||||
#include "xfs_defer.h"
|
#include "xfs_defer.h"
|
||||||
|
#include "xfs_metafile.h"
|
||||||
|
#include "xfs_quota.h"
|
||||||
#include "scrub/scrub.h"
|
#include "scrub/scrub.h"
|
||||||
#include "scrub/common.h"
|
#include "scrub/common.h"
|
||||||
#include "scrub/trace.h"
|
#include "scrub/trace.h"
|
||||||
|
|
@ -120,6 +122,43 @@ xrep_newbt_init_inode(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Initialize accounting resources for staging a new metadata inode btree.
|
||||||
|
* If the metadata file has a space reservation, the caller must adjust that
|
||||||
|
* reservation when committing the new ondisk btree.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xrep_newbt_init_metadir_inode(
|
||||||
|
struct xrep_newbt *xnr,
|
||||||
|
struct xfs_scrub *sc)
|
||||||
|
{
|
||||||
|
struct xfs_owner_info oinfo;
|
||||||
|
struct xfs_ifork *ifp;
|
||||||
|
|
||||||
|
ASSERT(xfs_is_metadir_inode(sc->ip));
|
||||||
|
|
||||||
|
xfs_rmap_ino_bmbt_owner(&oinfo, sc->ip->i_ino, XFS_DATA_FORK);
|
||||||
|
|
||||||
|
ifp = kmem_cache_zalloc(xfs_ifork_cache, XCHK_GFP_FLAGS);
|
||||||
|
if (!ifp)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate new metadir btree blocks with XFS_AG_RESV_NONE because the
|
||||||
|
* inode metadata space reservations can only account allocated space
|
||||||
|
* to the i_nblocks. We do not want to change the inode core fields
|
||||||
|
* until we're ready to commit the new tree, so we allocate the blocks
|
||||||
|
* as if they were regular file blocks. This exposes us to a higher
|
||||||
|
* risk of the repair being cancelled due to ENOSPC.
|
||||||
|
*/
|
||||||
|
xrep_newbt_init_ag(xnr, sc, &oinfo,
|
||||||
|
XFS_INO_TO_FSB(sc->mp, sc->ip->i_ino),
|
||||||
|
XFS_AG_RESV_NONE);
|
||||||
|
xnr->ifake.if_fork = ifp;
|
||||||
|
xnr->ifake.if_fork_size = xfs_inode_fork_size(sc->ip, XFS_DATA_FORK);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize accounting resources for staging a new btree. Callers are
|
* Initialize accounting resources for staging a new btree. Callers are
|
||||||
* expected to add their own reservations (and clean them up) manually.
|
* expected to add their own reservations (and clean them up) manually.
|
||||||
|
|
@ -224,6 +263,7 @@ xrep_newbt_alloc_ag_blocks(
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
ASSERT(sc->sa.pag != NULL);
|
ASSERT(sc->sa.pag != NULL);
|
||||||
|
ASSERT(xnr->resv != XFS_AG_RESV_METAFILE);
|
||||||
|
|
||||||
while (nr_blocks > 0) {
|
while (nr_blocks > 0) {
|
||||||
struct xfs_alloc_arg args = {
|
struct xfs_alloc_arg args = {
|
||||||
|
|
@ -297,6 +337,8 @@ xrep_newbt_alloc_file_blocks(
|
||||||
struct xfs_mount *mp = sc->mp;
|
struct xfs_mount *mp = sc->mp;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
|
ASSERT(xnr->resv != XFS_AG_RESV_METAFILE);
|
||||||
|
|
||||||
while (nr_blocks > 0) {
|
while (nr_blocks > 0) {
|
||||||
struct xfs_alloc_arg args = {
|
struct xfs_alloc_arg args = {
|
||||||
.tp = sc->tp,
|
.tp = sc->tp,
|
||||||
|
|
|
||||||
|
|
@ -63,6 +63,7 @@ void xrep_newbt_init_ag(struct xrep_newbt *xnr, struct xfs_scrub *sc,
|
||||||
enum xfs_ag_resv_type resv);
|
enum xfs_ag_resv_type resv);
|
||||||
int xrep_newbt_init_inode(struct xrep_newbt *xnr, struct xfs_scrub *sc,
|
int xrep_newbt_init_inode(struct xrep_newbt *xnr, struct xfs_scrub *sc,
|
||||||
int whichfork, const struct xfs_owner_info *oinfo);
|
int whichfork, const struct xfs_owner_info *oinfo);
|
||||||
|
int xrep_newbt_init_metadir_inode(struct xrep_newbt *xnr, struct xfs_scrub *sc);
|
||||||
int xrep_newbt_alloc_blocks(struct xrep_newbt *xnr, uint64_t nr_blocks);
|
int xrep_newbt_alloc_blocks(struct xrep_newbt *xnr, uint64_t nr_blocks);
|
||||||
int xrep_newbt_add_extent(struct xrep_newbt *xnr, struct xfs_perag *pag,
|
int xrep_newbt_add_extent(struct xrep_newbt *xnr, struct xfs_perag *pag,
|
||||||
xfs_agblock_t agbno, xfs_extlen_t len);
|
xfs_agblock_t agbno, xfs_extlen_t len);
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@
|
||||||
#include "xfs_attr.h"
|
#include "xfs_attr.h"
|
||||||
#include "xfs_attr_remote.h"
|
#include "xfs_attr_remote.h"
|
||||||
#include "xfs_defer.h"
|
#include "xfs_defer.h"
|
||||||
|
#include "xfs_metafile.h"
|
||||||
#include "scrub/scrub.h"
|
#include "scrub/scrub.h"
|
||||||
#include "scrub/common.h"
|
#include "scrub/common.h"
|
||||||
#include "scrub/trace.h"
|
#include "scrub/trace.h"
|
||||||
|
|
@ -390,6 +391,8 @@ xreap_agextent_iter(
|
||||||
xfs_fsblock_t fsbno;
|
xfs_fsblock_t fsbno;
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
|
ASSERT(rs->resv != XFS_AG_RESV_METAFILE);
|
||||||
|
|
||||||
fsbno = xfs_agbno_to_fsb(sc->sa.pag, agbno);
|
fsbno = xfs_agbno_to_fsb(sc->sa.pag, agbno);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -675,6 +678,44 @@ xrep_reap_fsblocks(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Dispose of every block of an old metadata btree that used to be rooted in a
|
||||||
|
* metadata directory file.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xrep_reap_metadir_fsblocks(
|
||||||
|
struct xfs_scrub *sc,
|
||||||
|
struct xfsb_bitmap *bitmap)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Reap old metadir btree blocks with XFS_AG_RESV_NONE because the old
|
||||||
|
* blocks are no longer mapped by the inode, and inode metadata space
|
||||||
|
* reservations can only account freed space to the i_nblocks.
|
||||||
|
*/
|
||||||
|
struct xfs_owner_info oinfo;
|
||||||
|
struct xreap_state rs = {
|
||||||
|
.sc = sc,
|
||||||
|
.oinfo = &oinfo,
|
||||||
|
.resv = XFS_AG_RESV_NONE,
|
||||||
|
};
|
||||||
|
int error;
|
||||||
|
|
||||||
|
ASSERT(xfs_has_rmapbt(sc->mp));
|
||||||
|
ASSERT(sc->ip != NULL);
|
||||||
|
ASSERT(xfs_is_metadir_inode(sc->ip));
|
||||||
|
|
||||||
|
xfs_rmap_ino_bmbt_owner(&oinfo, sc->ip->i_ino, XFS_DATA_FORK);
|
||||||
|
|
||||||
|
error = xfsb_bitmap_walk(bitmap, xreap_fsmeta_extent, &rs);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
|
||||||
|
if (xreap_dirty(&rs))
|
||||||
|
return xrep_defer_finish(sc);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Metadata files are not supposed to share blocks with anything else.
|
* Metadata files are not supposed to share blocks with anything else.
|
||||||
* If blocks are shared, we remove the reverse mapping (thus reducing the
|
* If blocks are shared, we remove the reverse mapping (thus reducing the
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,8 @@ int xrep_reap_agblocks(struct xfs_scrub *sc, struct xagb_bitmap *bitmap,
|
||||||
int xrep_reap_fsblocks(struct xfs_scrub *sc, struct xfsb_bitmap *bitmap,
|
int xrep_reap_fsblocks(struct xfs_scrub *sc, struct xfsb_bitmap *bitmap,
|
||||||
const struct xfs_owner_info *oinfo);
|
const struct xfs_owner_info *oinfo);
|
||||||
int xrep_reap_ifork(struct xfs_scrub *sc, struct xfs_inode *ip, int whichfork);
|
int xrep_reap_ifork(struct xfs_scrub *sc, struct xfs_inode *ip, int whichfork);
|
||||||
|
int xrep_reap_metadir_fsblocks(struct xfs_scrub *sc,
|
||||||
|
struct xfsb_bitmap *bitmap);
|
||||||
|
|
||||||
/* Buffer cache scan context. */
|
/* Buffer cache scan context. */
|
||||||
struct xrep_bufscan {
|
struct xrep_bufscan {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue