Re: [PATCH v6 0/5] mm/page_counter: move stock from mem_cgroup to page_counter
From: Joshua Hahn
Date: Thu Sep 17 2026 - 18:35:23 EST
On Wed, 16 Sep 2026 14:05:46 -0700 Joshua Hahn <joshua.hahnjy@xxxxxxxxx> wrote:
> v5 --> v6
> =========
> Following feedback that v5 combined the (1) stock abstraction move from
> memcg to page_counter and (2) changing the allocation / draining
> behavior, v6 limits itself to only the first goal. It retains the
> existing seven-slot per-CPU design and drain policy.
>
> INTRODUCTION
> ============
> Memcg keeps a per-CPU stock of precharged pages so that small, frequent
> allocations do not walk the page_counter hierarchy every time.
> Today, the stock implementation is within memcontrol code, even though
> the operation it caches is a page_counter charge. This makes it
> difficult to add new page_counters to a memcg and preserve the fast
> path behavior.
>
> This matters for future work like my tiered memcg limits series [1]
> which introduces multiple new page_counters to memcg. Without making
> stock a page_counter-level property, it means that every memcg charge
> now goes through multiple page_counter hierarchy walks, instead of
> being able to cache these charges.
>
> To make future page_counters scalable and performant, move stock from
> mem_cgroup to page_counter so that each page_counter can opt into its
> own per-CPU cache of pre-charged pages.
>
> We get an added benefit of simplifying try_charge_memcg code, which now
> has all the stock management handled transparently within the
> page_counter layer.
Sashiko raised one bug for the series:
@@ -192,11 +245,20 @@ bool page_counter_try_charge(struct page_counter *counter,
WRITE_ONCE(c->watermark, new);
}
}
+ if (charge > nr_pages)
+ page_counter_refill_stock(counter, charge - nr_pages);
+ if (nr_charged)
+ *nr_charged = charge;
return true;
failed:
And asked: Does this unconditionally report the batched size to the
caller even if the excess was rejected by the stock and uncharged from
the hierarchy?
---
This is true, but this is already the behavior for vanilla memcg.
In this series I'm hoping to preserve all existing semantics without
changing behaviors, so I can fix this problem in a separate issue.
Specifically, in vanilla try_charge_memcg:
done_restock:
if (batch > nr_pages)
refill_stock(memcg, batch - nr_pages);
...
current->memcg_nr_pages_over_high += batch;
So I've just preserved the exact semantics that we used to have before.
The problem isn't that big anyways though, it's a transient inflation
in memcg_over_high and will be wiped on the next high handling run,
and there is no effect on accounting or permanent inflations.
So I think this issue is pre-existing and a minor transient inflation
for memcg_over_high at best. If this looks problematic I can write an
orthogonal fix separately.
Thanks anyways, Sashiko!
Joshua