xfs: metadata directories and realtime groups
Subsystems (4)
Hi everyone,
Christoph and I have been working on getting the long-delayed metadata directory tree patchset into mergeable shape, and I think we're now satisfied that we've gotten the code to where we want it for 6.12.
First comes all the accumulated bug fixes for 6.11. After that is all the new code:
The metadata directory tree sets us up for much more flexible metadata within an XFS filesystem. Instead of rooting inodes in the superblock which has very limited space, we instead create a directory tree that can contain arbitrary numbers of metadata files.
Having done that, we can now shard the realtime volume into multiple allocation groups, much as we do with AGs for the data device. However, the realtime volume has a fun twist -- each rtgroup gets its own space metadata files, and for that we need a metadata directory tree.
Metadata directory trees and realtime groups also enable us to complete the realtime modernization project, which will add reverse mapping btrees, reflink, quota support, and zoned storage support for rt volumes. The commit-range ioctl is now part of the rt groups patchset, because that's the only practical way to defragment rt files when the rt extent size is larger than 1 fsblock and rmap is enabled. Also, with Jeff Layton's multigrained ctime work headed for 6.12, we can now measure file changes in a saner fashion.
Finally, quota inodes now live in the metadata directory tree, which is a pretty simple conversion. However, we added yet another new feature, which is that xfs will now remember the quota accounting and enforcement state across unmounts. You can still tweak them via mount options, but not specifying any is no longer interpreted the same as 'noquota'.
I'm only sending the kernel patches to the list for now, but please have a look at the git tree links for xfsprogs and fstests changes.
https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux.git/log/?h=metadir-quotas_2024-08-22
https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=metadir-quotas_2024-08-22
https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfstests-dev.git/log/?h=metadir-quotas_2024-08-22
--D
PATCH 1/10 xfs: various bug fixes for 6.11
Hi all,
Various bug fixes for 6.11.
If you're going to start using this code, I strongly recommend pulling from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy! Comments and questions are, as always, welcome.
--D
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=xfs-6.11-fixes
---
Commits in this patchset:
* xfs: fix di_onlink checking for V1/V2 inodes
* xfs: fix folio dirtying for XFILE_ALLOC callers
* xfs: xfs_finobt_count_blocks() walks the wrong btree
* xfs: don't bother reporting blocks trimmed via FITRIM
* xfs: Fix the owner setting issue for rmap query in xfs fsmap
* xfs: use XFS_BUF_DADDR_NULL for daddrs in getfsmap code
* xfs: Fix missing interval for missing_owner in xfs fsmap
* xfs: take m_growlock when running growfsrt
* xfs: reset rootdir extent size hint after growfsrt
--- fs/xfs/libxfs/xfs_ialloc_btree.c | 2 - fs/xfs/libxfs/xfs_inode_buf.c | 14 +++++-- fs/xfs/scrub/xfile.c | 2 - fs/xfs/xfs_discard.c | 36 +++++------------- fs/xfs/xfs_fsmap.c | 30 +++++++++++++-- fs/xfs/xfs_rtalloc.c | 78 ++++++++++++++++++++++++++++++++------ 6 files changed, 114 insertions(+), 48 deletions(-)
PATCH 1/9 xfs: fix di_onlink checking for V1/V2 inodes
From: Darrick J. Wong <[email protected]>
"KjellR" complained on IRC that an old V4 filesystem suddenly stopped mounting after upgrading from 6.9.11 to 6.10.3, with the following splat when trying to read the rt bitmap inode:
00000000: 49 4e 80 00 01 02 00 01 00 00 00 00 00 00 00 00 IN.............. 00000010: 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000020: 00 00 00 00 00 00 00 00 43 d2 a9 da 21 0f d6 30 ........C...!..0 00000030: 43 d2 a9 da 21 0f d6 30 00 00 00 00 00 00 00 00 C...!..0........ 00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000050: 00 00 00 02 00 00 00 00 00 00 00 04 00 00 00 00 ................ 00000060: ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
As Dave Chinner points out, this is a V1 inode with both di_onlink and di_nlink set to 1 and di_flushiter == 0. In other words, this inode was formatted this way by mkfs and hasn't been touched since then.
Back in the old days of xfsprogs 3.2.3, I observed that libxfs_ialloc would set di_nlink, but if the filesystem didn't have NLINK, it would then set di_version = 1. libxfs_iflush_int later sees the V1 inode and copies the value of di_nlink to di_onlink without zeroing di_onlink.
Eventually this filesystem must have been upgraded to support NLINK because 6.10 doesn't support !NLINK filesystems, which is how we tripped over this old behavior. The filesystem doesn't have a realtime section, so that's why the rtbitmap inode has never been touched.
Fix this by removing the di_onlink/di_nlink checking for all V1/V2 inodes because this is a muddy mess. The V3 inode handling code has always supported NLINK and written di_onlink==0 so keep that check. The removal of the V1 inode handling code when we dropped support for !NLINK obscured this old behavior.
--- fs/xfs/libxfs/xfs_inode_buf.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-)
fs/xfs/libxfs/xfs_inode_buf.c +10 -4
@@ -514,12 +514,18 @@ xfs_dinode_verify(return __this_address; } -if (dip->di_version >1 ) { +/* +* Historical note: xfsprogs in the 3.2 era set up its incore inodes to +* have di_nlink track the link count, even if the actual filesystem +* only supported V1 inodes (i.e. di_onlink). When writing out the +* ondisk inode, it would set both the ondisk di_nlink and di_onlink to +* the the incore di_nlink value, which is why we cannot check for +* di_nlink==0 on a V1 inode. V2/3 inodes would get written out with +* di_onlink==0, so we can check that. +*/ +if (dip->di_version >=2 ) {if (dip->di_onlink)return __this_address; - }else { -if (dip->di_nlink) -return __this_address; }/* don't allow invalid i_size */
PATCH 2/9 xfs: fix folio dirtying for XFILE_ALLOC callers
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
willy pointed out that folio_mark_dirty is the correct function to use to mark an xfile folio dirty because it calls out to the mapping's aops to mark it dirty. For tmpfs this likely doesn't matter much since it currently uses nop_dirty_folio, but let's use the abstractions properly.
--- fs/xfs/scrub/xfile.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/scrub/xfile.c +1 -1
@@ -293,7 +293,7 @@ xfile_get_folio(* (potentially last) reference in xfile_put_folio. */ if (flags &XFILE_ALLOC ) -folio_set_dirty (folio); +folio_mark_dirty (folio);return folio; }
PATCH 3/9 xfs: xfs_finobt_count_blocks() walks the wrong btree
In reply to Darrick J. WongFrom: Dave Chinner <[email protected]>
As a result of the factoring in commit 14dd46cf31f4 ("xfs: split xfs_inobt_init_cursor"), mount started taking a long time on a user's filesystem. For Anders, this made mount times regress from under a second to over 15 minutes for a filesystem with only 30 million inodes in it.
Anders bisected it down to the above commit, but even then the bug was not obvious. In this commit, over 20 calls to xfs_inobt_init_cursor() were modified, and some we modified to call a new function named xfs_finobt_init_cursor().
If that takes you a moment to reread those function names to see what the rename was, then you have realised why this bug wasn't spotted during review. And it wasn't spotted on inspection even after the bisect pointed at this commit - a single missing "f" isn't the easiest thing for a human eye to notice....
The result is that xfs_finobt_count_blocks() now incorrectly calls xfs_inobt_init_cursor() so it is now walking the inobt instead of the finobt. Hence when there are lots of allocated inodes in a filesystem, mount takes a -long- time run because it now walks a massive allocated inode btrees instead of the small, nearly empty free inode btrees. It also means all the finobt space reservations are wrong, so mount could potentially given ENOSPC on kernel upgrade.
In hindsight, commit 14dd46cf31f4 should have been two commits - the first to convert the finobt callers to the new API, the second to modify the xfs_inobt_init_cursor() API for the inobt callers. That would have made the bug very obvious during review.
--- fs/xfs/libxfs/xfs_ialloc_btree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/libxfs/xfs_ialloc_btree.c +1 -1
@@ -749,7 +749,7 @@ xfs_finobt_count_blocks(if (error)return error; - cur =xfs_inobt_init_cursor (pag, tp, agbp); + cur =xfs_finobt_init_cursor (pag, tp, agbp); error =xfs_btree_count_blocks (cur, tree_blocks);xfs_btree_del_cursor (cur, error);xfs_trans_brelse (tp, agbp);
PATCH 3/9 xfs: xfs_finobt_count_blocks() walks the wrong btree
Dave forgot to mark the original patch for stable, so after consulting with Dave, here it comes
@Greg: you might want to add the patch to all versions that received 14dd46cf31f4 ("xfs: split xfs_inobt_init_cursor") (which I think is v6.9 and v6.10)
/Anders
-------- Forwarded Message --------
Subject: [PATCH 3/9] xfs: xfs_finobt_count_blocks() walks the wrong btree
Date: Thu, 22 Aug 2024 16:59:33 -0700
From: Darrick J. Wong <[email protected]>
To: [email protected]
CC: Anders Blomdell <[email protected]>, Dave Chinner <[email protected]>, Christoph Hellwig <[email protected]>, [email protected], [email protected]
From: Dave Chinner <[email protected]>
As a result of the factoring in commit 14dd46cf31f4 ("xfs: split xfs_inobt_init_cursor"), mount started taking a long time on a user's filesystem. For Anders, this made mount times regress from under a second to over 15 minutes for a filesystem with only 30 million inodes in it.
Anders bisected it down to the above commit, but even then the bug was not obvious. In this commit, over 20 calls to xfs_inobt_init_cursor() were modified, and some we modified to call a new function named xfs_finobt_init_cursor().
If that takes you a moment to reread those function names to see what the rename was, then you have realised why this bug wasn't spotted during review. And it wasn't spotted on inspection even after the bisect pointed at this commit - a single missing "f" isn't the easiest thing for a human eye to notice....
The result is that xfs_finobt_count_blocks() now incorrectly calls xfs_inobt_init_cursor() so it is now walking the inobt instead of the finobt. Hence when there are lots of allocated inodes in a filesystem, mount takes a -long- time run because it now walks a massive allocated inode btrees instead of the small, nearly empty free inode btrees. It also means all the finobt space reservations are wrong, so mount could potentially given ENOSPC on kernel upgrade.
In hindsight, commit 14dd46cf31f4 should have been two commits - the first to convert the finobt callers to the new API, the second to modify the xfs_inobt_init_cursor() API for the inobt callers. That would have made the bug very obvious during review.
--- fs/xfs/libxfs/xfs_ialloc_btree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/libxfs/xfs_ialloc_btree.c +1 -1
@@ -749,7 +749,7 @@ xfs_finobt_count_blocks(if (error)return error; - cur =xfs_inobt_init_cursor (pag, tp, agbp); + cur =xfs_finobt_init_cursor (pag, tp, agbp); error =xfs_btree_count_blocks (cur, tree_blocks);xfs_btree_del_cursor (cur, error);xfs_trans_brelse (tp, agbp);
Greg KH
maintainerRe: Fwd: [PATCH 3/9] xfs: xfs_finobt_count_blocks() walks the wrong btree
Quoted 4 lines · Anders Blomdell
Dave forgot to mark the original patch for stable, so after consulting with Dave, here it comes
@Greg: you might want to add the patch to all versions that received
14dd46cf31f4("xfs: splitxfs_inobt_init_cursor") (which I think is v6.9 and v6.10)
What is the git commit id of this in Linus's tree?
thanks,
greg k-h
Re: Fwd: [PATCH 3/9] xfs: xfs_finobt_count_blocks() walks the wrong btree
It seems like it has not reached Linus's tree yet, but is still pending in 'https://git.kernel.org/pub/scm/fs/xfs/xfs-linux.git/' in the branches {xfs-6.11-fixes, for-next}, where it has commit id 95179935bead.
I'll come back to you when it has reached Linus's tree.
Sorry for making premature noise.
/Anders
Quoted 11 lines · Greg KH
Quoted 4 lines · Anders Blomdell
Dave forgot to mark the original patch for stable, so after consulting with Dave, here it comes
@Greg: you might want to add the patch to all versions that received
14dd46cf31f4("xfs: splitxfs_inobt_init_cursor") (which I think is v6.9 and v6.10)What is the git commit id of this in Linus's tree?
thanks,
greg k-h
Re: Fwd: [PATCH 3/9] xfs: xfs_finobt_count_blocks() walks the wrong btree
Quoted 5 lines · Anders Blomdell
It seems like it has not reached Linus's tree yet, but is still pending in 'https://git.kernel.org/pub/scm/fs/xfs/xfs-linux.git/' in the branches {xfs-6.11-fixes, for-next}, where it has commit id
95179935bead.I'll come back to you when it has reached Linus's tree.
Quoted 15 lines
Sorry for making premature noise.
/Anders
Quoted 7 lines · Greg KH
Quoted 4 lines · Anders Blomdell
Dave forgot to mark the original patch for stable, so after consulting with Dave, here it comes
@Greg: you might want to add the patch to all versions that received 14dd46cf31f4 ("xfs: split
xfs_inobt_init_cursor") (which I think is v6.9 and v6.10)What is the git commit id of this in Linus's tree?
The commit in Linus's tree is 95179935bead
Quoted 4 lines
Quoted 4 lines
thanks,
greg k-h
PATCH 4/9 xfs: don't bother reporting blocks trimmed via FITRIM
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Don't bother reporting the number of bytes that we "trimmed" because the underlying storage isn't required to do anything(!) and failed discard IOs aren't reported to the caller anyway. It's not like userspace can use the reported value for anything useful like adjusting the offset parameter of the next call, and it's not like anyone ever wrote a manpage about FITRIM's out parameters.
--- fs/xfs/xfs_discard.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-)
fs/xfs/xfs_discard.c +11 -25
@@ -158,8 +158,7 @@ static intxfs_trim_gather_extents ( struct xfs_perag *pag, struct xfs_trim_cur *tcur, - struct xfs_busy_extents *extents, - uint64_t *blocks_trimmed) + struct xfs_busy_extents *extents) { struct xfs_mount *mp = pag->pag_mount;struct xfs_trans *tp;
@@ -280,7 +279,6 @@ xfs_trim_gather_extents(xfs_extent_busy_insert_discard (pag, fbno, flen, &extents->extent_list); - *blocks_trimmed += flen; next_extent:if (tcur->by_bno) error =xfs_btree_increment (cur,0 , &i);
@@ -327,8 +325,7 @@ xfs_trim_perag_extents( struct xfs_perag *pag, xfs_agblock_t start, xfs_agblock_t end, - xfs_extlen_t minlen, - uint64_t *blocks_trimmed) + xfs_extlen_t minlen) { struct xfs_trim_cur tcur = { .start = start,
@@ -354,8 +351,7 @@ xfs_trim_perag_extents( extents->owner = extents;INIT_LIST_HEAD (&extents->extent_list); - error =xfs_trim_gather_extents (pag, &tcur, extents, - blocks_trimmed); + error =xfs_trim_gather_extents (pag, &tcur, extents);if (error) {kfree (extents);break ;
@@ -389,8 +385,7 @@ xfs_trim_datadev_extents(struct xfs_mount *mp, xfs_daddr_t start, xfs_daddr_t end, - xfs_extlen_t minlen, - uint64_t *blocks_trimmed) + xfs_extlen_t minlen) { xfs_agnumber_t start_agno, end_agno;xfs_agblock_t start_agbno, end_agbno;
@@ -411,8 +406,7 @@ xfs_trim_datadev_extents(if (start_agno == end_agno) agend = end_agbno; - error =xfs_trim_perag_extents (pag, start_agbno, agend, minlen, - blocks_trimmed); + error =xfs_trim_perag_extents (pag, start_agbno, agend, minlen);if (error) last_error = error;
@@ -431,9 +425,6 @@ struct xfs_trim_rtdev {/* list of rt extents to free */ struct list_head extent_list; -/* pointer to count of blocks trimmed */ -uint64_t *blocks_trimmed; -/* minimum length that caller allows us to trim */ xfs_rtblock_t minlen_fsb;
@@ -551,7 +542,6 @@ xfs_trim_gather_rtextent( busyp->length = rlen;INIT_LIST_HEAD (&busyp->list);list_add_tail (&busyp->list, &tr->extent_list); - *tr->blocks_trimmed += rlen; tr->restart_rtx = rec->ar_startext + rec->ar_extcount;return 0 ;
@@ -562,13 +552,11 @@ xfs_trim_rtdev_extents(struct xfs_mount *mp, xfs_daddr_t start, xfs_daddr_t end, - xfs_daddr_t minlen, - uint64_t *blocks_trimmed) + xfs_daddr_t minlen) { struct xfs_rtalloc_rec low = { };struct xfs_rtalloc_rec high = { };struct xfs_trim_rtdev tr = { - .blocks_trimmed = blocks_trimmed, .minlen_fsb =XFS_BB_TO_FSB (mp, minlen), };struct xfs_trans *tp;
@@ -634,7 +622,7 @@ xfs_trim_rtdev_extents( return error; }#else -# define xfs_trim_rtdev_extents (m,s,e,n,b) (-EOPNOTSUPP) +# define xfs_trim_rtdev_extents (...) (-EOPNOTSUPP)#endif /* CONFIG_XFS_RT */ /*
@@ -661,7 +649,6 @@ xfs_ioc_trim(xfs_daddr_t start, end;xfs_extlen_t minlen;xfs_rfsblock_t max_blocks; -uint64_t blocks_trimmed =0 ;int error, last_error =0 ;if (!capable (CAP_SYS_ADMIN ))
@@ -706,15 +693,13 @@ xfs_ioc_trim( end = start +BTOBBT (range.len) -1 ;if (bdev_max_discard_sectors (mp->m_ddev_targp->bt_bdev)) { - error =xfs_trim_datadev_extents (mp, start, end, minlen, - &blocks_trimmed); + error =xfs_trim_datadev_extents (mp, start, end, minlen);if (error) last_error = error; }if (rt_bdev && !xfs_trim_should_stop ()) { - error =xfs_trim_rtdev_extents (mp, start, end, minlen, - &blocks_trimmed); + error =xfs_trim_rtdev_extents (mp, start, end, minlen);if (error) last_error = error; }
@@ -722,7 +707,8 @@ xfs_ioc_trim(if (last_error)return last_error; - range.len =XFS_FSB_TO_B (mp, blocks_trimmed); + range.len = min_t(unsigned long long, range.len, +XFS_FSB_TO_B (mp, max_blocks));if (copy_to_user (urange, &range,sizeof (range)))return -EFAULT ;return 0 ;
PATCH 5/9 xfs: Fix the owner setting issue for rmap query in xfs fsmap
In reply to Darrick J. WongFrom: Zizhi Wo <[email protected]>
I notice a rmap query bug in xfs_io fsmap:
[root@fedora ~]# xfs_io -c 'fsmap -vvvv' /mnt
EXT: DEV BLOCK-RANGE OWNER FILE-OFFSET AG AG-OFFSET TOTAL 0: 253:16 [0..7]: static fs metadata 0 (0..7) 8 1: 253:16 [8..23]: per-AG metadata 0 (8..23) 16 2: 253:16 [24..39]: inode btree 0 (24..39) 16 3: 253:16 [40..47]: per-AG metadata 0 (40..47) 8 4: 253:16 [48..55]: refcount btree 0 (48..55) 8 5: 253:16 [56..103]: per-AG metadata 0 (56..103) 48 6: 253:16 [104..127]: free space 0 (104..127) 24
......
Bug:
[root@fedora ~]# xfs_io -c 'fsmap -vvvv -d 0 3' /mnt [root@fedora ~]#
Normally, we should be able to get one record, but we got nothing.
The root cause of this problem lies in the incorrect setting of rm_owner in the rmap query. In the case of the initial query where the owner is not set, __xfs_getfsmap_datadev() first sets info->high.rm_owner to ULLONG_MAX. This is done to prevent any omissions when comparing rmap items. However, if the current ag is detected to be the last one, the function sets info's high_irec based on the provided key. If high->rm_owner is not specified, it should continue to be set to ULLONG_MAX; otherwise, there will be issues with interval omissions. For example, consider "start" and "end" within the same block. If high->rm_owner == 0, it will be smaller than the founded record in rmapbt, resulting in a query with no records. The main call stack is as follows:
xfs_ioc_getfsmap
xfs_getfsmap
xfs_getfsmap_datadev_rmapbt
__xfs_getfsmap_datadev
info->high.rm_owner = ULLONG_MAX
if (pag->pag_agno == end_ag)
xfs_fsmap_owner_to_rmap
// set info->high.rm_owner = 0 because fmr_owner == -1ULL
dest->rm_owner = 0
// get nothing
xfs_getfsmap_datadev_rmapbt_queryThe problem can be resolved by simply modify the xfs_fsmap_owner_to_rmap function internal logic to achieve.
After applying this patch, the above problem have been solved:
[root@fedora ~]# xfs_io -c 'fsmap -vvvv -d 0 3' /mnt
EXT: DEV BLOCK-RANGE OWNER FILE-OFFSET AG AG-OFFSET TOTAL 0: 253:16 [0..7]: static fs metadata 0 (0..7) 8
--- fs/xfs/xfs_fsmap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_fsmap.c +1 -1
@@ -71,7 +71,7 @@ xfs_fsmap_owner_to_rmap(switch (src->fmr_owner) {case 0 :/* "lowest owner id possible" */ case -1ULL :/* "highest owner id possible" */ - dest->rm_owner =0 ; + dest->rm_owner = src->fmr_owner;break ;case XFS_FMR_OWN_FREE : dest->rm_owner =XFS_RMAP_OWN_NULL ;
Re: [PATCH 5/9] xfs: Fix the owner setting issue for rmap query in xfs fsmap
Looks good:
PATCH 6/9 xfs: use XFS_BUF_DADDR_NULL for daddrs in getfsmap code
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Use XFS_BUF_DADDR_NULL (instead of a magic sentinel value) to mean "this field is null" like the rest of xfs.
--- fs/xfs/xfs_fsmap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
fs/xfs/xfs_fsmap.c +2 -2
@@ -252,7 +252,7 @@ xfs_getfsmap_rec_before_start( const struct xfs_rmap_irec *rec, xfs_daddr_t rec_daddr) { - if (info->low_daddr != -1ULL) + if (info->low_daddr != XFS_BUF_DADDR_NULL) return rec_daddr < info->low_daddr;if (info->low.rm_blockcount)return xfs_rmap_compare (rec, &info->low) <0 ;
@@ -983,7 +983,7 @@ xfs_getfsmap( info.dev = handlers[i].dev; info.last = false; info.pag =NULL ; - info.low_daddr =-1ULL ; + info.low_daddr =XFS_BUF_DADDR_NULL ; info.low.rm_blockcount =0 ; error = handlers[i].fn (tp, dkeys, &info);if (error)
Re: [PATCH 6/9] xfs: use XFS_BUF_DADDR_NULL for daddrs in getfsmap code
Looks good:
PATCH 7/9 xfs: Fix missing interval for missing_owner in xfs fsmap
In reply to Darrick J. WongFrom: Zizhi Wo <[email protected]>
In the fsmap query of xfs, there is an interval missing problem:
[root@fedora ~]# xfs_io -c 'fsmap -vvvv' /mnt
EXT: DEV BLOCK-RANGE OWNER FILE-OFFSET AG AG-OFFSET TOTAL 0: 253:16 [0..7]: static fs metadata 0 (0..7) 8 1: 253:16 [8..23]: per-AG metadata 0 (8..23) 16 2: 253:16 [24..39]: inode btree 0 (24..39) 16 3: 253:16 [40..47]: per-AG metadata 0 (40..47) 8 4: 253:16 [48..55]: refcount btree 0 (48..55) 8 5: 253:16 [56..103]: per-AG metadata 0 (56..103) 48 6: 253:16 [104..127]: free space 0 (104..127) 24
......
BUG:
[root@fedora ~]# xfs_io -c 'fsmap -vvvv -d 104 107' /mnt [root@fedora ~]#
Normally, we should be able to get [104, 107), but we got nothing.
The problem is caused by shifting. The query for the problem-triggered scenario is for the missing_owner interval (e.g. freespace in rmapbt/ unknown space in bnobt), which is obtained by subtraction (gap). For this scenario, the interval is obtained by info->last. However, rec_daddr is calculated based on the start_block recorded in key[1], which is converted by calling XFS_BB_TO_FSBT. Then if rec_daddr does not exceed info->next_daddr, which means keys[1].fmr_physical >> (mp)->m_blkbb_log <= info->next_daddr, no records will be displayed. In the above example, 104 >> (mp)->m_blkbb_log = 12 and 107 >> (mp)->m_blkbb_log = 12, so the two are reduced to 0 and the gap is ignored:
before calculate ----------------> after shifting
104(st) 107(ed) 12(st/ed)
|---------| | sector size block size
Resolve this issue by introducing the "end_daddr" field in xfs_getfsmap_info. This records |key[1].fmr_physical + key[1].length| at the granularity of sector. If the current query is the last, the rec_daddr is end_daddr to prevent missing interval problems caused by shifting. We only need to focus on the last query, because xfs disks are internally aligned with disk blocksize that are powers of two and minimum 512, so there is no problem with shifting in previous queries.
After applying this patch, the above problem have been solved:
[root@fedora ~]# xfs_io -c 'fsmap -vvvv -d 104 107' /mnt
EXT: DEV BLOCK-RANGE OWNER FILE-OFFSET AG AG-OFFSET TOTAL 0: 253:16 [104..106]: free space 0 (104..106) 3
--- fs/xfs/xfs_fsmap.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-)
fs/xfs/xfs_fsmap.c +23 -1
@@ -162,6 +162,7 @@ struct xfs_getfsmap_info {xfs_daddr_t next_daddr;/* next daddr we expect */ /* daddr of low fsmap key when we're using the rtbitmap */ xfs_daddr_t low_daddr; +xfs_daddr_t end_daddr;/* daddr of high fsmap key */ u64 missing_owner;/* owner of holes */ u32 dev;/* device id */ /*
@@ -182,6 +183,7 @@ struct xfs_getfsmap_dev {int (*fn)(struct xfs_trans *tp,const struct xfs_fsmap *keys,struct xfs_getfsmap_info *info); +sector_t nr_sectors; };/* Compare two getfsmap device handlers. */
@@ -294,6 +296,18 @@ xfs_getfsmap_helper(return 0 ; } +/* +* For an info->last query, we're looking for a gap between the last +* mapping emitted and the high key specified by userspace. If the +* user's query spans less than 1 fsblock, then info->high and +* info->low will have the same rm_startblock, which causes rec_daddr +* and next_daddr to be the same. Therefore, use the end_daddr that +* we calculated from userspace's high key to synthesize the record. +* Note that if the btree query found a mapping, there won't be a gap. +*/ +if (info->last && info->end_daddr !=XFS_BUF_DADDR_NULL ) + rec_daddr = info->end_daddr; +/* Are we just counting mappings? */ if (info->head->fmh_count ==0 ) {if (info->head->fmh_entries ==UINT_MAX )
@@ -904,17 +918,21 @@ xfs_getfsmap(/* Set up our device handlers. */ memset (handlers,0 ,sizeof (handlers)); + handlers[0 ].nr_sectors =XFS_FSB_TO_BB (mp, mp->m_sb.sb_dblocks); handlers[0 ].dev =new_encode_dev (mp->m_ddev_targp->bt_dev);if (use_rmap) handlers[0 ].fn = xfs_getfsmap_datadev_rmapbt;else handlers[0 ].fn = xfs_getfsmap_datadev_bnobt;if (mp->m_logdev_targp != mp->m_ddev_targp) { + handlers[1 ].nr_sectors =XFS_FSB_TO_BB (mp, + mp->m_sb.sb_logblocks); handlers[1 ].dev =new_encode_dev (mp->m_logdev_targp->bt_dev); handlers[1 ].fn = xfs_getfsmap_logdev; }#ifdef CONFIG_XFS_RT if (mp->m_rtdev_targp) { + handlers[2 ].nr_sectors =XFS_FSB_TO_BB (mp, mp->m_sb.sb_rblocks); handlers[2 ].dev =new_encode_dev (mp->m_rtdev_targp->bt_dev); handlers[2 ].fn = xfs_getfsmap_rtdev_rtbitmap; }
@@ -946,6 +964,7 @@ xfs_getfsmap( info.next_daddr = head->fmh_keys[0 ].fmr_physical + head->fmh_keys[0 ].fmr_length; + info.end_daddr =XFS_BUF_DADDR_NULL ; info.fsmap_recs = fsmap_recs; info.head = head;
@@ -966,8 +985,11 @@ xfs_getfsmap(* low key, zero out the low key so that we get * everything from the beginning. */ -if (handlers[i].dev == head->fmh_keys[1 ].fmr_device) +if (handlers[i].dev == head->fmh_keys[1 ].fmr_device) { dkeys[1 ] = head->fmh_keys[1 ]; + info.end_daddr =min (handlers[i].nr_sectors -1 , + dkeys[1 ].fmr_physical); + }if (handlers[i].dev > head->fmh_keys[0 ].fmr_device)memset (&dkeys[0 ],0 ,sizeof (struct xfs_fsmap ));
Re: [PATCH 7/9] xfs: Fix missing interval for missing_owner in xfs fsmap
Hi!
Quoted 128 lines · Darrick J. Wong
From: Zizhi Wo <[email protected]>
In the fsmap query of xfs, there is an interval missing problem:
[root@fedora ~]#xfs_io-c 'fsmap -vvvv' /mntEXT: DEV BLOCK-RANGE OWNER FILE-OFFSET AG AG-OFFSET TOTAL 0: 253:16 [0..7]: static fs metadata 0 (0..7) 8 1: 253:16 [8..23]: per-AG metadata 0 (8..23) 16 2: 253:16 [24..39]: inode btree 0 (24..39) 16 3: 253:16 [40..47]: per-AG metadata 0 (40..47) 8 4: 253:16 [48..55]: refcount btree 0 (48..55) 8 5: 253:16 [56..103]: per-AG metadata 0 (56..103) 48 6: 253:16 [104..127]: free space 0 (104..127) 24 ......BUG:
[root@fedora ~]#xfs_io-c 'fsmap -vvvv -d 104 107' /mnt [root@fedora ~]#
Normally, we should be able to get [104, 107), but we got nothing.The problem is caused by shifting. The query for the problem-triggered scenario is for the
missing_ownerinterval (e.g. freespace in rmapbt/ unknown space in bnobt), which is obtained by subtraction (gap). For this scenario, the interval is obtained byinfo->last. However,rec_daddris calculated based on thestart_blockrecorded in key[1], which is converted by callingXFS_BB_TO_FSBT. Then ifrec_daddrdoes not exceedinfo->next_daddr, which means keys[1].fmr_physical >> (mp)->m_blkbb_log <=info->next_daddr, no records will be displayed. In the above example, 104 >> (mp)->m_blkbb_log = 12 and 107 >> (mp)->m_blkbb_log = 12, so the two are reduced to 0 and the gap is ignored:before calculate ----------------> after shifting
104(st) 107(ed) 12(st/ed)|---------| | sector size block sizeResolve this issue by introducing the "
end_daddr" field inxfs_getfsmap_info. This records |key[1].fmr_physical + key[1].length| at the granularity of sector. If the current query is the last, therec_daddrisend_daddrto prevent missing interval problems caused by shifting. We only need to focus on the last query, because xfs disks are internally aligned with disk blocksize that are powers of two and minimum 512, so there is no problem with shifting in previous queries.After applying this patch, the above problem have been solved:
[root@fedora ~]#xfs_io-c 'fsmap -vvvv -d 104 107' /mntEXT: DEV BLOCK-RANGE OWNER FILE-OFFSET AG AG-OFFSET TOTAL 0: 253:16 [104..106]: free space 0 (104..106) 3Fixes e89c041338ed ("xfs: implement the GETFSMAP ioctl")Signed-off-by Zizhi Wo <[email protected]>Reviewed-by Darrick J. Wong <[email protected]>[djwong: limit the range of end_addr correctly]Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/xfs_fsmap.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-)fs/xfs/xfs_fsmap.c +19
-0@@ -162,6 +162,7 @@ struct xfs_getfsmap_info {xfs_daddr_t next_daddr;/* next daddr we expect */ /* daddr of low fsmap key when we're using the rtbitmap */ xfs_daddr_t low_daddr; +xfs_daddr_t end_daddr;/* daddr of high fsmap key */ u64 missing_owner;/* owner of holes */ u32 dev;/* device id */ /* @@ -182,6 +183,7 @@ struct xfs_getfsmap_dev {int (*fn)(struct xfs_trans *tp,const struct xfs_fsmap *keys,struct xfs_getfsmap_info *info); +sector_t nr_sectors; };/* Compare two getfsmap device handlers. */ @@ -294,6 +296,18 @@ xfs_getfsmap_helper(return 0 ; } +/* +* For an info->last query, we're looking for a gap between the last +* mapping emitted and the high key specified by userspace. If the +* user's query spans less than 1 fsblock, then info->high and +* info->low will have the same rm_startblock, which causes rec_daddr +* and next_daddr to be the same. Therefore, use the end_daddr that +* we calculated from userspace's high key to synthesize the record. +* Note that if the btree query found a mapping, there won't be a gap. +*/ +if (info->last && info->end_daddr !=XFS_BUF_DADDR_NULL ) + rec_daddr = info->end_daddr; +/* Are we just counting mappings? */ if (info->head->fmh_count ==0 ) {if (info->head->fmh_entries ==UINT_MAX )@@ -904,17 +918,21 @@ xfs_getfsmap(/* Set up our device handlers. */ memset (handlers,0 ,sizeof (handlers)); + handlers[0 ].nr_sectors =XFS_FSB_TO_BB (mp, mp->m_sb.sb_dblocks); handlers[0 ].dev =new_encode_dev (mp->m_ddev_targp->bt_dev);if (use_rmap) handlers[0 ].fn = xfs_getfsmap_datadev_rmapbt;else handlers[0 ].fn = xfs_getfsmap_datadev_bnobt;if (mp->m_logdev_targp != mp->m_ddev_targp) { + handlers[1 ].nr_sectors =XFS_FSB_TO_BB (mp, + mp->m_sb.sb_logblocks); handlers[1 ].dev =new_encode_dev (mp->m_logdev_targp->bt_dev); handlers[1 ].fn = xfs_getfsmap_logdev; }#ifdef CONFIG_XFS_RT if (mp->m_rtdev_targp) { + handlers[2 ].nr_sectors =XFS_FSB_TO_BB (mp, mp->m_sb.sb_rblocks); handlers[2 ].dev =new_encode_dev (mp->m_rtdev_targp->bt_dev); handlers[2 ].fn = xfs_getfsmap_rtdev_rtbitmap; }@@ -946,6 +964,7 @@ xfs_getfsmap( info.next_daddr = head->fmh_keys[0 ].fmr_physical + head->fmh_keys[0 ].fmr_length; + info.end_daddr =XFS_BUF_DADDR_NULL ; info.fsmap_recs = fsmap_recs; info.head = head;
fs/xfs/xfs_fsmap.c xfs_getfsmap( · line 992
Quoted 9 lines
fs/xfs/xfs_fsmap.c +4
-1@@ -966,8 +985,11 @@ xfs_getfsmap(* low key, zero out the low key so that we get * everything from the beginning. */ -if (handlers[i].dev == head->fmh_keys[1 ].fmr_device) +if (handlers[i].dev == head->fmh_keys[1 ].fmr_device) { dkeys[1 ] = head->fmh_keys[1 ]; + info.end_daddr =min (handlers[i].nr_sectors -1 , + dkeys[1 ].fmr_physical); + }
In this case, we shouldn't subtract 1 from handlers[i].nr_sectors, otherwise we lose 1 sector, and after we've shifted it, we lose 1 block (8 sectors) . This boundary bug is similar to the latest patch set I sent[1].
Quoted 4 lines
fs/xfs/xfs_fsmap.c +0
-0if (handlers[i].dev > head->fmh_keys[0 ].fmr_device)memset (&dkeys[0 ],0 ,sizeof (struct xfs_fsmap ));
PATCH 8/9 xfs: take m_growlock when running growfsrt
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Take the grow lock when we're expanding the realtime volume, like we do for the other growfs calls.
--- fs/xfs/xfs_rtalloc.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-)
fs/xfs/xfs_rtalloc.c +25 -13
@@ -821,34 +821,39 @@ xfs_growfs_rt(/* Needs to have been mounted with an rt device. */ if (!XFS_IS_REALTIME_MOUNT (mp))return -EINVAL ; + +if (!mutex_trylock (&mp->m_growlock)) +return -EWOULDBLOCK ;/* * Mount should fail if the rt bitmap/summary files don't load, but * we'll check anyway. */ + error = -EINVAL ;if (!mp->m_rbmip || !mp->m_rsumip) -return -EINVAL ; + goto out_unlock;/* Shrink not supported. */ if (in->newblocks <= sbp->sb_rblocks) -return -EINVAL ; + goto out_unlock;/* Can only change rt extent size when adding rt volume. */ if (sbp->sb_rblocks >0 && in->extsize != sbp->sb_rextsize) -return -EINVAL ; + goto out_unlock;/* Range check the extent size. */ if (XFS_FSB_TO_B (mp, in->extsize) >XFS_MAX_RTEXTSIZE ||XFS_FSB_TO_B (mp, in->extsize) <XFS_MIN_RTEXTSIZE ) -return -EINVAL ; + goto out_unlock;/* Unsupported realtime features. */ + error = -EOPNOTSUPP ;if (xfs_has_rmapbt (mp) ||xfs_has_reflink (mp) ||xfs_has_quota (mp)) -return -EOPNOTSUPP ; + goto out_unlock; nrblocks = in->newblocks; error =xfs_sb_validate_fsb_count (sbp, nrblocks);if (error) -return error; + goto out_unlock;/* * Read in the last block of the device, make sure it exists. */
@@ -856,7 +861,7 @@ xfs_growfs_rt(XFS_FSB_TO_BB (mp, nrblocks -1 ), XFS_FSB_TO_BB(mp, 1), 0, &bp, NULL);if (error) -return error; + goto out_unlock;xfs_buf_relse (bp);/*
@@ -864,8 +869,10 @@ xfs_growfs_rt(*/ nrextents = nrblocks;do_div (nrextents, in->extsize); -if (!xfs_validate_rtextents (nrextents)) -return -EINVAL ; +if (!xfs_validate_rtextents (nrextents)) { + error = -EINVAL ; + goto out_unlock; + } nrbmblocks =xfs_rtbitmap_blockcount (mp, nrextents); nrextslog =xfs_compute_rextslog (nrextents); nrsumlevels = nrextslog +1 ;
@@ -876,8 +883,11 @@ xfs_growfs_rt(* the log. This prevents us from getting a log overflow, * since we'll log basically the whole summary file at once. */ -if (nrsumblocks > (mp->m_sb.sb_logblocks >>1 )) -return -EINVAL ; +if (nrsumblocks > (mp->m_sb.sb_logblocks >>1 )) { + error = -EINVAL ; + goto out_unlock; + } +/* * Get the old block counts for bitmap and summary inodes. * These can't change since other growfs callers are locked out.
@@ -889,10 +899,10 @@ xfs_growfs_rt(*/ error =xfs_growfs_rt_alloc (mp, rbmblocks, nrbmblocks, mp->m_rbmip);if (error) -return error; + goto out_unlock; error =xfs_growfs_rt_alloc (mp, rsumblocks, nrsumblocks, mp->m_rsumip);if (error) -return error; + goto out_unlock; rsum_cache = mp->m_rsum_cache;if (nrbmblocks != sbp->sb_rbmblocks)
@@ -1059,6 +1069,8 @@ xfs_growfs_rt( } } +out_unlock: +mutex_unlock (&mp->m_growlock);return error; }
Re: [PATCH 8/9] xfs: take m_growlock when running growfsrt
Quoted 4 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Take the grow lock when we're expanding the realtime volume, like we do for the other growfs calls.
Looks good:
PATCH 9/9 xfs: reset rootdir extent size hint after growfsrt
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
If growfsrt is run on a filesystem that doesn't have a rt volume, it's possible to change the rt extent size. If the root directory was previously set up with an inherited extent size hint and rtinherit, it's possible that the hint is no longer a multiple of the rt extent size. Although the verifiers don't complain about this, xfs_repair will, so if we detect this situation, log the root directory to clean it up. This is still racy, but it's better than nothing.
--- fs/xfs/xfs_rtalloc.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+)
fs/xfs/xfs_rtalloc.c +40 -0
@@ -784,6 +784,39 @@ xfs_alloc_rsum_cache(xfs_warn (mp,"could not allocate realtime summary cache" ); } +/* +* If we changed the rt extent size (meaning there was no rt volume previously) +* and the root directory had EXTSZINHERIT and RTINHERIT set, it's possible +* that the extent size hint on the root directory is no longer congruent with +* the new rt extent size. Log the rootdir inode to fix this. +*/ +static int +xfs_growfs_rt_fixup_extsize ( +struct xfs_mount *mp) +{ +struct xfs_inode *ip = mp->m_rootip; +struct xfs_trans *tp; +int error =0 ; + +xfs_ilock (ip,XFS_IOLOCK_EXCL ); +if (!(ip->i_diflags &XFS_DIFLAG_RTINHERIT ) || + !(ip->i_diflags &XFS_DIFLAG_EXTSZINHERIT )) + goto out_iolock; + + error =xfs_trans_alloc_inode (ip, &M_RES (mp)->tr_ichange,0 ,0 , false, + &tp); +if (error) + goto out_iolock; + +xfs_trans_log_inode (tp, ip,XFS_ILOG_CORE ); + error =xfs_trans_commit (tp); +xfs_iunlock (ip,XFS_ILOCK_EXCL ); + +out_iolock: +xfs_iunlock (ip,XFS_IOLOCK_EXCL ); +return error; +} +/* * Visible (exported) functions. */
@@ -812,6 +845,7 @@ xfs_growfs_rt(xfs_extlen_t rsumblocks;/* current number of rt summary blks */ xfs_sb_t *sbp;/* old superblock */ uint8_t *rsum_cache;/* old summary cache */ +xfs_agblock_t old_rextsize = mp->m_sb.sb_rextsize; sbp = &mp->m_sb;
@@ -1046,6 +1080,12 @@ xfs_growfs_rt(if (error) goto out_free; +if (old_rextsize != in->extsize) { + error =xfs_growfs_rt_fixup_extsize (mp); +if (error) + goto out_free; + } +/* Update secondary superblocks now the physical grow has completed */ error =xfs_update_secondary_sbs (mp);
Re: [PATCH 9/9] xfs: reset rootdir extent size hint after growfsrt
Looks good:
Re: [PATCHSET v4.0 01/10] xfs: various bug fixes for 6.11
In reply to Darrick J. WongQuoted 3 lines · Darrick J. Wong
Hi all,
Various bug fixes for 6.11.
FYI, patches 5+ seem to be pretty long and not critical issue. I'd probably defer them to 6.12 with the rest of the patchbomb.
PATCH 2/10 xfs: atomic file content commits
In reply to Darrick J. WongHi all,
This series creates XFS_IOC_START_COMMIT and XFS_IOC_COMMIT_RANGE ioctls to perform the exchange only if the target file has not been changed since a given sampling point.
This new functionality uses the mechanism underlying EXCHANGE_RANGE to stage and commit file updates such that reader programs will see either the old contents or the new contents in their entirety, with no chance of torn writes. A successful call completion guarantees that the new contents will be seen even if the system fails. The pair of ioctls allows userspace to perform what amounts to a compare and exchange operation on entire file contents.
Note that there are ongoing arguments in the community about how best to implement some sort of file data write counter that nfsd could also use to signal invalidations to clients. Until such a thing is implemented, this patch will rely on ctime/mtime updates.
Here are the proposed manual pages:
IOCTL-XFS-COMMIT-RANGE(2) System Calls ManualIOCTL-XFS-COMMIT-RANGE(2)
NAME
ioctl_xfs_start_commit - prepare to exchange the contents of two files ioctl_xfs_commit_range - conditionally exchange the contents of parts of two files
SYNOPSIS
#include <sys/ioctl.h>
#include <xfs/xfs_fs.h>
int ioctl(int file2_fd, XFS_IOC_START_COMMIT, struct xfs_com‐
mit_range *arg);
int ioctl(int file2_fd, XFS_IOC_COMMIT_RANGE, struct xfs_com‐
mit_range *arg);DESCRIPTION
Given a range of bytes in a first file file1_fd and a second range of bytes in a second file file2_fd, this ioctl(2) ex‐ changes the contents of the two ranges if file2_fd passes cer‐ tain freshness criteria.
Before exchanging the contents, the program must call the
XFS_IOC_START_COMMIT ioctl to sample freshness data for
file2_fd. If the sampled metadata does not match the file metadata at commit time, XFS_IOC_COMMIT_RANGE will return EBUSY.
Exchanges are atomic with regards to concurrent file opera‐
tions. Implementations must guarantee that readers see either
the old contents or the new contents in their entirety, even if the system fails.
The system call parameters are conveyed in structures of the following form:
struct xfs_commit_range {
__s32 file1_fd;
__u32 pad;
__u64 file1_offset;
__u64 file2_offset;
__u64 length;
__u64 flags;
__u64 file2_freshness[5];
};
The field pad must be zero.
The fields file1_fd, file1_offset, and length define the first range of bytes to be exchanged.
The fields file2_fd, file2_offset, and length define the second range of bytes to be exchanged.
The field file2_freshness is an opaque field whose contents are determined by the kernel. These file attributes are used to confirm that file2_fd has not changed by another thread since the current thread began staging its own update.
Both files must be from the same filesystem mount. If the two file descriptors represent the same file, the byte ranges must
not overlap. Most disk-based filesystems require that the
starts of both ranges must be aligned to the file block size. If this is the case, the ends of the ranges must also be so aligned unless the XFS_EXCHANGE_RANGE_TO_EOF flag is set.
The field flags control the behavior of the exchange operation.
XFS_EXCHANGE_RANGE_TO_EOF
Ignore the length parameter. All bytes in file1_fd from file1_offset to EOF are moved to file2_fd, and file2's size is set to (file2_offset+(file1_length-
file1_offset)). Meanwhile, all bytes in file2 from
file2_offset to EOF are moved to file1 and file1's
size is set to (file1_offset+(file2_length-
file2_offset)).
XFS_EXCHANGE_RANGE_DSYNC
Ensure that all modified in-core data in both file ranges and all metadata updates pertaining to the exchange operation are flushed to persistent storage before the call returns. Opening either file de‐ scriptor with O_SYNC or O_DSYNC will have the same effect.
XFS_EXCHANGE_RANGE_FILE1_WRITTEN
Only exchange sub-ranges of file1_fd that are known to contain data written by application software. Each sub-range may be expanded (both upwards and downwards) to align with the file allocation unit. For files on the data device, this is one filesystem block. For files on the realtime device, this is the realtime extent size. This facility can be used to implement fast atomic scatter-gather writes of any complexity for software-defined storage targets if all writes are aligned to the file allocation unit.
XFS_EXCHANGE_RANGE_DRY_RUN
Check the parameters and the feasibility of the op‐ eration, but do not change anything.
RETURN VALUE
On error, -1 is returned, and errno is set to indicate the er‐ ror.
ERRORS
Error codes can be one of, but are not limited to, the follow‐ ing:
EBADF file1_fd is not open for reading and writing or is open
for append-only writes; or file2_fd is not open for reading and writing or is open for append-only writes.
EBUSY The file2 inode number and timestamps supplied do not
match file2_fd.
EINVAL The parameters are not correct for these files. This
error can also appear if either file descriptor repre‐ sents a device, FIFO, or socket. Disk filesystems gen‐ erally require the offset and length arguments to be aligned to the fundamental block sizes of both files.
EIO An I/O error occurred.
EISDIR One of the files is a directory.
ENOMEM The kernel was unable to allocate sufficient memory to
perform the operation.
ENOSPC There is not enough free space in the filesystem ex‐
change the contents safely.
EOPNOTSUPP
The filesystem does not support exchanging bytes between the two files.
EPERM file1_fd or file2_fd are immutable.
ETXTBSY
One of the files is a swap file.
EUCLEAN
The filesystem is corrupt.
EXDEV file1_fd and file2_fd are not on the same mounted
filesystem.
CONFORMING TO
This API is XFS-specific.
USE CASES
Several use cases are imagined for this system call. Coordina‐
tion between multiple threads is performed by the kernel.
The first is a filesystem defragmenter, which copies the con‐
tents of a file into another file and wishes to exchange the
space mappings of the two files, provided that the original
file has not changed.
An example program might look like this:
int fd = open("/some/file", O_RDWR);
int temp_fd = open("/some", O_TMPFILE | O_RDWR);
struct stat sb;
struct xfs_commit_range args = {
.flags = XFS_EXCHANGE_RANGE_TO_EOF,
};
/* gather file2's freshness information */
ioctl(fd, XFS_IOC_START_COMMIT, &args);
fstat(fd, &sb);
/* make a fresh copy of the file with terrible alignment to avoid reflink */
clone_file_range(fd, NULL, temp_fd, NULL, 1, 0);
clone_file_range(fd, NULL, temp_fd, NULL, sb.st_size - 1, 0);
/* commit the entire update */
args.file1_fd = temp_fd;
ret = ioctl(fd, XFS_IOC_COMMIT_RANGE, &args);
if (ret && errno == EBUSY)
printf("file changed while defrag was underway");
The second is a data storage program that wants to commit non-
contiguous updates to a file atomically. This program cannot
coordinate updates to the file and therefore relies on the ker‐
nel to reject the COMMIT_RANGE command if the file has been up‐
dated by someone else. This can be done by creating a tempo‐
rary file, calling FICLONE(2) to share the contents, and stag‐
ing the updates into the temporary file. The FULL_FILES flag
is recommended for this purpose. The temporary file can be
deleted or punched out afterwards.
An example program might look like this:
int fd = open("/some/file", O_RDWR);
int temp_fd = open("/some", O_TMPFILE | O_RDWR);
struct xfs_commit_range args = {
.flags = XFS_EXCHANGE_RANGE_TO_EOF,
};
/* gather file2's freshness information */
ioctl(fd, XFS_IOC_START_COMMIT, &args);
ioctl(temp_fd, FICLONE, fd);
/* append 1MB of records */
lseek(temp_fd, 0, SEEK_END);
write(temp_fd, data1, 1000000);
/* update record index */
pwrite(temp_fd, data1, 600, 98765);
pwrite(temp_fd, data2, 320, 54321);
pwrite(temp_fd, data2, 15, 0);
/* commit the entire update */
args.file1_fd = temp_fd;
ret = ioctl(fd, XFS_IOC_COMMIT_RANGE, &args);
if (ret && errno == EBUSY)
printf("file changed before commit; will roll back");
NOTES
Some filesystems may limit the amount of data or the number of extents that can be exchanged in a single call.
SEE ALSO
ioctl(2)
XFS 2024-02-18 IOCTL-XFS-COMMIT-RANGE(2)
If you're going to start using this code, I strongly recommend pulling from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy! Comments and questions are, as always, welcome.
--D
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=atomic-file-commits
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=atomic-file-commits
fstests git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfstests-dev.git/log/?h=atomic-file-commits
---
Commits in this patchset:
* xfs: introduce new file range commit ioctls
--- fs/xfs/libxfs/xfs_fs.h | 26 +++++++++ fs/xfs/xfs_exchrange.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_exchrange.h | 16 +++++ fs/xfs/xfs_ioctl.c | 4 + fs/xfs/xfs_trace.h | 57 +++++++++++++++++++ 5 files changed, 243 insertions(+), 3 deletions(-)
PATCH 1/1 xfs: introduce new file range commit ioctls
From: Darrick J. Wong <[email protected]>
This patch introduces two more new ioctls to manage atomic updates to file contents -- XFS_IOC_START_COMMIT and XFS_IOC_COMMIT_RANGE. The commit mechanism here is exactly the same as what XFS_IOC_EXCHANGE_RANGE does, but with the additional requirement that file2 cannot have changed since some sampling point. The start-commit ioctl performs the sampling of file attributes.
--- fs/xfs/libxfs/xfs_fs.h | 26 +++++++++ fs/xfs/xfs_exchrange.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_exchrange.h | 16 +++++ fs/xfs/xfs_ioctl.c | 4 + fs/xfs/xfs_trace.h | 57 +++++++++++++++++++ 5 files changed, 243 insertions(+), 3 deletions(-)
fs/xfs/libxfs/xfs_fs.h +26 -0
@@ -825,6 +825,30 @@ struct xfs_exchange_range {__u64 flags;/* see XFS_EXCHANGE_RANGE_* below */ }; +/* +* Using the same definition of file2 as struct xfs_exchange_range, commit the +* contents of file1 into file2 if file2 has the same inode number, mtime, and +* ctime as the arguments provided to the call. The old contents of file2 will +* be moved to file1. +* +* Returns -EBUSY if there isn't an exact match for the file2 fields. +* +* Filesystems must be able to restart and complete the operation even after +* the system goes down. +*/ +struct xfs_commit_range { +__s32 file1_fd; +__u32 pad;/* must be zeroes */ +__u64 file1_offset;/* file1 offset, bytes */ +__u64 file2_offset;/* file2 offset, bytes */ +__u64 length;/* bytes to exchange */ + +__u64 flags;/* see XFS_EXCHANGE_RANGE_* below */ + +/* opaque file2 metadata for freshness checks */ +__u64 file2_freshness[6 ]; +}; +/* * Exchange file data all the way to the ends of both files, and then exchange * the file sizes. This flag can be used to replace a file's contents with a
@@ -997,6 +1021,8 @@ struct xfs_getparents_by_handle {#define XFS_IOC_BULKSTAT _IOR ('X', 127, struct xfs_bulkstat_req)#define XFS_IOC_INUMBERS _IOR ('X', 128, struct xfs_inumbers_req)#define XFS_IOC_EXCHANGE_RANGE _IOW ('X', 129, struct xfs_exchange_range) +#define XFS_IOC_START_COMMIT _IOR ('X', 130, struct xfs_commit_range) +#define XFS_IOC_COMMIT_RANGE _IOW ('X', 131, struct xfs_commit_range)/* XFS_IOC_GETFSUUID ---------- deprecated 140 */
fs/xfs/xfs_exchrange.c +142 -1
164 lines not on this page · raw message
fs/xfs/xfs_exchrange.h +15 -1
@@ -10,8 +10,12 @@#define __XFS_EXCHANGE_RANGE_UPD_CMTIME1 (1ULL << 63)#define __XFS_EXCHANGE_RANGE_UPD_CMTIME2 (1ULL << 62) +/* Freshness check required */ +#define __XFS_EXCHANGE_RANGE_CHECK_FRESH2 (1ULL << 61) +#define XFS_EXCHANGE_RANGE_PRIV_FLAGS (__XFS_EXCHANGE_RANGE_UPD_CMTIME1 | \ - __XFS_EXCHANGE_RANGE_UPD_CMTIME2) + __XFS_EXCHANGE_RANGE_UPD_CMTIME2 | \ + __XFS_EXCHANGE_RANGE_CHECK_FRESH2)struct xfs_exchrange {struct file *file1;
@@ -22,10 +26,20 @@ struct xfs_exchrange {u64 length;u64 flags;/* XFS_EXCHANGE_RANGE flags */ + +/* file2 metadata for freshness checks */ +u64 file2_ino; +struct timespec64 file2_mtime; +struct timespec64 file2_ctime; +u32 file2_gen; };long xfs_ioc_exchange_range (struct file *file,struct xfs_exchange_range __user *argp); +long xfs_ioc_start_commit (struct file *file, +struct xfs_commit_range __user *argp); +long xfs_ioc_commit_range (struct file *file, +struct xfs_commit_range __user *argp);struct xfs_exchmaps_req ;
fs/xfs/xfs_ioctl.c +4 -0
@@ -1518,6 +1518,10 @@ xfs_file_ioctl(case XFS_IOC_EXCHANGE_RANGE :return xfs_ioc_exchange_range (filp, arg); +case XFS_IOC_START_COMMIT : +return xfs_ioc_start_commit (filp, arg); +case XFS_IOC_COMMIT_RANGE : +return xfs_ioc_commit_range (filp, arg);default :return -ENOTTY ;
fs/xfs/xfs_trace.h +56 -1
69 lines not on this page · raw message
Re: [PATCH 1/1] xfs: introduce new file range commit ioctls
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
This patch introduces two more new ioctls to manage atomic updates to file contents --
XFS_IOC_START_COMMITandXFS_IOC_COMMIT_RANGE. The commit mechanism here is exactly the same as whatXFS_IOC_EXCHANGE_RANGEdoes, but with the additional requirement that file2 cannot have changed since some sampling point. The start-commit ioctl performs the sampling of file attributes.
The code itself looks simply enough now, but how do we guarantee that ctime actually works as a full change count and not just by chance here?
Re: [PATCH 1/1] xfs: introduce new file range commit ioctls
Quoted 10 lines · Christoph Hellwig
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
This patch introduces two more new ioctls to manage atomic updates to file contents --
XFS_IOC_START_COMMITandXFS_IOC_COMMIT_RANGE. The commit mechanism here is exactly the same as whatXFS_IOC_EXCHANGE_RANGEdoes, but with the additional requirement that file2 cannot have changed since some sampling point. The start-commit ioctl performs the sampling of file attributes.
Quoted 4 lines
The code itself looks simply enough now, but how do we guarantee that ctime actually works as a full change count and not just by chance here?
With current mainline kernels it won't, but the updated multigrain timestamp series is in linux-next and is slated to go into v6.12. At that point it should be fine for this purpose.
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH 1/1] xfs: introduce new file range commit ioctls
Quoted 11 lines · Jeff Layton
Quoted 10 lines · Christoph Hellwig
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
This patch introduces two more new ioctls to manage atomic updates to file contents --
XFS_IOC_START_COMMITandXFS_IOC_COMMIT_RANGE. The commit mechanism here is exactly the same as whatXFS_IOC_EXCHANGE_RANGEdoes, but with the additional requirement that file2 cannot have changed since some sampling point. The start-commit ioctl performs the sampling of file attributes.
Quoted 8 lines
Quoted 4 lines
The code itself looks simply enough now, but how do we guarantee that ctime actually works as a full change count and not just by chance here?
With current mainline kernels it won't, but the updated multigrain timestamp series is in linux-next and is slated to go into v6.12. At that point it should be fine for this purpose.
<nod> If these both get merged for 6.12, I think the appropriate port for this patch is to change xfs_ioc_start_commit to do:
struct kstat kstat; fill_mg_cmtime(&kstat, STATX_CTIME | STATX_MTIME, XFS_I(ip2)); kern_f->file2_ctime = kstat.ctime.tv_sec; kern_f->file2_ctime_nsec = kstat.ctime.tv_nsec; kern_f->file2_mtime = kstat.mtime.tv_sec; kern_f->file2_mtime_nsec = kstat.mtime.tv_nsec;
instead of open-coding the inode_get_[cm]time calls. The entire exchangerange feature is still marked experimental, so I didn't think it was worth rebasing my entire dev branch on the multigrain timestamp redux series; we can just fix it later.
--D
Quoted 3 lines
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH 1/1] xfs: introduce new file range commit ioctls
Quoted 30 lines · Darrick J. Wong
Quoted 21 lines · Jeff Layton
Quoted 15 lines · Christoph Hellwig
Quoted 13 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
This patch introduces two more new ioctls to manage atomic updates to
file contents --XFS_IOC_START_COMMITand XFS_IOC_COMMIT_RANGE. The
commit mechanism here is exactly the same as whatXFS_IOC_EXCHANGE_RANGE
does, but with the additional requirement that file2 cannot have changed
since some sampling point. The start-commit ioctl performs the sampling
of file attributes.Quoted 4 lines
The code itself looks simply enough now, but how do we guarantee that ctime actually works as a full change count and not just by chance here?
Quoted 4 lines
With current mainline kernels it won't, but the updated multigrain timestamp series is in linux-next and is slated to go into v6.12. At
that point it should be fine for this purpose.<nod> If these both get merged for 6.12, I think the appropriate port for this patch is to change
xfs_ioc_start_committo do:
Quoted 9 lines
struct kstat kstat; fill_mg_cmtime(&kstat, STATX_CTIME | STATX_MTIME, XFS_I(ip2)); kern_f->file2_ctime = kstat.ctime.tv_sec; kern_f->file2_ctime_nsec = kstat.ctime.tv_nsec; kern_f->file2_mtime = kstat.mtime.tv_sec; kern_f->file2_mtime_nsec = kstat.mtime.tv_nsec;
Yep, that's exactly what you'd want to do.
Quoted 6 lines
instead of open-coding the inode_get_[cm]time calls. The entire exchangerange feature is still marked experimental, so I didn't think it
was worth rebasing my entire dev branch on the multigrain timestamp redux series; we can just fix it later.
Sounds good.
Signature · 1 line
Jeff Layton <[email protected]>
Re: [PATCH 1/1] xfs: introduce new file range commit ioctls
In reply to Darrick J. WongQuoted 11 lines · Darrick J. Wong
<nod> If these both get merged for 6.12, I think the appropriate port for this patch is to change
xfs_ioc_start_committo do:struct kstat kstat; fill_mg_cmtime(&kstat, STATX_CTIME | STATX_MTIME, XFS_I(ip2)); kern_f->file2_ctime = kstat.ctime.tv_sec; kern_f->file2_ctime_nsec = kstat.ctime.tv_nsec; kern_f->file2_mtime = kstat.mtime.tv_sec; kern_f->file2_mtime_nsec = kstat.mtime.tv_nsec;
Quoted 4 lines
instead of open-coding the inode_get_[cm]time calls. The entire exchangerange feature is still marked experimental, so I didn't think it was worth rebasing my entire dev branch on the multigrain timestamp redux series; we can just fix it later.
But the commit log could really note this dependency. This will be especially useful for backports, but also for anyone reading through code history.
Re: [PATCH 1/1] xfs: introduce new file range commit ioctls
Quoted 12 lines · Christoph Hellwig
Quoted 11 lines · Darrick J. Wong
<nod> If these both get merged for 6.12, I think the appropriate port for this patch is to change
xfs_ioc_start_committo do:struct kstat kstat; fill_mg_cmtime(&kstat, STATX_CTIME | STATX_MTIME, XFS_I(ip2)); kern_f->file2_ctime = kstat.ctime.tv_sec; kern_f->file2_ctime_nsec = kstat.ctime.tv_nsec; kern_f->file2_mtime = kstat.mtime.tv_sec; kern_f->file2_mtime_nsec = kstat.mtime.tv_nsec;
Quoted 8 lines
Quoted 4 lines
instead of open-coding the inode_get_[cm]time calls. The entire exchangerange feature is still marked experimental, so I didn't think it was worth rebasing my entire dev branch on the multigrain timestamp redux series; we can just fix it later.
But the commit log could really note this dependency. This will be especially useful for backports, but also for anyone reading through code history.
Ok, how about this for a commit message:
"This patch introduces two more new ioctls to manage atomic updates to file contents -- XFS_IOC_START_COMMIT and XFS_IOC_COMMIT_RANGE. The commit mechanism here is exactly the same as what XFS_IOC_EXCHANGE_RANGE does, but with the additional requirement that file2 cannot have changed since some sampling point. The start-commit ioctl performs the sampling of file attributes.
"Note: This patch currently samples i_ctime during START_COMMIT and checks that it hasn't changed during COMMIT_RANGE. This isn't entirely safe in kernels prior to 6.12 because ctime only had coarse grained granularity and very fast updates could collide with a COMMIT_RANGE. With the multi-granularity ctime introduced in that release by Jeff Layton, it's now possible to update ctime such that this does not happen.
"It is critical, then, that this patch must not be backported to any kernel that does not support fine-grained file change timestamps."
Will that pass muster?
--D
Re: [PATCH 1/1] xfs: introduce new file range commit ioctls
Quoted 4 lines · Darrick J. Wong
"It is critical, then, that this patch must not be backported to any kernel that does not support fine-grained file change timestamps."
Will that pass muster?
I'd drop that last sentence as the previous part should be clear enough.
PATCH 1/1 xfs: introduce new file range commit ioctls
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
This patch introduces two more new ioctls to manage atomic updates to file contents -- XFS_IOC_START_COMMIT and XFS_IOC_COMMIT_RANGE. The commit mechanism here is exactly the same as what XFS_IOC_EXCHANGE_RANGE does, but with the additional requirement that file2 cannot have changed since some sampling point. The start-commit ioctl performs the sampling of file attributes.
Note: This patch currently samples i_ctime during START_COMMIT and checks that it hasn't changed during COMMIT_RANGE. This isn't entirely safe in kernels prior to 6.12 because ctime only had coarse grained granularity and very fast updates could collide with a COMMIT_RANGE. With the multi-granularity ctime introduced by Jeff Layton, it's now possible to update ctime such that this does not happen.
--- fs/xfs/libxfs/xfs_fs.h | 26 +++++++++ fs/xfs/xfs_exchrange.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_exchrange.h | 16 +++++ fs/xfs/xfs_ioctl.c | 4 + fs/xfs/xfs_trace.h | 57 +++++++++++++++++++ 5 files changed, 243 insertions(+), 3 deletions(-)
fs/xfs/libxfs/xfs_fs.h +26 -0
@@ -825,6 +825,30 @@ struct xfs_exchange_range {__u64 flags;/* see XFS_EXCHANGE_RANGE_* below */ }; +/* +* Using the same definition of file2 as struct xfs_exchange_range, commit the +* contents of file1 into file2 if file2 has the same inode number, mtime, and +* ctime as the arguments provided to the call. The old contents of file2 will +* be moved to file1. +* +* Returns -EBUSY if there isn't an exact match for the file2 fields. +* +* Filesystems must be able to restart and complete the operation even after +* the system goes down. +*/ +struct xfs_commit_range { +__s32 file1_fd; +__u32 pad;/* must be zeroes */ +__u64 file1_offset;/* file1 offset, bytes */ +__u64 file2_offset;/* file2 offset, bytes */ +__u64 length;/* bytes to exchange */ + +__u64 flags;/* see XFS_EXCHANGE_RANGE_* below */ + +/* opaque file2 metadata for freshness checks */ +__u64 file2_freshness[6 ]; +}; +/* * Exchange file data all the way to the ends of both files, and then exchange * the file sizes. This flag can be used to replace a file's contents with a
@@ -997,6 +1021,8 @@ struct xfs_getparents_by_handle {#define XFS_IOC_BULKSTAT _IOR ('X', 127, struct xfs_bulkstat_req)#define XFS_IOC_INUMBERS _IOR ('X', 128, struct xfs_inumbers_req)#define XFS_IOC_EXCHANGE_RANGE _IOW ('X', 129, struct xfs_exchange_range) +#define XFS_IOC_START_COMMIT _IOR ('X', 130, struct xfs_commit_range) +#define XFS_IOC_COMMIT_RANGE _IOW ('X', 131, struct xfs_commit_range)/* XFS_IOC_GETFSUUID ---------- deprecated 140 */
fs/xfs/xfs_exchrange.c +142 -1
164 lines not on this page · raw message
fs/xfs/xfs_exchrange.h +15 -1
@@ -10,8 +10,12 @@#define __XFS_EXCHANGE_RANGE_UPD_CMTIME1 (1ULL << 63)#define __XFS_EXCHANGE_RANGE_UPD_CMTIME2 (1ULL << 62) +/* Freshness check required */ +#define __XFS_EXCHANGE_RANGE_CHECK_FRESH2 (1ULL << 61) +#define XFS_EXCHANGE_RANGE_PRIV_FLAGS (__XFS_EXCHANGE_RANGE_UPD_CMTIME1 | \ - __XFS_EXCHANGE_RANGE_UPD_CMTIME2) + __XFS_EXCHANGE_RANGE_UPD_CMTIME2 | \ + __XFS_EXCHANGE_RANGE_CHECK_FRESH2)struct xfs_exchrange {struct file *file1;
@@ -22,10 +26,20 @@ struct xfs_exchrange {u64 length;u64 flags;/* XFS_EXCHANGE_RANGE flags */ + +/* file2 metadata for freshness checks */ +u64 file2_ino; +struct timespec64 file2_mtime; +struct timespec64 file2_ctime; +u32 file2_gen; };long xfs_ioc_exchange_range (struct file *file,struct xfs_exchange_range __user *argp); +long xfs_ioc_start_commit (struct file *file, +struct xfs_commit_range __user *argp); +long xfs_ioc_commit_range (struct file *file, +struct xfs_commit_range __user *argp);struct xfs_exchmaps_req ;
fs/xfs/xfs_ioctl.c +4 -0
@@ -1518,6 +1518,10 @@ xfs_file_ioctl(case XFS_IOC_EXCHANGE_RANGE :return xfs_ioc_exchange_range (filp, arg); +case XFS_IOC_START_COMMIT : +return xfs_ioc_start_commit (filp, arg); +case XFS_IOC_COMMIT_RANGE : +return xfs_ioc_commit_range (filp, arg);default :return -ENOTTY ;
fs/xfs/xfs_trace.h +56 -1
69 lines not on this page · raw message
Re: [PATCH v31.0.1 1/1] xfs: introduce new file range commit ioctls
Quoted 360 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
This patch introduces two more new ioctls to manage atomic updates to file contents --
XFS_IOC_START_COMMITandXFS_IOC_COMMIT_RANGE. The commit mechanism here is exactly the same as whatXFS_IOC_EXCHANGE_RANGEdoes, but with the additional requirement that file2 cannot have changed since some sampling point. The start-commit ioctl performs the sampling of file attributes.Note: This patch currently samples
i_ctimeduringSTART_COMMITand checks that it hasn't changed duringCOMMIT_RANGE. This isn't entirely safe in kernels prior to 6.12 because ctime only had coarse grained granularity and very fast updates could collide with aCOMMIT_RANGE. With the multi-granularity ctime introduced by Jeff Layton, it's now possible to update ctime such that this does not happen.Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/libxfs/xfs_fs.h | 26 +++++++++ fs/xfs/xfs_exchrange.c | 143 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_exchrange.h | 16 +++++ fs/xfs/xfs_ioctl.c | 4 + fs/xfs/xfs_trace.h | 57 +++++++++++++++++++ 5 files changed, 243 insertions(+), 3 deletions(-)fs/xfs/libxfs/xfs_fs.h +26
-0@@ -825,6 +825,30 @@ struct xfs_exchange_range {__u64 flags;/* see XFS_EXCHANGE_RANGE_* below */ }; +/* +* Using the same definition of file2 as struct xfs_exchange_range, commit the +* contents of file1 into file2 if file2 has the same inode number, mtime, and +* ctime as the arguments provided to the call. The old contents of file2 will +* be moved to file1. +* +* Returns -EBUSY if there isn't an exact match for the file2 fields. +* +* Filesystems must be able to restart and complete the operation even after +* the system goes down. +*/ +struct xfs_commit_range { +__s32 file1_fd; +__u32 pad;/* must be zeroes */ +__u64 file1_offset;/* file1 offset, bytes */ +__u64 file2_offset;/* file2 offset, bytes */ +__u64 length;/* bytes to exchange */ + +__u64 flags;/* see XFS_EXCHANGE_RANGE_* below */ + +/* opaque file2 metadata for freshness checks */ +__u64 file2_freshness[6 ]; +}; +/* * Exchange file data all the way to the ends of both files, and then exchange * the file sizes. This flag can be used to replace a file's contents with a @@ -997,6 +1021,8 @@ struct xfs_getparents_by_handle {#define XFS_IOC_BULKSTAT _IOR ('X', 127, struct xfs_bulkstat_req)#define XFS_IOC_INUMBERS _IOR ('X', 128, struct xfs_inumbers_req)#define XFS_IOC_EXCHANGE_RANGE _IOW ('X', 129, struct xfs_exchange_range) +#define XFS_IOC_START_COMMIT _IOR ('X', 130, struct xfs_commit_range) +#define XFS_IOC_COMMIT_RANGE _IOW ('X', 131, struct xfs_commit_range)/* XFS_IOC_GETFSUUID ---------- deprecated 140 */ fs/xfs/xfs_exchrange.c +142
-1@@ -72,6 +72,34 @@ xfs_exchrange_estimate(return error; } +/* +* Check that file2's metadata agree with the snapshot that we took for the +* range commit request. +* +* This should be called after the filesystem has locked /all/ inode metadata +* against modification. +*/ +STATIC int +xfs_exchrange_check_freshness ( +const struct xfs_exchrange *fxr, +struct xfs_inode *ip2) +{ +struct inode *inode2 =VFS_I (ip2); +struct timespec64 ctime =inode_get_ctime (inode2); +struct timespec64 mtime =inode_get_mtime (inode2); + +trace_xfs_exchrange_freshness (fxr, ip2); + +/* Check that file2 hasn't otherwise been modified. */ +if (fxr->file2_ino != ip2->i_ino || + fxr->file2_gen != inode2->i_generation || + !timespec64_equal (&fxr->file2_ctime, &ctime) || + !timespec64_equal (&fxr->file2_mtime, &mtime)) +return -EBUSY ; + +return 0 ; +} +#define QRETRY_IP1 (0x1)#define QRETRY_IP2 (0x2)@@ -607,6 +635,12 @@ xfs_exchrange_prep(if (error || fxr->length ==0 )return error; +if (fxr->flags & __XFS_EXCHANGE_RANGE_CHECK_FRESH2) { + error =xfs_exchrange_check_freshness (fxr, ip2); +if (error) +return error; + } +/* Attach dquots to both inodes before changing block maps. */ error =xfs_qm_dqattach (ip2);if (error)@@ -719,7 +753,8 @@ xfs_exchange_range(if (fxr->file1->f_path.mnt != fxr->file2->f_path.mnt)return -EXDEV ; -if (fxr->flags & ~XFS_EXCHANGE_RANGE_ALL_FLAGS ) +if (fxr->flags & ~(XFS_EXCHANGE_RANGE_ALL_FLAGS | + __XFS_EXCHANGE_RANGE_CHECK_FRESH2))return -EINVAL ;/* Userspace requests only honored for regular files. */ @@ -802,3 +837,109 @@ xfs_ioc_exchange_range(fdput (file1);return error; } + +/* Opaque freshness blob for XFS_IOC_COMMIT_RANGE */ +struct xfs_commit_range_fresh { +xfs_fsid_t fsid;/* m_fixedfsid */ +__u64 file2_ino;/* inode number */ +__s64 file2_mtime;/* modification time */ +__s64 file2_ctime;/* change time */ +__s32 file2_mtime_nsec;/* mod time, nsec */ +__s32 file2_ctime_nsec;/* change time, nsec */ +__u32 file2_gen;/* inode generation */ +__u32 magic;/* zero */ +}; +#define XCR_FRESH_MAGIC 0x444F524B/* DORK */ + +/* Set up a commitrange operation by sampling file2's write-related attrs */ +long +xfs_ioc_start_commit ( +struct file *file, +struct xfs_commit_range __user *argp) +{ +struct xfs_commit_range args = { }; +struct timespec64 ts; +struct xfs_commit_range_fresh *kern_f; +struct xfs_commit_range_fresh __user *user_f; +struct inode *inode2 =file_inode (file); +struct xfs_inode *ip2 =XFS_I (inode2); +const unsigned int lockflags =XFS_IOLOCK_SHARED | +XFS_MMAPLOCK_SHARED | +XFS_ILOCK_SHARED ; + +BUILD_BUG_ON (sizeof (struct xfs_commit_range_fresh ) != +sizeof (args.file2_freshness)); + + kern_f = (struct xfs_commit_range_fresh *)&args.file2_freshness; + +memcpy (&kern_f->fsid, ip2->i_mount->m_fixedfsid,sizeof (xfs_fsid_t)); + +xfs_ilock (ip2, lockflags); + ts =inode_get_ctime (inode2); + kern_f->file2_ctime = ts.tv_sec; + kern_f->file2_ctime_nsec = ts.tv_nsec; + ts =inode_get_mtime (inode2); + kern_f->file2_mtime = ts.tv_sec; + kern_f->file2_mtime_nsec = ts.tv_nsec; + kern_f->file2_ino = ip2->i_ino; + kern_f->file2_gen = inode2->i_generation; + kern_f->magic =XCR_FRESH_MAGIC ; +xfs_iunlock (ip2, lockflags); + + user_f = (struct xfs_commit_range_fresh __user *)&argp->file2_freshness; +if (copy_to_user (user_f, kern_f,sizeof (*kern_f))) +return -EFAULT ; + +return 0 ; +} + +/* +* Exchange file1 and file2 contents if file2 has not been written since the +* start commit operation. +*/ +long +xfs_ioc_commit_range ( +struct file *file, +struct xfs_commit_range __user *argp) +{ +struct xfs_exchrange fxr = { + .file2 = file, + }; +struct xfs_commit_range args; +struct xfs_commit_range_fresh *kern_f; +struct xfs_inode *ip2 =XFS_I (file_inode (file)); +struct xfs_mount *mp = ip2->i_mount; +struct fd file1; +int error; + + kern_f = (struct xfs_commit_range_fresh *)&args.file2_freshness; + +if (copy_from_user (&args, argp,sizeof (args))) +return -EFAULT ; +if (args.flags & ~XFS_EXCHANGE_RANGE_ALL_FLAGS ) +return -EINVAL ; +if (kern_f->magic !=XCR_FRESH_MAGIC ) +return -EBUSY ; +if (memcmp (&kern_f->fsid, mp->m_fixedfsid,sizeof (xfs_fsid_t))) +return -EBUSY ; + + fxr.file1_offset = args.file1_offset; + fxr.file2_offset = args.file2_offset; + fxr.length = args.length; + fxr.flags = args.flags | __XFS_EXCHANGE_RANGE_CHECK_FRESH2; + fxr.file2_ino = kern_f->file2_ino; + fxr.file2_gen = kern_f->file2_gen; + fxr.file2_mtime.tv_sec = kern_f->file2_mtime; + fxr.file2_mtime.tv_nsec = kern_f->file2_mtime_nsec; + fxr.file2_ctime.tv_sec = kern_f->file2_ctime; + fxr.file2_ctime.tv_nsec = kern_f->file2_ctime_nsec; + + file1 =fdget (args.file1_fd); +if (!file1.file) +return -EBADF ; + fxr.file1 = file1.file; + + error =xfs_exchange_range (&fxr); +fdput (file1); +return error; +}fs/xfs/xfs_exchrange.h +15
-1@@ -10,8 +10,12 @@#define __XFS_EXCHANGE_RANGE_UPD_CMTIME1 (1ULL << 63)#define __XFS_EXCHANGE_RANGE_UPD_CMTIME2 (1ULL << 62) +/* Freshness check required */ +#define __XFS_EXCHANGE_RANGE_CHECK_FRESH2 (1ULL << 61) +#define XFS_EXCHANGE_RANGE_PRIV_FLAGS (__XFS_EXCHANGE_RANGE_UPD_CMTIME1 | \ - __XFS_EXCHANGE_RANGE_UPD_CMTIME2) + __XFS_EXCHANGE_RANGE_UPD_CMTIME2 | \ + __XFS_EXCHANGE_RANGE_CHECK_FRESH2)struct xfs_exchrange {struct file *file1;@@ -22,10 +26,20 @@ struct xfs_exchrange {u64 length;u64 flags;/* XFS_EXCHANGE_RANGE flags */ + +/* file2 metadata for freshness checks */ +u64 file2_ino; +struct timespec64 file2_mtime; +struct timespec64 file2_ctime; +u32 file2_gen; };long xfs_ioc_exchange_range (struct file *file,struct xfs_exchange_range __user *argp); +long xfs_ioc_start_commit (struct file *file, +struct xfs_commit_range __user *argp); +long xfs_ioc_commit_range (struct file *file, +struct xfs_commit_range __user *argp);struct xfs_exchmaps_req ;fs/xfs/xfs_ioctl.c +4
-0@@ -1518,6 +1518,10 @@ xfs_file_ioctl(case XFS_IOC_EXCHANGE_RANGE :return xfs_ioc_exchange_range (filp, arg); +case XFS_IOC_START_COMMIT : +return xfs_ioc_start_commit (filp, arg); +case XFS_IOC_COMMIT_RANGE : +return xfs_ioc_commit_range (filp, arg);default :return -ENOTTY ;fs/xfs/xfs_trace.h +49
-1@@ -4926,7 +4926,8 @@ DEFINE_INODE_ERROR_EVENT(xfs_exchrange_error); { XFS_EXCHANGE_RANGE_DRY_RUN, "DRY_RUN" }, \ { XFS_EXCHANGE_RANGE_FILE1_WRITTEN, "F1_WRITTEN" }, \ { __XFS_EXCHANGE_RANGE_UPD_CMTIME1, "CMTIME1" }, \ - { __XFS_EXCHANGE_RANGE_UPD_CMTIME2, "CMTIME2" } + { __XFS_EXCHANGE_RANGE_UPD_CMTIME2, "CMTIME2" }, \ + { __XFS_EXCHANGE_RANGE_CHECK_FRESH2, "FRESH2" }/* file exchange-range tracepoint class */ DECLARE_EVENT_CLASS(xfs_exchrange_class,@@ -4986,6 +4987,60 @@ DEFINE_EXCHRANGE_EVENT(xfs_exchrange_prep);DEFINE_EXCHRANGE_EVENT (xfs_exchrange_flush);DEFINE_EXCHRANGE_EVENT (xfs_exchrange_mappings); +TRACE_EVENT(xfs_exchrange_freshness, + TP_PROTO(const struct xfs_exchrange *fxr, struct xfs_inode *ip2), + TP_ARGS(fxr, ip2), + TP_STRUCT__entry( + __field(dev_t, dev) + __field(xfs_ino_t, ip2_ino) + __field(long long, ip2_mtime) + __field(long long, ip2_ctime) + __field(int, ip2_mtime_nsec) + __field(int, ip2_ctime_nsec) + + __field(xfs_ino_t, file2_ino) + __field(long long, file2_mtime) + __field(long long, file2_ctime) + __field(int, file2_mtime_nsec) + __field(int, file2_ctime_nsec) + ), + TP_fast_assign( + struct timespec64 ts64; + struct inode *inode2 = VFS_I(ip2); + + __entry->dev = inode2->i_sb->s_dev; + __entry->ip2_ino = ip2->i_ino; + + ts64 = inode_get_ctime(inode2); + __entry->ip2_ctime = ts64.tv_sec; + __entry->ip2_ctime_nsec = ts64.tv_nsec; + + ts64 = inode_get_mtime(inode2); + __entry->ip2_mtime = ts64.tv_sec; + __entry->ip2_mtime_nsec = ts64.tv_nsec; + + __entry->file2_ino = fxr->file2_ino; + __entry->file2_mtime = fxr->file2_mtime.tv_sec; + __entry->file2_ctime = fxr->file2_ctime.tv_sec; + __entry->file2_mtime_nsec = fxr->file2_mtime.tv_nsec; + __entry->file2_ctime_nsec = fxr->file2_ctime.tv_nsec; + ), + TP_printk("dev %d:%d " + "ino 0x%llx mtime %lld:%d ctime %lld:%d -> " + "file 0x%llx mtime %lld:%d ctime %lld:%d", + MAJOR(__entry->dev), MINOR(__entry->dev), + __entry->ip2_ino, + __entry->ip2_mtime, + __entry->ip2_mtime_nsec, + __entry->ip2_ctime, + __entry->ip2_ctime_nsec,
fs/xfs/xfs_trace.h DEFINE_EXCHRANGE_EVENT(xfs_exchrange_prep); · line 5046
Quoted 10 lines
fs/xfs/xfs_trace.h +7
-0+ __entry->file2_ino, + __entry->file2_mtime, + __entry->file2_mtime_nsec, + __entry->file2_ctime, + __entry->file2_ctime_nsec) +); + TRACE_EVENT(xfs_exchmaps_overhead, TP_PROTO(struct xfs_mount *mp, unsigned long long bmbt_blocks, unsigned long long rmapbt_blocks),
Re: [PATCH v31.0.1 1/1] xfs: introduce new file range commit ioctls
In reply to Darrick J. WongLooks good:
PATCH 3/10 xfs: cleanups before adding metadata directories
In reply to Darrick J. WongHi all,
Before we start adding code for metadata directory trees, let's clean up some warts in the realtime bitmap code and the inode allocator code.
If you're going to start using this code, I strongly recommend pulling from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy! Comments and questions are, as always, welcome.
--D
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=metadir-cleanups
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=metadir-cleanups
---
Commits in this patchset:
* xfs: validate inumber in xfs_iget
* xfs: match on the global RT inode numbers in xfs_is_metadata_inode
* xfs: pass the icreate args object to xfs_dialloc
--- fs/xfs/libxfs/xfs_ialloc.c | 5 +++-- fs/xfs/libxfs/xfs_ialloc.h | 4 +++- fs/xfs/scrub/tempfile.c | 2 +- fs/xfs/xfs_icache.c | 2 +- fs/xfs/xfs_inode.c | 4 ++-- fs/xfs/xfs_inode.h | 7 ++++--- fs/xfs/xfs_qm.c | 2 +- fs/xfs/xfs_symlink.c | 2 +- 8 files changed, 16 insertions(+), 12 deletions(-)
PATCH 1/3 xfs: validate inumber in xfs_iget
From: Darrick J. Wong <[email protected]>
Actually use the inumber validator to check the argument passed in here.
--- fs/xfs/xfs_icache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
fs/xfs/xfs_icache.c +1 -1
@@ -755,7 +755,7 @@ xfs_iget(ASSERT ((lock_flags & (XFS_IOLOCK_EXCL |XFS_IOLOCK_SHARED )) ==0 );/* reject inode numbers outside existing AGs */ -if (!ino ||XFS_INO_TO_AGNO (mp, ino) >= mp->m_sb.sb_agcount) +if (!xfs_verify_ino (mp, ino))return -EINVAL ;XFS_STATS_INC (mp, xs_ig_attempts);
PATCH 2/3 xfs: match on the global RT inode numbers in xfs_is_metadata_inode
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Match the inode number instead of the inode pointers, as the inode pointers in the superblock will go away soon.
--- fs/xfs/xfs_inode.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
fs/xfs/xfs_inode.h +4 -3
@@ -276,12 +276,13 @@ static inline bool xfs_is_reflink_inode(struct xfs_inode *ip)return ip->i_diflags2 &XFS_DIFLAG2_REFLINK ; } -static inline bool xfs_is_metadata_inode (struct xfs_inode *ip) +static inline bool xfs_is_metadata_inode (const struct xfs_inode *ip) {struct xfs_mount *mp = ip->i_mount; -return ip == mp->m_rbmip || ip == mp->m_rsumip || -xfs_is_quota_inode (&mp->m_sb, ip->i_ino); +return ip->i_ino == mp->m_sb.sb_rbmino || + ip->i_ino == mp->m_sb.sb_rsumino || +xfs_is_quota_inode (&mp->m_sb, ip->i_ino); }bool xfs_is_always_cow_inode (struct xfs_inode *ip);
PATCH 3/3 xfs: pass the icreate args object to xfs_dialloc
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Pass the xfs_icreate_args object to xfs_dialloc since we can extract the relevant mode (really just the file type) and parent inumber from there. This simplifies the calling convention in preparation for the next patch.
--- fs/xfs/libxfs/xfs_ialloc.c | 5 +++-- fs/xfs/libxfs/xfs_ialloc.h | 4 +++- fs/xfs/scrub/tempfile.c | 2 +- fs/xfs/xfs_inode.c | 4 ++-- fs/xfs/xfs_qm.c | 2 +- fs/xfs/xfs_symlink.c | 2 +- 6 files changed, 11 insertions(+), 8 deletions(-)
fs/xfs/libxfs/xfs_ialloc.c +3 -2
@@ -1855,11 +1855,12 @@ xfs_dialloc_try_ag(int xfs_dialloc (struct xfs_trans **tpp, -xfs_ino_t parent, -umode_t mode, +const struct xfs_icreate_args *args,xfs_ino_t *new_ino) {struct xfs_mount *mp = (*tpp)->t_mountp; +xfs_ino_t parent = args->pip ? args->pip->i_ino :0 ; +umode_t mode = args->mode &S_IFMT ;xfs_agnumber_t agno;int error =0 ;xfs_agnumber_t start_agno;
fs/xfs/libxfs/xfs_ialloc.h +3 -1
@@ -33,11 +33,13 @@ xfs_make_iptr(struct xfs_mount *mp, struct xfs_buf *b, int o)return xfs_buf_offset (b, o << (mp)->m_sb.sb_inodelog); } +struct xfs_icreate_args ; +/* * Allocate an inode on disk. Mode is used to tell whether the new inode will * need space, and whether it is a directory. */ -int xfs_dialloc (struct xfs_trans **tpp,xfs_ino_t parent,umode_t mode, +int xfs_dialloc (struct xfs_trans **tpp,const struct xfs_icreate_args *args,xfs_ino_t *new_ino); int xfs_difree(struct xfs_trans *tp, struct xfs_perag *pag,
fs/xfs/scrub/tempfile.c +1 -1
@@ -88,7 +88,7 @@ xrep_tempfile_create( goto out_release_dquots;/* Allocate inode, set up directory. */ - error =xfs_dialloc (&tp, dp->i_ino, mode, &ino); + error =xfs_dialloc (&tp, &args, &ino);if (error) goto out_trans_cancel; error =xfs_icreate (tp, ino, &args, &sc->tempip);
fs/xfs/xfs_inode.c +2 -2
@@ -704,7 +704,7 @@ xfs_create(* entry pointing to them, but a directory also the "." entry * pointing to itself. */ - error =xfs_dialloc (&tp, dp->i_ino, args->mode, &ino); + error =xfs_dialloc (&tp, args, &ino);if (!error) error =xfs_icreate (tp, ino, args, &du.ip);if (error)
@@ -812,7 +812,7 @@ xfs_create_tmpfile(if (error) goto out_release_dquots; - error =xfs_dialloc (&tp, dp->i_ino, args->mode, &ino); + error =xfs_dialloc (&tp, args, &ino);if (!error) error =xfs_icreate (tp, ino, args, &ip);if (error)
fs/xfs/xfs_qm.c +1 -1
@@ -799,7 +799,7 @@ xfs_qm_qino_alloc( };xfs_ino_t ino; - error =xfs_dialloc (&tp,0 ,S_IFREG , &ino); + error =xfs_dialloc (&tp, &args, &ino);if (!error) error =xfs_icreate (tp, ino, &args, ipp);if (error) {
fs/xfs/xfs_symlink.c +1 -1
@@ -165,7 +165,7 @@ xfs_symlink(/* * Allocate an inode for the symlink. */ - error =xfs_dialloc (&tp, dp->i_ino,S_IFLNK , &ino); + error =xfs_dialloc (&tp, &args, &ino);if (!error) error =xfs_icreate (tp, ino, &args, &du.ip);if (error)
Re: [PATCH 3/3] xfs: pass the icreate args object to xfs_dialloc
Looks good:
PATCH 4/10 xfs: metadata inode directories
In reply to Darrick J. WongHi all,
This series delivers a new feature -- metadata inode directories. This is a separate directory tree (rooted in the superblock) that contains only inodes that contain filesystem metadata. Different metadata objects can be looked up with regular paths.
We start by creating xfs_imeta_* functions to mediate access to metadata inode pointers. This enables the imeta code to abstract inode pointers, whether they're the classic five in the superblock, or the much more complex directory tree. All current users of metadata inodes (rt+quota) are converted to use the boilerplate code.
Next, we define the metadir on-disk format, which consists of marking inodes with a new iflag that says they're metadata. This we use to prevent bulkstat and friends from ever getting their hands on fs metadata.
Finally, we implement metadir operations so that clients can create, delete, zap, and look up metadata inodes by path. Beware that much of this code is only lightly used, because the five current users of metadata inodes don't tend to change them very often. This is likely to change if and when the subvolume and multiple-rt-volume features get written/merged/etc.
If you're going to start using this code, I strongly recommend pulling from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy! Comments and questions are, as always, welcome.
--D
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=metadir
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=metadir
fstests git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfstests-dev.git/log/?h=metadir
xfsdocs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-documentation.git/log/?h=metadir
---
Commits in this patchset:
* xfs: define the on-disk format for the metadir feature
* xfs: refactor loading quota inodes in the regular case
* xfs: iget for metadata inodes
* xfs: load metadata directory root at mount time
* xfs: enforce metadata inode flag
* xfs: read and write metadata inode directory tree
* xfs: disable the agi rotor for metadata inodes
* xfs: hide metadata inodes from everyone because they are special
* xfs: advertise metadata directory feature
* xfs: allow bulkstat to return metadata directories
* xfs: don't count metadata directory files to quota
* xfs: mark quota inodes as metadata files
* xfs: adjust xfs_bmap_add_attrfork for metadir
* xfs: record health problems with the metadata directory
* xfs: refactor directory tree root predicates
* xfs: do not count metadata directory files when doing online quotacheck
* xfs: don't fail repairs on metadata files with no attr fork
* xfs: metadata files can have xattrs if metadir is enabled
* xfs: adjust parent pointer scrubber for sb-rooted metadata files
* xfs: fix di_metatype field of inodes that won't load
* xfs: scrub metadata directories
* xfs: check the metadata directory inumber in superblocks
* xfs: move repair temporary files to the metadata directory tree
* xfs: check metadata directory file path connectivity
* xfs: confirm dotdot target before replacing it during a repair
* xfs: repair metadata directory file path connectivity
--- fs/xfs/Makefile | 5 fs/xfs/libxfs/xfs_attr.c | 5 fs/xfs/libxfs/xfs_bmap.c | 5 fs/xfs/libxfs/xfs_format.h | 81 +++++- fs/xfs/libxfs/xfs_fs.h | 26 ++ fs/xfs/libxfs/xfs_health.h | 6 fs/xfs/libxfs/xfs_ialloc.c | 58 +++- fs/xfs/libxfs/xfs_inode_buf.c | 83 ++++++ fs/xfs/libxfs/xfs_inode_buf.h | 3 fs/xfs/libxfs/xfs_inode_util.c | 2 fs/xfs/libxfs/xfs_log_format.h | 2 fs/xfs/libxfs/xfs_metadir.c | 481 ++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_metadir.h | 47 ++++ fs/xfs/libxfs/xfs_metafile.c | 52 ++++ fs/xfs/libxfs/xfs_metafile.h | 31 ++ fs/xfs/libxfs/xfs_ondisk.h | 2 fs/xfs/libxfs/xfs_sb.c | 12 + fs/xfs/scrub/agheader.c | 5 fs/xfs/scrub/common.c | 65 ++++- fs/xfs/scrub/common.h | 5 fs/xfs/scrub/dir.c | 10 + fs/xfs/scrub/dir_repair.c | 20 + fs/xfs/scrub/dirtree.c | 32 ++ fs/xfs/scrub/dirtree.h | 12 - fs/xfs/scrub/findparent.c | 28 ++ fs/xfs/scrub/health.c | 1 fs/xfs/scrub/inode.c | 35 ++- fs/xfs/scrub/inode_repair.c | 34 ++- fs/xfs/scrub/metapath.c | 521 +++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/nlinks.c | 4 fs/xfs/scrub/nlinks_repair.c | 4 fs/xfs/scrub/orphanage.c | 4 fs/xfs/scrub/parent.c | 39 ++- fs/xfs/scrub/parent_repair.c | 37 ++- fs/xfs/scrub/quotacheck.c | 7 - fs/xfs/scrub/repair.c | 22 +- fs/xfs/scrub/repair.h | 3 fs/xfs/scrub/scrub.c | 9 + fs/xfs/scrub/scrub.h | 2 fs/xfs/scrub/stats.c | 1 fs/xfs/scrub/tempfile.c | 105 ++++++++ fs/xfs/scrub/tempfile.h | 3 fs/xfs/scrub/trace.c | 1 fs/xfs/scrub/trace.h | 42 +++ fs/xfs/xfs_dquot.c | 1 fs/xfs/xfs_health.c | 2 fs/xfs/xfs_icache.c | 73 +++++ fs/xfs/xfs_inode.c | 13 + fs/xfs/xfs_inode.h | 14 + fs/xfs/xfs_inode_item.c | 7 - fs/xfs/xfs_inode_item_recover.c | 5 fs/xfs/xfs_ioctl.c | 7 + fs/xfs/xfs_iops.c | 15 + fs/xfs/xfs_itable.c | 33 ++ fs/xfs/xfs_itable.h | 3 fs/xfs/xfs_mount.c | 31 ++ fs/xfs/xfs_mount.h | 3 fs/xfs/xfs_qm.c | 80 +++++- fs/xfs/xfs_qm.h | 3 fs/xfs/xfs_qm_syscalls.c | 13 - fs/xfs/xfs_quota.h | 5 fs/xfs/xfs_quotaops.c | 53 ++-- fs/xfs/xfs_rtalloc.c | 38 ++- fs/xfs/xfs_super.c | 4 fs/xfs/xfs_trace.c | 2 fs/xfs/xfs_trace.h | 102 ++++++++ fs/xfs/xfs_trans_dquot.c | 6 67 files changed, 2288 insertions(+), 177 deletions(-) create mode 100644 fs/xfs/libxfs/xfs_metadir.c create mode 100644 fs/xfs/libxfs/xfs_metadir.h create mode 100644 fs/xfs/libxfs/xfs_metafile.c create mode 100644 fs/xfs/libxfs/xfs_metafile.h create mode 100644 fs/xfs/scrub/metapath.c
PATCH 1/26 xfs: define the on-disk format for the metadir feature
From: Darrick J. Wong <[email protected]>
Define the on-disk layout and feature flags for the metadata inode directory feature. Add a xfs_sb_version_hasmetadir for benefit of xfs_repair, which needs to know where the new end of the superblock lies.
--- fs/xfs/libxfs/xfs_format.h | 81 +++++++++++++++++++++++++++++++++++---- fs/xfs/libxfs/xfs_inode_buf.c | 13 +++++- fs/xfs/libxfs/xfs_inode_util.c | 2 + fs/xfs/libxfs/xfs_log_format.h | 2 - fs/xfs/libxfs/xfs_ondisk.h | 2 - fs/xfs/libxfs/xfs_sb.c | 10 +++++ fs/xfs/scrub/inode.c | 3 + fs/xfs/scrub/inode_repair.c | 9 +++- fs/xfs/xfs_inode.h | 14 +++++++ fs/xfs/xfs_inode_item.c | 7 +++ fs/xfs/xfs_inode_item_recover.c | 5 ++ fs/xfs/xfs_mount.h | 2 + fs/xfs/xfs_super.c | 4 ++ 13 files changed, 136 insertions(+), 18 deletions(-)
fs/xfs/libxfs/xfs_format.h +73 -8
@@ -174,6 +174,8 @@ typedef struct xfs_sb {xfs_lsn_t sb_lsn;/* last write sequence */ uuid_t sb_meta_uuid;/* metadata file system unique id */ +xfs_ino_t sb_metadirino;/* metadata directory tree root */ +/* must be padded to 64 bit alignment */ } xfs_sb_t;
@@ -259,6 +261,8 @@ struct xfs_dsb {__be64 sb_lsn;/* last write sequence */ uuid_t sb_meta_uuid;/* metadata file system unique id */ +__be64 sb_metadirino;/* metadata directory tree root */ +/* must be padded to 64 bit alignment */ };
@@ -374,6 +378,7 @@ xfs_sb_has_ro_compat_feature(#define XFS_SB_FEAT_INCOMPAT_NREXT64 (1 << 5)/* large extent counters */ #define XFS_SB_FEAT_INCOMPAT_EXCHRANGE (1 << 6)/* exchangerange supported */ #define XFS_SB_FEAT_INCOMPAT_PARENT (1 << 7)/* parent pointers */ +#define XFS_SB_FEAT_INCOMPAT_METADIR (1 << 8)/* metadata dir tree */ #define XFS_SB_FEAT_INCOMPAT_ALL \ (XFS_SB_FEAT_INCOMPAT_FTYPE | \ XFS_SB_FEAT_INCOMPAT_SPINODES | \
@@ -426,6 +431,12 @@ static inline bool xfs_sb_version_haslogxattrs(struct xfs_sb *sbp) XFS_SB_FEAT_INCOMPAT_LOG_XATTRS); } +static inline bool xfs_sb_version_hasmetadir (const struct xfs_sb *sbp) +{ +return (XFS_SB_VERSION_NUM (sbp) ==XFS_SB_VERSION_5 ) && + (sbp->sb_features_incompat &XFS_SB_FEAT_INCOMPAT_METADIR ); +} +static inline bool xfs_is_quota_inode (struct xfs_sb *sbp,xfs_ino_t ino) {
@@ -790,6 +801,27 @@ static inline time64_t xfs_bigtime_to_unix(uint64_t ondisk_seconds)return (time64_t )ondisk_seconds -XFS_BIGTIME_EPOCH_OFFSET ; } +enum xfs_metafile_type { +XFS_METAFILE_UNKNOWN ,/* unknown */ +XFS_METAFILE_DIR ,/* metadir directory */ +XFS_METAFILE_USRQUOTA ,/* user quota */ +XFS_METAFILE_GRPQUOTA ,/* group quota */ +XFS_METAFILE_PRJQUOTA ,/* project quota */ +XFS_METAFILE_RTBITMAP ,/* rt bitmap */ +XFS_METAFILE_RTSUMMARY ,/* rt summary */ + +XFS_METAFILE_MAX +} __packed; + +#define XFS_METAFILE_TYPE_STR \ + { XFS_METAFILE_UNKNOWN, "unknown" }, \ + { XFS_METAFILE_DIR, "dir" }, \ + { XFS_METAFILE_USRQUOTA, "usrquota" }, \ + { XFS_METAFILE_GRPQUOTA, "grpquota" }, \ + { XFS_METAFILE_PRJQUOTA, "prjquota" }, \ + { XFS_METAFILE_RTBITMAP, "rtbitmap" }, \ + { XFS_METAFILE_RTSUMMARY, "rtsummary" } +/* * On-disk inode structure. *
@@ -812,7 +844,10 @@ struct xfs_dinode {__be16 di_mode;/* mode and type of file */ __u8 di_version;/* inode version */ __u8 di_format;/* format of di_c data */ -__be16 di_onlink;/* old number of links to file */ +union { +__be16 di_onlink;/* old number of links to file */ +__be16 di_metatype;/* XFS_METAFILE_* */ + } __packed;/* explicit packing because arm gcc bloats this up */ __be32 di_uid;/* owner's user id */ __be32 di_gid;/* owner's group id */ __be32 di_nlink;/* number of links to file */
@@ -1092,17 +1127,47 @@ static inline void xfs_dinode_put_rdev(struct xfs_dinode *dip, xfs_dev_t rdev)#define XFS_DIFLAG2_REFLINK_BIT 1/* file's blocks may be shared */ #define XFS_DIFLAG2_COWEXTSIZE_BIT 2/* copy on write extent size hint */ #define XFS_DIFLAG2_BIGTIME_BIT 3/* big timestamps */ -#define XFS_DIFLAG2_NREXT64_BIT 4/* large extent counters */ +#define XFS_DIFLAG2_NREXT64_BIT 4/* large extent counters */ +#define XFS_DIFLAG2_METADATA_BIT 63/* filesystem metadata */ -#define XFS_DIFLAG2_DAX (1 << XFS_DIFLAG2_DAX_BIT) -#define XFS_DIFLAG2_REFLINK (1 << XFS_DIFLAG2_REFLINK_BIT) -#define XFS_DIFLAG2_COWEXTSIZE (1 << XFS_DIFLAG2_COWEXTSIZE_BIT) -#define XFS_DIFLAG2_BIGTIME (1 << XFS_DIFLAG2_BIGTIME_BIT) -#define XFS_DIFLAG2_NREXT64 (1 << XFS_DIFLAG2_NREXT64_BIT) +#define XFS_DIFLAG2_DAX (1ULL << XFS_DIFLAG2_DAX_BIT) +#define XFS_DIFLAG2_REFLINK (1ULL << XFS_DIFLAG2_REFLINK_BIT) +#define XFS_DIFLAG2_COWEXTSIZE (1ULL << XFS_DIFLAG2_COWEXTSIZE_BIT) +#define XFS_DIFLAG2_BIGTIME (1ULL << XFS_DIFLAG2_BIGTIME_BIT) +#define XFS_DIFLAG2_NREXT64 (1ULL << XFS_DIFLAG2_NREXT64_BIT) + +/* +* The inode contains filesystem metadata and can be found through the metadata +* directory tree. Metadata inodes must satisfy the following constraints: +* +* - V5 filesystem (and ftype) are enabled; +* - The only valid modes are regular files and directories; +* - The access bits must be zero; +* - DMAPI event and state masks are zero; +* - The user and group IDs must be zero; +* - The project ID can be used as a u32 annotation; +* - The immutable, sync, noatime, nodump, nodefrag flags must be set. +* - The dax flag must not be set. +* - Directories must have nosymlinks set. +* +* These requirements are chosen defensively to minimize the ability of +* userspace to read or modify the contents, should a metadata file ever +* escape to userspace. +* +* There are further constraints on the directory tree itself: +* +* - Metadata inodes must never be resolvable through the root directory; +* - They must never be accessed by userspace; +* - Metadata directory entries must have correct ftype. +* +* Superblock-rooted metadata files must have the METADATA iflag set even +* though they do not have a parent directory. +*/ +#define XFS_DIFLAG2_METADATA (1ULL << XFS_DIFLAG2_METADATA_BIT)#define XFS_DIFLAG2_ANY \ (XFS_DIFLAG2_DAX | XFS_DIFLAG2_REFLINK | XFS_DIFLAG2_COWEXTSIZE | \ - XFS_DIFLAG2_BIGTIME | XFS_DIFLAG2_NREXT64) + XFS_DIFLAG2_BIGTIME | XFS_DIFLAG2_NREXT64 | XFS_DIFLAG2_METADATA)static inline bool xfs_dinode_has_bigtime (const struct xfs_dinode *dip) {
fs/xfs/libxfs/xfs_inode_buf.c +11 -2
@@ -215,6 +215,8 @@ xfs_inode_from_disk(set_nlink (inode,be32_to_cpu (from->di_nlink)); ip->i_projid = (prid_t )be16_to_cpu (from->di_projid_hi) <<16 |be16_to_cpu (from->di_projid_lo); +if (from->di_flags2 &cpu_to_be64 (XFS_DIFLAG2_METADATA )) + ip->i_metatype =be16_to_cpu (from->di_metatype); }i_uid_write (inode,be32_to_cpu (from->di_uid));
@@ -315,7 +317,10 @@ xfs_inode_to_disk(struct inode *inode =VFS_I (ip); to->di_magic =cpu_to_be16 (XFS_DINODE_MAGIC ); - to->di_onlink =0 ; +if (xfs_is_metadir_inode (ip)) + to->di_metatype =cpu_to_be16 (ip->i_metatype); +else + to->di_onlink =0 ; to->di_format =xfs_ifork_format (&ip->i_df); to->di_uid =cpu_to_be32 (i_uid_read (inode));
@@ -523,9 +528,13 @@ xfs_dinode_verify(* di_nlink==0 on a V1 inode. V2/3 inodes would get written out with * di_onlink==0, so we can check that. */ -if (dip->di_version >=2 ) { +if (dip->di_version ==2 ) {if (dip->di_onlink)return __this_address; + }else if (dip->di_version >=3 ) { +if (!(dip->di_flags2 &cpu_to_be64 (XFS_DIFLAG2_METADATA )) && + dip->di_onlink) +return __this_address; }/* don't allow invalid i_size */
fs/xfs/libxfs/xfs_inode_util.c +2 -0
@@ -224,6 +224,8 @@ xfs_inode_inherit_flags2( }if (pip->i_diflags2 &XFS_DIFLAG2_DAX ) ip->i_diflags2 |=XFS_DIFLAG2_DAX ; +if (pip->i_diflags2 &XFS_DIFLAG2_METADATA ) + ip->i_diflags2 |=XFS_DIFLAG2_METADATA ;/* Don't let invalid cowextsize hints propagate. */ failaddr = xfs_inode_validate_cowextsize(ip->i_mount, ip->i_cowextsize,
fs/xfs/libxfs/xfs_log_format.h +1 -1
@@ -404,7 +404,7 @@ struct xfs_log_dinode {uint16_t di_mode;/* mode and type of file */ int8_t di_version;/* inode version */ int8_t di_format;/* format of di_c data */ -uint8_t di_pad3[2 ];/* unused in v2/3 inodes */ +uint16_t di_metatype;/* metadata type, if DIFLAG2_METADATA */ uint32_t di_uid;/* owner's user id */ uint32_t di_gid;/* owner's group id */ uint32_t di_nlink;/* number of links to file */
fs/xfs/libxfs/xfs_ondisk.h +1 -1
@@ -37,7 +37,7 @@ xfs_check_ondisk_structs(void) XFS_CHECK_STRUCT_SIZE(struct xfs_dinode, 176); XFS_CHECK_STRUCT_SIZE(struct xfs_disk_dquot, 104); XFS_CHECK_STRUCT_SIZE(struct xfs_dqblk, 136); - XFS_CHECK_STRUCT_SIZE(struct xfs_dsb, 264); + XFS_CHECK_STRUCT_SIZE(struct xfs_dsb, 272); XFS_CHECK_STRUCT_SIZE(struct xfs_dsymlink_hdr, 56); XFS_CHECK_STRUCT_SIZE(struct xfs_inobt_key, 4); XFS_CHECK_STRUCT_SIZE(struct xfs_inobt_rec, 16);
fs/xfs/libxfs/xfs_sb.c +10 -0
@@ -180,6 +180,8 @@ xfs_sb_version_to_features( features |=XFS_FEAT_EXCHANGE_RANGE ;if (sbp->sb_features_incompat &XFS_SB_FEAT_INCOMPAT_PARENT ) features |=XFS_FEAT_PARENT ; +if (sbp->sb_features_incompat &XFS_SB_FEAT_INCOMPAT_METADIR ) + features |=XFS_FEAT_METADIR ;return features; }
@@ -683,6 +685,11 @@ __xfs_sb_from_disk(/* Convert on-disk flags to in-memory flags? */ if (convert_xquota)xfs_sb_quota_from_disk (to); + +if (to->sb_features_incompat &XFS_SB_FEAT_INCOMPAT_METADIR ) + to->sb_metadirino =be64_to_cpu (from->sb_metadirino); +else + to->sb_metadirino =NULLFSINO ; }void
@@ -830,6 +837,9 @@ xfs_sb_to_disk( to->sb_lsn =cpu_to_be64 (from->sb_lsn);if (from->sb_features_incompat &XFS_SB_FEAT_INCOMPAT_META_UUID )uuid_copy (&to->sb_meta_uuid, &from->sb_meta_uuid); + +if (from->sb_features_incompat &XFS_SB_FEAT_INCOMPAT_METADIR ) + to->sb_metadirino =cpu_to_be64 (from->sb_metadirino); }/*
fs/xfs/scrub/inode.c +2 -1
@@ -421,7 +421,8 @@ xchk_dinode(break ;case 2 :case 3 : -if (dip->di_onlink !=0 ) +if (!(dip->di_flags2 &cpu_to_be64 (XFS_DIFLAG2_METADATA )) && + dip->di_onlink !=0 )xchk_ino_set_corrupt (sc, ino);if (dip->di_mode ==0 && sc->ip)
fs/xfs/scrub/inode_repair.c +6 -3
@@ -521,10 +521,13 @@ STATIC voidxrep_dinode_nlinks ( struct xfs_dinode *dip) { -if (dip->di_version >1 ) - dip->di_onlink =0 ; -else +if (dip->di_version <2 ) { dip->di_nlink =0 ; +return ; + } + +if (!(dip->di_flags2 &cpu_to_be64 (XFS_DIFLAG2_METADATA ))) + dip->di_onlink =0 ; }/* Fix any conflicting flags that the verifiers complain about. */
fs/xfs/xfs_inode.h +14 -0
@@ -65,6 +65,7 @@ typedef struct xfs_inode {uint16_t i_flushiter;/* incremented on flush */ };uint8_t i_forkoff;/* attr fork offset >> 3 */ +enum xfs_metafile_type i_metatype;/* XFS_METAFILE_* */ uint16_t i_diflags;/* XFS_DIFLAG_... */ uint64_t i_diflags2;/* XFS_DIFLAG2_... */ struct timespec64 i_crtime;/* time created */
@@ -276,10 +277,23 @@ static inline bool xfs_is_reflink_inode(struct xfs_inode *ip)return ip->i_diflags2 &XFS_DIFLAG2_REFLINK ; } +static inline bool xfs_is_metadir_inode (const struct xfs_inode *ip) +{ +return ip->i_diflags2 &XFS_DIFLAG2_METADATA ; +} +static inline bool xfs_is_metadata_inode (const struct xfs_inode *ip) {struct xfs_mount *mp = ip->i_mount; +/* Any file in the metadata directory tree is a metadata inode. */ +if (xfs_has_metadir (mp)) +return xfs_is_metadir_inode (ip); + +/* +* Before metadata directories, the only metadata inodes were the +* three quota files, the realtime bitmap, and the realtime summary. +*/ return ip->i_ino == mp->m_sb.sb_rbmino || ip->i_ino == mp->m_sb.sb_rsumino ||xfs_is_quota_inode (&mp->m_sb, ip->i_ino);
fs/xfs/xfs_inode_item.c +6 -1
@@ -556,7 +556,6 @@ xfs_inode_to_log_dinode( to->di_projid_lo = ip->i_projid &0xffff ; to->di_projid_hi = ip->i_projid >>16 ; -memset (to->di_pad3,0 ,sizeof (to->di_pad3)); to->di_atime =xfs_inode_to_log_dinode_ts (ip,inode_get_atime (inode)); to->di_mtime =xfs_inode_to_log_dinode_ts (ip,inode_get_mtime (inode)); to->di_ctime =xfs_inode_to_log_dinode_ts (ip,inode_get_ctime (inode));
@@ -590,10 +589,16 @@ xfs_inode_to_log_dinode(/* dummy value for initialisation */ to->di_crc =0 ; + +if (xfs_is_metadir_inode (ip)) + to->di_metatype = ip->i_metatype; +else + to->di_metatype =0 ; }else { to->di_version =2 ; to->di_flushiter = ip->i_flushiter;memset (to->di_v2_pad,0 ,sizeof (to->di_v2_pad)); + to->di_metatype =0 ; }xfs_inode_to_log_dinode_iext_counters (ip, to);
fs/xfs/xfs_inode_item_recover.c +4 -1
@@ -175,7 +175,10 @@ xfs_log_dinode_to_disk( to->di_mode =cpu_to_be16 (from->di_mode); to->di_version = from->di_version; to->di_format = from->di_format; - to->di_onlink =0 ; +if (from->di_flags2 &XFS_DIFLAG2_METADATA ) + to->di_metatype =cpu_to_be16 (from->di_metatype); +else + to->di_onlink =0 ; to->di_uid =cpu_to_be32 (from->di_uid); to->di_gid =cpu_to_be32 (from->di_gid); to->di_nlink =cpu_to_be32 (from->di_nlink);
fs/xfs/xfs_mount.h +2 -0
@@ -299,6 +299,7 @@ typedef struct xfs_mount {#define XFS_FEAT_NEEDSREPAIR (1ULL << 25)/* needs xfs_repair */ #define XFS_FEAT_NREXT64 (1ULL << 26)/* large extent counters */ #define XFS_FEAT_EXCHANGE_RANGE (1ULL << 27)/* exchange range */ +#define XFS_FEAT_METADIR (1ULL << 28)/* metadata directory tree */ /* Mount features */ #define XFS_FEAT_NOATTR2 (1ULL << 48)/* disable attr2 creation */
@@ -354,6 +355,7 @@ __XFS_HAS_FEAT(bigtime, BIGTIME) __XFS_HAS_FEAT(needsrepair, NEEDSREPAIR) __XFS_HAS_FEAT(large_extent_counts, NREXT64) __XFS_HAS_FEAT(exchange_range, EXCHANGE_RANGE) +__XFS_HAS_FEAT (metadir,METADIR )/* * Some features are always on for v5 file systems, allow the compiler to
fs/xfs/xfs_super.c +4 -0
@@ -1721,6 +1721,10 @@ xfs_fs_fill_super( mp->m_features &= ~XFS_FEAT_DISCARD ; } +if (xfs_has_metadir (mp)) +xfs_warn (mp, +"EXPERIMENTAL metadata directory feature in use. Use at your own risk!" ); +if (xfs_has_reflink (mp)) {if (mp->m_sb.sb_rblocks) { xfs_alert(mp,
Re: [PATCH 01/26] xfs: define the on-disk format for the metadir feature
fs/xfs/libxfs/xfs_format.h static inline bool xfs_sb_version_haslogxattrs(struct xfs_sb *sbp) · line 436
Quoted 3 lines · Darrick J. Wong
fs/xfs/libxfs/xfs_format.h +3
-0+static inline bool xfs_sb_version_hasmetadir (const struct xfs_sb *sbp) +{ + return (XFS_SB_VERSION_NUM(sbp) == XFS_SB_VERSION_5) &&
This is copy and paste from the other xfs_sb_version_* helpers, but there really is no need for the braces here.
Otherwise looks good:
PATCH 2/26 xfs: refactor loading quota inodes in the regular case
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create a helper function to load quota inodes in the case where the dqtype and the sb quota inode fields correspond. This is true for nearly all the iget callsites in the quota code, except for when we're switching the group and project quota inodes. We'll need this in subsequent patches to make the metadir handling less convoluted.
--- fs/xfs/xfs_qm.c | 46 +++++++++++++++++++++++++++++++++++----- fs/xfs/xfs_qm.h | 3 +++ fs/xfs/xfs_qm_syscalls.c | 13 +++++------ fs/xfs/xfs_quotaops.c | 53 +++++++++++++++++++++++++++------------------- 4 files changed, 80 insertions(+), 35 deletions(-)
fs/xfs/xfs_qm.c +40 -6
@@ -1538,6 +1538,43 @@ xfs_qm_mount_quotas( } } +/* +* Load the inode for a given type of quota, assuming that the sb fields have +* been sorted out. This is not true when switching quota types on a V4 +* filesystem, so do not use this function for that. +* +* Returns -ENOENT if the quota inode field is NULLFSINO; 0 and an inode on +* success; or a negative errno. +*/ +int +xfs_qm_qino_load ( +struct xfs_mount *mp, +xfs_dqtype_t type, +struct xfs_inode **ipp) +{ +xfs_ino_t ino =NULLFSINO ; + +switch (type) { +case XFS_DQTYPE_USER : + ino = mp->m_sb.sb_uquotino; +break ; +case XFS_DQTYPE_GROUP : + ino = mp->m_sb.sb_gquotino; +break ; +case XFS_DQTYPE_PROJ : + ino = mp->m_sb.sb_pquotino; +break ; +default : +ASSERT (0 ); +return -EFSCORRUPTED ; + } + +if (ino ==NULLFSINO ) +return -ENOENT ; + +return xfs_iget (mp,NULL , ino,0 ,0 , ipp); +} +/* * This is called after the superblock has been read in and we're ready to * iget the quota inodes.
@@ -1561,24 +1598,21 @@ xfs_qm_init_quotainos(if (XFS_IS_UQUOTA_ON (mp) && mp->m_sb.sb_uquotino !=NULLFSINO ) {ASSERT (mp->m_sb.sb_uquotino >0 ); - error =xfs_iget (mp,NULL , mp->m_sb.sb_uquotino, -0 ,0 , &uip); + error =xfs_qm_qino_load (mp,XFS_DQTYPE_USER , &uip);if (error)return error; }if (XFS_IS_GQUOTA_ON (mp) && mp->m_sb.sb_gquotino !=NULLFSINO ) {ASSERT (mp->m_sb.sb_gquotino >0 ); - error =xfs_iget (mp,NULL , mp->m_sb.sb_gquotino, -0 ,0 , &gip); + error =xfs_qm_qino_load (mp,XFS_DQTYPE_GROUP , &gip);if (error) goto error_rele; }if (XFS_IS_PQUOTA_ON (mp) && mp->m_sb.sb_pquotino !=NULLFSINO ) {ASSERT (mp->m_sb.sb_pquotino >0 ); - error =xfs_iget (mp,NULL , mp->m_sb.sb_pquotino, -0 ,0 , &pip); + error =xfs_qm_qino_load (mp,XFS_DQTYPE_PROJ , &pip);if (error) goto error_rele; }
fs/xfs/xfs_qm.h +3 -0
@@ -184,4 +184,7 @@ xfs_get_defquota(struct xfs_quotainfo *qi, xfs_dqtype_t type) } } +int xfs_qm_qino_load (struct xfs_mount *mp,xfs_dqtype_t type, +struct xfs_inode **ipp); +#endif /* __XFS_QM_H__ */
fs/xfs/xfs_qm_syscalls.c +6 -7
@@ -53,16 +53,15 @@ xfs_qm_scall_quotaoff( STATIC intxfs_qm_scall_trunc_qfile (struct xfs_mount *mp, -xfs_ino_t ino) +xfs_dqtype_t type) {struct xfs_inode *ip;struct xfs_trans *tp;int error; -if (ino ==NULLFSINO ) + error =xfs_qm_qino_load (mp, type, &ip); +if (error == -ENOENT )return 0 ; - - error =xfs_iget (mp,NULL , ino,0 ,0 , &ip);if (error)return error;
@@ -113,17 +112,17 @@ xfs_qm_scall_trunc_qfiles( }if (flags &XFS_QMOPT_UQUOTA ) { - error =xfs_qm_scall_trunc_qfile (mp, mp->m_sb.sb_uquotino); + error =xfs_qm_scall_trunc_qfile (mp,XFS_DQTYPE_USER );if (error)return error; }if (flags &XFS_QMOPT_GQUOTA ) { - error =xfs_qm_scall_trunc_qfile (mp, mp->m_sb.sb_gquotino); + error =xfs_qm_scall_trunc_qfile (mp,XFS_DQTYPE_GROUP );if (error)return error; }if (flags &XFS_QMOPT_PQUOTA ) - error =xfs_qm_scall_trunc_qfile (mp, mp->m_sb.sb_pquotino); + error =xfs_qm_scall_trunc_qfile (mp,XFS_DQTYPE_PROJ );return error; }
fs/xfs/xfs_quotaops.c +31 -22
83 lines not on this page · raw message
Re: [PATCH 02/26] xfs: refactor loading quota inodes in the regular case
Quoted 6 lines
fs/xfs/xfs_qm.c +6
-0+xfs_ino_t ino =NULLFSINO ; + +switch (type) { +case XFS_DQTYPE_USER : + ino = mp->m_sb.sb_uquotino; +break ;
fs/xfs/xfs_qm.c xfs_qm_mount_quotas( · line 1570
Quoted 10 lines
fs/xfs/xfs_qm.c +10
-0+case XFS_DQTYPE_GROUP : + ino = mp->m_sb.sb_gquotino; +break ; +case XFS_DQTYPE_PROJ : + ino = mp->m_sb.sb_pquotino; +break ; +default : +ASSERT (0 ); +return -EFSCORRUPTED ; + }
I'd probably split this type to ino lookup into a separate helper, but that doesn't really matter.
Otherwise looks good:
Re: [PATCH 02/26] xfs: refactor loading quota inodes in the regular case
Quoted 11 lines · Christoph Hellwig
Quoted 11 lines
fs/xfs/xfs_qm.c +11
-0+xfs_ino_t ino =NULLFSINO ; + +switch (type) { +case XFS_DQTYPE_USER : + ino = mp->m_sb.sb_uquotino; +break ; +case XFS_DQTYPE_GROUP : + ino = mp->m_sb.sb_gquotino; +break ; +case XFS_DQTYPE_PROJ : + ino = mp->m_sb.sb_pquotino;
Quoted 8 lines
fs/xfs/xfs_qm.c xfs_qm_mount_quotas( · line 1570
Quoted 5 lines
fs/xfs/xfs_qm.c +5
-0+break ; +default : +ASSERT (0 ); +return -EFSCORRUPTED ; + }I'd probably split this type to ino lookup into a separate helper, but that doesn't really matter.
I tried that, but left it embedded here because I didn't want to write a helper function that then had to return a magic value for "some programmer f*cked up, let's just bail out" that also couldn't have been read in from disk. In theory 0 should work because xfs_sb_quota_from_disk should have converted that to NULLFSINO for us, but that felt like a good way to introduce a subtle bug that will blow up later.
I suppose 0 wouldn't be the worst magic value, since xfs_iget would just blow up noisily for us. OTOH all this gets deleted at the other end of the metadir series anyway so I'd preferentially fix xfs_dqinode_load.
Thanks for the review!
--D
Quoted 5 lines
Otherwise looks good:
Reviewed-by Christoph Hellwig <[email protected]>
PATCH 3/26 xfs: iget for metadata inodes
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create a xfs_metafile_iget function for metadata inodes to ensure that when we try to iget a metadata file, the inobt thinks a metadata inode is in use and that the metadata type matches what we are expecting.
--- fs/xfs/libxfs/xfs_metafile.h | 16 ++++++++++ fs/xfs/xfs_icache.c | 65 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/xfs_inode.c | 1 + fs/xfs/xfs_qm.c | 23 ++++++++++++++- fs/xfs/xfs_rtalloc.c | 38 ++++++++++++++----------- 5 files changed, 125 insertions(+), 18 deletions(-) create mode 100644 fs/xfs/libxfs/xfs_metafile.h
fs/xfs/libxfs/xfs_metafile.h new file +16 -0
@@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* +* Copyright (c) 2018-2024 Oracle. All Rights Reserved. +* Author: Darrick J. Wong <[email protected]> +*/ +#ifndef __XFS_METAFILE_H__ +#define __XFS_METAFILE_H__ + +/* Code specific to kernel/userspace; must be provided externally. */ + +int xfs_trans_metafile_iget (struct xfs_trans *tp,xfs_ino_t ino, +enum xfs_metafile_type metafile_type,struct xfs_inode **ipp); +int xfs_metafile_iget (struct xfs_mount *mp,xfs_ino_t ino, +enum xfs_metafile_type metafile_type,struct xfs_inode **ipp); + +#endif /* __XFS_METAFILE_H__ */
fs/xfs/xfs_icache.c +65 -0
77 lines not on this page · raw message
fs/xfs/xfs_inode.c +1 -0
@@ -43,6 +43,7 @@#include "xfs_parent.h" #include "xfs_xattr.h" #include "xfs_inode_util.h" +#include "xfs_metafile.h" struct kmem_cache *xfs_inode_cache;
fs/xfs/xfs_qm.c +21 -2
@@ -27,6 +27,8 @@#include "xfs_ialloc.h" #include "xfs_log_priv.h" #include "xfs_health.h" +#include "xfs_da_format.h" +#include "xfs_metafile.h" /* * The global quota manager. There is only one of these for the entire
@@ -733,6 +735,17 @@ xfs_qm_destroy_quotainfo( mp->m_quotainfo =NULL ; } +static inline enum xfs_metafile_type +xfs_qm_metafile_type ( +unsigned int flags) +{ +if (flags &XFS_QMOPT_UQUOTA ) +return XFS_METAFILE_USRQUOTA ; +else if (flags &XFS_QMOPT_GQUOTA ) +return XFS_METAFILE_GRPQUOTA ; +return XFS_METAFILE_PRJQUOTA ; +} +/* * Create an inode and return with a reference already taken, but unlocked * This is how we create quota inodes
@@ -744,6 +757,7 @@ xfs_qm_qino_alloc( unsigned int flags) {struct xfs_trans *tp; +enum xfs_metafile_type metafile_type =xfs_qm_metafile_type (flags);int error;bool need_alloc = true;
@@ -777,9 +791,10 @@ xfs_qm_qino_alloc( } }if (ino !=NULLFSINO ) { - error =xfs_iget (mp,NULL , ino,0 ,0 , ipp); + error =xfs_metafile_iget (mp, ino, metafile_type, ipp);if (error)return error; + mp->m_sb.sb_gquotino =NULLFSINO ; mp->m_sb.sb_pquotino =NULLFSINO ; need_alloc = false;
@@ -1553,16 +1568,20 @@ xfs_qm_qino_load( struct xfs_inode **ipp) {xfs_ino_t ino =NULLFSINO ; +enum xfs_metafile_type metafile_type =XFS_METAFILE_UNKNOWN ;switch (type) {case XFS_DQTYPE_USER : ino = mp->m_sb.sb_uquotino; + metafile_type =XFS_METAFILE_USRQUOTA ;break ;case XFS_DQTYPE_GROUP : ino = mp->m_sb.sb_gquotino; + metafile_type =XFS_METAFILE_GRPQUOTA ;break ;case XFS_DQTYPE_PROJ : ino = mp->m_sb.sb_pquotino; + metafile_type =XFS_METAFILE_PRJQUOTA ;break ;default :ASSERT (0 );
@@ -1572,7 +1591,7 @@ xfs_qm_qino_load(if (ino ==NULLFSINO )return -ENOENT ; -return xfs_iget (mp,NULL , ino,0 ,0 , ipp); +return xfs_metafile_iget (mp, ino, metafile_type, ipp); }/*
fs/xfs/xfs_rtalloc.c +22 -16
@@ -25,6 +25,8 @@#include "xfs_quota.h" #include "xfs_log_priv.h" #include "xfs_health.h" +#include "xfs_da_format.h" +#include "xfs_metafile.h" /* * Return whether there are any free extents in the size range given
@@ -1206,16 +1208,12 @@ xfs_rtalloc_reinit_frextents(*/ static inline int xfs_rtmount_iread_extents ( +struct xfs_trans *tp,struct xfs_inode *ip,unsigned int lock_class) { -struct xfs_trans *tp;int error; - error =xfs_trans_alloc_empty (ip->i_mount, &tp); -if (error) -return error; -xfs_ilock (ip,XFS_ILOCK_EXCL | lock_class); error =xfs_iread_extents (tp, ip,XFS_DATA_FORK );
@@ -1230,7 +1228,6 @@ xfs_rtmount_iread_extents( out_unlock:xfs_iunlock (ip,XFS_ILOCK_EXCL | lock_class); -xfs_trans_cancel (tp);return error; }
@@ -1238,43 +1235,52 @@ xfs_rtmount_iread_extents(* Get the bitmap and summary inodes and the summary cache into the mount * structure at mount time. */ -int /* error */ +int xfs_rtmount_inodes ( -xfs_mount_t *mp)/* file system mount structure */ +struct xfs_mount *mp) { -int error;/* error return value */ -xfs_sb_t *sbp; +struct xfs_trans *tp; +struct xfs_sb *sbp = &mp->m_sb; +int error; - sbp = &mp->m_sb; - error =xfs_iget (mp,NULL , sbp->sb_rbmino,0 ,0 , &mp->m_rbmip); + error =xfs_trans_alloc_empty (mp, &tp); +if (error) +return error; + + error =xfs_trans_metafile_iget (tp, mp->m_sb.sb_rbmino, +XFS_METAFILE_RTBITMAP , &mp->m_rbmip);if (xfs_metadata_is_sick (error))xfs_rt_mark_sick (mp,XFS_SICK_RT_BITMAP );if (error) -return error; + goto out_trans;ASSERT (mp->m_rbmip !=NULL ); - error =xfs_rtmount_iread_extents (mp->m_rbmip,XFS_ILOCK_RTBITMAP ); + error =xfs_rtmount_iread_extents (tp, mp->m_rbmip,XFS_ILOCK_RTBITMAP );if (error) goto out_rele_bitmap; - error =xfs_iget (mp,NULL , sbp->sb_rsumino,0 ,0 , &mp->m_rsumip); + error =xfs_trans_metafile_iget (tp, mp->m_sb.sb_rsumino, +XFS_METAFILE_RTSUMMARY , &mp->m_rsumip);if (xfs_metadata_is_sick (error))xfs_rt_mark_sick (mp,XFS_SICK_RT_SUMMARY );if (error) goto out_rele_bitmap;ASSERT (mp->m_rsumip !=NULL ); - error =xfs_rtmount_iread_extents (mp->m_rsumip,XFS_ILOCK_RTSUM ); + error =xfs_rtmount_iread_extents (tp, mp->m_rsumip,XFS_ILOCK_RTSUM );if (error) goto out_rele_summary;xfs_alloc_rsum_cache (mp, sbp->sb_rbmblocks); +xfs_trans_cancel (tp);return 0 ; out_rele_summary:xfs_irele (mp->m_rsumip); out_rele_bitmap:xfs_irele (mp->m_rbmip); +out_trans: +xfs_trans_cancel (tp);return error; }
Re: [PATCH 03/26] xfs: iget for metadata inodes
fs/xfs/xfs_icache.c line 30
Quoted 3 lines · Darrick J. Wong
fs/xfs/xfs_icache.c +3
-0+#include "xfs_da_format.h" +#include "xfs_dir2.h" +#include "xfs_metafile.h"
Hmm, there really should be no need to include xfs_da_format.h before metafile.h - enum xfs_metafile_type is in format.h and I can't see what else would need it.
I don't think dir2.h is needed here either.
Otherwise this looks good:
Re: [PATCH 03/26] xfs: iget for metadata inodes
Quoted 10 lines · Christoph Hellwig
fs/xfs/xfs_icache.c line 30
Quoted 3 lines · Darrick J. Wong
fs/xfs/xfs_icache.c +3
-0+#include "xfs_da_format.h" +#include "xfs_dir2.h" +#include "xfs_metafile.h" Hmm, there really should be no need to include
xfs_da_format.hbeforemetafile.h- enumxfs_metafile_typeis informat.hand I can't see what else would need it.I don't think
dir2.his needed here either.
Yeah, I'll go figure out which of these includes can go away.
--D
Quoted 4 lines
Otherwise this looks good:
Reviewed-by Christoph Hellwig <[email protected]>
PATCH 4/26 xfs: load metadata directory root at mount time
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Load the metadata directory root inode into memory at mount time and release it at unmount time.
--- fs/xfs/xfs_mount.c | 31 +++++++++++++++++++++++++++++-- fs/xfs/xfs_mount.h | 1 + 2 files changed, 30 insertions(+), 2 deletions(-)
fs/xfs/xfs_mount.c +29 -2
@@ -35,6 +35,7 @@#include "xfs_trace.h" #include "xfs_ag.h" #include "xfs_rtbitmap.h" +#include "xfs_metafile.h" #include "scrub/stats.h" static DEFINE_MUTEX (xfs_uuid_table_mutex);
@@ -616,6 +617,22 @@ xfs_mount_setup_inode_geom(xfs_ialloc_setup_geometry (mp); } +/* Mount the metadata directory tree root. */ +STATIC int +xfs_mount_setup_metadir ( +struct xfs_mount *mp) +{ +int error; + +/* Load the metadata directory root inode into memory. */ + error =xfs_metafile_iget (mp, mp->m_sb.sb_metadirino,XFS_METAFILE_DIR , + &mp->m_metadirip); +if (error) +xfs_warn (mp,"Failed to load metadir root directory, error %d" , + error); +return error; +} +/* Compute maximum possible height for per-AG btree types for this fs. */ static inline void xfs_agbtree_compute_maxlevels(
@@ -862,6 +879,12 @@ xfs_mountfs( mp->m_features |=XFS_FEAT_ATTR2 ; } +if (xfs_has_metadir (mp)) { + error =xfs_mount_setup_metadir (mp); +if (error) + goto out_free_metadir; + } +/* * Get and sanity-check the root inode. * Save the pointer to it in the mount structure.
@@ -872,7 +895,7 @@ xfs_mountfs(xfs_warn (mp,"Failed to read root inode 0x%llx, error %d" , sbp->sb_rootino, -error); - goto out_log_dealloc; + goto out_free_metadir; }ASSERT (rip !=NULL );
@@ -1014,6 +1037,9 @@ xfs_mountfs(xfs_irele (rip);/* Clean out dquots that might be in memory after quotacheck. */ xfs_qm_unmount (mp); + out_free_metadir: +if (mp->m_metadirip) +xfs_irele (mp->m_metadirip);/* * Inactivate all inodes that might still be in memory after a log
@@ -1035,7 +1061,6 @@ xfs_mountfs(* quota inodes. */ xfs_unmount_flush_inodes (mp); - out_log_dealloc:xfs_log_mount_cancel (mp); out_inodegc_shrinker:shrinker_free (mp->m_inodegc_shrinker);
@@ -1087,6 +1112,8 @@ xfs_unmountfs(xfs_qm_unmount_quotas (mp);xfs_rtunmount_inodes (mp);xfs_irele (mp->m_rootip); +if (mp->m_metadirip) +xfs_irele (mp->m_metadirip);xfs_unmount_flush_inodes (mp);
fs/xfs/xfs_mount.h +1 -0
@@ -93,6 +93,7 @@ typedef struct xfs_mount {struct xfs_inode *m_rbmip;/* pointer to bitmap inode */ struct xfs_inode *m_rsumip;/* pointer to summary inode */ struct xfs_inode *m_rootip;/* pointer to root directory */ +struct xfs_inode *m_metadirip;/* ptr to metadata directory */ struct xfs_quotainfo *m_quotainfo;/* disk quota information */ struct xfs_buftarg *m_ddev_targp;/* data device */ struct xfs_buftarg *m_logdev_targp;/* log device */
Re: [PATCH 04/26] xfs: load metadata directory root at mount time
Looks good:
PATCH 5/26 xfs: enforce metadata inode flag
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add checks for the metadata inode flag so that we don't ever leak metadata inodes out to userspace, and we don't ever try to read a regular inode as metadata.
--- fs/xfs/libxfs/xfs_inode_buf.c | 70 +++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_inode_buf.h | 3 ++ fs/xfs/libxfs/xfs_metafile.h | 11 ++++++ fs/xfs/scrub/common.c | 10 +++++- fs/xfs/scrub/inode.c | 26 ++++++++++++++- fs/xfs/scrub/inode_repair.c | 10 ++++++ fs/xfs/xfs_icache.c | 9 +++++ fs/xfs/xfs_inode.c | 11 ++++++ 8 files changed, 145 insertions(+), 5 deletions(-)
fs/xfs/libxfs/xfs_inode_buf.c +70 -0
@@ -19,6 +19,7 @@#include "xfs_ialloc.h" #include "xfs_dir2.h" #include "xfs_health.h" +#include "xfs_metafile.h" #include <linux/iversion.h>
@@ -488,6 +489,69 @@ xfs_dinode_verify_nrext64(return NULL ; } +/* +* Validate all the picky requirements we have for a file that claims to be +* filesystem metadata. +*/ +xfs_failaddr_t +xfs_dinode_verify_metadir ( +struct xfs_mount *mp, +struct xfs_dinode *dip, +uint16_t mode, +uint16_t flags, +uint64_t flags2) +{ +if (!xfs_has_metadir (mp)) +return __this_address; + +/* V5 filesystem only */ +if (dip->di_version <3 ) +return __this_address; + +if (be16_to_cpu (dip->di_metatype) >=XFS_METAFILE_MAX ) +return __this_address; + +/* V3 inode fields that are always zero */ +if ((flags2 &XFS_DIFLAG2_NREXT64 ) && dip->di_nrext64_pad) +return __this_address; +if (!(flags2 &XFS_DIFLAG2_NREXT64 ) && dip->di_flushiter) +return __this_address; + +/* Metadata files can only be directories or regular files */ +if (!S_ISDIR (mode) && !S_ISREG (mode)) +return __this_address; + +/* They must have zero access permissions */ +if (mode &0777 ) +return __this_address; + +/* DMAPI event and state masks are zero */ +if (dip->di_dmevmask || dip->di_dmstate) +return __this_address; + +/* +* User and group IDs must be zero. The project ID is used for +* grouping inodes. Metadata inodes are never accounted to quotas. +*/ +if (dip->di_uid || dip->di_gid) +return __this_address; + +/* Mandatory directory flags must be set */ +if (S_ISDIR (mode)) { +if ((flags &XFS_METADIR_DIFLAGS ) !=XFS_METADIR_DIFLAGS ) +return __this_address; + }else { +if ((flags &XFS_METAFILE_DIFLAGS ) !=XFS_METAFILE_DIFLAGS ) +return __this_address; + } + +/* dax flags2 must not be set */ +if (flags2 &XFS_DIFLAG2_DAX ) +return __this_address; + +return NULL ; +} + xfs_failaddr_t xfs_dinode_verify( struct xfs_mount *mp,
@@ -672,6 +736,12 @@ xfs_dinode_verify( !xfs_has_bigtime(mp))return __this_address; +if (flags2 &XFS_DIFLAG2_METADATA ) { + fa =xfs_dinode_verify_metadir (mp, dip, mode, flags, flags2); +if (fa) +return fa; + } +return NULL ; }
fs/xfs/libxfs/xfs_inode_buf.h +3 -0
@@ -28,6 +28,9 @@ int xfs_inode_from_disk(struct xfs_inode *ip, struct xfs_dinode *from);xfs_failaddr_t xfs_dinode_verify (struct xfs_mount *mp,xfs_ino_t ino,struct xfs_dinode *dip); +xfs_failaddr_t xfs_dinode_verify_metadir (struct xfs_mount *mp, +struct xfs_dinode *dip,uint16_t mode,uint16_t flags, +uint64_t flags2);xfs_failaddr_t xfs_inode_validate_extsize (struct xfs_mount *mp,uint32_t extsize,uint16_t mode,uint16_t flags); xfs_failaddr_t xfs_inode_validate_cowextsize(struct xfs_mount *mp,
fs/xfs/libxfs/xfs_metafile.h +11 -0
@@ -6,6 +6,17 @@#ifndef __XFS_METAFILE_H__#define __XFS_METAFILE_H__ +/* All metadata files must have these flags set. */ +#define XFS_METAFILE_DIFLAGS (XFS_DIFLAG_IMMUTABLE | \ + XFS_DIFLAG_SYNC | \ + XFS_DIFLAG_NOATIME | \ + XFS_DIFLAG_NODUMP | \ + XFS_DIFLAG_NODEFRAG) + +/* All metadata directory files must have these flags set. */ +#define XFS_METADIR_DIFLAGS (XFS_METAFILE_DIFLAGS | \ + XFS_DIFLAG_NOSYMLINKS) +/* Code specific to kernel/userspace; must be provided externally. */ int xfs_trans_metafile_iget(struct xfs_trans *tp, xfs_ino_t ino,
fs/xfs/scrub/common.c +8 -2
@@ -947,9 +947,15 @@ xchk_iget_for_scrubbing(if (sc->sm->sm_ino ==0 || sc->sm->sm_ino == ip_in->i_ino)return xchk_install_live_inode (sc, ip_in); -/* Reject internal metadata files and obviously bad inode numbers. */ -if (xfs_internal_inum (mp, sc->sm->sm_ino)) +/* +* On pre-metadir filesystems, reject internal metadata files. For +* metadir filesystems, limited scrubbing of any file in the metadata +* directory tree by handle is allowed, because that is the only way to +* validate the lack of parent pointers in the sb-root metadata inodes. +*/ +if (!xfs_has_metadir (mp) &&xfs_internal_inum (mp, sc->sm->sm_ino))return -ENOENT ; +/* Reject obviously bad inode numbers. */ if (!xfs_verify_ino (sc->mp, sc->sm->sm_ino))return -ENOENT ;
fs/xfs/scrub/inode.c +24 -2
@@ -60,6 +60,22 @@ xchk_install_handle_iscrub(if (error)return error; +/* +* Don't allow scrubbing by handle of any non-directory inode records +* in the metadata directory tree. We don't know if any of the scans +* launched by this scrubber will end up indirectly trying to lock this +* file. +* +* Scrubbers of inode-rooted metadata files (e.g. quota files) will +* attach all the resources needed to scrub the inode and call +* xchk_inode directly. Userspace cannot call this directly. +*/ +if (xfs_is_metadir_inode (ip) && !S_ISDIR (VFS_I (ip)->i_mode)) { +xchk_irele (sc, ip); + sc->ip =NULL ; +return -ENOENT ; + } +return xchk_prepare_iscrub (sc); }
@@ -94,9 +110,15 @@ xchk_setup_inode(return xchk_prepare_iscrub (sc); } -/* Reject internal metadata files and obviously bad inode numbers. */ -if (xfs_internal_inum (mp, sc->sm->sm_ino)) +/* +* On pre-metadir filesystems, reject internal metadata files. For +* metadir filesystems, limited scrubbing of any file in the metadata +* directory tree by handle is allowed, because that is the only way to +* validate the lack of parent pointers in the sb-root metadata inodes. +*/ +if (!xfs_has_metadir (mp) &&xfs_internal_inum (mp, sc->sm->sm_ino))return -ENOENT ; +/* Reject obviously bad inode numbers. */ if (!xfs_verify_ino (sc->mp, sc->sm->sm_ino))return -ENOENT ;
fs/xfs/scrub/inode_repair.c +10 -0
@@ -568,6 +568,16 @@ xrep_dinode_flags( dip->di_nrext64_pad =0 ; else if (dip->di_version >= 3) dip->di_v3_pad = 0; + +if (flags2 &XFS_DIFLAG2_METADATA ) { +xfs_failaddr_t fa; + + fa =xfs_dinode_verify_metadir (sc->mp, dip, mode, flags, + flags2); +if (fa) + flags2 &= ~XFS_DIFLAG2_METADATA ; + } + dip->di_flags =cpu_to_be16 (flags); dip->di_flags2 =cpu_to_be64 (flags2); }
fs/xfs/xfs_icache.c +8 -1
@@ -844,13 +844,20 @@ xfs_trans_metafile_iget( mode =S_IFREG ;if (inode_wrong_type (VFS_I (ip), mode)) goto bad_rele; +if (xfs_has_metadir (mp)) { +if (!xfs_is_metadir_inode (ip)) + goto bad_rele; +if (metafile_type != ip->i_metatype) + goto bad_rele; + } *ipp = ip;return 0 ; bad_rele:xfs_irele (ip); whine: -xfs_err (mp,"metadata inode 0x%llx is corrupt" , ino); +xfs_err (mp,"metadata inode 0x%llx type %u is corrupt" , ino, + metafile_type);return -EFSCORRUPTED ; }
fs/xfs/xfs_inode.c +11 -0
@@ -555,8 +555,19 @@ xfs_lookup(if (error) goto out_free_name; +/* +* Fail if a directory entry in the regular directory tree points to +* a metadata file. +*/ +if (XFS_IS_CORRUPT (dp->i_mount,xfs_is_metadir_inode (*ipp))) { + error = -EFSCORRUPTED ; + goto out_irele; + } +return 0 ; +out_irele: +xfs_irele (*ipp); out_free_name:if (ci_name)kfree (ci_name->name);
Re: [PATCH 05/26] xfs: enforce metadata inode flag
fs/xfs/libxfs/xfs_inode_buf.c xfs_dinode_verify_nrext64( · line 539
Quoted 1 line
fs/xfs/libxfs/xfs_inode_buf.c +1
-0+/* Mandatory directory flags must be set */
s/directory/inode/ ?
Quoted 1 line
+/* All metadata directory files must have these flags set. */
s/directory files/directories/ ?
Otherwise looks good:
Re: [PATCH 05/26] xfs: enforce metadata inode flag
Quoted 7 lines · Christoph Hellwig
fs/xfs/libxfs/xfs_inode_buf.c xfs_dinode_verify_nrext64( · line 539
Quoted 1 line
fs/xfs/libxfs/xfs_inode_buf.c +1
-0+/* Mandatory directory flags must be set */ s/directory/inode/ ?
Quoted 1 line
+/* All metadata directory files must have these flags set. */
s/directory files/directories/ ?
Fixed.
Quoted 3 lines
Otherwise looks good:
Reviewed-by Christoph Hellwig <[email protected]>
Thanks!
--D
PATCH 6/26 xfs: read and write metadata inode directory tree
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Plumb in the bits we need to load metadata inodes from a named entry in a metadir directory, create (or hardlink) inodes into a metadir directory, create metadir directories, and flag inodes as being metadata files.
--- fs/xfs/Makefile | 4 fs/xfs/libxfs/xfs_metadir.c | 474 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_metadir.h | 47 ++++ fs/xfs/libxfs/xfs_metafile.c | 52 +++++ fs/xfs/libxfs/xfs_metafile.h | 4 fs/xfs/xfs_icache.c | 2 fs/xfs/xfs_trace.c | 2 fs/xfs/xfs_trace.h | 102 +++++++++ 8 files changed, 685 insertions(+), 2 deletions(-) create mode 100644 fs/xfs/libxfs/xfs_metadir.c create mode 100644 fs/xfs/libxfs/xfs_metadir.h create mode 100644 fs/xfs/libxfs/xfs_metafile.c
fs/xfs/Makefile +3 -1
@@ -15,6 +15,7 @@ xfs-y += xfs_trace.o # build the libxfs code first xfs-y += $(addprefix libxfs/, \ xfs_ag.o \ + xfs_ag_resv.o \ xfs_alloc.o \ xfs_alloc_btree.o \ xfs_attr.o \
@@ -42,7 +43,8 @@ xfs-y += $(addprefix libxfs/, \ xfs_inode_buf.o \ xfs_inode_util.o \ xfs_log_rlimit.o \ - xfs_ag_resv.o \ + xfs_metadir.o \ + xfs_metafile.o \ xfs_parent.o \ xfs_rmap.o \ xfs_rmap_btree.o \
fs/xfs/libxfs/xfs_metadir.c new file +474 -0
474 lines not on this page · raw message
fs/xfs/libxfs/xfs_metadir.h new file +47 -0
47 lines not on this page · raw message
fs/xfs/libxfs/xfs_metafile.c new file +52 -0
52 lines not on this page · raw message
fs/xfs/libxfs/xfs_metafile.h +4 -0
@@ -17,6 +17,10 @@#define XFS_METADIR_DIFLAGS (XFS_METAFILE_DIFLAGS | \ XFS_DIFLAG_NOSYMLINKS) +void xfs_metafile_set_iflag (struct xfs_trans *tp,struct xfs_inode *ip, +enum xfs_metafile_type metafile_type); +void xfs_metafile_clear_iflag (struct xfs_trans *tp,struct xfs_inode *ip); +/* Code specific to kernel/userspace; must be provided externally. */ int xfs_trans_metafile_iget(struct xfs_trans *tp, xfs_ino_t ino,
fs/xfs/xfs_icache.c +1 -1
@@ -830,7 +830,7 @@ xfs_trans_metafile_iget(int error; error =xfs_iget (mp, tp, ino,XFS_IGET_UNTRUSTED ,0 , &ip); -if (error == -EFSCORRUPTED ) +if (error == -EFSCORRUPTED || error == -EINVAL ) goto whine;if (error)return error;
fs/xfs/xfs_trace.c +2 -0
@@ -44,6 +44,8 @@#include "xfs_parent.h" #include "xfs_rmap.h" #include "xfs_refcount.h" +#include "xfs_metafile.h" +#include "xfs_metadir.h" /* * We include this last to have the helpers above available for the trace
fs/xfs/xfs_trace.h +102 -0
114 lines not on this page · raw message
Re: [PATCH 06/26] xfs: read and write metadata inode directory tree
fs/xfs/libxfs/xfs_metadir.c line 311
Quoted 8 lines · Darrick J. Wong
fs/xfs/libxfs/xfs_metadir.c new file +8
-0+#ifndef __KERNEL__ +/* +* Begin the process of linking a metadata file by allocating transactions +* and locking whatever resources we're going to need. +*/ +int +xfs_metadir_start_link ( +struct xfs_metadir_update *upd)
I kinda hate placing this repair only code in libxfs, but given the dependencies on metadir internals I can't really think of anything better, so:
PATCH 7/26 xfs: disable the agi rotor for metadata inodes
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Ideally, we'd put all the metadata inodes in one place if we could, so that the metadata all stay reasonably close together instead of spreading out over the disk. Furthermore, if the log is internal we'd probably prefer to keep the metadata near the log. Therefore, disable AGI rotoring for metadata inode allocations.
--- fs/xfs/libxfs/xfs_ialloc.c | 58 ++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 18 deletions(-)
fs/xfs/libxfs/xfs_ialloc.c +40 -18
@@ -1844,6 +1844,40 @@ xfs_dialloc_try_ag(return error; } +/* +* Pick an AG for the new inode. +* +* Directories, symlinks, and regular files frequently allocate at least one +* block, so factor that potential expansion when we examine whether an AG has +* enough space for file creation. Try to keep metadata files all in the same +* AG. +*/ +static inline xfs_agnumber_t +xfs_dialloc_pick_ag ( +struct xfs_mount *mp, +struct xfs_inode *dp, +umode_t mode) +{ +xfs_agnumber_t start_agno; + +if (!dp) +return 0 ; +if (xfs_is_metadir_inode (dp)) { +if (mp->m_sb.sb_logstart) +return XFS_FSB_TO_AGNO (mp, mp->m_sb.sb_logstart); +return 0 ; + } + +if (S_ISDIR (mode)) +return (atomic_inc_return (&mp->m_agirotor) -1 ) % mp->m_maxagi; + + start_agno =XFS_INO_TO_AGNO (mp, dp->i_ino); +if (start_agno >= mp->m_maxagi) + start_agno =0 ; + +return start_agno; +} +/* * Allocate an on-disk inode. *
@@ -1859,31 +1893,19 @@ xfs_dialloc( xfs_ino_t *new_ino) {struct xfs_mount *mp = (*tpp)->t_mountp; +struct xfs_perag *pag; +struct xfs_ino_geometry *igeo =M_IGEO (mp); +xfs_ino_t ino =NULLFSINO ;xfs_ino_t parent = args->pip ? args->pip->i_ino :0 ; -umode_t mode = args->mode &S_IFMT ;xfs_agnumber_t agno; -int error =0 ;xfs_agnumber_t start_agno; -struct xfs_perag *pag; -struct xfs_ino_geometry *igeo =M_IGEO (mp); +umode_t mode = args->mode &S_IFMT ;bool ok_alloc = true;bool low_space = false;int flags; -xfs_ino_t ino =NULLFSINO ; +int error =0 ; -/* -* Directories, symlinks, and regular files frequently allocate at least -* one block, so factor that potential expansion when we examine whether -* an AG has enough space for file creation. -*/ -if (S_ISDIR (mode)) - start_agno = (atomic_inc_return (&mp->m_agirotor) -1 ) % - mp->m_maxagi; -else { - start_agno =XFS_INO_TO_AGNO (mp, parent); -if (start_agno >= mp->m_maxagi) - start_agno =0 ; - } + start_agno =xfs_dialloc_pick_ag (mp, args->pip, mode);/* * If we have already hit the ceiling of inode blocks then clear
Re: [PATCH 07/26] xfs: disable the agi rotor for metadata inodes
Looks good:
PATCH 8/26 xfs: hide metadata inodes from everyone because they are special
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Metadata inodes are private files and therefore cannot be exposed to userspace. This means no bulkstat, no open-by-handle, no linking them into the directory tree, and no feeding them to LSMs. As such, we mark them S_PRIVATE, which stops all that.
While we're at it, put them in a separate lockdep class so that it won't get confused by "recursive" i_rwsem locking such as what happens when we write to a rt file and need to allocate from the rt bitmap file. The static function that we use to do this will be exported in the rtgroups patchset.
--- fs/xfs/scrub/tempfile.c | 8 ++++++++ fs/xfs/xfs_iops.c | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-)
fs/xfs/scrub/tempfile.c +8 -0
@@ -844,6 +844,14 @@ xrep_is_tempfile( const struct xfs_inode *ip) {const struct inode *inode = &ip->i_vnode; +struct xfs_mount *mp = ip->i_mount; + +/* +* Files in the metadata directory tree also have S_PRIVATE set and +* IOP_XATTR unset, so we must distinguish them separately. +*/ +if (xfs_has_metadir (mp) && (ip->i_diflags2 &XFS_DIFLAG2_METADATA )) +return false;if (IS_PRIVATE (inode) && !(inode->i_opflags &IOP_XATTR ))return true;
fs/xfs/xfs_iops.c +14 -1
@@ -42,7 +42,9 @@* held. For regular files, the lock order is the other way around - the * mmap_lock is taken during the page fault, and then we lock the ilock to do * block mapping. Hence we need a different class for the directory ilock so -* that lockdep can tell them apart. +* that lockdep can tell them apart. Directories in the metadata directory +* tree get a separate class so that lockdep reports will warn us if someone +* ever tries to lock regular directories after locking metadata directories. */ static struct lock_class_key xfs_nondir_ilock_class;static struct lock_class_key xfs_dir_ilock_class;
@@ -1299,6 +1301,7 @@ xfs_setup_inode( {struct inode *inode = &ip->i_vnode;gfp_t gfp_mask; +bool is_meta =xfs_is_metadata_inode (ip); inode->i_ino = ip->i_ino; inode->i_state |=I_NEW ;
@@ -1310,6 +1313,16 @@ xfs_setup_inode(i_size_write (inode, ip->i_disk_size);xfs_diflags_to_iflags (ip, true); +/* +* Mark our metadata files as private so that LSMs and the ACL code +* don't try to add their own metadata or reason about these files, +* and users cannot ever obtain file handles to them. +*/ +if (is_meta) { + inode->i_flags |=S_PRIVATE ; + inode->i_opflags &= ~IOP_XATTR ; + } +if (S_ISDIR (inode->i_mode)) {/* * We set the i_rwsem class here to avoid potential races with
Re: [PATCH 08/26] xfs: hide metadata inodes from everyone because they are special
Looks good:
Re: [PATCH 08/26] xfs: hide metadata inodes from everyone because they are special
In reply to Darrick J. WongQuoted 6 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Metadata inodes are private files and therefore cannot be exposed to userspace. This means no bulkstat, no open-by-handle, no linking them into the directory tree, and no feeding them to LSMs. As such, we mark them
S_PRIVATE, which stops all that.
Can you merge this back up into the initial iget support code?
Quoted 6 lines
While we're at it, put them in a separate lockdep class so that it won't get confused by "recursive"
i_rwsemlocking such as what happens when we write to a rt file and need to allocate from the rt bitmap file. The static function that we use to do this will be exported in the rtgroups patchset.
Stale commit message? There's nothing of the sort in this patch....
Quoted 14 lines
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/tempfile.c | 8 ++++++++ fs/xfs/xfs_iops.c | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-)fs/xfs/scrub/tempfile.c +0
-0@@ -844,6 +844,14 @@ xrep_is_tempfile( const struct xfs_inode *ip)
fs/xfs/scrub/tempfile.c xrep_is_tempfile( · line 854
Quoted 10 lines
fs/xfs/scrub/tempfile.c +8
-0{const struct inode *inode = &ip->i_vnode; +struct xfs_mount *mp = ip->i_mount; + +/* +* Files in the metadata directory tree also have S_PRIVATE set and +* IOP_XATTR unset, so we must distinguish them separately. +*/ +if (xfs_has_metadir (mp) && (ip->i_diflags2 &XFS_DIFLAG2_METADATA )) +return false;
Why do you need to check both xfs_has_metadir() and the inode flag here? The latter should only be set if the former is set, yes? If it's the other way around, then we have an on-disk corruption...
Quoted 29 lines
fs/xfs/scrub/tempfile.c +0
-0if (IS_PRIVATE (inode) && !(inode->i_opflags &IOP_XATTR ))return true;fs/xfs/xfs_iops.c +4
-1@@ -42,7 +42,9 @@* held. For regular files, the lock order is the other way around - the * mmap_lock is taken during the page fault, and then we lock the ilock to do * block mapping. Hence we need a different class for the directory ilock so -* that lockdep can tell them apart. +* that lockdep can tell them apart. Directories in the metadata directory +* tree get a separate class so that lockdep reports will warn us if someone +* ever tries to lock regular directories after locking metadata directories. */ static struct lock_class_key xfs_nondir_ilock_class;static struct lock_class_key xfs_dir_ilock_class;@@ -1299,6 +1301,7 @@ xfs_setup_inode( {struct inode *inode = &ip->i_vnode;gfp_t gfp_mask; +bool is_meta =xfs_is_metadata_inode (ip); inode->i_ino = ip->i_ino; inode->i_state |=I_NEW ;@@ -1310,6 +1313,16 @@ xfs_setup_inode(i_size_write (inode, ip->i_disk_size);xfs_diflags_to_iflags (ip, true);
fs/xfs/xfs_iops.c xfs_setup_inode( · line 1324
Quoted 10 lines
fs/xfs/xfs_iops.c +9
-0+/* +* Mark our metadata files as private so that LSMs and the ACL code +* don't try to add their own metadata or reason about these files, +* and users cannot ever obtain file handles to them. +*/ +if (is_meta) { + inode->i_flags |=S_PRIVATE ; + inode->i_opflags &= ~IOP_XATTR ; + }
No need for a temporary variable here.
-Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 08/26] xfs: hide metadata inodes from everyone because they are special
Quoted 11 lines · Dave Chinner
Quoted 6 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Metadata inodes are private files and therefore cannot be exposed to userspace. This means no bulkstat, no open-by-handle, no linking them into the directory tree, and no feeding them to LSMs. As such, we mark them
S_PRIVATE, which stops all that.Can you merge this back up into the initial iget support code?
Quoted 1 line
Quoted 7 lines
Quoted 5 lines
While we're at it, put them in a separate lockdep class so that it won't get confused by "recursive"
i_rwsemlocking such as what happens when we write to a rt file and need to allocate from the rt bitmap file. The static function that we use to do this will be exported in the rtgroups patchset.Stale commit message? There's nothing of the sort in this patch....
Yeah, sorry. Previously there were separate lockdep classes for metadir directories and files each, but hch and I decided that each consumer of a metadata file should set its own class accordingly, and that the directories could continue using xfs_nondir_ilock_class as the only code that uses them is either mount time setup code or repair.
Quoted 20 lines
Quoted 20 lines
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/scrub/tempfile.c | 8 ++++++++ fs/xfs/xfs_iops.c | 15 ++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-)fs/xfs/scrub/tempfile.c +4
-0@@ -844,6 +844,14 @@ xrep_is_tempfile( const struct xfs_inode *ip) {const struct inode *inode = &ip->i_vnode; +struct xfs_mount *mp = ip->i_mount; + +/* +* Files in the metadata directory tree also have S_PRIVATE set and
Quoted 8 lines
fs/xfs/scrub/tempfile.c xrep_is_tempfile( · line 854
Quoted 4 lines
fs/xfs/scrub/tempfile.c +4
-0+* IOP_XATTR unset, so we must distinguish them separately. +*/ +if (xfs_has_metadir (mp) && (ip->i_diflags2 &XFS_DIFLAG2_METADATA )) +return false;Why do you need to check both
xfs_has_metadir()and the inode flag here? The latter should only be set if the former is set, yes? If it's the other way around, then we have an on-disk corruption...
Probably just stale code that's been sitting around for a while. But yes, this could all be:
if (xfs_is_metadir_inode(ip)) return false;
since the inode verifier should have already caught this.
Quoted 30 lines
Quoted 30 lines
fs/xfs/scrub/tempfile.c +0
-0if (IS_PRIVATE (inode) && !(inode->i_opflags &IOP_XATTR ))return true;fs/xfs/xfs_iops.c +4
-1@@ -42,7 +42,9 @@* held. For regular files, the lock order is the other way around - the * mmap_lock is taken during the page fault, and then we lock the ilock to do * block mapping. Hence we need a different class for the directory ilock so -* that lockdep can tell them apart. +* that lockdep can tell them apart. Directories in the metadata directory +* tree get a separate class so that lockdep reports will warn us if someone +* ever tries to lock regular directories after locking metadata directories. */ static struct lock_class_key xfs_nondir_ilock_class;static struct lock_class_key xfs_dir_ilock_class;@@ -1299,6 +1301,7 @@ xfs_setup_inode( {struct inode *inode = &ip->i_vnode;gfp_t gfp_mask; +bool is_meta =xfs_is_metadata_inode (ip); inode->i_ino = ip->i_ino; inode->i_state |=I_NEW ;@@ -1310,6 +1313,16 @@ xfs_setup_inode(i_size_write (inode, ip->i_disk_size);xfs_diflags_to_iflags (ip, true);
Quoted 11 lines
fs/xfs/xfs_iops.c xfs_setup_inode( · line 1324
Quoted 9 lines
fs/xfs/xfs_iops.c +9
-0+/* +* Mark our metadata files as private so that LSMs and the ACL code +* don't try to add their own metadata or reason about these files, +* and users cannot ever obtain file handles to them. +*/ +if (is_meta) { + inode->i_flags |=S_PRIVATE ; + inode->i_opflags &= ~IOP_XATTR ; + }No need for a temporary variable here.
<nod>
--D
Quoted 5 lines
-Dave.
Signature · 2 lines
Dave Chinner [email protected]
PATCH 9/26 xfs: advertise metadata directory feature
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Advertise the existence of the metadata directory feature; this will be used by scrub to decide if it needs to scan the metadir too.
--- fs/xfs/libxfs/xfs_fs.h | 2 ++ fs/xfs/libxfs/xfs_sb.c | 2 ++ 2 files changed, 4 insertions(+)
fs/xfs/libxfs/xfs_fs.h +2 -0
@@ -242,6 +242,8 @@ typedef struct xfs_fsop_resblks {#define XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE (1 << 24)/* exchange range */ #define XFS_FSOP_GEOM_FLAGS_PARENT (1 << 25)/* linux parent pointers */ +#define XFS_FSOP_GEOM_FLAGS_METADIR (1U << 30)/* metadata directories */ +/* * Minimum and maximum sizes need for growth checks. *
fs/xfs/libxfs/xfs_sb.c +2 -0
@@ -1279,6 +1279,8 @@ xfs_fs_geometry( geo->flags |=XFS_FSOP_GEOM_FLAGS_NREXT64 ;if (xfs_has_exchange_range (mp)) geo->flags |=XFS_FSOP_GEOM_FLAGS_EXCHANGE_RANGE ; +if (xfs_has_metadir (mp)) + geo->flags |=XFS_FSOP_GEOM_FLAGS_METADIR ; geo->rtsectsize = sbp->sb_blocksize; geo->dirblocksize =xfs_dir2_dirblock_bytes (sbp);
Re: [PATCH 09/26] xfs: advertise metadata directory feature
Looks good:
PATCH 10/26 xfs: allow bulkstat to return metadata directories
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Allow the V5 bulkstat ioctl to return information about metadata directory files so that xfs_scrub can find and scrub them, since they are otherwise ordinary directories.
(Metadata files of course require per-file scrub code and hence do not need exposure.)
--- fs/xfs/libxfs/xfs_fs.h | 10 +++++++++- fs/xfs/xfs_ioctl.c | 7 +++++++ fs/xfs/xfs_itable.c | 33 +++++++++++++++++++++++++++++---- fs/xfs/xfs_itable.h | 3 +++ 4 files changed, 48 insertions(+), 5 deletions(-)
fs/xfs/libxfs/xfs_fs.h +9 -1
@@ -490,9 +490,17 @@ struct xfs_bulk_ireq {*/ #define XFS_BULK_IREQ_NREXT64 (1U << 2) +/* +* Allow bulkstat to return information about metadata directories. This +* enables xfs_scrub to find them for scanning, as they are otherwise ordinary +* directories. +*/ +#define XFS_BULK_IREQ_METADIR (1U << 31) +#define XFS_BULK_IREQ_FLAGS_ALL (XFS_BULK_IREQ_AGNO | \ XFS_BULK_IREQ_SPECIAL | \ - XFS_BULK_IREQ_NREXT64) + XFS_BULK_IREQ_NREXT64 | \ + XFS_BULK_IREQ_METADIR)/* Operate on the root directory inode. */ #define XFS_BULK_IREQ_SPECIAL_ROOT (1)
fs/xfs/xfs_ioctl.c +7 -0
@@ -233,6 +233,10 @@ xfs_bulk_ireq_setup(if (hdr->flags &XFS_BULK_IREQ_NREXT64 ) breq->flags |=XFS_IBULK_NREXT64 ; +/* Caller wants to see metadata directories in bulkstat output. */ +if (hdr->flags &XFS_BULK_IREQ_METADIR ) + breq->flags |=XFS_IBULK_METADIR ; +return 0 ; }
@@ -323,6 +327,9 @@ xfs_ioc_inumbers(if (copy_from_user (&hdr, &arg->hdr,sizeof (hdr)))return -EFAULT ; +if (hdr.flags &XFS_BULK_IREQ_METADIR ) +return -EINVAL ; + error =xfs_bulk_ireq_setup (mp, &hdr, &breq, arg->inumbers);if (error == -ECANCELED ) goto out_teardown;
fs/xfs/xfs_itable.c +29 -4
@@ -36,6 +36,14 @@ struct xfs_bstat_chunk {struct xfs_bulkstat *buf; }; +static inline bool +want_metadir_file ( +struct xfs_inode *ip, +struct xfs_ibulk *breq) +{ +return xfs_is_metadir_inode (ip) && (breq->flags &XFS_IBULK_METADIR ); +} +/* * Fill out the bulkstat info for a single inode and report it somewhere. *
@@ -69,9 +77,6 @@ xfs_bulkstat_one_int(vfsuid_t vfsuid;vfsgid_t vfsgid; -if (xfs_internal_inum (mp, ino)) - goto out_advance; - error =xfs_iget (mp, tp, ino, (XFS_IGET_DONTCACHE |XFS_IGET_UNTRUSTED ),XFS_ILOCK_SHARED , &ip);
@@ -97,8 +102,28 @@ xfs_bulkstat_one_int( vfsuid =i_uid_into_vfsuid (idmap, inode); vfsgid =i_gid_into_vfsgid (idmap, inode); +/* +* If caller wants files from the metadata directories, push out the +* bare minimum information for enabling scrub. +*/ +if (want_metadir_file (ip, bc->breq)) { +memset (buf,0 ,sizeof (*buf)); + buf->bs_ino = ino; + buf->bs_gen = inode->i_generation; + buf->bs_mode = inode->i_mode &S_IFMT ; +xfs_bulkstat_health (ip, buf); + buf->bs_version =XFS_BULKSTAT_VERSION_V5 ; +xfs_iunlock (ip,XFS_ILOCK_SHARED ); +xfs_irele (ip); + + error = bc->formatter (bc->breq, buf); +if (!error || error == -ECANCELED ) + goto out_advance; + goto out; + } +/* If this is a private inode, don't leak its details to userspace. */ -if (IS_PRIVATE (inode)) { +if (IS_PRIVATE (inode) ||xfs_internal_inum (mp, ino)) {xfs_iunlock (ip,XFS_ILOCK_SHARED );xfs_irele (ip); error = -EINVAL ;
fs/xfs/xfs_itable.h +3 -0
@@ -22,6 +22,9 @@ struct xfs_ibulk {/* Fill out the bs_extents64 field if set. */ #define XFS_IBULK_NREXT64 (1U << 1) +/* Signal that we can return metadata directories. */ +#define XFS_IBULK_METADIR (1U << 2) +/* * Advance the user buffer pointer by one record of the given size. If the * buffer is now full, return the appropriate error code.
Re: [PATCH 10/26] xfs: allow bulkstat to return metadata directories
Looks good:
PATCH 11/26 xfs: don't count metadata directory files to quota
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Files in the metadata directory tree are internal to the filesystem. Don't count the inodes or the blocks they use in the root dquot because users do not need to know about their resource usage. This will also quiet down complaints about dquot usage not matching du output.
--- fs/xfs/xfs_dquot.c | 1 + fs/xfs/xfs_qm.c | 11 +++++++++++ fs/xfs/xfs_quota.h | 5 +++++ fs/xfs/xfs_trans_dquot.c | 6 ++++++ 4 files changed, 23 insertions(+)
fs/xfs/xfs_dquot.c +1 -0
@@ -983,6 +983,7 @@ xfs_qm_dqget_inode(xfs_assert_ilocked (ip,XFS_ILOCK_EXCL );ASSERT (xfs_inode_dquot (ip, type) ==NULL ); +ASSERT (!xfs_is_metadir_inode (ip)); id =xfs_qm_id_for_quotatype (ip, type);
fs/xfs/xfs_qm.c +11 -0
@@ -304,6 +304,8 @@ xfs_qm_need_dqattach(return false;if (xfs_is_quota_inode (&mp->m_sb, ip->i_ino))return false; +if (xfs_is_metadir_inode (ip)) +return false;return true; }
@@ -326,6 +328,7 @@ xfs_qm_dqattach_locked(return 0 ;xfs_assert_ilocked (ip,XFS_ILOCK_EXCL ); +ASSERT (!xfs_is_metadir_inode (ip));if (XFS_IS_UQUOTA_ON (mp) && !ip->i_udquot) { error = xfs_qm_dqattach_one(ip, XFS_DQTYPE_USER,
@@ -1204,6 +1207,10 @@ xfs_qm_dqusage_adjust( } } +/* Metadata directory files are not accounted to user-visible quotas. */ +if (xfs_is_metadir_inode (ip)) + goto error0; +ASSERT (ip->i_delayed_blks ==0 );if (XFS_IS_REALTIME_INODE (ip)) {
@@ -1754,6 +1761,8 @@ xfs_qm_vop_dqalloc(if (!XFS_IS_QUOTA_ON (mp))return 0 ; +ASSERT (!xfs_is_metadir_inode (ip)); + lockflags =XFS_ILOCK_EXCL ;xfs_ilock (ip, lockflags);
@@ -1883,6 +1892,7 @@ xfs_qm_vop_chown(xfs_assert_ilocked (ip,XFS_ILOCK_EXCL );ASSERT (XFS_IS_QUOTA_ON (ip->i_mount)); +ASSERT (!xfs_is_metadir_inode (ip));/* old dquot */ prevdq = *IO_olddq;
@@ -1970,6 +1980,7 @@ xfs_qm_vop_create_dqattach(return ;xfs_assert_ilocked (ip,XFS_ILOCK_EXCL ); +ASSERT (!xfs_is_metadir_inode (ip));if (udqp &&XFS_IS_UQUOTA_ON (mp)) {ASSERT (ip->i_udquot ==NULL );
fs/xfs/xfs_quota.h +5 -0
@@ -29,6 +29,11 @@ struct xfs_buf; (XFS_IS_GQUOTA_ON(mp) && (ip)->i_gdquot == NULL) || \ (XFS_IS_PQUOTA_ON(mp) && (ip)->i_pdquot == NULL)) +#define XFS_IS_DQDETACHED (ip) \ + ((ip)->i_udquot == NULL && \ + (ip)->i_gdquot == NULL && \ + (ip)->i_pdquot == NULL) +#define XFS_QM_NEED_QUOTACHECK (mp) \ ((XFS_IS_UQUOTA_ON(mp) && \ (mp->m_sb.sb_qflags & XFS_UQUOTA_CHKD) == 0) || \
fs/xfs/xfs_trans_dquot.c +6 -0
@@ -156,6 +156,8 @@ xfs_trans_mod_ino_dquot( unsigned int field, int64_t delta) { +ASSERT (!xfs_is_metadir_inode (ip) ||XFS_IS_DQDETACHED (ip)); +xfs_trans_mod_dquot (tp, dqp, field, delta);if (xfs_hooks_switched_on (&xfs_dqtrx_hooks_switch)) {
@@ -247,6 +249,8 @@ xfs_trans_mod_dquot_byino( xfs_is_quota_inode(&mp->m_sb, ip->i_ino)) return; +ASSERT (!xfs_is_metadir_inode (ip) ||XFS_IS_DQDETACHED (ip)); +if (XFS_IS_UQUOTA_ON (mp) && ip->i_udquot)xfs_trans_mod_ino_dquot (tp, ip, ip->i_udquot, field, delta);if (XFS_IS_GQUOTA_ON (mp) && ip->i_gdquot)
@@ -962,6 +966,8 @@ xfs_trans_reserve_quota_nblks(if (!XFS_IS_QUOTA_ON (mp))return 0 ; +if (xfs_is_metadir_inode (ip)) +return 0 ;ASSERT (!xfs_is_quota_inode (&mp->m_sb, ip->i_ino));xfs_assert_ilocked (ip,XFS_ILOCK_EXCL );
Re: [PATCH 11/26] xfs: don't count metadata directory files to quota
Looks good:
Re: [PATCH 11/26] xfs: don't count metadata directory files to quota
In reply to Darrick J. WongQuoted 50 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Files in the metadata directory tree are internal to the filesystem. Don't count the inodes or the blocks they use in the root dquot because users do not need to know about their resource usage. This will also quiet down complaints about dquot usage not matching du output.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/xfs_dquot.c | 1 + fs/xfs/xfs_qm.c | 11 +++++++++++ fs/xfs/xfs_quota.h | 5 +++++ fs/xfs/xfs_trans_dquot.c | 6 ++++++ 4 files changed, 23 insertions(+)fs/xfs/xfs_dquot.c +1
-0@@ -983,6 +983,7 @@ xfs_qm_dqget_inode(xfs_assert_ilocked (ip,XFS_ILOCK_EXCL );ASSERT (xfs_inode_dquot (ip, type) ==NULL ); +ASSERT (!xfs_is_metadir_inode (ip)); id =xfs_qm_id_for_quotatype (ip, type);fs/xfs/xfs_qm.c +3
-0@@ -304,6 +304,8 @@ xfs_qm_need_dqattach(return false;if (xfs_is_quota_inode (&mp->m_sb, ip->i_ino))return false; +if (xfs_is_metadir_inode (ip)) +return false;return true; }@@ -326,6 +328,7 @@ xfs_qm_dqattach_locked(return 0 ;xfs_assert_ilocked (ip,XFS_ILOCK_EXCL ); +ASSERT (!xfs_is_metadir_inode (ip));if (XFS_IS_UQUOTA_ON (mp) && !ip->i_udquot) { error = xfs_qm_dqattach_one(ip, XFS_DQTYPE_USER,
fs/xfs/xfs_qm.c xfs_qm_dqusage_adjust( · line 1213
Quoted 7 lines
fs/xfs/xfs_qm.c +4
-0@@ -1204,6 +1207,10 @@ xfs_qm_dqusage_adjust( } } +/* Metadata directory files are not accounted to user-visible quotas. */ +if (xfs_is_metadir_inode (ip)) + goto error0; +
Hmmmm. I'm starting to think that xfs_iget() should not return metadata inodes unless a new XFS_IGET_METAINODE flag is set.
That would replace all these post xfs_iget() checks with a single check in xfs_iget(), and then xfs_trans_metafile_iget() is the only place that sets this specific flag.
That means stuff like VFS lookups, bulkstat, quotacheck, and filehandle lookups will never return metadata inodes and we don't need to add special checks all over for them...
-Dave.
Signature · 2 lines
Dave Chinner [email protected]
Re: [PATCH 11/26] xfs: don't count metadata directory files to quota
Quoted 62 lines · Dave Chinner
Quoted 50 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Files in the metadata directory tree are internal to the filesystem. Don't count the inodes or the blocks they use in the root dquot because users do not need to know about their resource usage. This will also quiet down complaints about dquot usage not matching du output.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/xfs_dquot.c | 1 + fs/xfs/xfs_qm.c | 11 +++++++++++ fs/xfs/xfs_quota.h | 5 +++++ fs/xfs/xfs_trans_dquot.c | 6 ++++++ 4 files changed, 23 insertions(+)fs/xfs/xfs_dquot.c +1
-0@@ -983,6 +983,7 @@ xfs_qm_dqget_inode(xfs_assert_ilocked (ip,XFS_ILOCK_EXCL );ASSERT (xfs_inode_dquot (ip, type) ==NULL ); +ASSERT (!xfs_is_metadir_inode (ip)); id =xfs_qm_id_for_quotatype (ip, type);fs/xfs/xfs_qm.c +3
-0@@ -304,6 +304,8 @@ xfs_qm_need_dqattach(return false;if (xfs_is_quota_inode (&mp->m_sb, ip->i_ino))return false; +if (xfs_is_metadir_inode (ip)) +return false;return true; }@@ -326,6 +328,7 @@ xfs_qm_dqattach_locked(return 0 ;xfs_assert_ilocked (ip,XFS_ILOCK_EXCL ); +ASSERT (!xfs_is_metadir_inode (ip));if (XFS_IS_UQUOTA_ON (mp) && !ip->i_udquot) { error = xfs_qm_dqattach_one(ip, XFS_DQTYPE_USER,fs/xfs/xfs_qm.c xfs_qm_dqusage_adjust( · line 1213
Quoted 7 lines
fs/xfs/xfs_qm.c +4
-0@@ -1204,6 +1207,10 @@ xfs_qm_dqusage_adjust( } } +/* Metadata directory files are not accounted to user-visible quotas. */ +if (xfs_is_metadir_inode (ip)) + goto error0; +Hmmmm. I'm starting to think that
xfs_iget()should not return metadata inodes unless a newXFS_IGET_METAINODEflag is set.
Quoted 7 lines
That would replace all these post
xfs_iget()checks with a single check inxfs_iget(), and thenxfs_trans_metafile_iget()is the only place that sets this specific flag.That means stuff like VFS lookups, bulkstat, quotacheck, and filehandle lookups will never return metadata inodes and we don't need to add special checks all over for them...
I think doing so is likely an overall improvement for the codebase, but it will complicate life for the directory and parent pointer scrubbers because they can be called with the ino/gen of files in metadata directory tree, and there's a special bulkstat mode for xfs_scrub wherein one can get the ino/gen of metadata directory tree files. Anyway I'll think further about this as a separate cleanup.
Note that the checks in this particular patch exist so that we don't have to sprinkle them all over the bmap code so that the rtbitmap and summary files can be excluded from quota accounting.
--D
Quoted 6 lines
-Dave.
Signature · 2 lines
Dave Chinner [email protected]
PATCH 12/26 xfs: mark quota inodes as metadata files
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
When we're creating quota files at mount time, make sure to mark them as metadir inodes if appropriate.
--- fs/xfs/xfs_qm.c | 2 ++ 1 file changed, 2 insertions(+)
fs/xfs/xfs_qm.c +2 -0
@@ -824,6 +824,8 @@ xfs_qm_qino_alloc(xfs_trans_cancel (tp);return error; } +if (xfs_has_metadir (mp)) +xfs_metafile_set_iflag (tp, *ipp, metafile_type); }/*
Re: [PATCH 12/26] xfs: mark quota inodes as metadata files
Looks good:
PATCH 13/26 xfs: adjust xfs_bmap_add_attrfork for metadir
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Online repair might use the xfs_bmap_add_attrfork to repair a file in the metadata directory tree if (say) the metadata file lacks the correct parent pointers. In that case, it is not correct to check that the file is dqattached -- metadata files must be not have /any/ dquot attached at all.
--- fs/xfs/libxfs/xfs_attr.c | 5 ++++- fs/xfs/libxfs/xfs_bmap.c | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-)
fs/xfs/libxfs/xfs_attr.c +4 -1
@@ -953,7 +953,10 @@ xfs_attr_add_fork(unsigned int blks;/* space reservation */ int error;/* error return value */ -ASSERT (!XFS_NOT_DQATTACHED (mp, ip)); +if (xfs_is_metadir_inode (ip)) +ASSERT (XFS_IS_DQDETACHED (ip)); +else +ASSERT (!XFS_NOT_DQATTACHED (mp, ip)); blks =XFS_ADDAFORK_SPACE_RES (mp);
fs/xfs/libxfs/xfs_bmap.c +4 -1
@@ -1042,7 +1042,10 @@ xfs_bmap_add_attrfork(int error;/* error return value */ xfs_assert_ilocked (ip,XFS_ILOCK_EXCL ); -ASSERT (!XFS_NOT_DQATTACHED (mp, ip)); +if (xfs_is_metadir_inode (ip)) +ASSERT (XFS_IS_DQDETACHED (ip)); +else +ASSERT (!XFS_NOT_DQATTACHED (mp, ip));ASSERT (!xfs_inode_has_attr_fork (ip));xfs_trans_log_inode (tp, ip,XFS_ILOG_CORE );
Re: [PATCH 13/26] xfs: adjust xfs_bmap_add_attrfork for metadir
Quoted 7 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Online repair might use the
xfs_bmap_add_attrforkto repair a file in the metadata directory tree if (say) the metadata file lacks the correct parent pointers. In that case, it is not correct to check that the file is dqattached -- metadata files must be not have /any/ dquot attached at all.
Looks good:
PATCH 14/26 xfs: record health problems with the metadata directory
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Make a report to the health monitoring subsystem any time we encounter something in the metadata directory tree that looks like corruption.
--- fs/xfs/libxfs/xfs_fs.h | 1 + fs/xfs/libxfs/xfs_health.h | 4 +++- fs/xfs/libxfs/xfs_metadir.c | 13 ++++++++++--- fs/xfs/xfs_health.c | 1 + fs/xfs/xfs_icache.c | 1 + fs/xfs/xfs_inode.c | 1 + 6 files changed, 17 insertions(+), 4 deletions(-)
fs/xfs/libxfs/xfs_fs.h +1 -0
@@ -197,6 +197,7 @@ struct xfs_fsop_geom {#define XFS_FSOP_GEOM_SICK_RT_SUMMARY (1 << 5)/* realtime summary */ #define XFS_FSOP_GEOM_SICK_QUOTACHECK (1 << 6)/* quota counts */ #define XFS_FSOP_GEOM_SICK_NLINKS (1 << 7)/* inode link counts */ +#define XFS_FSOP_GEOM_SICK_METADIR (1 << 8)/* metadata directory */ /* Output for XFS_FS_COUNTS */ typedef struct xfs_fsop_counts {
fs/xfs/libxfs/xfs_health.h +3 -1
@@ -60,6 +60,7 @@ struct xfs_da_args;#define XFS_SICK_FS_PQUOTA (1 << 3)/* project quota */ #define XFS_SICK_FS_QUOTACHECK (1 << 4)/* quota counts */ #define XFS_SICK_FS_NLINKS (1 << 5)/* inode link counts */ +#define XFS_SICK_FS_METADIR (1 << 6)/* metadata directory tree */ /* Observable health issues for realtime volume metadata. */ #define XFS_SICK_RT_BITMAP (1 << 0)/* realtime bitmap */
@@ -103,7 +104,8 @@ struct xfs_da_args;XFS_SICK_FS_GQUOTA | \XFS_SICK_FS_PQUOTA | \XFS_SICK_FS_QUOTACHECK | \ - XFS_SICK_FS_NLINKS) +XFS_SICK_FS_NLINKS | \ + XFS_SICK_FS_METADIR) #define XFS_SICK_RT_PRIMARY (XFS_SICK_RT_BITMAP | \XFS_SICK_RT_SUMMARY )
fs/xfs/libxfs/xfs_metadir.c +10 -3
@@ -28,6 +28,7 @@#include "xfs_dir2.h" #include "xfs_dir2_priv.h" #include "xfs_parent.h" +#include "xfs_health.h" /* * Metadata Directory Tree
@@ -94,8 +95,10 @@ xfs_metadir_lookup( };int error; -if (!S_ISDIR (VFS_I (dp)->i_mode)) +if (!S_ISDIR (VFS_I (dp)->i_mode)) { +xfs_fs_mark_sick (mp,XFS_SICK_FS_METADIR );return -EFSCORRUPTED ; + }if (xfs_is_shutdown (mp))return -EIO ;
@@ -103,10 +106,14 @@ xfs_metadir_lookup(if (error)return error; -if (!xfs_verify_ino (mp, args.inumber)) +if (!xfs_verify_ino (mp, args.inumber)) { +xfs_fs_mark_sick (mp,XFS_SICK_FS_METADIR );return -EFSCORRUPTED ; -if (xname->type !=XFS_DIR3_FT_UNKNOWN && xname->type != args.filetype) + } +if (xname->type !=XFS_DIR3_FT_UNKNOWN && xname->type != args.filetype) { +xfs_fs_mark_sick (mp,XFS_SICK_FS_METADIR );return -EFSCORRUPTED ; + }trace_xfs_metadir_lookup (dp, xname, args.inumber); *ino = args.inumber;
fs/xfs/xfs_health.c +1 -0
@@ -376,6 +376,7 @@ static const struct ioctl_sick_map fs_map[] = { { XFS_SICK_FS_PQUOTA, XFS_FSOP_GEOM_SICK_PQUOTA }, { XFS_SICK_FS_QUOTACHECK, XFS_FSOP_GEOM_SICK_QUOTACHECK }, { XFS_SICK_FS_NLINKS, XFS_FSOP_GEOM_SICK_NLINKS }, + { XFS_SICK_FS_METADIR, XFS_FSOP_GEOM_SICK_METADIR }, { 0, 0 }, };
fs/xfs/xfs_icache.c +1 -0
@@ -858,6 +858,7 @@ xfs_trans_metafile_iget( whine:xfs_err (mp,"metadata inode 0x%llx type %u is corrupt" , ino, metafile_type); +xfs_fs_mark_sick (mp,XFS_SICK_FS_METADIR );return -EFSCORRUPTED ; }
fs/xfs/xfs_inode.c +1 -0
@@ -560,6 +560,7 @@ xfs_lookup(* a metadata file. */ if (XFS_IS_CORRUPT (dp->i_mount,xfs_is_metadir_inode (*ipp))) { +xfs_fs_mark_sick (dp->i_mount,XFS_SICK_FS_METADIR ); error = -EFSCORRUPTED ; goto out_irele; }
Re: [PATCH 14/26] xfs: record health problems with the metadata directory
Looks good:
PATCH 15/26 xfs: refactor directory tree root predicates
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Metadata directory trees make reasoning about the parent of a file more difficult. Traditionally, user files are children of sb_rootino, and metadata files are "children" of the superblock. Now, we add a third possibility -- some metadata files can be children of sb_metadirino, but the classic ones (rt free space data and quotas) are left alone.
Let's add some helper functions (instead of open-coding the logic everywhere) to make scrub logic easier to understand.
--- fs/xfs/scrub/common.c | 29 +++++++++++++++++++++++++++++ fs/xfs/scrub/common.h | 4 ++++ fs/xfs/scrub/dir.c | 2 +- fs/xfs/scrub/dir_repair.c | 2 +- fs/xfs/scrub/dirtree.c | 15 ++++++++++++++- fs/xfs/scrub/dirtree.h | 12 +----------- fs/xfs/scrub/findparent.c | 15 +++++++++------ fs/xfs/scrub/inode_repair.c | 11 ++--------- fs/xfs/scrub/nlinks.c | 4 ++-- fs/xfs/scrub/nlinks_repair.c | 4 +--- fs/xfs/scrub/orphanage.c | 4 +++- fs/xfs/scrub/parent.c | 17 ++++++++--------- fs/xfs/scrub/parent_repair.c | 2 +- 13 files changed, 76 insertions(+), 45 deletions(-)
fs/xfs/scrub/common.c +29 -0
@@ -1452,3 +1452,32 @@ xchk_inode_is_allocated(rcu_read_unlock ();return error; } + +/* Is this inode a root directory for either tree? */ +bool +xchk_inode_is_dirtree_root (const struct xfs_inode *ip) +{ +struct xfs_mount *mp = ip->i_mount; + +return ip == mp->m_rootip || + (xfs_has_metadir (mp) && ip == mp->m_metadirip); +} + +/* Does the superblock point down to this inode? */ +bool +xchk_inode_is_sb_rooted (const struct xfs_inode *ip) +{ +return xchk_inode_is_dirtree_root (ip) || +xfs_internal_inum (ip->i_mount, ip->i_ino); +} + +/* What is the root directory inumber for this inode? */ +xfs_ino_t +xchk_inode_rootdir_inum (const struct xfs_inode *ip) +{ +struct xfs_mount *mp = ip->i_mount; + +if (xfs_is_metadir_inode (ip)) +return mp->m_metadirip->i_ino; +return mp->m_rootip->i_ino; +}
fs/xfs/scrub/common.h +4 -0
@@ -252,4 +252,8 @@ void xchk_fsgates_enable(struct xfs_scrub *sc, unsigned int scrub_fshooks);int xchk_inode_is_allocated (struct xfs_scrub *sc,xfs_agino_t agino,bool *inuse); +bool xchk_inode_is_dirtree_root (const struct xfs_inode *ip); +bool xchk_inode_is_sb_rooted (const struct xfs_inode *ip); +xfs_ino_t xchk_inode_rootdir_inum (const struct xfs_inode *ip); +#endif /* __XFS_SCRUB_COMMON_H__ */
fs/xfs/scrub/dir.c +1 -1
@@ -253,7 +253,7 @@ xchk_dir_actor(* If this is ".." in the root inode, check that the inum * matches this dir. */ -if (dp->i_ino == mp->m_sb.sb_rootino && ino != dp->i_ino) +if (xchk_inode_is_dirtree_root (dp) && ino != dp->i_ino)xchk_fblock_set_corrupt (sc,XFS_DATA_FORK , offset); }
fs/xfs/scrub/dir_repair.c +1 -1
@@ -1270,7 +1270,7 @@ xrep_dir_scan_dirtree(int error;/* Roots of directory trees are their own parents. */ -if (sc->ip == sc->mp->m_rootip) +if (xchk_inode_is_dirtree_root (sc->ip))xrep_findparent_scan_found (&rd->pscan, sc->ip->i_ino);/*
fs/xfs/scrub/dirtree.c +14 -1
@@ -917,7 +917,7 @@ xchk_dirtree(* scan, because the hook doesn't detach until after sc->ip gets * released during teardown. */ - dl->root_ino = sc->mp->m_rootip->i_ino; + dl->root_ino =xchk_inode_rootdir_inum (sc->ip); dl->scan_ino = sc->ip->i_ino;trace_xchk_dirtree_start (sc->ip, sc->sm,0 );
@@ -983,3 +983,16 @@ xchk_dirtree(trace_xchk_dirtree_done (sc->ip, sc->sm, error);return error; } + +/* Does the directory targetted by this scrub have no parents? */ +bool +xchk_dirtree_parentless (const struct xchk_dirtree *dl) +{ +struct xfs_scrub *sc = dl->sc; + +if (xchk_inode_is_dirtree_root (sc->ip)) +return true; +if (VFS_I (sc->ip)->i_nlink ==0 ) +return true; +return false; +}
fs/xfs/scrub/dirtree.h +1 -11
@@ -156,17 +156,7 @@ struct xchk_dirtree {#define xchk_dirtree_for_each_path (dl, path) \ list_for_each_entry((path), &(dl)->path_list, list) -static inline bool -xchk_dirtree_parentless (const struct xchk_dirtree *dl) -{ -struct xfs_scrub *sc = dl->sc; - -if (sc->ip == sc->mp->m_rootip) -return true; -if (VFS_I (sc->ip)->i_nlink ==0 ) -return true; -return false; -} +bool xchk_dirtree_parentless (const struct xchk_dirtree *dl);int xchk_dirtree_find_paths_to_root (struct xchk_dirtree *dl); int xchk_dirpath_append(struct xchk_dirtree *dl, struct xfs_inode *ip,
fs/xfs/scrub/findparent.c +9 -6
@@ -362,15 +362,18 @@ xrep_findparent_confirm( };int error; -/* -* The root directory always points to itself. Unlinked dirs can point -* anywhere, so we point them at the root dir too. -*/ -if (sc->ip == sc->mp->m_rootip ||VFS_I (sc->ip)->i_nlink ==0 ) { +/* The root directory always points to itself. */ +if (sc->ip == sc->mp->m_rootip) { *parent_ino = sc->mp->m_sb.sb_rootino;return 0 ; } +/* Unlinked dirs can point anywhere; point them up to the root dir. */ +if (VFS_I (sc->ip)->i_nlink ==0 ) { + *parent_ino =xchk_inode_rootdir_inum (sc->ip); +return 0 ; + } +/* Reject garbage parent inode numbers and self-referential parents. */ if (*parent_ino ==NULLFSINO )return 0 ;
@@ -413,7 +416,7 @@ xrep_findparent_self_reference(return sc->mp->m_sb.sb_rootino;if (VFS_I (sc->ip)->i_nlink ==0 ) -return sc->mp->m_sb.sb_rootino; +return xchk_inode_rootdir_inum (sc->ip);return NULLFSINO ; }
fs/xfs/scrub/inode_repair.c +2 -9
@@ -1768,15 +1768,8 @@ xrep_inode_pptr(if (inode->i_nlink ==0 && !(inode->i_state &I_LINKABLE ))return 0 ; -/* The root directory doesn't have a parent pointer. */ -if (ip == mp->m_rootip) -return 0 ; - -/* -* Metadata inodes are rooted in the superblock and do not have any -* parents. -*/ -if (xfs_is_metadata_inode (ip)) +/* Children of the superblock do not have parent pointers. */ +if (xchk_inode_is_sb_rooted (ip))return 0 ;/* Inode already has an attr fork; no further work possible here. */
fs/xfs/scrub/nlinks.c +2 -2
@@ -279,7 +279,7 @@ xchk_nlinks_collect_dirent(* determine the backref count. */ if (dotdot) { -if (dp == sc->mp->m_rootip) +if (xchk_inode_is_dirtree_root (dp)) error =xchk_nlinks_update_incore (xnc, ino,1 ,0 ,0 );else if (!xfs_has_parent (sc->mp)) error =xchk_nlinks_update_incore (xnc, ino,0 ,1 ,0 );
@@ -735,7 +735,7 @@ xchk_nlinks_compare_inode( } } -if (ip == sc->mp->m_rootip) { +if (xchk_inode_is_dirtree_root (ip)) {/* * For the root of a directory tree, both the '.' and '..' * entries should point to the root directory. The dotdot
fs/xfs/scrub/nlinks_repair.c +1 -3
@@ -60,11 +60,9 @@ xrep_nlinks_is_orphaned( unsigned int actual_nlink, const struct xchk_nlink *obs) { - struct xfs_mount *mp = ip->i_mount; -if (obs->parents !=0 )return false; -if (ip == mp->m_rootip || ip == sc->orphanage) +if (xchk_inode_is_dirtree_root (ip) || ip == sc->orphanage)return false;return actual_nlink !=0 ; }
fs/xfs/scrub/orphanage.c +3 -1
@@ -295,7 +295,9 @@ xrep_orphanage_can_adopt(return false;if (sc->ip == sc->orphanage)return false; -if (xfs_internal_inum (sc->mp, sc->ip->i_ino)) +if (xchk_inode_is_sb_rooted (sc->ip)) +return false; +if (xfs_is_metadata_inode (sc->ip))return false;return true; }
fs/xfs/scrub/parent.c +8 -9
@@ -300,7 +300,7 @@ xchk_parent_pptr_and_dotdot( }/* Is this the root dir? Then '..' must point to itself. */ -if (sc->ip == sc->mp->m_rootip) { +if (xchk_inode_is_dirtree_root (sc->ip)) {if (sc->ip->i_ino != pp->parent_ino)xchk_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 );return 0 ;
@@ -711,7 +711,7 @@ xchk_parent_count_pptrs( }if (S_ISDIR (VFS_I (sc->ip)->i_mode)) { -if (sc->ip == sc->mp->m_rootip) +if (xchk_inode_is_dirtree_root (sc->ip)) pp->pptrs_found++;if (VFS_I (sc->ip)->i_nlink ==0 && pp->pptrs_found >0 )
@@ -885,10 +885,9 @@ boolxchk_pptr_looks_zapped ( struct xfs_inode *ip) { -struct xfs_mount *mp = ip->i_mount;struct inode *inode =VFS_I (ip); -ASSERT (xfs_has_parent (mp)); +ASSERT (xfs_has_parent (ip->i_mount));/* * Temporary files that cannot be linked into the directory tree do not
@@ -902,15 +901,15 @@ xchk_pptr_looks_zapped(* of a parent pointer scan is always the empty set. It's safe to scan * them even if the attr fork was zapped. */ -if (ip == mp->m_rootip) +if (xchk_inode_is_dirtree_root (ip))return false;/* -* Metadata inodes are all rooted in the superblock and do not have -* any parents. Hence the attr fork will not be initialized, but -* there are no parent pointers that might have been zapped. +* Metadata inodes that are rooted in the superblock do not have any +* parents. Hence the attr fork will not be initialized, but there are +* no parent pointers that might have been zapped. */ -if (xfs_is_metadata_inode (ip)) +if (xchk_inode_is_sb_rooted (ip))return false;/*
fs/xfs/scrub/parent_repair.c +1 -1
@@ -1334,7 +1334,7 @@ xrep_parent_rebuild_pptrs(* so that we can decide if we're moving this file to the orphanage. * For this purpose, root directories are their own parents. */ -if (sc->ip == sc->mp->m_rootip) { +if (xchk_inode_is_dirtree_root (sc->ip)) {xrep_findparent_scan_found (&rp->pscan, sc->ip->i_ino); }else { error =xrep_parent_lookup_pptrs (sc, &parent_ino);
Re: [PATCH 15/26] xfs: refactor directory tree root predicates
Looks good:
PATCH 16/26 xfs: do not count metadata directory files when doing online quotacheck
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Previously, we stated that files in the metadata directory tree are not counted in the dquot information. Fix the online quotacheck code to reflect this.
--- fs/xfs/scrub/quotacheck.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
fs/xfs/scrub/quotacheck.c +5 -2
@@ -398,10 +398,13 @@ xqcheck_collect_inode(bool isreg =S_ISREG (VFS_I (ip)->i_mode);int error =0 ; -if (xfs_is_quota_inode (&tp->t_mountp->m_sb, ip->i_ino)) { +if (xfs_is_metadir_inode (ip) || +xfs_is_quota_inode (&tp->t_mountp->m_sb, ip->i_ino)) {/* * Quota files are never counted towards quota, so we do not -* need to take the lock. +* need to take the lock. Files do not switch between the +* metadata and regular directory trees without a reallocation, +* so we do not need to ILOCK them either. */ xchk_iscan_mark_visited (&xqc->iscan, ip);return 0 ;
Re: [PATCH 16/26] xfs: do not count metadata directory files when doing online quotacheck
Looks good:
PATCH 17/26 xfs: don't fail repairs on metadata files with no attr fork
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Fix a minor bug where we fail repairs on metadata files that do not have attr forks because xrep_metadata_inode_subtype doesn't filter ENOENT.
--- fs/xfs/scrub/repair.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
fs/xfs/scrub/repair.c +5 -3
@@ -1084,9 +1084,11 @@ xrep_metadata_inode_forks(return error;/* Make sure the attr fork looks ok before we delete it. */ - error =xrep_metadata_inode_subtype (sc,XFS_SCRUB_TYPE_BMBTA ); -if (error) -return error; +if (xfs_inode_hasattr (sc->ip)) { + error =xrep_metadata_inode_subtype (sc,XFS_SCRUB_TYPE_BMBTA ); +if (error) +return error; + }/* Clear the reflink flag since metadata never shares. */ if (xfs_is_reflink_inode (sc->ip)) {
Re: [PATCH 17/26] xfs: don't fail repairs on metadata files with no attr fork
Looks good:
PATCH 18/26 xfs: metadata files can have xattrs if metadir is enabled
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
If metadata directory trees are enabled, it's possible that some future metadata file might want to store information in extended attributes. Or, if parent pointers are enabled, then children of the metadir tree need parent pointers. Either way, we start allowing xattr data when metadir is enabled, so we now need check and repair to examine attr forks for metadata files on metadir filesystems.
--- fs/xfs/scrub/common.c | 21 +++++++++++++++------ fs/xfs/scrub/repair.c | 14 +++++++++++--- 2 files changed, 26 insertions(+), 9 deletions(-)
fs/xfs/scrub/common.c +15 -6
@@ -1245,12 +1245,6 @@ xchk_metadata_inode_forks(return 0 ; } -/* They also should never have extended attributes. */ -if (xfs_inode_hasattr (sc->ip)) { -xchk_ino_set_corrupt (sc, sc->ip->i_ino); -return 0 ; - } -/* Invoke the data fork scrubber. */ error =xchk_metadata_inode_subtype (sc,XFS_SCRUB_TYPE_BMBTD );if (error || (sc->sm->sm_flags &XFS_SCRUB_OFLAG_CORRUPT ))
@@ -1267,6 +1261,21 @@ xchk_metadata_inode_forks(xchk_ino_set_corrupt (sc, sc->ip->i_ino); } +/* +* Metadata files can only have extended attributes on metadir +* filesystems, either for parent pointers or for actual xattr data. +*/ +if (xfs_inode_hasattr (sc->ip)) { +if (!xfs_has_metadir (sc->mp)) { +xchk_ino_set_corrupt (sc, sc->ip->i_ino); +return 0 ; + } + + error =xchk_metadata_inode_subtype (sc,XFS_SCRUB_TYPE_BMBTA ); +if (error || (sc->sm->sm_flags &XFS_SCRUB_OFLAG_CORRUPT )) +return error; + } +return 0 ; }
fs/xfs/scrub/repair.c +11 -3
@@ -1083,7 +1083,12 @@ xrep_metadata_inode_forks(if (error)return error; -/* Make sure the attr fork looks ok before we delete it. */ +/* +* Metadata files can only have extended attributes on metadir +* filesystems, either for parent pointers or for actual xattr data. +* For a non-metadir filesystem, make sure the attr fork looks ok +* before we delete it. +*/ if (xfs_inode_hasattr (sc->ip)) { error =xrep_metadata_inode_subtype (sc,XFS_SCRUB_TYPE_BMBTA );if (error)
@@ -1099,8 +1104,11 @@ xrep_metadata_inode_forks(return error; } -/* Clear the attr forks since metadata shouldn't have that. */ -if (xfs_inode_hasattr (sc->ip)) { +/* +* Metadata files on non-metadir filesystems cannot have attr forks, +* so clear them now. +*/ +if (xfs_inode_hasattr (sc->ip) && !xfs_has_metadir (sc->mp)) {if (!dirty) { dirty = true;xfs_trans_ijoin (sc->tp, sc->ip,0 );
Re: [PATCH 18/26] xfs: metadata files can have xattrs if metadir is enabled
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
If metadata directory trees are enabled, it's possible that some future metadata file might want to store information in extended attributes. Or, if parent pointers are enabled, then children of the metadir tree need parent pointers. Either way, we start allowing xattr data when metadir is enabled, so we now need check and repair to examine attr forks for metadata files on metadir filesystems.
I think the parent pointer case is the relevant here, so maybe state that more clearly?
Otherwise looks good:
Re: [PATCH 18/26] xfs: metadata files can have xattrs if metadir is enabled
Quoted 10 lines · Christoph Hellwig
Quoted 8 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
If metadata directory trees are enabled, it's possible that some future metadata file might want to store information in extended attributes. Or, if parent pointers are enabled, then children of the metadir tree need parent pointers. Either way, we start allowing xattr data when metadir is enabled, so we now need check and repair to examine attr forks for metadata files on metadir filesystems.
Quoted 2 lines
I think the parent pointer case is the relevant here, so maybe state that more clearly?
I'll change this to:
"If parent pointers are enabled, then metadata files will store parent pointers in xattrs, just like files in the user visible directory tree. Therefore, scrub and repair need to handle attr forks for metadata files on metadir filesystems."
Quoted 3 lines
Otherwise looks good:
Reviewed-by Christoph Hellwig <[email protected]>
Thanks!
--D
PATCH 19/26 xfs: adjust parent pointer scrubber for sb-rooted metadata files
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Starting with the metadata directory feature, we're allowed to call the directory and parent pointer scrubbers for every metadata file, including the ones that are children of the superblock.
For these children, checking the link count against the number of parent pointers is a bit funny -- there's no such thing as a parent pointer for a child of the superblock since there's no corresponding dirent. For purposes of validating nlink, we pretend that there is a parent pointer.
--- fs/xfs/scrub/parent.c | 8 ++++++++ fs/xfs/scrub/parent_repair.c | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 39 insertions(+), 4 deletions(-)
fs/xfs/scrub/parent.c +8 -0
@@ -720,6 +720,14 @@ xchk_parent_count_pptrs( pp->pptrs_found == 0)xchk_ino_set_corrupt (sc, sc->ip->i_ino); }else { +/* +* Starting with metadir, we allow checking of parent pointers +* of non-directory files that are children of the superblock. +* Pretend that we found a parent pointer attr. +*/ +if (xfs_has_metadir (sc->mp) &&xchk_inode_is_sb_rooted (sc->ip)) + pp->pptrs_found++; +if (VFS_I (sc->ip)->i_nlink != pp->pptrs_found)xchk_ino_set_corrupt (sc, sc->ip->i_ino); }
fs/xfs/scrub/parent_repair.c +31 -4
@@ -1354,21 +1354,40 @@ STATIC intxrep_parent_rebuild_tree ( struct xrep_parent *rp) { +struct xfs_scrub *sc = rp->sc; +bool try_adoption;int error; -if (xfs_has_parent (rp->sc->mp)) { +if (xfs_has_parent (sc->mp)) { error =xrep_parent_rebuild_pptrs (rp);if (error)return error; } -if (rp->pscan.parent_ino ==NULLFSINO ) { -if (xrep_orphanage_can_adopt (rp->sc)) +/* +* Any file with no parent could be adopted. This check happens after +* rebuilding the parent pointer structure because we might have cycled +* the ILOCK during that process. +*/ + try_adoption = rp->pscan.parent_ino ==NULLFSINO ; + +/* +* Starting with metadir, we allow checking of parent pointers +* of non-directory files that are children of the superblock. +* Lack of parent is ok here. +*/ +if (try_adoption &&xfs_has_metadir (sc->mp) && +xchk_inode_is_sb_rooted (sc->ip)) + try_adoption = false; + +if (try_adoption) { +if (xrep_orphanage_can_adopt (sc))return xrep_parent_move_to_orphanage (rp);return -EFSCORRUPTED ; + } -if (S_ISDIR (VFS_I (rp->sc->ip)->i_mode)) +if (S_ISDIR (VFS_I (sc->ip)->i_mode))return xrep_parent_reset_dotdot (rp);return 0 ;
@@ -1422,6 +1441,14 @@ xrep_parent_set_nondir_nlink(if (error)return error; +/* +* Starting with metadir, we allow checking of parent pointers of +* non-directory files that are children of the superblock. Pretend +* that we found a parent pointer attr. +*/ +if (xfs_has_metadir (sc->mp) &&xchk_inode_is_sb_rooted (sc->ip)) + rp->parents++; +if (rp->parents >0 &&xfs_inode_on_unlinked_list (ip)) {xfs_trans_ijoin (sc->tp, sc->ip,0 ); joined = true;
Re: [PATCH 19/26] xfs: adjust parent pointer scrubber for sb-rooted metadata files
Looks good:
PATCH 20/26 xfs: fix di_metatype field of inodes that won't load
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Make sure that the di_metatype field is at least set plausibly so that later scrubbers could set the real type.
--- fs/xfs/scrub/inode.c | 10 +++++++--- fs/xfs/scrub/inode_repair.c | 6 +++++- 2 files changed, 12 insertions(+), 4 deletions(-)
fs/xfs/scrub/inode.c +7 -3
@@ -443,9 +443,13 @@ xchk_dinode(break ;case 2 :case 3 : -if (!(dip->di_flags2 &cpu_to_be64 (XFS_DIFLAG2_METADATA )) && - dip->di_onlink !=0 ) -xchk_ino_set_corrupt (sc, ino); +if (dip->di_flags2 &cpu_to_be64 (XFS_DIFLAG2_METADATA )) { +if (be16_to_cpu (dip->di_metatype) >=XFS_METAFILE_MAX ) +xchk_ino_set_corrupt (sc, ino); + }else { +if (dip->di_onlink !=0 ) +xchk_ino_set_corrupt (sc, ino); + }if (dip->di_mode ==0 && sc->ip)xchk_ino_set_corrupt (sc, ino);
fs/xfs/scrub/inode_repair.c +5 -1
@@ -526,8 +526,12 @@ xrep_dinode_nlinks(return ; } -if (!(dip->di_flags2 &cpu_to_be64 (XFS_DIFLAG2_METADATA ))) +if (dip->di_flags2 &cpu_to_be64 (XFS_DIFLAG2_METADATA )) { +if (be16_to_cpu (dip->di_metatype) >=XFS_METAFILE_MAX ) + dip->di_metatype =cpu_to_be16 (XFS_METAFILE_UNKNOWN ); + }else { dip->di_onlink =0 ; + } }/* Fix any conflicting flags that the verifiers complain about. */
Re: [PATCH 20/26] xfs: fix di_metatype field of inodes that won't load
Looks good:
PATCH 21/26 xfs: scrub metadata directories
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Teach online scrub about the metadata directory tree.
--- fs/xfs/scrub/dir.c | 8 ++++++++ fs/xfs/scrub/dir_repair.c | 6 ++++++ fs/xfs/scrub/dirtree.c | 17 ++++++++++++++--- fs/xfs/scrub/findparent.c | 13 +++++++++++++ fs/xfs/scrub/parent.c | 14 ++++++++++++++ fs/xfs/scrub/trace.h | 1 + 6 files changed, 56 insertions(+), 3 deletions(-)
fs/xfs/scrub/dir.c +8 -0
@@ -100,6 +100,14 @@ xchk_dir_check_ftype(if (xfs_mode_to_ftype (VFS_I (ip)->i_mode) != ftype)xchk_fblock_set_corrupt (sc,XFS_DATA_FORK , offset); + +/* +* Metadata and regular inodes cannot cross trees. This property +* cannot change without a full inode free and realloc cycle, so it's +* safe to check this without holding locks. +*/ +if (xfs_is_metadir_inode (ip) !=xfs_is_metadir_inode (sc->ip)) +xchk_fblock_set_corrupt (sc,XFS_DATA_FORK , offset); }/*
fs/xfs/scrub/dir_repair.c +6 -0
@@ -415,6 +415,12 @@ xrep_dir_salvage_entry(if (error)return 0 ; +/* Don't mix metadata and regular directory trees. */ +if (xfs_is_metadir_inode (ip) !=xfs_is_metadir_inode (rd->sc->ip)) { +xchk_irele (sc, ip); +return 0 ; + } + xname.type =xfs_mode_to_ftype (VFS_I (ip)->i_mode);xchk_irele (sc, ip);
fs/xfs/scrub/dirtree.c +14 -3
@@ -362,7 +362,8 @@ xchk_dirpath_set_outcome( STATIC intxchk_dirpath_step_up (struct xchk_dirtree *dl, -struct xchk_dirpath *path) +struct xchk_dirpath *path, +bool is_metadir) {struct xfs_scrub *sc = dl->sc;struct xfs_inode *dp;
@@ -435,6 +436,14 @@ xchk_dirpath_step_up( goto out_scanlock; } +/* Parent must be in the same directory tree. */ +if (is_metadir !=xfs_is_metadir_inode (dp)) { +trace_xchk_dirpath_crosses_tree (dl->sc, dp, path->path_nr, + path->nr_steps, &dl->xname, &dl->pptr_rec); + error = -EFSCORRUPTED ; + goto out_scanlock; + } +/* * If the extended attributes look as though they has been zapped by * the inode record repair code, we cannot scan for parent pointers.
@@ -508,6 +517,7 @@ xchk_dirpath_walk_upwards( struct xchk_dirpath *path) {struct xfs_scrub *sc = dl->sc; +bool is_metadir;int error;ASSERT (sc->ilock_flags &XFS_ILOCK_EXCL );
@@ -538,6 +548,7 @@ xchk_dirpath_walk_upwards(* ILOCK state is no longer tracked in the scrub context. Hence we * must drop @sc->ip's ILOCK during the walk. */ + is_metadir =xfs_is_metadir_inode (sc->ip);mutex_unlock (&dl->lock);xchk_iunlock (sc,XFS_ILOCK_EXCL );
@@ -547,7 +558,7 @@ xchk_dirpath_walk_upwards(* If we see any kind of error here (including corruptions), the parent * pointer of @sc->ip is corrupt. Stop the whole scan. */ - error =xchk_dirpath_step_up (dl, path); + error =xchk_dirpath_step_up (dl, path, is_metadir);if (error) {xchk_ilock (sc,XFS_ILOCK_EXCL );mutex_lock (&dl->lock);
@@ -560,7 +571,7 @@ xchk_dirpath_walk_upwards(* *somewhere* in the path, but we don't need to stop scanning. */ while (!error && path->outcome ==XCHK_DIRPATH_SCANNING ) - error =xchk_dirpath_step_up (dl, path); + error =xchk_dirpath_step_up (dl, path, is_metadir);/* Retake the locks we had, mark paths, etc. */ xchk_ilock (sc,XFS_ILOCK_EXCL );
fs/xfs/scrub/findparent.c +13 -0
@@ -172,6 +172,10 @@ xrep_findparent_walk_directory(*/ lock_mode =xfs_ilock_data_map_shared (dp); +/* Don't mix metadata and regular directory trees. */ +if (xfs_is_metadir_inode (dp) !=xfs_is_metadir_inode (sc->ip)) + goto out_unlock; +/* * If this directory is known to be sick, we cannot scan it reliably * and must abort.
@@ -368,6 +372,12 @@ xrep_findparent_confirm(return 0 ; } +/* The metadata root directory always points to itself. */ +if (sc->ip == sc->mp->m_metadirip) { + *parent_ino = sc->mp->m_sb.sb_metadirino; +return 0 ; + } +/* Unlinked dirs can point anywhere; point them up to the root dir. */ if (VFS_I (sc->ip)->i_nlink ==0 ) { *parent_ino =xchk_inode_rootdir_inum (sc->ip);
@@ -415,6 +425,9 @@ xrep_findparent_self_reference(if (sc->ip->i_ino == sc->mp->m_sb.sb_rootino)return sc->mp->m_sb.sb_rootino; +if (sc->ip->i_ino == sc->mp->m_sb.sb_metadirino) +return sc->mp->m_sb.sb_metadirino; +if (VFS_I (sc->ip)->i_nlink ==0 )return xchk_inode_rootdir_inum (sc->ip);
fs/xfs/scrub/parent.c +14 -0
@@ -132,6 +132,14 @@ xchk_parent_validate(return 0 ; } +/* Is this the metadata root dir? Then '..' must point to itself. */ +if (sc->ip == mp->m_metadirip) { +if (sc->ip->i_ino != mp->m_sb.sb_metadirino || + sc->ip->i_ino != parent_ino) +xchk_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); +return 0 ; + } +/* '..' must not point to ourselves. */ if (sc->ip->i_ino == parent_ino) {xchk_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 );
@@ -185,6 +193,12 @@ xchk_parent_validate( goto out_unlock; } +/* Metadata and regular inodes cannot cross trees. */ +if (xfs_is_metadir_inode (dp) !=xfs_is_metadir_inode (sc->ip)) { +xchk_fblock_set_corrupt (sc,XFS_DATA_FORK ,0 ); + goto out_unlock; + } +/* Look for a directory entry in the parent pointing to the child. */ error =xchk_dir_walk (sc, dp, xchk_parent_actor, &spc);if (!xchk_fblock_xref_process_error (sc,XFS_DATA_FORK ,0 , &error))
fs/xfs/scrub/trace.h +1 -0
@@ -1752,6 +1752,7 @@ DEFINE_XCHK_DIRPATH_EVENT(xchk_dirpath_badgen);DEFINE_XCHK_DIRPATH_EVENT (xchk_dirpath_nondir_parent);DEFINE_XCHK_DIRPATH_EVENT (xchk_dirpath_unlinked_parent);DEFINE_XCHK_DIRPATH_EVENT (xchk_dirpath_found_next_step); +DEFINE_XCHK_DIRPATH_EVENT (xchk_dirpath_crosses_tree);TRACE_DEFINE_ENUM (XCHK_DIRPATH_SCANNING );TRACE_DEFINE_ENUM (XCHK_DIRPATH_DELETE );
Re: [PATCH 21/26] xfs: scrub metadata directories
Looks good:
PATCH 22/26 xfs: check the metadata directory inumber in superblocks
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
When metadata directories are enabled, make sure that the secondary superblocks point to the metadata directory. This isn't strictly required because the secondaries are only used to recover damaged filesystems.
--- fs/xfs/scrub/agheader.c | 5 +++++ 1 file changed, 5 insertions(+)
fs/xfs/scrub/agheader.c +5 -0
@@ -144,6 +144,11 @@ xchk_superblock(if (sb->sb_rootino !=cpu_to_be64 (mp->m_sb.sb_rootino))xchk_block_set_preen (sc, bp); +if (xfs_has_metadir (sc->mp)) { +if (sb->sb_metadirino !=cpu_to_be64 (mp->m_sb.sb_metadirino)) +xchk_block_set_preen (sc, bp); + } +if (sb->sb_rbmino !=cpu_to_be64 (mp->m_sb.sb_rbmino))xchk_block_set_preen (sc, bp);
Re: [PATCH 22/26] xfs: check the metadata directory inumber in superblocks
Looks good:
PATCH 23/26 xfs: move repair temporary files to the metadata directory tree
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Due to resource acquisition rules, we have to create the ondisk temporary files used to stage a filesystem repair before we can acquire a reference to the inode that we actually want to repair. Therefore, we do not know at tempfile creation time whether the tempfile will belong to the regular directory tree or the metadata directory tree.
This distinction becomes important when the swapext code tries to figure out the quota accounting of the two files whose mappings are being swapped. The swapext code assumes that accounting updates are required for a file if dqattach attaches dquots. Metadir files are never accounted in quota, which means that swapext must not update the quota accounting when swapping in a repaired directory/xattr/rtbitmap structure.
Prior to the swapext call, therefore, both files must be marked as METADIR for dqattach so that dqattach will ignore them. Add support for a repair tempfile to be switched to the metadir tree and switched back before being released so that ifree will just free the file.
--- fs/xfs/scrub/common.c | 5 ++ fs/xfs/scrub/tempfile.c | 97 +++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/tempfile.h | 3 + 3 files changed, 105 insertions(+)
fs/xfs/scrub/common.c +5 -0
@@ -39,6 +39,7 @@#include "scrub/trace.h" #include "scrub/repair.h" #include "scrub/health.h" +#include "scrub/tempfile.h" /* Common code for the metadata scrubbers. */
@@ -1090,6 +1091,10 @@ xchk_setup_inode_contents(if (error)return error; + error =xrep_tempfile_adjust_directory_tree (sc); +if (error) +return error; +/* Lock the inode so the VFS cannot touch this file. */ xchk_ilock (sc,XFS_IOLOCK_EXCL );
fs/xfs/scrub/tempfile.c +97 -0
115 lines not on this page · raw message
fs/xfs/scrub/tempfile.h +3 -0
@@ -10,6 +10,8 @@int xrep_tempfile_create (struct xfs_scrub *sc,uint16_t mode);void xrep_tempfile_rele (struct xfs_scrub *sc); +int xrep_tempfile_adjust_directory_tree (struct xfs_scrub *sc); +bool xrep_tempfile_iolock_nowait (struct xfs_scrub *sc);int xrep_tempfile_iolock_polled (struct xfs_scrub *sc);void xrep_tempfile_iounlock (struct xfs_scrub *sc);
@@ -42,6 +44,7 @@ static inline void xrep_tempfile_iolock_both(struct xfs_scrub *sc) xchk_ilock(sc, XFS_IOLOCK_EXCL); }# define xrep_is_tempfile (ip) (false) +# define xrep_tempfile_adjust_directory_tree (sc) (0)# define xrep_tempfile_rele (sc)#endif /* CONFIG_XFS_ONLINE_REPAIR */
Re: [PATCH 23/26] xfs: move repair temporary files to the metadata directory tree
Looks good:
PATCH 24/26 xfs: check metadata directory file path connectivity
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create a new scrubber type that checks that well known metadata directory paths are connected to the metadata inode that the incore structures think is in use. IOWs, check that "/quota/user" in the metadata directory tree actually points to mp->m_quotainfo->qi_uquotaip->i_ino.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_fs.h | 13 +++ fs/xfs/libxfs/xfs_health.h | 4 + fs/xfs/scrub/common.h | 1 fs/xfs/scrub/health.c | 1 fs/xfs/scrub/metapath.c | 174 ++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/scrub.c | 9 ++ fs/xfs/scrub/scrub.h | 2 + fs/xfs/scrub/stats.c | 1 fs/xfs/scrub/trace.c | 1 fs/xfs/scrub/trace.h | 36 +++++++++ fs/xfs/xfs_health.c | 1 12 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 fs/xfs/scrub/metapath.c
fs/xfs/Makefile +1 -0
@@ -173,6 +173,7 @@ xfs-y += $(addprefix scrub/, \ inode.o \ iscan.o \ listxattr.o \ + metapath.o \ nlinks.o \ parent.o \ readdir.o \
fs/xfs/libxfs/xfs_fs.h +12 -1
@@ -198,6 +198,7 @@ struct xfs_fsop_geom {#define XFS_FSOP_GEOM_SICK_QUOTACHECK (1 << 6)/* quota counts */ #define XFS_FSOP_GEOM_SICK_NLINKS (1 << 7)/* inode link counts */ #define XFS_FSOP_GEOM_SICK_METADIR (1 << 8)/* metadata directory */ +#define XFS_FSOP_GEOM_SICK_METAPATH (1 << 9)/* metadir tree path */ /* Output for XFS_FS_COUNTS */ typedef struct xfs_fsop_counts {
@@ -732,9 +733,10 @@ struct xfs_scrub_metadata {#define XFS_SCRUB_TYPE_NLINKS 26/* inode link counts */ #define XFS_SCRUB_TYPE_HEALTHY 27/* everything checked out ok */ #define XFS_SCRUB_TYPE_DIRTREE 28/* directory tree structure */ +#define XFS_SCRUB_TYPE_METAPATH 29/* metadata directory tree paths */ /* Number of scrub subcommands. */ -#define XFS_SCRUB_TYPE_NR 29 +#define XFS_SCRUB_TYPE_NR 30/* * This special type code only applies to the vectored scrub implementation.
@@ -812,6 +814,15 @@ struct xfs_scrub_vec_head {#define XFS_SCRUB_VEC_FLAGS_ALL (0) +/* +* i: sm_ino values for XFS_SCRUB_TYPE_METAPATH to select a metadata file for +* path checking. +*/ +#define XFS_SCRUB_METAPATH_PROBE (0)/* do we have a metapath scrubber? */ + +/* Number of metapath sm_ino values */ +#define XFS_SCRUB_METAPATH_NR (1) +/* * ioctl limits */
fs/xfs/libxfs/xfs_health.h +3 -1
@@ -61,6 +61,7 @@ struct xfs_da_args;#define XFS_SICK_FS_QUOTACHECK (1 << 4)/* quota counts */ #define XFS_SICK_FS_NLINKS (1 << 5)/* inode link counts */ #define XFS_SICK_FS_METADIR (1 << 6)/* metadata directory tree */ +#define XFS_SICK_FS_METAPATH (1 << 7)/* metadata directory tree path */ /* Observable health issues for realtime volume metadata. */ #define XFS_SICK_RT_BITMAP (1 << 0)/* realtime bitmap */
@@ -105,7 +106,8 @@ struct xfs_da_args;XFS_SICK_FS_PQUOTA | \XFS_SICK_FS_QUOTACHECK | \XFS_SICK_FS_NLINKS | \ - XFS_SICK_FS_METADIR) +XFS_SICK_FS_METADIR | \ + XFS_SICK_FS_METAPATH) #define XFS_SICK_RT_PRIMARY (XFS_SICK_RT_BITMAP | \XFS_SICK_RT_SUMMARY )
fs/xfs/scrub/common.h +1 -0
@@ -68,6 +68,7 @@ int xchk_setup_xattr(struct xfs_scrub *sc);int xchk_setup_symlink (struct xfs_scrub *sc);int xchk_setup_parent (struct xfs_scrub *sc);int xchk_setup_dirtree (struct xfs_scrub *sc); +int xchk_setup_metapath (struct xfs_scrub *sc);#ifdef CONFIG_XFS_RT int xchk_setup_rtbitmap (struct xfs_scrub *sc);int xchk_setup_rtsummary (struct xfs_scrub *sc);
fs/xfs/scrub/health.c +1 -0
@@ -109,6 +109,7 @@ static const struct xchk_health_map type_to_health_flag[XFS_SCRUB_TYPE_NR] = { [XFS_SCRUB_TYPE_QUOTACHECK] = { XHG_FS, XFS_SICK_FS_QUOTACHECK }, [XFS_SCRUB_TYPE_NLINKS] = { XHG_FS, XFS_SICK_FS_NLINKS }, [XFS_SCRUB_TYPE_DIRTREE] = { XHG_INO, XFS_SICK_INO_DIRTREE }, + [XFS_SCRUB_TYPE_METAPATH] = { XHG_FS, XFS_SICK_FS_METAPATH }, };/* Return the health status mask for this scrub type. */
fs/xfs/scrub/metapath.c new file +174 -0
174 lines not on this page · raw message
fs/xfs/scrub/scrub.c +9 -0
@@ -442,6 +442,13 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { .has = xfs_has_parent, .repair = xrep_dirtree, }, + [XFS_SCRUB_TYPE_METAPATH] = { /* metadata directory tree path */ + .type = ST_GENERIC, + .setup = xchk_setup_metapath, + .scrub = xchk_metapath, + .has = xfs_has_metadir, + .repair = xrep_notsupported, + }, }; static int
@@ -489,6 +496,8 @@ xchk_validate_inputs(if (sm->sm_agno || (sm->sm_gen && !sm->sm_ino)) goto out;break ; +case ST_GENERIC : +break ;default : goto out; }
fs/xfs/scrub/scrub.h +2 -0
@@ -73,6 +73,7 @@ enum xchk_type {ST_PERAG ,/* per-AG metadata */ ST_FS ,/* per-FS metadata */ ST_INODE ,/* per-inode metadata */ + ST_GENERIC, /* determined by the scrubber */ }; struct xchk_meta_ops {
@@ -250,6 +251,7 @@ int xchk_xattr(struct xfs_scrub *sc);int xchk_symlink (struct xfs_scrub *sc);int xchk_parent (struct xfs_scrub *sc);int xchk_dirtree (struct xfs_scrub *sc); +int xchk_metapath (struct xfs_scrub *sc);#ifdef CONFIG_XFS_RT int xchk_rtbitmap (struct xfs_scrub *sc);int xchk_rtsummary (struct xfs_scrub *sc);
fs/xfs/scrub/stats.c +1 -0
@@ -80,6 +80,7 @@ static const char *name_map[XFS_SCRUB_TYPE_NR] = { [XFS_SCRUB_TYPE_QUOTACHECK] = "quotacheck", [XFS_SCRUB_TYPE_NLINKS] = "nlinks", [XFS_SCRUB_TYPE_DIRTREE] = "dirtree", + [XFS_SCRUB_TYPE_METAPATH] = "metapath", };/* Format the scrub stats into a text buffer, similar to pcp style. */
fs/xfs/scrub/trace.c +1 -0
@@ -20,6 +20,7 @@#include "xfs_dir2.h" #include "xfs_rmap.h" #include "xfs_parent.h" +#include "xfs_metafile.h" #include "scrub/scrub.h" #include "scrub/xfile.h" #include "scrub/xfarray.h"
fs/xfs/scrub/trace.h +35 -1
@@ -70,6 +70,7 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_NLINKS);TRACE_DEFINE_ENUM (XFS_SCRUB_TYPE_HEALTHY );TRACE_DEFINE_ENUM (XFS_SCRUB_TYPE_DIRTREE );TRACE_DEFINE_ENUM (XFS_SCRUB_TYPE_BARRIER ); +TRACE_DEFINE_ENUM (XFS_SCRUB_TYPE_METAPATH );#define XFS_SCRUB_TYPE_STRINGS \ { XFS_SCRUB_TYPE_PROBE, "probe" }, \
@@ -101,7 +102,8 @@ TRACE_DEFINE_ENUM(XFS_SCRUB_TYPE_BARRIER); { XFS_SCRUB_TYPE_NLINKS, "nlinks" }, \ { XFS_SCRUB_TYPE_HEALTHY, "healthy" }, \ { XFS_SCRUB_TYPE_DIRTREE, "dirtree" }, \ - { XFS_SCRUB_TYPE_BARRIER, "barrier" } + { XFS_SCRUB_TYPE_BARRIER, "barrier" }, \ + { XFS_SCRUB_TYPE_METAPATH, "metapath" } #define XFS_SCRUB_FLAG_STRINGS \ { XFS_SCRUB_IFLAG_REPAIR, "repair" }, \
@@ -1915,6 +1917,38 @@ TRACE_EVENT(xchk_dirtree_live_update, __get_str(name)) ); +DECLARE_EVENT_CLASS (xchk_metapath_class, + TP_PROTO(struct xfs_scrub *sc, const char *path, + struct xfs_inode *dp, xfs_ino_t ino), +TP_ARGS (sc, path, dp, ino), +TP_STRUCT__entry ( + __field(dev_t, dev) + __field(xfs_ino_t, scrub_ino) + __field(xfs_ino_t, parent_ino) + __field(xfs_ino_t, ino) +__string (name, path) + ), +TP_fast_assign ( + __entry->dev = sc->mp->m_super->s_dev; + __entry->scrub_ino = sc->ip ? sc->ip->i_ino : NULLFSINO; + __entry->parent_ino = dp ? dp->i_ino : NULLFSINO; + __entry->ino = ino; + __assign_str(name); + ), +TP_printk ("dev %d:%d ino 0x%llx parent_ino 0x%llx name '%s' ino 0x%llx" , +MAJOR (__entry->dev),MINOR (__entry->dev), + __entry->scrub_ino, + __entry->parent_ino, +__get_str (name), + __entry->ino) +); +#define DEFINE_XCHK_METAPATH_EVENT (name) \ +DEFINE_EVENT(xchk_metapath_class, name, \ + TP_PROTO(struct xfs_scrub *sc, const char *path, \ + struct xfs_inode *dp, xfs_ino_t ino), \ + TP_ARGS(sc, path, dp, ino)) +DEFINE_XCHK_METAPATH_EVENT (xchk_metapath_lookup); +/* repair tracepoints */ #if IS_ENABLED (CONFIG_XFS_ONLINE_REPAIR )
fs/xfs/xfs_health.c +1 -0
@@ -377,6 +377,7 @@ static const struct ioctl_sick_map fs_map[] = { { XFS_SICK_FS_QUOTACHECK, XFS_FSOP_GEOM_SICK_QUOTACHECK }, { XFS_SICK_FS_NLINKS, XFS_FSOP_GEOM_SICK_NLINKS }, { XFS_SICK_FS_METADIR, XFS_FSOP_GEOM_SICK_METADIR }, + { XFS_SICK_FS_METAPATH, XFS_FSOP_GEOM_SICK_METAPATH }, { 0, 0 }, };
Re: [PATCH 24/26] xfs: check metadata directory file path connectivity
Looks good:
PATCH 25/26 xfs: confirm dotdot target before replacing it during a repair
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
xfs_dir_replace trips an assertion if you tell it to change a dirent to point to an inumber that it already points at. Look up the dotdot entry directly to confirm that we need to make a change.
--- fs/xfs/scrub/dir_repair.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-)
fs/xfs/scrub/dir_repair.c +8 -4
@@ -1638,6 +1638,7 @@ xrep_dir_swap( struct xrep_dir *rd) {struct xfs_scrub *sc = rd->sc; +xfs_ino_t ino;bool ip_local, temp_local;int error =0 ;
@@ -1655,14 +1656,17 @@ xrep_dir_swap(/* * Reset the temporary directory's '..' entry to point to the parent -* that we found. The temporary directory was created with the root -* directory as the parent, so we can skip this if repairing a -* subdirectory of the root. +* that we found. The dirent replace code asserts if the dirent +* already points at the new inumber, so we look it up here. * * It's also possible that this replacement could also expand a sf * tempdir into block format. */ -if (rd->pscan.parent_ino != sc->mp->m_rootip->i_ino) { + error =xchk_dir_lookup (sc, rd->sc->tempip, &xfs_name_dotdot, &ino); +if (error) +return error; + +if (rd->pscan.parent_ino != ino) { error =xrep_dir_replace (rd, rd->sc->tempip, &xfs_name_dotdot, rd->pscan.parent_ino, rd->tx.req.resblks);if (error)
Re: [PATCH 25/26] xfs: confirm dotdot target before replacing it during a repair
Looks good:
PATCH 26/26 xfs: repair metadata directory file path connectivity
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Fix disconnected or incorrect metadata directory paths.
--- fs/xfs/scrub/metapath.c | 351 +++++++++++++++++++++++++++++++++++++++++++++++ fs/xfs/scrub/repair.h | 3 fs/xfs/scrub/scrub.c | 2 fs/xfs/scrub/trace.h | 5 + 4 files changed, 358 insertions(+), 3 deletions(-)
fs/xfs/scrub/metapath.c +349 -2
@@ -16,10 +16,15 @@#include "xfs_quota.h" #include "xfs_qm.h" #include "xfs_dir2.h" +#include "xfs_parent.h" +#include "xfs_bmap_btree.h" +#include "xfs_trans_space.h" +#include "xfs_attr.h" #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/trace.h" #include "scrub/readdir.h" +#include "scrub/repair.h" /* * Metadata Directory Tree Paths
@@ -38,15 +43,28 @@ struct xchk_metapath {/* Name for lookup */ struct xfs_name xname; -/* Path for this metadata file and the parent directory */ +/* Directory update for repairs */ +struct xfs_dir_update du; + +/* Path down to this metadata file from the parent directory */ const char *path; -const char *parent_path;/* Directory parent of the metadata file. */ struct xfs_inode *dp;/* Locks held on dp */ unsigned int dp_ilock_flags; + +/* Transaction block reservations */ +unsigned int link_resblks; +unsigned int unlink_resblks; + +/* Parent pointer updates */ +struct xfs_parent_args link_ppargs; +struct xfs_parent_args unlink_ppargs; + +/* Scratchpads for removing links */ +struct xfs_da_args pptr_args; };/* Release resources tracked in the buffer. */
@@ -172,3 +190,332 @@ xchk_metapath(xchk_trans_cancel (sc);return error; } + +#ifdef CONFIG_XFS_ONLINE_REPAIR +/* Create the dirent represented by the final component of the path. */ +STATIC int +xrep_metapath_link ( +struct xchk_metapath *mpath) +{ +struct xfs_scrub *sc = mpath->sc; + + mpath->du.dp = mpath->dp; + mpath->du.name = &mpath->xname; + mpath->du.ip = sc->ip; + +if (xfs_has_parent (sc->mp)) + mpath->du.ppargs = &mpath->link_ppargs; +else + mpath->du.ppargs =NULL ; + +trace_xrep_metapath_link (sc, mpath->path, mpath->dp, sc->ip->i_ino); + +return xfs_dir_add_child (sc->tp, mpath->link_resblks, &mpath->du); +} + +/* Remove the dirent at the final component of the path. */ +STATIC int +xrep_metapath_unlink ( +struct xchk_metapath *mpath, +xfs_ino_t ino, +struct xfs_inode *ip) +{ +struct xfs_parent_rec rec; +struct xfs_scrub *sc = mpath->sc; +struct xfs_mount *mp = sc->mp; +int error; + +trace_xrep_metapath_unlink (sc, mpath->path, mpath->dp, ino); + +if (!ip) { +/* The child inode isn't allocated. Junk the dirent. */ +xfs_trans_log_inode (sc->tp, mpath->dp,XFS_ILOG_CORE ); +return xfs_dir_removename (sc->tp, mpath->dp, &mpath->xname, + ino, mpath->unlink_resblks); + } + + mpath->du.dp = mpath->dp; + mpath->du.name = &mpath->xname; + mpath->du.ip = ip; + mpath->du.ppargs =NULL ; + +/* Figure out if we're removing a parent pointer too. */ +if (xfs_has_parent (mp)) { +xfs_inode_to_parent_rec (&rec, ip); + error =xfs_parent_lookup (sc->tp, ip, &mpath->xname, &rec, + &mpath->pptr_args); +switch (error) { +case -ENOATTR : +break ; +case 0 : + mpath->du.ppargs = &mpath->unlink_ppargs; +break ; +default : +return error; + } + } + +return xfs_dir_remove_child (sc->tp, mpath->unlink_resblks, &mpath->du); +} + +/* +* Try to create a dirent in @mpath->dp with the name @mpath->xname that points +* to @sc->ip. Returns: +* +* -EEXIST and an @alleged_child if the dirent that points to the wrong inode; +* 0 if there is now a dirent pointing to @sc->ip; or +* A negative errno on error. +*/ +STATIC int +xrep_metapath_try_link ( +struct xchk_metapath *mpath, +xfs_ino_t *alleged_child) +{ +struct xfs_scrub *sc = mpath->sc; +xfs_ino_t ino; +int error; + +/* Allocate transaction, lock inodes, join to transaction. */ + error =xchk_trans_alloc (sc, mpath->link_resblks); +if (error) +return error; + + error =xchk_metapath_ilock_both (mpath); +if (error) { +xchk_trans_cancel (sc); +return error; + } +xfs_trans_ijoin (sc->tp, mpath->dp,0 ); +xfs_trans_ijoin (sc->tp, sc->ip,0 ); + + error =xchk_dir_lookup (sc, mpath->dp, &mpath->xname, &ino); +trace_xrep_metapath_lookup (sc, mpath->path, mpath->dp, ino); +if (error == -ENOENT ) { +/* +* There is no dirent in the directory. Create an entry +* pointing to @sc->ip. +*/ + error =xrep_metapath_link (mpath); +if (error) + goto out_cancel; + + error =xrep_trans_commit (sc); +xchk_metapath_iunlock (mpath); +return error; + } +if (error) + goto out_cancel; + +if (ino == sc->ip->i_ino) { +/* The dirent already points to @sc->ip; we're done. */ + error =0 ; + goto out_cancel; + } + +/* +* The dirent points elsewhere; pass that back so that the caller +* can try to remove the dirent. +*/ + *alleged_child = ino; + error = -EEXIST ; + +out_cancel: +xchk_trans_cancel (sc); +xchk_metapath_iunlock (mpath); +return error; +} + +/* +* Take the ILOCK on the metadata directory parent and a bad child, if one is +* supplied. We do not know that the metadata directory is not corrupt, so we +* lock the parent and try to lock the child. Returns 0 if successful, or +* -EINTR to abort the repair. The lock state of @dp is not recorded in @mpath. +*/ +STATIC int +xchk_metapath_ilock_parent_and_child ( +struct xchk_metapath *mpath, +struct xfs_inode *ip) +{ +struct xfs_scrub *sc = mpath->sc; +int error =0 ; + +while (true) { +xfs_ilock (mpath->dp,XFS_ILOCK_EXCL ); +if (!ip ||xfs_ilock_nowait (ip,XFS_ILOCK_EXCL )) +return 0 ; +xfs_iunlock (mpath->dp,XFS_ILOCK_EXCL ); + +if (xchk_should_terminate (sc, &error)) +return error; + +delay (1 ); + } + +ASSERT (0 ); +return -EINTR ; +} + +/* +* Try to remove a dirent in @mpath->dp with the name @mpath->xname that points +* to @alleged_child. Returns: +* +* 0 if there is no longer a dirent; +* -EEXIST if the dirent points to @sc->ip; +* -EAGAIN and an updated @alleged_child if the dirent points elsewhere; or +* A negative errno for any other error. +*/ +STATIC int +xrep_metapath_try_unlink ( +struct xchk_metapath *mpath, +xfs_ino_t *alleged_child) +{ +struct xfs_scrub *sc = mpath->sc; +struct xfs_inode *ip =NULL ; +xfs_ino_t ino; +int error; + +ASSERT (*alleged_child != sc->ip->i_ino); + +trace_xrep_metapath_try_unlink (sc, mpath->path, mpath->dp, + *alleged_child); + +/* +* Allocate transaction, grab the alleged child inode, lock inodes, +* join to transaction. +*/ + error =xchk_trans_alloc (sc, mpath->unlink_resblks); +if (error) +return error; + + error =xchk_iget (sc, *alleged_child, &ip); +if (error == -EINVAL || error == -ENOENT ) { +/* inode number is bogus, junk the dirent */ + error =0 ; + } +if (error) { +xchk_trans_cancel (sc); +return error; + } + + error =xchk_metapath_ilock_parent_and_child (mpath, ip); +if (error) { +xchk_trans_cancel (sc); +return error; + } +xfs_trans_ijoin (sc->tp, mpath->dp,0 ); +if (ip) +xfs_trans_ijoin (sc->tp, ip,0 ); + + error =xchk_dir_lookup (sc, mpath->dp, &mpath->xname, &ino); +trace_xrep_metapath_lookup (sc, mpath->path, mpath->dp, ino); +if (error == -ENOENT ) { +/* +* There is no dirent in the directory anymore. We're ready to +* try the link operation again. +*/ + error =0 ; + goto out_cancel; + } +if (error) + goto out_cancel; + +if (ino == sc->ip->i_ino) { +/* The dirent already points to @sc->ip; we're done. */ + error = -EEXIST ; + goto out_cancel; + } + +/* +* The dirent does not point to the alleged child. Update the caller +* and signal that we want to be called again. +*/ +if (ino != *alleged_child) { + *alleged_child = ino; + error = -EAGAIN ; + goto out_cancel; + } + +/* Remove the link to the child. */ + error =xrep_metapath_unlink (mpath, ino, ip); +if (error) + goto out_cancel; + + error =xrep_trans_commit (sc); + goto out_unlock; + +out_cancel: +xchk_trans_cancel (sc); +out_unlock: +xfs_iunlock (mpath->dp,XFS_ILOCK_EXCL ); +if (ip) { +xfs_iunlock (ip,XFS_ILOCK_EXCL ); +xchk_irele (sc, ip); + } +return error; +} + +/* +* Make sure the metadata directory path points to the child being examined. +* +* Repair needs to be able to create a directory structure, create its own +* transactions, and take ILOCKs. This function /must/ be called after all +* other repairs have completed. +*/ +int +xrep_metapath ( +struct xfs_scrub *sc) +{ +struct xchk_metapath *mpath = sc->buf; +struct xfs_mount *mp = sc->mp; +int error =0 ; + +/* Just probing, nothing to repair. */ +if (sc->sm->sm_ino ==XFS_SCRUB_METAPATH_PROBE ) +return 0 ; + +/* Parent required to do anything else. */ +if (mpath->dp ==NULL ) +return -EFSCORRUPTED ; + +/* +* Make sure the child file actually has an attr fork to receive a new +* parent pointer if the fs has parent pointers. +*/ +if (xfs_has_parent (mp)) { + error =xfs_attr_add_fork (sc->ip, +sizeof (struct xfs_attr_sf_hdr ),1 ); +if (error) +return error; + } + +/* Compute block reservation required to unlink and link a file. */ + mpath->unlink_resblks =xfs_remove_space_res (mp,MAXNAMELEN ); + mpath->link_resblks =xfs_link_space_res (mp,MAXNAMELEN ); + +do { +xfs_ino_t alleged_child; + +/* Re-establish the link, or tell us which inode to remove. */ + error =xrep_metapath_try_link (mpath, &alleged_child); +if (!error) +return 0 ; +if (error != -EEXIST ) +return error; + +/* +* Remove an incorrect link to an alleged child, or tell us +* which inode to remove. +*/ +do { + error =xrep_metapath_try_unlink (mpath, &alleged_child); + }while (error == -EAGAIN ); +if (error == -EEXIST ) { +/* Link established; we're done. */ + error =0 ; +break ; + } + }while (!error); + +return error; +} +#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/repair.h +3 -0
@@ -134,6 +134,7 @@ int xrep_directory(struct xfs_scrub *sc);int xrep_parent (struct xfs_scrub *sc);int xrep_symlink (struct xfs_scrub *sc);int xrep_dirtree (struct xfs_scrub *sc); +int xrep_metapath (struct xfs_scrub *sc);#ifdef CONFIG_XFS_RT int xrep_rtbitmap (struct xfs_scrub *sc);
@@ -208,6 +209,7 @@ xrep_setup_nothing(#define xrep_setup_parent xrep_setup_nothing#define xrep_setup_nlinks xrep_setup_nothing#define xrep_setup_dirtree xrep_setup_nothing +#define xrep_setup_metapath xrep_setup_nothing#define xrep_setup_inode (sc, imap) ((void)0)
@@ -243,6 +245,7 @@ static inline int xrep_setup_symlink(struct xfs_scrub *sc, unsigned int *x)#define xrep_parent xrep_notsupported#define xrep_symlink xrep_notsupported#define xrep_dirtree xrep_notsupported +#define xrep_metapath xrep_notsupported#endif /* CONFIG_XFS_ONLINE_REPAIR */
fs/xfs/scrub/scrub.c +1 -1
@@ -447,7 +447,7 @@ static const struct xchk_meta_ops meta_scrub_ops[] = { .setup = xchk_setup_metapath, .scrub = xchk_metapath, .has = xfs_has_metadir, - .repair = xrep_notsupported, + .repair = xrep_metapath, }, };
fs/xfs/scrub/trace.h +5 -0
@@ -3607,6 +3607,11 @@ DEFINE_XCHK_DIRTREE_EVENT(xrep_dirtree_delete_path);DEFINE_XCHK_DIRTREE_EVENT (xrep_dirtree_create_adoption);DEFINE_XCHK_DIRTREE_EVALUATE_EVENT (xrep_dirtree_decided_fate); +DEFINE_XCHK_METAPATH_EVENT (xrep_metapath_lookup); +DEFINE_XCHK_METAPATH_EVENT (xrep_metapath_try_unlink); +DEFINE_XCHK_METAPATH_EVENT (xrep_metapath_unlink); +DEFINE_XCHK_METAPATH_EVENT (xrep_metapath_link); +#endif /* IS_ENABLED(CONFIG_XFS_ONLINE_REPAIR) */ #endif /* _TRACE_XFS_SCRUB_TRACE_H */
Re: [PATCH 26/26] xfs: repair metadata directory file path connectivity
Looks good:
PATCH 5/10 xfs: clean up the rtbitmap code
In reply to Darrick J. WongHi all,
Here are some cleanups and reorganization of the realtime bitmap code to share more of that code between userspace and the kernel.
If you're going to start using this code, I strongly recommend pulling from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy! Comments and questions are, as always, welcome.
--D
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=rtbitmap-cleanups
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=rtbitmap-cleanups
---
Commits in this patchset:
* xfs: remove xfs_validate_rtextents
* xfs: factor out a xfs_validate_rt_geometry helper
* xfs: make the RT rsum_cache mandatory
* xfs: remove the limit argument to xfs_rtfind_back
* xfs: assert a valid limit in xfs_rtfind_forw
* xfs: add bounds checking to xfs_rt{bitmap,summary}_read_buf
* xfs: cleanup the calling convention for xfs_rtpick_extent
* xfs: push the calls to xfs_rtallocate_range out to xfs_bmap_rtalloc
* xfs: factor out a xfs_growfs_rt_bmblock helper
* xfs: factor out a xfs_last_rt_bmblock helper
* xfs: factor out rtbitmap/summary initialization helpers
* xfs: push transaction join out of xfs_rtbitmap_lock and xfs_rtgroup_lock
--- fs/xfs/libxfs/xfs_bmap.c | 3 fs/xfs/libxfs/xfs_rtbitmap.c | 192 ++++++++++++++- fs/xfs/libxfs/xfs_rtbitmap.h | 33 +-- fs/xfs/libxfs/xfs_sb.c | 64 +++-- fs/xfs/libxfs/xfs_sb.h | 1 fs/xfs/libxfs/xfs_types.h | 12 - fs/xfs/xfs_rtalloc.c | 535 +++++++++++++++++------------------------- 7 files changed, 438 insertions(+), 402 deletions(-)
PATCH 1/12 xfs: remove xfs_validate_rtextents
From: Christoph Hellwig <[email protected]>
Replace xfs_validate_rtextents with an open coded check for 0 rtextents. The name for the function implies it does a lot more than a zero check, which is more obvious when open coded.
--- fs/xfs/libxfs/xfs_sb.c | 2 +- fs/xfs/libxfs/xfs_types.h | 12 ------------ fs/xfs/xfs_rtalloc.c | 2 +- 3 files changed, 2 insertions(+), 14 deletions(-)
fs/xfs/libxfs/xfs_sb.c +1 -1
@@ -516,7 +516,7 @@ xfs_validate_sb_common( rbmblocks =howmany_64 (sbp->sb_rextents,NBBY * sbp->sb_blocksize); -if (!xfs_validate_rtextents (rexts) || +if (sbp->sb_rextents ==0 || sbp->sb_rextents != rexts || sbp->sb_rextslog !=xfs_compute_rextslog (rexts) || sbp->sb_rbmblocks != rbmblocks) {
fs/xfs/libxfs/xfs_types.h +0 -12
@@ -235,16 +235,4 @@ bool xfs_verify_fileoff(struct xfs_mount *mp, xfs_fileoff_t off);bool xfs_verify_fileext (struct xfs_mount *mp,xfs_fileoff_t off,xfs_fileoff_t len); -/* Do we support an rt volume having this number of rtextents? */ -static inline bool -xfs_validate_rtextents ( -xfs_rtbxlen_t rtextents) -{ -/* No runt rt volumes */ -if (rtextents ==0 ) -return false; - -return true; -} -#endif /* __XFS_TYPES_H__ */
fs/xfs/xfs_rtalloc.c +1 -1
@@ -905,7 +905,7 @@ xfs_growfs_rt(*/ nrextents = nrblocks;do_div (nrextents, in->extsize); -if (!xfs_validate_rtextents (nrextents)) { +if (nrextents ==0 ) { error = -EINVAL ; goto out_unlock; }
PATCH 2/12 xfs: factor out a xfs_validate_rt_geometry helper
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Split the RT geometry validation in the early mount code into a helper than can be reused by repair (from which this code was apparently originally stolen anyway).
--- fs/xfs/libxfs/xfs_sb.c | 64 ++++++++++++++++++++++++++---------------------- fs/xfs/libxfs/xfs_sb.h | 1 + 2 files changed, 36 insertions(+), 29 deletions(-)
fs/xfs/libxfs/xfs_sb.c +35 -29
@@ -234,6 +234,38 @@ xfs_validate_sb_read(return 0 ; } +static uint64_t +xfs_sb_calc_rbmblocks ( +struct xfs_sb *sbp) +{ +return howmany_64 (sbp->sb_rextents,NBBY * sbp->sb_blocksize); +} + +/* Validate the realtime geometry */ +bool +xfs_validate_rt_geometry ( +struct xfs_sb *sbp) +{ +if (sbp->sb_rextsize * sbp->sb_blocksize >XFS_MAX_RTEXTSIZE || + sbp->sb_rextsize * sbp->sb_blocksize <XFS_MIN_RTEXTSIZE ) +return false; + +if (sbp->sb_rblocks ==0 ) { +if (sbp->sb_rextents !=0 || sbp->sb_rbmblocks !=0 || + sbp->sb_rextslog !=0 || sbp->sb_frextents !=0 ) +return false; +return true; + } + +if (sbp->sb_rextents ==0 || + sbp->sb_rextents !=div_u64 (sbp->sb_rblocks, sbp->sb_rextsize) || + sbp->sb_rextslog !=xfs_compute_rextslog (sbp->sb_rextents) || + sbp->sb_rbmblocks !=xfs_sb_calc_rbmblocks (sbp)) +return false; + +return true; +} +/* Check all the superblock fields we care about when writing one out. */ STATIC int xfs_validate_sb_write(
@@ -493,39 +525,13 @@ xfs_validate_sb_common( } } -/* Validate the realtime geometry; stolen from xfs_repair */ -if (sbp->sb_rextsize * sbp->sb_blocksize >XFS_MAX_RTEXTSIZE || - sbp->sb_rextsize * sbp->sb_blocksize <XFS_MIN_RTEXTSIZE ) { +if (!xfs_validate_rt_geometry (sbp)) {xfs_notice (mp, -"realtime extent sanity check failed" ); +"realtime %sgeometry check failed" , + sbp->sb_rblocks ?"" :"zeroed " );return -EFSCORRUPTED ; } -if (sbp->sb_rblocks ==0 ) { -if (sbp->sb_rextents !=0 || sbp->sb_rbmblocks !=0 || - sbp->sb_rextslog !=0 || sbp->sb_frextents !=0 ) { -xfs_notice (mp, -"realtime zeroed geometry check failed" ); -return -EFSCORRUPTED ; - } - }else { -uint64_t rexts; -uint64_t rbmblocks; - - rexts =div_u64 (sbp->sb_rblocks, sbp->sb_rextsize); - rbmblocks =howmany_64 (sbp->sb_rextents, -NBBY * sbp->sb_blocksize); - -if (sbp->sb_rextents ==0 || - sbp->sb_rextents != rexts || - sbp->sb_rextslog !=xfs_compute_rextslog (rexts) || - sbp->sb_rbmblocks != rbmblocks) { -xfs_notice (mp, -"realtime geometry sanity check failed" ); -return -EFSCORRUPTED ; - } - } -/* * Either (sb_unit and !hasdalign) or (!sb_unit and hasdalign) * would imply the image is corrupted.
fs/xfs/libxfs/xfs_sb.h +1 -0
@@ -38,6 +38,7 @@ extern int xfs_sb_get_secondary(struct xfs_mount *mp,bool xfs_validate_stripe_geometry (struct xfs_mount *mp,__s64 sunit,__s64 swidth,int sectorsize,bool may_repair,bool silent); +bool xfs_validate_rt_geometry (struct xfs_sb *sbp);uint8_t xfs_compute_rextslog (xfs_rtbxlen_t rtextents);
PATCH 3/12 xfs: make the RT rsum_cache mandatory
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Currently the RT mount code simply ignores an allocation failure for the rsum_cache. The code mostly works fine with it, but not having it leads to nasty corner cases in the growfs code that we don't really handle well. Switch to failing the mount if we can't allocate the memory, the file system would not exactly be useful in such a constrained environment to start with.
--- fs/xfs/xfs_rtalloc.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-)
fs/xfs/xfs_rtalloc.c +15 -11
@@ -769,21 +769,20 @@ xfs_growfs_rt_alloc(return error; } -static void +static int xfs_alloc_rsum_cache ( -xfs_mount_t *mp,/* file system mount structure */ -xfs_extlen_t rbmblocks)/* number of rt bitmap blocks */ +struct xfs_mount *mp, +xfs_extlen_t rbmblocks) {/* * The rsum cache is initialized to the maximum value, which is * trivially an upper bound on the maximum level with any free extents. -* We can continue without the cache if it couldn't be allocated. */ mp->m_rsum_cache =kvmalloc (rbmblocks,GFP_KERNEL ); -if (mp->m_rsum_cache) -memset (mp->m_rsum_cache,-1 , rbmblocks); -else -xfs_warn (mp,"could not allocate realtime summary cache" ); +if (!mp->m_rsum_cache) +return -ENOMEM ; +memset (mp->m_rsum_cache,-1 , rbmblocks); +return 0 ; }/*
@@ -941,8 +940,11 @@ xfs_growfs_rt( goto out_unlock; rsum_cache = mp->m_rsum_cache; -if (nrbmblocks != sbp->sb_rbmblocks) -xfs_alloc_rsum_cache (mp, nrbmblocks); +if (nrbmblocks != sbp->sb_rbmblocks) { + error =xfs_alloc_rsum_cache (mp, nrbmblocks); +if (error) + goto out_unlock; + }/* * Allocate a new (fake) mount/sb.
@@ -1271,7 +1273,9 @@ xfs_rtmount_inodes(if (error) goto out_rele_summary; -xfs_alloc_rsum_cache (mp, sbp->sb_rbmblocks); + error =xfs_alloc_rsum_cache (mp, sbp->sb_rbmblocks); +if (error) + goto out_rele_summary;xfs_trans_cancel (tp);return 0 ;
PATCH 4/12 xfs: remove the limit argument to xfs_rtfind_back
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
All callers pass a 0 limit to xfs_rtfind_back, so remove the argument and hard code it.
--- fs/xfs/libxfs/xfs_rtbitmap.c | 9 ++++----- fs/xfs/libxfs/xfs_rtbitmap.h | 2 +- fs/xfs/xfs_rtalloc.c | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-)
fs/xfs/libxfs/xfs_rtbitmap.c +4 -5
@@ -139,14 +139,13 @@ xfs_rtbuf_get( }/* -* Searching backward from start to limit, find the first block whose -* allocated/free state is different from start's. +* Searching backward from start find the first block whose allocated/free state +* is different from start's. */ int xfs_rtfind_back (struct xfs_rtalloc_args *args,xfs_rtxnum_t start,/* starting rtext to look at */ -xfs_rtxnum_t limit,/* last rtext to look at */ xfs_rtxnum_t *rtx)/* out: start rtext found */ {struct xfs_mount *mp = args->mp;
@@ -175,7 +174,7 @@ xfs_rtfind_back(*/ word =xfs_rtx_to_rbmword (mp, start); bit = (int )(start & (XFS_NBWORD -1 )); - len = start - limit +1 ; + len = start +1 ;/* * Compute match value, based on the bit at start: if 1 (free) * then all-ones, else all-zeroes.
@@ -698,7 +697,7 @@ xfs_rtfree_range(* We need to find the beginning and end of the extent so we can * properly update the summary. */ - error =xfs_rtfind_back (args, start,0 , &preblock); + error =xfs_rtfind_back (args, start, &preblock);if (error) {return error; }
fs/xfs/libxfs/xfs_rtbitmap.h +1 -1
@@ -316,7 +316,7 @@ xfs_rtsummary_read_buf(int xfs_rtcheck_range (struct xfs_rtalloc_args *args,xfs_rtxnum_t start,xfs_rtxlen_t len,int val,xfs_rtxnum_t *new,int *stat);int xfs_rtfind_back (struct xfs_rtalloc_args *args,xfs_rtxnum_t start, -xfs_rtxnum_t limit,xfs_rtxnum_t *rtblock); +xfs_rtxnum_t *rtblock);int xfs_rtfind_forw (struct xfs_rtalloc_args *args,xfs_rtxnum_t start,xfs_rtxnum_t limit,xfs_rtxnum_t *rtblock); int xfs_rtmodify_range(struct xfs_rtalloc_args *args, xfs_rtxnum_t start,
fs/xfs/xfs_rtalloc.c +1 -1
@@ -144,7 +144,7 @@ xfs_rtallocate_range(* We need to find the beginning and end of the extent so we can * properly update the summary. */ - error =xfs_rtfind_back (args, start,0 , &preblock); + error =xfs_rtfind_back (args, start, &preblock);if (error)return error;
PATCH 5/12 xfs: assert a valid limit in xfs_rtfind_forw
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Protect against developers passing stupid limits when refactoring the RT code once again.
--- fs/xfs/libxfs/xfs_rtbitmap.c | 2 ++ 1 file changed, 2 insertions(+)
fs/xfs/libxfs/xfs_rtbitmap.c +2 -0
@@ -315,6 +315,8 @@ xfs_rtfind_forw(xfs_rtword_t incore;unsigned int word;/* word number in the buffer */ +ASSERT (start <= limit); +/* * Compute and read in starting bitmap block for starting block. */
PATCH 6/12 xfs: add bounds checking to xfs_rt{bitmap,summary}_read_buf
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Add a corruption check for passing an invalid block number, which is a lot easier to understand than the xfs_bmapi_read failure later on.
--- fs/xfs/libxfs/xfs_rtbitmap.c | 31 ++++++++++++++++++++++++++++++- fs/xfs/libxfs/xfs_rtbitmap.h | 22 ++-------------------- 2 files changed, 32 insertions(+), 21 deletions(-)
fs/xfs/libxfs/xfs_rtbitmap.c +30 -1
@@ -69,7 +69,7 @@ xfs_rtbuf_cache_relse(* Get a buffer for the bitmap or summary file block specified. * The buffer is returned read and locked. */ -int +static int xfs_rtbuf_get( struct xfs_rtalloc_args *args, xfs_fileoff_t block, /* block number in bitmap or summary */
@@ -138,6 +138,35 @@ xfs_rtbuf_get(return 0 ; } +int +xfs_rtbitmap_read_buf ( +struct xfs_rtalloc_args *args, +xfs_fileoff_t block) +{ +struct xfs_mount *mp = args->mp; + +if (XFS_IS_CORRUPT (mp, block >= mp->m_sb.sb_rbmblocks)) { +xfs_rt_mark_sick (mp,XFS_SICK_RT_BITMAP ); +return -EFSCORRUPTED ; + } + +return xfs_rtbuf_get (args, block,0 ); +} + +int +xfs_rtsummary_read_buf ( +struct xfs_rtalloc_args *args, +xfs_fileoff_t block) +{ +struct xfs_mount *mp = args->mp; + +if (XFS_IS_CORRUPT (mp, block >=XFS_B_TO_FSB (mp, mp->m_rsumsize))) { +xfs_rt_mark_sick (args->mp,XFS_SICK_RT_SUMMARY ); +return -EFSCORRUPTED ; + } +return xfs_rtbuf_get (args, block,1 ); +} +/* * Searching backward from start find the first block whose allocated/free state * is different from start's.
fs/xfs/libxfs/xfs_rtbitmap.h +2 -20
@@ -293,26 +293,8 @@ typedef int (*xfs_rtalloc_query_range_fn)(#ifdef CONFIG_XFS_RT void xfs_rtbuf_cache_relse (struct xfs_rtalloc_args *args); - -int xfs_rtbuf_get (struct xfs_rtalloc_args *args,xfs_fileoff_t block, -int issum); - -static inline int -xfs_rtbitmap_read_buf ( -struct xfs_rtalloc_args *args, -xfs_fileoff_t block) -{ -return xfs_rtbuf_get (args, block,0 ); -} - -static inline int -xfs_rtsummary_read_buf ( -struct xfs_rtalloc_args *args, -xfs_fileoff_t block) -{ -return xfs_rtbuf_get (args, block,1 ); -} - +int xfs_rtbitmap_read_buf (struct xfs_rtalloc_args *args,xfs_fileoff_t block); +int xfs_rtsummary_read_buf (struct xfs_rtalloc_args *args,xfs_fileoff_t block);int xfs_rtcheck_range (struct xfs_rtalloc_args *args,xfs_rtxnum_t start,xfs_rtxlen_t len,int val,xfs_rtxnum_t *new,int *stat); int xfs_rtfind_back(struct xfs_rtalloc_args *args, xfs_rtxnum_t start,
PATCH 7/12 xfs: cleanup the calling convention for xfs_rtpick_extent
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
xfs_rtpick_extent never returns an error. Do away with the error return and directly return the picked extent instead of doing that through a call by reference argument.
--- fs/xfs/xfs_rtalloc.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-)
fs/xfs/xfs_rtalloc.c +4 -8
@@ -1306,12 +1306,11 @@ xfs_rtunmount_inodes(* of rtextents and the fraction. * The fraction sequence is 0, 1/2, 1/4, 3/4, 1/8, ..., 7/8, 1/16, ... */ -static int +static xfs_rtxnum_t xfs_rtpick_extent (xfs_mount_t *mp,/* file system mount point */ xfs_trans_t *tp,/* transaction pointer */ -xfs_rtxlen_t len,/* allocation length (rtextents) */ -xfs_rtxnum_t *pick)/* result rt extent */ +xfs_rtxlen_t len)/* allocation length (rtextents) */ {xfs_rtxnum_t b;/* result rtext */ int log2;/* log of sequence number */
@@ -1342,8 +1341,7 @@ xfs_rtpick_extent( ts.tv_sec = seq +1 ;inode_set_atime_to_ts (VFS_I (mp->m_rbmip), ts);xfs_trans_log_inode (tp, mp->m_rbmip,XFS_ILOG_CORE ); - *pick = b; -return 0 ; +return b; } static void
@@ -1450,9 +1448,7 @@ xfs_bmap_rtalloc(* If it's an allocation to an empty file at offset 0, pick an * extent that will space things out in the rt area. */ - error =xfs_rtpick_extent (mp, ap->tp, ralen, &start); -if (error) -return error; + start =xfs_rtpick_extent (mp, ap->tp, ralen); }else { start =0 ; }
PATCH 8/12 xfs: push the calls to xfs_rtallocate_range out to xfs_bmap_rtalloc
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Currently the various low-level RT allocator functions call into xfs_rtallocate_range directly, which ties them into the locking protocol for the RT bitmap. As these helpers already return the allocated range, lift the call to xfs_rtallocate_range into xfs_bmap_rtalloc so that it happens as high as possible in the stack, which will simplify future changes to the locking protocol.
--- fs/xfs/xfs_rtalloc.c | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-)
fs/xfs/xfs_rtalloc.c +18 -20
@@ -261,9 +261,9 @@ xfs_rtallocate_extent_block(/* * i for maxlen is all free, allocate and return that. */ - bestlen = maxlen; - besti = i; - goto allocate; + *len = maxlen; + *rtx = i; +return 0 ; }/*
@@ -314,12 +314,8 @@ xfs_rtallocate_extent_block( }/* -* Allocate besti for bestlen & return that. +* Pick besti for bestlen & return that. */ -allocate: - error =xfs_rtallocate_range (args, besti, bestlen); -if (error) -return error; *len = bestlen; *rtx = besti;return 0 ;
@@ -373,12 +369,6 @@ xfs_rtallocate_extent_exact( } } -/* -* Allocate what we can and return it. -*/ - error =xfs_rtallocate_range (args, start, maxlen); -if (error) -return error; *len = maxlen; *rtx = start;return 0 ;
@@ -431,7 +421,6 @@ xfs_rtallocate_extent_near(if (error != -ENOSPC )return error; - bbno =xfs_rtx_to_rbmblock (mp, start); i =0 ; j =-1 ;
@@ -554,11 +543,11 @@ xfs_rtalloc_sumlevel( xfs_rtxnum_t *rtx) /* out: start rtext allocated */ {xfs_fileoff_t i;/* bitmap block number */ +int error;for (i =0 ; i < args->mp->m_sb.sb_rbmblocks; i++) {xfs_suminfo_t sum;/* summary information for extents */ xfs_rtxnum_t n;/* next rtext to be tried */ -int error; error =xfs_rtget_summary (args, l, i, &sum);if (error)
@@ -1473,9 +1462,12 @@ xfs_bmap_rtalloc( error =xfs_rtallocate_extent_size (&args, raminlen, ralen, &ralen, prod, &rtx); } -xfs_rtbuf_cache_relse (&args); -if (error == -ENOSPC ) { +if (error) { +xfs_rtbuf_cache_relse (&args); +if (error != -ENOSPC ) +return error; +if (align > mp->m_sb.sb_rextsize) {/* * We previously enlarged the request length to try to
@@ -1503,14 +1495,20 @@ xfs_bmap_rtalloc( ap->length =0 ;return 0 ; } + + error =xfs_rtallocate_range (&args, rtx, ralen);if (error) -return error; + goto out_release;xfs_trans_mod_sb (ap->tp, ap->wasdel ?XFS_TRANS_SB_RES_FREXTENTS :XFS_TRANS_SB_FREXTENTS , -(long )ralen); + ap->blkno =xfs_rtx_to_rtb (mp, rtx); ap->length =xfs_rtxlen_to_extlen (mp, ralen);xfs_bmap_alloc_account (ap); -return 0 ; + +out_release: +xfs_rtbuf_cache_relse (&args); +return error; }
PATCH 9/12 xfs: factor out a xfs_growfs_rt_bmblock helper
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Add a helper to contain the per-rtbitmap block logic in xfs_growfs_rt.
Note that this helper now allocates a new fake mount structure for each rtbitmap block iteration instead of reusing the memory for an entire growfs call. Compared to all the other work done when freeing the blocks the overhead for this is in the noise and it keeps the code nicely modular.
--- fs/xfs/xfs_rtalloc.c | 317 +++++++++++++++++++++++++------------------------- 1 file changed, 158 insertions(+), 159 deletions(-)
fs/xfs/xfs_rtalloc.c +158 -159
@@ -807,9 +807,148 @@ xfs_growfs_rt_fixup_extsize(return error; } -/* -* Visible (exported) functions. -*/ +static int +xfs_growfs_rt_bmblock ( +struct xfs_mount *mp, +xfs_rfsblock_t nrblocks, +xfs_agblock_t rextsize, +xfs_fileoff_t bmbno) +{ +struct xfs_inode *rbmip = mp->m_rbmip; +struct xfs_inode *rsumip = mp->m_rsumip; +struct xfs_rtalloc_args args = { + .mp = mp, + }; +struct xfs_rtalloc_args nargs = { + }; +struct xfs_mount *nmp; +xfs_rfsblock_t nrblocks_step; +xfs_rtbxlen_t freed_rtx; +int error; + + + nrblocks_step = (bmbno +1 ) *NBBY * mp->m_sb.sb_blocksize * rextsize; + + nmp = nargs.mp =kmemdup (mp,sizeof (*mp),GFP_KERNEL ); +if (!nmp) +return -ENOMEM ; + +/* +* Calculate new sb and mount fields for this round. +*/ + nmp->m_rtxblklog =-1 ;/* don't use shift or masking */ + nmp->m_sb.sb_rextsize = rextsize; + nmp->m_sb.sb_rbmblocks = bmbno +1 ; + nmp->m_sb.sb_rblocks =min (nrblocks, nrblocks_step); + nmp->m_sb.sb_rextents =xfs_rtb_to_rtx (nmp, nmp->m_sb.sb_rblocks); + nmp->m_sb.sb_rextslog =xfs_compute_rextslog (nmp->m_sb.sb_rextents); + nmp->m_rsumlevels = nmp->m_sb.sb_rextslog +1 ; + nmp->m_rsumsize =XFS_FSB_TO_B (mp, +xfs_rtsummary_blockcount (mp, nmp->m_rsumlevels, + nmp->m_sb.sb_rbmblocks)); + +/* recompute growfsrt reservation from new rsumsize */ +xfs_trans_resv_calc (nmp, &nmp->m_resv); + + error =xfs_trans_alloc (mp, &M_RES (mp)->tr_growrtfree,0 ,0 ,0 , + &args.tp); +if (error) + goto out_free; + nargs.tp = args.tp; + +xfs_rtbitmap_lock (args.tp, mp); + +/* +* Update the bitmap inode's size ondisk and incore. We need to update +* the incore size so that inode inactivation won't punch what it thinks +* are "posteof" blocks. +*/ + rbmip->i_disk_size = nmp->m_sb.sb_rbmblocks * nmp->m_sb.sb_blocksize; +i_size_write (VFS_I (rbmip), rbmip->i_disk_size); +xfs_trans_log_inode (args.tp, rbmip,XFS_ILOG_CORE ); + +/* +* Update the summary inode's size. We need to update the incore size +* so that inode inactivation won't punch what it thinks are "posteof" +* blocks. +*/ + rsumip->i_disk_size = nmp->m_rsumsize; +i_size_write (VFS_I (rsumip), rsumip->i_disk_size); +xfs_trans_log_inode (args.tp, rsumip,XFS_ILOG_CORE ); + +/* +* Copy summary data from old to new sizes when the real size (not +* block-aligned) changes. +*/ +if (mp->m_sb.sb_rbmblocks != nmp->m_sb.sb_rbmblocks || + mp->m_rsumlevels != nmp->m_rsumlevels) { + error =xfs_rtcopy_summary (&args, &nargs); +if (error) + goto out_cancel; + } + +/* +* Update superblock fields. +*/ +if (nmp->m_sb.sb_rextsize != mp->m_sb.sb_rextsize) +xfs_trans_mod_sb (args.tp,XFS_TRANS_SB_REXTSIZE , + nmp->m_sb.sb_rextsize - mp->m_sb.sb_rextsize); +if (nmp->m_sb.sb_rbmblocks != mp->m_sb.sb_rbmblocks) +xfs_trans_mod_sb (args.tp,XFS_TRANS_SB_RBMBLOCKS , + nmp->m_sb.sb_rbmblocks - mp->m_sb.sb_rbmblocks); +if (nmp->m_sb.sb_rblocks != mp->m_sb.sb_rblocks) +xfs_trans_mod_sb (args.tp,XFS_TRANS_SB_RBLOCKS , + nmp->m_sb.sb_rblocks - mp->m_sb.sb_rblocks); +if (nmp->m_sb.sb_rextents != mp->m_sb.sb_rextents) +xfs_trans_mod_sb (args.tp,XFS_TRANS_SB_REXTENTS , + nmp->m_sb.sb_rextents - mp->m_sb.sb_rextents); +if (nmp->m_sb.sb_rextslog != mp->m_sb.sb_rextslog) +xfs_trans_mod_sb (args.tp,XFS_TRANS_SB_REXTSLOG , + nmp->m_sb.sb_rextslog - mp->m_sb.sb_rextslog); + +/* +* Free the new extent. +*/ + freed_rtx = nmp->m_sb.sb_rextents - mp->m_sb.sb_rextents; + error =xfs_rtfree_range (&nargs, mp->m_sb.sb_rextents, freed_rtx); +xfs_rtbuf_cache_relse (&nargs); +if (error) + goto out_cancel; + +/* +* Mark more blocks free in the superblock. +*/ +xfs_trans_mod_sb (args.tp,XFS_TRANS_SB_FREXTENTS , freed_rtx); + +/* +* Update mp values into the real mp structure. +*/ + mp->m_rsumlevels = nmp->m_rsumlevels; + mp->m_rsumsize = nmp->m_rsumsize; + +/* +* Recompute the growfsrt reservation from the new rsumsize. +*/ +xfs_trans_resv_calc (mp, &mp->m_resv); + + error =xfs_trans_commit (args.tp); +if (error) + goto out_free; + +/* +* Ensure the mount RT feature flag is now set. +*/ + mp->m_features |=XFS_FEAT_REALTIME ; + +kfree (nmp); +return 0 ; + +out_cancel: +xfs_trans_cancel (args.tp); +out_free: +kfree (nmp); +return error; +}/* * Grow the realtime area of the filesystem.
@@ -822,23 +961,14 @@ xfs_growfs_rt(xfs_fileoff_t bmbno;/* bitmap block number */ struct xfs_buf *bp;/* temporary buffer */ int error;/* error return value */ -xfs_mount_t *nmp;/* new (fake) mount structure */ -xfs_rfsblock_t nrblocks;/* new number of realtime blocks */ xfs_extlen_t nrbmblocks;/* new number of rt bitmap blocks */ xfs_rtxnum_t nrextents;/* new number of realtime extents */ -uint8_t nrextslog;/* new log2 of sb_rextents */ xfs_extlen_t nrsumblocks;/* new number of summary blocks */ -uint nrsumlevels;/* new rt summary levels */ -uint nrsumsize;/* new size of rt summary, bytes */ -xfs_sb_t *nsbp;/* new superblock */ xfs_extlen_t rbmblocks;/* current number of rt bitmap blocks */ xfs_extlen_t rsumblocks;/* current number of rt summary blks */ -xfs_sb_t *sbp;/* old superblock */ uint8_t *rsum_cache;/* old summary cache */ xfs_agblock_t old_rextsize = mp->m_sb.sb_rextsize; - sbp = &mp->m_sb; -if (!capable (CAP_SYS_ADMIN ))return -EPERM ;
@@ -857,11 +987,10 @@ xfs_growfs_rt( goto out_unlock;/* Shrink not supported. */ -if (in->newblocks <= sbp->sb_rblocks) +if (in->newblocks <= mp->m_sb.sb_rblocks) goto out_unlock; -/* Can only change rt extent size when adding rt volume. */ -if (sbp->sb_rblocks >0 && in->extsize != sbp->sb_rextsize) +if (mp->m_sb.sb_rblocks >0 && in->extsize != mp->m_sb.sb_rextsize) goto out_unlock;/* Range check the extent size. */
@@ -874,15 +1003,14 @@ xfs_growfs_rt(if (xfs_has_rmapbt (mp) ||xfs_has_reflink (mp) ||xfs_has_quota (mp)) goto out_unlock; - nrblocks = in->newblocks; - error =xfs_sb_validate_fsb_count (sbp, nrblocks); + error =xfs_sb_validate_fsb_count (&mp->m_sb, in->newblocks);if (error) goto out_unlock;/* * Read in the last block of the device, make sure it exists. */ error =xfs_buf_read_uncached (mp->m_rtdev_targp, -XFS_FSB_TO_BB (mp, nrblocks -1 ), +XFS_FSB_TO_BB (mp, in->newblocks -1 ),XFS_FSB_TO_BB (mp,1 ),0 , &bp,NULL );if (error) goto out_unlock;
@@ -891,17 +1019,15 @@ xfs_growfs_rt(/* * Calculate new parameters. These are the final values to be reached. */ - nrextents = nrblocks; -do_div (nrextents, in->extsize); + nrextents =div_u64 (in->newblocks, in->extsize);if (nrextents ==0 ) { error = -EINVAL ; goto out_unlock; } nrbmblocks =xfs_rtbitmap_blockcount (mp, nrextents); - nrextslog =xfs_compute_rextslog (nrextents); - nrsumlevels = nrextslog +1 ; - nrsumblocks =xfs_rtsummary_blockcount (mp, nrsumlevels, nrbmblocks); - nrsumsize =XFS_FSB_TO_B (mp, nrsumblocks); + nrsumblocks =xfs_rtsummary_blockcount (mp, +xfs_compute_rextslog (nrextents) +1 , nrbmblocks); +/* * New summary size can't be more than half the size of * the log. This prevents us from getting a log overflow,
@@ -929,149 +1055,27 @@ xfs_growfs_rt( goto out_unlock; rsum_cache = mp->m_rsum_cache; -if (nrbmblocks != sbp->sb_rbmblocks) { +if (nrbmblocks != mp->m_sb.sb_rbmblocks) { error =xfs_alloc_rsum_cache (mp, nrbmblocks);if (error) goto out_unlock; } -/* -* Allocate a new (fake) mount/sb. -*/ - nmp =kmalloc (sizeof (*nmp),GFP_KERNEL | __GFP_NOFAIL);/* * Loop over the bitmap blocks. * We will do everything one bitmap block at a time. * Skip the current block if it is exactly full. * This also deals with the case where there were no rtextents before. */ -for (bmbno = sbp->sb_rbmblocks - - ((sbp->sb_rextents & ((1 << mp->m_blkbit_log) -1 )) !=0 ); - bmbno < nrbmblocks; - bmbno++) { -struct xfs_rtalloc_args args = { - .mp = mp, - }; -struct xfs_rtalloc_args nargs = { - .mp = nmp, - }; -struct xfs_trans *tp; -xfs_rfsblock_t nrblocks_step; - - *nmp = *mp; - nsbp = &nmp->m_sb; -/* -* Calculate new sb and mount fields for this round. -*/ - nsbp->sb_rextsize = in->extsize; - nmp->m_rtxblklog =-1 ;/* don't use shift or masking */ - nsbp->sb_rbmblocks = bmbno +1 ; - nrblocks_step = (bmbno +1 ) *NBBY * nsbp->sb_blocksize * - nsbp->sb_rextsize; - nsbp->sb_rblocks =min (nrblocks, nrblocks_step); - nsbp->sb_rextents =xfs_rtb_to_rtx (nmp, nsbp->sb_rblocks); -ASSERT (nsbp->sb_rextents !=0 ); - nsbp->sb_rextslog =xfs_compute_rextslog (nsbp->sb_rextents); - nrsumlevels = nmp->m_rsumlevels = nsbp->sb_rextslog +1 ; - nrsumblocks =xfs_rtsummary_blockcount (mp, nrsumlevels, - nsbp->sb_rbmblocks); - nmp->m_rsumsize = nrsumsize =XFS_FSB_TO_B (mp, nrsumblocks); -/* recompute growfsrt reservation from new rsumsize */ -xfs_trans_resv_calc (nmp, &nmp->m_resv); - -/* -* Start a transaction, get the log reservation. -*/ - error =xfs_trans_alloc (mp, &M_RES (mp)->tr_growrtfree,0 ,0 ,0 , - &tp); + bmbno = mp->m_sb.sb_rbmblocks; +if (xfs_rtx_to_rbmword (mp, mp->m_sb.sb_rextents) !=0 ) + bmbno--; +for (; bmbno < nrbmblocks; bmbno++) { + error =xfs_growfs_rt_bmblock (mp, in->newblocks, in->extsize, + bmbno);if (error) -break ; - args.tp = tp; - nargs.tp = tp; - -/* -* Lock out other callers by grabbing the bitmap and summary -* inode locks and joining them to the transaction. -*/ -xfs_rtbitmap_lock (tp, mp); -/* -* Update the bitmap inode's size ondisk and incore. We need -* to update the incore size so that inode inactivation won't -* punch what it thinks are "posteof" blocks. -*/ - mp->m_rbmip->i_disk_size = - nsbp->sb_rbmblocks * nsbp->sb_blocksize; -i_size_write (VFS_I (mp->m_rbmip), mp->m_rbmip->i_disk_size); -xfs_trans_log_inode (tp, mp->m_rbmip,XFS_ILOG_CORE ); -/* -* Update the summary inode's size. We need to update the -* incore size so that inode inactivation won't punch what it -* thinks are "posteof" blocks. -*/ - mp->m_rsumip->i_disk_size = nmp->m_rsumsize; -i_size_write (VFS_I (mp->m_rsumip), mp->m_rsumip->i_disk_size); -xfs_trans_log_inode (tp, mp->m_rsumip,XFS_ILOG_CORE ); -/* -* Copy summary data from old to new sizes. -* Do this when the real size (not block-aligned) changes. -*/ -if (sbp->sb_rbmblocks != nsbp->sb_rbmblocks || - mp->m_rsumlevels != nmp->m_rsumlevels) { - error =xfs_rtcopy_summary (&args, &nargs); -if (error) - goto error_cancel; - } -/* -* Update superblock fields. -*/ -if (nsbp->sb_rextsize != sbp->sb_rextsize) -xfs_trans_mod_sb (tp,XFS_TRANS_SB_REXTSIZE , - nsbp->sb_rextsize - sbp->sb_rextsize); -if (nsbp->sb_rbmblocks != sbp->sb_rbmblocks) -xfs_trans_mod_sb (tp,XFS_TRANS_SB_RBMBLOCKS , - nsbp->sb_rbmblocks - sbp->sb_rbmblocks); -if (nsbp->sb_rblocks != sbp->sb_rblocks) -xfs_trans_mod_sb (tp,XFS_TRANS_SB_RBLOCKS , - nsbp->sb_rblocks - sbp->sb_rblocks); -if (nsbp->sb_rextents != sbp->sb_rextents) -xfs_trans_mod_sb (tp,XFS_TRANS_SB_REXTENTS , - nsbp->sb_rextents - sbp->sb_rextents); -if (nsbp->sb_rextslog != sbp->sb_rextslog) -xfs_trans_mod_sb (tp,XFS_TRANS_SB_REXTSLOG , - nsbp->sb_rextslog - sbp->sb_rextslog); -/* -* Free new extent. -*/ - error =xfs_rtfree_range (&nargs, sbp->sb_rextents, - nsbp->sb_rextents - sbp->sb_rextents); -xfs_rtbuf_cache_relse (&nargs); -if (error) { -error_cancel: -xfs_trans_cancel (tp); -break ; - } -/* -* Mark more blocks free in the superblock. -*/ -xfs_trans_mod_sb (tp,XFS_TRANS_SB_FREXTENTS , - nsbp->sb_rextents - sbp->sb_rextents); -/* -* Update mp values into the real mp structure. -*/ - mp->m_rsumlevels = nrsumlevels; - mp->m_rsumsize = nrsumsize; -/* recompute growfsrt reservation from new rsumsize */ -xfs_trans_resv_calc (mp, &mp->m_resv); - - error =xfs_trans_commit (tp); -if (error) -break ; - -/* Ensure the mount RT feature flag is now set. */ - mp->m_features |=XFS_FEAT_REALTIME ; + goto out_free; } -if (error) - goto out_free;if (old_rextsize != in->extsize) { error =xfs_growfs_rt_fixup_extsize (mp);
@@ -1083,11 +1087,6 @@ xfs_growfs_rt( error =xfs_update_secondary_sbs (mp); out_free: -/* -* Free the fake mp structure. -*/ -kfree (nmp); -/* * If we had to allocate a new rsum_cache, we either need to free the * old one (if we succeeded) or free the new one and restore the old one
PATCH 10/12 xfs: factor out a xfs_last_rt_bmblock helper
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Add helper to calculate the last currently used rt bitmap block to better structure the growfs code and prepare for future changes to it.
--- fs/xfs/xfs_rtalloc.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-)
fs/xfs/xfs_rtalloc.c +19 -10
@@ -950,6 +950,23 @@ xfs_growfs_rt_bmblock(return error; } +/* +* Calculate the last rbmblock currently used. +* +* This also deals with the case where there were no rtextents before. +*/ +static xfs_fileoff_t +xfs_last_rt_bmblock ( +struct xfs_mount *mp) +{ +xfs_fileoff_t bmbno = mp->m_sb.sb_rbmblocks; + +/* Skip the current block if it is exactly full. */ +if (xfs_rtx_to_rbmword (mp, mp->m_sb.sb_rextents) !=0 ) + bmbno--; +return bmbno; +} +/* * Grow the realtime area of the filesystem. */
@@ -1061,16 +1078,8 @@ xfs_growfs_rt( goto out_unlock; } -/* -* Loop over the bitmap blocks. -* We will do everything one bitmap block at a time. -* Skip the current block if it is exactly full. -* This also deals with the case where there were no rtextents before. -*/ - bmbno = mp->m_sb.sb_rbmblocks; -if (xfs_rtx_to_rbmword (mp, mp->m_sb.sb_rextents) !=0 ) - bmbno--; -for (; bmbno < nrbmblocks; bmbno++) { +/* Initialize the free space bitmap one bitmap block at a time. */ +for (bmbno =xfs_last_rt_bmblock (mp); bmbno < nrbmblocks; bmbno++) { error =xfs_growfs_rt_bmblock (mp, in->newblocks, in->extsize, bmbno);if (error)
PATCH 11/12 xfs: factor out rtbitmap/summary initialization helpers
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Add helpers to libxfs that can be shared by growfs and mkfs for initializing the rtbitmap and summary, and by passing the optional data pointer also by repair for rebuilding them. This will become even more useful when the rtgroups feature adds a metadata header to each block, which means even more shared code.
--- fs/xfs/libxfs/xfs_rtbitmap.c | 126 ++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_rtbitmap.h | 3 + fs/xfs/xfs_rtalloc.c | 121 +--------------------------------------- 3 files changed, 133 insertions(+), 117 deletions(-)
fs/xfs/libxfs/xfs_rtbitmap.c +126 -0
@@ -13,6 +13,8 @@#include "xfs_mount.h" #include "xfs_inode.h" #include "xfs_bmap.h" +#include "xfs_bmap_btree.h" +#include "xfs_trans_space.h" #include "xfs_trans.h" #include "xfs_rtalloc.h" #include "xfs_error.h"
@@ -1255,3 +1257,127 @@ xfs_rtbitmap_unlock_shared(if (rbmlock_flags &XFS_RBMLOCK_BITMAP )xfs_iunlock (mp->m_rbmip,XFS_ILOCK_SHARED |XFS_ILOCK_RTBITMAP ); } + +static int +xfs_rtfile_alloc_blocks ( +struct xfs_inode *ip, +xfs_fileoff_t offset_fsb, +xfs_filblks_t count_fsb, +struct xfs_bmbt_irec *map) +{ +struct xfs_mount *mp = ip->i_mount; +struct xfs_trans *tp; +int nmap =1 ; +int error; + + error =xfs_trans_alloc (mp, &M_RES (mp)->tr_growrtalloc, +XFS_GROWFSRT_SPACE_RES (mp, count_fsb),0 ,0 , &tp); +if (error) +return error; + +xfs_ilock (ip,XFS_ILOCK_EXCL ); +xfs_trans_ijoin (tp, ip,XFS_ILOCK_EXCL ); + + error =xfs_iext_count_extend (tp, ip,XFS_DATA_FORK , +XFS_IEXT_ADD_NOSPLIT_CNT ); +if (error) + goto out_trans_cancel; + + error =xfs_bmapi_write (tp, ip, offset_fsb, count_fsb, +XFS_BMAPI_METADATA ,0 , map, &nmap); +if (error) + goto out_trans_cancel; + +return xfs_trans_commit (tp); + +out_trans_cancel: +xfs_trans_cancel (tp); +return error; +} + +/* Get a buffer for the block. */ +static int +xfs_rtfile_initialize_block ( +struct xfs_inode *ip, +xfs_fsblock_t fsbno, +void *data) +{ +struct xfs_mount *mp = ip->i_mount; +struct xfs_trans *tp; +struct xfs_buf *bp; +const size_t copylen = mp->m_blockwsize <<XFS_WORDLOG ; +enum xfs_blft buf_type; +int error; + +if (ip == mp->m_rsumip) + buf_type =XFS_BLFT_RTSUMMARY_BUF ; +else + buf_type =XFS_BLFT_RTBITMAP_BUF ; + + error =xfs_trans_alloc (mp, &M_RES (mp)->tr_growrtzero,0 ,0 ,0 , &tp); +if (error) +return error; +xfs_ilock (ip,XFS_ILOCK_EXCL ); +xfs_trans_ijoin (tp, ip,XFS_ILOCK_EXCL ); + + error =xfs_trans_get_buf (tp, mp->m_ddev_targp, +XFS_FSB_TO_DADDR (mp, fsbno), mp->m_bsize,0 , &bp); +if (error) { +xfs_trans_cancel (tp); +return error; + } + +xfs_trans_buf_set_type (tp, bp, buf_type); + bp->b_ops = &xfs_rtbuf_ops; +if (data) +memcpy (bp->b_addr, data, copylen); +else +memset (bp->b_addr,0 , copylen); +xfs_trans_log_buf (tp, bp,0 , mp->m_sb.sb_blocksize -1 ); +return xfs_trans_commit (tp); +} + +/* +* Allocate space to the bitmap or summary file, and zero it, for growfs. +* @data must be a contiguous buffer large enough to fill all blocks in the +* file; or NULL to initialize the contents to zeroes. +*/ +int +xfs_rtfile_initialize_blocks ( +struct xfs_inode *ip,/* inode (bitmap/summary) */ +xfs_fileoff_t offset_fsb,/* offset to start from */ +xfs_fileoff_t end_fsb,/* offset to allocate to */ +void *data)/* data to fill the blocks */ +{ +struct xfs_mount *mp = ip->i_mount; +const size_t copylen = mp->m_blockwsize <<XFS_WORDLOG ; + +while (offset_fsb < end_fsb) { +struct xfs_bmbt_irec map; +xfs_filblks_t i; +int error; + + error =xfs_rtfile_alloc_blocks (ip, offset_fsb, + end_fsb - offset_fsb, &map); +if (error) +return error; + +/* +* Now we need to clear the allocated blocks. +* +* Do this one block per transaction, to keep it simple. +*/ +for (i =0 ; i < map.br_blockcount; i++) { + error =xfs_rtfile_initialize_block (ip, + map.br_startblock + i, data); +if (error) +return error; +if (data) + data += copylen; + } + + offset_fsb = map.br_startoff + map.br_blockcount; + } + +return 0 ; +}
fs/xfs/libxfs/xfs_rtbitmap.h +3 -0
@@ -343,6 +343,9 @@ xfs_filblks_t xfs_rtsummary_blockcount(struct xfs_mount *mp,unsigned long long xfs_rtsummary_wordcount (struct xfs_mount *mp,unsigned int rsumlevels,xfs_extlen_t rbmblocks); +int xfs_rtfile_initialize_blocks (struct xfs_inode *ip, +xfs_fileoff_t offset_fsb,xfs_fileoff_t end_fsb,void *data); +void xfs_rtbitmap_lock (struct xfs_trans *tp,struct xfs_mount *mp);void xfs_rtbitmap_unlock (struct xfs_mount *mp);
fs/xfs/xfs_rtalloc.c +4 -117
135 lines not on this page · raw message
PATCH 12/12 xfs: push transaction join out of xfs_rtbitmap_lock and xfs_rtgroup_lock
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
To prepare for being able to join an already locked rtbitmap inode to a transaction split out separate helpers for joining the transaction from the locking helpers.
--- fs/xfs/libxfs/xfs_bmap.c | 3 ++- fs/xfs/libxfs/xfs_rtbitmap.c | 24 +++++++++++++----------- fs/xfs/libxfs/xfs_rtbitmap.h | 6 ++++-- fs/xfs/xfs_rtalloc.c | 6 ++++-- 4 files changed, 23 insertions(+), 16 deletions(-)
fs/xfs/libxfs/xfs_bmap.c +2 -1
@@ -5379,7 +5379,8 @@ xfs_bmap_del_extent_real(*/ if (!(tp->t_flags &XFS_TRANS_RTBITMAP_LOCKED )) { tp->t_flags |=XFS_TRANS_RTBITMAP_LOCKED ; -xfs_rtbitmap_lock (tp, mp); +xfs_rtbitmap_lock (mp); +xfs_rtbitmap_trans_join (tp); } error =xfs_rtfree_blocks (tp, del->br_startblock, del->br_blockcount);
fs/xfs/libxfs/xfs_rtbitmap.c +13 -11
@@ -1201,23 +1201,25 @@ xfs_rtsummary_wordcount(return XFS_FSB_TO_B (mp, blocks) >>XFS_WORDLOG ; } -/* -* Lock both realtime free space metadata inodes for a freespace update. If a -* transaction is given, the inodes will be joined to the transaction and the -* ILOCKs will be released on transaction commit. -*/ +/* Lock both realtime free space metadata inodes for a freespace update. */ void xfs_rtbitmap_lock ( -struct xfs_trans *tp,struct xfs_mount *mp) {xfs_ilock (mp->m_rbmip,XFS_ILOCK_EXCL |XFS_ILOCK_RTBITMAP ); -if (tp) -xfs_trans_ijoin (tp, mp->m_rbmip,XFS_ILOCK_EXCL ); -xfs_ilock (mp->m_rsumip,XFS_ILOCK_EXCL |XFS_ILOCK_RTSUM ); -if (tp) -xfs_trans_ijoin (tp, mp->m_rsumip,XFS_ILOCK_EXCL ); +} + +/* +* Join both realtime free space metadata inodes to the transaction. The +* ILOCKs will be released on transaction commit. +*/ +void +xfs_rtbitmap_trans_join ( +struct xfs_trans *tp) +{ +xfs_trans_ijoin (tp, tp->t_mountp->m_rbmip,XFS_ILOCK_EXCL ); +xfs_trans_ijoin (tp, tp->t_mountp->m_rsumip,XFS_ILOCK_EXCL ); }/* Unlock both realtime free space metadata inodes after a freespace update. */
fs/xfs/libxfs/xfs_rtbitmap.h +4 -2
@@ -346,8 +346,9 @@ unsigned long long xfs_rtsummary_wordcount(struct xfs_mount *mp,int xfs_rtfile_initialize_blocks (struct xfs_inode *ip,xfs_fileoff_t offset_fsb,xfs_fileoff_t end_fsb,void *data); -void xfs_rtbitmap_lock (struct xfs_trans *tp,struct xfs_mount *mp); +void xfs_rtbitmap_lock (struct xfs_mount *mp);void xfs_rtbitmap_unlock (struct xfs_mount *mp); +void xfs_rtbitmap_trans_join (struct xfs_trans *tp);/* Lock the rt bitmap inode in shared mode */ #define XFS_RBMLOCK_BITMAP (1U << 0)
@@ -376,7 +377,8 @@ xfs_rtbitmap_blockcount(struct xfs_mount *mp, xfs_rtbxlen_t rtextents)# define xfs_rtbitmap_wordcount (mp, r) (0)# define xfs_rtsummary_blockcount (mp, l, b) (0)# define xfs_rtsummary_wordcount (mp, l, b) (0) -# define xfs_rtbitmap_lock (tp, mp) do { } while (0) +# define xfs_rtbitmap_lock (mp) do { } while (0) +# define xfs_rtbitmap_trans_join (tp) do { } while (0)# define xfs_rtbitmap_unlock (mp) do { } while (0)# define xfs_rtbitmap_lock_shared (mp, lf) do { } while (0)# define xfs_rtbitmap_unlock_shared (mp, lf) do { } while (0)
fs/xfs/xfs_rtalloc.c +4 -2
@@ -741,7 +741,8 @@ xfs_growfs_rt_bmblock( goto out_free; nargs.tp = args.tp; -xfs_rtbitmap_lock (args.tp, mp); +xfs_rtbitmap_lock (mp); +xfs_rtbitmap_trans_join (args.tp);/* * Update the bitmap inode's size ondisk and incore. We need to update
@@ -1319,7 +1320,8 @@ xfs_bmap_rtalloc(* Lock out modifications to both the RT bitmap and summary inodes */ if (!rtlocked) { -xfs_rtbitmap_lock (ap->tp, mp); +xfs_rtbitmap_lock (mp); +xfs_rtbitmap_trans_join (ap->tp); rtlocked = true; }
PATCH 6/10 xfs: fixes and cleanups for the realtime allocator
In reply to Darrick J. WongHi all,
While I was reviewing how to integrate realtime allocation groups with the rt allocator, I noticed several bugs in the existing allocation code with regards to calculating the maximum range of rtx to scan for free space. This series fixes those range bugs and cleans up a few things too.
I also added a few cleanups from Christoph.
If you're going to start using this code, I strongly recommend pulling from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy! Comments and questions are, as always, welcome.
--D
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=rtalloc-fixes
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=rtalloc-fixes
---
Commits in this patchset:
* xfs: use the recalculated transaction reservation in xfs_growfs_rt_bmblock
* xfs: ensure rtx mask/shift are correct after growfs
* xfs: don't return too-short extents from xfs_rtallocate_extent_block
* xfs: don't scan off the end of the rt volume in xfs_rtallocate_extent_block
* xfs: refactor aligning bestlen to prod
* xfs: clean up xfs_rtallocate_extent_exact a bit
* xfs: reduce excessive clamping of maxlen in xfs_rtallocate_extent_near
* xfs: fix broken variable-sized allocation detection in xfs_rtallocate_extent_block
* xfs: remove xfs_rtb_to_rtxrem
* xfs: simplify xfs_rtalloc_query_range
--- fs/xfs/libxfs/xfs_rtbitmap.c | 51 ++++++--------- fs/xfs/libxfs/xfs_rtbitmap.h | 21 ------ fs/xfs/libxfs/xfs_sb.c | 12 +++ fs/xfs/libxfs/xfs_sb.h | 2 + fs/xfs/xfs_discard.c | 15 ++-- fs/xfs/xfs_fsmap.c | 11 +-- fs/xfs/xfs_rtalloc.c | 145 +++++++++++++++++++++++------------------- 7 files changed, 124 insertions(+), 133 deletions(-)
PATCH 1/10 xfs: use the recalculated transaction reservation in xfs_growfs_rt_bmblock
From: Christoph Hellwig <[email protected]>
After going great length to calculate the transaction reservation for the new geometry, we should also use it to allocate the transaction it was calculated for.
--- fs/xfs/xfs_rtalloc.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-)
fs/xfs/xfs_rtalloc.c +5 -3
@@ -732,10 +732,12 @@ xfs_growfs_rt_bmblock(xfs_rtsummary_blockcount (mp, nmp->m_rsumlevels, nmp->m_sb.sb_rbmblocks)); -/* recompute growfsrt reservation from new rsumsize */ +/* +* Recompute the growfsrt reservation from the new rsumsize, so that the +* transaction below use the new, potentially larger value. +* */ xfs_trans_resv_calc (nmp, &nmp->m_resv); - - error =xfs_trans_alloc (mp, &M_RES (mp)->tr_growrtfree,0 ,0 ,0 , + error =xfs_trans_alloc (mp, &M_RES (nmp)->tr_growrtfree,0 ,0 ,0 , &args.tp);if (error) goto out_free;
PATCH 2/10 xfs: ensure rtx mask/shift are correct after growfs
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
When growfs sets an extent size, it doesn't updated the m_rtxblklog and m_rtxblkmask values, which could lead to incorrect usage of them if they were set before and can't be used for the new extent size.
Add a xfs_mount_sb_set_rextsize helper that updates the two fields, and also use it when calculating the new RT geometry instead of disabling the optimization there.
--- fs/xfs/libxfs/xfs_sb.c | 12 ++++++++++-- fs/xfs/libxfs/xfs_sb.h | 2 ++ fs/xfs/xfs_rtalloc.c | 5 +++-- 3 files changed, 15 insertions(+), 4 deletions(-)
fs/xfs/libxfs/xfs_sb.c +10 -2
@@ -975,6 +975,15 @@ const struct xfs_buf_ops xfs_sb_quiet_buf_ops = { .verify_write = xfs_sb_write_verify, }; +void +xfs_mount_sb_set_rextsize ( +struct xfs_mount *mp, +struct xfs_sb *sbp) +{ + mp->m_rtxblklog =log2_if_power2 (sbp->sb_rextsize); + mp->m_rtxblkmask =mask64_if_power2 (sbp->sb_rextsize); +} +/* * xfs_mount_common *
@@ -999,8 +1008,7 @@ xfs_sb_mount_common( mp->m_blockmask = sbp->sb_blocksize -1 ; mp->m_blockwsize = sbp->sb_blocksize >>XFS_WORDLOG ; mp->m_blockwmask = mp->m_blockwsize -1 ; - mp->m_rtxblklog =log2_if_power2 (sbp->sb_rextsize); - mp->m_rtxblkmask =mask64_if_power2 (sbp->sb_rextsize); +xfs_mount_sb_set_rextsize (mp, sbp); mp->m_alloc_mxr[0 ] =xfs_allocbt_maxrecs (mp, sbp->sb_blocksize,1 ); mp->m_alloc_mxr[1 ] =xfs_allocbt_maxrecs (mp, sbp->sb_blocksize,0 );
fs/xfs/libxfs/xfs_sb.h +2 -0
@@ -17,6 +17,8 @@ extern void xfs_log_sb(struct xfs_trans *tp);extern int xfs_sync_sb (struct xfs_mount *mp,bool wait);extern int xfs_sync_sb_buf (struct xfs_mount *mp);extern void xfs_sb_mount_common (struct xfs_mount *mp,struct xfs_sb *sbp); +void xfs_mount_sb_set_rextsize (struct xfs_mount *mp, +struct xfs_sb *sbp);extern void xfs_sb_from_disk (struct xfs_sb *to,struct xfs_dsb *from);extern void xfs_sb_to_disk (struct xfs_dsb *to,struct xfs_sb *from);extern void xfs_sb_quota_from_disk (struct xfs_sb *sbp);
fs/xfs/xfs_rtalloc.c +3 -2
@@ -721,8 +721,8 @@ xfs_growfs_rt_bmblock(/* * Calculate new sb and mount fields for this round. */ - nmp->m_rtxblklog =-1 ;/* don't use shift or masking */ nmp->m_sb.sb_rextsize = rextsize; +xfs_mount_sb_set_rextsize (nmp, &nmp->m_sb); nmp->m_sb.sb_rbmblocks = bmbno +1 ; nmp->m_sb.sb_rblocks =min (nrblocks, nrblocks_step); nmp->m_sb.sb_rextents =xfs_rtb_to_rtx (nmp, nmp->m_sb.sb_rblocks);
@@ -809,10 +809,11 @@ xfs_growfs_rt_bmblock(xfs_trans_mod_sb (args.tp,XFS_TRANS_SB_FREXTENTS , freed_rtx);/* -* Update mp values into the real mp structure. +* Update the calculated values in the real mount structure. */ mp->m_rsumlevels = nmp->m_rsumlevels; mp->m_rsumsize = nmp->m_rsumsize; +xfs_mount_sb_set_rextsize (mp, &mp->m_sb);/* * Recompute the growfsrt reservation from the new rsumsize.
PATCH 3/10 xfs: don't return too-short extents from xfs_rtallocate_extent_block
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
If xfs_rtallocate_extent_block is asked for a variable-sized allocation, it will try to return the best-sized free extent, which is apparently the largest one that it finds starting in this rtbitmap block. It will then trim the size of the extent as needed to align it with prod.
However, it misses one thing -- rounding down the best-fit candidate to the required alignment could make the extent shorter than minlen. In the case where minlen > 1, we'd rather the caller relaxed its alignment requirements and tried again, as the allocator already supports that.
Returning a too-short extent that causes xfs_bmapi_write to return ENOSR if there aren't enough nmaps to handle multiple new allocations, which can then cause filesystem shutdowns.
I haven't seen this happen on any production systems, but then I don't think it's very common to set a per-file extent size hint on realtime files. I tripped it while working on the rtgroups feature and pounding on the realtime allocator enthusiastically.
--- fs/xfs/xfs_rtalloc.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-)
fs/xfs/xfs_rtalloc.c +11 -10
@@ -291,16 +291,9 @@ xfs_rtallocate_extent_block(return error; } -/* -* Searched the whole thing & didn't find a maxlen free extent. -*/ -if (minlen > maxlen || besti ==-1 ) { -/* -* Allocation failed. Set *nextp to the next block to try. -*/ - *nextp = next; -return -ENOSPC ; - } +/* Searched the whole thing & didn't find a maxlen free extent. */ +if (minlen > maxlen || besti ==-1 ) + goto nospace;/* * If size should be a multiple of prod, make that so.
@@ -313,12 +306,20 @@ xfs_rtallocate_extent_block( bestlen -= p; } +/* Don't return a too-short extent. */ +if (bestlen < minlen) + goto nospace; +/* * Pick besti for bestlen & return that. */ *len = bestlen; *rtx = besti;return 0 ; +nospace: +/* Allocation failed. Set *nextp to the next block to try. */ + *nextp = next; +return -ENOSPC ; }/*
Re: [PATCH 03/10] xfs: don't return too-short extents from xfs_rtallocate_extent_block
Looks good:
PATCH 4/10 xfs: don't scan off the end of the rt volume in xfs_rtallocate_extent_block
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
The loop conditional here is not quite correct because an rtbitmap block can represent rtextents beyond the end of the rt volume. There's no way that it makes sense to scan for free space beyond EOFS, so don't do it. This overrun has been present since v2.6.0.
Also fix the type of bestlen, which was incorrectly converted.
--- fs/xfs/xfs_rtalloc.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-)
fs/xfs/xfs_rtalloc.c +7 -9
@@ -231,22 +231,20 @@ xfs_rtallocate_extent_block( xfs_rtxnum_t *rtx) /* out: start rtext allocated */ {struct xfs_mount *mp = args->mp; -xfs_rtxnum_t besti;/* best rtext found so far */ -xfs_rtxnum_t bestlen;/* best length found so far */ +xfs_rtxnum_t besti =-1 ;/* best rtext found so far */ xfs_rtxnum_t end;/* last rtext in chunk */ -int error;xfs_rtxnum_t i;/* current rtext trying */ xfs_rtxnum_t next;/* next rtext to try */ +xfs_rtxlen_t bestlen =0 ;/* best length found so far */ int stat;/* status from internal calls */ +int error;/* -* Loop over all the extents starting in this bitmap block, -* looking for one that's long enough. +* Loop over all the extents starting in this bitmap block up to the +* end of the rt volume, looking for one that's long enough. */ -for (i =xfs_rbmblock_to_rtx (mp, bbno), besti =-1 , bestlen =0 , - end =xfs_rbmblock_to_rtx (mp, bbno +1 ) -1 ; - i <= end; - i++) { + end =min (mp->m_sb.sb_rextents,xfs_rbmblock_to_rtx (mp, bbno +1 )) -1 ; +for (i =xfs_rbmblock_to_rtx (mp, bbno); i <= end; i++) {/* Make sure we don't scan off the end of the rt volume. */ maxlen =xfs_rtallocate_clamp_len (mp, i, maxlen, prod);
Re: [PATCH 04/10] xfs: don't scan off the end of the rt volume in xfs_rtallocate_extent_block
Looks good:
PATCH 5/10 xfs: refactor aligning bestlen to prod
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
There are two places in xfs_rtalloc.c where we want to make sure that a count of rt extents is aligned with a particular prod(uct) factor. In one spot, we actually use rounddown(), albeit unnecessarily if prod < 2. In the other case, we open-code this rounding inefficiently by promoting the 32-bit length value to a 64-bit value and then performing a 64-bit division to figure out the subtraction.
Refactor this into a single helper that uses the correct types and division method for the type, and skips the division entirely unless prod is large enough to make a difference.
--- fs/xfs/xfs_rtalloc.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-)
fs/xfs/xfs_rtalloc.c +15 -11
@@ -196,6 +196,17 @@ xfs_rtallocate_range(return xfs_rtmodify_range (args, start, len,0 ); } +/* Reduce @rtxlen until it is a multiple of @prod. */ +static inline xfs_rtxlen_t +xfs_rtalloc_align_len ( +xfs_rtxlen_t rtxlen, +xfs_rtxlen_t prod) +{ +if (unlikely (prod >1 )) +return rounddown (rtxlen, prod); +return rtxlen; +} +/* * Make sure we don't run off the end of the rt volume. Be careful that * adjusting maxlen downwards doesn't cause us to fail the alignment checks.
@@ -210,7 +221,7 @@ xfs_rtallocate_clamp_len(xfs_rtxlen_t ret; ret =min (mp->m_sb.sb_rextents, startrtx + rtxlen) - startrtx; -return rounddown (ret, prod); +return xfs_rtalloc_align_len (ret, prod); }/*
@@ -294,17 +305,10 @@ xfs_rtallocate_extent_block( goto nospace;/* -* If size should be a multiple of prod, make that so. +* Ensure bestlen is a multiple of prod, but don't return a too-short +* extent. */ -if (prod >1 ) { -xfs_rtxlen_t p;/* amount to trim length by */ - -div_u64_rem (bestlen, prod, &p); -if (p) - bestlen -= p; - } - -/* Don't return a too-short extent. */ + bestlen =xfs_rtalloc_align_len (bestlen, prod);if (bestlen < minlen) goto nospace;
Re: [PATCH 05/10] xfs: refactor aligning bestlen to prod
Looks good:
PATCH 6/10 xfs: clean up xfs_rtallocate_extent_exact a bit
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Before we start doing more surgery on the rt allocator, let's clean up the exact allocator so that it doesn't change its arguments and uses the helper introduced in the previous patch.
--- fs/xfs/xfs_rtalloc.c | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-)
fs/xfs/xfs_rtalloc.c +21 -20
@@ -340,10 +340,10 @@ xfs_rtallocate_extent_exact(xfs_rtxlen_t prod,/* extent product factor */ xfs_rtxnum_t *rtx) /* out: start rtext allocated */ { - int error; -xfs_rtxlen_t i;/* extent length trimmed due to prod */ -int isfree;/* extent is free */ xfs_rtxnum_t next; /* next rtext to try (dummy) */ +xfs_rtxlen_t alloclen;/* candidate length */ +int isfree;/* extent is free */ +int error;ASSERT (minlen % prod ==0 );ASSERT (maxlen % prod ==0 );
@@ -354,25 +354,26 @@ xfs_rtallocate_extent_exact(if (error)return error; -if (!isfree) { -/* -* If not, allocate what there is, if it's at least minlen. -*/ - maxlen = next - start; -if (maxlen < minlen) -return -ENOSPC ; - -/* -* Trim off tail of extent, if prod is specified. -*/ -if (prod >1 && (i = maxlen % prod)) { - maxlen -= i; -if (maxlen < minlen) -return -ENOSPC ; - } +if (isfree) { +/* start to maxlen is all free; allocate it. */ + *len = maxlen; + *rtx = start; +return 0 ; } - *len = maxlen; +/* +* If not, allocate what there is, if it's at least minlen. +*/ + alloclen = next - start; +if (alloclen < minlen) +return -ENOSPC ; + +/* Ensure alloclen is a multiple of prod. */ + alloclen =xfs_rtalloc_align_len (alloclen, prod); +if (alloclen < minlen) +return -ENOSPC ; + + *len = alloclen; *rtx = start;return 0 ; }
Re: [PATCH 06/10] xfs: clean up xfs_rtallocate_extent_exact a bit
Looks good:
PATCH 7/10 xfs: reduce excessive clamping of maxlen in xfs_rtallocate_extent_near
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
The near rt allocator employs two allocation strategies -- first it tries to allocate at exactly @start. If that fails, it will pivot back and forth around that starting point looking for an appropriately sized free space.
However, I clamped maxlen ages ago to prevent the exact allocation scan from running off the end of the rt volume. This, I realize, was excessive. If the allocation request is (say) for 32 rtx but the start position is 5 rtx from the end of the volume, we clamp maxlen to 5. If the exact allocation fails, we then pivot back and forth looking for 5 rtx, even though the original intent was to try to get 32 rtx.
If we then find 5 rtx when we could have gotten 32 rtx, we've not done as well as we could have. This may be moot if the caller immediately comes back for more space, but it might not be. Either way, we can do better here.
--- fs/xfs/xfs_rtalloc.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-)
fs/xfs/xfs_rtalloc.c +12 -11
@@ -340,23 +340,29 @@ xfs_rtallocate_extent_exact(xfs_rtxlen_t prod,/* extent product factor */ xfs_rtxnum_t *rtx) /* out: start rtext allocated */ { + struct xfs_mount *mp = args->mp;xfs_rtxnum_t next;/* next rtext to try (dummy) */ xfs_rtxlen_t alloclen;/* candidate length */ +xfs_rtxlen_t scanlen;/* number of free rtx to look for */ int isfree;/* extent is free */ int error;ASSERT (minlen % prod ==0 );ASSERT (maxlen % prod ==0 ); -/* -* Check if the range in question (for maxlen) is free. -*/ - error =xfs_rtcheck_range (args, start, maxlen,1 , &next, &isfree); + +/* Make sure we don't run off the end of the rt volume. */ + scanlen =xfs_rtallocate_clamp_len (mp, start, maxlen, prod); +if (scanlen < minlen) +return -ENOSPC ; + +/* Check if the range in question (for scanlen) is free. */ + error =xfs_rtcheck_range (args, start, scanlen,1 , &next, &isfree);if (error)return error;if (isfree) { -/* start to maxlen is all free; allocate it. */ - *len = maxlen; +/* start to scanlen is all free; allocate it. */ + *len = scanlen; *rtx = start;return 0 ; }
@@ -412,11 +418,6 @@ xfs_rtallocate_extent_near(if (start >= mp->m_sb.sb_rextents) start = mp->m_sb.sb_rextents -1 ; -/* Make sure we don't run off the end of the rt volume. */ - maxlen =xfs_rtallocate_clamp_len (mp, start, maxlen, prod); -if (maxlen < minlen) -return -ENOSPC ; -/* * Try the exact allocation first. */
Re: [PATCH 07/10] xfs: reduce excessive clamping of maxlen in xfs_rtallocate_extent_near
Looks good:
PATCH 8/10 xfs: fix broken variable-sized allocation detection in xfs_rtallocate_extent_block
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
This function tries to find a suitable free space extent starting from a particular rtbitmap block. Some time ago, I added a clamping function to prevent the free space scans from running off the end of the bitmap, but I didn't quite get the logic right.
Let's say there's an allocation request with a minlen of 5 and a maxlen of 32 and we're scanning the last rtbitmap block. If we come within 4 rtx of the end of the rt volume, maxlen will get clamped to 4. If the next 3 rtx are free, we could have satisfied the allocation, but the code setting partial besti/bestlen for "minlen < maxlen" will think that we're doing a non-variable allocation and ignore it.
The root of this problem is overwriting maxlen; I should have stuffed the results in a different variable, which would not have introduced this bug.
--- fs/xfs/xfs_rtalloc.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
fs/xfs/xfs_rtalloc.c +9 -6
@@ -246,6 +246,7 @@ xfs_rtallocate_extent_block(xfs_rtxnum_t end;/* last rtext in chunk */ xfs_rtxnum_t i;/* current rtext trying */ xfs_rtxnum_t next;/* next rtext to try */ +xfs_rtxlen_t scanlen;/* number of free rtx to look for */ xfs_rtxlen_t bestlen =0 ;/* best length found so far */ int stat;/* status from internal calls */ int error;
@@ -257,20 +258,22 @@ xfs_rtallocate_extent_block( end =min (mp->m_sb.sb_rextents,xfs_rbmblock_to_rtx (mp, bbno +1 )) -1 ;for (i =xfs_rbmblock_to_rtx (mp, bbno); i <= end; i++) {/* Make sure we don't scan off the end of the rt volume. */ - maxlen =xfs_rtallocate_clamp_len (mp, i, maxlen, prod); + scanlen =xfs_rtallocate_clamp_len (mp, i, maxlen, prod); +if (scanlen < minlen) +break ;/* -* See if there's a free extent of maxlen starting at i. +* See if there's a free extent of scanlen starting at i. * If it's not so then next will contain the first non-free. */ - error =xfs_rtcheck_range (args, i, maxlen,1 , &next, &stat); + error =xfs_rtcheck_range (args, i, scanlen,1 , &next, &stat);if (error)return error;if (stat) {/* -* i for maxlen is all free, allocate and return that. +* i to scanlen is all free, allocate and return that. */ - *len = maxlen; + *len = scanlen; *rtx = i;return 0 ; }
@@ -301,7 +304,7 @@ xfs_rtallocate_extent_block( }/* Searched the whole thing & didn't find a maxlen free extent. */ -if (minlen > maxlen || besti ==-1 ) +if (besti ==-1 ) goto nospace;/*
Re: [PATCH 08/10] xfs: fix broken variable-sized allocation detection in xfs_rtallocate_extent_block
Looks:
PATCH 9/10 xfs: remove xfs_rtb_to_rtxrem
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Simplify the number of block number conversion helpers by removing xfs_rtb_to_rtxrem. Any recent compiler is smart enough to eliminate the double divisions if using separate xfs_rtb_to_rtx and xfs_rtb_to_rtxoff calls.
--- fs/xfs/libxfs/xfs_rtbitmap.c | 9 ++++----- fs/xfs/libxfs/xfs_rtbitmap.h | 18 ------------------ 2 files changed, 4 insertions(+), 23 deletions(-)
fs/xfs/libxfs/xfs_rtbitmap.c +4 -5
@@ -1022,25 +1022,24 @@ xfs_rtfree_blocks( xfs_filblks_t rtlen) {struct xfs_mount *mp = tp->t_mountp; -xfs_rtxnum_t start; -xfs_filblks_t len;xfs_extlen_t mod;ASSERT (rtlen <=XFS_MAX_BMBT_EXTLEN ); - len =xfs_rtb_to_rtxrem (mp, rtlen, &mod); + mod =xfs_rtb_to_rtxoff (mp, rtlen);if (mod) {ASSERT (mod ==0 );return -EIO ; } - start =xfs_rtb_to_rtxrem (mp, rtbno, &mod); + mod =xfs_rtb_to_rtxoff (mp, rtbno);if (mod) {ASSERT (mod ==0 );return -EIO ; } -return xfs_rtfree_extent (tp, start, len); +return xfs_rtfree_extent (tp,xfs_rtb_to_rtx (mp, rtbno), +xfs_rtb_to_rtx (mp, rtlen)); }/* Find all the free records within a given range. */
fs/xfs/libxfs/xfs_rtbitmap.h +0 -18
@@ -86,24 +86,6 @@ xfs_rtb_to_rtxoff(return do_div (rtbno, mp->m_sb.sb_rextsize); } -/* -* Crack an rt block number into an rt extent number and an offset within that -* rt extent. Returns the rt extent number directly and the offset in @off. -*/ -static inline xfs_rtxnum_t -xfs_rtb_to_rtxrem ( -struct xfs_mount *mp, -xfs_rtblock_t rtbno, -xfs_extlen_t *off) -{ -if (likely (mp->m_rtxblklog >=0 )) { - *off = rtbno & mp->m_rtxblkmask; -return rtbno >> mp->m_rtxblklog; - } - -return div_u64_rem (rtbno, mp->m_sb.sb_rextsize, off); -} -/* * Convert an rt block number into an rt extent number, rounding up to the next * rt extent if the rt block is not aligned to an rt extent boundary.
PATCH 10/10 xfs: simplify xfs_rtalloc_query_range
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
There isn't much of a good reason to pass the xfs_rtalloc_rec structures that describe extents to xfs_rtalloc_query_range as we really just want a lower and upper bound xfs_rtxnum_t. Pass the rtxnum directly and simply the interface.
--- fs/xfs/libxfs/xfs_rtbitmap.c | 42 +++++++++++++++++------------------------- fs/xfs/libxfs/xfs_rtbitmap.h | 3 +-- fs/xfs/xfs_discard.c | 15 +++++++-------- fs/xfs/xfs_fsmap.c | 11 +++++------ 4 files changed, 30 insertions(+), 41 deletions(-)
fs/xfs/libxfs/xfs_rtbitmap.c +17 -25
@@ -1047,8 +1047,8 @@ intxfs_rtalloc_query_range ( struct xfs_mount *mp, struct xfs_trans *tp, - const struct xfs_rtalloc_rec *low_rec, - const struct xfs_rtalloc_rec *high_rec, + xfs_rtxnum_t start, + xfs_rtxnum_t end, xfs_rtalloc_query_range_fn fn, void *priv) {
@@ -1056,45 +1056,42 @@ xfs_rtalloc_query_range( .mp = mp, .tp = tp, }; -struct xfs_rtalloc_rec rec; -xfs_rtxnum_t rtstart; -xfs_rtxnum_t rtend; -xfs_rtxnum_t high_key; -int is_free;int error =0 ; -if (low_rec->ar_startext > high_rec->ar_startext) +if (start > end)return -EINVAL ; -if (low_rec->ar_startext >= mp->m_sb.sb_rextents || - low_rec->ar_startext == high_rec->ar_startext) +if (start == end || start >= mp->m_sb.sb_rextents)return 0 ; - high_key =min (high_rec->ar_startext, mp->m_sb.sb_rextents -1 ); + end =min (end, mp->m_sb.sb_rextents -1 );/* Iterate the bitmap, looking for discrepancies. */ - rtstart = low_rec->ar_startext; -while (rtstart <= high_key) { +while (start <= end) { +struct xfs_rtalloc_rec rec; +int is_free; +xfs_rtxnum_t rtend; +/* Is the first block free? */ - error =xfs_rtcheck_range (&args, rtstart,1 ,1 , &rtend, + error =xfs_rtcheck_range (&args, start,1 ,1 , &rtend, &is_free);if (error)break ;/* How long does the extent go for? */ - error =xfs_rtfind_forw (&args, rtstart, high_key, &rtend); + error =xfs_rtfind_forw (&args, start, end, &rtend);if (error)break ;if (is_free) { - rec.ar_startext = rtstart; - rec.ar_extcount = rtend - rtstart +1 ; + rec.ar_startext = start; + rec.ar_extcount = rtend - start +1 ; error =fn (mp, tp, &rec, priv);if (error)break ; } - rtstart = rtend +1 ; + start = rtend +1 ; }xfs_rtbuf_cache_relse (&args);
@@ -1109,13 +1106,8 @@ xfs_rtalloc_query_all(xfs_rtalloc_query_range_fn fn, void *priv) { - struct xfs_rtalloc_rec keys[2]; - - keys[0 ].ar_startext =0 ; - keys[1 ].ar_startext = mp->m_sb.sb_rextents -1 ; - keys[0 ].ar_extcount = keys[1 ].ar_extcount =0 ; - -return xfs_rtalloc_query_range (mp, tp, &keys[0 ], &keys[1 ], fn, priv); + return xfs_rtalloc_query_range(mp, tp, 0, mp->m_sb.sb_rextents - 1, fn, +priv ); }/* Is the given extent all free? */
fs/xfs/libxfs/xfs_rtbitmap.h +1 -2
@@ -292,8 +292,7 @@ int xfs_rtmodify_summary(struct xfs_rtalloc_args *args, int log,int xfs_rtfree_range (struct xfs_rtalloc_args *args,xfs_rtxnum_t start,xfs_rtxlen_t len);int xfs_rtalloc_query_range (struct xfs_mount *mp,struct xfs_trans *tp, -const struct xfs_rtalloc_rec *low_rec, -const struct xfs_rtalloc_rec *high_rec, +xfs_rtxnum_t start,xfs_rtxnum_t end,xfs_rtalloc_query_range_fn fn,void *priv); int xfs_rtalloc_query_all(struct xfs_mount *mp, struct xfs_trans *tp, xfs_rtalloc_query_range_fn fn,
fs/xfs/xfs_discard.c +7 -8
@@ -554,11 +554,10 @@ xfs_trim_rtdev_extents( xfs_daddr_t end, xfs_daddr_t minlen) { -struct xfs_rtalloc_rec low = { }; -struct xfs_rtalloc_rec high = { };struct xfs_trim_rtdev tr = { .minlen_fsb =XFS_BB_TO_FSB (mp, minlen), }; +xfs_rtxnum_t low, high;struct xfs_trans *tp;xfs_daddr_t rtdev_daddr;int error;
@@ -584,17 +583,17 @@ xfs_trim_rtdev_extents( XFS_FSB_TO_BB(mp, mp->m_sb.sb_rblocks) - 1);/* Convert the rt blocks to rt extents */ - low.ar_startext =xfs_rtb_to_rtxup (mp,XFS_BB_TO_FSB (mp, start)); - high.ar_startext =xfs_rtb_to_rtx (mp,XFS_BB_TO_FSBT (mp, end)); + low =xfs_rtb_to_rtxup (mp,XFS_BB_TO_FSB (mp, start)); + high =xfs_rtb_to_rtx (mp,XFS_BB_TO_FSBT (mp, end));/* * Walk the free ranges between low and high. The query_range function * trims the extents returned. */ do { - tr.stop_rtx = low.ar_startext + (mp->m_sb.sb_blocksize * NBBY); + tr.stop_rtx = low + (mp->m_sb.sb_blocksize * NBBY); xfs_rtbitmap_lock_shared(mp, XFS_RBMLOCK_BITMAP); - error = xfs_rtalloc_query_range(mp, tp, &low, &high, + error = xfs_rtalloc_query_range(mp, tp, low, high, xfs_trim_gather_rtextent, &tr); if (error == -ECANCELED)
@@ -615,8 +614,8 @@ xfs_trim_rtdev_extents(if (error)break ; - low.ar_startext = tr.restart_rtx; - }while (!xfs_trim_should_stop () && low.ar_startext <= high.ar_startext); + low = tr.restart_rtx; + }while (!xfs_trim_should_stop () && low <= high);xfs_trans_cancel (tp);return error;
fs/xfs/xfs_fsmap.c +5 -6
@@ -520,11 +520,11 @@ xfs_getfsmap_rtdev_rtbitmap( struct xfs_getfsmap_info *info) { -struct xfs_rtalloc_rec alow = {0 };struct xfs_rtalloc_rec ahigh = {0 };struct xfs_mount *mp = tp->t_mountp;xfs_rtblock_t start_rtb;xfs_rtblock_t end_rtb; +xfs_rtxnum_t high;uint64_t eofs;int error;
@@ -553,10 +553,9 @@ xfs_getfsmap_rtdev_rtbitmap(* Set up query parameters to return free rtextents covering the range * we want. */ - alow.ar_startext =xfs_rtb_to_rtx (mp, start_rtb); - ahigh.ar_startext =xfs_rtb_to_rtxup (mp, end_rtb); - error =xfs_rtalloc_query_range (mp, tp, &alow, &ahigh, - xfs_getfsmap_rtdev_rtbitmap_helper, info); + high =xfs_rtb_to_rtxup (mp, end_rtb); + error =xfs_rtalloc_query_range (mp, tp,xfs_rtb_to_rtx (mp, start_rtb), + high, xfs_getfsmap_rtdev_rtbitmap_helper, info);if (error) goto err;
@@ -565,7 +564,7 @@ xfs_getfsmap_rtdev_rtbitmap(* rmap starting at the block after the end of the query range. */ info->last = true; - ahigh.ar_startext =min (mp->m_sb.sb_rextents, ahigh.ar_startext); + ahigh.ar_startext =min (mp->m_sb.sb_rextents, high); error =xfs_getfsmap_rtdev_rtbitmap_helper (mp, tp, &ahigh, info);if (error)
PATCH 7/10 xfs: create incore rt allocation groups
In reply to Darrick J. WongHi all,
This series adds in-memory data structures for sharding the realtime volume into independent allocation groups. For existing filesystems, the entire rt volume is modelled as having a single large group, with (potentially) a number of rt extents exceeding 2^32, though these are not likely to exist because the codebase has been a bit broken for decades. The next series fills in the ondisk format and other supporting structures.
If you're going to start using this code, I strongly recommend pulling from my git trees, which are linked below.
This has been running on the djcloud for months with no problems. Enjoy! Comments and questions are, as always, welcome.
--D
kernel git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfs-linux.git/log/?h=incore-rtgroups
xfsprogs git tree:
https://git.kernel.org/cgit/linux/kernel/git/djwong/xfsprogs-dev.git/log/?h=incore-rtgroups
---
Commits in this patchset:
* xfs: clean up the ISVALID macro in xfs_bmap_adjacent
* xfs: factor out a xfs_rtallocate helper
* xfs: rework the rtalloc fallback handling
* xfs: factor out a xfs_rtallocate_align helper
* xfs: make the rtalloc start hint a xfs_rtblock_t
* xfs: add xchk_setup_nothing and xchk_nothing helpers
* xfs: remove xfs_{rtbitmap,rtsummary}_wordcount
* xfs: replace m_rsumsize with m_rsumblocks
* xfs: rearrange xfs_fsmap.c a little bit
* xfs: move xfs_ioc_getfsmap out of xfs_ioctl.c
* xfs: create incore realtime group structures
* xfs: define locking primitives for realtime groups
* xfs: add a lockdep class key for rtgroup inodes
* xfs: support caching rtgroup metadata inodes
* xfs: add rtgroup-based realtime scrubbing context management
* xfs: move RT bitmap and summary information to the rtgroup
* xfs: remove XFS_ILOCK_RT*
* xfs: calculate RT bitmap and summary blocks based on sb_rextents
* xfs: factor out a xfs_growfs_rt_alloc_fake_mount helper
* xfs: use xfs_growfs_rt_alloc_fake_mount in xfs_growfs_rt_alloc_blocks
* xfs: factor out a xfs_growfs_check_rtgeom helper
* xfs: refactor xfs_rtbitmap_blockcount
* xfs: refactor xfs_rtsummary_blockcount
* xfs: make RT extent numbers relative to the rtgroup
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_bmap.c | 101 +++-- fs/xfs/libxfs/xfs_format.h | 3 fs/xfs/libxfs/xfs_rtbitmap.c | 222 +++++------ fs/xfs/libxfs/xfs_rtbitmap.h | 152 ++++---- fs/xfs/libxfs/xfs_rtgroup.c | 529 +++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_rtgroup.h | 268 ++++++++++++++ fs/xfs/libxfs/xfs_sb.c | 7 fs/xfs/libxfs/xfs_trans_resv.c | 4 fs/xfs/libxfs/xfs_types.h | 4 fs/xfs/scrub/bmap.c | 13 + fs/xfs/scrub/common.c | 78 ++++ fs/xfs/scrub/common.h | 59 ++- fs/xfs/scrub/fscounters.c | 26 + fs/xfs/scrub/repair.c | 24 + fs/xfs/scrub/repair.h | 7 fs/xfs/scrub/rtbitmap.c | 54 ++- fs/xfs/scrub/rtsummary.c | 118 +++--- fs/xfs/scrub/rtsummary.h | 2 fs/xfs/scrub/rtsummary_repair.c | 19 - fs/xfs/scrub/scrub.c | 33 ++ fs/xfs/scrub/scrub.h | 42 +- fs/xfs/xfs_discard.c | 100 +++-- fs/xfs/xfs_fsmap.c | 435 +++++++++++++++-------- fs/xfs/xfs_fsmap.h | 6 fs/xfs/xfs_inode.c | 3 fs/xfs/xfs_inode.h | 13 - fs/xfs/xfs_ioctl.c | 130 ------- fs/xfs/xfs_iomap.c | 4 fs/xfs/xfs_log_recover.c | 20 + fs/xfs/xfs_mount.c | 18 + fs/xfs/xfs_mount.h | 29 +- fs/xfs/xfs_qm.c | 27 + fs/xfs/xfs_rtalloc.c | 753 ++++++++++++++++++++++++--------------- fs/xfs/xfs_super.c | 4 fs/xfs/xfs_trace.c | 1 fs/xfs/xfs_trace.h | 38 ++ 37 files changed, 2326 insertions(+), 1021 deletions(-) create mode 100644 fs/xfs/libxfs/xfs_rtgroup.c create mode 100644 fs/xfs/libxfs/xfs_rtgroup.h
PATCH 1/24 xfs: clean up the ISVALID macro in xfs_bmap_adjacent
From: Christoph Hellwig <[email protected]>
Turn the ISVALID macro defined and used inside in xfs_bmap_adjacent that relies on implict context into a proper inline function.
--- fs/xfs/libxfs/xfs_bmap.c | 55 +++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 23 deletions(-)
fs/xfs/libxfs/xfs_bmap.c +32 -23
@@ -3115,6 +3115,23 @@ xfs_bmap_extsize_align(return 0 ; } +static inline bool +xfs_bmap_adjacent_valid ( +struct xfs_bmalloca *ap, +xfs_fsblock_t x, +xfs_fsblock_t y) +{ +struct xfs_mount *mp = ap->ip->i_mount; + +if (XFS_IS_REALTIME_INODE (ap->ip) && + (ap->datatype &XFS_ALLOC_USERDATA )) +return x < mp->m_sb.sb_rblocks; + +return XFS_FSB_TO_AGNO (mp, x) ==XFS_FSB_TO_AGNO (mp, y) && +XFS_FSB_TO_AGNO (mp, x) < mp->m_sb.sb_agcount && +XFS_FSB_TO_AGBNO (mp, x) < mp->m_sb.sb_agblocks; +} +#define XFS_ALLOC_GAP_UNITS 4/* returns true if ap->blkno was modified */
@@ -3122,36 +3139,25 @@ boolxfs_bmap_adjacent ( struct xfs_bmalloca *ap) /* bmap alloc argument struct */ { -xfs_fsblock_t adjust;/* adjustment to block numbers */ -xfs_mount_t *mp;/* mount point structure */ -int rt;/* true if inode is realtime */ +xfs_fsblock_t adjust;/* adjustment to block numbers */ -#define ISVALID (x,y) \ - (rt ? \ - (x) < mp->m_sb.sb_rblocks : \ - XFS_FSB_TO_AGNO(mp, x) == XFS_FSB_TO_AGNO(mp, y) && \ - XFS_FSB_TO_AGNO(mp, x) < mp->m_sb.sb_agcount && \ - XFS_FSB_TO_AGBNO(mp, x) < mp->m_sb.sb_agblocks) - - mp = ap->ip->i_mount; - rt =XFS_IS_REALTIME_INODE (ap->ip) && - (ap->datatype &XFS_ALLOC_USERDATA );/* * If allocating at eof, and there's a previous real block, * try to use its last block as our starting point. */ if (ap->eof && ap->prev.br_startoff !=NULLFILEOFF && !isnullstartblock (ap->prev.br_startblock) && -ISVALID (ap->prev.br_startblock + ap->prev.br_blockcount, - ap->prev.br_startblock)) { +xfs_bmap_adjacent_valid (ap, + ap->prev.br_startblock + ap->prev.br_blockcount, + ap->prev.br_startblock)) { ap->blkno = ap->prev.br_startblock + ap->prev.br_blockcount;/* * Adjust for the gap between prevp and us. */ adjust = ap->offset - (ap->prev.br_startoff + ap->prev.br_blockcount); -if (adjust && -ISVALID (ap->blkno + adjust, ap->prev.br_startblock)) +if (adjust &&xfs_bmap_adjacent_valid (ap, ap->blkno + adjust, + ap->prev.br_startblock)) ap->blkno += adjust;return true; }
@@ -3174,7 +3180,8 @@ xfs_bmap_adjacent( !isnullstartblock (ap->prev.br_startblock) && (prevbno = ap->prev.br_startblock + ap->prev.br_blockcount) && - ISVALID(prevbno, ap->prev.br_startblock)) { +xfs_bmap_adjacent_valid (ap, prevbno, + ap->prev.br_startblock)) {/* * Calculate gap to end of previous block. */
@@ -3190,8 +3197,8 @@ xfs_bmap_adjacent(* number, then just use the end of the previous block. */ if (prevdiff <=XFS_ALLOC_GAP_UNITS * ap->length && -ISVALID (prevbno + prevdiff, - ap->prev.br_startblock)) +xfs_bmap_adjacent_valid (ap, prevbno + prevdiff, + ap->prev.br_startblock)) prevbno += adjust;else prevdiff += adjust;
@@ -3223,9 +3230,11 @@ xfs_bmap_adjacent(* offset by our length. */ if (gotdiff <=XFS_ALLOC_GAP_UNITS * ap->length && -ISVALID (gotbno - gotdiff, gotbno)) +xfs_bmap_adjacent_valid (ap, gotbno - gotdiff, + gotbno)) gotbno -= adjust; -else if (ISVALID (gotbno - ap->length, gotbno)) { +else if (xfs_bmap_adjacent_valid (ap, gotbno - ap->length, + gotbno)) { gotbno -= ap->length; gotdiff += adjust - ap->length; }else
@@ -3253,7 +3262,7 @@ xfs_bmap_adjacent(return true; } } -#undef ISVALID +return false; }
PATCH 2/24 xfs: factor out a xfs_rtallocate helper
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Split out a helper from xfs_rtallocate that performs the actual allocation. This keeps the scope of the xfs_rtalloc_args structure contained, and prepares for rtgroups support.
--- fs/xfs/xfs_rtalloc.c | 81 +++++++++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 31 deletions(-)
fs/xfs/xfs_rtalloc.c +50 -31
@@ -1269,6 +1269,51 @@ xfs_rtalloc_align_minmax( *raminlen = newminlen; } +static int +xfs_rtallocate ( +struct xfs_trans *tp, +xfs_rtxnum_t start, +xfs_rtxlen_t minlen, +xfs_rtxlen_t maxlen, +xfs_rtxlen_t prod, +bool wasdel, +xfs_rtblock_t *bno, +xfs_extlen_t *blen) +{ +struct xfs_rtalloc_args args = { + .mp = tp->t_mountp, + .tp = tp, + }; +xfs_rtxnum_t rtx; +xfs_rtxlen_t len =0 ; +int error; + +if (start) { + error =xfs_rtallocate_extent_near (&args, start, minlen, maxlen, + &len, prod, &rtx); + }else { + error =xfs_rtallocate_extent_size (&args, minlen, maxlen, &len, + prod, &rtx); + } + +if (error) + goto out_release; + + error =xfs_rtallocate_range (&args, rtx, len); +if (error) + goto out_release; + +xfs_trans_mod_sb (tp, wasdel ? +XFS_TRANS_SB_RES_FREXTENTS :XFS_TRANS_SB_FREXTENTS , + -(long )len); + *bno =xfs_rtx_to_rtb (args.mp, rtx); + *blen =xfs_rtxlen_to_extlen (args.mp, len); + +out_release: +xfs_rtbuf_cache_relse (&args); +return error; +} +int xfs_bmap_rtalloc (struct xfs_bmalloca *ap)
@@ -1276,7 +1321,6 @@ xfs_bmap_rtalloc(struct xfs_mount *mp = ap->ip->i_mount;xfs_fileoff_t orig_offset = ap->offset;xfs_rtxnum_t start;/* allocation hint rtextent no */ -xfs_rtxnum_t rtx;/* actually allocated rtextent no */ xfs_rtxlen_t prod =0 ;/* product factor for allocators */ xfs_extlen_t mod =0 ;/* product factor for allocators */ xfs_rtxlen_t ralen =0 ;/* realtime allocation length */
@@ -1286,10 +1330,6 @@ xfs_bmap_rtalloc(xfs_rtxlen_t raminlen;bool rtlocked = false;bool ignore_locality = false; -struct xfs_rtalloc_args args = { - .mp = mp, - .tp = ap->tp, - };int error; align =xfs_get_extsz_hint (ap->ip);
@@ -1363,19 +1403,9 @@ xfs_bmap_rtalloc(xfs_rtalloc_align_minmax (&raminlen, &ralen, &prod); } -if (start) { - error =xfs_rtallocate_extent_near (&args, start, raminlen, - ralen, &ralen, prod, &rtx); - }else { - error =xfs_rtallocate_extent_size (&args, raminlen, - ralen, &ralen, prod, &rtx); - } - -if (error) { -xfs_rtbuf_cache_relse (&args); -if (error != -ENOSPC ) -return error; - + error =xfs_rtallocate (ap->tp, start, raminlen, ralen, prod, ap->wasdel, + &ap->blkno, &ap->length); +if (error == -ENOSPC ) {if (align > mp->m_sb.sb_rextsize) {/* * We previously enlarged the request length to try to
@@ -1403,20 +1433,9 @@ xfs_bmap_rtalloc( ap->length =0 ;return 0 ; } - - error =xfs_rtallocate_range (&args, rtx, ralen);if (error) - goto out_release; +return error; -xfs_trans_mod_sb (ap->tp, ap->wasdel ? -XFS_TRANS_SB_RES_FREXTENTS :XFS_TRANS_SB_FREXTENTS , - -(long )ralen); - - ap->blkno =xfs_rtx_to_rtb (mp, rtx); - ap->length =xfs_rtxlen_to_extlen (mp, ralen);xfs_bmap_alloc_account (ap); - -out_release: -xfs_rtbuf_cache_relse (&args); -return error; +return 0 ; }
PATCH 3/24 xfs: rework the rtalloc fallback handling
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
xfs_rtallocate currently has two fallbacks, when an allocation fails:
1) drop the requested extent size alignment, if any, and retry
2) ignore the locality hint
Oddly enough it does those in order, as trying a different location is more in line with what the user asked for, and does it in a very unstructured way.
Lift the fallback to try to allocate without the locality hint into xfs_rtallocate to both perform them in a more sensible order and to clean up the code.
--- fs/xfs/xfs_rtalloc.c | 69 +++++++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 35 deletions(-)
fs/xfs/xfs_rtalloc.c +34 -35
@@ -1277,6 +1277,8 @@ xfs_rtallocate(xfs_rtxlen_t maxlen, xfs_rtxlen_t prod, bool wasdel, + bool initial_user_data, + bool *rtlocked, xfs_rtblock_t *bno, xfs_extlen_t *blen) {
@@ -1286,12 +1288,38 @@ xfs_rtallocate( };xfs_rtxnum_t rtx;xfs_rtxlen_t len =0 ; -int error; +int error =0 ; + +/* +* Lock out modifications to both the RT bitmap and summary inodes. +*/ +if (!*rtlocked) { +xfs_rtbitmap_lock (args.mp); +xfs_rtbitmap_trans_join (tp); + *rtlocked = true; + } + +/* +* For an allocation to an empty file at offset 0, pick an extent that +* will space things out in the rt area. +*/ +if (!start && initial_user_data) + start =xfs_rtpick_extent (args.mp, tp, maxlen);if (start) { error =xfs_rtallocate_extent_near (&args, start, minlen, maxlen, &len, prod, &rtx); - }else { +/* +* If we can't allocate near a specific rt extent, try again +* without locality criteria. +*/ +if (error == -ENOSPC ) { +xfs_rtbuf_cache_relse (&args); + error =0 ; + } + } + +if (!error) { error =xfs_rtallocate_extent_size (&args, minlen, maxlen, &len, prod, &rtx); }
@@ -1320,7 +1348,7 @@ xfs_bmap_rtalloc( {struct xfs_mount *mp = ap->ip->i_mount;xfs_fileoff_t orig_offset = ap->offset; -xfs_rtxnum_t start;/* allocation hint rtextent no */ +xfs_rtxnum_t start =0 ;/* allocation hint rtextent no */ xfs_rtxlen_t prod =0 ;/* product factor for allocators */ xfs_extlen_t mod =0 ;/* product factor for allocators */ xfs_rtxlen_t ralen =0 ;/* realtime allocation length */
@@ -1329,7 +1357,6 @@ xfs_bmap_rtalloc(xfs_extlen_t minlen = mp->m_sb.sb_rextsize;xfs_rtxlen_t raminlen;bool rtlocked = false; -bool ignore_locality = false;int error; align =xfs_get_extsz_hint (ap->ip);
@@ -1367,28 +1394,8 @@ xfs_bmap_rtalloc(ASSERT (raminlen >0 );ASSERT (raminlen <= ralen); -/* -* Lock out modifications to both the RT bitmap and summary inodes -*/ -if (!rtlocked) { -xfs_rtbitmap_lock (mp); -xfs_rtbitmap_trans_join (ap->tp); - rtlocked = true; - } - -if (ignore_locality) { - start =0 ; - }else if (xfs_bmap_adjacent (ap)) { +if (xfs_bmap_adjacent (ap)) start =xfs_rtb_to_rtx (mp, ap->blkno); - }else if (ap->datatype &XFS_ALLOC_INITIAL_USER_DATA ) { -/* -* If it's an allocation to an empty file at offset 0, pick an -* extent that will space things out in the rt area. -*/ - start =xfs_rtpick_extent (mp, ap->tp, ralen); - }else { - start =0 ; - }/* * Only bother calculating a real prod factor if offset & length are
@@ -1404,7 +1411,8 @@ xfs_bmap_rtalloc( } error =xfs_rtallocate (ap->tp, start, raminlen, ralen, prod, ap->wasdel, - &ap->blkno, &ap->length); + ap->datatype &XFS_ALLOC_INITIAL_USER_DATA , &rtlocked, + &ap->blkno, &ap->length);if (error == -ENOSPC ) {if (align > mp->m_sb.sb_rextsize) {/*
@@ -1420,15 +1428,6 @@ xfs_bmap_rtalloc( goto retry; } -if (!ignore_locality && start !=0 ) { -/* -* If we can't allocate near a specific rt extent, try -* again without locality criteria. -*/ - ignore_locality = true; - goto retry; - } - ap->blkno =NULLFSBLOCK ; ap->length =0 ;return 0 ;
PATCH 4/24 xfs: factor out a xfs_rtallocate_align helper
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Split the code to calculate the aligned allocation request from xfs_bmap_rtalloc into a separate self-contained helper.
--- fs/xfs/xfs_rtalloc.c | 93 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 59 insertions(+), 34 deletions(-)
fs/xfs/xfs_rtalloc.c +59 -34
@@ -1342,30 +1342,33 @@ xfs_rtallocate(return error; } -int -xfs_bmap_rtalloc ( -struct xfs_bmalloca *ap) +static int +xfs_rtallocate_align ( +struct xfs_bmalloca *ap, +xfs_rtxlen_t *ralen, +xfs_rtxlen_t *raminlen, +xfs_rtxlen_t *prod, +bool *noalign) {struct xfs_mount *mp = ap->ip->i_mount;xfs_fileoff_t orig_offset = ap->offset; -xfs_rtxnum_t start =0 ;/* allocation hint rtextent no */ -xfs_rtxlen_t prod =0 ;/* product factor for allocators */ -xfs_extlen_t mod =0 ;/* product factor for allocators */ -xfs_rtxlen_t ralen =0 ;/* realtime allocation length */ -xfs_extlen_t align;/* minimum allocation alignment */ -xfs_extlen_t orig_length = ap->length;xfs_extlen_t minlen = mp->m_sb.sb_rextsize; -xfs_rtxlen_t raminlen; -bool rtlocked = false; +xfs_extlen_t align;/* minimum allocation alignment */ +xfs_extlen_t mod;/* product factor for allocators */ int error; - align =xfs_get_extsz_hint (ap->ip); -if (!align) - align =1 ; -retry: - error =xfs_bmap_extsize_align (mp, &ap->got, &ap->prev, - align,1 , ap->eof,0 , - ap->conv, &ap->offset, &ap->length); +if (*noalign) { + align = mp->m_sb.sb_rextsize; + }else { + align =xfs_get_extsz_hint (ap->ip); +if (!align) + align =1 ; +if (align == mp->m_sb.sb_rextsize) + *noalign = true; + } + + error =xfs_bmap_extsize_align (mp, &ap->got, &ap->prev, align,1 , + ap->eof,0 , ap->conv, &ap->offset, &ap->length);if (error)return error;ASSERT (ap->length);
@@ -1389,32 +1392,54 @@ xfs_bmap_rtalloc(* XFS_BMBT_MAX_EXTLEN), we don't hear about that number, and can't * adjust the starting point to match it. */ - ralen =xfs_extlen_to_rtxlen (mp,min (ap->length,XFS_MAX_BMBT_EXTLEN )); - raminlen =max_t (xfs_rtxlen_t,1 ,xfs_extlen_to_rtxlen (mp, minlen)); -ASSERT (raminlen >0 ); -ASSERT (raminlen <= ralen); - -if (xfs_bmap_adjacent (ap)) - start =xfs_rtb_to_rtx (mp, ap->blkno); + *ralen =xfs_extlen_to_rtxlen (mp,min (ap->length,XFS_MAX_BMBT_EXTLEN )); + *raminlen =max_t (xfs_rtxlen_t,1 ,xfs_extlen_to_rtxlen (mp, minlen)); +ASSERT (*raminlen >0 ); +ASSERT (*raminlen <= *ralen);/* * Only bother calculating a real prod factor if offset & length are * perfectly aligned, otherwise it will just get us in trouble. */ div_u64_rem (ap->offset, align, &mod); -if (mod || ap->length % align) { - prod =1 ; - }else { - prod =xfs_extlen_to_rtxlen (mp, align); -if (prod >1 ) -xfs_rtalloc_align_minmax (&raminlen, &ralen, &prod); - } +if (mod || ap->length % align) + *prod =1 ; +else + *prod =xfs_extlen_to_rtxlen (mp, align); + +if (*prod >1 ) +xfs_rtalloc_align_minmax (raminlen, ralen, prod); +return 0 ; +} + +int +xfs_bmap_rtalloc ( +struct xfs_bmalloca *ap) +{ +struct xfs_mount *mp = ap->ip->i_mount; +xfs_fileoff_t orig_offset = ap->offset; +xfs_rtxnum_t start =0 ;/* allocation hint rtextent no */ +xfs_rtxlen_t prod =0 ;/* product factor for allocators */ +xfs_rtxlen_t ralen =0 ;/* realtime allocation length */ +xfs_extlen_t orig_length = ap->length; +xfs_rtxlen_t raminlen; +bool rtlocked = false; +bool noalign = false; +int error; + +retry: + error =xfs_rtallocate_align (ap, &ralen, &raminlen, &prod, &noalign); +if (error) +return error; + +if (xfs_bmap_adjacent (ap)) + start =xfs_rtb_to_rtx (mp, ap->blkno); error =xfs_rtallocate (ap->tp, start, raminlen, ralen, prod, ap->wasdel, ap->datatype &XFS_ALLOC_INITIAL_USER_DATA , &rtlocked, &ap->blkno, &ap->length);if (error == -ENOSPC ) { -if (align > mp->m_sb.sb_rextsize) { +if (!noalign) {/* * We previously enlarged the request length to try to * satisfy an extent size hint. The allocator didn't
@@ -1424,7 +1449,7 @@ xfs_bmap_rtalloc(*/ ap->offset = orig_offset; ap->length = orig_length; - minlen = align = mp->m_sb.sb_rextsize; + noalign = true; goto retry; }
PATCH 5/24 xfs: make the rtalloc start hint a xfs_rtblock_t
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
0 is a valid start RT extent, and with pending changes it will become both more common and non-unique. Switch to pass a xfs_rtblock_t instead so that we can use NULLRTBLOCK to determine if a hint was set or not.
--- fs/xfs/xfs_rtalloc.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-)
fs/xfs/xfs_rtalloc.c +11 -7
@@ -1272,7 +1272,7 @@ xfs_rtalloc_align_minmax( static int xfs_rtallocate( struct xfs_trans *tp, - xfs_rtxnum_t start, + xfs_rtblock_t bno_hint, xfs_rtxlen_t minlen, xfs_rtxlen_t maxlen, xfs_rtxlen_t prod,
@@ -1286,6 +1286,7 @@ xfs_rtallocate( .mp = tp->t_mountp, .tp = tp, }; +xfs_rtxnum_t start =0 ;xfs_rtxnum_t rtx;xfs_rtxlen_t len =0 ;int error =0 ;
@@ -1303,7 +1304,9 @@ xfs_rtallocate(* For an allocation to an empty file at offset 0, pick an extent that * will space things out in the rt area. */ -if (!start && initial_user_data) +if (bno_hint) + start =xfs_rtb_to_rtx (args.mp, bno_hint); +else if (initial_user_data) start =xfs_rtpick_extent (args.mp, tp, maxlen);if (start) {
@@ -1416,15 +1419,16 @@ intxfs_bmap_rtalloc ( struct xfs_bmalloca *ap) { -struct xfs_mount *mp = ap->ip->i_mount;xfs_fileoff_t orig_offset = ap->offset; -xfs_rtxnum_t start =0 ;/* allocation hint rtextent no */ xfs_rtxlen_t prod =0 ;/* product factor for allocators */ xfs_rtxlen_t ralen =0 ;/* realtime allocation length */ +xfs_rtblock_t bno_hint =NULLRTBLOCK ;xfs_extlen_t orig_length = ap->length;xfs_rtxlen_t raminlen;bool rtlocked = false;bool noalign = false; +bool initial_user_data = + ap->datatype &XFS_ALLOC_INITIAL_USER_DATA ;int error; retry:
@@ -1433,10 +1437,10 @@ xfs_bmap_rtalloc(return error;if (xfs_bmap_adjacent (ap)) - start =xfs_rtb_to_rtx (mp, ap->blkno); + bno_hint = ap->blkno; - error =xfs_rtallocate (ap->tp, start, raminlen, ralen, prod, ap->wasdel, - ap->datatype &XFS_ALLOC_INITIAL_USER_DATA , &rtlocked, + error =xfs_rtallocate (ap->tp, bno_hint, raminlen, ralen, prod, + ap->wasdel, initial_user_data, &rtlocked, &ap->blkno, &ap->length);if (error == -ENOSPC ) {if (!noalign) {
PATCH 6/24 xfs: add xchk_setup_nothing and xchk_nothing helpers
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Add common helpers for no-op scrubbing methods.
--- fs/xfs/scrub/common.h | 29 +++++++++-------------------- fs/xfs/scrub/scrub.h | 29 +++++++++-------------------- 2 files changed, 18 insertions(+), 40 deletions(-)
fs/xfs/scrub/common.h +9 -20
@@ -53,6 +53,11 @@ int xchk_checkpoint_log(struct xfs_mount *mp);bool xchk_should_check_xref (struct xfs_scrub *sc,int *error,struct xfs_btree_cur **curpp); +static inline int xchk_setup_nothing (struct xfs_scrub *sc) +{ +return -ENOENT ; +} +/* Setup functions */ int xchk_setup_agheader (struct xfs_scrub *sc);int xchk_setup_fs (struct xfs_scrub *sc);
@@ -73,16 +78,8 @@ int xchk_setup_metapath(struct xfs_scrub *sc);int xchk_setup_rtbitmap (struct xfs_scrub *sc);int xchk_setup_rtsummary (struct xfs_scrub *sc);#else -static inline int -xchk_setup_rtbitmap (struct xfs_scrub *sc) -{ -return -ENOENT ; -} -static inline int -xchk_setup_rtsummary (struct xfs_scrub *sc) -{ -return -ENOENT ; -} +# define xchk_setup_rtbitmap xchk_setup_nothing +# define xchk_setup_rtsummary xchk_setup_nothing#endif #ifdef CONFIG_XFS_QUOTA int xchk_ino_dqattach (struct xfs_scrub *sc);
@@ -94,16 +91,8 @@ xchk_ino_dqattach(struct xfs_scrub *sc) {return 0 ; } -static inline int -xchk_setup_quota (struct xfs_scrub *sc) -{ -return -ENOENT ; -} -static inline int -xchk_setup_quotacheck (struct xfs_scrub *sc) -{ -return -ENOENT ; -} +# define xchk_setup_quota xchk_setup_nothing +# define xchk_setup_quotacheck xchk_setup_nothing#endif int xchk_setup_fscounters (struct xfs_scrub *sc);int xchk_setup_nlinks (struct xfs_scrub *sc);
fs/xfs/scrub/scrub.h +9 -20
@@ -232,6 +232,11 @@ xchk_should_terminate(return false; } +static inline int xchk_nothing (struct xfs_scrub *sc) +{ +return -ENOENT ; +} +/* Metadata scrubbers */ int xchk_tester (struct xfs_scrub *sc);int xchk_superblock (struct xfs_scrub *sc);
@@ -256,31 +261,15 @@ int xchk_metapath(struct xfs_scrub *sc);int xchk_rtbitmap (struct xfs_scrub *sc);int xchk_rtsummary (struct xfs_scrub *sc);#else -static inline int -xchk_rtbitmap (struct xfs_scrub *sc) -{ -return -ENOENT ; -} -static inline int -xchk_rtsummary (struct xfs_scrub *sc) -{ -return -ENOENT ; -} +# define xchk_rtbitmap xchk_nothing +# define xchk_rtsummary xchk_nothing#endif #ifdef CONFIG_XFS_QUOTA int xchk_quota (struct xfs_scrub *sc);int xchk_quotacheck (struct xfs_scrub *sc);#else -static inline int -xchk_quota (struct xfs_scrub *sc) -{ -return -ENOENT ; -} -static inline int -xchk_quotacheck (struct xfs_scrub *sc) -{ -return -ENOENT ; -} +# define xchk_quota xchk_nothing +# define xchk_quotacheck xchk_nothing#endif int xchk_fscounters (struct xfs_scrub *sc);int xchk_nlinks (struct xfs_scrub *sc);
Re: [PATCH 06/24] xfs: add xchk_setup_nothing and xchk_nothing helpers
Looks good:
PATCH 7/24 xfs: remove xfs_{rtbitmap,rtsummary}_wordcount
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
xfs_rtbitmap_wordcount and xfs_rtsummary_wordcount are currently unused, so remove them to simplify refactoring other rtbitmap helpers. They can be added back or simply open coded when actually needed.
--- fs/xfs/libxfs/xfs_rtbitmap.c | 31 ------------------------------- fs/xfs/libxfs/xfs_rtbitmap.h | 7 ------- 2 files changed, 38 deletions(-)
fs/xfs/libxfs/xfs_rtbitmap.c +0 -31
@@ -1148,21 +1148,6 @@ xfs_rtbitmap_blockcount(return howmany_64 (rtextents,NBBY * mp->m_sb.sb_blocksize); } -/* -* Compute the number of rtbitmap words needed to populate every block of a -* bitmap that is large enough to track the given number of rt extents. -*/ -unsigned long long -xfs_rtbitmap_wordcount ( -struct xfs_mount *mp, -xfs_rtbxlen_t rtextents) -{ -xfs_filblks_t blocks; - - blocks =xfs_rtbitmap_blockcount (mp, rtextents); -return XFS_FSB_TO_B (mp, blocks) >>XFS_WORDLOG ; -} -/* Compute the number of rtsummary blocks needed to track the given rt space. */ xfs_filblks_t xfs_rtsummary_blockcount(
@@ -1176,22 +1161,6 @@ xfs_rtsummary_blockcount(return XFS_B_TO_FSB (mp, rsumwords <<XFS_WORDLOG ); } -/* -* Compute the number of rtsummary info words needed to populate every block of -* a summary file that is large enough to track the given rt space. -*/ -unsigned long long -xfs_rtsummary_wordcount ( -struct xfs_mount *mp, -unsigned int rsumlevels, -xfs_extlen_t rbmblocks) -{ -xfs_filblks_t blocks; - - blocks =xfs_rtsummary_blockcount (mp, rsumlevels, rbmblocks); -return XFS_FSB_TO_B (mp, blocks) >>XFS_WORDLOG ; -} -/* Lock both realtime free space metadata inodes for a freespace update. */ void xfs_rtbitmap_lock(
fs/xfs/libxfs/xfs_rtbitmap.h +0 -7
@@ -316,13 +316,8 @@ int xfs_rtfree_blocks(struct xfs_trans *tp, xfs_fsblock_t rtbno,xfs_filblks_t xfs_rtbitmap_blockcount (struct xfs_mount *mp,xfs_rtbxlen_t rtextents); -unsigned long long xfs_rtbitmap_wordcount (struct xfs_mount *mp, -xfs_rtbxlen_t rtextents); -xfs_filblks_t xfs_rtsummary_blockcount (struct xfs_mount *mp,unsigned int rsumlevels,xfs_extlen_t rbmblocks); -unsigned long long xfs_rtsummary_wordcount (struct xfs_mount *mp, -unsigned int rsumlevels,xfs_extlen_t rbmblocks);int xfs_rtfile_initialize_blocks (struct xfs_inode *ip,xfs_fileoff_t offset_fsb,xfs_fileoff_t end_fsb,void *data);
@@ -355,9 +350,7 @@ xfs_rtbitmap_blockcount(struct xfs_mount *mp, xfs_rtbxlen_t rtextents)/* shut up gcc */ return 0 ; } -# define xfs_rtbitmap_wordcount (mp, r) (0)# define xfs_rtsummary_blockcount (mp, l, b) (0) -# define xfs_rtsummary_wordcount (mp, l, b) (0)# define xfs_rtbitmap_lock (mp) do { } while (0)# define xfs_rtbitmap_trans_join (tp) do { } while (0)# define xfs_rtbitmap_unlock (mp) do { } while (0)
PATCH 8/24 xfs: replace m_rsumsize with m_rsumblocks
In reply to Darrick J. WongFrom: Christoph Hellwig <[email protected]>
Track the RT summary file size in blocks, just like the RT bitmap file. While we have users of both units, blocks are used slightly more often and this matches the bitmap file for consistency.
--- fs/xfs/libxfs/xfs_rtbitmap.c | 2 +- fs/xfs/libxfs/xfs_trans_resv.c | 2 +- fs/xfs/scrub/rtsummary.c | 11 +++++------ fs/xfs/scrub/rtsummary.h | 2 +- fs/xfs/scrub/rtsummary_repair.c | 12 +++++------- fs/xfs/xfs_mount.h | 2 +- fs/xfs/xfs_rtalloc.c | 13 +++++-------- 7 files changed, 19 insertions(+), 25 deletions(-)
fs/xfs/libxfs/xfs_rtbitmap.c +1 -1
@@ -162,7 +162,7 @@ xfs_rtsummary_read_buf( {struct xfs_mount *mp = args->mp; -if (XFS_IS_CORRUPT (mp, block >=XFS_B_TO_FSB (mp, mp->m_rsumsize))) { +if (XFS_IS_CORRUPT (mp, block >= mp->m_rsumblocks)) {xfs_rt_mark_sick (args->mp,XFS_SICK_RT_SUMMARY );return -EFSCORRUPTED ; }
fs/xfs/libxfs/xfs_trans_resv.c +1 -1
@@ -918,7 +918,7 @@ xfs_calc_growrtfree_reservation(return xfs_calc_buf_res (1 , mp->m_sb.sb_sectsize) +xfs_calc_inode_res (mp,2 ) +xfs_calc_buf_res (1 , mp->m_sb.sb_blocksize) + -xfs_calc_buf_res (1 , mp->m_rsumsize); +xfs_calc_buf_res (1 ,XFS_FSB_TO_B (mp, mp->m_rsumblocks)); }/*
fs/xfs/scrub/rtsummary.c +5 -6
@@ -63,7 +63,8 @@ xchk_setup_rtsummary(* us to avoid pinning kernel memory for this purpose. */ descr =xchk_xfile_descr (sc,"realtime summary file" ); - error =xfile_create (descr, mp->m_rsumsize, &sc->xfile); + error =xfile_create (descr,XFS_FSB_TO_B (mp, mp->m_rsumblocks), + &sc->xfile);kfree (descr);if (error)return error;
@@ -95,16 +96,14 @@ xchk_setup_rtsummary(* volume. Hence it is safe to compute and check the geometry values. */ if (mp->m_sb.sb_rblocks) { -xfs_filblks_t rsumblocks;int rextslog; rts->rextents =xfs_rtb_to_rtx (mp, mp->m_sb.sb_rblocks); rextslog =xfs_compute_rextslog (rts->rextents); rts->rsumlevels = rextslog +1 ; rts->rbmblocks =xfs_rtbitmap_blockcount (mp, rts->rextents); - rsumblocks =xfs_rtsummary_blockcount (mp, rts->rsumlevels, + rts->rsumblocks =xfs_rtsummary_blockcount (mp, rts->rsumlevels, rts->rbmblocks); - rts->rsumsize =XFS_FSB_TO_B (mp, rsumblocks); }return 0 ; }
@@ -316,7 +315,7 @@ xchk_rtsummary( }/* Is m_rsumsize correct? */ -if (mp->m_rsumsize != rts->rsumsize) { +if (mp->m_rsumblocks != rts->rsumblocks) {xchk_ino_set_corrupt (sc, mp->m_rsumip->i_ino); goto out_rbm; }
@@ -332,7 +331,7 @@ xchk_rtsummary(* growfsrt expands the summary file before updating sb_rextents, so * the file can be larger than rsumsize. */ -if (mp->m_rsumip->i_disk_size < rts->rsumsize) { +if (mp->m_rsumip->i_disk_size <XFS_FSB_TO_B (mp, rts->rsumblocks)) {xchk_ino_set_corrupt (sc, mp->m_rsumip->i_ino); goto out_rbm; }
fs/xfs/scrub/rtsummary.h +1 -1
@@ -14,7 +14,7 @@ struct xchk_rtsummary {uint64_t rextents;uint64_t rbmblocks; -uint64_t rsumsize; +xfs_filblks_t rsumblocks;unsigned int rsumlevels;unsigned int resblks;
fs/xfs/scrub/rtsummary_repair.c +5 -7
@@ -56,7 +56,7 @@ xrep_setup_rtsummary(* transaction (which we cannot drop because we cannot drop the * rtsummary ILOCK) and cannot ask for more reservation. */ - blocks =XFS_B_TO_FSB (mp, mp->m_rsumsize); + blocks = mp->m_rsumblocks; blocks +=xfs_bmbt_calc_size (mp, blocks) *2 ;if (blocks >UINT_MAX )return -EOPNOTSUPP ;
@@ -100,7 +100,6 @@ xrep_rtsummary( {struct xchk_rtsummary *rts = sc->buf;struct xfs_mount *mp = sc->mp; -xfs_filblks_t rsumblocks;int error;/* We require the rmapbt to rebuild anything. */
@@ -131,10 +130,9 @@ xrep_rtsummary( }/* Make sure we have space allocated for the entire summary file. */ - rsumblocks =XFS_B_TO_FSB (mp, rts->rsumsize);xfs_trans_ijoin (sc->tp, sc->ip,0 );xfs_trans_ijoin (sc->tp, sc->tempip,0 ); - error =xrep_tempfile_prealloc (sc,0 , rsumblocks); + error =xrep_tempfile_prealloc (sc,0 , rts->rsumblocks);if (error)return error;
@@ -143,11 +141,11 @@ xrep_rtsummary(return error;/* Copy the rtsummary file that we generated. */ - error =xrep_tempfile_copyin (sc,0 , rsumblocks, + error =xrep_tempfile_copyin (sc,0 , rts->rsumblocks, xrep_rtsummary_prep_buf, rts);if (error)return error; - error =xrep_tempfile_set_isize (sc, rts->rsumsize); + error =xrep_tempfile_set_isize (sc,XFS_FSB_TO_B (mp, rts->rsumblocks));if (error)return error;
@@ -168,7 +166,7 @@ xrep_rtsummary(memset (mp->m_rsum_cache,0xFF , mp->m_sb.sb_rbmblocks); mp->m_rsumlevels = rts->rsumlevels; - mp->m_rsumsize = rts->rsumsize; + mp->m_rsumblocks = rts->rsumblocks;/* Free the old rtsummary blocks if they're not in use. */ return xrep_reap_ifork (sc, sc->tempip,XFS_DATA_FORK );
fs/xfs/xfs_mount.h +1 -1
@@ -148,7 +148,7 @@ typedef struct xfs_mount {int m_logbufs;/* number of log buffers */ int m_logbsize;/* size of each log buffer */ uint m_rsumlevels;/* rt summary levels */ -uint m_rsumsize;/* size of rt summary, bytes */ +xfs_filblks_t m_rsumblocks;/* size of rt summary, FSBs */ int m_fixedfsid[2 ];/* unchanged for life of FS */ uint m_qflags;/* quota status flags */ uint64_t m_features;/* active filesystem features */
fs/xfs/xfs_rtalloc.c +5 -8
@@ -736,9 +736,8 @@ xfs_growfs_rt_bmblock( nmp->m_sb.sb_rextents =xfs_rtb_to_rtx (nmp, nmp->m_sb.sb_rblocks); nmp->m_sb.sb_rextslog =xfs_compute_rextslog (nmp->m_sb.sb_rextents); nmp->m_rsumlevels = nmp->m_sb.sb_rextslog +1 ; - nmp->m_rsumsize =XFS_FSB_TO_B (mp, -xfs_rtsummary_blockcount (mp, nmp->m_rsumlevels, - nmp->m_sb.sb_rbmblocks)); + nmp->m_rsumblocks =xfs_rtsummary_blockcount (mp, nmp->m_rsumlevels, + nmp->m_sb.sb_rbmblocks);/* * Recompute the growfsrt reservation from the new rsumsize, so that the
@@ -768,7 +767,7 @@ xfs_growfs_rt_bmblock(* so that inode inactivation won't punch what it thinks are "posteof" * blocks. */ - rsumip->i_disk_size = nmp->m_rsumsize; + rsumip->i_disk_size = nmp->m_rsumblocks * nmp->m_sb.sb_blocksize;i_size_write (VFS_I (rsumip), rsumip->i_disk_size);xfs_trans_log_inode (args.tp, rsumip,XFS_ILOG_CORE );
@@ -820,7 +819,7 @@ xfs_growfs_rt_bmblock(* Update the calculated values in the real mount structure. */ mp->m_rsumlevels = nmp->m_rsumlevels; - mp->m_rsumsize = nmp->m_rsumsize; + mp->m_rsumblocks = nmp->m_rsumblocks;xfs_mount_sb_set_rextsize (mp, &mp->m_sb);/*
@@ -1024,7 +1023,6 @@ xfs_rtmount_init(struct xfs_buf *bp;/* buffer for last block of subvolume */ struct xfs_sb *sbp;/* filesystem superblock copy in mount */ xfs_daddr_t d;/* address of last block of subvolume */ -unsigned int rsumblocks;int error; sbp = &mp->m_sb;
@@ -1036,9 +1034,8 @@ xfs_rtmount_init(return -ENODEV ; } mp->m_rsumlevels = sbp->sb_rextslog +1 ; - rsumblocks =xfs_rtsummary_blockcount (mp, mp->m_rsumlevels, + mp->m_rsumblocks =xfs_rtsummary_blockcount (mp, mp->m_rsumlevels, mp->m_sb.sb_rbmblocks); - mp->m_rsumsize =XFS_FSB_TO_B (mp, rsumblocks); mp->m_rbmip = mp->m_rsumip =NULL ;/* * Check that the realtime section is an ok size.
PATCH 9/24 xfs: rearrange xfs_fsmap.c a little bit
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
The order of the functions in this file has gotten a little confusing over the years. Specifically, the two data device implementations (bnobt and rmapbt) could be adjacent in the source code instead of split in two by the logdev and rtdev fsmap implementations. We're about to add more functionality to this file, so rearrange things now.
--- fs/xfs/xfs_fsmap.c | 268 ++++++++++++++++++++++++++-------------------------- 1 file changed, 134 insertions(+), 134 deletions(-)
fs/xfs/xfs_fsmap.c +134 -134
@@ -441,140 +441,6 @@ xfs_getfsmap_set_irec_flags( irec->rm_flags |=XFS_RMAP_UNWRITTEN ; } -/* Execute a getfsmap query against the log device. */ -STATIC int -xfs_getfsmap_logdev ( -struct xfs_trans *tp, -const struct xfs_fsmap *keys, -struct xfs_getfsmap_info *info) -{ -struct xfs_mount *mp = tp->t_mountp; -struct xfs_rmap_irec rmap; -xfs_daddr_t rec_daddr, len_daddr; -xfs_fsblock_t start_fsb, end_fsb; -uint64_t eofs; - - eofs =XFS_FSB_TO_BB (mp, mp->m_sb.sb_logblocks); -if (keys[0 ].fmr_physical >= eofs) -return 0 ; - start_fsb =XFS_BB_TO_FSBT (mp, - keys[0 ].fmr_physical + keys[0 ].fmr_length); - end_fsb =XFS_BB_TO_FSB (mp,min (eofs -1 , keys[1 ].fmr_physical)); - -/* Adjust the low key if we are continuing from where we left off. */ -if (keys[0 ].fmr_length >0 ) - info->low_daddr =XFS_FSB_TO_BB (mp, start_fsb); - -trace_xfs_fsmap_low_key_linear (mp, info->dev, start_fsb); -trace_xfs_fsmap_high_key_linear (mp, info->dev, end_fsb); - -if (start_fsb >0 ) -return 0 ; - -/* Fabricate an rmap entry for the external log device. */ - rmap.rm_startblock =0 ; - rmap.rm_blockcount = mp->m_sb.sb_logblocks; - rmap.rm_owner =XFS_RMAP_OWN_LOG ; - rmap.rm_offset =0 ; - rmap.rm_flags =0 ; - - rec_daddr =XFS_FSB_TO_BB (mp, rmap.rm_startblock); - len_daddr =XFS_FSB_TO_BB (mp, rmap.rm_blockcount); -return xfs_getfsmap_helper (tp, info, &rmap, rec_daddr, len_daddr); -} - -#ifdef CONFIG_XFS_RT -/* Transform a rtbitmap "record" into a fsmap */ -STATIC int -xfs_getfsmap_rtdev_rtbitmap_helper ( -struct xfs_mount *mp, -struct xfs_trans *tp, -const struct xfs_rtalloc_rec *rec, -void *priv) -{ -struct xfs_getfsmap_info *info = priv; -struct xfs_rmap_irec irec; -xfs_rtblock_t rtbno; -xfs_daddr_t rec_daddr, len_daddr; - - rtbno =xfs_rtx_to_rtb (mp, rec->ar_startext); - rec_daddr =XFS_FSB_TO_BB (mp, rtbno); - irec.rm_startblock = rtbno; - - rtbno =xfs_rtx_to_rtb (mp, rec->ar_extcount); - len_daddr =XFS_FSB_TO_BB (mp, rtbno); - irec.rm_blockcount = rtbno; - - irec.rm_owner =XFS_RMAP_OWN_NULL ;/* "free" */ - irec.rm_offset =0 ; - irec.rm_flags =0 ; - -return xfs_getfsmap_helper (tp, info, &irec, rec_daddr, len_daddr); -} - -/* Execute a getfsmap query against the realtime device rtbitmap. */ -STATIC int -xfs_getfsmap_rtdev_rtbitmap ( -struct xfs_trans *tp, -const struct xfs_fsmap *keys, -struct xfs_getfsmap_info *info) -{ - -struct xfs_rtalloc_rec ahigh = {0 }; -struct xfs_mount *mp = tp->t_mountp; -xfs_rtblock_t start_rtb; -xfs_rtblock_t end_rtb; -xfs_rtxnum_t high; -uint64_t eofs; -int error; - - eofs =XFS_FSB_TO_BB (mp,xfs_rtx_to_rtb (mp, mp->m_sb.sb_rextents)); -if (keys[0 ].fmr_physical >= eofs) -return 0 ; - start_rtb =XFS_BB_TO_FSBT (mp, - keys[0 ].fmr_physical + keys[0 ].fmr_length); - end_rtb =XFS_BB_TO_FSB (mp,min (eofs -1 , keys[1 ].fmr_physical)); - - info->missing_owner =XFS_FMR_OWN_UNKNOWN ; - -/* Adjust the low key if we are continuing from where we left off. */ -if (keys[0 ].fmr_length >0 ) { - info->low_daddr =XFS_FSB_TO_BB (mp, start_rtb); -if (info->low_daddr >= eofs) -return 0 ; - } - -trace_xfs_fsmap_low_key_linear (mp, info->dev, start_rtb); -trace_xfs_fsmap_high_key_linear (mp, info->dev, end_rtb); - -xfs_rtbitmap_lock_shared (mp,XFS_RBMLOCK_BITMAP ); - -/* -* Set up query parameters to return free rtextents covering the range -* we want. -*/ - high =xfs_rtb_to_rtxup (mp, end_rtb); - error =xfs_rtalloc_query_range (mp, tp,xfs_rtb_to_rtx (mp, start_rtb), - high, xfs_getfsmap_rtdev_rtbitmap_helper, info); -if (error) - goto err; - -/* -* Report any gaps at the end of the rtbitmap by simulating a null -* rmap starting at the block after the end of the query range. -*/ - info->last = true; - ahigh.ar_startext =min (mp->m_sb.sb_rextents, high); - - error =xfs_getfsmap_rtdev_rtbitmap_helper (mp, tp, &ahigh, info); -if (error) - goto err; -err: -xfs_rtbitmap_unlock_shared (mp,XFS_RBMLOCK_BITMAP ); -return error; -} -#endif /* CONFIG_XFS_RT */ -static inline bool rmap_not_shareable (struct xfs_mount *mp,const struct xfs_rmap_irec *r) {
@@ -799,6 +665,140 @@ xfs_getfsmap_datadev_bnobt( xfs_getfsmap_datadev_bnobt_query, &akeys[0]); } +/* Execute a getfsmap query against the log device. */ +STATIC int +xfs_getfsmap_logdev ( +struct xfs_trans *tp, +const struct xfs_fsmap *keys, +struct xfs_getfsmap_info *info) +{ +struct xfs_mount *mp = tp->t_mountp; +struct xfs_rmap_irec rmap; +xfs_daddr_t rec_daddr, len_daddr; +xfs_fsblock_t start_fsb, end_fsb; +uint64_t eofs; + + eofs =XFS_FSB_TO_BB (mp, mp->m_sb.sb_logblocks); +if (keys[0 ].fmr_physical >= eofs) +return 0 ; + start_fsb =XFS_BB_TO_FSBT (mp, + keys[0 ].fmr_physical + keys[0 ].fmr_length); + end_fsb =XFS_BB_TO_FSB (mp,min (eofs -1 , keys[1 ].fmr_physical)); + +/* Adjust the low key if we are continuing from where we left off. */ +if (keys[0 ].fmr_length >0 ) + info->low_daddr =XFS_FSB_TO_BB (mp, start_fsb); + +trace_xfs_fsmap_low_key_linear (mp, info->dev, start_fsb); +trace_xfs_fsmap_high_key_linear (mp, info->dev, end_fsb); + +if (start_fsb >0 ) +return 0 ; + +/* Fabricate an rmap entry for the external log device. */ + rmap.rm_startblock =0 ; + rmap.rm_blockcount = mp->m_sb.sb_logblocks; + rmap.rm_owner =XFS_RMAP_OWN_LOG ; + rmap.rm_offset =0 ; + rmap.rm_flags =0 ; + + rec_daddr =XFS_FSB_TO_BB (mp, rmap.rm_startblock); + len_daddr =XFS_FSB_TO_BB (mp, rmap.rm_blockcount); +return xfs_getfsmap_helper (tp, info, &rmap, rec_daddr, len_daddr); +} + +#ifdef CONFIG_XFS_RT +/* Transform a rtbitmap "record" into a fsmap */ +STATIC int +xfs_getfsmap_rtdev_rtbitmap_helper ( +struct xfs_mount *mp, +struct xfs_trans *tp, +const struct xfs_rtalloc_rec *rec, +void *priv) +{ +struct xfs_getfsmap_info *info = priv; +struct xfs_rmap_irec irec; +xfs_rtblock_t rtbno; +xfs_daddr_t rec_daddr, len_daddr; + + rtbno =xfs_rtx_to_rtb (mp, rec->ar_startext); + rec_daddr =XFS_FSB_TO_BB (mp, rtbno); + irec.rm_startblock = rtbno; + + rtbno =xfs_rtx_to_rtb (mp, rec->ar_extcount); + len_daddr =XFS_FSB_TO_BB (mp, rtbno); + irec.rm_blockcount = rtbno; + + irec.rm_owner =XFS_RMAP_OWN_NULL ;/* "free" */ + irec.rm_offset =0 ; + irec.rm_flags =0 ; + +return xfs_getfsmap_helper (tp, info, &irec, rec_daddr, len_daddr); +} + +/* Execute a getfsmap query against the realtime device rtbitmap. */ +STATIC int +xfs_getfsmap_rtdev_rtbitmap ( +struct xfs_trans *tp, +const struct xfs_fsmap *keys, +struct xfs_getfsmap_info *info) +{ + +struct xfs_rtalloc_rec ahigh = {0 }; +struct xfs_mount *mp = tp->t_mountp; +xfs_rtblock_t start_rtb; +xfs_rtblock_t end_rtb; +xfs_rtxnum_t high; +uint64_t eofs; +int error; + + eofs =XFS_FSB_TO_BB (mp,xfs_rtx_to_rtb (mp, mp->m_sb.sb_rextents)); +if (keys[0 ].fmr_physical >= eofs) +return 0 ; + start_rtb =XFS_BB_TO_FSBT (mp, + keys[0 ].fmr_physical + keys[0 ].fmr_length); + end_rtb =XFS_BB_TO_FSB (mp,min (eofs -1 , keys[1 ].fmr_physical)); + + info->missing_owner =XFS_FMR_OWN_UNKNOWN ; + +/* Adjust the low key if we are continuing from where we left off. */ +if (keys[0 ].fmr_length >0 ) { + info->low_daddr =XFS_FSB_TO_BB (mp, start_rtb); +if (info->low_daddr >= eofs) +return 0 ; + } + +trace_xfs_fsmap_low_key_linear (mp, info->dev, start_rtb); +trace_xfs_fsmap_high_key_linear (mp, info->dev, end_rtb); + +xfs_rtbitmap_lock_shared (mp,XFS_RBMLOCK_BITMAP ); + +/* +* Set up query parameters to return free rtextents covering the range +* we want. +*/ + high =xfs_rtb_to_rtxup (mp, end_rtb); + error =xfs_rtalloc_query_range (mp, tp,xfs_rtb_to_rtx (mp, start_rtb), + high, xfs_getfsmap_rtdev_rtbitmap_helper, info); +if (error) + goto err; + +/* +* Report any gaps at the end of the rtbitmap by simulating a null +* rmap starting at the block after the end of the query range. +*/ + info->last = true; + ahigh.ar_startext =min (mp->m_sb.sb_rextents, high); + + error =xfs_getfsmap_rtdev_rtbitmap_helper (mp, tp, &ahigh, info); +if (error) + goto err; +err: +xfs_rtbitmap_unlock_shared (mp,XFS_RBMLOCK_BITMAP ); +return error; +} +#endif /* CONFIG_XFS_RT */ +/* Do we recognize the device? */ STATIC bool xfs_getfsmap_is_valid_device(
Re: [PATCH 09/24] xfs: rearrange xfs_fsmap.c a little bit
Looks good:
PATCH 10/24 xfs: move xfs_ioc_getfsmap out of xfs_ioctl.c
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Move this function out of xfs_ioctl.c to reduce the clutter in there, and make the entire getfsmap implementation self-contained in a single file.
--- fs/xfs/xfs_fsmap.c | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++- fs/xfs/xfs_fsmap.h | 6 +- fs/xfs/xfs_ioctl.c | 130 -------------------------------------------------- 3 files changed, 134 insertions(+), 136 deletions(-)
fs/xfs/xfs_fsmap.c +132 -2
@@ -44,7 +44,7 @@ xfs_fsmap_from_internal( }/* Convert an fsmap to an xfs_fsmap. */ -void +static void xfs_fsmap_to_internal (struct xfs_fsmap *dest,struct fsmap *src)
@@ -889,7 +889,7 @@ xfs_getfsmap_check_keys(* xfs_getfsmap_info.low/high -- per-AG low/high keys computed from * dkeys; used to query the metadata. */ -int +STATIC int xfs_getfsmap( struct xfs_mount *mp, struct xfs_fsmap_head *head,
@@ -1019,3 +1019,133 @@ xfs_getfsmap( head->fmh_oflags =FMH_OF_DEV_T ;return error; } + +int +xfs_ioc_getfsmap ( +struct xfs_inode *ip, +struct fsmap_head __user *arg) +{ +struct xfs_fsmap_head xhead = {0 }; +struct fsmap_head head; +struct fsmap *recs; +unsigned int count; +__u32 last_flags =0 ; +bool done = false; +int error; + +if (copy_from_user (&head, arg,sizeof (struct fsmap_head ))) +return -EFAULT ; +if (memchr_inv (head.fmh_reserved,0 ,sizeof (head.fmh_reserved)) || +memchr_inv (head.fmh_keys[0 ].fmr_reserved,0 , +sizeof (head.fmh_keys[0 ].fmr_reserved)) || +memchr_inv (head.fmh_keys[1 ].fmr_reserved,0 , +sizeof (head.fmh_keys[1 ].fmr_reserved))) +return -EINVAL ; + +/* +* Use an internal memory buffer so that we don't have to copy fsmap +* data to userspace while holding locks. Start by trying to allocate +* up to 128k for the buffer, but fall back to a single page if needed. +*/ + count = min_t(unsigned int, head.fmh_count, +131072 /sizeof (struct fsmap )); + recs =kvcalloc (count,sizeof (struct fsmap ),GFP_KERNEL ); +if (!recs) { + count = min_t(unsigned int, head.fmh_count, +PAGE_SIZE /sizeof (struct fsmap )); + recs =kvcalloc (count,sizeof (struct fsmap ),GFP_KERNEL ); +if (!recs) +return -ENOMEM ; + } + + xhead.fmh_iflags = head.fmh_iflags; +xfs_fsmap_to_internal (&xhead.fmh_keys[0 ], &head.fmh_keys[0 ]); +xfs_fsmap_to_internal (&xhead.fmh_keys[1 ], &head.fmh_keys[1 ]); + +trace_xfs_getfsmap_low_key (ip->i_mount, &xhead.fmh_keys[0 ]); +trace_xfs_getfsmap_high_key (ip->i_mount, &xhead.fmh_keys[1 ]); + + head.fmh_entries =0 ; +do { +struct fsmap __user *user_recs; +struct fsmap *last_rec; + + user_recs = &arg->fmh_recs[head.fmh_entries]; + xhead.fmh_entries =0 ; + xhead.fmh_count = min_t(unsigned int, count, + head.fmh_count - head.fmh_entries); + +/* Run query, record how many entries we got. */ + error =xfs_getfsmap (ip->i_mount, &xhead, recs); +switch (error) { +case 0 : +/* +* There are no more records in the result set. Copy +* whatever we got to userspace and break out. +*/ + done = true; +break ; +case -ECANCELED : +/* +* The internal memory buffer is full. Copy whatever +* records we got to userspace and go again if we have +* not yet filled the userspace buffer. +*/ + error =0 ; +break ; +default : + goto out_free; + } + head.fmh_entries += xhead.fmh_entries; + head.fmh_oflags = xhead.fmh_oflags; + +/* +* If the caller wanted a record count or there aren't any +* new records to return, we're done. +*/ +if (head.fmh_count ==0 || xhead.fmh_entries ==0 ) +break ; + +/* Copy all the records we got out to userspace. */ +if (copy_to_user (user_recs, recs, + xhead.fmh_entries *sizeof (struct fsmap ))) { + error = -EFAULT ; + goto out_free; + } + +/* Remember the last record flags we copied to userspace. */ + last_rec = &recs[xhead.fmh_entries -1 ]; + last_flags = last_rec->fmr_flags; + +/* Set up the low key for the next iteration. */ +xfs_fsmap_to_internal (&xhead.fmh_keys[0 ], last_rec); +trace_xfs_getfsmap_low_key (ip->i_mount, &xhead.fmh_keys[0 ]); + }while (!done && head.fmh_entries < head.fmh_count); + +/* +* If there are no more records in the query result set and we're not +* in counting mode, mark the last record returned with the LAST flag. +*/ +if (done && head.fmh_count >0 && head.fmh_entries >0 ) { +struct fsmap __user *user_rec; + + last_flags |=FMR_OF_LAST ; + user_rec = &arg->fmh_recs[head.fmh_entries -1 ]; + +if (copy_to_user (&user_rec->fmr_flags, &last_flags, +sizeof (last_flags))) { + error = -EFAULT ; + goto out_free; + } + } + +/* copy back header */ +if (copy_to_user (arg, &head,sizeof (struct fsmap_head ))) { + error = -EFAULT ; + goto out_free; + } + +out_free: +kvfree (recs); +return error; +}
fs/xfs/xfs_fsmap.h +2 -4
@@ -7,6 +7,7 @@#define __XFS_FSMAP_H__struct fsmap ; +struct fsmap_head ;/* internal fsmap representation */ struct xfs_fsmap {
@@ -27,9 +28,6 @@ struct xfs_fsmap_head { struct xfs_fsmap fmh_keys[2]; /* low and high keys */ }; -void xfs_fsmap_to_internal (struct xfs_fsmap *dest,struct fsmap *src); - -int xfs_getfsmap (struct xfs_mount *mp,struct xfs_fsmap_head *head, -struct fsmap *out_recs); +int xfs_ioc_getfsmap (struct xfs_inode *ip,struct fsmap_head __user *arg);#endif /* __XFS_FSMAP_H__ */
fs/xfs/xfs_ioctl.c +0 -130
136 lines not on this page · raw message
Re: [PATCH 10/24] xfs: move xfs_ioc_getfsmap out of xfs_ioctl.c
Looks good:
PATCH 11/24 xfs: create incore realtime group structures
In reply to Darrick J. WongFrom: Darrick J. Wong <[email protected]>
Create an incore object that will contain information about a realtime allocation group. This will eventually enable us to shard the realtime section in a similar manner to how we shard the data section, but for now just a single object for the entire RT subvolume is created.
--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_format.h | 3 + fs/xfs/libxfs/xfs_rtgroup.c | 196 ++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_rtgroup.h | 212 +++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_sb.c | 7 + fs/xfs/libxfs/xfs_types.h | 4 + fs/xfs/xfs_log_recover.c | 20 ++++ fs/xfs/xfs_mount.c | 16 +++ fs/xfs/xfs_mount.h | 14 +++ fs/xfs/xfs_rtalloc.c | 6 + fs/xfs/xfs_super.c | 1 fs/xfs/xfs_trace.c | 1 fs/xfs/xfs_trace.h | 38 ++++++++ 13 files changed, 517 insertions(+), 2 deletions(-) create mode 100644 fs/xfs/libxfs/xfs_rtgroup.c create mode 100644 fs/xfs/libxfs/xfs_rtgroup.h
fs/xfs/Makefile +1 -0
@@ -60,6 +60,7 @@ xfs-y += $(addprefix libxfs/, \ # xfs_rtbitmap is shared with libxfs xfs-$(CONFIG_XFS_RT) += $(addprefix libxfs/, \ xfs_rtbitmap.o \ + xfs_rtgroup.o \ ) # highlevel code
fs/xfs/libxfs/xfs_format.h +3 -0
@@ -176,6 +176,9 @@ typedef struct xfs_sb {xfs_ino_t sb_metadirino;/* metadata directory tree root */ +xfs_rgnumber_t sb_rgcount;/* number of realtime groups */ +xfs_rtxlen_t sb_rgextents;/* size of a realtime group in rtx */ +/* must be padded to 64 bit alignment */ } xfs_sb_t;
fs/xfs/libxfs/xfs_rtgroup.c new file +196 -0
196 lines not on this page · raw message
fs/xfs/libxfs/xfs_rtgroup.h new file +212 -0
212 lines not on this page · raw message
fs/xfs/libxfs/xfs_sb.c +7 -0
@@ -696,6 +696,9 @@ __xfs_sb_from_disk( to->sb_metadirino =be64_to_cpu (from->sb_metadirino); else to->sb_metadirino = NULLFSINO; + + to->sb_rgcount =1 ; + to->sb_rgextents =0 ; }void
@@ -982,6 +985,10 @@ xfs_mount_sb_set_rextsize( { mp->m_rtxblklog =log2_if_power2 (sbp->sb_rextsize); mp->m_rtxblkmask =mask64_if_power2 (sbp->sb_rextsize); + + mp->m_rgblocks =0 ; + mp->m_rgblklog =0 ; + mp->m_rgblkmask =0 ; }/*
fs/xfs/libxfs/xfs_types.h +4 -0
@@ -9,10 +9,12 @@typedef uint32_t prid_t ;/* project ID */ typedef uint32_t xfs_agblock_t ;/* blockno in alloc. group */ +typedef uint32_t xfs_rgblock_t ;/* blockno in realtime group */ typedef uint32_t xfs_agino_t ;/* inode # within allocation grp */ typedef uint32_t xfs_extlen_t ;/* extent length in blocks */ typedef uint32_t xfs_rtxlen_t ;/* file extent length in rtextents */ typedef uint32_t xfs_agnumber_t ;/* allocation group number */ +typedef uint32_t xfs_rgnumber_t ;/* realtime group number */ typedef uint64_t xfs_extnum_t ;/* # of extents in a file */ typedef uint32_t xfs_aextnum_t ;/* # extents in an attribute fork */ typedef int64_t xfs_fsize_t ;/* bytes in a file */
@@ -53,7 +55,9 @@ typedef void * xfs_failaddr_t;#define NULLFILEOFF ((xfs_fileoff_t)-1)#define NULLAGBLOCK ((xfs_agblock_t)-1) +#define NULLRGBLOCK ((xfs_rgblock_t)-1)#define NULLAGNUMBER ((xfs_agnumber_t)-1) +#define NULLRGNUMBER ((xfs_rgnumber_t)-1)#define NULLCOMMITLSN ((xfs_lsn_t)-1)
fs/xfs/xfs_log_recover.c +20 -0
@@ -28,6 +28,7 @@#include "xfs_ag.h" #include "xfs_quota.h" #include "xfs_reflink.h" +#include "xfs_rtgroup.h" #define BLK_AVG (blk1, blk2) ((blk1+blk2) >> 1)
@@ -3346,6 +3347,7 @@ xlog_do_recover(struct xfs_mount *mp = log->l_mp;struct xfs_buf *bp = mp->m_sb_bp;struct xfs_sb *sbp = &mp->m_sb; +xfs_rgnumber_t old_rgcount = sbp->sb_rgcount;int error;trace_xfs_log_recover (log, head_blk, tail_blk);
@@ -3399,6 +3401,24 @@ xlog_do_recover(xfs_warn (mp,"Failed post-recovery per-ag init: %d" , error);return error; } + +if (sbp->sb_rgcount < old_rgcount) { +xfs_warn (mp,"rgcount shrink not supported" ); +return -EINVAL ; + } +if (sbp->sb_rgcount > old_rgcount) { +xfs_rgnumber_t rgno; + +for (rgno = old_rgcount; rgno < sbp->sb_rgcount; rgno++) { + error =xfs_rtgroup_alloc (mp, rgno); +if (error) { +xfs_warn (mp, +"Failed post-recovery rtgroup init: %d" , + error); +return error; + } + } + } mp->m_alloc_set_aside =xfs_alloc_set_aside (mp);/* Normal transactions can now occur */
fs/xfs/xfs_mount.c +14 -2
@@ -36,6 +36,7 @@#include "xfs_ag.h" #include "xfs_rtbitmap.h" #include "xfs_metafile.h" +#include "xfs_rtgroup.h" #include "scrub/stats.h" static DEFINE_MUTEX (xfs_uuid_table_mutex);
@@ -664,6 +665,7 @@ xfs_mountfs(struct xfs_ino_geometry *igeo =M_IGEO (mp);uint quotamount =0 ;uint quotaflags =0 ; +xfs_rgnumber_t rgno;int error =0 ;xfs_sb_mount_common (mp, sbp);
@@ -830,10 +832,18 @@ xfs_mountfs( goto out_free_dir; } +for (rgno =0 ; rgno < mp->m_sb.sb_rgcount; rgno++) { + error =xfs_rtgroup_alloc (mp, rgno); +if (error) { +xfs_warn (mp,"Failed rtgroup init: %d" , error); + goto out_free_rtgroup; + } + } +if (XFS_IS_CORRUPT (mp, !sbp->sb_logblocks)) {xfs_warn (mp,"no log defined" ); error = -EFSCORRUPTED ; - goto out_free_perag; + goto out_free_rtgroup; } error =xfs_inodegc_register_shrinker (mp);
@@ -1068,7 +1078,8 @@ xfs_mountfs(if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp)xfs_buftarg_drain (mp->m_logdev_targp);xfs_buftarg_drain (mp->m_ddev_targp); - out_free_perag: + out_free_rtgroup: +xfs_free_rtgroups (mp, rgno);xfs_free_perag (mp); out_free_dir:xfs_da_unmount (mp);
@@ -1152,6 +1163,7 @@ xfs_unmountfs(xfs_errortag_clearall (mp);#endif shrinker_free (mp->m_inodegc_shrinker); +xfs_free_rtgroups (mp, mp->m_sb.sb_rgcount);xfs_free_perag (mp);xfs_errortag_del (mp);
fs/xfs/xfs_mount.h +14 -0
@@ -121,6 +121,7 @@ typedef struct xfs_mount {uint8_t m_agno_log;/* log #ag's */ uint8_t m_sectbb_log;/* sectlog - BBSHIFT */ int8_t m_rtxblklog;/* log2 of rextsize, if possible */ +int8_t m_rgblklog;/* log2 of rt group sz if possible */ uint m_blockmask;/* sb_blocksize-1 */ uint m_blockwsize;/* sb_blocksize in words */ uint m_blockwmask;/* blockwsize-1 */
@@ -149,12 +150,14 @@ typedef struct xfs_mount {int m_logbsize;/* size of each log buffer */ uint m_rsumlevels;/* rt summary levels */ xfs_filblks_t m_rsumblocks;/* size of rt summary, FSBs */ +uint32_t m_rgblocks;/* size of rtgroup in rtblocks */ int m_fixedfsid[2 ];/* unchanged for life of FS */ uint m_qflags;/* quota status flags */ uint64_t m_features;/* active filesystem features */ uint64_t m_low_space[XFS_LOWSP_MAX ];uint64_t m_low_rtexts[XFS_LOWSP_MAX ];uint64_t m_rtxblkmask;/* rt extent block mask */ +uint64_t m_rgblkmask;/* rt group block mask */ struct xfs_ino_geometry m_ino_geo;/* inode geometry */ struct xfs_trans_resv m_resv;/* precomputed res values */ /* low free space thresholds */
@@ -209,6 +212,7 @@ typedef struct xfs_mount {*/ atomic64_t m_allocbt_blks; +struct xarray m_rtgroups;/* per-rt group info */ struct radix_tree_root m_perag_tree;/* per-ag accounting info */ spinlock_t m_perag_lock;/* lock for m_perag_tree */ uint64_t m_resblks;/* total reserved blocks */
@@ -358,6 +362,16 @@ __XFS_HAS_FEAT(large_extent_counts, NREXT64) __XFS_HAS_FEAT(exchange_range, EXCHANGE_RANGE) __XFS_HAS_FEAT(metadir, METADIR) +static inline bool xfs_has_rtgroups (struct xfs_mount *mp) +{ +return false; +} + +static inline bool xfs_has_rtsb (struct xfs_mount *mp) +{ +return false; +} +/* * Some features are always on for v5 file systems, allow the compiler to * eliminiate dead code when building without v4 support.
fs/xfs/xfs_rtalloc.c +6 -0
@@ -27,6 +27,7 @@#include "xfs_health.h" #include "xfs_da_format.h" #include "xfs_metafile.h" +#include "xfs_rtgroup.h" /* * Return whether there are any free extents in the size range given
@@ -1136,6 +1137,8 @@ xfs_rtmount_inodes( {struct xfs_trans *tp;struct xfs_sb *sbp = &mp->m_sb; +struct xfs_rtgroup *rtg; +xfs_rgnumber_t rgno;int error; error =xfs_trans_alloc_empty (mp, &tp);
@@ -1166,6 +1169,9 @@ xfs_rtmount_inodes(if (error) goto out_rele_summary; +for_each_rtgroup (mp, rgno, rtg) + rtg->rtg_extents =xfs_rtgroup_extents (mp, rtg->rtg_rgno); + error =xfs_alloc_rsum_cache (mp, sbp->sb_rbmblocks);if (error) goto out_rele_summary;
fs/xfs/xfs_super.c +1 -0
@@ -2015,6 +2015,7 @@ static int xfs_init_fs_context(spin_lock_init (&mp->m_sb_lock);INIT_RADIX_TREE (&mp->m_perag_tree,GFP_ATOMIC );spin_lock_init (&mp->m_perag_lock); +xa_init (&mp->m_rtgroups);mutex_init (&mp->m_growlock);INIT_WORK (&mp->m_flush_inodes_work, xfs_flush_inodes_worker);INIT_DELAYED_WORK (&mp->m_reclaim_work, xfs_reclaim_worker);
fs/xfs/xfs_trace.c +1 -0
@@ -46,6 +46,7 @@#include "xfs_refcount.h" #include "xfs_metafile.h" #include "xfs_metadir.h" +#include "xfs_rtgroup.h" /* * We include this last to have the helpers above available for the trace
fs/xfs/xfs_trace.h +38 -0
@@ -94,6 +94,7 @@ struct xfs_extent_free_item;struct xfs_rmap_intent ;struct xfs_refcount_intent ;struct xfs_metadir_update ; +struct xfs_rtgroup ;#define XFS_ATTR_FILTER_FLAGS \ { XFS_ATTR_ROOT, "ROOT" }, \
@@ -220,6 +221,43 @@ DEFINE_PERAG_REF_EVENT(xfs_perag_rele);DEFINE_PERAG_REF_EVENT (xfs_perag_set_inode_tag);DEFINE_PERAG_REF_EVENT (xfs_perag_clear_inode_tag); +#ifdef CONFIG_XFS_RT +DECLARE_EVENT_CLASS (xfs_rtgroup_class, + TP_PROTO(struct xfs_rtgroup *rtg, unsigned long caller_ip), +TP_ARGS (rtg, caller_ip), +TP_STRUCT__entry ( + __field(dev_t, dev) + __field(xfs_rgnumber_t, rgno) + __field(int, refcount) + __field(int, active_refcount) + __field(unsigned long, caller_ip) + ), +TP_fast_assign ( + __entry->dev = rtg->rtg_mount->m_super->s_dev; + __entry->rgno = rtg->rtg_rgno; + __entry->refcount = atomic_read(&rtg->rtg_ref); + __entry->active_refcount = atomic_read(&rtg->rtg_active_ref); + __entry->caller_ip = caller_ip; + ), +TP_printk ("dev %d:%d rgno 0x%x passive refs %d active refs %d caller %pS" , +MAJOR (__entry->dev),MINOR (__entry->dev), + __entry->rgno, + __entry->refcount, + __entry->active_refcount, + (char *)__entry->caller_ip) +); + +#define DEFINE_RTGROUP_REF_EVENT (name) \ +DEFINE_EVENT(xfs_rtgroup_class, name, \ + TP_PROTO(struct xfs_rtgroup *rtg, unsigned long caller_ip), \ + TP_ARGS(rtg, caller_ip)) +DEFINE_RTGROUP_REF_EVENT (xfs_rtgroup_get); +DEFINE_RTGROUP_REF_EVENT (xfs_rtgroup_hold); +DEFINE_RTGROUP_REF_EVENT (xfs_rtgroup_put); +DEFINE_RTGROUP_REF_EVENT (xfs_rtgroup_grab); +DEFINE_RTGROUP_REF_EVENT (xfs_rtgroup_rele); +#endif /* CONFIG_XFS_RT */ + TRACE_EVENT(xfs_inodegc_worker, TP_PROTO(struct xfs_mount *mp, unsigned int shrinker_hits), TP_ARGS(mp, shrinker_hits),
Re: [PATCH 11/24] xfs: create incore realtime group structures
Looks good:
Re: [PATCH 11/24] xfs: create incore realtime group structures
In reply to Darrick J. WongQuoted 17 lines · Darrick J. Wong
From: Darrick J. Wong <[email protected]>
Create an incore object that will contain information about a realtime allocation group. This will eventually enable us to shard the realtime section in a similar manner to how we shard the data section, but for now just a single object for the entire RT subvolume is created.
Signed-off-by Darrick J. Wong <[email protected]>--- fs/xfs/Makefile | 1 fs/xfs/libxfs/xfs_format.h | 3 + fs/xfs/libxfs/xfs_rtgroup.c | 196 ++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_rtgroup.h | 212 +++++++++++++++++++++++++++++++++++++++++++ fs/xfs/libxfs/xfs_sb.c | 7 + fs/xfs/libxfs/xfs_types.h | 4 + fs/xfs/xfs_log_recover.c | 20 ++++ fs/xfs/xfs_mount.c | 16 +++
Quoted 8 lines
fs/xfs/xfs_mount.h | 14 +++ fs/xfs/xfs_rtalloc.c | 6 + fs/xfs/xfs_super.c | 1 fs/xfs/xfs_trace.c | 1 fs/xfs/xfs_trace.h | 38 ++++++++ 13 files changed, 517 insertions(+), 2 deletions(-) create mode 100644 fs/xfs/libxfs/xfs_rtgroup.c create mode 100644 fs/xfs/libxfs/xfs_rtgroup.h
Ok, how is the global address space for real time extents laid out across rt groups? i.e. is it sparse similar to how fsbnos and inode numbers are created for the data device like so?
fsbno = (agno << agblklog) | agbno
Or is it something different? I can't find that defined anywhere in this patch, so I can't determine if the unit conversion code and validation is correct or not...
Quoted 17 lines
fs/xfs/Makefile +1
-0@@ -60,6 +60,7 @@ xfs-y += $(addprefix libxfs/, \ # xfs_rtbitmap is shared with libxfs xfs-$(CONFIG_XFS_RT) += $(addprefix libxfs/, \ xfs_rtbitmap.o \ + xfs_rtgroup.o \ ) # highlevel codefs/xfs/libxfs/xfs_format.h +0
-0
fs/xfs/libxfs/xfs_format.h typedef struct xfs_sb { · line 180
Quoted 5 lines
fs/xfs/libxfs/xfs_format.h +2
-0@@ -176,6 +176,9 @@ typedef struct xfs_sb {xfs_ino_t sb_metadirino;/* metadata directory tree root */ +xfs_rgnumber_t sb_rgcount;/* number of realtime groups */ +xfs_rtxlen_t sb_rgextents;/* size of a realtime group in rtx */
So min/max rtgroup size is defined by the sb_rextsize field? What redundant metadata do we end up with that allows us to validate the sb_rextsize field is still valid w.r.t. rtgroups geometry?
Also, rtgroup lengths are defined by "rtx counts", but the definitions in the xfs_mount later on are "m_rtblklog" and "m_rgblocks" and we use xfs_rgblock_t and rgbno all over the place.
Just from the context of this patch, it is somewhat confusing trying to work out what the difference is...
Quoted 109 lines
fs/xfs/libxfs/xfs_format.h +0
-0/* must be padded to 64 bit alignment */ } xfs_sb_t;fs/xfs/libxfs/xfs_rtgroup.c new file +100
-0@@ -0,0 +1,196 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* +* Copyright (c) 2022-2024 Oracle. All Rights Reserved. +* Author: Darrick J. Wong <[email protected]> +*/ +#include "xfs.h" +#include "xfs_fs.h" +#include "xfs_shared.h" +#include "xfs_format.h" +#include "xfs_trans_resv.h" +#include "xfs_bit.h" +#include "xfs_sb.h" +#include "xfs_mount.h" +#include "xfs_btree.h" +#include "xfs_alloc_btree.h" +#include "xfs_rmap_btree.h" +#include "xfs_alloc.h" +#include "xfs_ialloc.h" +#include "xfs_rmap.h" +#include "xfs_ag.h" +#include "xfs_ag_resv.h" +#include "xfs_health.h" +#include "xfs_error.h" +#include "xfs_bmap.h" +#include "xfs_defer.h" +#include "xfs_log_format.h" +#include "xfs_trans.h" +#include "xfs_trace.h" +#include "xfs_inode.h" +#include "xfs_icache.h" +#include "xfs_rtgroup.h" +#include "xfs_rtbitmap.h" + +/* +* Passive reference counting access wrappers to the rtgroup structures. If +* the rtgroup structure is to be freed, the freeing code is responsible for +* cleaning up objects with passive references before freeing the structure. +*/ +struct xfs_rtgroup * +xfs_rtgroup_get ( +struct xfs_mount *mp, +xfs_rgnumber_t rgno) +{ +struct xfs_rtgroup *rtg; + +rcu_read_lock (); + rtg =xa_load (&mp->m_rtgroups, rgno); +if (rtg) { +trace_xfs_rtgroup_get (rtg, _RET_IP_); +ASSERT (atomic_read (&rtg->rtg_ref) >=0 ); +atomic_inc (&rtg->rtg_ref); + } +rcu_read_unlock (); +return rtg; +} + +/* Get a passive reference to the given rtgroup. */ +struct xfs_rtgroup * +xfs_rtgroup_hold ( +struct xfs_rtgroup *rtg) +{ +ASSERT (atomic_read (&rtg->rtg_ref) >0 || +atomic_read (&rtg->rtg_active_ref) >0 ); + +trace_xfs_rtgroup_hold (rtg, _RET_IP_); +atomic_inc (&rtg->rtg_ref); +return rtg; +} + +void +xfs_rtgroup_put ( +struct xfs_rtgroup *rtg) +{ +trace_xfs_rtgroup_put (rtg, _RET_IP_); +ASSERT (atomic_read (&rtg->rtg_ref) >0 ); +atomic_dec (&rtg->rtg_ref); +} + +/* +* Active references for rtgroup structures. This is for short term access to +* the rtgroup structures for walking trees or accessing state. If an rtgroup +* is being shrunk or is offline, then this will fail to find that group and +* return NULL instead. +*/ +struct xfs_rtgroup * +xfs_rtgroup_grab ( +struct xfs_mount *mp, +xfs_agnumber_t agno) +{ +struct xfs_rtgroup *rtg; + +rcu_read_lock (); + rtg =xa_load (&mp->m_rtgroups, agno); +if (rtg) { +trace_xfs_rtgroup_grab (rtg, _RET_IP_); +if (!atomic_inc_not_zero (&rtg->rtg_active_ref)) + rtg =NULL ; + } +rcu_read_unlock (); +return rtg;
fs/xfs/libxfs/xfs_rtgroup.c line 110
Quoted 10 lines
fs/xfs/libxfs/xfs_rtgroup.c new file +10
-0+} + +void +xfs_rtgroup_rele ( +struct xfs_rtgroup *rtg) +{ +trace_xfs_rtgroup_rele (rtg, _RET_IP_); +if (atomic_dec_and_test (&rtg->rtg_active_ref)) +wake_up (&rtg->rtg_active_wq); +}
This is all duplicates of the xfs_perag code. Can you put together a patchset to abstract this into a "xfs_group" and embed them in both the perag and and rtgroup structures?
That way we only need one set of lookup and iterator infrastructure, and it will work for both data and rt groups...
fs/xfs/libxfs/xfs_rtgroup.c line 183
Quoted 4 lines
fs/xfs/libxfs/xfs_rtgroup.c new file +4
-0+ +/* Compute the number of rt extents in this realtime group. */ +xfs_rtxnum_t +xfs_rtgroup_extents(
+ struct xfs_mount *mp,
fs/xfs/libxfs/xfs_rtgroup.c line 192
Quoted 8 lines
fs/xfs/libxfs/xfs_rtgroup.c new file +8
-0+ xfs_rgnumber_t rgno) +{ +xfs_rgnumber_t rgcount = mp->m_sb.sb_rgcount; + +ASSERT (rgno < rgcount); +if (rgno == rgcount -1 ) +return mp->m_sb.sb_rextents - + ((xfs_rtxnum_t )rgno * mp->m_sb.sb_rgextents);
Urk. So this relies on a non-rtgroup filesystem doing a multiplication by zero of a field that the on-disk format does not understand to get the right result. I think this is a copying a bad pattern we've been slowly trying to remove from the normal allocation group code.
fs/xfs/libxfs/xfs_rtgroup.c line 196
Quoted 4 lines
fs/xfs/libxfs/xfs_rtgroup.c new file +4
-0+ +ASSERT (xfs_has_rtgroups (mp)); +return mp->m_sb.sb_rgextents; +}
We already embed the length of the rtgroup in the rtgroup structure. THis should be looking up the rtgroup (or being passed the rtgroup the caller already has) and doing the right thing. i.e.
if (!rtg || !xfs_has_rtgroups(rtg->rtg_mount)) return mp->m_sb.sb_rextents; return rtg->rtg_extents;
Quoted 16 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +10
-0@@ -0,0 +1,212 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* +* Copyright (c) 2022-2024 Oracle. All Rights Reserved. +* Author: Darrick J. Wong <[email protected]> +*/ +#ifndef __LIBXFS_RTGROUP_H +#define __LIBXFS_RTGROUP_H 1 + +struct xfs_mount ; +struct xfs_trans ;
fs/xfs/libxfs/xfs_rtgroup.h line 20
Quoted 10 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +10
-0+ +/* +* Realtime group incore structure, similar to the per-AG structure. +*/ +struct xfs_rtgroup { +struct xfs_mount *rtg_mount; +xfs_rgnumber_t rtg_rgno; +atomic_t rtg_ref;/* passive reference count */ +atomic_t rtg_active_ref;/* active reference count */ +wait_queue_head_t rtg_active_wq;/* woken active_ref falls to zero */
Yeah, that's all common with xfs_perag....
....
Quoted 20 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +20
-0+/* +* rt group iteration APIs +*/ +static inline struct xfs_rtgroup * +xfs_rtgroup_next ( +struct xfs_rtgroup *rtg, +xfs_rgnumber_t *rgno, +xfs_rgnumber_t end_rgno) +{ +struct xfs_mount *mp = rtg->rtg_mount; + + *rgno = rtg->rtg_rgno +1 ; +xfs_rtgroup_rele (rtg); +if (*rgno > end_rgno) +return NULL ; +return xfs_rtgroup_grab (mp, *rgno); +} + +#define for_each_rtgroup_range (mp, rgno, end_rgno, rtg) \ + for ((rtg) = xfs_rtgroup_grab((mp), (rgno)); \
fs/xfs/libxfs/xfs_rtgroup.h line 100
Quoted 10 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +10
-0+ (rtg) !=NULL ; \ + (rtg) = xfs_rtgroup_next((rtg), &(rgno), (end_rgno))) + +#define for_each_rtgroup_from (mp, rgno, rtg) \ + for_each_rtgroup_range((mp), (rgno), (mp)->m_sb.sb_rgcount - 1, (rtg)) + + +#define for_each_rtgroup (mp, rgno, rtg) \ + (rgno) = 0; \ + for_each_rtgroup_from((mp), (rgno), (rtg))
Yup, that's all common with xfs_perag iteration, too. Can you put together a patchset to unify these, please?
fs/xfs/libxfs/xfs_rtgroup.h line 105
Quoted 4 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +4
-0+static inline bool +xfs_verify_rgbno ( +struct xfs_rtgroup *rtg, +xfs_rgblock_t rgbno)
Ok, what's the difference between and xfs_rgblock_t and a "rtx"?
OH.... Then penny just dropped - it's another "single letter difference that's really, really hard to spot" problem. You've defined "xfs_r*g*block_t" for the like a a*g*bno, but we have xfs_r*t*block_t for the global 64bit block number instead of a xfs_fsbno_t.
We just had a bug caused by exactly this sort of confusion with a patch that mixed xfs_[f]inobt changes together and one of the conversions was incorrect. Nobody spotted the single incorrect letter in the bigger patch, and I can see -exactly- the same sort of confusion happening with rtblock vs rgblock causing implicit 32/64 bit integer promotion bugs...
fs/xfs/libxfs/xfs_rtgroup.h line 110
Quoted 5 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +5
-0+{ +struct xfs_mount *mp = rtg->rtg_mount; + +if (rgbno >= rtg->rtg_extents * mp->m_sb.sb_rextsize) +return false;
Why isn't the max valid "rgbno" stored in the rtgroup instead of having to multiply the extent count by extent size every time we have to verify a rgbno? (i.e. same as pag->block_count).
We know from the agbno verification this will be a -very- hot path, and so precalculating all the constants and storing them in the rtg should be done right from the start here.
fs/xfs/libxfs/xfs_rtgroup.h line 113
Quoted 3 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +3
-0+if (xfs_has_rtsb (mp) && rtg->rtg_rgno ==0 && + rgbno < mp->m_sb.sb_rextsize) +return false;
Same here - this value is stored in pag->min_block...
fs/xfs/libxfs/xfs_rtgroup.h line 64
Quoted 2 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +2
-0+return true; +}
And then, if we put the max_bno and min_bno in the generic "xfs_group" structure, we suddenly have a generic "group bno" verification mechanism that is independent of whether the group
static inline bool
xfs_verify_gbno(
struct xfs_group *g,
xfs_gblock_t gbno)
{
struct xfs_mount *mp = g->g_mount;
if (gbno >= g->block_count)
return false;
if (gbno < g->min_block)
return false;
return true;
}And the rest of these functions fall out the same way....
fs/xfs/libxfs/xfs_rtgroup.h line 140
Quoted 9 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +9
-0+static inline xfs_rtblock_t +xfs_rgno_start_rtb ( +struct xfs_mount *mp, +xfs_rgnumber_t rgno) +{ +if (mp->m_rgblklog >=0 ) +return ((xfs_rtblock_t )rgno << mp->m_rgblklog); +return ((xfs_rtblock_t )rgno * mp->m_rgblocks); +}
Where does mp->m_rgblklog come from? That wasn't added to the on-disk superblock structure and it is always initialised to zero in this patch.
When will m_rgblklog be zero and when will it be non-zero? If it's only going to be zero for existing non-rtg realtime systems, then this code makes little sense (again, relying on multiplication by zero to get the right result). If it's not always used for rtg enabled filesytsems, then the reason for that has not been explained and I can't work out why this would ever need to be done.
Quoted 12 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +12
-0+static inline xfs_rtblock_t +xfs_rgbno_to_rtb ( +struct xfs_mount *mp, +xfs_rgnumber_t rgno, +xfs_rgblock_t rgbno) +{ +return xfs_rgno_start_rtb (mp, rgno) + rgbno; +} + +static inline xfs_rgnumber_t +xfs_rtb_to_rgno( + struct xfs_mount *mp,
fs/xfs/libxfs/xfs_rtgroup.h line 163
Quoted 10 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +10
-0+ xfs_rtblock_t rtbno) +{ +if (!xfs_has_rtgroups (mp)) +return 0 ; + +if (mp->m_rgblklog >=0 ) +return rtbno >> mp->m_rgblklog; + +return div_u64 (rtbno, mp->m_rgblocks); +}
Ah, now I'm really confused, because m_rgblklog is completely bypassed for legacy rt filesystems.
And I just realised, this "if (mp->m_rgblklog >= 0)" implies that m_rgblklog can have negative values and there's no comments anywhere about why that can happen and what would trigger it.
We validate sb_agblklog during the superblock verifier, and so once the filesystem is mounted we never, ever need to check whether sb_agblklog is in range. Why is the rtblklog being handled so differently here?
Quoted 7 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +7
-0+ +static inline uint64_t +__xfs_rtb_to_rgbno ( +struct xfs_mount *mp, +xfs_rtblock_t rtbno) +{ +uint32_t rem;
fs/xfs/libxfs/xfs_rtgroup.h line 180
Quoted 10 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +10
-0+ +if (!xfs_has_rtgroups (mp)) +return rtbno; + +if (mp->m_rgblklog >=0 ) +return rtbno & mp->m_rgblkmask; + +div_u64_rem (rtbno, mp->m_rgblocks, &rem); +return rem; +}
Why is this function returning a uint64_t - a xfs_rgblock_t is only a 32 bit type...
Quoted 14 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +14
-0+ +static inline xfs_rgblock_t +xfs_rtb_to_rgbno ( +struct xfs_mount *mp, +xfs_rtblock_t rtbno) +{ +return __xfs_rtb_to_rgbno (mp, rtbno); +} + +static inline xfs_daddr_t +xfs_rtb_to_daddr ( +struct xfs_mount *mp, +xfs_rtblock_t rtbno) +{
fs/xfs/libxfs/xfs_rtgroup.h line 204
Quoted 10 lines
fs/xfs/libxfs/xfs_rtgroup.h new file +10
-0+return rtbno << mp->m_blkbb_log; +} + +static inline xfs_rtblock_t +xfs_daddr_to_rtb ( +struct xfs_mount *mp, +xfs_daddr_t daddr) +{ +return daddr >> mp->m_blkbb_log; +}
Ah. This code doesn't sparsify the xfs_rtblock_t address space for rtgroups. xfs_rtblock_t is still direct physical encoding of the location on disk.
I really think that needs to be changed to match how xfs_fsbno_t is a sparse encoding before these changes get merged. It shouldn't affect any of the other code in the patch set - the existing rt code has a rtgno of 0, so it will always be a direct physical encoding even when using a sparse xfs_rtblock_t address space.
All that moving to a sparse encoding means is that the addresses stored in the BMBT are logical addresses rather than physical addresses. It should not affect any of the other code, just what ends up stored on disk for global 64-bit rt extent addresses...
In doing this, I think we can greatly simply all this group management stuff as most of the verification, type conversion and iteration infrastructure can then be shared between the exist perag and the new rtg infrastructure....
fs/xfs/libxfs/xfs_types.h line 12
Quoted 9 lines
fs/xfs/libxfs/xfs_types.h +1
-0@@ -9,10 +9,12 @@typedef uint32_t prid_t ;/* project ID */ typedef uint32_t xfs_agblock_t ;/* blockno in alloc. group */ +typedef uint32_t xfs_rgblock_t ;/* blockno in realtime group */
Is that right? The rtg length is 2^32 * rtextsize, and rtextsize can be 2^20 bytes:
#define XFS_MAX_RTEXTSIZE (1024 * 1024 * 1024)
Hence for a 4kB fsbno filesystem, the actual maximum size of an rtg in filesystem blocks far exceeds what we can address with a 32 bit variable.
If xfs_rgblock_t is actually indexing multi-fsbno rtextents, then it is an extent number index, not a "block" index. An extent number index won't overflow 32 bits (because the rtg has a max of 2^32 - 1 rtextents)
IOWs, shouldn't this be named soemthing like:
typedef uint32_t xfs_rgext_t; /* extent number in realtime group */
Quoted 9 lines
fs/xfs/libxfs/xfs_types.h +1
-0typedef uint32_t xfs_agino_t ;/* inode # within allocation grp */ typedef uint32_t xfs_extlen_t ;/* extent length in blocks */ typedef uint32_t xfs_rtxlen_t ;/* file extent length in rtextents */ typedef uint32_t xfs_agnumber_t ;/* allocation group number */ +typedef uint32_t xfs_rgnumber_t ;/* realtime group number */ typedef uint64_t xfs_extnum_t ;/* # of extents in a file */ typedef uint32_t xfs_aextnum_t ;/* # extents in an attribute fork */ typedef int64_t xfs_fsize_t ;/* bytes in a file */
fs/xfs/libxfs/xfs_types.h typedef void * xfs_failaddr_t; · line 60
Quoted 6 lines
fs/xfs/libxfs/xfs_types.h +2
-0@@ -53,7 +55,9 @@ typedef void * xfs_failaddr_t;#define NULLFILEOFF ((xfs_fileoff_t)-1)#define NULLAGBLOCK ((xfs_agblock_t)-1) +#define NULLRGBLOCK ((xfs_rgblock_t)-1)#define NULLAGNUMBER ((xfs_agnumber_t)-1) +#define NULLRGNUMBER ((xfs_rgnumber_t)-1)
What's the maximum valid rtg number? We're not ever going to be supporting 2^32 - 2 rtgs, so what is a realistic maximum we can cap this at and validate it at?
Quoted 34 lines
fs/xfs/libxfs/xfs_types.h +0
-0#define NULLCOMMITLSN ((xfs_lsn_t)-1)fs/xfs/xfs_log_recover.c +10
-0@@ -28,6 +28,7 @@#include "xfs_ag.h" #include "xfs_quota.h" #include "xfs_reflink.h" +#include "xfs_rtgroup.h" #define BLK_AVG (blk1, blk2) ((blk1+blk2) >> 1)@@ -3346,6 +3347,7 @@ xlog_do_recover(struct xfs_mount *mp = log->l_mp;struct xfs_buf *bp = mp->m_sb_bp;struct xfs_sb *sbp = &mp->m_sb; +xfs_rgnumber_t old_rgcount = sbp->sb_rgcount;int error;trace_xfs_log_recover (log, head_blk, tail_blk);@@ -3399,6 +3401,24 @@ xlog_do_recover(xfs_warn (mp,"Failed post-recovery per-ag init: %d" , error);return error; } + +if (sbp->sb_rgcount < old_rgcount) { +xfs_warn (mp,"rgcount shrink not supported" ); +return -EINVAL ; + } +if (sbp->sb_rgcount > old_rgcount) { +xfs_rgnumber_t rgno; +
fs/xfs/xfs_log_recover.c xlog_do_recover( · line 3421
Quoted 10 lines
fs/xfs/xfs_log_recover.c +10
-0+for (rgno = old_rgcount; rgno < sbp->sb_rgcount; rgno++) { + error =xfs_rtgroup_alloc (mp, rgno); +if (error) { +xfs_warn (mp, +"Failed post-recovery rtgroup init: %d" , + error); +return error; + } + } + }
Please factor this out into a separate function with all the other rtgroup init/teardown code. That means we don't have to care about how rtgrowfs functions in recovery code, similar to the xfs_initialize_perag() already in this function for handling recovery of data device growing...
Quoted 24 lines
fs/xfs/xfs_log_recover.c +0
-0mp->m_alloc_set_aside =xfs_alloc_set_aside (mp);/* Normal transactions can now occur */ fs/xfs/xfs_mount.c +2
-0@@ -36,6 +36,7 @@#include "xfs_ag.h" #include "xfs_rtbitmap.h" #include "xfs_metafile.h" +#include "xfs_rtgroup.h" #include "scrub/stats.h" static DEFINE_MUTEX (xfs_uuid_table_mutex);@@ -664,6 +665,7 @@ xfs_mountfs(struct xfs_ino_geometry *igeo =M_IGEO (mp);uint quotamount =0 ;uint quotaflags =0 ; +xfs_rgnumber_t rgno;int error =0 ;xfs_sb_mount_common (mp, sbp);
fs/xfs/xfs_mount.c xfs_mountfs( · line 841
Quoted 10 lines
fs/xfs/xfs_mount.c +7
-0@@ -830,10 +832,18 @@ xfs_mountfs( goto out_free_dir; } +for (rgno =0 ; rgno < mp->m_sb.sb_rgcount; rgno++) { + error =xfs_rtgroup_alloc (mp, rgno); +if (error) { +xfs_warn (mp,"Failed rtgroup init: %d" , error); + goto out_free_rtgroup; + } + }
Same - factor this to a xfs_rtgroup_init() function located with the rest of the rtgroup infrastructure...
Quoted 20 lines
fs/xfs/xfs_mount.c +4
-2+if (XFS_IS_CORRUPT (mp, !sbp->sb_logblocks)) {xfs_warn (mp,"no log defined" ); error = -EFSCORRUPTED ; - goto out_free_perag; + goto out_free_rtgroup; } error =xfs_inodegc_register_shrinker (mp);@@ -1068,7 +1078,8 @@ xfs_mountfs(if (mp->m_logdev_targp && mp->m_logdev_targp != mp->m_ddev_targp)xfs_buftarg_drain (mp->m_logdev_targp);xfs_buftarg_drain (mp->m_ddev_targp); - out_free_perag: + out_free_rtgroup: +xfs_free_rtgroups (mp, rgno);xfs_free_perag (mp); out_free_dir:xfs_da_unmount (mp);
fs/xfs/xfs_mount.c xfs_unmountfs( · line 1166
Quoted 4 lines
fs/xfs/xfs_mount.c +1
-0@@ -1152,6 +1163,7 @@ xfs_unmountfs(xfs_errortag_clearall (mp);#endif shrinker_free (mp->m_inodegc_shrinker); +xfs_free_rtgroups (mp, mp->m_sb.sb_rgcount);
... like you've already for the cleanup side ;)
....
fs/xfs/xfs_mount.c xfs_rtmount_inodes( · line 1174
Quoted 7 lines
fs/xfs/xfs_mount.c +3
-0@@ -1166,6 +1169,9 @@ xfs_rtmount_inodes(if (error) goto out_rele_summary; +for_each_rtgroup (mp, rgno, rtg) + rtg->rtg_extents =xfs_rtgroup_extents (mp, rtg->rtg_rgno); +
This also needs to be done after recovery has initialised new rtgs as a result fo replaying a sb growfs modification, right?
Which leads to the next question: if there are thousands of rtgs, this requires walking every rtg at mount time, right? We know that walking thousands of static structures at mount time is a scalability issue, so can we please avoid this if at all possible? i.e. do demand loading of per-rtg metadata when it is first required (like we do with agf/agi information) rather than doing it all at mount time...
-Dave.
Signature · 2 lines
Dave Chinner [email protected]