Re: [PATCH v2] mm: vmscan: avoid anon scanning for GFP_NOIO with low swapcache
From: Bo Zhang
Date: Sat Sep 05 2026 - 23:56:43 EST
Thanks a lot for the review, Andrew - much appreciated.
On Sat, 5 Sep 2026 19:46:02 -0700 Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> wrote:
> > To stay conservative, this only skips anon when the swapcache is really
> > tiny - below 1/64 of the anon LRU - i.e. when essentially no anon on the
> > list can be reclaimed without IO. Whenever there is a meaningful amount of
> > swapcached anon, the normal path is used and anon is scanned and aged as
> > before.
>
> Argh. The thing about magic numbers is that they're always suboptimal
> for everyone. But I understand that a full-on dynamic tuning setup is
> a big project and hopefully not worthwhile. And yet another /proc knob
> would require quite some justification.
Agreed - a full dynamic tuning setup would be complex, and I'd rather not
add a knob for this either. For now this uses a conservative threshold to
catch only the case where anon is effectively unreclaimable; the reasoning
is explained in the function comment (below).
> I think this function deserves a comment. One which explains why isn't
> doing what it does rather than what it does. That comment would
> highlight the heuristic and explain the thinking behind it.
Done in v3. The comment now explains the "why": a !__GFP_IO reclaimer can
only reclaim anon already in the swapcache, so when swapcache is far below
the anon LRU, scanning anon reclaims nothing and only burns CPU - and the
aging it would have done is merely deferred to later __GFP_IO reclaimers.
It also notes that 1/64 is a conservative "negligible swapcache" threshold.
> Also, AI review asks "does reclaimable_anon_is_low() incorrectly use
> root memcg statistics instead of node-wide statistics during global
> memory reclaim?".
Good catch - it did, and I've fixed it in v3.
For memcg reclaim, can_reclaim_anon_pages() is called per-memcg (memcg is
the concrete cgroup being scanned), so using its lruvec stats is correct.
But for global reclaim it is also called with memcg == NULL - e.g. from
set_initial_priority() - and there mem_cgroup_lruvec(NULL) resolves to the
root memcg, whose stats exclude the child cgroups where most anon lives.
That could make the check fire on the root's tiny stats even when the node
has plenty of anon and swapcache elsewhere.
v3 splits the two cases: use the memcg's lruvec stats when memcg is set,
and node_page_state() when memcg == NULL, matching the node-wide view its
global callers already use for the file side.
I'll send v3 with these changes.
Thanks,
Bo