Skip to content

Commit 7b67b83

Browse files
kext: sysfs: count VFS_GETATTR and vnode operations
sysfs.live_nodes reports how many nodes exist, not how hard they are worked, and VFS_GETATTR creates no vnodes at all - so a filesystem being hammered by statfs, or serving heavy traffic on a single cached path, registered as completely idle. That blind spot cost several wrong diagnoses. Add sysfs.vfs_getattr and sysfs.vnops so activity is visible independently of node count.
1 parent c0cd6e3 commit 7b67b83

3 files changed

Lines changed: 55 additions & 0 deletions

File tree

kext/sysfs.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ extern vfstable_t sysfs_vfs_table_ref;
4949
* Sample them a few times a minute apart with /sys mounted: whichever one grows
5050
* without bound identifies the subsystem at fault.
5151
*/
52+
extern int64_t sysfs_stat_vfs_getattr;
53+
extern int64_t sysfs_stat_vnops;
5254
extern int64_t sysfs_stat_snap_builds;
5355
extern int64_t sysfs_stat_snap_bytes;
5456
extern int64_t sysfs_stat_live_nodes;
@@ -118,10 +120,38 @@ static struct sysctl_oid sysfs_sysctl_live_nodes = {
118120
.oid_version = SYSCTL_OID_VERSION,
119121
};
120122

123+
static struct sysctl_oid sysfs_sysctl_vfs_getattr = {
124+
.oid_parent = &sysfs_sysctl_children,
125+
.oid_number = OID_AUTO,
126+
.oid_kind = CTLTYPE_QUAD | CTLFLAG_RD | CTLFLAG_LOCKED | CTLFLAG_OID2,
127+
.oid_arg1 = &sysfs_stat_vfs_getattr,
128+
.oid_arg2 = 0,
129+
.oid_name = "vfs_getattr",
130+
.oid_handler = sysctl_handle_quad,
131+
.oid_fmt = "Q",
132+
.oid_descr = "VFS_GETATTR (statfs) calls served for this mount",
133+
.oid_version = SYSCTL_OID_VERSION,
134+
};
135+
136+
static struct sysctl_oid sysfs_sysctl_vnops = {
137+
.oid_parent = &sysfs_sysctl_children,
138+
.oid_number = OID_AUTO,
139+
.oid_kind = CTLTYPE_QUAD | CTLFLAG_RD | CTLFLAG_LOCKED | CTLFLAG_OID2,
140+
.oid_arg1 = &sysfs_stat_vnops,
141+
.oid_arg2 = 0,
142+
.oid_name = "vnops",
143+
.oid_handler = sysctl_handle_quad,
144+
.oid_fmt = "Q",
145+
.oid_descr = "vnode operations served (lookup/getattr/readdir/read)",
146+
.oid_version = SYSCTL_OID_VERSION,
147+
};
148+
121149
STATIC void
122150
sysfs_sysctl_register(void)
123151
{
124152
sysctl_register_oid(&sysfs_sysctl_node); /* parent first */
153+
sysctl_register_oid(&sysfs_sysctl_vfs_getattr);
154+
sysctl_register_oid(&sysfs_sysctl_vnops);
125155
sysctl_register_oid(&sysfs_sysctl_snap_builds);
126156
sysctl_register_oid(&sysfs_sysctl_snap_bytes);
127157
sysctl_register_oid(&sysfs_sysctl_live_nodes);
@@ -133,6 +163,8 @@ sysfs_sysctl_unregister(void)
133163
sysctl_unregister_oid(&sysfs_sysctl_live_nodes);
134164
sysctl_unregister_oid(&sysfs_sysctl_snap_bytes);
135165
sysctl_unregister_oid(&sysfs_sysctl_snap_builds);
166+
sysctl_unregister_oid(&sysfs_sysctl_vnops);
167+
sysctl_unregister_oid(&sysfs_sysctl_vfs_getattr);
136168
sysctl_unregister_oid(&sysfs_sysctl_node);
137169
}
138170

kext/sysfs_vfsops.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,19 @@ sysfs_root(struct mount *mp, vnode_t *vpp, __unused vfs_context_t context)
305305
* sysfs. Most of them are dummy values and none of them change once the
306306
* file system has been mounted.
307307
*/
308+
/*
309+
* Counts VFS_GETATTR (statfs) calls against this mount. These create no vnodes,
310+
* so they are invisible to sysfs.live_nodes - yet statfs-on-every-mount is
311+
* exactly what Finder, Disk Arbitration, Spotlight and the Dock do constantly.
312+
* Without this counter a storm of them would look like the filesystem was
313+
* completely idle. Exposed as sysfs.vfs_getattr.
314+
*/
315+
int64_t sysfs_stat_vfs_getattr = 0;
316+
308317
STATIC int
309318
sysfs_getattr(struct mount *mp, struct vfs_attr *fsap, __unused vfs_context_t context)
310319
{
320+
OSAddAtomic64(1, &sysfs_stat_vfs_getattr);
311321
populate_vfs_attr(mp, fsap);
312322
return 0;
313323
}

kext/sysfs_vnops.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
* process/thread markers.
1616
*/
1717
#include <libkern/libkern.h>
18+
#include <libkern/OSAtomic.h>
1819

1920
#include <sys/dirent.h>
2021
#include <sys/errno.h>
@@ -134,6 +135,14 @@ STATIC size_t sysfs_device_attr_size(sfsnode_t *snp);
134135
*/
135136
int (**sysfs_vnodeop_p)(void *);
136137

138+
/*
139+
* Counts every vnode operation this filesystem services. sysfs.live_nodes shows
140+
* how many nodes exist, not how hard they are being worked - a caller that
141+
* repeatedly stats or reads one already-cached path drives no new nodes at all.
142+
* Exposed as sysfs.vnops.
143+
*/
144+
int64_t sysfs_stat_vnops = 0;
145+
137146
/*
138147
* Entries for the vnode operations that this file system supports. This table is
139148
* converted to a fully-populated vnode operations vector when sysfs is
@@ -295,6 +304,7 @@ sysfs_vnop_getattrlistbulk(__unused struct vnop_getattrlistbulk_args *ap)
295304
STATIC int
296305
sysfs_vnop_lookup(struct vnop_lookup_args *ap)
297306
{
307+
OSAddAtomic64(1, &sysfs_stat_vnops);
298308
char name[NAME_MAX + 1];
299309
int error = 0;
300310
struct componentname *cnp = ap->a_cnp;
@@ -504,6 +514,7 @@ sysfs_vnop_lookup(struct vnop_lookup_args *ap)
504514
STATIC int
505515
sysfs_vnop_readdir(struct vnop_readdir_args *ap)
506516
{
517+
OSAddAtomic64(1, &sysfs_stat_vnops);
507518
vnode_t vp = ap->a_vp;
508519
if (vnode_vtype(vp) != VDIR) {
509520
return ENOTDIR;
@@ -798,6 +809,7 @@ sysfs_copyout_dirent(int type, uint64_t file_id, const char *name, uio_t uio, in
798809
STATIC int
799810
sysfs_vnop_getattr(struct vnop_getattr_args *ap)
800811
{
812+
OSAddAtomic64(1, &sysfs_stat_vnops);
801813
vnode_t vp = ap->a_vp;
802814
sfsnode_t *sysfs_node = VTOSFS(vp);
803815
sfssnode_t *snode = sysfs_node->node_structure_node;
@@ -889,6 +901,7 @@ sysfs_vnop_readlink(struct vnop_readlink_args *ap)
889901
STATIC int
890902
sysfs_vnop_read(struct vnop_read_args *ap)
891903
{
904+
OSAddAtomic64(1, &sysfs_stat_vnops);
892905
vnode_t vp = ap->a_vp;
893906
sfsnode_t *snp = VTOSFS(vp);
894907
sfssnode_t *snode = snp->node_structure_node;

0 commit comments

Comments
 (0)