1

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 @@ restart:
error = -EFSCORRUPTED; error = -EFSCORRUPTED;
goto error0; goto error0;
} }
if (flen < bestrlen) if (flen <= bestrlen)
break; break;
busy = xfs_alloc_compute_aligned(args, fbno, flen, busy = xfs_alloc_compute_aligned(args, fbno, flen,
&rbno, &rlen, &busy_gen); &rbno, &rlen, &busy_gen);

View File

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

View File

@ -1409,7 +1409,7 @@ xfs_inactive(
if (S_ISREG(VFS_I(ip)->i_mode) && if (S_ISREG(VFS_I(ip)->i_mode) &&
(ip->i_disk_size != 0 || XFS_ISIZE(ip) != 0 || (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; truncate = 1;
if (xfs_iflags_test(ip, XFS_IQUOTAUNCHECKED)) { 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); 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 * 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. * 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)) { if (rtflag != XFS_IS_REALTIME_INODE(ip)) {
/* Can't change realtime flag if any extents are allocated. */ /* 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; return -EINVAL;
/* /*
@ -602,7 +602,7 @@ xfs_ioctl_setattr_check_extsize(
if (!fa->fsx_valid) if (!fa->fsx_valid)
return 0; 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) XFS_FSB_TO_B(mp, ip->i_extsize) != fa->fsx_extsize)
return -EINVAL; return -EINVAL;

View File

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