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_ag.h"
|
||||
#include "xfs_defer.h"
|
||||
#include "xfs_metafile.h"
|
||||
#include "xfs_quota.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/common.h"
|
||||
#include "scrub/trace.h"
|
||||
|
|
@ -120,6 +122,43 @@ xrep_newbt_init_inode(
|
|||
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
|
||||
* expected to add their own reservations (and clean them up) manually.
|
||||
|
|
@ -224,6 +263,7 @@ xrep_newbt_alloc_ag_blocks(
|
|||
int error = 0;
|
||||
|
||||
ASSERT(sc->sa.pag != NULL);
|
||||
ASSERT(xnr->resv != XFS_AG_RESV_METAFILE);
|
||||
|
||||
while (nr_blocks > 0) {
|
||||
struct xfs_alloc_arg args = {
|
||||
|
|
@ -297,6 +337,8 @@ xrep_newbt_alloc_file_blocks(
|
|||
struct xfs_mount *mp = sc->mp;
|
||||
int error = 0;
|
||||
|
||||
ASSERT(xnr->resv != XFS_AG_RESV_METAFILE);
|
||||
|
||||
while (nr_blocks > 0) {
|
||||
struct xfs_alloc_arg args = {
|
||||
.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);
|
||||
int xrep_newbt_init_inode(struct xrep_newbt *xnr, struct xfs_scrub *sc,
|
||||
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_add_extent(struct xrep_newbt *xnr, struct xfs_perag *pag,
|
||||
xfs_agblock_t agbno, xfs_extlen_t len);
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
#include "xfs_attr.h"
|
||||
#include "xfs_attr_remote.h"
|
||||
#include "xfs_defer.h"
|
||||
#include "xfs_metafile.h"
|
||||
#include "scrub/scrub.h"
|
||||
#include "scrub/common.h"
|
||||
#include "scrub/trace.h"
|
||||
|
|
@ -390,6 +391,8 @@ xreap_agextent_iter(
|
|||
xfs_fsblock_t fsbno;
|
||||
int error = 0;
|
||||
|
||||
ASSERT(rs->resv != XFS_AG_RESV_METAFILE);
|
||||
|
||||
fsbno = xfs_agbno_to_fsb(sc->sa.pag, agbno);
|
||||
|
||||
/*
|
||||
|
|
@ -675,6 +678,44 @@ xrep_reap_fsblocks(
|
|||
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.
|
||||
* 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,
|
||||
const struct xfs_owner_info *oinfo);
|
||||
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. */
|
||||
struct xrep_bufscan {
|
||||
|
|
|
|||
Loading…
Reference in New Issue