xfs-4.18: online repair support
cover xfs-4.18: online repair support
Hi all,
This is the fifteenth revision of a patchset that adds to XFS kernel support for online metadata scrubbing and repair. There aren't any on-disk format changes.
New in v15 of the patch series is the ability to scavenge broken attribute forks for intact extended attributes, to repair minor corruptions in the on-disk quota records, and to perform quotacheck online. The series was rebased atop for-next for 4.18 and a number of minor bitrots bugs were fixed.
The first seven patches are helper functions that are internal to the online repair code, as follows:
- Estimating transaction block reservations and creating transactions suitable for repairing metadata.
- Allocating and initializing per-AG btree blocks.
- Managing lists of AG blocks; we find all the dead blocks of a btree we're rebuilding by making a list of blocks with the same rmap owner, and subtracting out any blocks in use by other data structures.
- Disposing of lists of dead per-AG btree blocks.
- Finding btree roots given only rmap information.
- Resetting filesystem counters.
- Calling dqattach for inodes being repaired and scheduling quotacheck.
Patches 8-22 introduce the online repair functionality for space metadata and certain file data. Our general strategy for rebuilding damaged primary metadata is to rebuild the structure completely from secondary metadata and free the old structure after the fact; we do not try to salvage anything. Consequently, online repair requires rmapbt. Rebuilding the secondary metadata (rmap) is much harder -- due to our locking rules (primary and then secondary) we have to shut down the filesystem temporarily while we scan all the primary metadata for data to put in the new secondary structure.
Reconstructing inodes is difficult -- the ability to rebuild files depends on the filesystem being able to load an inode (xfs_iget), which means repair has to know how to zap any part of an inode record that might trigger corruption errors from iget. To that end, we can now reset most of an inode record or an inode fork so that we can rebuild the file.
The refcount rebuilder is more or less the same algorithm that xfs_repair uses, but modified to reflect the constraints of running in kernel space.
For rmap rebuilds, we cannot have anything on the filesystem taking exclusive locks and we cannot have any allocation activity at all. Therefore, we start by freezing the filesystem to allow other transactions to finish. Next, we scan all other AG metadata structures, every inode, and every block map to reconstruct the rmap data. Then, we reinitialize the rmap btree root and reload the rmap btree. Finally, we release all the resource we grabbed and the filesystem returns to normal.
The extended attribute repair function uses a different strategy from the other repair code. Since there are no secondary metadata for extended attributes, we can't simply rebuild from an alternate data source. Therefore, this repairer simply walks through the blocks in the attribute fork looking for attribute names and values that appear to be intact, zaps the attr fork, and re-adds the collected names and values to the new fork. This enables us to trigger optimization notices for attributes blocks with holes.
Quota repairs are fairly straightforward -- repair anything wrong with the inode data fork, eliminate garbage extents, and then iterate all the dquot blocks fixing up things that the dquot buffer verifier will complain about. This should leave the quota ip in good enough shape for online quotacheck! Here we reuse the same fs freezing mechanism as in the rmap repair to block all other filesystem users. Then we zero all the quota counters, iterate all the inodes in the system to recalculate the counts, and log all the dquots to disk. We of course clear the CHKD flags before starting out, so if we crash midway through, the mount time quotacheck will run.
Looking forward, the parent pointer feature that Allison Henderson is working on will enable us to reconstruct directories, at which point we'll be able to reconstruct most of a lightly damaged filesystem. But that's future talk.
If you're going to start using this mess, you probably ought to just pull from my git trees. The kernel patches[1] should apply against 4.17-rc5. xfsprogs[2] and xfstests[3] can be found in their usual places. The git trees contain all four series' worth of changes.
This is an extraordinary way to destroy everything. Enjoy! Comments and questions are, as always, welcome.
--D
[1] https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=djwong-devel
[2] https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=djwong-devel
[3] https://git.kernel.org/cgit/linux/kernel/git/djwong/xfstests-dev.git/log/?h=djwong-devel
PATCH 1/22 xfs: add helpers to deal with transaction allocation and rolling
From: Darrick J. Wong <[email protected]>
For repairs, we need to reserve at least as many blocks as we think we're going to need to rebuild the data structure, and we're going to need some helpers to roll transactions while maintaining locks on the AG headers so that other threads cannot wander into the middle of a repair.
--- fs/xfs/scrub/bmap.c | 2 - fs/xfs/scrub/common.c | 21 ++++++- fs/xfs/scrub/common.h | 2 - fs/xfs/scrub/inode.c | 4 + fs/xfs/scrub/repair.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 12 ++++ 6 files changed, 186 insertions(+), 7 deletions(-)
fs/xfs/scrub/bmap.c +1 -1
@@ -74,7 +74,7 @@ xfs_scrub_setup_inode_bmap( }/* Got the inode, lock it and we're ready to go. */ - error =xfs_scrub_trans_alloc (sc); + error =xfs_scrub_trans_alloc (sc,0 );if (error) goto out; sc->ilock_flags |=XFS_ILOCK_EXCL ;
fs/xfs/scrub/common.c +18 -3
@@ -51,6 +51,7 @@#include "scrub/common.h" #include "scrub/trace.h" #include "scrub/btree.h" +#include "scrub/repair.h" /* Common code for the metadata scrubbers. */
@@ -573,11 +574,22 @@ xfs_scrub_ag_init(/* * Grab an empty transaction so that we can re-grab locked buffers if * one of our btrees turns out to be cyclic. +* +* If we're going to repair something, we need to ask for the largest possible +* log reservation so that we can handle the worst case scenario for metadata +* updates while rebuilding a metadata item. We also need to reserve as many +* blocks in the head transaction as we think we're going to need to rebuild +* the metadata object. */ int xfs_scrub_trans_alloc ( -struct xfs_scrub_context *sc) +struct xfs_scrub_context *sc, +uint resblks) { +if (sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR ) +return xfs_trans_alloc (sc->mp, &M_RES (sc->mp)->tr_itruncate, + resblks,0 ,0 , &sc->tp); +return xfs_trans_alloc_empty (sc->mp, &sc->tp); }
@@ -587,7 +599,10 @@ xfs_scrub_setup_fs(struct xfs_scrub_context *sc, struct xfs_inode *ip) { - return xfs_scrub_trans_alloc(sc); + uint resblks; + + resblks =xfs_repair_calc_ag_resblks (sc); +return xfs_scrub_trans_alloc (sc, resblks); }/* Set us up with AG headers and btree cursors. */
@@ -717,7 +732,7 @@ xfs_scrub_setup_inode_contents(/* Got the inode, lock it and we're ready to go. */ sc->ilock_flags =XFS_IOLOCK_EXCL |XFS_MMAPLOCK_EXCL ;xfs_ilock (sc->ip, sc->ilock_flags); - error =xfs_scrub_trans_alloc (sc); + error =xfs_scrub_trans_alloc (sc, resblks);if (error) goto out; sc->ilock_flags |=XFS_ILOCK_EXCL ;
fs/xfs/scrub/common.h +1 -1
@@ -38,7 +38,7 @@ xfs_scrub_should_terminate(return false; } -int xfs_scrub_trans_alloc (struct xfs_scrub_context *sc); +int xfs_scrub_trans_alloc (struct xfs_scrub_context *sc,uint resblks);bool xfs_scrub_process_error (struct xfs_scrub_context *sc,xfs_agnumber_t agno,xfs_agblock_t bno,int *error); bool xfs_scrub_fblock_process_error(struct xfs_scrub_context *sc, int whichfork,
fs/xfs/scrub/inode.c +2 -2
@@ -67,7 +67,7 @@ xfs_scrub_setup_inode(break ;case -EFSCORRUPTED :case -EFSBADCRC : -return xfs_scrub_trans_alloc (sc); +return xfs_scrub_trans_alloc (sc,0 );default :return error; }
@@ -75,7 +75,7 @@ xfs_scrub_setup_inode(/* Got the inode, lock it and we're ready to go. */ sc->ilock_flags =XFS_IOLOCK_EXCL |XFS_MMAPLOCK_EXCL ;xfs_ilock (sc->ip, sc->ilock_flags); - error =xfs_scrub_trans_alloc (sc); + error =xfs_scrub_trans_alloc (sc,0 );if (error) goto out; sc->ilock_flags |=XFS_ILOCK_EXCL ;
fs/xfs/scrub/repair.c +152 -0
155 lines not on this page · raw message
fs/xfs/scrub/repair.h +12 -0
@@ -32,6 +32,10 @@ static inline int xfs_repair_notsupported(struct xfs_scrub_context *sc)int xfs_repair_attempt (struct xfs_inode *ip,struct xfs_scrub_context *sc,bool *fixed);void xfs_repair_failure (struct xfs_mount *mp); +int xfs_repair_roll_ag_trans (struct xfs_scrub_context *sc); +bool xfs_repair_ag_has_space (struct xfs_perag *pag,xfs_extlen_t nr_blocks, +enum xfs_ag_resv_type type); +xfs_extlen_t xfs_repair_calc_ag_resblks (struct xfs_scrub_context *sc);/* Metadata repairers */
@@ -49,6 +53,14 @@ static inline int xfs_repair_attempt(static inline void xfs_repair_failure (struct xfs_mount *mp) {} +static inline xfs_extlen_t +xfs_repair_calc_ag_resblks ( +struct xfs_scrub_context *sc) +{ +ASSERT (!(sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR )); +return 0 ; +} +#define xfs_repair_probe xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
Re: [PATCH 01/22] xfs: add helpers to deal with transaction allocation and rolling
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
For repairs, we need to reserve at least as many blocks as we think we're going to need to rebuild the data structure, and we're going to need some helpers to roll transactions while maintaining locks on the AG headers so that other threads cannot wander into the middle of a repair.
Signed-off-by Darrick J. Wong <[email protected]>
Quoted 8 lines
--- fs/xfs/scrub/bmap.c | 2 - fs/xfs/scrub/common.c | 21 ++++++- fs/xfs/scrub/common.h | 2 - fs/xfs/scrub/inode.c | 4 + fs/xfs/scrub/repair.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 12 ++++ 6 files changed, 186 insertions(+), 7 deletions(-)
mostly looks good.
[...]
Quoted 21 lines
fs/xfs/scrub/repair.c +21
-0+/* +* Figure out how many blocks to reserve for an AG repair. We calculate the +* worst case estimate for the number of blocks we'd need to rebuild one of +* any type of per-AG btree. +*/ +xfs_extlen_t +xfs_repair_calc_ag_resblks ( +struct xfs_scrub_context *sc) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_scrub_metadata *sm = sc->sm; +struct xfs_perag *pag; +struct xfs_buf *bp; +xfs_agino_t icount =0 ; +xfs_extlen_t aglen =0 ; +xfs_extlen_t usedlen; +xfs_extlen_t freelen; +xfs_extlen_t bnobt_sz; +xfs_extlen_t inobt_sz; +xfs_extlen_t rmapbt_sz; +xfs_extlen_t refcbt_sz;
fs/xfs/scrub/repair.c xfs_repair_probe( · line 218
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+int error; + +if (!(sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR )) +return 0 ; + +/* Use in-core counters if possible. */ + pag =xfs_perag_get (mp, sm->sm_agno); +if (pag->pagi_init) + icount = pag->pagi_count; +xfs_perag_put (pag);
Don't drop the pag here, do it ....
Quoted 21 lines
fs/xfs/scrub/repair.c +21
-0+ +/* +* Otherwise try to get the actual counters from disk; if not, make +* some worst case assumptions. +*/ +if (icount ==0 ) { + error =xfs_ialloc_read_agi (mp,NULL , sm->sm_agno, &bp); +if (error) { + icount = mp->m_sb.sb_agblocks / mp->m_sb.sb_inopblock; + }else { + pag =xfs_perag_get (mp, sm->sm_agno); + icount = pag->pagi_count; +xfs_perag_put (pag); +xfs_buf_relse (bp); + } + } + +/* Now grab the block counters from the AGF. */ + error =xfs_alloc_read_agf (mp,NULL , sm->sm_agno,0 , &bp); +if (error) { + aglen = mp->m_sb.sb_agblocks;
fs/xfs/scrub/repair.c xfs_repair_probe( · line 249
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ freelen = aglen; + usedlen = aglen; + }else { + pag =xfs_perag_get (mp, sm->sm_agno); + aglen =be32_to_cpu (XFS_BUF_TO_AGF (bp)->agf_length); + freelen = pag->pagf_freeblks; + usedlen = aglen - freelen; +xfs_perag_put (pag); +xfs_buf_relse (bp); + }
.... here, so we don't have to do repeated lookups.
Quoted 16 lines
fs/xfs/scrub/repair.c +16
-0+trace_xfs_repair_calc_ag_resblks (mp, sm->sm_agno, icount, aglen, + freelen, usedlen); + +/* +* Figure out how many blocks we'd need worst case to rebuild +* each type of btree. Note that we can only rebuild the +* bnobt/cntbt or inobt/finobt as pairs. +*/ + bnobt_sz =2 *xfs_allocbt_calc_size (mp, freelen); +if (xfs_sb_version_hassparseinodes (&mp->m_sb)) + inobt_sz =xfs_iallocbt_calc_size (mp, icount / +XFS_INODES_PER_HOLEMASK_BIT ); +else + inobt_sz =xfs_iallocbt_calc_size (mp, icount / +XFS_INODES_PER_CHUNK ); +if (xfs_sb_version_hasfinobt (&mp->m_sb))
fs/xfs/scrub/repair.c xfs_repair_probe( · line 276
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ inobt_sz *=2 ; +if (xfs_sb_version_hasreflink (&mp->m_sb)) { + rmapbt_sz =xfs_rmapbt_calc_size (mp, aglen); + refcbt_sz =xfs_refcountbt_calc_size (mp, usedlen); + }else { + rmapbt_sz =xfs_rmapbt_calc_size (mp, usedlen); + refcbt_sz =0 ; + } +if (!xfs_sb_version_hasrmapbt (&mp->m_sb)) + rmapbt_sz =0 ;
This looks kinda whacky. reflink and rmapbt are different features, maybe a comment to explain why the rmapbt size calc is done all back to front?
Otherwise looks ok.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 01/22] xfs: add helpers to deal with transaction allocation and rolling
Quoted 79 lines · Dave Chinner
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
For repairs, we need to reserve at least as many blocks as we think we're going to need to rebuild the data structure, and we're going to need some helpers to roll transactions while maintaining locks on the AG headers so that other threads cannot wander into the middle of a repair.
Signed-off-by Darrick J. Wong <[email protected]>Quoted 8 lines
--- fs/xfs/scrub/bmap.c | 2 - fs/xfs/scrub/common.c | 21 ++++++- fs/xfs/scrub/common.h | 2 - fs/xfs/scrub/inode.c | 4 + fs/xfs/scrub/repair.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 12 ++++ 6 files changed, 186 insertions(+), 7 deletions(-)mostly looks good.
[...]
Quoted 21 lines
fs/xfs/scrub/repair.c +21
-0+/* +* Figure out how many blocks to reserve for an AG repair. We calculate the +* worst case estimate for the number of blocks we'd need to rebuild one of +* any type of per-AG btree. +*/ +xfs_extlen_t +xfs_repair_calc_ag_resblks ( +struct xfs_scrub_context *sc) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_scrub_metadata *sm = sc->sm; +struct xfs_perag *pag; +struct xfs_buf *bp; +xfs_agino_t icount =0 ; +xfs_extlen_t aglen =0 ; +xfs_extlen_t usedlen; +xfs_extlen_t freelen; +xfs_extlen_t bnobt_sz; +xfs_extlen_t inobt_sz; +xfs_extlen_t rmapbt_sz; +xfs_extlen_t refcbt_sz;fs/xfs/scrub/repair.c xfs_repair_probe( · line 218
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+int error; + +if (!(sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR )) +return 0 ; + +/* Use in-core counters if possible. */ + pag =xfs_perag_get (mp, sm->sm_agno); +if (pag->pagi_init) + icount = pag->pagi_count; +xfs_perag_put (pag);Don't drop the pag here, do it ....
Quoted 25 lines
fs/xfs/scrub/repair.c +25
-0+ +/* +* Otherwise try to get the actual counters from disk; if not, make +* some worst case assumptions. +*/ +if (icount ==0 ) { + error =xfs_ialloc_read_agi (mp,NULL , sm->sm_agno, &bp); +if (error) { + icount = mp->m_sb.sb_agblocks / mp->m_sb.sb_inopblock; + }else { + pag =xfs_perag_get (mp, sm->sm_agno); + icount = pag->pagi_count; +xfs_perag_put (pag); +xfs_buf_relse (bp); + } + } + +/* Now grab the block counters from the AGF. */ + error =xfs_alloc_read_agf (mp,NULL , sm->sm_agno,0 , &bp); +if (error) { + aglen = mp->m_sb.sb_agblocks; + freelen = aglen; + usedlen = aglen; + }else { + pag =xfs_perag_get (mp, sm->sm_agno);
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_probe( · line 249
Quoted 6 lines
fs/xfs/scrub/repair.c +6
-0+ aglen =be32_to_cpu (XFS_BUF_TO_AGF (bp)->agf_length); + freelen = pag->pagf_freeblks; + usedlen = aglen - freelen; +xfs_perag_put (pag); +xfs_buf_relse (bp); + }.... here, so we don't have to do repeated lookups.
Ok, fixed.
Quoted 22 lines
Quoted 22 lines
fs/xfs/scrub/repair.c +22
-0+trace_xfs_repair_calc_ag_resblks (mp, sm->sm_agno, icount, aglen, + freelen, usedlen); + +/* +* Figure out how many blocks we'd need worst case to rebuild +* each type of btree. Note that we can only rebuild the +* bnobt/cntbt or inobt/finobt as pairs. +*/ + bnobt_sz =2 *xfs_allocbt_calc_size (mp, freelen); +if (xfs_sb_version_hassparseinodes (&mp->m_sb)) + inobt_sz =xfs_iallocbt_calc_size (mp, icount / +XFS_INODES_PER_HOLEMASK_BIT ); +else + inobt_sz =xfs_iallocbt_calc_size (mp, icount / +XFS_INODES_PER_CHUNK ); +if (xfs_sb_version_hasfinobt (&mp->m_sb)) + inobt_sz *=2 ; +if (xfs_sb_version_hasreflink (&mp->m_sb)) { + rmapbt_sz =xfs_rmapbt_calc_size (mp, aglen); + refcbt_sz =xfs_refcountbt_calc_size (mp, usedlen); + }else { + rmapbt_sz =xfs_rmapbt_calc_size (mp, usedlen);
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_probe( · line 276
Quoted 4 lines
fs/xfs/scrub/repair.c +4
-0+ refcbt_sz =0 ; + } +if (!xfs_sb_version_hasrmapbt (&mp->m_sb)) + rmapbt_sz =0 ;This looks kinda whacky. reflink and rmapbt are different features, maybe a comment to explain why the rmapbt size calc is done all back to front?
Yeah. Replace that whole section with:
if (xfs_sb_version_hasreflink(&mp->m_sb))
refcbt_sz = xfs_refcountbt_calc_size(mp, usedlen);
else
refcbt_sz = 0;
if (xfs_sb_version_hasrmapbt(&mp->m_sb)) {
/*
* Guess how many blocks we need to rebuild the rmapbt.
* For non-reflink filesystems we can't have more
* records than used blocks. However, with reflink it's
* possible to have more than one rmap record per AG
* block. We don't know how many rmaps there could be
* in the AG, so we start off with what we hope is an
* generous over-estimation.
*/
if (xfs_sb_version_hasreflink(&mp->m_sb))
rmapbt_sz = xfs_rmapbt_calc_size(mp,
(unsigned long long)aglen * 2);
else
rmapbt_sz = xfs_rmapbt_calc_size(mp, usedlen);
} else {
rmapbt_sz = 0;
}--D
Quoted 11 lines
Otherwise looks ok.
Reviewed-by Dave Chinner <[email protected]>Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 01/22] xfs: add helpers to deal with transaction allocation and rolling
Quoted 68 lines · Darrick J. Wong
Quoted 42 lines · Dave Chinner
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
For repairs, we need to reserve at least as many blocks as we think we're going to need to rebuild the data structure, and we're going to need some helpers to roll transactions while maintaining locks on the AG headers so that other threads cannot wander into the middle of a repair.
Signed-off-by Darrick J. Wong <[email protected]>Quoted 8 lines
--- fs/xfs/scrub/bmap.c | 2 - fs/xfs/scrub/common.c | 21 ++++++- fs/xfs/scrub/common.h | 2 - fs/xfs/scrub/inode.c | 4 + fs/xfs/scrub/repair.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 12 ++++ 6 files changed, 186 insertions(+), 7 deletions(-)mostly looks good.
[...]
Quoted 21 lines
fs/xfs/scrub/repair.c +21
-0+ freelen, usedlen); + +/* +* Figure out how many blocks we'd need worst case to rebuild +* each type of btree. Note that we can only rebuild the +* bnobt/cntbt or inobt/finobt as pairs. +*/ + bnobt_sz =2 *xfs_allocbt_calc_size (mp, freelen); +if (xfs_sb_version_hassparseinodes (&mp->m_sb)) + inobt_sz =xfs_iallocbt_calc_size (mp, icount / +XFS_INODES_PER_HOLEMASK_BIT ); +else + inobt_sz =xfs_iallocbt_calc_size (mp, icount / +XFS_INODES_PER_CHUNK ); +if (xfs_sb_version_hasfinobt (&mp->m_sb)) + inobt_sz *=2 ; +if (xfs_sb_version_hasreflink (&mp->m_sb)) { + rmapbt_sz =xfs_rmapbt_calc_size (mp, aglen); + refcbt_sz =xfs_refcountbt_calc_size (mp, usedlen); + }else { + rmapbt_sz =xfs_rmapbt_calc_size (mp, usedlen);Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_probe( · line 276
Quoted 4 lines
fs/xfs/scrub/repair.c +4
-0+ refcbt_sz =0 ; + } +if (!xfs_sb_version_hasrmapbt (&mp->m_sb)) + rmapbt_sz =0 ;This looks kinda whacky. reflink and rmapbt are different features, maybe a comment to explain why the rmapbt size calc is done all back to front?
Yeah. Replace that whole section with:
if (xfs_sb_version_hasreflink(&mp->m_sb)) refcbt_sz = xfs_refcountbt_calc_size(mp, usedlen); else refcbt_sz = 0; if (xfs_sb_version_hasrmapbt(&mp->m_sb)) { /* * Guess how many blocks we need to rebuild the rmapbt. * For non-reflink filesystems we can't have more * records than used blocks. However, with reflink it's * possible to have more than one rmap record per AG * block. We don't know how many rmaps there could be * in the AG, so we start off with what we hope is an * generous over-estimation. */
Quoted 8 lines
if (xfs_sb_version_hasreflink(&mp->m_sb))
rmapbt_sz = xfs_rmapbt_calc_size(mp,
(unsigned long long)aglen * 2);
else
rmapbt_sz = xfs_rmapbt_calc_size(mp, usedlen);
} else {
rmapbt_sz = 0;
}Yup, that's much better :)
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 01/22] xfs: add helpers to deal with transaction allocation and rolling
In reply to Darrick J. WongLooks good to me, you can add:
Reviewed by: Allison Henderson <[email protected]>
Quoted 318 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
For repairs, we need to reserve at least as many blocks as we think we're going to need to rebuild the data structure, and we're going to need some helpers to roll transactions while maintaining locks on the AG headers so that other threads cannot wander into the middle of a repair.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/bmap.c | 2 - fs/xfs/scrub/common.c | 21 ++++++- fs/xfs/scrub/common.h | 2 - fs/xfs/scrub/inode.c | 4 + fs/xfs/scrub/repair.c | 152 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 12 ++++ 6 files changed, 186 insertions(+), 7 deletions(-)fs/xfs/scrub/bmap.c +1
-1@@ -74,7 +74,7 @@ xfs_scrub_setup_inode_bmap( }/* Got the inode, lock it and we're ready to go. */ - error =xfs_scrub_trans_alloc (sc); + error =xfs_scrub_trans_alloc (sc,0 );if (error) goto out; sc->ilock_flags |=XFS_ILOCK_EXCL ;fs/xfs/scrub/common.c +18
-3@@ -51,6 +51,7 @@#include "scrub/common.h" #include "scrub/trace.h" #include "scrub/btree.h" +#include "scrub/repair.h" /* Common code for the metadata scrubbers. */ @@ -573,11 +574,22 @@ xfs_scrub_ag_init(/* * Grab an empty transaction so that we can re-grab locked buffers if * one of our btrees turns out to be cyclic. +* +* If we're going to repair something, we need to ask for the largest possible +* log reservation so that we can handle the worst case scenario for metadata +* updates while rebuilding a metadata item. We also need to reserve as many +* blocks in the head transaction as we think we're going to need to rebuild +* the metadata object. */ int xfs_scrub_trans_alloc ( -struct xfs_scrub_context *sc) +struct xfs_scrub_context *sc, +uint resblks) { +if (sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR ) +return xfs_trans_alloc (sc->mp, &M_RES (sc->mp)->tr_itruncate, + resblks,0 ,0 , &sc->tp); +return xfs_trans_alloc_empty (sc->mp, &sc->tp); }@@ -587,7 +599,10 @@ xfs_scrub_setup_fs(struct xfs_scrub_context *sc, struct xfs_inode *ip) { - return xfs_scrub_trans_alloc(sc); + uint resblks; + + resblks =xfs_repair_calc_ag_resblks (sc); +return xfs_scrub_trans_alloc (sc, resblks); }/* Set us up with AG headers and btree cursors. */ @@ -717,7 +732,7 @@ xfs_scrub_setup_inode_contents(/* Got the inode, lock it and we're ready to go. */ sc->ilock_flags =XFS_IOLOCK_EXCL |XFS_MMAPLOCK_EXCL ;xfs_ilock (sc->ip, sc->ilock_flags); - error =xfs_scrub_trans_alloc (sc); + error =xfs_scrub_trans_alloc (sc, resblks);if (error) goto out; sc->ilock_flags |=XFS_ILOCK_EXCL ;fs/xfs/scrub/common.h +1
-1@@ -38,7 +38,7 @@ xfs_scrub_should_terminate(return false; } -int xfs_scrub_trans_alloc (struct xfs_scrub_context *sc); +int xfs_scrub_trans_alloc (struct xfs_scrub_context *sc,uint resblks);bool xfs_scrub_process_error (struct xfs_scrub_context *sc,xfs_agnumber_t agno,xfs_agblock_t bno,int *error); bool xfs_scrub_fblock_process_error(struct xfs_scrub_context *sc, int whichfork,fs/xfs/scrub/inode.c +2
-2@@ -67,7 +67,7 @@ xfs_scrub_setup_inode(break ;case -EFSCORRUPTED :case -EFSBADCRC : -return xfs_scrub_trans_alloc (sc); +return xfs_scrub_trans_alloc (sc,0 );default :return error; }@@ -75,7 +75,7 @@ xfs_scrub_setup_inode(/* Got the inode, lock it and we're ready to go. */ sc->ilock_flags =XFS_IOLOCK_EXCL |XFS_MMAPLOCK_EXCL ;xfs_ilock (sc->ip, sc->ilock_flags); - error =xfs_scrub_trans_alloc (sc); + error =xfs_scrub_trans_alloc (sc,0 );if (error) goto out; sc->ilock_flags |=XFS_ILOCK_EXCL ;fs/xfs/scrub/repair.c +152
-0@@ -128,3 +128,155 @@ xfs_repair_probe(return 0 ; } + +/* +* Roll a transaction, keeping the AG headers locked and reinitializing +* the btree cursors. +*/ +int +xfs_repair_roll_ag_trans ( +struct xfs_scrub_context *sc) +{ +int error; + +/* Keep the AG header buffers locked so we can keep going. */ +xfs_trans_bhold (sc->tp, sc->sa.agi_bp); +xfs_trans_bhold (sc->tp, sc->sa.agf_bp); +xfs_trans_bhold (sc->tp, sc->sa.agfl_bp); + +/* Roll the transaction. */ + error =xfs_trans_roll (&sc->tp); +if (error) + goto out_release; + +/* Join AG headers to the new transaction. */ +xfs_trans_bjoin (sc->tp, sc->sa.agi_bp); +xfs_trans_bjoin (sc->tp, sc->sa.agf_bp); +xfs_trans_bjoin (sc->tp, sc->sa.agfl_bp); + +return 0 ; + +out_release: +/* +* Rolling failed, so release the hold on the buffers. The +* buffers will be released during teardown on our way out +* of the kernel. +*/ +xfs_trans_bhold_release (sc->tp, sc->sa.agi_bp); +xfs_trans_bhold_release (sc->tp, sc->sa.agf_bp); +xfs_trans_bhold_release (sc->tp, sc->sa.agfl_bp); + +return error; +} + +/* +* Does the given AG have enough space to rebuild a btree? Neither AG +* reservation can be critical, and we must have enough space (factoring +* in AG reservations) to construct a whole btree. +*/ +bool +xfs_repair_ag_has_space ( +struct xfs_perag *pag, +xfs_extlen_t nr_blocks, +enum xfs_ag_resv_type type) +{ +return !xfs_ag_resv_critical (pag,XFS_AG_RESV_RMAPBT ) && + !xfs_ag_resv_critical (pag,XFS_AG_RESV_METADATA ) && + pag->pagf_freeblks >xfs_ag_resv_needed (pag, type) + nr_blocks; +} + +/* +* Figure out how many blocks to reserve for an AG repair. We calculate the +* worst case estimate for the number of blocks we'd need to rebuild one of +* any type of per-AG btree. +*/ +xfs_extlen_t +xfs_repair_calc_ag_resblks ( +struct xfs_scrub_context *sc) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_scrub_metadata *sm = sc->sm; +struct xfs_perag *pag; +struct xfs_buf *bp; +xfs_agino_t icount =0 ; +xfs_extlen_t aglen =0 ; +xfs_extlen_t usedlen; +xfs_extlen_t freelen; +xfs_extlen_t bnobt_sz; +xfs_extlen_t inobt_sz; +xfs_extlen_t rmapbt_sz; +xfs_extlen_t refcbt_sz; +int error; + +if (!(sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR )) +return 0 ; + +/* Use in-core counters if possible. */ + pag =xfs_perag_get (mp, sm->sm_agno); +if (pag->pagi_init) + icount = pag->pagi_count; +xfs_perag_put (pag); + +/* +* Otherwise try to get the actual counters from disk; if not, make +* some worst case assumptions. +*/ +if (icount ==0 ) { + error =xfs_ialloc_read_agi (mp,NULL , sm->sm_agno, &bp); +if (error) { + icount = mp->m_sb.sb_agblocks / mp->m_sb.sb_inopblock; + }else { + pag =xfs_perag_get (mp, sm->sm_agno); + icount = pag->pagi_count; +xfs_perag_put (pag); +xfs_buf_relse (bp); + } + } + +/* Now grab the block counters from the AGF. */ + error =xfs_alloc_read_agf (mp,NULL , sm->sm_agno,0 , &bp); +if (error) { + aglen = mp->m_sb.sb_agblocks; + freelen = aglen; + usedlen = aglen; + }else { + pag =xfs_perag_get (mp, sm->sm_agno); + aglen =be32_to_cpu (XFS_BUF_TO_AGF (bp)->agf_length); + freelen = pag->pagf_freeblks; + usedlen = aglen - freelen; +xfs_perag_put (pag); +xfs_buf_relse (bp); + } + +trace_xfs_repair_calc_ag_resblks (mp, sm->sm_agno, icount, aglen, + freelen, usedlen); + +/* +* Figure out how many blocks we'd need worst case to rebuild +* each type of btree. Note that we can only rebuild the +* bnobt/cntbt or inobt/finobt as pairs. +*/ + bnobt_sz =2 *xfs_allocbt_calc_size (mp, freelen); +if (xfs_sb_version_hassparseinodes (&mp->m_sb)) + inobt_sz =xfs_iallocbt_calc_size (mp, icount / +XFS_INODES_PER_HOLEMASK_BIT ); +else + inobt_sz =xfs_iallocbt_calc_size (mp, icount / +XFS_INODES_PER_CHUNK ); +if (xfs_sb_version_hasfinobt (&mp->m_sb)) + inobt_sz *=2 ; +if (xfs_sb_version_hasreflink (&mp->m_sb)) { + rmapbt_sz =xfs_rmapbt_calc_size (mp, aglen); + refcbt_sz =xfs_refcountbt_calc_size (mp, usedlen); + }else { + rmapbt_sz =xfs_rmapbt_calc_size (mp, usedlen); + refcbt_sz =0 ; + } +if (!xfs_sb_version_hasrmapbt (&mp->m_sb)) + rmapbt_sz =0 ; + +trace_xfs_repair_calc_ag_resblks_btsize (mp, sm->sm_agno, bnobt_sz, + inobt_sz, rmapbt_sz, refcbt_sz); + +return max (max (bnobt_sz, inobt_sz),max (rmapbt_sz, refcbt_sz)); +}fs/xfs/scrub/repair.h +12
-1@@ -32,6 +32,10 @@ static inline int xfs_repair_notsupported(struct xfs_scrub_context *sc)int xfs_repair_attempt (struct xfs_inode *ip,struct xfs_scrub_context *sc,bool *fixed);void xfs_repair_failure (struct xfs_mount *mp); +int xfs_repair_roll_ag_trans (struct xfs_scrub_context *sc); +bool xfs_repair_ag_has_space (struct xfs_perag *pag,xfs_extlen_t nr_blocks, +enum xfs_ag_resv_type type); +xfs_extlen_t xfs_repair_calc_ag_resblks (struct xfs_scrub_context *sc);/* Metadata repairers */ @@ -49,6 +53,14 @@ static inline int xfs_repair_attempt(static inline void xfs_repair_failure (struct xfs_mount *mp) {} +static inline xfs_extlen_t +xfs_repair_calc_ag_resblks ( +struct xfs_scrub_context *sc) +{ +ASSERT (!(sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR )); +return 0 ; +} +#define xfs_repair_probe xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */ --To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwICaQ&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=TQHk1pYShWjO6ANE7iY6Bn29jiBqkeNo5IMsc5kla3U&s=vF097w5pOXeZmfX1JYpzuN-nJX8gG5B7EUcQQkONWKk&e=
PATCH v2 1/22 xfs: add helpers to deal with transaction allocation and rolling
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
For repairs, we need to reserve at least as many blocks as we think we're going to need to rebuild the data structure, and we're going to need some helpers to roll transactions while maintaining locks on the AG headers so that other threads cannot wander into the middle of a repair.
---
v2: fix whacky rmap reservation code, better docs
--- fs/xfs/scrub/bmap.c | 2 - fs/xfs/scrub/common.c | 21 ++++++ fs/xfs/scrub/common.h | 2 - fs/xfs/scrub/inode.c | 4 + fs/xfs/scrub/repair.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 12 ++++ 6 files changed, 194 insertions(+), 7 deletions(-)
fs/xfs/scrub/bmap.c +1 -1
@@ -74,7 +74,7 @@ xfs_scrub_setup_inode_bmap( }/* Got the inode, lock it and we're ready to go. */ - error =xfs_scrub_trans_alloc (sc); + error =xfs_scrub_trans_alloc (sc,0 );if (error) goto out; sc->ilock_flags |=XFS_ILOCK_EXCL ;
fs/xfs/scrub/common.c +18 -3
@@ -51,6 +51,7 @@#include "scrub/common.h" #include "scrub/trace.h" #include "scrub/btree.h" +#include "scrub/repair.h" /* Common code for the metadata scrubbers. */
@@ -590,11 +591,22 @@ xfs_scrub_perag_get(/* * Grab an empty transaction so that we can re-grab locked buffers if * one of our btrees turns out to be cyclic. +* +* If we're going to repair something, we need to ask for the largest possible +* log reservation so that we can handle the worst case scenario for metadata +* updates while rebuilding a metadata item. We also need to reserve as many +* blocks in the head transaction as we think we're going to need to rebuild +* the metadata object. */ int xfs_scrub_trans_alloc ( -struct xfs_scrub_context *sc) +struct xfs_scrub_context *sc, +uint resblks) { +if (sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR ) +return xfs_trans_alloc (sc->mp, &M_RES (sc->mp)->tr_itruncate, + resblks,0 ,0 , &sc->tp); +return xfs_trans_alloc_empty (sc->mp, &sc->tp); }
@@ -604,7 +616,10 @@ xfs_scrub_setup_fs(struct xfs_scrub_context *sc, struct xfs_inode *ip) { - return xfs_scrub_trans_alloc(sc); + uint resblks; + + resblks =xfs_repair_calc_ag_resblks (sc); +return xfs_scrub_trans_alloc (sc, resblks); }/* Set us up with AG headers and btree cursors. */
@@ -734,7 +749,7 @@ xfs_scrub_setup_inode_contents(/* Got the inode, lock it and we're ready to go. */ sc->ilock_flags =XFS_IOLOCK_EXCL |XFS_MMAPLOCK_EXCL ;xfs_ilock (sc->ip, sc->ilock_flags); - error =xfs_scrub_trans_alloc (sc); + error =xfs_scrub_trans_alloc (sc, resblks);if (error) goto out; sc->ilock_flags |=XFS_ILOCK_EXCL ;
fs/xfs/scrub/common.h +1 -1
@@ -38,7 +38,7 @@ xfs_scrub_should_terminate(return false; } -int xfs_scrub_trans_alloc (struct xfs_scrub_context *sc); +int xfs_scrub_trans_alloc (struct xfs_scrub_context *sc,uint resblks);bool xfs_scrub_process_error (struct xfs_scrub_context *sc,xfs_agnumber_t agno,xfs_agblock_t bno,int *error); bool xfs_scrub_fblock_process_error(struct xfs_scrub_context *sc, int whichfork,
fs/xfs/scrub/inode.c +2 -2
@@ -67,7 +67,7 @@ xfs_scrub_setup_inode(break ;case -EFSCORRUPTED :case -EFSBADCRC : -return xfs_scrub_trans_alloc (sc); +return xfs_scrub_trans_alloc (sc,0 );default :return error; }
@@ -75,7 +75,7 @@ xfs_scrub_setup_inode(/* Got the inode, lock it and we're ready to go. */ sc->ilock_flags =XFS_IOLOCK_EXCL |XFS_MMAPLOCK_EXCL ;xfs_ilock (sc->ip, sc->ilock_flags); - error =xfs_scrub_trans_alloc (sc); + error =xfs_scrub_trans_alloc (sc,0 );if (error) goto out; sc->ilock_flags |=XFS_ILOCK_EXCL ;
fs/xfs/scrub/repair.c +160 -0
163 lines not on this page · raw message
fs/xfs/scrub/repair.h +12 -0
@@ -32,6 +32,10 @@ static inline int xfs_repair_notsupported(struct xfs_scrub_context *sc)int xfs_repair_attempt (struct xfs_inode *ip,struct xfs_scrub_context *sc,bool *fixed);void xfs_repair_failure (struct xfs_mount *mp); +int xfs_repair_roll_ag_trans (struct xfs_scrub_context *sc); +bool xfs_repair_ag_has_space (struct xfs_perag *pag,xfs_extlen_t nr_blocks, +enum xfs_ag_resv_type type); +xfs_extlen_t xfs_repair_calc_ag_resblks (struct xfs_scrub_context *sc);/* Metadata repairers */
@@ -49,6 +53,14 @@ static inline int xfs_repair_attempt(static inline void xfs_repair_failure (struct xfs_mount *mp) {} +static inline xfs_extlen_t +xfs_repair_calc_ag_resblks ( +struct xfs_scrub_context *sc) +{ +ASSERT (!(sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR )); +return 0 ; +} +#define xfs_repair_probe xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
PATCH 2/22 xfs: add helpers to allocate and initialize fresh btree roots
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add a pair of helper functions to allocate and initialize fresh btree roots. The repair functions will use these as part of recreating corrupted metadata.
--- fs/xfs/scrub/repair.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 6 ++++ 2 files changed, 87 insertions(+)
fs/xfs/scrub/repair.c +81 -0
@@ -280,3 +280,84 @@ xfs_repair_calc_ag_resblks(return max (max (bnobt_sz, inobt_sz),max (rmapbt_sz, refcbt_sz)); } + +/* Allocate a block in an AG. */ +int +xfs_repair_alloc_ag_block ( +struct xfs_scrub_context *sc, +struct xfs_owner_info *oinfo, +xfs_fsblock_t *fsbno, +enum xfs_ag_resv_type resv) +{ +struct xfs_alloc_arg args = {0 }; +xfs_agblock_t bno; +int error; + +switch (resv) { +case XFS_AG_RESV_AGFL : +case XFS_AG_RESV_RMAPBT : + error =xfs_alloc_get_freelist (sc->tp, sc->sa.agf_bp, &bno,1 ); +if (error) +return error; +if (bno ==NULLAGBLOCK ) +return -ENOSPC ; +xfs_extent_busy_reuse (sc->mp, sc->sa.agno, bno, +1 , false); + *fsbno =XFS_AGB_TO_FSB (sc->mp, sc->sa.agno, bno); +if (resv ==XFS_AG_RESV_RMAPBT ) +xfs_ag_resv_rmapbt_alloc (sc->mp, sc->sa.agno); +return 0 ; +default : +break ; + } + + args.tp = sc->tp; + args.mp = sc->mp; + args.oinfo = *oinfo; + args.fsbno =XFS_AGB_TO_FSB (args.mp, sc->sa.agno,0 ); + args.minlen =1 ; + args.maxlen =1 ; + args.prod =1 ; + args.type =XFS_ALLOCTYPE_THIS_AG ; + args.resv = resv; + + error =xfs_alloc_vextent (&args); +if (error) +return error; +if (args.fsbno ==NULLFSBLOCK ) +return -ENOSPC ; +ASSERT (args.len ==1 ); + *fsbno = args.fsbno; + +return 0 ; +} + +/* Initialize a new AG btree root block with zero entries. */ +int +xfs_repair_init_btblock ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsb, +struct xfs_buf **bpp, +xfs_btnum_t btnum, +const struct xfs_buf_ops *ops) +{ +struct xfs_trans *tp = sc->tp; +struct xfs_mount *mp = sc->mp; +struct xfs_buf *bp; + +trace_xfs_repair_init_btblock (mp,XFS_FSB_TO_AGNO (mp, fsb), +XFS_FSB_TO_AGBNO (mp, fsb), btnum); + +ASSERT (XFS_FSB_TO_AGNO (mp, fsb) == sc->sa.agno); + bp =xfs_trans_get_buf (tp, mp->m_ddev_targp,XFS_FSB_TO_DADDR (mp, fsb), +XFS_FSB_TO_BB (mp,1 ),0 ); +xfs_buf_zero (bp,0 ,BBTOB (bp->b_length)); +xfs_btree_init_block (mp, bp, btnum,0 ,0 , sc->sa.agno, +XFS_BTREE_CRC_BLOCKS ); +xfs_trans_buf_set_type (tp, bp,XFS_BLFT_BTREE_BUF ); +xfs_trans_log_buf (tp, bp,0 , bp->b_length); + bp->b_ops = ops; + *bpp = bp; + +return 0 ; +}
fs/xfs/scrub/repair.h +6 -0
@@ -36,6 +36,12 @@ int xfs_repair_roll_ag_trans(struct xfs_scrub_context *sc);bool xfs_repair_ag_has_space (struct xfs_perag *pag,xfs_extlen_t nr_blocks,enum xfs_ag_resv_type type);xfs_extlen_t xfs_repair_calc_ag_resblks (struct xfs_scrub_context *sc); +int xfs_repair_alloc_ag_block (struct xfs_scrub_context *sc, +struct xfs_owner_info *oinfo,xfs_fsblock_t *fsbno, +enum xfs_ag_resv_type resv); +int xfs_repair_init_btblock (struct xfs_scrub_context *sc,xfs_fsblock_t fsb, +struct xfs_buf **bpp,xfs_btnum_t btnum, +const struct xfs_buf_ops *ops);/* Metadata repairers */
Re: [PATCH 02/22] xfs: add helpers to allocate and initialize fresh btree roots
Quoted 11 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a pair of helper functions to allocate and initialize fresh btree roots. The repair functions will use these as part of recreating corrupted metadata.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 6 ++++ 2 files changed, 87 insertions(+)
Looks good, but....
Quoted 12 lines
fs/xfs/scrub/repair.c +12
-0+/* Initialize a new AG btree root block with zero entries. */ +int +xfs_repair_init_btblock ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsb, +struct xfs_buf **bpp, +xfs_btnum_t btnum, +const struct xfs_buf_ops *ops) +{ +struct xfs_trans *tp = sc->tp; +struct xfs_mount *mp = sc->mp; +struct xfs_buf *bp;
fs/xfs/scrub/repair.c xfs_repair_calc_ag_resblks( · line 356
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ +trace_xfs_repair_init_btblock (mp,XFS_FSB_TO_AGNO (mp, fsb), +XFS_FSB_TO_AGBNO (mp, fsb), btnum); + +ASSERT (XFS_FSB_TO_AGNO (mp, fsb) == sc->sa.agno); + bp =xfs_trans_get_buf (tp, mp->m_ddev_targp,XFS_FSB_TO_DADDR (mp, fsb), +XFS_FSB_TO_BB (mp,1 ),0 ); +xfs_buf_zero (bp,0 ,BBTOB (bp->b_length)); +xfs_btree_init_block (mp, bp, btnum,0 ,0 , sc->sa.agno, +XFS_BTREE_CRC_BLOCKS );
This flag does nothing in xfs_btree_init_block(). Any reason for setting it?
With that fixed, though,
fs/xfs/scrub/repair.c xfs_repair_calc_ag_resblks( · line 363
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+xfs_trans_buf_set_type (tp, bp,XFS_BLFT_BTREE_BUF ); +xfs_trans_log_buf (tp, bp,0 , bp->b_length); + bp->b_ops = ops; + *bpp = bp; + +return 0 ; +}
For followup patches, I think there's some overlap with the new libxfs/xfs_ag.c functions for initialising new btree root blocks for growfs? e.g. Make the struct xfs_aghdr_grow_data aghdr_data[] array a static global, and then we can do something like:
bp = xfs_ag_init_btroot(mp, tp, agno, agbno, btnum); xfs_trans_log_buf(tp, bp, 0, bp->b_length);
and all the details of reading the btree block and setting it up go away from this code....
-Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 02/22] xfs: add helpers to allocate and initialize fresh btree roots
Quoted 32 lines · Dave Chinner
Quoted 11 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a pair of helper functions to allocate and initialize fresh btree roots. The repair functions will use these as part of recreating corrupted metadata.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 6 ++++ 2 files changed, 87 insertions(+)Looks good, but....
Quoted 17 lines
fs/xfs/scrub/repair.c +17
-0+/* Initialize a new AG btree root block with zero entries. */ +int +xfs_repair_init_btblock ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsb, +struct xfs_buf **bpp, +xfs_btnum_t btnum, +const struct xfs_buf_ops *ops) +{ +struct xfs_trans *tp = sc->tp; +struct xfs_mount *mp = sc->mp; +struct xfs_buf *bp; + +trace_xfs_repair_init_btblock (mp,XFS_FSB_TO_AGNO (mp, fsb), +XFS_FSB_TO_AGBNO (mp, fsb), btnum); + +ASSERT (XFS_FSB_TO_AGNO (mp, fsb) == sc->sa.agno);
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_calc_ag_resblks( · line 356
Quoted 5 lines
fs/xfs/scrub/repair.c +5
-0+ bp =xfs_trans_get_buf (tp, mp->m_ddev_targp,XFS_FSB_TO_DADDR (mp, fsb), +XFS_FSB_TO_BB (mp,1 ),0 ); +xfs_buf_zero (bp,0 ,BBTOB (bp->b_length)); +xfs_btree_init_block (mp, bp, btnum,0 ,0 , sc->sa.agno, +XFS_BTREE_CRC_BLOCKS );This flag does nothing in
xfs_btree_init_block(). Any reason for setting it?
Nope. Will fix.
Quoted 17 lines
With that fixed, though,
Reviewed-by Dave Chinner <[email protected]>fs/xfs/scrub/repair.c xfs_repair_calc_ag_resblks( · line 363
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+xfs_trans_buf_set_type (tp, bp,XFS_BLFT_BTREE_BUF ); +xfs_trans_log_buf (tp, bp,0 , bp->b_length); + bp->b_ops = ops; + *bpp = bp; + +return 0 ; +}For followup patches, I think there's some overlap with the new
libxfs/xfs_ag.cfunctions for initialising new btree root blocks for growfs? e.g. Make the structxfs_aghdr_grow_dataaghdr_data[] array a static global, and then we can do something like:
Quoted 5 lines
bp = xfs_ag_init_btroot(mp, tp, agno, agbno, btnum); xfs_trans_log_buf(tp, bp, 0, bp->b_length);and all the details of reading the btree block and setting it up go away from this code....
I'm not sure aghdr_data can be made static global since parts of it depend on the struct xfs_mount, but at the very least we should try to refactor this (in the followup series) to simplify the repair code.
There may be complications relating to the xfs_*root_init functions initializing default records that are appropriate for a freshly created AG that are totally wrong for regenerating a broken AG, and in any case repair really requires totally empty root blocks since it has already generated the list of records it's going to put into the new btree.
--D
Quoted 8 lines
-Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 02/22] xfs: add helpers to allocate and initialize fresh btree roots
In reply to Darrick J. WongAlrighty, you can add my review:
Reviewed by: Allison Henderson <[email protected]>
Thx!
Quoted 124 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a pair of helper functions to allocate and initialize fresh btree roots. The repair functions will use these as part of recreating corrupted metadata.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 6 ++++ 2 files changed, 87 insertions(+)fs/xfs/scrub/repair.c +81
-0@@ -280,3 +280,84 @@ xfs_repair_calc_ag_resblks(return max (max (bnobt_sz, inobt_sz),max (rmapbt_sz, refcbt_sz)); } + +/* Allocate a block in an AG. */ +int +xfs_repair_alloc_ag_block ( +struct xfs_scrub_context *sc, +struct xfs_owner_info *oinfo, +xfs_fsblock_t *fsbno, +enum xfs_ag_resv_type resv) +{ +struct xfs_alloc_arg args = {0 }; +xfs_agblock_t bno; +int error; + +switch (resv) { +case XFS_AG_RESV_AGFL : +case XFS_AG_RESV_RMAPBT : + error =xfs_alloc_get_freelist (sc->tp, sc->sa.agf_bp, &bno,1 ); +if (error) +return error; +if (bno ==NULLAGBLOCK ) +return -ENOSPC ; +xfs_extent_busy_reuse (sc->mp, sc->sa.agno, bno, +1 , false); + *fsbno =XFS_AGB_TO_FSB (sc->mp, sc->sa.agno, bno); +if (resv ==XFS_AG_RESV_RMAPBT ) +xfs_ag_resv_rmapbt_alloc (sc->mp, sc->sa.agno); +return 0 ; +default : +break ; + } + + args.tp = sc->tp; + args.mp = sc->mp; + args.oinfo = *oinfo; + args.fsbno =XFS_AGB_TO_FSB (args.mp, sc->sa.agno,0 ); + args.minlen =1 ; + args.maxlen =1 ; + args.prod =1 ; + args.type =XFS_ALLOCTYPE_THIS_AG ; + args.resv = resv; + + error =xfs_alloc_vextent (&args); +if (error) +return error; +if (args.fsbno ==NULLFSBLOCK ) +return -ENOSPC ; +ASSERT (args.len ==1 ); + *fsbno = args.fsbno; + +return 0 ; +} + +/* Initialize a new AG btree root block with zero entries. */ +int +xfs_repair_init_btblock ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsb, +struct xfs_buf **bpp, +xfs_btnum_t btnum, +const struct xfs_buf_ops *ops) +{ +struct xfs_trans *tp = sc->tp; +struct xfs_mount *mp = sc->mp; +struct xfs_buf *bp; + +trace_xfs_repair_init_btblock (mp,XFS_FSB_TO_AGNO (mp, fsb), +XFS_FSB_TO_AGBNO (mp, fsb), btnum); + +ASSERT (XFS_FSB_TO_AGNO (mp, fsb) == sc->sa.agno); + bp =xfs_trans_get_buf (tp, mp->m_ddev_targp,XFS_FSB_TO_DADDR (mp, fsb), +XFS_FSB_TO_BB (mp,1 ),0 ); +xfs_buf_zero (bp,0 ,BBTOB (bp->b_length)); +xfs_btree_init_block (mp, bp, btnum,0 ,0 , sc->sa.agno, +XFS_BTREE_CRC_BLOCKS ); +xfs_trans_buf_set_type (tp, bp,XFS_BLFT_BTREE_BUF ); +xfs_trans_log_buf (tp, bp,0 , bp->b_length); + bp->b_ops = ops; + *bpp = bp; + +return 0 ; +}fs/xfs/scrub/repair.h +6
-1@@ -36,6 +36,12 @@ int xfs_repair_roll_ag_trans(struct xfs_scrub_context *sc);bool xfs_repair_ag_has_space (struct xfs_perag *pag,xfs_extlen_t nr_blocks,enum xfs_ag_resv_type type);xfs_extlen_t xfs_repair_calc_ag_resblks (struct xfs_scrub_context *sc); +int xfs_repair_alloc_ag_block (struct xfs_scrub_context *sc, +struct xfs_owner_info *oinfo,xfs_fsblock_t *fsbno, +enum xfs_ag_resv_type resv); +int xfs_repair_init_btblock (struct xfs_scrub_context *sc,xfs_fsblock_t fsb, +struct xfs_buf **bpp,xfs_btnum_t btnum, +const struct xfs_buf_ops *ops);/* Metadata repairers */ --To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwICaQ&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=4QHdmNFIP7trSueA8XyY8TjPFBlv0bJ8UpmheOu6zJA&s=nvq_2KNHpSbVaDCf2vQai6hS8Pkmm8mnLSLrA8erFCk&e=
PATCH 3/22 xfs: add helpers to collect and sift btree block pointers during repair
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
--- fs/xfs/scrub/repair.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 31 +++++++ 2 files changed, 238 insertions(+)
fs/xfs/scrub/repair.c +207 -0
@@ -361,3 +361,210 @@ xfs_repair_init_btblock(return 0 ; } + +/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len) +{ +struct xfs_repair_extent *rex; + +trace_xfs_repair_collect_btree_extent (sc->mp, +XFS_FSB_TO_AGNO (sc->mp, fsbno), +XFS_FSB_TO_AGBNO (sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ; + +INIT_LIST_HEAD (&rex->list); + rex->fsbno = fsbno; + rex->len = len; +list_add_tail (&rex->list, &exlist->list); + +return 0 ; +} + +/* +* Dispose of btree blocks from the old btree so that we can start using the +* new btree once the transaction commits. +*/ +int +xfs_repair_reap_btree_extents ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type type) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; +int error =0 ; + +for_each_xfs_repair_extent_safe (rex, n, exlist) { +/* TODO: free the extent */ +list_del (&rex->list); +kmem_free (rex); + } + +return error; +} + +/* +* An error happened during the rebuild so the transaction will be cancelled. +* The fs will shut down, and the administrator has to unmount and run repair. +* Therefore, free all the memory associated with the list so we can die. +*/ +void +xfs_repair_cancel_btree_extents ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; + +for_each_xfs_repair_extent_safe (rex, n, exlist) { +list_del (&rex->list); +kmem_free (rex); + } +} + +/* Compare two btree extents. */ +static int +xfs_repair_btree_extent_cmp ( +void *priv, +struct list_head *a, +struct list_head *b) +{ +struct xfs_repair_extent *ap; +struct xfs_repair_extent *bp; + + ap = container_of(a, struct xfs_repair_extent, list); + bp = container_of(b, struct xfs_repair_extent, list); + +if (ap->fsbno > bp->fsbno) +return 1 ; +else if (ap->fsbno < bp->fsbno) +return -1 ; +return 0 ; +} + +/* +* Remove all the blocks mentioned in sublist from the extents in exlist. +* +* The intent is that callers will iterate the rmapbt for all of its records +* for a given owner to generate exlist; and iterate all the blocks of the +* metadata structures that are not being rebuilt and have the same rmapbt +* owner to generate sublist. This routine subtracts all the extents +* mentioned in sublist from all the extents linked in exlist, which leaves +* exlist as the list of blocks that are not accounted for, which we assume +* are the dead blocks of the old metadata structure. The blocks mentioned in +* exlist can be reaped. +*/ +#define XFS_REPAIR_EXT_LEFT_CONTIG (1 << 0) +#define XFS_REPAIR_EXT_RIGHT_CONTIG (1 << 1) +int +xfs_repair_subtract_extents ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_repair_extent_list *sublist) +{ +struct list_head *lp; +struct xfs_repair_extent *ex; +struct xfs_repair_extent *newex; +struct xfs_repair_extent *subex; +xfs_fsblock_t sub_fsb; +xfs_extlen_t sub_len; +int state; +int error =0 ; + +if (list_empty (&exlist->list) ||list_empty (&sublist->list)) +return 0 ; +ASSERT (!list_empty (&sublist->list)); + +list_sort (NULL , &exlist->list, xfs_repair_btree_extent_cmp); +list_sort (NULL , &sublist->list, xfs_repair_btree_extent_cmp); + + subex = list_first_entry(&sublist->list, struct xfs_repair_extent, + list); + lp = exlist->list.next; +while (lp != &exlist->list) { + ex = list_entry(lp, struct xfs_repair_extent, list); + +/* +* Advance subex and/or ex until we find a pair that +* intersect or we run out of extents. +*/ +while (subex->fsbno + subex->len <= ex->fsbno) { +if (list_is_last (&subex->list, &sublist->list)) + goto out; + subex =list_next_entry (subex, list); + } +if (subex->fsbno >= ex->fsbno + ex->len) { + lp = lp->next; +continue ; + } + +/* trim subex to fit the extent we have */ + sub_fsb = subex->fsbno; + sub_len = subex->len; +if (subex->fsbno < ex->fsbno) { + sub_len -= ex->fsbno - subex->fsbno; + sub_fsb = ex->fsbno; + } +if (sub_len > ex->len) + sub_len = ex->len; + + state =0 ; +if (sub_fsb == ex->fsbno) + state |=XFS_REPAIR_EXT_LEFT_CONTIG ; +if (sub_fsb + sub_len == ex->fsbno + ex->len) + state |=XFS_REPAIR_EXT_RIGHT_CONTIG ; +switch (state) { +case XFS_REPAIR_EXT_LEFT_CONTIG : +/* Coincides with only the left. */ + ex->fsbno += sub_len; + ex->len -= sub_len; +break ; +case XFS_REPAIR_EXT_RIGHT_CONTIG : +/* Coincides with only the right. */ + ex->len -= sub_len; + lp = lp->next; +break ; +case XFS_REPAIR_EXT_LEFT_CONTIG |XFS_REPAIR_EXT_RIGHT_CONTIG : +/* Total overlap, just delete ex. */ + lp = lp->next; +list_del (&ex->list); +kmem_free (ex); +break ; +case 0 : +/* +* Deleting from the middle: add the new right extent +* and then shrink the left extent. +*/ + newex =kmem_alloc (sizeof (struct xfs_repair_extent ), +KM_MAYFAIL ); +if (!newex) { + error = -ENOMEM ; + goto out; + } +INIT_LIST_HEAD (&newex->list); + newex->fsbno = sub_fsb + sub_len; + newex->len = ex->len - (sub_fsb - ex->fsbno) - sub_len; +list_add (&newex->list, &ex->list); + ex->len = sub_fsb - ex->fsbno; + lp = lp->next; +break ; +default : +ASSERT (0 ); +break ; + } + } + +out: +return error; +} +#undef XFS_REPAIR_EXT_LEFT_CONTIG +#undef XFS_REPAIR_EXT_RIGHT_CONTIG
fs/xfs/scrub/repair.h +31 -0
@@ -43,6 +43,37 @@ int xfs_repair_init_btblock(struct xfs_scrub_context *sc, xfs_fsblock_t fsb, struct xfs_buf **bpp, xfs_btnum_t btnum, const struct xfs_buf_ops *ops); +struct xfs_repair_extent { +struct list_head list; +xfs_fsblock_t fsbno; +xfs_extlen_t len; +}; + +struct xfs_repair_extent_list { +struct list_head list; +}; + +static inline void +xfs_repair_init_extent_list ( +struct xfs_repair_extent_list *exlist) +{ +INIT_LIST_HEAD (&exlist->list); +} + +#define for_each_xfs_repair_extent_safe (rbe, n, exlist) \ + list_for_each_entry_safe((rbe), (n), &(exlist)->list, list) +int xfs_repair_collect_btree_extent (struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *btlist,xfs_fsblock_t fsbno, +xfs_extlen_t len); +int xfs_repair_reap_btree_extents (struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *btlist, +struct xfs_owner_info *oinfo,enum xfs_ag_resv_type type); +void xfs_repair_cancel_btree_extents (struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *btlist); +int xfs_repair_subtract_extents (struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_repair_extent_list *sublist); +/* Metadata repairers */ int xfs_repair_probe (struct xfs_scrub_context *sc);
Re: [PATCH 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Quoted 33 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 31 +++++++ 2 files changed, 238 insertions(+)fs/xfs/scrub/repair.c +8
-0@@ -361,3 +361,210 @@ xfs_repair_init_btblock(return 0 ; } + +/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len)
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 381
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+{ +struct xfs_repair_extent *rex; + +trace_xfs_repair_collect_btree_extent (sc->mp, +XFS_FSB_TO_AGNO (sc->mp, fsbno), +XFS_FSB_TO_AGBNO (sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ;
Is this in transaction context? Regardless, I think we need to run the entire of scrub/repair in a memalloc_nofs_save() context so we don't have memory reclaim recursion issues...
[...]
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+/* Compare two btree extents. */ +static int +xfs_repair_btree_extent_cmp ( +void *priv, +struct list_head *a, +struct list_head *b) +{
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 450
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+struct xfs_repair_extent *ap; +struct xfs_repair_extent *bp; + + ap = container_of(a, struct xfs_repair_extent, list); + bp = container_of(b, struct xfs_repair_extent, list); + +if (ap->fsbno > bp->fsbno) +return 1 ; +else if (ap->fsbno < bp->fsbno) +return -1 ;
No need for the else there.
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 458
Quoted 8 lines
fs/xfs/scrub/repair.c +8
-0+return 0 ; +} + +/* +* Remove all the blocks mentioned in sublist from the extents in exlist. +* +* The intent is that callers will iterate the rmapbt for all of its records +* for a given owner to generate exlist; and iterate all the blocks of the
generate @exlist
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 460
Quoted 2 lines
fs/xfs/scrub/repair.c +2
-0+* metadata structures that are not being rebuilt and have the same rmapbt +* owner to generate sublist. This routine subtracts all the extents
generate @sublist.
Quoted 34 lines
fs/xfs/scrub/repair.c +34
-0+* mentioned in sublist from all the extents linked in exlist, which leaves +* exlist as the list of blocks that are not accounted for, which we assume +* are the dead blocks of the old metadata structure. The blocks mentioned in +* exlist can be reaped. +*/ +#define XFS_REPAIR_EXT_LEFT_CONTIG (1 << 0) +#define XFS_REPAIR_EXT_RIGHT_CONTIG (1 << 1) +int +xfs_repair_subtract_extents ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_repair_extent_list *sublist) +{ +struct list_head *lp; +struct xfs_repair_extent *ex; +struct xfs_repair_extent *newex; +struct xfs_repair_extent *subex; +xfs_fsblock_t sub_fsb; +xfs_extlen_t sub_len; +int state; +int error =0 ; + +if (list_empty (&exlist->list) ||list_empty (&sublist->list)) +return 0 ; +ASSERT (!list_empty (&sublist->list)); + +list_sort (NULL , &exlist->list, xfs_repair_btree_extent_cmp); +list_sort (NULL , &sublist->list, xfs_repair_btree_extent_cmp); + + subex = list_first_entry(&sublist->list, struct xfs_repair_extent, + list); + lp = exlist->list.next; +while (lp != &exlist->list) { + ex = list_entry(lp, struct xfs_repair_extent, list);
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 504
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ +/* +* Advance subex and/or ex until we find a pair that +* intersect or we run out of extents. +*/ +while (subex->fsbno + subex->len <= ex->fsbno) { +if (list_is_last (&subex->list, &sublist->list)) + goto out; + subex =list_next_entry (subex, list); + }
So this is a O(n^2) algorithm, right? How does it scale with large extent lists? Given that these extents are dynamically allocated, and we're already burning 16 bytes for a list head on each extent, would it be better to use a smarter structure better suited for exact lookups? e.g. an rbtree only takes an extra 8 bytes per extent, and we get O(log N) searches on the inner loop here...
I guess this isn't necessary to fix right now, but I think it's going to be an issue for maybe mark this down as "needing to be fixed before removing EXPERIMENTAL tags"?
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+if (subex->fsbno >= ex->fsbno + ex->len) { + lp = lp->next; +continue ; + } + +/* trim subex to fit the extent we have */ + sub_fsb = subex->fsbno; + sub_len = subex->len; +if (subex->fsbno < ex->fsbno) { + sub_len -= ex->fsbno - subex->fsbno;
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 524
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ sub_fsb = ex->fsbno; + } +if (sub_len > ex->len) + sub_len = ex->len; + + state =0 ; +if (sub_fsb == ex->fsbno) + state |=XFS_REPAIR_EXT_LEFT_CONTIG ; +if (sub_fsb + sub_len == ex->fsbno + ex->len) + state |=XFS_REPAIR_EXT_RIGHT_CONTIG ;
Ok, I think "CONTIG" is not the right word to use here. In the BMAP btrees, the merge state flags were to tell us whether the edge of the new extent is contiguous with the left and right extents in the tree, not whether the new extents overlapped to the left/right edges.
i.e. we're checking whether extent start/end overlaps are aligned here, not whether they are contiguous with some other extent. So in this case, I'd just name the variables "LEFT_ALIGNED" and "RIGHT_ALIGNED" and drop all the namespace bits from them.
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 527
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+switch (state) { +case XFS_REPAIR_EXT_LEFT_CONTIG : +/* Coincides with only the left. */
And by calling them aligned, the comments become redundant:
case LEFT_ALIGNED:
Quoted 18 lines
fs/xfs/scrub/repair.c +18
-0+ ex->fsbno += sub_len; + ex->len -= sub_len; +break ; +case XFS_REPAIR_EXT_RIGHT_CONTIG : +/* Coincides with only the right. */ + ex->len -= sub_len; + lp = lp->next; +break ; +case XFS_REPAIR_EXT_LEFT_CONTIG |XFS_REPAIR_EXT_RIGHT_CONTIG : +/* Total overlap, just delete ex. */ + lp = lp->next; +list_del (&ex->list); +kmem_free (ex); +break ; +case 0 : +/* +* Deleting from the middle: add the new right extent +* and then shrink the left extent.
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 555
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+*/ + newex =kmem_alloc (sizeof (struct xfs_repair_extent ), +KM_MAYFAIL ); +if (!newex) { + error = -ENOMEM ; + goto out; + } +INIT_LIST_HEAD (&newex->list); + newex->fsbno = sub_fsb + sub_len; + newex->len = ex->len - (sub_fsb - ex->fsbno) - sub_len;
so: new len = old len - (length of first extent) - length of overlap.
I think this is more obvious as "new len = old end - new start", i.e.:
newex->len = ex->fsbno + ex->len - newex->fsbno;
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Ok, with the points Dave made:
Reviewed by: Allison Henderson <[email protected]>
Quoted 60 lines · Dave Chinner
Quoted 33 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 31 +++++++ 2 files changed, 238 insertions(+)fs/xfs/scrub/repair.c +8
-0@@ -361,3 +361,210 @@ xfs_repair_init_btblock(return 0 ; } + +/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len)fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 381
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+{ +struct xfs_repair_extent *rex; + +trace_xfs_repair_collect_btree_extent (sc->mp, +XFS_FSB_TO_AGNO (sc->mp, fsbno), +XFS_FSB_TO_AGBNO (sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ;Is this in transaction context? Regardless, I think we need to run the entire of scrub/repair in a
memalloc_nofs_save()context so we don't have memory reclaim recursion issues...[...]
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+/* Compare two btree extents. */ +static int +xfs_repair_btree_extent_cmp ( +void *priv, +struct list_head *a, +struct list_head *b) +{ +struct xfs_repair_extent *ap; +struct xfs_repair_extent *bp; +
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 450
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+ ap = container_of(a, struct xfs_repair_extent, list); + bp = container_of(b, struct xfs_repair_extent, list); + +if (ap->fsbno > bp->fsbno) +return 1 ; +else if (ap->fsbno < bp->fsbno) +return -1 ;No need for the else there.
Well, I think he meant to return 0 in the case of ap->fsbno == bp->fsbno? Am i reading that right? caller expects 1 for greater than, -1 for less than and 0 on equivalence?
Quoted 143 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 458
Quoted 8 lines
fs/xfs/scrub/repair.c +8
-0+return 0 ; +} + +/* +* Remove all the blocks mentioned in sublist from the extents in exlist. +* +* The intent is that callers will iterate the rmapbt for all of its records +* for a given owner to generate exlist; and iterate all the blocks of the generate @exlist
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 460
Quoted 2 lines
fs/xfs/scrub/repair.c +2
-0+* metadata structures that are not being rebuilt and have the same rmapbt +* owner to generate sublist. This routine subtracts all the extents generate @sublist.
Quoted 34 lines
fs/xfs/scrub/repair.c +34
-0+* mentioned in sublist from all the extents linked in exlist, which leaves +* exlist as the list of blocks that are not accounted for, which we assume +* are the dead blocks of the old metadata structure. The blocks mentioned in +* exlist can be reaped. +*/ +#define XFS_REPAIR_EXT_LEFT_CONTIG (1 << 0) +#define XFS_REPAIR_EXT_RIGHT_CONTIG (1 << 1) +int +xfs_repair_subtract_extents ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_repair_extent_list *sublist) +{ +struct list_head *lp; +struct xfs_repair_extent *ex; +struct xfs_repair_extent *newex; +struct xfs_repair_extent *subex; +xfs_fsblock_t sub_fsb; +xfs_extlen_t sub_len; +int state; +int error =0 ; + +if (list_empty (&exlist->list) ||list_empty (&sublist->list)) +return 0 ; +ASSERT (!list_empty (&sublist->list)); + +list_sort (NULL , &exlist->list, xfs_repair_btree_extent_cmp); +list_sort (NULL , &sublist->list, xfs_repair_btree_extent_cmp); + + subex = list_first_entry(&sublist->list, struct xfs_repair_extent, + list); + lp = exlist->list.next; +while (lp != &exlist->list) { + ex = list_entry(lp, struct xfs_repair_extent, list);fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 504
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ +/* +* Advance subex and/or ex until we find a pair that +* intersect or we run out of extents. +*/ +while (subex->fsbno + subex->len <= ex->fsbno) { +if (list_is_last (&subex->list, &sublist->list)) + goto out; + subex =list_next_entry (subex, list); + }So this is a O(n^2) algorithm, right? How does it scale with large extent lists? Given that these extents are dynamically allocated, and we're already burning 16 bytes for a list head on each extent, would it be better to use a smarter structure better suited for exact lookups? e.g. an rbtree only takes an extra 8 bytes per extent, and we get O(log N) searches on the inner loop here...
I guess this isn't necessary to fix right now, but I think it's going to be an issue for maybe mark this down as "needing to be fixed before removing EXPERIMENTAL tags"?
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+if (subex->fsbno >= ex->fsbno + ex->len) { + lp = lp->next; +continue ; + } + +/* trim subex to fit the extent we have */ + sub_fsb = subex->fsbno; + sub_len = subex->len; +if (subex->fsbno < ex->fsbno) { + sub_len -= ex->fsbno - subex->fsbno;fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 524
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ sub_fsb = ex->fsbno; + } +if (sub_len > ex->len) + sub_len = ex->len; + + state =0 ; +if (sub_fsb == ex->fsbno) + state |=XFS_REPAIR_EXT_LEFT_CONTIG ; +if (sub_fsb + sub_len == ex->fsbno + ex->len) + state |=XFS_REPAIR_EXT_RIGHT_CONTIG ;Ok, I think "CONTIG" is not the right word to use here. In the BMAP btrees, the merge state flags were to tell us whether the edge of the new extent is contiguous with the left and right extents in the tree, not whether the new extents overlapped to the left/right edges.
i.e. we're checking whether extent start/end overlaps are aligned here, not whether they are contiguous with some other extent. So in this case, I'd just name the variables "
LEFT_ALIGNED" and "RIGHT_ALIGNED" and drop all the namespace bits from them.fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 527
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+switch (state) { +case XFS_REPAIR_EXT_LEFT_CONTIG : +/* Coincides with only the left. */ And by calling them aligned, the comments become redundant:
case
LEFT_ALIGNED:Quoted 18 lines
fs/xfs/scrub/repair.c +18
-0+ ex->fsbno += sub_len; + ex->len -= sub_len; +break ; +case XFS_REPAIR_EXT_RIGHT_CONTIG : +/* Coincides with only the right. */ + ex->len -= sub_len; + lp = lp->next; +break ; +case XFS_REPAIR_EXT_LEFT_CONTIG |XFS_REPAIR_EXT_RIGHT_CONTIG : +/* Total overlap, just delete ex. */ + lp = lp->next; +list_del (&ex->list); +kmem_free (ex); +break ; +case 0 : +/* +* Deleting from the middle: add the new right extent +* and then shrink the left extent. fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 555
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+*/ + newex =kmem_alloc (sizeof (struct xfs_repair_extent ), +KM_MAYFAIL ); +if (!newex) { + error = -ENOMEM ; + goto out; + } +INIT_LIST_HEAD (&newex->list); + newex->fsbno = sub_fsb + sub_len; + newex->len = ex->len - (sub_fsb - ex->fsbno) - sub_len;so: new len = old len - (length of first extent) - length of overlap.
I think this is more obvious as "new len = old end - new start", i.e.:
newex->len = ex->fsbno + ex->len - newex->fsbno;Cheers,
Dave.
Re: [PATCH 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Quoted 71 lines · Allison Henderson
Ok, with the points Dave made:
Reviewed by: Allison Henderson <[email protected]>Quoted 59 lines · Dave Chinner
Quoted 32 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 31 +++++++ 2 files changed, 238 insertions(+)fs/xfs/scrub/repair.c +8
-0@@ -361,3 +361,210 @@ xfs_repair_init_btblock(return 0 ; } + +/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len)fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 380
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+{ +struct xfs_repair_extent *rex; + +trace_xfs_repair_collect_btree_extent (sc->mp, +XFS_FSB_TO_AGNO (sc->mp, fsbno), +XFS_FSB_TO_AGBNO (sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ;Is this in transaction context? Regardless, I think we need to run the entire of scrub/repair in a
memalloc_nofs_save()context so we don't have memory reclaim recursion issues...[...]
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+/* Compare two btree extents. */ +static int +xfs_repair_btree_extent_cmp ( +void *priv, +struct list_head *a, +struct list_head *b) +{ +struct xfs_repair_extent *ap; +struct xfs_repair_extent *bp; +Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 450
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+ ap = container_of(a, struct xfs_repair_extent, list); + bp = container_of(b, struct xfs_repair_extent, list); + +if (ap->fsbno > bp->fsbno) +return 1 ; +else if (ap->fsbno < bp->fsbno) +return -1 ;No need for the else there.
Quoted 3 lines
Well, I think he meant to return 0 in the case of
ap->fsbno==bp->fsbno? Am i reading that right? caller expects 1 for greater than, -1 for less than and 0 on equivalence?
Correct. I think Dave was pointing out that else-after-return is unnecessary, since the following behaves equivalently:
if (a > b)
return 1; if (a < b) return -1;
return 0;
Note that gcc (7.3, anyway) generates the same asm for either version so I assume this is mostly stylistic cleanup to make the comparisons line up? I don't have a preference either way. :)
--D
Quoted 148 lines
Quoted 136 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 458
Quoted 8 lines
fs/xfs/scrub/repair.c +8
-0+return 0 ; +} + +/* +* Remove all the blocks mentioned in sublist from the extents in exlist. +* +* The intent is that callers will iterate the rmapbt for all of its records +* for a given owner to generate exlist; and iterate all the blocks of the generate @exlist
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 460
Quoted 2 lines
fs/xfs/scrub/repair.c +2
-0+* metadata structures that are not being rebuilt and have the same rmapbt +* owner to generate sublist. This routine subtracts all the extents generate @sublist.
Quoted 34 lines
fs/xfs/scrub/repair.c +34
-0+* mentioned in sublist from all the extents linked in exlist, which leaves +* exlist as the list of blocks that are not accounted for, which we assume +* are the dead blocks of the old metadata structure. The blocks mentioned in +* exlist can be reaped. +*/ +#define XFS_REPAIR_EXT_LEFT_CONTIG (1 << 0) +#define XFS_REPAIR_EXT_RIGHT_CONTIG (1 << 1) +int +xfs_repair_subtract_extents ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_repair_extent_list *sublist) +{ +struct list_head *lp; +struct xfs_repair_extent *ex; +struct xfs_repair_extent *newex; +struct xfs_repair_extent *subex; +xfs_fsblock_t sub_fsb; +xfs_extlen_t sub_len; +int state; +int error =0 ; + +if (list_empty (&exlist->list) ||list_empty (&sublist->list)) +return 0 ; +ASSERT (!list_empty (&sublist->list)); + +list_sort (NULL , &exlist->list, xfs_repair_btree_extent_cmp); +list_sort (NULL , &sublist->list, xfs_repair_btree_extent_cmp); + + subex = list_first_entry(&sublist->list, struct xfs_repair_extent, + list); + lp = exlist->list.next; +while (lp != &exlist->list) { + ex = list_entry(lp, struct xfs_repair_extent, list);fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 504
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ +/* +* Advance subex and/or ex until we find a pair that +* intersect or we run out of extents. +*/ +while (subex->fsbno + subex->len <= ex->fsbno) { +if (list_is_last (&subex->list, &sublist->list)) + goto out; + subex =list_next_entry (subex, list); + }So this is a O(n^2) algorithm, right? How does it scale with large extent lists? Given that these extents are dynamically allocated, and we're already burning 16 bytes for a list head on each extent, would it be better to use a smarter structure better suited for exact lookups? e.g. an rbtree only takes an extra 8 bytes per extent, and we get O(log N) searches on the inner loop here...
I guess this isn't necessary to fix right now, but I think it's going to be an issue for maybe mark this down as "needing to be fixed before removing EXPERIMENTAL tags"?
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+if (subex->fsbno >= ex->fsbno + ex->len) { + lp = lp->next; +continue ; + } + +/* trim subex to fit the extent we have */ + sub_fsb = subex->fsbno; + sub_len = subex->len; +if (subex->fsbno < ex->fsbno) { + sub_len -= ex->fsbno - subex->fsbno;fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 524
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ sub_fsb = ex->fsbno; + } +if (sub_len > ex->len) + sub_len = ex->len; + + state =0 ; +if (sub_fsb == ex->fsbno) + state |=XFS_REPAIR_EXT_LEFT_CONTIG ; +if (sub_fsb + sub_len == ex->fsbno + ex->len) + state |=XFS_REPAIR_EXT_RIGHT_CONTIG ;Ok, I think "CONTIG" is not the right word to use here. In the BMAP btrees, the merge state flags were to tell us whether the edge of the new extent is contiguous with the left and right extents in the tree, not whether the new extents overlapped to the left/right edges.
i.e. we're checking whether extent start/end overlaps are aligned here, not whether they are contiguous with some other extent. So in this case, I'd just name the variables "
LEFT_ALIGNED" and "RIGHT_ALIGNED" and drop all the namespace bits from them.fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 527
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+switch (state) { +case XFS_REPAIR_EXT_LEFT_CONTIG : +/* Coincides with only the left. */ And by calling them aligned, the comments become redundant:
case
LEFT_ALIGNED:Quoted 18 lines
fs/xfs/scrub/repair.c +18
-0+ ex->fsbno += sub_len; + ex->len -= sub_len; +break ; +case XFS_REPAIR_EXT_RIGHT_CONTIG : +/* Coincides with only the right. */ + ex->len -= sub_len; + lp = lp->next; +break ; +case XFS_REPAIR_EXT_LEFT_CONTIG |XFS_REPAIR_EXT_RIGHT_CONTIG : +/* Total overlap, just delete ex. */ + lp = lp->next; +list_del (&ex->list); +kmem_free (ex); +break ; +case 0 : +/* +* Deleting from the middle: add the new right extent +* and then shrink the left extent. fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 555
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+*/ + newex =kmem_alloc (sizeof (struct xfs_repair_extent ), +KM_MAYFAIL ); +if (!newex) { + error = -ENOMEM ; + goto out; + } +INIT_LIST_HEAD (&newex->list); + newex->fsbno = sub_fsb + sub_len; + newex->len = ex->len - (sub_fsb - ex->fsbno) - sub_len;so: new len = old len - (length of first extent) - length of overlap.
Quoted 7 lines
I think this is more obvious as "new len = old end - new start", i.e.:
newex->len = ex->fsbno + ex->len - newex->fsbno;Cheers,
Dave.
--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Quoted 80 lines · Darrick J. Wong
Quoted 71 lines · Allison Henderson
Ok, with the points Dave made:
Reviewed by: Allison Henderson <[email protected]>Quoted 59 lines · Dave Chinner
Quoted 32 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 31 +++++++ 2 files changed, 238 insertions(+)fs/xfs/scrub/repair.c +8
-0@@ -361,3 +361,210 @@ xfs_repair_init_btblock(return 0 ; } + +/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len)fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 380
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+{ +struct xfs_repair_extent *rex; + +trace_xfs_repair_collect_btree_extent (sc->mp, +XFS_FSB_TO_AGNO (sc->mp, fsbno), +XFS_FSB_TO_AGBNO (sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ;Is this in transaction context? Regardless, I think we need to run the entire of scrub/repair in a
memalloc_nofs_save()context so we don't have memory reclaim recursion issues...[...]
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+/* Compare two btree extents. */ +static int +xfs_repair_btree_extent_cmp ( +void *priv, +struct list_head *a, +struct list_head *b) +{ +struct xfs_repair_extent *ap; +struct xfs_repair_extent *bp; +Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 450
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+ ap = container_of(a, struct xfs_repair_extent, list); + bp = container_of(b, struct xfs_repair_extent, list); + +if (ap->fsbno > bp->fsbno) +return 1 ; +else if (ap->fsbno < bp->fsbno) +return -1 ;No need for the else there.
Quoted 3 lines
Well, I think he meant to return 0 in the case of
ap->fsbno==bp->fsbno? Am i reading that right? caller expects 1 for greater than, -1 for less than and 0 on equivalence?Correct. I think Dave was pointing out that else-after-return is unnecessary, since the following behaves equivalently:
if (a > b)
Quoted 8 lines
return 1; if (a < b) return -1;return 0;
Note that gcc (7.3, anyway) generates the same asm for either version so I assume this is mostly stylistic cleanup to make the comparisons line up? I don't have a preference either way. :)
It's a style and maintenance thing. The else is redundant and could lead to future confusion, so IMO it is best to leave it out....
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Quoted 90 lines · Dave Chinner
Quoted 80 lines · Darrick J. Wong
Quoted 71 lines · Allison Henderson
Ok, with the points Dave made:
Reviewed by: Allison Henderson <[email protected]>Quoted 59 lines · Dave Chinner
Quoted 32 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 31 +++++++ 2 files changed, 238 insertions(+)fs/xfs/scrub/repair.c +8
-0@@ -361,3 +361,210 @@ xfs_repair_init_btblock(return 0 ; } + +/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len)fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 380
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+{ +struct xfs_repair_extent *rex; + +trace_xfs_repair_collect_btree_extent (sc->mp, +XFS_FSB_TO_AGNO (sc->mp, fsbno), +XFS_FSB_TO_AGBNO (sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ;Is this in transaction context? Regardless, I think we need to run the entire of scrub/repair in a
memalloc_nofs_save()context so we don't have memory reclaim recursion issues...[...]
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+/* Compare two btree extents. */ +static int +xfs_repair_btree_extent_cmp ( +void *priv, +struct list_head *a, +struct list_head *b) +{ +struct xfs_repair_extent *ap; +struct xfs_repair_extent *bp; +Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 450
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+ ap = container_of(a, struct xfs_repair_extent, list); + bp = container_of(b, struct xfs_repair_extent, list); + +if (ap->fsbno > bp->fsbno) +return 1 ; +else if (ap->fsbno < bp->fsbno) +return -1 ;No need for the else there.
Quoted 3 lines
Well, I think he meant to return 0 in the case of
ap->fsbno==bp->fsbno? Am i reading that right? caller expects 1 for greater than, -1 for less than and 0 on equivalence?Correct. I think Dave was pointing out that else-after-return is unnecessary, since the following behaves equivalently:
if (a > b)
Quoted 8 lines
return 1; if (a < b) return -1;return 0;
Note that gcc (7.3, anyway) generates the same asm for either version so I assume this is mostly stylistic cleanup to make the comparisons line up? I don't have a preference either way. :)
Quoted 7 lines
It's a style and maintenance thing. The else is redundant and could lead to future confusion, so IMO it is best to leave it out....
Cheers,
Dave.
Alrighty then, thx for the clarification! :-)
Allison
Re: [PATCH 03/22] xfs: add helpers to collect and sift btree block pointers during repair
In reply to Dave ChinnerQuoted 38 lines · Dave Chinner
Quoted 37 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 31 +++++++ 2 files changed, 238 insertions(+)fs/xfs/scrub/repair.c +12
-0@@ -361,3 +361,210 @@ xfs_repair_init_btblock(return 0 ; } + +/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len) +{ +struct xfs_repair_extent *rex; + + trace_xfs_repair_collect_btree_extent(sc->mp,
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 381
Quoted 6 lines
fs/xfs/scrub/repair.c +6
-0+XFS_FSB_TO_AGNO (sc->mp, fsbno), + XFS_FSB_TO_AGBNO(sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ;Is this in transaction context?
Yes. After the setup function finishes we're required to own a transaction and hold a lock on whatever resource we're playing with.
Quoted 3 lines
Regardless, I think we need to run the entire of scrub/repair in a
memalloc_nofs_save()context so we don't have memory reclaim recursion issues...
xfs_trans_reserve should take care of this, right? So we don't have to feed KM_NOFS to kmem_*_alloc because this is already taken care of. The MAYFAIL exists because we prefer ENOMEM'ing out to pushing on reclaim.
Quoted 14 lines
[...]
Quoted 11 lines
fs/xfs/scrub/repair.c +11
-0+/* Compare two btree extents. */ +static int +xfs_repair_btree_extent_cmp ( +void *priv, +struct list_head *a, +struct list_head *b) +{ +struct xfs_repair_extent *ap; +struct xfs_repair_extent *bp; + + ap = container_of(a, struct xfs_repair_extent, list);
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 450
Quoted 6 lines
fs/xfs/scrub/repair.c +6
-0+ bp = container_of(b, struct xfs_repair_extent, list); + +if (ap->fsbno > bp->fsbno) +return 1 ; +else if (ap->fsbno < bp->fsbno) +return -1 ;No need for the else there.
Fixed.
Quoted 11 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 458
Quoted 8 lines
fs/xfs/scrub/repair.c +8
-0+return 0 ; +} + +/* +* Remove all the blocks mentioned in sublist from the extents in exlist. +* +* The intent is that callers will iterate the rmapbt for all of its records +* for a given owner to generate exlist; and iterate all the blocks of the generate @exlist
Quoted 4 lines
Quoted 2 lines
+ * metadata structures that are not being rebuilt and have the same rmapbt
+ * owner to generate sublist. This routine subtracts all the extentsgenerate @sublist.
Fixed both.
Quoted 39 lines
Quoted 39 lines
+ * mentioned in sublist from all the extents linked in exlist, which leaves
+ * exlist as the list of blocks that are not accounted for, which we assume
+ * are the dead blocks of the old metadata structure. The blocks mentioned in
+ * exlist can be reaped.
+ */fs/xfs/scrub/repair.c +34
-0+#define XFS_REPAIR_EXT_LEFT_CONTIG (1 << 0) +#define XFS_REPAIR_EXT_RIGHT_CONTIG (1 << 1) +int +xfs_repair_subtract_extents ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_repair_extent_list *sublist) +{ +struct list_head *lp; +struct xfs_repair_extent *ex; +struct xfs_repair_extent *newex; +struct xfs_repair_extent *subex; +xfs_fsblock_t sub_fsb; +xfs_extlen_t sub_len; +int state; +int error =0 ; + +if (list_empty (&exlist->list) ||list_empty (&sublist->list)) +return 0 ; +ASSERT (!list_empty (&sublist->list)); + +list_sort (NULL , &exlist->list, xfs_repair_btree_extent_cmp); +list_sort (NULL , &sublist->list, xfs_repair_btree_extent_cmp); + + subex = list_first_entry(&sublist->list, struct xfs_repair_extent, + list); + lp = exlist->list.next; +while (lp != &exlist->list) { + ex = list_entry(lp, struct xfs_repair_extent, list); + +/* +* Advance subex and/or ex until we find a pair that +* intersect or we run out of extents. +*/
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 504
Quoted 5 lines
fs/xfs/scrub/repair.c +5
-0+while (subex->fsbno + subex->len <= ex->fsbno) { +if (list_is_last (&subex->list, &sublist->list)) + goto out; + subex =list_next_entry (subex, list); + }So this is a O(n^2) algorithm, right? How does it scale with large extent lists?
I don't think this is O(n^2) -- each list sort is O(n log n). Then we iterate exlist once, rolling forward through sublist as necessary. We never reset lp to the head of exlist nor do we reset subex to the head of sublist. We're not performing random lookups on the sublist extents at all.
So far I haven't noticed /much/ heat from this routine even with deliberately aged filesystems, but that's probably due to the slab allocator eating way more time. :(
Quoted 9 lines
Given that these extents are dynamically allocated, and we're already burning 16 bytes for a list head on each extent, would it be better to use a smarter structure better suited for exact lookups? e.g. an rbtree only takes an extra 8 bytes per extent, and we get O(log N) searches on the inner loop here...
I guess this isn't necessary to fix right now, but I think it's going to be an issue for maybe mark this down as "needing to be fixed before removing EXPERIMENTAL tags"?
I've thought about converting this to an rbtree or something, since these are basically glorified bitmap operations. Set all the bits corresponding to this rmap owner; set all the bits corresponding to blocks with the same rmap owner but owned by the other data structures sharing the rmap owner; then free anything in (ex & ~sub) because those are the old btree blocks. It's on my list for optimization, but first I want to get this thing working correctly.
TBH the other thing that irks me about the current orepair design is its heavy reliance on creating potentially huge linked lists of the records that need to be put into the new structure. I'd really like a data structure where I can do fast unsorted appends without list overhead, sort the data structure once, and then insert in sorted order. The slab thing I put into xfs_repair provides this, but we can't easily allocate 128M of space in the kernel. I also want to do a bulk load of an empty a btree leaf so that we log the leaf block once and update the node pointers once, kind of like what xfs_repair does during phase 5.
...all this optimization can come after merging.
Quoted 32 lines
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+if (subex->fsbno >= ex->fsbno + ex->len) { + lp = lp->next; +continue ; + } + +/* trim subex to fit the extent we have */ + sub_fsb = subex->fsbno; + sub_len = subex->len; +if (subex->fsbno < ex->fsbno) { + sub_len -= ex->fsbno - subex->fsbno;fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 524
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ sub_fsb = ex->fsbno; + } +if (sub_len > ex->len) + sub_len = ex->len; + + state =0 ; +if (sub_fsb == ex->fsbno) + state |=XFS_REPAIR_EXT_LEFT_CONTIG ; +if (sub_fsb + sub_len == ex->fsbno + ex->len) + state |=XFS_REPAIR_EXT_RIGHT_CONTIG ;Ok, I think "CONTIG" is not the right word to use here. In the BMAP btrees, the merge state flags were to tell us whether the edge of the new extent is contiguous with the left and right extents in the tree, not whether the new extents overlapped to the left/right edges.
i.e. we're checking whether extent start/end overlaps are aligned here, not whether they are contiguous with some other extent. So in this case, I'd just name the variables "
LEFT_ALIGNED" and "RIGHT_ALIGNED" and drop all the namespace bits from them.
Quoted 5 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 527
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+switch (state) { +case XFS_REPAIR_EXT_LEFT_CONTIG : +/* Coincides with only the left. */ And by calling them aligned, the comments become redundant:
Fixed.
Quoted 32 lines
case
LEFT_ALIGNED:Quoted 18 lines
fs/xfs/scrub/repair.c +18
-0+ ex->fsbno += sub_len; + ex->len -= sub_len; +break ; +case XFS_REPAIR_EXT_RIGHT_CONTIG : +/* Coincides with only the right. */ + ex->len -= sub_len; + lp = lp->next; +break ; +case XFS_REPAIR_EXT_LEFT_CONTIG |XFS_REPAIR_EXT_RIGHT_CONTIG : +/* Total overlap, just delete ex. */ + lp = lp->next; +list_del (&ex->list); +kmem_free (ex); +break ; +case 0 : +/* +* Deleting from the middle: add the new right extent +* and then shrink the left extent. fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 555
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+*/ + newex =kmem_alloc (sizeof (struct xfs_repair_extent ), +KM_MAYFAIL ); +if (!newex) { + error = -ENOMEM ; + goto out; + } +INIT_LIST_HEAD (&newex->list); + newex->fsbno = sub_fsb + sub_len; + newex->len = ex->len - (sub_fsb - ex->fsbno) - sub_len;so: new len = old len - (length of first extent) - length of overlap.
Quoted 3 lines
I think this is more obvious as "new len = old end - new start", i.e.:
newex->len = ex->fsbno + ex->len - newex->fsbno;
Yep. Fixed.
--D
Quoted 11 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Quoted 51 lines · Darrick J. Wong
Quoted 38 lines · Dave Chinner
Quoted 37 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 31 +++++++ 2 files changed, 238 insertions(+)fs/xfs/scrub/repair.c +12
-0@@ -361,3 +361,210 @@ xfs_repair_init_btblock(return 0 ; } + +/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len) +{ +struct xfs_repair_extent *rex; + + trace_xfs_repair_collect_btree_extent(sc->mp,Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 381
Quoted 6 lines
fs/xfs/scrub/repair.c +6
-0+XFS_FSB_TO_AGNO (sc->mp, fsbno), + XFS_FSB_TO_AGBNO(sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ;Is this in transaction context?
Yes. After the setup function finishes we're required to own a transaction and hold a lock on whatever resource we're playing with.
Quoted 7 lines
Quoted 3 lines
Regardless, I think we need to run the entire of scrub/repair in a
memalloc_nofs_save()context so we don't have memory reclaim recursion issues...
xfs_trans_reserveshould take care of this, right? So we don't have to feedKM_NOFSto kmem_*_alloc because this is already taken care of. The MAYFAIL exists because we prefer ENOMEM'ing out to pushing on reclaim.
Right, if we have an active transaction we are under NOFS allocation conditions. I'm jus tnot sure how much of scrub/repair is covered by the transaction context (too early in the morning to go code spelunking!).
w.r.t reclaim, NOFS allocations will still push on reclaim - NOFS just means it won't push on any dirty file pages or scan/reclaim filesystem caches.
Quoted 16 lines
Quoted 7 lines
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+while (lp != &exlist->list) { + ex = list_entry(lp, struct xfs_repair_extent, list); + +/* +* Advance subex and/or ex until we find a pair that +* intersect or we run out of extents. +*/ Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 504
Quoted 5 lines
fs/xfs/scrub/repair.c +5
-0+while (subex->fsbno + subex->len <= ex->fsbno) { +if (list_is_last (&subex->list, &sublist->list)) + goto out; + subex =list_next_entry (subex, list); + }So this is a O(n^2) algorithm, right? How does it scale with large extent lists?
Quoted 1 line
I don't think this is O(n^2) -- each list sort is O(n log n).
I'm not worried about the list sort. :)
Quoted 5 lines
Then we
iterate exlist once, rolling forward through sublist as necessary. We never reset lp to the head of exlist nor do we reset subex to the head of sublist. We're not performing random lookups on the sublist extents at all.
Ah, I missed the fact the loop doesn't reset the subex list to the start for each ex entry. Perhaps a better comment explaining the way the algorithm steps through both lists?
Quoted 3 lines
So far I haven't noticed /much/ heat from this routine even with deliberately aged filesystems, but that's probably due to the slab allocator eating way more time. :(
Perhaps it is worth looking at using named slab caches for some of these objects to take some heat off of the heap slabs?
Quoted 6 lines
Quoted 6 lines
Given that these extents are dynamically allocated, and we're already burning 16 bytes for a list head on each extent, would it be better to use a smarter structure better suited for exact lookups? e.g. an rbtree only takes an extra 8 bytes per extent, and we get O(log N) searches on the inner loop here...
Quoted 6 lines
Quoted 3 lines
I guess this isn't necessary to fix right now, but I think it's going to be an issue for maybe mark this down as "needing to be fixed before removing EXPERIMENTAL tags"?
I've thought about converting this to an rbtree or something, since these are basically glorified bitmap operations.
You can probably ignore that because I was thinking it was a full subex list search for each ex, which is not the case...
Quoted 11 lines
TBH the other thing that irks me about the current orepair design is its heavy reliance on creating potentially huge linked lists of the records that need to be put into the new structure. I'd really like a data structure where I can do fast unsorted appends without list overhead, sort the data structure once, and then insert in sorted order. The slab thing I put into
xfs_repairprovides this, but we can't easily allocate 128M of space in the kernel. I also want to do a bulk load of an empty a btree leaf so that we log the leaf block once and update the node pointers once, kind of like whatxfs_repairdoes during phase 5.
Quoted 1 line
...all this optimization can come after merging.
*nod*.
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Quoted 56 lines · Dave Chinner
Quoted 55 lines · Darrick J. Wong
Quoted 38 lines · Dave Chinner
Quoted 37 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 207 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 31 +++++++ 2 files changed, 238 insertions(+)fs/xfs/scrub/repair.c +12
-0@@ -361,3 +361,210 @@ xfs_repair_init_btblock(return 0 ; } + +/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len) +{ +struct xfs_repair_extent *rex; + + trace_xfs_repair_collect_btree_extent(sc->mp,Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 381
Quoted 6 lines
fs/xfs/scrub/repair.c +6
-0+XFS_FSB_TO_AGNO (sc->mp, fsbno), + XFS_FSB_TO_AGBNO(sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ;Is this in transaction context?
Yes. After the setup function finishes we're required to own a transaction and hold a lock on whatever resource we're playing with.
Quoted 3 lines
Regardless, I think we need to run the entire of scrub/repair in a
memalloc_nofs_save()context so we don't have memory reclaim recursion issues...
Quoted 8 lines
Quoted 3 lines
xfs_trans_reserveshould take care of this, right? So we don't have to feedKM_NOFSto kmem_*_alloc because this is already taken care of. The MAYFAIL exists because we prefer ENOMEM'ing out to pushing on reclaim.Right, if we have an active transaction we are under NOFS allocation conditions. I'm jus tnot sure how much of scrub/repair is covered by the transaction context (too early in the morning to go code spelunking!).
Everything that runs between ->setup and xfs_scrub_teardown is covered by transaction context.
Quoted 3 lines
w.r.t reclaim, NOFS allocations will still push on reclaim - NOFS just means it won't push on any dirty file pages or scan/reclaim filesystem caches.
Noted. I was assuming that NOFS|NOIO meant it would only try reclaim in other parts of the kernel, it sounds like we're ok here.
Quoted 26 lines
Quoted 16 lines
Quoted 7 lines
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+while (lp != &exlist->list) { + ex = list_entry(lp, struct xfs_repair_extent, list); + +/* +* Advance subex and/or ex until we find a pair that +* intersect or we run out of extents. +*/ Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 504
Quoted 5 lines
fs/xfs/scrub/repair.c +5
-0+while (subex->fsbno + subex->len <= ex->fsbno) { +if (list_is_last (&subex->list, &sublist->list)) + goto out; + subex =list_next_entry (subex, list); + }So this is a O(n^2) algorithm, right? How does it scale with large extent lists?
Quoted 1 line
I don't think this is O(n^2) -- each list sort is O(n log n).
I'm not worried about the list sort. :)
Quoted 5 lines
Then we
iterate exlist once, rolling forward through sublist as necessary. We never reset lp to the head of exlist nor do we reset subex to the head of sublist. We're not performing random lookups on the sublist extents at all.
Quoted 3 lines
Ah, I missed the fact the loop doesn't reset the subex list to the start for each ex entry. Perhaps a better comment explaining the way the algorithm steps through both lists?
Ok:
/*
* Now that we've sorted both lists, we iterate exlist once, rolling
* forward through sublist and/or exlist as necessary until we find an
* overlap or reach the end of either list. We do not reset lp to the
* head of exlist nor do we reset subex to the head of sublist. The
* list traversal is similar to merge sort, but we're deleting instead.
* In this manner we avoid O(n^2) operations.
*/
Quoted 6 lines
Quoted 3 lines
So far I haven't noticed /much/ heat from this routine even with deliberately aged filesystems, but that's probably due to the slab allocator eating way more time. :(
Perhaps it is worth looking at using named slab caches for some of these objects to take some heat off of the heap slabs?
Early versions of repair actually did that to try to avoid fragmenting the power-of-2 slabs by using named slab caches, but slub emits udev events whenever a named slab is created, and the resulting horrible cacophony of udev rule processing ate an entire CPU core and uncovered deadlocks in the parts of the slab management code that manage /sys/kernel/slab. That code /really/ does not like you creating and removing slabs concurrently.
As part of re-evaluating how orepair stores in-core records I will take another pass at reducing the efficiency problems.
--D
Quoted 40 lines
Quoted 6 lines
Quoted 6 lines
Given that these extents are dynamically allocated, and we're already burning 16 bytes for a list head on each extent, would it be better to use a smarter structure better suited for exact lookups? e.g. an rbtree only takes an extra 8 bytes per extent, and we get O(log N) searches on the inner loop here...
Quoted 6 lines
Quoted 3 lines
I guess this isn't necessary to fix right now, but I think it's going to be an issue for maybe mark this down as "needing to be fixed before removing EXPERIMENTAL tags"?
I've thought about converting this to an rbtree or something, since these are basically glorified bitmap operations.
You can probably ignore that because I was thinking it was a full subex list search for each ex, which is not the case...
Quoted 11 lines
TBH the other thing that irks me about the current orepair design is its heavy reliance on creating potentially huge linked lists of the records that need to be put into the new structure. I'd really like a data structure where I can do fast unsorted appends without list overhead, sort the data structure once, and then insert in sorted order. The slab thing I put into
xfs_repairprovides this, but we can't easily allocate 128M of space in the kernel. I also want to do a bulk load of an empty a btree leaf so that we log the leaf block once and update the node pointers once, kind of like whatxfs_repairdoes during phase 5.Quoted 1 line
...all this optimization can come after merging.
*nod*.
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Quoted 6 lines · Darrick J. Wong
Quoted 5 lines · Dave Chinner
Quoted 3 lines · Darrick J. Wong
So far I haven't noticed /much/ heat from this routine even with deliberately aged filesystems, but that's probably due to the slab allocator eating way more time. :(
Quoted 6 lines
Quoted 2 lines
Perhaps it is worth looking at using named slab caches for some of these objects to take some heat off of the heap slabs?
Early versions of repair actually did that to try to avoid fragmenting the power-of-2 slabs by using named slab caches, but slub emits udev events whenever a named slab is created, and the resulting horrible
Really? That's so gross!
Anyway, WTF are udev events on internal kernel slab cache creation needed for?
Quoted 4 lines
cacophony of udev rule processing ate an entire CPU core and uncovered deadlocks in the parts of the slab management code that manage
/sys/kernel/slab. That code /really/ does not like you creating and removing slabs concurrently.
I wasn't suggesting dynamically creating slab caches during a repair operation, just have one for the structure type created when we first initialise the XFS kernel module like we do for xfs_buf, xfs_inode, etc.
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Quoted 10 lines · Dave Chinner
Quoted 9 lines · Darrick J. Wong
Quoted 7 lines · Dave Chinner
Quoted 3 lines · Darrick J. Wong
So far I haven't noticed /much/ heat from this routine even with deliberately aged filesystems, but that's probably due to the slab allocator eating way more time. :(
Perhaps it is worth looking at using named slab caches for some of these objects to take some heat off of the heap slabs?
Quoted 8 lines
Quoted 3 lines
Early versions of repair actually did that to try to avoid fragmenting the power-of-2 slabs by using named slab caches, but slub emits udev events whenever a named slab is created, and the resulting horrible
Really? That's so gross!
Anyway, WTF are udev events on internal kernel slab cache creation needed for?
No idea. slab and slob don't do it, just slub.
Quoted 9 lines
Quoted 4 lines
cacophony of udev rule processing ate an entire CPU core and uncovered deadlocks in the parts of the slab management code that manage
/sys/kernel/slab. That code /really/ does not like you creating and removing slabs concurrently.I wasn't suggesting dynamically creating slab caches during a repair operation, just have one for the structure type created when we first initialise the XFS kernel module like we do for
xfs_buf,xfs_inode, etc.
I know, I was abusing slab caches so that I could just dump everything at the end by deleting the slab. :P
--D
Quoted 10 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v2 3/22 xfs: add helpers to collect and sift btree block pointers during repair
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
---
v2: document the design of how repair functions are supposed to work,
implement review comments
--- fs/xfs/scrub/repair.c | 217 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 28 ++++++ 2 files changed, 245 insertions(+)
fs/xfs/scrub/repair.c +217 -0
@@ -368,3 +368,220 @@ xfs_repair_init_btblock(return 0 ; } + +/* +* Reconstructing per-AG Btrees +* +* When a space btree is corrupt, we don't bother trying to fix it. Instead, +* we scan secondary space metadata to derive the records that should be in +* the damaged btree, initialize a fresh btree root, and insert the records. +* Note that for rebuilding the rmapbt we scan all the primary data to +* generate the new records. +* +* However, that leaves the matter of removing all the metadata describing the +* old broken structure. For primary metadata we use the rmap data to collect +* every extent with a matching rmap owner (exlist); we then iterate all other +* metadata structures with the same rmap owner to collect the extents that +* cannot be removed (sublist). We then subtract sublist from exlist to +* derive the blocks that were used by the old btree. These blocks can be +* reaped. +* +* For rmapbt reconstructions we must use different tactics for extent +* collection. First we iterate all primary metadata (this excludes the old +* rmapbt, obviously) to generate new rmap records. The gaps in the rmap +* records are collected as exlist. The bnobt records are collected as +* sublist. As with the other btrees we subtract sublist from exlist, and the +* result (since the rmapbt lives in the free space) are the blocks from the +* old rmapbt. +*/ + +/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len) +{ +struct xfs_repair_extent *rex; + +trace_xfs_repair_collect_btree_extent (sc->mp, +XFS_FSB_TO_AGNO (sc->mp, fsbno), +XFS_FSB_TO_AGBNO (sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ; + +INIT_LIST_HEAD (&rex->list); + rex->fsbno = fsbno; + rex->len = len; +list_add_tail (&rex->list, &exlist->list); + +return 0 ; +} + +/* +* An error happened during the rebuild so the transaction will be cancelled. +* The fs will shut down, and the administrator has to unmount and run repair. +* Therefore, free all the memory associated with the list so we can die. +*/ +void +xfs_repair_cancel_btree_extents ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; + +for_each_xfs_repair_extent_safe (rex, n, exlist) { +list_del (&rex->list); +kmem_free (rex); + } +} + +/* Compare two btree extents. */ +static int +xfs_repair_btree_extent_cmp ( +void *priv, +struct list_head *a, +struct list_head *b) +{ +struct xfs_repair_extent *ap; +struct xfs_repair_extent *bp; + + ap = container_of(a, struct xfs_repair_extent, list); + bp = container_of(b, struct xfs_repair_extent, list); + +if (ap->fsbno > bp->fsbno) +return 1 ; +if (ap->fsbno < bp->fsbno) +return -1 ; +return 0 ; +} + +/* +* Remove all the blocks mentioned in @sublist from the extents in @exlist. +* +* The intent is that callers will iterate the rmapbt for all of its records +* for a given owner to generate @exlist; and iterate all the blocks of the +* metadata structures that are not being rebuilt and have the same rmapbt +* owner to generate @sublist. This routine subtracts all the extents +* mentioned in sublist from all the extents linked in @exlist, which leaves +* @exlist as the list of blocks that are not accounted for, which we assume +* are the dead blocks of the old metadata structure. The blocks mentioned in +* @exlist can be reaped. +*/ +#define LEFT_ALIGNED (1 << 0) +#define RIGHT_ALIGNED (1 << 1) +int +xfs_repair_subtract_extents ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_repair_extent_list *sublist) +{ +struct list_head *lp; +struct xfs_repair_extent *ex; +struct xfs_repair_extent *newex; +struct xfs_repair_extent *subex; +xfs_fsblock_t sub_fsb; +xfs_extlen_t sub_len; +int state; +int error =0 ; + +if (list_empty (&exlist->list) ||list_empty (&sublist->list)) +return 0 ; +ASSERT (!list_empty (&sublist->list)); + +list_sort (NULL , &exlist->list, xfs_repair_btree_extent_cmp); +list_sort (NULL , &sublist->list, xfs_repair_btree_extent_cmp); + +/* +* Now that we've sorted both lists, we iterate exlist once, rolling +* forward through sublist and/or exlist as necessary until we find an +* overlap or reach the end of either list. We do not reset lp to the +* head of exlist nor do we reset subex to the head of sublist. The +* list traversal is similar to merge sort, but we're deleting +* instead. In this manner we avoid O(n^2) operations. +*/ + subex = list_first_entry(&sublist->list, struct xfs_repair_extent, + list); + lp = exlist->list.next; +while (lp != &exlist->list) { + ex = list_entry(lp, struct xfs_repair_extent, list); + +/* +* Advance subex and/or ex until we find a pair that +* intersect or we run out of extents. +*/ +while (subex->fsbno + subex->len <= ex->fsbno) { +if (list_is_last (&subex->list, &sublist->list)) + goto out; + subex =list_next_entry (subex, list); + } +if (subex->fsbno >= ex->fsbno + ex->len) { + lp = lp->next; +continue ; + } + +/* trim subex to fit the extent we have */ + sub_fsb = subex->fsbno; + sub_len = subex->len; +if (subex->fsbno < ex->fsbno) { + sub_len -= ex->fsbno - subex->fsbno; + sub_fsb = ex->fsbno; + } +if (sub_len > ex->len) + sub_len = ex->len; + + state =0 ; +if (sub_fsb == ex->fsbno) + state |=LEFT_ALIGNED ; +if (sub_fsb + sub_len == ex->fsbno + ex->len) + state |=RIGHT_ALIGNED ; +switch (state) { +case LEFT_ALIGNED : +/* Coincides with only the left. */ + ex->fsbno += sub_len; + ex->len -= sub_len; +break ; +case RIGHT_ALIGNED : +/* Coincides with only the right. */ + ex->len -= sub_len; + lp = lp->next; +break ; +case LEFT_ALIGNED |RIGHT_ALIGNED : +/* Total overlap, just delete ex. */ + lp = lp->next; +list_del (&ex->list); +kmem_free (ex); +break ; +case 0 : +/* +* Deleting from the middle: add the new right extent +* and then shrink the left extent. +*/ + newex =kmem_alloc (sizeof (struct xfs_repair_extent ), +KM_MAYFAIL ); +if (!newex) { + error = -ENOMEM ; + goto out; + } +INIT_LIST_HEAD (&newex->list); + newex->fsbno = sub_fsb + sub_len; + newex->len = ex->fsbno + ex->len - newex->fsbno; +list_add (&newex->list, &ex->list); + ex->len = sub_fsb - ex->fsbno; + lp = lp->next; +break ; +default : +ASSERT (0 ); +break ; + } + } + +out: +return error; +} +#undef LEFT_ALIGNED +#undef RIGHT_ALIGNED
fs/xfs/scrub/repair.h +28 -0
@@ -43,6 +43,34 @@ int xfs_repair_init_btblock(struct xfs_scrub_context *sc, xfs_fsblock_t fsb, struct xfs_buf **bpp, xfs_btnum_t btnum, const struct xfs_buf_ops *ops); +struct xfs_repair_extent { +struct list_head list; +xfs_fsblock_t fsbno; +xfs_extlen_t len; +}; + +struct xfs_repair_extent_list { +struct list_head list; +}; + +static inline void +xfs_repair_init_extent_list ( +struct xfs_repair_extent_list *exlist) +{ +INIT_LIST_HEAD (&exlist->list); +} + +#define for_each_xfs_repair_extent_safe (rbe, n, exlist) \ + list_for_each_entry_safe((rbe), (n), &(exlist)->list, list) +int xfs_repair_collect_btree_extent (struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *btlist,xfs_fsblock_t fsbno, +xfs_extlen_t len); +void xfs_repair_cancel_btree_extents (struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *btlist); +int xfs_repair_subtract_extents (struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_repair_extent_list *sublist); +/* Metadata repairers */ int xfs_repair_probe (struct xfs_scrub_context *sc);
Re: [PATCH v2 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Quoted 10 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
Quoted 5 lines
Signed-off-by Darrick J. Wong <[email protected]>---
v2: document the design of how repair functions are supposed to work,
implement review comments
---
Much easier to understand now. Thanks!
One thing for a followup patch, though.
Quoted 11 lines
fs/xfs/scrub/repair.c +11
-0+/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len) +{ +struct xfs_repair_extent *rex; + + trace_xfs_repair_collect_btree_extent(sc->mp,
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 418
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+XFS_FSB_TO_AGNO (sc->mp, fsbno), + XFS_FSB_TO_AGBNO(sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ; + +INIT_LIST_HEAD (&rex->list); + rex->fsbno = fsbno; + rex->len = len;
This allocation is repeated a couple of times. Single function to do it would be nice to encapsulate alloc+setup - something like rex = alloc_rex(fsbno, len) which returns NULL if it fails.
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+case LEFT_ALIGNED |RIGHT_ALIGNED : +/* Total overlap, just delete ex. */ + lp = lp->next; +list_del (&ex->list); +kmem_free (ex); +break ; +case 0 : +/* +* Deleting from the middle: add the new right extent +* and then shrink the left extent.
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 572
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+*/ + newex =kmem_alloc (sizeof (struct xfs_repair_extent ), +KM_MAYFAIL ); +if (!newex) { + error = -ENOMEM ; + goto out; + } +INIT_LIST_HEAD (&newex->list); + newex->fsbno = sub_fsb + sub_len; + newex->len = ex->fsbno + ex->len - newex->fsbno;
This is the other place it is called...
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH v2 03/22] xfs: add helpers to collect and sift btree block pointers during repair
Quoted 63 lines · Dave Chinner
Quoted 10 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to assemble a list of fs block extents. Generally, repair functions will iterate the rmapbt to make a list (1) of all extents owned by the nominal owner of the metadata structure; then they will iterate all other structures with the same rmap owner to make a list (2) of active blocks; and finally we have a subtraction function to subtract all the blocks in (2) from (1), with the result that (1) is now a list of blocks that were owned by the old btree and must be disposed.
Quoted 5 lines
Signed-off-by Darrick J. Wong <[email protected]>---
v2: document the design of how repair functions are supposed to work,
implement review comments
---Much easier to understand now. Thanks!
Reviewed-by Dave Chinner <[email protected]>One thing for a followup patch, though.
Quoted 11 lines
fs/xfs/scrub/repair.c +11
-0+/* Collect a dead btree extent for later disposal. */ +int +xfs_repair_collect_btree_extent ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +xfs_fsblock_t fsbno, +xfs_extlen_t len) +{ +struct xfs_repair_extent *rex; + + trace_xfs_repair_collect_btree_extent(sc->mp,fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 418
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+XFS_FSB_TO_AGNO (sc->mp, fsbno), + XFS_FSB_TO_AGBNO(sc->mp, fsbno), len); + + rex =kmem_alloc (sizeof (struct xfs_repair_extent ),KM_MAYFAIL ); +if (!rex) +return -ENOMEM ; + +INIT_LIST_HEAD (&rex->list); + rex->fsbno = fsbno; + rex->len = len;This allocation is repeated a couple of times. Single function to do it would be nice to encapsulate alloc+setup - something like rex =
alloc_rex(fsbno, len)which returns NULL if it fails.Quoted 14 lines
fs/xfs/scrub/repair.c +14
-0+case LEFT_ALIGNED |RIGHT_ALIGNED : +/* Total overlap, just delete ex. */ + lp = lp->next; +list_del (&ex->list); +kmem_free (ex); +break ; +case 0 : +/* +* Deleting from the middle: add the new right extent +* and then shrink the left extent. +*/ + newex =kmem_alloc (sizeof (struct xfs_repair_extent ), +KM_MAYFAIL ); +if (!newex) {
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 572
Quoted 6 lines
fs/xfs/scrub/repair.c +6
-0+ error = -ENOMEM ; + goto out; + } +INIT_LIST_HEAD (&newex->list); + newex->fsbno = sub_fsb + sub_len; + newex->len = ex->fsbno + ex->len - newex->fsbno;This is the other place it is called...
<nod> I'll add this to the pile of enhancements to come after upstreaming; maybe this will turn into more generic per-ag bitmap structure.
I'd also like to replace the list usage in the later repair patches with some kind of sort-once array structure and an xfs_btree_bulkload to reduce overhead and memory consumption.
--D
Quoted 10 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH 4/22 xfs: add helpers to dispose of old btree blocks after a repair
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Now that we've plumbed in the ability to construct a list of dead btree blocks following a repair, add more helpers to dispose of them. This is done by examining the rmapbt -- if the btree was the only owner we can free the block, otherwise it's crosslinked and we can only remove the rmapbt record.
--- fs/xfs/scrub/repair.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 3 + 2 files changed, 202 insertions(+), 1 deletion(-)
fs/xfs/scrub/repair.c +199 -1
@@ -362,6 +362,173 @@ xfs_repair_init_btblock(return 0 ; } +/* Ensure the freelist is the correct size. */ +int +xfs_repair_fix_freelist ( +struct xfs_scrub_context *sc, +bool can_shrink) +{ +struct xfs_alloc_arg args = {0 }; +int error; + + args.mp = sc->mp; + args.tp = sc->tp; + args.agno = sc->sa.agno; + args.alignment =1 ; + args.pag =xfs_perag_get (args.mp, sc->sa.agno); + + error =xfs_alloc_fix_freelist (&args, + can_shrink ?0 :XFS_ALLOC_FLAG_NOSHRINK ); +xfs_perag_put (args.pag); + +return error; +} + +/* +* Put a block back on the AGFL. +*/ +STATIC int +xfs_repair_put_freelist ( +struct xfs_scrub_context *sc, +xfs_agblock_t agbno) +{ +struct xfs_owner_info oinfo; +struct xfs_perag *pag; +int error; + +/* Make sure there's space on the freelist. */ + error =xfs_repair_fix_freelist (sc, true); +if (error) +return error; + pag =xfs_perag_get (sc->mp, sc->sa.agno); +if (pag->pagf_flcount ==0 ) { +xfs_perag_put (pag); +return -EFSCORRUPTED ; + } +xfs_perag_put (pag); + +/* +* Since we're "freeing" a lost block onto the AGFL, we have to +* create an rmap for the block prior to merging it or else other +* parts will break. +*/ +xfs_rmap_ag_owner (&oinfo,XFS_RMAP_OWN_AG ); + error =xfs_rmap_alloc (sc->tp, sc->sa.agf_bp, sc->sa.agno, agbno,1 , + &oinfo); +if (error) +return error; + +/* Put the block on the AGFL. */ + error =xfs_alloc_put_freelist (sc->tp, sc->sa.agf_bp, sc->sa.agfl_bp, + agbno,0 ); +if (error) +return error; +xfs_extent_busy_insert (sc->tp, sc->sa.agno, agbno,1 , +XFS_EXTENT_BUSY_SKIP_DISCARD ); + +return 0 ; +} + +/* Dispose of a single metadata block. */ +STATIC int +xfs_repair_dispose_btree_block ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsbno, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type resv) +{ +struct xfs_btree_cur *cur; +struct xfs_buf *agf_bp =NULL ; +xfs_agnumber_t agno; +xfs_agblock_t agbno; +bool has_other_rmap; +int error; + + agno =XFS_FSB_TO_AGNO (sc->mp, fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, fsbno); + +if (sc->ip) { +/* Repairing per-inode metadata, read in the AGF. */ + error =xfs_alloc_read_agf (sc->mp, sc->tp, agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + }else { +/* Repairing per-AG btree, reuse existing AGF buffer. */ + agf_bp = sc->sa.agf_bp; + } + cur =xfs_rmapbt_init_cursor (sc->mp, sc->tp, agf_bp, agno); + +/* Can we find any other rmappings? */ + error =xfs_rmap_has_other_keys (cur, agbno,1 , oinfo, &has_other_rmap); +if (error) + goto out_cur; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + +/* +* If there are other rmappings, this block is cross linked and must +* not be freed. Remove the reverse mapping and move on. Otherwise, +* we were the only owner of the block, so free the extent, which will +* also remove the rmap. +*/ +if (has_other_rmap) + error =xfs_rmap_free (sc->tp, agf_bp, agno, agbno,1 , oinfo); +else if (resv ==XFS_AG_RESV_AGFL ) + error =xfs_repair_put_freelist (sc, agbno); +else + error =xfs_free_extent (sc->tp, fsbno,1 , oinfo, resv); +if (agf_bp != sc->sa.agf_bp) +xfs_trans_brelse (sc->tp, agf_bp); +if (error) +return error; + +if (sc->ip) +return xfs_trans_roll_inode (&sc->tp, sc->ip); +return xfs_repair_roll_ag_trans (sc); + +out_cur: +xfs_btree_del_cursor (cur,XFS_BTREE_ERROR ); +if (agf_bp != sc->sa.agf_bp) +xfs_trans_brelse (sc->tp, agf_bp); +return error; +} + +/* +* Dispose of a given metadata extent. +* +* If the rmapbt says the extent has multiple owners, we simply remove the +* rmap associated with this owner hoping that we'll eventually disentangle +* the crosslinked metadata. Otherwise, there's one owner, so call the +* regular free code to remove the rmap and free the extent. Any existing +* buffers for the blocks in the extent must have been _binval'd previously. +*/ +STATIC int +xfs_repair_dispose_btree_extent ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsbno, +xfs_extlen_t len, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type resv) +{ +struct xfs_mount *mp = sc->mp; +int error =0 ; + +ASSERT (xfs_sb_version_hasrmapbt (&mp->m_sb)); +ASSERT (sc->ip !=NULL ||XFS_FSB_TO_AGNO (mp, fsbno) == sc->sa.agno); + +trace_xfs_repair_dispose_btree_extent (mp,XFS_FSB_TO_AGNO (mp, fsbno), +XFS_FSB_TO_AGBNO (mp, fsbno), len); + +for (; len >0 ; len--, fsbno++) { + error =xfs_repair_dispose_btree_block (sc, fsbno, oinfo, resv); +if (error) +return error; + } + +return 0 ; +} +/* Collect a dead btree extent for later disposal. */ int xfs_repair_collect_btree_extent(
@@ -404,7 +571,9 @@ xfs_repair_reap_btree_extents(int error =0 ;for_each_xfs_repair_extent_safe (rex, n, exlist) { -/* TODO: free the extent */ +if (!error) + error =xfs_repair_dispose_btree_extent (sc, rex->fsbno, + rex->len, oinfo, type);list_del (&rex->list);kmem_free (rex); }
@@ -568,3 +737,32 @@ xfs_repair_subtract_extents( }#undef XFS_REPAIR_EXT_LEFT_CONTIG#undef XFS_REPAIR_EXT_RIGHT_CONTIG + +/* +* Invalidate buffers for per-AG btree blocks we're dumping. We assume that +* exlist points only to metadata blocks. +*/ +int +xfs_repair_invalidate_blocks ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; +struct xfs_buf *bp; +xfs_agnumber_t agno; +xfs_agblock_t agbno; +xfs_agblock_t i; + +for_each_xfs_repair_extent_safe (rex, n, exlist) { + agno =XFS_FSB_TO_AGNO (sc->mp, rex->fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, rex->fsbno); +for (i =0 ; i < rex->len; i++) { + bp =xfs_btree_get_bufs (sc->mp, sc->tp, agno, + agbno + i,0 ); +xfs_trans_binval (sc->tp, bp); + } + } + +return 0 ; +}
fs/xfs/scrub/repair.h +3 -0
@@ -73,6 +73,9 @@ void xfs_repair_cancel_btree_extents(struct xfs_scrub_context *sc,int xfs_repair_subtract_extents (struct xfs_scrub_context *sc,struct xfs_repair_extent_list *exlist,struct xfs_repair_extent_list *sublist); +int xfs_repair_fix_freelist (struct xfs_scrub_context *sc,bool can_shrink); +int xfs_repair_invalidate_blocks (struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *btlist);/* Metadata repairers */
Re: [PATCH 04/22] xfs: add helpers to dispose of old btree blocks after a repair
Quoted 31 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Now that we've plumbed in the ability to construct a list of dead btree blocks following a repair, add more helpers to dispose of them. This is done by examining the rmapbt -- if the btree was the only owner we can free the block, otherwise it's crosslinked and we can only remove the rmapbt record.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 3 + 2 files changed, 202 insertions(+), 1 deletion(-)fs/xfs/scrub/repair.c +8
-0@@ -362,6 +362,173 @@ xfs_repair_init_btblock(return 0 ; } +/* Ensure the freelist is the correct size. */ +int +xfs_repair_fix_freelist ( +struct xfs_scrub_context *sc, +bool can_shrink) +{ +struct xfs_alloc_arg args = {0 }; +int error;
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 382
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ + args.mp = sc->mp; + args.tp = sc->tp; + args.agno = sc->sa.agno; + args.alignment =1 ; + args.pag =xfs_perag_get (args.mp, sc->sa.agno); + + error =xfs_alloc_fix_freelist (&args, + can_shrink ?0 :XFS_ALLOC_FLAG_NOSHRINK ); +xfs_perag_put (args.pag);
with all these pag lookups, I'm starting to wonder if you should just add a lookup and store the pag in the scrub context? That'd save a lot of lookups - the pag structures never go away and i never really intended them to be used like this in single, very short use contexts. Grab once per operation, hold the reference across the entire operation context, then release it....
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+return error; +} + +/* +* Put a block back on the AGFL. +*/ +STATIC int +xfs_repair_put_freelist ( +struct xfs_scrub_context *sc, +xfs_agblock_t agbno)
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 403
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+{ +struct xfs_owner_info oinfo; +struct xfs_perag *pag; +int error; + +/* Make sure there's space on the freelist. */ + error =xfs_repair_fix_freelist (sc, true); +if (error) +return error; + pag =xfs_perag_get (sc->mp, sc->sa.agno);
Because this is how it quickly gets it gets to silly numbers of lookups. That's two now in this function.
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 406
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+if (pag->pagf_flcount ==0 ) { +xfs_perag_put (pag); +return -EFSCORRUPTED ;
Why is having an empty freelist a problem here? It's an AG thatis completely out of space, but it isn't corruption? And I don't see why an empty freelist prevents us from adding a backs back onto the AGFL?
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+ } +xfs_perag_put (pag); + +/* +* Since we're "freeing" a lost block onto the AGFL, we have to +* create an rmap for the block prior to merging it or else other +* parts will break.
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 423
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+*/ +xfs_rmap_ag_owner (&oinfo,XFS_RMAP_OWN_AG ); + error =xfs_rmap_alloc (sc->tp, sc->sa.agf_bp, sc->sa.agno, agbno,1 , + &oinfo); +if (error) +return error; + +/* Put the block on the AGFL. */ + error =xfs_alloc_put_freelist (sc->tp, sc->sa.agf_bp, sc->sa.agfl_bp, + agbno,0 );
There's another pag lookup in here.
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 427
Quoted 4 lines
fs/xfs/scrub/repair.c +4
-0+if (error) +return error; +xfs_extent_busy_insert (sc->tp, sc->sa.agno, agbno,1 , +XFS_EXTENT_BUSY_SKIP_DISCARD );
And another in here, so 4 perag lookups for the same structure in one simple operation. The code here in the function is fine, but I really think we need to rethink how we use the perag in our allocation code...
Quoted 14 lines
fs/xfs/scrub/repair.c +14
-0+ +return 0 ; +} + +/* Dispose of a single metadata block. */ +STATIC int +xfs_repair_dispose_btree_block ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsbno, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type resv) +{ +struct xfs_btree_cur *cur; +struct xfs_buf *agf_bp =NULL ;
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 451
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+xfs_agnumber_t agno; +xfs_agblock_t agbno; +bool has_other_rmap; +int error; + + agno =XFS_FSB_TO_AGNO (sc->mp, fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, fsbno); + +if (sc->ip) { +/* Repairing per-inode metadata, read in the AGF. */
This would be better with a comment above saying:
/*
* If we are repairing per-inode metadata, we need to read
* in the AGF buffer. Otherwise we can re-use the existing
* AGF buffer we set up for repairing the per-AG btrees. */
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ error =xfs_alloc_read_agf (sc->mp, sc->tp, agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + }else { +/* Repairing per-AG btree, reuse existing AGF buffer. */ + agf_bp = sc->sa.agf_bp; + } + cur =xfs_rmapbt_init_cursor (sc->mp, sc->tp, agf_bp, agno);
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 471
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ +/* Can we find any other rmappings? */ + error =xfs_rmap_has_other_keys (cur, agbno,1 , oinfo, &has_other_rmap); +if (error) + goto out_cur; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + +/* +* If there are other rmappings, this block is cross linked and must +* not be freed. Remove the reverse mapping and move on. Otherwise,
Why do we just remove the reverse mapping if the block cannot be freed? I have my suspicions that this is removing cross-links one by one until there's only one reference left to the extent, but then I ask "how do we know which one is the correct mapping"?
i.e. this comment raised more questions about the algorithm for dealing with cross-linked blocks - which doesn't appear to be explained anywhere - than it answers....
Quoted 23 lines
fs/xfs/scrub/repair.c +23
-0+* we were the only owner of the block, so free the extent, which will +* also remove the rmap. +*/ +if (has_other_rmap) + error =xfs_rmap_free (sc->tp, agf_bp, agno, agbno,1 , oinfo); +else if (resv ==XFS_AG_RESV_AGFL ) + error =xfs_repair_put_freelist (sc, agbno); +else + error =xfs_free_extent (sc->tp, fsbno,1 , oinfo, resv); +if (agf_bp != sc->sa.agf_bp) +xfs_trans_brelse (sc->tp, agf_bp); +if (error) +return error; + +if (sc->ip) +return xfs_trans_roll_inode (&sc->tp, sc->ip); +return xfs_repair_roll_ag_trans (sc); + +out_cur: +xfs_btree_del_cursor (cur,XFS_BTREE_ERROR ); +if (agf_bp != sc->sa.agf_bp) +xfs_trans_brelse (sc->tp, agf_bp); +return error;
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 504
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+} + +/* +* Dispose of a given metadata extent. +* +* If the rmapbt says the extent has multiple owners, we simply remove the +* rmap associated with this owner hoping that we'll eventually disentangle +* the crosslinked metadata. Otherwise, there's one owner, so call the +* regular free code to remove the rmap and free the extent. Any existing +* buffers for the blocks in the extent must have been _binval'd previously.
Ok, so there's a little more detail about cross-linked files. Seems my suspicions of "remove and hope" were close to the mark :P
Perhaps we need a document that describes the various algorithms we use for resolving these problems, so they can be discussed and improved without having to troll through the code to understand?
Quoted 12 lines
fs/xfs/scrub/repair.c +12
-0+*/ +STATIC int +xfs_repair_dispose_btree_extent ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsbno, +xfs_extlen_t len, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type resv) +{ +struct xfs_mount *mp = sc->mp; +int error =0 ; +
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 526
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ASSERT (xfs_sb_version_hasrmapbt (&mp->m_sb)); +ASSERT (sc->ip !=NULL ||XFS_FSB_TO_AGNO (mp, fsbno) == sc->sa.agno); + +trace_xfs_repair_dispose_btree_extent (mp,XFS_FSB_TO_AGNO (mp, fsbno), +XFS_FSB_TO_AGBNO (mp, fsbno), len); + +for (; len >0 ; len--, fsbno++) { + error =xfs_repair_dispose_btree_block (sc, fsbno, oinfo, resv); +if (error) +return error;
So why do we do this one block at a time, rather than freeing it as an entire extent in one go? And if there is only a single caller, why not just open code the loop in the caller?
Quoted 14 lines
fs/xfs/scrub/repair.c +14
-0+/* +* Invalidate buffers for per-AG btree blocks we're dumping. We assume that +* exlist points only to metadata blocks. +*/ +int +xfs_repair_invalidate_blocks ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; +struct xfs_buf *bp; +xfs_agnumber_t agno; +xfs_agblock_t agbno;
fs/xfs/scrub/repair.c xfs_repair_subtract_extents( · line 764
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+xfs_agblock_t i; + +for_each_xfs_repair_extent_safe (rex, n, exlist) { + agno =XFS_FSB_TO_AGNO (sc->mp, rex->fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, rex->fsbno); +for (i =0 ; i < rex->len; i++) { + bp =xfs_btree_get_bufs (sc->mp, sc->tp, agno, + agbno + i,0 ); +xfs_trans_binval (sc->tp, bp); + }
Again, this is doing things by single blocks. We do have multi-block metadata (inodes, directory blocks, remote attrs) that, if it is already in memory, needs to be treated as multi-block extents. If we don't do that, we'll cause aliasing problems in the buffer cache (see _xfs_buf_obj_cmp()) and it's all downhill from there.
That's why I get worried when I see assumptions that we can process contiguous metadata ranges in single block buffers....
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 04/22] xfs: add helpers to dispose of old btree blocks after a repair
Alrighty, with Daves concerns addressed:
Reviewed by: Allison Henderson <[email protected]>
Quoted 271 lines · Dave Chinner
Quoted 31 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Now that we've plumbed in the ability to construct a list of dead btree blocks following a repair, add more helpers to dispose of them. This is done by examining the rmapbt -- if the btree was the only owner we can free the block, otherwise it's crosslinked and we can only remove the rmapbt record.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 3 + 2 files changed, 202 insertions(+), 1 deletion(-)fs/xfs/scrub/repair.c +8
-0@@ -362,6 +362,173 @@ xfs_repair_init_btblock(return 0 ; } +/* Ensure the freelist is the correct size. */ +int +xfs_repair_fix_freelist ( +struct xfs_scrub_context *sc, +bool can_shrink) +{ +struct xfs_alloc_arg args = {0 }; +int error;fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 382
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ + args.mp = sc->mp; + args.tp = sc->tp; + args.agno = sc->sa.agno; + args.alignment =1 ; + args.pag =xfs_perag_get (args.mp, sc->sa.agno); + + error =xfs_alloc_fix_freelist (&args, + can_shrink ?0 :XFS_ALLOC_FLAG_NOSHRINK ); +xfs_perag_put (args.pag);with all these pag lookups, I'm starting to wonder if you should just add a lookup and store the pag in the scrub context? That'd save a lot of lookups - the pag structures never go away and i never really intended them to be used like this in single, very short use contexts. Grab once per operation, hold the reference across the entire operation context, then release it....
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+return error; +} + +/* +* Put a block back on the AGFL. +*/ +STATIC int +xfs_repair_put_freelist ( +struct xfs_scrub_context *sc, +xfs_agblock_t agbno)fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 403
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+{ +struct xfs_owner_info oinfo; +struct xfs_perag *pag; +int error; + +/* Make sure there's space on the freelist. */ + error =xfs_repair_fix_freelist (sc, true); +if (error) +return error; + pag =xfs_perag_get (sc->mp, sc->sa.agno);Because this is how it quickly gets it gets to silly numbers of lookups. That's two now in this function.
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 406
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+if (pag->pagf_flcount ==0 ) { +xfs_perag_put (pag); +return -EFSCORRUPTED ;Why is having an empty freelist a problem here? It's an AG thatis completely out of space, but it isn't corruption? And I don't see why an empty freelist prevents us from adding a backs back onto the AGFL?
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+ } +xfs_perag_put (pag); + +/* +* Since we're "freeing" a lost block onto the AGFL, we have to +* create an rmap for the block prior to merging it or else other +* parts will break. fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 423
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+*/ +xfs_rmap_ag_owner (&oinfo,XFS_RMAP_OWN_AG ); + error =xfs_rmap_alloc (sc->tp, sc->sa.agf_bp, sc->sa.agno, agbno,1 , + &oinfo); +if (error) +return error; + +/* Put the block on the AGFL. */ + error =xfs_alloc_put_freelist (sc->tp, sc->sa.agf_bp, sc->sa.agfl_bp, + agbno,0 );There's another pag lookup in here.
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 427
Quoted 4 lines
fs/xfs/scrub/repair.c +4
-0+if (error) +return error; +xfs_extent_busy_insert (sc->tp, sc->sa.agno, agbno,1 , +XFS_EXTENT_BUSY_SKIP_DISCARD );And another in here, so 4 perag lookups for the same structure in one simple operation. The code here in the function is fine, but I really think we need to rethink how we use the perag in our allocation code...
Quoted 14 lines
fs/xfs/scrub/repair.c +14
-0+ +return 0 ; +} + +/* Dispose of a single metadata block. */ +STATIC int +xfs_repair_dispose_btree_block ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsbno, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type resv) +{ +struct xfs_btree_cur *cur; +struct xfs_buf *agf_bp =NULL ;fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 451
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+xfs_agnumber_t agno; +xfs_agblock_t agbno; +bool has_other_rmap; +int error; + + agno =XFS_FSB_TO_AGNO (sc->mp, fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, fsbno); + +if (sc->ip) { +/* Repairing per-inode metadata, read in the AGF. */ This would be better with a comment above saying:
/*
* If we are repairing per-inode metadata, we need to read
* in the AGF buffer. Otherwise we can re-use the existing
* AGF buffer we set up for repairing the per-AG btrees. */Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ error =xfs_alloc_read_agf (sc->mp, sc->tp, agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + }else { +/* Repairing per-AG btree, reuse existing AGF buffer. */ + agf_bp = sc->sa.agf_bp; + } + cur =xfs_rmapbt_init_cursor (sc->mp, sc->tp, agf_bp, agno);fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 471
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ +/* Can we find any other rmappings? */ + error =xfs_rmap_has_other_keys (cur, agbno,1 , oinfo, &has_other_rmap); +if (error) + goto out_cur; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + +/* +* If there are other rmappings, this block is cross linked and must +* not be freed. Remove the reverse mapping and move on. Otherwise, Why do we just remove the reverse mapping if the block cannot be freed? I have my suspicions that this is removing cross-links one by one until there's only one reference left to the extent, but then I ask "how do we know which one is the correct mapping"?
i.e. this comment raised more questions about the algorithm for dealing with cross-linked blocks - which doesn't appear to be explained anywhere - than it answers....
Quoted 23 lines
fs/xfs/scrub/repair.c +23
-0+* we were the only owner of the block, so free the extent, which will +* also remove the rmap. +*/ +if (has_other_rmap) + error =xfs_rmap_free (sc->tp, agf_bp, agno, agbno,1 , oinfo); +else if (resv ==XFS_AG_RESV_AGFL ) + error =xfs_repair_put_freelist (sc, agbno); +else + error =xfs_free_extent (sc->tp, fsbno,1 , oinfo, resv); +if (agf_bp != sc->sa.agf_bp) +xfs_trans_brelse (sc->tp, agf_bp); +if (error) +return error; + +if (sc->ip) +return xfs_trans_roll_inode (&sc->tp, sc->ip); +return xfs_repair_roll_ag_trans (sc); + +out_cur: +xfs_btree_del_cursor (cur,XFS_BTREE_ERROR ); +if (agf_bp != sc->sa.agf_bp) +xfs_trans_brelse (sc->tp, agf_bp); +return error;fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 504
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+} + +/* +* Dispose of a given metadata extent. +* +* If the rmapbt says the extent has multiple owners, we simply remove the +* rmap associated with this owner hoping that we'll eventually disentangle +* the crosslinked metadata. Otherwise, there's one owner, so call the +* regular free code to remove the rmap and free the extent. Any existing +* buffers for the blocks in the extent must have been _binval'd previously. Ok, so there's a little more detail about cross-linked files. Seems my suspicions of "remove and hope" were close to the mark :P
Perhaps we need a document that describes the various algorithms we use for resolving these problems, so they can be discussed and improved without having to troll through the code to understand?
Quoted 12 lines
fs/xfs/scrub/repair.c +12
-0+*/ +STATIC int +xfs_repair_dispose_btree_extent ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsbno, +xfs_extlen_t len, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type resv) +{ +struct xfs_mount *mp = sc->mp; +int error =0 ; +fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 526
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+ASSERT (xfs_sb_version_hasrmapbt (&mp->m_sb)); +ASSERT (sc->ip !=NULL ||XFS_FSB_TO_AGNO (mp, fsbno) == sc->sa.agno); + +trace_xfs_repair_dispose_btree_extent (mp,XFS_FSB_TO_AGNO (mp, fsbno), +XFS_FSB_TO_AGBNO (mp, fsbno), len); + +for (; len >0 ; len--, fsbno++) { + error =xfs_repair_dispose_btree_block (sc, fsbno, oinfo, resv); +if (error) +return error;So why do we do this one block at a time, rather than freeing it as an entire extent in one go? And if there is only a single caller, why not just open code the loop in the caller?
Quoted 14 lines
fs/xfs/scrub/repair.c +14
-0+/* +* Invalidate buffers for per-AG btree blocks we're dumping. We assume that +* exlist points only to metadata blocks. +*/ +int +xfs_repair_invalidate_blocks ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; +struct xfs_buf *bp; +xfs_agnumber_t agno; +xfs_agblock_t agbno;fs/xfs/scrub/repair.c xfs_repair_subtract_extents( · line 764
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+xfs_agblock_t i; + +for_each_xfs_repair_extent_safe (rex, n, exlist) { + agno =XFS_FSB_TO_AGNO (sc->mp, rex->fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, rex->fsbno); +for (i =0 ; i < rex->len; i++) { + bp =xfs_btree_get_bufs (sc->mp, sc->tp, agno, + agbno + i,0 ); +xfs_trans_binval (sc->tp, bp); + }Again, this is doing things by single blocks. We do have multi-block metadata (inodes, directory blocks, remote attrs) that, if it is already in memory, needs to be treated as multi-block extents. If we don't do that, we'll cause aliasing problems in the buffer cache (see
_xfs_buf_obj_cmp()) and it's all downhill from there.That's why I get worried when I see assumptions that we can process contiguous metadata ranges in single block buffers....
Cheers,
Dave.
Re: [PATCH 04/22] xfs: add helpers to dispose of old btree blocks after a repair
In reply to Dave ChinnerQuoted 41 lines · Dave Chinner
Quoted 40 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Now that we've plumbed in the ability to construct a list of dead btree blocks following a repair, add more helpers to dispose of them. This is done by examining the rmapbt -- if the btree was the only owner we can free the block, otherwise it's crosslinked and we can only remove the rmapbt record.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 200 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 3 + 2 files changed, 202 insertions(+), 1 deletion(-)fs/xfs/scrub/repair.c +17
-0@@ -362,6 +362,173 @@ xfs_repair_init_btblock(return 0 ; } +/* Ensure the freelist is the correct size. */ +int +xfs_repair_fix_freelist ( +struct xfs_scrub_context *sc, +bool can_shrink) +{ +struct xfs_alloc_arg args = {0 }; +int error; + + args.mp = sc->mp; + args.tp = sc->tp; + args.agno = sc->sa.agno; + args.alignment =1 ; + args.pag =xfs_perag_get (args.mp, sc->sa.agno); + + error =xfs_alloc_fix_freelist (&args, + can_shrink ?0 :XFS_ALLOC_FLAG_NOSHRINK );
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 382
Quoted 1 line
fs/xfs/scrub/repair.c +1
-0+xfs_perag_put (args.pag);with all these pag lookups, I'm starting to wonder if you should just add a lookup and store the pag in the scrub context? That'd save a lot of lookups - the pag structures never go away and i never really intended them to be used like this in single, very short use contexts. Grab once per operation, hold the reference across the entire operation context, then release it....
Ok, I'll change xfs_scrub_ag_{init,free} to get / put the perag structure so we don't have to do this repeatedly.
Quoted 53 lines
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+return error; +} + +/* +* Put a block back on the AGFL. +*/ +STATIC int +xfs_repair_put_freelist ( +struct xfs_scrub_context *sc, +xfs_agblock_t agbno)fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 403
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+{ +struct xfs_owner_info oinfo; +struct xfs_perag *pag; +int error; + +/* Make sure there's space on the freelist. */ + error =xfs_repair_fix_freelist (sc, true); +if (error) +return error; + pag =xfs_perag_get (sc->mp, sc->sa.agno);Because this is how it quickly gets it gets to silly numbers of lookups. That's two now in this function.
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 406
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+if (pag->pagf_flcount ==0 ) { +xfs_perag_put (pag); +return -EFSCORRUPTED ;Why is having an empty freelist a problem here? It's an AG thatis completely out of space, but it isn't corruption? And I don't see why an empty freelist prevents us from adding a backs back onto the AGFL?
Quoted 7 lines
fs/xfs/scrub/repair.c +7
-0+ } +xfs_perag_put (pag); + +/* +* Since we're "freeing" a lost block onto the AGFL, we have to +* create an rmap for the block prior to merging it or else other +* parts will break. fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 423
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+*/ +xfs_rmap_ag_owner (&oinfo,XFS_RMAP_OWN_AG ); + error =xfs_rmap_alloc (sc->tp, sc->sa.agf_bp, sc->sa.agno, agbno,1 , + &oinfo); +if (error) +return error; + +/* Put the block on the AGFL. */ + error =xfs_alloc_put_freelist (sc->tp, sc->sa.agf_bp, sc->sa.agfl_bp, + agbno,0 );There's another pag lookup in here.
Quoted 9 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 427
Quoted 4 lines
fs/xfs/scrub/repair.c +4
-0+if (error) +return error; +xfs_extent_busy_insert (sc->tp, sc->sa.agno, agbno,1 , +XFS_EXTENT_BUSY_SKIP_DISCARD );And another in here, so 4 perag lookups for the same structure in one simple operation. The code here in the function is fine, but I really think we need to rethink how we use the perag in our allocation code...
Yeah. If we ever introduce the ability to lock an AG for the duration of a transaction then we might consider linking the perag structures through the transaction at the same time so that these library functions can skip the _perag_get.
Quoted 27 lines
Quoted 14 lines
fs/xfs/scrub/repair.c +14
-0+ +return 0 ; +} + +/* Dispose of a single metadata block. */ +STATIC int +xfs_repair_dispose_btree_block ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsbno, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type resv) +{ +struct xfs_btree_cur *cur; +struct xfs_buf *agf_bp =NULL ;fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 451
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+xfs_agnumber_t agno; +xfs_agblock_t agbno; +bool has_other_rmap; +int error; + + agno =XFS_FSB_TO_AGNO (sc->mp, fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, fsbno); + +if (sc->ip) { +/* Repairing per-inode metadata, read in the AGF. */ This would be better with a comment above saying:
Quoted 5 lines
/*
* If we are repairing per-inode metadata, we need to read
* in the AGF buffer. Otherwise we can re-use the existing
* AGF buffer we set up for repairing the per-AG btrees. */
Ok.
Quoted 17 lines
Quoted 17 lines
fs/xfs/scrub/repair.c +17
-0+ error =xfs_alloc_read_agf (sc->mp, sc->tp, agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + }else { +/* Repairing per-AG btree, reuse existing AGF buffer. */ + agf_bp = sc->sa.agf_bp; + } + cur =xfs_rmapbt_init_cursor (sc->mp, sc->tp, agf_bp, agno); + +/* Can we find any other rmappings? */ + error =xfs_rmap_has_other_keys (cur, agbno,1 , oinfo, &has_other_rmap); +if (error) + goto out_cur; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); +
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 471
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+/* +* If there are other rmappings, this block is cross linked and must +* not be freed. Remove the reverse mapping and move on. Otherwise, Why do we just remove the reverse mapping if the block cannot be freed? I have my suspicions that this is removing cross-links one by one until there's only one reference left to the extent, but then I ask "how do we know which one is the correct mapping"?
Right. Prior to calling this function we built a totally new btree with blocks from the freespace, so now we need to remove the rmaps that covered the old btree and/or free the block. The goal is to rebuild /all/ the trees that think they own this block so that we can free the block and not have to care which one is correct.
Quoted 41 lines
i.e. this comment raised more questions about the algorithm for dealing with cross-linked blocks - which doesn't appear to be explained anywhere - than it answers....
Quoted 23 lines
fs/xfs/scrub/repair.c +23
-0+* we were the only owner of the block, so free the extent, which will +* also remove the rmap. +*/ +if (has_other_rmap) + error =xfs_rmap_free (sc->tp, agf_bp, agno, agbno,1 , oinfo); +else if (resv ==XFS_AG_RESV_AGFL ) + error =xfs_repair_put_freelist (sc, agbno); +else + error =xfs_free_extent (sc->tp, fsbno,1 , oinfo, resv); +if (agf_bp != sc->sa.agf_bp) +xfs_trans_brelse (sc->tp, agf_bp); +if (error) +return error; + +if (sc->ip) +return xfs_trans_roll_inode (&sc->tp, sc->ip); +return xfs_repair_roll_ag_trans (sc); + +out_cur: +xfs_btree_del_cursor (cur,XFS_BTREE_ERROR ); +if (agf_bp != sc->sa.agf_bp) +xfs_trans_brelse (sc->tp, agf_bp); +return error;fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 504
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+} + +/* +* Dispose of a given metadata extent. +* +* If the rmapbt says the extent has multiple owners, we simply remove the +* rmap associated with this owner hoping that we'll eventually disentangle +* the crosslinked metadata. Otherwise, there's one owner, so call the +* regular free code to remove the rmap and free the extent. Any existing +* buffers for the blocks in the extent must have been _binval'd previously. Ok, so there's a little more detail about cross-linked files. Seems my suspicions of "remove and hope" were close to the mark :P
Quoted 3 lines
Perhaps we need a document that describes the various algorithms we use for resolving these problems, so they can be discussed and improved without having to troll through the code to understand?
Yeah. I'll work on consolidating the crosslink functions after lunch and documenting how the tree rebuilder interacts with fixing crosslinks.
Quoted 17 lines
Quoted 17 lines
fs/xfs/scrub/repair.c +17
-0+*/ +STATIC int +xfs_repair_dispose_btree_extent ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsbno, +xfs_extlen_t len, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type resv) +{ +struct xfs_mount *mp = sc->mp; +int error =0 ; + +ASSERT (xfs_sb_version_hasrmapbt (&mp->m_sb)); +ASSERT (sc->ip !=NULL ||XFS_FSB_TO_AGNO (mp, fsbno) == sc->sa.agno); + +trace_xfs_repair_dispose_btree_extent (mp,XFS_FSB_TO_AGNO (mp, fsbno), +XFS_FSB_TO_AGBNO (mp, fsbno), len);
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 526
Quoted 5 lines
fs/xfs/scrub/repair.c +5
-0+ +for (; len >0 ; len--, fsbno++) { + error =xfs_repair_dispose_btree_block (sc, fsbno, oinfo, resv); +if (error) +return error;So why do we do this one block at a time, rather than freeing it as an entire extent in one go?
At the moment the xfs_rmap_has_other_keys helper can only tell you if there are multiple rmap owners for any part of a given extent. For example, if the rmap records were:
(start = 35, len = 3, owner = rmap)
(start = 35, len = 1, owner = refcount)
(start = 37, len = 1, owner = inobt)
Notice how block 35 and 37 are crosslinked, but 36 isn't. A call to xfs_rmap_has_other_keys(35, 3) will say "yes" but doesn't have a way to signal back that the yes applies to 35 but that the caller should try again with block 36. Doing so would require _has_other_keys to maintain a refcount and to return to the caller any time the refcount changed, and the caller would still have to loop the extent. It's easier to have a dumb loop for the initial implementation and optimize it if we start taking more heat than we'd like on crosslinked filesystems.
Quoted 2 lines
And if there is only a single caller, why not just open code the loop in the caller?
I certainly could do that.
Quoted 23 lines
Quoted 23 lines
fs/xfs/scrub/repair.c +22
-0+/* +* Invalidate buffers for per-AG btree blocks we're dumping. We assume that +* exlist points only to metadata blocks. +*/ +int +xfs_repair_invalidate_blocks ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; +struct xfs_buf *bp; +xfs_agnumber_t agno; +xfs_agblock_t agbno; +xfs_agblock_t i; + +for_each_xfs_repair_extent_safe (rex, n, exlist) { + agno =XFS_FSB_TO_AGNO (sc->mp, rex->fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, rex->fsbno); +for (i =0 ; i < rex->len; i++) { + bp =xfs_btree_get_bufs (sc->mp, sc->tp, agno, + agbno + i,0 );
Quoted 8 lines
fs/xfs/scrub/repair.c
Quoted 2 lines
fs/xfs/scrub/repair.c +2
-0+xfs_trans_binval (sc->tp, bp); + }Again, this is doing things by single blocks. We do have multi-block metadata (inodes, directory blocks, remote attrs) that, if it is already in memory, needs to be treated as multi-block extents. If we don't do that, we'll cause aliasing problems in the buffer cache (see
_xfs_buf_obj_cmp()) and it's all downhill from there.
I only recently started testing with filesystems containing multiblock dir/rmt metadata, and this is an unsolved problem. :(
I /think/ the solution is that we need to query the buffer cache to see if it has a buffer for the given disk blocks, and if it matches the btree we're discarding (correct magic/uuid/b_length) then we invalidate it, otherwise we assume that something else owns it and move on. I think that we have to do this on a block-by-block basis since we have no idea what else could be crosslinked with the old btree.
for_each_xfs_repair_extent_safe(rex, n, exlist) {
agno = ...;
agbno = ...;
for (i = 0; i < rex->len, i++) {
/* don't touch ag headers or post-eofs blocks */
if (!xfs_verify_agbno(sc->mp, agno, agbno))
continue;
bp = xfs_buf_get_anything_caching_this_block(...);
if (!bp)
continue;
if (bp does not belong to this tree) {
xfs_trans_brelse(sc->tp, bp);
continue;
}
xfs_trans_binval(sc->tp, bp);
}
}Quoted 2 lines
That's why I get worried when I see assumptions that we can process contiguous metadata ranges in single block buffers....
:/
--D
Quoted 10 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 04/22] xfs: add helpers to dispose of old btree blocks after a repair
Quoted 12 lines · Darrick J. Wong
Quoted 11 lines · Dave Chinner
Quoted 10 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Now that we've plumbed in the ability to construct a list of dead btree blocks following a repair, add more helpers to dispose of them. This is done by examining the rmapbt -- if the btree was the only owner we can free the block, otherwise it's crosslinked and we can only remove the rmapbt record.
Signed-off-by Darrick J. Wong <[email protected]>---
[...]
Quoted 13 lines
Quoted 13 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 403
Quoted 9 lines
fs/xfs/scrub/repair.c +9
-0+struct xfs_owner_info oinfo; +struct xfs_perag *pag; +int error; + +/* Make sure there's space on the freelist. */ + error =xfs_repair_fix_freelist (sc, true); +if (error) +return error; + pag =xfs_perag_get (sc->mp, sc->sa.agno);Because this is how it quickly gets it gets to silly numbers of lookups. That's two now in this function.
Quoted 8 lines
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 406
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+if (pag->pagf_flcount ==0 ) { +xfs_perag_put (pag); +return -EFSCORRUPTED ;Why is having an empty freelist a problem here? It's an AG thatis completely out of space, but it isn't corruption? And I don't see why an empty freelist prevents us from adding a backs back onto the AGFL?
I think you missed a question :P
Quoted 15 lines
Quoted 6 lines
Quoted 6 lines
fs/xfs/scrub/repair.c +6
-0+/* Can we find any other rmappings? */ + error =xfs_rmap_has_other_keys (cur, agbno,1 , oinfo, &has_other_rmap); +if (error) + goto out_cur; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); +Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 471
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+/* +* If there are other rmappings, this block is cross linked and must +* not be freed. Remove the reverse mapping and move on. Otherwise, Why do we just remove the reverse mapping if the block cannot be freed? I have my suspicions that this is removing cross-links one by one until there's only one reference left to the extent, but then I ask "how do we know which one is the correct mapping"?
Quoted 5 lines
Right. Prior to calling this function we built a totally new btree with blocks from the freespace, so now we need to remove the rmaps that covered the old btree and/or free the block. The goal is to rebuild /all/ the trees that think they own this block so that we can free the block and not have to care which one is correct.
Ok, so we've already rebuilt the new btree, and this is removing stale references to cross-linked blocks that have owners different to the one we are currently scanning.
What happens if the cross-linked block is cross-linked within the same owner context?
Quoted 31 lines
Quoted 14 lines
Quoted 14 lines
fs/xfs/scrub/repair.c +14
-0+struct xfs_scrub_context *sc, + xfs_fsblock_t fsbno, + xfs_extlen_t len, + struct xfs_owner_info *oinfo, + enum xfs_ag_resv_type resv) +{ + struct xfs_mount *mp = sc->mp; +int error =0 ; + +ASSERT (xfs_sb_version_hasrmapbt (&mp->m_sb)); +ASSERT (sc->ip !=NULL ||XFS_FSB_TO_AGNO (mp, fsbno) == sc->sa.agno); + +trace_xfs_repair_dispose_btree_extent (mp,XFS_FSB_TO_AGNO (mp, fsbno), +XFS_FSB_TO_AGBNO (mp, fsbno), len);Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 526
Quoted 5 lines
fs/xfs/scrub/repair.c +5
-0+ +for (; len >0 ; len--, fsbno++) { + error =xfs_repair_dispose_btree_block (sc, fsbno, oinfo, resv); +if (error) +return error;So why do we do this one block at a time, rather than freeing it as an entire extent in one go?
At the moment the
xfs_rmap_has_other_keyshelper can only tell you if there are multiple rmap owners for any part of a given extent. For example, if the rmap records were:(start = 35, len = 3, owner = rmap)
(start = 35, len = 1, owner = refcount)
(start = 37, len = 1, owner = inobt)
Quoted 8 lines
Notice how block 35 and 37 are crosslinked, but 36 isn't. A call to
xfs_rmap_has_other_keys(35, 3)will say "yes" but doesn't have a way to signal back that the yes applies to 35 but that the caller should try again with block 36. Doing so would require_has_other_keysto maintain a refcount and to return to the caller any time the refcount changed, and the caller would still have to loop the extent. It's easier to have a dumb loop for the initial implementation and optimize it if we start taking more heat than we'd like on crosslinked filesystems.
Well, I can see why you are doing this now, but the problems with multi-block metadata makes me think that we really need to know more detail of the owner in the rmap. e.g. that it's directory or attribute data, not user file data and hence we can infer things about expected block sizes, do the correct sort of buffer lookups for invalidation, etc.
I'm tending towards "this needs a design doc to explain all this stuff" right now. Code is great, but I'm struggling understand (reverse engineer!) all the algorithms and decisions that have been made from the code...
Quoted 31 lines
Quoted 22 lines
Quoted 22 lines
fs/xfs/scrub/repair.c +22
-0+/* +* Invalidate buffers for per-AG btree blocks we're dumping. We assume that +* exlist points only to metadata blocks. +*/ +int +xfs_repair_invalidate_blocks ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; +struct xfs_buf *bp; +xfs_agnumber_t agno; +xfs_agblock_t agbno; +xfs_agblock_t i; + +for_each_xfs_repair_extent_safe (rex, n, exlist) { + agno =XFS_FSB_TO_AGNO (sc->mp, rex->fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, rex->fsbno); +for (i =0 ; i < rex->len; i++) { + bp =xfs_btree_get_bufs (sc->mp, sc->tp, agno, + agbno + i,0 );Quoted 8 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ xfs_trans_binval(sc->tp, bp); + }Again, this is doing things by single blocks. We do have multi-block metadata (inodes, directory blocks, remote attrs) that, if it is already in memory, needs to be treated as multi-block extents. If we don't do that, we'll cause aliasing problems in the buffer cache (see
_xfs_buf_obj_cmp()) and it's all downhill from there.
Quoted 2 lines
I only recently started testing with filesystems containing multiblock dir/rmt metadata, and this is an unsolved problem. :(
That needs documenting, too. Perhaps explicitly, by rejecting repair requests on filesystems or types that have multi-block constructs until we solve these problems.
Quoted 4 lines
I /think/ the solution is that we need to query the buffer cache to see if it has a buffer for the given disk blocks, and if it matches the btree we're discarding (correct magic/uuid/b_length) then we invalidate it,
I don't think that provides any guarantees. Even ignoring all the problems with invalidation while the buffer is dirty and tracked in the AIL, there's nothing stopping the other code from attempting to re-instantiate the buffer due to some other access. And then we have aliasing problems again....
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 04/22] xfs: add helpers to dispose of old btree blocks after a repair
Quoted 38 lines · Dave Chinner
Quoted 12 lines · Darrick J. Wong
Quoted 11 lines · Dave Chinner
Quoted 10 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Now that we've plumbed in the ability to construct a list of dead btree blocks following a repair, add more helpers to dispose of them. This is done by examining the rmapbt -- if the btree was the only owner we can free the block, otherwise it's crosslinked and we can only remove the rmapbt record.
Signed-off-by Darrick J. Wong <[email protected]>---
[...]
Quoted 13 lines
Quoted 13 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 403
Quoted 9 lines
fs/xfs/scrub/repair.c +9
-0+struct xfs_owner_info oinfo; +struct xfs_perag *pag; +int error; + +/* Make sure there's space on the freelist. */ + error =xfs_repair_fix_freelist (sc, true); +if (error) +return error; + pag =xfs_perag_get (sc->mp, sc->sa.agno);Because this is how it quickly gets it gets to silly numbers of lookups. That's two now in this function.
Quoted 8 lines
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 406
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+if (pag->pagf_flcount ==0 ) { +xfs_perag_put (pag); +return -EFSCORRUPTED ;Why is having an empty freelist a problem here? It's an AG thatis completely out of space, but it isn't corruption? And I don't see why an empty freelist prevents us from adding a backs back onto the AGFL?
Quoted 1 line
I think you missed a question :P
Doh, sorry. I don't remember exactly why I put that in there; judging from my notes I think the idea was that if the AG is completely full we'd rather shut down with a corruption signal hoping that the admin will run xfs_repair.
I also don't see why it's necessary now, I'll see what happens if I remove it.
Quoted 21 lines
Quoted 15 lines
Quoted 6 lines
Quoted 6 lines
fs/xfs/scrub/repair.c +6
-0+/* Can we find any other rmappings? */ + error =xfs_rmap_has_other_keys (cur, agbno,1 , oinfo, &has_other_rmap); +if (error) + goto out_cur; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); +Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 471
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+/* +* If there are other rmappings, this block is cross linked and must +* not be freed. Remove the reverse mapping and move on. Otherwise, Why do we just remove the reverse mapping if the block cannot be freed? I have my suspicions that this is removing cross-links one by one until there's only one reference left to the extent, but then I ask "how do we know which one is the correct mapping"?
Quoted 5 lines
Right. Prior to calling this function we built a totally new btree with blocks from the freespace, so now we need to remove the rmaps that covered the old btree and/or free the block. The goal is to rebuild /all/ the trees that think they own this block so that we can free the block and not have to care which one is correct.
Quoted 6 lines
Ok, so we've already rebuilt the new btree, and this is removing stale references to cross-linked blocks that have owners different to the one we are currently scanning.
What happens if the cross-linked block is cross-linked within the same owner context?
It won't end up on the reap list in first place, because we scan every block of every object with the same rmap owner to construct sublist. Then we subtract sublist from exlist (which we got from rmap) and only reap the difference.
Quoted 40 lines
Quoted 31 lines
Quoted 14 lines
Quoted 14 lines
fs/xfs/scrub/repair.c +14
-0+struct xfs_scrub_context *sc, + xfs_fsblock_t fsbno, + xfs_extlen_t len, + struct xfs_owner_info *oinfo, + enum xfs_ag_resv_type resv) +{ + struct xfs_mount *mp = sc->mp; +int error =0 ; + +ASSERT (xfs_sb_version_hasrmapbt (&mp->m_sb)); +ASSERT (sc->ip !=NULL ||XFS_FSB_TO_AGNO (mp, fsbno) == sc->sa.agno); + +trace_xfs_repair_dispose_btree_extent (mp,XFS_FSB_TO_AGNO (mp, fsbno), +XFS_FSB_TO_AGBNO (mp, fsbno), len);Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 526
Quoted 5 lines
fs/xfs/scrub/repair.c +5
-0+ +for (; len >0 ; len--, fsbno++) { + error =xfs_repair_dispose_btree_block (sc, fsbno, oinfo, resv); +if (error) +return error;So why do we do this one block at a time, rather than freeing it as an entire extent in one go?
At the moment the
xfs_rmap_has_other_keyshelper can only tell you if there are multiple rmap owners for any part of a given extent. For example, if the rmap records were:(start = 35, len = 3, owner = rmap)
(start = 35, len = 1, owner = refcount)
(start = 37, len = 1, owner = inobt)Quoted 8 lines
Notice how block 35 and 37 are crosslinked, but 36 isn't. A call to
xfs_rmap_has_other_keys(35, 3)will say "yes" but doesn't have a way to signal back that the yes applies to 35 but that the caller should try again with block 36. Doing so would require_has_other_keysto maintain a refcount and to return to the caller any time the refcount changed, and the caller would still have to loop the extent. It's easier to have a dumb loop for the initial implementation and optimize it if we start taking more heat than we'd like on crosslinked filesystems.
Quoted 6 lines
Well, I can see why you are doing this now, but the problems with multi-block metadata makes me think that we really need to know more detail of the owner in the rmap. e.g. that it's directory or attribute data, not user file data and hence we can infer things about expected block sizes, do the correct sort of buffer lookups for invalidation, etc.
I'm not sure we can do that without causing a deadlocking problem, since we lock all the AG headers to rebuild a btree and in general we can't _iget an inode to find out if it's a dir or not. But I have more to say on this in a few paragraphs...
Quoted 4 lines
I'm tending towards "this needs a design doc to explain all this stuff" right now. Code is great, but I'm struggling understand (reverse engineer!) all the algorithms and decisions that have been made from the code...
Working on it.
Quoted 31 lines
Quoted 31 lines
Quoted 30 lines
Quoted 14 lines
fs/xfs/scrub/repair.c +14
-0+/* +* Invalidate buffers for per-AG btree blocks we're dumping. We assume that +* exlist points only to metadata blocks. +*/ +int +xfs_repair_invalidate_blocks ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; +struct xfs_buf *bp; +xfs_agnumber_t agno; +xfs_agblock_t agbno;fs/xfs/scrub/repair.c xfs_repair_subtract_extents( · line 764
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+xfs_agblock_t i; + +for_each_xfs_repair_extent_safe (rex, n, exlist) { + agno =XFS_FSB_TO_AGNO (sc->mp, rex->fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, rex->fsbno); +for (i =0 ; i < rex->len; i++) { + bp =xfs_btree_get_bufs (sc->mp, sc->tp, agno, + agbno + i,0 ); +xfs_trans_binval (sc->tp, bp); + }Again, this is doing things by single blocks. We do have multi-block metadata (inodes, directory blocks, remote attrs) that, if it is already in memory, needs to be treated as multi-block extents. If we don't do that, we'll cause aliasing problems in the buffer cache (see
_xfs_buf_obj_cmp()) and it's all downhill from there.
Quoted 6 lines
Quoted 2 lines
I only recently started testing with filesystems containing multiblock dir/rmt metadata, and this is an unsolved problem. :(
That needs documenting, too. Perhaps explicitly, by rejecting repair requests on filesystems or types that have multi-block constructs until we solve these problems.
Trouble is, remote attr values can have an xfs_buf that spans however many blocks you need to store a full 64k value, and what happens if the rmapbt collides with that? It sorta implies that we can't do invalidation on /any/ filesystem, which is unfortunate....
...unless we have an easy way of finding /any/ buffer that points to a given block? Probably not, since iirc they're indexed by the first disk block number. Hm. I suppose we could use the rmap data to look for anything within 64k of the logical offset of an attr/data rmap overlapping the same block...
...but on second thought we only care about invalidating the buffer if the block belonged to the ag btree we've just killed, right? If there's a multi-block buffer because it's part of a directory or an rmt block then the buffer is clearly owned by someone else and we don't even have to look for that. Likewise, if it's a single-block buffer but the block has some other magic then we don't own it and we should leave it alone.
Quoted 10 lines
Quoted 4 lines
I /think/ the solution is that we need to query the buffer cache to see if it has a buffer for the given disk blocks, and if it matches the btree we're discarding (correct magic/uuid/b_length) then we invalidate it,
I don't think that provides any guarantees. Even ignoring all the problems with invalidation while the buffer is dirty and tracked in the AIL, there's nothing stopping the other code from attempting to re-instantiate the buffer due to some other access. And then we have aliasing problems again....
Well, we /could/ just freeze the fs while we do repairs on any ag btree.
--D
Quoted 11 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 04/22] xfs: add helpers to dispose of old btree blocks after a repair
Quoted 134 lines · Darrick J. Wong
Quoted 38 lines · Dave Chinner
Quoted 12 lines · Darrick J. Wong
Quoted 11 lines · Dave Chinner
Quoted 10 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Now that we've plumbed in the ability to construct a list of dead btree blocks following a repair, add more helpers to dispose of them. This is done by examining the rmapbt -- if the btree was the only owner we can free the block, otherwise it's crosslinked and we can only remove the rmapbt record.
Signed-off-by Darrick J. Wong <[email protected]>---
[...]
Quoted 13 lines
Quoted 13 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 403
Quoted 9 lines
fs/xfs/scrub/repair.c +9
-0+struct xfs_owner_info oinfo; +struct xfs_perag *pag; +int error; + +/* Make sure there's space on the freelist. */ + error =xfs_repair_fix_freelist (sc, true); +if (error) +return error; + pag =xfs_perag_get (sc->mp, sc->sa.agno);Because this is how it quickly gets it gets to silly numbers of lookups. That's two now in this function.
Quoted 8 lines
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 406
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+if (pag->pagf_flcount ==0 ) { +xfs_perag_put (pag); +return -EFSCORRUPTED ;Why is having an empty freelist a problem here? It's an AG thatis completely out of space, but it isn't corruption? And I don't see why an empty freelist prevents us from adding a backs back onto the AGFL?
Quoted 1 line
I think you missed a question :P
Doh, sorry. I don't remember exactly why I put that in there; judging from my notes I think the idea was that if the AG is completely full we'd rather shut down with a corruption signal hoping that the admin will run
xfs_repair.I also don't see why it's necessary now, I'll see what happens if I remove it.
Quoted 21 lines
Quoted 15 lines
Quoted 6 lines
Quoted 6 lines
fs/xfs/scrub/repair.c +6
-0+/* Can we find any other rmappings? */ + error =xfs_rmap_has_other_keys (cur, agbno,1 , oinfo, &has_other_rmap); +if (error) + goto out_cur; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); +Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 471
Quoted 3 lines
fs/xfs/scrub/repair.c +3
-0+/* +* If there are other rmappings, this block is cross linked and must +* not be freed. Remove the reverse mapping and move on. Otherwise, Why do we just remove the reverse mapping if the block cannot be freed? I have my suspicions that this is removing cross-links one by one until there's only one reference left to the extent, but then I ask "how do we know which one is the correct mapping"?
Quoted 5 lines
Right. Prior to calling this function we built a totally new btree with blocks from the freespace, so now we need to remove the rmaps that covered the old btree and/or free the block. The goal is to rebuild /all/ the trees that think they own this block so that we can free the block and not have to care which one is correct.
Quoted 6 lines
Ok, so we've already rebuilt the new btree, and this is removing stale references to cross-linked blocks that have owners different to the one we are currently scanning.
What happens if the cross-linked block is cross-linked within the same owner context?
It won't end up on the reap list in first place, because we scan every block of every object with the same rmap owner to construct sublist. Then we subtract sublist from exlist (which we got from rmap) and only reap the difference.
Quoted 40 lines
Quoted 31 lines
Quoted 14 lines
Quoted 14 lines
fs/xfs/scrub/repair.c +14
-0+struct xfs_scrub_context *sc, + xfs_fsblock_t fsbno, + xfs_extlen_t len, + struct xfs_owner_info *oinfo, + enum xfs_ag_resv_type resv) +{ + struct xfs_mount *mp = sc->mp; +int error =0 ; + +ASSERT (xfs_sb_version_hasrmapbt (&mp->m_sb)); +ASSERT (sc->ip !=NULL ||XFS_FSB_TO_AGNO (mp, fsbno) == sc->sa.agno); + +trace_xfs_repair_dispose_btree_extent (mp,XFS_FSB_TO_AGNO (mp, fsbno), +XFS_FSB_TO_AGBNO (mp, fsbno), len);Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_init_btblock( · line 526
Quoted 5 lines
fs/xfs/scrub/repair.c +5
-0+ +for (; len >0 ; len--, fsbno++) { + error =xfs_repair_dispose_btree_block (sc, fsbno, oinfo, resv); +if (error) +return error;So why do we do this one block at a time, rather than freeing it as an entire extent in one go?
At the moment the
xfs_rmap_has_other_keyshelper can only tell you if there are multiple rmap owners for any part of a given extent. For example, if the rmap records were:(start = 35, len = 3, owner = rmap)
(start = 35, len = 1, owner = refcount)
(start = 37, len = 1, owner = inobt)Quoted 8 lines
Notice how block 35 and 37 are crosslinked, but 36 isn't. A call to
xfs_rmap_has_other_keys(35, 3)will say "yes" but doesn't have a way to signal back that the yes applies to 35 but that the caller should try again with block 36. Doing so would require_has_other_keysto maintain a refcount and to return to the caller any time the refcount changed, and the caller would still have to loop the extent. It's easier to have a dumb loop for the initial implementation and optimize it if we start taking more heat than we'd like on crosslinked filesystems.Quoted 6 lines
Well, I can see why you are doing this now, but the problems with multi-block metadata makes me think that we really need to know more detail of the owner in the rmap. e.g. that it's directory or attribute data, not user file data and hence we can infer things about expected block sizes, do the correct sort of buffer lookups for invalidation, etc.
I'm not sure we can do that without causing a deadlocking problem, since we lock all the AG headers to rebuild a btree and in general we can't
_igetan inode to find out if it's a dir or not. But I have more to say on this in a few paragraphs...
Quoted 6 lines
Quoted 4 lines
I'm tending towards "this needs a design doc to explain all this stuff" right now. Code is great, but I'm struggling understand (reverse engineer!) all the algorithms and decisions that have been made from the code...
Working on it.
Nearly my bedtime, so here's the current draft:
/*
* Reconstructing per-AG Btrees
*
* When a space btree is corrupt, we don't bother trying to fix it.
* Instead, we scan secondary space metadata to derive the records that
* should be in the damaged btree, initialize a fresh btree root, and
* insert the records. Note that for rebuilding the rmapbt we scan all
* the primary data.
*
* However, that leaves the matter of removing all the metadata
* describing the old broken structure. For primary metadata we use the
* rmap data to construct a first bitmap of every extent with a matching
* rmap owner; we then iterate all other metadata structures with the
* same rmap owner to construct a second bitmap of rmaps that cannot be
* removed. We then subtract the second bitmap from the first bitmap
* (first & ~second) to derive the blocks that were used by the old
* btree. These blocks can be reaped.
*
* For rmapbt reconstructions we must use different tactics. First we
* iterate all primary metadata (this excludes the old rmapbt,
* obviously) to generate new rmap records. Then we iterate the new
* rmap records to find the gaps, which should be encompass the free
* space and the old rmapbt blocks. That corresponds to the 'first
* bitmap' of the previous section. The bnobt is iterated to generate
* the second bitmap of the previous section. We then reap the blocks
* corresponding to the difference just like we do for primary data. *
* The comment for xfs_repair_reap_btree_extents will describe the block
* disposal process in more detail.
*/
And later, down by xfs_repair_reap_btree_extents,
/*
* Dispose of btree blocks from the old per-AG btree.
*
* Now that we've constructed a new btree to replace the damaged one, we
* want to dispose of the blocks that (we think) the old btree was
* using. Previously, we used the rmapbt to construct a list of extents
* (@exlist) with the rmap owner corresponding to the tree we rebuilt,
* then subtracted out any other blocks with the same rmap owner that
* are owned by another data structure. In theory the extents in
* @exlist are the old btree's blocks.
*
* Unfortunately, it's possible that the btree was crosslinked with
* other blocks on disk. The rmap data can tell us if there are
* multiple owners, so if the rmapbt says there is an owner of this
* block other than @oinfo, then the block is crosslinked. Remove the
* reverse mapping and continue.
*
* If there is one rmap record, we can free the block, which removes the
* reverse mapping but doesn't add the block to the free space. Our
* repair strategy is to hope the other metadata objects crosslinked on
* this block will be rebuilt (atop different blocks), thereby removing
* all the cross links.
*
* If there are no rmap records at all, we also free the block. If the
* btree being rebuilt lives in the free space (bnobt/cntbt/rmapbt) then
* there isn't supposed to be a rmap record and everything is ok. For
* other btrees there had to have been an rmap entry for the block to
* have ended up on @exlist, so if it's gone now there's something wrong
* and the fs will shut down.
*
* The caller is responsible for locking the AG headers for the entire
* rebuild operation so that nothing else can sneak in and change the AG
* state while we're not looking. We also assume that the caller
* already invalidated any buffers associated with @exlist. */
Later, for the function that finds AG btree roots for agf/agi reconstruction:
/*
* Find the roots of the per-AG btrees described in btree_info. *
* The caller provides information about the btrees to look for by
* passing in an array (@btree_info) of xfs_repair_find_ag_btree with
* the (rmap owner, buf_ops, magic) fields set. The last element of the
* array should have a NULL buf_ops, and the (root, height) fields will
* be set on return if anything is found.
*
* For every rmapbt record matching any of the rmap owners in
* @btree_info, read each block referenced by the rmap record. If the
* block is a btree block from this filesystem matching any of the magic
* numbers and has a level higher than what we've already seen, remember
* the block and the height of the tree required to have such a block.
* When the call completes, we return the highest block we've found for
* each btree description; those should be the roots.
*
* The caller must lock the applicable per-AG header buffers (AGF, AGI)
* to prevent other threads from changing the shape of the btrees that
* we are looking for. It must maintain those locks until it's safe for
* other threads to change the btrees' shapes.
*/
--D
Quoted 88 lines
Quoted 32 lines
Quoted 32 lines
Quoted 31 lines
Quoted 15 lines
fs/xfs/scrub/repair.c +14
-0+/* +* Invalidate buffers for per-AG btree blocks we're dumping. We assume that +* exlist points only to metadata blocks. +*/ +int +xfs_repair_invalidate_blocks ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; +struct xfs_buf *bp; +xfs_agnumber_t agno; +xfs_agblock_t agbno;fs/xfs/scrub/repair.c xfs_repair_subtract_extents( · line 764
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+xfs_agblock_t i; + +for_each_xfs_repair_extent_safe (rex, n, exlist) { + agno =XFS_FSB_TO_AGNO (sc->mp, rex->fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, rex->fsbno); +for (i =0 ; i < rex->len; i++) { + bp =xfs_btree_get_bufs (sc->mp, sc->tp, agno, + agbno + i,0 ); +xfs_trans_binval (sc->tp, bp); + }Again, this is doing things by single blocks. We do have multi-block metadata (inodes, directory blocks, remote attrs) that, if it is already in memory, needs to be treated as multi-block extents. If we don't do that, we'll cause aliasing problems in the buffer cache (see
_xfs_buf_obj_cmp()) and it's all downhill from there.Quoted 6 lines
Quoted 2 lines
I only recently started testing with filesystems containing multiblock dir/rmt metadata, and this is an unsolved problem. :(
That needs documenting, too. Perhaps explicitly, by rejecting repair requests on filesystems or types that have multi-block constructs until we solve these problems.
Trouble is, remote attr values can have an
xfs_bufthat spans however many blocks you need to store a full 64k value, and what happens if the rmapbt collides with that? It sorta implies that we can't do invalidation on /any/ filesystem, which is unfortunate.......unless we have an easy way of finding /any/ buffer that points to a given block? Probably not, since iirc they're indexed by the first disk block number. Hm. I suppose we could use the rmap data to look for anything within 64k of the logical offset of an attr/data rmap overlapping the same block...
...but on second thought we only care about invalidating the buffer if the block belonged to the ag btree we've just killed, right? If there's a multi-block buffer because it's part of a directory or an rmt block then the buffer is clearly owned by someone else and we don't even have to look for that. Likewise, if it's a single-block buffer but the block has some other magic then we don't own it and we should leave it alone.
Quoted 10 lines
Quoted 4 lines
I /think/ the solution is that we need to query the buffer cache to see if it has a buffer for the given disk blocks, and if it matches the btree we're discarding (correct magic/uuid/b_length) then we invalidate it,
I don't think that provides any guarantees. Even ignoring all the problems with invalidation while the buffer is dirty and tracked in the AIL, there's nothing stopping the other code from attempting to re-instantiate the buffer due to some other access. And then we have aliasing problems again....
Well, we /could/ just freeze the fs while we do repairs on any ag btree.
--D
Quoted 11 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v2 4/22 xfs: add helpers to dispose of old btree blocks after a repair
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Now that we've plumbed in the ability to construct a list of dead btree blocks following a repair, add more helpers to dispose of them. This is done by examining the rmapbt -- if the btree was the only owner we can free the block, otherwise it's crosslinked and we can only remove the rmapbt record.
---
v2: document the design of how old btree block disposal is supposed to
work and document some of the limitations of the buffer cache that we can't fix here, and reduce perag_get/put traffic
--- fs/xfs/scrub/repair.c | 251 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 6 + 2 files changed, 257 insertions(+)
fs/xfs/scrub/repair.c +251 -0
@@ -585,3 +585,254 @@ xfs_repair_subtract_extents( }#undef LEFT_ALIGNED#undef RIGHT_ALIGNED + +/* +* Disposal of Blocks from Old per-AG Btrees +* +* Now that we've constructed a new btree to replace the damaged one, we want +* to dispose of the blocks that (we think) the old btree was using. +* Previously, we used the rmapbt to collect the extents (exlist) with the +* rmap owner corresponding to the tree we rebuilt, collected extents for any +* blocks with the same rmap owner that are owned by another data structure +* (sublist), and subtracted sublist from exlist. In theory the extents +* remaining in exlist are the old btree's blocks. +* +* Unfortunately, it's possible that the btree was crosslinked with other +* blocks on disk. The rmap data can tell us if there are multiple owners, so +* if the rmapbt says there is an owner of this block other than @oinfo, then +* the block is crosslinked. Remove the reverse mapping and continue. +* +* If there is one rmap record, we can free the block, which removes the +* reverse mapping but doesn't add the block to the free space. Our repair +* strategy is to hope the other metadata objects crosslinked on this block +* will be rebuilt (atop different blocks), thereby removing all the cross +* links. +* +* If there are no rmap records at all, we also free the block. If the btree +* being rebuilt lives in the free space (bnobt/cntbt/rmapbt) then there isn't +* supposed to be a rmap record and everything is ok. For other btrees there +* had to have been an rmap entry for the block to have ended up on @exlist, +* so if it's gone now there's something wrong and the fs will shut down. +* +* Note: If there are multiple rmap records with only the same rmap owner as +* the btree we're trying to rebuild and the block is indeed owned by another +* data structure with the same rmap owner, then the block will be in sublist +* and therefore doesn't need disposal. If there are multiple rmap records +* with only the same rmap owner but the block is not owned by something with +* the same rmap owner, the block will be freed. +* +* The caller is responsible for locking the AG headers for the entire rebuild +* operation so that nothing else can sneak in and change the AG state while +* we're not looking. We also assume that the caller already invalidated any +* buffers associated with @exlist. +*/ + +/* +* Invalidate buffers for per-AG btree blocks we're dumping. This function +* is not intended for use with file data repairs; we have bunmapi for that. +*/ +int +xfs_repair_invalidate_blocks ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; +struct xfs_buf *bp; +xfs_fsblock_t fsbno; +xfs_agblock_t i; + +/* +* For each block in each extent, see if there's an incore buffer for +* exactly that block; if so, invalidate it. The buffer cache only +* lets us look for one buffer at a time, so we have to look one block +* at a time. Avoid invalidating AG headers and post-EOFS blocks +* because we never own those; and if we can't TRYLOCK the buffer we +* assume it's owned by someone else. +*/ +for_each_xfs_repair_extent_safe (rex, n, exlist) { +for (fsbno = rex->fsbno, i = rex->len; i >0 ; fsbno++, i--) { +/* Skip AG headers and post-EOFS blocks */ +if (!xfs_verify_fsbno (sc->mp, fsbno)) +continue ; + bp =xfs_buf_incore (sc->mp->m_ddev_targp, +XFS_FSB_TO_DADDR (sc->mp, fsbno), +XFS_FSB_TO_BB (sc->mp,1 ),XBF_TRYLOCK ); +if (bp) { +xfs_trans_bjoin (sc->tp, bp); +xfs_trans_binval (sc->tp, bp); + } + } + } + +return 0 ; +} + +/* Ensure the freelist is the correct size. */ +int +xfs_repair_fix_freelist ( +struct xfs_scrub_context *sc, +bool can_shrink) +{ +struct xfs_alloc_arg args = {0 }; + + args.mp = sc->mp; + args.tp = sc->tp; + args.agno = sc->sa.agno; + args.alignment =1 ; + args.pag = sc->sa.pag; + +return xfs_alloc_fix_freelist (&args, + can_shrink ?0 :XFS_ALLOC_FLAG_NOSHRINK ); +} + +/* +* Put a block back on the AGFL. +*/ +STATIC int +xfs_repair_put_freelist ( +struct xfs_scrub_context *sc, +xfs_agblock_t agbno) +{ +struct xfs_owner_info oinfo; +int error; + +/* Make sure there's space on the freelist. */ + error =xfs_repair_fix_freelist (sc, true); +if (error) +return error; + +/* +* Since we're "freeing" a lost block onto the AGFL, we have to +* create an rmap for the block prior to merging it or else other +* parts will break. +*/ +xfs_rmap_ag_owner (&oinfo,XFS_RMAP_OWN_AG ); + error =xfs_rmap_alloc (sc->tp, sc->sa.agf_bp, sc->sa.agno, agbno,1 , + &oinfo); +if (error) +return error; + +/* Put the block on the AGFL. */ + error =xfs_alloc_put_freelist (sc->tp, sc->sa.agf_bp, sc->sa.agfl_bp, + agbno,0 ); +if (error) +return error; +xfs_extent_busy_insert (sc->tp, sc->sa.agno, agbno,1 , +XFS_EXTENT_BUSY_SKIP_DISCARD ); + +return 0 ; +} + +/* Dispose of a single metadata block. */ +STATIC int +xfs_repair_dispose_btree_block ( +struct xfs_scrub_context *sc, +xfs_fsblock_t fsbno, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type resv) +{ +struct xfs_btree_cur *cur; +struct xfs_buf *agf_bp =NULL ; +xfs_agnumber_t agno; +xfs_agblock_t agbno; +bool has_other_rmap; +int error; + + agno =XFS_FSB_TO_AGNO (sc->mp, fsbno); + agbno =XFS_FSB_TO_AGBNO (sc->mp, fsbno); + +/* +* If we are repairing per-inode metadata, we need to read in the AGF +* buffer. Otherwise, we're repairing a per-AG structure, so reuse +* the AGF buffer that the setup functions already grabbed. +*/ +if (sc->ip) { + error =xfs_alloc_read_agf (sc->mp, sc->tp, agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + }else { + agf_bp = sc->sa.agf_bp; + } + cur =xfs_rmapbt_init_cursor (sc->mp, sc->tp, agf_bp, agno); + +/* Can we find any other rmappings? */ + error =xfs_rmap_has_other_keys (cur, agbno,1 , oinfo, &has_other_rmap); +if (error) + goto out_cur; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + +/* +* If there are other rmappings, this block is cross linked and must +* not be freed. Remove the reverse mapping and move on. Otherwise, +* we were the only owner of the block, so free the extent, which will +* also remove the rmap. +* +* XXX: XFS doesn't support detecting the case where a single block +* metadata structure is crosslinked with a multi-block structure +* because the buffer cache doesn't detect aliasing problems, so we +* can't fix 100% of crosslinking problems (yet). The verifiers will +* blow on writeout, the filesystem will shut down, and the admin gets +* to run xfs_repair. +*/ +if (has_other_rmap) + error =xfs_rmap_free (sc->tp, agf_bp, agno, agbno,1 , oinfo); +else if (resv ==XFS_AG_RESV_AGFL ) + error =xfs_repair_put_freelist (sc, agbno); +else + error =xfs_free_extent (sc->tp, fsbno,1 , oinfo, resv); +if (agf_bp != sc->sa.agf_bp) +xfs_trans_brelse (sc->tp, agf_bp); +if (error) +return error; + +if (sc->ip) +return xfs_trans_roll_inode (&sc->tp, sc->ip); +return xfs_repair_roll_ag_trans (sc); + +out_cur: +xfs_btree_del_cursor (cur,XFS_BTREE_ERROR ); +if (agf_bp != sc->sa.agf_bp) +xfs_trans_brelse (sc->tp, agf_bp); +return error; +} + +/* Dispose of btree blocks from an old per-AG btree. */ +int +xfs_repair_reap_btree_extents ( +struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_owner_info *oinfo, +enum xfs_ag_resv_type type) +{ +struct xfs_repair_extent *rex; +struct xfs_repair_extent *n; +int error =0 ; + +ASSERT (xfs_sb_version_hasrmapbt (&sc->mp->m_sb)); + +/* Dispose of every block from the old btree. */ +for_each_xfs_repair_extent_safe (rex, n, exlist) { +ASSERT (sc->ip !=NULL || +XFS_FSB_TO_AGNO (sc->mp, rex->fsbno) == sc->sa.agno); + +trace_xfs_repair_dispose_btree_extent (sc->mp, +XFS_FSB_TO_AGNO (sc->mp, rex->fsbno), +XFS_FSB_TO_AGBNO (sc->mp, rex->fsbno), rex->len); + +for (; rex->len >0 ; rex->len--, rex->fsbno++) { + error =xfs_repair_dispose_btree_block (sc, rex->fsbno, + oinfo, type); +if (error) + goto out; + } +list_del (&rex->list); +kmem_free (rex); + } + +out: +xfs_repair_cancel_btree_extents (sc, exlist); +return error; +}
fs/xfs/scrub/repair.h +6 -0
@@ -70,6 +70,12 @@ void xfs_repair_cancel_btree_extents(struct xfs_scrub_context *sc,int xfs_repair_subtract_extents (struct xfs_scrub_context *sc,struct xfs_repair_extent_list *exlist,struct xfs_repair_extent_list *sublist); +int xfs_repair_fix_freelist (struct xfs_scrub_context *sc,bool can_shrink); +int xfs_repair_invalidate_blocks (struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *btlist); +int xfs_repair_reap_btree_extents (struct xfs_scrub_context *sc, +struct xfs_repair_extent_list *exlist, +struct xfs_owner_info *oinfo,enum xfs_ag_resv_type type);/* Metadata repairers */
Re: [PATCH v2 04/22] xfs: add helpers to dispose of old btree blocks after a repair
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Now that we've plumbed in the ability to construct a list of dead btree blocks following a repair, add more helpers to dispose of them. This is done by examining the rmapbt -- if the btree was the only owner we can free the block, otherwise it's crosslinked and we can only remove the rmapbt record.
Quoted 5 lines
Signed-off-by Darrick J. Wong <[email protected]>---
v2: document the design of how old btree block disposal is supposed to
work and document some of the limitations of the buffer cache that we can't fix here, and reduce perag_get/put traffic
Yup, I can accept those limitations for the initial implementation. Gotta leave something for you to work on once it's been merged, right?
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH v2 04/22] xfs: add helpers to dispose of old btree blocks after a repair
Quoted 15 lines · Dave Chinner
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Now that we've plumbed in the ability to construct a list of dead btree blocks following a repair, add more helpers to dispose of them. This is done by examining the rmapbt -- if the btree was the only owner we can free the block, otherwise it's crosslinked and we can only remove the rmapbt record.
Quoted 5 lines
Signed-off-by Darrick J. Wong <[email protected]>---
v2: document the design of how old btree block disposal is supposed to
work and document some of the limitations of the buffer cache that we can't fix here, and reduce perag_get/put traffic
Quoted 3 lines
Yup, I can accept those limitations for the initial implementation. Gotta leave something for you to work on once it's been merged, right?
Yeah. I'd spec'd out adding a bitmap to each AG's xfs_buf cache to track non-stale cached buffers and scream if someone tries to add an overlapping buffer, but then started talking to Dan last week about connecting XFS to pmem poison notifications.
If the metadata block using the poisoned pmem is cached in dram then we'd want to find the associated xfs_buf and write it back out. For that we'd need full block -> buf mapping abilities (and not just a bitmap), which will likely bloat the radix tree significantly. This became a mess of ink in my notebook so I gave up, for now.
--D
Quoted 9 lines
Reviewed-by Dave Chinner <[email protected]>Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH 5/22 xfs: recover AG btree roots from rmap data
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add a helper function to help us recover btree roots from the rmap data. Callers pass in a list of rmap owner codes, buffer ops, and magic numbers. We iterate the rmap records looking for owner matches, and then read the matching blocks to see if the magic number & uuid match. If so, we then read-verify the block, and if that passes then we retain a pointer to the block with the highest level, assuming that by the end of the call we will have found the root. This will be used to reset the AGF/AGI btree root fields during their rebuild procedures.
--- fs/xfs/scrub/repair.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 20 ++++++ 2 files changed, 198 insertions(+)
fs/xfs/scrub/repair.c +178 -0
@@ -766,3 +766,181 @@ xfs_repair_invalidate_blocks(return 0 ; } + +/* See if our block is in the AGFL. */ +STATIC int +xfs_repair_findroot_agfl_walk ( +struct xfs_mount *mp, +xfs_agblock_t bno, +void *priv) +{ +xfs_agblock_t *agbno = priv; + +return (*agbno == bno) ?XFS_BTREE_QUERY_RANGE_ABORT :0 ; +} + +struct xfs_repair_findroot { +struct xfs_scrub_context *sc; +struct xfs_buf *agfl_bp; +struct xfs_agf *agf; +struct xfs_repair_find_ag_btree *btree_info; +}; + +/* Does this block match the btree information passed in? */ +STATIC int +xfs_repair_findroot_block ( +struct xfs_repair_findroot *ri, +struct xfs_repair_find_ag_btree *fab, +uint64_t owner, +xfs_agblock_t agbno, +bool *found_it) +{ +struct xfs_mount *mp = ri->sc->mp; +struct xfs_buf *bp; +struct xfs_btree_block *btblock; +xfs_daddr_t daddr; +int error; + +/* rmap owner match? */ +if (owner != fab->rmap_owner) +return 0 ; + + daddr =XFS_AGB_TO_DADDR (mp, ri->sc->sa.agno, agbno); + +/* +* Blocks in the AGFL have stale contents that might just happen to +* have a matching magic and uuid. We don't want to pull these blocks +* in as part of a tree root, so we have to filter out the AGFL stuff +* here. If the AGFL looks insane we'll just refuse to repair. +*/ +if (owner ==XFS_RMAP_OWN_AG ) { + error =xfs_agfl_walk (mp, ri->agf, ri->agfl_bp, + xfs_repair_findroot_agfl_walk, &agbno); +if (error ==XFS_BTREE_QUERY_RANGE_ABORT ) +return 0 ; +if (error) +return error; + } + + error =xfs_trans_read_buf (mp, ri->sc->tp, mp->m_ddev_targp, daddr, + mp->m_bsize,0 , &bp,NULL ); +if (error) +return error; + +/* +* Does this look like a block matching our fs and higher than any +* other block we've found so far? If so, reattach buffer verifiers +* so the AIL won't complain if the buffer is also dirty. +*/ + btblock =XFS_BUF_TO_BLOCK (bp); +if (be32_to_cpu (btblock->bb_magic) != fab->magic) + goto out; +if (xfs_sb_version_hascrc (&mp->m_sb) && + !uuid_equal (&btblock->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid)) + goto out; + bp->b_ops = fab->buf_ops; + +/* Ignore this block if it's lower in the tree than we've seen. */ +if (fab->root !=NULLAGBLOCK && +xfs_btree_get_level (btblock) < fab->height) + goto out; + +/* Make sure we pass the verifiers. */ + bp->b_ops->verify_read (bp); +if (bp->b_error) + goto out; + fab->root = agbno; + fab->height =xfs_btree_get_level (btblock) +1 ; + *found_it = true; + +trace_xfs_repair_findroot_block (mp, ri->sc->sa.agno, agbno, +be32_to_cpu (btblock->bb_magic), fab->height -1 ); +out: +xfs_trans_brelse (ri->sc->tp, bp); +return error; +} + +/* +* Do any of the blocks in this rmap record match one of the btrees we're +* looking for? +*/ +STATIC int +xfs_repair_findroot_rmap ( +struct xfs_btree_cur *cur, +struct xfs_rmap_irec *rec, +void *priv) +{ +struct xfs_repair_findroot *ri = priv; +struct xfs_repair_find_ag_btree *fab; +xfs_agblock_t b; +bool found_it; +int error =0 ; + +/* Ignore anything that isn't AG metadata. */ +if (!XFS_RMAP_NON_INODE_OWNER (rec->rm_owner)) +return 0 ; + +/* Otherwise scan each block + btree type. */ +for (b =0 ; b < rec->rm_blockcount; b++) { + found_it = false; +for (fab = ri->btree_info; fab->buf_ops; fab++) { + error =xfs_repair_findroot_block (ri, fab, + rec->rm_owner, rec->rm_startblock + b, + &found_it); +if (error) +return error; +if (found_it) +break ; + } + } + +return 0 ; +} + +/* +* Find the roots of the per-AG btrees described in btree_info. +* +* The caller provides information about the btrees to look for by passing in +* an array (@btree_info) of xfs_repair_find_ag_btree with the (rmap owner, +* buf_ops, magic) fields set. The last element of the array should have a +* NULL buf_ops, and the (root, height) fields will be set on return if +* anything is found. +* +* For every rmapbt record matching any of the rmap owners in @btree_info, +* read each block referenced by the rmap record. If the block is a btree +* block from this filesystem matching any of the magic numbers and has a +* level higher than what we've already seen, remember the block and the +* height of the tree required to have such a block. When the call completes, +* we return the highest block we've found for each btree description; those +* should be the roots. +*/ +int +xfs_repair_find_ag_btree_roots ( +struct xfs_scrub_context *sc, +struct xfs_buf *agf_bp, +struct xfs_repair_find_ag_btree *btree_info, +struct xfs_buf *agfl_bp) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_repair_findroot ri; +struct xfs_repair_find_ag_btree *fab; +struct xfs_btree_cur *cur; +int error; + + ri.sc = sc; + ri.btree_info = btree_info; + ri.agf =XFS_BUF_TO_AGF (agf_bp); + ri.agfl_bp = agfl_bp; +for (fab = btree_info; fab->buf_ops; fab++) { +ASSERT (agfl_bp || fab->rmap_owner !=XFS_RMAP_OWN_AG ); +ASSERT (XFS_RMAP_NON_INODE_OWNER (fab->rmap_owner)); + fab->root =NULLAGBLOCK ; + fab->height =0 ; + } + + cur =xfs_rmapbt_init_cursor (mp, sc->tp, agf_bp, sc->sa.agno); + error =xfs_rmap_query_all (cur, xfs_repair_findroot_rmap, &ri); +xfs_btree_del_cursor (cur, error ?XFS_BTREE_ERROR :XFS_BTREE_NOERROR ); + +return error; +}
fs/xfs/scrub/repair.h +20 -0
@@ -77,6 +77,26 @@ int xfs_repair_fix_freelist(struct xfs_scrub_context *sc, bool can_shrink);int xfs_repair_invalidate_blocks (struct xfs_scrub_context *sc,struct xfs_repair_extent_list *btlist); +struct xfs_repair_find_ag_btree { +/* in: rmap owner of the btree we're looking for */ +uint64_t rmap_owner; + +/* in: buffer ops */ +const struct xfs_buf_ops *buf_ops; + +/* in: magic number of the btree */ +uint32_t magic; + +/* out: the highest btree block found and the tree height */ +xfs_agblock_t root; +unsigned int height; +}; + +int xfs_repair_find_ag_btree_roots (struct xfs_scrub_context *sc, +struct xfs_buf *agf_bp, +struct xfs_repair_find_ag_btree *btree_info, +struct xfs_buf *agfl_bp); +/* Metadata repairers */ int xfs_repair_probe (struct xfs_scrub_context *sc);
Re: [PATCH 05/22] xfs: recover AG btree roots from rmap data
Quoted 54 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper function to help us recover btree roots from the rmap data. Callers pass in a list of rmap owner codes, buffer ops, and magic numbers. We iterate the rmap records looking for owner matches, and then read the matching blocks to see if the magic number & uuid match. If so, we then read-verify the block, and if that passes then we retain a pointer to the block with the highest level, assuming that by the end of the call we will have found the root. This will be used to reset the AGF/AGI btree root fields during their rebuild procedures.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 20 ++++++ 2 files changed, 198 insertions(+)fs/xfs/scrub/repair.c +28
-0@@ -766,3 +766,181 @@ xfs_repair_invalidate_blocks(return 0 ; } + +/* See if our block is in the AGFL. */ +STATIC int +xfs_repair_findroot_agfl_walk ( +struct xfs_mount *mp, +xfs_agblock_t bno, +void *priv) +{ +xfs_agblock_t *agbno = priv; + +return (*agbno == bno) ?XFS_BTREE_QUERY_RANGE_ABORT :0 ; +} + +struct xfs_repair_findroot { +struct xfs_scrub_context *sc; +struct xfs_buf *agfl_bp; +struct xfs_agf *agf; +struct xfs_repair_find_ag_btree *btree_info; +}; + +/* Does this block match the btree information passed in? */ +STATIC int +xfs_repair_findroot_block ( +struct xfs_repair_findroot *ri, +struct xfs_repair_find_ag_btree *fab, +uint64_t owner, +xfs_agblock_t agbno, +bool *found_it)
fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 806
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+{ +struct xfs_mount *mp = ri->sc->mp; +struct xfs_buf *bp; +struct xfs_btree_block *btblock; +xfs_daddr_t daddr; +int error; + +/* rmap owner match? */ +if (owner != fab->rmap_owner) +return 0 ;
I'd put that in the caller - it's iterating the fab array and it knows the owner it is looking for, so I think it makes more sense to go there....
Quoted 43 lines
fs/xfs/scrub/repair.c +43
-0+ + daddr =XFS_AGB_TO_DADDR (mp, ri->sc->sa.agno, agbno); + +/* +* Blocks in the AGFL have stale contents that might just happen to +* have a matching magic and uuid. We don't want to pull these blocks +* in as part of a tree root, so we have to filter out the AGFL stuff +* here. If the AGFL looks insane we'll just refuse to repair. +*/ +if (owner ==XFS_RMAP_OWN_AG ) { + error =xfs_agfl_walk (mp, ri->agf, ri->agfl_bp, + xfs_repair_findroot_agfl_walk, &agbno); +if (error ==XFS_BTREE_QUERY_RANGE_ABORT ) +return 0 ; +if (error) +return error; + } + + error =xfs_trans_read_buf (mp, ri->sc->tp, mp->m_ddev_targp, daddr, + mp->m_bsize,0 , &bp,NULL ); +if (error) +return error; + +/* +* Does this look like a block matching our fs and higher than any +* other block we've found so far? If so, reattach buffer verifiers +* so the AIL won't complain if the buffer is also dirty. +*/ + btblock =XFS_BUF_TO_BLOCK (bp); +if (be32_to_cpu (btblock->bb_magic) != fab->magic) + goto out; +if (xfs_sb_version_hascrc (&mp->m_sb) && + !uuid_equal (&btblock->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid)) + goto out; + bp->b_ops = fab->buf_ops; + +/* Ignore this block if it's lower in the tree than we've seen. */ +if (fab->root !=NULLAGBLOCK && +xfs_btree_get_level (btblock) < fab->height) + goto out; + +/* Make sure we pass the verifiers. */ + bp->b_ops->verify_read (bp);
fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 859
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+if (bp->b_error) + goto out; + fab->root = agbno; + fab->height =xfs_btree_get_level (btblock) +1 ; + *found_it = true; + +trace_xfs_repair_findroot_block (mp, ri->sc->sa.agno, agbno, +be32_to_cpu (btblock->bb_magic), fab->height -1 ); +out: +xfs_trans_brelse (ri->sc->tp, bp);
So we release the buffer once we've found it, which also unlocks it. That means when we come back to it later, it may have been accessed and changed by something else and no longer be the block we are looking for. How do you protect against this sort of race given we are unlocking the buffer? Perhaps it should be held on the fab structure, and released when a better candidate is found?
Quoted 20 lines
fs/xfs/scrub/repair.c +20
-0+return error; +} + +/* +* Do any of the blocks in this rmap record match one of the btrees we're +* looking for? +*/ +STATIC int +xfs_repair_findroot_rmap ( +struct xfs_btree_cur *cur, +struct xfs_rmap_irec *rec, +void *priv) +{ +struct xfs_repair_findroot *ri = priv; +struct xfs_repair_find_ag_btree *fab; +xfs_agblock_t b; +bool found_it; +int error =0 ; + +/* Ignore anything that isn't AG metadata. */
fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 889
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+if (!XFS_RMAP_NON_INODE_OWNER (rec->rm_owner)) +return 0 ; + +/* Otherwise scan each block + btree type. */ +for (b =0 ; b < rec->rm_blockcount; b++) { + found_it = false; +for (fab = ri->btree_info; fab->buf_ops; fab++) { + error =xfs_repair_findroot_block (ri, fab, + rec->rm_owner, rec->rm_startblock + b, + &found_it);
This loop is where I think the fab->owner/rec->rm_owner check should go....
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 05/22] xfs: recover AG btree roots from rmap data
Quoted 61 lines · Dave Chinner
Quoted 60 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper function to help us recover btree roots from the rmap data. Callers pass in a list of rmap owner codes, buffer ops, and magic numbers. We iterate the rmap records looking for owner matches, and then read the matching blocks to see if the magic number & uuid match. If so, we then read-verify the block, and if that passes then we retain a pointer to the block with the highest level, assuming that by the end of the call we will have found the root. This will be used to reset the AGF/AGI btree root fields during their rebuild procedures.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 20 ++++++ 2 files changed, 198 insertions(+)fs/xfs/scrub/repair.c +34
-0@@ -766,3 +766,181 @@ xfs_repair_invalidate_blocks(return 0 ; } + +/* See if our block is in the AGFL. */ +STATIC int +xfs_repair_findroot_agfl_walk ( +struct xfs_mount *mp, +xfs_agblock_t bno, +void *priv) +{ +xfs_agblock_t *agbno = priv; + +return (*agbno == bno) ?XFS_BTREE_QUERY_RANGE_ABORT :0 ; +} + +struct xfs_repair_findroot { +struct xfs_scrub_context *sc; +struct xfs_buf *agfl_bp; +struct xfs_agf *agf; +struct xfs_repair_find_ag_btree *btree_info; +}; + +/* Does this block match the btree information passed in? */ +STATIC int +xfs_repair_findroot_block ( +struct xfs_repair_findroot *ri, +struct xfs_repair_find_ag_btree *fab, +uint64_t owner, +xfs_agblock_t agbno, +bool *found_it) +{ +struct xfs_mount *mp = ri->sc->mp; +struct xfs_buf *bp; +struct xfs_btree_block *btblock; +xfs_daddr_t daddr; +int error;
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 806
Quoted 4 lines
fs/xfs/scrub/repair.c +4
-0+ +/* rmap owner match? */ +if (owner != fab->rmap_owner) +return 0 ;I'd put that in the caller - it's iterating the fab array and it knows the owner it is looking for, so I think it makes more sense to go there....
Ok.
Quoted 52 lines
Quoted 52 lines
fs/xfs/scrub/repair.c +52
-0+ + daddr =XFS_AGB_TO_DADDR (mp, ri->sc->sa.agno, agbno); + +/* +* Blocks in the AGFL have stale contents that might just happen to +* have a matching magic and uuid. We don't want to pull these blocks +* in as part of a tree root, so we have to filter out the AGFL stuff +* here. If the AGFL looks insane we'll just refuse to repair. +*/ +if (owner ==XFS_RMAP_OWN_AG ) { + error =xfs_agfl_walk (mp, ri->agf, ri->agfl_bp, + xfs_repair_findroot_agfl_walk, &agbno); +if (error ==XFS_BTREE_QUERY_RANGE_ABORT ) +return 0 ; +if (error) +return error; + } + + error =xfs_trans_read_buf (mp, ri->sc->tp, mp->m_ddev_targp, daddr, + mp->m_bsize,0 , &bp,NULL ); +if (error) +return error; + +/* +* Does this look like a block matching our fs and higher than any +* other block we've found so far? If so, reattach buffer verifiers +* so the AIL won't complain if the buffer is also dirty. +*/ + btblock =XFS_BUF_TO_BLOCK (bp); +if (be32_to_cpu (btblock->bb_magic) != fab->magic) + goto out; +if (xfs_sb_version_hascrc (&mp->m_sb) && + !uuid_equal (&btblock->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid)) + goto out; + bp->b_ops = fab->buf_ops; + +/* Ignore this block if it's lower in the tree than we've seen. */ +if (fab->root !=NULLAGBLOCK && +xfs_btree_get_level (btblock) < fab->height) + goto out; + +/* Make sure we pass the verifiers. */ + bp->b_ops->verify_read (bp); +if (bp->b_error) + goto out; + fab->root = agbno; + fab->height =xfs_btree_get_level (btblock) +1 ; + *found_it = true; + +trace_xfs_repair_findroot_block (mp, ri->sc->sa.agno, agbno, +be32_to_cpu (btblock->bb_magic), fab->height -1 ); +out:
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 859
Quoted 1 line
fs/xfs/scrub/repair.c +1
-0+xfs_trans_brelse (ri->sc->tp, bp);So we release the buffer once we've found it, which also unlocks it. That means when we come back to it later, it may have been accessed and changed by something else and no longer be the block we are looking for. How do you protect against this sort of race given we are unlocking the buffer? Perhaps it should be held on the fab structure, and released when a better candidate is found?
The two callers of this function are the AGF and AGI repair functions. AGF repair holds the locked AGF buffer, and AGI repair holds the locked AGF & AGI buffers, which should be enough to prevent anyone else from accessing the AG btrees. They keep the all the AG header buffers locked until they're completely finished with rebuilding the headers (i.e. xfs_scrub_teardown) and it's safe for the shape to change.
How about I add to the comment for this function:
/*
* The caller must lock the applicable per-AG header buffers (AGF, AGI)
* to prevent other threads from changing the shape of the btrees that
* we are looking for. It must maintain those locks until it's safe for
* other threads to change the btrees' shapes.
*/
--D
Quoted 44 lines
Quoted 20 lines
fs/xfs/scrub/repair.c +20
-0+return error; +} + +/* +* Do any of the blocks in this rmap record match one of the btrees we're +* looking for? +*/ +STATIC int +xfs_repair_findroot_rmap ( +struct xfs_btree_cur *cur, +struct xfs_rmap_irec *rec, +void *priv) +{ +struct xfs_repair_findroot *ri = priv; +struct xfs_repair_find_ag_btree *fab; +xfs_agblock_t b; +bool found_it; +int error =0 ; + +/* Ignore anything that isn't AG metadata. */ fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 889
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+if (!XFS_RMAP_NON_INODE_OWNER (rec->rm_owner)) +return 0 ; + +/* Otherwise scan each block + btree type. */ +for (b =0 ; b < rec->rm_blockcount; b++) { + found_it = false; +for (fab = ri->btree_info; fab->buf_ops; fab++) { + error =xfs_repair_findroot_block (ri, fab, + rec->rm_owner, rec->rm_startblock + b, + &found_it);This loop is where I think the fab->owner/rec->rm_owner check should go....
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH 05/22] xfs: recover AG btree roots from rmap data
Ok, you can add my review here as well:
Reviewed by: Allison Henderson <[email protected]>
Thx!
Quoted 201 lines · Darrick J. Wong
Quoted 61 lines · Dave Chinner
Quoted 60 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper function to help us recover btree roots from the rmap data. Callers pass in a list of rmap owner codes, buffer ops, and magic numbers. We iterate the rmap records looking for owner matches, and then read the matching blocks to see if the magic number & uuid match. If so, we then read-verify the block, and if that passes then we retain a pointer to the block with the highest level, assuming that by the end of the call we will have found the root. This will be used to reset the AGF/AGI btree root fields during their rebuild procedures.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 178 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 20 ++++++ 2 files changed, 198 insertions(+)fs/xfs/scrub/repair.c +34
-0@@ -766,3 +766,181 @@ xfs_repair_invalidate_blocks(return 0 ; } + +/* See if our block is in the AGFL. */ +STATIC int +xfs_repair_findroot_agfl_walk ( +struct xfs_mount *mp, +xfs_agblock_t bno, +void *priv) +{ +xfs_agblock_t *agbno = priv; + +return (*agbno == bno) ?XFS_BTREE_QUERY_RANGE_ABORT :0 ; +} + +struct xfs_repair_findroot { +struct xfs_scrub_context *sc; +struct xfs_buf *agfl_bp; +struct xfs_agf *agf; +struct xfs_repair_find_ag_btree *btree_info; +}; + +/* Does this block match the btree information passed in? */ +STATIC int +xfs_repair_findroot_block ( +struct xfs_repair_findroot *ri, +struct xfs_repair_find_ag_btree *fab, +uint64_t owner, +xfs_agblock_t agbno, +bool *found_it) +{ +struct xfs_mount *mp = ri->sc->mp; +struct xfs_buf *bp; +struct xfs_btree_block *btblock; +xfs_daddr_t daddr; +int error;Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 806
Quoted 4 lines
fs/xfs/scrub/repair.c +4
-0+ +/* rmap owner match? */ +if (owner != fab->rmap_owner) +return 0 ;I'd put that in the caller - it's iterating the fab array and it knows the owner it is looking for, so I think it makes more sense to go there....
Ok.
Quoted 52 lines
Quoted 52 lines
fs/xfs/scrub/repair.c +52
-0+ + daddr =XFS_AGB_TO_DADDR (mp, ri->sc->sa.agno, agbno); + +/* +* Blocks in the AGFL have stale contents that might just happen to +* have a matching magic and uuid. We don't want to pull these blocks +* in as part of a tree root, so we have to filter out the AGFL stuff +* here. If the AGFL looks insane we'll just refuse to repair. +*/ +if (owner ==XFS_RMAP_OWN_AG ) { + error =xfs_agfl_walk (mp, ri->agf, ri->agfl_bp, + xfs_repair_findroot_agfl_walk, &agbno); +if (error ==XFS_BTREE_QUERY_RANGE_ABORT ) +return 0 ; +if (error) +return error; + } + + error =xfs_trans_read_buf (mp, ri->sc->tp, mp->m_ddev_targp, daddr, + mp->m_bsize,0 , &bp,NULL ); +if (error) +return error; + +/* +* Does this look like a block matching our fs and higher than any +* other block we've found so far? If so, reattach buffer verifiers +* so the AIL won't complain if the buffer is also dirty. +*/ + btblock =XFS_BUF_TO_BLOCK (bp); +if (be32_to_cpu (btblock->bb_magic) != fab->magic) + goto out; +if (xfs_sb_version_hascrc (&mp->m_sb) && + !uuid_equal (&btblock->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid)) + goto out; + bp->b_ops = fab->buf_ops; + +/* Ignore this block if it's lower in the tree than we've seen. */ +if (fab->root !=NULLAGBLOCK && +xfs_btree_get_level (btblock) < fab->height) + goto out; + +/* Make sure we pass the verifiers. */ + bp->b_ops->verify_read (bp); +if (bp->b_error) + goto out; + fab->root = agbno; + fab->height =xfs_btree_get_level (btblock) +1 ; + *found_it = true; + +trace_xfs_repair_findroot_block (mp, ri->sc->sa.agno, agbno, +be32_to_cpu (btblock->bb_magic), fab->height -1 ); +out:Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 859
Quoted 1 line
fs/xfs/scrub/repair.c +1
-0+xfs_trans_brelse (ri->sc->tp, bp);So we release the buffer once we've found it, which also unlocks it. That means when we come back to it later, it may have been accessed and changed by something else and no longer be the block we are looking for. How do you protect against this sort of race given we are unlocking the buffer? Perhaps it should be held on the fab structure, and released when a better candidate is found?
The two callers of this function are the AGF and AGI repair functions. AGF repair holds the locked AGF buffer, and AGI repair holds the locked AGF & AGI buffers, which should be enough to prevent anyone else from accessing the AG btrees. They keep the all the AG header buffers locked until they're completely finished with rebuilding the headers (i.e.
xfs_scrub_teardown) and it's safe for the shape to change.How about I add to the comment for this function:
/*
* The caller must lock the applicable per-AG header buffers (AGF, AGI)
* to prevent other threads from changing the shape of the btrees that
* we are looking for. It must maintain those locks until it's safe for
* other threads to change the btrees' shapes.
*/--D
Quoted 34 lines
Quoted 20 lines
fs/xfs/scrub/repair.c +20
-0+return error; +} + +/* +* Do any of the blocks in this rmap record match one of the btrees we're +* looking for? +*/ +STATIC int +xfs_repair_findroot_rmap ( +struct xfs_btree_cur *cur, +struct xfs_rmap_irec *rec, +void *priv) +{ +struct xfs_repair_findroot *ri = priv; +struct xfs_repair_find_ag_btree *fab; +xfs_agblock_t b; +bool found_it; +int error =0 ; + +/* Ignore anything that isn't AG metadata. */ fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 889
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+if (!XFS_RMAP_NON_INODE_OWNER (rec->rm_owner)) +return 0 ; + +/* Otherwise scan each block + btree type. */ +for (b =0 ; b < rec->rm_blockcount; b++) { + found_it = false; +for (fab = ri->btree_info; fab->buf_ops; fab++) { + error =xfs_repair_findroot_block (ri, fab, + rec->rm_owner, rec->rm_startblock + b, + &found_it);This loop is where I think the fab->owner/rec->rm_owner check should go....
Quoted 10 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwIBAg&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=rBPwCYkOfcA0Ljv7VWlTAZN8EPqKEvw5_1z9fzrl1S4&s=9iowSgTX47PO1FTR1YOuG2QOVgfa2kVqnVNbwqcqsI8&e=--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwIBAg&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=rBPwCYkOfcA0Ljv7VWlTAZN8EPqKEvw5_1z9fzrl1S4&s=9iowSgTX47PO1FTR1YOuG2QOVgfa2kVqnVNbwqcqsI8&e=
Re: [PATCH 05/22] xfs: recover AG btree roots from rmap data
In reply to Darrick J. WongQuoted 14 lines · Darrick J. Wong
Quoted 13 lines · Dave Chinner
Quoted 12 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper function to help us recover btree roots from the rmap data. Callers pass in a list of rmap owner codes, buffer ops, and magic numbers. We iterate the rmap records looking for owner matches, and then read the matching blocks to see if the magic number & uuid match. If so, we then read-verify the block, and if that passes then we retain a pointer to the block with the highest level, assuming that by the end of the call we will have found the root. This will be used to reset the AGF/AGI btree root fields during their rebuild procedures.
Signed-off-by Darrick J. Wong <[email protected]>
.....
Quoted 32 lines
Quoted 16 lines
Quoted 16 lines
fs/xfs/scrub/repair.c +16
-0+/* Ignore this block if it's lower in the tree than we've seen. */ +if (fab->root !=NULLAGBLOCK && +xfs_btree_get_level (btblock) < fab->height) + goto out; + +/* Make sure we pass the verifiers. */ + bp->b_ops->verify_read (bp); +if (bp->b_error) + goto out; + fab->root = agbno; + fab->height =xfs_btree_get_level (btblock) +1 ; + *found_it = true; + +trace_xfs_repair_findroot_block (mp, ri->sc->sa.agno, agbno, +be32_to_cpu (btblock->bb_magic), fab->height -1 ); +out:Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 859
Quoted 1 line
fs/xfs/scrub/repair.c +1
-0+xfs_trans_brelse (ri->sc->tp, bp);So we release the buffer once we've found it, which also unlocks it. That means when we come back to it later, it may have been accessed and changed by something else and no longer be the block we are looking for. How do you protect against this sort of race given we are unlocking the buffer? Perhaps it should be held on the fab structure, and released when a better candidate is found?
The two callers of this function are the AGF and AGI repair functions. AGF repair holds the locked AGF buffer, and AGI repair holds the locked AGF & AGI buffers, which should be enough to prevent anyone else from accessing the AG btrees. They keep the all the AG header buffers locked until they're completely finished with rebuilding the headers (i.e.
xfs_scrub_teardown) and it's safe for the shape to change.
Quoted 8 lines
How about I add to the comment for this function:
/*
* The caller must lock the applicable per-AG header buffers (AGF, AGI)
* to prevent other threads from changing the shape of the btrees that
* we are looking for. It must maintain those locks until it's safe for
* other threads to change the btrees' shapes.
*/
That's helpful. :) Can you sprinkle some checks like ASSERT(xfs_buf_islocked(agbp)) to remind readers of the leaf/callback functions that they expect the AGF/AGI to be locked on entry?
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 05/22] xfs: recover AG btree roots from rmap data
Quoted 57 lines · Dave Chinner
Quoted 14 lines · Darrick J. Wong
Quoted 13 lines · Dave Chinner
Quoted 12 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper function to help us recover btree roots from the rmap data. Callers pass in a list of rmap owner codes, buffer ops, and magic numbers. We iterate the rmap records looking for owner matches, and then read the matching blocks to see if the magic number & uuid match. If so, we then read-verify the block, and if that passes then we retain a pointer to the block with the highest level, assuming that by the end of the call we will have found the root. This will be used to reset the AGF/AGI btree root fields during their rebuild procedures.
Signed-off-by Darrick J. Wong <[email protected]>.....
Quoted 32 lines
Quoted 16 lines
Quoted 16 lines
fs/xfs/scrub/repair.c +16
-0+/* Ignore this block if it's lower in the tree than we've seen. */ +if (fab->root !=NULLAGBLOCK && +xfs_btree_get_level (btblock) < fab->height) + goto out; + +/* Make sure we pass the verifiers. */ + bp->b_ops->verify_read (bp); +if (bp->b_error) + goto out; + fab->root = agbno; + fab->height =xfs_btree_get_level (btblock) +1 ; + *found_it = true; + +trace_xfs_repair_findroot_block (mp, ri->sc->sa.agno, agbno, +be32_to_cpu (btblock->bb_magic), fab->height -1 ); +out:Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_invalidate_blocks( · line 859
Quoted 1 line
fs/xfs/scrub/repair.c +1
-0+xfs_trans_brelse (ri->sc->tp, bp);So we release the buffer once we've found it, which also unlocks it. That means when we come back to it later, it may have been accessed and changed by something else and no longer be the block we are looking for. How do you protect against this sort of race given we are unlocking the buffer? Perhaps it should be held on the fab structure, and released when a better candidate is found?
The two callers of this function are the AGF and AGI repair functions. AGF repair holds the locked AGF buffer, and AGI repair holds the locked AGF & AGI buffers, which should be enough to prevent anyone else from accessing the AG btrees. They keep the all the AG header buffers locked until they're completely finished with rebuilding the headers (i.e.
xfs_scrub_teardown) and it's safe for the shape to change.Quoted 8 lines
How about I add to the comment for this function:
/*
* The caller must lock the applicable per-AG header buffers (AGF, AGI)
* to prevent other threads from changing the shape of the btrees that
* we are looking for. It must maintain those locks until it's safe for
* other threads to change the btrees' shapes.
*/
Quoted 4 lines
That's helpful. :) Can you sprinkle some checks like ASSERT(xfs_buf_islocked(agbp)) to remind readers of the leaf/callback functions that they expect the AGF/AGI to be locked on entry?
Ok, will do.
Quoted 10 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v2 5/22 xfs: recover AG btree roots from rmap data
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add a helper function to help us recover btree roots from the rmap data. Callers pass in a list of rmap owner codes, buffer ops, and magic numbers. We iterate the rmap records looking for owner matches, and then read the matching blocks to see if the magic number & uuid match. If so, we then read-verify the block, and if that passes then we retain a pointer to the block with the highest level, assuming that by the end of the call we will have found the root. This will be used to reset the AGF/AGI btree root fields during their rebuild procedures.
---
v2: document how we probe for btree roots and dcoument the locking
requirements for the callers
--- fs/xfs/scrub/repair.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 20 +++++ 2 files changed, 210 insertions(+)
fs/xfs/scrub/repair.c +190 -0
@@ -836,3 +836,193 @@ xfs_repair_reap_btree_extents(xfs_repair_cancel_btree_extents (sc, exlist);return error; } + +/* +* Finding per-AG Btree Roots for AGF/AGI Reconstruction +* +* If the AGF or AGI become slightly corrupted, it may be necessary to rebuild +* the AG headers by using the rmap data to rummage through the AG looking for +* btree roots. This is not guaranteed to work if the AG is heavily damaged +* or the rmap data are corrupt. +* +* Callers of xfs_repair_find_ag_btree_roots must lock the AGF and AGFL +* buffers if the AGF is being rebuilt; or the AGF and AGI buffers if the +* AGI is being rebuilt. It must maintain these locks until it's safe for +* other threads to change the btrees' shapes. The caller provides +* information about the btrees to look for by passing in an array of +* xfs_repair_find_ag_btree with the (rmap owner, buf_ops, magic) fields set. +* The (root, height) fields will be set on return if anything is found. The +* last element of the array should have a NULL buf_ops to mark the end of the +* array. +* +* For every rmapbt record matching any of the rmap owners in btree_info, +* read each block referenced by the rmap record. If the block is a btree +* block from this filesystem matching any of the magic numbers and has a +* level higher than what we've already seen, remember the block and the +* height of the tree required to have such a block. When the call completes, +* we return the highest block we've found for each btree description; those +* should be the roots. +*/ + +struct xfs_repair_findroot { +struct xfs_scrub_context *sc; +struct xfs_buf *agfl_bp; +struct xfs_agf *agf; +struct xfs_repair_find_ag_btree *btree_info; +}; + +/* See if our block is in the AGFL. */ +STATIC int +xfs_repair_findroot_agfl_walk ( +struct xfs_mount *mp, +xfs_agblock_t bno, +void *priv) +{ +xfs_agblock_t *agbno = priv; + +return (*agbno == bno) ?XFS_BTREE_QUERY_RANGE_ABORT :0 ; +} + +/* Does this block match the btree information passed in? */ +STATIC int +xfs_repair_findroot_block ( +struct xfs_repair_findroot *ri, +struct xfs_repair_find_ag_btree *fab, +uint64_t owner, +xfs_agblock_t agbno, +bool *found_it) +{ +struct xfs_mount *mp = ri->sc->mp; +struct xfs_buf *bp; +struct xfs_btree_block *btblock; +xfs_daddr_t daddr; +int error; + + daddr =XFS_AGB_TO_DADDR (mp, ri->sc->sa.agno, agbno); + +/* +* Blocks in the AGFL have stale contents that might just happen to +* have a matching magic and uuid. We don't want to pull these blocks +* in as part of a tree root, so we have to filter out the AGFL stuff +* here. If the AGFL looks insane we'll just refuse to repair. +*/ +if (owner ==XFS_RMAP_OWN_AG ) { + error =xfs_agfl_walk (mp, ri->agf, ri->agfl_bp, + xfs_repair_findroot_agfl_walk, &agbno); +if (error ==XFS_BTREE_QUERY_RANGE_ABORT ) +return 0 ; +if (error) +return error; + } + + error =xfs_trans_read_buf (mp, ri->sc->tp, mp->m_ddev_targp, daddr, + mp->m_bsize,0 , &bp,NULL ); +if (error) +return error; + +/* +* Does this look like a block matching our fs and higher than any +* other block we've found so far? If so, reattach buffer verifiers +* so the AIL won't complain if the buffer is also dirty. +*/ + btblock =XFS_BUF_TO_BLOCK (bp); +if (be32_to_cpu (btblock->bb_magic) != fab->magic) + goto out; +if (xfs_sb_version_hascrc (&mp->m_sb) && + !uuid_equal (&btblock->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid)) + goto out; + bp->b_ops = fab->buf_ops; + +/* Ignore this block if it's lower in the tree than we've seen. */ +if (fab->root !=NULLAGBLOCK && +xfs_btree_get_level (btblock) < fab->height) + goto out; + +/* Make sure we pass the verifiers. */ + bp->b_ops->verify_read (bp); +if (bp->b_error) + goto out; + fab->root = agbno; + fab->height =xfs_btree_get_level (btblock) +1 ; + *found_it = true; + +trace_xfs_repair_findroot_block (mp, ri->sc->sa.agno, agbno, +be32_to_cpu (btblock->bb_magic), fab->height -1 ); +out: +xfs_trans_brelse (ri->sc->tp, bp); +return error; +} + +/* +* Do any of the blocks in this rmap record match one of the btrees we're +* looking for? +*/ +STATIC int +xfs_repair_findroot_rmap ( +struct xfs_btree_cur *cur, +struct xfs_rmap_irec *rec, +void *priv) +{ +struct xfs_repair_findroot *ri = priv; +struct xfs_repair_find_ag_btree *fab; +xfs_agblock_t b; +bool found_it; +int error =0 ; + +/* Ignore anything that isn't AG metadata. */ +if (!XFS_RMAP_NON_INODE_OWNER (rec->rm_owner)) +return 0 ; + +/* Otherwise scan each block + btree type. */ +for (b =0 ; b < rec->rm_blockcount; b++) { + found_it = false; +for (fab = ri->btree_info; fab->buf_ops; fab++) { +if (rec->rm_owner != fab->rmap_owner) +continue ; + error =xfs_repair_findroot_block (ri, fab, + rec->rm_owner, rec->rm_startblock + b, + &found_it); +if (error) +return error; +if (found_it) +break ; + } + } + +return 0 ; +} + +/* Find the roots of the per-AG btrees described in btree_info. */ +int +xfs_repair_find_ag_btree_roots ( +struct xfs_scrub_context *sc, +struct xfs_buf *agf_bp, +struct xfs_repair_find_ag_btree *btree_info, +struct xfs_buf *agfl_bp) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_repair_findroot ri; +struct xfs_repair_find_ag_btree *fab; +struct xfs_btree_cur *cur; +int error; + +ASSERT (xfs_buf_islocked (agf_bp)); +ASSERT (agfl_bp ==NULL ||xfs_buf_islocked (agfl_bp)); + + ri.sc = sc; + ri.btree_info = btree_info; + ri.agf =XFS_BUF_TO_AGF (agf_bp); + ri.agfl_bp = agfl_bp; +for (fab = btree_info; fab->buf_ops; fab++) { +ASSERT (agfl_bp || fab->rmap_owner !=XFS_RMAP_OWN_AG ); +ASSERT (XFS_RMAP_NON_INODE_OWNER (fab->rmap_owner)); + fab->root =NULLAGBLOCK ; + fab->height =0 ; + } + + cur =xfs_rmapbt_init_cursor (mp, sc->tp, agf_bp, sc->sa.agno); + error =xfs_rmap_query_all (cur, xfs_repair_findroot_rmap, &ri); +xfs_btree_del_cursor (cur, error ?XFS_BTREE_ERROR :XFS_BTREE_NOERROR ); + +return error; +}
fs/xfs/scrub/repair.h +20 -0
@@ -77,6 +77,26 @@ int xfs_repair_reap_btree_extents(struct xfs_scrub_context *sc,struct xfs_repair_extent_list *exlist, struct xfs_owner_info *oinfo, enum xfs_ag_resv_type type); +struct xfs_repair_find_ag_btree { +/* in: rmap owner of the btree we're looking for */ +uint64_t rmap_owner; + +/* in: buffer ops */ +const struct xfs_buf_ops *buf_ops; + +/* in: magic number of the btree */ +uint32_t magic; + +/* out: the highest btree block found and the tree height */ +xfs_agblock_t root; +unsigned int height; +}; + +int xfs_repair_find_ag_btree_roots (struct xfs_scrub_context *sc, +struct xfs_buf *agf_bp, +struct xfs_repair_find_ag_btree *btree_info, +struct xfs_buf *agfl_bp); +/* Metadata repairers */ int xfs_repair_probe (struct xfs_scrub_context *sc);
Re: [PATCH v2 05/22] xfs: recover AG btree roots from rmap data
Quoted 11 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper function to help us recover btree roots from the rmap data. Callers pass in a list of rmap owner codes, buffer ops, and magic numbers. We iterate the rmap records looking for owner matches, and then read the matching blocks to see if the magic number & uuid match. If so, we then read-verify the block, and if that passes then we retain a pointer to the block with the highest level, assuming that by the end of the call we will have found the root. This will be used to reset the AGF/AGI btree root fields during their rebuild procedures.
Quoted 4 lines
Signed-off-by Darrick J. Wong <[email protected]>---
v2: document how we probe for btree roots and dcoument the locking
requirements for the callers
Looks good.
Signature · 2 lines
Dave Chinner [email protected]
PATCH 6/22 xfs: add a repair helper to reset superblock counters
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add a helper function to reset the superblock inode and block counters. The AG rebuilding functions will need these to adjust the counts if they need to change as a part of recovering from corruption.
--- fs/xfs/scrub/repair.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 7 ++++ fs/xfs/scrub/scrub.c | 2 + fs/xfs/scrub/scrub.h | 1 + 4 files changed, 89 insertions(+)
fs/xfs/scrub/repair.c +79 -0
@@ -944,3 +944,82 @@ xfs_repair_find_ag_btree_roots(return error; } + +/* Reset the superblock counters from the AGF/AGI. */ +int +xfs_repair_reset_counters ( +struct xfs_mount *mp) +{ +struct xfs_buf *agi_bp; +struct xfs_buf *agf_bp; +struct xfs_agi *agi; +struct xfs_agf *agf; +xfs_agnumber_t agno; +xfs_ino_t icount =0 ; +xfs_ino_t ifree =0 ; +xfs_filblks_t fdblocks =0 ; +int64_t delta_icount; +int64_t delta_ifree; +int64_t delta_fdblocks; +int error; + +trace_xfs_repair_reset_counters (mp); + +for (agno =0 ; agno < mp->m_sb.sb_agcount; agno++) { +/* Count all the inodes... */ + error =xfs_ialloc_read_agi (mp,NULL , agno, &agi_bp); +if (error) +return error; + agi =XFS_BUF_TO_AGI (agi_bp); + icount +=be32_to_cpu (agi->agi_count); + ifree +=be32_to_cpu (agi->agi_freecount); +xfs_buf_relse (agi_bp); + +/* Add up the free/freelist/bnobt/cntbt blocks... */ + error =xfs_alloc_read_agf (mp,NULL , agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + agf =XFS_BUF_TO_AGF (agf_bp); + fdblocks +=be32_to_cpu (agf->agf_freeblks); + fdblocks +=be32_to_cpu (agf->agf_flcount); + fdblocks +=be32_to_cpu (agf->agf_btreeblks); +xfs_buf_relse (agf_bp); + } + +/* +* Reinitialize the counters. The on-disk and in-core counters differ +* by the number of inodes/blocks reserved by the admin, the per-AG +* reservation, and any transactions in progress, so we have to +* account for that. First we take the sb lock and update its +* counters... +*/ +spin_lock (&mp->m_sb_lock); + delta_icount = (int64_t )mp->m_sb.sb_icount - icount; + delta_ifree = (int64_t )mp->m_sb.sb_ifree - ifree; + delta_fdblocks = (int64_t )mp->m_sb.sb_fdblocks - fdblocks; + mp->m_sb.sb_icount = icount; + mp->m_sb.sb_ifree = ifree; + mp->m_sb.sb_fdblocks = fdblocks; +spin_unlock (&mp->m_sb_lock); + +/* ...and then update the per-cpu counters. */ +if (delta_icount) { + error =xfs_mod_icount (mp, delta_icount); +if (error) +return error; + } +if (delta_ifree) { + error =xfs_mod_ifree (mp, delta_ifree); +if (error) +return error; + } +if (delta_fdblocks) { + error =xfs_mod_fdblocks (mp, delta_fdblocks, false); +if (error) +return error; + } + +return 0 ; +}
fs/xfs/scrub/repair.h +7 -0
@@ -96,6 +96,7 @@ int xfs_repair_find_ag_btree_roots(struct xfs_scrub_context *sc,struct xfs_buf *agf_bp, struct xfs_repair_find_ag_btree *btree_info, struct xfs_buf *agfl_bp); +int xfs_repair_reset_counters (struct xfs_mount *mp);/* Metadata repairers */
@@ -121,6 +122,12 @@ xfs_repair_calc_ag_resblks( return 0; } +static inline int xfs_repair_reset_counters (struct xfs_mount *mp) +{ +ASSERT (0 ); +return -EIO ; +} +#define xfs_repair_probe xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +2 -0
@@ -200,6 +200,8 @@ xfs_scrub_teardown(kmem_free (sc->buf); sc->buf =NULL ; } +if (sc->reset_counters && !error) + error =xfs_repair_reset_counters (sc->mp);return error; }
fs/xfs/scrub/scrub.h +1 -0
@@ -77,6 +77,7 @@ struct xfs_scrub_context {uint ilock_flags;bool try_harder;bool has_quotaofflock; +bool reset_counters;/* State tracking for single-AG operations. */ struct xfs_scrub_ag sa;
Re: [PATCH 06/22] xfs: add a repair helper to reset superblock counters
Quoted 145 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper function to reset the superblock inode and block counters. The AG rebuilding functions will need these to adjust the counts if they need to change as a part of recovering from corruption.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 7 ++++ fs/xfs/scrub/scrub.c | 2 + fs/xfs/scrub/scrub.h | 1 + 4 files changed, 89 insertions(+)fs/xfs/scrub/repair.c +79
-0@@ -944,3 +944,82 @@ xfs_repair_find_ag_btree_roots(return error; } + +/* Reset the superblock counters from the AGF/AGI. */ +int +xfs_repair_reset_counters ( +struct xfs_mount *mp) +{ +struct xfs_buf *agi_bp; +struct xfs_buf *agf_bp; +struct xfs_agi *agi; +struct xfs_agf *agf; +xfs_agnumber_t agno; +xfs_ino_t icount =0 ; +xfs_ino_t ifree =0 ; +xfs_filblks_t fdblocks =0 ; +int64_t delta_icount; +int64_t delta_ifree; +int64_t delta_fdblocks; +int error; + +trace_xfs_repair_reset_counters (mp); + +for (agno =0 ; agno < mp->m_sb.sb_agcount; agno++) { +/* Count all the inodes... */ + error =xfs_ialloc_read_agi (mp,NULL , agno, &agi_bp); +if (error) +return error; + agi =XFS_BUF_TO_AGI (agi_bp); + icount +=be32_to_cpu (agi->agi_count); + ifree +=be32_to_cpu (agi->agi_freecount); +xfs_buf_relse (agi_bp); + +/* Add up the free/freelist/bnobt/cntbt blocks... */ + error =xfs_alloc_read_agf (mp,NULL , agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + agf =XFS_BUF_TO_AGF (agf_bp); + fdblocks +=be32_to_cpu (agf->agf_freeblks); + fdblocks +=be32_to_cpu (agf->agf_flcount); + fdblocks +=be32_to_cpu (agf->agf_btreeblks); +xfs_buf_relse (agf_bp); + } + +/* +* Reinitialize the counters. The on-disk and in-core counters differ +* by the number of inodes/blocks reserved by the admin, the per-AG +* reservation, and any transactions in progress, so we have to +* account for that. First we take the sb lock and update its +* counters... +*/ +spin_lock (&mp->m_sb_lock); + delta_icount = (int64_t )mp->m_sb.sb_icount - icount; + delta_ifree = (int64_t )mp->m_sb.sb_ifree - ifree; + delta_fdblocks = (int64_t )mp->m_sb.sb_fdblocks - fdblocks; + mp->m_sb.sb_icount = icount; + mp->m_sb.sb_ifree = ifree; + mp->m_sb.sb_fdblocks = fdblocks; +spin_unlock (&mp->m_sb_lock); + +/* ...and then update the per-cpu counters. */ +if (delta_icount) { + error =xfs_mod_icount (mp, delta_icount); +if (error) +return error; + } +if (delta_ifree) { + error =xfs_mod_ifree (mp, delta_ifree); +if (error) +return error; + } +if (delta_fdblocks) { + error =xfs_mod_fdblocks (mp, delta_fdblocks, false); +if (error) +return error; + } + +return 0 ; +}fs/xfs/scrub/repair.h +7
-0@@ -96,6 +96,7 @@ int xfs_repair_find_ag_btree_roots(struct xfs_scrub_context *sc,struct xfs_buf *agf_bp, struct xfs_repair_find_ag_btree *btree_info, struct xfs_buf *agfl_bp); +int xfs_repair_reset_counters (struct xfs_mount *mp);/* Metadata repairers */ @@ -121,6 +122,12 @@ xfs_repair_calc_ag_resblks( return 0; } +static inline int xfs_repair_reset_counters (struct xfs_mount *mp) +{ +ASSERT (0 ); +return -EIO ; +} +#define xfs_repair_probe xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */ fs/xfs/scrub/scrub.c +2
-0@@ -200,6 +200,8 @@ xfs_scrub_teardown(kmem_free (sc->buf); sc->buf =NULL ; } +if (sc->reset_counters && !error) + error =xfs_repair_reset_counters (sc->mp);return error; }fs/xfs/scrub/scrub.h +0
-0
fs/xfs/scrub/scrub.h struct xfs_scrub_context { · line 80
Quoted 4 lines
fs/xfs/scrub/scrub.h +1
-0@@ -77,6 +77,7 @@ struct xfs_scrub_context {uint ilock_flags;bool try_harder;bool has_quotaofflock; +bool reset_counters;
I noticed this bool is added here, and used in the conditional above, but I don't see where it gets set to anything? Maybe it gets used later in the set?
Other than that looks good. Thx!
Reviewed by: Allison Henderson <[email protected]>
Quoted 9 lines
fs/xfs/scrub/scrub.h +0
-1/* State tracking for single-AG operations. */ struct xfs_scrub_ag sa; --To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwICaQ&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=QJevHm77WCVmLISD9kjEPpN22CjDaymobWOYR-vTC2c&s=LEL8S70qR88zGjqrODTcuvE_OA75SuB5hdZ86g6FD70&e=
Re: [PATCH 06/22] xfs: add a repair helper to reset superblock counters
Quoted 139 lines · Allison Henderson
Quoted 138 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper function to reset the superblock inode and block counters. The AG rebuilding functions will need these to adjust the counts if they need to change as a part of recovering from corruption.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 7 ++++ fs/xfs/scrub/scrub.c | 2 + fs/xfs/scrub/scrub.h | 1 + 4 files changed, 89 insertions(+)fs/xfs/scrub/repair.c +79
-0@@ -944,3 +944,82 @@ xfs_repair_find_ag_btree_roots(return error; } + +/* Reset the superblock counters from the AGF/AGI. */ +int +xfs_repair_reset_counters ( +struct xfs_mount *mp) +{ +struct xfs_buf *agi_bp; +struct xfs_buf *agf_bp; +struct xfs_agi *agi; +struct xfs_agf *agf; +xfs_agnumber_t agno; +xfs_ino_t icount =0 ; +xfs_ino_t ifree =0 ; +xfs_filblks_t fdblocks =0 ; +int64_t delta_icount; +int64_t delta_ifree; +int64_t delta_fdblocks; +int error; + +trace_xfs_repair_reset_counters (mp); + +for (agno =0 ; agno < mp->m_sb.sb_agcount; agno++) { +/* Count all the inodes... */ + error =xfs_ialloc_read_agi (mp,NULL , agno, &agi_bp); +if (error) +return error; + agi =XFS_BUF_TO_AGI (agi_bp); + icount +=be32_to_cpu (agi->agi_count); + ifree +=be32_to_cpu (agi->agi_freecount); +xfs_buf_relse (agi_bp); + +/* Add up the free/freelist/bnobt/cntbt blocks... */ + error =xfs_alloc_read_agf (mp,NULL , agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + agf =XFS_BUF_TO_AGF (agf_bp); + fdblocks +=be32_to_cpu (agf->agf_freeblks); + fdblocks +=be32_to_cpu (agf->agf_flcount); + fdblocks +=be32_to_cpu (agf->agf_btreeblks); +xfs_buf_relse (agf_bp); + } + +/* +* Reinitialize the counters. The on-disk and in-core counters differ +* by the number of inodes/blocks reserved by the admin, the per-AG +* reservation, and any transactions in progress, so we have to +* account for that. First we take the sb lock and update its +* counters... +*/ +spin_lock (&mp->m_sb_lock); + delta_icount = (int64_t )mp->m_sb.sb_icount - icount; + delta_ifree = (int64_t )mp->m_sb.sb_ifree - ifree; + delta_fdblocks = (int64_t )mp->m_sb.sb_fdblocks - fdblocks; + mp->m_sb.sb_icount = icount; + mp->m_sb.sb_ifree = ifree; + mp->m_sb.sb_fdblocks = fdblocks; +spin_unlock (&mp->m_sb_lock); + +/* ...and then update the per-cpu counters. */ +if (delta_icount) { + error =xfs_mod_icount (mp, delta_icount); +if (error) +return error; + } +if (delta_ifree) { + error =xfs_mod_ifree (mp, delta_ifree); +if (error) +return error; + } +if (delta_fdblocks) { + error =xfs_mod_fdblocks (mp, delta_fdblocks, false); +if (error) +return error; + } + +return 0 ; +}fs/xfs/scrub/repair.h +7
-0@@ -96,6 +96,7 @@ int xfs_repair_find_ag_btree_roots(struct xfs_scrub_context *sc,struct xfs_buf *agf_bp, struct xfs_repair_find_ag_btree *btree_info, struct xfs_buf *agfl_bp); +int xfs_repair_reset_counters (struct xfs_mount *mp);/* Metadata repairers */ @@ -121,6 +122,12 @@ xfs_repair_calc_ag_resblks( return 0; } +static inline int xfs_repair_reset_counters (struct xfs_mount *mp) +{ +ASSERT (0 ); +return -EIO ; +} +#define xfs_repair_probe xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */ fs/xfs/scrub/scrub.c +2
-0@@ -200,6 +200,8 @@ xfs_scrub_teardown(kmem_free (sc->buf); sc->buf =NULL ; } +if (sc->reset_counters && !error) + error =xfs_repair_reset_counters (sc->mp);return error; }fs/xfs/scrub/scrub.h +0
-0
Quoted 8 lines
fs/xfs/scrub/scrub.h struct xfs_scrub_context { · line 80
Quoted 5 lines
fs/xfs/scrub/scrub.h +1
-0@@ -77,6 +77,7 @@ struct xfs_scrub_context {uint ilock_flags;bool try_harder;bool has_quotaofflock; +bool reset_counters;I noticed this bool is added here, and used in the conditional above, but I don't see where it gets set to anything? Maybe it gets used later in the set?
Yeah, there are no callers of any of these helper functions until you get to the part of the series where we actually start repairing metadata.
--D
Quoted 16 lines
Other than that looks good. Thx!
Reviewed by: Allison Henderson <[email protected]>
Quoted 8 lines
fs/xfs/scrub/scrub.h +0
-1/* State tracking for single-AG operations. */ struct xfs_scrub_ag sa; --To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwICaQ&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=QJevHm77WCVmLISD9kjEPpN22CjDaymobWOYR-vTC2c&s=LEL8S70qR88zGjqrODTcuvE_OA75SuB5hdZ86g6FD70&e=--
To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v2 6/22 xfs: add a repair helper to reset superblock counters
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add a helper function to reset the superblock inode and block counters. The AG rebuilding functions will need these to adjust the counts if they need to change as a part of recovering from corruption.
---
v2: improve documentation
--- fs/xfs/scrub/repair.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 7 ++++ fs/xfs/scrub/scrub.c | 2 + fs/xfs/scrub/scrub.h | 1 + 4 files changed, 99 insertions(+)
fs/xfs/scrub/repair.c +89 -0
@@ -1026,3 +1026,92 @@ xfs_repair_find_ag_btree_roots(return error; } + +/* +* Reset the superblock counters. +* +* If a repair function changes the inode or free block counters, it must set +* reset_counters to push this function to reset the global counters. Repair +* functions are responsible for resetting all other in-core state. This +* function runs outside of transaction context after the repair context has +* been torn down, so if there's further filesystem corruption we'll error out +* to userspace and give userspace a chance to call back to fix the further +* errors. +*/ +int +xfs_repair_reset_counters ( +struct xfs_mount *mp) +{ +struct xfs_buf *agi_bp; +struct xfs_buf *agf_bp; +struct xfs_agi *agi; +struct xfs_agf *agf; +xfs_agnumber_t agno; +xfs_ino_t icount =0 ; +xfs_ino_t ifree =0 ; +xfs_filblks_t fdblocks =0 ; +int64_t delta_icount; +int64_t delta_ifree; +int64_t delta_fdblocks; +int error; + +trace_xfs_repair_reset_counters (mp); + +for (agno =0 ; agno < mp->m_sb.sb_agcount; agno++) { +/* Count all the inodes... */ + error =xfs_ialloc_read_agi (mp,NULL , agno, &agi_bp); +if (error) +return error; + agi =XFS_BUF_TO_AGI (agi_bp); + icount +=be32_to_cpu (agi->agi_count); + ifree +=be32_to_cpu (agi->agi_freecount); +xfs_buf_relse (agi_bp); + +/* Add up the free/freelist/bnobt/cntbt blocks... */ + error =xfs_alloc_read_agf (mp,NULL , agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + agf =XFS_BUF_TO_AGF (agf_bp); + fdblocks +=be32_to_cpu (agf->agf_freeblks); + fdblocks +=be32_to_cpu (agf->agf_flcount); + fdblocks +=be32_to_cpu (agf->agf_btreeblks); +xfs_buf_relse (agf_bp); + } + +/* +* Reinitialize the counters. The on-disk and in-core counters differ +* by the number of inodes/blocks reserved by the admin, the per-AG +* reservation, and any transactions in progress, so we have to +* account for that. First we take the sb lock and update its +* counters... +*/ +spin_lock (&mp->m_sb_lock); + delta_icount = (int64_t )mp->m_sb.sb_icount - icount; + delta_ifree = (int64_t )mp->m_sb.sb_ifree - ifree; + delta_fdblocks = (int64_t )mp->m_sb.sb_fdblocks - fdblocks; + mp->m_sb.sb_icount = icount; + mp->m_sb.sb_ifree = ifree; + mp->m_sb.sb_fdblocks = fdblocks; +spin_unlock (&mp->m_sb_lock); + +/* ...and then update the per-cpu counters. */ +if (delta_icount) { + error =xfs_mod_icount (mp, delta_icount); +if (error) +return error; + } +if (delta_ifree) { + error =xfs_mod_ifree (mp, delta_ifree); +if (error) +return error; + } +if (delta_fdblocks) { + error =xfs_mod_fdblocks (mp, delta_fdblocks, false); +if (error) +return error; + } + +return 0 ; +}
fs/xfs/scrub/repair.h +7 -0
@@ -96,6 +96,7 @@ int xfs_repair_find_ag_btree_roots(struct xfs_scrub_context *sc,struct xfs_buf *agf_bp, struct xfs_repair_find_ag_btree *btree_info, struct xfs_buf *agfl_bp); +int xfs_repair_reset_counters (struct xfs_mount *mp);/* Metadata repairers */
@@ -121,6 +122,12 @@ xfs_repair_calc_ag_resblks( return 0; } +static inline int xfs_repair_reset_counters (struct xfs_mount *mp) +{ +ASSERT (0 ); +return -EIO ; +} +#define xfs_repair_probe xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +2 -0
@@ -200,6 +200,8 @@ xfs_scrub_teardown(kmem_free (sc->buf); sc->buf =NULL ; } +if (sc->reset_counters && !error) + error =xfs_repair_reset_counters (sc->mp);return error; }
fs/xfs/scrub/scrub.h +1 -0
@@ -78,6 +78,7 @@ struct xfs_scrub_context {uint ilock_flags;bool try_harder;bool has_quotaofflock; +bool reset_counters;/* State tracking for single-AG operations. */ struct xfs_scrub_ag sa;
Re: [PATCH v2 06/22] xfs: add a repair helper to reset superblock counters
Quoted 84 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper function to reset the superblock inode and block counters. The AG rebuilding functions will need these to adjust the counts if they need to change as a part of recovering from corruption.
Signed-off-by Darrick J. Wong <[email protected]>Reviewed-by Allison Henderson <[email protected]>---
v2: improve documentation--- fs/xfs/scrub/repair.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 7 ++++ fs/xfs/scrub/scrub.c | 2 + fs/xfs/scrub/scrub.h | 1 + 4 files changed, 99 insertions(+)fs/xfs/scrub/repair.c +59
-0@@ -1026,3 +1026,92 @@ xfs_repair_find_ag_btree_roots(return error; } + +/* +* Reset the superblock counters. +* +* If a repair function changes the inode or free block counters, it must set +* reset_counters to push this function to reset the global counters. Repair +* functions are responsible for resetting all other in-core state. This +* function runs outside of transaction context after the repair context has +* been torn down, so if there's further filesystem corruption we'll error out +* to userspace and give userspace a chance to call back to fix the further +* errors. +*/ +int +xfs_repair_reset_counters ( +struct xfs_mount *mp) +{ +struct xfs_buf *agi_bp; +struct xfs_buf *agf_bp; +struct xfs_agi *agi; +struct xfs_agf *agf; +xfs_agnumber_t agno; +xfs_ino_t icount =0 ; +xfs_ino_t ifree =0 ; +xfs_filblks_t fdblocks =0 ; +int64_t delta_icount; +int64_t delta_ifree; +int64_t delta_fdblocks; +int error; + +trace_xfs_repair_reset_counters (mp); + +for (agno =0 ; agno < mp->m_sb.sb_agcount; agno++) { +/* Count all the inodes... */ + error =xfs_ialloc_read_agi (mp,NULL , agno, &agi_bp); +if (error) +return error; + agi =XFS_BUF_TO_AGI (agi_bp); + icount +=be32_to_cpu (agi->agi_count); + ifree +=be32_to_cpu (agi->agi_freecount); +xfs_buf_relse (agi_bp); + +/* Add up the free/freelist/bnobt/cntbt blocks... */ + error =xfs_alloc_read_agf (mp,NULL , agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + agf =XFS_BUF_TO_AGF (agf_bp); + fdblocks +=be32_to_cpu (agf->agf_freeblks); + fdblocks +=be32_to_cpu (agf->agf_flcount); + fdblocks +=be32_to_cpu (agf->agf_btreeblks); +xfs_buf_relse (agf_bp); + } + +/* +* Reinitialize the counters. The on-disk and in-core counters differ +* by the number of inodes/blocks reserved by the admin, the per-AG +* reservation, and any transactions in progress, so we have to +* account for that. First we take the sb lock and update its
fs/xfs/scrub/repair.c xfs_repair_find_ag_btree_roots( · line 1097
Quoted 10 lines
fs/xfs/scrub/repair.c +10
-0+* counters... +*/ +spin_lock (&mp->m_sb_lock); + delta_icount = (int64_t )mp->m_sb.sb_icount - icount; + delta_ifree = (int64_t )mp->m_sb.sb_ifree - ifree; + delta_fdblocks = (int64_t )mp->m_sb.sb_fdblocks - fdblocks; + mp->m_sb.sb_icount = icount; + mp->m_sb.sb_ifree = ifree; + mp->m_sb.sb_fdblocks = fdblocks; +spin_unlock (&mp->m_sb_lock);
This seems racy to me ? i.e. the per-ag counters can change while we are summing them, and once we've summed them then sb counters can change while we are waiting for the m_sb_lock. It's looks to me like the summed per-ag counters are not in any way coherent wit the superblock or the in-core per-CPU counters, so I'm struggling to understand why this is safe?
We can do this sort of summation at mount time (in xfs_initialize_perag_data()) because the filesystem is running single threaded while the summation is taking place and so nothing is changing during th summation. The filesystem is active in this case, so I don't think we can do the same thing here.
Also, it brought a question to mind because I haven't clearly noted it happening yet: when do the xfs_perag counters get corrected? And if they are already correct, why not just iterate the perag counters?
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH v2 06/22] xfs: add a repair helper to reset superblock counters
Quoted 94 lines · Dave Chinner
Quoted 93 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper function to reset the superblock inode and block counters. The AG rebuilding functions will need these to adjust the counts if they need to change as a part of recovering from corruption.
Signed-off-by Darrick J. Wong <[email protected]>Reviewed-by Allison Henderson <[email protected]>---
v2: improve documentation--- fs/xfs/scrub/repair.c | 89 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 7 ++++ fs/xfs/scrub/scrub.c | 2 + fs/xfs/scrub/scrub.h | 1 + 4 files changed, 99 insertions(+)fs/xfs/scrub/repair.c +68
-0@@ -1026,3 +1026,92 @@ xfs_repair_find_ag_btree_roots(return error; } + +/* +* Reset the superblock counters. +* +* If a repair function changes the inode or free block counters, it must set +* reset_counters to push this function to reset the global counters. Repair +* functions are responsible for resetting all other in-core state. This +* function runs outside of transaction context after the repair context has +* been torn down, so if there's further filesystem corruption we'll error out +* to userspace and give userspace a chance to call back to fix the further +* errors. +*/ +int +xfs_repair_reset_counters ( +struct xfs_mount *mp) +{ +struct xfs_buf *agi_bp; +struct xfs_buf *agf_bp; +struct xfs_agi *agi; +struct xfs_agf *agf; +xfs_agnumber_t agno; +xfs_ino_t icount =0 ; +xfs_ino_t ifree =0 ; +xfs_filblks_t fdblocks =0 ; +int64_t delta_icount; +int64_t delta_ifree; +int64_t delta_fdblocks; +int error; + +trace_xfs_repair_reset_counters (mp); + +for (agno =0 ; agno < mp->m_sb.sb_agcount; agno++) { +/* Count all the inodes... */ + error =xfs_ialloc_read_agi (mp,NULL , agno, &agi_bp); +if (error) +return error; + agi =XFS_BUF_TO_AGI (agi_bp); + icount +=be32_to_cpu (agi->agi_count); + ifree +=be32_to_cpu (agi->agi_freecount); +xfs_buf_relse (agi_bp); + +/* Add up the free/freelist/bnobt/cntbt blocks... */ + error =xfs_alloc_read_agf (mp,NULL , agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + agf =XFS_BUF_TO_AGF (agf_bp); + fdblocks +=be32_to_cpu (agf->agf_freeblks); + fdblocks +=be32_to_cpu (agf->agf_flcount); + fdblocks +=be32_to_cpu (agf->agf_btreeblks); +xfs_buf_relse (agf_bp); + } + +/* +* Reinitialize the counters. The on-disk and in-core counters differ +* by the number of inodes/blocks reserved by the admin, the per-AG +* reservation, and any transactions in progress, so we have to +* account for that. First we take the sb lock and update its +* counters... +*/ +spin_lock (&mp->m_sb_lock); + delta_icount = (int64_t )mp->m_sb.sb_icount - icount; + delta_ifree = (int64_t )mp->m_sb.sb_ifree - ifree; + delta_fdblocks = (int64_t )mp->m_sb.sb_fdblocks - fdblocks; + mp->m_sb.sb_icount = icount; + mp->m_sb.sb_ifree = ifree; + mp->m_sb.sb_fdblocks = fdblocks;
Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_find_ag_btree_roots( · line 1097
Quoted 1 line
fs/xfs/scrub/repair.c +1
-0+spin_unlock (&mp->m_sb_lock);This seems racy to me ? i.e. the per-ag counters can change while we are summing them, and once we've summed them then sb counters can change while we are waiting for the
m_sb_lock. It's looks to me like the summed per-ag counters are not in any way coherent wit the superblock or the in-core per-CPU counters, so I'm struggling to understand why this is safe?
Hmm, yes, I think this is racy too. The purpose of this code is to recompute the global counters from the AG counters after any operation that modifies anything that would affect the icount/ifreecount/fdblocks counters...
Quoted 5 lines
We can do this sort of summation at mount time (in
xfs_initialize_perag_data()) because the filesystem is running single threaded while the summation is taking place and so nothing is changing during th summation. The filesystem is active in this case, so I don't think we can do the same thing here.
...however, you're correct to point out that the fs must be quiesced before we can actually do this. In other words, I think the filesystem has to be completely frozen before we can do this. Perhaps it's better to have the per-ag rebuilders fix only the per-ag counters and leave the global counters alone. Then add a new scrubber that checks the summary counters and fixes them if necessary.
Quoted 4 lines
Also, it brought a question to mind because I haven't clearly noted it happening yet: when do the
xfs_peragcounters get corrected? And if they are already correct, why not just iterate the perag counters?
The xfs_perag counters are updated by the AGF/AGI/inobt rebuild code.
--D
Quoted 10 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 06/22] xfs: add a repair helper to reset superblock counters
Quoted 25 lines · Darrick J. Wong
Quoted 15 lines · Dave Chinner
Quoted 14 lines · Darrick J. Wong
fs/xfs/scrub/repair.c +14
-0+/* +* Reinitialize the counters. The on-disk and in-core counters differ +* by the number of inodes/blocks reserved by the admin, the per-AG +* reservation, and any transactions in progress, so we have to +* account for that. First we take the sb lock and update its +* counters... +*/ +spin_lock (&mp->m_sb_lock); + delta_icount = (int64_t )mp->m_sb.sb_icount - icount; + delta_ifree = (int64_t )mp->m_sb.sb_ifree - ifree; + delta_fdblocks = (int64_t )mp->m_sb.sb_fdblocks - fdblocks; + mp->m_sb.sb_icount = icount; + mp->m_sb.sb_ifree = ifree; + mp->m_sb.sb_fdblocks = fdblocks;Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_find_ag_btree_roots( · line 1097
Quoted 1 line
fs/xfs/scrub/repair.c +1
-0+spin_unlock (&mp->m_sb_lock);This seems racy to me ? i.e. the per-ag counters can change while we are summing them, and once we've summed them then sb counters can change while we are waiting for the
m_sb_lock. It's looks to me like the summed per-ag counters are not in any way coherent wit the superblock or the in-core per-CPU counters, so I'm struggling to understand why this is safe?
Quoted 4 lines
Hmm, yes, I think this is racy too. The purpose of this code is to recompute the global counters from the AG counters after any operation that modifies anything that would affect the icount/ifreecount/fdblocks counters...
*nod*
Quoted 6 lines
Quoted 5 lines
We can do this sort of summation at mount time (in
xfs_initialize_perag_data()) because the filesystem is running single threaded while the summation is taking place and so nothing is changing during th summation. The filesystem is active in this case, so I don't think we can do the same thing here.
Quoted 6 lines
...however, you're correct to point out that the fs must be quiesced before we can actually do this. In other words, I think the filesystem has to be completely frozen before we can do this. Perhaps it's better to have the per-ag rebuilders fix only the per-ag counters and leave the global counters alone. Then add a new scrubber that checks the summary counters and fixes them if necessary.
So the question here is whether we actually need to accurately correct the global superblock counters? We know that if we have a dirty unmount, the counters will we re-initialised on mount from the AG header information, so perhaps what we need here is a flag to tell unmount to dirty the log again after it has written the unmount record (like we currently do for quiesce).
That was we can do a racy "near enough" update here to get us out of the worst of the space accounting mismatches, knowing that on the next mount it will be accurately rebuilt.
Thoughts?
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH v2 06/22] xfs: add a repair helper to reset superblock counters
Quoted 46 lines · Dave Chinner
Quoted 25 lines · Darrick J. Wong
Quoted 15 lines · Dave Chinner
Quoted 14 lines · Darrick J. Wong
fs/xfs/scrub/repair.c +14
-0+/* +* Reinitialize the counters. The on-disk and in-core counters differ +* by the number of inodes/blocks reserved by the admin, the per-AG +* reservation, and any transactions in progress, so we have to +* account for that. First we take the sb lock and update its +* counters... +*/ +spin_lock (&mp->m_sb_lock); + delta_icount = (int64_t )mp->m_sb.sb_icount - icount; + delta_ifree = (int64_t )mp->m_sb.sb_ifree - ifree; + delta_fdblocks = (int64_t )mp->m_sb.sb_fdblocks - fdblocks; + mp->m_sb.sb_icount = icount; + mp->m_sb.sb_ifree = ifree; + mp->m_sb.sb_fdblocks = fdblocks;Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_find_ag_btree_roots( · line 1097
Quoted 1 line
fs/xfs/scrub/repair.c +1
-0+spin_unlock (&mp->m_sb_lock);This seems racy to me ? i.e. the per-ag counters can change while we are summing them, and once we've summed them then sb counters can change while we are waiting for the
m_sb_lock. It's looks to me like the summed per-ag counters are not in any way coherent wit the superblock or the in-core per-CPU counters, so I'm struggling to understand why this is safe?Quoted 4 lines
Hmm, yes, I think this is racy too. The purpose of this code is to recompute the global counters from the AG counters after any operation that modifies anything that would affect the icount/ifreecount/fdblocks counters...
*nod*
Quoted 6 lines
Quoted 5 lines
We can do this sort of summation at mount time (in
xfs_initialize_perag_data()) because the filesystem is running single threaded while the summation is taking place and so nothing is changing during th summation. The filesystem is active in this case, so I don't think we can do the same thing here.Quoted 6 lines
...however, you're correct to point out that the fs must be quiesced before we can actually do this. In other words, I think the filesystem has to be completely frozen before we can do this. Perhaps it's better to have the per-ag rebuilders fix only the per-ag counters and leave the global counters alone. Then add a new scrubber that checks the summary counters and fixes them if necessary.
Quoted 2 lines
So the question here is whether we actually need to accurately correct the global superblock counters?
I think so, because what happens if the superblock counter is artificially high but the AGs do not actually have the free space? xfs_trans_reserve won't ENOSPC like it should, so we could end up blowing out of transactions and shutting down because some allocation that has to succeed ("because trans_reserve said there was space!") fails...
Quoted 5 lines
We know that if we have a dirty unmount, the counters will we re-initialised on mount from the AG header information, so perhaps what we need here is a flag to tell unmount to dirty the log again after it has written the unmount record (like we currently do for quiesce).
...but now that we've repaired the filesystem, it could potentially run for a very long time until the next unmount. During that run, we'd be misleading users about the real amount of free space and risking a hard shutdown. I prefer that online repair try not to leave any weird state around after xfs_scrub exits.
Quoted 5 lines
That was we can do a racy "near enough" update here to get us out of the worst of the space accounting mismatches, knowing that on the next mount it will be accurately rebuilt.
Thoughts?
Well, I think the best solution is to have the AGF/AGI/inobt rebuilders adjust the global counters by the same amount that they're adjusting the counters in the AGF/AGI, then add a new scrubber that runs at the end to freeze the fs and check/repair the global counter state. :)
--D
Quoted 11 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 06/22] xfs: add a repair helper to reset superblock counters
Quoted 50 lines · Darrick J. Wong
Quoted 46 lines · Dave Chinner
Quoted 25 lines · Darrick J. Wong
Quoted 15 lines · Dave Chinner
Quoted 14 lines · Darrick J. Wong
fs/xfs/scrub/repair.c +14
-0+/* +* Reinitialize the counters. The on-disk and in-core counters differ +* by the number of inodes/blocks reserved by the admin, the per-AG +* reservation, and any transactions in progress, so we have to +* account for that. First we take the sb lock and update its +* counters... +*/ +spin_lock (&mp->m_sb_lock); + delta_icount = (int64_t )mp->m_sb.sb_icount - icount; + delta_ifree = (int64_t )mp->m_sb.sb_ifree - ifree; + delta_fdblocks = (int64_t )mp->m_sb.sb_fdblocks - fdblocks; + mp->m_sb.sb_icount = icount; + mp->m_sb.sb_ifree = ifree; + mp->m_sb.sb_fdblocks = fdblocks;Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_find_ag_btree_roots( · line 1097
Quoted 1 line
fs/xfs/scrub/repair.c +1
-0+spin_unlock (&mp->m_sb_lock);This seems racy to me ? i.e. the per-ag counters can change while we are summing them, and once we've summed them then sb counters can change while we are waiting for the
m_sb_lock. It's looks to me like the summed per-ag counters are not in any way coherent wit the superblock or the in-core per-CPU counters, so I'm struggling to understand why this is safe?Quoted 4 lines
Hmm, yes, I think this is racy too. The purpose of this code is to recompute the global counters from the AG counters after any operation that modifies anything that would affect the icount/ifreecount/fdblocks counters...
*nod*
Quoted 6 lines
Quoted 5 lines
We can do this sort of summation at mount time (in
xfs_initialize_perag_data()) because the filesystem is running single threaded while the summation is taking place and so nothing is changing during th summation. The filesystem is active in this case, so I don't think we can do the same thing here.Quoted 6 lines
...however, you're correct to point out that the fs must be quiesced before we can actually do this. In other words, I think the filesystem has to be completely frozen before we can do this. Perhaps it's better to have the per-ag rebuilders fix only the per-ag counters and leave the global counters alone. Then add a new scrubber that checks the summary counters and fixes them if necessary.
Quoted 2 lines
So the question here is whether we actually need to accurately correct the global superblock counters?
Quoted 6 lines
I think so, because what happens if the superblock counter is artificially high but the AGs do not actually have the free space?
xfs_trans_reservewon't ENOSPC like it should, so we could end up blowing out of transactions and shutting down because some allocation that has to succeed ("becausetrans_reservesaid there was space!") fails...
Yes, but I would have thought the reset will get us close enough that this wouldn't be an issue for the vast majority of people.
And the other side of it is that if we get close enough to ENOSPC that it matters, we could freeze/sum/thaw to be fully accurate on demand in xfs_trans_reserve(), right? We already slow down greatly at ENOSPC, so at that point the perf overhead fo a freeze/thaw cycle just doesn't matter...
Quoted 11 lines
Quoted 5 lines
We know that if we have a dirty unmount, the counters will we re-initialised on mount from the AG header information, so perhaps what we need here is a flag to tell unmount to dirty the log again after it has written the unmount record (like we currently do for quiesce).
...but now that we've repaired the filesystem, it could potentially run for a very long time until the next unmount. During that run, we'd be misleading users about the real amount of free space and risking a hard shutdown. I prefer that online repair try not to leave any weird state around after
xfs_scrubexits.
Sure, but user's may not want a freeze/read-all-ag-headers/thaw cycle as part of repair if it can be avoided. If there are thousands of AGs, this could take many seconds....
Quoted 10 lines
Quoted 5 lines
That was we can do a racy "near enough" update here to get us out of the worst of the space accounting mismatches, knowing that on the next mount it will be accurately rebuilt.
Thoughts?
Well, I think the best solution is to have the AGF/AGI/inobt rebuilders adjust the global counters by the same amount that they're adjusting the counters in the AGF/AGI, then add a new scrubber that runs at the end to freeze the fs and check/repair the global counter state. :)
I'm just not convinced that we can get away with a global freeze to do this summation without having noticable impact on applications.
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH v2 06/22] xfs: add a repair helper to reset superblock counters
Quoted 58 lines · Dave Chinner
Quoted 50 lines · Darrick J. Wong
Quoted 46 lines · Dave Chinner
Quoted 25 lines · Darrick J. Wong
Quoted 15 lines · Dave Chinner
Quoted 14 lines · Darrick J. Wong
fs/xfs/scrub/repair.c +14
-0+/* +* Reinitialize the counters. The on-disk and in-core counters differ +* by the number of inodes/blocks reserved by the admin, the per-AG +* reservation, and any transactions in progress, so we have to +* account for that. First we take the sb lock and update its +* counters... +*/ +spin_lock (&mp->m_sb_lock); + delta_icount = (int64_t )mp->m_sb.sb_icount - icount; + delta_ifree = (int64_t )mp->m_sb.sb_ifree - ifree; + delta_fdblocks = (int64_t )mp->m_sb.sb_fdblocks - fdblocks; + mp->m_sb.sb_icount = icount; + mp->m_sb.sb_ifree = ifree; + mp->m_sb.sb_fdblocks = fdblocks;Quoted 8 lines
fs/xfs/scrub/repair.c xfs_repair_find_ag_btree_roots( · line 1097
Quoted 1 line
fs/xfs/scrub/repair.c +1
-0+spin_unlock (&mp->m_sb_lock);This seems racy to me ? i.e. the per-ag counters can change while we are summing them, and once we've summed them then sb counters can change while we are waiting for the
m_sb_lock. It's looks to me like the summed per-ag counters are not in any way coherent wit the superblock or the in-core per-CPU counters, so I'm struggling to understand why this is safe?Quoted 4 lines
Hmm, yes, I think this is racy too. The purpose of this code is to recompute the global counters from the AG counters after any operation that modifies anything that would affect the icount/ifreecount/fdblocks counters...
*nod*
Quoted 6 lines
Quoted 5 lines
We can do this sort of summation at mount time (in
xfs_initialize_perag_data()) because the filesystem is running single threaded while the summation is taking place and so nothing is changing during th summation. The filesystem is active in this case, so I don't think we can do the same thing here.Quoted 6 lines
...however, you're correct to point out that the fs must be quiesced before we can actually do this. In other words, I think the filesystem has to be completely frozen before we can do this. Perhaps it's better to have the per-ag rebuilders fix only the per-ag counters and leave the global counters alone. Then add a new scrubber that checks the summary counters and fixes them if necessary.
Quoted 2 lines
So the question here is whether we actually need to accurately correct the global superblock counters?
Quoted 6 lines
I think so, because what happens if the superblock counter is artificially high but the AGs do not actually have the free space?
xfs_trans_reservewon't ENOSPC like it should, so we could end up blowing out of transactions and shutting down because some allocation that has to succeed ("becausetrans_reservesaid there was space!") fails...
Quoted 2 lines
Yes, but I would have thought the reset will get us close enough that this wouldn't be an issue for the vast majority of people.
<nod> I'll adjust the sb counters based on the agf/agi/inobt adjustments and we'll leave verifying and/or fixing the superblock counters as a Future Research Topic(tm).
Quoted 28 lines
And the other side of it is that if we get close enough to ENOSPC that it matters, we could freeze/sum/thaw to be fully accurate on demand in
xfs_trans_reserve(), right? We already slow down greatly at ENOSPC, so at that point the perf overhead fo a freeze/thaw cycle just doesn't matter...Quoted 11 lines
Quoted 5 lines
We know that if we have a dirty unmount, the counters will we re-initialised on mount from the AG header information, so perhaps what we need here is a flag to tell unmount to dirty the log again after it has written the unmount record (like we currently do for quiesce).
...but now that we've repaired the filesystem, it could potentially run for a very long time until the next unmount. During that run, we'd be misleading users about the real amount of free space and risking a hard shutdown. I prefer that online repair try not to leave any weird state around after
xfs_scrubexits.Sure, but user's may not want a freeze/read-all-ag-headers/thaw cycle as part of repair if it can be avoided. If there are thousands of AGs, this could take many seconds....
Quoted 6 lines
Quoted 5 lines
That was we can do a racy "near enough" update here to get us out of the worst of the space accounting mismatches, knowing that on the next mount it will be accurately rebuilt.
Thoughts?
Quoted 7 lines
Quoted 4 lines
Well, I think the best solution is to have the AGF/AGI/inobt rebuilders adjust the global counters by the same amount that they're adjusting the counters in the AGF/AGI, then add a new scrubber that runs at the end to freeze the fs and check/repair the global counter state. :)
I'm just not convinced that we can get away with a global freeze to do this summation without having noticable impact on applications.
Admittedly, online repair has a semi-implicit design assumption that either (a) it's running on fast enough storage that a bunch of random IOs won't seriously harm performance or (b) whoever runs the client program will throttle it to avoid starving regular operations, and (c) repairs will not frequently be required.
Of course, the fsfreeze repairs totally blow (b) out of the water, which means that in the long run I'm going to have to find a way shorten the runtime of those repair types (rmap, quota). If (a) is true then maybe we can parallelize some of the AG accesses to reduce freeze time. I'm hoping that will reduce the pain of such things, though. $god help the users on floppy disks.
--D
Quoted 10 lines
Cheers,
Dave.
Signature · 6 lines
Dave Chinner [email protected] -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH 7/22 xfs: add helpers to attach quotas to inodes
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add a helper routine to attach quota information to inodes that are about to undergo repair. If that fails, we need to schedule a quotacheck for the next mount but allow the corrupted metadata repair to continue.
--- fs/xfs/scrub/repair.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 ++ fs/xfs/xfs_quota.h | 16 ++++++++++++++ 3 files changed, 76 insertions(+)
fs/xfs/scrub/repair.c +58 -0
@@ -42,6 +42,7 @@#include "xfs_extent_busy.h" #include "xfs_ag_resv.h" #include "xfs_trans_space.h" +#include "xfs_quota.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h"
@@ -1023,3 +1024,60 @@ xfs_repair_reset_counters(return 0 ; } + +/* Force a quotacheck the next time we mount. */ +void +xfs_repair_force_quotacheck ( +struct xfs_scrub_context *sc, +uint dqtype) +{ +uint flag; + + flag =xfs_quota_chkd_flag (dqtype); +if (!(flag & sc->mp->m_qflags)) +return ; + + sc->mp->m_qflags &= ~flag; +spin_lock (&sc->mp->m_sb_lock); + sc->mp->m_sb.sb_qflags &= ~flag; +spin_unlock (&sc->mp->m_sb_lock); +xfs_log_sb (sc->tp); +} + +/* +* Ensure that dquots are attached to this inode. We cannot allow the dquot +* code to allocate an on-disk dquot block here because we're already in +* transaction context with the inode locked. The on-disk dquot should +* already exist anyway. If the quota code signals corruption or missing +* quota information, schedule quotacheck at next mount. +*/ +int +xfs_repair_ino_dqattach ( +struct xfs_scrub_context *sc) +{ +int error; + + error =xfs_qm_dqattach_locked (sc->ip, false); +switch (error) { +case -EFSBADCRC : +case -EFSCORRUPTED : +case -ENOENT : +xfs_err_ratelimited (sc->mp, +"inode %llu repair encountered quota error %d, quotacheck forced." , + (unsigned long long )sc->ip->i_ino, error); +if (XFS_IS_UQUOTA_ON (sc->mp) && !sc->ip->i_udquot) +xfs_repair_force_quotacheck (sc,XFS_DQ_USER ); +if (XFS_IS_GQUOTA_ON (sc->mp) && !sc->ip->i_gdquot) +xfs_repair_force_quotacheck (sc,XFS_DQ_GROUP ); +if (XFS_IS_PQUOTA_ON (sc->mp) && !sc->ip->i_pdquot) +xfs_repair_force_quotacheck (sc,XFS_DQ_PROJ ); +/* fall through */ +case -ESRCH : + error =0 ; +break ; +default : +break ; + } + +return error; +}
fs/xfs/scrub/repair.h +2 -0
@@ -97,6 +97,8 @@ int xfs_repair_find_ag_btree_roots(struct xfs_scrub_context *sc,struct xfs_repair_find_ag_btree *btree_info, struct xfs_buf *agfl_bp);int xfs_repair_reset_counters (struct xfs_mount *mp); +void xfs_repair_force_quotacheck (struct xfs_scrub_context *sc,uint dqtype); +int xfs_repair_ino_dqattach (struct xfs_scrub_context *sc);/* Metadata repairers */
fs/xfs/xfs_quota.h +16 -0
@@ -48,6 +48,22 @@ struct xfs_trans; (XFS_IS_PQUOTA_ON(mp) && \ (mp->m_sb.sb_qflags & XFS_PQUOTA_CHKD) == 0)) +static inline uint +xfs_quota_chkd_flag ( +uint dqtype) +{ +switch (dqtype) { +case XFS_DQ_USER : +return XFS_UQUOTA_CHKD ; +case XFS_DQ_GROUP : +return XFS_GQUOTA_CHKD ; +case XFS_DQ_PROJ : +return XFS_PQUOTA_CHKD ; +default : +return 0 ; + } +} +/* * The structure kept inside the xfs_trans_t keep track of dquot changes * within a transaction and apply them later.
Re: [PATCH 07/22] xfs: add helpers to attach quotas to inodes
This one looks good to me. Thx!
Reviewed by: Allison Henderson <[email protected]>
Quoted 134 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper routine to attach quota information to inodes that are about to undergo repair. If that fails, we need to schedule a quotacheck for the next mount but allow the corrupted metadata repair to continue.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/repair.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 ++ fs/xfs/xfs_quota.h | 16 ++++++++++++++ 3 files changed, 76 insertions(+)fs/xfs/scrub/repair.c +58
-0@@ -42,6 +42,7 @@#include "xfs_extent_busy.h" #include "xfs_ag_resv.h" #include "xfs_trans_space.h" +#include "xfs_quota.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h" @@ -1023,3 +1024,60 @@ xfs_repair_reset_counters(return 0 ; } + +/* Force a quotacheck the next time we mount. */ +void +xfs_repair_force_quotacheck ( +struct xfs_scrub_context *sc, +uint dqtype) +{ +uint flag; + + flag =xfs_quota_chkd_flag (dqtype); +if (!(flag & sc->mp->m_qflags)) +return ; + + sc->mp->m_qflags &= ~flag; +spin_lock (&sc->mp->m_sb_lock); + sc->mp->m_sb.sb_qflags &= ~flag; +spin_unlock (&sc->mp->m_sb_lock); +xfs_log_sb (sc->tp); +} + +/* +* Ensure that dquots are attached to this inode. We cannot allow the dquot +* code to allocate an on-disk dquot block here because we're already in +* transaction context with the inode locked. The on-disk dquot should +* already exist anyway. If the quota code signals corruption or missing +* quota information, schedule quotacheck at next mount. +*/ +int +xfs_repair_ino_dqattach ( +struct xfs_scrub_context *sc) +{ +int error; + + error =xfs_qm_dqattach_locked (sc->ip, false); +switch (error) { +case -EFSBADCRC : +case -EFSCORRUPTED : +case -ENOENT : +xfs_err_ratelimited (sc->mp, +"inode %llu repair encountered quota error %d, quotacheck forced." , + (unsigned long long )sc->ip->i_ino, error); +if (XFS_IS_UQUOTA_ON (sc->mp) && !sc->ip->i_udquot) +xfs_repair_force_quotacheck (sc,XFS_DQ_USER ); +if (XFS_IS_GQUOTA_ON (sc->mp) && !sc->ip->i_gdquot) +xfs_repair_force_quotacheck (sc,XFS_DQ_GROUP ); +if (XFS_IS_PQUOTA_ON (sc->mp) && !sc->ip->i_pdquot) +xfs_repair_force_quotacheck (sc,XFS_DQ_PROJ ); +/* fall through */ +case -ESRCH : + error =0 ; +break ; +default : +break ; + } + +return error; +}fs/xfs/scrub/repair.h +2
-0@@ -97,6 +97,8 @@ int xfs_repair_find_ag_btree_roots(struct xfs_scrub_context *sc,struct xfs_repair_find_ag_btree *btree_info, struct xfs_buf *agfl_bp);int xfs_repair_reset_counters (struct xfs_mount *mp); +void xfs_repair_force_quotacheck (struct xfs_scrub_context *sc,uint dqtype); +int xfs_repair_ino_dqattach (struct xfs_scrub_context *sc);/* Metadata repairers */ fs/xfs/xfs_quota.h +16
-1@@ -48,6 +48,22 @@ struct xfs_trans; (XFS_IS_PQUOTA_ON(mp) && \ (mp->m_sb.sb_qflags & XFS_PQUOTA_CHKD) == 0)) +static inline uint +xfs_quota_chkd_flag ( +uint dqtype) +{ +switch (dqtype) { +case XFS_DQ_USER : +return XFS_UQUOTA_CHKD ; +case XFS_DQ_GROUP : +return XFS_GQUOTA_CHKD ; +case XFS_DQ_PROJ : +return XFS_PQUOTA_CHKD ; +default : +return 0 ; + } +} +/* * The structure kept inside the xfs_trans_t keep track of dquot changes * within a transaction and apply them later. -- To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwICaQ&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=i_MKNIO9laMI_y2D3Tgc19EICKaIyy10IeEKdvc3soc&s=cw8pXW84tup1SVubP_G8I3xvwifkDLcg8scJFxK9nZ4&e=
PATCH v2 7/22 xfs: add helpers to attach quotas to inodes
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add a helper routine to attach quota information to inodes that are about to undergo repair. If that fails, we need to schedule a quotacheck for the next mount but allow the corrupted metadata repair to continue.
---
v2: improve documentation
--- fs/xfs/scrub/repair.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 ++ fs/xfs/xfs_quota.h | 16 +++++++++++++ 3 files changed, 79 insertions(+)
fs/xfs/scrub/repair.c +61 -0
@@ -42,6 +42,7 @@#include "xfs_extent_busy.h" #include "xfs_ag_resv.h" #include "xfs_trans_space.h" +#include "xfs_quota.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h"
@@ -1115,3 +1116,63 @@ xfs_repair_reset_counters(return 0 ; } + +/* Force a quotacheck the next time we mount. */ +void +xfs_repair_force_quotacheck ( +struct xfs_scrub_context *sc, +uint dqtype) +{ +uint flag; + + flag =xfs_quota_chkd_flag (dqtype); +if (!(flag & sc->mp->m_qflags)) +return ; + + sc->mp->m_qflags &= ~flag; +spin_lock (&sc->mp->m_sb_lock); + sc->mp->m_sb.sb_qflags &= ~flag; +spin_unlock (&sc->mp->m_sb_lock); +xfs_log_sb (sc->tp); +} + +/* +* Attach dquots to this inode, or schedule quotacheck to fix them. +* +* This function ensures that the appropriate dquots are attached to an inode. +* We cannot allow the dquot code to allocate an on-disk dquot block here +* because we're already in transaction context with the inode locked. The +* on-disk dquot should already exist anyway. If the quota code signals +* corruption or missing quota information, schedule quotacheck, which will +* repair corruptions in the quota metadata. +*/ +int +xfs_repair_ino_dqattach ( +struct xfs_scrub_context *sc) +{ +int error; + + error =xfs_qm_dqattach_locked (sc->ip, false); +switch (error) { +case -EFSBADCRC : +case -EFSCORRUPTED : +case -ENOENT : +xfs_err_ratelimited (sc->mp, +"inode %llu repair encountered quota error %d, quotacheck forced." , + (unsigned long long )sc->ip->i_ino, error); +if (XFS_IS_UQUOTA_ON (sc->mp) && !sc->ip->i_udquot) +xfs_repair_force_quotacheck (sc,XFS_DQ_USER ); +if (XFS_IS_GQUOTA_ON (sc->mp) && !sc->ip->i_gdquot) +xfs_repair_force_quotacheck (sc,XFS_DQ_GROUP ); +if (XFS_IS_PQUOTA_ON (sc->mp) && !sc->ip->i_pdquot) +xfs_repair_force_quotacheck (sc,XFS_DQ_PROJ ); +/* fall through */ +case -ESRCH : + error =0 ; +break ; +default : +break ; + } + +return error; +}
fs/xfs/scrub/repair.h +2 -0
@@ -97,6 +97,8 @@ int xfs_repair_find_ag_btree_roots(struct xfs_scrub_context *sc,struct xfs_repair_find_ag_btree *btree_info, struct xfs_buf *agfl_bp);int xfs_repair_reset_counters (struct xfs_mount *mp); +void xfs_repair_force_quotacheck (struct xfs_scrub_context *sc,uint dqtype); +int xfs_repair_ino_dqattach (struct xfs_scrub_context *sc);/* Metadata repairers */
fs/xfs/xfs_quota.h +16 -0
@@ -48,6 +48,22 @@ struct xfs_trans; (XFS_IS_PQUOTA_ON(mp) && \ (mp->m_sb.sb_qflags & XFS_PQUOTA_CHKD) == 0)) +static inline uint +xfs_quota_chkd_flag ( +uint dqtype) +{ +switch (dqtype) { +case XFS_DQ_USER : +return XFS_UQUOTA_CHKD ; +case XFS_DQ_GROUP : +return XFS_GQUOTA_CHKD ; +case XFS_DQ_PROJ : +return XFS_PQUOTA_CHKD ; +default : +return 0 ; + } +} +/* * The structure kept inside the xfs_trans_t keep track of dquot changes * within a transaction and apply them later.
Re: [PATCH v2 07/22] xfs: add helpers to attach quotas to inodes
Quoted 7 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a helper routine to attach quota information to inodes that are about to undergo repair. If that fails, we need to schedule a quotacheck for the next mount but allow the corrupted metadata repair to continue.
Quoted 5 lines
Signed-off-by Darrick J. Wong <[email protected]>Reviewed-by Allison Henderson <[email protected]>---
v2: improve documentation
---
Looks good.
Signature · 2 lines
Dave Chinner [email protected]
PATCH 8/22 xfs: repair superblocks
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
If one of the backup superblocks is found to differ seriously from superblock 0, write out a fresh copy from the in-core sb.
--- fs/xfs/Makefile | 1 + fs/xfs/libxfs/xfs_sb.c | 22 +++++++++++++ fs/xfs/libxfs/xfs_sb.h | 3 ++ fs/xfs/scrub/agheader_repair.c | 70 ++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 + fs/xfs/scrub/scrub.c | 2 + 6 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/agheader_repair.c
fs/xfs/Makefile +1 -0
@@ -174,6 +174,7 @@ xfs-$(CONFIG_XFS_QUOTA) += scrub/quota.o # online repair ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y) xfs-y += $(addprefix scrub/, \ + agheader_repair.o \ repair.o \ ) endif
fs/xfs/libxfs/xfs_sb.c +22 -0
@@ -1097,3 +1097,25 @@ xfs_sb_read_secondary( *bpp = bp;return 0 ; } + +/* Get a secondary superblock buffer. */ +int +xfs_sb_get_secondary ( +struct xfs_mount *mp, +struct xfs_trans *tp, +xfs_agnumber_t agno, +struct xfs_buf **bpp) +{ +struct xfs_buf *bp; + +ASSERT (agno !=0 && agno !=NULLAGNUMBER ); + bp =xfs_trans_get_buf (tp, mp->m_ddev_targp, +XFS_AG_DADDR (mp, agno,XFS_SB_BLOCK (mp)), +XFS_FSS_TO_BB (mp,1 ),0 ); +if (!bp) +return -ENOMEM ; + bp->b_ops = &xfs_sb_buf_ops; +xfs_buf_set_ref (bp,XFS_SSB_REF ); + *bpp = bp; +return 0 ; +}
fs/xfs/libxfs/xfs_sb.h +3 -0
@@ -50,5 +50,8 @@ extern int xfs_fs_geometry(struct xfs_sb *sbp, struct xfs_fsop_geom *geo,extern int xfs_sb_read_secondary (struct xfs_mount *mp,struct xfs_trans *tp,xfs_agnumber_t agno,struct xfs_buf **bpp); +extern int xfs_sb_get_secondary (struct xfs_mount *mp, +struct xfs_trans *tp,xfs_agnumber_t agno, +struct xfs_buf **bpp);#endif /* __XFS_SB_H__ */
fs/xfs/scrub/agheader_repair.c new file +70 -0
70 lines not on this page · raw message
fs/xfs/scrub/repair.h +2 -0
@@ -103,6 +103,7 @@ int xfs_repair_ino_dqattach(struct xfs_scrub_context *sc);/* Metadata repairers */ int xfs_repair_probe (struct xfs_scrub_context *sc); +int xfs_repair_superblock (struct xfs_scrub_context *sc);#else
@@ -131,6 +132,7 @@ static inline int xfs_repair_reset_counters(struct xfs_mount *mp) }#define xfs_repair_probe xfs_repair_notsupported +#define xfs_repair_superblock xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +1 -1
@@ -218,7 +218,7 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_PERAG, .setup = xfs_scrub_setup_fs, .scrub = xfs_scrub_superblock, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_superblock, }, [XFS_SCRUB_TYPE_AGF] = { /* agf */ .type = ST_PERAG,
Re: [PATCH 08/22] xfs: repair superblocks
Looks ok. Thx!
Reviewed by: Allison Henderson <[email protected]>
Quoted 187 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
If one of the backup superblocks is found to differ seriously from superblock 0, write out a fresh copy from the in-core sb.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/Makefile | 1 + fs/xfs/libxfs/xfs_sb.c | 22 +++++++++++++ fs/xfs/libxfs/xfs_sb.h | 3 ++ fs/xfs/scrub/agheader_repair.c | 70 ++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 + fs/xfs/scrub/scrub.c | 2 + 6 files changed, 99 insertions(+), 1 deletion(-)create mode 100644
fs/xfs/scrub/agheader_repair.cfs/xfs/Makefile +1
-0@@ -174,6 +174,7 @@ xfs-$(CONFIG_XFS_QUOTA) += scrub/quota.o # online repair ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y) xfs-y += $(addprefix scrub/, \ + agheader_repair.o \ repair.o \ ) endiffs/xfs/libxfs/xfs_sb.c +22
-0@@ -1097,3 +1097,25 @@ xfs_sb_read_secondary( *bpp = bp;return 0 ; } + +/* Get a secondary superblock buffer. */ +int +xfs_sb_get_secondary ( +struct xfs_mount *mp, +struct xfs_trans *tp, +xfs_agnumber_t agno, +struct xfs_buf **bpp) +{ +struct xfs_buf *bp; + +ASSERT (agno !=0 && agno !=NULLAGNUMBER ); + bp =xfs_trans_get_buf (tp, mp->m_ddev_targp, +XFS_AG_DADDR (mp, agno,XFS_SB_BLOCK (mp)), +XFS_FSS_TO_BB (mp,1 ),0 ); +if (!bp) +return -ENOMEM ; + bp->b_ops = &xfs_sb_buf_ops; +xfs_buf_set_ref (bp,XFS_SSB_REF ); + *bpp = bp; +return 0 ; +}fs/xfs/libxfs/xfs_sb.h +3
-0@@ -50,5 +50,8 @@ extern int xfs_fs_geometry(struct xfs_sb *sbp, struct xfs_fsop_geom *geo,extern int xfs_sb_read_secondary (struct xfs_mount *mp,struct xfs_trans *tp,xfs_agnumber_t agno,struct xfs_buf **bpp); +extern int xfs_sb_get_secondary (struct xfs_mount *mp, +struct xfs_trans *tp,xfs_agnumber_t agno, +struct xfs_buf **bpp);#endif /* __XFS_SB_H__ */ fs/xfs/scrub/agheader_repair.c new file +70
-0@@ -0,0 +1,70 @@ +/* +* Copyright (C) 2018 Oracle. All Rights Reserved. +* +* Author: Darrick J. Wong <[email protected]> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it would be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include "xfs.h" +#include "xfs_fs.h" +#include "xfs_shared.h" +#include "xfs_format.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" +#include "xfs_defer.h" +#include "xfs_btree.h" +#include "xfs_bit.h" +#include "xfs_log_format.h" +#include "xfs_trans.h" +#include "xfs_sb.h" +#include "xfs_inode.h" +#include "xfs_alloc.h" +#include "xfs_ialloc.h" +#include "xfs_rmap.h" +#include "scrub/xfs_scrub.h" +#include "scrub/scrub.h" +#include "scrub/common.h" +#include "scrub/trace.h" + +/* Superblock */ + +/* Repair the superblock. */ +int +xfs_repair_superblock ( +struct xfs_scrub_context *sc) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_buf *bp; +xfs_agnumber_t agno; +int error; + +/* Don't try to repair AG 0's sb; let xfs_repair deal with it. */ + agno = sc->sm->sm_agno; +if (agno ==0 ) +return -EOPNOTSUPP ; + + error =xfs_sb_get_secondary (mp, sc->tp, agno, &bp); +if (error) +return error; + +/* Copy AG 0's superblock to this one. */ +xfs_buf_zero (bp,0 ,BBTOB (bp->b_length)); +xfs_sb_to_disk (XFS_BUF_TO_SBP (bp), &mp->m_sb); + +/* Write this to disk. */ +xfs_trans_buf_set_type (sc->tp, bp,XFS_BLFT_SB_BUF ); +xfs_trans_log_buf (sc->tp, bp,0 ,BBTOB (bp->b_length) -1 ); +return error; +}fs/xfs/scrub/repair.h +2
-0@@ -103,6 +103,7 @@ int xfs_repair_ino_dqattach(struct xfs_scrub_context *sc);/* Metadata repairers */ int xfs_repair_probe (struct xfs_scrub_context *sc); +int xfs_repair_superblock (struct xfs_scrub_context *sc);#else @@ -131,6 +132,7 @@ static inline int xfs_repair_reset_counters(struct xfs_mount *mp) }#define xfs_repair_probe xfs_repair_notsupported +#define xfs_repair_superblock xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */ fs/xfs/scrub/scrub.c +1
-2@@ -218,7 +218,7 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_PERAG, .setup = xfs_scrub_setup_fs, .scrub = xfs_scrub_superblock, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_superblock, }, [XFS_SCRUB_TYPE_AGF] = { /* agf */ .type = ST_PERAG, --To unsubscribe from this list: send the line "unsubscribe linux-xfs" in
the body of a message to [email protected]
More majordomo info at https://urldefense.proofpoint.com/v2/url?u=http-3A__vger.kernel.org_majordomo-2Dinfo.html&d=DwICaQ&c=RoP1YumCXCgaWHvlZYR8PZh8Bv7qIrMUB65eapI_JnE&r=LHZQ8fHvy6wDKXGTWcm97burZH5sQKHRDMaY1UthQxc&m=1JFQm3tOfB4lBtH4Ota_fVXDFbuFCNX6jUVpnImv860&s=p9BNrzBGzQZvFfEbGNgnVPBbDjyfRy-ZnwcLyqXcrl8&e=
Re: [PATCH 08/22] xfs: repair superblocks
In reply to Darrick J. WongQuoted 6 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
If one of the backup superblocks is found to differ seriously from superblock 0, write out a fresh copy from the in-core sb.
Signed-off-by Darrick J. Wong <[email protected]>
Looks good, couple of minor things and one for a followup patch.
Quoted 28 lines
--- fs/xfs/Makefile | 1 + fs/xfs/libxfs/xfs_sb.c | 22 +++++++++++++ fs/xfs/libxfs/xfs_sb.h | 3 ++ fs/xfs/scrub/agheader_repair.c | 70 ++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 + fs/xfs/scrub/scrub.c | 2 + 6 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/agheader_repair.cfs/xfs/Makefile +1
-0@@ -174,6 +174,7 @@ xfs-$(CONFIG_XFS_QUOTA) += scrub/quota.o # online repair ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y) xfs-y += $(addprefix scrub/, \ + agheader_repair.o \ repair.o \ ) endiffs/xfs/libxfs/xfs_sb.c +0
-0
fs/xfs/libxfs/xfs_sb.c xfs_sb_read_secondary( · line 1101
Quoted 5 lines
fs/xfs/libxfs/xfs_sb.c +2
-0@@ -1097,3 +1097,25 @@ xfs_sb_read_secondary( *bpp = bp;return 0 ; } + +/* Get a secondary superblock buffer. */
/* get an uninitialised secondary superblock buffer */
Quoted 8 lines
fs/xfs/libxfs/xfs_sb.c +8
-0+int +xfs_sb_get_secondary ( +struct xfs_mount *mp, +struct xfs_trans *tp, +xfs_agnumber_t agno, +struct xfs_buf **bpp) +{ +struct xfs_buf *bp;
fs/xfs/libxfs/xfs_sb.c xfs_sb_read_secondary( · line 1119
Quoted 10 lines
fs/xfs/libxfs/xfs_sb.c +10
-0+ +ASSERT (agno !=0 && agno !=NULLAGNUMBER ); + bp =xfs_trans_get_buf (tp, mp->m_ddev_targp, +XFS_AG_DADDR (mp, agno,XFS_SB_BLOCK (mp)), +XFS_FSS_TO_BB (mp,1 ),0 ); +if (!bp) +return -ENOMEM ; + bp->b_ops = &xfs_sb_buf_ops; +xfs_buf_set_ref (bp,XFS_SSB_REF ); + *bpp = bp;
This should probably call xfs_buf_oneshot(bp) rather than having multiple mechanisms for doing the same thing. Same goes for xfs_sb_read_secondary().
Quoted 81 lines
fs/xfs/libxfs/xfs_sb.c +2
-0+return 0 ; +}fs/xfs/libxfs/xfs_sb.h +3
-0@@ -50,5 +50,8 @@ extern int xfs_fs_geometry(struct xfs_sb *sbp, struct xfs_fsop_geom *geo,extern int xfs_sb_read_secondary (struct xfs_mount *mp,struct xfs_trans *tp,xfs_agnumber_t agno,struct xfs_buf **bpp); +extern int xfs_sb_get_secondary (struct xfs_mount *mp, +struct xfs_trans *tp,xfs_agnumber_t agno, +struct xfs_buf **bpp);#endif /* __XFS_SB_H__ */ fs/xfs/scrub/agheader_repair.c new file +60
-0@@ -0,0 +1,70 @@ +/* +* Copyright (C) 2018 Oracle. All Rights Reserved. +* +* Author: Darrick J. Wong <[email protected]> +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it would be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write the Free Software Foundation, +* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. +*/ +#include "xfs.h" +#include "xfs_fs.h" +#include "xfs_shared.h" +#include "xfs_format.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" +#include "xfs_defer.h" +#include "xfs_btree.h" +#include "xfs_bit.h" +#include "xfs_log_format.h" +#include "xfs_trans.h" +#include "xfs_sb.h" +#include "xfs_inode.h" +#include "xfs_alloc.h" +#include "xfs_ialloc.h" +#include "xfs_rmap.h" +#include "scrub/xfs_scrub.h" +#include "scrub/scrub.h" +#include "scrub/common.h" +#include "scrub/trace.h" + +/* Superblock */ + +/* Repair the superblock. */ +int +xfs_repair_superblock ( +struct xfs_scrub_context *sc) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_buf *bp; +xfs_agnumber_t agno; +int error; + +/* Don't try to repair AG 0's sb; let xfs_repair deal with it. */ + agno = sc->sm->sm_agno; +if (agno ==0 ) +return -EOPNOTSUPP ; + + error =xfs_sb_get_secondary (mp, sc->tp, agno, &bp); +if (error) +return error;
fs/xfs/scrub/agheader_repair.c line 70
Quoted 10 lines
fs/xfs/scrub/agheader_repair.c new file +10
-0+ +/* Copy AG 0's superblock to this one. */ +xfs_buf_zero (bp,0 ,BBTOB (bp->b_length)); +xfs_sb_to_disk (XFS_BUF_TO_SBP (bp), &mp->m_sb); + +/* Write this to disk. */ +xfs_trans_buf_set_type (sc->tp, bp,XFS_BLFT_SB_BUF ); +xfs_trans_log_buf (sc->tp, bp,0 ,BBTOB (bp->b_length) -1 ); +return error; +}
This is largely common with the core of xfs_update_secondary_sbs() and can go into fs/xfs/libxfs/xfs_sb.c
int
xfs_sb_init_secondary(
struct xfs_mount *mp,
struct xfs_trans *tp,
xfs_agnumber_t agno
struct xfs_buf **bpp)
{
struct xfs_buf *bp;
int error;
error = xfs_sb_get_secondary(mp, tp, agno, &bp);
if (error)
return error;
/* Copy AG 0's superblock to this one. */
xfs_buf_zero(bp, 0, BBTOB(bp->b_length));
xfs_sb_to_disk(XFS_BUF_TO_SBP(bp), &mp->m_sb);
if (tp) {
xfs_trans_buf_set_type(sc->tp, bp, XFS_BLFT_SB_BUF);
xfs_trans_log_buf(sc->tp, bp, 0, BBTOB(bp->b_length) - 1);
}
if (bpp)
*bpp = bp;
return 0;
}Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
PATCH 9/22 xfs: repair the AGF and AGFL
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Regenerate the AGF and AGFL from the rmap data.
--- fs/xfs/scrub/agheader_repair.c | 459 ++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 4 fs/xfs/scrub/scrub.c | 4 3 files changed, 465 insertions(+), 2 deletions(-)
fs/xfs/scrub/agheader_repair.c +459 -0
@@ -31,12 +31,18 @@#include "xfs_sb.h" #include "xfs_inode.h" #include "xfs_alloc.h" +#include "xfs_alloc_btree.h" #include "xfs_ialloc.h" +#include "xfs_ialloc_btree.h" #include "xfs_rmap.h" +#include "xfs_rmap_btree.h" +#include "xfs_refcount.h" +#include "xfs_refcount_btree.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" +#include "scrub/repair.h" /* Superblock */
@@ -68,3 +74,456 @@ xfs_repair_superblock(xfs_trans_log_buf (sc->tp, bp,0 ,BBTOB (bp->b_length) -1 );return error; } + +/* AGF */ + +struct xfs_repair_agf_allocbt { +struct xfs_scrub_context *sc; +xfs_agblock_t freeblks; +xfs_agblock_t longest; +}; + +/* Record free space shape information. */ +STATIC int +xfs_repair_agf_walk_allocbt ( +struct xfs_btree_cur *cur, +struct xfs_alloc_rec_incore *rec, +void *priv) +{ +struct xfs_repair_agf_allocbt *raa = priv; +int error =0 ; + +if (xfs_scrub_should_terminate (raa->sc, &error)) +return error; + + raa->freeblks += rec->ar_blockcount; +if (rec->ar_blockcount > raa->longest) + raa->longest = rec->ar_blockcount; +return error; +} + +/* Does this AGFL block look sane? */ +STATIC int +xfs_repair_agf_check_agfl_block ( +struct xfs_mount *mp, +xfs_agblock_t agbno, +void *priv) +{ +struct xfs_scrub_context *sc = priv; + +if (!xfs_verify_agbno (mp, sc->sa.agno, agbno)) +return -EFSCORRUPTED ; +return 0 ; +} + +/* Repair the AGF. */ +int +xfs_repair_agf ( +struct xfs_scrub_context *sc) +{ +struct xfs_repair_find_ag_btree fab[] = { + { + .rmap_owner =XFS_RMAP_OWN_AG , + .buf_ops = &xfs_allocbt_buf_ops, + .magic =XFS_ABTB_CRC_MAGIC , + }, + { + .rmap_owner =XFS_RMAP_OWN_AG , + .buf_ops = &xfs_allocbt_buf_ops, + .magic =XFS_ABTC_CRC_MAGIC , + }, + { + .rmap_owner =XFS_RMAP_OWN_AG , + .buf_ops = &xfs_rmapbt_buf_ops, + .magic =XFS_RMAP_CRC_MAGIC , + }, + { + .rmap_owner =XFS_RMAP_OWN_REFC , + .buf_ops = &xfs_refcountbt_buf_ops, + .magic =XFS_REFC_CRC_MAGIC , + }, + { + .buf_ops =NULL , + }, + }; +struct xfs_repair_agf_allocbt raa; +struct xfs_agf old_agf; +struct xfs_mount *mp = sc->mp; +struct xfs_buf *agf_bp; +struct xfs_buf *agfl_bp; +struct xfs_agf *agf; +struct xfs_btree_cur *cur =NULL ; +struct xfs_perag *pag; +xfs_agblock_t blocks; +xfs_agblock_t freesp_blocks; +int error; + +/* We require the rmapbt to rebuild anything. */ +if (!xfs_sb_version_hasrmapbt (&mp->m_sb)) +return -EOPNOTSUPP ; + +memset (&raa,0 ,sizeof (raa)); + error =xfs_trans_read_buf (mp, sc->tp, mp->m_ddev_targp, +XFS_AG_DADDR (mp, sc->sa.agno,XFS_AGF_DADDR (mp)), +XFS_FSS_TO_BB (mp,1 ),0 , &agf_bp,NULL ); +if (error) +return error; + agf_bp->b_ops = &xfs_agf_buf_ops; + +/* +* Load the AGFL so that we can screen out OWN_AG blocks that +* are on the AGFL now; these blocks might have once been part +* of the bno/cnt/rmap btrees but are not now. +*/ + error =xfs_alloc_read_agfl (mp, sc->tp, sc->sa.agno, &agfl_bp); +if (error) +return error; + error =xfs_agfl_walk (sc->mp,XFS_BUF_TO_AGF (agf_bp), agfl_bp, + xfs_repair_agf_check_agfl_block, sc); +if (error) +return error; + +/* Find the btree roots. */ + error =xfs_repair_find_ag_btree_roots (sc, agf_bp, fab, agfl_bp); +if (error) +return error; +if (fab[0 ].root ==NULLAGBLOCK || fab[0 ].height >XFS_BTREE_MAXLEVELS || + fab[1 ].root ==NULLAGBLOCK || fab[1 ].height >XFS_BTREE_MAXLEVELS || + fab[2 ].root ==NULLAGBLOCK || fab[2 ].height >XFS_BTREE_MAXLEVELS ) +return -EFSCORRUPTED ; +if (xfs_sb_version_hasreflink (&mp->m_sb) && + (fab[3 ].root ==NULLAGBLOCK || fab[3 ].height >XFS_BTREE_MAXLEVELS )) +return -EFSCORRUPTED ; + +/* Start rewriting the header. */ + agf =XFS_BUF_TO_AGF (agf_bp); + old_agf = *agf; +/* +* We relied on the rmapbt to reconstruct the AGF. If we get a +* different root then something's seriously wrong. +*/ +if (be32_to_cpu (old_agf.agf_roots[XFS_BTNUM_RMAPi]) != fab[2 ].root) +return -EFSCORRUPTED ; +memset (agf,0 , mp->m_sb.sb_sectsize); + agf->agf_magicnum =cpu_to_be32 (XFS_AGF_MAGIC ); + agf->agf_versionnum =cpu_to_be32 (XFS_AGF_VERSION ); + agf->agf_seqno =cpu_to_be32 (sc->sa.agno); + agf->agf_length =cpu_to_be32 (xfs_ag_block_count (mp, sc->sa.agno)); + agf->agf_roots[XFS_BTNUM_BNOi] =cpu_to_be32 (fab[0 ].root); + agf->agf_roots[XFS_BTNUM_CNTi] =cpu_to_be32 (fab[1 ].root); + agf->agf_roots[XFS_BTNUM_RMAPi] =cpu_to_be32 (fab[2 ].root); + agf->agf_levels[XFS_BTNUM_BNOi] =cpu_to_be32 (fab[0 ].height); + agf->agf_levels[XFS_BTNUM_CNTi] =cpu_to_be32 (fab[1 ].height); + agf->agf_levels[XFS_BTNUM_RMAPi] =cpu_to_be32 (fab[2 ].height); + agf->agf_flfirst = old_agf.agf_flfirst; + agf->agf_fllast = old_agf.agf_fllast; + agf->agf_flcount = old_agf.agf_flcount; +if (xfs_sb_version_hascrc (&mp->m_sb)) +uuid_copy (&agf->agf_uuid, &mp->m_sb.sb_meta_uuid); +if (xfs_sb_version_hasreflink (&mp->m_sb)) { + agf->agf_refcount_root =cpu_to_be32 (fab[3 ].root); + agf->agf_refcount_level =cpu_to_be32 (fab[3 ].height); + } + +/* Update the AGF counters from the bnobt. */ + cur =xfs_allocbt_init_cursor (mp, sc->tp, agf_bp, sc->sa.agno, +XFS_BTNUM_BNO ); + raa.sc = sc; + error =xfs_alloc_query_all (cur, xfs_repair_agf_walk_allocbt, &raa); +if (error) + goto err; + error =xfs_btree_count_blocks (cur, &blocks); +if (error) + goto err; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + freesp_blocks = blocks -1 ; + agf->agf_freeblks =cpu_to_be32 (raa.freeblks); + agf->agf_longest =cpu_to_be32 (raa.longest); + +/* Update the AGF counters from the cntbt. */ + cur =xfs_allocbt_init_cursor (mp, sc->tp, agf_bp, sc->sa.agno, +XFS_BTNUM_CNT ); + error =xfs_btree_count_blocks (cur, &blocks); +if (error) + goto err; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + freesp_blocks += blocks -1 ; + +/* Update the AGF counters from the rmapbt. */ + cur =xfs_rmapbt_init_cursor (mp, sc->tp, agf_bp, sc->sa.agno); + error =xfs_btree_count_blocks (cur, &blocks); +if (error) + goto err; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + agf->agf_rmap_blocks =cpu_to_be32 (blocks); + freesp_blocks += blocks -1 ; + +/* Update the AGF counters from the refcountbt. */ +if (xfs_sb_version_hasreflink (&mp->m_sb)) { + cur =xfs_refcountbt_init_cursor (mp, sc->tp, agf_bp, + sc->sa.agno,NULL ); + error =xfs_btree_count_blocks (cur, &blocks); +if (error) + goto err; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + agf->agf_refcount_blocks =cpu_to_be32 (blocks); + } + agf->agf_btreeblks =cpu_to_be32 (freesp_blocks); + cur =NULL ; + +/* Trigger reinitialization of the in-core data. */ +if (raa.freeblks !=be32_to_cpu (old_agf.agf_freeblks) || + freesp_blocks !=be32_to_cpu (old_agf.agf_btreeblks) || + raa.longest !=be32_to_cpu (old_agf.agf_longest) || + fab[0 ].height !=be32_to_cpu (old_agf.agf_levels[XFS_BTNUM_BNOi]) || + fab[1 ].height !=be32_to_cpu (old_agf.agf_levels[XFS_BTNUM_CNTi]) || + fab[2 ].height !=be32_to_cpu (old_agf.agf_levels[XFS_BTNUM_RMAPi]) || + fab[3 ].height !=be32_to_cpu (old_agf.agf_refcount_level)) { + pag =xfs_perag_get (mp, sc->sa.agno); +if (pag->pagf_init) { + pag->pagf_freeblks =be32_to_cpu (agf->agf_freeblks); + pag->pagf_btreeblks =be32_to_cpu (agf->agf_btreeblks); + pag->pagf_flcount =be32_to_cpu (agf->agf_flcount); + pag->pagf_longest =be32_to_cpu (agf->agf_longest); + pag->pagf_levels[XFS_BTNUM_BNOi] = +be32_to_cpu (agf->agf_levels[XFS_BTNUM_BNOi]); + pag->pagf_levels[XFS_BTNUM_CNTi] = +be32_to_cpu (agf->agf_levels[XFS_BTNUM_CNTi]); + pag->pagf_levels[XFS_BTNUM_RMAPi] = +be32_to_cpu (agf->agf_levels[XFS_BTNUM_RMAPi]); + pag->pagf_refcount_level = +be32_to_cpu (agf->agf_refcount_level); + } +xfs_perag_put (pag); + sc->reset_counters = true; + } + +/* Write this to disk. */ +xfs_trans_buf_set_type (sc->tp, agf_bp,XFS_BLFT_AGF_BUF ); +xfs_trans_log_buf (sc->tp, agf_bp,0 , mp->m_sb.sb_sectsize -1 ); +return error; + +err: +if (cur) +xfs_btree_del_cursor (cur, error ?XFS_BTREE_ERROR : +XFS_BTREE_NOERROR ); + *agf = old_agf; +return error; +} + +/* AGFL */ + +struct xfs_repair_agfl { +struct xfs_repair_extent_list freesp_list; +struct xfs_repair_extent_list agmeta_list; +struct xfs_scrub_context *sc; +}; + +/* Record all freespace information. */ +STATIC int +xfs_repair_agfl_rmap_fn ( +struct xfs_btree_cur *cur, +struct xfs_rmap_irec *rec, +void *priv) +{ +struct xfs_repair_agfl *ra = priv; +struct xfs_buf *bp; +xfs_fsblock_t fsb; +int i; +int error =0 ; + +if (xfs_scrub_should_terminate (ra->sc, &error)) +return error; + +/* Record all the OWN_AG blocks... */ +if (rec->rm_owner ==XFS_RMAP_OWN_AG ) { + fsb =XFS_AGB_TO_FSB (cur->bc_mp, cur->bc_private.a.agno, + rec->rm_startblock); + error =xfs_repair_collect_btree_extent (ra->sc, + &ra->freesp_list, fsb, rec->rm_blockcount); +if (error) +return error; + } + +/* ...and all the rmapbt blocks... */ +for (i =0 ; i < cur->bc_nlevels && cur->bc_ptrs[i] ==1 ; i++) { +xfs_btree_get_block (cur, i, &bp); +if (!bp) +continue ; + fsb =XFS_DADDR_TO_FSB (cur->bc_mp, bp->b_bn); + error =xfs_repair_collect_btree_extent (ra->sc, + &ra->agmeta_list, fsb,1 ); +if (error) +return error; + } + +return 0 ; +} + +/* Add a btree block to the agmeta list. */ +STATIC int +xfs_repair_agfl_visit_btblock ( +struct xfs_btree_cur *cur, +int level, +void *priv) +{ +struct xfs_repair_agfl *ra = priv; +struct xfs_buf *bp; +xfs_fsblock_t fsb; +int error =0 ; + +if (xfs_scrub_should_terminate (ra->sc, &error)) +return error; + +xfs_btree_get_block (cur, level, &bp); +if (!bp) +return 0 ; + + fsb =XFS_DADDR_TO_FSB (cur->bc_mp, bp->b_bn); +return xfs_repair_collect_btree_extent (ra->sc, &ra->agmeta_list, + fsb,1 ); +} + +/* Repair the AGFL. */ +int +xfs_repair_agfl ( +struct xfs_scrub_context *sc) +{ +struct xfs_repair_agfl ra; +struct xfs_owner_info oinfo; +struct xfs_mount *mp = sc->mp; +struct xfs_buf *agf_bp; +struct xfs_buf *agfl_bp; +struct xfs_agf *agf; +struct xfs_agfl *agfl; +struct xfs_btree_cur *cur =NULL ; +struct xfs_perag *pag; +__be32 *agfl_bno; +struct xfs_repair_extent *rae; +struct xfs_repair_extent *n; +xfs_agblock_t flcount; +xfs_agblock_t agbno; +xfs_agblock_t bno; +xfs_agblock_t old_flcount; +int error; + +/* We require the rmapbt to rebuild anything. */ +if (!xfs_sb_version_hasrmapbt (&mp->m_sb)) +return -EOPNOTSUPP ; + +xfs_repair_init_extent_list (&ra.freesp_list); +xfs_repair_init_extent_list (&ra.agmeta_list); + ra.sc = sc; + + error =xfs_alloc_read_agf (mp, sc->tp, sc->sa.agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + + error =xfs_trans_read_buf (mp, sc->tp, mp->m_ddev_targp, +XFS_AG_DADDR (mp, sc->sa.agno,XFS_AGFL_DADDR (mp)), +XFS_FSS_TO_BB (mp,1 ),0 , &agfl_bp,NULL ); +if (error) +return error; + agfl_bp->b_ops = &xfs_agfl_buf_ops; + +/* Find all space used by the free space btrees & rmapbt. */ + cur =xfs_rmapbt_init_cursor (mp, sc->tp, agf_bp, sc->sa.agno); + error =xfs_rmap_query_all (cur, xfs_repair_agfl_rmap_fn, &ra); +if (error) + goto err; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + +/* Find all space used by bnobt. */ + cur =xfs_allocbt_init_cursor (mp, sc->tp, agf_bp, sc->sa.agno, +XFS_BTNUM_BNO ); + error =xfs_btree_visit_blocks (cur, xfs_repair_agfl_visit_btblock, + &ra); +if (error) + goto err; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + +/* Find all space used by cntbt. */ + cur =xfs_allocbt_init_cursor (mp, sc->tp, agf_bp, sc->sa.agno, +XFS_BTNUM_CNT ); + error =xfs_btree_visit_blocks (cur, xfs_repair_agfl_visit_btblock, + &ra); +if (error) + goto err; +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); + cur =NULL ; + +/* +* Drop the freesp meta blocks that are in use by btrees. +* The remaining blocks /should/ be AGFL blocks. +*/ + error =xfs_repair_subtract_extents (sc, &ra.freesp_list, + &ra.agmeta_list); +if (error) + goto err; +xfs_repair_cancel_btree_extents (sc, &ra.agmeta_list); + +/* Start rewriting the header. */ + agfl =XFS_BUF_TO_AGFL (agfl_bp); +memset (agfl,0xFF , mp->m_sb.sb_sectsize); + agfl->agfl_magicnum =cpu_to_be32 (XFS_AGFL_MAGIC ); + agfl->agfl_seqno =cpu_to_be32 (sc->sa.agno); +uuid_copy (&agfl->agfl_uuid, &mp->m_sb.sb_meta_uuid); + +/* Fill the AGFL with the remaining blocks. */ + flcount =0 ; + agfl_bno =XFS_BUF_TO_AGFL_BNO (mp, agfl_bp); +for_each_xfs_repair_extent_safe (rae, n, &ra.freesp_list) { + agbno =XFS_FSB_TO_AGBNO (mp, rae->fsbno); + +trace_xfs_repair_agfl_insert (mp, sc->sa.agno, agbno, rae->len); + +for (bno =0 ; bno < rae->len; bno++) { +if (flcount >=xfs_agfl_size (mp) -1 ) +break ; + agfl_bno[flcount +1 ] =cpu_to_be32 (agbno + bno); + flcount++; + } + rae->fsbno += bno; + rae->len -= bno; +if (rae->len) +break ; +list_del (&rae->list); +kmem_free (rae); + } + +/* Update the AGF counters. */ + agf =XFS_BUF_TO_AGF (agf_bp); + old_flcount =be32_to_cpu (agf->agf_flcount); + agf->agf_flfirst =cpu_to_be32 (1 ); + agf->agf_flcount =cpu_to_be32 (flcount); + agf->agf_fllast =cpu_to_be32 (flcount); + +/* Trigger reinitialization of the in-core data. */ +if (flcount != old_flcount) { + pag =xfs_perag_get (mp, sc->sa.agno); +if (pag->pagf_init) + pag->pagf_flcount = flcount; +xfs_perag_put (pag); + sc->reset_counters = true; + } + +/* Write AGF and AGFL to disk. */ +xfs_alloc_log_agf (sc->tp, agf_bp, +XFS_AGF_FLFIRST |XFS_AGF_FLLAST |XFS_AGF_FLCOUNT ); +xfs_trans_buf_set_type (sc->tp, agfl_bp,XFS_BLFT_AGFL_BUF ); +xfs_trans_log_buf (sc->tp, agfl_bp,0 , mp->m_sb.sb_sectsize -1 ); + +/* Dump any AGFL overflow. */ +xfs_rmap_ag_owner (&oinfo,XFS_RMAP_OWN_AG ); +return xfs_repair_reap_btree_extents (sc, &ra.freesp_list, &oinfo, +XFS_AG_RESV_AGFL ); +err: +xfs_repair_cancel_btree_extents (sc, &ra.agmeta_list); +xfs_repair_cancel_btree_extents (sc, &ra.freesp_list); +if (cur) +xfs_btree_del_cursor (cur, error ?XFS_BTREE_ERROR : +XFS_BTREE_NOERROR ); +return error; +}
fs/xfs/scrub/repair.h +4 -0
@@ -104,6 +104,8 @@ int xfs_repair_ino_dqattach(struct xfs_scrub_context *sc);int xfs_repair_probe (struct xfs_scrub_context *sc);int xfs_repair_superblock (struct xfs_scrub_context *sc); +int xfs_repair_agf (struct xfs_scrub_context *sc); +int xfs_repair_agfl (struct xfs_scrub_context *sc);#else
@@ -133,6 +135,8 @@ static inline int xfs_repair_reset_counters(struct xfs_mount *mp)#define xfs_repair_probe xfs_repair_notsupported#define xfs_repair_superblock xfs_repair_notsupported +#define xfs_repair_agf xfs_repair_notsupported +#define xfs_repair_agfl xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +2 -2
@@ -224,13 +224,13 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_PERAG, .setup = xfs_scrub_setup_fs, .scrub = xfs_scrub_agf, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_agf, }, [XFS_SCRUB_TYPE_AGFL]= { /* agfl */ .type = ST_PERAG, .setup = xfs_scrub_setup_fs, .scrub = xfs_scrub_agfl, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_agfl, }, [XFS_SCRUB_TYPE_AGI] = { /* agi */ .type = ST_PERAG,
PATCH 10/22 xfs: repair the AGI
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Rebuild the AGI header items with some help from the rmapbt.
--- fs/xfs/scrub/agheader_repair.c | 111 ++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 + fs/xfs/scrub/scrub.c | 2 - 3 files changed, 114 insertions(+), 1 deletion(-)
fs/xfs/scrub/agheader_repair.c +111 -0
@@ -527,3 +527,114 @@ xfs_repair_agfl( XFS_BTREE_NOERROR);return error; } + +/* AGI */ + +int +xfs_repair_agi ( +struct xfs_scrub_context *sc) +{ +struct xfs_repair_find_ag_btree fab[] = { + { + .rmap_owner =XFS_RMAP_OWN_INOBT , + .buf_ops = &xfs_inobt_buf_ops, + .magic =XFS_IBT_CRC_MAGIC , + }, + { + .rmap_owner =XFS_RMAP_OWN_INOBT , + .buf_ops = &xfs_inobt_buf_ops, + .magic =XFS_FIBT_CRC_MAGIC , + }, + { + .buf_ops =NULL + }, + }; +struct xfs_agi old_agi; +struct xfs_mount *mp = sc->mp; +struct xfs_buf *agi_bp; +struct xfs_buf *agf_bp; +struct xfs_agi *agi; +struct xfs_btree_cur *cur; +struct xfs_perag *pag; +xfs_agino_t old_count; +xfs_agino_t old_freecount; +xfs_agino_t count; +xfs_agino_t freecount; +int bucket; +int error; + +/* We require the rmapbt to rebuild anything. */ +if (!xfs_sb_version_hasrmapbt (&mp->m_sb)) +return -EOPNOTSUPP ; + + error =xfs_trans_read_buf (mp, sc->tp, mp->m_ddev_targp, +XFS_AG_DADDR (mp, sc->sa.agno,XFS_AGI_DADDR (mp)), +XFS_FSS_TO_BB (mp,1 ),0 , &agi_bp,NULL ); +if (error) +return error; + agi_bp->b_ops = &xfs_agi_buf_ops; + + error =xfs_alloc_read_agf (mp, sc->tp, sc->sa.agno,0 , &agf_bp); +if (error) +return error; +if (!agf_bp) +return -ENOMEM ; + +/* Find the btree roots. */ + error =xfs_repair_find_ag_btree_roots (sc, agf_bp, fab,NULL ); +if (error) +return error; +if (fab[0 ].root ==NULLAGBLOCK || fab[0 ].height >XFS_BTREE_MAXLEVELS ) +return -EFSCORRUPTED ; +if (xfs_sb_version_hasfinobt (&mp->m_sb) && + (fab[1 ].root ==NULLAGBLOCK || fab[1 ].height >XFS_BTREE_MAXLEVELS )) +return -EFSCORRUPTED ; + +/* Start rewriting the header. */ + agi =XFS_BUF_TO_AGI (agi_bp); + old_agi = *agi; + old_count =be32_to_cpu (old_agi.agi_count); + old_freecount =be32_to_cpu (old_agi.agi_freecount); +memset (agi,0 , mp->m_sb.sb_sectsize); + agi->agi_magicnum =cpu_to_be32 (XFS_AGI_MAGIC ); + agi->agi_versionnum =cpu_to_be32 (XFS_AGI_VERSION ); + agi->agi_seqno =cpu_to_be32 (sc->sa.agno); + agi->agi_length =cpu_to_be32 (xfs_ag_block_count (mp, sc->sa.agno)); + agi->agi_newino =cpu_to_be32 (NULLAGINO ); + agi->agi_dirino =cpu_to_be32 (NULLAGINO ); +if (xfs_sb_version_hascrc (&mp->m_sb)) +uuid_copy (&agi->agi_uuid, &mp->m_sb.sb_meta_uuid); +for (bucket =0 ; bucket <XFS_AGI_UNLINKED_BUCKETS ; bucket++) + agi->agi_unlinked[bucket] =cpu_to_be32 (NULLAGINO ); + agi->agi_root =cpu_to_be32 (fab[0 ].root); + agi->agi_level =cpu_to_be32 (fab[0 ].height); +if (xfs_sb_version_hasfinobt (&mp->m_sb)) { + agi->agi_free_root =cpu_to_be32 (fab[1 ].root); + agi->agi_free_level =cpu_to_be32 (fab[1 ].height); + } + +/* Update the AGI counters. */ + cur =xfs_inobt_init_cursor (mp, sc->tp, agi_bp, sc->sa.agno, +XFS_BTNUM_INO ); + error =xfs_ialloc_count_inodes (cur, &count, &freecount); +xfs_btree_del_cursor (cur,XFS_BTREE_NOERROR ); +if (error) + goto err; + agi->agi_count =cpu_to_be32 (count); + agi->agi_freecount =cpu_to_be32 (freecount); +if (old_count != count || old_freecount != freecount) { + pag =xfs_perag_get (mp, sc->sa.agno); + pag->pagi_init =0 ; +xfs_perag_put (pag); + sc->reset_counters = true; + } + +/* Write this to disk. */ +xfs_trans_buf_set_type (sc->tp, agi_bp,XFS_BLFT_AGI_BUF ); +xfs_trans_log_buf (sc->tp, agi_bp,0 , mp->m_sb.sb_sectsize -1 ); +return error; + +err: + *agi = old_agi; +return error; +}
fs/xfs/scrub/repair.h +2 -0
@@ -106,6 +106,7 @@ int xfs_repair_probe(struct xfs_scrub_context *sc);int xfs_repair_superblock (struct xfs_scrub_context *sc);int xfs_repair_agf (struct xfs_scrub_context *sc);int xfs_repair_agfl (struct xfs_scrub_context *sc); +int xfs_repair_agi (struct xfs_scrub_context *sc);#else
@@ -137,6 +138,7 @@ static inline int xfs_repair_reset_counters(struct xfs_mount *mp)#define xfs_repair_superblock xfs_repair_notsupported#define xfs_repair_agf xfs_repair_notsupported#define xfs_repair_agfl xfs_repair_notsupported +#define xfs_repair_agi xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +1 -1
@@ -236,7 +236,7 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_PERAG, .setup = xfs_scrub_setup_fs, .scrub = xfs_scrub_agi, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_agi, }, [XFS_SCRUB_TYPE_BNOBT] = { /* bnobt */ .type = ST_PERAG,
PATCH 11/22 xfs: repair free space btrees
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Rebuild the free space btrees from the gaps in the rmap btree.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/alloc.c | 1 fs/xfs/scrub/alloc_repair.c | 438 +++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/common.c | 8 + fs/xfs/scrub/repair.h | 2 fs/xfs/scrub/scrub.c | 4 6 files changed, 450 insertions(+), 4 deletions(-) create mode 100644 fs/xfs/scrub/alloc_repair.c
fs/xfs/Makefile +1 -0
@@ -175,6 +175,7 @@ xfs-$(CONFIG_XFS_QUOTA) += scrub/quota.o ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y) xfs-y += $(addprefix scrub/, \ agheader_repair.o \ + alloc_repair.o \ repair.o \ ) endif
fs/xfs/scrub/alloc.c +0 -1
@@ -29,7 +29,6 @@#include "xfs_log_format.h" #include "xfs_trans.h" #include "xfs_sb.h" -#include "xfs_alloc.h" #include "xfs_rmap.h" #include "xfs_alloc.h" #include "scrub/xfs_scrub.h"
fs/xfs/scrub/alloc_repair.c new file +438 -0
438 lines not on this page · raw message
fs/xfs/scrub/common.c +7 -1
@@ -620,8 +620,14 @@ xfs_scrub_setup_ag_btree(* expensive operation should be performed infrequently and only * as a last resort. Any caller that sets force_log should * document why they need to do so. +* +* Force everything in memory out to disk if we're repairing. +* This ensures we won't get tripped up by btree blocks sitting +* in memory waiting to have LSNs stamped in. The AGF/AGI repair +* routines use any available rmap data to try to find a btree +* root that also passes the read verifiers. */ -if (force_log) { +if (force_log || (sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR )) { error =xfs_scrub_checkpoint_log (mp);if (error)return error;
fs/xfs/scrub/repair.h +2 -0
@@ -107,6 +107,7 @@ int xfs_repair_superblock(struct xfs_scrub_context *sc);int xfs_repair_agf (struct xfs_scrub_context *sc);int xfs_repair_agfl (struct xfs_scrub_context *sc);int xfs_repair_agi (struct xfs_scrub_context *sc); +int xfs_repair_allocbt (struct xfs_scrub_context *sc);#else
@@ -139,6 +140,7 @@ static inline int xfs_repair_reset_counters(struct xfs_mount *mp)#define xfs_repair_agf xfs_repair_notsupported#define xfs_repair_agfl xfs_repair_notsupported#define xfs_repair_agi xfs_repair_notsupported +#define xfs_repair_allocbt xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +2 -2
@@ -242,13 +242,13 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_PERAG, .setup = xfs_scrub_setup_ag_allocbt, .scrub = xfs_scrub_bnobt, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_allocbt, }, [XFS_SCRUB_TYPE_CNTBT] = { /* cntbt */ .type = ST_PERAG, .setup = xfs_scrub_setup_ag_allocbt, .scrub = xfs_scrub_cntbt, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_allocbt, }, [XFS_SCRUB_TYPE_INOBT] = { /* inobt */ .type = ST_PERAG,
PATCH 12/22 xfs: repair inode btrees
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Use the rmapbt to find inode chunks, query the chunks to compute hole and free masks, and with that information rebuild the inobt and finobt.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/ialloc_repair.c | 468 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 fs/xfs/scrub/scrub.c | 4 4 files changed, 473 insertions(+), 2 deletions(-) create mode 100644 fs/xfs/scrub/ialloc_repair.c
fs/xfs/Makefile +1 -0
@@ -176,6 +176,7 @@ ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y) xfs-y += $(addprefix scrub/, \ agheader_repair.o \ alloc_repair.o \ + ialloc_repair.o \ repair.o \ ) endif
fs/xfs/scrub/ialloc_repair.c new file +468 -0
468 lines not on this page · raw message
fs/xfs/scrub/repair.h +2 -0
@@ -108,6 +108,7 @@ int xfs_repair_agf(struct xfs_scrub_context *sc);int xfs_repair_agfl (struct xfs_scrub_context *sc);int xfs_repair_agi (struct xfs_scrub_context *sc);int xfs_repair_allocbt (struct xfs_scrub_context *sc); +int xfs_repair_iallocbt (struct xfs_scrub_context *sc);#else
@@ -141,6 +142,7 @@ static inline int xfs_repair_reset_counters(struct xfs_mount *mp)#define xfs_repair_agfl xfs_repair_notsupported#define xfs_repair_agi xfs_repair_notsupported#define xfs_repair_allocbt xfs_repair_notsupported +#define xfs_repair_iallocbt xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +2 -2
@@ -254,14 +254,14 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_PERAG, .setup = xfs_scrub_setup_ag_iallocbt, .scrub = xfs_scrub_inobt, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_iallocbt, }, [XFS_SCRUB_TYPE_FINOBT] = { /* finobt */ .type = ST_PERAG, .setup = xfs_scrub_setup_ag_iallocbt, .scrub = xfs_scrub_finobt, .has = xfs_sb_version_hasfinobt, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_iallocbt, }, [XFS_SCRUB_TYPE_RMAPBT] = { /* rmapbt */ .type = ST_PERAG,
PATCH 13/22 xfs: repair the rmapbt
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Rebuild the reverse mapping btree from all primary metadata.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/common.c | 6 fs/xfs/scrub/repair.c | 119 +++++++ fs/xfs/scrub/repair.h | 27 + fs/xfs/scrub/rmap.c | 6 fs/xfs/scrub/rmap_repair.c | 802 ++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 18 + fs/xfs/scrub/scrub.h | 2 fs/xfs/xfs_mount.h | 1 fs/xfs/xfs_super.c | 27 + fs/xfs/xfs_trans.c | 7 11 files changed, 1010 insertions(+), 6 deletions(-) create mode 100644 fs/xfs/scrub/rmap_repair.c
fs/xfs/Makefile +1 -0
@@ -178,6 +178,7 @@ xfs-y += $(addprefix scrub/, \ alloc_repair.o \ ialloc_repair.o \ repair.o \ + rmap_repair.o \ ) endif endif
fs/xfs/scrub/common.c +5 -1
@@ -586,9 +586,13 @@ xfs_scrub_trans_alloc( struct xfs_scrub_context *sc, uint resblks) { +uint flags =0 ; + +if (sc->fs_frozen) + flags |=XFS_TRANS_NO_WRITECOUNT ;if (sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR )return xfs_trans_alloc (sc->mp, &M_RES (sc->mp)->tr_itruncate, - resblks,0 ,0 , &sc->tp); + resblks,0 , flags, &sc->tp);return xfs_trans_alloc_empty (sc->mp, &sc->tp); }
fs/xfs/scrub/repair.c +119 -0
128 lines not on this page · raw message
fs/xfs/scrub/repair.h +27 -0
@@ -99,6 +99,11 @@ int xfs_repair_find_ag_btree_roots(struct xfs_scrub_context *sc,int xfs_repair_reset_counters (struct xfs_mount *mp);void xfs_repair_force_quotacheck (struct xfs_scrub_context *sc,uint dqtype);int xfs_repair_ino_dqattach (struct xfs_scrub_context *sc); +int xfs_repair_fs_freeze (struct xfs_scrub_context *sc); +int xfs_repair_fs_thaw (struct xfs_scrub_context *sc); +void xfs_repair_frozen_iput (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_repair_grab_all_ag_headers (struct xfs_scrub_context *sc); +int xfs_repair_rmapbt_setup (struct xfs_scrub_context *sc,struct xfs_inode *ip);/* Metadata repairers */
@@ -109,6 +114,7 @@ int xfs_repair_agfl(struct xfs_scrub_context *sc);int xfs_repair_agi (struct xfs_scrub_context *sc);int xfs_repair_allocbt (struct xfs_scrub_context *sc);int xfs_repair_iallocbt (struct xfs_scrub_context *sc); +int xfs_repair_rmapbt (struct xfs_scrub_context *sc);#else
@@ -136,6 +142,26 @@ static inline int xfs_repair_reset_counters(struct xfs_mount *mp)return -EIO ; } +static inline int xfs_repair_fs_freeze (struct xfs_scrub_context *sc) +{ +ASSERT (0 ); +return -EOPNOTSUPP ; +} + +static inline int xfs_repair_fs_thaw (struct xfs_scrub_context *sc) +{ +ASSERT (0 ); +return -EIO ; +} + +static inline int xfs_repair_rmapbt_setup ( +struct xfs_scrub_context *sc, +struct xfs_inode *ip) +{ +/* We don't support rmap repair, but we can still do a scan. */ +return xfs_scrub_setup_ag_btree (sc, ip, false); +} +#define xfs_repair_probe xfs_repair_notsupported#define xfs_repair_superblock xfs_repair_notsupported#define xfs_repair_agf xfs_repair_notsupported
@@ -143,6 +169,7 @@ static inline int xfs_repair_reset_counters(struct xfs_mount *mp)#define xfs_repair_agi xfs_repair_notsupported#define xfs_repair_allocbt xfs_repair_notsupported#define xfs_repair_iallocbt xfs_repair_notsupported +#define xfs_repair_rmapbt xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/rmap.c +5 -1
@@ -38,6 +38,7 @@#include "scrub/common.h" #include "scrub/btree.h" #include "scrub/trace.h" +#include "scrub/repair.h" /* * Set us up to scrub reverse mapping btrees.
@@ -47,7 +48,10 @@ xfs_scrub_setup_ag_rmapbt( struct xfs_scrub_context *sc, struct xfs_inode *ip) { - return xfs_scrub_setup_ag_btree(sc, ip, false); +if (sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR ) +return xfs_repair_rmapbt_setup (sc, ip); +else +return xfs_scrub_setup_ag_btree (sc, ip, false); }/* Reverse-mapping scrubber. */
fs/xfs/scrub/rmap_repair.c new file +802 -0
802 lines not on this page · raw message
fs/xfs/scrub/scrub.c +15 -3
@@ -178,6 +178,8 @@ xfs_scrub_teardown(struct xfs_inode *ip_in, int error) { + int err2; +xfs_scrub_ag_free (sc, &sc->sa);if (sc->tp) {if (error ==0 && (sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR ))
@@ -194,6 +196,12 @@ xfs_scrub_teardown(iput (VFS_I (sc->ip)); sc->ip =NULL ; } +if (sc->fs_frozen) { + err2 =xfs_repair_fs_thaw (sc); +if (!error && err2) + error = err2; + sc->fs_frozen = false; + }if (sc->has_quotaofflock)mutex_unlock (&sc->mp->m_quotainfo->qi_quotaofflock);if (sc->buf) {
@@ -268,7 +276,7 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_ag_rmapbt, .scrub = xfs_scrub_rmapbt, .has = xfs_sb_version_hasrmapbt, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_rmapbt, }, [XFS_SCRUB_TYPE_REFCNTBT] = { /* refcountbt */ .type = ST_PERAG,
@@ -514,6 +522,8 @@ xfs_scrub_metadata(xfs_scrub_experimental_warning (mp); +atomic_inc (&mp->m_scrubbers); + retry_op:/* Set up for the operation. */ memset (&sc,0 ,sizeof (sc));
@@ -536,7 +546,7 @@ xfs_scrub_metadata(*/ error =xfs_scrub_teardown (&sc, ip,0 );if (error) - goto out; + goto out_dec; try_harder = true; goto retry_op; }else if (error)
@@ -572,7 +582,7 @@ xfs_scrub_metadata( error =xfs_scrub_teardown (&sc, ip,0 );if (error) {xfs_repair_failure (mp); - goto out; + goto out_dec; } goto retry_op; }
@@ -582,6 +592,8 @@ xfs_scrub_metadata(xfs_scrub_postmortem (&sc); out_teardown: error =xfs_scrub_teardown (&sc, ip, error); +out_dec: +atomic_dec (&mp->m_scrubbers); out:trace_xfs_scrub_done (ip, sm, error);if (error == -EFSCORRUPTED || error == -EFSBADCRC ) {
fs/xfs/scrub/scrub.h +2 -0
@@ -68,6 +68,7 @@ struct xfs_scrub_ag {struct xfs_scrub_context {/* General scrub state. */ +struct inode *frozen_inode_list;struct xfs_mount *mp;struct xfs_scrub_metadata *sm;const struct xfs_scrub_meta_ops *ops;
@@ -78,6 +79,7 @@ struct xfs_scrub_context {bool try_harder;bool has_quotaofflock;bool reset_counters; +bool fs_frozen;/* State tracking for single-AG operations. */ struct xfs_scrub_ag sa;
fs/xfs/xfs_mount.h +1 -0
@@ -205,6 +205,7 @@ typedef struct xfs_mount {unsigned int *m_errortag;struct xfs_kobj m_errortag_kobj;#endif +atomic_t m_scrubbers;/* # of active scrub processes */ } xfs_mount_t;/*
fs/xfs/xfs_super.c +27 -0
@@ -1457,6 +1457,30 @@ xfs_fs_unfreeze(return 0 ; } +/* Don't let userspace freeze while we're scrubbing the filesystem. */ +STATIC int +xfs_fs_freeze_super ( +struct super_block *sb) +{ +struct xfs_mount *mp =XFS_M (sb); + +if (atomic_read (&mp->m_scrubbers) >0 ) +return -EBUSY ; +return freeze_super (sb); +} + +/* Don't let userspace thaw while we're scrubbing the filesystem. */ +STATIC int +xfs_fs_thaw_super ( +struct super_block *sb) +{ +struct xfs_mount *mp =XFS_M (sb); + +if (atomic_read (&mp->m_scrubbers) >0 ) +return -EBUSY ; +return thaw_super (sb); +} + STATIC int xfs_fs_show_options( struct seq_file *m,
@@ -1595,6 +1619,7 @@ xfs_mount_alloc(spin_lock_init (&mp->m_perag_lock);mutex_init (&mp->m_growlock);atomic_set (&mp->m_active_trans,0 ); +atomic_set (&mp->m_scrubbers,0 );INIT_DELAYED_WORK (&mp->m_reclaim_work, xfs_reclaim_worker);INIT_DELAYED_WORK (&mp->m_eofblocks_work, xfs_eofblocks_worker);INIT_DELAYED_WORK (&mp->m_cowblocks_work, xfs_cowblocks_worker);
@@ -1852,6 +1877,8 @@ static const struct super_operations xfs_super_operations = { .show_options = xfs_fs_show_options, .nr_cached_objects = xfs_fs_nr_cached_objects, .free_cached_objects = xfs_fs_free_cached_objects, + .freeze_super = xfs_fs_freeze_super, + .thaw_super = xfs_fs_thaw_super, };static struct file_system_type xfs_fs_type = {
fs/xfs/xfs_trans.c +6 -1
@@ -270,7 +270,12 @@ xfs_trans_alloc(if (!(flags &XFS_TRANS_NO_WRITECOUNT ))sb_start_intwrite (mp->m_super); -WARN_ON (mp->m_super->s_writers.frozen ==SB_FREEZE_COMPLETE ); +/* +* Scrub is allowed to freeze the filesystem in order to obtain +* exclusive access to the filesystem. +*/ +WARN_ON (atomic_read (&mp->m_scrubbers) ==0 && + mp->m_super->s_writers.frozen ==SB_FREEZE_COMPLETE );atomic_inc (&mp->m_active_trans); tp = kmem_zone_zalloc(xfs_trans_zone,
PATCH 14/22 xfs: repair refcount btrees
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Reconstruct the refcount data from the rmap btree.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/refcount_repair.c | 529 ++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 fs/xfs/scrub/scrub.c | 2 4 files changed, 533 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/refcount_repair.c
fs/xfs/Makefile +1 -0
@@ -177,6 +177,7 @@ xfs-y += $(addprefix scrub/, \ agheader_repair.o \ alloc_repair.o \ ialloc_repair.o \ + refcount_repair.o \ repair.o \ rmap_repair.o \ )
fs/xfs/scrub/refcount_repair.c new file +529 -0
529 lines not on this page · raw message
fs/xfs/scrub/repair.h +2 -0
@@ -115,6 +115,7 @@ int xfs_repair_agi(struct xfs_scrub_context *sc);int xfs_repair_allocbt (struct xfs_scrub_context *sc);int xfs_repair_iallocbt (struct xfs_scrub_context *sc);int xfs_repair_rmapbt (struct xfs_scrub_context *sc); +int xfs_repair_refcountbt (struct xfs_scrub_context *sc);#else
@@ -170,6 +171,7 @@ static inline int xfs_repair_rmapbt_setup(#define xfs_repair_allocbt xfs_repair_notsupported#define xfs_repair_iallocbt xfs_repair_notsupported#define xfs_repair_rmapbt xfs_repair_notsupported +#define xfs_repair_refcountbt xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +1 -1
@@ -283,7 +283,7 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_ag_refcountbt, .scrub = xfs_scrub_refcountbt, .has = xfs_sb_version_hasreflink, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_refcountbt, }, [XFS_SCRUB_TYPE_INODE] = { /* inode record */ .type = ST_INODE,
PATCH 15/22 xfs: repair inode records
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Try to reinitialize corrupt inodes, or clear the reflink flag if it's not needed.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/inode_repair.c | 392 +++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 fs/xfs/scrub/scrub.c | 2 4 files changed, 396 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/inode_repair.c
fs/xfs/Makefile +1 -0
@@ -177,6 +177,7 @@ xfs-y += $(addprefix scrub/, \ agheader_repair.o \ alloc_repair.o \ ialloc_repair.o \ + inode_repair.o \ refcount_repair.o \ repair.o \ rmap_repair.o \
fs/xfs/scrub/inode_repair.c new file +392 -0
392 lines not on this page · raw message
fs/xfs/scrub/repair.h +2 -0
@@ -116,6 +116,7 @@ int xfs_repair_allocbt(struct xfs_scrub_context *sc);int xfs_repair_iallocbt (struct xfs_scrub_context *sc);int xfs_repair_rmapbt (struct xfs_scrub_context *sc);int xfs_repair_refcountbt (struct xfs_scrub_context *sc); +int xfs_repair_inode (struct xfs_scrub_context *sc);#else
@@ -172,6 +173,7 @@ static inline int xfs_repair_rmapbt_setup(#define xfs_repair_iallocbt xfs_repair_notsupported#define xfs_repair_rmapbt xfs_repair_notsupported#define xfs_repair_refcountbt xfs_repair_notsupported +#define xfs_repair_inode xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +1 -1
@@ -289,7 +289,7 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_INODE, .setup = xfs_scrub_setup_inode, .scrub = xfs_scrub_inode, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_inode, }, [XFS_SCRUB_TYPE_BMBTD] = { /* inode data fork */ .type = ST_INODE,
PATCH 16/22 xfs: zap broken inode forks
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Determine if inode fork damage is responsible for the inode being unable to pass the ifork verifiers in xfs_iget and zap the fork contents if this is true. Once this is done the fork will be empty but we'll be able to construct an in-core inode, and a subsequent call to the inode fork repair ioctl will search the rmapbt to rebuild the records that were in the fork.
--- fs/xfs/libxfs/xfs_attr_leaf.c | 32 ++- fs/xfs/libxfs/xfs_attr_leaf.h | 2 fs/xfs/libxfs/xfs_bmap.c | 21 ++ fs/xfs/libxfs/xfs_bmap.h | 2 fs/xfs/scrub/inode_repair.c | 393 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 433 insertions(+), 17 deletions(-)
fs/xfs/libxfs/xfs_attr_leaf.c +21 -11
@@ -889,23 +889,16 @@ xfs_attr_shortform_allfit(return xfs_attr_shortform_bytesfit (dp, bytes); } -/* Verify the consistency of an inline attribute fork. */ +/* Verify the consistency of a raw inline attribute fork. */ xfs_failaddr_t -xfs_attr_shortform_verify ( -struct xfs_inode *ip) +xfs_attr_shortform_verify_struct ( +struct xfs_attr_shortform *sfp, +size_t size) { -struct xfs_attr_shortform *sfp;struct xfs_attr_sf_entry *sfep;struct xfs_attr_sf_entry *next_sfep;char *endp; -struct xfs_ifork *ifp;int i; -int size; - -ASSERT (ip->i_d.di_aformat ==XFS_DINODE_FMT_LOCAL ); - ifp =XFS_IFORK_PTR (ip,XFS_ATTR_FORK ); - sfp = (struct xfs_attr_shortform *)ifp->if_u1.if_data; - size = ifp->if_bytes;/* * Give up if the attribute is way too short.
@@ -963,6 +956,23 @@ xfs_attr_shortform_verify(return NULL ; } +/* Verify the consistency of an inline attribute fork. */ +xfs_failaddr_t +xfs_attr_shortform_verify ( +struct xfs_inode *ip) +{ +struct xfs_attr_shortform *sfp; +struct xfs_ifork *ifp; +int size; + +ASSERT (ip->i_d.di_aformat ==XFS_DINODE_FMT_LOCAL ); + ifp =XFS_IFORK_PTR (ip,XFS_ATTR_FORK ); + sfp = (struct xfs_attr_shortform *)ifp->if_u1.if_data; + size = ifp->if_bytes; + +return xfs_attr_shortform_verify_struct (sfp, size); +} +/* * Convert a leaf attribute list to shortform attribute list */
fs/xfs/libxfs/xfs_attr_leaf.h +2 -0
@@ -53,6 +53,8 @@ int xfs_attr_shortform_to_leaf(struct xfs_da_args *args,int xfs_attr_shortform_remove (struct xfs_da_args *args);int xfs_attr_shortform_allfit (struct xfs_buf *bp,struct xfs_inode *dp);int xfs_attr_shortform_bytesfit (struct xfs_inode *dp,int bytes); +xfs_failaddr_t xfs_attr_shortform_verify_struct (struct xfs_attr_shortform *sfp, +size_t size);xfs_failaddr_t xfs_attr_shortform_verify (struct xfs_inode *ip);void xfs_attr_fork_remove (struct xfs_inode *ip,struct xfs_trans *tp);
fs/xfs/libxfs/xfs_bmap.c +15 -6
@@ -6187,18 +6187,16 @@ xfs_bmap_finish_one(return error; } -/* Check that an inode's extent does not have invalid flags or bad ranges. */ +/* Check that an extent does not have invalid flags or bad ranges. */ xfs_failaddr_t -xfs_bmap_validate_extent ( -struct xfs_inode *ip, +xfs_bmbt_validate_extent ( +struct xfs_mount *mp, +bool isrt,int whichfork,struct xfs_bmbt_irec *irec) { -struct xfs_mount *mp = ip->i_mount;xfs_fsblock_t endfsb; -bool isrt; - isrt =XFS_IS_REALTIME_INODE (ip); endfsb = irec->br_startblock + irec->br_blockcount -1 ;if (isrt) {if (!xfs_verify_rtbno (mp, irec->br_startblock))
@@ -6222,3 +6220,14 @@ xfs_bmap_validate_extent( }return NULL ; } + +/* Check that an inode's extent does not have invalid flags or bad ranges. */ +xfs_failaddr_t +xfs_bmap_validate_extent ( +struct xfs_inode *ip, +int whichfork, +struct xfs_bmbt_irec *irec) +{ +return xfs_bmbt_validate_extent (ip->i_mount,XFS_IS_REALTIME_INODE (ip), + whichfork, irec); +}
fs/xfs/libxfs/xfs_bmap.h +2 -0
@@ -294,6 +294,8 @@ static inline int xfs_bmap_fork_to_state(int whichfork) } } +xfs_failaddr_t xfs_bmbt_validate_extent (struct xfs_mount *mp,bool isrt, +int whichfork,struct xfs_bmbt_irec *irec);xfs_failaddr_t xfs_bmap_validate_extent (struct xfs_inode *ip,int whichfork,struct xfs_bmbt_irec *irec);
fs/xfs/scrub/inode_repair.c +393 -0
433 lines not on this page · raw message
PATCH 17/22 xfs: repair inode block maps
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Use the reverse-mapping btree information to rebuild an inode fork.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/bmap.c | 8 + fs/xfs/scrub/bmap_repair.c | 399 ++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 4 fs/xfs/scrub/scrub.c | 4 fs/xfs/xfs_trans.c | 54 ++++++ fs/xfs/xfs_trans.h | 2 7 files changed, 470 insertions(+), 2 deletions(-) create mode 100644 fs/xfs/scrub/bmap_repair.c
fs/xfs/Makefile +1 -0
@@ -176,6 +176,7 @@ ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y) xfs-y += $(addprefix scrub/, \ agheader_repair.o \ alloc_repair.o \ + bmap_repair.o \ ialloc_repair.o \ inode_repair.o \ refcount_repair.o \
fs/xfs/scrub/bmap.c +8 -0
@@ -71,6 +71,14 @@ xfs_scrub_setup_inode_bmap( error =filemap_write_and_wait (VFS_I (sc->ip)->i_mapping);if (error) goto out; + +/* Drop the page cache if we're repairing block mappings. */ +if (sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR ) { + error =invalidate_inode_pages2 ( +VFS_I (sc->ip)->i_mapping); +if (error) + goto out; + } }/* Got the inode, lock it and we're ready to go. */
fs/xfs/scrub/bmap_repair.c new file +399 -0
399 lines not on this page · raw message
fs/xfs/scrub/repair.h +4 -0
@@ -117,6 +117,8 @@ int xfs_repair_iallocbt(struct xfs_scrub_context *sc);int xfs_repair_rmapbt (struct xfs_scrub_context *sc);int xfs_repair_refcountbt (struct xfs_scrub_context *sc);int xfs_repair_inode (struct xfs_scrub_context *sc); +int xfs_repair_bmap_data (struct xfs_scrub_context *sc); +int xfs_repair_bmap_attr (struct xfs_scrub_context *sc);#else
@@ -174,6 +176,8 @@ static inline int xfs_repair_rmapbt_setup(#define xfs_repair_rmapbt xfs_repair_notsupported#define xfs_repair_refcountbt xfs_repair_notsupported#define xfs_repair_inode xfs_repair_notsupported +#define xfs_repair_bmap_data xfs_repair_notsupported +#define xfs_repair_bmap_attr xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +2 -2
@@ -295,13 +295,13 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_INODE, .setup = xfs_scrub_setup_inode_bmap, .scrub = xfs_scrub_bmap_data, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_bmap_data, }, [XFS_SCRUB_TYPE_BMBTA] = { /* inode attr fork */ .type = ST_INODE, .setup = xfs_scrub_setup_inode_bmap, .scrub = xfs_scrub_bmap_attr, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_bmap_attr, }, [XFS_SCRUB_TYPE_BMBTC] = { /* inode CoW fork */ .type = ST_INODE,
fs/xfs/xfs_trans.c +54 -0
60 lines not on this page · raw message
fs/xfs/xfs_trans.h +2 -0
@@ -165,6 +165,8 @@ typedef struct xfs_trans {int xfs_trans_alloc (struct xfs_mount *mp,struct xfs_trans_res *resp,uint blocks,uint rtextents,uint flags,struct xfs_trans **tpp); +int xfs_trans_reserve_more (struct xfs_trans *tp,uint blocks, +uint rtextents);int xfs_trans_alloc_empty (struct xfs_mount *mp,struct xfs_trans **tpp);void xfs_trans_mod_sb (xfs_trans_t *,uint ,int64_t );
PATCH 18/22 xfs: repair damaged symlinks
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Repair inconsistent symbolic link data.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/repair.h | 2 fs/xfs/scrub/scrub.c | 2 fs/xfs/scrub/symlink.c | 2 fs/xfs/scrub/symlink_repair.c | 284 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 fs/xfs/scrub/symlink_repair.c
fs/xfs/Makefile +1 -0
@@ -182,6 +182,7 @@ xfs-y += $(addprefix scrub/, \ refcount_repair.o \ repair.o \ rmap_repair.o \ + symlink_repair.o \ ) endif endif
fs/xfs/scrub/repair.h +2 -0
@@ -119,6 +119,7 @@ int xfs_repair_refcountbt(struct xfs_scrub_context *sc);int xfs_repair_inode (struct xfs_scrub_context *sc);int xfs_repair_bmap_data (struct xfs_scrub_context *sc);int xfs_repair_bmap_attr (struct xfs_scrub_context *sc); +int xfs_repair_symlink (struct xfs_scrub_context *sc);#else
@@ -178,6 +179,7 @@ static inline int xfs_repair_rmapbt_setup(#define xfs_repair_inode xfs_repair_notsupported#define xfs_repair_bmap_data xfs_repair_notsupported#define xfs_repair_bmap_attr xfs_repair_notsupported +#define xfs_repair_symlink xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +1 -1
@@ -325,7 +325,7 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_INODE, .setup = xfs_scrub_setup_symlink, .scrub = xfs_scrub_symlink, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_symlink, }, [XFS_SCRUB_TYPE_PARENT] = { /* parent pointers */ .type = ST_INODE,
fs/xfs/scrub/symlink.c +1 -1
@@ -48,7 +48,7 @@ xfs_scrub_setup_symlink(if (!sc->buf)return -ENOMEM ; -return xfs_scrub_setup_inode_contents (sc, ip,0 ); +return xfs_scrub_setup_inode_contents (sc, ip,XFS_SYMLINK_MAPS ); }/* Symbolic links. */
fs/xfs/scrub/symlink_repair.c new file +284 -0
284 lines not on this page · raw message
PATCH 19/22 xfs: repair extended attributes
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
If the extended attributes look bad, try to sift through the rubble to find whatever keys/values we can, zap the attr tree, and re-add the values.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/attr.c | 2 fs/xfs/scrub/attr_repair.c | 519 ++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 2 fs/xfs/scrub/scrub.c | 2 fs/xfs/scrub/scrub.h | 3 6 files changed, 527 insertions(+), 2 deletions(-) create mode 100644 fs/xfs/scrub/attr_repair.c
fs/xfs/Makefile +1 -0
@@ -175,6 +175,7 @@ xfs-$(CONFIG_XFS_QUOTA) += scrub/quota.o ifeq ($(CONFIG_XFS_ONLINE_REPAIR),y) xfs-y += $(addprefix scrub/, \ agheader_repair.o \ + attr_repair.o \ alloc_repair.o \ bmap_repair.o \ ialloc_repair.o \
fs/xfs/scrub/attr.c +1 -1
@@ -139,7 +139,7 @@ xfs_scrub_xattr_listent(* Within a char, the lowest bit of the char represents the byte with * the smallest address */ -STATIC bool +bool xfs_scrub_xattr_set_map( struct xfs_scrub_context *sc, unsigned long *map,
fs/xfs/scrub/attr_repair.c new file +519 -0
519 lines not on this page · raw message
fs/xfs/scrub/repair.h +2 -0
@@ -120,6 +120,7 @@ int xfs_repair_inode(struct xfs_scrub_context *sc);int xfs_repair_bmap_data (struct xfs_scrub_context *sc);int xfs_repair_bmap_attr (struct xfs_scrub_context *sc);int xfs_repair_symlink (struct xfs_scrub_context *sc); +int xfs_repair_xattr (struct xfs_scrub_context *sc);#else
@@ -180,6 +181,7 @@ static inline int xfs_repair_rmapbt_setup(#define xfs_repair_bmap_data xfs_repair_notsupported#define xfs_repair_bmap_attr xfs_repair_notsupported#define xfs_repair_symlink xfs_repair_notsupported +#define xfs_repair_xattr xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +1 -1
@@ -319,7 +319,7 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_INODE, .setup = xfs_scrub_setup_xattr, .scrub = xfs_scrub_xattr, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_xattr, }, [XFS_SCRUB_TYPE_SYMLINK] = { /* symbolic link */ .type = ST_INODE,
fs/xfs/scrub/scrub.h +3 -0
@@ -156,4 +156,7 @@ void xfs_scrub_xref_is_used_rt_space(struct xfs_scrub_context *sc,# define xfs_scrub_xref_is_used_rt_space (sc, rtbno, len) do { } while (0)#endif +bool xfs_scrub_xattr_set_map (struct xfs_scrub_context *sc,unsigned long *map, +unsigned int start,unsigned int len); +#endif /* __XFS_SCRUB_SCRUB_H__ */
PATCH 20/22 xfs: scrub should set preen if attr leaf has holes
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
If an attr block indicates that it could use compaction, set the preen flag to have the attr fork rebuilt, since the attr fork rebuilder can take care of that for us.
--- fs/xfs/scrub/attr.c | 2 ++ fs/xfs/scrub/dabtree.c | 15 +++++++++++++++ fs/xfs/scrub/dabtree.h | 1 + fs/xfs/scrub/trace.h | 1 + 4 files changed, 19 insertions(+)
fs/xfs/scrub/attr.c +2 -0
@@ -307,6 +307,8 @@ xfs_scrub_xattr_block(xfs_scrub_da_set_corrupt (ds, level);if (!xfs_scrub_xattr_set_map (ds->sc, usedmap,0 , hdrsize))xfs_scrub_da_set_corrupt (ds, level); +if (leafhdr.holes) +xfs_scrub_da_set_preen (ds, level);if (ds->sc->sm->sm_flags &XFS_SCRUB_OFLAG_CORRUPT ) goto out;
fs/xfs/scrub/dabtree.c +15 -0
@@ -99,6 +99,21 @@ xfs_scrub_da_set_corrupt( __return_address); } +/* Flag a da btree node in need of optimization. */ +void +xfs_scrub_da_set_preen ( +struct xfs_scrub_da_btree *ds, +int level) +{ +struct xfs_scrub_context *sc = ds->sc; + + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_PREEN ; +trace_xfs_scrub_fblock_preen (sc, ds->dargs.whichfork, +xfs_dir2_da_to_db (ds->dargs.geo, + ds->state->path.blk[level].blkno), + __return_address); +} +/* Find an entry at a certain level in a da btree. */ STATIC void * xfs_scrub_da_btree_entry(
fs/xfs/scrub/dabtree.h +1 -0
@@ -50,6 +50,7 @@ bool xfs_scrub_da_process_error(struct xfs_scrub_da_btree *ds, int level, int *e/* Check for da btree corruption. */ void xfs_scrub_da_set_corrupt (struct xfs_scrub_da_btree *ds,int level); +void xfs_scrub_da_set_preen (struct xfs_scrub_da_btree *ds,int level);int xfs_scrub_da_btree_hash (struct xfs_scrub_da_btree *ds,int level,__be32 *hashp);
fs/xfs/scrub/trace.h +1 -0
@@ -244,6 +244,7 @@ DEFINE_EVENT(xfs_scrub_fblock_error_class, name, \DEFINE_SCRUB_FBLOCK_ERROR_EVENT (xfs_scrub_fblock_error);DEFINE_SCRUB_FBLOCK_ERROR_EVENT (xfs_scrub_fblock_warning); +DEFINE_SCRUB_FBLOCK_ERROR_EVENT (xfs_scrub_fblock_preen); TRACE_EVENT(xfs_scrub_incomplete, TP_PROTO(struct xfs_scrub_context *sc, void *ret_ip),
PATCH 21/22 xfs: repair quotas
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Fix anything that causes the quota verifiers to fail.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/attr_repair.c | 2 fs/xfs/scrub/common.h | 8 + fs/xfs/scrub/quota.c | 2 fs/xfs/scrub/quota_repair.c | 355 +++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.c | 58 +++++++ fs/xfs/scrub/repair.h | 8 + fs/xfs/scrub/scrub.c | 11 + fs/xfs/scrub/scrub.h | 1 9 files changed, 438 insertions(+), 8 deletions(-) create mode 100644 fs/xfs/scrub/quota_repair.c
fs/xfs/Makefile +1 -0
@@ -185,5 +185,6 @@ xfs-y += $(addprefix scrub/, \ rmap_repair.o \ symlink_repair.o \ ) +xfs-$(CONFIG_XFS_QUOTA) += scrub/quota_repair.o endif endif
fs/xfs/scrub/attr_repair.c +1 -1
@@ -360,7 +360,7 @@ xfs_repair_xattr_recover( }/* Free all the attribute fork blocks and delete the fork. */ -STATIC int +int xfs_repair_xattr_zap (struct xfs_scrub_context *sc) {
fs/xfs/scrub/common.h +8 -0
@@ -151,6 +151,14 @@ static inline bool xfs_scrub_skip_xref(struct xfs_scrub_metadata *sm) XFS_SCRUB_OFLAG_XCORRUPT); } +/* Do we need to invoke the repair tool? */ +static inline bool xfs_scrub_needs_repair (struct xfs_scrub_metadata *sm) +{ +return sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT | +XFS_SCRUB_OFLAG_XCORRUPT | +XFS_SCRUB_OFLAG_PREEN ); +} +int xfs_scrub_metadata_inode_forks (struct xfs_scrub_context *sc);int xfs_scrub_ilock_inverted (struct xfs_inode *ip,uint lock_mode);
fs/xfs/scrub/quota.c +1 -1
@@ -43,7 +43,7 @@#include "scrub/trace.h" /* Convert a scrub type code to a DQ flag, or return 0 if error. */ -static inline uint +uint xfs_scrub_quota_to_dqtype (struct xfs_scrub_context *sc) {
fs/xfs/scrub/quota_repair.c new file +355 -0
355 lines not on this page · raw message
fs/xfs/scrub/repair.c +58 -0
67 lines not on this page · raw message
fs/xfs/scrub/repair.h +8 -0
@@ -104,6 +104,8 @@ int xfs_repair_fs_thaw(struct xfs_scrub_context *sc);void xfs_repair_frozen_iput (struct xfs_scrub_context *sc,struct xfs_inode *ip);int xfs_repair_grab_all_ag_headers (struct xfs_scrub_context *sc);int xfs_repair_rmapbt_setup (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_repair_xattr_zap (struct xfs_scrub_context *sc); +int xfs_repair_metadata_inode_forks (struct xfs_scrub_context *sc);/* Metadata repairers */
@@ -121,6 +123,11 @@ int xfs_repair_bmap_data(struct xfs_scrub_context *sc);int xfs_repair_bmap_attr (struct xfs_scrub_context *sc);int xfs_repair_symlink (struct xfs_scrub_context *sc);int xfs_repair_xattr (struct xfs_scrub_context *sc); +#ifdef CONFIG_XFS_QUOTA +int xfs_repair_quota (struct xfs_scrub_context *sc); +#else +# define xfs_repair_quota xfs_repair_notsupported +#endif /* CONFIG_XFS_QUOTA */ #else
@@ -182,6 +189,7 @@ static inline int xfs_repair_rmapbt_setup(#define xfs_repair_bmap_attr xfs_repair_notsupported#define xfs_repair_symlink xfs_repair_notsupported#define xfs_repair_xattr xfs_repair_notsupported +#define xfs_repair_quota xfs_repair_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +5 -6
@@ -351,19 +351,19 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .type = ST_FS, .setup = xfs_scrub_setup_quota, .scrub = xfs_scrub_quota, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_quota, }, [XFS_SCRUB_TYPE_GQUOTA] = { /* group quota */ .type = ST_FS, .setup = xfs_scrub_setup_quota, .scrub = xfs_scrub_quota, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_quota, }, [XFS_SCRUB_TYPE_PQUOTA] = { /* project quota */ .type = ST_FS, .setup = xfs_scrub_setup_quota, .scrub = xfs_scrub_quota, - .repair = xfs_repair_notsupported, + .repair = xfs_repair_quota, }, };
@@ -559,9 +559,8 @@ xfs_scrub_metadata(if (XFS_TEST_ERROR (false, mp,XFS_ERRTAG_FORCE_SCRUB_REPAIR )) sc.sm->sm_flags |=XFS_SCRUB_OFLAG_CORRUPT ; - needs_fix = (sc.sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT | -XFS_SCRUB_OFLAG_XCORRUPT | -XFS_SCRUB_OFLAG_PREEN )); + needs_fix =xfs_scrub_needs_repair (sc.sm); +/* * If userspace asked for a repair but it wasn't necessary, * report that back to userspace.
fs/xfs/scrub/scrub.h +1 -0
@@ -158,5 +158,6 @@ void xfs_scrub_xref_is_used_rt_space(struct xfs_scrub_context *sc,bool xfs_scrub_xattr_set_map (struct xfs_scrub_context *sc,unsigned long *map,unsigned int start,unsigned int len); +uint xfs_scrub_quota_to_dqtype (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
PATCH 22/22 xfs: implement live quotacheck as part of quota repair
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Use the fs freezing mechanism we developed for the rmapbt repair to freeze the fs, this time to scan the fs for a live quotacheck. We add a new dqget variant to use the existing scrub transaction to allocate an on-disk dquot block if it is missing.
--- fs/xfs/scrub/quota.c | 20 +++ fs/xfs/scrub/quota_repair.c | 286 +++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_dquot.c | 59 ++++++++- fs/xfs/xfs_dquot.h | 3 4 files changed, 360 insertions(+), 8 deletions(-)
fs/xfs/scrub/quota.c +19 -1
@@ -41,6 +41,7 @@#include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" +#include "scrub/repair.h" /* Convert a scrub type code to a DQ flag, or return 0 if error. */ uint
@@ -78,12 +79,29 @@ xfs_scrub_setup_quota(mutex_lock (&sc->mp->m_quotainfo->qi_quotaofflock);if (!xfs_this_quota_on (sc->mp, dqtype))return -ENOENT ; +/* +* Freeze out anything that can alter an inode because we reconstruct +* the quota counts by iterating all the inodes in the system. +*/ +if ((sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR ) && + (sc->try_harder ||XFS_QM_NEED_QUOTACHECK (sc->mp))) { + error =xfs_repair_fs_freeze (sc); +if (error) +return error; + } error =xfs_scrub_setup_fs (sc, ip);if (error)return error; sc->ip =xfs_quota_inode (sc->mp, dqtype); -xfs_ilock (sc->ip,XFS_ILOCK_EXCL ); sc->ilock_flags =XFS_ILOCK_EXCL ; +/* +* Pretend to be an ILOCK parent to shut up lockdep if we're going to +* do a full inode scan of the fs. Quota inodes do not count towards +* quota accounting, so we shouldn't deadlock on ourselves. +*/ +if (sc->fs_frozen) + sc->ilock_flags |=XFS_ILOCK_PARENT ; +xfs_ilock (sc->ip, sc->ilock_flags);return 0 ; }
fs/xfs/scrub/quota_repair.c +285 -1
319 lines not on this page · raw message
fs/xfs/xfs_dquot.c +53 -6
108 lines not on this page · raw message
fs/xfs/xfs_dquot.h +3 -0
@@ -180,6 +180,9 @@ extern int xfs_qm_dqget_next(struct xfs_mount *mp, xfs_dqid_t id,extern int xfs_qm_dqget_uncached (struct xfs_mount *mp,xfs_dqid_t id,uint type,struct xfs_dquot **dqpp); +extern int xfs_qm_dqget_alloc (struct xfs_trans **tpp, +xfs_dqid_t id,uint type, +struct xfs_dquot **dqpp);extern void xfs_qm_dqput (xfs_dquot_t *);extern void xfs_dqlock2 (struct xfs_dquot *,struct xfs_dquot *);
PATCH 1/1 xfs: grab the per-ag structure whenever relevant
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Grab and hold the per-AG data across a scrub run whenever relevant. This helps us avoid repeated trips through rcu in the repair code.
--- fs/xfs/scrub/common.c | 17 +++++++++++++++++ fs/xfs/scrub/common.h | 1 + fs/xfs/scrub/scrub.h | 1 + 3 files changed, 19 insertions(+)
fs/xfs/scrub/common.c +17 -0
@@ -541,6 +541,10 @@ xfs_scrub_ag_free(xfs_trans_brelse (sc->tp, sa->agi_bp); sa->agi_bp =NULL ; } +if (sa->pag) { +xfs_perag_put (sa->pag); + sa->pag =NULL ; + } sa->agno =NULLAGNUMBER ; }
@@ -568,6 +572,19 @@ xfs_scrub_ag_init(return xfs_scrub_ag_btcur_init (sc, sa); } +/* +* Grab the per-ag structure if we haven't already gotten it. Teardown of the +* xfs_scrub_ag will release it for us. +*/ +void +xfs_scrub_perag_get ( +struct xfs_mount *mp, +struct xfs_scrub_ag *sa) +{ +if (!sa->pag) + sa->pag =xfs_perag_get (mp, sa->agno); +} +/* Per-scrubber setup functions */ /*
fs/xfs/scrub/common.h +1 -0
@@ -123,6 +123,7 @@ xfs_scrub_setup_quota(struct xfs_scrub_context *sc, struct xfs_inode *ip)void xfs_scrub_ag_free (struct xfs_scrub_context *sc,struct xfs_scrub_ag *sa);int xfs_scrub_ag_init (struct xfs_scrub_context *sc,xfs_agnumber_t agno,struct xfs_scrub_ag *sa); +void xfs_scrub_perag_get (struct xfs_mount *mp,struct xfs_scrub_ag *sa);int xfs_scrub_ag_read_headers (struct xfs_scrub_context *sc,xfs_agnumber_t agno,struct xfs_buf **agi,struct xfs_buf **agf,struct xfs_buf **agfl);
fs/xfs/scrub/scrub.h +1 -0
@@ -51,6 +51,7 @@ struct xfs_scrub_meta_ops {/* Buffer pointers and btree cursors for an entire AG. */ struct xfs_scrub_ag {xfs_agnumber_t agno; +struct xfs_perag *pag;/* AG btree roots */ struct xfs_buf *agf_bp;
Re: [PATCH 0.5/22] xfs: grab the per-ag structure whenever relevant
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Grab and hold the per-AG data across a scrub run whenever relevant. This helps us avoid repeated trips through rcu in the repair code.
rcu and the radix tree
Quoted 1 line
Signed-off-by Darrick J. Wong <[email protected]>
Code looks fine.
Signature · 2 lines
Dave Chinner [email protected]