xfs: update BDI {io,ra}_pages values based on the RT device limits

When using XFS with a main device on an SSD that stores metadata and a RT
device to store data on a HDD, we fail to take the I/O sizes for the RT
device into accounting, leading to up to 5% slower read performance when
using an SSD for metadata vs storing data and metadata on the HDD.

Fix this up by taking the RT settings into account at mount an restoring
the old settings at unmount time, unless the BDI settings have changed
from those set by XFS.

Reported-by: Filip Blagojevic <filip.blagojevic@wdc.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>
Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
This commit is contained in:
Christoph Hellwig 2026-07-20 16:08:47 +02:00 committed by Carlos Maiolino
parent d852729c5f
commit 7aa67044e7
2 changed files with 59 additions and 1 deletions

View File

@ -349,6 +349,13 @@ typedef struct xfs_mount {
/* Index of uuid record in the uuid xarray. */
unsigned int m_uuid_table_index;
/*
* Old io_pages/ra_pages valued in the main bdev BDI, and our initial
* calculated values.
*/
unsigned long m_old_io_pages, m_initial_io_pages;
unsigned long m_old_ra_pages, m_initial_ra_pages;
} xfs_mount_t;
#define M_IGEO(mp) (&(mp)->m_ino_geo)

View File

@ -545,6 +545,52 @@ xfs_open_devices(
return error;
}
/*
* When using a RT device some or all data I/O is using the RT device, but
* the BDI is inherited from the main data device. When the underlying block
* device for the RT device has larger I/O sizes, the BDI settings might be
* incorrect, which is especially bad if the main device is a SSD and the
* RT device is a HDD, as the io_opt fixup in blk_apply_bdi_limits is missing
* for this case.
*
* Update the BDI values to the max of the data and RT device to cover our
* bases.
*/
static void
xfs_update_bdi_rahead(
struct xfs_mount *mp)
{
struct backing_dev_info *rt_bdi =
mp->m_rtdev_targp->bt_bdev->bd_disk->bdi;
struct backing_dev_info *sb_bdi = mp->m_super->s_bdi;
mp->m_old_io_pages = sb_bdi->io_pages;
mp->m_old_ra_pages = sb_bdi->ra_pages;
sb_bdi->io_pages = mp->m_initial_io_pages =
max(sb_bdi->io_pages, rt_bdi->io_pages);
sb_bdi->ra_pages = mp->m_initial_ra_pages =
max(sb_bdi->ra_pages, rt_bdi->ra_pages);
}
static void
xfs_restore_bdi_rahead(
struct xfs_mount *mp)
{
struct backing_dev_info *sb_bdi = mp->m_super->s_bdi;
if (sb_bdi->io_pages == mp->m_initial_io_pages)
sb_bdi->io_pages = mp->m_old_io_pages;
else
xfs_info(mp, "io_pages changed from %lu to %lu, not restoring.",
mp->m_initial_io_pages, sb_bdi->io_pages);
if (sb_bdi->ra_pages == mp->m_initial_ra_pages)
sb_bdi->ra_pages = mp->m_old_ra_pages;
else
xfs_info(mp, "ra_pages changed from %lu to %lu, not restoring.",
mp->m_initial_ra_pages, sb_bdi->ra_pages);
}
/*
* Setup xfs_mount buffer target pointers based on superblock
*/
@ -582,6 +628,7 @@ xfs_setup_devices(
mp->m_sb.sb_sectsize, mp->m_sb.sb_rblocks);
if (error)
return error;
xfs_update_bdi_rahead(mp);
}
return 0;
@ -2280,8 +2327,12 @@ static void
xfs_kill_sb(
struct super_block *sb)
{
struct xfs_mount *mp = XFS_M(sb);
if (mp->m_rtdev_targp && mp->m_rtdev_targp != mp->m_ddev_targp)
xfs_restore_bdi_rahead(mp);
kill_block_super(sb);
xfs_mount_free(XFS_M(sb));
xfs_mount_free(mp);
}
static struct file_system_type xfs_fs_type = {