mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-26 02:39:48 +00:00
![Dave Chinner](/assets/img/avatar_default.png)
From a concurrent rm -rf workload: 41.04% [kernel] [k] xfs_dir3_leaf_check_int 9.85% [kernel] [k] __xfs_dir3_data_check 5.60% [kernel] [k] xfs_verify_ino 5.32% [kernel] [k] xfs_agino_range 4.21% [kernel] [k] memcpy 3.06% [kernel] [k] xfs_errortag_test 2.57% [kernel] [k] xfs_dir_ino_validate 1.66% [kernel] [k] xfs_dir2_data_get_ftype 1.17% [kernel] [k] do_raw_spin_lock 1.11% [kernel] [k] xfs_verify_dir_ino 0.84% [kernel] [k] __raw_callee_save___pv_queued_spin_unlock 0.83% [kernel] [k] xfs_buf_find 0.64% [kernel] [k] xfs_log_commit_cil THere's an awful lot of overhead in just range checking inode numbers in that, but each inode number check is not a lot of code. The total is a bit over 14.5% of the CPU time is spent validating inode numbers. The problem is that they deeply nested global scope functions so the overhead here is all in function call marshalling. text data bss dec hex filename 2077 0 0 2077 81d fs/xfs/libxfs/xfs_types.o.orig 2197 0 0 2197 895 fs/xfs/libxfs/xfs_types.o There's a small increase in binary size by inlining all the local nested calls in the verifier functions, but the same workload now profiles as: 40.69% [kernel] [k] xfs_dir3_leaf_check_int 10.52% [kernel] [k] __xfs_dir3_data_check 6.68% [kernel] [k] xfs_verify_dir_ino 4.22% [kernel] [k] xfs_errortag_test 4.15% [kernel] [k] memcpy 3.53% [kernel] [k] xfs_dir_ino_validate 1.87% [kernel] [k] xfs_dir2_data_get_ftype 1.37% [kernel] [k] do_raw_spin_lock 0.98% [kernel] [k] xfs_buf_find 0.94% [kernel] [k] __raw_callee_save___pv_queued_spin_unlock 0.73% [kernel] [k] xfs_log_commit_cil Now we only spend just over 10% of the time validing inode numbers for the same workload. Hence a few "inline" keyworks is good enough to reduce the validation overhead by 30%... Signed-off-by: Dave Chinner <dchinner@redhat.com> Reviewed-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Darrick J. Wong <djwong@kernel.org>
285 lines
6 KiB
C
285 lines
6 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2000-2002,2005 Silicon Graphics, Inc.
|
|
* Copyright (C) 2017 Oracle.
|
|
* All Rights Reserved.
|
|
*/
|
|
#include "xfs.h"
|
|
#include "xfs_fs.h"
|
|
#include "xfs_format.h"
|
|
#include "xfs_shared.h"
|
|
#include "xfs_trans_resv.h"
|
|
#include "xfs_bit.h"
|
|
#include "xfs_mount.h"
|
|
|
|
/* Find the size of the AG, in blocks. */
|
|
inline xfs_agblock_t
|
|
xfs_ag_block_count(
|
|
struct xfs_mount *mp,
|
|
xfs_agnumber_t agno)
|
|
{
|
|
ASSERT(agno < mp->m_sb.sb_agcount);
|
|
|
|
if (agno < mp->m_sb.sb_agcount - 1)
|
|
return mp->m_sb.sb_agblocks;
|
|
return mp->m_sb.sb_dblocks - (agno * mp->m_sb.sb_agblocks);
|
|
}
|
|
|
|
/*
|
|
* Verify that an AG block number pointer neither points outside the AG
|
|
* nor points at static metadata.
|
|
*/
|
|
inline bool
|
|
xfs_verify_agbno(
|
|
struct xfs_mount *mp,
|
|
xfs_agnumber_t agno,
|
|
xfs_agblock_t agbno)
|
|
{
|
|
xfs_agblock_t eoag;
|
|
|
|
eoag = xfs_ag_block_count(mp, agno);
|
|
if (agbno >= eoag)
|
|
return false;
|
|
if (agbno <= XFS_AGFL_BLOCK(mp))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Verify that an FS block number pointer neither points outside the
|
|
* filesystem nor points at static AG metadata.
|
|
*/
|
|
inline bool
|
|
xfs_verify_fsbno(
|
|
struct xfs_mount *mp,
|
|
xfs_fsblock_t fsbno)
|
|
{
|
|
xfs_agnumber_t agno = XFS_FSB_TO_AGNO(mp, fsbno);
|
|
|
|
if (agno >= mp->m_sb.sb_agcount)
|
|
return false;
|
|
return xfs_verify_agbno(mp, agno, XFS_FSB_TO_AGBNO(mp, fsbno));
|
|
}
|
|
|
|
/*
|
|
* Verify that a data device extent is fully contained inside the filesystem,
|
|
* does not cross an AG boundary, and does not point at static metadata.
|
|
*/
|
|
bool
|
|
xfs_verify_fsbext(
|
|
struct xfs_mount *mp,
|
|
xfs_fsblock_t fsbno,
|
|
xfs_fsblock_t len)
|
|
{
|
|
if (fsbno + len <= fsbno)
|
|
return false;
|
|
|
|
if (!xfs_verify_fsbno(mp, fsbno))
|
|
return false;
|
|
|
|
if (!xfs_verify_fsbno(mp, fsbno + len - 1))
|
|
return false;
|
|
|
|
return XFS_FSB_TO_AGNO(mp, fsbno) ==
|
|
XFS_FSB_TO_AGNO(mp, fsbno + len - 1);
|
|
}
|
|
|
|
/* Calculate the first and last possible inode number in an AG. */
|
|
inline void
|
|
xfs_agino_range(
|
|
struct xfs_mount *mp,
|
|
xfs_agnumber_t agno,
|
|
xfs_agino_t *first,
|
|
xfs_agino_t *last)
|
|
{
|
|
xfs_agblock_t bno;
|
|
xfs_agblock_t eoag;
|
|
|
|
eoag = xfs_ag_block_count(mp, agno);
|
|
|
|
/*
|
|
* Calculate the first inode, which will be in the first
|
|
* cluster-aligned block after the AGFL.
|
|
*/
|
|
bno = round_up(XFS_AGFL_BLOCK(mp) + 1, M_IGEO(mp)->cluster_align);
|
|
*first = XFS_AGB_TO_AGINO(mp, bno);
|
|
|
|
/*
|
|
* Calculate the last inode, which will be at the end of the
|
|
* last (aligned) cluster that can be allocated in the AG.
|
|
*/
|
|
bno = round_down(eoag, M_IGEO(mp)->cluster_align);
|
|
*last = XFS_AGB_TO_AGINO(mp, bno) - 1;
|
|
}
|
|
|
|
/*
|
|
* Verify that an AG inode number pointer neither points outside the AG
|
|
* nor points at static metadata.
|
|
*/
|
|
inline bool
|
|
xfs_verify_agino(
|
|
struct xfs_mount *mp,
|
|
xfs_agnumber_t agno,
|
|
xfs_agino_t agino)
|
|
{
|
|
xfs_agino_t first;
|
|
xfs_agino_t last;
|
|
|
|
xfs_agino_range(mp, agno, &first, &last);
|
|
return agino >= first && agino <= last;
|
|
}
|
|
|
|
/*
|
|
* Verify that an AG inode number pointer neither points outside the AG
|
|
* nor points at static metadata, or is NULLAGINO.
|
|
*/
|
|
bool
|
|
xfs_verify_agino_or_null(
|
|
struct xfs_mount *mp,
|
|
xfs_agnumber_t agno,
|
|
xfs_agino_t agino)
|
|
{
|
|
return agino == NULLAGINO || xfs_verify_agino(mp, agno, agino);
|
|
}
|
|
|
|
/*
|
|
* Verify that an FS inode number pointer neither points outside the
|
|
* filesystem nor points at static AG metadata.
|
|
*/
|
|
inline bool
|
|
xfs_verify_ino(
|
|
struct xfs_mount *mp,
|
|
xfs_ino_t ino)
|
|
{
|
|
xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, ino);
|
|
xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ino);
|
|
|
|
if (agno >= mp->m_sb.sb_agcount)
|
|
return false;
|
|
if (XFS_AGINO_TO_INO(mp, agno, agino) != ino)
|
|
return false;
|
|
return xfs_verify_agino(mp, agno, agino);
|
|
}
|
|
|
|
/* Is this an internal inode number? */
|
|
inline bool
|
|
xfs_internal_inum(
|
|
struct xfs_mount *mp,
|
|
xfs_ino_t ino)
|
|
{
|
|
return ino == mp->m_sb.sb_rbmino || ino == mp->m_sb.sb_rsumino ||
|
|
(xfs_sb_version_hasquota(&mp->m_sb) &&
|
|
xfs_is_quota_inode(&mp->m_sb, ino));
|
|
}
|
|
|
|
/*
|
|
* Verify that a directory entry's inode number doesn't point at an internal
|
|
* inode, empty space, or static AG metadata.
|
|
*/
|
|
bool
|
|
xfs_verify_dir_ino(
|
|
struct xfs_mount *mp,
|
|
xfs_ino_t ino)
|
|
{
|
|
if (xfs_internal_inum(mp, ino))
|
|
return false;
|
|
return xfs_verify_ino(mp, ino);
|
|
}
|
|
|
|
/*
|
|
* Verify that an realtime block number pointer doesn't point off the
|
|
* end of the realtime device.
|
|
*/
|
|
inline bool
|
|
xfs_verify_rtbno(
|
|
struct xfs_mount *mp,
|
|
xfs_rtblock_t rtbno)
|
|
{
|
|
return rtbno < mp->m_sb.sb_rblocks;
|
|
}
|
|
|
|
/* Verify that a realtime device extent is fully contained inside the volume. */
|
|
bool
|
|
xfs_verify_rtext(
|
|
struct xfs_mount *mp,
|
|
xfs_rtblock_t rtbno,
|
|
xfs_rtblock_t len)
|
|
{
|
|
if (rtbno + len <= rtbno)
|
|
return false;
|
|
|
|
if (!xfs_verify_rtbno(mp, rtbno))
|
|
return false;
|
|
|
|
return xfs_verify_rtbno(mp, rtbno + len - 1);
|
|
}
|
|
|
|
/* Calculate the range of valid icount values. */
|
|
inline void
|
|
xfs_icount_range(
|
|
struct xfs_mount *mp,
|
|
unsigned long long *min,
|
|
unsigned long long *max)
|
|
{
|
|
unsigned long long nr_inos = 0;
|
|
xfs_agnumber_t agno;
|
|
|
|
/* root, rtbitmap, rtsum all live in the first chunk */
|
|
*min = XFS_INODES_PER_CHUNK;
|
|
|
|
for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
|
|
xfs_agino_t first, last;
|
|
|
|
xfs_agino_range(mp, agno, &first, &last);
|
|
nr_inos += last - first + 1;
|
|
}
|
|
*max = nr_inos;
|
|
}
|
|
|
|
/* Sanity-checking of inode counts. */
|
|
bool
|
|
xfs_verify_icount(
|
|
struct xfs_mount *mp,
|
|
unsigned long long icount)
|
|
{
|
|
unsigned long long min, max;
|
|
|
|
xfs_icount_range(mp, &min, &max);
|
|
return icount >= min && icount <= max;
|
|
}
|
|
|
|
/* Sanity-checking of dir/attr block offsets. */
|
|
bool
|
|
xfs_verify_dablk(
|
|
struct xfs_mount *mp,
|
|
xfs_fileoff_t dabno)
|
|
{
|
|
xfs_dablk_t max_dablk = -1U;
|
|
|
|
return dabno <= max_dablk;
|
|
}
|
|
|
|
/* Check that a file block offset does not exceed the maximum. */
|
|
bool
|
|
xfs_verify_fileoff(
|
|
struct xfs_mount *mp,
|
|
xfs_fileoff_t off)
|
|
{
|
|
return off <= XFS_MAX_FILEOFF;
|
|
}
|
|
|
|
/* Check that a range of file block offsets do not exceed the maximum. */
|
|
bool
|
|
xfs_verify_fileext(
|
|
struct xfs_mount *mp,
|
|
xfs_fileoff_t off,
|
|
xfs_fileoff_t len)
|
|
{
|
|
if (off + len <= off)
|
|
return false;
|
|
|
|
if (!xfs_verify_fileoff(mp, off))
|
|
return false;
|
|
|
|
return xfs_verify_fileoff(mp, off + len - 1);
|
|
}
|