Re: [PATCH v4 4/5] mm/memcontrol: convert memcg to use page_counter_stock

From: Joshua Hahn

Date: Wed Jun 24 2026 - 14:24:13 EST


On Wed, 24 Jun 2026 17:43:56 +0100 Usama Arif <usama.arif@xxxxxxxxx> wrote:

>
>
> On 24/06/2026 16:23, Joshua Hahn wrote:
> > On Wed, 24 Jun 2026 07:43:47 -0700 Usama Arif <usama.arif@xxxxxxxxx> wrote:
> >
> >> On Tue, 23 Jun 2026 11:01:22 -0700 Joshua Hahn <joshua.hahnjy@xxxxxxxxx> wrote:
> >
> > Hello Usama!!
> >
> > Thank you for reviewing the patch : -)
> >
> > [...snip...]
> >
> >>> @@ -2595,7 +2596,6 @@ void __mem_cgroup_handle_over_high(gfp_t gfp_mask)
> >>> static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
> >>> unsigned int nr_pages)
> >>> {
> >>> - unsigned int batch = max(MEMCG_CHARGE_BATCH, nr_pages);
> >>> int nr_retries = MAX_RECLAIM_RETRIES;
> >>> struct mem_cgroup *mem_over_limit;
> >>> struct page_counter *counter;
> >>> @@ -2606,36 +2606,30 @@ static int try_charge_memcg(struct mem_cgroup *memcg, gfp_t gfp_mask,
> >>> bool raised_max_event = false;
> >>> unsigned long pflags;
> >>> bool allow_spinning = gfpflags_allow_spinning(gfp_mask);
> >>> + unsigned long nr_charged = 0;
> >>>
> >>> retry:
> >>> - if (consume_stock(memcg, nr_pages))
> >>> - return 0;
> >>> -
> >>> - if (!allow_spinning)
> >>> - /* Avoid the refill and flush of the older stock */
> >>> - batch = nr_pages;
> >>> -
> >>> reclaim_options = MEMCG_RECLAIM_MAY_SWAP;
> >>> if (do_memsw_account() &&
> >>> - !page_counter_try_charge(&memcg->memsw, batch, &counter)) {
> >>> + !page_counter_try_charge_stock(&memcg->memsw, nr_pages,
> >>> + &counter, NULL)) {
> >>> mem_over_limit = mem_cgroup_from_counter(counter, memsw);
> >>> reclaim_options &= ~MEMCG_RECLAIM_MAY_SWAP;
> >>> goto reclaim;
> >>> }
> >>>
> >>> - if (page_counter_try_charge(&memcg->memory, batch, &counter))
> >>> - goto done_restock;
> >>> + if (page_counter_try_charge_stock(&memcg->memory, nr_pages,
> >>> + &counter, &nr_charged)) {
> >>> + if (!nr_charged)
> >>> + return 0;
> >>> + goto handle_high;
> >>> + }
> >>>
> >>> if (do_memsw_account())
> >>> - page_counter_uncharge(&memcg->memsw, batch);
> >>> + page_counter_uncharge(&memcg->memsw, nr_pages);
> >>
> >> This needs a transactional rollback. page_counter_try_charge_stock() can
> >> succeed by consuming memsw stock and charging 0 new pages, but the
> >> memory-failure path unconditionally uncharges nr_pages from memsw.
> >> That turns a failed allocation into a real memsw usage decrement.
> >
> > Hmmmmmmmmmm....... I'm not sure.
> >
> > At this point in the code, we are either (1) using cgroup v1 with memsw
> > and charged successfully, or (2) not using cgroup v1 with memsw. So I'm
> > not sure if this really is unconditional, we're just distinguishing
> > between cases (1) and (2) by checking if we're using cgroupv1.
> >
> > Or is your concern with taking a charge via stock, but uncharging with
> > a hierarchical page_counter walk?
>
> This was my concern. But I re-read the page_counter stock invariant,
> and the stock-hit case is not an undercount? Consuming stock transfers
> already-charged credit to the pending allocation; if the later memory charge
> fails, page_counter_uncharge() discards that consumed credit from the
> hierarchy. That should keeps usage equal to real charges plus remaining stock?

Yes, stock-hit case just does some math without doing any actual
charging. It's stuff that was pre-charged before, so we're not doing
any undercounting or leaking any charges.

What do you mean by "consumed credit"? From what I can see
page_counter_uncharge --> page_counter_cancel subtracts from
counter->usage, which should be the real charge + hierarchy walk.

Am I missing something :p please feel free to let me know!
Joshua