xfs: online scrub support
v12 cover xfs: online scrub support
Hi all,
This is the twelfth revision of a patchset that adds to XFS kernel support for online metadata scrubbing and repair. There aren't any on-disk format changes. Changes since v11 include following all the review comments from Dave Chinner to add more helper functions and de-grossify the scrub code, and rebasing to 4.14-rc4. I have been performing daily online scrubs of my XFS filesystems for several months now, with surprisingly few problems.
Online scrub/repair support consists of four major pieces -- first, an ioctl that maps physical extents to their owners (GETFSMAP; already in 4.12); second, various in-kernel metadata scrubbing ioctls to examine metadata records and cross-reference them with other filesystem metadata; third, an in-kernel mechanism for rebuilding damaged metadata objects and btrees; and fourth, a userspace component to coordinate scrubbing and repair operations.
This new utility, xfs_scrub, is separate from the existing offline xfs_repair tool. The program uses various XFS ioctls to iterate all XFS metadata and asks the kernel to check the metadata and repair it if necessary.
While I understand that reviewer bandwidth is limited, I would like to get this series prepped for 4.15, if possible. I have isolated the scrub code such that it can be compiled out entirely, in the hopes that we can stabilize the code while not exposing regular users to riskier code.
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.14-rc4. 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 eat your data. 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 v12 1/30 xfs: return a distinct error code value for IGET_INCORE cache misses
From: Darrick J. Wong <[email protected]>
For an XFS_IGET_INCORE iget operation, if the inode isn't in the cache, return ENODATA so that we don't confuse it with the pre-existing ENOENT cases (inode is in cache, but freed).
--- fs/xfs/xfs_icache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_icache.c +1 -1
@@ -610,7 +610,7 @@ xfs_iget( }else {rcu_read_unlock ();if (flags &XFS_IGET_INCORE ) { - error = -ENOENT ; + error = -ENODATA ; goto out_error_or_again; }XFS_STATS_INC (mp, xs_ig_missed);
Re: [PATCH 01/30] xfs: return a distinct error code value for IGET_INCORE cache misses
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
For an
XFS_IGET_INCOREiget operation, if the inode isn't in the cache, return ENODATA so that we don't confuse it with the pre-existing ENOENT cases (inode is in cache, but freed).Signed-off-by Darrick J. Wong <[email protected]>Reviewed-by Brian Foster <[email protected]>
Quoted 24 lines
--- fs/xfs/xfs_icache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)fs/xfs/xfs_icache.c +1
-2@@ -610,7 +610,7 @@ xfs_iget( }else {rcu_read_unlock ();if (flags &XFS_IGET_INCORE ) { - error = -ENOENT ; + error = -ENODATA ; goto out_error_or_again; }XFS_STATS_INC (mp, xs_ig_missed); --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
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 2/30 xfs: create block pointer check functions
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create some helper functions to check that a block pointer points within the filesystem (or AG) and doesn't point at static metadata. We will use this for scrub.
--- fs/xfs/libxfs/xfs_alloc.c | 49 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_alloc.h | 4 +++ fs/xfs/libxfs/xfs_rtbitmap.c | 12 ++++++++++ fs/xfs/xfs_rtalloc.h | 2 ++ 4 files changed, 67 insertions(+)
fs/xfs/libxfs/xfs_alloc.c +49 -0
@@ -2923,3 +2923,52 @@ xfs_alloc_query_all( query.fn = fn;return xfs_btree_query_all (cur, xfs_alloc_query_range_helper, &query); } + +/* Find the size of the AG, in blocks. */ +xfs_agblock_t +xfs_ag_block_count ( +struct xfs_mount *mp, +xfs_agnumber_t agno) +{ +ASSERT (agno < mp->m_sb.sb_agcount); + +if (agno < mp->m_sb.sb_agcount -1 ) +return mp->m_sb.sb_agblocks; +return mp->m_sb.sb_dblocks - (agno * mp->m_sb.sb_agblocks); +} + +/* +* Verify that an AG block number pointer neither points outside the AG +* nor points at static metadata. +*/ +bool +xfs_verify_agbno_ptr ( +struct xfs_mount *mp, +xfs_agnumber_t agno, +xfs_agblock_t agbno) +{ +xfs_agblock_t eoag; + + eoag =xfs_ag_block_count (mp, agno); +if (agbno >= eoag) +return false; +if (agbno <=XFS_AGFL_BLOCK (mp)) +return false; +return true; +} + +/* +* Verify that an FS block number pointer neither points outside the +* filesystem nor points at static AG metadata. +*/ +bool +xfs_verify_fsbno_ptr ( +struct xfs_mount *mp, +xfs_fsblock_t fsbno) +{ +xfs_agnumber_t agno =XFS_FSB_TO_AGNO (mp, fsbno); + +if (agno >= mp->m_sb.sb_agcount) +return false; +return xfs_verify_agbno_ptr (mp, agno,XFS_FSB_TO_AGBNO (mp, fsbno)); +}
fs/xfs/libxfs/xfs_alloc.h +4 -0
@@ -232,5 +232,9 @@ int xfs_alloc_query_range(struct xfs_btree_cur *cur, xfs_alloc_query_range_fn fn, void *priv);int xfs_alloc_query_all (struct xfs_btree_cur *cur,xfs_alloc_query_range_fn fn,void *priv); +xfs_agblock_t xfs_ag_block_count (struct xfs_mount *mp,xfs_agnumber_t agno); +bool xfs_verify_agbno_ptr (struct xfs_mount *mp,xfs_agnumber_t agno, +xfs_agblock_t agbno); +bool xfs_verify_fsbno_ptr (struct xfs_mount *mp,xfs_fsblock_t fsbno);#endif /* __XFS_ALLOC_H__ */
fs/xfs/libxfs/xfs_rtbitmap.c +12 -0
@@ -1086,3 +1086,15 @@ xfs_rtalloc_query_all(return xfs_rtalloc_query_range (tp, &keys[0 ], &keys[1 ], fn, priv); } + +/* +* Verify that an realtime block number pointer doesn't point off the +* end of the realtime device. +*/ +bool +xfs_verify_rtbno_ptr ( +struct xfs_mount *mp, +xfs_rtblock_t rtbno) +{ +return rtbno < mp->m_sb.sb_rblocks; +}
fs/xfs/xfs_rtalloc.h +2 -0
@@ -138,6 +138,7 @@ int xfs_rtalloc_query_range(struct xfs_trans *tp,int xfs_rtalloc_query_all (struct xfs_trans *tp,xfs_rtalloc_query_range_fn fn,void *priv); +bool xfs_verify_rtbno_ptr (struct xfs_mount *mp,xfs_rtblock_t rtbno);#else # define xfs_rtallocate_extent (t,b,min,max,l,f,p,rb) (ENOSYS)# define xfs_rtfree_extent (t,b,l) (ENOSYS)
@@ -146,6 +147,7 @@ int xfs_rtalloc_query_all(struct xfs_trans *tp,# define xfs_rtalloc_query_range (t,l,h,f,p) (ENOSYS)# define xfs_rtalloc_query_all (t,f,p) (ENOSYS)# define xfs_rtbuf_get (m,t,b,i,p) (ENOSYS) +# define xfs_verify_rtbno_ptr (m, r) (false)static inline int /* error */ xfs_rtmount_init (xfs_mount_t *mp)/* file system mount structure */
Re: [PATCH 02/30] xfs: create block pointer check functions
Quoted 7 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create some helper functions to check that a block pointer points within the filesystem (or AG) and doesn't point at static metadata. We will use this for scrub.
Signed-off-by Darrick J. Wong <[email protected]>
Look fine
Quoted 125 lines
--- fs/xfs/libxfs/xfs_alloc.c | 49 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_alloc.h | 4 +++ fs/xfs/libxfs/xfs_rtbitmap.c | 12 ++++++++++ fs/xfs/xfs_rtalloc.h | 2 ++ 4 files changed, 67 insertions(+)fs/xfs/libxfs/xfs_alloc.c +49
-0@@ -2923,3 +2923,52 @@ xfs_alloc_query_all( query.fn = fn;return xfs_btree_query_all (cur, xfs_alloc_query_range_helper, &query); } + +/* Find the size of the AG, in blocks. */ +xfs_agblock_t +xfs_ag_block_count ( +struct xfs_mount *mp, +xfs_agnumber_t agno) +{ +ASSERT (agno < mp->m_sb.sb_agcount); + +if (agno < mp->m_sb.sb_agcount -1 ) +return mp->m_sb.sb_agblocks; +return mp->m_sb.sb_dblocks - (agno * mp->m_sb.sb_agblocks); +} + +/* +* Verify that an AG block number pointer neither points outside the AG +* nor points at static metadata. +*/ +bool +xfs_verify_agbno_ptr ( +struct xfs_mount *mp, +xfs_agnumber_t agno, +xfs_agblock_t agbno) +{ +xfs_agblock_t eoag; + + eoag =xfs_ag_block_count (mp, agno); +if (agbno >= eoag) +return false; +if (agbno <=XFS_AGFL_BLOCK (mp)) +return false; +return true; +} + +/* +* Verify that an FS block number pointer neither points outside the +* filesystem nor points at static AG metadata. +*/ +bool +xfs_verify_fsbno_ptr ( +struct xfs_mount *mp, +xfs_fsblock_t fsbno) +{ +xfs_agnumber_t agno =XFS_FSB_TO_AGNO (mp, fsbno); + +if (agno >= mp->m_sb.sb_agcount) +return false; +return xfs_verify_agbno_ptr (mp, agno,XFS_FSB_TO_AGBNO (mp, fsbno)); +}fs/xfs/libxfs/xfs_alloc.h +4
-0@@ -232,5 +232,9 @@ int xfs_alloc_query_range(struct xfs_btree_cur *cur, xfs_alloc_query_range_fn fn, void *priv);int xfs_alloc_query_all (struct xfs_btree_cur *cur,xfs_alloc_query_range_fn fn,void *priv); +xfs_agblock_t xfs_ag_block_count (struct xfs_mount *mp,xfs_agnumber_t agno); +bool xfs_verify_agbno_ptr (struct xfs_mount *mp,xfs_agnumber_t agno, +xfs_agblock_t agbno); +bool xfs_verify_fsbno_ptr (struct xfs_mount *mp,xfs_fsblock_t fsbno);#endif /* __XFS_ALLOC_H__ */ fs/xfs/libxfs/xfs_rtbitmap.c +12
-0@@ -1086,3 +1086,15 @@ xfs_rtalloc_query_all(return xfs_rtalloc_query_range (tp, &keys[0 ], &keys[1 ], fn, priv); } + +/* +* Verify that an realtime block number pointer doesn't point off the +* end of the realtime device. +*/ +bool +xfs_verify_rtbno_ptr ( +struct xfs_mount *mp, +xfs_rtblock_t rtbno) +{ +return rtbno < mp->m_sb.sb_rblocks; +}fs/xfs/xfs_rtalloc.h +2
-1@@ -138,6 +138,7 @@ int xfs_rtalloc_query_range(struct xfs_trans *tp,int xfs_rtalloc_query_all (struct xfs_trans *tp,xfs_rtalloc_query_range_fn fn,void *priv); +bool xfs_verify_rtbno_ptr (struct xfs_mount *mp,xfs_rtblock_t rtbno);#else # define xfs_rtallocate_extent (t,b,min,max,l,f,p,rb) (ENOSYS)# define xfs_rtfree_extent (t,b,l) (ENOSYS)@@ -146,6 +147,7 @@ int xfs_rtalloc_query_all(struct xfs_trans *tp,# define xfs_rtalloc_query_range (t,l,h,f,p) (ENOSYS)# define xfs_rtalloc_query_all (t,f,p) (ENOSYS)# define xfs_rtbuf_get (m,t,b,i,p) (ENOSYS) +# define xfs_verify_rtbno_ptr (m, r) (false)static inline int /* error */ xfs_rtmount_init (xfs_mount_t *mp)/* file system mount structure */ --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
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 02/30] xfs: create block pointer check functions
Quoted 10 lines · Dave Chinner
Quoted 7 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create some helper functions to check that a block pointer points within the filesystem (or AG) and doesn't point at static metadata. We will use this for scrub.
Signed-off-by Darrick J. Wong <[email protected]>Look fine
now that I think about it and seen a bit more code....
Quoted 31 lines
Reviewed-by Dave Chinner <[email protected]>Quoted 28 lines
--- fs/xfs/libxfs/xfs_alloc.c | 49 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_alloc.h | 4 +++ fs/xfs/libxfs/xfs_rtbitmap.c | 12 ++++++++++ fs/xfs/xfs_rtalloc.h | 2 ++ 4 files changed, 67 insertions(+)fs/xfs/libxfs/xfs_alloc.c +12
-0@@ -2923,3 +2923,52 @@ xfs_alloc_query_all( query.fn = fn;return xfs_btree_query_all (cur, xfs_alloc_query_range_helper, &query); } + +/* Find the size of the AG, in blocks. */ +xfs_agblock_t +xfs_ag_block_count ( +struct xfs_mount *mp, +xfs_agnumber_t agno) +{ +ASSERT (agno < mp->m_sb.sb_agcount); + +if (agno < mp->m_sb.sb_agcount -1 ) +return mp->m_sb.sb_agblocks; +return mp->m_sb.sb_dblocks - (agno * mp->m_sb.sb_agblocks);
Quoted 8 lines
fs/xfs/libxfs/xfs_alloc.c xfs_alloc_query_all( · line 2945
Quoted 8 lines
fs/xfs/libxfs/xfs_alloc.c +8
-0+} + +/* +* Verify that an AG block number pointer neither points outside the AG +* nor points at static metadata. +*/ +bool +xfs_verify_agbno_ptr(
You can probably drop the "_ptr" prefix from these because I don't think we every try to check/validate the agbno/fsbno of the static metadata....
Some of the code just reads a bit weird with the "_ptr" suffix in it...
Still consider it reviewed....
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 02/30] xfs: create block pointer check functions
Quoted 58 lines · Dave Chinner
Quoted 10 lines · Dave Chinner
Quoted 7 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create some helper functions to check that a block pointer points within the filesystem (or AG) and doesn't point at static metadata. We will use this for scrub.
Signed-off-by Darrick J. Wong <[email protected]>Look fine
now that I think about it and seen a bit more code....
Quoted 31 lines
Reviewed-by Dave Chinner <[email protected]>Quoted 28 lines
--- fs/xfs/libxfs/xfs_alloc.c | 49 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_alloc.h | 4 +++ fs/xfs/libxfs/xfs_rtbitmap.c | 12 ++++++++++ fs/xfs/xfs_rtalloc.h | 2 ++ 4 files changed, 67 insertions(+)fs/xfs/libxfs/xfs_alloc.c +12
-0@@ -2923,3 +2923,52 @@ xfs_alloc_query_all( query.fn = fn;return xfs_btree_query_all (cur, xfs_alloc_query_range_helper, &query); } + +/* Find the size of the AG, in blocks. */ +xfs_agblock_t +xfs_ag_block_count ( +struct xfs_mount *mp, +xfs_agnumber_t agno) +{ +ASSERT (agno < mp->m_sb.sb_agcount); + +if (agno < mp->m_sb.sb_agcount -1 ) +return mp->m_sb.sb_agblocks; +return mp->m_sb.sb_dblocks - (agno * mp->m_sb.sb_agblocks);Quoted 8 lines
fs/xfs/libxfs/xfs_alloc.c xfs_alloc_query_all( · line 2945
Quoted 8 lines
fs/xfs/libxfs/xfs_alloc.c +8
-0+} + +/* +* Verify that an AG block number pointer neither points outside the AG +* nor points at static metadata. +*/ +bool +xfs_verify_agbno_ptr(You can probably drop the "
_ptr" prefix from these because I don't think we every try to check/validate the agbno/fsbno of the static metadata....
Quoted 2 lines
Some of the code just reads a bit weird with the "
_ptr" suffix in it...
I wrangled with the name for a while too -- a generic block number could refer to any part of the AG, whereas a block number in a metadata structure is a pointer and should never point to static metadata, hence the _ptr suffix. On the other hand, some of the block pointers can be NULL{FS,AG}BLOCK and others can't, and we don't check that here so it's not quite a pointer check either.
Meh.
xfs_verify_agbno() it is. Anyone reading the comments will figure out why xfs_verify_agbno(mp, agno, XFS_AGFL_BLOCK(mp)) == false.
Quoted 1 line
Still consider it reviewed....
Ok.
--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 v12 3/30 xfs: refactor btree pointer checks
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Refactor the btree pointer checks so that we can call them from the scrub code without logging errors to dmesg. Preserve the existing error reporting for regular operations.
--- fs/xfs/libxfs/xfs_bmap.c | 4 +-- fs/xfs/libxfs/xfs_btree.c | 70 +++++++++++++++++++++------------------------ fs/xfs/libxfs/xfs_btree.h | 13 +++++++- 3 files changed, 45 insertions(+), 42 deletions(-)
fs/xfs/libxfs/xfs_bmap.c +2 -2
@@ -657,8 +657,8 @@ xfs_bmap_btree_to_extents( cbno =be64_to_cpu (*pp); *logflagsp =0 ;#ifdef DEBUG -if ((error =xfs_btree_check_lptr (cur, cbno,1 ))) -return error; +XFS_WANT_CORRUPTED_RETURN (cur->bc_mp, +xfs_btree_check_lptr (cur, cbno,1 ));#endif error =xfs_btree_read_bufl (mp, tp, cbno,0 , &cbp,XFS_BMAP_BTREE_REF , &xfs_bmbt_buf_ops);
fs/xfs/libxfs/xfs_btree.c +32 -38
91 lines not on this page · raw message
fs/xfs/libxfs/xfs_btree.h +11 -2
@@ -269,10 +269,19 @@ xfs_btree_check_block(/* * Check that (long) pointer is ok. */ -int /* error (0 or EFSCORRUPTED) */ +bool /* error (0 or EFSCORRUPTED) */ xfs_btree_check_lptr (struct xfs_btree_cur *cur,/* btree cursor */ -xfs_fsblock_t ptr,/* btree block disk address */ +xfs_fsblock_t fsbno,/* btree block disk address */ +int level);/* btree block level */ + +/* +* Check that (short) pointer is ok. +*/ +bool /* error (0 or EFSCORRUPTED) */ +xfs_btree_check_sptr ( +struct xfs_btree_cur *cur,/* btree cursor */ +xfs_agblock_t agbno,/* btree block disk address */ int level);/* btree block level */ /*
Re: [PATCH 03/30] xfs: refactor btree pointer checks
Quoted 6 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Refactor the btree pointer checks so that we can call them from the scrub code without logging errors to dmesg. Preserve the existing error reporting for regular operations.
Quoted 6 lines
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/libxfs/xfs_bmap.c | 4 +-- fs/xfs/libxfs/xfs_btree.c | 70 +++++++++++++++++++++------------------------ fs/xfs/libxfs/xfs_btree.h | 13 +++++++- 3 files changed, 45 insertions(+), 42 deletions(-)
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 4/30 xfs: refactor btree block header checking functions
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Refactor the btree block header checks to have an internal function that returns the address of the failing check without logging errors. The scrubber will call the internal function, while the external version will maintain the current logging behavior.
--- fs/xfs/libxfs/xfs_btree.c | 166 +++++++++++++++++++++++++++------------------ fs/xfs/libxfs/xfs_btree.h | 5 + fs/xfs/xfs_linux.h | 7 ++ 3 files changed, 110 insertions(+), 68 deletions(-)
fs/xfs/libxfs/xfs_btree.c +98 -68
@@ -63,44 +63,61 @@ xfs_btree_magic(return magic; } -STATIC int /* error (0 or EFSCORRUPTED) */ -xfs_btree_check_lblock ( -struct xfs_btree_cur *cur,/* btree cursor */ -struct xfs_btree_block *block,/* btree long form block pointer */ -int level,/* level of the btree block */ -struct xfs_buf *bp)/* buffer for block, if any */ +/* +* Check a long btree block header. Return the address of the failing check, +* or NULL if everything is ok. +*/ +void * +__xfs_btree_check_lblock ( +struct xfs_btree_cur *cur, +struct xfs_btree_block *block, +int level, +struct xfs_buf *bp) { -int lblock_ok =1 ;/* block passes checks */ -struct xfs_mount *mp;/* file system mount point */ +struct xfs_mount *mp = cur->bc_mp;xfs_btnum_t btnum = cur->bc_btnum; -int crc; - - mp = cur->bc_mp; - crc =xfs_sb_version_hascrc (&mp->m_sb); +int crc =xfs_sb_version_hascrc (&mp->m_sb);if (crc) { - lblock_ok = lblock_ok && -uuid_equal (&block->bb_u.l.bb_uuid, - &mp->m_sb.sb_meta_uuid) && - block->bb_u.l.bb_blkno ==cpu_to_be64 ( - bp ? bp->b_bn :XFS_BUF_DADDR_NULL ); +if (!uuid_equal (&block->bb_u.l.bb_uuid, &mp->m_sb.sb_meta_uuid)) +return __this_address; +if (block->bb_u.l.bb_blkno != +cpu_to_be64 (bp ? bp->b_bn :XFS_BUF_DADDR_NULL )) +return __this_address; } - lblock_ok = lblock_ok && -be32_to_cpu (block->bb_magic) ==xfs_btree_magic (crc, btnum) && -be16_to_cpu (block->bb_level) == level && -be16_to_cpu (block->bb_numrecs) <= - cur->bc_ops->get_maxrecs (cur, level) && - block->bb_u.l.bb_leftsib && - (block->bb_u.l.bb_leftsib ==cpu_to_be64 (NULLFSBLOCK ) || -XFS_FSB_SANITY_CHECK (mp, -be64_to_cpu (block->bb_u.l.bb_leftsib))) && - block->bb_u.l.bb_rightsib && - (block->bb_u.l.bb_rightsib ==cpu_to_be64 (NULLFSBLOCK ) || -XFS_FSB_SANITY_CHECK (mp, -be64_to_cpu (block->bb_u.l.bb_rightsib))); - -if (unlikely (XFS_TEST_ERROR (!lblock_ok, mp, +if (be32_to_cpu (block->bb_magic) !=xfs_btree_magic (crc, btnum)) +return __this_address; +if (be16_to_cpu (block->bb_level) != level) +return __this_address; +if (be16_to_cpu (block->bb_numrecs) > + cur->bc_ops->get_maxrecs (cur, level)) +return __this_address; +if (block->bb_u.l.bb_leftsib !=cpu_to_be64 (NULLFSBLOCK ) && + !xfs_btree_check_lptr (cur,be64_to_cpu (block->bb_u.l.bb_leftsib), + level +1 )) +return __this_address; +if (block->bb_u.l.bb_rightsib !=cpu_to_be64 (NULLFSBLOCK ) && + !xfs_btree_check_lptr (cur,be64_to_cpu (block->bb_u.l.bb_rightsib), + level +1 )) +return __this_address; + +return NULL ; +} + +/* Check a long btree block header. */ +int +xfs_btree_check_lblock ( +struct xfs_btree_cur *cur, +struct xfs_btree_block *block, +int level, +struct xfs_buf *bp) +{ +struct xfs_mount *mp = cur->bc_mp; +void *failed_at; + + failed_at =__xfs_btree_check_lblock (cur, block, level, bp); +if (unlikely (XFS_TEST_ERROR (failed_at !=NULL , mp,XFS_ERRTAG_BTREE_CHECK_LBLOCK ))) {if (bp)trace_xfs_btree_corrupt (bp, _RET_IP_);
@@ -110,48 +127,61 @@ xfs_btree_check_lblock(return 0 ; } -STATIC int /* error (0 or EFSCORRUPTED) */ -xfs_btree_check_sblock ( -struct xfs_btree_cur *cur,/* btree cursor */ -struct xfs_btree_block *block,/* btree short form block pointer */ -int level,/* level of the btree block */ -struct xfs_buf *bp)/* buffer containing block */ +/* +* Check a short btree block header. Return the address of the failing check, +* or NULL if everything is ok. +*/ +void * +__xfs_btree_check_sblock ( +struct xfs_btree_cur *cur, +struct xfs_btree_block *block, +int level, +struct xfs_buf *bp) { -struct xfs_mount *mp;/* file system mount point */ -struct xfs_buf *agbp;/* buffer for ag. freespace struct */ -struct xfs_agf *agf;/* ag. freespace structure */ -xfs_agblock_t agflen;/* native ag. freespace length */ -int sblock_ok =1 ;/* block passes checks */ +struct xfs_mount *mp = cur->bc_mp;xfs_btnum_t btnum = cur->bc_btnum; -int crc; - - mp = cur->bc_mp; - crc =xfs_sb_version_hascrc (&mp->m_sb); - agbp = cur->bc_private.a.agbp; - agf =XFS_BUF_TO_AGF (agbp); - agflen =be32_to_cpu (agf->agf_length); +int crc =xfs_sb_version_hascrc (&mp->m_sb);if (crc) { - sblock_ok = sblock_ok && -uuid_equal (&block->bb_u.s.bb_uuid, - &mp->m_sb.sb_meta_uuid) && - block->bb_u.s.bb_blkno ==cpu_to_be64 ( - bp ? bp->b_bn :XFS_BUF_DADDR_NULL ); +if (!uuid_equal (&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid)) +return __this_address; +if (block->bb_u.s.bb_blkno != +cpu_to_be64 (bp ? bp->b_bn :XFS_BUF_DADDR_NULL )) +return __this_address; } - sblock_ok = sblock_ok && -be32_to_cpu (block->bb_magic) ==xfs_btree_magic (crc, btnum) && -be16_to_cpu (block->bb_level) == level && -be16_to_cpu (block->bb_numrecs) <= - cur->bc_ops->get_maxrecs (cur, level) && - (block->bb_u.s.bb_leftsib ==cpu_to_be32 (NULLAGBLOCK ) || -be32_to_cpu (block->bb_u.s.bb_leftsib) < agflen) && - block->bb_u.s.bb_leftsib && - (block->bb_u.s.bb_rightsib ==cpu_to_be32 (NULLAGBLOCK ) || -be32_to_cpu (block->bb_u.s.bb_rightsib) < agflen) && - block->bb_u.s.bb_rightsib; - -if (unlikely (XFS_TEST_ERROR (!sblock_ok, mp, +if (be32_to_cpu (block->bb_magic) !=xfs_btree_magic (crc, btnum)) +return __this_address; +if (be16_to_cpu (block->bb_level) != level) +return __this_address; +if (be16_to_cpu (block->bb_numrecs) > + cur->bc_ops->get_maxrecs (cur, level)) +return __this_address; +if (block->bb_u.s.bb_leftsib !=cpu_to_be32 (NULLAGBLOCK ) && + !xfs_btree_check_sptr (cur,be32_to_cpu (block->bb_u.s.bb_leftsib), + level +1 )) +return __this_address; +if (block->bb_u.s.bb_rightsib !=cpu_to_be32 (NULLAGBLOCK ) && + !xfs_btree_check_sptr (cur,be32_to_cpu (block->bb_u.s.bb_rightsib), + level +1 )) +return __this_address; + +return NULL ; +} + +/* Check a short btree block header. */ +STATIC int +xfs_btree_check_sblock ( +struct xfs_btree_cur *cur, +struct xfs_btree_block *block, +int level, +struct xfs_buf *bp) +{ +struct xfs_mount *mp = cur->bc_mp; +void *failed_at; + + failed_at =__xfs_btree_check_sblock (cur, block, level, bp); +if (unlikely (XFS_TEST_ERROR (failed_at !=NULL , mp,XFS_ERRTAG_BTREE_CHECK_SBLOCK ))) {if (bp)trace_xfs_btree_corrupt (bp, _RET_IP_);
fs/xfs/libxfs/xfs_btree.h +5 -0
@@ -255,6 +255,11 @@ typedef struct xfs_btree_cur*/ #define XFS_BUF_TO_BLOCK (bp) ((struct xfs_btree_block *)((bp)->b_addr)) +/* Internal long and short btree block checks. */ +void *__xfs_btree_check_lblock (struct xfs_btree_cur *cur, +struct xfs_btree_block *block,int level,struct xfs_buf *bp); +void *__xfs_btree_check_sblock (struct xfs_btree_cur *cur, +struct xfs_btree_block *block,int level,struct xfs_buf *bp);/* * Check that block header is ok.
fs/xfs/xfs_linux.h +7 -0
@@ -142,6 +142,13 @@ typedef __u32 xfs_nlink_t;#define SYNCHRONIZE () barrier()#define __return_address __builtin_return_address(0) +/* +* Return the address of a label. Use asm volatile so that the optimizer +* won't try anything stupid like refactoring the error jumpouts into a +* single return, which throws off the reported address. +*/ +#define __this_address ({ __label__ __here; __here: asm volatile(""); &&__here; }) +#define XFS_PROJID_DEFAULT 0#define MIN (a,b) (min(a,b))
Re: [PATCH 04/30] xfs: refactor btree block header checking functions
Quoted 6 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Refactor the btree block header checks to have an internal function that returns the address of the failing check without logging errors. The scrubber will call the internal function, while the external version will maintain the current logging behavior.
.....
fs/xfs/libxfs/xfs_btree.h typedef struct xfs_btree_cur · line 262
Quoted 13 lines
fs/xfs/libxfs/xfs_btree.h +5
-0@@ -255,6 +255,11 @@ typedef struct xfs_btree_cur*/ #define XFS_BUF_TO_BLOCK (bp) ((struct xfs_btree_block *)((bp)->b_addr)) +/* Internal long and short btree block checks. */ +void *__xfs_btree_check_lblock (struct xfs_btree_cur *cur, +struct xfs_btree_block *block,int level,struct xfs_buf *bp); +void *__xfs_btree_check_sblock (struct xfs_btree_cur *cur, +struct xfs_btree_block *block,int level,struct xfs_buf *bp);
/*
* Internal long and short btree block checks. They return NULL if
* the block is OK, otherwise they return the address of the failed
* check.
*/
Quoted 8 lines
fs/xfs/libxfs/xfs_btree.h +0
-0/* * Check that block header is ok. fs/xfs/xfs_linux.h +0
-0
fs/xfs/xfs_linux.h typedef __u32 xfs_nlink_t; · line 150
Quoted 9 lines
fs/xfs/xfs_linux.h +6
-0@@ -142,6 +142,13 @@ typedef __u32 xfs_nlink_t;#define SYNCHRONIZE () barrier()#define __return_address __builtin_return_address(0) +/* +* Return the address of a label. Use asm volatile so that the optimizer +* won't try anything stupid like refactoring the error jumpouts into a +* single return, which throws off the reported address. +*/ +#define __this_address ({ __label__ __here; __here: asm volatile(""); &&__here; })
I think this should probably use barrier() rather than an asm statement - can you check that this works correctly with gcc? Other compilers won't work with a asm statement (llvm/intel) but should DTRT with a compiler barrier intrinsic...
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 04/30] xfs: refactor btree block header checking functions
Quoted 18 lines · Dave Chinner
Quoted 6 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Refactor the btree block header checks to have an internal function that returns the address of the failing check without logging errors. The scrubber will call the internal function, while the external version will maintain the current logging behavior.
.....
Quoted 8 lines
fs/xfs/libxfs/xfs_btree.h +0
-0@@ -255,6 +255,11 @@ typedef struct xfs_btree_cur*/ #define XFS_BUF_TO_BLOCK (bp) ((struct xfs_btree_block *)((bp)->b_addr))
Quoted 11 lines
fs/xfs/libxfs/xfs_btree.h typedef struct xfs_btree_cur · line 262
Quoted 5 lines
fs/xfs/libxfs/xfs_btree.h +5
-0+/* Internal long and short btree block checks. */ +void *__xfs_btree_check_lblock (struct xfs_btree_cur *cur, +struct xfs_btree_block *block,int level,struct xfs_buf *bp); +void *__xfs_btree_check_sblock (struct xfs_btree_cur *cur, +struct xfs_btree_block *block,int level,struct xfs_buf *bp);/*
* Internal long and short btree block checks. They return NULL if
* the block is OK, otherwise they return the address of the failed
* check.
*/
Ok.
Quoted 12 lines
Quoted 12 lines
fs/xfs/libxfs/xfs_btree.h +0
-0/* * Check that block header is ok. fs/xfs/xfs_linux.h +0
-0@@ -142,6 +142,13 @@ typedef __u32 xfs_nlink_t;#define SYNCHRONIZE () barrier()#define __return_address __builtin_return_address(0)
Quoted 11 lines
fs/xfs/xfs_linux.h typedef __u32 xfs_nlink_t; · line 150
Quoted 6 lines
fs/xfs/xfs_linux.h +6
-0+/* +* Return the address of a label. Use asm volatile so that the optimizer +* won't try anything stupid like refactoring the error jumpouts into a +* single return, which throws off the reported address. +*/ +#define __this_address ({ __label__ __here; __here: asm volatile(""); &&__here; })I think this should probably use
barrier()rather than an asm statement - can you check that this works correctly with gcc? Other compilers won't work with a asm statement (llvm/intel) but should DTRT with a compiler barrier intrinsic...
Ok. The asm output is identical under asm volatile/barrier, at least on gcc.
--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
PATCH v2 4/30 xfs: refactor btree block header checking functions
In reply to Darrick J. WongRefactor the btree block header checks to have an internal function that returns the address of the failing check without logging errors. The scrubber will call the internal function, while the external version will maintain the current logging behavior.
---
v2: fix raw use of asm volatile in __this_address and lengthen comments
--- fs/xfs/libxfs/xfs_btree.c | 166 +++++++++++++++++++++++++++------------------ fs/xfs/libxfs/xfs_btree.h | 8 ++ fs/xfs/xfs_linux.h | 7 ++ 3 files changed, 113 insertions(+), 68 deletions(-)
fs/xfs/libxfs/xfs_btree.c +98 -68
@@ -63,44 +63,61 @@ xfs_btree_magic(return magic; } -STATIC int /* error (0 or EFSCORRUPTED) */ -xfs_btree_check_lblock ( -struct xfs_btree_cur *cur,/* btree cursor */ -struct xfs_btree_block *block,/* btree long form block pointer */ -int level,/* level of the btree block */ -struct xfs_buf *bp)/* buffer for block, if any */ +/* +* Check a long btree block header. Return the address of the failing check, +* or NULL if everything is ok. +*/ +void * +__xfs_btree_check_lblock ( +struct xfs_btree_cur *cur, +struct xfs_btree_block *block, +int level, +struct xfs_buf *bp) { -int lblock_ok =1 ;/* block passes checks */ -struct xfs_mount *mp;/* file system mount point */ +struct xfs_mount *mp = cur->bc_mp;xfs_btnum_t btnum = cur->bc_btnum; -int crc; - - mp = cur->bc_mp; - crc =xfs_sb_version_hascrc (&mp->m_sb); +int crc =xfs_sb_version_hascrc (&mp->m_sb);if (crc) { - lblock_ok = lblock_ok && -uuid_equal (&block->bb_u.l.bb_uuid, - &mp->m_sb.sb_meta_uuid) && - block->bb_u.l.bb_blkno ==cpu_to_be64 ( - bp ? bp->b_bn :XFS_BUF_DADDR_NULL ); +if (!uuid_equal (&block->bb_u.l.bb_uuid, &mp->m_sb.sb_meta_uuid)) +return __this_address; +if (block->bb_u.l.bb_blkno != +cpu_to_be64 (bp ? bp->b_bn :XFS_BUF_DADDR_NULL )) +return __this_address; } - lblock_ok = lblock_ok && -be32_to_cpu (block->bb_magic) ==xfs_btree_magic (crc, btnum) && -be16_to_cpu (block->bb_level) == level && -be16_to_cpu (block->bb_numrecs) <= - cur->bc_ops->get_maxrecs (cur, level) && - block->bb_u.l.bb_leftsib && - (block->bb_u.l.bb_leftsib ==cpu_to_be64 (NULLFSBLOCK ) || -XFS_FSB_SANITY_CHECK (mp, -be64_to_cpu (block->bb_u.l.bb_leftsib))) && - block->bb_u.l.bb_rightsib && - (block->bb_u.l.bb_rightsib ==cpu_to_be64 (NULLFSBLOCK ) || -XFS_FSB_SANITY_CHECK (mp, -be64_to_cpu (block->bb_u.l.bb_rightsib))); - -if (unlikely (XFS_TEST_ERROR (!lblock_ok, mp, +if (be32_to_cpu (block->bb_magic) !=xfs_btree_magic (crc, btnum)) +return __this_address; +if (be16_to_cpu (block->bb_level) != level) +return __this_address; +if (be16_to_cpu (block->bb_numrecs) > + cur->bc_ops->get_maxrecs (cur, level)) +return __this_address; +if (block->bb_u.l.bb_leftsib !=cpu_to_be64 (NULLFSBLOCK ) && + !xfs_btree_check_lptr (cur,be64_to_cpu (block->bb_u.l.bb_leftsib), + level +1 )) +return __this_address; +if (block->bb_u.l.bb_rightsib !=cpu_to_be64 (NULLFSBLOCK ) && + !xfs_btree_check_lptr (cur,be64_to_cpu (block->bb_u.l.bb_rightsib), + level +1 )) +return __this_address; + +return NULL ; +} + +/* Check a long btree block header. */ +int +xfs_btree_check_lblock ( +struct xfs_btree_cur *cur, +struct xfs_btree_block *block, +int level, +struct xfs_buf *bp) +{ +struct xfs_mount *mp = cur->bc_mp; +void *failed_at; + + failed_at =__xfs_btree_check_lblock (cur, block, level, bp); +if (unlikely (XFS_TEST_ERROR (failed_at !=NULL , mp,XFS_ERRTAG_BTREE_CHECK_LBLOCK ))) {if (bp)trace_xfs_btree_corrupt (bp, _RET_IP_);
@@ -110,48 +127,61 @@ xfs_btree_check_lblock(return 0 ; } -STATIC int /* error (0 or EFSCORRUPTED) */ -xfs_btree_check_sblock ( -struct xfs_btree_cur *cur,/* btree cursor */ -struct xfs_btree_block *block,/* btree short form block pointer */ -int level,/* level of the btree block */ -struct xfs_buf *bp)/* buffer containing block */ +/* +* Check a short btree block header. Return the address of the failing check, +* or NULL if everything is ok. +*/ +void * +__xfs_btree_check_sblock ( +struct xfs_btree_cur *cur, +struct xfs_btree_block *block, +int level, +struct xfs_buf *bp) { -struct xfs_mount *mp;/* file system mount point */ -struct xfs_buf *agbp;/* buffer for ag. freespace struct */ -struct xfs_agf *agf;/* ag. freespace structure */ -xfs_agblock_t agflen;/* native ag. freespace length */ -int sblock_ok =1 ;/* block passes checks */ +struct xfs_mount *mp = cur->bc_mp;xfs_btnum_t btnum = cur->bc_btnum; -int crc; - - mp = cur->bc_mp; - crc =xfs_sb_version_hascrc (&mp->m_sb); - agbp = cur->bc_private.a.agbp; - agf =XFS_BUF_TO_AGF (agbp); - agflen =be32_to_cpu (agf->agf_length); +int crc =xfs_sb_version_hascrc (&mp->m_sb);if (crc) { - sblock_ok = sblock_ok && -uuid_equal (&block->bb_u.s.bb_uuid, - &mp->m_sb.sb_meta_uuid) && - block->bb_u.s.bb_blkno ==cpu_to_be64 ( - bp ? bp->b_bn :XFS_BUF_DADDR_NULL ); +if (!uuid_equal (&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid)) +return __this_address; +if (block->bb_u.s.bb_blkno != +cpu_to_be64 (bp ? bp->b_bn :XFS_BUF_DADDR_NULL )) +return __this_address; } - sblock_ok = sblock_ok && -be32_to_cpu (block->bb_magic) ==xfs_btree_magic (crc, btnum) && -be16_to_cpu (block->bb_level) == level && -be16_to_cpu (block->bb_numrecs) <= - cur->bc_ops->get_maxrecs (cur, level) && - (block->bb_u.s.bb_leftsib ==cpu_to_be32 (NULLAGBLOCK ) || -be32_to_cpu (block->bb_u.s.bb_leftsib) < agflen) && - block->bb_u.s.bb_leftsib && - (block->bb_u.s.bb_rightsib ==cpu_to_be32 (NULLAGBLOCK ) || -be32_to_cpu (block->bb_u.s.bb_rightsib) < agflen) && - block->bb_u.s.bb_rightsib; - -if (unlikely (XFS_TEST_ERROR (!sblock_ok, mp, +if (be32_to_cpu (block->bb_magic) !=xfs_btree_magic (crc, btnum)) +return __this_address; +if (be16_to_cpu (block->bb_level) != level) +return __this_address; +if (be16_to_cpu (block->bb_numrecs) > + cur->bc_ops->get_maxrecs (cur, level)) +return __this_address; +if (block->bb_u.s.bb_leftsib !=cpu_to_be32 (NULLAGBLOCK ) && + !xfs_btree_check_sptr (cur,be32_to_cpu (block->bb_u.s.bb_leftsib), + level +1 )) +return __this_address; +if (block->bb_u.s.bb_rightsib !=cpu_to_be32 (NULLAGBLOCK ) && + !xfs_btree_check_sptr (cur,be32_to_cpu (block->bb_u.s.bb_rightsib), + level +1 )) +return __this_address; + +return NULL ; +} + +/* Check a short btree block header. */ +STATIC int +xfs_btree_check_sblock ( +struct xfs_btree_cur *cur, +struct xfs_btree_block *block, +int level, +struct xfs_buf *bp) +{ +struct xfs_mount *mp = cur->bc_mp; +void *failed_at; + + failed_at =__xfs_btree_check_sblock (cur, block, level, bp); +if (unlikely (XFS_TEST_ERROR (failed_at !=NULL , mp,XFS_ERRTAG_BTREE_CHECK_SBLOCK ))) {if (bp)trace_xfs_btree_corrupt (bp, _RET_IP_);
fs/xfs/libxfs/xfs_btree.h +8 -0
@@ -255,6 +255,14 @@ typedef struct xfs_btree_cur*/ #define XFS_BUF_TO_BLOCK (bp) ((struct xfs_btree_block *)((bp)->b_addr)) +/* +* Internal long and short btree block checks. They return NULL if the +* block is ok or the address of the failed check otherwise. +*/ +void *__xfs_btree_check_lblock (struct xfs_btree_cur *cur, +struct xfs_btree_block *block,int level,struct xfs_buf *bp); +void *__xfs_btree_check_sblock (struct xfs_btree_cur *cur, +struct xfs_btree_block *block,int level,struct xfs_buf *bp);/* * Check that block header is ok.
fs/xfs/xfs_linux.h +7 -0
@@ -142,6 +142,13 @@ typedef __u32 xfs_nlink_t;#define SYNCHRONIZE () barrier()#define __return_address __builtin_return_address(0) +/* +* Return the address of a label. Use barrier() so that the optimizer +* won't reorder code to refactor the error jumpouts into a single +* return, which throws off the reported address. +*/ +#define __this_address ({ __label__ __here; __here: barrier(); &&__here; }) +#define XFS_PROJID_DEFAULT 0#define MIN (a,b) (min(a,b))
Re: [PATCH v2 04/30] xfs: refactor btree block header checking functions
Quoted 6 lines · Darrick J. Wong
Refactor the btree block header checks to have an internal function that returns the address of the failing check without logging errors. The scrubber will call the internal function, while the external version will maintain the current logging behavior.
Signed-off-by Darrick J. Wong <[email protected]>
Looks good.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 5/30 xfs: create inode pointer verifiers
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create some helper functions to check that inode pointers point to somewhere within the filesystem and not at the static AG metadata. Move xfs_internal_inum and create a directory inode check function. We will use these functions in scrub and elsewhere.
--- fs/xfs/libxfs/xfs_dir2.c | 19 ++-------- fs/xfs/libxfs/xfs_ialloc.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_ialloc.h | 7 ++++ fs/xfs/xfs_itable.c | 10 ----- fs/xfs/xfs_itable.h | 2 - 5 files changed, 91 insertions(+), 28 deletions(-)
fs/xfs/libxfs/xfs_dir2.c +3 -16
@@ -30,6 +30,7 @@#include "xfs_bmap.h" #include "xfs_dir2.h" #include "xfs_dir2_priv.h" +#include "xfs_ialloc.h" #include "xfs_error.h" #include "xfs_trace.h"
@@ -202,22 +203,8 @@ xfs_dir_ino_validate(xfs_mount_t *mp, xfs_ino_t ino) { -xfs_agblock_t agblkno; -xfs_agino_t agino; -xfs_agnumber_t agno; -int ino_ok; -int ioff; - - agno =XFS_INO_TO_AGNO (mp, ino); - agblkno =XFS_INO_TO_AGBNO (mp, ino); - ioff =XFS_INO_TO_OFFSET (mp, ino); - agino =XFS_OFFBNO_TO_AGINO (mp, agblkno, ioff); - ino_ok = - agno < mp->m_sb.sb_agcount && - agblkno < mp->m_sb.sb_agblocks && - agblkno !=0 && - ioff < (1 << mp->m_sb.sb_inopblog) && -XFS_AGINO_TO_INO (mp, agno, agino) == ino; + bool ino_ok = xfs_verify_dir_ino_ptr(mp, ino); +if (unlikely (XFS_TEST_ERROR (!ino_ok, mp,XFS_ERRTAG_DIR_INO_VALIDATE ))) {xfs_warn (mp,"Invalid inode number 0x%Lx" , (unsigned long long ) ino);
fs/xfs/libxfs/xfs_ialloc.c +81 -0
84 lines not on this page · raw message
fs/xfs/libxfs/xfs_ialloc.h +7 -0
@@ -173,5 +173,12 @@ void xfs_inobt_btrec_to_irec(struct xfs_mount *mp, union xfs_btree_rec *rec, struct xfs_inobt_rec_incore *irec);int xfs_ialloc_cluster_alignment (struct xfs_mount *mp); +void xfs_ialloc_aginode_range (struct xfs_mount *mp,xfs_agnumber_t agno, +xfs_agino_t *first,xfs_agino_t *last); +bool xfs_verify_agino_ptr (struct xfs_mount *mp,xfs_agnumber_t agno, +xfs_agino_t agino); +bool xfs_verify_ino_ptr (struct xfs_mount *mp,xfs_ino_t ino); +bool xfs_internal_inum (struct xfs_mount *mp,xfs_ino_t ino); +bool xfs_verify_dir_ino_ptr (struct xfs_mount *mp,xfs_ino_t ino);#endif /* __XFS_IALLOC_H__ */
fs/xfs/xfs_itable.c +0 -10
@@ -31,16 +31,6 @@#include "xfs_trace.h" #include "xfs_icache.h" -int -xfs_internal_inum ( -xfs_mount_t *mp, -xfs_ino_t ino) -{ -return (ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino || - (xfs_sb_version_hasquota (&mp->m_sb) && -xfs_is_quota_inode (&mp->m_sb, ino))); -} -/* * Return stat information for one inode. * Return 0 if ok, else errno.
fs/xfs/xfs_itable.h +0 -2
@@ -96,6 +96,4 @@ xfs_inumbers(void __user *buffer,/* buffer with inode info */ inumbers_fmt_pf formatter); -int xfs_internal_inum (struct xfs_mount *mp,xfs_ino_t ino); -#endif /* __XFS_ITABLE_H__ */
Re: [PATCH 05/30] xfs: create inode pointer verifiers
Quoted 68 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create some helper functions to check that inode pointers point to somewhere within the filesystem and not at the static AG metadata. Move
xfs_internal_inumand create a directory inode check function. We will use these functions in scrub and elsewhere.Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/libxfs/xfs_dir2.c | 19 ++-------- fs/xfs/libxfs/xfs_ialloc.c | 81 ++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_ialloc.h | 7 ++++ fs/xfs/xfs_itable.c | 10 ----- fs/xfs/xfs_itable.h | 2 - 5 files changed, 91 insertions(+), 28 deletions(-)fs/xfs/libxfs/xfs_dir2.c +3
-16@@ -30,6 +30,7 @@#include "xfs_bmap.h" #include "xfs_dir2.h" #include "xfs_dir2_priv.h" +#include "xfs_ialloc.h" #include "xfs_error.h" #include "xfs_trace.h" @@ -202,22 +203,8 @@ xfs_dir_ino_validate(xfs_mount_t *mp, xfs_ino_t ino) { -xfs_agblock_t agblkno; -xfs_agino_t agino; -xfs_agnumber_t agno; -int ino_ok; -int ioff; - - agno =XFS_INO_TO_AGNO (mp, ino); - agblkno =XFS_INO_TO_AGBNO (mp, ino); - ioff =XFS_INO_TO_OFFSET (mp, ino); - agino =XFS_OFFBNO_TO_AGINO (mp, agblkno, ioff); - ino_ok = - agno < mp->m_sb.sb_agcount && - agblkno < mp->m_sb.sb_agblocks && - agblkno !=0 && - ioff < (1 << mp->m_sb.sb_inopblog) && -XFS_AGINO_TO_INO (mp, agno, agino) == ino; + bool ino_ok = xfs_verify_dir_ino_ptr(mp, ino); +if (unlikely (XFS_TEST_ERROR (!ino_ok, mp,XFS_ERRTAG_DIR_INO_VALIDATE ))) {xfs_warn (mp,"Invalid inode number 0x%Lx" , (unsigned long long ) ino);fs/xfs/libxfs/xfs_ialloc.c +6
-0@@ -2664,3 +2664,84 @@ xfs_ialloc_pagi_init(xfs_trans_brelse (tp, bp);return 0 ; } + +/* Calculate the first and last possible inode number in an AG. */ +void +xfs_ialloc_aginode_range( + struct xfs_mount *mp, + xfs_agnumber_t agno,
fs/xfs/libxfs/xfs_ialloc.c xfs_ialloc_pagi_init( · line 2682
Quoted 10 lines
fs/xfs/libxfs/xfs_ialloc.c +10
-0+ xfs_agino_t *first, + xfs_agino_t *last) +{ +xfs_agblock_t eoag; + + eoag =xfs_ag_block_count (mp, agno); + *first =round_up (XFS_OFFBNO_TO_AGINO (mp,XFS_AGFL_BLOCK (mp) +1 ,0 ), +XFS_INODES_PER_CHUNK ); + *last =round_down (XFS_OFFBNO_TO_AGINO (mp, eoag,0 ), +XFS_INODES_PER_CHUNK ) -1 ;
This is incorrect; we allocate inode chunks aligned to xfs_ialloc_cluster_alignment blocks, which doesn't necessarily result in ir_startino being aligned to XFS_INODES_PER_CHUNK.
I think the correct code is this:
/* Calculate the first inode. */ bno = round_up(XFS_AGFL_BLOCK(mp) + 1, xfs_ialloc_cluster_alignment(mp)); *first = XFS_OFFBNO_TO_AGINO(mp, bno, 0); /* Calculate the last inode. */ bno = round_down(eoag, xfs_ialloc_cluster_alignment(mp)); *last = XFS_OFFBNO_TO_AGINO(mp, bno, 0) - 1;
...which unfortunately I didn't realize until trying to play with nondefault geometry options (1k blocks, no sparse inodes).
--D
Quoted 119 lines
fs/xfs/libxfs/xfs_ialloc.c +65
-0+} + +/* +* Verify that an AG inode number pointer neither points outside the AG +* nor points at static metadata. +*/ +bool +xfs_verify_agino_ptr ( +struct xfs_mount *mp, +xfs_agnumber_t agno, +xfs_agino_t agino) +{ +xfs_agino_t first; +xfs_agino_t last; +int ioff; + + ioff =XFS_AGINO_TO_OFFSET (mp, agino); +xfs_ialloc_aginode_range (mp, agno, &first, &last); +return agino >= first && agino <= last && + ioff < (1 << mp->m_sb.sb_inopblog); +} + +/* +* Verify that an FS inode number pointer neither points outside the +* filesystem nor points at static AG metadata. +*/ +bool +xfs_verify_ino_ptr ( +struct xfs_mount *mp, +xfs_ino_t ino) +{ +xfs_agnumber_t agno =XFS_INO_TO_AGNO (mp, ino); +xfs_agino_t agino =XFS_INO_TO_AGINO (mp, ino); + +if (agno >= mp->m_sb.sb_agcount) +return false; +if (XFS_AGINO_TO_INO (mp, agno, agino) != ino) +return false; +return xfs_verify_agino_ptr (mp, agno, agino); +} + +/* Is this an internal inode number? */ +bool +xfs_internal_inum ( +struct xfs_mount *mp, +xfs_ino_t ino) +{ +return ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino || + (xfs_sb_version_hasquota (&mp->m_sb) && +xfs_is_quota_inode (&mp->m_sb, ino)); +} + +/* +* Verify that a directory entry's inode number doesn't point at an internal +* inode, empty space, or static AG metadata. +*/ +bool +xfs_verify_dir_ino_ptr ( +struct xfs_mount *mp, +xfs_ino_t ino) +{ +if (xfs_internal_inum (mp, ino)) +return false; +return xfs_verify_ino_ptr (mp, ino); +}fs/xfs/libxfs/xfs_ialloc.h +7
-0@@ -173,5 +173,12 @@ void xfs_inobt_btrec_to_irec(struct xfs_mount *mp, union xfs_btree_rec *rec, struct xfs_inobt_rec_incore *irec);int xfs_ialloc_cluster_alignment (struct xfs_mount *mp); +void xfs_ialloc_aginode_range (struct xfs_mount *mp,xfs_agnumber_t agno, +xfs_agino_t *first,xfs_agino_t *last); +bool xfs_verify_agino_ptr (struct xfs_mount *mp,xfs_agnumber_t agno, +xfs_agino_t agino); +bool xfs_verify_ino_ptr (struct xfs_mount *mp,xfs_ino_t ino); +bool xfs_internal_inum (struct xfs_mount *mp,xfs_ino_t ino); +bool xfs_verify_dir_ino_ptr (struct xfs_mount *mp,xfs_ino_t ino);#endif /* __XFS_IALLOC_H__ */ fs/xfs/xfs_itable.c +0
-10@@ -31,16 +31,6 @@#include "xfs_trace.h" #include "xfs_icache.h" -int -xfs_internal_inum ( -xfs_mount_t *mp, -xfs_ino_t ino) -{ -return (ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino || - (xfs_sb_version_hasquota (&mp->m_sb) && -xfs_is_quota_inode (&mp->m_sb, ino))); -} -/* * Return stat information for one inode. * Return 0 if ok, else errno. fs/xfs/xfs_itable.h +0
-3@@ -96,6 +96,4 @@ xfs_inumbers(void __user *buffer,/* buffer with inode info */ inumbers_fmt_pf formatter); -int xfs_internal_inum (struct xfs_mount *mp,xfs_ino_t ino); -#endif /* __XFS_ITABLE_H__ */ --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/30] xfs: create inode pointer verifiers
Quoted 7 lines · Darrick J. Wong
Quoted 6 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create some helper functions to check that inode pointers point to somewhere within the filesystem and not at the static AG metadata. Move
xfs_internal_inumand create a directory inode check function. We will use these functions in scrub and elsewhere.
....
Quoted 4 lines
Quoted 4 lines
fs/xfs/libxfs/xfs_ialloc.c +0
-0
Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c xfs_ialloc_pagi_init( · line 2670
Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c +4
-0@@ -2664,3 +2664,84 @@ xfs_ialloc_pagi_init(xfs_trans_brelse (tp, bp);return 0 ; } + +/* Calculate the first and last possible inode number in an AG. */ +void +xfs_ialloc_aginode_range(
agino_range?
Quoted 8 lines
Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c +8
-0+struct xfs_mount *mp, + xfs_agnumber_t agno, + xfs_agino_t *first, + xfs_agino_t *last) +{ + xfs_agblock_t eoag; + + eoag =xfs_ag_block_count (mp, agno);
Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c xfs_ialloc_pagi_init( · line 2682
Quoted 4 lines
fs/xfs/libxfs/xfs_ialloc.c +4
-0+ *first =round_up (XFS_OFFBNO_TO_AGINO (mp,XFS_AGFL_BLOCK (mp) +1 ,0 ), +XFS_INODES_PER_CHUNK ); + *last =round_down (XFS_OFFBNO_TO_AGINO (mp, eoag,0 ), +XFS_INODES_PER_CHUNK ) -1 ;This is incorrect; we allocate inode chunks aligned to
xfs_ialloc_cluster_alignmentblocks, which doesn't necessarily result inir_startinobeing aligned toXFS_INODES_PER_CHUNK.
*nod*
Quoted 6 lines
I think the correct code is this:
/* Calculate the first inode. */ bno = round_up(XFS_AGFL_BLOCK(mp) + 1, xfs_ialloc_cluster_alignment(mp)); *first = XFS_OFFBNO_TO_AGINO(mp, bno, 0);
*nod*
Quoted 3 lines
/* Calculate the last inode. */ bno = round_down(eoag, xfs_ialloc_cluster_alignment(mp)); *last = XFS_OFFBNO_TO_AGINO(mp, bno, 0) - 1;
Bit tricky - I'm not sure that this will give the same inode number in all cases as rounding down to last valid chunk start offset and then adding (MAX(XFS_INODES_PER_CHUNK, inodes-per-block) - 1) to it....
Quoted 2 lines
...which unfortunately I didn't realize until trying to play with nondefault geometry options (1k blocks, no sparse inodes).
Ok, that might explain a bunch of inode noise on my 1k block size test runs...
Quoted 6 lines
fs/xfs/libxfs/xfs_ialloc.c xfs_ialloc_pagi_init( · line 2690
Quoted 6 lines
fs/xfs/libxfs/xfs_ialloc.c +6
-0+/* +* Verify that an AG inode number pointer neither points outside the AG +* nor points at static metadata. +*/ +bool +xfs_verify_agino_ptr(
Again, I'd probably drop the _ptr suffix here.
Quoted 4 lines
Quoted 4 lines
fs/xfs/libxfs/xfs_ialloc.c +4
-0+struct xfs_mount *mp, + xfs_agnumber_t agno, + xfs_agino_t agino) +{
Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c xfs_ialloc_pagi_init( · line 2702
Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c +8
-0+xfs_agino_t first; +xfs_agino_t last; +int ioff; + + ioff =XFS_AGINO_TO_OFFSET (mp, agino); +xfs_ialloc_aginode_range (mp, agno, &first, &last); +return agino >= first && agino <= last && + ioff < (1 << mp->m_sb.sb_inopblog);
This ioff check will always evaluate as true, yes?
ioff = XFS_AGINO_TO_OFFSET(i)
= ((i) & XFS_INO_MASK(XFS_INO_OFFSET_BITS(mp))) = (i & XFS_INO_MASK((mp)->m_sb.sb_inopblog)) = (i & (uint32_t)((1ULL << (mp)->m_sb.sb_inopblog)) - 1)
say sb_inopblog = 8:
ioff = (i & 0xFF)
And so:
ioff < (1 << 8)
will always be true.
So I'm not sure this check is needed?
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 05/30] xfs: create inode pointer verifiers
Quoted 100 lines · Dave Chinner
Quoted 7 lines · Darrick J. Wong
Quoted 6 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create some helper functions to check that inode pointers point to somewhere within the filesystem and not at the static AG metadata. Move
xfs_internal_inumand create a directory inode check function. We will use these functions in scrub and elsewhere.....
Quoted 4 lines
Quoted 4 lines
fs/xfs/libxfs/xfs_ialloc.c +0
-0Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c xfs_ialloc_pagi_init( · line 2670
Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c +4
-0@@ -2664,3 +2664,84 @@ xfs_ialloc_pagi_init(xfs_trans_brelse (tp, bp);return 0 ; } + +/* Calculate the first and last possible inode number in an AG. */ +void +xfs_ialloc_aginode_range(
agino_range?Quoted 8 lines
Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c +8
-0+struct xfs_mount *mp, + xfs_agnumber_t agno, + xfs_agino_t *first, + xfs_agino_t *last) +{ + xfs_agblock_t eoag; + + eoag =xfs_ag_block_count (mp, agno);Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c xfs_ialloc_pagi_init( · line 2682
Quoted 4 lines
fs/xfs/libxfs/xfs_ialloc.c +4
-0+ *first =round_up (XFS_OFFBNO_TO_AGINO (mp,XFS_AGFL_BLOCK (mp) +1 ,0 ), +XFS_INODES_PER_CHUNK ); + *last =round_down (XFS_OFFBNO_TO_AGINO (mp, eoag,0 ), +XFS_INODES_PER_CHUNK ) -1 ;This is incorrect; we allocate inode chunks aligned to
xfs_ialloc_cluster_alignmentblocks, which doesn't necessarily result inir_startinobeing aligned toXFS_INODES_PER_CHUNK.*nod*
Quoted 6 lines
I think the correct code is this:
/* Calculate the first inode. */ bno = round_up(XFS_AGFL_BLOCK(mp) + 1, xfs_ialloc_cluster_alignment(mp)); *first = XFS_OFFBNO_TO_AGINO(mp, bno, 0);*nod*
Quoted 3 lines
/* Calculate the last inode. */ bno = round_down(eoag, xfs_ialloc_cluster_alignment(mp)); *last = XFS_OFFBNO_TO_AGINO(mp, bno, 0) - 1;Bit tricky - I'm not sure that this will give the same inode number in all cases as rounding down to last valid chunk start offset and then adding (
MAX(XFS_INODES_PER_CHUNK, inodes-per-block)- 1) to it....Quoted 2 lines
...which unfortunately I didn't realize until trying to play with nondefault geometry options (1k blocks, no sparse inodes).
Ok, that might explain a bunch of inode noise on my 1k block size test runs...
Quoted 6 lines
fs/xfs/libxfs/xfs_ialloc.c xfs_ialloc_pagi_init( · line 2690
Quoted 6 lines
fs/xfs/libxfs/xfs_ialloc.c +6
-0+/* +* Verify that an AG inode number pointer neither points outside the AG +* nor points at static metadata. +*/ +bool +xfs_verify_agino_ptr(Again, I'd probably drop the
_ptrsuffix here.Quoted 4 lines
Quoted 4 lines
fs/xfs/libxfs/xfs_ialloc.c +4
-0+struct xfs_mount *mp, + xfs_agnumber_t agno, + xfs_agino_t agino) +{Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c xfs_ialloc_pagi_init( · line 2702
Quoted 8 lines
fs/xfs/libxfs/xfs_ialloc.c +8
-0+xfs_agino_t first; +xfs_agino_t last; +int ioff; + + ioff =XFS_AGINO_TO_OFFSET (mp, agino); +xfs_ialloc_aginode_range (mp, agno, &first, &last); +return agino >= first && agino <= last && + ioff < (1 << mp->m_sb.sb_inopblog);This ioff check will always evaluate as true, yes?
ioff =
XFS_AGINO_TO_OFFSET(i)= ((i) & XFS_INO_MASK(XFS_INO_OFFSET_BITS(mp))) = (i & XFS_INO_MASK((mp)->m_sb.sb_inopblog)) = (i & (uint32_t)((1ULL << (mp)->m_sb.sb_inopblog)) - 1)say
sb_inopblog= 8:ioff = (i &
0xFF)
Quoted 7 lines
And so:
ioff < (1 << 8)will always be true.
So I'm not sure this check is needed?
Indeed not.
--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
PATCH v2 5/30 xfs: create inode pointer verifiers
In reply to Darrick J. WongCreate some helper functions to check that inode pointers point to somewhere within the filesystem and not at the static AG metadata. Move xfs_internal_inum and create a directory inode check function. We will use these functions in scrub and elsewhere.
---
v2: fix names, fix incorrect results returned from agino_range, remove unnecessary tests
--- fs/xfs/libxfs/xfs_dir2.c | 19 +-------- fs/xfs/libxfs/xfs_ialloc.c | 90 ++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_ialloc.h | 7 +++ fs/xfs/xfs_itable.c | 10 ----- fs/xfs/xfs_itable.h | 2 - 5 files changed, 100 insertions(+), 28 deletions(-)
fs/xfs/libxfs/xfs_dir2.c +3 -16
@@ -30,6 +30,7 @@#include "xfs_bmap.h" #include "xfs_dir2.h" #include "xfs_dir2_priv.h" +#include "xfs_ialloc.h" #include "xfs_error.h" #include "xfs_trace.h"
@@ -202,22 +203,8 @@ xfs_dir_ino_validate(xfs_mount_t *mp, xfs_ino_t ino) { -xfs_agblock_t agblkno; -xfs_agino_t agino; -xfs_agnumber_t agno; -int ino_ok; -int ioff; - - agno =XFS_INO_TO_AGNO (mp, ino); - agblkno =XFS_INO_TO_AGBNO (mp, ino); - ioff =XFS_INO_TO_OFFSET (mp, ino); - agino =XFS_OFFBNO_TO_AGINO (mp, agblkno, ioff); - ino_ok = - agno < mp->m_sb.sb_agcount && - agblkno < mp->m_sb.sb_agblocks && - agblkno !=0 && - ioff < (1 << mp->m_sb.sb_inopblog) && -XFS_AGINO_TO_INO (mp, agno, agino) == ino; + bool ino_ok = xfs_verify_dir_ino(mp, ino); +if (unlikely (XFS_TEST_ERROR (!ino_ok, mp,XFS_ERRTAG_DIR_INO_VALIDATE ))) {xfs_warn (mp,"Invalid inode number 0x%Lx" , (unsigned long long ) ino);
fs/xfs/libxfs/xfs_ialloc.c +90 -0
93 lines not on this page · raw message
fs/xfs/libxfs/xfs_ialloc.h +7 -0
@@ -173,5 +173,12 @@ void xfs_inobt_btrec_to_irec(struct xfs_mount *mp, union xfs_btree_rec *rec, struct xfs_inobt_rec_incore *irec);int xfs_ialloc_cluster_alignment (struct xfs_mount *mp); +void xfs_ialloc_agino_range (struct xfs_mount *mp,xfs_agnumber_t agno, +xfs_agino_t *first,xfs_agino_t *last); +bool xfs_verify_agino (struct xfs_mount *mp,xfs_agnumber_t agno, +xfs_agino_t agino); +bool xfs_verify_ino (struct xfs_mount *mp,xfs_ino_t ino); +bool xfs_internal_inum (struct xfs_mount *mp,xfs_ino_t ino); +bool xfs_verify_dir_ino (struct xfs_mount *mp,xfs_ino_t ino);#endif /* __XFS_IALLOC_H__ */
fs/xfs/xfs_itable.c +0 -10
@@ -31,16 +31,6 @@#include "xfs_trace.h" #include "xfs_icache.h" -int -xfs_internal_inum ( -xfs_mount_t *mp, -xfs_ino_t ino) -{ -return (ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino || - (xfs_sb_version_hasquota (&mp->m_sb) && -xfs_is_quota_inode (&mp->m_sb, ino))); -} -/* * Return stat information for one inode. * Return 0 if ok, else errno.
fs/xfs/xfs_itable.h +0 -2
@@ -96,6 +96,4 @@ xfs_inumbers(void __user *buffer,/* buffer with inode info */ inumbers_fmt_pf formatter); -int xfs_internal_inum (struct xfs_mount *mp,xfs_ino_t ino); -#endif /* __XFS_ITABLE_H__ */
Re: [PATCH v2 05/30] xfs: create inode pointer verifiers
Quoted 9 lines · Darrick J. Wong
Create some helper functions to check that inode pointers point to somewhere within the filesystem and not at the static AG metadata. Move
xfs_internal_inumand create a directory inode check function. We will use these functions in scrub and elsewhere.Signed-off-by Darrick J. Wong <[email protected]>---
v2: fix names, fix incorrect results returned fromagino_range, remove unnecessary tests
Looks good.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 6/30 xfs: create an ioctl to scrub AG metadata
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create an ioctl that can be used to scrub internal filesystem metadata. The new ioctl takes the metadata type, an (optional) AG number, an (optional) inode number and generation, and a flags argument. This will be used by the upcoming XFS online scrub tool.
--- fs/xfs/Kconfig | 17 ++++++++++++++ fs/xfs/Makefile | 11 +++++++++ fs/xfs/libxfs/xfs_fs.h | 53 +++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.h | 25 +++++++++++++++++++++ fs/xfs/scrub/trace.c | 41 +++++++++++++++++++++++++++++++++++ fs/xfs/scrub/trace.h | 33 ++++++++++++++++++++++++++++ fs/xfs/scrub/xfs_scrub.h | 29 +++++++++++++++++++++++++ fs/xfs/xfs_ioctl.c | 28 ++++++++++++++++++++++++ fs/xfs/xfs_ioctl32.c | 1 + 10 files changed, 292 insertions(+) create mode 100644 fs/xfs/scrub/scrub.c create mode 100644 fs/xfs/scrub/scrub.h create mode 100644 fs/xfs/scrub/trace.c create mode 100644 fs/xfs/scrub/trace.h create mode 100644 fs/xfs/scrub/xfs_scrub.h
fs/xfs/Kconfig +17 -0
@@ -71,6 +71,23 @@ config XFS_RT If unsure, say N. +config XFS_ONLINE_SCRUB + bool "XFS online metadata check support" + default n + depends on XFS_FS + help + If you say Y here you will be able to check metadata on a + mounted XFS filesystem. This feature is intended to reduce + filesystem downtime by supplementing xfs_repair. The key + advantage here is to look for problems proactively so that + they can be dealt with in a controlled manner. + + This feature is considered EXPERIMENTAL. Use with caution! + + See the xfs_scrub man page in section 8 for additional information. + + If unsure, say N. + config XFS_WARN bool "XFS Verbose Warnings" depends on XFS_FS && !XFS_DEBUG
fs/xfs/Makefile +11 -0
@@ -138,3 +138,14 @@ xfs-$(CONFIG_XFS_POSIX_ACL) += xfs_acl.o xfs-$(CONFIG_SYSCTL) += xfs_sysctl.o xfs-$(CONFIG_COMPAT) += xfs_ioctl32.o xfs-$(CONFIG_EXPORTFS_BLOCK_OPS) += xfs_pnfs.o + +# online scrub/repair +ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y) + +# Tracepoints like to blow up, so build that before everything else + +xfs-y += $(addprefix scrub/, \ + trace.o \ + scrub.o \ + ) +endif
fs/xfs/libxfs/xfs_fs.h +53 -0
65 lines not on this page · raw message
fs/xfs/scrub/scrub.c new file +54 -0
54 lines not on this page · raw message
fs/xfs/scrub/scrub.h new file +25 -0
@@ -0,0 +1,25 @@ +/* +* Copyright (C) 2017 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. +*/ +#ifndef __XFS_SCRUB_SCRUB_H__ +#define __XFS_SCRUB_SCRUB_H__ + +/* Metadata scrubbers */ + +#endif /* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.c new file +41 -0
41 lines not on this page · raw message
fs/xfs/scrub/trace.h new file +33 -0
@@ -0,0 +1,33 @@ +/* +* Copyright (C) 2017 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. +*/ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM xfs_scrub + +#if !defined(_TRACE_XFS_SCRUB_TRACE_H) || defined(TRACE_HEADER_MULTI_READ ) +#define _TRACE_XFS_SCRUB_TRACE_H + +#include <linux/tracepoint.h> + +#endif /* _TRACE_XFS_SCRUB_TRACE_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . +#define TRACE_INCLUDE_FILE scrub/trace +#include <trace/define_trace.h>
fs/xfs/scrub/xfs_scrub.h new file +29 -0
@@ -0,0 +1,29 @@ +/* +* Copyright (C) 2017 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. +*/ +#ifndef __XFS_SCRUB_H__ +#define __XFS_SCRUB_H__ + +#ifndef CONFIG_XFS_ONLINE_SCRUB +# define xfs_scrub_metadata (ip, sm) (-ENOTTY) +#else +int xfs_scrub_metadata (struct xfs_inode *ip,struct xfs_scrub_metadata *sm); +#endif /* CONFIG_XFS_ONLINE_SCRUB */ + +#endif /* __XFS_SCRUB_H__ */
fs/xfs/xfs_ioctl.c +28 -0
@@ -44,6 +44,7 @@#include "xfs_btree.h" #include <linux/fsmap.h> #include "xfs_fsmap.h" +#include "scrub/xfs_scrub.h" #include <linux/capability.h> #include <linux/cred.h>
@@ -1703,6 +1704,30 @@ xfs_ioc_getfsmap(return 0 ; } +STATIC int +xfs_ioc_scrub_metadata ( +struct xfs_inode *ip, +void __user *arg) +{ +struct xfs_scrub_metadata scrub; +int error; + +if (!capable (CAP_SYS_ADMIN )) +return -EPERM ; + +if (copy_from_user (&scrub, arg,sizeof (scrub))) +return -EFAULT ; + + error =xfs_scrub_metadata (ip, &scrub); +if (error) +return error; + +if (copy_to_user (arg, &scrub,sizeof (scrub))) +return -EFAULT ; + +return 0 ; +} +int xfs_ioc_swapext (xfs_swapext_t *sxp)
@@ -1886,6 +1911,9 @@ xfs_file_ioctl(case FS_IOC_GETFSMAP :return xfs_ioc_getfsmap (ip, arg); +case XFS_IOC_SCRUB_METADATA : +return xfs_ioc_scrub_metadata (ip, arg); +case XFS_IOC_FD_TO_HANDLE :case XFS_IOC_PATH_TO_HANDLE :case XFS_IOC_PATH_TO_FSHANDLE : {
fs/xfs/xfs_ioctl32.c +1 -0
@@ -556,6 +556,7 @@ xfs_file_compat_ioctl(case XFS_IOC_ERROR_INJECTION :case XFS_IOC_ERROR_CLEARALL :case FS_IOC_GETFSMAP : +case XFS_IOC_SCRUB_METADATA :return xfs_file_ioctl (filp, cmd, p);#ifndef BROKEN_X86_ALIGNMENT /* These are handled fine if no alignment issues */
Re: [PATCH 06/30] xfs: create an ioctl to scrub AG metadata
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create an ioctl that can be used to scrub internal filesystem metadata. The new ioctl takes the metadata type, an (optional) AG number, an (optional) inode number and generation, and a flags argument. This will be used by the upcoming XFS online scrub tool.
Signed-off-by Darrick J. Wong <[email protected]>
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 7/30 xfs: dispatch metadata scrub subcommands
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create structures needed to hold scrubbing context and dispatch incoming commands to the individual scrubbers.
--- fs/xfs/scrub/scrub.c | 192 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.h | 24 ++++++ fs/xfs/scrub/trace.h | 43 +++++++++++ 3 files changed, 258 insertions(+), 1 deletion(-)
fs/xfs/scrub/scrub.c +191 -1
@@ -44,11 +44,201 @@#include "scrub/scrub.h" #include "scrub/trace.h" +/* +* Online Scrub and Repair +* +* Traditionally, XFS (the kernel driver) did not know how to check or +* repair on-disk data structures. That task was left to the xfs_check +* and xfs_repair tools, both of which require taking the filesystem +* offline for a thorough but time consuming examination. Online +* scrub & repair, on the other hand, enables us to check the metadata +* for obvious errors while carefully stepping around the filesystem's +* ongoing operations, locking rules, etc. +* +* Given that most XFS metadata consist of records stored in a btree, +* most of the checking functions iterate the btree blocks themselves +* looking for irregularities. When a record block is encountered, each +* record can be checked for obviously bad values. Record values can +* also be cross-referenced against other btrees to look for potential +* misunderstandings between pieces of metadata. +* +* It is expected that the checkers responsible for per-AG metadata +* structures will lock the AG headers (AGI, AGF, AGFL), iterate the +* metadata structure, and perform any relevant cross-referencing before +* unlocking the AG and returning the results to userspace. These +* scrubbers must not keep an AG locked for too long to avoid tying up +* the block and inode allocators. +* +* Block maps and b-trees rooted in an inode present a special challenge +* because they can involve extents from any AG. The general scrubber +* structure of lock -> check -> xref -> unlock still holds, but AG +* locking order rules /must/ be obeyed to avoid deadlocks. The +* ordering rule, of course, is that we must lock in increasing AG +* order. Helper functions are provided to track which AG headers we've +* already locked. If we detect an imminent locking order violation, we +* can signal a potential deadlock, in which case the scrubber can jump +* out to the top level, lock all the AGs in order, and retry the scrub. +* +* For file data (directories, extended attributes, symlinks) scrub, we +* can simply lock the inode and walk the data. For btree data +* (directories and attributes) we follow the same btree-scrubbing +* strategy outlined previously to check the records. +* +* We use a bit of trickery with transactions to avoid buffer deadlocks +* if there is a cycle in the metadata. The basic problem is that +* travelling down a btree involves locking the current buffer at each +* tree level. If a pointer should somehow point back to a buffer that +* we've already examined, we will deadlock due to the second buffer +* locking attempt. Note however that grabbing a buffer in transaction +* context links the locked buffer to the transaction. If we try to +* re-grab the buffer in the context of the same transaction, we avoid +* the second lock attempt and continue. Between the verifier and the +* scrubber, something will notice that something is amiss and report +* the corruption. Therefore, each scrubber will allocate an empty +* transaction, attach buffers to it, and cancel the transaction at the +* end of the scrub run. Cancelling a non-dirty transaction simply +* unlocks the buffers. +* +* There are four pieces of data that scrub can communicate to +* userspace. The first is the error code (errno), which can be used to +* communicate operational errors in performing the scrub. There are +* also three flags that can be set in the scrub context. If the data +* structure itself is corrupt, the CORRUPT flag will be set. If +* the metadata is correct but otherwise suboptimal, the PREEN flag +* will be set. +*/ + +/* Scrub setup and teardown */ + +/* Free all the resources and finish the transactions. */ +STATIC int +xfs_scrub_teardown ( +struct xfs_scrub_context *sc, +int error) +{ +if (sc->tp) { +xfs_trans_cancel (sc->tp); + sc->tp =NULL ; + } +return error; +} + +/* Scrubbing dispatch. */ + +static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { +}; + +/* This isn't a stable feature, warn once per day. */ +static inline void +xfs_scrub_experimental_warning ( +struct xfs_mount *mp) +{ +static struct ratelimit_state scrub_warning =RATELIMIT_STATE_INIT ( +"xfs_scrub_warning" ,86400 *HZ ,1 ); +ratelimit_set_flags (&scrub_warning,RATELIMIT_MSG_ON_RELEASE ); + +if (__ratelimit (&scrub_warning)) +xfs_alert (mp, +"EXPERIMENTAL online scrub feature in use. Use at your own risk!" ); +} +/* Dispatch metadata scrubbing. */ int xfs_scrub_metadata (struct xfs_inode *ip,struct xfs_scrub_metadata *sm) { -return -EOPNOTSUPP ; +struct xfs_scrub_context sc; +struct xfs_mount *mp = ip->i_mount; +const struct xfs_scrub_meta_ops *ops; +bool try_harder = false; +int error =0 ; + +trace_xfs_scrub_start (ip, sm, error); + +/* Forbidden if we are shut down or mounted norecovery. */ + error = -ESHUTDOWN ; +if (XFS_FORCED_SHUTDOWN (mp)) + goto out; + error = -ENOTRECOVERABLE ; +if (mp->m_flags &XFS_MOUNT_NORECOVERY ) + goto out; + +/* Check our inputs. */ + error = -EINVAL ; + sm->sm_flags &= ~XFS_SCRUB_FLAGS_OUT ; +if (sm->sm_flags & ~XFS_SCRUB_FLAGS_IN ) + goto out; +if (memchr_inv (sm->sm_reserved,0 ,sizeof (sm->sm_reserved))) + goto out; + +/* Do we know about this type of metadata? */ + error = -ENOENT ; +if (sm->sm_type >=XFS_SCRUB_TYPE_NR ) + goto out; + ops = &meta_scrub_ops[sm->sm_type]; +if (ops->scrub ==NULL ) + goto out; + +/* +* We won't scrub any filesystem that doesn't have the ability +* to record unwritten extents. The option was made default in +* 2003, removed from mkfs in 2007, and cannot be disabled in +* v5, so if we find a filesystem without this flag it's either +* really old or totally unsupported. Avoid it either way. +* We also don't support v1-v3 filesystems, which aren't +* mountable. +*/ + error = -EOPNOTSUPP ; +if (!xfs_sb_version_hasextflgbit (&mp->m_sb)) + goto out; + +/* Does this fs even support this type of metadata? */ + error = -ENOENT ; +if (ops->has && !ops->has (&mp->m_sb)) + goto out; + +/* We don't know how to repair anything yet. */ + error = -EOPNOTSUPP ; +if (sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR ) + goto out; + +xfs_scrub_experimental_warning (mp); + +retry_op: +/* Set up for the operation. */ +memset (&sc,0 ,sizeof (sc)); + sc.mp = ip->i_mount; + sc.sm = sm; + sc.ops = ops; + sc.try_harder = try_harder; + error = sc.ops->setup (&sc, ip); +if (error) + goto out_teardown; + +/* Scrub for errors. */ + error = sc.ops->scrub (&sc); +if (!try_harder && error == -EDEADLOCK ) { +/* +* Scrubbers return -EDEADLOCK to mean 'try harder'. +* Tear down everything we hold, then set up again with +* preparation for worst-case scenarios. +*/ + error =xfs_scrub_teardown (&sc,0 ); +if (error) + goto out; + try_harder = true; + goto retry_op; + }else if (error) + goto out_teardown; + +if (sc.sm->sm_flags & (XFS_SCRUB_OFLAG_CORRUPT | +XFS_SCRUB_OFLAG_XCORRUPT )) +xfs_alert_ratelimited (mp,"Corruption detected during scrub." ); + +out_teardown: + error =xfs_scrub_teardown (&sc, error); +out: +trace_xfs_scrub_done (ip, sm, error); +return error; }
fs/xfs/scrub/scrub.h +24 -0
@@ -20,6 +20,30 @@#ifndef __XFS_SCRUB_SCRUB_H__#define __XFS_SCRUB_SCRUB_H__ +struct xfs_scrub_context ; + +struct xfs_scrub_meta_ops { +/* Acquire whatever resources are needed for the operation. */ +int (*setup)(struct xfs_scrub_context *, +struct xfs_inode *); + +/* Examine metadata for errors. */ +int (*scrub)(struct xfs_scrub_context *); + +/* Decide if we even have this piece of metadata. */ +bool (*has)(struct xfs_sb *); +}; + +struct xfs_scrub_context { +/* General scrub state. */ +struct xfs_mount *mp; +struct xfs_scrub_metadata *sm; +const struct xfs_scrub_meta_ops *ops; +struct xfs_trans *tp; +struct xfs_inode *ip; +bool try_harder; +}; +/* Metadata scrubbers */ #endif /* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.h +43 -0
49 lines not on this page · raw message
Re: [PATCH 07/30] xfs: dispatch metadata scrub subcommands
Quoted 6 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create structures needed to hold scrubbing context and dispatch incoming commands to the individual scrubbers.
Signed-off-by Darrick J. Wong <[email protected]>
Looks good.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 8/30 xfs: probe the scrub ioctl
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create a probe scrubber with id 0. This will be used by xfs_scrub to probe the kernel's abilities to scrub (and repair) the metadata.
--- fs/xfs/Makefile | 1 + fs/xfs/libxfs/xfs_fs.h | 3 ++ fs/xfs/scrub/common.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/common.h | 39 ++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 19 +++++++++++++++ fs/xfs/scrub/scrub.h | 1 + fs/xfs/scrub/trace.c | 1 + 7 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/common.c create mode 100644 fs/xfs/scrub/common.h
fs/xfs/Makefile +1 -0
@@ -146,6 +146,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y) xfs-y += $(addprefix scrub/, \ trace.o \ + common.o \ scrub.o \ ) endif
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -483,9 +483,10 @@ struct xfs_scrub_metadata {*/ /* Scrub subcommands. */ +#define XFS_SCRUB_TYPE_PROBE 0/* presence test ioctl */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 0 +#define XFS_SCRUB_TYPE_NR 1/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.c new file +59 -0
59 lines not on this page · raw message
fs/xfs/scrub/common.h new file +39 -0
@@ -0,0 +1,39 @@ +/* +* Copyright (C) 2017 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. +*/ +#ifndef __XFS_SCRUB_COMMON_H__ +#define __XFS_SCRUB_COMMON_H__ + +/* +* Grab an empty transaction so that we can re-grab locked buffers if +* one of our btrees turns out to be cyclic. +*/ +static inline int +xfs_scrub_trans_alloc ( +struct xfs_scrub_metadata *sm, +struct xfs_mount *mp, +struct xfs_trans **tpp) +{ +return xfs_trans_alloc_empty (mp, tpp); +} + +/* Setup functions */ +int xfs_scrub_setup_fs (struct xfs_scrub_context *sc,struct xfs_inode *ip); + +#endif /* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/scrub.c +19 -0
@@ -42,6 +42,7 @@#include "xfs_rmap_btree.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" +#include "scrub/common.h" #include "scrub/trace.h" /*
@@ -108,6 +109,20 @@* will be set. */ +/* +* Scrub probe -- userspace uses this to probe if we're willing to +* scrub or repair a given mountpoint. +*/ +int +xfs_scrub_probe ( +struct xfs_scrub_context *sc) +{ +if (sc->sm->sm_ino || sc->sm->sm_agno) +return -EINVAL ; + +return 0 ; +} +/* Scrub setup and teardown */ /* Free all the resources and finish the transactions. */
@@ -126,6 +141,10 @@ xfs_scrub_teardown(/* Scrubbing dispatch. */ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { + {/* ioctl presence test */ + .setup = xfs_scrub_setup_fs, + .scrub = xfs_scrub_probe, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +1 -0
@@ -45,5 +45,6 @@ struct xfs_scrub_context { };/* Metadata scrubbers */ +int xfs_scrub_tester (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/trace.c +1 -0
@@ -32,6 +32,7 @@#include "xfs_trans.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" +#include "scrub/common.h" /* * We include this last to have the helpers above available for the trace
Re: [PATCH 08/30] xfs: probe the scrub ioctl
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create a probe scrubber with id 0. This will be used by
xfs_scrubto probe the kernel's abilities to scrub (and repair) the metadata.
This no longer returns anything to userspace it indicate capabilities. I can see that the previous patch checks for valid/invalid input flags, so we have unknown feature checking in place, just not obviously through the probe function implementation. Can you expand this comment a little to explain where the supported feature checks occur and so all that is required here is a stub that does nothing?
Otherwise, consider it:
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 08/30] xfs: probe the scrub ioctl
Quoted 6 lines · Dave Chinner
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create a probe scrubber with id 0. This will be used by
xfs_scrubto probe the kernel's abilities to scrub (and repair) the metadata.
Quoted 7 lines
This no longer returns anything to userspace it indicate capabilities. I can see that the previous patch checks for valid/invalid input flags, so we have unknown feature checking in place, just not obviously through the probe function implementation. Can you expand this comment a little to explain where the supported feature checks occur and so all that is required here is a stub that does nothing?
Ok. I propose:
"Create a probe scrubber with id 0. This will be used by xfs_scrub to probe the kernel's abilities to scrub (and repair) the metadata. We do this by validating the ioctl inputs from userspace, preparing the filesystem for a scrub (or a repair) operation, and immediately returning to userspace. Userspace can use the returned errno and structure state to decide (in broad terms) if scrub/repair are supported by the running kernel."
--D
Quoted 12 lines
Otherwise, consider it:
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 08/30] xfs: probe the scrub ioctl
Quoted 17 lines · Darrick J. Wong
Quoted 6 lines · Dave Chinner
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create a probe scrubber with id 0. This will be used by
xfs_scrubto probe the kernel's abilities to scrub (and repair) the metadata.Quoted 7 lines
This no longer returns anything to userspace it indicate capabilities. I can see that the previous patch checks for valid/invalid input flags, so we have unknown feature checking in place, just not obviously through the probe function implementation. Can you expand this comment a little to explain where the supported feature checks occur and so all that is required here is a stub that does nothing?
Ok. I propose:
Quoted 7 lines
"Create a probe scrubber with id 0. This will be used by
xfs_scrubto probe the kernel's abilities to scrub (and repair) the metadata. We do this by validating the ioctl inputs from userspace, preparing the filesystem for a scrub (or a repair) operation, and immediately returning to userspace. Userspace can use the returned errno and structure state to decide (in broad terms) if scrub/repair are supported by the running kernel."
Yup, that works for me. :P
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 9/30 xfs: create helpers to record and deal with scrub problems
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create helper functions to record crc and corruption problems, and deal with any other runtime errors that arise.
--- fs/xfs/scrub/common.c | 190 +++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/common.h | 23 +++++ fs/xfs/scrub/trace.h | 215 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 428 insertions(+)
fs/xfs/scrub/common.c +190 -0
@@ -47,6 +47,196 @@/* Common code for the metadata scrubbers. */ +/* +* Handling operational errors. +* +* The *_process_error() family of functions are used to process error return +* codes from functions called as part of a scrub operation. +* +* If there's no error, we return true to tell the caller that it's ok +* to move on to the next check in its list. +* +* For non-verifier errors (e.g. ENOMEM) we return false to tell the +* caller that something bad happened, and we preserve *error so that +* the caller can return the *error up the stack to userspace. +* +* Verifier errors (EFSBADCRC/EFSCORRUPTED) are recorded by setting +* OFLAG_CORRUPT in sm_flags and the *error is cleared. In other words, +* we track verifier errors (and failed scrub checks) via OFLAG_CORRUPT, +* not via return codes. We return false to tell the caller that +* something bad happened. Since the error has been cleared, the caller +* will (presumably) return that zero and scrubbing will move on to +* whatever's next. +* +* ftrace can be used to record the precise metadata location and the +* approximate code location of the failed operation. +*/ + +/* Check for operational errors. */ +bool +xfs_scrub_process_error ( +struct xfs_scrub_context *sc, +xfs_agnumber_t agno, +xfs_agblock_t bno, +int *error) +{ +switch (*error) { +case 0 : +return true; +case -EDEADLOCK : +/* Used to restart an op with deadlock avoidance. */ +trace_xfs_scrub_deadlock_retry (sc->ip, sc->sm, *error); +break ; +case -EFSBADCRC : +case -EFSCORRUPTED : +/* Note the badness but don't abort. */ + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_CORRUPT ; + *error =0 ; +/* fall through */ +default : +trace_xfs_scrub_op_error (sc, agno, bno, *error, + __return_address); +break ; + } +return false; +} + +/* Check for operational errors for a file offset. */ +bool +xfs_scrub_fblock_process_error ( +struct xfs_scrub_context *sc, +int whichfork, +xfs_fileoff_t offset, +int *error) +{ +switch (*error) { +case 0 : +return true; +case -EDEADLOCK : +/* Used to restart an op with deadlock avoidance. */ +trace_xfs_scrub_deadlock_retry (sc->ip, sc->sm, *error); +break ; +case -EFSBADCRC : +case -EFSCORRUPTED : +/* Note the badness but don't abort. */ + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_CORRUPT ; + *error =0 ; +/* fall through */ +default : +trace_xfs_scrub_file_op_error (sc, whichfork, offset, *error, + __return_address); +break ; + } +return false; +} + +/* +* Handling scrub corruption/optimization/warning checks. +* +* The *_set_{corrupt,preen,warning}() family of functions are used to +* record the presence of metadata that is incorrect (corrupt), could be +* optimized somehow (preen), or should be flagged for administrative +* review but is not incorrect (warn). +* +* ftrace can be used to record the precise metadata location and +* approximate code location of the failed check. +*/ + +/* Record a block which could be optimized. */ +void +xfs_scrub_block_set_preen ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp) +{ + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_PREEN ; +trace_xfs_scrub_block_preen (sc, bp->b_bn, __return_address); +} + +/* +* Record an inode which could be optimized. The trace data will +* include the block given by bp if bp is given; otherwise it will use +* the block location of the inode record itself. +*/ +void +xfs_scrub_ino_set_preen ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp) +{ + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_PREEN ; +trace_xfs_scrub_ino_preen (sc, sc->ip->i_ino, bp ? bp->b_bn :0 , + __return_address); +} + +/* Record a corrupt block. */ +void +xfs_scrub_block_set_corrupt ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp) +{ + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_CORRUPT ; +trace_xfs_scrub_block_error (sc, bp->b_bn, __return_address); +} + +/* +* Record a corrupt inode. The trace data will include the block given +* by bp if bp is given; otherwise it will use the block location of the +* inode record itself. +*/ +void +xfs_scrub_ino_set_corrupt ( +struct xfs_scrub_context *sc, +xfs_ino_t ino, +struct xfs_buf *bp) +{ + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_CORRUPT ; +trace_xfs_scrub_ino_error (sc, ino, bp ? bp->b_bn :0 , __return_address); +} + +/* Record corruption in a block indexed by a file fork. */ +void +xfs_scrub_fblock_set_corrupt ( +struct xfs_scrub_context *sc, +int whichfork, +xfs_fileoff_t offset) +{ + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_CORRUPT ; +trace_xfs_scrub_fblock_error (sc, whichfork, offset, __return_address); +} + +/* +* Warn about inodes that need administrative review but is not +* incorrect. +*/ +void +xfs_scrub_ino_set_warning ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp) +{ + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_WARNING ; +trace_xfs_scrub_ino_warning (sc, sc->ip->i_ino, bp ? bp->b_bn :0 , + __return_address); +} + +/* Warn about a block indexed by a file fork that needs review. */ +void +xfs_scrub_fblock_set_warning ( +struct xfs_scrub_context *sc, +int whichfork, +xfs_fileoff_t offset) +{ + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_WARNING ; +trace_xfs_scrub_fblock_warning (sc, whichfork, offset, __return_address); +} + +/* Signal an incomplete scrub. */ +void +xfs_scrub_set_incomplete ( +struct xfs_scrub_context *sc) +{ + sc->sm->sm_flags |=XFS_SCRUB_OFLAG_INCOMPLETE ; +trace_xfs_scrub_incomplete (sc, __return_address); +} +/* Per-scrubber setup functions */ /* Set us up with a transaction and an empty context. */
fs/xfs/scrub/common.h +23 -0
@@ -33,6 +33,29 @@ xfs_scrub_trans_alloc(return xfs_trans_alloc_empty (mp, tpp); } +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, +xfs_fileoff_t offset,int *error); + +void xfs_scrub_block_set_preen (struct xfs_scrub_context *sc, +struct xfs_buf *bp); +void xfs_scrub_ino_set_preen (struct xfs_scrub_context *sc,struct xfs_buf *bp); + +void xfs_scrub_block_set_corrupt (struct xfs_scrub_context *sc, +struct xfs_buf *bp); +void xfs_scrub_ino_set_corrupt (struct xfs_scrub_context *sc,xfs_ino_t ino, +struct xfs_buf *bp); +void xfs_scrub_fblock_set_corrupt (struct xfs_scrub_context *sc,int whichfork, +xfs_fileoff_t offset); + +void xfs_scrub_ino_set_warning (struct xfs_scrub_context *sc, +struct xfs_buf *bp); +void xfs_scrub_fblock_set_warning (struct xfs_scrub_context *sc,int whichfork, +xfs_fileoff_t offset); + +void xfs_scrub_set_incomplete (struct xfs_scrub_context *sc); +/* Setup functions */ int xfs_scrub_setup_fs (struct xfs_scrub_context *sc,struct xfs_inode *ip);
fs/xfs/scrub/trace.h +215 -0
227 lines not on this page · raw message
Re: [PATCH 09/30] xfs: create helpers to record and deal with scrub problems
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create helper functions to record crc and corruption problems, and deal with any other runtime errors that arise.
xfs_scrub_process_error() makes much more sense :)
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 10/30 xfs: create helpers to scrub a metadata btree
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create helper functions and tracepoints to deal with errors while scrubbing a metadata btree.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/btree.c | 114 +++++++++++++++++++++++++++++++++++ fs/xfs/scrub/btree.h | 57 +++++++++++++++++ fs/xfs/scrub/trace.c | 17 +++++ fs/xfs/scrub/trace.h | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 352 insertions(+) create mode 100644 fs/xfs/scrub/btree.c create mode 100644 fs/xfs/scrub/btree.h
fs/xfs/Makefile +1 -0
@@ -146,6 +146,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y) xfs-y += $(addprefix scrub/, \ trace.o \ + btree.o \ common.o \ scrub.o \ )
fs/xfs/scrub/btree.c new file +114 -0
114 lines not on this page · raw message
fs/xfs/scrub/btree.h new file +57 -0
57 lines not on this page · raw message
fs/xfs/scrub/trace.c +17 -0
@@ -30,10 +30,27 @@#include "xfs_inode.h" #include "xfs_btree.h" #include "xfs_trans.h" +#include "xfs_bit.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h" +/* Figure out which block the btree cursor was pointing to. */ +static inline xfs_fsblock_t +xfs_scrub_btree_cur_fsbno ( +struct xfs_btree_cur *cur, +int level) +{ +if (level < cur->bc_nlevels && cur->bc_bufs[level]) +return XFS_DADDR_TO_FSB (cur->bc_mp, cur->bc_bufs[level]->b_bn); +else if (level == cur->bc_nlevels -1 && + cur->bc_flags &XFS_BTREE_LONG_PTRS ) +return XFS_INO_TO_FSB (cur->bc_mp, cur->bc_private.b.ip->i_ino); +else if (!(cur->bc_flags &XFS_BTREE_LONG_PTRS )) +return XFS_AGB_TO_FSB (cur->bc_mp, cur->bc_private.a.agno,0 ); +return NULLFSBLOCK ; +} +/* * We include this last to have the helpers above available for the trace * event implementations.
fs/xfs/scrub/trace.h +163 -0
169 lines not on this page · raw message
Re: [PATCH 10/30] xfs: create helpers to scrub a metadata btree
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create helper functions and tracepoints to deal with errors while scrubbing a metadata btree.
looks good.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 11/30 xfs: scrub the shape of a metadata btree
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create a function that can check the shape of a btree -- each block passes basic inspection and all the pointers look ok. In the next patch we'll add the ability to check the actual keys and records stored within the btree. Add some helper functions so that we report detailed scrub errors in a uniform manner in dmesg. These are helper functions for subsequent patches.
--- fs/xfs/libxfs/xfs_btree.c | 16 +++ fs/xfs/libxfs/xfs_btree.h | 7 + fs/xfs/scrub/btree.c | 249 ++++++++++++++++++++++++++++++++++++++++++++- fs/xfs/scrub/common.h | 18 +++ 4 files changed, 283 insertions(+), 7 deletions(-)
fs/xfs/libxfs/xfs_btree.c +14 -2
@@ -1051,7 +1051,7 @@ xfs_btree_setbuf( } } -STATIC int +bool xfs_btree_ptr_is_null (struct xfs_btree_cur *cur,union xfs_btree_ptr *ptr)
@@ -1076,7 +1076,7 @@ xfs_btree_set_ptr_null(/* * Get/set/init sibling pointers */ -STATIC void +void xfs_btree_get_sibling( struct xfs_btree_cur *cur, struct xfs_btree_block *block,
@@ -4938,3 +4938,15 @@ xfs_btree_count_blocks(return xfs_btree_visit_blocks (cur, xfs_btree_count_blocks_helper, blocks); } + +/* Compare two btree pointers. */ +int64_t +xfs_btree_diff_two_ptrs ( +struct xfs_btree_cur *cur, +const union xfs_btree_ptr *a, +const union xfs_btree_ptr *b) +{ +if (cur->bc_flags &XFS_BTREE_LONG_PTRS ) +return (int64_t )be64_to_cpu (a->l) -be64_to_cpu (b->l); +return (int64_t )be32_to_cpu (a->s) -be32_to_cpu (b->s); +}
fs/xfs/libxfs/xfs_btree.h +7 -0
@@ -531,5 +531,12 @@ int xfs_btree_lookup_get_block(struct xfs_btree_cur *cur, int level, union xfs_btree_ptr *pp, struct xfs_btree_block **blkp);struct xfs_btree_block *xfs_btree_get_block (struct xfs_btree_cur *cur,int level,struct xfs_buf **bpp); +bool xfs_btree_ptr_is_null (struct xfs_btree_cur *cur,union xfs_btree_ptr *ptr); +int64_t xfs_btree_diff_two_ptrs (struct xfs_btree_cur *cur, +const union xfs_btree_ptr *a, +const union xfs_btree_ptr *b); +void xfs_btree_get_sibling (struct xfs_btree_cur *cur, +struct xfs_btree_block *block, +union xfs_btree_ptr *ptr,int lr);#endif /* __XFS_BTREE_H__ */
fs/xfs/scrub/btree.c +244 -5
263 lines not on this page · raw message
fs/xfs/scrub/common.h +18 -0
@@ -21,6 +21,24 @@#define __XFS_SCRUB_COMMON_H__/* +* We /could/ terminate a scrub/repair operation early. If we're not +* in a good place to continue (fatal signal, etc.) then bail out. +* Note that we're careful not to make any judgements about *error. +*/ +static inline bool +xfs_scrub_should_terminate ( +struct xfs_scrub_context *sc, +int *error) +{ +if (fatal_signal_pending (current)) { +if (*error ==0 ) + *error = -EAGAIN ; +return true; + } +return false; +} + +/* * Grab an empty transaction so that we can re-grab locked buffers if * one of our btrees turns out to be cyclic. */
Re: [PATCH 11/30] xfs: scrub the shape of a metadata btree
Quoted 10 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create a function that can check the shape of a btree -- each block passes basic inspection and all the pointers look ok. In the next patch we'll add the ability to check the actual keys and records stored within the btree. Add some helper functions so that we report detailed scrub errors in a uniform manner in dmesg. These are helper functions for subsequent patches.
Signed-off-by Darrick J. Wong <[email protected]>
Minor thing:
Quoted 17 lines
/*
+ * Check a btree pointer. Returns true if it's ok to use this pointer.
+ * Callers do not need to set the corrupt flag.
+ */fs/xfs/scrub/btree.c +13
-0+static bool +xfs_scrub_btree_ptr_ok ( +struct xfs_scrub_btree *bs, +int level, +union xfs_btree_ptr *ptr) +{ +bool res; + +/* A btree rooted in an inode has no block pointer to the root. */ +if ((bs->cur->bc_flags &XFS_BTREE_ROOT_IN_INODE ) && + level == bs->cur->bc_nlevels) +return true; +
fs/xfs/scrub/btree.c xfs_scrub_btree_set_corrupt( · line 121
Quoted 10 lines
fs/xfs/scrub/btree.c +10
-0+/* Otherwise, check the pointers. */ +if (bs->cur->bc_flags &XFS_BTREE_LONG_PTRS ) { + res =xfs_btree_check_lptr (bs->cur,be64_to_cpu (ptr->l), level); +if (!res) +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur, level); + }else { + res =xfs_btree_check_sptr (bs->cur,be32_to_cpu (ptr->s), level); +if (!res) +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur, level); + }
We should already know what type of btree we are scrubbing, so I think this can be simplified to a single xfs_scrub_btree_set_corrupt() tracepoint.
Quoted 22 lines
fs/xfs/scrub/btree.c +22
-0+STATIC int +xfs_scrub_btree_get_block ( +struct xfs_scrub_btree *bs, +int level, +union xfs_btree_ptr *pp, +struct xfs_btree_block **pblock, +struct xfs_buf **pbp) +{ +void *failed_at; +int error; + + error =xfs_btree_lookup_get_block (bs->cur, level, pp, pblock); +if (!xfs_scrub_btree_process_error (bs->sc, bs->cur, level, &error) || + !pblock) +return error; + +xfs_btree_get_block (bs->cur, level, pbp); +if (bs->cur->bc_flags &XFS_BTREE_LONG_PTRS ) { + failed_at =__xfs_btree_check_lblock (bs->cur, *pblock, + level, *pbp); +if (failed_at) { +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur, level);
fs/xfs/scrub/btree.c xfs_scrub_btree_set_corrupt( · line 247
Quoted 10 lines
fs/xfs/scrub/btree.c +10
-0+return 0 ; + } + }else { + failed_at =__xfs_btree_check_sblock (bs->cur, *pblock, + level, *pbp); +if (failed_at) { +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur, level); +return 0 ; + } + }
And same here.
Quoted 14 lines
fs/xfs/scrub/common.h +6
-0@@ -21,6 +21,24 @@#define __XFS_SCRUB_COMMON_H__/* +* We /could/ terminate a scrub/repair operation early. If we're not +* in a good place to continue (fatal signal, etc.) then bail out. +* Note that we're careful not to make any judgements about *error. +*/ +static inline bool +xfs_scrub_should_terminate(
fs/xfs/scrub/common.h line 39
Quoted 10 lines
fs/xfs/scrub/common.h +10
-0+ struct xfs_scrub_context *sc, + int *error) +{ + if (fatal_signal_pending(current)) { +if (*error ==0 ) + *error = -EAGAIN ; +return true; + } +return false; +}
Probably should move that to the original scrub infrastructure patch.
Otherwise looks fine.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 11/30] xfs: scrub the shape of a metadata btree
Quoted 37 lines · Dave Chinner
Quoted 10 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create a function that can check the shape of a btree -- each block passes basic inspection and all the pointers look ok. In the next patch we'll add the ability to check the actual keys and records stored within the btree. Add some helper functions so that we report detailed scrub errors in a uniform manner in dmesg. These are helper functions for subsequent patches.
Signed-off-by Darrick J. Wong <[email protected]>Minor thing:
Quoted 23 lines
/*
+ * Check a btree pointer. Returns true if it's ok to use this pointer.
+ * Callers do not need to set the corrupt flag.
+ */fs/xfs/scrub/btree.c +19
-0+static bool +xfs_scrub_btree_ptr_ok ( +struct xfs_scrub_btree *bs, +int level, +union xfs_btree_ptr *ptr) +{ +bool res; + +/* A btree rooted in an inode has no block pointer to the root. */ +if ((bs->cur->bc_flags &XFS_BTREE_ROOT_IN_INODE ) && + level == bs->cur->bc_nlevels) +return true; + +/* Otherwise, check the pointers. */ +if (bs->cur->bc_flags &XFS_BTREE_LONG_PTRS ) { + res =xfs_btree_check_lptr (bs->cur,be64_to_cpu (ptr->l), level); +if (!res) +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur, level); + }else {
Quoted 8 lines
fs/xfs/scrub/btree.c xfs_scrub_btree_set_corrupt( · line 121
Quoted 4 lines
fs/xfs/scrub/btree.c +4
-0+ res =xfs_btree_check_sptr (bs->cur,be32_to_cpu (ptr->s), level); +if (!res) +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur, level); + }We should already know what type of btree we are scrubbing, so I think this can be simplified to a single
xfs_scrub_btree_set_corrupt()tracepoint.
Ok to both.
Quoted 54 lines
Quoted 22 lines
fs/xfs/scrub/btree.c +22
-0+STATIC int +xfs_scrub_btree_get_block ( +struct xfs_scrub_btree *bs, +int level, +union xfs_btree_ptr *pp, +struct xfs_btree_block **pblock, +struct xfs_buf **pbp) +{ +void *failed_at; +int error; + + error =xfs_btree_lookup_get_block (bs->cur, level, pp, pblock); +if (!xfs_scrub_btree_process_error (bs->sc, bs->cur, level, &error) || + !pblock) +return error; + +xfs_btree_get_block (bs->cur, level, pbp); +if (bs->cur->bc_flags &XFS_BTREE_LONG_PTRS ) { + failed_at =__xfs_btree_check_lblock (bs->cur, *pblock, + level, *pbp); +if (failed_at) { +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur, level);fs/xfs/scrub/btree.c xfs_scrub_btree_set_corrupt( · line 247
Quoted 10 lines
fs/xfs/scrub/btree.c +10
-0+return 0 ; + } + }else { + failed_at =__xfs_btree_check_sblock (bs->cur, *pblock, + level, *pbp); +if (failed_at) { +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur, level); +return 0 ; + } + }And same here.
Quoted 19 lines
fs/xfs/scrub/common.h +11
-0@@ -21,6 +21,24 @@#define __XFS_SCRUB_COMMON_H__/* +* We /could/ terminate a scrub/repair operation early. If we're not +* in a good place to continue (fatal signal, etc.) then bail out. +* Note that we're careful not to make any judgements about *error. +*/ +static inline bool +xfs_scrub_should_terminate ( +struct xfs_scrub_context *sc, +int *error) +{ +if (fatal_signal_pending (current)) { +if (*error ==0 )
Quoted 8 lines
fs/xfs/scrub/common.h line 39
Quoted 5 lines
fs/xfs/scrub/common.h +5
-0+ *error = -EAGAIN ; +return true; + } +return false; +}Probably should move that to the original scrub infrastructure patch.
Will do. It's in this patch because (iirc) this is the first time anything uses it.
--D
Quoted 12 lines
Otherwise looks fine.
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 v12 12/30 xfs: scrub btree keys and records
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add to the btree scrubber the ability to check that the keys and records are in the right order and actually call out to our record iterator to do actual checking of the records.
--- fs/xfs/scrub/btree.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/trace.h | 45 ++++++++++++++++++++ 2 files changed, 154 insertions(+), 1 deletion(-)
fs/xfs/scrub/btree.c +109 -1
@@ -93,6 +93,101 @@ xfs_scrub_btree_set_corrupt( }/* +* Make sure this record is in order and doesn't stray outside of the parent +* keys. +*/ +STATIC void +xfs_scrub_btree_rec ( +struct xfs_scrub_btree *bs) +{ +struct xfs_btree_cur *cur = bs->cur; +union xfs_btree_rec *rec; +union xfs_btree_key key; +union xfs_btree_key hkey; +union xfs_btree_key *keyp; +struct xfs_btree_block *block; +struct xfs_btree_block *keyblock; +struct xfs_buf *bp; + + block =xfs_btree_get_block (cur,0 , &bp); + rec =xfs_btree_rec_addr (cur, cur->bc_ptrs[0 ], block); + +trace_xfs_scrub_btree_rec (bs->sc, cur,0 ); + +/* If this isn't the first record, are they in order? */ +if (!bs->firstrec && !cur->bc_ops->recs_inorder (cur, &bs->lastrec, rec)) +xfs_scrub_btree_set_corrupt (bs->sc, cur,0 ); + bs->firstrec = false; +memcpy (&bs->lastrec, rec, cur->bc_ops->rec_len); + +if (cur->bc_nlevels ==1 ) +return ; + +/* Is this at least as large as the parent low key? */ + cur->bc_ops->init_key_from_rec (&key, rec); + keyblock =xfs_btree_get_block (cur,1 , &bp); + keyp =xfs_btree_key_addr (cur, cur->bc_ptrs[1 ], keyblock); +if (cur->bc_ops->diff_two_keys (cur, &key, keyp) <0 ) +xfs_scrub_btree_set_corrupt (bs->sc, cur,1 ); + +if (!(cur->bc_flags &XFS_BTREE_OVERLAPPING )) +return ; + +/* Is this no larger than the parent high key? */ + cur->bc_ops->init_high_key_from_rec (&hkey, rec); + keyp =xfs_btree_high_key_addr (cur, cur->bc_ptrs[1 ], keyblock); +if (cur->bc_ops->diff_two_keys (cur, keyp, &hkey) <0 ) +xfs_scrub_btree_set_corrupt (bs->sc, cur,1 ); +} + +/* +* Make sure this key is in order and doesn't stray outside of the parent +* keys. +*/ +STATIC void +xfs_scrub_btree_key ( +struct xfs_scrub_btree *bs, +int level) +{ +struct xfs_btree_cur *cur = bs->cur; +union xfs_btree_key *key; +union xfs_btree_key *keyp; +struct xfs_btree_block *block; +struct xfs_btree_block *keyblock; +struct xfs_buf *bp; + + block =xfs_btree_get_block (cur, level, &bp); + key =xfs_btree_key_addr (cur, cur->bc_ptrs[level], block); + +trace_xfs_scrub_btree_key (bs->sc, cur, level); + +/* If this isn't the first key, are they in order? */ +if (!bs->firstkey[level] && + !cur->bc_ops->keys_inorder (cur, &bs->lastkey[level], key)) +xfs_scrub_btree_set_corrupt (bs->sc, cur, level); + bs->firstkey[level] = false; +memcpy (&bs->lastkey[level], key, cur->bc_ops->key_len); + +if (level +1 >= cur->bc_nlevels) +return ; + +/* Is this at least as large as the parent low key? */ + keyblock =xfs_btree_get_block (cur, level +1 , &bp); + keyp =xfs_btree_key_addr (cur, cur->bc_ptrs[level +1 ], keyblock); +if (cur->bc_ops->diff_two_keys (cur, key, keyp) <0 ) +xfs_scrub_btree_set_corrupt (bs->sc, cur, level); + +if (!(cur->bc_flags &XFS_BTREE_OVERLAPPING )) +return ; + +/* Is this no larger than the parent high key? */ + key =xfs_btree_high_key_addr (cur, cur->bc_ptrs[level], block); + keyp =xfs_btree_high_key_addr (cur, cur->bc_ptrs[level +1 ], keyblock); +if (cur->bc_ops->diff_two_keys (cur, keyp, key) <0 ) +xfs_scrub_btree_set_corrupt (bs->sc, cur, level); +} + +/* * Check a btree pointer. Returns true if it's ok to use this pointer. * Callers do not need to set the corrupt flag. */
@@ -269,6 +364,7 @@ xfs_scrub_btree(struct xfs_scrub_btree bs = {0 };union xfs_btree_ptr ptr;union xfs_btree_ptr *pp; +union xfs_btree_rec *recp;struct xfs_btree_block *block;int level;struct xfs_buf *bp;
@@ -319,7 +415,16 @@ xfs_scrub_btree(continue ; } -if (xfs_scrub_should_terminate (sc, &error)) +/* Records in order for scrub? */ +xfs_scrub_btree_rec (&bs); + +/* Call out to the record checker. */ + recp =xfs_btree_rec_addr (cur, cur->bc_ptrs[0 ], block); + error = bs.scrub_rec (&bs, recp); +if (error) +break ; +if (xfs_scrub_should_terminate (sc, &error) || + (sc->sm->sm_flags &XFS_SCRUB_OFLAG_CORRUPT ))break ; cur->bc_ptrs[level]++;
@@ -334,6 +439,9 @@ xfs_scrub_btree(continue ; } +/* Keys in order for scrub? */ +xfs_scrub_btree_key (&bs, level); +/* Drill another level deeper. */ pp =xfs_btree_ptr_addr (cur, cur->bc_ptrs[level], block);if (!xfs_scrub_btree_ptr_ok (&bs, level, pp)) {
fs/xfs/scrub/trace.h +45 -0
51 lines not on this page · raw message
Re: [PATCH 12/30] xfs: scrub btree keys and records
Quoted 7 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add to the btree scrubber the ability to check that the keys and records are in the right order and actually call out to our record iterator to do actual checking of the records.
Signed-off-by Darrick J. Wong <[email protected]>
Looks fine.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 13/30 xfs: create helpers to scan an allocation group
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add some helpers to enable us to lock an AG's headers, create btree cursors for all btrees in that allocation group, and clean up afterwards.
--- fs/xfs/scrub/common.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/common.h | 10 +++ fs/xfs/scrub/scrub.c | 4 + fs/xfs/scrub/scrub.h | 21 ++++++ 4 files changed, 214 insertions(+)
fs/xfs/scrub/common.c +179 -0
@@ -44,6 +44,7 @@#include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" +#include "scrub/btree.h" /* Common code for the metadata scrubbers. */
@@ -237,6 +238,184 @@ xfs_scrub_set_incomplete(trace_xfs_scrub_incomplete (sc, __return_address); } +/* +* AG scrubbing +* +* These helpers facilitate locking an allocation group's header +* buffers, setting up cursors for all btrees that are present, and +* cleaning everything up once we're through. +*/ + +/* +* Grab all the headers for an AG. +* +* The headers should be released by xfs_scrub_ag_free, but as a fail +* safe we attach all the buffers we grab to the scrub transaction so +* they'll all be freed when we cancel it. +*/ +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) +{ +struct xfs_mount *mp = sc->mp; +int error; + + error =xfs_ialloc_read_agi (mp, sc->tp, agno, agi); +if (error) + goto out; + + error =xfs_alloc_read_agf (mp, sc->tp, agno,0 , agf); +if (error) + goto out; +if (!*agf) { + error = -ENOMEM ; + goto out; + } + + error =xfs_alloc_read_agfl (mp, sc->tp, agno, agfl); +if (error) + goto out; + +out: +return error; +} + +/* Release all the AG btree cursors. */ +void +xfs_scrub_ag_btcur_free ( +struct xfs_scrub_ag *sa) +{ +if (sa->refc_cur) +xfs_btree_del_cursor (sa->refc_cur,XFS_BTREE_ERROR ); +if (sa->rmap_cur) +xfs_btree_del_cursor (sa->rmap_cur,XFS_BTREE_ERROR ); +if (sa->fino_cur) +xfs_btree_del_cursor (sa->fino_cur,XFS_BTREE_ERROR ); +if (sa->ino_cur) +xfs_btree_del_cursor (sa->ino_cur,XFS_BTREE_ERROR ); +if (sa->cnt_cur) +xfs_btree_del_cursor (sa->cnt_cur,XFS_BTREE_ERROR ); +if (sa->bno_cur) +xfs_btree_del_cursor (sa->bno_cur,XFS_BTREE_ERROR ); + + sa->refc_cur =NULL ; + sa->rmap_cur =NULL ; + sa->fino_cur =NULL ; + sa->ino_cur =NULL ; + sa->bno_cur =NULL ; + sa->cnt_cur =NULL ; +} + +/* Initialize all the btree cursors for an AG. */ +int +xfs_scrub_ag_btcur_init ( +struct xfs_scrub_context *sc, +struct xfs_scrub_ag *sa) +{ +struct xfs_mount *mp = sc->mp; +xfs_agnumber_t agno = sa->agno; + +if (sa->agf_bp) { +/* Set up a bnobt cursor for cross-referencing. */ + sa->bno_cur =xfs_allocbt_init_cursor (mp, sc->tp, sa->agf_bp, + agno,XFS_BTNUM_BNO ); +if (!sa->bno_cur) + goto err; + +/* Set up a cntbt cursor for cross-referencing. */ + sa->cnt_cur =xfs_allocbt_init_cursor (mp, sc->tp, sa->agf_bp, + agno,XFS_BTNUM_CNT ); +if (!sa->cnt_cur) + goto err; + } + +/* Set up a inobt cursor for cross-referencing. */ +if (sa->agi_bp) { + sa->ino_cur =xfs_inobt_init_cursor (mp, sc->tp, sa->agi_bp, + agno,XFS_BTNUM_INO ); +if (!sa->ino_cur) + goto err; + } + +/* Set up a finobt cursor for cross-referencing. */ +if (sa->agi_bp &&xfs_sb_version_hasfinobt (&mp->m_sb)) { + sa->fino_cur =xfs_inobt_init_cursor (mp, sc->tp, sa->agi_bp, + agno,XFS_BTNUM_FINO ); +if (!sa->fino_cur) + goto err; + } + +/* Set up a rmapbt cursor for cross-referencing. */ +if (sa->agf_bp &&xfs_sb_version_hasrmapbt (&mp->m_sb)) { + sa->rmap_cur =xfs_rmapbt_init_cursor (mp, sc->tp, sa->agf_bp, + agno); +if (!sa->rmap_cur) + goto err; + } + +/* Set up a refcountbt cursor for cross-referencing. */ +if (sa->agf_bp &&xfs_sb_version_hasreflink (&mp->m_sb)) { + sa->refc_cur =xfs_refcountbt_init_cursor (mp, sc->tp, + sa->agf_bp, agno,NULL ); +if (!sa->refc_cur) + goto err; + } + +return 0 ; +err: +return -ENOMEM ; +} + +/* Release the AG header context and btree cursors. */ +void +xfs_scrub_ag_free ( +struct xfs_scrub_context *sc, +struct xfs_scrub_ag *sa) +{ +xfs_scrub_ag_btcur_free (sa); +if (sa->agfl_bp) { +xfs_trans_brelse (sc->tp, sa->agfl_bp); + sa->agfl_bp =NULL ; + } +if (sa->agf_bp) { +xfs_trans_brelse (sc->tp, sa->agf_bp); + sa->agf_bp =NULL ; + } +if (sa->agi_bp) { +xfs_trans_brelse (sc->tp, sa->agi_bp); + sa->agi_bp =NULL ; + } + sa->agno =NULLAGNUMBER ; +} + +/* +* For scrub, grab the AGI and the AGF headers, in that order. Locking +* order requires us to get the AGI before the AGF. We use the +* transaction to avoid deadlocking on crosslinked metadata buffers; +* either the caller passes one in (bmap scrub) or we have to create a +* transaction ourselves. +*/ +int +xfs_scrub_ag_init ( +struct xfs_scrub_context *sc, +xfs_agnumber_t agno, +struct xfs_scrub_ag *sa) +{ +int error; + + sa->agno = agno; + error =xfs_scrub_ag_read_headers (sc, agno, &sa->agi_bp, + &sa->agf_bp, &sa->agfl_bp); +if (error) +return error; + +return xfs_scrub_ag_btcur_init (sc, sa); +} +/* Per-scrubber setup functions */ /* Set us up with a transaction and an empty context. */
fs/xfs/scrub/common.h +10 -0
@@ -77,4 +77,14 @@ void xfs_scrub_set_incomplete(struct xfs_scrub_context *sc);/* Setup functions */ int xfs_scrub_setup_fs (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); +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); +void xfs_scrub_ag_btcur_free (struct xfs_scrub_ag *sa); +int xfs_scrub_ag_btcur_init (struct xfs_scrub_context *sc, +struct xfs_scrub_ag *sa); +#endif /* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/scrub.c +4 -0
@@ -44,6 +44,8 @@#include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" +#include "scrub/scrub.h" +#include "scrub/btree.h" /* * Online Scrub and Repair
@@ -131,6 +133,7 @@ xfs_scrub_teardown(struct xfs_scrub_context *sc, int error) { + xfs_scrub_ag_free(sc, &sc->sa);if (sc->tp) {xfs_trans_cancel (sc->tp); sc->tp =NULL ;
@@ -231,6 +234,7 @@ xfs_scrub_metadata( sc.sm = sm; sc.ops = ops; sc.try_harder = try_harder; + sc.sa.agno =NULLAGNUMBER ; error = sc.ops->setup (&sc, ip);if (error) goto out_teardown;
fs/xfs/scrub/scrub.h +21 -0
@@ -34,6 +34,24 @@ struct xfs_scrub_meta_ops {bool (*has)(struct xfs_sb *); }; +/* Buffer pointers and btree cursors for an entire AG. */ +struct xfs_scrub_ag { +xfs_agnumber_t agno; + +/* AG btree roots */ +struct xfs_buf *agf_bp; +struct xfs_buf *agfl_bp; +struct xfs_buf *agi_bp; + +/* AG btrees */ +struct xfs_btree_cur *bno_cur; +struct xfs_btree_cur *cnt_cur; +struct xfs_btree_cur *ino_cur; +struct xfs_btree_cur *fino_cur; +struct xfs_btree_cur *rmap_cur; +struct xfs_btree_cur *refc_cur; +}; +struct xfs_scrub_context {/* General scrub state. */ struct xfs_mount *mp;
@@ -42,6 +60,9 @@ struct xfs_scrub_context {struct xfs_trans *tp;struct xfs_inode *ip;bool try_harder; + +/* State tracking for single-AG operations. */ +struct xfs_scrub_ag sa; };/* Metadata scrubbers */
Re: [PATCH 13/30] xfs: create helpers to scan an allocation group
Quoted 5 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add some helpers to enable us to lock an AG's headers, create btree cursors for all btrees in that allocation group, and clean up afterwards.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 14/30 xfs: scrub the secondary superblocks
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Ensure that the geometry presented in the backup superblocks matches the primary superblock so that repair can recover the filesystem if that primary gets corrupted.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 3 fs/xfs/scrub/agheader.c | 330 +++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/common.h | 2 fs/xfs/scrub/scrub.c | 4 + fs/xfs/scrub/scrub.h | 1 6 files changed, 340 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/agheader.c
fs/xfs/Makefile +1 -0
@@ -146,6 +146,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y) xfs-y += $(addprefix scrub/, \ trace.o \ + agheader.o \ btree.o \ common.o \ scrub.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -484,9 +484,10 @@ struct xfs_scrub_metadata {/* Scrub subcommands. */ #define XFS_SCRUB_TYPE_PROBE 0/* presence test ioctl */ +#define XFS_SCRUB_TYPE_SB 1/* superblock */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 1 +#define XFS_SCRUB_TYPE_NR 2/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/agheader.c new file +330 -0
330 lines not on this page · raw message
fs/xfs/scrub/common.h +2 -0
@@ -76,6 +76,8 @@ void xfs_scrub_set_incomplete(struct xfs_scrub_context *sc);/* Setup functions */ int xfs_scrub_setup_fs (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_ag_header (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,
fs/xfs/scrub/scrub.c +4 -0
@@ -148,6 +148,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_fs, .scrub = xfs_scrub_probe, }, + {/* superblock */ + .setup = xfs_scrub_setup_ag_header, + .scrub = xfs_scrub_superblock, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +1 -0
@@ -67,5 +67,6 @@ struct xfs_scrub_context {/* Metadata scrubbers */ int xfs_scrub_tester (struct xfs_scrub_context *sc); +int xfs_scrub_superblock (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 14/30] xfs: scrub the secondary superblocks
Quoted 5 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Ensure that the geometry presented in the backup superblocks matches the primary superblock so that repair can recover the filesystem if that primary gets corrupted.
I've noticed that scrub on certain fstests will report PREEN for secondary superblocks and repair thinks there is nothing wrong and doesn't fix them. I'm not sure which field it's complaining about, but at this point I don't see this as a blocker. Follow up patches would be fine.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 14/30] xfs: scrub the secondary superblocks
Quoted 7 lines · Dave Chinner
Quoted 5 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Ensure that the geometry presented in the backup superblocks matches the primary superblock so that repair can recover the filesystem if that primary gets corrupted.
Quoted 5 lines
I've noticed that scrub on certain fstests will report PREEN for secondary superblocks and repair thinks there is nothing wrong and doesn't fix them. I'm not sure which field it's complaining about, but at this point I don't see this as a blocker. Follow up patches would be fine.
These are the sb fields that currently trigger preen reports:
rootino rbmino rsumino imax_pct uquotino gquotino pquotino unit width uuid fname
rootino: This will always be the first inode in the first possible inode cluster in the filesystem, right? mkfs copies rootino to the last and the middle superblock, though if the location is fixed then we don't strictly need to propagate it, do we?
rbmino/rsumino: have fixed ino numbers (rootino + 1 and + 2, respectively) and are always rebuilt by repair.
uquotino/gquotino/pquotino can be totally rebuilt by quotacheck, right? So if repair zaps these (and it does) either as part of a secondary -> primary rebuild or for any other reason then it doesn't matter.
imax_pct: only set by mkfs and copied to all the other sb's. This
might be mislabeled as a preen.
uuid/fname: seems to be set in all sb copies by xfs_db. Maybe this one is mislabeled?
unit/width: will be copied into sb 0 from mount options. We only lose information if repair recovers the primary sb and the mount options don't include unit/width information.
Now for sb_versionnum, sb_version2, and the v5 feature bits:
versionnum: quotabit is turned on any time we mount with quota, and quota has to be activated with mount options, right?
nlinkbit is always turned on at mount time...
versionnum attrbit: turned on any time we set an attr, and nothing much (other than setting attr/attr2 bits in the sb) seem to depend on whether or not this is set.
features2 attr2bit: I think the attr2 bit can be controlled by mounting with 'attr2' and 'noattr2', right? So, similar to unit/width we only lose information if repair recovers a superblock and the mount options don't include attr2 information, and even then XFS seems to like to turn it on except in the noattr2 case.
So I get the impression that I might be able to turn uuid/fname/imax_pct into regular _corrupt checks since (AFAICT) xfsprogs actually updates them correctly, and simply drop the other fields from checking.
--D
Quoted 10 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 v12 15/30 xfs: scrub AGF and AGFL
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Check the block references in the AGF and AGFL headers to make sure they make sense.
--- fs/xfs/libxfs/xfs_fs.h | 4 + fs/xfs/scrub/agheader.c | 184 +++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/common.c | 28 ++++++- fs/xfs/scrub/common.h | 4 + fs/xfs/scrub/scrub.c | 8 ++ fs/xfs/scrub/scrub.h | 2 + 6 files changed, 223 insertions(+), 7 deletions(-)
fs/xfs/libxfs/xfs_fs.h +3 -1
@@ -485,9 +485,11 @@ struct xfs_scrub_metadata {/* Scrub subcommands. */ #define XFS_SCRUB_TYPE_PROBE 0/* presence test ioctl */ #define XFS_SCRUB_TYPE_SB 1/* superblock */ +#define XFS_SCRUB_TYPE_AGF 2/* AG free header */ +#define XFS_SCRUB_TYPE_AGFL 3/* AG free list */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 2 +#define XFS_SCRUB_TYPE_NR 4/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/agheader.c +184 -0
199 lines not on this page · raw message
fs/xfs/scrub/common.c +22 -6
@@ -246,6 +246,26 @@ xfs_scrub_set_incomplete(* cleaning everything up once we're through. */ +/* Decide if we want to return an AG header read failure. */ +static inline bool +want_ag_read_header_failure ( +struct xfs_scrub_context *sc, +unsigned int type) +{ +/* Return all AG header read failures when scanning btrees. */ +if (sc->sm->sm_type !=XFS_SCRUB_TYPE_AGF && + sc->sm->sm_type !=XFS_SCRUB_TYPE_AGFL ) +return true; +/* +* If we're scanning a given type of AG header, we only want to +* see read failures from that specific header. We'd like the +* other headers to cross-check them, but this isn't required. +*/ +if (sc->sm->sm_type == type) +return true; +return false; +} +/* * Grab all the headers for an AG. *
@@ -269,15 +289,11 @@ xfs_scrub_ag_read_headers( goto out; error =xfs_alloc_read_agf (mp, sc->tp, agno,0 , agf); -if (error) - goto out; -if (!*agf) { - error = -ENOMEM ; +if (error &&want_ag_read_header_failure (sc,XFS_SCRUB_TYPE_AGF )) goto out; - } error =xfs_alloc_read_agfl (mp, sc->tp, agno, agfl); -if (error) +if (error &&want_ag_read_header_failure (sc,XFS_SCRUB_TYPE_AGFL )) goto out; out:
fs/xfs/scrub/common.h +4 -0
@@ -88,5 +88,9 @@ int xfs_scrub_ag_read_headers(struct xfs_scrub_context *sc, xfs_agnumber_t agno,void xfs_scrub_ag_btcur_free (struct xfs_scrub_ag *sa);int xfs_scrub_ag_btcur_init (struct xfs_scrub_context *sc,struct xfs_scrub_ag *sa); +int xfs_scrub_walk_agfl (struct xfs_scrub_context *sc, +int (*fn)(struct xfs_scrub_context *,xfs_agblock_t bno, +void *), +void *priv);#endif /* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/scrub.c +8 -0
@@ -152,6 +152,14 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_ag_header, .scrub = xfs_scrub_superblock, }, + {/* agf */ + .setup = xfs_scrub_setup_ag_header, + .scrub = xfs_scrub_agf, + }, + {/* agfl */ + .setup = xfs_scrub_setup_ag_header, + .scrub = xfs_scrub_agfl, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +2 -0
@@ -68,5 +68,7 @@ struct xfs_scrub_context {/* Metadata scrubbers */ int xfs_scrub_tester (struct xfs_scrub_context *sc);int xfs_scrub_superblock (struct xfs_scrub_context *sc); +int xfs_scrub_agf (struct xfs_scrub_context *sc); +int xfs_scrub_agfl (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 15/30] xfs: scrub AGF and AGFL
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the block references in the AGF and AGFL headers to make sure they make sense.
Looks good. Much simpler without all the duplicated AG header reading. :P
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 16/30 xfs: scrub the AGI
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add a forgotten check to the AGI verifier, then wire up the scrub infrastructure to check the AGI contents.
--- fs/xfs/libxfs/xfs_fs.h | 3 +- fs/xfs/scrub/agheader.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/common.c | 5 ++- fs/xfs/scrub/scrub.c | 4 ++ fs/xfs/scrub/scrub.h | 1 + 5 files changed, 92 insertions(+), 3 deletions(-)
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -487,9 +487,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_SB 1/* superblock */ #define XFS_SCRUB_TYPE_AGF 2/* AG free header */ #define XFS_SCRUB_TYPE_AGFL 3/* AG free list */ +#define XFS_SCRUB_TYPE_AGI 4/* AG inode header */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 4 +#define XFS_SCRUB_TYPE_NR 5/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/agheader.c +82 -0
91 lines not on this page · raw message
fs/xfs/scrub/common.c +3 -2
@@ -254,7 +254,8 @@ want_ag_read_header_failure( {/* Return all AG header read failures when scanning btrees. */ if (sc->sm->sm_type !=XFS_SCRUB_TYPE_AGF && - sc->sm->sm_type !=XFS_SCRUB_TYPE_AGFL ) + sc->sm->sm_type !=XFS_SCRUB_TYPE_AGFL && + sc->sm->sm_type !=XFS_SCRUB_TYPE_AGI )return true;/* * If we're scanning a given type of AG header, we only want to
@@ -285,7 +286,7 @@ xfs_scrub_ag_read_headers(int error; error =xfs_ialloc_read_agi (mp, sc->tp, agno, agi); -if (error) +if (error &&want_ag_read_header_failure (sc,XFS_SCRUB_TYPE_AGI )) goto out; error =xfs_alloc_read_agf (mp, sc->tp, agno,0 , agf);
fs/xfs/scrub/scrub.c +4 -0
@@ -160,6 +160,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_ag_header, .scrub = xfs_scrub_agfl, }, + {/* agi */ + .setup = xfs_scrub_setup_ag_header, + .scrub = xfs_scrub_agi, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +1 -0
@@ -70,5 +70,6 @@ int xfs_scrub_tester(struct xfs_scrub_context *sc);int xfs_scrub_superblock (struct xfs_scrub_context *sc);int xfs_scrub_agf (struct xfs_scrub_context *sc);int xfs_scrub_agfl (struct xfs_scrub_context *sc); +int xfs_scrub_agi (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 16/30] xfs: scrub the AGI
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Add a forgotten check to the AGI verifier, then wire up the scrub infrastructure to check the AGI contents.
Looks good.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 17/30 xfs: scrub free space btrees
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Check the extent records free space btrees to ensure that the values look sane.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 4 +- fs/xfs/scrub/alloc.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/common.c | 16 +++++++ fs/xfs/scrub/common.h | 6 +++ fs/xfs/scrub/scrub.c | 8 ++++ fs/xfs/scrub/scrub.h | 2 + 7 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/alloc.c
fs/xfs/Makefile +1 -0
@@ -147,6 +147,7 @@ ifeq ($(CONFIG_XFS_ONLINE_SCRUB),y) xfs-y += $(addprefix scrub/, \ trace.o \ agheader.o \ + alloc.o \ btree.o \ common.o \ scrub.o \
fs/xfs/libxfs/xfs_fs.h +3 -1
@@ -488,9 +488,11 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_AGF 2/* AG free header */ #define XFS_SCRUB_TYPE_AGFL 3/* AG free list */ #define XFS_SCRUB_TYPE_AGI 4/* AG inode header */ +#define XFS_SCRUB_TYPE_BNOBT 5/* freesp by block btree */ +#define XFS_SCRUB_TYPE_CNTBT 6/* freesp by length btree */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 5 +#define XFS_SCRUB_TYPE_NR 7/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/alloc.c new file +103 -0
103 lines not on this page · raw message
fs/xfs/scrub/common.c +16 -0
@@ -443,3 +443,19 @@ xfs_scrub_setup_fs( {return xfs_scrub_trans_alloc (sc->sm, sc->mp, &sc->tp); } + +/* Set us up with AG headers and btree cursors. */ +int +xfs_scrub_setup_ag_btree ( +struct xfs_scrub_context *sc, +struct xfs_inode *ip, +bool force_log) +{ +int error; + + error =xfs_scrub_setup_ag_header (sc, ip); +if (error) +return error; + +return xfs_scrub_ag_init (sc, sc->sm->sm_agno, &sc->sa); +}
fs/xfs/scrub/common.h +6 -0
@@ -78,6 +78,9 @@ void xfs_scrub_set_incomplete(struct xfs_scrub_context *sc);int xfs_scrub_setup_fs (struct xfs_scrub_context *sc,struct xfs_inode *ip);int xfs_scrub_setup_ag_header (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_ag_allocbt (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,
@@ -93,4 +96,7 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc, void *), void *priv); +int xfs_scrub_setup_ag_btree (struct xfs_scrub_context *sc, +struct xfs_inode *ip,bool force_log); +#endif /* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/scrub.c +8 -0
@@ -164,6 +164,14 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_ag_header, .scrub = xfs_scrub_agi, }, + {/* bnobt */ + .setup = xfs_scrub_setup_ag_allocbt, + .scrub = xfs_scrub_bnobt, + }, + {/* cntbt */ + .setup = xfs_scrub_setup_ag_allocbt, + .scrub = xfs_scrub_cntbt, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +2 -0
@@ -71,5 +71,7 @@ int xfs_scrub_superblock(struct xfs_scrub_context *sc);int xfs_scrub_agf (struct xfs_scrub_context *sc);int xfs_scrub_agfl (struct xfs_scrub_context *sc);int xfs_scrub_agi (struct xfs_scrub_context *sc); +int xfs_scrub_bnobt (struct xfs_scrub_context *sc); +int xfs_scrub_cntbt (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 17/30] xfs: scrub free space btrees
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the extent records free space btrees to ensure that the values look sane.
Minor thing:
fs/xfs/scrub/alloc.c line 55
Quoted 3 lines
fs/xfs/scrub/alloc.c new file +3
-0+/* Scrub a bnobt/cntbt record. */ +STATIC int +xfs_scrub_allocbt_helper(
xfs_scrub_allocbt_rec()
Reads much more nicely with this name. :P
Otherwise, consider it:
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 17/30] xfs: scrub free space btrees
Quoted 8 lines · Dave Chinner
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the extent records free space btrees to ensure that the values look sane.
Minor thing:
Quoted 7 lines
fs/xfs/scrub/alloc.c line 55
Quoted 3 lines
fs/xfs/scrub/alloc.c new file +3
-0+/* Scrub a bnobt/cntbt record. */ +STATIC int +xfs_scrub_allocbt_helper(
xfs_scrub_allocbt_rec()Reads much more nicely with this name. :P
<nod> Will change as I go through the patches.
--D
Quoted 11 lines
Otherwise, consider it:
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 v12 18/30 xfs: scrub inode btrees
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Check the records of the inode btrees to make sure that the values make sense given the inode records themselves.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_format.h | 2 fs/xfs/libxfs/xfs_fs.h | 4 - fs/xfs/scrub/common.c | 29 ++++ fs/xfs/scrub/common.h | 3 fs/xfs/scrub/ialloc.c | 333 ++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 9 + fs/xfs/scrub/scrub.h | 2 8 files changed, 381 insertions(+), 2 deletions(-) create mode 100644 fs/xfs/scrub/ialloc.c
fs/xfs/Makefile +1 -0
@@ -150,6 +150,7 @@ xfs-y += $(addprefix scrub/, \ alloc.o \ btree.o \ common.o \ + ialloc.o \ scrub.o \ ) endif
fs/xfs/libxfs/xfs_format.h +1 -1
@@ -518,7 +518,7 @@ static inline int xfs_sb_version_hasftype(struct xfs_sb *sbp) (sbp->sb_features2 & XFS_SB_VERSION2_FTYPE)); } -static inline int xfs_sb_version_hasfinobt (xfs_sb_t *sbp) +static inline bool xfs_sb_version_hasfinobt (xfs_sb_t *sbp) {return (XFS_SB_VERSION_NUM (sbp) ==XFS_SB_VERSION_5 ) && (sbp->sb_features_ro_compat &XFS_SB_FEAT_RO_COMPAT_FINOBT );
fs/xfs/libxfs/xfs_fs.h +3 -1
@@ -490,9 +490,11 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_AGI 4/* AG inode header */ #define XFS_SCRUB_TYPE_BNOBT 5/* freesp by block btree */ #define XFS_SCRUB_TYPE_CNTBT 6/* freesp by length btree */ +#define XFS_SCRUB_TYPE_INOBT 7/* inode btree */ +#define XFS_SCRUB_TYPE_FINOBT 8/* free inode btree */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 7 +#define XFS_SCRUB_TYPE_NR 9/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.c +29 -0
@@ -40,6 +40,8 @@#include "xfs_refcount_btree.h" #include "xfs_rmap.h" #include "xfs_rmap_btree.h" +#include "xfs_log.h" +#include "xfs_trans_priv.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h"
@@ -451,11 +453,38 @@ xfs_scrub_setup_ag_btree( struct xfs_inode *ip, bool force_log) { +struct xfs_mount *mp = sc->mp;int error; +/* +* If the caller asks us to checkpont the log, do so. This +* 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. +*/ +if (force_log) { + error =xfs_scrub_checkpoint_log (mp); +if (error) +return error; + } + error =xfs_scrub_setup_ag_header (sc, ip);if (error)return error;return xfs_scrub_ag_init (sc, sc->sm->sm_agno, &sc->sa); } + +/* Push everything out of the log onto disk. */ +int +xfs_scrub_checkpoint_log ( +struct xfs_mount *mp) +{ +int error; + + error =_xfs_log_force (mp,XFS_LOG_SYNC ,NULL ); +if (error) +return error; +xfs_ail_push_all_sync (mp->m_ail); +return 0 ; +}
fs/xfs/scrub/common.h +3 -0
@@ -73,6 +73,7 @@ void xfs_scrub_fblock_set_warning(struct xfs_scrub_context *sc, int whichfork, xfs_fileoff_t offset);void xfs_scrub_set_incomplete (struct xfs_scrub_context *sc); +int xfs_scrub_checkpoint_log (struct xfs_mount *mp);/* Setup functions */ int xfs_scrub_setup_fs (struct xfs_scrub_context *sc,struct xfs_inode *ip);
@@ -80,6 +81,8 @@ int xfs_scrub_setup_ag_header(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_ag_allocbt (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_ag_iallocbt (struct xfs_scrub_context *sc, +struct xfs_inode *ip);void xfs_scrub_ag_free (struct xfs_scrub_context *sc,struct xfs_scrub_ag *sa);
fs/xfs/scrub/ialloc.c new file +333 -0
333 lines not on this page · raw message
fs/xfs/scrub/scrub.c +9 -0
@@ -172,6 +172,15 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_ag_allocbt, .scrub = xfs_scrub_cntbt, }, + {/* inobt */ + .setup = xfs_scrub_setup_ag_iallocbt, + .scrub = xfs_scrub_inobt, + }, + {/* finobt */ + .setup = xfs_scrub_setup_ag_iallocbt, + .scrub = xfs_scrub_finobt, + .has = xfs_sb_version_hasfinobt, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +2 -0
@@ -73,5 +73,7 @@ int xfs_scrub_agfl(struct xfs_scrub_context *sc);int xfs_scrub_agi (struct xfs_scrub_context *sc);int xfs_scrub_bnobt (struct xfs_scrub_context *sc);int xfs_scrub_cntbt (struct xfs_scrub_context *sc); +int xfs_scrub_inobt (struct xfs_scrub_context *sc); +int xfs_scrub_finobt (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 18/30] xfs: scrub inode btrees
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the records of the inode btrees to make sure that the values make sense given the inode records themselves.
.....
I think maybe I missed this first time around...
Quoted 15 lines
fs/xfs/scrub/ialloc.c new file +15
-0+/* Check a particular inode with ir_free. */ +STATIC int +xfs_scrub_iallocbt_check_cluster_freemask ( +struct xfs_scrub_btree *bs, +xfs_ino_t fsino, +xfs_agino_t chunkino, +xfs_agino_t clusterino, +struct xfs_inobt_rec_incore *irec, +struct xfs_buf *bp) +{ +struct xfs_dinode *dip; +struct xfs_mount *mp = bs->cur->bc_mp; +bool freemask_ok; +bool inuse; +int error;
fs/xfs/scrub/ialloc.c line 115
Quoted 10 lines
fs/xfs/scrub/ialloc.c new file +10
-0+ + dip =xfs_buf_offset (bp, clusterino * mp->m_sb.sb_inodesize); +if (be16_to_cpu (dip->di_magic) !=XFS_DINODE_MAGIC || + (dip->di_version >=3 && +be64_to_cpu (dip->di_ino) != fsino + clusterino)) { +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur,0 ); + goto out; + } + + freemask_ok = (irec->ir_free &XFS_INOBT_MASK (chunkino + clusterino));
Ok, so if the inode if free, the corresponding bit in the mask will be set....
fs/xfs/scrub/ialloc.c line 120
Quoted 5 lines
fs/xfs/scrub/ialloc.c new file +5
-0+ error =xfs_icache_inode_is_allocated (mp, bs->cur->bc_tp, + fsino + clusterino, &inuse); +if (error == -ENODATA ) { +/* Not cached, just read the disk buffer */ + freemask_ok ^= !!(dip->di_mode);
And this uses the lowest bit of the mask? How does that work?
/me spends 10 minutes trying to work out this function before he realises that freemask_ok is a boolean, so the initial freemask bit is collapsed down to a single bit.
Ok, that's definitely unexpected and not obvious from the code or comments. The name "freemask_ok" is misleading the way it's used. The first time it is set is means "inode is free", then after this operation it means "inode matches free mask"....
fs/xfs/scrub/ialloc.c line 129
Quoted 9 lines
fs/xfs/scrub/ialloc.c new file +9
-0+if (!bs->sc->try_harder && !freemask_ok) +return -EDEADLOCK ; + }else if (error <0 ) { +/* +* Inode is only half assembled, or there was an IO error, +* or the verifier failed, so don't bother trying to check. +* The inode scrubber can deal with this. +*/ + freemask_ok = true;
And here it means "we didn't check the free mask"
fs/xfs/scrub/ialloc.c line 132
Quoted 3 lines
fs/xfs/scrub/ialloc.c new file +3
-0+ }else { +/* Inode is all there. */ + freemask_ok ^= inuse;
And here is means "inode matches free mask" again....
fs/xfs/scrub/ialloc.c line 137
Quoted 5 lines
fs/xfs/scrub/ialloc.c new file +5
-0+ } +if (!freemask_ok) +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur,0 ); +out: +return 0 ;
Can we rewrite this to be a little more obvious?
bool inode_is_free = false;
bool freemask_ok;
....
if (irec->ir_free & XFS_INOBT_MASK(chunkino + clusterino))
inode_is_free = true;
....
if (error == -ENODATA) {
freemask_ok = inode_is_free ^ !!(dip->di_mode);
....
else if (error < 0) {
/*
* Inode is only half assembled, .....
*/
goto out;
} else {
freemask_ok = inode_is_free ^ inuse;
}That's a lot more obvious what the code is checking...
fs/xfs/scrub/ialloc.c line 220
Quoted 3 lines
fs/xfs/scrub/ialloc.c new file +3
-0+/* Scrub an inobt/finobt record. */ +STATIC int +xfs_scrub_iallocbt_helper(
s/helper/rec/
-Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 18/30] xfs: scrub inode btrees
Quoted 47 lines · Dave Chinner
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the records of the inode btrees to make sure that the values make sense given the inode records themselves.
.....
I think maybe I missed this first time around...
Quoted 15 lines
fs/xfs/scrub/ialloc.c new file +15
-0+/* Check a particular inode with ir_free. */ +STATIC int +xfs_scrub_iallocbt_check_cluster_freemask ( +struct xfs_scrub_btree *bs, +xfs_ino_t fsino, +xfs_agino_t chunkino, +xfs_agino_t clusterino, +struct xfs_inobt_rec_incore *irec, +struct xfs_buf *bp) +{ +struct xfs_dinode *dip; +struct xfs_mount *mp = bs->cur->bc_mp; +bool freemask_ok; +bool inuse; +int error;fs/xfs/scrub/ialloc.c line 115
Quoted 10 lines
fs/xfs/scrub/ialloc.c new file +10
-0+ + dip =xfs_buf_offset (bp, clusterino * mp->m_sb.sb_inodesize); +if (be16_to_cpu (dip->di_magic) !=XFS_DINODE_MAGIC || + (dip->di_version >=3 && +be64_to_cpu (dip->di_ino) != fsino + clusterino)) { +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur,0 ); + goto out; + } + + freemask_ok = (irec->ir_free &XFS_INOBT_MASK (chunkino + clusterino));Ok, so if the inode if free, the corresponding bit in the mask will be set....
fs/xfs/scrub/ialloc.c line 120
Quoted 5 lines
fs/xfs/scrub/ialloc.c new file +5
-0+ error =xfs_icache_inode_is_allocated (mp, bs->cur->bc_tp, + fsino + clusterino, &inuse); +if (error == -ENODATA ) { +/* Not cached, just read the disk buffer */ + freemask_ok ^= !!(dip->di_mode);And this uses the lowest bit of the mask? How does that work?
Quoted 8 lines
/me spends 10 minutes trying to work out this function before he realises that
freemask_okis a boolean, so the initial freemask bit is collapsed down to a single bit.Ok, that's definitely unexpected and not obvious from the code or comments. The name "
freemask_ok" is misleading the way it's used. The first time it is set is means "inode is free", then after this operation it means "inode matches free mask"....
Ugh, sorry. Thank you for complaining about this!
Quoted 45 lines
fs/xfs/scrub/ialloc.c line 129
Quoted 10 lines
fs/xfs/scrub/ialloc.c new file +9
-0+if (!bs->sc->try_harder && !freemask_ok) +return -EDEADLOCK ; + }else if (error <0 ) { +/* +* Inode is only half assembled, or there was an IO error, +* or the verifier failed, so don't bother trying to check. +* The inode scrubber can deal with this. +*/ + freemask_ok = true;And here it means "we didn't check the free mask"
fs/xfs/scrub/ialloc.c line 132
Quoted 3 lines
fs/xfs/scrub/ialloc.c new file +3
-0+ }else { +/* Inode is all there. */ + freemask_ok ^= inuse;And here is means "inode matches free mask" again....
fs/xfs/scrub/ialloc.c line 137
Quoted 5 lines
fs/xfs/scrub/ialloc.c new file +5
-0+ } +if (!freemask_ok) +xfs_scrub_btree_set_corrupt (bs->sc, bs->cur,0 ); +out: +return 0 ;Can we rewrite this to be a little more obvious?
bool inode_is_free = false; bool freemask_ok; .... if (irec->ir_free & XFS_INOBT_MASK(chunkino + clusterino)) inode_is_free = true; .... if (error == -ENODATA) { freemask_ok = inode_is_free ^ !!(dip->di_mode); .... else if (error < 0) { /* * Inode is only half assembled, ..... */ goto out; } else { freemask_ok = inode_is_free ^ inuse; }
Quoted 1 line
That's a lot more obvious what the code is checking...
Done.
Quoted 6 lines
fs/xfs/scrub/ialloc.c line 220
Quoted 4 lines
fs/xfs/scrub/ialloc.c new file +3
-0+/* Scrub an inobt/finobt record. */ +STATIC int +xfs_scrub_iallocbt_helper(s/helper/rec/
Done.
--D
Quoted 9 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
PATCH v2 18/30 xfs: scrub inode btrees
In reply to Darrick J. WongCheck the records of the inode btrees to make sure that the values make sense given the inode records themselves.
---
v2: fix insane freemask variable usage, shorten helper function names
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_format.h | 2 fs/xfs/libxfs/xfs_fs.h | 4 - fs/xfs/scrub/common.c | 29 ++++ fs/xfs/scrub/common.h | 3 fs/xfs/scrub/ialloc.c | 334 ++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 9 + fs/xfs/scrub/scrub.h | 2 8 files changed, 382 insertions(+), 2 deletions(-) create mode 100644 fs/xfs/scrub/ialloc.c
fs/xfs/Makefile +1 -0
@@ -150,6 +150,7 @@ xfs-y += $(addprefix scrub/, \ alloc.o \ btree.o \ common.o \ + ialloc.o \ scrub.o \ ) endif
fs/xfs/libxfs/xfs_format.h +1 -1
@@ -518,7 +518,7 @@ static inline int xfs_sb_version_hasftype(struct xfs_sb *sbp) (sbp->sb_features2 & XFS_SB_VERSION2_FTYPE)); } -static inline int xfs_sb_version_hasfinobt (xfs_sb_t *sbp) +static inline bool xfs_sb_version_hasfinobt (xfs_sb_t *sbp) {return (XFS_SB_VERSION_NUM (sbp) ==XFS_SB_VERSION_5 ) && (sbp->sb_features_ro_compat &XFS_SB_FEAT_RO_COMPAT_FINOBT );
fs/xfs/libxfs/xfs_fs.h +3 -1
@@ -490,9 +490,11 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_AGI 4/* AG inode header */ #define XFS_SCRUB_TYPE_BNOBT 5/* freesp by block btree */ #define XFS_SCRUB_TYPE_CNTBT 6/* freesp by length btree */ +#define XFS_SCRUB_TYPE_INOBT 7/* inode btree */ +#define XFS_SCRUB_TYPE_FINOBT 8/* free inode btree */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 7 +#define XFS_SCRUB_TYPE_NR 9/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.c +29 -0
@@ -40,6 +40,8 @@#include "xfs_refcount_btree.h" #include "xfs_rmap.h" #include "xfs_rmap_btree.h" +#include "xfs_log.h" +#include "xfs_trans_priv.h" #include "scrub/xfs_scrub.h" #include "scrub/scrub.h" #include "scrub/common.h"
@@ -451,11 +453,38 @@ xfs_scrub_setup_ag_btree( struct xfs_inode *ip, bool force_log) { +struct xfs_mount *mp = sc->mp;int error; +/* +* If the caller asks us to checkpont the log, do so. This +* 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. +*/ +if (force_log) { + error =xfs_scrub_checkpoint_log (mp); +if (error) +return error; + } + error =xfs_scrub_setup_ag_header (sc, ip);if (error)return error;return xfs_scrub_ag_init (sc, sc->sm->sm_agno, &sc->sa); } + +/* Push everything out of the log onto disk. */ +int +xfs_scrub_checkpoint_log ( +struct xfs_mount *mp) +{ +int error; + + error =_xfs_log_force (mp,XFS_LOG_SYNC ,NULL ); +if (error) +return error; +xfs_ail_push_all_sync (mp->m_ail); +return 0 ; +}
fs/xfs/scrub/common.h +3 -0
@@ -73,6 +73,7 @@ void xfs_scrub_fblock_set_warning(struct xfs_scrub_context *sc, int whichfork, xfs_fileoff_t offset);void xfs_scrub_set_incomplete (struct xfs_scrub_context *sc); +int xfs_scrub_checkpoint_log (struct xfs_mount *mp);/* Setup functions */ int xfs_scrub_setup_fs (struct xfs_scrub_context *sc,struct xfs_inode *ip);
@@ -80,6 +81,8 @@ int xfs_scrub_setup_ag_header(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_ag_allocbt (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_ag_iallocbt (struct xfs_scrub_context *sc, +struct xfs_inode *ip);void xfs_scrub_ag_free (struct xfs_scrub_context *sc,struct xfs_scrub_ag *sa);
fs/xfs/scrub/ialloc.c new file +334 -0
334 lines not on this page · raw message
fs/xfs/scrub/scrub.c +9 -0
@@ -182,6 +182,15 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_ag_allocbt, .scrub = xfs_scrub_cntbt, }, + {/* inobt */ + .setup = xfs_scrub_setup_ag_iallocbt, + .scrub = xfs_scrub_inobt, + }, + {/* finobt */ + .setup = xfs_scrub_setup_ag_iallocbt, + .scrub = xfs_scrub_finobt, + .has = xfs_sb_version_hasfinobt, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +2 -0
@@ -73,5 +73,7 @@ int xfs_scrub_agfl(struct xfs_scrub_context *sc);int xfs_scrub_agi (struct xfs_scrub_context *sc);int xfs_scrub_bnobt (struct xfs_scrub_context *sc);int xfs_scrub_cntbt (struct xfs_scrub_context *sc); +int xfs_scrub_inobt (struct xfs_scrub_context *sc); +int xfs_scrub_finobt (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH v2 18/30] xfs: scrub inode btrees
Quoted 6 lines · Darrick J. Wong
Check the records of the inode btrees to make sure that the values make sense given the inode records themselves.
Signed-off-by Darrick J. Wong <[email protected]>---
v2: fix insane freemask variable usage, shorten helper function names
looks good to me.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 19/30 xfs: scrub rmap btrees
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Check the reverse mapping records to make sure that the contents make sense.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 3 + fs/xfs/scrub/common.h | 2 + fs/xfs/scrub/rmap.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 5 ++ fs/xfs/scrub/scrub.h | 1 6 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/rmap.c
fs/xfs/Makefile +1 -0
@@ -151,6 +151,7 @@ xfs-y += $(addprefix scrub/, \ btree.o \ common.o \ ialloc.o \ + rmap.o \ scrub.o \ ) endif
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -492,9 +492,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_CNTBT 6/* freesp by length btree */ #define XFS_SCRUB_TYPE_INOBT 7/* inode btree */ #define XFS_SCRUB_TYPE_FINOBT 8/* free inode btree */ +#define XFS_SCRUB_TYPE_RMAPBT 9/* reverse mapping btree */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 9 +#define XFS_SCRUB_TYPE_NR 10/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.h +2 -0
@@ -83,6 +83,8 @@ int xfs_scrub_setup_ag_allocbt(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_ag_iallocbt (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_ag_rmapbt (struct xfs_scrub_context *sc, +struct xfs_inode *ip);void xfs_scrub_ag_free (struct xfs_scrub_context *sc,struct xfs_scrub_ag *sa);
fs/xfs/scrub/rmap.c new file +138 -0
138 lines not on this page · raw message
fs/xfs/scrub/scrub.c +5 -0
@@ -181,6 +181,11 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .scrub = xfs_scrub_finobt, .has = xfs_sb_version_hasfinobt, }, + {/* rmapbt */ + .setup = xfs_scrub_setup_ag_rmapbt, + .scrub = xfs_scrub_rmapbt, + .has = xfs_sb_version_hasrmapbt, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +1 -0
@@ -75,5 +75,6 @@ int xfs_scrub_bnobt(struct xfs_scrub_context *sc);int xfs_scrub_cntbt (struct xfs_scrub_context *sc);int xfs_scrub_inobt (struct xfs_scrub_context *sc);int xfs_scrub_finobt (struct xfs_scrub_context *sc); +int xfs_scrub_rmapbt (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 19/30] xfs: scrub rmap btrees
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the reverse mapping records to make sure that the contents make sense.
Minor:
fs/xfs/scrub/rmap.c line 56
Quoted 3 lines
fs/xfs/scrub/rmap.c new file +3
-0+/* Scrub an rmapbt record. */ +STATIC int +xfs_scrub_rmapbt_helper(
s/helper/rec/
otherwise:
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 20/30 xfs: scrub refcount btrees
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Plumb in the pieces necessary to check the refcount btree. If rmap is available, check the reference count by performing an interval query against the rmapbt.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 3 + fs/xfs/scrub/common.h | 2 + fs/xfs/scrub/refcount.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 5 ++ fs/xfs/scrub/scrub.h | 1 6 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/refcount.c
fs/xfs/Makefile +1 -0
@@ -151,6 +151,7 @@ xfs-y += $(addprefix scrub/, \ btree.o \ common.o \ ialloc.o \ + refcount.o \ rmap.o \ scrub.o \ )
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -493,9 +493,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_INOBT 7/* inode btree */ #define XFS_SCRUB_TYPE_FINOBT 8/* free inode btree */ #define XFS_SCRUB_TYPE_RMAPBT 9/* reverse mapping btree */ +#define XFS_SCRUB_TYPE_REFCNTBT 10/* reference count btree */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 10 +#define XFS_SCRUB_TYPE_NR 11/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.h +2 -0
@@ -85,6 +85,8 @@ int xfs_scrub_setup_ag_iallocbt(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_ag_rmapbt (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_ag_refcountbt (struct xfs_scrub_context *sc, +struct xfs_inode *ip);void xfs_scrub_ag_free (struct xfs_scrub_context *sc,struct xfs_scrub_ag *sa);
fs/xfs/scrub/refcount.c new file +99 -0
99 lines not on this page · raw message
fs/xfs/scrub/scrub.c +5 -0
@@ -186,6 +186,11 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .scrub = xfs_scrub_rmapbt, .has = xfs_sb_version_hasrmapbt, }, + {/* refcountbt */ + .setup = xfs_scrub_setup_ag_refcountbt, + .scrub = xfs_scrub_refcountbt, + .has = xfs_sb_version_hasreflink, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +1 -0
@@ -76,5 +76,6 @@ int xfs_scrub_cntbt(struct xfs_scrub_context *sc);int xfs_scrub_inobt (struct xfs_scrub_context *sc);int xfs_scrub_finobt (struct xfs_scrub_context *sc);int xfs_scrub_rmapbt (struct xfs_scrub_context *sc); +int xfs_scrub_refcountbt (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 20/30] xfs: scrub refcount btrees
Quoted 5 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Plumb in the pieces necessary to check the refcount btree. If rmap is available, check the reference count by performing an interval query against the rmapbt.
....
fs/xfs/scrub/refcount.c line 55
Quoted 3 lines
fs/xfs/scrub/refcount.c new file +3
-0+/* Scrub a refcountbt record. */ +STATIC int +xfs_scrub_refcountbt_helper(
s/helper/rec/
Apart from that
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 21/30 xfs: scrub inodes
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Scrub the fields within an inode.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 3 fs/xfs/scrub/common.c | 54 ++++ fs/xfs/scrub/common.h | 3 fs/xfs/scrub/inode.c | 607 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 18 + fs/xfs/scrub/scrub.h | 2 7 files changed, 685 insertions(+), 3 deletions(-) create mode 100644 fs/xfs/scrub/inode.c
fs/xfs/Makefile +1 -0
@@ -151,6 +151,7 @@ xfs-y += $(addprefix scrub/, \ btree.o \ common.o \ ialloc.o \ + inode.o \ refcount.o \ rmap.o \ scrub.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -494,9 +494,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_FINOBT 8/* free inode btree */ #define XFS_SCRUB_TYPE_RMAPBT 9/* reverse mapping btree */ #define XFS_SCRUB_TYPE_REFCNTBT 10/* reference count btree */ +#define XFS_SCRUB_TYPE_INODE 11/* inode record */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 11 +#define XFS_SCRUB_TYPE_NR 12/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.c +54 -0
63 lines not on this page · raw message
fs/xfs/scrub/common.h +3 -0
@@ -87,6 +87,8 @@ int xfs_scrub_setup_ag_rmapbt(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_ag_refcountbt (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_inode (struct xfs_scrub_context *sc, +struct xfs_inode *ip);void xfs_scrub_ag_free (struct xfs_scrub_context *sc,struct xfs_scrub_ag *sa);
@@ -105,5 +107,6 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,int xfs_scrub_setup_ag_btree (struct xfs_scrub_context *sc,struct xfs_inode *ip,bool force_log); +int xfs_scrub_get_inode (struct xfs_scrub_context *sc,struct xfs_inode *ip_in);#endif /* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/inode.c new file +607 -0
607 lines not on this page · raw message
fs/xfs/scrub/scrub.c +16 -2
@@ -30,6 +30,8 @@#include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_inode.h" +#include "xfs_icache.h" +#include "xfs_itable.h" #include "xfs_alloc.h" #include "xfs_alloc_btree.h" #include "xfs_bmap.h"
@@ -131,6 +133,7 @@ xfs_scrub_probe( STATIC intxfs_scrub_teardown (struct xfs_scrub_context *sc, +struct xfs_inode *ip_in,int error) {xfs_scrub_ag_free (sc, &sc->sa);
@@ -138,6 +141,13 @@ xfs_scrub_teardown(xfs_trans_cancel (sc->tp); sc->tp =NULL ; } +if (sc->ip) { +xfs_iunlock (sc->ip, sc->ilock_flags); +if (sc->ip != ip_in && + !xfs_internal_inum (sc->mp, sc->ip->i_ino)) +iput (VFS_I (sc->ip)); + sc->ip =NULL ; + }return error; }
@@ -191,6 +201,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .scrub = xfs_scrub_refcountbt, .has = xfs_sb_version_hasreflink, }, + {/* inode record */ + .setup = xfs_scrub_setup_inode, + .scrub = xfs_scrub_inode, + }, };/* This isn't a stable feature, warn once per day. */
@@ -290,7 +304,7 @@ xfs_scrub_metadata(* Tear down everything we hold, then set up again with * preparation for worst-case scenarios. */ - error =xfs_scrub_teardown (&sc,0 ); + error =xfs_scrub_teardown (&sc, ip,0 );if (error) goto out; try_harder = true;
@@ -303,7 +317,7 @@ xfs_scrub_metadata(xfs_alert_ratelimited (mp,"Corruption detected during scrub." ); out_teardown: - error =xfs_scrub_teardown (&sc, error); + error =xfs_scrub_teardown (&sc, ip, error); out:trace_xfs_scrub_done (ip, sm, error);return error;
fs/xfs/scrub/scrub.h +2 -0
@@ -59,6 +59,7 @@ struct xfs_scrub_context {const struct xfs_scrub_meta_ops *ops;struct xfs_trans *tp;struct xfs_inode *ip; +uint ilock_flags;bool try_harder;/* State tracking for single-AG operations. */
@@ -77,5 +78,6 @@ int xfs_scrub_inobt(struct xfs_scrub_context *sc);int xfs_scrub_finobt (struct xfs_scrub_context *sc);int xfs_scrub_rmapbt (struct xfs_scrub_context *sc);int xfs_scrub_refcountbt (struct xfs_scrub_context *sc); +int xfs_scrub_inode (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 21/30] xfs: scrub inodes
Quoted 321 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub the fields within an inode.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 3 fs/xfs/scrub/common.c | 54 ++++ fs/xfs/scrub/common.h | 3 fs/xfs/scrub/inode.c | 607 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 18 + fs/xfs/scrub/scrub.h | 2 7 files changed, 685 insertions(+), 3 deletions(-) create mode 100644 fs/xfs/scrub/inode.cfs/xfs/Makefile +1
-0@@ -151,6 +151,7 @@ xfs-y += $(addprefix scrub/, \ btree.o \ common.o \ ialloc.o \ + inode.o \ refcount.o \ rmap.o \ scrub.o \fs/xfs/libxfs/xfs_fs.h +2
-1@@ -494,9 +494,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_FINOBT 8/* free inode btree */ #define XFS_SCRUB_TYPE_RMAPBT 9/* reverse mapping btree */ #define XFS_SCRUB_TYPE_REFCNTBT 10/* reference count btree */ +#define XFS_SCRUB_TYPE_INODE 11/* inode record */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 11 +#define XFS_SCRUB_TYPE_NR 12/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)fs/xfs/scrub/common.c +54
-0@@ -30,6 +30,8 @@#include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_inode.h" +#include "xfs_icache.h" +#include "xfs_itable.h" #include "xfs_alloc.h" #include "xfs_alloc_btree.h" #include "xfs_bmap.h" @@ -488,3 +490,55 @@ xfs_scrub_checkpoint_log(xfs_ail_push_all_sync (mp->m_ail);return 0 ; } + +/* +* Given an inode and the scrub control structure, grab either the +* inode referenced in the control structure or the inode passed in. +* The inode is not locked. +*/ +int +xfs_scrub_get_inode ( +struct xfs_scrub_context *sc, +struct xfs_inode *ip_in) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_inode *ip =NULL ; +int error; + +/* +* If userspace passed us an AG number or a generation number +* without an inode number, they haven't got a clue so bail out +* immediately. +*/ +if (sc->sm->sm_agno || (sc->sm->sm_gen && !sc->sm->sm_ino)) +return -EINVAL ; + +/* We want to scan the inode we already had opened. */ +if (sc->sm->sm_ino ==0 || sc->sm->sm_ino == ip_in->i_ino) { + sc->ip = ip_in; +return 0 ; + } + +/* Look up the inode, see if the generation number matches. */ +if (xfs_internal_inum (mp, sc->sm->sm_ino)) +return -ENOENT ; + error =xfs_iget (mp,NULL , sc->sm->sm_ino, +XFS_IGET_UNTRUSTED |XFS_IGET_DONTCACHE ,0 , &ip); +if (error == -ENOENT || error == -EINVAL ) { +/* inode doesn't exist... */ +return -ENOENT ; + }else if (error) { +trace_xfs_scrub_op_error (sc, +XFS_INO_TO_AGNO (mp, sc->sm->sm_ino), +XFS_INO_TO_AGBNO (mp, sc->sm->sm_ino), + error, __return_address); +return error; + } +if (VFS_I (ip)->i_generation != sc->sm->sm_gen) { +iput (VFS_I (ip)); +return -ENOENT ; + } + + sc->ip = ip; +return 0 ; +}fs/xfs/scrub/common.h +3
-0@@ -87,6 +87,8 @@ int xfs_scrub_setup_ag_rmapbt(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_ag_refcountbt (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_inode (struct xfs_scrub_context *sc, +struct xfs_inode *ip);void xfs_scrub_ag_free (struct xfs_scrub_context *sc,struct xfs_scrub_ag *sa);@@ -105,5 +107,6 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,int xfs_scrub_setup_ag_btree (struct xfs_scrub_context *sc,struct xfs_inode *ip,bool force_log); +int xfs_scrub_get_inode (struct xfs_scrub_context *sc,struct xfs_inode *ip_in);#endif /* __XFS_SCRUB_COMMON_H__ */ fs/xfs/scrub/inode.c new file +181
-0@@ -0,0 +1,607 @@ +/* +* Copyright (C) 2017 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_icache.h" +#include "xfs_inode_buf.h" +#include "xfs_inode_fork.h" +#include "xfs_ialloc.h" +#include "xfs_da_format.h" +#include "xfs_reflink.h" +#include "scrub/xfs_scrub.h" +#include "scrub/scrub.h" +#include "scrub/common.h" +#include "scrub/trace.h" + +/* +* Grab total control of the inode metadata. It doesn't matter here if +* the file data is still changing; exclusive access to the metadata is +* the goal. +*/ +int +xfs_scrub_setup_inode ( +struct xfs_scrub_context *sc, +struct xfs_inode *ip) +{ +struct xfs_mount *mp = sc->mp; +int error; + +/* +* Try to get the inode. If the verifiers fail, we try again +* in raw mode. +*/ + error =xfs_scrub_get_inode (sc, ip); +switch (error) { +case 0 : +break ; +case -EFSCORRUPTED : +case -EFSBADCRC : +return 0 ; +default : +return error; + } + +/* 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->sm, mp, &sc->tp); +if (error) + goto out; + sc->ilock_flags |=XFS_ILOCK_EXCL ; +xfs_ilock (sc->ip,XFS_ILOCK_EXCL ); + +out: +/* scrub teardown will unlock and release the inode for us */ +return error; +} + +/* Inode core */ + +/* +* di_extsize hint validation is somewhat cumbersome. Rules are: +* +* 1. extent size hint is only valid for directories and regular files +* 2. DIFLAG_EXTSIZE is only valid for regular files +* 3. DIFLAG_EXTSZINHERIT is only valid for directories. +* 4. extsize hint of 0 turns off hints, clears inode flags. +* 5. either flag must be set if extsize != 0 +* 6. Extent size must be a multiple of the appropriate block size. +* 7. extent size hint cannot be longer than maximum extent length +* 8. for non-realtime files, the extent size hint must be limited +* to half the AG size to avoid alignment extending the extent +* beyond the limits of the AG. +*/ +STATIC void +xfs_scrub_inode_extsize ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp, +struct xfs_dinode *dip, +xfs_ino_t ino, +uint16_t mode, +uint16_t flags) +{ +struct xfs_mount *mp = sc->mp; +bool rt_flag; +bool hint_flag; +bool inherit_flag; +uint32_t extsize; +uint32_t extsize_bytes; +uint32_t blocksize_bytes; + + rt_flag = (flags &XFS_DIFLAG_REALTIME ); + hint_flag = (flags &XFS_DIFLAG_EXTSIZE ); + inherit_flag = (flags &XFS_DIFLAG_EXTSZINHERIT ); + extsize =be32_to_cpu (dip->di_extsize); + extsize_bytes =XFS_FSB_TO_B (sc->mp, extsize); + +if (rt_flag) + blocksize_bytes = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; +else + blocksize_bytes = mp->m_sb.sb_blocksize; + +if ((hint_flag || inherit_flag) && (!S_ISDIR (mode) && !S_ISREG (mode))) + goto bad; + +if (hint_flag && !S_ISREG (mode)) + goto bad; + +if (inherit_flag && !S_ISDIR (mode)) + goto bad; + +if ((hint_flag || inherit_flag) && extsize ==0 ) + goto bad; + +if (!(hint_flag || inherit_flag) && extsize !=0 ) + goto bad; + +if (extsize_bytes % blocksize_bytes) + goto bad; + +if (extsize >MAXEXTLEN ) + goto bad; + +if (!rt_flag && extsize > mp->m_sb.sb_agblocks /2 ) + goto bad; + +return ; +bad: +xfs_scrub_ino_set_corrupt (sc, ino, bp); +} + +/* +* di_cowextsize hint validation is somewhat cumbersome. Rules are: +* +* 1. flag requires reflink feature +* 2. cow extent size hint is only valid for directories and regular files +* 3. cow extsize hint of 0 turns off hints, clears inode flags. +* 4. either flag must be set if cow extsize != 0 +* 5. flag cannot be set for rt files +* 6. Extent size must be a multiple of the appropriate block size. +* 7. extent size hint cannot be longer than maximum extent length +* 8. the extent size hint must be limited +* to half the AG size to avoid alignment extending the extent +* beyond the limits of the AG. +*/ +STATIC void +xfs_scrub_inode_cowextsize ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp, +struct xfs_dinode *dip, +xfs_ino_t ino, +uint16_t mode, +uint16_t flags, +uint64_t flags2)
fs/xfs/scrub/inode.c line 191
Quoted 10 lines
fs/xfs/scrub/inode.c new file +10
-0+{ +struct xfs_mount *mp = sc->mp; +bool rt_flag; +bool hint_flag; +uint32_t extsize; +uint32_t extsize_bytes; + + rt_flag = (flags &XFS_DIFLAG_REALTIME ); + hint_flag = (flags2 &XFS_DIFLAG2_COWEXTSIZE ); + extsize =be32_to_cpu (dip->di_extsize);
Doh, this ought to be extsize = be32_to_cpu(dip->di_cowextsize); will fix.
--D
Quoted 504 lines
fs/xfs/scrub/inode.c new file +416
-0+ extsize_bytes =XFS_FSB_TO_B (sc->mp, extsize); + +if (hint_flag && !xfs_sb_version_hasreflink (&mp->m_sb)) + goto bad; + +if (hint_flag && (!S_ISDIR (mode) && !S_ISREG (mode))) + goto bad; + +if (hint_flag && extsize ==0 ) + goto bad; + +if (!hint_flag && extsize !=0 ) + goto bad; + +if (hint_flag && rt_flag) + goto bad; + +if (extsize_bytes % mp->m_sb.sb_blocksize) + goto bad; + +if (extsize >MAXEXTLEN ) + goto bad; + +if (extsize > mp->m_sb.sb_agblocks /2 ) + goto bad; + +return ; +bad: +xfs_scrub_ino_set_corrupt (sc, ino, bp); +} + +/* Make sure the di_flags make sense for the inode. */ +STATIC void +xfs_scrub_inode_flags ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp, +struct xfs_dinode *dip, +xfs_ino_t ino, +uint16_t mode, +uint16_t flags) +{ +struct xfs_mount *mp = sc->mp; + +if (flags & ~XFS_DIFLAG_ANY ) + goto bad; + +/* rt flags require rt device */ +if ((flags & (XFS_DIFLAG_REALTIME |XFS_DIFLAG_RTINHERIT )) && + !mp->m_rtdev_targp) + goto bad; + +/* new rt bitmap flag only valid for rbmino */ +if ((flags &XFS_DIFLAG_NEWRTBM ) && ino != mp->m_sb.sb_rbmino) + goto bad; + +/* directory-only flags */ +if ((flags & (XFS_DIFLAG_RTINHERIT | +XFS_DIFLAG_EXTSZINHERIT | +XFS_DIFLAG_PROJINHERIT | +XFS_DIFLAG_NOSYMLINKS )) && + !S_ISDIR (mode)) + goto bad; + +/* file-only flags */ +if ((flags & (XFS_DIFLAG_REALTIME |FS_XFLAG_EXTSIZE )) && + !S_ISREG (mode)) + goto bad; + +/* filestreams and rt make no sense */ +if ((flags &XFS_DIFLAG_FILESTREAM ) && (flags &XFS_DIFLAG_REALTIME )) + goto bad; + +return ; +bad: +xfs_scrub_ino_set_corrupt (sc, ino, bp); +} + +/* Make sure the di_flags2 make sense for the inode. */ +STATIC void +xfs_scrub_inode_flags2 ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp, +struct xfs_dinode *dip, +xfs_ino_t ino, +uint16_t mode, +uint64_t flags2) +{ +struct xfs_mount *mp = sc->mp; + +if (flags2 & ~XFS_DIFLAG2_ANY ) + goto bad; + +/* reflink flag requires reflink feature */ +if ((flags2 &XFS_DIFLAG2_REFLINK ) && + !xfs_sb_version_hasreflink (&mp->m_sb)) + goto bad; + +/* cowextsize flag is checked w.r.t. mode separately */ + +/* file-only flags */ +if ((flags2 & (XFS_DIFLAG2_DAX |XFS_DIFLAG2_REFLINK )) && + !S_ISREG (mode)) + goto bad; + +/* dax and reflink make no sense, currently */ +if ((flags2 &XFS_DIFLAG2_DAX ) && (flags2 &XFS_DIFLAG2_REFLINK )) + goto bad; + +return ; +bad: +xfs_scrub_ino_set_corrupt (sc, ino, bp); +} + +/* Scrub all the ondisk inode fields. */ +STATIC void +xfs_scrub_dinode ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp, +struct xfs_dinode *dip, +xfs_ino_t ino) +{ +struct xfs_mount *mp = sc->mp; +size_t fork_recs; +unsigned long long isize; +uint64_t flags2; +uint32_t nextents; +uint16_t flags; +uint16_t mode; + + flags =be16_to_cpu (dip->di_flags); +if (dip->di_version >=3 ) + flags2 =be64_to_cpu (dip->di_flags2); +else + flags2 =0 ; + +/* di_mode */ + mode =be16_to_cpu (dip->di_mode); +if (mode & ~(S_IALLUGO |S_IFMT )) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + +/* v1/v2 fields */ +switch (dip->di_version) { +case 1 : +/* +* We autoconvert v1 inodes into v2 inodes on writeout, +* so just mark this inode for preening. +*/ +xfs_scrub_ino_set_preen (sc, bp); +break ; +case 2 : +case 3 : +if (dip->di_onlink !=0 ) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + +if (dip->di_mode ==0 && sc->ip) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + +if (dip->di_projid_hi !=0 && + !xfs_sb_version_hasprojid32bit (&mp->m_sb)) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; +default : +xfs_scrub_ino_set_corrupt (sc, ino, bp); +return ; + } + +/* +* di_uid/di_gid -- -1 isn't invalid, but there's no way that +* userspace could have created that. +*/ +if (dip->di_uid ==cpu_to_be32 (-1U ) || + dip->di_gid ==cpu_to_be32 (-1U )) +xfs_scrub_ino_set_warning (sc, bp); + +/* di_format */ +switch (dip->di_format) { +case XFS_DINODE_FMT_DEV : +if (!S_ISCHR (mode) && !S_ISBLK (mode) && + !S_ISFIFO (mode) && !S_ISSOCK (mode)) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; +case XFS_DINODE_FMT_LOCAL : +if (!S_ISDIR (mode) && !S_ISLNK (mode)) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; +case XFS_DINODE_FMT_EXTENTS : +if (!S_ISREG (mode) && !S_ISDIR (mode) && !S_ISLNK (mode)) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; +case XFS_DINODE_FMT_BTREE : +if (!S_ISREG (mode) && !S_ISDIR (mode)) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; +case XFS_DINODE_FMT_UUID : +default : +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; + } + +/* +* di_size. xfs_dinode_verify checks for things that screw up +* the VFS such as the upper bit being set and zero-length +* symlinks/directories, but we can do more here. +*/ + isize =be64_to_cpu (dip->di_size); +if (isize & (1ULL <<63 )) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + +/* Devices, fifos, and sockets must have zero size */ +if (!S_ISDIR (mode) && !S_ISREG (mode) && !S_ISLNK (mode) && isize !=0 ) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + +/* Directories can't be larger than the data section size (32G) */ +if (S_ISDIR (mode) && (isize ==0 || isize >=XFS_DIR2_SPACE_SIZE )) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + +/* Symlinks can't be larger than SYMLINK_MAXLEN */ +if (S_ISLNK (mode) && (isize ==0 || isize >=XFS_SYMLINK_MAXLEN )) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + +/* +* Warn if the running kernel can't handle the kinds of offsets +* needed to deal with the file size. In other words, if the +* pagecache can't cache all the blocks in this file due to +* overly large offsets, flag the inode for admin review. +*/ +if (isize >= mp->m_super->s_maxbytes) +xfs_scrub_ino_set_warning (sc, bp); + +/* di_nblocks */ +if (flags2 &XFS_DIFLAG2_REFLINK ) { + ;/* nblocks can exceed dblocks */ + }else if (flags &XFS_DIFLAG_REALTIME ) { +/* +* nblocks is the sum of data extents (in the rtdev), +* attr extents (in the datadev), and both forks' bmbt +* blocks (in the datadev). This clumsy check is the +* best we can do without cross-referencing with the +* inode forks. +*/ +if (be64_to_cpu (dip->di_nblocks) >= + mp->m_sb.sb_dblocks + mp->m_sb.sb_rblocks) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + }else { +if (be64_to_cpu (dip->di_nblocks) >= mp->m_sb.sb_dblocks) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + } + +xfs_scrub_inode_flags (sc, bp, dip, ino, mode, flags); + +xfs_scrub_inode_extsize (sc, bp, dip, ino, mode, flags); + +/* di_nextents */ + nextents =be32_to_cpu (dip->di_nextents); + fork_recs =XFS_DFORK_DSIZE (dip, mp) /sizeof (struct xfs_bmbt_rec ); +switch (dip->di_format) { +case XFS_DINODE_FMT_EXTENTS : +if (nextents > fork_recs) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; +case XFS_DINODE_FMT_BTREE : +if (nextents <= fork_recs) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; +default : +if (nextents !=0 ) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; + } + +/* di_forkoff */ +if (XFS_DFORK_APTR (dip) >= (char *)dip + mp->m_sb.sb_inodesize) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +if (dip->di_anextents !=0 && dip->di_forkoff ==0 ) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +if (dip->di_forkoff ==0 && dip->di_aformat !=XFS_DINODE_FMT_EXTENTS ) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + +/* di_aformat */ +if (dip->di_aformat !=XFS_DINODE_FMT_LOCAL && + dip->di_aformat !=XFS_DINODE_FMT_EXTENTS && + dip->di_aformat !=XFS_DINODE_FMT_BTREE ) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + +/* di_anextents */ + nextents =be16_to_cpu (dip->di_anextents); + fork_recs =XFS_DFORK_ASIZE (dip, mp) /sizeof (struct xfs_bmbt_rec ); +switch (dip->di_aformat) { +case XFS_DINODE_FMT_EXTENTS : +if (nextents > fork_recs) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; +case XFS_DINODE_FMT_BTREE : +if (nextents <= fork_recs) +xfs_scrub_ino_set_corrupt (sc, ino, bp); +break ; +default : +if (nextents !=0 ) +xfs_scrub_ino_set_corrupt (sc, ino, bp); + } + +if (flags2) +xfs_scrub_inode_flags2 (sc, bp, dip, ino, mode, flags2); + +xfs_scrub_inode_cowextsize (sc, bp, dip, ino, mode, flags, flags2); +} + +/* Map and read a raw inode. */ +STATIC int +xfs_scrub_inode_map_raw ( +struct xfs_scrub_context *sc, +xfs_ino_t ino, +struct xfs_buf **bpp, +struct xfs_dinode **dipp) +{ +struct xfs_imap imap; +struct xfs_mount *mp = sc->mp; +struct xfs_buf *bp; +struct xfs_dinode *dip; +int error; + + error =xfs_imap (mp, sc->tp, ino, &imap,XFS_IGET_UNTRUSTED ); +if (error == -EINVAL ) { +/* +* Inode could have gotten deleted out from under us; +* just forget about it. +*/ + error = -ENOENT ; + goto out; + } +if (!xfs_scrub_process_error (sc,XFS_INO_TO_AGNO (mp, ino), +XFS_INO_TO_AGBNO (mp, ino), &error)) + goto out; + + error =xfs_trans_read_buf (mp, sc->tp, mp->m_ddev_targp, + imap.im_blkno, imap.im_len,XBF_UNMAPPED , &bp, +NULL ); +if (!xfs_scrub_process_error (sc,XFS_INO_TO_AGNO (mp, ino), +XFS_INO_TO_AGBNO (mp, ino), &error)) + goto out; + +/* Is this really an inode? */ + bp->b_ops = &xfs_inode_buf_ops; + dip =xfs_buf_offset (bp, imap.im_boffset); +if (!xfs_dinode_verify (mp, ino, dip) || + !xfs_dinode_good_version (mp, dip->di_version)) { +xfs_scrub_ino_set_corrupt (sc, ino, bp); + goto out; + } + +/* ...and is it the one we asked for? */ +if (be32_to_cpu (dip->di_gen) != sc->sm->sm_gen) { + error = -ENOENT ; + goto out; + } + + *dipp = dip; + *bpp = bp; +out: +return error; +} + +/* Scrub an inode. */ +int +xfs_scrub_inode ( +struct xfs_scrub_context *sc) +{ +struct xfs_dinode di; +struct xfs_mount *mp = sc->mp; +struct xfs_buf *bp =NULL ; +struct xfs_dinode *dip; +xfs_ino_t ino; + +bool has_shared; +int error =0 ; + +/* Did we get the in-core inode, or are we doing this manually? */ +if (sc->ip) { + ino = sc->ip->i_ino; +xfs_inode_to_disk (sc->ip, &di,0 ); + dip = &di; + }else { +/* Map & read inode. */ + ino = sc->sm->sm_ino; + error =xfs_scrub_inode_map_raw (sc, ino, &bp, &dip); +if (error) + goto out; + } + +xfs_scrub_dinode (sc, bp, dip, ino); +if (sc->sm->sm_flags &XFS_SCRUB_OFLAG_CORRUPT ) + goto out; + +/* Now let's do the things that require a live inode. */ +if (!sc->ip) + goto out; + +/* +* Does this inode have the reflink flag set but no shared extents? +* Set the preening flag if this is the case. +*/ +if (xfs_is_reflink_inode (sc->ip)) { + error =xfs_reflink_inode_has_shared_extents (sc->tp, sc->ip, + &has_shared); +if (!xfs_scrub_process_error (sc,XFS_INO_TO_AGNO (mp, ino), +XFS_INO_TO_AGBNO (mp, ino), &error)) + goto out; +if (!has_shared) +xfs_scrub_ino_set_preen (sc, bp); + } + +out: +if (bp) +xfs_trans_brelse (sc->tp, bp); +return error; +}fs/xfs/scrub/scrub.c +16
-2@@ -30,6 +30,8 @@#include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_inode.h" +#include "xfs_icache.h" +#include "xfs_itable.h" #include "xfs_alloc.h" #include "xfs_alloc_btree.h" #include "xfs_bmap.h" @@ -131,6 +133,7 @@ xfs_scrub_probe( STATIC intxfs_scrub_teardown (struct xfs_scrub_context *sc, +struct xfs_inode *ip_in,int error) {xfs_scrub_ag_free (sc, &sc->sa);@@ -138,6 +141,13 @@ xfs_scrub_teardown(xfs_trans_cancel (sc->tp); sc->tp =NULL ; } +if (sc->ip) { +xfs_iunlock (sc->ip, sc->ilock_flags); +if (sc->ip != ip_in && + !xfs_internal_inum (sc->mp, sc->ip->i_ino)) +iput (VFS_I (sc->ip)); + sc->ip =NULL ; + }return error; }@@ -191,6 +201,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .scrub = xfs_scrub_refcountbt, .has = xfs_sb_version_hasreflink, }, + {/* inode record */ + .setup = xfs_scrub_setup_inode, + .scrub = xfs_scrub_inode, + }, };/* This isn't a stable feature, warn once per day. */ @@ -290,7 +304,7 @@ xfs_scrub_metadata(* Tear down everything we hold, then set up again with * preparation for worst-case scenarios. */ - error =xfs_scrub_teardown (&sc,0 ); + error =xfs_scrub_teardown (&sc, ip,0 );if (error) goto out; try_harder = true;@@ -303,7 +317,7 @@ xfs_scrub_metadata(xfs_alert_ratelimited (mp,"Corruption detected during scrub." ); out_teardown: - error =xfs_scrub_teardown (&sc, error); + error =xfs_scrub_teardown (&sc, ip, error); out:trace_xfs_scrub_done (ip, sm, error);return error;fs/xfs/scrub/scrub.h +2
-1@@ -59,6 +59,7 @@ struct xfs_scrub_context {const struct xfs_scrub_meta_ops *ops;struct xfs_trans *tp;struct xfs_inode *ip; +uint ilock_flags;bool try_harder;/* State tracking for single-AG operations. */ @@ -77,5 +78,6 @@ int xfs_scrub_inobt(struct xfs_scrub_context *sc);int xfs_scrub_finobt (struct xfs_scrub_context *sc);int xfs_scrub_rmapbt (struct xfs_scrub_context *sc);int xfs_scrub_refcountbt (struct xfs_scrub_context *sc); +int xfs_scrub_inode (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */ --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 21/30] xfs: scrub inodes
Quoted 4 lines · Darrick J. Wong
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub the fields within an inode.
.....
Quoted 44 lines
Quoted 44 lines
fs/xfs/scrub/common.c +44
-0+ +/* +* Given an inode and the scrub control structure, grab either the +* inode referenced in the control structure or the inode passed in. +* The inode is not locked. +*/ +int +xfs_scrub_get_inode ( +struct xfs_scrub_context *sc, +struct xfs_inode *ip_in) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_inode *ip =NULL ; +int error; + +/* +* If userspace passed us an AG number or a generation number +* without an inode number, they haven't got a clue so bail out +* immediately. +*/ +if (sc->sm->sm_agno || (sc->sm->sm_gen && !sc->sm->sm_ino)) +return -EINVAL ; + +/* We want to scan the inode we already had opened. */ +if (sc->sm->sm_ino ==0 || sc->sm->sm_ino == ip_in->i_ino) { + sc->ip = ip_in; +return 0 ; + } + +/* Look up the inode, see if the generation number matches. */ +if (xfs_internal_inum (mp, sc->sm->sm_ino)) +return -ENOENT ; + error =xfs_iget (mp,NULL , sc->sm->sm_ino, +XFS_IGET_UNTRUSTED |XFS_IGET_DONTCACHE ,0 , &ip); +if (error == -ENOENT || error == -EINVAL ) { +/* inode doesn't exist... */ +return -ENOENT ; + }else if (error) { +trace_xfs_scrub_op_error (sc, +XFS_INO_TO_AGNO (mp, sc->sm->sm_ino), +XFS_INO_TO_AGBNO (mp, sc->sm->sm_ino), + error, __return_address); +return error; + }
Quoted 8 lines
fs/xfs/scrub/common.c xfs_scrub_checkpoint_log( · line 544
Quoted 8 lines
fs/xfs/scrub/common.c +8
-0+if (VFS_I (ip)->i_generation != sc->sm->sm_gen) { +iput (VFS_I (ip)); +return -ENOENT ; + } + + sc->ip = ip; +return 0 ; +}
Much nicer with the way everything is clearly spelled out :P
Quoted 37 lines
Quoted 37 lines
fs/xfs/scrub/inode.c new file +37
-0+/* Inode core */ + +/* +* di_extsize hint validation is somewhat cumbersome. Rules are: +* +* 1. extent size hint is only valid for directories and regular files +* 2. DIFLAG_EXTSIZE is only valid for regular files +* 3. DIFLAG_EXTSZINHERIT is only valid for directories. +* 4. extsize hint of 0 turns off hints, clears inode flags. +* 5. either flag must be set if extsize != 0 +* 6. Extent size must be a multiple of the appropriate block size. +* 7. extent size hint cannot be longer than maximum extent length +* 8. for non-realtime files, the extent size hint must be limited +* to half the AG size to avoid alignment extending the extent +* beyond the limits of the AG. +*/ +STATIC void +xfs_scrub_inode_extsize ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp, +struct xfs_dinode *dip, +xfs_ino_t ino, +uint16_t mode, +uint16_t flags) +{ +struct xfs_mount *mp = sc->mp; +bool rt_flag; +bool hint_flag; +bool inherit_flag; +uint32_t extsize; +uint32_t extsize_bytes; +uint32_t blocksize_bytes; + + rt_flag = (flags &XFS_DIFLAG_REALTIME ); + hint_flag = (flags &XFS_DIFLAG_EXTSIZE ); + inherit_flag = (flags &XFS_DIFLAG_EXTSZINHERIT ); + extsize =be32_to_cpu (dip->di_extsize);
Quoted 8 lines
fs/xfs/scrub/inode.c line 130
Quoted 8 lines
fs/xfs/scrub/inode.c new file +8
-0+ extsize_bytes =XFS_FSB_TO_B (sc->mp, extsize); + +if (rt_flag) + blocksize_bytes = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; +else + blocksize_bytes = mp->m_sb.sb_blocksize; + +if ((hint_flag || inherit_flag) && (!S_ISDIR (mode) && !S_ISREG (mode)))
Logic is a correct but reads funny:
if ((hint_flag || inherit_flag) && !(S_ISREG(mode) || S_ISDIR(mode)))
Quoted 6 lines
Quoted 6 lines
+/*
+ *di_cowextsizehint validation is somewhat cumbersome. Rules are:
+ *
+ * 1. flag requires reflink feature
+ * 2. cow extent size hint is only valid for directories and regular files
+ * 3. cow extsize hint of 0 turns off hints, clears inode flags.
Quoted 8 lines
Quoted 8 lines
+ * 4. either flag must be set if cow extsize != 0
+ * 5. flag cannot be set for rt files
+ * 6. Extent size must be a multiple of the appropriate block size.
+ * 7. extent size hint cannot be longer than maximum extent length
+ * 8. the extent size hint must be limited
+ * to half the AG size to avoid alignment extending the extent
+ * beyond the limits of the AG.
+ */
Perhaps this comment doesn't need duplicating for a 3rd time. Maybe for both di_extsize and di_cowextsize just say:
/*
* Extent size hints have explicit rules. They are documented at
* xfs_ioctl_setattr_check_extsize() - these functions need to be
* kept in sync with each other.
*/
Quoted 13 lines
Quoted 13 lines
fs/xfs/scrub/inode.c new file +13
-0+STATIC void +xfs_scrub_inode_cowextsize ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp, +struct xfs_dinode *dip, +xfs_ino_t ino, +uint16_t mode, +uint16_t flags, +uint64_t flags2) +{ +struct xfs_mount *mp = sc->mp; +bool rt_flag; +bool hint_flag;
Quoted 8 lines
fs/xfs/scrub/inode.c line 191
Quoted 6 lines
fs/xfs/scrub/inode.c new file +6
-0+uint32_t extsize; +uint32_t extsize_bytes; + + rt_flag = (flags &XFS_DIFLAG_REALTIME ); + hint_flag = (flags2 &XFS_DIFLAG2_COWEXTSIZE ); + extsize =be32_to_cpu (dip->di_extsize);Doh, this ought to be extsize =
be32_to_cpu(dip->di_cowextsize); will fix.
Yup, with that fix in place all the spurious inode warnings I was getting went away.
Quoted 28 lines
Quoted 28 lines
fs/xfs/scrub/inode.c new file +28
-0+/* Map and read a raw inode. */ +STATIC int +xfs_scrub_inode_map_raw ( +struct xfs_scrub_context *sc, +xfs_ino_t ino, +struct xfs_buf **bpp, +struct xfs_dinode **dipp) +{ +struct xfs_imap imap; +struct xfs_mount *mp = sc->mp; +struct xfs_buf *bp; +struct xfs_dinode *dip; +int error; + + error =xfs_imap (mp, sc->tp, ino, &imap,XFS_IGET_UNTRUSTED ); +if (error == -EINVAL ) { +/* +* Inode could have gotten deleted out from under us; +* just forget about it. +*/ + error = -ENOENT ; + goto out; + } +if (!xfs_scrub_process_error (sc,XFS_INO_TO_AGNO (mp, ino), +XFS_INO_TO_AGBNO (mp, ino), &error)) + goto out; + + error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp,
Quoted 8 lines
fs/xfs/scrub/inode.c line 534
Quoted 8 lines
fs/xfs/scrub/inode.c new file +8
-0+ imap.im_blkno, imap.im_len,XBF_UNMAPPED , &bp, + NULL); +if (!xfs_scrub_process_error (sc,XFS_INO_TO_AGNO (mp, ino), +XFS_INO_TO_AGBNO (mp, ino), &error)) + goto out; + +/* Is this really an inode? */ + bp->b_ops = &xfs_inode_buf_ops;
A comment here on why we skip the read verifier when pulling in the inode buffer would be nice.
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 21/30] xfs: scrub inodes
Quoted 111 lines · Dave Chinner
Quoted 4 lines · Darrick J. Wong
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub the fields within an inode.
.....
Quoted 44 lines
Quoted 44 lines
fs/xfs/scrub/common.c +44
-0+ +/* +* Given an inode and the scrub control structure, grab either the +* inode referenced in the control structure or the inode passed in. +* The inode is not locked. +*/ +int +xfs_scrub_get_inode ( +struct xfs_scrub_context *sc, +struct xfs_inode *ip_in) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_inode *ip =NULL ; +int error; + +/* +* If userspace passed us an AG number or a generation number +* without an inode number, they haven't got a clue so bail out +* immediately. +*/ +if (sc->sm->sm_agno || (sc->sm->sm_gen && !sc->sm->sm_ino)) +return -EINVAL ; + +/* We want to scan the inode we already had opened. */ +if (sc->sm->sm_ino ==0 || sc->sm->sm_ino == ip_in->i_ino) { + sc->ip = ip_in; +return 0 ; + } + +/* Look up the inode, see if the generation number matches. */ +if (xfs_internal_inum (mp, sc->sm->sm_ino)) +return -ENOENT ; + error =xfs_iget (mp,NULL , sc->sm->sm_ino, +XFS_IGET_UNTRUSTED |XFS_IGET_DONTCACHE ,0 , &ip); +if (error == -ENOENT || error == -EINVAL ) { +/* inode doesn't exist... */ +return -ENOENT ; + }else if (error) { +trace_xfs_scrub_op_error (sc, +XFS_INO_TO_AGNO (mp, sc->sm->sm_ino), +XFS_INO_TO_AGBNO (mp, sc->sm->sm_ino), + error, __return_address); +return error; + }Quoted 8 lines
fs/xfs/scrub/common.c xfs_scrub_checkpoint_log( · line 544
Quoted 8 lines
fs/xfs/scrub/common.c +8
-0+if (VFS_I (ip)->i_generation != sc->sm->sm_gen) { +iput (VFS_I (ip)); +return -ENOENT ; + } + + sc->ip = ip; +return 0 ; +}Much nicer with the way everything is clearly spelled out :P
Quoted 37 lines
Quoted 37 lines
fs/xfs/scrub/inode.c new file +37
-0+/* Inode core */ + +/* +* di_extsize hint validation is somewhat cumbersome. Rules are: +* +* 1. extent size hint is only valid for directories and regular files +* 2. DIFLAG_EXTSIZE is only valid for regular files +* 3. DIFLAG_EXTSZINHERIT is only valid for directories. +* 4. extsize hint of 0 turns off hints, clears inode flags. +* 5. either flag must be set if extsize != 0 +* 6. Extent size must be a multiple of the appropriate block size. +* 7. extent size hint cannot be longer than maximum extent length +* 8. for non-realtime files, the extent size hint must be limited +* to half the AG size to avoid alignment extending the extent +* beyond the limits of the AG. +*/ +STATIC void +xfs_scrub_inode_extsize ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp, +struct xfs_dinode *dip, +xfs_ino_t ino, +uint16_t mode, +uint16_t flags) +{ +struct xfs_mount *mp = sc->mp; +bool rt_flag; +bool hint_flag; +bool inherit_flag; +uint32_t extsize; +uint32_t extsize_bytes; +uint32_t blocksize_bytes; + + rt_flag = (flags &XFS_DIFLAG_REALTIME ); + hint_flag = (flags &XFS_DIFLAG_EXTSIZE ); + inherit_flag = (flags &XFS_DIFLAG_EXTSZINHERIT ); + extsize =be32_to_cpu (dip->di_extsize);Quoted 8 lines
fs/xfs/scrub/inode.c line 130
Quoted 8 lines
fs/xfs/scrub/inode.c new file +8
-0+ extsize_bytes =XFS_FSB_TO_B (sc->mp, extsize); + +if (rt_flag) + blocksize_bytes = mp->m_sb.sb_rextsize << mp->m_sb.sb_blocklog; +else + blocksize_bytes = mp->m_sb.sb_blocksize; + +if ((hint_flag || inherit_flag) && (!S_ISDIR (mode) && !S_ISREG (mode)))Logic is a correct but reads funny:
Quoted 2 lines
if ((hint_flag || inherit_flag) && !(S_ISREG(mode) || S_ISDIR(mode)))
Ok. Fixed this and the cowextsize.
Quoted 15 lines
Quoted 6 lines
Quoted 6 lines
+/*
+ *di_cowextsizehint validation is somewhat cumbersome. Rules are:
+ *
+ * 1. flag requires reflink feature
+ * 2. cow extent size hint is only valid for directories and regular files
+ * 3. cow extsize hint of 0 turns off hints, clears inode flags.Quoted 8 lines
Quoted 8 lines
+ * 4. either flag must be set if cow extsize != 0
+ * 5. flag cannot be set for rt files
+ * 6. Extent size must be a multiple of the appropriate block size.
+ * 7. extent size hint cannot be longer than maximum extent length
+ * 8. the extent size hint must be limited
+ * to half the AG size to avoid alignment extending the extent
+ * beyond the limits of the AG.
+ */
Quoted 8 lines
Perhaps this comment doesn't need duplicating for a 3rd time. Maybe for both
di_extsizeanddi_cowextsizejust say:/*
* Extent size hints have explicit rules. They are documented at
*xfs_ioctl_setattr_check_extsize()- these functions need to be
* kept in sync with each other.
*/
Ok. I've also amended the comment at xfs_ioctl_setattr_check_extsize to remind people to keep the scrub version in sync.
Quoted 56 lines
Quoted 13 lines
Quoted 13 lines
fs/xfs/scrub/inode.c new file +13
-0+STATIC void +xfs_scrub_inode_cowextsize ( +struct xfs_scrub_context *sc, +struct xfs_buf *bp, +struct xfs_dinode *dip, +xfs_ino_t ino, +uint16_t mode, +uint16_t flags, +uint64_t flags2) +{ +struct xfs_mount *mp = sc->mp; +bool rt_flag; +bool hint_flag;Quoted 8 lines
fs/xfs/scrub/inode.c line 191
Quoted 6 lines
fs/xfs/scrub/inode.c new file +6
-0+uint32_t extsize; +uint32_t extsize_bytes; + + rt_flag = (flags &XFS_DIFLAG_REALTIME ); + hint_flag = (flags2 &XFS_DIFLAG2_COWEXTSIZE ); + extsize =be32_to_cpu (dip->di_extsize);Doh, this ought to be extsize =
be32_to_cpu(dip->di_cowextsize); will fix.Yup, with that fix in place all the spurious inode warnings I was getting went away.
Quoted 31 lines
Quoted 31 lines
fs/xfs/scrub/inode.c new file +31
-0+/* Map and read a raw inode. */ +STATIC int +xfs_scrub_inode_map_raw( + struct xfs_scrub_context *sc, + xfs_ino_t ino, + struct xfs_buf **bpp, + struct xfs_dinode **dipp) +{ + struct xfs_imap imap; + struct xfs_mount *mp = sc->mp; + struct xfs_buf *bp; + struct xfs_dinode *dip; + int error; + + error = xfs_imap(mp, sc->tp, ino, &imap, XFS_IGET_UNTRUSTED); + if (error == -EINVAL) { + /* + * Inode could have gotten deleted out from under us; + * just forget about it. + */ + error = -ENOENT; + goto out; + } + if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino), + XFS_INO_TO_AGBNO(mp, ino), &error)) + goto out; + + error = xfs_trans_read_buf(mp, sc->tp, mp->m_ddev_targp, + imap.im_blkno, imap.im_len, XBF_UNMAPPED, &bp, + NULL); + if (!xfs_scrub_process_error(sc, XFS_INO_TO_AGNO(mp, ino),
Quoted 8 lines
Quoted 5 lines
fs/xfs/scrub/inode.c line 534
Quoted 5 lines
fs/xfs/scrub/inode.c new file +5
-0+ XFS_INO_TO_AGBNO(mp, ino), &error)) + goto out; + +/* Is this really an inode? */ + bp->b_ops = &xfs_inode_buf_ops;A comment here on why we skip the read verifier when pulling in the inode buffer would be nice.
/*
* Is this really an inode? We disabled verifiers in the above
* xfs_trans_read_buf call because the inode buffer verifier
* fails on /any/ inode record in the inode cluster with a bad
* magic or version number, not just the one that we're
* checking. Therefore, grab the buffer unconditionally, attach
* the inode verifiers by hand, and run the inode verifier only
* on the one inode we want.
*/
--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
PATCH v2 21/30 xfs: scrub inodes
In reply to Darrick J. WongScrub the fields within an inode.
---
v2: fix illegible logic, enhance comments
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 3 fs/xfs/scrub/common.c | 54 ++++ fs/xfs/scrub/common.h | 3 fs/xfs/scrub/inode.c | 605 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 18 + fs/xfs/scrub/scrub.h | 2 fs/xfs/xfs_ioctl.c | 4 8 files changed, 687 insertions(+), 3 deletions(-) create mode 100644 fs/xfs/scrub/inode.c
fs/xfs/Makefile +1 -0
@@ -151,6 +151,7 @@ xfs-y += $(addprefix scrub/, \ btree.o \ common.o \ ialloc.o \ + inode.o \ refcount.o \ rmap.o \ scrub.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -494,9 +494,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_FINOBT 8/* free inode btree */ #define XFS_SCRUB_TYPE_RMAPBT 9/* reverse mapping btree */ #define XFS_SCRUB_TYPE_REFCNTBT 10/* reference count btree */ +#define XFS_SCRUB_TYPE_INODE 11/* inode record */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 11 +#define XFS_SCRUB_TYPE_NR 12/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.c +54 -0
63 lines not on this page · raw message
fs/xfs/scrub/common.h +3 -0
@@ -87,6 +87,8 @@ int xfs_scrub_setup_ag_rmapbt(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_ag_refcountbt (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_inode (struct xfs_scrub_context *sc, +struct xfs_inode *ip);void xfs_scrub_ag_free (struct xfs_scrub_context *sc,struct xfs_scrub_ag *sa);
@@ -105,5 +107,6 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,int xfs_scrub_setup_ag_btree (struct xfs_scrub_context *sc,struct xfs_inode *ip,bool force_log); +int xfs_scrub_get_inode (struct xfs_scrub_context *sc,struct xfs_inode *ip_in);#endif /* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/inode.c new file +605 -0
605 lines not on this page · raw message
fs/xfs/scrub/scrub.c +16 -2
@@ -30,6 +30,8 @@#include "xfs_trans.h" #include "xfs_sb.h" #include "xfs_inode.h" +#include "xfs_icache.h" +#include "xfs_itable.h" #include "xfs_alloc.h" #include "xfs_alloc_btree.h" #include "xfs_bmap.h"
@@ -141,6 +143,7 @@ xfs_scrub_probe( STATIC intxfs_scrub_teardown (struct xfs_scrub_context *sc, +struct xfs_inode *ip_in,int error) {xfs_scrub_ag_free (sc, &sc->sa);
@@ -148,6 +151,13 @@ xfs_scrub_teardown(xfs_trans_cancel (sc->tp); sc->tp =NULL ; } +if (sc->ip) { +xfs_iunlock (sc->ip, sc->ilock_flags); +if (sc->ip != ip_in && + !xfs_internal_inum (sc->mp, sc->ip->i_ino)) +iput (VFS_I (sc->ip)); + sc->ip =NULL ; + }return error; }
@@ -201,6 +211,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .scrub = xfs_scrub_refcountbt, .has = xfs_sb_version_hasreflink, }, + {/* inode record */ + .setup = xfs_scrub_setup_inode, + .scrub = xfs_scrub_inode, + }, };/* This isn't a stable feature, warn once per day. */
@@ -300,7 +314,7 @@ xfs_scrub_metadata(* Tear down everything we hold, then set up again with * preparation for worst-case scenarios. */ - error =xfs_scrub_teardown (&sc,0 ); + error =xfs_scrub_teardown (&sc, ip,0 );if (error) goto out; try_harder = true;
@@ -313,7 +327,7 @@ xfs_scrub_metadata(xfs_alert_ratelimited (mp,"Corruption detected during scrub." ); out_teardown: - error =xfs_scrub_teardown (&sc, error); + error =xfs_scrub_teardown (&sc, ip, error); out:trace_xfs_scrub_done (ip, sm, error);return error;
fs/xfs/scrub/scrub.h +2 -0
@@ -59,6 +59,7 @@ struct xfs_scrub_context {const struct xfs_scrub_meta_ops *ops;struct xfs_trans *tp;struct xfs_inode *ip; +uint ilock_flags;bool try_harder;/* State tracking for single-AG operations. */
@@ -77,5 +78,6 @@ int xfs_scrub_inobt(struct xfs_scrub_context *sc);int xfs_scrub_finobt (struct xfs_scrub_context *sc);int xfs_scrub_rmapbt (struct xfs_scrub_context *sc);int xfs_scrub_refcountbt (struct xfs_scrub_context *sc); +int xfs_scrub_inode (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/xfs_ioctl.c +4 -0
@@ -1202,6 +1202,8 @@ xfs_ioctl_setattr_get_trans(* 8. for non-realtime files, the extent size hint must be limited * to half the AG size to avoid alignment extending the extent beyond the * limits of the AG. +* +* Please keep this function in sync with xfs_scrub_inode_extsize. */ static int xfs_ioctl_setattr_check_extsize(
@@ -1258,6 +1260,8 @@ xfs_ioctl_setattr_check_extsize(* 5. Extent size must be a multiple of the appropriate block size. * 6. The extent size hint must be limited to half the AG size to avoid * alignment extending the extent beyond the limits of the AG. +* +* Please keep this function in sync with xfs_scrub_inode_cowextsize. */ static int xfs_ioctl_setattr_check_cowextsize(
Re: [PATCH v2 21/30] xfs: scrub inodes
Quoted 6 lines · Darrick J. Wong
Scrub the fields within an inode.
Signed-off-by Darrick J. Wong <[email protected]>---
v2: fix illegible logic, enhance comments
---
Good to go.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 22/30 xfs: scrub inode block mappings
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Scrub an individual inode's block mappings to make sure they make sense.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 5 + fs/xfs/scrub/bmap.c | 362 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/common.h | 5 + fs/xfs/scrub/scrub.c | 12 ++ fs/xfs/scrub/scrub.h | 3 6 files changed, 386 insertions(+), 2 deletions(-) create mode 100644 fs/xfs/scrub/bmap.c
fs/xfs/Makefile +1 -0
@@ -148,6 +148,7 @@ xfs-y += $(addprefix scrub/, \ trace.o \ agheader.o \ alloc.o \ + bmap.o \ btree.o \ common.o \ ialloc.o \
fs/xfs/libxfs/xfs_fs.h +4 -1
@@ -495,9 +495,12 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_RMAPBT 9/* reverse mapping btree */ #define XFS_SCRUB_TYPE_REFCNTBT 10/* reference count btree */ #define XFS_SCRUB_TYPE_INODE 11/* inode record */ +#define XFS_SCRUB_TYPE_BMBTD 12/* data fork block mapping */ +#define XFS_SCRUB_TYPE_BMBTA 13/* attr fork block mapping */ +#define XFS_SCRUB_TYPE_BMBTC 14/* CoW fork block mapping */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 12 +#define XFS_SCRUB_TYPE_NR 15/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/bmap.c new file +362 -0
362 lines not on this page · raw message
fs/xfs/scrub/common.h +4 -1
@@ -89,7 +89,10 @@ int xfs_scrub_setup_ag_refcountbt(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_inode (struct xfs_scrub_context *sc,struct xfs_inode *ip); - +int xfs_scrub_setup_inode_bmap (struct xfs_scrub_context *sc, +struct xfs_inode *ip); +int xfs_scrub_setup_inode_bmap_data (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,
fs/xfs/scrub/scrub.c +12 -0
@@ -205,6 +205,18 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_inode, .scrub = xfs_scrub_inode, }, + { /* inode data fork */ + .setup = xfs_scrub_setup_inode_bmap, + .scrub = xfs_scrub_bmap_data, + }, + { /* inode attr fork */ + .setup = xfs_scrub_setup_inode_bmap, + .scrub = xfs_scrub_bmap_attr, + }, + { /* inode CoW fork */ + .setup = xfs_scrub_setup_inode_bmap, + .scrub = xfs_scrub_bmap_cow, + }, }; /* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +3 -0
@@ -79,5 +79,8 @@ int xfs_scrub_finobt(struct xfs_scrub_context *sc);int xfs_scrub_rmapbt (struct xfs_scrub_context *sc);int xfs_scrub_refcountbt (struct xfs_scrub_context *sc);int xfs_scrub_inode (struct xfs_scrub_context *sc); +int xfs_scrub_bmap_data (struct xfs_scrub_context *sc); +int xfs_scrub_bmap_attr (struct xfs_scrub_context *sc); +int xfs_scrub_bmap_cow (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 22/30] xfs: scrub inode block mappings
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub an individual inode's block mappings to make sure they make sense.
....
Quoted 24 lines
fs/xfs/scrub/bmap.c new file +24
-0+/* Set us up with an inode's bmap. */ +int +xfs_scrub_setup_inode_bmap ( +struct xfs_scrub_context *sc, +struct xfs_inode *ip) +{ +struct xfs_mount *mp = sc->mp; +int error; + + error =xfs_scrub_get_inode (sc, ip); +if (error) + goto out; + + sc->ilock_flags =XFS_IOLOCK_EXCL |XFS_MMAPLOCK_EXCL ; +xfs_ilock (sc->ip, sc->ilock_flags); + +/* +* We don't want any ephemeral data fork updates sitting around +* while we inspect block mappings, so wait for directio to finish +* and flush dirty data if we have delalloc reservations. +*/ +if (S_ISREG (VFS_I (sc->ip)->i_mode) && + sc->sm->sm_type ==XFS_SCRUB_TYPE_BMBTD ) { +inode_dio_wait (VFS_I (sc->ip));
fs/xfs/scrub/bmap.c line 79
Quoted 10 lines
fs/xfs/scrub/bmap.c new file +10
-0+ 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;
I'll point this out just to say I've seen it. It's a little out of place for this patch set, but it's harmless.
fs/xfs/scrub/bmap.c line 174
Quoted 3 lines
fs/xfs/scrub/bmap.c new file +3
-0+/* Scrub a bmbt record. */ +STATIC int +xfs_scrub_bmapbt_helper(
s/helper/rec/
Quoted 35 lines
+ *
+ * First we scan every record in every btree block, if applicable.
+ * Then we unconditionally scan the incore extent cache.
+ */fs/xfs/scrub/bmap.c new file +31
-0+STATIC int +xfs_scrub_bmap ( +struct xfs_scrub_context *sc, +int whichfork) +{ +struct xfs_bmbt_irec irec; +struct xfs_scrub_bmap_info info = {0 }; +struct xfs_mount *mp = sc->mp; +struct xfs_inode *ip = sc->ip; +struct xfs_ifork *ifp; +xfs_fileoff_t endoff; +xfs_extnum_t idx; +bool found; +int error =0 ; + + ifp =XFS_IFORK_PTR (ip, whichfork); + + info.is_rt = whichfork ==XFS_DATA_FORK &&XFS_IS_REALTIME_INODE (ip); + info.whichfork = whichfork; + info.is_shared = whichfork ==XFS_DATA_FORK &&xfs_is_reflink_inode (ip); + info.sc = sc; + +switch (whichfork) { +case XFS_COW_FORK : +/* Non-existent CoW forks are ignorable. */ +if (!ifp) + goto out; +/* No CoW forks on non-reflink inodes/filesystems. */ +if (!xfs_is_reflink_inode (ip)) { +xfs_scrub_ino_set_corrupt (sc, sc->ip->i_ino,NULL ); + goto out;
fs/xfs/scrub/bmap.c line 276
Quoted 10 lines
fs/xfs/scrub/bmap.c new file +10
-0+ } +break ; +case XFS_ATTR_FORK : +if (!ifp) + goto out; +if (!xfs_sb_version_hasattr (&mp->m_sb) && + !xfs_sb_version_hasattr2 (&mp->m_sb)) +xfs_scrub_ino_set_corrupt (sc, sc->ip->i_ino,NULL ); +break ; + }
Missing a default option here for other values. Some compilers will warn about this.
Otherwise this look fine.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 22/30] xfs: scrub inode block mappings
Quoted 36 lines · Dave Chinner
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub an individual inode's block mappings to make sure they make sense.
....
Quoted 29 lines
fs/xfs/scrub/bmap.c new file +29
-0+/* Set us up with an inode's bmap. */ +int +xfs_scrub_setup_inode_bmap ( +struct xfs_scrub_context *sc, +struct xfs_inode *ip) +{ +struct xfs_mount *mp = sc->mp; +int error; + + error =xfs_scrub_get_inode (sc, ip); +if (error) + goto out; + + sc->ilock_flags =XFS_IOLOCK_EXCL |XFS_MMAPLOCK_EXCL ; +xfs_ilock (sc->ip, sc->ilock_flags); + +/* +* We don't want any ephemeral data fork updates sitting around +* while we inspect block mappings, so wait for directio to finish +* and flush dirty data if we have delalloc reservations. +*/ +if (S_ISREG (VFS_I (sc->ip)->i_mode) && + sc->sm->sm_type ==XFS_SCRUB_TYPE_BMBTD ) { +inode_dio_wait (VFS_I (sc->ip)); + 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. */
Quoted 8 lines
fs/xfs/scrub/bmap.c line 79
Quoted 5 lines
fs/xfs/scrub/bmap.c new file +5
-0+if (sc->sm->sm_flags &XFS_SCRUB_IFLAG_REPAIR ) { + error =invalidate_inode_pages2 ( +VFS_I (sc->ip)->i_mapping); +if (error) + goto out;I'll point this out just to say I've seen it. It's a little out of place for this patch set, but it's harmless.
Oops, I'll move this to the repair patches since I'm already reworking this patch anyway.
Quoted 46 lines
fs/xfs/scrub/bmap.c line 174
Quoted 3 lines
fs/xfs/scrub/bmap.c new file +3
-0+/* Scrub a bmbt record. */ +STATIC int +xfs_scrub_bmapbt_helper(s/helper/rec/
Quoted 40 lines
+ *
+ * First we scan every record in every btree block, if applicable.
+ * Then we unconditionally scan the incore extent cache.
+ */fs/xfs/scrub/bmap.c new file +36
-0+STATIC int +xfs_scrub_bmap ( +struct xfs_scrub_context *sc, +int whichfork) +{ +struct xfs_bmbt_irec irec; +struct xfs_scrub_bmap_info info = {0 }; +struct xfs_mount *mp = sc->mp; +struct xfs_inode *ip = sc->ip; +struct xfs_ifork *ifp; +xfs_fileoff_t endoff; +xfs_extnum_t idx; +bool found; +int error =0 ; + + ifp =XFS_IFORK_PTR (ip, whichfork); + + info.is_rt = whichfork ==XFS_DATA_FORK &&XFS_IS_REALTIME_INODE (ip); + info.whichfork = whichfork; + info.is_shared = whichfork ==XFS_DATA_FORK &&xfs_is_reflink_inode (ip); + info.sc = sc; + +switch (whichfork) { +case XFS_COW_FORK : +/* Non-existent CoW forks are ignorable. */ +if (!ifp) + goto out; +/* No CoW forks on non-reflink inodes/filesystems. */ +if (!xfs_is_reflink_inode (ip)) { +xfs_scrub_ino_set_corrupt (sc, sc->ip->i_ino,NULL ); + goto out; + } +break ; +case XFS_ATTR_FORK : +if (!ifp) + goto out;
Quoted 8 lines
fs/xfs/scrub/bmap.c line 276
Quoted 5 lines
fs/xfs/scrub/bmap.c new file +5
-0+if (!xfs_sb_version_hasattr (&mp->m_sb) && + !xfs_sb_version_hasattr2 (&mp->m_sb)) +xfs_scrub_ino_set_corrupt (sc, sc->ip->i_ino,NULL ); +break ; + }Missing a default option here for other values. Some compilers will warn about this.
Ok.
Quoted 11 lines
Otherwise this look fine.
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 v12 23/30 xfs: scrub directory/attribute btrees
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Provide a way to check the shape and scrub the hashes and records in a directory or extended attribute btree. These are helper functions for the directory & attribute scrubbers in subsequent patches.
--- fs/xfs/Makefile | 1 fs/xfs/scrub/dabtree.c | 570 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/dabtree.h | 58 +++++ 3 files changed, 629 insertions(+) create mode 100644 fs/xfs/scrub/dabtree.c create mode 100644 fs/xfs/scrub/dabtree.h
fs/xfs/Makefile +1 -0
@@ -151,6 +151,7 @@ xfs-y += $(addprefix scrub/, \ bmap.o \ btree.o \ common.o \ + dabtree.o \ ialloc.o \ inode.o \ refcount.o \
fs/xfs/scrub/dabtree.c new file +570 -0
570 lines not on this page · raw message
fs/xfs/scrub/dabtree.h new file +58 -0
58 lines not on this page · raw message
Re: [PATCH 23/30] xfs: scrub directory/attribute btrees
Quoted 5 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Provide a way to check the shape and scrub the hashes and records in a directory or extended attribute btree. These are helper functions for the directory & attribute scrubbers in subsequent patches.
looks good
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 24/30 xfs: scrub directory metadata
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Scrub the hash tree and all the entries in a directory.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 3 fs/xfs/scrub/common.c | 28 ++++ fs/xfs/scrub/common.h | 4 + fs/xfs/scrub/dir.c | 318 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 4 + fs/xfs/scrub/scrub.h | 1 7 files changed, 358 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/dir.c
fs/xfs/Makefile +1 -0
@@ -152,6 +152,7 @@ xfs-y += $(addprefix scrub/, \ btree.o \ common.o \ dabtree.o \ + dir.o \ ialloc.o \ inode.o \ refcount.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -498,9 +498,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_BMBTD 12/* data fork block mapping */ #define XFS_SCRUB_TYPE_BMBTA 13/* attr fork block mapping */ #define XFS_SCRUB_TYPE_BMBTC 14/* CoW fork block mapping */ +#define XFS_SCRUB_TYPE_DIR 15/* directory */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 15 +#define XFS_SCRUB_TYPE_NR 16/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.c +28 -0
@@ -542,3 +542,31 @@ xfs_scrub_get_inode( sc->ip = ip;return 0 ; } + +/* Set us up to scrub a file's contents. */ +int +xfs_scrub_setup_inode_contents ( +struct xfs_scrub_context *sc, +struct xfs_inode *ip, +unsigned int resblks) +{ +struct xfs_mount *mp = sc->mp; +int error; + + error =xfs_scrub_get_inode (sc, ip); +if (error) +return error; + +/* 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->sm, mp, &sc->tp); +if (error) + goto out; + sc->ilock_flags |=XFS_ILOCK_EXCL ; +xfs_ilock (sc->ip,XFS_ILOCK_EXCL ); + +out: +/* scrub teardown will unlock and release the inode for us */ +return error; +}
fs/xfs/scrub/common.h +4 -0
@@ -93,6 +93,8 @@ int xfs_scrub_setup_inode_bmap(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_inode_bmap_data (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_directory (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,
@@ -111,5 +113,7 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,int xfs_scrub_setup_ag_btree (struct xfs_scrub_context *sc,struct xfs_inode *ip,bool force_log);int xfs_scrub_get_inode (struct xfs_scrub_context *sc,struct xfs_inode *ip_in); +int xfs_scrub_setup_inode_contents (struct xfs_scrub_context *sc, +struct xfs_inode *ip,unsigned int resblks);#endif /* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/dir.c new file +318 -0
318 lines not on this page · raw message
fs/xfs/scrub/scrub.c +4 -0
@@ -217,6 +217,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_inode_bmap, .scrub = xfs_scrub_bmap_cow, }, + {/* directory */ + .setup = xfs_scrub_setup_directory, + .scrub = xfs_scrub_directory, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +1 -0
@@ -82,5 +82,6 @@ int xfs_scrub_inode(struct xfs_scrub_context *sc);int xfs_scrub_bmap_data (struct xfs_scrub_context *sc);int xfs_scrub_bmap_attr (struct xfs_scrub_context *sc);int xfs_scrub_bmap_cow (struct xfs_scrub_context *sc); +int xfs_scrub_directory (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 24/30] xfs: scrub directory metadata
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub the hash tree and all the entries in a directory.
.....
Quoted 17 lines
fs/xfs/scrub/dir.c new file +17
-0+/* Check that an inode's mode matches a given DT_ type. */ +STATIC int +xfs_scrub_dir_check_ftype ( +struct xfs_scrub_dir_ctx *sdc, +xfs_fileoff_t offset, +xfs_ino_t inum, +int dtype) +{ +struct xfs_mount *mp = sdc->sc->mp; +struct xfs_inode *ip; +int ino_dtype; +int error =0 ; + +if (!xfs_sb_version_hasftype (&mp->m_sb)) { +if (dtype !=DT_UNKNOWN && dtype !=DT_DIR ) +xfs_scrub_fblock_set_corrupt (sdc->sc,XFS_DATA_FORK , + offset);
fs/xfs/scrub/dir.c line 92
Quoted 10 lines
fs/xfs/scrub/dir.c new file +10
-0+ goto out; + } + + error =xfs_iget (mp, sdc->sc->tp, inum,XFS_IGET_DONTCACHE ,0 , &ip); +if (!xfs_scrub_fblock_process_error (sdc->sc,XFS_DATA_FORK , offset, + &error)) + goto out; + +/* Convert mode to the DT_* values that dir_emit uses. */ + ino_dtype = (VFS_I (ip)->i_mode &S_IFMT ) >>12 ;
xfs_mode_to_ftype() ?
Otherwise it looks ok.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 24/30] xfs: scrub directory metadata
Quoted 27 lines · Dave Chinner
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub the hash tree and all the entries in a directory.
.....
Quoted 21 lines
fs/xfs/scrub/dir.c new file +21
-0+/* Check that an inode's mode matches a given DT_ type. */ +STATIC int +xfs_scrub_dir_check_ftype ( +struct xfs_scrub_dir_ctx *sdc, +xfs_fileoff_t offset, +xfs_ino_t inum, +int dtype) +{ +struct xfs_mount *mp = sdc->sc->mp; +struct xfs_inode *ip; +int ino_dtype; +int error =0 ; + +if (!xfs_sb_version_hasftype (&mp->m_sb)) { +if (dtype !=DT_UNKNOWN && dtype !=DT_DIR ) +xfs_scrub_fblock_set_corrupt (sdc->sc,XFS_DATA_FORK , + offset); + goto out; + } + + error =xfs_iget (mp, sdc->sc->tp, inum,XFS_IGET_DONTCACHE ,0 , &ip);
Quoted 8 lines
fs/xfs/scrub/dir.c line 92
Quoted 6 lines
fs/xfs/scrub/dir.c new file +6
-0+if (!xfs_scrub_fblock_process_error (sdc->sc,XFS_DATA_FORK , offset, + &error)) + goto out; + +/* Convert mode to the DT_* values that dir_emit uses. */ + ino_dtype = (VFS_I (ip)->i_mode &S_IFMT ) >>12 ;
xfs_mode_to_ftype()?
Yep, will fix.
--D
Quoted 11 lines
Otherwise it 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
PATCH v2 24/30 xfs: scrub directory metadata
In reply to Darrick J. WongScrub the hash tree and all the entries in a directory.
---
v2: use helpers to extract DT_ codes, #define buffer size magic numbers
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_dir2.c | 4 - fs/xfs/libxfs/xfs_fs.h | 3 fs/xfs/scrub/common.c | 28 ++++ fs/xfs/scrub/common.h | 4 + fs/xfs/scrub/dir.c | 320 +++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 4 + fs/xfs/scrub/scrub.h | 1 fs/xfs/xfs_dir2_readdir.c | 2 fs/xfs/xfs_file.c | 2 fs/xfs/xfs_mount.h | 17 ++ 11 files changed, 382 insertions(+), 4 deletions(-) create mode 100644 fs/xfs/scrub/dir.c
fs/xfs/Makefile +1 -0
@@ -152,6 +152,7 @@ xfs-y += $(addprefix scrub/, \ btree.o \ common.o \ dabtree.o \ + dir.o \ ialloc.o \ inode.o \ refcount.o \
fs/xfs/libxfs/xfs_dir2.c +3 -1
@@ -39,7 +39,9 @@ struct xfs_name xfs_name_dotdot = { (unsigned char *)"..", 2, XFS_DIR3_FT_DIR };/* * Convert inode mode to directory entry filetype */ -unsigned char xfs_mode_to_ftype (int mode) +unsigned char +xfs_mode_to_ftype ( +int mode) {switch (mode &S_IFMT ) {case S_IFREG :
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -498,9 +498,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_BMBTD 12/* data fork block mapping */ #define XFS_SCRUB_TYPE_BMBTA 13/* attr fork block mapping */ #define XFS_SCRUB_TYPE_BMBTC 14/* CoW fork block mapping */ +#define XFS_SCRUB_TYPE_DIR 15/* directory */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 15 +#define XFS_SCRUB_TYPE_NR 16/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.c +28 -0
@@ -542,3 +542,31 @@ xfs_scrub_get_inode( sc->ip = ip;return 0 ; } + +/* Set us up to scrub a file's contents. */ +int +xfs_scrub_setup_inode_contents ( +struct xfs_scrub_context *sc, +struct xfs_inode *ip, +unsigned int resblks) +{ +struct xfs_mount *mp = sc->mp; +int error; + + error =xfs_scrub_get_inode (sc, ip); +if (error) +return error; + +/* 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->sm, mp, &sc->tp); +if (error) + goto out; + sc->ilock_flags |=XFS_ILOCK_EXCL ; +xfs_ilock (sc->ip,XFS_ILOCK_EXCL ); + +out: +/* scrub teardown will unlock and release the inode for us */ +return error; +}
fs/xfs/scrub/common.h +4 -0
@@ -93,6 +93,8 @@ int xfs_scrub_setup_inode_bmap(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_inode_bmap_data (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_directory (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,
@@ -111,5 +113,7 @@ int xfs_scrub_walk_agfl(struct xfs_scrub_context *sc,int xfs_scrub_setup_ag_btree (struct xfs_scrub_context *sc,struct xfs_inode *ip,bool force_log);int xfs_scrub_get_inode (struct xfs_scrub_context *sc,struct xfs_inode *ip_in); +int xfs_scrub_setup_inode_contents (struct xfs_scrub_context *sc, +struct xfs_inode *ip,unsigned int resblks);#endif /* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/dir.c new file +320 -0
320 lines not on this page · raw message
fs/xfs/scrub/scrub.c +4 -0
@@ -227,6 +227,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_inode_bmap, .scrub = xfs_scrub_bmap_cow, }, + {/* directory */ + .setup = xfs_scrub_setup_directory, + .scrub = xfs_scrub_directory, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +1 -0
@@ -82,5 +82,6 @@ int xfs_scrub_inode(struct xfs_scrub_context *sc);int xfs_scrub_bmap_data (struct xfs_scrub_context *sc);int xfs_scrub_bmap_attr (struct xfs_scrub_context *sc);int xfs_scrub_bmap_cow (struct xfs_scrub_context *sc); +int xfs_scrub_directory (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/xfs_dir2_readdir.c +1 -1
@@ -41,7 +41,7 @@ static unsigned char xfs_dir3_filetype_table[] = { DT_FIFO, DT_SOCK, DT_LNK, DT_WHT, }; -static unsigned char +unsigned char xfs_dir3_get_dtype (struct xfs_mount *mp,uint8_t filetype)
fs/xfs/xfs_file.c +1 -1
@@ -979,7 +979,7 @@ xfs_file_readdir(* point we can change the ->readdir prototype to include the * buffer size. For now we use the current glibc buffer size. */ - bufsize = (size_t )min_t (loff_t,32768 , ip->i_d.di_size); + bufsize = (size_t )min_t (loff_t,XFS_READDIR_BUFSIZE , ip->i_d.di_size);return xfs_readdir (NULL , ip, ctx, bufsize); }
fs/xfs/xfs_mount.h +17 -0
@@ -446,4 +446,21 @@ int xfs_zero_extent(struct xfs_inode *ip, xfs_fsblock_t start_fsb,struct xfs_error_cfg *xfs_error_get_cfg (struct xfs_mount *mp,int error_class,int error); +/* +* The Linux API doesn't pass down the total size of the buffer +* we read into down to the filesystem. With the filldir concept +* it's not needed for correct information, but the XFS dir2 leaf +* code wants an estimate of the buffer size to calculate it's +* readahead window and size the buffers used for mapping to +* physical blocks. +* +* Try to give it an estimate that's good enough, maybe at some +* point we can change the ->readdir prototype to include the +* buffer size. For now we use the current glibc buffer size. +* musl libc hardcodes 2k and dietlibc uses PAGE_SIZE. +*/ +#define XFS_READDIR_BUFSIZE (32768) + +unsigned char xfs_dir3_get_dtype (struct xfs_mount *mp,uint8_t filetype); +#endif /* __XFS_MOUNT_H__ */
Re: [PATCH v2 24/30] xfs: scrub directory metadata
Quoted 6 lines · Darrick J. Wong
Scrub the hash tree and all the entries in a directory.
Signed-off-by Darrick J. Wong <[email protected]>---
v2: use helpers to extractDT_codes, #define buffer size magic numbers
---
....
Quoted 16 lines
fs/xfs/xfs_mount.h +8
-0@@ -446,4 +446,21 @@ int xfs_zero_extent(struct xfs_inode *ip, xfs_fsblock_t start_fsb,struct xfs_error_cfg *xfs_error_get_cfg (struct xfs_mount *mp,int error_class,int error); +/* +* The Linux API doesn't pass down the total size of the buffer +* we read into down to the filesystem. With the filldir concept +* it's not needed for correct information, but the XFS dir2 leaf +* code wants an estimate of the buffer size to calculate it's +* readahead window and size the buffers used for mapping to +* physical blocks. +*
fs/xfs/xfs_mount.h int xfs_zero_extent(struct xfs_inode *ip, xfs_fsblock_t start_fsb, · line 466
Quoted 10 lines
fs/xfs/xfs_mount.h +9
-0+* Try to give it an estimate that's good enough, maybe at some +* point we can change the ->readdir prototype to include the +* buffer size. For now we use the current glibc buffer size. +* musl libc hardcodes 2k and dietlibc uses PAGE_SIZE. +*/ +#define XFS_READDIR_BUFSIZE (32768) + +unsigned char xfs_dir3_get_dtype (struct xfs_mount *mp,uint8_t filetype); +#endif /* __XFS_MOUNT_H__ */
I think these belong in xfs_dir2.h, next to the declaration of xfs_mode_to_ftype()....
Other than that,
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 25/30 xfs: scrub directory freespace
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Check the free space information in a directory.
--- fs/xfs/scrub/dir.c | 425 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 425 insertions(+)
fs/xfs/scrub/dir.c +425 -0
@@ -250,6 +250,426 @@ xfs_scrub_dir_rec(return error; } +/* +* Is this unused entry either in the bestfree or smaller than all of them? +* We assume the bestfrees are sorted longest to shortest, and that there +* aren't any bogus entries. +*/ +static inline void +xfs_scrub_directory_check_free_entry ( +struct xfs_scrub_context *sc, +xfs_dablk_t lblk, +struct xfs_dir2_data_free *bf, +struct xfs_dir2_data_unused *dup) +{ +struct xfs_dir2_data_free *dfp; +unsigned int dup_length; + + dup_length =be16_to_cpu (dup->length); + +/* Unused entry is shorter than any of the bestfrees */ +if (dup_length <be16_to_cpu (bf[XFS_DIR2_DATA_FD_COUNT -1 ].length)) +return ; + +for (dfp = &bf[XFS_DIR2_DATA_FD_COUNT -1 ]; dfp >= bf; dfp--) +if (dup_length ==be16_to_cpu (dfp->length)) +return ; + +/* Unused entry should be in the bestfrees but wasn't found. */ +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +} + +/* Check free space info in a directory data block. */ +STATIC int +xfs_scrub_directory_data_bestfree ( +struct xfs_scrub_context *sc, +xfs_dablk_t lblk, +bool is_block) +{ +struct xfs_dir2_data_unused *dup; +struct xfs_dir2_data_free *dfp; +struct xfs_buf *bp; +struct xfs_dir2_data_free *bf; +struct xfs_mount *mp = sc->mp; +const struct xfs_dir_ops *d_ops; +char *ptr; +char *endptr; +u16 tag; +unsigned int nr_bestfrees =0 ; +unsigned int nr_frees =0 ; +unsigned int smallest_bestfree; +int newlen; +int offset; +int error; + + d_ops = sc->ip->d_ops; + +if (is_block) { +/* dir block format */ +if (lblk !=XFS_B_TO_FSBT (mp,XFS_DIR2_DATA_OFFSET )) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + error =xfs_dir3_block_read (sc->tp, sc->ip, &bp); + }else { +/* dir data format */ + error =xfs_dir3_data_read (sc->tp, sc->ip, lblk,-1 , &bp); + } +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Do the bestfrees correspond to actual free space? */ + bf = d_ops->data_bestfree_p (bp->b_addr); + smallest_bestfree =UINT_MAX ; +for (dfp = &bf[0 ]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT ]; dfp++) { + offset =be16_to_cpu (dfp->offset); +if (offset ==0 ) +continue ; +if (offset >= mp->m_dir_geo->blksize) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + dup = (struct xfs_dir2_data_unused *)(bp->b_addr + offset); + tag =be16_to_cpu (*xfs_dir2_data_unused_tag_p (dup)); + +/* bestfree doesn't match the entry it points at? */ +if (dup->freetag !=cpu_to_be16 (XFS_DIR2_DATA_FREE_TAG ) || +be16_to_cpu (dup->length) !=be16_to_cpu (dfp->length) || + tag != ((char *)dup - (char *)bp->b_addr)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + +/* bestfree records should be ordered largest to smallest */ +if (smallest_bestfree <be16_to_cpu (dfp->length)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + + smallest_bestfree =be16_to_cpu (dfp->length); + nr_bestfrees++; + } + +/* Make sure the bestfrees are actually the best free spaces. */ + ptr = (char *)d_ops->data_entry_p (bp->b_addr); +if (is_block) { +struct xfs_dir2_block_tail *btp; + + btp =xfs_dir2_block_tail_p (mp->m_dir_geo, bp->b_addr); + endptr = (char *)xfs_dir2_block_leaf_p (btp); + }else + endptr = (char *)bp->b_addr +BBTOB (bp->b_length); +while (ptr < endptr) { + dup = (struct xfs_dir2_data_unused *)ptr; +/* Skip real entries */ +if (dup->freetag !=cpu_to_be16 (XFS_DIR2_DATA_FREE_TAG )) { +struct xfs_dir2_data_entry *dep; + + dep = (struct xfs_dir2_data_entry *)ptr; + newlen = d_ops->data_entsize (dep->namelen); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); + goto out_buf; + } + ptr += newlen; +if (endptr < ptr) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); +continue ; + } + +/* Spot check this free entry */ + tag =be16_to_cpu (*xfs_dir2_data_unused_tag_p (dup)); +if (tag != ((char *)dup - (char *)bp->b_addr)) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + +/* +* Either this entry is a bestfree or it's smaller than +* any of the bestfrees. +*/ +xfs_scrub_directory_check_free_entry (sc, lblk, bf, dup); + +/* Move on. */ + newlen =be16_to_cpu (dup->length); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + ptr += newlen; +if (endptr < ptr) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +else + nr_frees++; + } + +/* Did we see at least as many free slots as there are bestfrees? */ +if (nr_frees < nr_bestfrees) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +out_buf: +xfs_trans_brelse (sc->tp, bp); +out: +return error; +} + +/* +* Does the free space length in the free space index block ($len) match +* the longest length in the directory data block's bestfree array? +* Assume that we've already checked that the data block's bestfree +* array is in order. +*/ +static inline void +xfs_scrub_directory_check_freesp ( +struct xfs_scrub_context *sc, +xfs_dablk_t lblk, +struct xfs_buf *dbp, +unsigned int len) +{ +struct xfs_dir2_data_free *bf; +struct xfs_dir2_data_free *dfp; +int offset; + +if (len ==0 ) +return ; + + bf = sc->ip->d_ops->data_bestfree_p (dbp->b_addr); +for (dfp = &bf[0 ]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT ]; dfp++) { + offset =be16_to_cpu (dfp->offset); +if (offset ==0 ) +break ; +if (len ==be16_to_cpu (dfp->length)) +return ; +/* Didn't find the best length in the bestfree data */ +break ; + } + +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +} + +/* Check free space info in a directory leaf1 block. */ +STATIC int +xfs_scrub_directory_leaf1_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir2_leaf_tail *ltp; +struct xfs_buf *dbp; +struct xfs_buf *bp; +struct xfs_mount *mp = sc->mp; +__be16 *bestp; +__u16 best; +int i; +int error; + +/* +* Read the free space block. The verifier will check for hash +* value ordering problems and check the stale entry count. +*/ + error =xfs_dir3_leaf_read (sc->tp, sc->ip, lblk,-1 , &bp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ + ltp =xfs_dir2_leaf_tail_p (mp->m_dir_geo, bp->b_addr); + bestp =xfs_dir2_leaf_bests_p (ltp); +for (i =0 ; i <be32_to_cpu (ltp->bestcount); i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ; + error =xfs_dir3_data_read (sc->tp, sc->ip, + i * args->geo->fsbcount,-1 , &dbp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, + &error)) +continue ; +xfs_scrub_directory_check_freesp (sc, lblk, dbp, best); +xfs_trans_brelse (sc->tp, dbp); + } +out: +return error; +} + +/* Check free space info in a directory freespace block. */ +STATIC int +xfs_scrub_directory_free_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir3_icfree_hdr freehdr; +struct xfs_buf *dbp; +struct xfs_buf *bp; +__be16 *bestp; +__be16 best; +int i; +int error; + +/* Read the free space block */ + error =xfs_dir2_free_read (sc->tp, sc->ip, lblk, &bp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ + sc->ip->d_ops->free_hdr_from_disk (&freehdr, bp->b_addr); + bestp = sc->ip->d_ops->free_bests_p (bp->b_addr); +for (i =0 ; i < freehdr.nvalid; i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ; + error =xfs_dir3_data_read (sc->tp, sc->ip, + (freehdr.firstdb + i) * args->geo->fsbcount, +-1 , &dbp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, + &error)) +continue ; +xfs_scrub_directory_check_freesp (sc, lblk, dbp, best); +xfs_trans_brelse (sc->tp, dbp); + } +out: +return error; +} + +/* Check free space information in directories. */ +STATIC int +xfs_scrub_directory_blocks ( +struct xfs_scrub_context *sc) +{ +struct xfs_bmbt_irec got; +struct xfs_da_args args; +struct xfs_ifork *ifp; +struct xfs_mount *mp = sc->mp; +xfs_fileoff_t leaf_lblk; +xfs_fileoff_t free_lblk; +xfs_fileoff_t lblk; +xfs_extnum_t idx; +bool found; +int is_block =0 ; +int error; + +/* Ignore local format directories. */ +if (sc->ip->i_d.di_format !=XFS_DINODE_FMT_EXTENTS && + sc->ip->i_d.di_format !=XFS_DINODE_FMT_BTREE ) +return 0 ; + + ifp =XFS_IFORK_PTR (sc->ip,XFS_DATA_FORK ); + lblk =XFS_B_TO_FSB (mp,XFS_DIR2_DATA_OFFSET ); + leaf_lblk =XFS_B_TO_FSB (mp,XFS_DIR2_LEAF_OFFSET ); + free_lblk =XFS_B_TO_FSB (mp,XFS_DIR2_FREE_OFFSET ); + +/* Is this a block dir? */ + args.dp = sc->ip; + args.geo = mp->m_dir_geo; + args.trans = sc->tp; + error =xfs_dir2_isblock (&args, &is_block); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Iterate all the data extents in the directory... */ + found =xfs_iext_lookup_extent (sc->ip, ifp, lblk, &idx, &got); +while (found) { +/* Block directories only have a single block at offset 0. */ +if (is_block && + (got.br_startoff >0 || + got.br_blockcount != args.geo->fsbcount)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + got.br_startoff); +break ; + } + +/* No more data blocks... */ +if (got.br_startoff >= leaf_lblk) +break ; + +/* +* Check each data block's bestfree data. +* +* Iterate all the fsbcount-aligned block offsets in +* this directory. The directory block reading code is +* smart enough to do its own bmap lookups to handle +* discontiguous directory blocks. When we're done +* with the extent record, re-query the bmap at the +* next fsbcount-aligned offset to avoid redundant +* block checks. +*/ +for (lblk =roundup ((xfs_dablk_t )got.br_startoff, + args.geo->fsbcount); + lblk < got.br_startoff + got.br_blockcount; + lblk += args.geo->fsbcount) { + error =xfs_scrub_directory_data_bestfree (sc, lblk, + is_block); +if (error) + goto out; + } + lblk =roundup ((xfs_dablk_t )got.br_startoff + got.br_blockcount, + args.geo->fsbcount); + found =xfs_iext_lookup_extent (sc->ip, ifp, lblk, &idx, &got); + } + +if (sc->sm->sm_flags &XFS_SCRUB_OFLAG_CORRUPT ) + goto out; + +/* Look for a leaf1 block, which has free info. */ +if (xfs_iext_lookup_extent (sc->ip, ifp, leaf_lblk, &idx, &got) && + got.br_startoff == leaf_lblk && + got.br_blockcount == args.geo->fsbcount && + !xfs_iext_get_extent (ifp, ++idx, &got)) { +if (is_block) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out; + } + error =xfs_scrub_directory_leaf1_bestfree (sc, &args, + leaf_lblk); +if (error) + goto out; + } + +if (sc->sm->sm_flags &XFS_SCRUB_OFLAG_CORRUPT ) + goto out; + +/* Scan for free blocks */ + lblk = free_lblk; + found =xfs_iext_lookup_extent (sc->ip, ifp, lblk, &idx, &got); +while (found) { +/* +* Dirs can't have blocks mapped above 2^32. +* Single-block dirs shouldn't even be here. +*/ + lblk = got.br_startoff; +if (lblk & ~0xFFFFFFFFULL ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out; + } +if (is_block) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out; + } + +/* +* Check each dir free block's bestfree data. +* +* Iterate all the fsbcount-aligned block offsets in +* this directory. The directory block reading code is +* smart enough to do its own bmap lookups to handle +* discontiguous directory blocks. When we're done +* with the extent record, re-query the bmap at the +* next fsbcount-aligned offset to avoid redundant +* block checks. +*/ +for (lblk =roundup ((xfs_dablk_t )got.br_startoff, + args.geo->fsbcount); + lblk < got.br_startoff + got.br_blockcount; + lblk += args.geo->fsbcount) { + error =xfs_scrub_directory_free_bestfree (sc, &args, + lblk); +if (error) + goto out; + } + lblk =roundup ((xfs_dablk_t )got.br_startoff + got.br_blockcount, + args.geo->fsbcount); + found =xfs_iext_lookup_extent (sc->ip, ifp, lblk, &idx, &got); + } +out: +return error; +} +/* Scrub a whole directory. */ int xfs_scrub_directory(
@@ -278,6 +698,11 @@ xfs_scrub_directory(if (error)return error; +/* Check the freespace. */ + error =xfs_scrub_directory_blocks (sc); +if (error) +return error; +/* * Check that every dirent we see can also be looked up by hash. * Userspace usually asks for a 32k buffer, so we will too.
Re: [PATCH 25/30] xfs: scrub directory freespace
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the free space information in a directory.
....
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 256
Quoted 12 lines
fs/xfs/scrub/dir.c +4
-0@@ -250,6 +250,426 @@ xfs_scrub_dir_rec(return error; } +/* +* Is this unused entry either in the bestfree or smaller than all of them? +* We assume the bestfrees are sorted longest to shortest, and that there +* aren't any bogus entries.
s/We assume/We've already checked/
Quoted 7 lines
fs/xfs/scrub/dir.c +7
-0+*/ +static inline void +xfs_scrub_directory_check_free_entry ( +struct xfs_scrub_context *sc, +xfs_dablk_t lblk, +struct xfs_dir2_data_free *bf, +struct xfs_dir2_data_unused *dup)
....
Quoted 30 lines
fs/xfs/scrub/dir.c +30
-0+while (ptr < endptr) { + dup = (struct xfs_dir2_data_unused *)ptr; +/* Skip real entries */ +if (dup->freetag !=cpu_to_be16 (XFS_DIR2_DATA_FREE_TAG )) { +struct xfs_dir2_data_entry *dep; + + dep = (struct xfs_dir2_data_entry *)ptr; + newlen = d_ops->data_entsize (dep->namelen); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); + goto out_buf; + } + ptr += newlen; +if (endptr < ptr) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); +continue ; + } + +/* Spot check this free entry */ + tag =be16_to_cpu (*xfs_dir2_data_unused_tag_p (dup)); +if (tag != ((char *)dup - (char *)bp->b_addr)) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + +/* +* Either this entry is a bestfree or it's smaller than +* any of the bestfrees. +*/ +xfs_scrub_directory_check_free_entry (sc, lblk, bf, dup);
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 399
Quoted 10 lines
fs/xfs/scrub/dir.c +10
-0+ +/* Move on. */ + newlen =be16_to_cpu (dup->length); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + ptr += newlen; +if (endptr < ptr) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk);
I'd prefer this matches the loop logic order. ie.
if (ptr >= endptr)
Quoted 11 lines
fs/xfs/scrub/dir.c +11
-0+else + nr_frees++; + } + +/* Did we see at least as many free slots as there are bestfrees? */ +if (nr_frees < nr_bestfrees) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +out_buf: +xfs_trans_brelse (sc->tp, bp); +out: +return error;
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 420
Quoted 10 lines
fs/xfs/scrub/dir.c +10
-0+} + +/* +* Does the free space length in the free space index block ($len) match +* the longest length in the directory data block's bestfree array? +* Assume that we've already checked that the data block's bestfree +* array is in order. +*/ +static inline void +xfs_scrub_directory_check_freesp(
No need for inline here, the compiler will do that automatically if appropriate.
Quoted 47 lines
fs/xfs/scrub/dir.c +47
-0+struct xfs_scrub_context *sc, + xfs_dablk_t lblk, + struct xfs_buf *dbp, + unsigned int len) +{ + struct xfs_dir2_data_free *bf; +struct xfs_dir2_data_free *dfp; +int offset; + +if (len ==0 ) +return ; + + bf = sc->ip->d_ops->data_bestfree_p (dbp->b_addr); +for (dfp = &bf[0 ]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT ]; dfp++) { + offset =be16_to_cpu (dfp->offset); +if (offset ==0 ) +break ; +if (len ==be16_to_cpu (dfp->length)) +return ; +/* Didn't find the best length in the bestfree data */ +break ; + } + +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +} + +/* Check free space info in a directory leaf1 block. */ +STATIC int +xfs_scrub_directory_leaf1_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir2_leaf_tail *ltp; +struct xfs_buf *dbp; +struct xfs_buf *bp; +struct xfs_mount *mp = sc->mp; +__be16 *bestp; +__u16 best; +int i; +int error; + +/* +* Read the free space block. The verifier will check for hash +* value ordering problems and check the stale entry count. +*/ + error =xfs_dir3_leaf_read (sc->tp, sc->ip, lblk,-1 , &bp);
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 477
Quoted 10 lines
fs/xfs/scrub/dir.c +10
-0+if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ + ltp =xfs_dir2_leaf_tail_p (mp->m_dir_geo, bp->b_addr); + bestp =xfs_dir2_leaf_bests_p (ltp); +for (i =0 ; i <be32_to_cpu (ltp->bestcount); i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ;
Count stale entries, check if matches hdr->stale ?
Quoted 29 lines
fs/xfs/scrub/dir.c +29
-0+ error =xfs_dir3_data_read (sc->tp, sc->ip, + i * args->geo->fsbcount,-1 , &dbp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, + &error)) +continue ; +xfs_scrub_directory_check_freesp (sc, lblk, dbp, best); +xfs_trans_brelse (sc->tp, dbp); + } +out: +return error; +} + +/* Check free space info in a directory freespace block. */ +STATIC int +xfs_scrub_directory_free_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir3_icfree_hdr freehdr; +struct xfs_buf *dbp; +struct xfs_buf *bp; +__be16 *bestp; +__be16 best; +int i; +int error; + +/* Read the free space block */ + error =xfs_dir2_free_read (sc->tp, sc->ip, lblk, &bp);
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 516
Quoted 10 lines
fs/xfs/scrub/dir.c +10
-0+if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ + sc->ip->d_ops->free_hdr_from_disk (&freehdr, bp->b_addr); + bestp = sc->ip->d_ops->free_bests_p (bp->b_addr); +for (i =0 ; i < freehdr.nvalid; i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ;
Count stale entries, check freehdr.nvalid + stale = freehdr.nused?
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 25/30] xfs: scrub directory freespace
Quoted 15 lines · Dave Chinner
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the free space information in a directory.
....
Quoted 8 lines
fs/xfs/scrub/dir.c +0
-0@@ -250,6 +250,426 @@ xfs_scrub_dir_rec(return error; }
Quoted 6 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 256
Quoted 4 lines
fs/xfs/scrub/dir.c +4
-0+/* +* Is this unused entry either in the bestfree or smaller than all of them? +* We assume the bestfrees are sorted longest to shortest, and that there +* aren't any bogus entries. s/We assume/We've already checked/
Ok.
Quoted 53 lines
Quoted 7 lines
fs/xfs/scrub/dir.c +7
-0+*/ +static inline void +xfs_scrub_directory_check_free_entry ( +struct xfs_scrub_context *sc, +xfs_dablk_t lblk, +struct xfs_dir2_data_free *bf, +struct xfs_dir2_data_unused *dup)....
Quoted 30 lines
fs/xfs/scrub/dir.c +30
-0+while (ptr < endptr) { + dup = (struct xfs_dir2_data_unused *)ptr; +/* Skip real entries */ +if (dup->freetag !=cpu_to_be16 (XFS_DIR2_DATA_FREE_TAG )) { +struct xfs_dir2_data_entry *dep; + + dep = (struct xfs_dir2_data_entry *)ptr; + newlen = d_ops->data_entsize (dep->namelen); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); + goto out_buf; + } + ptr += newlen; +if (endptr < ptr) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); +continue ; + } + +/* Spot check this free entry */ + tag =be16_to_cpu (*xfs_dir2_data_unused_tag_p (dup)); +if (tag != ((char *)dup - (char *)bp->b_addr)) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + +/* +* Either this entry is a bestfree or it's smaller than +* any of the bestfrees. +*/ +xfs_scrub_directory_check_free_entry (sc, lblk, bf, dup);fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 399
Quoted 10 lines
fs/xfs/scrub/dir.c +10
-0+ +/* Move on. */ + newlen =be16_to_cpu (dup->length); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + ptr += newlen; +if (endptr < ptr) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk);I'd prefer this matches the loop logic order. ie.
Quoted 2 lines
if (ptr >= endptr)
Ok. That check can be lifted out of the loop anyway.
Quoted 16 lines
Quoted 16 lines
fs/xfs/scrub/dir.c +16
-0+else + nr_frees++; + } + +/* Did we see at least as many free slots as there are bestfrees? */ +if (nr_frees < nr_bestfrees) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +out_buf: +xfs_trans_brelse (sc->tp, bp); +out: +return error; +} + +/* +* Does the free space length in the free space index block ($len) match +* the longest length in the directory data block's bestfree array?
Quoted 8 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 420
Quoted 5 lines
+ * Assume that we've already checked that the data block's bestfree
+ * array is in order.
+ */fs/xfs/scrub/dir.c +2
-0+static inline void +xfs_scrub_directory_check_freesp(No need for inline here, the compiler will do that automatically if appropriate.
Ok. I'll make it STATIC like everything else here.
Quoted 51 lines
Quoted 51 lines
fs/xfs/scrub/dir.c +51
-0+struct xfs_scrub_context *sc, + xfs_dablk_t lblk, + struct xfs_buf *dbp, + unsigned int len) +{ + struct xfs_dir2_data_free *bf; +struct xfs_dir2_data_free *dfp; +int offset; + +if (len ==0 ) +return ; + + bf = sc->ip->d_ops->data_bestfree_p (dbp->b_addr); +for (dfp = &bf[0 ]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT ]; dfp++) { + offset =be16_to_cpu (dfp->offset); +if (offset ==0 ) +break ; +if (len ==be16_to_cpu (dfp->length)) +return ; +/* Didn't find the best length in the bestfree data */ +break ; + } + +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +} + +/* Check free space info in a directory leaf1 block. */ +STATIC int +xfs_scrub_directory_leaf1_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir2_leaf_tail *ltp; +struct xfs_buf *dbp; +struct xfs_buf *bp; +struct xfs_mount *mp = sc->mp; +__be16 *bestp; +__u16 best; +int i; +int error; + +/* +* Read the free space block. The verifier will check for hash +* value ordering problems and check the stale entry count. +*/ + error =xfs_dir3_leaf_read (sc->tp, sc->ip, lblk,-1 , &bp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */
Quoted 8 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 477
Quoted 6 lines
fs/xfs/scrub/dir.c +6
-0+ ltp =xfs_dir2_leaf_tail_p (mp->m_dir_geo, bp->b_addr); + bestp =xfs_dir2_leaf_bests_p (ltp); +for (i =0 ; i <be32_to_cpu (ltp->bestcount); i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ;Count stale entries, check if matches
hdr->stale?
This should already be done by xfs_dir3_leaf_read -> xfs_dir3_leaf1_read_verify -> __read_verify -> xfs_dir3_leaf_verify -> xfs_dir3_leaf_check_int, right?
Quoted 34 lines
Quoted 34 lines
fs/xfs/scrub/dir.c +33
-0+ error =xfs_dir3_data_read (sc->tp, sc->ip, + i * args->geo->fsbcount,-1 , &dbp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, + &error)) +continue ; +xfs_scrub_directory_check_freesp (sc, lblk, dbp, best); +xfs_trans_brelse (sc->tp, dbp); + } +out: +return error; +} + +/* Check free space info in a directory freespace block. */ +STATIC int +xfs_scrub_directory_free_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir3_icfree_hdr freehdr; +struct xfs_buf *dbp; +struct xfs_buf *bp; +__be16 *bestp; +__be16 best; +int i; +int error; + +/* Read the free space block */ + error =xfs_dir2_free_read (sc->tp, sc->ip, lblk, &bp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */
Quoted 8 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 516
Quoted 6 lines
fs/xfs/scrub/dir.c +6
-0+ sc->ip->d_ops->free_hdr_from_disk (&freehdr, bp->b_addr); + bestp = sc->ip->d_ops->free_bests_p (bp->b_addr); +for (i =0 ; i < freehdr.nvalid; i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ;Count stale entries, check freehdr.nvalid + stale = freehdr.nused?
Aha, yes, that needs to be checked.
Also that for loop needs to be terminated on i < freehdr.nused.
--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 25/30] xfs: scrub directory freespace
Quoted 217 lines · Darrick J. Wong
Quoted 15 lines · Dave Chinner
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the free space information in a directory.
....
Quoted 8 lines
fs/xfs/scrub/dir.c +0
-0@@ -250,6 +250,426 @@ xfs_scrub_dir_rec(return error; }Quoted 6 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 256
Quoted 4 lines
fs/xfs/scrub/dir.c +4
-0+/* +* Is this unused entry either in the bestfree or smaller than all of them? +* We assume the bestfrees are sorted longest to shortest, and that there +* aren't any bogus entries. s/We assume/We've already checked/
Ok.
Quoted 53 lines
Quoted 7 lines
fs/xfs/scrub/dir.c +7
-0+*/ +static inline void +xfs_scrub_directory_check_free_entry ( +struct xfs_scrub_context *sc, +xfs_dablk_t lblk, +struct xfs_dir2_data_free *bf, +struct xfs_dir2_data_unused *dup)....
Quoted 30 lines
fs/xfs/scrub/dir.c +30
-0+while (ptr < endptr) { + dup = (struct xfs_dir2_data_unused *)ptr; +/* Skip real entries */ +if (dup->freetag !=cpu_to_be16 (XFS_DIR2_DATA_FREE_TAG )) { +struct xfs_dir2_data_entry *dep; + + dep = (struct xfs_dir2_data_entry *)ptr; + newlen = d_ops->data_entsize (dep->namelen); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); + goto out_buf; + } + ptr += newlen; +if (endptr < ptr) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); +continue ; + } + +/* Spot check this free entry */ + tag =be16_to_cpu (*xfs_dir2_data_unused_tag_p (dup)); +if (tag != ((char *)dup - (char *)bp->b_addr)) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + +/* +* Either this entry is a bestfree or it's smaller than +* any of the bestfrees. +*/ +xfs_scrub_directory_check_free_entry (sc, lblk, bf, dup);fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 399
Quoted 10 lines
fs/xfs/scrub/dir.c +10
-0+ +/* Move on. */ + newlen =be16_to_cpu (dup->length); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + ptr += newlen; +if (endptr < ptr) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk);I'd prefer this matches the loop logic order. ie.
Quoted 2 lines
if (ptr >= endptr)
Ok. That check can be lifted out of the loop anyway.
Quoted 16 lines
Quoted 16 lines
fs/xfs/scrub/dir.c +16
-0+else + nr_frees++; + } + +/* Did we see at least as many free slots as there are bestfrees? */ +if (nr_frees < nr_bestfrees) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +out_buf: +xfs_trans_brelse (sc->tp, bp); +out: +return error; +} + +/* +* Does the free space length in the free space index block ($len) match +* the longest length in the directory data block's bestfree array? Quoted 8 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 420
Quoted 5 lines
+ * Assume that we've already checked that the data block's bestfree
+ * array is in order.
+ */fs/xfs/scrub/dir.c +2
-0+static inline void +xfs_scrub_directory_check_freesp(No need for inline here, the compiler will do that automatically if appropriate.
Ok. I'll make it STATIC like everything else here.
Quoted 51 lines
Quoted 51 lines
fs/xfs/scrub/dir.c +51
-0+struct xfs_scrub_context *sc, + xfs_dablk_t lblk, + struct xfs_buf *dbp, + unsigned int len) +{ + struct xfs_dir2_data_free *bf; +struct xfs_dir2_data_free *dfp; +int offset; + +if (len ==0 ) +return ; + + bf = sc->ip->d_ops->data_bestfree_p (dbp->b_addr); +for (dfp = &bf[0 ]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT ]; dfp++) { + offset =be16_to_cpu (dfp->offset); +if (offset ==0 ) +break ; +if (len ==be16_to_cpu (dfp->length)) +return ; +/* Didn't find the best length in the bestfree data */ +break ; + } + +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +} + +/* Check free space info in a directory leaf1 block. */ +STATIC int +xfs_scrub_directory_leaf1_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir2_leaf_tail *ltp; +struct xfs_buf *dbp; +struct xfs_buf *bp; +struct xfs_mount *mp = sc->mp; +__be16 *bestp; +__u16 best; +int i; +int error; + +/* +* Read the free space block. The verifier will check for hash +* value ordering problems and check the stale entry count. +*/ + error =xfs_dir3_leaf_read (sc->tp, sc->ip, lblk,-1 , &bp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ Quoted 8 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 477
Quoted 6 lines
fs/xfs/scrub/dir.c +6
-0+ ltp =xfs_dir2_leaf_tail_p (mp->m_dir_geo, bp->b_addr); + bestp =xfs_dir2_leaf_bests_p (ltp); +for (i =0 ; i <be32_to_cpu (ltp->bestcount); i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ;Count stale entries, check if matches
hdr->stale?This should already be done by
xfs_dir3_leaf_read->xfs_dir3_leaf1_read_verify->__read_verify->xfs_dir3_leaf_verify->xfs_dir3_leaf_check_int, right?Quoted 34 lines
Quoted 34 lines
fs/xfs/scrub/dir.c +33
-0+ error =xfs_dir3_data_read (sc->tp, sc->ip, + i * args->geo->fsbcount,-1 , &dbp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, + &error)) +continue ; +xfs_scrub_directory_check_freesp (sc, lblk, dbp, best); +xfs_trans_brelse (sc->tp, dbp); + } +out: +return error; +} + +/* Check free space info in a directory freespace block. */ +STATIC int +xfs_scrub_directory_free_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir3_icfree_hdr freehdr; +struct xfs_buf *dbp; +struct xfs_buf *bp; +__be16 *bestp; +__be16 best; +int i; +int error; + +/* Read the free space block */ + error =xfs_dir2_free_read (sc->tp, sc->ip, lblk, &bp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ Quoted 8 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 516
Quoted 6 lines
fs/xfs/scrub/dir.c +6
-0+ sc->ip->d_ops->free_hdr_from_disk (&freehdr, bp->b_addr); + bestp = sc->ip->d_ops->free_bests_p (bp->b_addr); +for (i =0 ; i < freehdr.nvalid; i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ;Count stale entries, check freehdr.nvalid + stale = freehdr.nused?
Quoted 3 lines
Aha, yes, that needs to be checked.
Also that for loop needs to be terminated on i < freehdr.nused.
(Er... NAK on that last sentence; it's the documentation that are wrong.)
--D
Quoted 17 lines
--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--
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 25/30] xfs: scrub directory freespace
In reply to Darrick J. WongQuoted 42 lines · Darrick J. Wong
Quoted 32 lines · Dave Chinner
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the free space information in a directory.
....
Quoted 25 lines
fs/xfs/scrub/dir.c +25
-0+/* Check free space info in a directory leaf1 block. */ +STATIC int +xfs_scrub_directory_leaf1_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir2_leaf_tail *ltp; +struct xfs_buf *dbp; +struct xfs_buf *bp; +struct xfs_mount *mp = sc->mp; +__be16 *bestp; +__u16 best; +int i; +int error; + +/* +* Read the free space block. The verifier will check for hash +* value ordering problems and check the stale entry count. +*/ + error =xfs_dir3_leaf_read (sc->tp, sc->ip, lblk,-1 , &bp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ Quoted 8 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 477
Quoted 6 lines
fs/xfs/scrub/dir.c +6
-0+ ltp =xfs_dir2_leaf_tail_p (mp->m_dir_geo, bp->b_addr); + bestp =xfs_dir2_leaf_bests_p (ltp); +for (i =0 ; i <be32_to_cpu (ltp->bestcount); i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ;Count stale entries, check if matches
hdr->stale?
Quoted 3 lines
This should already be done by
xfs_dir3_leaf_read->xfs_dir3_leaf1_read_verify->__read_verify->xfs_dir3_leaf_verify->xfs_dir3_leaf_check_int, right?
True. However, it's simple to do and we're already iterating over all the structures necessary and detecting the case necessary to check it, so it doesn't hurt and might catch in-core corruptions before they hit the write verifier?
And it makes it do the same checks as the free_bestfree checks below....
Quoted 40 lines
Quoted 31 lines
Quoted 31 lines
fs/xfs/scrub/dir.c +31
-0+if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, + &error)) +continue ; +xfs_scrub_directory_check_freesp (sc, lblk, dbp, best); +xfs_trans_brelse (sc->tp, dbp); + } +out: +return error; +} + +/* Check free space info in a directory freespace block. */ +STATIC int +xfs_scrub_directory_free_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir3_icfree_hdr freehdr; +struct xfs_buf *dbp; +struct xfs_buf *bp; +__be16 *bestp; +__be16 best; +int i; +int error; + +/* Read the free space block */ + error =xfs_dir2_free_read (sc->tp, sc->ip, lblk, &bp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ Quoted 8 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 516
Quoted 6 lines
fs/xfs/scrub/dir.c +6
-0+ sc->ip->d_ops->free_hdr_from_disk (&freehdr, bp->b_addr); + bestp = sc->ip->d_ops->free_bests_p (bp->b_addr); +for (i =0 ; i < freehdr.nvalid; i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ;Count stale entries, check freehdr.nvalid + stale = freehdr.nused?
Quoted 3 lines
Aha, yes, that needs to be checked.
Also that for loop needs to be terminated on i < freehdr.nused.
Good catch - I missed that, too :P
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 25/30] xfs: scrub directory freespace
Quoted 43 lines · Dave Chinner
Quoted 42 lines · Darrick J. Wong
Quoted 40 lines · Dave Chinner
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Check the free space information in a directory.
....
Quoted 21 lines
fs/xfs/scrub/dir.c +21
-0+/* Check free space info in a directory leaf1 block. */ +STATIC int +xfs_scrub_directory_leaf1_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir2_leaf_tail *ltp; +struct xfs_buf *dbp; +struct xfs_buf *bp; +struct xfs_mount *mp = sc->mp; +__be16 *bestp; +__u16 best; +int i; +int error; + +/* +* Read the free space block. The verifier will check for hash +* value ordering problems and check the stale entry count. +*/ + error =xfs_dir3_leaf_read (sc->tp, sc->ip, lblk,-1 , &bp);fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 477
Quoted 10 lines
fs/xfs/scrub/dir.c +10
-0+if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ + ltp =xfs_dir2_leaf_tail_p (mp->m_dir_geo, bp->b_addr); + bestp =xfs_dir2_leaf_bests_p (ltp); +for (i =0 ; i <be32_to_cpu (ltp->bestcount); i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ;Count stale entries, check if matches
hdr->stale?
Quoted 8 lines
Quoted 3 lines
This should already be done by
xfs_dir3_leaf_read->xfs_dir3_leaf1_read_verify->__read_verify->xfs_dir3_leaf_verify->xfs_dir3_leaf_check_int, right?True. However, it's simple to do and we're already iterating over all the structures necessary and detecting the case necessary to check it, so it doesn't hurt and might catch in-core corruptions before they hit the write verifier?
Yes, though the verifier rework series will (in addition to printing instruction pointer addresses of the failing verifier checks) expose the structure verification code via a new b_ops function pointer, and then enhances scrub to call it.
(That said, from a completeness standpoint I don't mind duplicating the code...)
Quoted 43 lines
And it makes it do the same checks as the
free_bestfreechecks below....Quoted 40 lines
Quoted 39 lines
Quoted 27 lines
fs/xfs/scrub/dir.c +27
-0+if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, + &error)) +continue ; +xfs_scrub_directory_check_freesp (sc, lblk, dbp, best); +xfs_trans_brelse (sc->tp, dbp); + } +out: +return error; +} + +/* Check free space info in a directory freespace block. */ +STATIC int +xfs_scrub_directory_free_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir3_icfree_hdr freehdr; +struct xfs_buf *dbp; +struct xfs_buf *bp; +__be16 *bestp; +__be16 best; +int i; +int error; + +/* Read the free space block */ + error =xfs_dir2_free_read (sc->tp, sc->ip, lblk, &bp);fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 516
Quoted 10 lines
fs/xfs/scrub/dir.c +10
-0+if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ + sc->ip->d_ops->free_hdr_from_disk (&freehdr, bp->b_addr); + bestp = sc->ip->d_ops->free_bests_p (bp->b_addr); +for (i =0 ; i < freehdr.nvalid; i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ;Count stale entries, check freehdr.nvalid + stale = freehdr.nused?
Quoted 5 lines
Quoted 3 lines
Aha, yes, that needs to be checked.
Also that for loop needs to be terminated on i < freehdr.nused.
Good catch - I missed that, too :P
Well.... the documentation says that nused is the number of entries that have been filled out and nvalid is the number of entries that aren't 0xFFFF, but....
fhdr.firstdb = 0
fhdr.nvalid = 11
fhdr.nused = 9
fbests[0-10] = 0:0x60 1:0x8 3:0x10 4:0xffff 5:0x10 6:0x8 7:0x10 8:0xffff 9:0x180 10:0xe10
So the documentation is wrong w.r.t. whatever libxfs writes to disk. :)
"The freeindex’s hdr.nvalid should always be the same as the number of allocated data directory blocks containing name/inode data and will always be less than or equal to hdr.nused. The value of hdr.nused should be the same as the index of the last data directory block plus one (i.e. when the last data block is freed, nused and nvalid are decremented)."
--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
PATCH v2 25/30 xfs: scrub directory freespace
In reply to Darrick J. WongCheck the free space information in a directory.
---
v2: make the freespace and leaf checks more complete
--- fs/xfs/scrub/dir.c | 474 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 474 insertions(+)
fs/xfs/scrub/dir.c +474 -0
@@ -251,6 +251,475 @@ xfs_scrub_dir_rec(return error; } +/* +* Is this unused entry either in the bestfree or smaller than all of +* them? We've already checked that the bestfrees are sorted longest to +* shortest, and that there aren't any bogus entries. +*/ +STATIC void +xfs_scrub_directory_check_free_entry ( +struct xfs_scrub_context *sc, +xfs_dablk_t lblk, +struct xfs_dir2_data_free *bf, +struct xfs_dir2_data_unused *dup) +{ +struct xfs_dir2_data_free *dfp; +unsigned int dup_length; + + dup_length =be16_to_cpu (dup->length); + +/* Unused entry is shorter than any of the bestfrees */ +if (dup_length <be16_to_cpu (bf[XFS_DIR2_DATA_FD_COUNT -1 ].length)) +return ; + +for (dfp = &bf[XFS_DIR2_DATA_FD_COUNT -1 ]; dfp >= bf; dfp--) +if (dup_length ==be16_to_cpu (dfp->length)) +return ; + +/* Unused entry should be in the bestfrees but wasn't found. */ +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +} + +/* Check free space info in a directory data block. */ +STATIC int +xfs_scrub_directory_data_bestfree ( +struct xfs_scrub_context *sc, +xfs_dablk_t lblk, +bool is_block) +{ +struct xfs_dir2_data_unused *dup; +struct xfs_dir2_data_free *dfp; +struct xfs_buf *bp; +struct xfs_dir2_data_free *bf; +struct xfs_mount *mp = sc->mp; +const struct xfs_dir_ops *d_ops; +char *ptr; +char *endptr; +u16 tag; +unsigned int nr_bestfrees =0 ; +unsigned int nr_frees =0 ; +unsigned int smallest_bestfree; +int newlen; +int offset; +int error; + + d_ops = sc->ip->d_ops; + +if (is_block) { +/* dir block format */ +if (lblk !=XFS_B_TO_FSBT (mp,XFS_DIR2_DATA_OFFSET )) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + error =xfs_dir3_block_read (sc->tp, sc->ip, &bp); + }else { +/* dir data format */ + error =xfs_dir3_data_read (sc->tp, sc->ip, lblk,-1 , &bp); + } +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Do the bestfrees correspond to actual free space? */ + bf = d_ops->data_bestfree_p (bp->b_addr); + smallest_bestfree =UINT_MAX ; +for (dfp = &bf[0 ]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT ]; dfp++) { + offset =be16_to_cpu (dfp->offset); +if (offset ==0 ) +continue ; +if (offset >= mp->m_dir_geo->blksize) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + dup = (struct xfs_dir2_data_unused *)(bp->b_addr + offset); + tag =be16_to_cpu (*xfs_dir2_data_unused_tag_p (dup)); + +/* bestfree doesn't match the entry it points at? */ +if (dup->freetag !=cpu_to_be16 (XFS_DIR2_DATA_FREE_TAG ) || +be16_to_cpu (dup->length) !=be16_to_cpu (dfp->length) || + tag != ((char *)dup - (char *)bp->b_addr)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + +/* bestfree records should be ordered largest to smallest */ +if (smallest_bestfree <be16_to_cpu (dfp->length)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + + smallest_bestfree =be16_to_cpu (dfp->length); + nr_bestfrees++; + } + +/* Make sure the bestfrees are actually the best free spaces. */ + ptr = (char *)d_ops->data_entry_p (bp->b_addr); +if (is_block) { +struct xfs_dir2_block_tail *btp; + + btp =xfs_dir2_block_tail_p (mp->m_dir_geo, bp->b_addr); + endptr = (char *)xfs_dir2_block_leaf_p (btp); + }else + endptr = (char *)bp->b_addr +BBTOB (bp->b_length); +while (ptr < endptr) { + dup = (struct xfs_dir2_data_unused *)ptr; +/* Skip real entries */ +if (dup->freetag !=cpu_to_be16 (XFS_DIR2_DATA_FREE_TAG )) { +struct xfs_dir2_data_entry *dep; + + dep = (struct xfs_dir2_data_entry *)ptr; + newlen = d_ops->data_entsize (dep->namelen); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); + goto out_buf; + } + ptr += newlen; +continue ; + } + +/* Spot check this free entry */ + tag =be16_to_cpu (*xfs_dir2_data_unused_tag_p (dup)); +if (tag != ((char *)dup - (char *)bp->b_addr)) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + +/* +* Either this entry is a bestfree or it's smaller than +* any of the bestfrees. +*/ +xfs_scrub_directory_check_free_entry (sc, lblk, bf, dup); + +/* Move on. */ + newlen =be16_to_cpu (dup->length); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out_buf; + } + ptr += newlen; +if (ptr <= endptr) + nr_frees++; + } + +/* Did we go off the end? */ +if (ptr > endptr) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + +/* Did we see at least as many free slots as there are bestfrees? */ +if (nr_frees < nr_bestfrees) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +out_buf: +xfs_trans_brelse (sc->tp, bp); +out: +return error; +} + +/* +* Does the free space length in the free space index block ($len) match +* the longest length in the directory data block's bestfree array? +* Assume that we've already checked that the data block's bestfree +* array is in order. +*/ +STATIC void +xfs_scrub_directory_check_freesp ( +struct xfs_scrub_context *sc, +xfs_dablk_t lblk, +struct xfs_buf *dbp, +unsigned int len) +{ +struct xfs_dir2_data_free *bf; +struct xfs_dir2_data_free *dfp; +int offset; + +if (len ==0 ) +return ; + + bf = sc->ip->d_ops->data_bestfree_p (dbp->b_addr); +for (dfp = &bf[0 ]; dfp < &bf[XFS_DIR2_DATA_FD_COUNT ]; dfp++) { + offset =be16_to_cpu (dfp->offset); +if (offset ==0 ) +break ; +if (len ==be16_to_cpu (dfp->length)) +return ; +/* Didn't find the best length in the bestfree data */ +break ; + } + +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +} + +/* Check free space info in a directory leaf1 block. */ +STATIC int +xfs_scrub_directory_leaf1_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir3_icleaf_hdr leafhdr; +struct xfs_dir2_leaf_entry *ents; +struct xfs_dir2_leaf_tail *ltp; +struct xfs_dir2_leaf *leaf; +struct xfs_buf *dbp; +struct xfs_buf *bp; +struct xfs_mount *mp = sc->mp; +const struct xfs_dir_ops *d_ops = sc->ip->d_ops; +__be16 *bestp; +__u16 best; +__u32 hash; +__u32 lasthash =0 ; +__u32 bestcount; +unsigned int stale =0 ; +int i; +int error; + +/* Read the free space block. */ + error =xfs_dir3_leaf_read (sc->tp, sc->ip, lblk,-1 , &bp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + + leaf = bp->b_addr; + d_ops->leaf_hdr_from_disk (&leafhdr, leaf); + ents = d_ops->leaf_ents_p (leaf); + ltp =xfs_dir2_leaf_tail_p (mp->m_dir_geo, leaf); + bestcount =be32_to_cpu (ltp->bestcount); + bestp =xfs_dir2_leaf_bests_p (ltp); + +/* +* There should be as many bestfree slots as there are dir data +* blocks that can fit under i_size. +*/ +if (bestcount !=XFS_B_TO_FSB (mp, sc->ip->i_d.di_size)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out; + } + +/* Is the leaf count even remotely sane? */ +if (leafhdr.count > d_ops->leaf_max_ents (mp->m_dir_geo)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out; + } + +/* Leaves and bests don't overlap in leaf format. */ +if ((char *)&ents[leafhdr.count] > (char *)xfs_dir2_leaf_bests_p (ltp)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out; + } + +/* Check hash value order, count stale entries. */ +for (i =0 ; i < leafhdr.count; i++) { + hash =be32_to_cpu (ents[i].hashval); +if (i >0 && lasthash > hash) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + lasthash = hash; +if (ents[i].address ==cpu_to_be32 (XFS_DIR2_NULL_DATAPTR )) + stale++; + } +if (leafhdr.stale != stale) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + +/* Check all the bestfree entries. */ +for (i =0 ; i <be32_to_cpu (ltp->bestcount); i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) +continue ; + error =xfs_dir3_data_read (sc->tp, sc->ip, + i * args->geo->fsbcount,-1 , &dbp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, + &error)) +continue ; +xfs_scrub_directory_check_freesp (sc, lblk, dbp, best); +xfs_trans_brelse (sc->tp, dbp); + } +out: +return error; +} + +/* Check free space info in a directory freespace block. */ +STATIC int +xfs_scrub_directory_free_bestfree ( +struct xfs_scrub_context *sc, +struct xfs_da_args *args, +xfs_dablk_t lblk) +{ +struct xfs_dir3_icfree_hdr freehdr; +struct xfs_buf *dbp; +struct xfs_buf *bp; +__be16 *bestp; +__be16 best; +unsigned int stale =0 ; +int i; +int error; + +/* Read the free space block */ + error =xfs_dir2_free_read (sc->tp, sc->ip, lblk, &bp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Check all the entries. */ + sc->ip->d_ops->free_hdr_from_disk (&freehdr, bp->b_addr); + bestp = sc->ip->d_ops->free_bests_p (bp->b_addr); +for (i =0 ; i < freehdr.nvalid; i++, bestp++) { + best =be16_to_cpu (*bestp); +if (best ==NULLDATAOFF ) { + stale++; +continue ; + } + error =xfs_dir3_data_read (sc->tp, sc->ip, + (freehdr.firstdb + i) * args->geo->fsbcount, +-1 , &dbp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, + &error)) +continue ; +xfs_scrub_directory_check_freesp (sc, lblk, dbp, best); +xfs_trans_brelse (sc->tp, dbp); + } + +if (freehdr.nused + stale != freehdr.nvalid) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); +out: +return error; +} + +/* Check free space information in directories. */ +STATIC int +xfs_scrub_directory_blocks ( +struct xfs_scrub_context *sc) +{ +struct xfs_bmbt_irec got; +struct xfs_da_args args; +struct xfs_ifork *ifp; +struct xfs_mount *mp = sc->mp; +xfs_fileoff_t leaf_lblk; +xfs_fileoff_t free_lblk; +xfs_fileoff_t lblk; +xfs_extnum_t idx; +xfs_dablk_t dabno; +bool found; +int is_block =0 ; +int error; + +/* Ignore local format directories. */ +if (sc->ip->i_d.di_format !=XFS_DINODE_FMT_EXTENTS && + sc->ip->i_d.di_format !=XFS_DINODE_FMT_BTREE ) +return 0 ; + + ifp =XFS_IFORK_PTR (sc->ip,XFS_DATA_FORK ); + lblk =XFS_B_TO_FSB (mp,XFS_DIR2_DATA_OFFSET ); + leaf_lblk =XFS_B_TO_FSB (mp,XFS_DIR2_LEAF_OFFSET ); + free_lblk =XFS_B_TO_FSB (mp,XFS_DIR2_FREE_OFFSET ); + +/* Is this a block dir? */ + args.dp = sc->ip; + args.geo = mp->m_dir_geo; + args.trans = sc->tp; + error =xfs_dir2_isblock (&args, &is_block); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK , lblk, &error)) + goto out; + +/* Iterate all the data extents in the directory... */ + found =xfs_iext_lookup_extent (sc->ip, ifp, lblk, &idx, &got); +while (found) { +/* Block directories only have a single block at offset 0. */ +if (is_block && + (got.br_startoff >0 || + got.br_blockcount != args.geo->fsbcount)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + got.br_startoff); +break ; + } + +/* No more data blocks... */ +if (got.br_startoff >= leaf_lblk) +break ; + +/* +* Check each data block's bestfree data. +* +* Iterate all the fsbcount-aligned block offsets in +* this directory. The directory block reading code is +* smart enough to do its own bmap lookups to handle +* discontiguous directory blocks. When we're done +* with the extent record, re-query the bmap at the +* next fsbcount-aligned offset to avoid redundant +* block checks. +*/ +for (lblk =roundup ((xfs_dablk_t )got.br_startoff, + args.geo->fsbcount); + lblk < got.br_startoff + got.br_blockcount; + lblk += args.geo->fsbcount) { + error =xfs_scrub_directory_data_bestfree (sc, lblk, + is_block); +if (error) + goto out; + } + dabno = got.br_startoff + got.br_blockcount; + lblk =roundup (dabno, args.geo->fsbcount); + found =xfs_iext_lookup_extent (sc->ip, ifp, lblk, &idx, &got); + } + +if (sc->sm->sm_flags &XFS_SCRUB_OFLAG_CORRUPT ) + goto out; + +/* Look for a leaf1 block, which has free info. */ +if (xfs_iext_lookup_extent (sc->ip, ifp, leaf_lblk, &idx, &got) && + got.br_startoff == leaf_lblk && + got.br_blockcount == args.geo->fsbcount && + !xfs_iext_get_extent (ifp, ++idx, &got)) { +if (is_block) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out; + } + error =xfs_scrub_directory_leaf1_bestfree (sc, &args, + leaf_lblk); +if (error) + goto out; + } + +if (sc->sm->sm_flags &XFS_SCRUB_OFLAG_CORRUPT ) + goto out; + +/* Scan for free blocks */ + lblk = free_lblk; + found =xfs_iext_lookup_extent (sc->ip, ifp, lblk, &idx, &got); +while (found) { +/* +* Dirs can't have blocks mapped above 2^32. +* Single-block dirs shouldn't even be here. +*/ + lblk = got.br_startoff; +if (lblk & ~0xFFFFFFFFULL ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out; + } +if (is_block) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + goto out; + } + +/* +* Check each dir free block's bestfree data. +* +* Iterate all the fsbcount-aligned block offsets in +* this directory. The directory block reading code is +* smart enough to do its own bmap lookups to handle +* discontiguous directory blocks. When we're done +* with the extent record, re-query the bmap at the +* next fsbcount-aligned offset to avoid redundant +* block checks. +*/ +for (lblk =roundup ((xfs_dablk_t )got.br_startoff, + args.geo->fsbcount); + lblk < got.br_startoff + got.br_blockcount; + lblk += args.geo->fsbcount) { + error =xfs_scrub_directory_free_bestfree (sc, &args, + lblk); +if (error) + goto out; + } + dabno = got.br_startoff + got.br_blockcount; + lblk =roundup (dabno, args.geo->fsbcount); + found =xfs_iext_lookup_extent (sc->ip, ifp, lblk, &idx, &got); + } +out: +return error; +} +/* Scrub a whole directory. */ int xfs_scrub_directory(
@@ -279,6 +748,11 @@ xfs_scrub_directory(if (error)return error; +/* Check the freespace. */ + error =xfs_scrub_directory_blocks (sc); +if (error) +return error; +/* * Check that every dirent we see can also be looked up by hash. * Userspace usually asks for a 32k buffer, so we will too.
Re: [PATCH v2 25/30] xfs: scrub directory freespace
Quoted 6 lines · Darrick J. Wong
Check the free space information in a directory.
Signed-off-by Darrick J. Wong <[email protected]>---
v2: make the freespace and leaf checks more complete
---
.....
Quoted 41 lines
fs/xfs/scrub/dir.c +41
-0+/* Make sure the bestfrees are actually the best free spaces. */ + ptr = (char *)d_ops->data_entry_p (bp->b_addr); +if (is_block) { +struct xfs_dir2_block_tail *btp; + + btp =xfs_dir2_block_tail_p (mp->m_dir_geo, bp->b_addr); + endptr = (char *)xfs_dir2_block_leaf_p (btp); + }else + endptr = (char *)bp->b_addr +BBTOB (bp->b_length); +while (ptr < endptr) { + dup = (struct xfs_dir2_data_unused *)ptr; +/* Skip real entries */ +if (dup->freetag !=cpu_to_be16 (XFS_DIR2_DATA_FREE_TAG )) { +struct xfs_dir2_data_entry *dep; + + dep = (struct xfs_dir2_data_entry *)ptr; + newlen = d_ops->data_entsize (dep->namelen); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); + goto out_buf; + } + ptr += newlen; +continue ; + } + +/* Spot check this free entry */ + tag =be16_to_cpu (*xfs_dir2_data_unused_tag_p (dup)); +if (tag != ((char *)dup - (char *)bp->b_addr)) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + +/* +* Either this entry is a bestfree or it's smaller than +* any of the bestfrees. +*/ +xfs_scrub_directory_check_free_entry (sc, lblk, bf, dup); + +/* Move on. */ + newlen =be16_to_cpu (dup->length); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk);
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 402
Quoted 10 lines
fs/xfs/scrub/dir.c +10
-0+ goto out_buf; + } + ptr += newlen; +if (ptr <= endptr) + nr_frees++; + } + +/* Did we go off the end? */ +if (ptr > endptr) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk);
ptr >= endptr?
Otherwise looks good.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH v2 25/30] xfs: scrub directory freespace
Quoted 8 lines · Dave Chinner
Quoted 6 lines · Darrick J. Wong
Check the free space information in a directory.
Signed-off-by Darrick J. Wong <[email protected]>---
v2: make the freespace and leaf checks more complete
---.....
Quoted 9 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 360
Quoted 9 lines
fs/xfs/scrub/dir.c +9
-0+/* Make sure the bestfrees are actually the best free spaces. */ + ptr = (char *)d_ops->data_entry_p (bp->b_addr); +if (is_block) { +struct xfs_dir2_block_tail *btp; + + btp =xfs_dir2_block_tail_p (mp->m_dir_geo, bp->b_addr); + endptr = (char *)xfs_dir2_block_leaf_p (btp); + }else + endptr = (char *)bp->b_addr +BBTOB (bp->b_length);
/* Iterate the entries, stopping when we hit or go past the end. */
Quoted 32 lines
Quoted 32 lines
fs/xfs/scrub/dir.c +32
-0+while (ptr < endptr) { + dup = (struct xfs_dir2_data_unused *)ptr; +/* Skip real entries */ +if (dup->freetag !=cpu_to_be16 (XFS_DIR2_DATA_FREE_TAG )) { +struct xfs_dir2_data_entry *dep; + + dep = (struct xfs_dir2_data_entry *)ptr; + newlen = d_ops->data_entsize (dep->namelen); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , + lblk); + goto out_buf; + } + ptr += newlen; +continue ; + } + +/* Spot check this free entry */ + tag =be16_to_cpu (*xfs_dir2_data_unused_tag_p (dup)); +if (tag != ((char *)dup - (char *)bp->b_addr)) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk); + +/* +* Either this entry is a bestfree or it's smaller than +* any of the bestfrees. +*/ +xfs_scrub_directory_check_free_entry (sc, lblk, bf, dup); + +/* Move on. */ + newlen =be16_to_cpu (dup->length); +if (newlen <=0 ) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK , lblk);
Quoted 8 lines
fs/xfs/scrub/dir.c xfs_scrub_dir_rec( · line 400
Quoted 8 lines
fs/xfs/scrub/dir.c +8
-0+ goto out_buf; + } + ptr += newlen; +if (ptr <= endptr) + nr_frees++; + } + +/* Did we go off the end? */
/* We're required to fill all the space. */
--D
Quoted 16 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (ptr > endptr) + xfs_scrub_fblock_set_corrupt(sc, XFS_DATA_FORK, lblk);ptr >= endptr?
Otherwise looks good.
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 v12 26/30 xfs: scrub extended attributes
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Scrub the hash tree, keys, and values in an extended attribute structure. Refactor the attribute code to use the transaction if the caller supplied one to avoid buffer deadocks.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 3 - fs/xfs/scrub/attr.c | 239 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/common.h | 2 fs/xfs/scrub/scrub.c | 8 ++ fs/xfs/scrub/scrub.h | 2 fs/xfs/xfs_attr.h | 5 + fs/xfs/xfs_attr_list.c | 7 + 8 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 fs/xfs/scrub/attr.c
fs/xfs/Makefile +1 -0
@@ -148,6 +148,7 @@ xfs-y += $(addprefix scrub/, \ trace.o \ agheader.o \ alloc.o \ + attr.o \ bmap.o \ btree.o \ common.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -499,9 +499,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_BMBTA 13/* attr fork block mapping */ #define XFS_SCRUB_TYPE_BMBTC 14/* CoW fork block mapping */ #define XFS_SCRUB_TYPE_DIR 15/* directory */ +#define XFS_SCRUB_TYPE_XATTR 16/* extended attribute */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 16 +#define XFS_SCRUB_TYPE_NR 17/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/attr.c new file +239 -0
239 lines not on this page · raw message
fs/xfs/scrub/common.h +2 -0
@@ -95,6 +95,8 @@ int xfs_scrub_setup_inode_bmap_data(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_directory (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_xattr (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,
fs/xfs/scrub/scrub.c +8 -0
@@ -148,6 +148,10 @@ xfs_scrub_teardown(iput (VFS_I (sc->ip)); sc->ip =NULL ; } +if (sc->buf) { +kmem_free (sc->buf); + sc->buf =NULL ; + }return error; }
@@ -221,6 +225,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_directory, .scrub = xfs_scrub_directory, }, + {/* extended attributes */ + .setup = xfs_scrub_setup_xattr, + .scrub = xfs_scrub_xattr, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +2 -0
@@ -59,6 +59,7 @@ struct xfs_scrub_context {const struct xfs_scrub_meta_ops *ops;struct xfs_trans *tp;struct xfs_inode *ip; +void *buf;uint ilock_flags;bool try_harder;
@@ -83,5 +84,6 @@ int xfs_scrub_bmap_data(struct xfs_scrub_context *sc);int xfs_scrub_bmap_attr (struct xfs_scrub_context *sc);int xfs_scrub_bmap_cow (struct xfs_scrub_context *sc);int xfs_scrub_directory (struct xfs_scrub_context *sc); +int xfs_scrub_xattr (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/xfs_attr.h +4 -1
@@ -48,6 +48,8 @@ struct xfs_attr_list_context;#define ATTR_KERNOTIME 0x1000/* [kernel] don't update inode timestamps */ #define ATTR_KERNOVAL 0x2000/* [kernel] get attr size only, not value */ +#define ATTR_INCOMPLETE 0x4000/* [kernel] return INCOMPLETE attr keys */ +#define XFS_ATTR_FLAGS \ { ATTR_DONTFOLLOW, "DONTFOLLOW" }, \ { ATTR_ROOT, "ROOT" }, \
@@ -56,7 +58,8 @@ struct xfs_attr_list_context; { ATTR_CREATE, "CREATE" }, \ { ATTR_REPLACE, "REPLACE" }, \ { ATTR_KERNOTIME, "KERNOTIME" }, \ - { ATTR_KERNOVAL, "KERNOVAL" } + { ATTR_KERNOVAL, "KERNOVAL" }, \ + { ATTR_INCOMPLETE, "INCOMPLETE" }/* * The maximum size (into the kernel or returned from the kernel) of an
fs/xfs/xfs_attr_list.c +6 -1
@@ -407,7 +407,8 @@ xfs_attr3_leaf_list_int( cursor->offset =0 ; } -if (entry->flags &XFS_ATTR_INCOMPLETE ) +if ((entry->flags &XFS_ATTR_INCOMPLETE ) && + !(context->flags &ATTR_INCOMPLETE ))continue ;/* skip incomplete entries */ if (entry->flags &XFS_ATTR_LOCAL ) {
@@ -583,6 +584,10 @@ xfs_attr_list( (cursor->hashval || cursor->blkno || cursor->offset))return -EINVAL ; +/* Only internal consumers can retrieve incomplete attrs. */ +if (flags &ATTR_INCOMPLETE ) +return -EINVAL ; +/* * Check for a properly aligned buffer. */
Re: [PATCH 26/30] xfs: scrub extended attributes
Quoted 5 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub the hash tree, keys, and values in an extended attribute structure. Refactor the attribute code to use the transaction if the caller supplied one to avoid buffer deadocks.
Looks good.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 27/30 xfs: scrub symbolic links
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create the infrastructure to scrub symbolic link data.
--- fs/xfs/Makefile | 1 + fs/xfs/libxfs/xfs_fs.h | 3 +- fs/xfs/scrub/common.h | 2 + fs/xfs/scrub/scrub.c | 4 ++ fs/xfs/scrub/scrub.h | 1 + fs/xfs/scrub/symlink.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/symlink.c
fs/xfs/Makefile +1 -0
@@ -159,5 +159,6 @@ xfs-y += $(addprefix scrub/, \ refcount.o \ rmap.o \ scrub.o \ + symlink.o \ ) endif
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -500,9 +500,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_BMBTC 14/* CoW fork block mapping */ #define XFS_SCRUB_TYPE_DIR 15/* directory */ #define XFS_SCRUB_TYPE_XATTR 16/* extended attribute */ +#define XFS_SCRUB_TYPE_SYMLINK 17/* symbolic link */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 17 +#define XFS_SCRUB_TYPE_NR 18/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.h +2 -0
@@ -97,6 +97,8 @@ int xfs_scrub_setup_directory(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_xattr (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_symlink (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,
fs/xfs/scrub/scrub.c +4 -0
@@ -229,6 +229,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_xattr, .scrub = xfs_scrub_xattr, }, + {/* symbolic link */ + .setup = xfs_scrub_setup_symlink, + .scrub = xfs_scrub_symlink, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +1 -0
@@ -85,5 +85,6 @@ int xfs_scrub_bmap_attr(struct xfs_scrub_context *sc);int xfs_scrub_bmap_cow (struct xfs_scrub_context *sc);int xfs_scrub_directory (struct xfs_scrub_context *sc);int xfs_scrub_xattr (struct xfs_scrub_context *sc); +int xfs_scrub_symlink (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
fs/xfs/scrub/symlink.c new file +92 -0
92 lines not on this page · raw message
Re: [PATCH 27/30] xfs: scrub symbolic links
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create the infrastructure to scrub symbolic link data.
looks good.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 28/30 xfs: scrub directory parent pointers
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Scrub parent pointers, sort of. For directories, we can ride the '..' entry up to the parent to confirm that there's at most one dentry that points back to this directory.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 3 - fs/xfs/scrub/common.h | 2 fs/xfs/scrub/parent.c | 277 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 4 + fs/xfs/scrub/scrub.h | 1 6 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/parent.c
fs/xfs/Makefile +1 -0
@@ -156,6 +156,7 @@ xfs-y += $(addprefix scrub/, \ dir.o \ ialloc.o \ inode.o \ + parent.o \ refcount.o \ rmap.o \ scrub.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -501,9 +501,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_DIR 15/* directory */ #define XFS_SCRUB_TYPE_XATTR 16/* extended attribute */ #define XFS_SCRUB_TYPE_SYMLINK 17/* symbolic link */ +#define XFS_SCRUB_TYPE_PARENT 18/* parent pointers */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 18 +#define XFS_SCRUB_TYPE_NR 19/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.h +2 -0
@@ -99,6 +99,8 @@ int xfs_scrub_setup_xattr(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_symlink (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_parent (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,
fs/xfs/scrub/parent.c new file +277 -0
277 lines not on this page · raw message
fs/xfs/scrub/scrub.c +4 -0
@@ -233,6 +233,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_symlink, .scrub = xfs_scrub_symlink, }, + {/* parent pointers */ + .setup = xfs_scrub_setup_parent, + .scrub = xfs_scrub_parent, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +1 -0
@@ -86,5 +86,6 @@ int xfs_scrub_bmap_cow(struct xfs_scrub_context *sc);int xfs_scrub_directory (struct xfs_scrub_context *sc);int xfs_scrub_xattr (struct xfs_scrub_context *sc);int xfs_scrub_symlink (struct xfs_scrub_context *sc); +int xfs_scrub_parent (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 28/30] xfs: scrub directory parent pointers
Quoted 5 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub parent pointers, sort of. For directories, we can ride the '..' entry up to the parent to confirm that there's at most one dentry that points back to this directory.
....
Quoted 26 lines
fs/xfs/scrub/parent.c new file +26
-0+/* Count the number of dentries in the parent dir that point to this inode. */ +STATIC int +xfs_scrub_parent_count_parent_dentries ( +struct xfs_scrub_context *sc, +struct xfs_inode *parent, +xfs_nlink_t *nlink) +{ +struct xfs_scrub_parent_ctx spc = { + .dc.actor = xfs_scrub_parent_actor, + .dc.pos =0 , + .ino = sc->ip->i_ino, + .nlink =0 , + }; +struct xfs_ifork *ifp; +size_t bufsize; +loff_t oldpos; +uint lock_mode; +int error; + +/* +* Load the parent directory's extent map. A regular directory +* open would start readahead (and thus load the extent map) +* before we even got to a readdir call, but this isn't +* guaranteed here. +*/ + lock_mode =xfs_ilock_data_map_shared (parent);
fs/xfs/scrub/parent.c line 114
Quoted 10 lines
fs/xfs/scrub/parent.c new file +10
-0+ ifp =XFS_IFORK_PTR (parent,XFS_DATA_FORK ); +if (XFS_IFORK_FORMAT (parent,XFS_DATA_FORK ) ==XFS_DINODE_FMT_BTREE && + !(ifp->if_flags &XFS_IFEXTENTS )) { + error =xfs_iread_extents (sc->tp, parent,XFS_DATA_FORK ); +if (error) { +xfs_iunlock (parent, lock_mode); +return error; + } + } +xfs_iunlock (parent, lock_mode);
Why not just do what xfs_dir_open() does? i.e.
/*
* If there are any blocks, read-ahead block 0 as we're almost
* certain to have the next operation be a read there.
*/
mode = xfs_ilock_data_map_shared(ip);
if (ip->i_d.di_nextents > 0)
error = xfs_dir3_data_readahead(ip, 0, -1);
xfs_iunlock(ip, mode);fs/xfs/scrub/parent.c line 121
Quoted 6 lines
fs/xfs/scrub/parent.c new file +6
-0+/* +* Iterate the parent dir to confirm that there is +* exactly one entry pointing back to the inode being +* scanned. +*/ + bufsize = (size_t )min_t (loff_t,32768 , parent->i_d.di_size);
Perhaps we need a define for that 32k magic number now it's being used in multiple places?
Quoted 62 lines
fs/xfs/scrub/parent.c new file +62
-0+ oldpos =0 ; +while (true) { + error =xfs_readdir (sc->tp, parent, &spc.dc, bufsize); +if (error) + goto out; +if (oldpos == spc.dc.pos) +break ; + oldpos = spc.dc.pos; + } + *nlink = spc.nlink; +out: +return error; +} + +/* Scrub a parent pointer. */ +int +xfs_scrub_parent ( +struct xfs_scrub_context *sc) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_inode *dp =NULL ; +xfs_ino_t dnum; +xfs_nlink_t expected_nlink; +xfs_nlink_t nlink; +int tries =0 ; +int error; + +/* +* If we're a directory, check that the '..' link points up to +* a directory that has one entry pointing to us. +*/ +if (!S_ISDIR (VFS_I (sc->ip)->i_mode)) +return -ENOENT ; + +/* We're not a special inode, are we? */ +if (!xfs_verify_dir_ino_ptr (mp, sc->ip->i_ino)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); + goto out; + } + +/* +* If we're an unlinked directory, the parent /won't/ have a link +* to us. Otherwise, it should have one link. +*/ + expected_nlink =VFS_I (sc->ip)->i_nlink ==0 ?0 :1 ; + +/* +* The VFS grabs a read or write lock via i_rwsem before it reads +* or writes to a directory. If we've gotten this far we've +* already obtained IOLOCK_EXCL, which (since 4.10) is the same as +* getting a write lock on i_rwsem. Therefore, it is safe for us +* to drop the ILOCK here in order to do directory lookups. +*/ + sc->ilock_flags &= ~(XFS_ILOCK_EXCL |XFS_MMAPLOCK_EXCL ); +xfs_iunlock (sc->ip,XFS_ILOCK_EXCL |XFS_MMAPLOCK_EXCL ); + +/* Look up '..' */ + error =xfs_dir_lookup (sc->tp, sc->ip, &xfs_name_dotdot, &dnum,NULL ); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK ,0 , &error)) + goto out; +if (!xfs_verify_dir_ino_ptr (mp, dnum)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 );
fs/xfs/scrub/parent.c line 193
Quoted 10 lines
fs/xfs/scrub/parent.c new file +10
-0+ goto out; + } + +/* Is this the root dir? Then '..' must point to itself. */ +if (sc->ip == mp->m_rootip) { +if (sc->ip->i_ino != mp->m_sb.sb_rootino || + sc->ip->i_ino != dnum) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); +return 0 ; + }
All good to here.
Quoted 57 lines
fs/xfs/scrub/parent.c new file +57
-0+try_again: +/* Otherwise, '..' must not point to ourselves. */ +if (sc->ip->i_ino == dnum) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); + goto out; + } + + error =xfs_iget (mp, sc->tp, dnum,XFS_IGET_DONTCACHE ,0 , &dp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK ,0 , &error)) + goto out; +if (dp == sc->ip) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); + goto out_rele; + } + +/* +* We prefer to keep the inode locked while we lock and search +* its alleged parent for a forward reference. However, this +* child -> parent scheme can deadlock with the parent -> child +* scheme that is normally used. Therefore, if we can lock the +* parent, just validate the references and get out. +*/ +if (xfs_ilock_nowait (dp,XFS_IOLOCK_SHARED )) { + error =xfs_scrub_parent_count_parent_dentries (sc, dp, &nlink); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK ,0 , + &error)) + goto out_unlock; +if (nlink != expected_nlink) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); + goto out_unlock; + } + +/* +* The game changes if we get here. We failed to lock the parent, +* so we're going to try to verify both pointers while only holding +* one lock so as to avoid deadlocking with something that's actually +* trying to traverse down the directory tree. +*/ +xfs_iunlock (sc->ip, sc->ilock_flags); + sc->ilock_flags =0 ; +xfs_ilock (dp,XFS_IOLOCK_SHARED ); + +/* Go looking for our dentry. */ + error =xfs_scrub_parent_count_parent_dentries (sc, dp, &nlink); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK ,0 , &error)) + goto out_unlock; + +/* Drop the parent lock, relock this inode. */ +xfs_iunlock (dp,XFS_IOLOCK_SHARED ); + sc->ilock_flags =XFS_IOLOCK_EXCL ; +xfs_ilock (sc->ip, sc->ilock_flags); + +/* Look up '..' to see if the inode changed. */ + error =xfs_dir_lookup (sc->tp, sc->ip, &xfs_name_dotdot, &dnum,NULL ); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK ,0 , &error)) + goto out_rele; +
fs/xfs/scrub/parent.c line 261
Quoted 10 lines
fs/xfs/scrub/parent.c new file +10
-0+/* Drat, parent changed. Try again! */ +if (dnum != dp->i_ino) { +iput (VFS_I (dp)); + tries++; +if (tries <20 ) + goto try_again; +xfs_scrub_set_incomplete (sc); + goto out; + } +iput (VFS_I (dp));
Can you factor this into a loop and function?
do {
valid = xfs_scrub_parent_validate(&error)
if (error)
goto out_unlock;
} while (!valid && ++retries < 20)Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 28/30] xfs: scrub directory parent pointers
Quoted 48 lines · Dave Chinner
Quoted 5 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub parent pointers, sort of. For directories, we can ride the '..' entry up to the parent to confirm that there's at most one dentry that points back to this directory.
....
Quoted 26 lines
fs/xfs/scrub/parent.c new file +26
-0+/* Count the number of dentries in the parent dir that point to this inode. */ +STATIC int +xfs_scrub_parent_count_parent_dentries ( +struct xfs_scrub_context *sc, +struct xfs_inode *parent, +xfs_nlink_t *nlink) +{ +struct xfs_scrub_parent_ctx spc = { + .dc.actor = xfs_scrub_parent_actor, + .dc.pos =0 , + .ino = sc->ip->i_ino, + .nlink =0 , + }; +struct xfs_ifork *ifp; +size_t bufsize; +loff_t oldpos; +uint lock_mode; +int error; + +/* +* Load the parent directory's extent map. A regular directory +* open would start readahead (and thus load the extent map) +* before we even got to a readdir call, but this isn't +* guaranteed here. +*/ + lock_mode =xfs_ilock_data_map_shared (parent);fs/xfs/scrub/parent.c line 114
Quoted 10 lines
fs/xfs/scrub/parent.c new file +10
-0+ ifp =XFS_IFORK_PTR (parent,XFS_DATA_FORK ); +if (XFS_IFORK_FORMAT (parent,XFS_DATA_FORK ) ==XFS_DINODE_FMT_BTREE && + !(ifp->if_flags &XFS_IFEXTENTS )) { + error =xfs_iread_extents (sc->tp, parent,XFS_DATA_FORK ); +if (error) { +xfs_iunlock (parent, lock_mode); +return error; + } + } +xfs_iunlock (parent, lock_mode);Why not just do what
xfs_dir_open()does? i.e.
Quoted 8 lines
/*
* If there are any blocks, read-ahead block 0 as we're almost
* certain to have the next operation be a read there.
*/
mode = xfs_ilock_data_map_shared(ip);
if (ip->i_d.di_nextents > 0)
error = xfs_dir3_data_readahead(ip, 0, -1);
xfs_iunlock(ip, mode);Ok.
Quoted 9 lines
fs/xfs/scrub/parent.c line 121
Quoted 6 lines
fs/xfs/scrub/parent.c new file +6
-0+/* +* Iterate the parent dir to confirm that there is +* exactly one entry pointing back to the inode being +* scanned. +*/ + bufsize = (size_t )min_t (loff_t,32768 , parent->i_d.di_size);Perhaps we need a define for that 32k magic number now it's being used in multiple places?
Magic indeed; glibc uses the minimum of (4 * BUFSIZ) or (sizeof(struct dirent)); musl uses a static 2k buffer; dietlibc uses (PAGE_SIZE - sizeof(structure header))...
...what would we call it?
/*
* The Linux API doesn't pass down the total size of the buffer we read
* into down to the filesystem. With the filldir concept it's not
* needed for correct information, but the XFS dir2 leaf code wants an
* estimate of the buffer size to calculate its readahead window and
* size the buffers used for mapping to physical blocks.
*
* Try to give it an estimate that's good enough, maybe at some point we
* can change the ->readdir prototype to include the buffer size. For
* now we use the current glibc buffer size.
*/
#define XFS_DEFAULT_READDIR_BUFSIZE (32768)
(As a side question, do we want to bump this up to a full pagesize on architectures that have 64k pages? I'd probably just leave it, but let's see if anyone running those architectures complains or sends in a patch?)
Quoted 145 lines
Quoted 62 lines
fs/xfs/scrub/parent.c new file +62
-0+ oldpos =0 ; +while (true) { + error =xfs_readdir (sc->tp, parent, &spc.dc, bufsize); +if (error) + goto out; +if (oldpos == spc.dc.pos) +break ; + oldpos = spc.dc.pos; + } + *nlink = spc.nlink; +out: +return error; +} + +/* Scrub a parent pointer. */ +int +xfs_scrub_parent ( +struct xfs_scrub_context *sc) +{ +struct xfs_mount *mp = sc->mp; +struct xfs_inode *dp =NULL ; +xfs_ino_t dnum; +xfs_nlink_t expected_nlink; +xfs_nlink_t nlink; +int tries =0 ; +int error; + +/* +* If we're a directory, check that the '..' link points up to +* a directory that has one entry pointing to us. +*/ +if (!S_ISDIR (VFS_I (sc->ip)->i_mode)) +return -ENOENT ; + +/* We're not a special inode, are we? */ +if (!xfs_verify_dir_ino_ptr (mp, sc->ip->i_ino)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); + goto out; + } + +/* +* If we're an unlinked directory, the parent /won't/ have a link +* to us. Otherwise, it should have one link. +*/ + expected_nlink =VFS_I (sc->ip)->i_nlink ==0 ?0 :1 ; + +/* +* The VFS grabs a read or write lock via i_rwsem before it reads +* or writes to a directory. If we've gotten this far we've +* already obtained IOLOCK_EXCL, which (since 4.10) is the same as +* getting a write lock on i_rwsem. Therefore, it is safe for us +* to drop the ILOCK here in order to do directory lookups. +*/ + sc->ilock_flags &= ~(XFS_ILOCK_EXCL |XFS_MMAPLOCK_EXCL ); +xfs_iunlock (sc->ip,XFS_ILOCK_EXCL |XFS_MMAPLOCK_EXCL ); + +/* Look up '..' */ + error =xfs_dir_lookup (sc->tp, sc->ip, &xfs_name_dotdot, &dnum,NULL ); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK ,0 , &error)) + goto out; +if (!xfs_verify_dir_ino_ptr (mp, dnum)) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 );fs/xfs/scrub/parent.c line 193
Quoted 10 lines
fs/xfs/scrub/parent.c new file +10
-0+ goto out; + } + +/* Is this the root dir? Then '..' must point to itself. */ +if (sc->ip == mp->m_rootip) { +if (sc->ip->i_ino != mp->m_sb.sb_rootino || + sc->ip->i_ino != dnum) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); +return 0 ; + }All good to here.
Quoted 57 lines
fs/xfs/scrub/parent.c new file +57
-0+try_again: +/* Otherwise, '..' must not point to ourselves. */ +if (sc->ip->i_ino == dnum) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); + goto out; + } + + error =xfs_iget (mp, sc->tp, dnum,XFS_IGET_DONTCACHE ,0 , &dp); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK ,0 , &error)) + goto out; +if (dp == sc->ip) { +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); + goto out_rele; + } + +/* +* We prefer to keep the inode locked while we lock and search +* its alleged parent for a forward reference. However, this +* child -> parent scheme can deadlock with the parent -> child +* scheme that is normally used. Therefore, if we can lock the +* parent, just validate the references and get out. +*/ +if (xfs_ilock_nowait (dp,XFS_IOLOCK_SHARED )) { + error =xfs_scrub_parent_count_parent_dentries (sc, dp, &nlink); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK ,0 , + &error)) + goto out_unlock; +if (nlink != expected_nlink) +xfs_scrub_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); + goto out_unlock; + } + +/* +* The game changes if we get here. We failed to lock the parent, +* so we're going to try to verify both pointers while only holding +* one lock so as to avoid deadlocking with something that's actually +* trying to traverse down the directory tree. +*/ +xfs_iunlock (sc->ip, sc->ilock_flags); + sc->ilock_flags =0 ; +xfs_ilock (dp,XFS_IOLOCK_SHARED ); + +/* Go looking for our dentry. */ + error =xfs_scrub_parent_count_parent_dentries (sc, dp, &nlink); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK ,0 , &error)) + goto out_unlock; + +/* Drop the parent lock, relock this inode. */ +xfs_iunlock (dp,XFS_IOLOCK_SHARED ); + sc->ilock_flags =XFS_IOLOCK_EXCL ; +xfs_ilock (sc->ip, sc->ilock_flags); + +/* Look up '..' to see if the inode changed. */ + error =xfs_dir_lookup (sc->tp, sc->ip, &xfs_name_dotdot, &dnum,NULL ); +if (!xfs_scrub_fblock_process_error (sc,XFS_DATA_FORK ,0 , &error)) + goto out_rele; +fs/xfs/scrub/parent.c line 261
Quoted 10 lines
fs/xfs/scrub/parent.c new file +10
-0+/* Drat, parent changed. Try again! */ +if (dnum != dp->i_ino) { +iput (VFS_I (dp)); + tries++; +if (tries <20 ) + goto try_again; +xfs_scrub_set_incomplete (sc); + goto out; + } +iput (VFS_I (dp));Can you factor this into a loop and function?
Quoted 5 lines
do {
valid = xfs_scrub_parent_validate(&error)
if (error)
goto out_unlock;
} while (!valid && ++retries < 20)Ok.
--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 28/30] xfs: scrub directory parent pointers
Quoted 76 lines · Darrick J. Wong
Quoted 48 lines · Dave Chinner
Quoted 5 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub parent pointers, sort of. For directories, we can ride the '..' entry up to the parent to confirm that there's at most one dentry that points back to this directory.
....
Quoted 26 lines
fs/xfs/scrub/parent.c new file +26
-0+/* Count the number of dentries in the parent dir that point to this inode. */ +STATIC int +xfs_scrub_parent_count_parent_dentries ( +struct xfs_scrub_context *sc, +struct xfs_inode *parent, +xfs_nlink_t *nlink) +{ +struct xfs_scrub_parent_ctx spc = { + .dc.actor = xfs_scrub_parent_actor, + .dc.pos =0 , + .ino = sc->ip->i_ino, + .nlink =0 , + }; +struct xfs_ifork *ifp; +size_t bufsize; +loff_t oldpos; +uint lock_mode; +int error; + +/* +* Load the parent directory's extent map. A regular directory +* open would start readahead (and thus load the extent map) +* before we even got to a readdir call, but this isn't +* guaranteed here. +*/ + lock_mode =xfs_ilock_data_map_shared (parent);fs/xfs/scrub/parent.c line 114
Quoted 10 lines
fs/xfs/scrub/parent.c new file +10
-0+ ifp =XFS_IFORK_PTR (parent,XFS_DATA_FORK ); +if (XFS_IFORK_FORMAT (parent,XFS_DATA_FORK ) ==XFS_DINODE_FMT_BTREE && + !(ifp->if_flags &XFS_IFEXTENTS )) { + error =xfs_iread_extents (sc->tp, parent,XFS_DATA_FORK ); +if (error) { +xfs_iunlock (parent, lock_mode); +return error; + } + } +xfs_iunlock (parent, lock_mode);Why not just do what
xfs_dir_open()does? i.e.Quoted 8 lines
/* * If there are any blocks, read-ahead block 0 as we're almost * certain to have the next operation be a read there. */ mode = xfs_ilock_data_map_shared(ip); if (ip->i_d.di_nextents > 0) error = xfs_dir3_data_readahead(ip, 0, -1); xfs_iunlock(ip, mode);Ok.
Quoted 9 lines
fs/xfs/scrub/parent.c line 121
Quoted 6 lines
fs/xfs/scrub/parent.c new file +6
-0+/* +* Iterate the parent dir to confirm that there is +* exactly one entry pointing back to the inode being +* scanned. +*/ + bufsize = (size_t )min_t (loff_t,32768 , parent->i_d.di_size);Perhaps we need a define for that 32k magic number now it's being used in multiple places?
Magic indeed; glibc uses the minimum of (4 * BUFSIZ) or (sizeof(struct dirent)); musl uses a static 2k buffer; dietlibc uses (
PAGE_SIZE- sizeof(structure header))......what would we call it?
Quoted 12 lines
/*
* The Linux API doesn't pass down the total size of the buffer we read
* into down to the filesystem. With the filldir concept it's not
* needed for correct information, but the XFS dir2 leaf code wants an
* estimate of the buffer size to calculate its readahead window and
* size the buffers used for mapping to physical blocks.
*
* Try to give it an estimate that's good enough, maybe at some point we
* can change the ->readdir prototype to include the buffer size. For
* now we use the current glibc buffer size.
*/
#defineXFS_DEFAULT_READDIR_BUFSIZE(32768)
That looks fine, though I think XFS_READDIR_BUFSIZE (or purple!) is sufficient.
Quoted 4 lines
(As a side question, do we want to bump this up to a full pagesize on architectures that have 64k pages? I'd probably just leave it, but let's see if anyone running those architectures complains or sends in a patch?)
If it was to be dynamic, it should be determined by the directory block size, not the arch page size.
Cheers,
Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 28/30] xfs: scrub directory parent pointers
Quoted 90 lines · Dave Chinner
Quoted 76 lines · Darrick J. Wong
Quoted 48 lines · Dave Chinner
Quoted 5 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Scrub parent pointers, sort of. For directories, we can ride the '..' entry up to the parent to confirm that there's at most one dentry that points back to this directory.
....
Quoted 26 lines
fs/xfs/scrub/parent.c new file +26
-0+/* Count the number of dentries in the parent dir that point to this inode. */ +STATIC int +xfs_scrub_parent_count_parent_dentries ( +struct xfs_scrub_context *sc, +struct xfs_inode *parent, +xfs_nlink_t *nlink) +{ +struct xfs_scrub_parent_ctx spc = { + .dc.actor = xfs_scrub_parent_actor, + .dc.pos =0 , + .ino = sc->ip->i_ino, + .nlink =0 , + }; +struct xfs_ifork *ifp; +size_t bufsize; +loff_t oldpos; +uint lock_mode; +int error; + +/* +* Load the parent directory's extent map. A regular directory +* open would start readahead (and thus load the extent map) +* before we even got to a readdir call, but this isn't +* guaranteed here. +*/ + lock_mode =xfs_ilock_data_map_shared (parent);fs/xfs/scrub/parent.c line 114
Quoted 10 lines
fs/xfs/scrub/parent.c new file +10
-0+ ifp =XFS_IFORK_PTR (parent,XFS_DATA_FORK ); +if (XFS_IFORK_FORMAT (parent,XFS_DATA_FORK ) ==XFS_DINODE_FMT_BTREE && + !(ifp->if_flags &XFS_IFEXTENTS )) { + error =xfs_iread_extents (sc->tp, parent,XFS_DATA_FORK ); +if (error) { +xfs_iunlock (parent, lock_mode); +return error; + } + } +xfs_iunlock (parent, lock_mode);Why not just do what
xfs_dir_open()does? i.e.Quoted 8 lines
/* * If there are any blocks, read-ahead block 0 as we're almost * certain to have the next operation be a read there. */ mode = xfs_ilock_data_map_shared(ip); if (ip->i_d.di_nextents > 0) error = xfs_dir3_data_readahead(ip, 0, -1); xfs_iunlock(ip, mode);Ok.
Quoted 9 lines
fs/xfs/scrub/parent.c line 121
Quoted 6 lines
fs/xfs/scrub/parent.c new file +6
-0+/* +* Iterate the parent dir to confirm that there is +* exactly one entry pointing back to the inode being +* scanned. +*/ + bufsize = (size_t )min_t (loff_t,32768 , parent->i_d.di_size);Perhaps we need a define for that 32k magic number now it's being used in multiple places?
Magic indeed; glibc uses the minimum of (4 * BUFSIZ) or (sizeof(struct dirent)); musl uses a static 2k buffer; dietlibc uses (
PAGE_SIZE- sizeof(structure header))......what would we call it?
Quoted 12 lines
/*
* The Linux API doesn't pass down the total size of the buffer we read
* into down to the filesystem. With the filldir concept it's not
* needed for correct information, but the XFS dir2 leaf code wants an
* estimate of the buffer size to calculate its readahead window and
* size the buffers used for mapping to physical blocks.
*
* Try to give it an estimate that's good enough, maybe at some point we
* can change the ->readdir prototype to include the buffer size. For
* now we use the current glibc buffer size.
*/
#defineXFS_DEFAULT_READDIR_BUFSIZE(32768)
Quoted 2 lines
That looks fine, though I think
XFS_READDIR_BUFSIZE(or purple!) is sufficient.
I like the shorter name, done.
Quoted 7 lines
Quoted 4 lines
(As a side question, do we want to bump this up to a full pagesize on architectures that have 64k pages? I'd probably just leave it, but let's see if anyone running those architectures complains or sends in a patch?)
If it was to be dynamic, it should be determined by the directory block size, not the arch page size.
<nod>
--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
PATCH v2 28/30 xfs: scrub directory parent pointers
In reply to Darrick J. WongScrub parent pointers, sort of. For directories, we can ride the '..' entry up to the parent to confirm that there's at most one dentry that points back to this directory.
---
v2: refactor the single-ilock-retry code into a separate validate function
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 3 fs/xfs/scrub/common.h | 2 fs/xfs/scrub/parent.c | 309 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 4 + fs/xfs/scrub/scrub.h | 1 6 files changed, 319 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/parent.c
fs/xfs/Makefile +1 -0
@@ -156,6 +156,7 @@ xfs-y += $(addprefix scrub/, \ dir.o \ ialloc.o \ inode.o \ + parent.o \ refcount.o \ rmap.o \ scrub.o \
fs/xfs/libxfs/xfs_fs.h +2 -1
@@ -501,9 +501,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_DIR 15/* directory */ #define XFS_SCRUB_TYPE_XATTR 16/* extended attribute */ #define XFS_SCRUB_TYPE_SYMLINK 17/* symbolic link */ +#define XFS_SCRUB_TYPE_PARENT 18/* parent pointers */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 18 +#define XFS_SCRUB_TYPE_NR 19/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.h +2 -0
@@ -99,6 +99,8 @@ int xfs_scrub_setup_xattr(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_symlink (struct xfs_scrub_context *sc,struct xfs_inode *ip); +int xfs_scrub_setup_parent (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,
fs/xfs/scrub/parent.c new file +309 -0
309 lines not on this page · raw message
fs/xfs/scrub/scrub.c +4 -0
@@ -243,6 +243,10 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_symlink, .scrub = xfs_scrub_symlink, }, + {/* parent pointers */ + .setup = xfs_scrub_setup_parent, + .scrub = xfs_scrub_parent, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +1 -0
@@ -86,5 +86,6 @@ int xfs_scrub_bmap_cow(struct xfs_scrub_context *sc);int xfs_scrub_directory (struct xfs_scrub_context *sc);int xfs_scrub_xattr (struct xfs_scrub_context *sc);int xfs_scrub_symlink (struct xfs_scrub_context *sc); +int xfs_scrub_parent (struct xfs_scrub_context *sc);#endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH v2 28/30] xfs: scrub directory parent pointers
Quoted 7 lines · Darrick J. Wong
Scrub parent pointers, sort of. For directories, we can ride the '..' entry up to the parent to confirm that there's at most one dentry that points back to this directory.
Signed-off-by Darrick J. Wong <[email protected]>---
v2: refactor the single-ilock-retry code into a separate validate function
Looks fine.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 29/30 xfs: scrub realtime bitmap/summary
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Perform simple tests of the realtime bitmap and summary.
--- fs/xfs/Makefile | 2 + fs/xfs/libxfs/xfs_format.h | 5 ++ fs/xfs/libxfs/xfs_fs.h | 4 +- fs/xfs/scrub/common.h | 9 ++++ fs/xfs/scrub/rtbitmap.c | 108 ++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 10 ++++ fs/xfs/scrub/scrub.h | 15 ++++++ 7 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/rtbitmap.c
fs/xfs/Makefile +2 -0
@@ -162,4 +162,6 @@ xfs-y += $(addprefix scrub/, \ scrub.o \ symlink.o \ ) + +xfs-$(CONFIG_XFS_RT) += scrub/rtbitmap.o endif
fs/xfs/libxfs/xfs_format.h +5 -0
@@ -315,6 +315,11 @@ static inline bool xfs_sb_good_version(struct xfs_sb *sbp)return false; } +static inline bool xfs_sb_version_hasrealtime (struct xfs_sb *sbp) +{ +return sbp->sb_rblocks >0 ; +} +/* * Detect a mismatched features2 field. Older kernels read/wrote * this into the wrong slot, so to be safe we keep them in sync.
fs/xfs/libxfs/xfs_fs.h +3 -1
@@ -502,9 +502,11 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_XATTR 16/* extended attribute */ #define XFS_SCRUB_TYPE_SYMLINK 17/* symbolic link */ #define XFS_SCRUB_TYPE_PARENT 18/* parent pointers */ +#define XFS_SCRUB_TYPE_RTBITMAP 19/* realtime bitmap */ +#define XFS_SCRUB_TYPE_RTSUM 20/* realtime summary */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 19 +#define XFS_SCRUB_TYPE_NR 21/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.h +9 -0
@@ -101,6 +101,15 @@ int xfs_scrub_setup_symlink(struct xfs_scrub_context *sc, struct xfs_inode *ip);int xfs_scrub_setup_parent (struct xfs_scrub_context *sc,struct xfs_inode *ip); +#ifdef CONFIG_XFS_RT +int xfs_scrub_setup_rt (struct xfs_scrub_context *sc,struct xfs_inode *ip); +#else +static inline int +xfs_scrub_setup_rt (struct xfs_scrub_context *sc,struct xfs_inode *ip) +{ +return -ENOENT ; +} +#endif 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,
fs/xfs/scrub/rtbitmap.c new file +108 -0
108 lines not on this page · raw message
fs/xfs/scrub/scrub.c +10 -0
@@ -237,6 +237,16 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .setup = xfs_scrub_setup_parent, .scrub = xfs_scrub_parent, }, + {/* realtime bitmap */ + .setup = xfs_scrub_setup_rt, + .scrub = xfs_scrub_rtbitmap, + .has = xfs_sb_version_hasrealtime, + }, + {/* realtime summary */ + .setup = xfs_scrub_setup_rt, + .scrub = xfs_scrub_rtsummary, + .has = xfs_sb_version_hasrealtime, + }, };/* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +15 -0
@@ -87,5 +87,20 @@ int xfs_scrub_directory(struct xfs_scrub_context *sc);int xfs_scrub_xattr (struct xfs_scrub_context *sc);int xfs_scrub_symlink (struct xfs_scrub_context *sc);int xfs_scrub_parent (struct xfs_scrub_context *sc); +#ifdef CONFIG_XFS_RT +int xfs_scrub_rtbitmap (struct xfs_scrub_context *sc); +int xfs_scrub_rtsummary (struct xfs_scrub_context *sc); +#else +static inline int +xfs_scrub_rtbitmap (struct xfs_scrub_context *sc) +{ +return -ENOENT ; +} +static inline int +xfs_scrub_rtsummary (struct xfs_scrub_context *sc) +{ +return -ENOENT ; +} +#endif #endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 29/30] xfs: scrub realtime bitmap/summary
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Perform simple tests of the realtime bitmap and summary.
....
fs/xfs/scrub/rtbitmap.c line 71
Quoted 5 lines
fs/xfs/scrub/rtbitmap.c new file +5
-0+/* Realtime bitmap. */ + +/* Scrub a free extent record from the realtime bitmap. */ +STATIC int +xfs_scrub_rtbitmap_helper(
s/helper/rec/
Otherwise good.
Signature · 2 lines
Dave Chinner [email protected]
PATCH v12 30/30 xfs: scrub quota information
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Perform some quick sanity testing of the disk quota information.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 5 + fs/xfs/scrub/common.h | 9 + fs/xfs/scrub/quota.c | 301 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 12 ++ fs/xfs/scrub/scrub.h | 9 + 6 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 fs/xfs/scrub/quota.c
fs/xfs/Makefile +1 -0
@@ -164,4 +164,5 @@ xfs-y += $(addprefix scrub/, \ ) xfs-$(CONFIG_XFS_RT) += scrub/rtbitmap.o +xfs-$(CONFIG_XFS_QUOTA) += scrub/quota.o endif
fs/xfs/libxfs/xfs_fs.h +4 -1
@@ -504,9 +504,12 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_PARENT 18/* parent pointers */ #define XFS_SCRUB_TYPE_RTBITMAP 19/* realtime bitmap */ #define XFS_SCRUB_TYPE_RTSUM 20/* realtime summary */ +#define XFS_SCRUB_TYPE_UQUOTA 21/* user quotas */ +#define XFS_SCRUB_TYPE_GQUOTA 22/* group quotas */ +#define XFS_SCRUB_TYPE_PQUOTA 23/* project quotas */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 21 +#define XFS_SCRUB_TYPE_NR 24/* i: Repair this metadata. */ #define XFS_SCRUB_IFLAG_REPAIR (1 << 0)
fs/xfs/scrub/common.h +9 -0
@@ -110,6 +110,15 @@ xfs_scrub_setup_rt(struct xfs_scrub_context *sc, struct xfs_inode *ip)return -ENOENT ; }#endif +#ifdef CONFIG_XFS_QUOTA +int xfs_scrub_setup_quota (struct xfs_scrub_context *sc,struct xfs_inode *ip); +#else +static inline int +xfs_scrub_setup_quota (struct xfs_scrub_context *sc,struct xfs_inode *ip) +{ +return -ENOENT ; +} +#endif 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,
fs/xfs/scrub/quota.c new file +301 -0
301 lines not on this page · raw message
fs/xfs/scrub/scrub.c +12 -0
@@ -247,6 +247,18 @@ static const struct xfs_scrub_meta_ops meta_scrub_ops[] = { .scrub = xfs_scrub_rtsummary, .has = xfs_sb_version_hasrealtime, }, + { /* user quota */ + .setup = xfs_scrub_setup_quota, + .scrub = xfs_scrub_quota, + }, + { /* group quota */ + .setup = xfs_scrub_setup_quota, + .scrub = xfs_scrub_quota, + }, + { /* project quota */ + .setup = xfs_scrub_setup_quota, + .scrub = xfs_scrub_quota, + }, }; /* This isn't a stable feature, warn once per day. */
fs/xfs/scrub/scrub.h +9 -0
@@ -102,5 +102,14 @@ xfs_scrub_rtsummary(struct xfs_scrub_context *sc)return -ENOENT ; }#endif +#ifdef CONFIG_XFS_QUOTA +int xfs_scrub_quota (struct xfs_scrub_context *sc); +#else +static inline int +xfs_scrub_quota (struct xfs_scrub_context *sc) +{ +return -ENOENT ; +} +#endif #endif /* __XFS_SCRUB_SCRUB_H__ */
Re: [PATCH 30/30] xfs: scrub quota information
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Perform some quick sanity testing of the disk quota information.
Looks good.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 30/30] xfs: scrub quota information
Quoted 8 lines · Dave Chinner
Quoted 3 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Perform some quick sanity testing of the disk quota information.
Looks good.
Reviewed-by Dave Chinner <[email protected]>
Hey, you got all the way to the last patch! Thanks a lot for the review!
--D
Quoted 8 lines
Signature · 7 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