diff options
| author | Qiliang Yuan <odys.yuan@gmail.com> | 2026-09-28 20:54:40 +0800 |
|---|---|---|
| committer | Jan Kara <jack@suse.cz> | 2026-09-29 22:55:40 +0200 |
| commit | 8b1534b6c47b6f85d3eaa1f169d2e298db19de24 (patch) | |
| tree | 1b81a25a54b6b9c0279c98ba7a6807d1ab03f017 /tools/perf/scripts/python/bin/stackcollapse-report | |
| parent | 1e2ef9f221e142ed19d874a71b2cae7fabbaf138 (diff) | |
| download | linux-8b1534b6c47b6f85d3eaa1f169d2e298db19de24.tar.gz linux-8b1534b6c47b6f85d3eaa1f169d2e298db19de24.zip | |
quota: fix shrinker scan budget accounting in dqcache_shrink_scan()
The dquot cache shrinker's count_objects() callback,
dqcache_shrink_count(), reports the number of unused dquot objects
via the DQST_FREE_DQUOTS percpu counter (through vfs_pressure_ratio()).
do_shrink_slab() reads this count exactly once per invocation and
derives a one-shot scan budget (total_scan) from it, then repeatedly
calls scan_objects() in fixed-size batches until that budget is
exhausted.
dqcache_shrink_scan() never updates sc->nr_scanned, even though
include/linux/shrinker.h documents that "the callee should track its
actual progress" in that field. The function already decrements
sc->nr_to_scan in place as it walks free_dquots, one per dquot
destroyed, and its while loop exits immediately without touching
sc->nr_to_scan at all when free_dquots is already empty. Because
sc->nr_scanned defaults to sc->nr_to_scan before every call,
do_shrink_slab() always believes a full batch was examined regardless
of what actually happened, so it has no way to tell "there was
nothing to do this pass" from "a full batch was scanned and none of
it was freeable", and keeps calling scan_objects() until the
original, one-shot total_scan budget computed from the percpu count
is drained, even though free_dquots emptied out long before that
budget was used up.
Compute sc->nr_scanned from the difference between the nr_to_scan
value on entry and the value left behind by the reclaim loop, and
return SHRINK_STOP once that difference comes back as zero, since
that only happens when free_dquots was already empty and no further
calls in this reclaim pass can make progress.
Tested on a loop-mounted ext4 image with the quota feature enabled:
chown()ing several hundred files to distinct uid:gid pairs to
populate allocated dquots, then triggering
"echo 2 > /proc/sys/vm/drop_caches" to let them drop to zero
references and become reclaimable, while using an ad hoc kprobe pair
on dqcache_shrink_scan() (this shrinker has no dedicated tracepoints)
to record sc->nr_to_scan on entry and the return value on exit:
total scan_objects() calls with
calls freed == 0
before this patch 12 5 (42%)
after this patch 5 0
The last call in the "after" run returns SHRINK_STOP
(0xffffffffffffffff) instead of a fifth zero-progress attempt.
This is the same missing sc->nr_scanned accounting already fixed in
the analogous ext4 extents_status shrinker and the jbd2 checkpoint
shrinker; dqcache_shrink_scan() was found by auditing every shrinker
in the tree whose count_objects() reads a percpu_counter (the
precondition for count_objects() to report a stale, still-positive
value after the reclaimable list has actually drained), while ruling
out the much larger set of shrinkers built on the exact list_lru
atomic counters, which cannot observe this kind of staleness.
Signed-off-by: Qiliang Yuan <odys.yuan@gmail.com>
Link: https://patch.msgid.link/20260928-fix-dquot-shrink-scan-nr-scanned-v1-1-789c661c9dd6@gmail.com
Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'tools/perf/scripts/python/bin/stackcollapse-report')
0 files changed, 0 insertions, 0 deletions
