XFS bug fies for 6.12-rc6

* fix a sysbot reported crash on filestreams
 * Reduce cpu time spent searching for extents in
   a very fragmented FS
 * Check for delayed allocations before setting extsize
 
 Signed-off-by: Carlos Maiolino <cem@kernel.org>
 -----BEGIN PGP SIGNATURE-----
 
 iJUEABMJAB0WIQQMHYkcUKcy4GgPe2RGdaER5QtfpgUCZyIMDwAKCRBGdaER5Qtf
 pllxAYCkk+mtDTD5xBfOVGZWO5MMFz8HqYcro5wrSCzgL8HDmW29kXTBYFviGn3R
 3l/H6BEBgOk0EkI5qGOzijpzbsWyJeLzPzZtxQFPD8zFBdxSERCtbpqFDLLvLQWG
 M+TLhUNkPQ==
 =kKX4
 -----END PGP SIGNATURE-----

Merge tag 'xfs-6.12-fixes-6' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux

Pull xfs fixes from Carlos Maiolino:

 - fix a sysbot reported crash on filestreams

 - Reduce cpu time spent searching for extents in a very fragmented FS

 - Check for delayed allocations before setting extsize

* tag 'xfs-6.12-fixes-6' of git://git.kernel.org/pub/scm/fs/xfs/xfs-linux:
  xfs: streamline xfs_filestream_pick_ag
  xfs: fix finding a last resort AG in xfs_filestream_pick_ag
  xfs: Reduce unnecessary searches when searching for the best extents
  xfs: Check for delayed allocations before setting extsize
This commit is contained in:
Linus Torvalds 2024-11-02 09:22:16 -10:00
commit f6a7b4ec74
6 changed files with 65 additions and 68 deletions

View File

@ -1923,7 +1923,7 @@ xfs_alloc_ag_vextent_size(
error = -EFSCORRUPTED;
goto error0;
}
if (flen < bestrlen)
if (flen <= bestrlen)
break;
busy = xfs_alloc_compute_aligned(args, fbno, flen,
&rbno, &rlen, &busy_gen);

View File

@ -64,25 +64,31 @@ xfs_filestream_pick_ag(
struct xfs_perag *pag;
struct xfs_perag *max_pag = NULL;
xfs_extlen_t minlen = *longest;
xfs_extlen_t free = 0, minfree, maxfree = 0;
xfs_extlen_t minfree, maxfree = 0;
xfs_agnumber_t agno;
bool first_pass = true;
int err;
/* 2% of an AG's blocks must be free for it to be chosen. */
minfree = mp->m_sb.sb_agblocks / 50;
restart:
for_each_perag_wrap(mp, start_agno, agno, pag) {
int err;
trace_xfs_filestream_scan(pag, pino);
*longest = 0;
err = xfs_bmap_longest_free_extent(pag, NULL, longest);
if (err) {
if (err != -EAGAIN)
break;
/* Couldn't lock the AGF, skip this AG. */
err = 0;
continue;
if (err == -EAGAIN) {
/* Couldn't lock the AGF, skip this AG. */
err = 0;
continue;
}
xfs_perag_rele(pag);
if (max_pag)
xfs_perag_rele(max_pag);
return err;
}
/* Keep track of the AG with the most free blocks. */
@ -107,8 +113,9 @@ xfs_filestream_pick_ag(
!(flags & XFS_PICK_USERDATA) ||
(flags & XFS_PICK_LOWSPACE))) {
/* Break out, retaining the reference on the AG. */
free = pag->pagf_freeblks;
break;
if (max_pag)
xfs_perag_rele(max_pag);
goto done;
}
}
@ -116,57 +123,47 @@ xfs_filestream_pick_ag(
atomic_dec(&pag->pagf_fstrms);
}
if (err) {
xfs_perag_rele(pag);
if (max_pag)
xfs_perag_rele(max_pag);
return err;
/*
* Allow a second pass to give xfs_bmap_longest_free_extent() another
* attempt at locking AGFs that it might have skipped over before we
* fail.
*/
if (first_pass) {
first_pass = false;
goto restart;
}
if (!pag) {
/*
* Allow a second pass to give xfs_bmap_longest_free_extent()
* another attempt at locking AGFs that it might have skipped
* over before we fail.
*/
if (first_pass) {
first_pass = false;
goto restart;
}
/*
* We must be low on data space, so run a final lowspace
* optimised selection pass if we haven't already.
*/
if (!(flags & XFS_PICK_LOWSPACE)) {
flags |= XFS_PICK_LOWSPACE;
goto restart;
}
/*
* No unassociated AGs are available, so select the AG with the
* most free space, regardless of whether it's already in use by
* another filestream. It none suit, just use whatever AG we can
* grab.
*/
if (!max_pag) {
for_each_perag_wrap(args->mp, 0, start_agno, args->pag)
break;
atomic_inc(&args->pag->pagf_fstrms);
*longest = 0;
} else {
pag = max_pag;
free = maxfree;
atomic_inc(&pag->pagf_fstrms);
}
} else if (max_pag) {
xfs_perag_rele(max_pag);
/*
* We must be low on data space, so run a final lowspace optimised
* selection pass if we haven't already.
*/
if (!(flags & XFS_PICK_LOWSPACE)) {
flags |= XFS_PICK_LOWSPACE;
goto restart;
}
trace_xfs_filestream_pick(pag, pino, free);
/*
* No unassociated AGs are available, so select the AG with the most
* free space, regardless of whether it's already in use by another
* filestream. It none suit, just use whatever AG we can grab.
*/
if (!max_pag) {
for_each_perag_wrap(args->mp, 0, start_agno, pag) {
max_pag = pag;
break;
}
/* Bail if there are no AGs at all to select from. */
if (!max_pag)
return -ENOSPC;
}
pag = max_pag;
atomic_inc(&pag->pagf_fstrms);
done:
trace_xfs_filestream_pick(pag, pino);
args->pag = pag;
return 0;
}
static struct xfs_inode *

View File

@ -1409,7 +1409,7 @@ xfs_inactive(
if (S_ISREG(VFS_I(ip)->i_mode) &&
(ip->i_disk_size != 0 || XFS_ISIZE(ip) != 0 ||
ip->i_df.if_nextents > 0 || ip->i_delayed_blks > 0))
xfs_inode_has_filedata(ip)))
truncate = 1;
if (xfs_iflags_test(ip, XFS_IQUOTAUNCHECKED)) {

View File

@ -292,6 +292,11 @@ static inline bool xfs_is_cow_inode(struct xfs_inode *ip)
return xfs_is_reflink_inode(ip) || xfs_is_always_cow_inode(ip);
}
static inline bool xfs_inode_has_filedata(const struct xfs_inode *ip)
{
return ip->i_df.if_nextents > 0 || ip->i_delayed_blks > 0;
}
/*
* Check if an inode has any data in the COW fork. This might be often false
* even for inodes with the reflink flag when there is no pending COW operation.

View File

@ -481,7 +481,7 @@ xfs_ioctl_setattr_xflags(
if (rtflag != XFS_IS_REALTIME_INODE(ip)) {
/* Can't change realtime flag if any extents are allocated. */
if (ip->i_df.if_nextents || ip->i_delayed_blks)
if (xfs_inode_has_filedata(ip))
return -EINVAL;
/*
@ -602,7 +602,7 @@ xfs_ioctl_setattr_check_extsize(
if (!fa->fsx_valid)
return 0;
if (S_ISREG(VFS_I(ip)->i_mode) && ip->i_df.if_nextents &&
if (S_ISREG(VFS_I(ip)->i_mode) && xfs_inode_has_filedata(ip) &&
XFS_FSB_TO_B(mp, ip->i_extsize) != fa->fsx_extsize)
return -EINVAL;

View File

@ -691,8 +691,8 @@ DEFINE_FILESTREAM_EVENT(xfs_filestream_lookup);
DEFINE_FILESTREAM_EVENT(xfs_filestream_scan);
TRACE_EVENT(xfs_filestream_pick,
TP_PROTO(struct xfs_perag *pag, xfs_ino_t ino, xfs_extlen_t free),
TP_ARGS(pag, ino, free),
TP_PROTO(struct xfs_perag *pag, xfs_ino_t ino),
TP_ARGS(pag, ino),
TP_STRUCT__entry(
__field(dev_t, dev)
__field(xfs_ino_t, ino)
@ -703,14 +703,9 @@ TRACE_EVENT(xfs_filestream_pick,
TP_fast_assign(
__entry->dev = pag->pag_mount->m_super->s_dev;
__entry->ino = ino;
if (pag) {
__entry->agno = pag->pag_agno;
__entry->streams = atomic_read(&pag->pagf_fstrms);
} else {
__entry->agno = NULLAGNUMBER;
__entry->streams = 0;
}
__entry->free = free;
__entry->agno = pag->pag_agno;
__entry->streams = atomic_read(&pag->pagf_fstrms);
__entry->free = pag->pagf_freeblks;
),
TP_printk("dev %d:%d ino 0x%llx agno 0x%x streams %d free %d",
MAJOR(__entry->dev), MINOR(__entry->dev),