// SPDX-License-Identifier: GPL-2.0-or-later // // Counters for the filesystem and block paths, read through /proc/fsstat. // // These exist because "the system feels slow" is not a bug report anyone can // act on, and every guess about where the time goes in a filesystem is wrong // until it is measured. `ls /bin` taking three seconds could be the directory // walk, the tree descents, the copy path, or the disk -- and those four call // for completely different fixes. // // Deliberately plain `uint64` with no locking. A counter that is off by one // because two CPUs raced tells you the same thing as one that is exact, and a // lock here would change the very timings it is meant to report. #pragma once struct fsstat { // Buffer cache. `bread_miss` is the one that matters: a miss is a real // transfer, a hit is a memcpy. uint64 bread_calls; uint64 bread_miss; uint64 bget_evict_dirty; // had to write a buffer out to make room // Actual device transfers, and the cycles spent inside them. uint64 disk_reads; uint64 disk_writes; uint64 disk_cycles; // The VFS read path: one call is one read(2) reaching a SchFS file. uint64 read_calls; uint64 read_bytes; uint64 read_cycles; // Directory listing. uint64 getdents_calls; uint64 getdents_cycles; uint64 getdents_walked; // entries stepped over, including ones re-walked // B-tree descents from the root, and the block reads they cost. uint64 descents; // exec(2), which on this system means reading an 800 KB multicall binary. uint64 exec_calls; uint64 exec_cycles; // --------------------------------------------------------------------- // Table occupancy, for finding leaks. // // `_used` is the live count; `_peak` is the high-water mark, and the peak is // the number that answers the question. A live count only tells you about the // moment you looked, and a reference leak is invisible in it right up until // the table is full -- at which point iget() panics and filealloc() returns // 0, which is where the trail goes cold. // // Maintained at the 0->1 and 1->0 transitions rather than by scanning, so // they cost one increment on a path that already holds the relevant lock. // // What to watch: run a long ls-heavy session and read /proc/fsstat between // commands. A peak that climbs and never comes back down after the commands // finish is the leak, and which of the three it is says where to look. uint64 inode_used, inode_peak; uint64 file_used, file_peak; uint64 buf_pinned, buf_pinned_peak; }; // Bump a (used, peak) pair. Callers hold the table's own lock already. #define FSSTAT_TAKE(used, peak) \ do { if (++(used) > (peak)) (peak) = (used); } while (0) #define FSSTAT_DROP(used) \ do { if ((used) > 0) (used)--; } while (0) extern struct fsstat fsstat;