* fix inode stats with lots of metafiles
@ 2026-02-02 14:14 Christoph Hellwig
2026-02-02 14:14 ` [PATCH 1/2] xfs: cleanup inode counter stats Christoph Hellwig
` (2 more replies)
0 siblings, 3 replies; 14+ messages in thread
From: Christoph Hellwig @ 2026-02-02 14:14 UTC (permalink / raw)
To: Carlos Maiolino; +Cc: Darrick J. Wong, linux-xfs
Hi all,
the second patch fixes the active inode/vnode count on file systems with
lots of metafiles, i.e. on SMR hard disks.
The first patch cleans up the counters a bit to prepare for that.
Subject:
libxfs/xfs_inode_buf.c | 4 ++++
libxfs/xfs_metafile.c | 5 +++++
xfs_icache.c | 9 ++++++---
xfs_stats.c | 17 +++++++++++------
xfs_stats.h | 19 ++++++++++---------
xfs_super.c | 4 ++--
6 files changed, 38 insertions(+), 20 deletions(-)
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 1/2] xfs: cleanup inode counter stats
2026-02-02 14:14 fix inode stats with lots of metafiles Christoph Hellwig
@ 2026-02-02 14:14 ` Christoph Hellwig
2026-02-03 6:16 ` Nirjhar Roy (IBM)
2026-02-06 6:38 ` Darrick J. Wong
2026-02-02 14:14 ` [PATCH 2/2] xfs: remove metafile inodes from the active inode stat Christoph Hellwig
2026-02-25 9:38 ` fix inode stats with lots of metafiles Carlos Maiolino
2 siblings, 2 replies; 14+ messages in thread
From: Christoph Hellwig @ 2026-02-02 14:14 UTC (permalink / raw)
To: Carlos Maiolino; +Cc: Darrick J. Wong, linux-xfs
Most of the are unused, so mark them as such. Give the remaining ones
names that match their use instead of the historic IRIX ones based on
vnodes. Note that the names are purely internal to the XFS code, the
user interface is based on section names and arrays of counters.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/xfs_icache.c | 6 +++---
fs/xfs/xfs_stats.c | 10 +++++-----
fs/xfs/xfs_stats.h | 16 ++++++++--------
fs/xfs/xfs_super.c | 4 ++--
4 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index dbaab4ae709f..f76c6decdaa3 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -106,7 +106,7 @@ xfs_inode_alloc(
mapping_set_folio_min_order(VFS_I(ip)->i_mapping,
M_IGEO(mp)->min_folio_order);
- XFS_STATS_INC(mp, vn_active);
+ XFS_STATS_INC(mp, xs_inodes_active);
ASSERT(atomic_read(&ip->i_pincount) == 0);
ASSERT(ip->i_ino == 0);
@@ -172,7 +172,7 @@ __xfs_inode_free(
/* asserts to verify all state is correct here */
ASSERT(atomic_read(&ip->i_pincount) == 0);
ASSERT(!ip->i_itemp || list_empty(&ip->i_itemp->ili_item.li_bio_list));
- XFS_STATS_DEC(ip->i_mount, vn_active);
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback);
}
@@ -2234,7 +2234,7 @@ xfs_inode_mark_reclaimable(
struct xfs_mount *mp = ip->i_mount;
bool need_inactive;
- XFS_STATS_INC(mp, vn_reclaim);
+ XFS_STATS_INC(mp, xs_inode_mark_reclaimable);
/*
* We should never get here with any of the reclaim flags already set.
diff --git a/fs/xfs/xfs_stats.c b/fs/xfs/xfs_stats.c
index 017db0361cd8..bc4a5d6dc795 100644
--- a/fs/xfs/xfs_stats.c
+++ b/fs/xfs/xfs_stats.c
@@ -42,7 +42,7 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
{ "xstrat", xfsstats_offset(xs_write_calls) },
{ "rw", xfsstats_offset(xs_attr_get) },
{ "attr", xfsstats_offset(xs_iflush_count)},
- { "icluster", xfsstats_offset(vn_active) },
+ { "icluster", xfsstats_offset(xs_inodes_active) },
{ "vnodes", xfsstats_offset(xb_get) },
{ "buf", xfsstats_offset(xs_abtb_2) },
{ "abtb2", xfsstats_offset(xs_abtc_2) },
@@ -100,15 +100,15 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
void xfs_stats_clearall(struct xfsstats __percpu *stats)
{
int c;
- uint32_t vn_active;
+ uint32_t xs_inodes_active;
xfs_notice(NULL, "Clearing xfsstats");
for_each_possible_cpu(c) {
preempt_disable();
- /* save vn_active, it's a universal truth! */
- vn_active = per_cpu_ptr(stats, c)->s.vn_active;
+ /* save xs_inodes_active, it's a universal truth! */
+ xs_inodes_active = per_cpu_ptr(stats, c)->s.xs_inodes_active;
memset(per_cpu_ptr(stats, c), 0, sizeof(*stats));
- per_cpu_ptr(stats, c)->s.vn_active = vn_active;
+ per_cpu_ptr(stats, c)->s.xs_inodes_active = xs_inodes_active;
preempt_enable();
}
}
diff --git a/fs/xfs/xfs_stats.h b/fs/xfs/xfs_stats.h
index 153d2381d0a8..64bc0cc18126 100644
--- a/fs/xfs/xfs_stats.h
+++ b/fs/xfs/xfs_stats.h
@@ -100,14 +100,14 @@ struct __xfsstats {
uint32_t xs_iflush_count;
uint32_t xs_icluster_flushcnt;
uint32_t xs_icluster_flushinode;
- uint32_t vn_active; /* # vnodes not on free lists */
- uint32_t vn_alloc; /* # times vn_alloc called */
- uint32_t vn_get; /* # times vn_get called */
- uint32_t vn_hold; /* # times vn_hold called */
- uint32_t vn_rele; /* # times vn_rele called */
- uint32_t vn_reclaim; /* # times vn_reclaim called */
- uint32_t vn_remove; /* # times vn_remove called */
- uint32_t vn_free; /* # times vn_free called */
+ uint32_t xs_inodes_active;
+ uint32_t __unused_vn_alloc;
+ uint32_t __unused_vn_get;
+ uint32_t __unused_vn_hold;
+ uint32_t xs_inode_destroy;
+ uint32_t xs_inode_destroy2; /* same as xs_inode_destroy */
+ uint32_t xs_inode_mark_reclaimable;
+ uint32_t __unused_vn_free;
uint32_t xb_get;
uint32_t xb_create;
uint32_t xb_get_locked;
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index 149b659e1692..3f25a0001a61 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -725,8 +725,8 @@ xfs_fs_destroy_inode(
trace_xfs_destroy_inode(ip);
ASSERT(!rwsem_is_locked(&inode->i_rwsem));
- XFS_STATS_INC(ip->i_mount, vn_rele);
- XFS_STATS_INC(ip->i_mount, vn_remove);
+ XFS_STATS_INC(ip->i_mount, xs_inode_destroy);
+ XFS_STATS_INC(ip->i_mount, xs_inode_destroy2);
xfs_inode_mark_reclaimable(ip);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH 2/2] xfs: remove metafile inodes from the active inode stat
2026-02-02 14:14 fix inode stats with lots of metafiles Christoph Hellwig
2026-02-02 14:14 ` [PATCH 1/2] xfs: cleanup inode counter stats Christoph Hellwig
@ 2026-02-02 14:14 ` Christoph Hellwig
2026-02-03 7:11 ` Nirjhar Roy (IBM)
2026-02-06 6:43 ` Darrick J. Wong
2026-02-25 9:38 ` fix inode stats with lots of metafiles Carlos Maiolino
2 siblings, 2 replies; 14+ messages in thread
From: Christoph Hellwig @ 2026-02-02 14:14 UTC (permalink / raw)
To: Carlos Maiolino; +Cc: Darrick J. Wong, linux-xfs
The active inode (or active vnode until recently) stat can get much larger
than expected on file systems with a lot of metafile inodes like zoned
file systems on SMR hard disks with 10.000s of rtg rmap inodes.
Remove all metafile inodes from the active counter to make it more useful
to track actual workloads and add a separate counter for active metafile
inodes.
This fixes xfs/177 on SMR hard drives.
Signed-off-by: Christoph Hellwig <hch@lst.de>
---
fs/xfs/libxfs/xfs_inode_buf.c | 4 ++++
fs/xfs/libxfs/xfs_metafile.c | 5 +++++
fs/xfs/xfs_icache.c | 5 ++++-
fs/xfs/xfs_stats.c | 11 ++++++++---
fs/xfs/xfs_stats.h | 3 ++-
5 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index dcc9566ef5fe..91a499ced4e8 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -268,6 +268,10 @@ xfs_inode_from_disk(
}
if (xfs_is_reflink_inode(ip))
xfs_ifork_init_cow(ip);
+ if (xfs_is_metadir_inode(ip)) {
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
+ XFS_STATS_INC(ip->i_mount, xs_inodes_meta);
+ }
return 0;
out_destroy_data_fork:
diff --git a/fs/xfs/libxfs/xfs_metafile.c b/fs/xfs/libxfs/xfs_metafile.c
index cf239f862212..71f004e9dc64 100644
--- a/fs/xfs/libxfs/xfs_metafile.c
+++ b/fs/xfs/libxfs/xfs_metafile.c
@@ -61,6 +61,9 @@ xfs_metafile_set_iflag(
ip->i_diflags2 |= XFS_DIFLAG2_METADATA;
ip->i_metatype = metafile_type;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
+ XFS_STATS_INC(ip->i_mount, xs_inodes_meta);
}
/* Clear the metadata directory inode flag. */
@@ -74,6 +77,8 @@ xfs_metafile_clear_iflag(
ip->i_diflags2 &= ~XFS_DIFLAG2_METADATA;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ XFS_STATS_INC(ip->i_mount, xs_inodes_active);
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_meta);
}
/*
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index f76c6decdaa3..f2d4294efd37 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -172,7 +172,10 @@ __xfs_inode_free(
/* asserts to verify all state is correct here */
ASSERT(atomic_read(&ip->i_pincount) == 0);
ASSERT(!ip->i_itemp || list_empty(&ip->i_itemp->ili_item.li_bio_list));
- XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
+ if (xfs_is_metadir_inode(ip))
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_meta);
+ else
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback);
}
diff --git a/fs/xfs/xfs_stats.c b/fs/xfs/xfs_stats.c
index bc4a5d6dc795..c13d600732c9 100644
--- a/fs/xfs/xfs_stats.c
+++ b/fs/xfs/xfs_stats.c
@@ -59,7 +59,8 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
{ "rtrefcntbt", xfsstats_offset(xs_qm_dqreclaims)},
/* we print both series of quota information together */
{ "qm", xfsstats_offset(xs_gc_read_calls)},
- { "zoned", xfsstats_offset(__pad1)},
+ { "zoned", xfsstats_offset(xs_inodes_meta)},
+ { "metafile", xfsstats_offset(xs_xstrat_bytes)},
};
/* Loop over all stats groups */
@@ -99,16 +100,20 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
void xfs_stats_clearall(struct xfsstats __percpu *stats)
{
+ uint32_t xs_inodes_active, xs_inodes_meta;
int c;
- uint32_t xs_inodes_active;
xfs_notice(NULL, "Clearing xfsstats");
for_each_possible_cpu(c) {
preempt_disable();
- /* save xs_inodes_active, it's a universal truth! */
+ /*
+ * Save the active / meta inode counters, as they are stateful.
+ */
xs_inodes_active = per_cpu_ptr(stats, c)->s.xs_inodes_active;
+ xs_inodes_meta = per_cpu_ptr(stats, c)->s.xs_inodes_meta;
memset(per_cpu_ptr(stats, c), 0, sizeof(*stats));
per_cpu_ptr(stats, c)->s.xs_inodes_active = xs_inodes_active;
+ per_cpu_ptr(stats, c)->s.xs_inodes_meta = xs_inodes_meta;
preempt_enable();
}
}
diff --git a/fs/xfs/xfs_stats.h b/fs/xfs/xfs_stats.h
index 64bc0cc18126..57c32b86c358 100644
--- a/fs/xfs/xfs_stats.h
+++ b/fs/xfs/xfs_stats.h
@@ -142,7 +142,8 @@ struct __xfsstats {
uint32_t xs_gc_read_calls;
uint32_t xs_gc_write_calls;
uint32_t xs_gc_zone_reset_calls;
- uint32_t __pad1;
+/* Metafile counters */
+ uint32_t xs_inodes_meta;
/* Extra precision counters */
uint64_t xs_xstrat_bytes;
uint64_t xs_write_bytes;
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xfs: cleanup inode counter stats
2026-02-02 14:14 ` [PATCH 1/2] xfs: cleanup inode counter stats Christoph Hellwig
@ 2026-02-03 6:16 ` Nirjhar Roy (IBM)
2026-02-03 6:47 ` Christoph Hellwig
2026-02-06 6:38 ` Darrick J. Wong
1 sibling, 1 reply; 14+ messages in thread
From: Nirjhar Roy (IBM) @ 2026-02-03 6:16 UTC (permalink / raw)
To: Christoph Hellwig, Carlos Maiolino; +Cc: Darrick J. Wong, linux-xfs
On Mon, 2026-02-02 at 15:14 +0100, Christoph Hellwig wrote:
> Most of the are unused, so mark them as such. Give the remaining ones
Nit - Typo - "Most of the are <...>" -> "Most of them are <...>"
> names that match their use instead of the historic IRIX ones based on
> vnodes. Note that the names are purely internal to the XFS code, the
> user interface is based on section names and arrays of counters.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> fs/xfs/xfs_icache.c | 6 +++---
> fs/xfs/xfs_stats.c | 10 +++++-----
> fs/xfs/xfs_stats.h | 16 ++++++++--------
> fs/xfs/xfs_super.c | 4 ++--
> 4 files changed, 18 insertions(+), 18 deletions(-)
>
> diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
> index dbaab4ae709f..f76c6decdaa3 100644
> --- a/fs/xfs/xfs_icache.c
> +++ b/fs/xfs/xfs_icache.c
> @@ -106,7 +106,7 @@ xfs_inode_alloc(
> mapping_set_folio_min_order(VFS_I(ip)->i_mapping,
> M_IGEO(mp)->min_folio_order);
>
> - XFS_STATS_INC(mp, vn_active);
> + XFS_STATS_INC(mp, xs_inodes_active);
> ASSERT(atomic_read(&ip->i_pincount) == 0);
> ASSERT(ip->i_ino == 0);
>
> @@ -172,7 +172,7 @@ __xfs_inode_free(
> /* asserts to verify all state is correct here */
> ASSERT(atomic_read(&ip->i_pincount) == 0);
> ASSERT(!ip->i_itemp || list_empty(&ip->i_itemp->ili_item.li_bio_list));
> - XFS_STATS_DEC(ip->i_mount, vn_active);
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
>
> call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback);
> }
> @@ -2234,7 +2234,7 @@ xfs_inode_mark_reclaimable(
> struct xfs_mount *mp = ip->i_mount;
> bool need_inactive;
>
> - XFS_STATS_INC(mp, vn_reclaim);
> + XFS_STATS_INC(mp, xs_inode_mark_reclaimable);
>
> /*
> * We should never get here with any of the reclaim flags already set.
> diff --git a/fs/xfs/xfs_stats.c b/fs/xfs/xfs_stats.c
> index 017db0361cd8..bc4a5d6dc795 100644
> --- a/fs/xfs/xfs_stats.c
> +++ b/fs/xfs/xfs_stats.c
> @@ -42,7 +42,7 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
> { "xstrat", xfsstats_offset(xs_write_calls) },
> { "rw", xfsstats_offset(xs_attr_get) },
> { "attr", xfsstats_offset(xs_iflush_count)},
> - { "icluster", xfsstats_offset(vn_active) },
> + { "icluster", xfsstats_offset(xs_inodes_active) },
> { "vnodes", xfsstats_offset(xb_get) },
> { "buf", xfsstats_offset(xs_abtb_2) },
> { "abtb2", xfsstats_offset(xs_abtc_2) },
> @@ -100,15 +100,15 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
> void xfs_stats_clearall(struct xfsstats __percpu *stats)
> {
> int c;
> - uint32_t vn_active;
> + uint32_t xs_inodes_active;
>
> xfs_notice(NULL, "Clearing xfsstats");
> for_each_possible_cpu(c) {
> preempt_disable();
> - /* save vn_active, it's a universal truth! */
> - vn_active = per_cpu_ptr(stats, c)->s.vn_active;
> + /* save xs_inodes_active, it's a universal truth! */
> + xs_inodes_active = per_cpu_ptr(stats, c)->s.xs_inodes_active;
> memset(per_cpu_ptr(stats, c), 0, sizeof(*stats));
> - per_cpu_ptr(stats, c)->s.vn_active = vn_active;
> + per_cpu_ptr(stats, c)->s.xs_inodes_active = xs_inodes_active;
> preempt_enable();
> }
> }
> diff --git a/fs/xfs/xfs_stats.h b/fs/xfs/xfs_stats.h
> index 153d2381d0a8..64bc0cc18126 100644
> --- a/fs/xfs/xfs_stats.h
> +++ b/fs/xfs/xfs_stats.h
> @@ -100,14 +100,14 @@ struct __xfsstats {
> uint32_t xs_iflush_count;
> uint32_t xs_icluster_flushcnt;
> uint32_t xs_icluster_flushinode;
> - uint32_t vn_active; /* # vnodes not on free lists */
> - uint32_t vn_alloc; /* # times vn_alloc called */
> - uint32_t vn_get; /* # times vn_get called */
> - uint32_t vn_hold; /* # times vn_hold called */
> - uint32_t vn_rele; /* # times vn_rele called */
> - uint32_t vn_reclaim; /* # times vn_reclaim called */
> - uint32_t vn_remove; /* # times vn_remove called */
> - uint32_t vn_free; /* # times vn_free called */
> + uint32_t xs_inodes_active;
> + uint32_t __unused_vn_alloc;
> + uint32_t __unused_vn_get;
> + uint32_t __unused_vn_hold;
Any reason to keep the unused field?
> + uint32_t xs_inode_destroy;
> + uint32_t xs_inode_destroy2; /* same as xs_inode_destroy */
> + uint32_t xs_inode_mark_reclaimable;
> + uint32_t __unused_vn_free;
> uint32_t xb_get;
> uint32_t xb_create;
> uint32_t xb_get_locked;
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index 149b659e1692..3f25a0001a61 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -725,8 +725,8 @@ xfs_fs_destroy_inode(
> trace_xfs_destroy_inode(ip);
>
> ASSERT(!rwsem_is_locked(&inode->i_rwsem));
> - XFS_STATS_INC(ip->i_mount, vn_rele);
> - XFS_STATS_INC(ip->i_mount, vn_remove);
> + XFS_STATS_INC(ip->i_mount, xs_inode_destroy);
> + XFS_STATS_INC(ip->i_mount, xs_inode_destroy2);
So this patch basically renames some of the fields from the older legacy conventions (concept of
vn/virtual-inodes) to new linux-XFS conventions. Based on this understanding, this change looks okay
to me.
> xfs_inode_mark_reclaimable(ip);
> }
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xfs: cleanup inode counter stats
2026-02-03 6:16 ` Nirjhar Roy (IBM)
@ 2026-02-03 6:47 ` Christoph Hellwig
0 siblings, 0 replies; 14+ messages in thread
From: Christoph Hellwig @ 2026-02-03 6:47 UTC (permalink / raw)
To: Nirjhar Roy (IBM)
Cc: Christoph Hellwig, Carlos Maiolino, Darrick J. Wong, linux-xfs
On Tue, Feb 03, 2026 at 11:46:41AM +0530, Nirjhar Roy (IBM) wrote:
> On Mon, 2026-02-02 at 15:14 +0100, Christoph Hellwig wrote:
> > Most of the are unused, so mark them as such. Give the remaining ones
>
> Nit - Typo - "Most of the are <...>" -> "Most of them are <...>"
Yes, thanks.
> > + uint32_t xs_inodes_active;
> > + uint32_t __unused_vn_alloc;
> > + uint32_t __unused_vn_get;
> > + uint32_t __unused_vn_hold;
> Any reason to keep the unused field?
If we drop the unused fields the sysfs output would change.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] xfs: remove metafile inodes from the active inode stat
2026-02-02 14:14 ` [PATCH 2/2] xfs: remove metafile inodes from the active inode stat Christoph Hellwig
@ 2026-02-03 7:11 ` Nirjhar Roy (IBM)
2026-02-03 7:14 ` Christoph Hellwig
2026-02-06 6:43 ` Darrick J. Wong
1 sibling, 1 reply; 14+ messages in thread
From: Nirjhar Roy (IBM) @ 2026-02-03 7:11 UTC (permalink / raw)
To: Christoph Hellwig, Carlos Maiolino; +Cc: Darrick J. Wong, linux-xfs
On Mon, 2026-02-02 at 15:14 +0100, Christoph Hellwig wrote:
> The active inode (or active vnode until recently) stat can get much larger
> than expected on file systems with a lot of metafile inodes like zoned
> file systems on SMR hard disks with 10.000s of rtg rmap inodes.
And this was causing (or could have caused) some sort of counter overflows or something?
>
> Remove all metafile inodes from the active counter to make it more useful
> to track actual workloads and add a separate counter for active metafile
> inodes.
>
> This fixes xfs/177 on SMR hard drives.
I can see that xfs/177 has a couple of sub-test cases (like Round 1,2, ...) - do you remember if 1
particular round was causing problems or were there issues with all/most of them?
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
> ---
> fs/xfs/libxfs/xfs_inode_buf.c | 4 ++++
> fs/xfs/libxfs/xfs_metafile.c | 5 +++++
> fs/xfs/xfs_icache.c | 5 ++++-
> fs/xfs/xfs_stats.c | 11 ++++++++---
> fs/xfs/xfs_stats.h | 3 ++-
> 5 files changed, 23 insertions(+), 5 deletions(-)
>
> diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
> index dcc9566ef5fe..91a499ced4e8 100644
> --- a/fs/xfs/libxfs/xfs_inode_buf.c
> +++ b/fs/xfs/libxfs/xfs_inode_buf.c
> @@ -268,6 +268,10 @@ xfs_inode_from_disk(
> }
> if (xfs_is_reflink_inode(ip))
> xfs_ifork_init_cow(ip);
> + if (xfs_is_metadir_inode(ip)) {
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
> + XFS_STATS_INC(ip->i_mount, xs_inodes_meta);
So is it like then there is a state(or at some function) where xs_inodes_active counter was bumped
up even though "ip" was a metadir inode and here in the above line it is corrected(i.e, decremented
by 1) and xs_inodes_meta is incremented - shouldn't the appropriate counter have been directly
bumped up whenever it was created? Something similar to what is being done in __xfs_inode_free()?
For example, in xfs_inode_alloc() I can see a XFS_STATS_INC(mp, vn_active) - shouldn't we bump the
appropriate counter(xs_inode_{active or meta} there directly? Can you please let me know if my
understanding is correct or am I missing something?
> + }
> return 0;
>
> out_destroy_data_fork:
> diff --git a/fs/xfs/libxfs/xfs_metafile.c b/fs/xfs/libxfs/xfs_metafile.c
> index cf239f862212..71f004e9dc64 100644
> --- a/fs/xfs/libxfs/xfs_metafile.c
> +++ b/fs/xfs/libxfs/xfs_metafile.c
> @@ -61,6 +61,9 @@ xfs_metafile_set_iflag(
> ip->i_diflags2 |= XFS_DIFLAG2_METADATA;
> ip->i_metatype = metafile_type;
> xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
> +
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
> + XFS_STATS_INC(ip->i_mount, xs_inodes_meta);
> }
>
> /* Clear the metadata directory inode flag. */
> @@ -74,6 +77,8 @@ xfs_metafile_clear_iflag(
>
> ip->i_diflags2 &= ~XFS_DIFLAG2_METADATA;
> xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
> + XFS_STATS_INC(ip->i_mount, xs_inodes_active);
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_meta);
> }
>
> /*
> diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
> index f76c6decdaa3..f2d4294efd37 100644
> --- a/fs/xfs/xfs_icache.c
> +++ b/fs/xfs/xfs_icache.c
> @@ -172,7 +172,10 @@ __xfs_inode_free(
> /* asserts to verify all state is correct here */
> ASSERT(atomic_read(&ip->i_pincount) == 0);
> ASSERT(!ip->i_itemp || list_empty(&ip->i_itemp->ili_item.li_bio_list));
> - XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
> + if (xfs_is_metadir_inode(ip))
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_meta);
> + else
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
>
> call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback);
> }
> diff --git a/fs/xfs/xfs_stats.c b/fs/xfs/xfs_stats.c
> index bc4a5d6dc795..c13d600732c9 100644
> --- a/fs/xfs/xfs_stats.c
> +++ b/fs/xfs/xfs_stats.c
> @@ -59,7 +59,8 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
> { "rtrefcntbt", xfsstats_offset(xs_qm_dqreclaims)},
> /* we print both series of quota information together */
> { "qm", xfsstats_offset(xs_gc_read_calls)},
> - { "zoned", xfsstats_offset(__pad1)},
> + { "zoned", xfsstats_offset(xs_inodes_meta)},
> + { "metafile", xfsstats_offset(xs_xstrat_bytes)},
> };
>
> /* Loop over all stats groups */
> @@ -99,16 +100,20 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
>
> void xfs_stats_clearall(struct xfsstats __percpu *stats)
> {
> + uint32_t xs_inodes_active, xs_inodes_meta;
> int c;
> - uint32_t xs_inodes_active;
>
> xfs_notice(NULL, "Clearing xfsstats");
> for_each_possible_cpu(c) {
> preempt_disable();
> - /* save xs_inodes_active, it's a universal truth! */
> + /*
> + * Save the active / meta inode counters, as they are stateful.
> + */
> xs_inodes_active = per_cpu_ptr(stats, c)->s.xs_inodes_active;
> + xs_inodes_meta = per_cpu_ptr(stats, c)->s.xs_inodes_meta;
> memset(per_cpu_ptr(stats, c), 0, sizeof(*stats));
> per_cpu_ptr(stats, c)->s.xs_inodes_active = xs_inodes_active;
> + per_cpu_ptr(stats, c)->s.xs_inodes_meta = xs_inodes_meta;
> preempt_enable();
> }
> }
> diff --git a/fs/xfs/xfs_stats.h b/fs/xfs/xfs_stats.h
> index 64bc0cc18126..57c32b86c358 100644
> --- a/fs/xfs/xfs_stats.h
> +++ b/fs/xfs/xfs_stats.h
> @@ -142,7 +142,8 @@ struct __xfsstats {
> uint32_t xs_gc_read_calls;
> uint32_t xs_gc_write_calls;
> uint32_t xs_gc_zone_reset_calls;
> - uint32_t __pad1;
> +/* Metafile counters */
> + uint32_t xs_inodes_meta;
uint64_t would be an overkill, isn't it?
This patch looks okay to me - I have some questions posted above. I have mostly looked into whether
the appropriate checks for meta/normal inodes have been made, whether the counter updations are done
correctly etc and from those perspectives - this intent of this patch looks okay to me.
--NR
> /* Extra precision counters */
> uint64_t xs_xstrat_bytes;
> uint64_t xs_write_bytes;
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] xfs: remove metafile inodes from the active inode stat
2026-02-03 7:11 ` Nirjhar Roy (IBM)
@ 2026-02-03 7:14 ` Christoph Hellwig
2026-02-03 7:24 ` Nirjhar Roy (IBM)
0 siblings, 1 reply; 14+ messages in thread
From: Christoph Hellwig @ 2026-02-03 7:14 UTC (permalink / raw)
To: Nirjhar Roy (IBM)
Cc: Christoph Hellwig, Carlos Maiolino, Darrick J. Wong, linux-xfs
On Tue, Feb 03, 2026 at 12:41:23PM +0530, Nirjhar Roy (IBM) wrote:
> On Mon, 2026-02-02 at 15:14 +0100, Christoph Hellwig wrote:
> > The active inode (or active vnode until recently) stat can get much larger
> > than expected on file systems with a lot of metafile inodes like zoned
> > file systems on SMR hard disks with 10.000s of rtg rmap inodes.
> And this was causing (or could have caused) some sort of counter overflows or something?
Not really an overflow. But if you have a lot of metadir inodes it
messes up the stats with extra counts that are not user visible.
> > This fixes xfs/177 on SMR hard drives.
> I can see that xfs/177 has a couple of sub-test cases (like Round 1,2, ...) - do you remember if 1
> particular round was causing problems or were there issues with all/most of them?
Comparing the cached values to the expected ones.
> So is it like then there is a state(or at some function) where
> xs_inodes_active counter was bumped up even though "ip" was a metadir
> inode and here in the above line it is corrected (i.e, decremented
> by 1) and xs_inodes_meta is incremented - shouldn't the appropriate
> counter have been directly bumped up whenever it was created?
xfs_inode_alloc doesn't know if the inode is going to be a meta inode,
as it hasn't been read from disk yet for the common case. For the
less common inode allocation case we'd know it, but passing it down
would be a bit annoying.
> > +/* Metafile counters */
> > + uint32_t xs_inodes_meta;
> uint64_t would be an overkill, isn't it?
Yes. Sticking to the same type as the active inodes here.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] xfs: remove metafile inodes from the active inode stat
2026-02-03 7:14 ` Christoph Hellwig
@ 2026-02-03 7:24 ` Nirjhar Roy (IBM)
2026-02-03 7:29 ` Christoph Hellwig
0 siblings, 1 reply; 14+ messages in thread
From: Nirjhar Roy (IBM) @ 2026-02-03 7:24 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Carlos Maiolino, Darrick J. Wong, linux-xfs
On 2/3/26 12:44, Christoph Hellwig wrote:
> On Tue, Feb 03, 2026 at 12:41:23PM +0530, Nirjhar Roy (IBM) wrote:
>> On Mon, 2026-02-02 at 15:14 +0100, Christoph Hellwig wrote:
>>> The active inode (or active vnode until recently) stat can get much larger
>>> than expected on file systems with a lot of metafile inodes like zoned
>>> file systems on SMR hard disks with 10.000s of rtg rmap inodes.
>> And this was causing (or could have caused) some sort of counter overflows or something?
> Not really an overflow. But if you have a lot of metadir inodes it
> messes up the stats with extra counts that are not user visible.
Okay.
>
>>> This fixes xfs/177 on SMR hard drives.
>> I can see that xfs/177 has a couple of sub-test cases (like Round 1,2, ...) - do you remember if 1
>> particular round was causing problems or were there issues with all/most of them?
> Comparing the cached values to the expected ones.
Noted.
>
>> So is it like then there is a state(or at some function) where
>> xs_inodes_active counter was bumped up even though "ip" was a metadir
>> inode and here in the above line it is corrected (i.e, decremented
>> by 1) and xs_inodes_meta is incremented - shouldn't the appropriate
>> counter have been directly bumped up whenever it was created?
> xfs_inode_alloc doesn't know if the inode is going to be a meta inode,
> as it hasn't been read from disk yet for the common case. For the
> less common inode allocation case we'd know it, but passing it down
> would be a bit annoying.
Okay, now I get it. Thank you. So we increment the stats for regular
inode (since this is more common) and later adjust if our assumption is
wrong, i.e, the inode turns out to be a metadir inode. Right?
>
>>> +/* Metafile counters */
>>> + uint32_t xs_inodes_meta;
>> uint64_t would be an overkill, isn't it?
> Yes. Sticking to the same type as the active inodes here.
Okay, makes sense.
--NR
>
--
Nirjhar Roy
Linux Kernel Developer
IBM, Bangalore
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] xfs: remove metafile inodes from the active inode stat
2026-02-03 7:24 ` Nirjhar Roy (IBM)
@ 2026-02-03 7:29 ` Christoph Hellwig
0 siblings, 0 replies; 14+ messages in thread
From: Christoph Hellwig @ 2026-02-03 7:29 UTC (permalink / raw)
To: Nirjhar Roy (IBM)
Cc: Christoph Hellwig, Carlos Maiolino, Darrick J. Wong, linux-xfs
On Tue, Feb 03, 2026 at 12:54:55PM +0530, Nirjhar Roy (IBM) wrote:
> Okay, now I get it. Thank you. So we increment the stats for regular inode
> (since this is more common) and later adjust if our assumption is wrong,
> i.e, the inode turns out to be a metadir inode. Right?
Yes, although it would not really call it an assumption, but just the
fast path.
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xfs: cleanup inode counter stats
2026-02-02 14:14 ` [PATCH 1/2] xfs: cleanup inode counter stats Christoph Hellwig
2026-02-03 6:16 ` Nirjhar Roy (IBM)
@ 2026-02-06 6:38 ` Darrick J. Wong
1 sibling, 0 replies; 14+ messages in thread
From: Darrick J. Wong @ 2026-02-06 6:38 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Carlos Maiolino, linux-xfs
On Mon, Feb 02, 2026 at 03:14:31PM +0100, Christoph Hellwig wrote:
> Most of the are unused, so mark them as such. Give the remaining ones
> names that match their use instead of the historic IRIX ones based on
> vnodes. Note that the names are purely internal to the XFS code, the
> user interface is based on section names and arrays of counters.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
With the s/of the/of them/ typo that Nirjhar mentioned fixed, this looks
fine to me. I've long thought those fields were unused, so I'm glad
they're finally getting cleaned out.
(and yes I agree that the struct layout stuff means we should just leave
the fields and maybe reuse the space some day; and not otherwise change
the struct layout)
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
--D
> ---
> fs/xfs/xfs_icache.c | 6 +++---
> fs/xfs/xfs_stats.c | 10 +++++-----
> fs/xfs/xfs_stats.h | 16 ++++++++--------
> fs/xfs/xfs_super.c | 4 ++--
> 4 files changed, 18 insertions(+), 18 deletions(-)
>
> diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
> index dbaab4ae709f..f76c6decdaa3 100644
> --- a/fs/xfs/xfs_icache.c
> +++ b/fs/xfs/xfs_icache.c
> @@ -106,7 +106,7 @@ xfs_inode_alloc(
> mapping_set_folio_min_order(VFS_I(ip)->i_mapping,
> M_IGEO(mp)->min_folio_order);
>
> - XFS_STATS_INC(mp, vn_active);
> + XFS_STATS_INC(mp, xs_inodes_active);
> ASSERT(atomic_read(&ip->i_pincount) == 0);
> ASSERT(ip->i_ino == 0);
>
> @@ -172,7 +172,7 @@ __xfs_inode_free(
> /* asserts to verify all state is correct here */
> ASSERT(atomic_read(&ip->i_pincount) == 0);
> ASSERT(!ip->i_itemp || list_empty(&ip->i_itemp->ili_item.li_bio_list));
> - XFS_STATS_DEC(ip->i_mount, vn_active);
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
>
> call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback);
> }
> @@ -2234,7 +2234,7 @@ xfs_inode_mark_reclaimable(
> struct xfs_mount *mp = ip->i_mount;
> bool need_inactive;
>
> - XFS_STATS_INC(mp, vn_reclaim);
> + XFS_STATS_INC(mp, xs_inode_mark_reclaimable);
>
> /*
> * We should never get here with any of the reclaim flags already set.
> diff --git a/fs/xfs/xfs_stats.c b/fs/xfs/xfs_stats.c
> index 017db0361cd8..bc4a5d6dc795 100644
> --- a/fs/xfs/xfs_stats.c
> +++ b/fs/xfs/xfs_stats.c
> @@ -42,7 +42,7 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
> { "xstrat", xfsstats_offset(xs_write_calls) },
> { "rw", xfsstats_offset(xs_attr_get) },
> { "attr", xfsstats_offset(xs_iflush_count)},
> - { "icluster", xfsstats_offset(vn_active) },
> + { "icluster", xfsstats_offset(xs_inodes_active) },
> { "vnodes", xfsstats_offset(xb_get) },
> { "buf", xfsstats_offset(xs_abtb_2) },
> { "abtb2", xfsstats_offset(xs_abtc_2) },
> @@ -100,15 +100,15 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
> void xfs_stats_clearall(struct xfsstats __percpu *stats)
> {
> int c;
> - uint32_t vn_active;
> + uint32_t xs_inodes_active;
>
> xfs_notice(NULL, "Clearing xfsstats");
> for_each_possible_cpu(c) {
> preempt_disable();
> - /* save vn_active, it's a universal truth! */
> - vn_active = per_cpu_ptr(stats, c)->s.vn_active;
> + /* save xs_inodes_active, it's a universal truth! */
> + xs_inodes_active = per_cpu_ptr(stats, c)->s.xs_inodes_active;
> memset(per_cpu_ptr(stats, c), 0, sizeof(*stats));
> - per_cpu_ptr(stats, c)->s.vn_active = vn_active;
> + per_cpu_ptr(stats, c)->s.xs_inodes_active = xs_inodes_active;
> preempt_enable();
> }
> }
> diff --git a/fs/xfs/xfs_stats.h b/fs/xfs/xfs_stats.h
> index 153d2381d0a8..64bc0cc18126 100644
> --- a/fs/xfs/xfs_stats.h
> +++ b/fs/xfs/xfs_stats.h
> @@ -100,14 +100,14 @@ struct __xfsstats {
> uint32_t xs_iflush_count;
> uint32_t xs_icluster_flushcnt;
> uint32_t xs_icluster_flushinode;
> - uint32_t vn_active; /* # vnodes not on free lists */
> - uint32_t vn_alloc; /* # times vn_alloc called */
> - uint32_t vn_get; /* # times vn_get called */
> - uint32_t vn_hold; /* # times vn_hold called */
> - uint32_t vn_rele; /* # times vn_rele called */
> - uint32_t vn_reclaim; /* # times vn_reclaim called */
> - uint32_t vn_remove; /* # times vn_remove called */
> - uint32_t vn_free; /* # times vn_free called */
> + uint32_t xs_inodes_active;
> + uint32_t __unused_vn_alloc;
> + uint32_t __unused_vn_get;
> + uint32_t __unused_vn_hold;
> + uint32_t xs_inode_destroy;
> + uint32_t xs_inode_destroy2; /* same as xs_inode_destroy */
> + uint32_t xs_inode_mark_reclaimable;
> + uint32_t __unused_vn_free;
> uint32_t xb_get;
> uint32_t xb_create;
> uint32_t xb_get_locked;
> diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
> index 149b659e1692..3f25a0001a61 100644
> --- a/fs/xfs/xfs_super.c
> +++ b/fs/xfs/xfs_super.c
> @@ -725,8 +725,8 @@ xfs_fs_destroy_inode(
> trace_xfs_destroy_inode(ip);
>
> ASSERT(!rwsem_is_locked(&inode->i_rwsem));
> - XFS_STATS_INC(ip->i_mount, vn_rele);
> - XFS_STATS_INC(ip->i_mount, vn_remove);
> + XFS_STATS_INC(ip->i_mount, xs_inode_destroy);
> + XFS_STATS_INC(ip->i_mount, xs_inode_destroy2);
> xfs_inode_mark_reclaimable(ip);
> }
>
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] xfs: remove metafile inodes from the active inode stat
2026-02-02 14:14 ` [PATCH 2/2] xfs: remove metafile inodes from the active inode stat Christoph Hellwig
2026-02-03 7:11 ` Nirjhar Roy (IBM)
@ 2026-02-06 6:43 ` Darrick J. Wong
2026-02-06 6:52 ` Christoph Hellwig
1 sibling, 1 reply; 14+ messages in thread
From: Darrick J. Wong @ 2026-02-06 6:43 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Carlos Maiolino, linux-xfs
On Mon, Feb 02, 2026 at 03:14:32PM +0100, Christoph Hellwig wrote:
> The active inode (or active vnode until recently) stat can get much larger
> than expected on file systems with a lot of metafile inodes like zoned
> file systems on SMR hard disks with 10.000s of rtg rmap inodes.
>
> Remove all metafile inodes from the active counter to make it more useful
> to track actual workloads and add a separate counter for active metafile
> inodes.
>
> This fixes xfs/177 on SMR hard drives.
>
> Signed-off-by: Christoph Hellwig <hch@lst.de>
Looks ok to me, though I wonder slightly about the atomicity of the
percpu counter inc/decrements. But it's been that way for a long time
so
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
--D
> ---
> fs/xfs/libxfs/xfs_inode_buf.c | 4 ++++
> fs/xfs/libxfs/xfs_metafile.c | 5 +++++
> fs/xfs/xfs_icache.c | 5 ++++-
> fs/xfs/xfs_stats.c | 11 ++++++++---
> fs/xfs/xfs_stats.h | 3 ++-
> 5 files changed, 23 insertions(+), 5 deletions(-)
>
> diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
> index dcc9566ef5fe..91a499ced4e8 100644
> --- a/fs/xfs/libxfs/xfs_inode_buf.c
> +++ b/fs/xfs/libxfs/xfs_inode_buf.c
> @@ -268,6 +268,10 @@ xfs_inode_from_disk(
> }
> if (xfs_is_reflink_inode(ip))
> xfs_ifork_init_cow(ip);
> + if (xfs_is_metadir_inode(ip)) {
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
> + XFS_STATS_INC(ip->i_mount, xs_inodes_meta);
> + }
> return 0;
>
> out_destroy_data_fork:
> diff --git a/fs/xfs/libxfs/xfs_metafile.c b/fs/xfs/libxfs/xfs_metafile.c
> index cf239f862212..71f004e9dc64 100644
> --- a/fs/xfs/libxfs/xfs_metafile.c
> +++ b/fs/xfs/libxfs/xfs_metafile.c
> @@ -61,6 +61,9 @@ xfs_metafile_set_iflag(
> ip->i_diflags2 |= XFS_DIFLAG2_METADATA;
> ip->i_metatype = metafile_type;
> xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
> +
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
> + XFS_STATS_INC(ip->i_mount, xs_inodes_meta);
> }
>
> /* Clear the metadata directory inode flag. */
> @@ -74,6 +77,8 @@ xfs_metafile_clear_iflag(
>
> ip->i_diflags2 &= ~XFS_DIFLAG2_METADATA;
> xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
> + XFS_STATS_INC(ip->i_mount, xs_inodes_active);
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_meta);
> }
>
> /*
> diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
> index f76c6decdaa3..f2d4294efd37 100644
> --- a/fs/xfs/xfs_icache.c
> +++ b/fs/xfs/xfs_icache.c
> @@ -172,7 +172,10 @@ __xfs_inode_free(
> /* asserts to verify all state is correct here */
> ASSERT(atomic_read(&ip->i_pincount) == 0);
> ASSERT(!ip->i_itemp || list_empty(&ip->i_itemp->ili_item.li_bio_list));
> - XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
> + if (xfs_is_metadir_inode(ip))
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_meta);
> + else
> + XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
>
> call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback);
> }
> diff --git a/fs/xfs/xfs_stats.c b/fs/xfs/xfs_stats.c
> index bc4a5d6dc795..c13d600732c9 100644
> --- a/fs/xfs/xfs_stats.c
> +++ b/fs/xfs/xfs_stats.c
> @@ -59,7 +59,8 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
> { "rtrefcntbt", xfsstats_offset(xs_qm_dqreclaims)},
> /* we print both series of quota information together */
> { "qm", xfsstats_offset(xs_gc_read_calls)},
> - { "zoned", xfsstats_offset(__pad1)},
> + { "zoned", xfsstats_offset(xs_inodes_meta)},
> + { "metafile", xfsstats_offset(xs_xstrat_bytes)},
> };
>
> /* Loop over all stats groups */
> @@ -99,16 +100,20 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
>
> void xfs_stats_clearall(struct xfsstats __percpu *stats)
> {
> + uint32_t xs_inodes_active, xs_inodes_meta;
> int c;
> - uint32_t xs_inodes_active;
>
> xfs_notice(NULL, "Clearing xfsstats");
> for_each_possible_cpu(c) {
> preempt_disable();
> - /* save xs_inodes_active, it's a universal truth! */
> + /*
> + * Save the active / meta inode counters, as they are stateful.
> + */
> xs_inodes_active = per_cpu_ptr(stats, c)->s.xs_inodes_active;
> + xs_inodes_meta = per_cpu_ptr(stats, c)->s.xs_inodes_meta;
> memset(per_cpu_ptr(stats, c), 0, sizeof(*stats));
> per_cpu_ptr(stats, c)->s.xs_inodes_active = xs_inodes_active;
> + per_cpu_ptr(stats, c)->s.xs_inodes_meta = xs_inodes_meta;
> preempt_enable();
> }
> }
> diff --git a/fs/xfs/xfs_stats.h b/fs/xfs/xfs_stats.h
> index 64bc0cc18126..57c32b86c358 100644
> --- a/fs/xfs/xfs_stats.h
> +++ b/fs/xfs/xfs_stats.h
> @@ -142,7 +142,8 @@ struct __xfsstats {
> uint32_t xs_gc_read_calls;
> uint32_t xs_gc_write_calls;
> uint32_t xs_gc_zone_reset_calls;
> - uint32_t __pad1;
> +/* Metafile counters */
> + uint32_t xs_inodes_meta;
> /* Extra precision counters */
> uint64_t xs_xstrat_bytes;
> uint64_t xs_write_bytes;
> --
> 2.47.3
>
>
^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] xfs: remove metafile inodes from the active inode stat
2026-02-06 6:43 ` Darrick J. Wong
@ 2026-02-06 6:52 ` Christoph Hellwig
0 siblings, 0 replies; 14+ messages in thread
From: Christoph Hellwig @ 2026-02-06 6:52 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: Christoph Hellwig, Carlos Maiolino, linux-xfs
On Thu, Feb 05, 2026 at 10:43:42PM -0800, Darrick J. Wong wrote:
> > Signed-off-by: Christoph Hellwig <hch@lst.de>
>
> Looks ok to me, though I wonder slightly about the atomicity of the
> percpu counter inc/decrements. But it's been that way for a long time
> so
It's not atomic, but the percpu summing is by definition never fully
atomic anyway. So it might add a bit more of error margin than the
pure summing, but it removes the much larger error margin of having the
metadir inodes accounted in the first place.
^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 2/2] xfs: remove metafile inodes from the active inode stat
2026-02-24 13:59 fix inode stats with lots of metafiles v2 Christoph Hellwig
@ 2026-02-24 13:59 ` Christoph Hellwig
0 siblings, 0 replies; 14+ messages in thread
From: Christoph Hellwig @ 2026-02-24 13:59 UTC (permalink / raw)
To: Carlos Maiolino; +Cc: Darrick J. Wong, linux-xfs
The active inode (or active vnode until recently) stat can get much larger
than expected on file systems with a lot of metafile inodes like zoned
file systems on SMR hard disks with 10.000s of rtg rmap inodes.
Remove all metafile inodes from the active counter to make it more useful
to track actual workloads and add a separate counter for active metafile
inodes.
This fixes xfs/177 on SMR hard drives.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: "Darrick J. Wong" <djwong@kernel.org>
---
fs/xfs/libxfs/xfs_inode_buf.c | 4 ++++
fs/xfs/libxfs/xfs_metafile.c | 5 +++++
fs/xfs/xfs_icache.c | 5 ++++-
fs/xfs/xfs_stats.c | 11 ++++++++---
fs/xfs/xfs_stats.h | 3 ++-
5 files changed, 23 insertions(+), 5 deletions(-)
diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c
index a017016e9075..3794e5412eba 100644
--- a/fs/xfs/libxfs/xfs_inode_buf.c
+++ b/fs/xfs/libxfs/xfs_inode_buf.c
@@ -268,6 +268,10 @@ xfs_inode_from_disk(
}
if (xfs_is_reflink_inode(ip))
xfs_ifork_init_cow(ip);
+ if (xfs_is_metadir_inode(ip)) {
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
+ XFS_STATS_INC(ip->i_mount, xs_inodes_meta);
+ }
return 0;
out_destroy_data_fork:
diff --git a/fs/xfs/libxfs/xfs_metafile.c b/fs/xfs/libxfs/xfs_metafile.c
index cf239f862212..71f004e9dc64 100644
--- a/fs/xfs/libxfs/xfs_metafile.c
+++ b/fs/xfs/libxfs/xfs_metafile.c
@@ -61,6 +61,9 @@ xfs_metafile_set_iflag(
ip->i_diflags2 |= XFS_DIFLAG2_METADATA;
ip->i_metatype = metafile_type;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
+ XFS_STATS_INC(ip->i_mount, xs_inodes_meta);
}
/* Clear the metadata directory inode flag. */
@@ -74,6 +77,8 @@ xfs_metafile_clear_iflag(
ip->i_diflags2 &= ~XFS_DIFLAG2_METADATA;
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
+ XFS_STATS_INC(ip->i_mount, xs_inodes_active);
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_meta);
}
/*
diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c
index f76c6decdaa3..f2d4294efd37 100644
--- a/fs/xfs/xfs_icache.c
+++ b/fs/xfs/xfs_icache.c
@@ -172,7 +172,10 @@ __xfs_inode_free(
/* asserts to verify all state is correct here */
ASSERT(atomic_read(&ip->i_pincount) == 0);
ASSERT(!ip->i_itemp || list_empty(&ip->i_itemp->ili_item.li_bio_list));
- XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
+ if (xfs_is_metadir_inode(ip))
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_meta);
+ else
+ XFS_STATS_DEC(ip->i_mount, xs_inodes_active);
call_rcu(&VFS_I(ip)->i_rcu, xfs_inode_free_callback);
}
diff --git a/fs/xfs/xfs_stats.c b/fs/xfs/xfs_stats.c
index bc4a5d6dc795..c13d600732c9 100644
--- a/fs/xfs/xfs_stats.c
+++ b/fs/xfs/xfs_stats.c
@@ -59,7 +59,8 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
{ "rtrefcntbt", xfsstats_offset(xs_qm_dqreclaims)},
/* we print both series of quota information together */
{ "qm", xfsstats_offset(xs_gc_read_calls)},
- { "zoned", xfsstats_offset(__pad1)},
+ { "zoned", xfsstats_offset(xs_inodes_meta)},
+ { "metafile", xfsstats_offset(xs_xstrat_bytes)},
};
/* Loop over all stats groups */
@@ -99,16 +100,20 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
void xfs_stats_clearall(struct xfsstats __percpu *stats)
{
+ uint32_t xs_inodes_active, xs_inodes_meta;
int c;
- uint32_t xs_inodes_active;
xfs_notice(NULL, "Clearing xfsstats");
for_each_possible_cpu(c) {
preempt_disable();
- /* save xs_inodes_active, it's a universal truth! */
+ /*
+ * Save the active / meta inode counters, as they are stateful.
+ */
xs_inodes_active = per_cpu_ptr(stats, c)->s.xs_inodes_active;
+ xs_inodes_meta = per_cpu_ptr(stats, c)->s.xs_inodes_meta;
memset(per_cpu_ptr(stats, c), 0, sizeof(*stats));
per_cpu_ptr(stats, c)->s.xs_inodes_active = xs_inodes_active;
+ per_cpu_ptr(stats, c)->s.xs_inodes_meta = xs_inodes_meta;
preempt_enable();
}
}
diff --git a/fs/xfs/xfs_stats.h b/fs/xfs/xfs_stats.h
index 64bc0cc18126..57c32b86c358 100644
--- a/fs/xfs/xfs_stats.h
+++ b/fs/xfs/xfs_stats.h
@@ -142,7 +142,8 @@ struct __xfsstats {
uint32_t xs_gc_read_calls;
uint32_t xs_gc_write_calls;
uint32_t xs_gc_zone_reset_calls;
- uint32_t __pad1;
+/* Metafile counters */
+ uint32_t xs_inodes_meta;
/* Extra precision counters */
uint64_t xs_xstrat_bytes;
uint64_t xs_write_bytes;
--
2.47.3
^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: fix inode stats with lots of metafiles
2026-02-02 14:14 fix inode stats with lots of metafiles Christoph Hellwig
2026-02-02 14:14 ` [PATCH 1/2] xfs: cleanup inode counter stats Christoph Hellwig
2026-02-02 14:14 ` [PATCH 2/2] xfs: remove metafile inodes from the active inode stat Christoph Hellwig
@ 2026-02-25 9:38 ` Carlos Maiolino
2 siblings, 0 replies; 14+ messages in thread
From: Carlos Maiolino @ 2026-02-25 9:38 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: Darrick J. Wong, linux-xfs
On Mon, 02 Feb 2026 15:14:30 +0100, Christoph Hellwig wrote:
> the second patch fixes the active inode/vnode count on file systems with
> lots of metafiles, i.e. on SMR hard disks.
>
> The first patch cleans up the counters a bit to prepare for that.
>
> Subject:
> libxfs/xfs_inode_buf.c | 4 ++++
> libxfs/xfs_metafile.c | 5 +++++
> xfs_icache.c | 9 ++++++---
> xfs_stats.c | 17 +++++++++++------
> xfs_stats.h | 19 ++++++++++---------
> xfs_super.c | 4 ++--
> 6 files changed, 38 insertions(+), 20 deletions(-)
>
> [...]
Applied to for-next, thanks!
[1/2] xfs: cleanup inode counter stats
commit: 810e363df769b096bfc9546b1c43fae803492cde
[2/2] xfs: remove metafile inodes from the active inode stat
commit: 428980f6202be859428afd9887cb122028de00d5
Best regards,
--
Carlos Maiolino <cem@kernel.org>
^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-02-25 9:38 UTC | newest]
Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-02 14:14 fix inode stats with lots of metafiles Christoph Hellwig
2026-02-02 14:14 ` [PATCH 1/2] xfs: cleanup inode counter stats Christoph Hellwig
2026-02-03 6:16 ` Nirjhar Roy (IBM)
2026-02-03 6:47 ` Christoph Hellwig
2026-02-06 6:38 ` Darrick J. Wong
2026-02-02 14:14 ` [PATCH 2/2] xfs: remove metafile inodes from the active inode stat Christoph Hellwig
2026-02-03 7:11 ` Nirjhar Roy (IBM)
2026-02-03 7:14 ` Christoph Hellwig
2026-02-03 7:24 ` Nirjhar Roy (IBM)
2026-02-03 7:29 ` Christoph Hellwig
2026-02-06 6:43 ` Darrick J. Wong
2026-02-06 6:52 ` Christoph Hellwig
2026-02-25 9:38 ` fix inode stats with lots of metafiles Carlos Maiolino
-- strict thread matches above, loose matches on Subject: below --
2026-02-24 13:59 fix inode stats with lots of metafiles v2 Christoph Hellwig
2026-02-24 13:59 ` [PATCH 2/2] xfs: remove metafile inodes from the active inode stat Christoph Hellwig
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox