mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Konstantin Khorenko <khorenko@virtuozzo.com>
To: Andrew Morton <akpm@linux-foundation.org>
Cc: Dave Chinner <david@fromorbit.com>, Qi Zheng <qi.zheng@linux.dev>,
	Roman Gushchin <roman.gushchin@linux.dev>,
	Muchun Song <muchun.song@linux.dev>,
	Johannes Weiner <hannes@cmpxchg.org>,
	Michal Hocko <mhocko@kernel.org>,
	Shakeel Butt <shakeel.butt@linux.dev>,
	linux-mm@kvack.org, linux-kernel@vger.kernel.org,
	cgroups@vger.kernel.org
Subject: Re: [PATCH v2 0/2] mm/shrinker: make per-memcg shrinker units cheaper
Date: Fri, 16 Oct 2026 14:27:32 +0200	[thread overview]
Message-ID: <79c1246f-8c9e-4e2a-adbf-e730732b33b1@virtuozzo.com> (raw)
In-Reply-To: <20261015175836.efc93cf9de01306402da61f9@linux-foundation.org>

On 10/16/26 02:58, Andrew Morton wrote:
> On Thu, 15 Oct 2026 13:42:41 +0200 Konstantin Khorenko <khorenko@virtuozzo.com> wrote:
...
>> If you prefer the cache, I'm happy to redo patch 1 that way.
>> Patch 2 does not care about the unit size.
> 
> OK, thanks.  Yes, using a kmem_cache is a simple no-brainer.  Let's get
> that done first and then let's look at additional optimizations on top
> as a separate activity.

OK, no problem. I'll redo patch 1 with a dedicated kmem_cache and send
it separately.


> But a tweak which reduces 145GB of bloat to half of that is lipsticking
> the pig, and what a pig it is.
> 
> Why the heck is memcg allocating so many of these things?  What sort of
> rip-out/rethink/start-over can we do to reduce this by 99.x%?

That is what patch 2 does:
  [PATCH v2 2/2] mm/shrinker: allocate the per-memcg shrinker units on demand

Today every memcg allocates, on every node, a unit for every 63 shrinker
ids up to shrinker_nr_max, whether or not it ever has an object on those
shrinkers. Patch 2 allocates a unit only when the memcg gets a list on that
shrinker's list_lru, so a memcg pays for the shrinkers it uses, not for
every superblock on the host.

In the synthetic test from the cover letter (8000 tmpfs mounts,
shrinker_nr_max 8064, 2000 empty memcgs, 4 nodes):

                     units               slab per memcg
  base               1011518 x 1024 B    548 KB
  + patch 1          1023787 x  512 B    289 KB
  + patches 1-2          136 x  512 B     42 KB

So patch 2 removes 99.99% of the units, not half. These are the numbers
of a synthetic test, of course. The 42 KB left is the memcg's own
structures plus 8 KB of shrinker_info pointer arrays. A memcg that uses
two filesystems gets 8 units (4 KB) on top of that.

On the host from the cover letter I'd expect the 145 GB to drop to the
pointer arrays plus the units the memcgs really use. The arrays are
about 1.4 GB there (45000 memcgs x 4 nodes x 8 KB at shrinker_nr_max
52480). That is an estimate, not a measurement.

What remains is the pointer array: it is still dense in
shrinker_nr_max, per memcg and per node. We can finish that path, or
do something different.

One option is to replace the array with an xarray of units per memcg
and node, indexed the same way.

Pros:

- An empty memcg pays nothing, and a memcg that uses a few units pays
  one or two 576-byte xa_nodes per node. The worst case is about what
  the array costs today: a leaf covers 64 units, i.e. 4032 ids, so at
  shrinker_nr_max 52480 a memcg that touches every leaf needs 15 nodes
  (8.6 KB) against an 8 KB array.

- expand_shrinker_info() goes away. Today a mount that raises
  shrinker_nr_max past a multiple of 63 reallocates and copies the
  array of every memcg on every node under shrinker_mutex: 180000
  allocations with 45000 memcgs on 4 nodes.

- shrink_slab_memcg() walks only the units present, with xa_find(),
  instead of every index up to shrinker_nr_max / 63.

- The shrinker_unit_lock that patch 2 adds to order an install against
  the copy in expand is no longer needed, the xa_lock does that.

Cons:

- The install can run under the i_pages lock with interrupts off
  (__filemap_add_folio() -> xas_alloc() -> memcg_list_lru_alloc()), so
  the xarray needs XA_FLAGS_LOCK_IRQ and its nodes are allocated with
  the caller's gfp. That is one more allocation on that path that can
  fail. A failure fails the list allocation, as a failed unit
  allocation does today.

- set_shrinker_bit() and the nr_deferred accessors do an xa_load(), a
  two-level walk at these sizes, instead of an array index.
  set_shrinker_bit() runs when a list goes from empty to non-empty,
  not per object, so I don't expect it to show.

- Root still needs its units up front for the deferred work of global
  reclaim. They would be installed when a shrinker registers instead
  of in expand.

Size: it would replace patch 2 with two patches. The first turns the
array into an xarray, with the units still allocated up front, and
removes struct shrinker_info and the expand path. The second allocates
the units on demand, without the lock and the expand handling patch 2
needs now. Together I expect about the size of patch 2 today, around
170 changed lines, mostly in mm/shrinker.c, with fewer lines in total
than now.

Which way would you prefer: patch 2 as it is, or the xarray version in
its place? Or something else?


Cheers,
Konstantin

      reply	other threads:[~2026-10-16 12:27 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-14 14:54 Konstantin Khorenko
2026-10-14 14:54 ` [PATCH v2 1/2] mm/shrinker: size shrinker_info_unit to a power of two Konstantin Khorenko
2026-10-14 14:54 ` [PATCH v2 2/2] mm/shrinker: allocate the per-memcg shrinker units on demand Konstantin Khorenko
2026-10-14 20:50 ` [PATCH v2 0/2] mm/shrinker: make per-memcg shrinker units cheaper Andrew Morton
2026-10-15 11:42   ` Konstantin Khorenko
2026-10-15 13:29     ` Muchun Song
2026-10-16  0:58     ` Andrew Morton
2026-10-16 12:27       ` Konstantin Khorenko [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=79c1246f-8c9e-4e2a-adbf-e730732b33b1@virtuozzo.com \
    --to=khorenko@virtuozzo.com \
    --cc=akpm@linux-foundation.org \
    --cc=cgroups@vger.kernel.org \
    --cc=david@fromorbit.com \
    --cc=hannes@cmpxchg.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=mhocko@kernel.org \
    --cc=muchun.song@linux.dev \
    --cc=qi.zheng@linux.dev \
    --cc=roman.gushchin@linux.dev \
    --cc=shakeel.butt@linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®